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