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, adjustable_dpi::AdjustableDpiFeature,
15 device_friendly_name::DeviceFriendlyNameFeature,
16 device_information::DeviceInformationFeature,
17 device_type_and_name::DeviceTypeAndNameFeature, feature_set::FeatureSetFeature,
18 hires_wheel::HiResWheelFeature, root::RootFeature, smartshift::SmartShiftFeature,
19 thumbwheel::ThumbwheelFeature, unified_battery::UnifiedBatteryFeature,
20 wireless_device_status::WirelessDeviceStatusFeature,
21 },
22};
23
24pub type FeatureImplProducer =
27 fn(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> (TypeId, Arc<dyn Feature>);
28
29#[derive(Clone, Copy, Debug, Hash)]
32pub struct FeatureVersion {
33 pub starting_version: u8,
35
36 pub producer: FeatureImplProducer,
38}
39
40#[derive(Clone, Copy, Debug, Hash)]
42pub struct KnownFeature {
43 pub name: &'static str,
47
48 pub versions: &'static [FeatureVersion],
51}
52
53pub fn lookup(feature_id: u16) -> Option<KnownFeature> {
55 KNOWN_FEATURES.get(&feature_id).copied()
56}
57
58pub fn lookup_version(feature_id: u16, feature_version: u8) -> Option<Vec<FeatureVersion>> {
61 lookup(feature_id).map(|feat| {
62 feat.versions
63 .iter()
64 .filter(|&ver| ver.starting_version <= feature_version)
65 .copied()
66 .collect::<Vec<FeatureVersion>>()
67 })
68}
69
70fn new_dyn<F: CreatableFeature>(
72 chan: Arc<HidppChannel>,
73 device_index: u8,
74 feature_index: u8,
75) -> (TypeId, Arc<dyn Feature>) {
76 (
77 TypeId::of::<F>(),
78 Arc::new(F::new(chan, device_index, feature_index)),
79 )
80}
81
82macro_rules! known_features {
88 ( $( $id:literal $name:literal $( => $($feat:ty),+ )? ),* $(,)? ) => {
89 HashMap::from([ $(
90 ($id, KnownFeature { name: $name, versions: known_features!(@versions $( $($feat),+ )?) }),
91 )* ])
92 };
93 (@versions) => { &[] };
94 (@versions $($feat:ty),+) => {
95 &[$(FeatureVersion {
96 starting_version: <$feat>::STARTING_VERSION,
97 producer: new_dyn::<$feat>,
98 }),+]
99 };
100}
101
102static KNOWN_FEATURES: LazyLock<HashMap<u16, KnownFeature>> = LazyLock::new(|| {
103 known_features! {
104 0x0000 "Root" => RootFeature,
105 0x0001 "FeatureSet" => FeatureSetFeature,
106 0x0002 "FeatureInfo",
107 0x0003 "DeviceInformation" => DeviceInformationFeature,
108 0x0004 "UnitId",
109 0x0005 "DeviceTypeAndName" => DeviceTypeAndNameFeature,
110 0x0006 "DeviceGroups",
111 0x0007 "DeviceFriendlyName" => DeviceFriendlyNameFeature,
112 0x0008 "KeepAlive",
113 0x0020 "ConfigChange",
114 0x0021 "UniqueRandomId",
115 0x0030 "TargetSoftware",
116 0x0080 "WirelessSignalStrength",
117 0x00c0 "DfuControlLegacy",
118 0x00c1 "DfuControlUnsigned",
119 0x00c2 "DfuControlSigned",
120 0x00c3 "DfuControlBolt",
121 0x00d0 "Dfu",
122 0x00d1 "DfuResumable",
123 0x1000 "BatteryStatus",
124 0x1001 "BatteryVoltage",
125 0x1004 "UnifiedBattery" => UnifiedBatteryFeature,
126 0x1010 "ChargingControl",
127 0x1300 "LedControl",
128 0x1800 "GenericTest",
129 0x1802 "DeviceReset",
130 0x1805 "OobState",
131 0x1806 "ConfigDeviceProps",
132 0x1814 "ChangeHost",
133 0x1815 "HostsInfo",
134 0x1981 "Backlight1",
135 0x1982 "Backlight2",
136 0x1983 "Backlight3",
137 0x1990 "Illumination",
138 0x19b0 "HapticFeedback",
139 0x19c0 "ForceSensingButton",
140 0x1a00 "PresenterControl",
141 0x1a01 "Sensor3D",
142 0x1b00 "ReprogControls",
143 0x1b01 "ReprogControls2",
144 0x1b02 "ReprogControls3",
145 0x1b03 "ReprogControls4",
146 0x1b04 "ReprogControls5",
147 0x1bc0 "ReportHidUsages",
148 0x1c00 "PersistentRemappableAction",
149 0x1d4b "WirelessDeviceStatus" => WirelessDeviceStatusFeature,
150 0x1df0 "RemainingPairings",
151 0x1f1f "FirmwareProperties",
152 0x1f20 "AdcMeasurement",
153 0x2001 "SwapLeftRightButton",
154 0x2005 "ButtonSwapCancel",
155 0x2006 "PointerAxesOrientation",
156 0x2100 "VerticalScrolling",
157 0x2110 "SmartShiftWheel" => SmartShiftFeature,
158 0x2111 "SmartShiftWheelEnhanced",
159 0x2120 "HighResolutionScrolling",
160 0x2121 "HiResWheel" => HiResWheelFeature,
161 0x2130 "RatchetWheel",
162 0x2150 "Thumbwheel" => ThumbwheelFeature,
163 0x2200 "MousePointer",
164 0x2201 "AdjustableDpi" => AdjustableDpiFeature,
165 0x2202 "ExtendedAdjustableDpi",
166 0x2205 "PointerMotionScaling",
167 0x2230 "SensorAngleSnapping",
168 0x2240 "SurfaceTuning",
169 0x2250 "XyStats",
170 0x2251 "WheelStats",
171 0x2400 "HybridTrackingEngine",
172 0x40a0 "FnInversion",
173 0x40a2 "FnInversionWithDefaultState",
174 0x40a3 "FnInversionForMultiHostDevices",
175 0x4100 "Encryption",
176 0x4220 "LockKeyState",
177 0x4301 "SolarKeyboardDashboard",
178 0x4520 "KeyboardLayout",
179 0x4521 "DisableKeys",
180 0x4522 "DisableKeysByUsage",
181 0x4530 "DualPlatform",
182 0x4531 "MultiPlatform",
183 0x4540 "KeyboardInternationalLayouts",
184 0x4600 "Crown",
185 0x6010 "TouchpadFwItems",
186 0x6011 "TouchpadSwItems",
187 0x6012 "TouchpadWin8FwItems",
188 0x6020 "TapEnable",
189 0x6021 "TapEnableExtended",
190 0x6030 "CursorBallistic",
191 0x6040 "TouchpadResolutionDivider",
192 0x6100 "TouchpadRawXy",
193 0x6110 "TouchMouseRawTouchPoints",
194 0x6120 "BtTouchMouseSettings",
195 0x6500 "Gestures1",
196 0x6501 "Gestures2",
197 0x8010 "GamingGKeys",
198 0x8020 "GamingMKeys",
199 0x8030 "MacroRecord",
200 0x8040 "BrightnessControl",
201 0x8060 "AdjustableReportRate",
202 0x8061 "ExtendedAdjustableReportRate",
203 0x8070 "ColorLedEffects",
204 0x8071 "RgbEffects",
205 0x8080 "PerKeyLighting",
206 0x8081 "PerKeyLighting2",
207 0x8090 "ModeStatus",
208 0x8100 "OnboardProfiles",
209 0x8110 "MouseButtonFilter",
210 0x8111 "LatencyMonitoring",
211 0x8120 "GamingAttachments",
212 0x8123 "ForceFeedback",
213 0x8300 "Sidetone",
214 0x8310 "Equalizer",
215 0x8320 "HeadsetOut",
216 }
217});
218
219#[cfg(test)]
220mod tests {
221 use std::collections::HashMap;
222
223 use super::{FeatureVersion, KnownFeature, new_dyn};
224 use crate::feature::{CreatableFeature, feature_set::FeatureSetFeature, root::RootFeature};
225
226 #[test]
227 fn macro_registers_one_version_per_listed_impl() {
228 let map: HashMap<u16, KnownFeature> = known_features! {
231 0x0000 "NameOnly",
232 0x0001 "OneImpl" => RootFeature,
233 0xffff "TwoImpls" => RootFeature, FeatureSetFeature,
234 };
235
236 assert_eq!(map[&0x0000].versions.len(), 0);
237 assert_eq!(map[&0x0001].versions.len(), 1);
238 assert_eq!(map[&0xffff].versions.len(), 2);
239 }
240}