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_server_device_config::Endpoint; 9use byteorder::LittleEndian; 10 11use crate::device::{ 12 hardware::{Hardware, HardwareCommand, HardwareReadCmd, HardwareWriteCmd}, 13 protocol::{ProtocolHandler, generic_protocol_setup}, 14}; 15use buttplug_core::{ 16 errors::ButtplugDeviceError, 17 message::{self, InputData, InputReadingV4, InputTypeData}, 18}; 19use byteorder::WriteBytesExt; 20use futures::future::{BoxFuture, FutureExt}; 21use std::sync::{ 22 Arc, 23 atomic::{AtomicU16, Ordering}, 24}; 25 26generic_protocol_setup!(XInput, "xinput"); 27 28#[derive(Default)] 29pub struct XInput { 30 speeds: [AtomicU16; 2], 31} 32 33impl ProtocolHandler for XInput { 34 fn handle_output_vibrate_cmd( 35 &self, 36 feature_index: u32, 37 feature_id: uuid::Uuid, 38 speed: u32, 39 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 40 self.speeds[feature_index as usize].store(speed as u16, Ordering::Relaxed); 41 // XInput is fast enough that we can ignore the commands handed 42 // back by the manager and just form our own packet. This means 43 // we'll just use the manager's return for command validity 44 // checking. 45 let mut cmd = vec![]; 46 if cmd 47 .write_u16::<LittleEndian>(self.speeds[1].load(Ordering::Relaxed)) 48 .is_err() 49 || cmd 50 .write_u16::<LittleEndian>(self.speeds[0].load(Ordering::Relaxed)) 51 .is_err() 52 { 53 return Err(ButtplugDeviceError::ProtocolSpecificError( 54 "XInput".to_owned(), 55 "Cannot convert XInput value for processing".to_owned(), 56 )); 57 } 58 Ok(vec![ 59 HardwareWriteCmd::new(&[feature_id], Endpoint::Tx, cmd, false).into(), 60 ]) 61 } 62 63 fn handle_input_read_cmd( 64 &self, 65 device_index: u32, 66 device: Arc<Hardware>, 67 feature_index: u32, 68 feature_id: uuid::Uuid, 69 _sensor_type: message::InputType, 70 ) -> BoxFuture<'_, Result<InputReadingV4, ButtplugDeviceError>> { 71 async move { 72 let reading = device 73 .read_value(&HardwareReadCmd::new(feature_id, Endpoint::Rx, 0, 0)) 74 .await?; 75 let battery = match reading.data()[0] { 76 0 => 0u8, 77 1 => 33, 78 2 => 66, 79 3 => 100, 80 _ => { 81 return Err(ButtplugDeviceError::DeviceCommunicationError( 82 "something went wrong".to_string(), 83 )); 84 } 85 }; 86 Ok(message::InputReadingV4::new( 87 device_index, 88 feature_index, 89 InputTypeData::Battery(InputData::new(battery)), 90 )) 91 } 92 .boxed() 93 } 94}