Buttplug sex toy control library

chore: Start removing RotateWithDirection

+51
buttplug-user-device-config-v4.json
··· 1 + { 2 + "version": { 3 + "major": 4, 4 + "minor": 0 5 + }, 6 + "user_configs": { 7 + "protocols": {}, 8 + "devices": [ 9 + { 10 + "identifier": { 11 + "protocol": "lovense", 12 + "identifier": "EL", 13 + "address": "PeripheralId(81b8f016-ba95-e1ae-3a71-a2e6b1d83b21)" 14 + }, 15 + "config": { 16 + "id": "5d434198-4697-4b00-985e-33788fc955d1", 17 + "base_id": "bbfd764c-b419-4c13-aeb0-e753a86318ed", 18 + "features": [ 19 + { 20 + "id": "16099312-8bf8-45f7-9a37-9c56ffd972ae", 21 + "base_id": "0627be5e-8553-4f20-b4cf-15f5e1896e5f", 22 + "output": { 23 + "vibrate": { 24 + "disabled": false 25 + } 26 + } 27 + }, 28 + { 29 + "id": "c82ea967-3014-49ef-8189-e7e065c4bdbf", 30 + "base_id": "360d81e7-5126-4dbb-b72d-7bb60eb67400", 31 + "output": { 32 + "rotate": { 33 + "disabled": false 34 + } 35 + } 36 + }, 37 + { 38 + "id": "3ad9adee-8c32-408f-8e8c-4a275bb9421c", 39 + "base_id": "50b9b31f-c2a8-459a-81fd-c54604f5184e" 40 + } 41 + ], 42 + "user_config": { 43 + "allow": false, 44 + "deny": false, 45 + "index": 0 46 + } 47 + } 48 + } 49 + ] 50 + } 51 + }
+1 -7
crates/buttplug_client/src/device/command.rs
··· 29 29 Led(u32), 30 30 Spray(u32), 31 31 Position(u32), 32 - RotateWithDirection(u32, bool), 33 32 PositionWithDuration(u32, u32), 34 33 // f64 types are old style float, will need to convert before sending 35 34 VibrateFloat(f64), ··· 40 39 LedFloat(f64), 41 40 SprayFloat(f64), 42 41 PositionFloat(f64), 43 - RotateWithDirectionFloat(f64, bool), 44 42 PositionWithDurationFloat(f64, u32), 45 43 } 46 44 ··· 55 53 OutputType::Led => Ok(ClientDeviceOutputCommand::LedFloat(value)), 56 54 OutputType::Spray => Ok(ClientDeviceOutputCommand::SprayFloat(value)), 57 55 OutputType::Position => Ok(ClientDeviceOutputCommand::PositionFloat(value)), 58 - _ => Err(ButtplugClientError::ButtplugOutputCommandConversionError("Cannot use PositionWithDuration or RotateWithDirection with this method".to_owned())) 56 + _ => Err(ButtplugClientError::ButtplugOutputCommandConversionError("Cannot use PositionWithDuration with this method".to_owned())) 59 57 } 60 58 } 61 59 } ··· 78 76 ClientDeviceOutputCommand::PositionWithDuration(_, _) 79 77 | ClientDeviceOutputCommand::PositionWithDurationFloat(_, _) => { 80 78 OutputType::PositionWithDuration 81 - } 82 - ClientDeviceOutputCommand::RotateWithDirection(_, _) 83 - | ClientDeviceOutputCommand::RotateWithDirectionFloat(_, _) => { 84 - OutputType::RotateWithDirection 85 79 } 86 80 } 87 81 }
+15 -41
crates/buttplug_client/src/device/feature.rs
··· 15 15 OutputCmdV4, 16 16 OutputCommand, 17 17 OutputPositionWithDuration, 18 - OutputRotateWithDirection, 19 18 OutputType, 20 19 OutputValue, 21 20 }, ··· 64 63 fn check_step_value( 65 64 &self, 66 65 feature_output: &dyn DeviceFeatureOutputLimits, 67 - steps: u32, 68 - ) -> Result<u32, ButtplugClientError> { 69 - if feature_output.step_limit().contains(&(steps as i32)) { 66 + steps: i32, 67 + ) -> Result<i32, ButtplugClientError> { 68 + if feature_output.step_limit().contains(&(steps)) { 70 69 Ok(steps) 71 70 } else { 72 71 Err(ButtplugClientError::ButtplugOutputCommandConversionError( ··· 83 82 &self, 84 83 feature_output: &dyn DeviceFeatureOutputLimits, 85 84 float_amt: f64, 86 - ) -> Result<u32, ButtplugClientError> { 85 + ) -> Result<i32, ButtplugClientError> { 87 86 if !(0.0f64..=1.0f64).contains(&float_amt) { 88 87 Err(ButtplugClientError::ButtplugOutputCommandConversionError( 89 88 "Float values must be between 0.0 and 1.0".to_owned(), 90 89 )) 91 90 } else { 92 - Ok((float_amt * feature_output.step_count() as f64).ceil() as u32) 91 + Ok((float_amt * feature_output.step_count() as f64).ceil() as i32) 93 92 } 94 93 } 95 94 ··· 144 143 } 145 144 ClientDeviceOutputCommand::PositionWithDurationFloat(v, d) => { 146 145 OutputCommand::PositionWithDuration(OutputPositionWithDuration::new( 147 - self.convert_float_value(output, *v)?, 148 - *d, 149 - )) 150 - } 151 - ClientDeviceOutputCommand::RotateWithDirectionFloat(v, d) => { 152 - OutputCommand::RotateWithDirection(OutputRotateWithDirection::new( 153 - self.convert_float_value(output, *v)?, 146 + self.convert_float_value(output, *v)? as u32, 154 147 *d, 155 148 )) 156 149 } 157 150 ClientDeviceOutputCommand::Vibrate(v) => { 158 - OutputCommand::Vibrate(OutputValue::new(self.check_step_value(output, *v)?)) 151 + OutputCommand::Vibrate(OutputValue::new(self.check_step_value(output, *v as i32)?)) 159 152 } 160 153 ClientDeviceOutputCommand::Oscillate(v) => { 161 - OutputCommand::Oscillate(OutputValue::new(self.check_step_value(output, *v)?)) 154 + OutputCommand::Oscillate(OutputValue::new(self.check_step_value(output, *v as i32)?)) 162 155 } 163 156 ClientDeviceOutputCommand::Rotate(v) => { 164 - OutputCommand::Rotate(OutputValue::new(self.check_step_value(output, *v)?)) 157 + OutputCommand::Rotate(OutputValue::new(self.check_step_value(output, *v as i32)?)) 165 158 } 166 159 ClientDeviceOutputCommand::Constrict(v) => { 167 - OutputCommand::Constrict(OutputValue::new(self.check_step_value(output, *v)?)) 160 + OutputCommand::Constrict(OutputValue::new(self.check_step_value(output, *v as i32)?)) 168 161 } 169 162 ClientDeviceOutputCommand::Heater(v) => { 170 - OutputCommand::Heater(OutputValue::new(self.check_step_value(output, *v)?)) 163 + OutputCommand::Heater(OutputValue::new(self.check_step_value(output, *v as i32)?)) 171 164 } 172 165 ClientDeviceOutputCommand::Led(v) => { 173 - OutputCommand::Led(OutputValue::new(self.check_step_value(output, *v)?)) 166 + OutputCommand::Led(OutputValue::new(self.check_step_value(output, *v as i32)?)) 174 167 } 175 168 ClientDeviceOutputCommand::Spray(v) => { 176 - OutputCommand::Spray(OutputValue::new(self.check_step_value(output, *v)?)) 169 + OutputCommand::Spray(OutputValue::new(self.check_step_value(output, *v as i32)?)) 177 170 } 178 171 ClientDeviceOutputCommand::Position(v) => { 179 - OutputCommand::Position(OutputValue::new(self.check_step_value(output, *v)?)) 172 + OutputCommand::Position(OutputValue::new(self.check_step_value(output, *v as i32)?)) 180 173 } 181 174 ClientDeviceOutputCommand::PositionWithDuration(v, d) => OutputCommand::PositionWithDuration( 182 - OutputPositionWithDuration::new(self.check_step_value(output, *v)?, *d), 183 - ), 184 - ClientDeviceOutputCommand::RotateWithDirection(v, d) => OutputCommand::RotateWithDirection( 185 - OutputRotateWithDirection::new(self.check_step_value(output, *v)?, *d), 175 + OutputPositionWithDuration::new(self.check_step_value(output, *v as i32)? as u32, *d), 186 176 ), 187 177 }; 188 178 Ok(OutputCmdV4::new( ··· 268 258 } 269 259 ClientDeviceCommandValue::Float(f) => { 270 260 ClientDeviceOutputCommand::PositionWithDurationFloat(f, duration_in_ms) 271 - } 272 - }) 273 - } 274 - 275 - pub fn rotate_with_direction( 276 - &self, 277 - level: impl Into<ClientDeviceCommandValue>, 278 - clockwise: bool, 279 - ) -> ButtplugClientResultFuture { 280 - let val = level.into(); 281 - self.send_command(&match val { 282 - ClientDeviceCommandValue::Int(v) => { 283 - ClientDeviceOutputCommand::RotateWithDirection(v, clockwise) 284 - } 285 - ClientDeviceCommandValue::Float(f) => { 286 - ClientDeviceOutputCommand::RotateWithDirectionFloat(f, clockwise) 287 261 } 288 262 }) 289 263 }
+2
crates/buttplug_core/src/errors.rs
··· 157 157 DeviceScanningAlreadyStopped, 158 158 /// Device permission error: {0} 159 159 DevicePermissionError(String), 160 + /// Device command does not take negative numbers 161 + DeviceCommandSignError, 160 162 /// {0} 161 163 ProtocolAttributesNotFound(String), 162 164 /// Protocol {0} not implemented in library
-11
crates/buttplug_core/src/message/device_feature.rs
··· 16 16 Unknown, 17 17 #[serde(alias = "vibrate")] 18 18 Vibrate, 19 - // Single Direction Rotation Speed 20 19 #[serde(alias = "rotate")] 21 20 Rotate, 22 - // Two Direction Rotation Speed 23 - #[serde(alias = "rotate_with_direction")] 24 - RotateWithDirection, 25 21 #[serde(alias = "oscillate")] 26 22 Oscillate, 27 23 #[serde(alias = "constrict")] ··· 196 192 #[serde(skip_serializing_if = "Option::is_none")] 197 193 rotate: Option<DeviceFeatureOutputValueProperties>, 198 194 #[serde(skip_serializing_if = "Option::is_none")] 199 - rotate_with_direction: Option<DeviceFeatureOutputValueProperties>, 200 - #[serde(skip_serializing_if = "Option::is_none")] 201 195 oscillate: Option<DeviceFeatureOutputValueProperties>, 202 196 #[serde(skip_serializing_if = "Option::is_none")] 203 197 constrict: Option<DeviceFeatureOutputValueProperties>, ··· 223 217 OutputType::Position => self.position.is_some(), 224 218 OutputType::PositionWithDuration => self.position_with_duration.is_some(), 225 219 OutputType::Rotate => self.rotate.is_some(), 226 - OutputType::RotateWithDirection => self.rotate_with_direction.is_some(), 227 220 OutputType::Spray => self.spray.is_some(), 228 221 OutputType::Unknown => false, 229 222 OutputType::Vibrate => self.vibrate.is_some(), ··· 258 251 .map(|x| x as &dyn DeviceFeatureOutputLimits), 259 252 OutputType::Rotate => self 260 253 .rotate() 261 - .as_ref() 262 - .map(|x| x as &dyn DeviceFeatureOutputLimits), 263 - OutputType::RotateWithDirection => self 264 - .rotate_with_direction() 265 254 .as_ref() 266 255 .map(|x| x as &dyn DeviceFeatureOutputLimits), 267 256 OutputType::Spray => self
-1
crates/buttplug_core/src/message/v4/mod.rs
··· 23 23 OutputCmdV4, 24 24 OutputCommand, 25 25 OutputPositionWithDuration, 26 - OutputRotateWithDirection, 27 26 OutputValue, 28 27 }, 29 28 request_server_info::RequestServerInfoV4,
+7 -27
crates/buttplug_core/src/message/v4/output_cmd.rs
··· 23 23 #[getset(get_copy = "pub")] 24 24 pub struct OutputValue { 25 25 #[serde(rename = "Value")] 26 - value: u32, 26 + value: i32, 27 27 } 28 28 29 29 impl OutputValue { 30 - pub fn new(value: u32) -> Self { 30 + pub fn new(value: i32) -> Self { 31 31 Self { value } 32 32 } 33 33 } ··· 47 47 } 48 48 } 49 49 50 - #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, CopyGetters)] 51 - #[getset(get_copy = "pub")] 52 - pub struct OutputRotateWithDirection { 53 - #[serde(rename = "Speed")] 54 - speed: u32, 55 - #[serde(rename = "Clockwise")] 56 - clockwise: bool, 57 - } 58 - 59 - impl OutputRotateWithDirection { 60 - pub fn new(speed: u32, clockwise: bool) -> Self { 61 - Self { speed, clockwise } 62 - } 63 - } 64 - 65 50 #[derive(Debug, Display, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] 66 51 pub enum OutputCommand { 67 52 Vibrate(OutputValue), 68 53 // Single Direction Rotation Speed 69 54 Rotate(OutputValue), 70 - // Two Direction Rotation Speed 71 - RotateWithDirection(OutputRotateWithDirection), 72 55 Oscillate(OutputValue), 73 56 Constrict(OutputValue), 74 57 Spray(OutputValue), ··· 81 64 } 82 65 83 66 impl OutputCommand { 84 - pub fn value(&self) -> u32 { 67 + pub fn value(&self) -> i32 { 85 68 match self { 86 69 OutputCommand::Constrict(x) 87 70 | OutputCommand::Spray(x) ··· 91 74 | OutputCommand::Position(x) 92 75 | OutputCommand::Rotate(x) 93 76 | OutputCommand::Vibrate(x) => x.value(), 94 - OutputCommand::RotateWithDirection(x) => x.speed(), 95 - OutputCommand::PositionWithDuration(x) => x.position(), 77 + OutputCommand::PositionWithDuration(x) => x.position() as i32, 96 78 } 97 79 } 98 80 99 - pub fn set_value(&mut self, value: u32) { 81 + pub fn set_value(&mut self, value: i32) { 100 82 match self { 101 83 OutputCommand::Constrict(x) 102 84 | OutputCommand::Spray(x) ··· 106 88 | OutputCommand::Position(x) 107 89 | OutputCommand::Rotate(x) 108 90 | OutputCommand::Vibrate(x) => x.value = value, 109 - OutputCommand::RotateWithDirection(x) => x.speed = value, 110 - OutputCommand::PositionWithDuration(x) => x.position = value, 91 + OutputCommand::PositionWithDuration(x) => x.position = value as u32, 111 92 } 112 93 } 113 94 ··· 115 96 match self { 116 97 Self::Vibrate(_) => OutputType::Vibrate, 117 98 Self::Rotate(_) => OutputType::Rotate, 118 - Self::RotateWithDirection(_) => OutputType::RotateWithDirection, 119 99 Self::Oscillate(_) => OutputType::Oscillate, 120 100 Self::Constrict(_) => OutputType::Constrict, 121 101 Self::Spray(_) => OutputType::Spray, ··· 126 106 } 127 107 } 128 108 129 - pub fn from_output_type(output_type: OutputType, value: u32) -> Result<Self, ButtplugError> { 109 + pub fn from_output_type(output_type: OutputType, value: i32) -> Result<Self, ButtplugError> { 130 110 match output_type { 131 111 OutputType::Constrict => Ok(Self::Constrict(OutputValue::new(value))), 132 112 OutputType::Heater => Ok(Self::Heater(OutputValue::new(value))),
+8 -14
crates/buttplug_server/src/device/protocol.rs
··· 227 227 let output_command = cmd.output_command(); 228 228 match output_command { 229 229 OutputCommand::Constrict(x) => { 230 - self.handle_output_constrict_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) 230 + self.handle_output_constrict_cmd(cmd.feature_index(), cmd.feature_id(), x.value().try_into().map_err(|e| ButtplugDeviceError::DeviceCommandSignError)?) 231 231 } 232 232 OutputCommand::Spray(x) => { 233 - self.handle_output_spray_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) 233 + self.handle_output_spray_cmd(cmd.feature_index(), cmd.feature_id(), x.value().try_into().map_err(|e| ButtplugDeviceError::DeviceCommandSignError)?) 234 234 } 235 235 OutputCommand::Oscillate(x) => { 236 - self.handle_output_oscillate_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) 236 + self.handle_output_oscillate_cmd(cmd.feature_index(), cmd.feature_id(), x.value().try_into().map_err(|e| ButtplugDeviceError::DeviceCommandSignError)?) 237 237 } 238 238 OutputCommand::Rotate(x) => { 239 239 self.handle_output_rotate_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) 240 240 } 241 241 OutputCommand::Vibrate(x) => { 242 - self.handle_output_vibrate_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) 242 + self.handle_output_vibrate_cmd(cmd.feature_index(), cmd.feature_id(), x.value().try_into().map_err(|e| ButtplugDeviceError::DeviceCommandSignError)?) 243 243 } 244 244 OutputCommand::Position(x) => { 245 - self.handle_output_position_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) 245 + self.handle_output_position_cmd(cmd.feature_index(), cmd.feature_id(), x.value().try_into().map_err(|e| ButtplugDeviceError::DeviceCommandSignError)?) 246 246 } 247 247 OutputCommand::Heater(x) => { 248 - self.handle_output_heater_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) 248 + self.handle_output_heater_cmd(cmd.feature_index(), cmd.feature_id(), x.value().try_into().map_err(|e| ButtplugDeviceError::DeviceCommandSignError)?) 249 249 } 250 250 OutputCommand::Led(x) => { 251 - self.handle_output_led_cmd(cmd.feature_index(), cmd.feature_id(), x.value()) 251 + self.handle_output_led_cmd(cmd.feature_index(), cmd.feature_id(), x.value().try_into().map_err(|e| ButtplugDeviceError::DeviceCommandSignError)?) 252 252 } 253 253 OutputCommand::PositionWithDuration(x) => self.handle_position_with_duration_cmd( 254 254 cmd.feature_index(), 255 255 cmd.feature_id(), 256 256 x.position(), 257 257 x.duration(), 258 - ), 259 - OutputCommand::RotateWithDirection(x) => self.handle_rotation_with_direction_cmd( 260 - cmd.feature_index(), 261 - cmd.feature_id(), 262 - x.speed(), 263 - x.clockwise(), 264 258 ), 265 259 } 266 260 } ··· 278 272 &self, 279 273 _feature_index: u32, 280 274 _feature_id: Uuid, 281 - _speed: u32, 275 + _speed: i32, 282 276 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 283 277 self.command_unimplemented("OutputCmd (Rotate Actuator)") 284 278 }
+1 -1
crates/buttplug_server/src/device/protocol_impl/cowgirl.rs
··· 65 65 &self, 66 66 _feature_index: u32, 67 67 _feature_id: Uuid, 68 - speed: u32, 68 + speed: i32, 69 69 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 70 70 self.speeds[1].store(speed as u8, Ordering::Relaxed); 71 71 Ok(self.hardware_commands())
+2 -2
crates/buttplug_server/src/device/protocol_impl/joyhub/joyhub.rs
··· 66 66 &self, 67 67 feature_index: u32, 68 68 _feature_id: Uuid, 69 - speed: u32, 69 + speed: i32, 70 70 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 71 - self.form_hardware_command(feature_index, speed) 71 + self.form_hardware_command(feature_index, speed as u32) 72 72 } 73 73 74 74 fn handle_output_oscillate_cmd(
+2 -2
crates/buttplug_server/src/device/protocol_impl/joyhub/joyhub_v2.rs
··· 66 66 &self, 67 67 feature_index: u32, 68 68 _feature_id: Uuid, 69 - speed: u32, 69 + speed: i32, 70 70 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 71 - self.form_hardware_command(feature_index, speed) 71 + self.form_hardware_command(feature_index, speed as u32) 72 72 } 73 73 74 74 fn handle_output_oscillate_cmd(
+2 -2
crates/buttplug_server/src/device/protocol_impl/joyhub/joyhub_v4.rs
··· 65 65 &self, 66 66 feature_index: u32, 67 67 _feature_id: Uuid, 68 - speed: u32, 68 + speed: i32, 69 69 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 70 - self.form_hardware_command(feature_index, speed) 70 + self.form_hardware_command(feature_index, speed as u32) 71 71 } 72 72 73 73 fn handle_output_oscillate_cmd(
+2 -2
crates/buttplug_server/src/device/protocol_impl/joyhub/joyhub_v5.rs
··· 65 65 &self, 66 66 feature_index: u32, 67 67 _feature_id: Uuid, 68 - speed: u32, 68 + speed: i32, 69 69 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 70 - self.form_hardware_command(feature_index, speed) 70 + self.form_hardware_command(feature_index, speed as u32) 71 71 } 72 72 73 73 fn handle_output_oscillate_cmd(
+2 -2
crates/buttplug_server/src/device/protocol_impl/joyhub/joyhub_v6.rs
··· 65 65 &self, 66 66 feature_index: u32, 67 67 _feature_id: Uuid, 68 - speed: u32, 68 + speed: i32, 69 69 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 70 - self.form_hardware_command(feature_index, speed) 70 + self.form_hardware_command(feature_index, speed as u32) 71 71 } 72 72 73 73 fn handle_output_oscillate_cmd(
+1 -1
crates/buttplug_server/src/device/protocol_impl/kizuna.rs
··· 24 24 &self, 25 25 _feature_index: u32, 26 26 feature_id: Uuid, 27 - speed: u32, 27 + speed: i32, 28 28 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 29 29 Ok(vec![ 30 30 HardwareWriteCmd::new(
+2 -2
crates/buttplug_server/src/device/protocol_impl/lelo_harmony.rs
··· 147 147 &self, 148 148 feature_index: u32, 149 149 feature_id: Uuid, 150 - speed: u32, 150 + speed: i32, 151 151 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 152 - self.handle_input_cmd(feature_index, feature_id, speed) 152 + self.handle_input_cmd(feature_index, feature_id, speed as u32) 153 153 } 154 154 155 155 fn handle_output_vibrate_cmd(
+1
crates/buttplug_server/src/device/protocol_impl/lovense/lovense_multi_actuator.rs
··· 42 42 feature_id: Uuid, 43 43 speed: u32, 44 44 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 45 + debug!("Lovense multi-actuator command: {}", format!("Vibrate{}:{};", feature_index + 1, speed)); 45 46 let lovense_cmd = format!("Vibrate{}:{};", feature_index + 1, speed) 46 47 .as_bytes() 47 48 .to_vec();
+2 -2
crates/buttplug_server/src/device/protocol_impl/lovense/lovense_rotate_vibrator.rs
··· 42 42 &self, 43 43 _feature_index: u32, 44 44 _feature_id: Uuid, 45 - speed: u32, 45 + speed: i32, 46 46 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 47 - form_rotate_with_direction_command(speed, false) 47 + form_rotate_with_direction_command(speed.abs() as u32, speed < 0) 48 48 } 49 49 50 50 fn handle_rotation_with_direction_cmd(
+1 -1
crates/buttplug_server/src/device/protocol_impl/lovense/mod.rs
··· 200 200 .filter(|x| { 201 201 x.output() 202 202 .as_ref() 203 - .is_some_and(|x| x.contains(OutputType::RotateWithDirection)) 203 + .is_some_and(|x| x.contains(OutputType::Rotate)) 204 204 }) 205 205 .count() 206 206 == 1;
+1 -1
crates/buttplug_server/src/device/protocol_impl/luvmazer.rs
··· 41 41 &self, 42 42 _feature_index: u32, 43 43 feature_id: Uuid, 44 - speed: u32, 44 + speed: i32, 45 45 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 46 46 Ok(vec![ 47 47 HardwareWriteCmd::new(
+2 -2
crates/buttplug_server/src/device/protocol_impl/metaxsire.rs
··· 126 126 &self, 127 127 feature_index: u32, 128 128 _feature_id: Uuid, 129 - speed: u32, 129 + speed: i32, 130 130 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 131 - self.form_command(feature_index, speed) 131 + self.form_command(feature_index, speed as u32) 132 132 } 133 133 134 134 fn handle_output_constrict_cmd(
+2 -2
crates/buttplug_server/src/device/protocol_impl/metaxsire_v3.rs
··· 60 60 &self, 61 61 feature_index: u32, 62 62 feature_id: uuid::Uuid, 63 - speed: u32, 63 + speed: i32, 64 64 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 65 - self.form_command(feature_index, feature_id, speed) 65 + self.form_command(feature_index, feature_id, speed as u32) 66 66 } 67 67 }
+1 -1
crates/buttplug_server/src/device/protocol_impl/sakuraneko.rs
··· 54 54 &self, 55 55 _feature_index: u32, 56 56 feature_id: Uuid, 57 - speed: u32, 57 + speed: i32, 58 58 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 59 59 Ok(vec![ 60 60 HardwareWriteCmd::new(
+1 -1
crates/buttplug_server/src/device/protocol_impl/svakom/svakom_v3.rs
··· 53 53 &self, 54 54 _feature_index: u32, 55 55 feature_id: Uuid, 56 - speed: u32, 56 + speed: i32, 57 57 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 58 58 Ok(vec![ 59 59 HardwareWriteCmd::new(
+1 -1
crates/buttplug_server/src/device/protocol_impl/tryfun.rs
··· 44 44 &self, 45 45 _feature_index: u32, 46 46 feature_id: uuid::Uuid, 47 - speed: u32, 47 + speed: i32, 48 48 ) -> Result<Vec<HardwareCommand>, ButtplugDeviceError> { 49 49 let mut sum: u8 = 0xff; 50 50 let mut data = vec![0xAA, 0x02, 0x08, speed as u8];
-7
crates/buttplug_server/src/device/server_device.rs
··· 53 53 DeviceMessageInfoV4, 54 54 InputCommandType, 55 55 InputType, 56 - OutputRotateWithDirection, 57 56 OutputType, 58 57 OutputValue, 59 58 }, ··· 438 437 } 439 438 OutputType::Rotate => { 440 439 stop_cmd(message::OutputCommand::Rotate(OutputValue::new(0))); 441 - break; 442 - } 443 - OutputType::RotateWithDirection => { 444 - stop_cmd(message::OutputCommand::RotateWithDirection( 445 - OutputRotateWithDirection::new(0, true), 446 - )); 447 440 break; 448 441 } 449 442 OutputType::Vibrate => {
+3 -6
crates/buttplug_server/src/message/v3/client_device_message_attributes.rs
··· 10 10 v2::{ClientDeviceMessageAttributesV2, GenericDeviceMessageAttributesV2}, 11 11 }; 12 12 use buttplug_core::message::{ 13 - DeviceFeature, 14 - DeviceFeatureOutputValueProperties, 15 - InputCommandType, 16 - InputType, 17 - OutputType, 13 + DeviceFeature, DeviceFeatureOutputLimits, DeviceFeatureOutputValueProperties, InputCommandType, InputType, OutputType 18 14 }; 19 15 use getset::{Getters, MutGetters, Setters}; 20 16 use serde::{Deserialize, Serialize, Serializer, ser::SerializeSeq}; ··· 293 289 .flat_map(|feature| { 294 290 let mut actuator_vec = vec![]; 295 291 if let Some(output_map) = feature.output() 296 - && let Some(actuator) = output_map.rotate_with_direction() { 292 + && let Some(actuator) = output_map.rotate() 293 + && *actuator.value().start() < 0 { 297 294 let actuator_type = OutputType::Rotate; 298 295 let attrs = ClientGenericDeviceMessageAttributesV3 { 299 296 feature_descriptor: feature.description().to_owned(),
+2 -1
crates/buttplug_server/src/message/v3/server_device_message_attributes.rs
··· 108 108 .flat_map(|feature| { 109 109 let mut actuator_vec = vec![]; 110 110 if let Some(output_map) = feature.output() 111 - && let Some(actuator) = output_map.rotate_with_direction() { 111 + && let Some(actuator) = output_map.rotate() 112 + && *actuator.value().base().start() < 0 { 112 113 let actuator_type = OutputType::Rotate; 113 114 let step_count = actuator.value().step_count(); 114 115 let attrs = ServerGenericDeviceMessageAttributesV3 {
+2 -2
crates/buttplug_server/src/message/v4/checked_output_cmd.rs
··· 109 109 let value = cmd.command().value(); 110 110 let new_value = output_map 111 111 .calculate_from_value(output_type, value as i32) 112 - .map_err(|_| ButtplugDeviceError::DeviceStepRangeError(0, value))?; 112 + .map_err(|_| ButtplugDeviceError::DeviceStepRangeError(0, value.abs() as u32))?; 113 113 let mut new_command = cmd.command(); 114 - new_command.set_value(new_value as u32); 114 + new_command.set_value(new_value); 115 115 // We can't make a private trait impl to turn a ValueCmd into a CheckedValueCmd, and this 116 116 // is all about security, so we just copy. Silly, but it works for our needs in terms of 117 117 // making this a barrier.
+6 -9
crates/buttplug_server/src/message/v4/checked_output_vec_cmd.rs
··· 24 24 ButtplugMessageValidator, 25 25 OutputCommand, 26 26 OutputPositionWithDuration, 27 - OutputRotateWithDirection, 28 27 OutputType, 29 28 OutputValue, 30 29 }, ··· 122 121 |e: buttplug_server_device_config::ButtplugDeviceConfigError| { 123 122 ButtplugMessageError::InvalidMessageContents(e.to_string()) 124 123 }, 125 - )? as u32, 124 + )?, 126 125 )), 127 126 )) 128 127 } ··· 192 191 OutputCommand::Vibrate(OutputValue::new( 193 192 actuator 194 193 .calculate_scaled_float(vibrate_cmd.speed()) 195 - .map_err(|e| ButtplugMessageError::InvalidMessageContents(e.to_string()))? 196 - as u32, 194 + .map_err(|e| ButtplugMessageError::InvalidMessageContents(e.to_string()))?, 197 195 )), 198 196 )) 199 197 } ··· 257 255 msg.device_index(), 258 256 idx, 259 257 feature.feature.id(), 260 - OutputCommand::from_output_type(cmd.actuator_type(), output_value as u32).unwrap(), 258 + OutputCommand::from_output_type(cmd.actuator_type(), output_value).unwrap(), 261 259 )); 262 260 } 263 261 ··· 374 372 .ok_or(ButtplugError::from( 375 373 ButtplugDeviceError::DeviceNoActuatorError("RotateCmdV1".to_owned()), 376 374 ))? 377 - .rotate_with_direction() 375 + .rotate() 378 376 .as_ref() 379 377 .ok_or(ButtplugError::from( 380 378 ButtplugDeviceError::DeviceNoActuatorError("RotateCmdV1".to_owned()), ··· 384 382 msg.device_index(), 385 383 idx, 386 384 feature.feature.id(), 387 - OutputCommand::RotateWithDirection(OutputRotateWithDirection::new( 385 + OutputCommand::Rotate(OutputValue::new( 388 386 actuator.calculate_scaled_float(cmd.speed()).map_err(|_| { 389 387 ButtplugError::from(ButtplugMessageError::InvalidMessageContents( 390 388 "Position should be 0.0 < x < 1.0".to_owned(), 391 389 )) 392 - })? as u32, 393 - cmd.clockwise(), 390 + })? as i32 * (if cmd.clockwise() { 1 } else { -1 }) 394 391 )), 395 392 )); 396 393 }
+14 -14
crates/buttplug_server_device_config/build-config/buttplug-device-config-v4.json
··· 1 1 { 2 2 "version": { 3 3 "major": 4, 4 - "minor": 67 4 + "minor": 69 5 5 }, 6 6 "protocols": { 7 7 "activejoy": { ··· 10040 10040 { 10041 10041 "id": "515e07e2-a6e6-4ac0-a4b0-512504311260", 10042 10042 "output": { 10043 - "rotate_with_direction": { 10043 + "rotate": { 10044 10044 "value": [ 10045 10045 -20, 10046 10046 20 ··· 10572 10572 { 10573 10573 "id": "360d81e7-5126-4dbb-b72d-7bb60eb67400", 10574 10574 "output": { 10575 - "rotate_with_direction": { 10575 + "rotate": { 10576 10576 "value": [ 10577 10577 -20, 10578 10578 20 ··· 10918 10918 { 10919 10919 "id": "af885c72-ce2b-47d5-87be-3847f24d18a5", 10920 10920 "output": { 10921 - "rotate_with_direction": { 10921 + "rotate": { 10922 10922 "value": [ 10923 10923 -20, 10924 10924 20 ··· 11323 11323 { 11324 11324 "id": "d49001e8-5f6b-43ac-9cc7-7e68fab7c323", 11325 11325 "output": { 11326 - "rotate_with_direction": { 11326 + "rotate": { 11327 11327 "value": [ 11328 11328 -20, 11329 11329 20 ··· 13295 13295 { 13296 13296 "id": "683b450d-bb1a-4fca-b61a-83f8b56086fa", 13297 13297 "output": { 13298 - "rotate_with_direction": { 13298 + "rotate": { 13299 13299 "value": [ 13300 13300 -255, 13301 13301 255 ··· 13914 13914 { 13915 13915 "id": "fabe3961-dc17-4f32-856f-13880c0a29a3", 13916 13916 "output": { 13917 - "rotate_with_direction": { 13917 + "rotate": { 13918 13918 "value": [ 13919 13919 -2, 13920 13920 2 ··· 18224 18224 { 18225 18225 "id": "b7495351-9101-448a-94c4-4598cf541dca", 18226 18226 "output": { 18227 - "rotate_with_direction": { 18227 + "rotate": { 18228 18228 "value": [ 18229 18229 -6, 18230 18230 6 ··· 18461 18461 { 18462 18462 "id": "26402ebe-7ee0-4c7d-ae40-205ec4f3a1b0", 18463 18463 "output": { 18464 - "rotate_with_direction": { 18464 + "rotate": { 18465 18465 "value": [ 18466 18466 -100, 18467 18467 100 ··· 18776 18776 { 18777 18777 "id": "1d1b4dea-ab29-4426-a9f4-dda2c594eefb", 18778 18778 "output": { 18779 - "rotate_with_direction": { 18779 + "rotate": { 18780 18780 "value": [ 18781 18781 -10, 18782 18782 10 ··· 18857 18857 { 18858 18858 "id": "8e249d53-8d80-4f42-bc40-e6edb7779e92", 18859 18859 "output": { 18860 - "rotate_with_direction": { 18860 + "rotate": { 18861 18861 "value": [ 18862 18862 -99, 18863 18863 99 ··· 18878 18878 { 18879 18879 "id": "2d8d1443-c394-4df4-b9bb-1659d8323b45", 18880 18880 "output": { 18881 - "rotate_with_direction": { 18881 + "rotate": { 18882 18882 "value": [ 18883 18883 -99, 18884 18884 99 ··· 18899 18899 { 18900 18900 "id": "a1632ce4-314f-481d-9ae2-2a11a0c4caa4", 18901 18901 "output": { 18902 - "rotate_with_direction": { 18902 + "rotate": { 18903 18903 "value": [ 18904 18904 -99, 18905 18905 99 ··· 18910 18910 { 18911 18911 "id": "4b09a02d-9a4a-4c8b-8340-8e6ca3cecfc2", 18912 18912 "output": { 18913 - "rotate_with_direction": { 18913 + "rotate": { 18914 18914 "value": [ 18915 18915 -99, 18916 18916 99
+2 -2
crates/buttplug_server_device_config/device-config-v4/buttplug-device-config-schema-v4.json
··· 186 186 "output": { 187 187 "type": "object", 188 188 "patternProperties": { 189 - "^(vibrate|rotate|oscillate|constrict|spray|position|rotate_with_direction|heater|led)$": { 189 + "^(vibrate|rotate|oscillate|constrict|spray|position|heater|led)$": { 190 190 "type": "object", 191 191 "properties": { 192 192 "value": { ··· 276 276 "output": { 277 277 "type": "object", 278 278 "patternProperties": { 279 - "^(vibrate|rotate|oscillate|constrict|spray|rotate_with_direction|heater|led)$": { 279 + "^(vibrate|rotate|oscillate|constrict|spray|heater|led)$": { 280 280 "type": "object", 281 281 "properties": { 282 282 "value": {
+2 -2
crates/buttplug_server_device_config/device-config-v4/protocols/lovense-connect-service.yml
··· 84 84 - 20 85 85 - id: af885c72-ce2b-47d5-87be-3847f24d18a5 86 86 output: 87 - rotate_with_direction: 87 + rotate: 88 88 value: 89 89 - -20 90 90 - 20 ··· 307 307 - 20 308 308 - id: d49001e8-5f6b-43ac-9cc7-7e68fab7c323 309 309 output: 310 - rotate_with_direction: 310 + rotate: 311 311 value: 312 312 - -20 313 313 - 20
+2 -2
crates/buttplug_server_device_config/device-config-v4/protocols/lovense.yml
··· 85 85 - 20 86 86 - id: 515e07e2-a6e6-4ac0-a4b0-512504311260 87 87 output: 88 - rotate_with_direction: 88 + rotate: 89 89 value: 90 90 - -20 91 91 - 20 ··· 378 378 - 20 379 379 - id: 360d81e7-5126-4dbb-b72d-7bb60eb67400 380 380 output: 381 - rotate_with_direction: 381 + rotate: 382 382 value: 383 383 - -20 384 384 - 20
+1 -1
crates/buttplug_server_device_config/device-config-v4/protocols/motorbunny.yml
··· 9 9 - 255 10 10 - id: 683b450d-bb1a-4fca-b61a-83f8b56086fa 11 11 output: 12 - rotate_with_direction: 12 + rotate: 13 13 value: 14 14 - -255 15 15 - 255
+1 -1
crates/buttplug_server_device_config/device-config-v4/protocols/nexus-revo.yml
··· 9 9 - 10 10 10 - id: fabe3961-dc17-4f32-856f-13880c0a29a3 11 11 output: 12 - rotate_with_direction: 12 + rotate: 13 13 value: 14 14 - -2 15 15 - 2
+1 -1
crates/buttplug_server_device_config/device-config-v4/protocols/synchro.yml
··· 3 3 features: 4 4 - id: b7495351-9101-448a-94c4-4598cf541dca 5 5 output: 6 - rotate_with_direction: 6 + rotate: 7 7 value: 8 8 - -6 9 9 - 6
+1 -1
crates/buttplug_server_device_config/device-config-v4/protocols/tryfun-meta2.yml
··· 15 15 - 100 16 16 - id: 26402ebe-7ee0-4c7d-ae40-205ec4f3a1b0 17 17 output: 18 - rotate_with_direction: 18 + rotate: 19 19 value: 20 20 - -100 21 21 - 100
+1 -1
crates/buttplug_server_device_config/device-config-v4/protocols/vorze-cyclone-x.yml
··· 3 3 features: 4 4 - id: 1d1b4dea-ab29-4426-a9f4-dda2c594eefb 5 5 output: 6 - rotate_with_direction: 6 + rotate: 7 7 value: 8 8 - -10 9 9 - 10
+4 -4
crates/buttplug_server_device_config/device-config-v4/protocols/vorze-sa.yml
··· 34 34 features: 35 35 - id: 8e249d53-8d80-4f42-bc40-e6edb7779e92 36 36 output: 37 - rotate_with_direction: 37 + rotate: 38 38 value: 39 39 - -99 40 40 - 99 ··· 46 46 features: 47 47 - id: 2d8d1443-c394-4df4-b9bb-1659d8323b45 48 48 output: 49 - rotate_with_direction: 49 + rotate: 50 50 value: 51 51 - -99 52 52 - 99 ··· 58 58 features: 59 59 - id: a1632ce4-314f-481d-9ae2-2a11a0c4caa4 60 60 output: 61 - rotate_with_direction: 61 + rotate: 62 62 value: 63 63 - -99 64 64 - 99 65 65 - id: 4b09a02d-9a4a-4c8b-8340-8e6ca3cecfc2 66 66 output: 67 - rotate_with_direction: 67 + rotate: 68 68 value: 69 69 - -99 70 70 - 99
+1 -1
crates/buttplug_server_device_config/device-config-v4/version.yaml
··· 1 1 version: 2 2 major: 4 3 - minor: 67 3 + minor: 69
+1 -8
crates/buttplug_server_device_config/src/device_config_file/feature.rs
··· 83 83 #[serde(skip_serializing_if = "Option::is_none")] 84 84 rotate: Option<BaseDeviceFeatureOutputValueProperties>, 85 85 #[serde(skip_serializing_if = "Option::is_none")] 86 - rotate_with_direction: Option<BaseDeviceFeatureOutputValueProperties>, 87 - #[serde(skip_serializing_if = "Option::is_none")] 88 86 oscillate: Option<BaseDeviceFeatureOutputValueProperties>, 89 87 #[serde(skip_serializing_if = "Option::is_none")] 90 88 constrict: Option<BaseDeviceFeatureOutputValueProperties>, ··· 108 106 } 109 107 if let Some(rotate) = val.rotate { 110 108 output.set_rotate(Some(rotate.into())); 111 - } 112 - if let Some(rotate_with_direction) = val.rotate_with_direction { 113 - output.set_rotate_with_direction(Some(rotate_with_direction.into())); 114 109 } 115 110 if let Some(oscillate) = val.oscillate { 116 111 output.set_oscillate(Some(oscillate.into())); ··· 254 249 #[serde(skip_serializing_if = "Option::is_none")] 255 250 rotate: Option<UserDeviceFeatureOutputValueProperties>, 256 251 #[serde(skip_serializing_if = "Option::is_none")] 257 - rotate_with_direction: Option<UserDeviceFeatureOutputValueProperties>, 258 - #[serde(skip_serializing_if = "Option::is_none")] 259 252 oscillate: Option<UserDeviceFeatureOutputValueProperties>, 260 253 #[serde(skip_serializing_if = "Option::is_none")] 261 254 constrict: Option<UserDeviceFeatureOutputValueProperties>, ··· 277 270 base_output: &ServerDeviceFeatureOutput, 278 271 ) -> Result<ServerDeviceFeatureOutput, ButtplugDeviceConfigError> { 279 272 let mut output = ServerDeviceFeatureOutput::default(); 273 + // TODO Flip logic and output errors if user has something base doesn't, or vice versa. 280 274 if let Some(base_vibrate) = base_output.vibrate() { 281 275 if let Some(user_vibrate) = &self.vibrate { 282 276 output.set_vibrate(Some(user_vibrate.with_base_properties(base_vibrate)?)); ··· 349 343 Self { 350 344 vibrate: value.vibrate().as_ref().map(|x| x.into()), 351 345 rotate: value.rotate().as_ref().map(|x| x.into()), 352 - rotate_with_direction: value.rotate_with_direction().as_ref().map(|x| x.into()), 353 346 oscillate: value.oscillate().as_ref().map(|x| x.into()), 354 347 constrict: value.constrict().as_ref().map(|x| x.into()), 355 348 heater: value.heater().as_ref().map(|x| x.into()),
-18
crates/buttplug_server_device_config/src/server_device_feature.rs
··· 292 292 pub struct ServerDeviceFeatureOutput { 293 293 vibrate: Option<ServerDeviceFeatureOutputValueProperties>, 294 294 rotate: Option<ServerDeviceFeatureOutputValueProperties>, 295 - rotate_with_direction: Option<ServerDeviceFeatureOutputValueProperties>, 296 295 oscillate: Option<ServerDeviceFeatureOutputValueProperties>, 297 296 constrict: Option<ServerDeviceFeatureOutputValueProperties>, 298 297 heater: Option<ServerDeviceFeatureOutputValueProperties>, ··· 312 311 OutputType::Position => self.position.is_some(), 313 312 OutputType::PositionWithDuration => self.position_with_duration.is_some(), 314 313 OutputType::Rotate => self.rotate.is_some(), 315 - OutputType::RotateWithDirection => self.rotate.is_some(), 316 314 OutputType::Spray => self.spray.is_some(), 317 315 OutputType::Unknown => false, 318 316 OutputType::Vibrate => self.vibrate.is_some(), ··· 346 344 .rotate 347 345 .is_some() 348 346 .then(|| types.push(OutputType::Rotate)); 349 - self 350 - .rotate_with_direction 351 - .is_some() 352 - .then(|| types.push(OutputType::RotateWithDirection)); 353 347 self.spray.is_some().then(|| types.push(OutputType::Spray)); 354 348 self 355 349 .vibrate ··· 393 387 Err(ButtplugDeviceConfigError::InvalidOutput(output_type)), 394 388 |x| x.calculate_scaled_value(value), 395 389 ), 396 - OutputType::RotateWithDirection => self.rotate_with_direction.as_ref().map_or( 397 - Err(ButtplugDeviceConfigError::InvalidOutput(output_type)), 398 - |x| x.calculate_scaled_value(value), 399 - ), 400 390 OutputType::Spray => self.spray.as_ref().map_or( 401 391 Err(ButtplugDeviceConfigError::InvalidOutput(output_type)), 402 392 |x| x.calculate_scaled_value(value), ··· 443 433 Err(ButtplugDeviceConfigError::InvalidOutput(output_type)), 444 434 |x| x.calculate_scaled_float(value), 445 435 ), 446 - OutputType::RotateWithDirection => self.rotate_with_direction.as_ref().map_or( 447 - Err(ButtplugDeviceConfigError::InvalidOutput(output_type)), 448 - |x| x.calculate_scaled_float(value), 449 - ), 450 436 OutputType::Spray => self.spray.as_ref().map_or( 451 437 Err(ButtplugDeviceConfigError::InvalidOutput(output_type)), 452 438 |x| x.calculate_scaled_float(value), ··· 465 451 let mut builder = DeviceFeatureOutputBuilder::default(); 466 452 val.vibrate.as_ref().map(|x| builder.vibrate(x.into())); 467 453 val.rotate.as_ref().map(|x| builder.rotate(x.into())); 468 - val 469 - .rotate_with_direction 470 - .as_ref() 471 - .map(|x| builder.rotate_with_direction(x.into())); 472 454 val.oscillate.as_ref().map(|x| builder.oscillate(x.into())); 473 455 val.constrict.as_ref().map(|x| builder.constrict(x.into())); 474 456 val.heater.as_ref().map(|x| builder.heater(x.into()));
-1
crates/examples/src/bin/device_tester.rs
··· 17 17 DeviceFeature, 18 18 DeviceFeatureOutput, 19 19 OutputCommand, 20 - OutputRotateWithDirection, 21 20 OutputType, 22 21 OutputValue, 23 22 };