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