diff --git a/RGB.NET.Devices.Logitech/HID/Extensions.cs b/RGB.NET.Devices.Logitech/HID/Extensions.cs index 4a0bf68..0f04602 100644 --- a/RGB.NET.Devices.Logitech/HID/Extensions.cs +++ b/RGB.NET.Devices.Logitech/HID/Extensions.cs @@ -3,27 +3,23 @@ using HidSharp.Reports.Encodings; using System; using System.Linq; using System.Runtime.InteropServices; -using System.Collections.Generic; namespace RGB.NET.Devices.Logitech.HID { - public static class Extensions + internal static class Extensions { - public static Span AsSpan(this ref T val) where T : unmanaged + internal static Span AsSpan(this ref T val) + where T : unmanaged { Span valSpan = MemoryMarshal.CreateSpan(ref val, 1); return MemoryMarshal.Cast(valSpan); } - public static uint GetUsagePage(this HidDevice device) + internal static uint GetUsagePage(this HidDevice device) { try { - byte[]? descriptor = device.GetRawReportDescriptor(); - IEnumerable? decodedItems = EncodedItem.DecodeItems(descriptor, 0, descriptor.Length); - IEnumerable? usefulItems = decodedItems.Where(de => de.TagForLocal == LocalItemTag.Usage && de.TagForGlobal == GlobalItemTag.UsagePage); - EncodedItem? usagePage = usefulItems.FirstOrDefault(de => de.ItemType == ItemType.Global); - return usagePage.DataValue; + return device.GetItemByType(ItemType.Global)?.DataValue ?? uint.MaxValue; } catch { @@ -31,20 +27,24 @@ namespace RGB.NET.Devices.Logitech.HID } } - public static uint GetUsage(this HidDevice device) + internal static uint GetUsage(this HidDevice device) { try { - byte[]? descriptor = device.GetRawReportDescriptor(); - IEnumerable? decodedItems = EncodedItem.DecodeItems(descriptor, 0, descriptor.Length); - IEnumerable? usefulItems = decodedItems.Where(de => de.TagForLocal == LocalItemTag.Usage && de.TagForGlobal == GlobalItemTag.UsagePage); - EncodedItem? usage = usefulItems.FirstOrDefault(de => de.ItemType == ItemType.Local); - return usage.DataValue; + return device.GetItemByType(ItemType.Local)?.DataValue ?? uint.MaxValue; } catch { return uint.MaxValue; } } + + private static EncodedItem? GetItemByType(this HidDevice device, ItemType itemType) + { + byte[] descriptor = device.GetRawReportDescriptor(); + return EncodedItem.DecodeItems(descriptor, 0, descriptor.Length) + .Where(de => (de.TagForLocal == LocalItemTag.Usage) && (de.TagForGlobal == GlobalItemTag.UsagePage)) + .FirstOrDefault(de => de.ItemType == itemType); + } } } diff --git a/RGB.NET.Devices.Logitech/HID/FapResponse.cs b/RGB.NET.Devices.Logitech/HID/FapResponse.cs index 010c67d..f0379a3 100644 --- a/RGB.NET.Devices.Logitech/HID/FapResponse.cs +++ b/RGB.NET.Devices.Logitech/HID/FapResponse.cs @@ -3,7 +3,7 @@ namespace RGB.NET.Devices.Logitech.HID { [StructLayout(LayoutKind.Sequential, Pack = 0, Size = 64)] - public struct FapResponse + internal struct FapResponse { public byte Command; public byte DeviceIndex; diff --git a/RGB.NET.Devices.Logitech/HID/FapShortRequest.cs b/RGB.NET.Devices.Logitech/HID/FapShortRequest.cs index c76f2b7..f7a687f 100644 --- a/RGB.NET.Devices.Logitech/HID/FapShortRequest.cs +++ b/RGB.NET.Devices.Logitech/HID/FapShortRequest.cs @@ -3,9 +3,15 @@ namespace RGB.NET.Devices.Logitech.HID { [StructLayout(LayoutKind.Sequential, Pack = 0, Size = 7)] - public struct FapShortRequest + internal struct FapShortRequest { - const byte LOGITECH_SHORT_MESSAGE = 0x10; + #region Constants + + private const byte LOGITECH_SHORT_MESSAGE = 0x10; + + #endregion + + #region Properties & Fields public byte ReportId; public byte DeviceIndex; @@ -15,15 +21,22 @@ namespace RGB.NET.Devices.Logitech.HID public byte Data1; public byte Data2; + #endregion + + #region Constructors + public void Init(byte deviceIndex, byte featureIndex) { + this.DeviceIndex = deviceIndex; + this.FeatureIndex = featureIndex; + ReportId = LOGITECH_SHORT_MESSAGE; - DeviceIndex = deviceIndex; - FeatureIndex = featureIndex; FeatureCommand = 0; Data0 = 0; Data1 = 0; Data2 = 0; } + + #endregion } } diff --git a/RGB.NET.Devices.Logitech/HID/LightspeedHidLoader.cs b/RGB.NET.Devices.Logitech/HID/LightspeedHidLoader.cs index 2edff17..d90b5ac 100644 --- a/RGB.NET.Devices.Logitech/HID/LightspeedHidLoader.cs +++ b/RGB.NET.Devices.Logitech/HID/LightspeedHidLoader.cs @@ -5,14 +5,32 @@ using System; using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace RGB.NET.Devices.Logitech.HID { + /// + /// Represents a loaded for logitech HID-devices. + /// + /// The type of the identifier leds are mapped to. + /// The type of the custom data added to the HID-device. public class LightspeedHIDLoader : IEnumerable> where TLed : notnull { + #region Constants + + private const int VENDOR_ID = 0x046D; + + // ReSharper disable once StaticMemberInGenericType - This is used like a const + private static readonly List RECEIVER_PIDS = new() + { + 0xC539, + 0xC53A, + 0xC541, + 0xC545 + }; + + #endregion + #region Properties & Fields private readonly Dictionary> _deviceDefinitions = new(); @@ -20,21 +38,13 @@ namespace RGB.NET.Devices.Logitech.HID /// /// Gets the vendor id used for this loader. /// - public int VendorId => 0x046d; + public int VendorId => VENDOR_ID; /// /// Gets or sets the filter used to determine which devices should be loaded. /// public RGBDeviceType LoadFilter { get; set; } = RGBDeviceType.All; - private static List ReceiverPids { get; } = new() - { - 0xC539, - 0xC53A, - 0xC541, - 0xC545 - }; - #endregion #region Methods @@ -72,7 +82,7 @@ namespace RGB.NET.Devices.Logitech.HID /// The function grouping the devices. /// The enumerable containing the selected devices. public IEnumerable> GetConnectedDevices(Func, TKey> groupBy) - => GetConnectedDevices().GroupBy(x => groupBy(x)) + => GetConnectedDevices().GroupBy(groupBy) .Select(group => group.First()); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); @@ -84,36 +94,20 @@ namespace RGB.NET.Devices.Logitech.HID #region Private Methods - private IEnumerable Detect() - { - foreach (int receiverPid in ReceiverPids) - { - foreach (int wirelessPid in Detect(receiverPid)) - { - yield return wirelessPid; - } - } - } + private IEnumerable Detect() => RECEIVER_PIDS.SelectMany(Detect); private IEnumerable Detect(int pid) { - IEnumerable? receiverDevices = DeviceList.Local.GetHidDevices(VendorId, pid); - IEnumerable? interfaceTwo = receiverDevices.Where(d => d.DevicePath.Contains("mi_02")); - //this is terrible but i don't know how else to filter interfaces + Dictionary deviceUsages = DeviceList.Local + .GetHidDevices(VendorId, pid) + .Where(d => d.DevicePath.Contains("mi_02")) + .ToDictionary(x => (byte)x.GetUsage(), x => x); - Dictionary deviceUsages = new(); - foreach (HidDevice? item in interfaceTwo) - { - deviceUsages.Add((byte)item.GetUsage(), item); - } - - foreach ((int wirelessPid, byte deviceIndex) in GetWirelessDevices(deviceUsages)) - { + foreach ((int wirelessPid, byte _) in GetWirelessDevices(deviceUsages)) yield return wirelessPid; - } } - private Dictionary GetWirelessDevices(Dictionary device_usages) + private Dictionary GetWirelessDevices(IReadOnlyDictionary deviceUsages) { const byte LOGITECH_RECEIVER_ADDRESS = 0xFF; const byte LOGITECH_SET_REGISTER_REQUEST = 0x80; @@ -121,20 +115,20 @@ namespace RGB.NET.Devices.Logitech.HID Dictionary map = new(); - if (device_usages.TryGetValue(1, out HidDevice? device)) + if (deviceUsages.TryGetValue(1, out HidDevice? device)) { HidStream? stream = device.Open(); - FapResponse response = new FapResponse(); + FapResponse response = new(); - FapShortRequest getConnectedDevices = new FapShortRequest(); + FapShortRequest getConnectedDevices = new(); getConnectedDevices.Init(LOGITECH_RECEIVER_ADDRESS, LOGITECH_GET_REGISTER_REQUEST); stream.Write(getConnectedDevices.AsSpan()); stream.Read(response.AsSpan()); - bool wireless_notifications = (response.Data01 & 1) == 1; - if (!wireless_notifications) + bool wirelessNotifications = (response.Data01 & 1) == 1; + if (!wirelessNotifications) { response = new FapResponse(); @@ -163,8 +157,7 @@ namespace RGB.NET.Devices.Logitech.HID { //log "Faking a reconnect to get device list" deviceCount++; - - response = new FapResponse(); + getConnectedDevices.Init(LOGITECH_RECEIVER_ADDRESS, LOGITECH_SET_REGISTER_REQUEST); getConnectedDevices.FeatureCommand = 0x02; getConnectedDevices.Data0 = 0x02; @@ -172,13 +165,11 @@ namespace RGB.NET.Devices.Logitech.HID for (int i = 0; i < deviceCount; i++) { - FapResponse devices = new FapResponse(); + FapResponse devices = new(); stream.Read(devices.AsSpan()); int wirelessPid = (devices.Data02 << 8) | devices.Data01; if (devices.DeviceIndex != 0xff) - { map.Add(wirelessPid, devices.DeviceIndex); - } } } } diff --git a/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs b/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs index 1dc0cc4..590911b 100644 --- a/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs +++ b/RGB.NET.Devices.Logitech/LogitechDeviceProvider.cs @@ -131,7 +131,7 @@ namespace RGB.NET.Devices.Logitech }; /// - /// Gets the HID-definitions for per-device-devices. + /// Gets the HID-definitions for wired per-device-devices. /// public static HIDLoader PerDeviceDeviceDefinitions { get; } = new(VENDOR_ID) { @@ -149,12 +149,12 @@ namespace RGB.NET.Devices.Logitech { 0xC225, RGBDeviceType.Keyboard, "G11", LedMappings.Device, 0 }, }; + /// + /// Gets the HID-definitions for wireless per-device-devices. + /// public static LightspeedHIDLoader PerDeviceWirelessDeviceDefinitions { get; } = new() - { - - }; - - + { }; + #endregion #region Constructors @@ -198,28 +198,59 @@ namespace RGB.NET.Devices.Logitech /// protected override IEnumerable LoadDevices() { - HIDDeviceDefinition? perKeyDeviceDefinition = PerKeyDeviceDefinitions.GetConnectedDevices().FirstOrDefault().definition ?? - PerKeyWirelessDeviceDefinitions.GetConnectedDevices().FirstOrDefault(); - if ((_perKeyUpdateQueue != null) && (perKeyDeviceDefinition != default)) + #region PerKey + + if (_perKeyUpdateQueue != null) { - yield return new LogitechPerKeyRGBDevice(new LogitechRGBDeviceInfo(perKeyDeviceDefinition.DeviceType, perKeyDeviceDefinition.Name, LogitechDeviceCaps.PerKeyRGB, 0), _perKeyUpdateQueue, perKeyDeviceDefinition.LedMapping); + (HIDDeviceDefinition definition, HidDevice device) perKeyDevice = PerKeyDeviceDefinitions.GetConnectedDevices().FirstOrDefault(); + if (perKeyDevice != default) + { + (HIDDeviceDefinition definition, _) = perKeyDevice; + yield return new LogitechPerKeyRGBDevice(new LogitechRGBDeviceInfo(definition.DeviceType, definition.Name, LogitechDeviceCaps.PerKeyRGB, 0), _perKeyUpdateQueue, definition.LedMapping); + } + else + { + HIDDeviceDefinition? perKeyWirelessDevice = PerKeyWirelessDeviceDefinitions.GetConnectedDevices().FirstOrDefault(); + if (perKeyWirelessDevice != null) + yield return new LogitechPerKeyRGBDevice(new LogitechRGBDeviceInfo(perKeyWirelessDevice.DeviceType, perKeyWirelessDevice.Name, LogitechDeviceCaps.PerKeyRGB, 0), _perKeyUpdateQueue, perKeyWirelessDevice.LedMapping); + } } + #endregion + + #region PerZone + IEnumerable> wiredPerZoneDevices = PerZoneDeviceDefinitions.GetConnectedDevices().Select(x => x.definition); IEnumerable> wirelessPerZoneDevices = PerZoneWirelessDeviceDefinitions.GetConnectedDevices(); - IEnumerable>? wiredAndWirelessPerZoneDevices = wiredPerZoneDevices.Concat(wirelessPerZoneDevices); - foreach (HIDDeviceDefinition definition in wiredAndWirelessPerZoneDevices.GroupBy(x => x.CustomData.deviceType).Select(group => group.First())) + foreach (HIDDeviceDefinition definition in wiredPerZoneDevices.Concat(wirelessPerZoneDevices) + .GroupBy(x => x.CustomData.deviceType) + .Select(group => group.First())) { LogitechZoneUpdateQueue updateQueue = new(GetUpdateTrigger(), definition.CustomData.deviceType); yield return new LogitechZoneRGBDevice(new LogitechRGBDeviceInfo(definition.DeviceType, definition.Name, LogitechDeviceCaps.DeviceRGB, definition.CustomData.zones), updateQueue, definition.LedMapping); } - HIDDeviceDefinition? perDeviceDeviceDefinition = PerDeviceDeviceDefinitions.GetConnectedDevices().FirstOrDefault().definition ?? - PerDeviceWirelessDeviceDefinitions.GetConnectedDevices().FirstOrDefault(); - if ((_perDeviceUpdateQueue != null) && (perDeviceDeviceDefinition != default)) + #endregion + + #region PerDevice + + if (_perDeviceUpdateQueue != null) { - yield return new LogitechPerDeviceRGBDevice(new LogitechRGBDeviceInfo(perDeviceDeviceDefinition.DeviceType, perDeviceDeviceDefinition.Name, LogitechDeviceCaps.DeviceRGB, 0), _perDeviceUpdateQueue, perDeviceDeviceDefinition.LedMapping); + (HIDDeviceDefinition definition, HidDevice device) perDeviceDevice = PerDeviceDeviceDefinitions.GetConnectedDevices().FirstOrDefault(); + if (perDeviceDevice != default) + { + (HIDDeviceDefinition definition, _) = perDeviceDevice; + yield return new LogitechPerDeviceRGBDevice(new LogitechRGBDeviceInfo(definition.DeviceType, definition.Name, LogitechDeviceCaps.DeviceRGB, 0), _perDeviceUpdateQueue, definition.LedMapping); + } + else + { + HIDDeviceDefinition? perDeviceWirelessDevice = PerDeviceWirelessDeviceDefinitions.GetConnectedDevices().FirstOrDefault(); + if (perDeviceWirelessDevice != null) + yield return new LogitechPerDeviceRGBDevice(new LogitechRGBDeviceInfo(perDeviceWirelessDevice.DeviceType, perDeviceWirelessDevice.Name, LogitechDeviceCaps.DeviceRGB, 0), _perDeviceUpdateQueue, perDeviceWirelessDevice.LedMapping); + } } + + #endregion } ///