use std::{
any::TypeId,
collections::HashMap,
sync::{Arc, LazyLock},
};
use super::Feature;
use crate::{
channel::HidppChannel,
feature::{
CreatableFeature, adjustable_dpi::AdjustableDpiFeature,
device_friendly_name::DeviceFriendlyNameFeature,
device_information::DeviceInformationFeature,
device_type_and_name::DeviceTypeAndNameFeature, feature_set::FeatureSetFeature,
hires_wheel::HiResWheelFeature, root::RootFeature, smartshift::SmartShiftFeature,
thumbwheel::ThumbwheelFeature, unified_battery::UnifiedBatteryFeature,
wireless_device_status::WirelessDeviceStatusFeature,
},
};
pub type FeatureImplProducer =
fn(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> (TypeId, Arc<dyn Feature>);
#[derive(Clone, Copy, Debug, Hash)]
pub struct FeatureVersion {
pub starting_version: u8,
pub producer: FeatureImplProducer,
}
#[derive(Clone, Copy, Debug, Hash)]
pub struct KnownFeature {
pub name: &'static str,
pub versions: &'static [FeatureVersion],
}
pub fn lookup(feature_id: u16) -> Option<KnownFeature> {
KNOWN_FEATURES.get(&feature_id).copied()
}
pub fn lookup_version(feature_id: u16, feature_version: u8) -> Option<Vec<FeatureVersion>> {
lookup(feature_id).map(|feat| {
feat.versions
.iter()
.filter(|&ver| ver.starting_version <= feature_version)
.copied()
.collect::<Vec<FeatureVersion>>()
})
}
fn new_dyn<F: CreatableFeature>(
chan: Arc<HidppChannel>,
device_index: u8,
feature_index: u8,
) -> (TypeId, Arc<dyn Feature>) {
(
TypeId::of::<F>(),
Arc::new(F::new(chan, device_index, feature_index)),
)
}
macro_rules! known_features {
( $( $id:literal $name:literal $( => $($feat:ty),+ )? ),* $(,)? ) => {
HashMap::from([ $(
($id, KnownFeature { name: $name, versions: known_features!(@versions $( $($feat),+ )?) }),
)* ])
};
(@versions) => { &[] };
(@versions $($feat:ty),+) => {
&[$(FeatureVersion {
starting_version: <$feat>::STARTING_VERSION,
producer: new_dyn::<$feat>,
}),+]
};
}
static KNOWN_FEATURES: LazyLock<HashMap<u16, KnownFeature>> = LazyLock::new(|| {
known_features! {
0x0000 "Root" => RootFeature,
0x0001 "FeatureSet" => FeatureSetFeature,
0x0002 "FeatureInfo",
0x0003 "DeviceInformation" => DeviceInformationFeature,
0x0004 "UnitId",
0x0005 "DeviceTypeAndName" => DeviceTypeAndNameFeature,
0x0006 "DeviceGroups",
0x0007 "DeviceFriendlyName" => DeviceFriendlyNameFeature,
0x0008 "KeepAlive",
0x0020 "ConfigChange",
0x0021 "UniqueRandomId",
0x0030 "TargetSoftware",
0x0080 "WirelessSignalStrength",
0x00c0 "DfuControlLegacy",
0x00c1 "DfuControlUnsigned",
0x00c2 "DfuControlSigned",
0x00c3 "DfuControlBolt",
0x00d0 "Dfu",
0x00d1 "DfuResumable",
0x1000 "BatteryStatus",
0x1001 "BatteryVoltage",
0x1004 "UnifiedBattery" => UnifiedBatteryFeature,
0x1010 "ChargingControl",
0x1300 "LedControl",
0x1800 "GenericTest",
0x1802 "DeviceReset",
0x1805 "OobState",
0x1806 "ConfigDeviceProps",
0x1814 "ChangeHost",
0x1815 "HostsInfo",
0x1981 "Backlight1",
0x1982 "Backlight2",
0x1983 "Backlight3",
0x1990 "Illumination",
0x19b0 "HapticFeedback",
0x19c0 "ForceSensingButton",
0x1a00 "PresenterControl",
0x1a01 "Sensor3D",
0x1b00 "ReprogControls",
0x1b01 "ReprogControls2",
0x1b02 "ReprogControls3",
0x1b03 "ReprogControls4",
0x1b04 "ReprogControls5",
0x1bc0 "ReportHidUsages",
0x1c00 "PersistentRemappableAction",
0x1d4b "WirelessDeviceStatus" => WirelessDeviceStatusFeature,
0x1df0 "RemainingPairings",
0x1f1f "FirmwareProperties",
0x1f20 "AdcMeasurement",
0x2001 "SwapLeftRightButton",
0x2005 "ButtonSwapCancel",
0x2006 "PointerAxesOrientation",
0x2100 "VerticalScrolling",
0x2110 "SmartShiftWheel" => SmartShiftFeature,
0x2111 "SmartShiftWheelEnhanced",
0x2120 "HighResolutionScrolling",
0x2121 "HiResWheel" => HiResWheelFeature,
0x2130 "RatchetWheel",
0x2150 "Thumbwheel" => ThumbwheelFeature,
0x2200 "MousePointer",
0x2201 "AdjustableDpi" => AdjustableDpiFeature,
0x2202 "ExtendedAdjustableDpi",
0x2205 "PointerMotionScaling",
0x2230 "SensorAngleSnapping",
0x2240 "SurfaceTuning",
0x2250 "XyStats",
0x2251 "WheelStats",
0x2400 "HybridTrackingEngine",
0x40a0 "FnInversion",
0x40a2 "FnInversionWithDefaultState",
0x40a3 "FnInversionForMultiHostDevices",
0x4100 "Encryption",
0x4220 "LockKeyState",
0x4301 "SolarKeyboardDashboard",
0x4520 "KeyboardLayout",
0x4521 "DisableKeys",
0x4522 "DisableKeysByUsage",
0x4530 "DualPlatform",
0x4531 "MultiPlatform",
0x4540 "KeyboardInternationalLayouts",
0x4600 "Crown",
0x6010 "TouchpadFwItems",
0x6011 "TouchpadSwItems",
0x6012 "TouchpadWin8FwItems",
0x6020 "TapEnable",
0x6021 "TapEnableExtended",
0x6030 "CursorBallistic",
0x6040 "TouchpadResolutionDivider",
0x6100 "TouchpadRawXy",
0x6110 "TouchMouseRawTouchPoints",
0x6120 "BtTouchMouseSettings",
0x6500 "Gestures1",
0x6501 "Gestures2",
0x8010 "GamingGKeys",
0x8020 "GamingMKeys",
0x8030 "MacroRecord",
0x8040 "BrightnessControl",
0x8060 "AdjustableReportRate",
0x8061 "ExtendedAdjustableReportRate",
0x8070 "ColorLedEffects",
0x8071 "RgbEffects",
0x8080 "PerKeyLighting",
0x8081 "PerKeyLighting2",
0x8090 "ModeStatus",
0x8100 "OnboardProfiles",
0x8110 "MouseButtonFilter",
0x8111 "LatencyMonitoring",
0x8120 "GamingAttachments",
0x8123 "ForceFeedback",
0x8300 "Sidetone",
0x8310 "Equalizer",
0x8320 "HeadsetOut",
}
});
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::{FeatureVersion, KnownFeature, new_dyn};
use crate::feature::{CreatableFeature, feature_set::FeatureSetFeature, root::RootFeature};
#[test]
fn macro_registers_one_version_per_listed_impl() {
let map: HashMap<u16, KnownFeature> = known_features! {
0x0000 "NameOnly",
0x0001 "OneImpl" => RootFeature,
0xffff "TwoImpls" => RootFeature, FeatureSetFeature,
};
assert_eq!(map[&0x0000].versions.len(), 0);
assert_eq!(map[&0x0001].versions.len(), 1);
assert_eq!(map[&0xffff].versions.len(), 2);
}
}