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 super::fleshlight_launch_helper::calculate_speed;
9
10use crate::device::{
11 hardware::{Hardware, HardwareCommand, HardwareWriteCmd},
12 protocol::{
13 ProtocolHandler,
14 ProtocolIdentifier,
15 ProtocolInitializer,
16 generic_protocol_initializer_setup,
17 },
18};
19use async_trait::async_trait;
20use buttplug_core::errors::ButtplugDeviceError;
21use buttplug_server_device_config::Endpoint;
22use buttplug_server_device_config::{
23 ProtocolCommunicationSpecifier,
24 ServerDeviceDefinition,
25 UserDeviceIdentifier,
26};
27use std::sync::{
28 Arc,
29 atomic::{AtomicU8, Ordering},
30};
31use uuid::{Uuid, uuid};
32
33const KIIROO_V21_INITIALIZED_PROTOCOL_UUID: Uuid = uuid!("22329023-5464-41b6-a0de-673d7e993055");
34
35generic_protocol_initializer_setup!(KiirooV21Initialized, "kiiroo-v21-initialized");
36
37#[derive(Default)]
38pub struct KiirooV21InitializedInitializer {}
39
40#[async_trait]
41impl ProtocolInitializer for KiirooV21InitializedInitializer {
42 async fn initialize(
43 &mut self,
44 hardware: Arc<Hardware>,
45 _: &ServerDeviceDefinition,
46 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
47 debug!("calling Onyx+ init");
48 hardware
49 .write_value(&HardwareWriteCmd::new(
50 &[KIIROO_V21_INITIALIZED_PROTOCOL_UUID],
51 Endpoint::Tx,
52 vec![0x03u8, 0x00u8, 0x64u8, 0x19u8],
53 true,
54 ))
55 .await?;
56 hardware
57 .write_value(&HardwareWriteCmd::new(
58 &[KIIROO_V21_INITIALIZED_PROTOCOL_UUID],
59 Endpoint::Tx,
60 vec![0x03u8, 0x00u8, 0x64u8, 0x00u8],
61 true,
62 ))
63 .await?;
64 Ok(Arc::new(KiirooV21Initialized::default()))
65 }
66}
67
68#[derive(Default)]
69pub struct KiirooV21Initialized {
70 previous_position: Arc<AtomicU8>,
71}
72
73impl ProtocolHandler for KiirooV21Initialized {
74 fn handle_output_vibrate_cmd(
75 &self,
76 _feature_index: u32,
77 feature_id: Uuid,
78 speed: u32,
79 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
80 Ok(vec![
81 HardwareWriteCmd::new(&[feature_id], Endpoint::Tx, vec![0x01, speed as u8], false).into(),
82 ])
83 }
84
85 fn handle_position_with_duration_cmd(
86 &self,
87 _feature_index: u32,
88 feature_id: Uuid,
89 position: u32,
90 duration: u32,
91 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
92 // In the protocol, we know max speed is 99, so convert here. We have to
93 // use AtomicU8 because there's no AtomicF64 yet.
94 let previous_position = self.previous_position.load(Ordering::Relaxed);
95 let distance = (previous_position as f64 - (position as f64)).abs() / 99f64;
96 let calculated_speed = (calculate_speed(distance, duration) * 99f64) as u8;
97
98 self
99 .previous_position
100 .store(position as u8, Ordering::Relaxed);
101 Ok(vec![
102 HardwareWriteCmd::new(
103 &[feature_id],
104 Endpoint::Tx,
105 [0x03, 0x00, calculated_speed, position as u8].to_vec(),
106 false,
107 )
108 .into(),
109 ])
110 }
111}