1use std::{
5 any::TypeId,
6 collections::HashMap,
7 sync::{Arc, LazyLock},
8};
9
10use super::Feature;
11use crate::{
12 channel::HidppChannel,
13 feature::{
14 CreatableFeature,
15 adc_measurement::AdcMeasurementFeature,
16 adjustable_dpi::AdjustableDpiFeature,
17 backlight::BacklightFeature,
18 battery_status::BatteryStatusFeature,
19 battery_voltage::BatteryVoltageFeature,
20 brightness_control::BrightnessControlFeature,
21 change_host::ChangeHostFeature,
22 color_led_effects::ColorLedEffectsFeature,
23 crown::CrownFeature,
24 device_friendly_name::DeviceFriendlyNameFeature,
25 device_information::DeviceInformationFeature,
26 device_type_and_name::DeviceTypeAndNameFeature,
27 disable_keys::DisableKeysFeature,
28 disable_keys_by_usage::DisableKeysByUsageFeature,
29 dual_platform::DualPlatformFeature,
30 equalizer::EqualizerFeature,
31 extended_dpi::ExtendedDpiFeature,
32 extended_report_rate::ExtendedReportRateFeature,
33 feature_set::FeatureSetFeature,
34 fn_inversion::{FnInversionMultiHostFeature, FnInversionWithDefaultStateFeature},
35 gestures2::Gestures2Feature,
36 haptic_feedback::HapticFeedbackFeature,
37 hires_wheel::HiResWheelFeature,
38 hosts_info::HostsInfoFeature,
39 illumination::IlluminationFeature,
40 mode_status::ModeStatusFeature,
41 mouse_pointer::MousePointerFeature,
42 multi_platform::MultiPlatformFeature,
43 per_key_lighting::PerKeyLightingFeature,
44 persistent_remappable_action::PersistentRemappableActionFeature,
45 pointer_motion_scaling::PointerMotionScalingFeature,
46 report_rate::ReportRateFeature,
47 reprog_controls::ReprogControlsFeature,
48 rgb_effects::RgbEffectsFeature,
49 root::RootFeature,
50 sidetone::SidetoneFeature,
51 smartshift::SmartShiftFeature,
52 smartshift_enhanced::SmartShiftEnhancedFeature,
53 solar_dashboard::SolarDashboardFeature,
54 thumbwheel::ThumbwheelFeature,
55 touch_mouse_raw::TouchMouseRawFeature,
56 touchpad_raw_xy::TouchpadRawXyFeature,
57 unified_battery::UnifiedBatteryFeature,
58 vertical_scrolling::VerticalScrollingFeature,
59 wireless_device_status::WirelessDeviceStatusFeature,
60 },
61};
62
63pub type FeatureImplProducer =
66 fn(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> (TypeId, Arc<dyn Feature>);
67
68#[derive(Clone, Copy, Debug, Hash)]
71pub struct FeatureVersion {
72 pub starting_version: u8,
74
75 pub producer: FeatureImplProducer,
77}
78
79#[derive(Clone, Copy, Debug, Hash)]
81pub struct KnownFeature {
82 pub name: &'static str,
86
87 pub versions: &'static [FeatureVersion],
90}
91
92pub fn lookup(feature_id: u16) -> Option<KnownFeature> {
94 KNOWN_FEATURES.get(&feature_id).copied()
95}
96
97#[must_use]
100pub fn lookup_version(feature_id: u16, feature_version: u8) -> Option<Vec<FeatureVersion>> {
101 lookup(feature_id).map(|feat| {
102 feat.versions
103 .iter()
104 .filter(|&ver| ver.starting_version <= feature_version)
105 .copied()
106 .collect::<Vec<FeatureVersion>>()
107 })
108}
109
110fn new_dyn<F: CreatableFeature>(
112 chan: Arc<HidppChannel>,
113 device_index: u8,
114 feature_index: u8,
115) -> (TypeId, Arc<dyn Feature>) {
116 (
117 TypeId::of::<F>(),
118 Arc::new(F::new(chan, device_index, feature_index)),
119 )
120}
121
122macro_rules! known_features {
132 ( $( $id:literal $name:literal $( => $($feat:ty),+ )? ),* $(,)? ) => {
133 HashMap::from([ $(
134 ($id, KnownFeature { name: $name, versions: known_features!(@versions $id $( $($feat),+ )?) }),
135 )* ])
136 };
137 (@versions $id:literal) => { &[] };
138 (@versions $id:literal $($feat:ty),+) => {
139 &[$(FeatureVersion {
140 starting_version: {
141 const {
142 assert!(
143 <$feat>::ID == $id,
144 "registry id must match the impl's CreatableFeature::ID"
145 );
146 }
147 <$feat>::STARTING_VERSION
148 },
149 producer: new_dyn::<$feat>,
150 }),+]
151 };
152}
153
154static KNOWN_FEATURES: LazyLock<HashMap<u16, KnownFeature>> = LazyLock::new(|| {
155 known_features! {
156 0x0000 "Root" => RootFeature,
157 0x0001 "FeatureSet" => FeatureSetFeature,
158 0x0002 "FeatureInfo",
159 0x0003 "DeviceInformation" => DeviceInformationFeature,
160 0x0004 "UnitId",
161 0x0005 "DeviceTypeAndName" => DeviceTypeAndNameFeature,
162 0x0006 "DeviceGroups",
163 0x0007 "DeviceFriendlyName" => DeviceFriendlyNameFeature,
164 0x0008 "KeepAlive",
165 0x0020 "ConfigChange",
166 0x0021 "UniqueRandomId",
167 0x0030 "TargetSoftware",
168 0x0080 "WirelessSignalStrength",
169 0x00c0 "DfuControlLegacy",
170 0x00c1 "DfuControlUnsigned",
171 0x00c2 "DfuControlSigned",
172 0x00c3 "DfuControlBolt",
173 0x00d0 "Dfu",
174 0x00d1 "DfuResumable",
175 0x1000 "BatteryStatus" => BatteryStatusFeature,
176 0x1001 "BatteryVoltage" => BatteryVoltageFeature,
177 0x1004 "UnifiedBattery" => UnifiedBatteryFeature,
178 0x1010 "ChargingControl",
179 0x1300 "LedControl",
180 0x1800 "GenericTest",
181 0x1802 "DeviceReset",
182 0x1805 "OobState",
183 0x1806 "ConfigDeviceProps",
184 0x1814 "ChangeHost" => ChangeHostFeature,
185 0x1815 "HostsInfo" => HostsInfoFeature,
186 0x1981 "Backlight1",
187 0x1982 "Backlight2" => BacklightFeature,
188 0x1983 "Backlight3",
189 0x1990 "Illumination" => IlluminationFeature,
190 0x19b0 "HapticFeedback" => HapticFeedbackFeature,
191 0x19c0 "ForceSensingButton",
194 0x1a00 "PresenterControl",
195 0x1a01 "Sensor3D",
196 0x1b00 "ReprogControls",
197 0x1b01 "ReprogControls2",
198 0x1b02 "ReprogControls3",
199 0x1b03 "ReprogControls4",
200 0x1b04 "ReprogControls5" => ReprogControlsFeature,
201 0x1bc0 "ReportHidUsages",
202 0x1c00 "PersistentRemappableAction" => PersistentRemappableActionFeature,
203 0x1d4b "WirelessDeviceStatus" => WirelessDeviceStatusFeature,
204 0x1df0 "RemainingPairings",
205 0x1f1f "FirmwareProperties",
206 0x1f20 "AdcMeasurement" => AdcMeasurementFeature,
207 0x2001 "SwapLeftRightButton",
208 0x2005 "ButtonSwapCancel",
209 0x2006 "PointerAxesOrientation",
210 0x2100 "VerticalScrolling" => VerticalScrollingFeature,
211 0x2110 "SmartShiftWheel" => SmartShiftFeature,
212 0x2111 "SmartShiftWheelEnhanced" => SmartShiftEnhancedFeature,
213 0x2120 "HighResolutionScrolling",
214 0x2121 "HiResWheel" => HiResWheelFeature,
215 0x2130 "RatchetWheel",
216 0x2150 "Thumbwheel" => ThumbwheelFeature,
217 0x2200 "MousePointer" => MousePointerFeature,
218 0x2201 "AdjustableDpi" => AdjustableDpiFeature,
219 0x2202 "ExtendedAdjustableDpi" => ExtendedDpiFeature,
220 0x2205 "PointerMotionScaling" => PointerMotionScalingFeature,
221 0x2230 "SensorAngleSnapping",
222 0x2240 "SurfaceTuning",
223 0x2250 "XyStats",
224 0x2251 "WheelStats",
225 0x2400 "HybridTrackingEngine",
226 0x40a0 "FnInversion",
227 0x40a2 "FnInversionWithDefaultState" => FnInversionWithDefaultStateFeature,
228 0x40a3 "FnInversionForMultiHostDevices" => FnInversionMultiHostFeature,
229 0x4100 "Encryption",
230 0x4220 "LockKeyState",
231 0x4301 "SolarKeyboardDashboard" => SolarDashboardFeature,
232 0x4520 "KeyboardLayout",
233 0x4521 "DisableKeys" => DisableKeysFeature,
234 0x4522 "DisableKeysByUsage" => DisableKeysByUsageFeature,
235 0x4530 "DualPlatform" => DualPlatformFeature,
236 0x4531 "MultiPlatform" => MultiPlatformFeature,
237 0x4540 "KeyboardInternationalLayouts",
238 0x4600 "Crown" => CrownFeature,
239 0x6010 "TouchpadFwItems",
240 0x6011 "TouchpadSwItems",
241 0x6012 "TouchpadWin8FwItems",
242 0x6020 "TapEnable",
243 0x6021 "TapEnableExtended",
244 0x6030 "CursorBallistic",
245 0x6040 "TouchpadResolutionDivider",
246 0x6100 "TouchpadRawXy" => TouchpadRawXyFeature,
247 0x6110 "TouchMouseRawTouchPoints" => TouchMouseRawFeature,
248 0x6120 "BtTouchMouseSettings",
249 0x6500 "Gestures1",
250 0x6501 "Gestures2" => Gestures2Feature,
251 0x8010 "GamingGKeys",
252 0x8020 "GamingMKeys",
253 0x8030 "MacroRecord",
254 0x8040 "BrightnessControl" => BrightnessControlFeature,
255 0x8060 "AdjustableReportRate" => ReportRateFeature,
256 0x8061 "ExtendedAdjustableReportRate" => ExtendedReportRateFeature,
257 0x8070 "ColorLedEffects" => ColorLedEffectsFeature,
258 0x8071 "RgbEffects" => RgbEffectsFeature,
259 0x8080 "PerKeyLighting",
260 0x8081 "PerKeyLighting2" => PerKeyLightingFeature,
261 0x8090 "ModeStatus" => ModeStatusFeature,
262 0x8100 "OnboardProfiles",
263 0x8110 "MouseButtonFilter",
264 0x8111 "LatencyMonitoring",
265 0x8120 "GamingAttachments",
266 0x8123 "ForceFeedback",
267 0x8300 "Sidetone" => SidetoneFeature,
268 0x8310 "Equalizer" => EqualizerFeature,
269 0x8320 "HeadsetOut",
270 }
271});
272
273#[cfg(test)]
274mod tests {
275 use std::collections::HashMap;
276
277 use super::{FeatureVersion, KnownFeature, new_dyn};
278 use crate::feature::{CreatableFeature, feature_set::FeatureSetFeature, root::RootFeature};
279
280 #[test]
281 fn macro_registers_one_version_per_listed_impl() {
282 let map: HashMap<u16, KnownFeature> = known_features! {
290 0x0002 "NameOnly",
291 0x0000 "OneImpl" => RootFeature,
292 0x0001 "TwoImpls" => FeatureSetFeature, FeatureSetFeature,
293 };
294
295 assert_eq!(map[&0x0002].versions.len(), 0);
296 assert_eq!(map[&0x0000].versions.len(), 1);
297 assert_eq!(map[&0x0001].versions.len(), 2);
298 }
299}