Buttplug sex toy control library
at master 142 lines 3.6 kB view raw
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 std::sync::Arc; 9use std::sync::atomic::{AtomicU8, Ordering}; 10 11use async_trait::async_trait; 12use uuid::{Uuid, uuid}; 13 14use buttplug_core::{errors::ButtplugDeviceError, message::OutputType}; 15use buttplug_server_device_config::{ 16 Endpoint, 17 ProtocolCommunicationSpecifier, 18 ServerDeviceDefinition, 19 UserDeviceIdentifier, 20}; 21 22use crate::device::{ 23 hardware::{Hardware, HardwareCommand, HardwareWriteCmd}, 24 protocol::{ 25 ProtocolHandler, 26 ProtocolIdentifier, 27 ProtocolInitializer, 28 generic_protocol_initializer_setup, 29 }, 30}; 31 32generic_protocol_initializer_setup!(SexverseV1, "sexverse-v1"); 33 34#[derive(Default)] 35pub struct SexverseV1Initializer {} 36 37#[async_trait] 38impl ProtocolInitializer for SexverseV1Initializer { 39 async fn initialize( 40 &mut self, 41 _: Arc<Hardware>, 42 def: &ServerDeviceDefinition, 43 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> { 44 let mut commands = vec![]; 45 def.features().iter().for_each(|x| { 46 if let Some(m) = x.output() { 47 for output in m.output_types() { 48 commands.push((output, AtomicU8::default())) 49 } 50 } 51 }); 52 Ok(Arc::new(SexverseV1::new(commands))) 53 } 54} 55 56const SEXVERSE_PROTOCOL_UUID: Uuid = uuid!("6485a762-2ea7-48c1-a4ba-ab724e618348"); 57 58#[derive(Default)] 59pub struct SexverseV1 { 60 commands: Vec<(OutputType, AtomicU8)>, 61} 62 63impl SexverseV1 { 64 fn new(commands: Vec<(OutputType, AtomicU8)>) -> Self { 65 Self { commands } 66 } 67 68 fn form_command( 69 &self, 70 feature_index: u32, 71 speed: u32, 72 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 73 self.commands[feature_index as usize] 74 .1 75 .store(speed as u8, Ordering::Relaxed); 76 let mut data: Vec<u8> = vec![0x23, 0x07]; 77 data.push((self.commands.len() * 3) as u8); 78 79 for (i, (output_type, speed)) in self.commands.iter().enumerate() { 80 // motor number 81 data.push(0x80 | ((i + 1) as u8)); 82 // motor type: 03=vibe 04=pump 06=rotate 83 data.push(if *output_type == OutputType::Rotate { 84 0x06 85 } else if *output_type == OutputType::Constrict || *output_type == OutputType::Oscillate { 86 0x04 87 } else { 88 // Vibrate 89 0x03 90 }); 91 data.push(speed.load(Ordering::Relaxed)); 92 } 93 94 let mut crc: u8 = 0; 95 for b in data.clone() { 96 crc ^= b; 97 } 98 data.push(crc); 99 100 Ok(vec![ 101 HardwareWriteCmd::new(&[SEXVERSE_PROTOCOL_UUID], Endpoint::Tx, data, false).into(), 102 ]) 103 } 104} 105 106impl ProtocolHandler for SexverseV1 { 107 fn handle_output_vibrate_cmd( 108 &self, 109 feature_index: u32, 110 _feature_id: Uuid, 111 speed: u32, 112 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 113 self.form_command(feature_index, speed) 114 } 115 116 fn handle_output_oscillate_cmd( 117 &self, 118 feature_index: u32, 119 _feature_id: Uuid, 120 speed: u32, 121 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 122 self.form_command(feature_index, speed) 123 } 124 125 fn handle_output_rotate_cmd( 126 &self, 127 feature_index: u32, 128 _feature_id: Uuid, 129 speed: i32, 130 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 131 self.form_command(feature_index, speed as u32) 132 } 133 134 fn handle_output_constrict_cmd( 135 &self, 136 feature_index: u32, 137 _feature_id: Uuid, 138 level: u32, 139 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 140 self.form_command(feature_index, level) 141 } 142}