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 std::sync::{
9 Arc,
10 atomic::{AtomicU8, Ordering},
11};
12
13use async_trait::async_trait;
14use uuid::{Uuid, uuid};
15
16use crate::device::{
17 hardware::{Hardware, HardwareCommand, HardwareWriteCmd},
18 protocol::{
19 ProtocolHandler,
20 ProtocolIdentifier,
21 ProtocolInitializer,
22 generic_protocol_initializer_setup,
23 },
24};
25use buttplug_core::errors::ButtplugDeviceError;
26use buttplug_server_device_config::Endpoint;
27use buttplug_server_device_config::{
28 ProtocolCommunicationSpecifier,
29 ServerDeviceDefinition,
30 UserDeviceIdentifier,
31};
32const LOVEHONEY_DESIRE_PROTOCOL_UUID: Uuid = uuid!("5dcd8487-4814-44cb-a768-13bf81d545c0");
33const LOVEHONEY_DESIRE_VIBE2_PROTOCOL_UUID: Uuid = uuid!("d44a99fe-903b-4fff-bee7-1141767c9cca");
34
35generic_protocol_initializer_setup!(LovehoneyDesire, "lovehoney-desire");
36
37#[derive(Default)]
38pub struct LovehoneyDesireInitializer {}
39
40#[async_trait]
41impl ProtocolInitializer for LovehoneyDesireInitializer {
42 async fn initialize(
43 &mut self,
44 _: Arc<Hardware>,
45 def: &ServerDeviceDefinition,
46 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
47 Ok(Arc::new(LovehoneyDesire::new(
48 def
49 .features()
50 .iter()
51 .filter(|x| x.output().is_some())
52 .count() as u8,
53 )))
54 }
55}
56
57pub struct LovehoneyDesire {
58 current_commands: Vec<AtomicU8>,
59}
60
61impl LovehoneyDesire {
62 fn new(num_vibrators: u8) -> Self {
63 Self {
64 current_commands: std::iter::repeat_with(AtomicU8::default)
65 .take(num_vibrators as usize)
66 .collect(),
67 }
68 }
69}
70
71impl ProtocolHandler for LovehoneyDesire {
72 fn handle_output_vibrate_cmd(
73 &self,
74 feature_index: u32,
75 _feature_id: Uuid,
76 speed: u32,
77 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
78 // The Lovehoney Desire has 2 types of commands
79 //
80 // - Set both motors with one command
81 // - Set each motor separately
82 //
83 // We'll need to check what we got back and write our
84 // commands accordingly.
85 if self.current_commands.len() == 1 {
86 Ok(vec![
87 HardwareWriteCmd::new(
88 &[LOVEHONEY_DESIRE_PROTOCOL_UUID],
89 Endpoint::Tx,
90 vec![0xF3, 0, speed as u8],
91 true,
92 )
93 .into(),
94 ])
95 } else {
96 self.current_commands[feature_index as usize].store(speed as u8, Ordering::Relaxed);
97 let speed0 = self.current_commands[0].load(Ordering::Relaxed);
98 let speed1 = self.current_commands[1].load(Ordering::Relaxed);
99 if speed0 == speed1 {
100 Ok(vec![
101 HardwareWriteCmd::new(
102 &[
103 LOVEHONEY_DESIRE_PROTOCOL_UUID,
104 LOVEHONEY_DESIRE_VIBE2_PROTOCOL_UUID,
105 ],
106 Endpoint::Tx,
107 vec![0xF3, 0, speed0],
108 true,
109 )
110 .into(),
111 ])
112 } else {
113 Ok(vec![
114 HardwareWriteCmd::new(
115 &[LOVEHONEY_DESIRE_PROTOCOL_UUID],
116 Endpoint::Tx,
117 vec![0xF3, 1, speed0],
118 true,
119 )
120 .into(),
121 HardwareWriteCmd::new(
122 &[LOVEHONEY_DESIRE_VIBE2_PROTOCOL_UUID],
123 Endpoint::Tx,
124 vec![0xF3, 2, speed1],
125 true,
126 )
127 .into(),
128 ])
129 }
130 }
131 }
132}