Buttplug sex toy control library
1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2025 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 async_trait::async_trait;
9use uuid::{Uuid, uuid};
10
11use buttplug_core::{errors::ButtplugDeviceError, message::OutputType};
12
13use crate::device::{
14 hardware::{Hardware, HardwareCommand, HardwareWriteCmd},
15 protocol::{
16 ProtocolHandler,
17 ProtocolIdentifier,
18 ProtocolInitializer,
19 ProtocolKeepaliveStrategy,
20 generic_protocol_initializer_setup,
21 },
22};
23use buttplug_server_device_config::{
24 Endpoint,
25 ProtocolCommunicationSpecifier,
26 ServerDeviceDefinition,
27 UserDeviceIdentifier,
28};
29use std::sync::{
30 Arc,
31 atomic::{AtomicU8, Ordering},
32};
33
34const SVAKOM_V6_VIBRATOR_UUID: Uuid = uuid!("4cf33d95-a3d1-4ed4-9ac6-9ba6d6ccb091");
35
36generic_protocol_initializer_setup!(SvakomV6, "svakom-v6");
37
38#[derive(Default)]
39pub struct SvakomV6Initializer {}
40
41#[async_trait]
42impl ProtocolInitializer for SvakomV6Initializer {
43 async fn initialize(
44 &mut self,
45 _: Arc<Hardware>,
46 def: &ServerDeviceDefinition,
47 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> {
48 let num_vibrators = def
49 .features()
50 .iter()
51 .filter(|x| {
52 if let Some(output_map) = x.output() {
53 output_map.contains(OutputType::Vibrate)
54 } else {
55 false
56 }
57 })
58 .count() as u8;
59 Ok(Arc::new(SvakomV6::new(num_vibrators)))
60 }
61}
62
63#[derive(Default)]
64pub struct SvakomV6 {
65 num_vibrators: u8,
66 last_vibrator_speeds: [AtomicU8; 3],
67}
68
69impl SvakomV6 {
70 fn new(num_vibrators: u8) -> Self {
71 Self {
72 num_vibrators,
73 ..Default::default()
74 }
75 }
76}
77
78impl ProtocolHandler for SvakomV6 {
79 fn keepalive_strategy(&self) -> ProtocolKeepaliveStrategy {
80 ProtocolKeepaliveStrategy::HardwareRequiredRepeatLastPacketStrategy
81 }
82
83 fn handle_output_vibrate_cmd(
84 &self,
85 feature_index: u32,
86 feature_id: uuid::Uuid,
87 speed: u32,
88 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> {
89 self.last_vibrator_speeds[feature_index as usize].store(speed as u8, Ordering::Relaxed);
90 if feature_index < 2 {
91 let vibe1 = self.last_vibrator_speeds[0].load(Ordering::Relaxed);
92 let vibe2 = self.last_vibrator_speeds[1].load(Ordering::Relaxed);
93 Ok(vec![
94 HardwareWriteCmd::new(
95 &[SVAKOM_V6_VIBRATOR_UUID],
96 Endpoint::Tx,
97 [
98 0x55,
99 0x03,
100 if self.num_vibrators == 1 || (vibe1 > 0 && vibe2 > 0) || vibe1 == vibe2 {
101 0x00
102 } else if vibe1 > 0 {
103 0x01
104 } else {
105 0x02
106 },
107 0x00,
108 if vibe1 == vibe2 && vibe1 == 0 {
109 0x00
110 } else {
111 0x01
112 },
113 { vibe1.max(vibe2) },
114 0x00,
115 ]
116 .to_vec(),
117 false,
118 )
119 .into(),
120 ])
121 } else {
122 let vibe3 = self.last_vibrator_speeds[2].load(Ordering::Relaxed);
123 Ok(vec![
124 HardwareWriteCmd::new(
125 &[feature_id],
126 Endpoint::Tx,
127 [
128 0x55,
129 0x07,
130 0x00,
131 0x00,
132 if vibe3 == 0 { 0x00 } else { 0x01 },
133 { vibe3 },
134 0x00,
135 ]
136 .to_vec(),
137 false,
138 )
139 .into(),
140 ])
141 }
142 }
143}