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;
9use crate::device::{
10 hardware::{Hardware, HardwareCommand, HardwareWriteCmd},
11 protocol::{
12 ProtocolHandler,
13 ProtocolIdentifier,
14 ProtocolInitializer,
15 generic_protocol_initializer_setup,
16 },
17};
18use async_trait::async_trait;
19use buttplug_core::errors::ButtplugDeviceError;
20use buttplug_server_device_config::Endpoint;
21use buttplug_server_device_config::{
22 ProtocolCommunicationSpecifier,
23 ServerDeviceDefinition,
24 UserDeviceIdentifier,
25};
26use std::sync::{
27 Arc,
28 atomic::{AtomicU8, Ordering},
29};
30use uuid::{Uuid, uuid};
31
32const KIIROO_V2_PROTOCOL_UUID: Uuid = uuid!("05ab9d57-5e65-47b2-add4-5bad3e8663e5");
33generic_protocol_initializer_setup!(KiirooV2, "kiiroo-v2");
34
35#[derive(Default)]
36pub struct KiirooV2Initializer {}
37
38#[async_trait]
39impl ProtocolInitializer for KiirooV2Initializer {
40 async fn initialize(
41 &mut self,
42 hardware: Arc<Hardware>,
43 _: &ServerDeviceDefinition,
44 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
45 let msg = HardwareWriteCmd::new(
46 &[KIIROO_V2_PROTOCOL_UUID],
47 Endpoint::Firmware,
48 vec![0x0u8],
49 true,
50 );
51 hardware.write_value(&msg).await?;
52 Ok(Arc::new(KiirooV2::default()))
53 }
54}
55
56#[derive(Default)]
57pub struct KiirooV2 {
58 previous_position: Arc<AtomicU8>,
59}
60
61impl ProtocolHandler for KiirooV2 {
62 fn handle_position_with_duration_cmd(
63 &self,
64 _feature_index: u32,
65 feature_id: Uuid,
66 position: u32,
67 duration: u32,
68 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
69 // In the protocol, we know max speed is 99, so convert here. We have to
70 // use AtomicU8 because there's no AtomicF64 yet.
71 let previous_position = self.previous_position.load(Ordering::Relaxed);
72 let distance = (previous_position as f64 - (position as f64)).abs() / 99f64;
73 let position = position as u8;
74 let calculated_speed = (calculate_speed(distance, duration) * 99f64) as u8;
75 self.previous_position.store(position, Ordering::Relaxed);
76 Ok(vec![
77 HardwareWriteCmd::new(
78 &[feature_id],
79 Endpoint::Tx,
80 [position, calculated_speed].to_vec(),
81 false,
82 )
83 .into(),
84 ])
85 }
86}