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