Buttplug sex toy control library
at dev 197 lines 7.5 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::message::v1::NullDeviceMessageAttributesV1; 9use buttplug_core::message::{InputType, OutputType}; 10use buttplug_server_device_config::ServerDeviceFeature; 11 12use getset::{Getters, MutGetters, Setters}; 13use std::ops::RangeInclusive; 14 15#[derive(Clone, Debug, Default, PartialEq, Eq, Getters, MutGetters, Setters)] 16#[getset(get = "pub")] 17pub struct ServerDeviceMessageAttributesV3 { 18 // Generic commands 19 pub(in crate::message) scalar_cmd: Option<Vec<ServerGenericDeviceMessageAttributesV3>>, 20 pub(in crate::message) rotate_cmd: Option<Vec<ServerGenericDeviceMessageAttributesV3>>, 21 pub(in crate::message) linear_cmd: Option<Vec<ServerGenericDeviceMessageAttributesV3>>, 22 23 // Sensor Messages 24 pub(in crate::message) sensor_read_cmd: Option<Vec<ServerSensorDeviceMessageAttributesV3>>, 25 pub(in crate::message) sensor_subscribe_cmd: Option<Vec<ServerSensorDeviceMessageAttributesV3>>, 26 27 // StopDeviceCmd always exists 28 pub(in crate::message) stop_device_cmd: NullDeviceMessageAttributesV1, 29 30 // Needed to load from config for fallback, but unused here. 31 pub(in crate::message) fleshlight_launch_fw12_cmd: Option<NullDeviceMessageAttributesV1>, 32 pub(in crate::message) vorze_a10_cyclone_cmd: Option<NullDeviceMessageAttributesV1>, 33} 34 35#[derive(Clone, Debug, PartialEq, Eq, Getters, Setters)] 36#[getset(get = "pub")] 37pub struct ServerGenericDeviceMessageAttributesV3 { 38 pub(in crate::message) feature_descriptor: String, 39 pub(in crate::message) actuator_type: OutputType, 40 pub(in crate::message) step_count: u32, 41 pub(in crate::message) index: u32, 42 pub(in crate::message) feature: ServerDeviceFeature, 43} 44 45#[derive(Clone, Debug, PartialEq, Eq, Getters, Setters)] 46#[getset(get = "pub")] 47pub struct ServerSensorDeviceMessageAttributesV3 { 48 pub(in crate::message) feature_descriptor: String, 49 pub(in crate::message) sensor_type: InputType, 50 pub(in crate::message) sensor_range: Vec<RangeInclusive<i32>>, 51 pub(in crate::message) index: u32, 52 pub(in crate::message) feature: ServerDeviceFeature, 53} 54 55impl From<Vec<ServerDeviceFeature>> for ServerDeviceMessageAttributesV3 { 56 fn from(features: Vec<ServerDeviceFeature>) -> Self { 57 let scalar_attrs: Vec<ServerGenericDeviceMessageAttributesV3> = features 58 .iter() 59 .flat_map(|feature| { 60 let mut actuator_vec = vec![]; 61 if let Some(output_map) = feature.output() { 62 let mut create_attribute = |actuator_type, step_count| { 63 let actuator_type = actuator_type; 64 let attrs = ServerGenericDeviceMessageAttributesV3 { 65 feature_descriptor: feature.description().to_owned(), 66 actuator_type, 67 step_count, 68 feature: feature.clone(), 69 index: 0, 70 }; 71 actuator_vec.push(attrs) 72 }; 73 // TODO oh come on just make a fucking iterator here. At least, once we figure out the 74 // unifying trait we can use to make an iterator on this. 75 if let Some(attr) = output_map.constrict().as_ref() { 76 create_attribute(OutputType::Constrict, attr.value().step_count()) 77 } 78 if let Some(attr) = output_map.oscillate().as_ref() { 79 create_attribute(OutputType::Oscillate, attr.value().step_count()) 80 } 81 if let Some(attr) = output_map.position().as_ref() { 82 create_attribute(OutputType::Position, attr.position().step_count()) 83 } 84 if let Some(attr) = output_map.rotate().as_ref() { 85 create_attribute(OutputType::Rotate, attr.value().step_count()) 86 } 87 if let Some(attr) = output_map.temperature().as_ref() { 88 create_attribute(OutputType::Temperature, attr.value().step_count()) 89 } 90 if let Some(attr) = output_map.led().as_ref() { 91 create_attribute(OutputType::Led, attr.value().step_count()) 92 } 93 if let Some(attr) = output_map.vibrate().as_ref() { 94 create_attribute(OutputType::Vibrate, attr.value().step_count()) 95 } 96 if let Some(attr) = output_map.spray().as_ref() { 97 create_attribute(OutputType::Spray, attr.value().step_count()) 98 } 99 } 100 actuator_vec 101 }) 102 .collect(); 103 104 // We have to calculate rotation attributes seperately, since they're a combination of 105 // feature type and message in >= v4. 106 let rotate_attrs: Vec<ServerGenericDeviceMessageAttributesV3> = features 107 .iter() 108 .flat_map(|feature| { 109 let mut actuator_vec = vec![]; 110 if let Some(output_map) = feature.output() 111 && let Some(actuator) = output_map.rotate() 112 && *actuator.value().base().start() < 0 113 { 114 let actuator_type = OutputType::Rotate; 115 let step_count = actuator.value().step_count(); 116 let attrs = ServerGenericDeviceMessageAttributesV3 { 117 feature_descriptor: feature.description().to_owned(), 118 actuator_type, 119 step_count, 120 feature: feature.clone(), 121 index: 0, 122 }; 123 actuator_vec.push(attrs) 124 } 125 actuator_vec 126 }) 127 .collect(); 128 129 let linear_attrs: Vec<ServerGenericDeviceMessageAttributesV3> = features 130 .iter() 131 .flat_map(|feature| { 132 let mut actuator_vec = vec![]; 133 if let Some(output_map) = feature.output() 134 && let Some(actuator) = output_map.position_with_duration() 135 { 136 let actuator_type = OutputType::Position; 137 let step_count = actuator.position().step_count(); 138 let attrs = ServerGenericDeviceMessageAttributesV3 { 139 feature_descriptor: feature.description().to_owned(), 140 actuator_type, 141 step_count, 142 feature: feature.clone(), 143 index: 0, 144 }; 145 actuator_vec.push(attrs) 146 } 147 actuator_vec 148 }) 149 .collect(); 150 151 let sensor_filter = { 152 let attrs: Vec<ServerSensorDeviceMessageAttributesV3> = features 153 .iter() 154 .map(|feature| { 155 let mut sensor_vec = vec![]; 156 if let Some(sensor_map) = feature.input() 157 && let Some(battery) = sensor_map.battery() 158 { 159 // Only convert Battery backwards. Other sensors weren't really built for v3 and we 160 // never recommended using them or implemented much for them. 161 sensor_vec.push(ServerSensorDeviceMessageAttributesV3 { 162 feature_descriptor: feature.description().to_owned(), 163 sensor_type: InputType::Battery, 164 sensor_range: battery.value_range().clone(), 165 feature: feature.clone(), 166 index: 0, 167 }); 168 } 169 sensor_vec 170 }) 171 .flatten() 172 .collect(); 173 if !attrs.is_empty() { Some(attrs) } else { None } 174 }; 175 176 Self { 177 scalar_cmd: if scalar_attrs.is_empty() { 178 None 179 } else { 180 Some(scalar_attrs) 181 }, 182 rotate_cmd: if rotate_attrs.is_empty() { 183 None 184 } else { 185 Some(rotate_attrs) 186 }, 187 linear_cmd: if linear_attrs.is_empty() { 188 None 189 } else { 190 Some(linear_attrs) 191 }, 192 sensor_read_cmd: sensor_filter, 193 sensor_subscribe_cmd: None, 194 ..Default::default() 195 } 196 } 197}