Buttplug sex toy control library
at dev 8.9 kB view raw
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 crate::device::{ 9 hardware::{Hardware, HardwareCommand, HardwareWriteCmd}, 10 protocol::{ 11 ProtocolHandler, 12 ProtocolIdentifier, 13 ProtocolInitializer, 14 generic_protocol_initializer_setup, 15 }, 16}; 17use async_trait::async_trait; 18use buttplug_core::{errors::ButtplugDeviceError, util::async_manager}; 19use buttplug_server_device_config::{ 20 Endpoint, 21 ProtocolCommunicationSpecifier, 22 ServerDeviceDefinition, 23 UserDeviceIdentifier, 24}; 25use std::{ 26 sync::{ 27 Arc, 28 atomic::{AtomicBool, AtomicU16, Ordering}, 29 }, 30 time::Duration, 31}; 32use tokio::sync::Notify; 33use uuid::{Uuid, uuid}; 34 35const NINTENDO_JOYCON_PROTOCOL_UUID: Uuid = uuid!("de9cce17-abb7-4ad5-9754-f1872733c197"); 36 37/// Send command, sub-command, and data (sub-command's arguments) with u8 integers 38/// This returns ACK packet for the command or Error. 39async fn send_command_raw( 40 device: Arc<Hardware>, 41 packet_number: u8, 42 command: u8, 43 sub_command: u8, 44 data: &[u8], 45 rumble_r: Option<Rumble>, 46 rumble_l: Option<Rumble>, 47) -> Result<(), ButtplugDeviceError> { 48 let mut buf = [0x0; 0x40]; 49 // set command 50 buf[0] = command; 51 // set packet number 52 buf[1] = packet_number; 53 54 // rumble 55 if let Some(rumble_l) = rumble_l { 56 let rumble_left: [u8; 4] = rumble_l.into(); 57 buf[2..6].copy_from_slice(&rumble_left); 58 } 59 if let Some(rumble_r) = rumble_r { 60 let rumble_right: [u8; 4] = rumble_r.into(); 61 buf[6..10].copy_from_slice(&rumble_right); 62 } 63 64 // set sub command 65 buf[10] = sub_command; 66 // set data 67 buf[11..11 + data.len()].copy_from_slice(data); 68 69 // send command 70 device 71 .write_value(&HardwareWriteCmd::new( 72 &[NINTENDO_JOYCON_PROTOCOL_UUID], 73 Endpoint::Tx, 74 buf.to_vec(), 75 false, 76 )) 77 .await 78} 79 80/// Send sub-command, and data (sub-command's arguments) with u8 integers 81/// This returns ACK packet for the command or Error. 82/// 83/// # Notice 84/// If you are using non-blocking mode, 85/// it is more likely to fail to validate the sub command reply. 86async fn send_sub_command_raw( 87 device: Arc<Hardware>, 88 packet_number: u8, 89 sub_command: u8, 90 data: &[u8], 91) -> Result<(), ButtplugDeviceError> { 92 //use input_report_mode::sub_command_mode::AckByte; 93 94 send_command_raw(device, packet_number, 1, sub_command, data, None, None).await 95 /* 96 // check reply 97 if self.valid_reply() { 98 std::iter::repeat(()) 99 .take(Self::ACK_TRY) 100 .flat_map(|()| { 101 let mut buf = [0u8; 362]; 102 self.read(&mut buf).ok()?; 103 let ack_byte = AckByte::from(buf[13]); 104 105 match ack_byte { 106 AckByte::Ack { .. } => Some(buf), 107 AckByte::Nack => None 108 } 109 }) 110 .next() 111 .map(SubCommandReply::Checked) 112 .ok_or_else(|| JoyConError::SubCommandError(sub_command, Vec::new())) 113 } else { 114 Ok(SubCommandReply::Unchecked) 115 } 116 */ 117} 118 119/// Send sub-command, and data (sub-command's arguments) with `Command` and `SubCommand` 120/// This returns ACK packet for the command or Error. 121async fn send_sub_command( 122 device: Arc<Hardware>, 123 packet_number: u8, 124 sub_command: u8, 125 data: &[u8], 126) -> Result<(), ButtplugDeviceError> { 127 send_sub_command_raw(device, packet_number, sub_command, data).await 128} 129 130/// Rumble data for vibration. 131/// 132/// # Notice 133/// Constraints exist. 134/// * frequency - 0.0 < freq < 1252.0 135/// * amplitude - 0.0 < amp < 1.799.0 136#[derive(Debug, Clone, Copy, PartialEq)] 137pub struct Rumble { 138 frequency: f32, 139 amplitude: f32, 140} 141 142impl Rumble { 143 pub fn frequency(self) -> f32 { 144 self.frequency 145 } 146 147 pub fn amplitude(self) -> f32 { 148 self.amplitude 149 } 150 151 /// Constructor of Rumble. 152 /// If arguments not in line with constraints, args will be saturated. 153 pub fn new(freq: f32, amp: f32) -> Self { 154 let freq = if freq < 0.0 { 155 0.0 156 } else if freq > 1252.0 { 157 1252.0 158 } else { 159 freq 160 }; 161 162 let amp = if amp < 0.0 { 163 0.0 164 } else if amp > 1.799 { 165 1.799 166 } else { 167 amp 168 }; 169 170 Self { 171 frequency: freq, 172 amplitude: amp, 173 } 174 } 175 176 /// The amplitudes over 1.003 are not safe for the integrity of the linear resonant actuators. 177 pub fn is_safe(self) -> bool { 178 self.amplitude < 1.003 179 } 180 181 /// Generates stopper of rumbling. 182 pub fn stop() -> Self { 183 Self { 184 frequency: 0.0, 185 amplitude: 0.0, 186 } 187 } 188} 189 190impl From<Rumble> for [u8; 4] { 191 fn from(val: Rumble) -> Self { 192 let encoded_hex_freq = f32::round(f32::log2(val.frequency / 10.0) * 32.0) as u8; 193 194 let hf_freq: u16 = (encoded_hex_freq as u16).saturating_sub(0x60) * 4; 195 let lf_freq: u8 = encoded_hex_freq.saturating_sub(0x41) + 1; 196 197 let encoded_hex_amp = if val.amplitude > 0.23 { 198 f32::round(f32::log2(val.amplitude * 8.7) * 32.0) as u8 199 } else if val.amplitude > 0.12 { 200 f32::round(f32::log2(val.amplitude * 17.0) * 16.0) as u8 201 } else { 202 f32::round(((f32::log2(val.amplitude) * 32.0) - 96.0) / (4.0 - 2.0 * val.amplitude)) as u8 203 }; 204 205 let hf_amp: u16 = { 206 let hf_amp: u16 = encoded_hex_amp as u16 * 2; 207 if hf_amp > 0x01FC { 0x01FC } else { hf_amp } 208 }; // encoded_hex_amp<<1; 209 let lf_amp: u8 = { 210 let lf_amp = encoded_hex_amp / 2 + 64; 211 if lf_amp > 0x7F { 0x7F } else { lf_amp } 212 }; // (encoded_hex_amp>>1)+0x40; 213 214 let mut buf = [0u8; 4]; 215 216 // HF: Byte swapping 217 buf[0] = (hf_freq & 0xFF) as u8; 218 // buf[1] = (hf_amp + ((hf_freq >> 8) & 0xFF)) as u8; //Add amp + 1st byte of frequency to amplitude byte 219 buf[1] = (hf_amp + (hf_freq.wrapping_shr(8) & 0xFF)) as u8; //Add amp + 1st byte of frequency to amplitude byte 220 221 // LF: Byte swapping 222 buf[2] = lf_freq.saturating_add(lf_amp.wrapping_shr(8)); 223 buf[3] = lf_amp; 224 225 buf 226 } 227} 228 229generic_protocol_initializer_setup!(NintendoJoycon, "nintendo-joycon"); 230 231#[derive(Default)] 232pub struct NintendoJoyconInitializer {} 233 234#[async_trait] 235impl ProtocolInitializer for NintendoJoyconInitializer { 236 async fn initialize( 237 &mut self, 238 hardware: Arc<Hardware>, 239 _: &ServerDeviceDefinition, 240 ) -> Result<Arc<dyn ProtocolHandler>, ButtplugDeviceError> { 241 send_sub_command(hardware.clone(), 0, 72, &[0x01]) 242 .await 243 .map_err(|_| { 244 ButtplugDeviceError::DeviceConnectionError("Cannot initialize joycon".to_owned()) 245 })?; 246 Ok(Arc::new(NintendoJoycon::new(hardware))) 247 } 248} 249 250pub struct NintendoJoycon { 251 //packet_number: Arc<AtomicU8>, 252 speed_val: Arc<AtomicU16>, 253 notifier: Arc<Notify>, 254 is_stopped: Arc<AtomicBool>, 255} 256 257impl NintendoJoycon { 258 fn new(hardware: Arc<Hardware>) -> Self { 259 let speed_val = Arc::new(AtomicU16::new(0)); 260 let speed_val_clone = speed_val.clone(); 261 let notifier = Arc::new(Notify::new()); 262 #[cfg(not(feature = "wasm"))] 263 let notifier_clone = notifier.clone(); 264 let is_stopped = Arc::new(AtomicBool::new(false)); 265 let is_stopped_clone = is_stopped.clone(); 266 async_manager::spawn(async move { 267 #[cfg(feature = "wasm")] 268 use buttplug_core::util; 269 loop { 270 if is_stopped_clone.load(Ordering::Relaxed) { 271 return; 272 } 273 let amp = speed_val_clone.load(Ordering::Relaxed) as f32 / 1000f32; 274 let rumble = if amp > 0.001 { 275 Rumble::new(200.0f32, amp) 276 } else { 277 Rumble::stop() 278 }; 279 280 if let Err(_) = 281 send_command_raw(hardware.clone(), 1, 16, 0, &[], Some(rumble), Some(rumble)).await 282 { 283 error!("Joycon command failed, exiting update loop"); 284 break; 285 } 286 #[cfg(not(feature = "wasm"))] 287 let _ = tokio::time::timeout(Duration::from_millis(15), notifier_clone.notified()).await; 288 289 // If we're using WASM, we can't use tokio's timeout due to lack of time library in WASM. 290 // I'm also too lazy to make this a select. So, this'll do. We can't even access this 291 // protocol in a web context yet since there's no WebHID comm manager yet. 292 #[cfg(feature = "wasm")] 293 util::sleep(Duration::from_millis(15)).await; 294 } 295 }); 296 Self { 297 //packet_number: Arc::new(AtomicU8::new(0)), 298 speed_val, 299 notifier, 300 is_stopped, 301 } 302 } 303} 304 305impl ProtocolHandler for NintendoJoycon { 306 fn handle_output_vibrate_cmd( 307 &self, 308 _feature_index: u32, 309 _feature_id: Uuid, 310 speed: u32, 311 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 312 self.speed_val.store(speed as u16, Ordering::Relaxed); 313 Ok(vec![]) 314 } 315} 316 317impl Drop for NintendoJoycon { 318 fn drop(&mut self) { 319 self.is_stopped.store(false, Ordering::Relaxed); 320 self.notifier.notify_one(); 321 } 322}