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 crate::device::{
9 hardware::{Hardware, HardwareCommand, HardwareWriteCmd},
10 protocol::{
11 ProtocolHandler,
12 ProtocolIdentifier,
13 ProtocolInitializer,
14 generic_protocol_initializer_setup,
15 },
16};
17use async_trait::async_trait;
18use buttplug_core::{
19 errors::ButtplugDeviceError,
20 util::{async_manager, sleep},
21};
22use buttplug_server_device_config::{
23 Endpoint,
24 ProtocolCommunicationSpecifier,
25 ServerDeviceDefinition,
26 UserDeviceIdentifier,
27};
28use std::{
29 sync::{
30 Arc,
31 atomic::{AtomicU8, Ordering},
32 },
33 time::Duration,
34};
35use uuid::{Uuid, uuid};
36
37const XUANHUAN_PROTOCOL_ID: Uuid = uuid!("e9f9f8ab-4fd5-4573-a4ec-ab542568849b");
38generic_protocol_initializer_setup!(Xuanhuan, "xuanhuan");
39
40#[derive(Default)]
41pub struct XuanhuanInitializer {}
42
43#[async_trait]
44impl ProtocolInitializer for XuanhuanInitializer {
45 async fn initialize(
46 &mut self,
47 hardware: Arc<Hardware>,
48 _: &ServerDeviceDefinition,
49 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
50 Ok(Arc::new(Xuanhuan::new(hardware)))
51 }
52}
53
54async fn vibration_update_handler(device: Arc<Hardware>, command_holder: Arc<AtomicU8>) {
55 info!("Entering Xuanhuan Control Loop");
56 loop {
57 let speed = command_holder.load(Ordering::Relaxed);
58 if speed != 0 {
59 let current_command = vec![0x03, 0x02, 0x00, speed];
60 if device
61 .write_value(&HardwareWriteCmd::new(
62 &[XUANHUAN_PROTOCOL_ID],
63 Endpoint::Tx,
64 current_command,
65 true,
66 ))
67 .await
68 .is_err()
69 {
70 break;
71 }
72 }
73 sleep(Duration::from_millis(300)).await;
74 }
75 info!("Xuanhuan control loop exiting, most likely due to device disconnection.");
76}
77
78pub struct Xuanhuan {
79 current_command: Arc<AtomicU8>,
80}
81
82impl Xuanhuan {
83 fn new(device: Arc<Hardware>) -> Self {
84 let current_command = Arc::new(AtomicU8::new(0));
85 let current_command_clone = current_command.clone();
86 async_manager::spawn(
87 async move { vibration_update_handler(device, current_command_clone).await },
88 );
89 Self { current_command }
90 }
91}
92
93impl ProtocolHandler for Xuanhuan {
94 fn handle_output_vibrate_cmd(
95 &self,
96 _feature_index: u32,
97 _feature_id: Uuid,
98 speed: u32,
99 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
100 let speed = speed as u8;
101 self.current_command.store(speed, Ordering::Relaxed);
102
103 Ok(vec![
104 HardwareWriteCmd::new(
105 &[XUANHUAN_PROTOCOL_ID],
106 Endpoint::Tx,
107 vec![0x03, 0x02, 0x00, speed],
108 true,
109 )
110 .into(),
111 ])
112 }
113}