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
37// Time between Hgod update commands, in milliseconds.
38const HGOD_COMMAND_DELAY_MS: u64 = 100;
39
40const HGOD_PROTOCOL_UUID: Uuid = uuid!("0a086d5b-9918-4b73-b2dd-86ed66de6f51");
41generic_protocol_initializer_setup!(Hgod, "hgod");
42
43#[derive(Default)]
44pub struct HgodInitializer {}
45
46#[async_trait]
47impl ProtocolInitializer for HgodInitializer {
48 async fn initialize(
49 &mut self,
50 hardware: Arc<Hardware>,
51 _: &ServerDeviceDefinition,
52 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
53 Ok(Arc::new(Hgod::new(hardware)))
54 }
55}
56
57pub struct Hgod {
58 last_command: Arc<AtomicU8>,
59}
60
61impl Hgod {
62 fn new(hardware: Arc<Hardware>) -> Self {
63 let last_command = Arc::new(AtomicU8::new(0));
64
65 let last_command_clone = last_command.clone();
66 async_manager::spawn(async move {
67 send_hgod_updates(hardware, last_command_clone).await;
68 });
69
70 Self { last_command }
71 }
72}
73
74// HGod toys vibes only last ~100ms seconds.
75async fn send_hgod_updates(device: Arc<Hardware>, data: Arc<AtomicU8>) {
76 loop {
77 let speed = data.load(Ordering::Relaxed);
78 let command = vec![0x55, 0x04, 0, 0, 0, speed];
79 if speed > 0
80 && let Err(e) = device
81 .write_value(&HardwareWriteCmd::new(
82 &[HGOD_PROTOCOL_UUID],
83 Endpoint::Tx,
84 command,
85 false,
86 ))
87 .await
88 {
89 error!(
90 "Got an error from a hgod device, exiting control loop: {:?}",
91 e
92 );
93 break;
94 }
95 sleep(Duration::from_millis(HGOD_COMMAND_DELAY_MS)).await;
96 }
97}
98
99impl ProtocolHandler for Hgod {
100 fn handle_output_vibrate_cmd(
101 &self,
102 _feature_index: u32,
103 _feature_id: Uuid,
104 speed: u32,
105 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
106 self.last_command.store(speed as u8, Ordering::Relaxed);
107 Ok(vec![])
108 }
109}