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 buttplug_core::errors::ButtplugDeviceError; 9use buttplug_server_device_config::Endpoint; 10use buttplug_server_device_config::{ 11 ProtocolCommunicationSpecifier, 12 ServerDeviceDefinition, 13 UserDeviceIdentifier, 14}; 15 16use crate::device::{ 17 hardware::{Hardware, HardwareCommand, HardwareWriteCmd}, 18 protocol::{ProtocolHandler, ProtocolIdentifier, ProtocolInitializer}, 19}; 20use async_trait::async_trait; 21use std::sync::{ 22 Arc, 23 atomic::{AtomicU8, Ordering}, 24}; 25use uuid::{Uuid, uuid}; 26 27pub mod setup { 28 use crate::device::protocol::{ProtocolIdentifier, ProtocolIdentifierFactory}; 29 #[derive(Default)] 30 pub struct PatooIdentifierFactory {} 31 32 impl ProtocolIdentifierFactory for PatooIdentifierFactory { 33 fn identifier(&self) -> &str { 34 "patoo" 35 } 36 37 fn create(&self) -> Box<dyn ProtocolIdentifier> { 38 Box::new(super::PatooIdentifier::default()) 39 } 40 } 41} 42 43#[derive(Default)] 44pub struct PatooIdentifier {} 45 46#[async_trait] 47impl ProtocolIdentifier for PatooIdentifier { 48 async fn identify( 49 &mut self, 50 hardware: Arc<Hardware>, 51 _: ProtocolCommunicationSpecifier, 52 ) -> Result<(UserDeviceIdentifier, Box<dyn ProtocolInitializer>), ButtplugDeviceError> { 53 // Patoo Love devices have wildcarded names of ([A-Z]+)\d* 54 // Force the identifier lookup to the non-numeric portion 55 let c: Vec<char> = hardware.name().chars().collect(); 56 let mut i = 0; 57 while i < c.len() && !c[i].is_ascii_digit() { 58 i += 1; 59 } 60 let name: String = c[0..i].iter().collect(); 61 Ok(( 62 UserDeviceIdentifier::new(hardware.address(), "Patoo", &Some(name)), 63 Box::new(PatooInitializer::default()), 64 )) 65 } 66} 67 68#[derive(Default)] 69pub struct PatooInitializer {} 70 71#[async_trait] 72impl ProtocolInitializer for PatooInitializer { 73 async fn initialize( 74 &mut self, 75 _: Arc<Hardware>, 76 _: &ServerDeviceDefinition, 77 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> { 78 Ok(Arc::new(Patoo::default())) 79 } 80} 81 82const PATOO_TX_PROTOCOL_UUID: Uuid = uuid!("2366a70f-9a7c-4fea-8ba6-8b21a7d5d641"); 83const PATOO_TX_MODE_PROTOCOL_UUID: Uuid = uuid!("b17714be-fc66-4d9b-bf52-afb3b91212a4"); 84 85#[derive(Default)] 86pub struct Patoo { 87 speeds: [AtomicU8; 2], 88} 89 90impl ProtocolHandler for Patoo { 91 fn handle_output_vibrate_cmd( 92 &self, 93 feature_index: u32, 94 _feature_id: uuid::Uuid, 95 speed: u32, 96 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 97 self.speeds[feature_index as usize].store(speed as u8, Ordering::Relaxed); 98 let mut msg_vec = vec![]; 99 // Default to vibes 100 let mut mode: u8 = 4u8; 101 102 // Use vibe 1 as speed 103 let mut speed = self.speeds[0].load(Ordering::Relaxed); 104 if speed == 0 { 105 mode = 0; 106 107 let speed2 = self.speeds[1].load(Ordering::Relaxed); 108 // If we have a second vibe and it's not also 0, use that 109 if speed2 != 0 { 110 speed = speed2; 111 mode |= 0x80; 112 } 113 } 114 115 msg_vec.push( 116 HardwareWriteCmd::new(&[PATOO_TX_PROTOCOL_UUID], Endpoint::Tx, vec![speed], true).into(), 117 ); 118 msg_vec.push( 119 HardwareWriteCmd::new( 120 &[PATOO_TX_MODE_PROTOCOL_UUID], 121 Endpoint::TxMode, 122 vec![mode], 123 true, 124 ) 125 .into(), 126 ); 127 128 Ok(msg_vec) 129 } 130}