Buttplug sex toy control library
1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2024 Nonpolynomial Labs LLC. All rights reserved.
4//
5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
6// for full license information.
7
8use buttplug_core::errors::ButtplugDeviceError;
9use buttplug_server_device_config::Endpoint;
10use buttplug_server_device_config::{
11 ProtocolCommunicationSpecifier,
12 ServerDeviceDefinition,
13 UserDeviceIdentifier,
14};
15
16use crate::device::{
17 hardware::{Hardware, HardwareCommand, HardwareWriteCmd},
18 protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer},
19};
20use async_trait::async_trait;
21use std::sync::{
22 Arc,
23 atomic::{AtomicU8, Ordering},
24};
25use uuid::{Uuid, uuid};
26
27pub mod setup {
28 use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory};
29 #[derive(Default)]
30 pub struct PatooIdentifierFactory {}
31
32 impl ProtocolIdentifierFactory for PatooIdentifierFactory {
33 fn identifier(&self) -> &str {
34 "patoo"
35 }
36
37 fn create(&self) -> Box<dyn ProtocolIdentifier> {
38 Box::new(super::PatooIdentifier::default())
39 }
40 }
41}
42
43#[derive(Default)]
44pub struct PatooIdentifier {}
45
46#[async_trait]
47impl ProtocolIdentifier for PatooIdentifier {
48 async fn identify(
49 &mut self,
50 hardware: Arc<Hardware>,
51 _: ProtocolCommunicationSpecifier,
52 ) -> Result<(UserDeviceIdentifier, Box<dyn ProtocolInitializer>), ButtplugDeviceError> {
53 // Patoo Love devices have wildcarded names of ([A-Z]+)\d*
54 // Force the identifier lookup to the non-numeric portion
55 let c: Vec<char> = hardware.name().chars().collect();
56 let mut i = 0;
57 while i < c.len() && !c[i].is_ascii_digit() {
58 i += 1;
59 }
60 let name: String = c[0..i].iter().collect();
61 Ok((
62 UserDeviceIdentifier::new(hardware.address(), "Patoo", &Some(name)),
63 Box::new(PatooInitializer::default()),
64 ))
65 }
66}
67
68#[derive(Default)]
69pub struct PatooInitializer {}
70
71#[async_trait]
72impl ProtocolInitializer for PatooInitializer {
73 async fn initialize(
74 &mut self,
75 _: Arc<Hardware>,
76 _: &ServerDeviceDefinition,
77 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
78 Ok(Arc::new(Patoo::default()))
79 }
80}
81
82const PATOO_TX_PROTOCOL_UUID: Uuid = uuid!("2366a70f-9a7c-4fea-8ba6-8b21a7d5d641");
83const PATOO_TX_MODE_PROTOCOL_UUID: Uuid = uuid!("b17714be-fc66-4d9b-bf52-afb3b91212a4");
84
85#[derive(Default)]
86pub struct Patoo {
87 speeds: [AtomicU8; 2],
88}
89
90impl ProtocolHandler for Patoo {
91 fn handle_output_vibrate_cmd(
92 &self,
93 feature_index: u32,
94 _feature_id: uuid::Uuid,
95 speed: u32,
96 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
97 self.speeds[feature_index as usize].store(speed as u8, Ordering::Relaxed);
98 let mut msg_vec = vec![];
99 // Default to vibes
100 let mut mode: u8 = 4u8;
101
102 // Use vibe 1 as speed
103 let mut speed = self.speeds[0].load(Ordering::Relaxed);
104 if speed == 0 {
105 mode = 0;
106
107 let speed2 = self.speeds[1].load(Ordering::Relaxed);
108 // If we have a second vibe and it's not also 0, use that
109 if speed2 != 0 {
110 speed = speed2;
111 mode |= 0x80;
112 }
113 }
114
115 msg_vec.push(
116 HardwareWriteCmd::new(&[PATOO_TX_PROTOCOL_UUID], Endpoint::Tx, vec![speed], true).into(),
117 );
118 msg_vec.push(
119 HardwareWriteCmd::new(
120 &[PATOO_TX_MODE_PROTOCOL_UUID],
121 Endpoint::TxMode,
122 vec![mode],
123 true,
124 )
125 .into(),
126 );
127
128 Ok(msg_vec)
129 }
130}