From 2ef7c446eaaabfc03bc2b5f8b0b546b647a38fe4 Mon Sep 17 00:00:00 2001 From: Lesueur Benjamin Date: Wed, 1 Nov 2023 14:03:26 +0100 Subject: [PATCH] Improve ROGAlly OEM buttons support (#54) * tentative * improve ROGAlly OEM buttons reading --- HandheldCompanion/Devices/ASUS/ROGAlly.cs | 77 ++++++++++++++++----- HandheldCompanion/HandheldCompanion.csproj | 4 +- Resources/HidLibrary.dll | Bin 0 -> 42496 bytes 3 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 Resources/HidLibrary.dll diff --git a/HandheldCompanion/Devices/ASUS/ROGAlly.cs b/HandheldCompanion/Devices/ASUS/ROGAlly.cs index 261073e36..67e0f1278 100644 --- a/HandheldCompanion/Devices/ASUS/ROGAlly.cs +++ b/HandheldCompanion/Devices/ASUS/ROGAlly.cs @@ -7,13 +7,13 @@ using System; using System.Collections.Generic; using System.Windows.Media; -using System.Linq; using System.Management; using System.Numerics; using System.Text; using WindowsInput.Events; using Task = System.Threading.Tasks.Task; using static HandheldCompanion.Utils.DeviceUtils; +using System.Threading.Tasks; namespace HandheldCompanion.Devices; @@ -22,13 +22,11 @@ public class ROGAlly : IDevice private readonly Dictionary keyMapping = new() { { 0, ButtonFlags.None }, - { 56, ButtonFlags.OEM2 }, - { 162, ButtonFlags.None }, { 166, ButtonFlags.OEM1 }, + { 56, ButtonFlags.OEM2 }, { 165, ButtonFlags.OEM3 }, { 167, ButtonFlags.OEM4 }, { 168, ButtonFlags.OEM4 }, - { 236, ButtonFlags.None } }; private HidDevice hidDevice; @@ -47,7 +45,7 @@ public class ROGAlly : IDevice static byte[] MESSAGE_APPLY = { AURA_HID_ID, 0xb4 }; static byte[] MESSAGE_SET = { AURA_HID_ID, 0xb5, 0, 0, 0 }; - public override bool IsOpen => hidDevice is not null && asusACPI is not null && asusACPI.IsOpen(); + public override bool IsOpen => hidDevice is not null && hidDevice.IsOpen && asusACPI is not null && asusACPI.IsOpen(); private enum AuraMode { @@ -167,15 +165,13 @@ public override bool Open() if (!success) return false; - if (hidDevice is not null) - hidDevice.OpenDevice(); - // try open asus ACPI asusACPI = new AsusACPI(); if (asusACPI is null) return false; - asusACPI.SubscribeToEvents(WatcherEventArrived); + // deprecated + // asusACPI.SubscribeToEvents(WatcherEventArrived); return true; } @@ -203,20 +199,58 @@ public void WatcherEventArrived(object sender, EventArrivedEventArgs e) public override bool IsReady() { - hidDevice = GetHidDevices(_vid, _pid).FirstOrDefault(); + IEnumerable devices = GetHidDevices(_vid, _pid); + foreach (HidDevice device in devices) + { + if (!device.IsConnected) + continue; + + try + { + device.OpenDevice(); + device.MonitorDeviceEvents = true; + } + catch + { + continue; + } + + Task ReportDevice = Task.Run(async () => await device.ReadReportAsync()); + ReportDevice.ContinueWith(t => OnReport(ReportDevice.Result, device)); + + hidDevice = device; + } + if (hidDevice is null) return false; - var pnpDevice = PnPDevice.GetDeviceByInterfaceId(hidDevice.DevicePath); - var device_parent = pnpDevice.GetProperty(DevicePropertyKey.Device_Parent); + PnPDevice pnpDevice = PnPDevice.GetDeviceByInterfaceId(hidDevice.DevicePath); + string device_parent = pnpDevice.GetProperty(DevicePropertyKey.Device_Parent); - var pnpParent = PnPDevice.GetDeviceByInstanceId(device_parent); - var parent_guid = pnpParent.GetProperty(DevicePropertyKey.Device_ClassGuid); - var parent_instanceId = pnpParent.GetProperty(DevicePropertyKey.Device_InstanceId); + PnPDevice pnpParent = PnPDevice.GetDeviceByInstanceId(device_parent); + Guid parent_guid = pnpParent.GetProperty(DevicePropertyKey.Device_ClassGuid); + string parent_instanceId = pnpParent.GetProperty(DevicePropertyKey.Device_InstanceId); return DeviceHelper.IsDeviceAvailable(parent_guid, parent_instanceId); } + private void OnReport(HidReport result, HidDevice device) + { + Task ReportDevice = Task.Run(async () => await device.ReadReportAsync()); + ReportDevice.ContinueWith(t => OnReport(ReportDevice.Result, device)); + + switch(result.ReportId) + { + case 90: + { + // get key + byte key = result.Data[0]; + HandleEvent(key); + } + break; + } + } + public override void SetFanControl(bool enable, int mode = 0) { if (!IsOpen) @@ -268,10 +302,16 @@ private void HandleEvent(byte key) return; // get button - var button = keyMapping[key]; - + ButtonFlags button = keyMapping[key]; switch (key) { + case 0: // Back paddles: Release + { + KeyRelease(ButtonFlags.OEM3); + } + return; + + case 165: // Back paddles: Press case 167: // Armory crate: Hold KeyPress(button); break; @@ -282,10 +322,9 @@ private void HandleEvent(byte key) default: case 56: // Armory crate: Click - case 165: // Back paddles: Click case 166: // Command center: Click { - Task.Run(async () => + Task.Factory.StartNew(async () => { KeyPress(button); await Task.Delay(KeyPressDelay); diff --git a/HandheldCompanion/HandheldCompanion.csproj b/HandheldCompanion/HandheldCompanion.csproj index 7169bf88b..dfa4e4c6f 100644 --- a/HandheldCompanion/HandheldCompanion.csproj +++ b/HandheldCompanion/HandheldCompanion.csproj @@ -139,7 +139,6 @@ - @@ -2270,6 +2269,9 @@ ..\Resources\Gma.System.MouseKeyHook.dll + + ..\Resources\HidLibrary.dll + ..\Resources\Inkore.UI.WPF.Modern.dll diff --git a/Resources/HidLibrary.dll b/Resources/HidLibrary.dll new file mode 100644 index 0000000000000000000000000000000000000000..e59bb1e3219a1af116d3aedffda0e104feeea710 GIT binary patch literal 42496 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