diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 0a22a429f..62317f6d8 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -2,7 +2,7 @@
name: Bug report
about: Create a report to help us improve
title: ''
-labels: bug
+labels: Bug
---
@@ -12,13 +12,15 @@ labels: bug
- [ ] AOKZOE
- [ ] ASUS
- [ ] GPD
+- [ ] LENOVO
+- [ ] MSI
- [ ] ONEXPLAYER
- [ ] VALVE
**Device model**
Your device model
-**Handheld Companinion Version**
+**Handheld Companion Version**
0.X.X.X
**Describe the bug**
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index 11fc491ef..9843a1d8b 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -2,7 +2,7 @@
name: Feature request
about: Suggest an idea for this project
title: ''
-labels: enhancement
+labels: Enhancement
assignees: ''
---
diff --git a/HandheldCompanion.iss b/HandheldCompanion.iss
index d2fb42c0b..0743ce7b2 100644
--- a/HandheldCompanion.iss
+++ b/HandheldCompanion.iss
@@ -482,7 +482,7 @@ begin
if not(keepHidhideCheckbox.Checked) then
begin
- if(ShellExec('', 'msiexec.exe', '/X{50D7EB6D-6A4A-4A38-B09C-CC28F75F082E} /L*V "C:\HidHide-Uninstall.log"', '', SW_SHOW, ewWaitUntilTerminated, resultCode)) then
+ if(ShellExec('', 'msiexec.exe', '/X{41DC2CF5-D952-4EC5-B90B-136E59430EA0} /L*V "C:\HidHide-Uninstall.log"', '', SW_SHOW, ewWaitUntilTerminated, resultCode)) then
begin
log('Successfully executed Hidhide uninstaller');
if(resultCode = 0) then
diff --git a/HandheldCompanion/ADLX/ADLXBackend.cs b/HandheldCompanion/ADLX/ADLXBackend.cs
index a3702e47e..b0353e79e 100644
--- a/HandheldCompanion/ADLX/ADLXBackend.cs
+++ b/HandheldCompanion/ADLX/ADLXBackend.cs
@@ -1,4 +1,5 @@
using System.Runtime.InteropServices;
+using System.Text;
namespace HandheldCompanion.ADLX
{
@@ -50,9 +51,39 @@ public struct AdlxTelemetryData
public double gpuTotalBoardPowerValue;
}
+ public enum ADLX_RESULT
+ {
+ ADLX_OK = 0, /**< @ENG_START_DOX This result indicates success. @ENG_END_DOX */
+ ADLX_ALREADY_ENABLED, /**< @ENG_START_DOX This result indicates that the asked action is already enabled. @ENG_END_DOX */
+ ADLX_ALREADY_INITIALIZED, /**< @ENG_START_DOX This result indicates that ADLX has a unspecified type of initialization. @ENG_END_DOX */
+ ADLX_FAIL, /**< @ENG_START_DOX This result indicates an unspecified failure. @ENG_END_DOX */
+ ADLX_INVALID_ARGS, /**< @ENG_START_DOX This result indicates that the arguments are invalid. @ENG_END_DOX */
+ ADLX_BAD_VER, /**< @ENG_START_DOX This result indicates that the asked version is incompatible with the current version. @ENG_END_DOX */
+ ADLX_UNKNOWN_INTERFACE, /**< @ENG_START_DOX This result indicates that an unknown interface was asked. @ENG_END_DOX */
+ ADLX_TERMINATED, /**< @ENG_START_DOX This result indicates that the calls were made in an interface after ADLX was terminated. @ENG_END_DOX */
+ ADLX_ADL_INIT_ERROR, /**< @ENG_START_DOX This result indicates that the ADL initialization failed. @ENG_END_DOX */
+ ADLX_NOT_FOUND, /**< @ENG_START_DOX This result indicates that the item is not found. @ENG_END_DOX */
+ ADLX_INVALID_OBJECT, /**< @ENG_START_DOX This result indicates that the method was called into an invalid object. @ENG_END_DOX */
+ ADLX_ORPHAN_OBJECTS, /**< @ENG_START_DOX This result indicates that ADLX was terminated with outstanding ADLX objects. Any interface obtained from ADLX points to invalid memory and calls in their methods will result in unexpected behavior. @ENG_END_DOX */
+ ADLX_NOT_SUPPORTED, /**< @ENG_START_DOX This result indicates that the asked feature is not supported. @ENG_END_DOX */
+ ADLX_PENDING_OPERATION, /**< @ENG_START_DOX This result indicates a failure due to an operation currently in progress. @ENG_END_DOX */
+ ADLX_GPU_INACTIVE /**< @ENG_START_DOX This result indicates that the GPU is inactive. @ENG_END_DOX */
+ }
+
[DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool IntializeAdlx();
[DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool CloseAdlx();
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern ADLX_RESULT GetNumberOfDisplays(ref int displayNum);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+ public static extern ADLX_RESULT GetDisplayName(int idx, StringBuilder dispName, int nameLength);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)]
+ public static extern ADLX_RESULT GetDisplayGPU(int idx, ref int UniqueId);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)]
+ public static extern ADLX_RESULT GetGPUIndex(int UniqueId, ref int idx);
+
[DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasRSRSupport();
[DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetRSR();
[DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetRSR(bool enable);
@@ -79,17 +110,17 @@ public struct AdlxTelemetryData
[DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetImageSharpeningSharpness(int GPU);
[DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetImageSharpeningSharpness(int GPU, int sharpness);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasIntegerScalingSupport(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetIntegerScaling(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetIntegerScaling(int GPU, bool enabled);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasIntegerScalingSupport(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetIntegerScaling(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetIntegerScaling(int displayIdx, bool enabled);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasGPUScalingSupport(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetGPUScaling(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetGPUScaling(int GPU, bool enabled);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasGPUScalingSupport(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetGPUScaling(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetGPUScaling(int displayIdx, bool enabled);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasScalingModeSupport(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetScalingMode(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetScalingMode(int GPU, int mode);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasScalingModeSupport(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetScalingMode(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetScalingMode(int displayIdx, int mode);
[DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetAdlxTelemetry(int GPU, ref AdlxTelemetryData adlxTelemetryData);
diff --git a/HandheldCompanion/ADLX_Wrapper.dll b/HandheldCompanion/ADLX_Wrapper.dll
index 9a61d0678..469cc74a4 100644
Binary files a/HandheldCompanion/ADLX_Wrapper.dll and b/HandheldCompanion/ADLX_Wrapper.dll differ
diff --git a/HandheldCompanion/Actions/GyroActions.cs b/HandheldCompanion/Actions/GyroActions.cs
index 66601f66a..7b75a9478 100644
--- a/HandheldCompanion/Actions/GyroActions.cs
+++ b/HandheldCompanion/Actions/GyroActions.cs
@@ -7,7 +7,7 @@ namespace HandheldCompanion.Actions
[Serializable]
public class GyroActions : IActions
{
- public MotionInput MotionInput = MotionInput.JoystickCamera;
+ public MotionInput MotionInput = MotionInput.LocalSpace;
public MotionMode MotionMode = MotionMode.Off;
public bool MotionToggleStatus = false;
public bool MotionTogglePressed = false; // for debouncing
diff --git a/HandheldCompanion/App.config b/HandheldCompanion/App.config
index a6c1d1472..ea88d8c68 100644
--- a/HandheldCompanion/App.config
+++ b/HandheldCompanion/App.config
@@ -11,276 +11,279 @@
-
- False
-
-
- False
-
-
- False
-
-
- False
-
-
- 720
-
-
- 1280
-
-
- 0
-
-
- 0
-
-
- 2
-
-
- 0
-
-
- 1989-12-20
-
-
- 0
-
-
- 7
-
-
- 300
-
-
- 1
-
-
- 0.25
-
-
- 250
-
-
- en-US
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- False
-
-
- -1
-
-
- 0
-
-
- 30
-
-
- False
-
-
- True
-
-
- 720
-
-
- False
-
-
- 0
-
-
- 0
-
-
- 1
-
-
- #00000000
-
-
- 0
-
-
- True
-
-
- 1
-
-
- False
-
-
- https://api.github.com/repos/Valkirie/HandheldCompanion
-
-
- True
-
-
- True
-
-
- 100
-
-
-
-
-
- True
-
-
- False
-
-
- True
-
-
- False
-
-
- False
-
-
- True
-
-
- 40
-
-
- False
-
-
- False
-
-
- True
-
-
- False
-
-
- False
-
-
- 0
-
-
- 1000
-
-
- -1
-
-
- True
-
-
- 3
-
-
- 1
-
-
- False
-
-
- True
-
-
- False
-
-
- 0
-
-
- 1
-
-
- True
-
-
- 26760
-
-
- 127.0.0.1
-
-
- 50
-
-
- #FFFFFF00
-
-
- False
-
-
- False
-
-
- 0
-
-
- True
-
-
- #FFFFFF00
-
-
- False
-
-
- 50
-
-
- False
-
-
- False
-
-
- 2
-
-
- 2
-
-
- 2
-
-
- 2
-
-
- 2
-
-
- 2
-
-
- 2
-
-
- 2
-
-
- 2
-
-
- Time,GPU,CPU,VRAM,RAM,BATT,FPS
-
-
- False
-
-
+
+ False
+
+
+ False
+
+
+ False
+
+
+ False
+
+
+ 720
+
+
+ 1280
+
+
+ 0
+
+
+ 0
+
+
+ 2
+
+
+ 0
+
+
+ 1989-12-20
+
+
+ 0
+
+
+ 7
+
+
+ 300
+
+
+ 1
+
+
+ 0.25
+
+
+ 250
+
+
+ en-US
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ False
+
+
+ -1
+
+
+ 0
+
+
+ 30
+
+
+ False
+
+
+ True
+
+
+ 720
+
+
+ False
+
+
+ 0
+
+
+ 0
+
+
+ 1
+
+
+ #00000000
+
+
+ 0
+
+
+ True
+
+
+ 1
+
+
+ False
+
+
+ https://api.github.com/repos/Valkirie/HandheldCompanion
+
+
+ True
+
+
+ True
+
+
+ 100
+
+
+
+
+
+ True
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ True
+
+
+ 40
+
+
+ False
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ False
+
+
+ 0
+
+
+ 1000
+
+
+ -1
+
+
+ True
+
+
+ 3
+
+
+ 1
+
+
+ False
+
+
+ True
+
+
+ False
+
+
+ 0
+
+
+ 1
+
+
+ True
+
+
+ 26760
+
+
+ 127.0.0.1
+
+
+ 50
+
+
+ #FFFFFF00
+
+
+ False
+
+
+ False
+
+
+ 0
+
+
+ True
+
+
+ #FFFFFF00
+
+
+ False
+
+
+ 50
+
+
+ False
+
+
+ False
+
+
+ 2
+
+
+ False
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ Time,GPU,CPU,VRAM,RAM,BATT,FPS
+
+
+ 0
+
+
\ No newline at end of file
diff --git a/HandheldCompanion/App.xaml b/HandheldCompanion/App.xaml
index 6e2074da8..e52ae8850 100644
--- a/HandheldCompanion/App.xaml
+++ b/HandheldCompanion/App.xaml
@@ -56,16 +56,20 @@
+
+
+
+
diff --git a/HandheldCompanion/App.xaml.cs b/HandheldCompanion/App.xaml.cs
index 8c54b7125..53a794076 100644
--- a/HandheldCompanion/App.xaml.cs
+++ b/HandheldCompanion/App.xaml.cs
@@ -45,20 +45,25 @@ protected override void OnStartup(StartupEventArgs args)
// force high priority
SetPriorityClass(process.Handle, (int)PriorityClass.HIGH_PRIORITY_CLASS);
- var processes = Process.GetProcessesByName(process.ProcessName);
+ Process[] processes = Process.GetProcessesByName(process.ProcessName);
if (processes.Length > 1)
- using (var prevProcess = processes[0])
+ {
+ using (Process prevProcess = processes[0])
{
- var handle = prevProcess.MainWindowHandle;
+ nint handle = prevProcess.MainWindowHandle;
if (ProcessUtils.IsIconic(handle))
ProcessUtils.ShowWindow(handle, (int)ProcessUtils.ShowWindowCommands.Restored);
- ProcessUtils.SetForegroundWindow(handle);
+ // force close this process if we were able to bring previous process to foreground
+ // kill previous process otherwise (means it's stalled)
+ if (ProcessUtils.SetForegroundWindow(handle))
+ process.Kill();
+ else
+ prevProcess.Kill();
- // force close this iteration
- process.Kill();
return;
}
+ }
}
// define culture settings
@@ -105,6 +110,10 @@ private void Application_ThreadException(object sender, ThreadExceptionEventArgs
{
var ex = default(Exception);
ex = (Exception)e.Exception;
+ if (ex.InnerException != null)
+ {
+ LogManager.LogCritical(ex.InnerException.Message + "\t" + ex.InnerException.StackTrace);
+ }
LogManager.LogCritical(ex.Message + "\t" + ex.StackTrace);
}
@@ -112,6 +121,10 @@ private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionE
{
var ex = default(Exception);
ex = (Exception)e.ExceptionObject;
+ if (ex.InnerException != null)
+ {
+ LogManager.LogCritical(ex.InnerException.Message + "\t" + ex.InnerException.StackTrace);
+ }
LogManager.LogCritical(ex.Message + "\t" + ex.StackTrace);
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/DInputController.cs b/HandheldCompanion/Controllers/DInputController.cs
index 966bcb580..0c83ce3c7 100644
--- a/HandheldCompanion/Controllers/DInputController.cs
+++ b/HandheldCompanion/Controllers/DInputController.cs
@@ -8,8 +8,7 @@ public class DInputController : IController
protected JoystickState State = new();
public DInputController()
- {
- }
+ { }
public DInputController(Joystick joystick, PnPDetails details)
{
@@ -43,9 +42,9 @@ public override string ToString()
return $"DInput Controller {UserIndex}";
}
- public override void UpdateInputs(long ticks)
+ public override void UpdateInputs(long ticks, float delta)
{
- base.UpdateInputs(ticks);
+ base.UpdateInputs(ticks, delta);
}
public override bool IsConnected()
diff --git a/HandheldCompanion/Controllers/DS4Controller.cs b/HandheldCompanion/Controllers/DS4Controller.cs
index a081322cf..cb307cc08 100644
--- a/HandheldCompanion/Controllers/DS4Controller.cs
+++ b/HandheldCompanion/Controllers/DS4Controller.cs
@@ -1,7 +1,6 @@
using HandheldCompanion.Inputs;
using HandheldCompanion.Managers;
using HandheldCompanion.Utils;
-using System.Windows;
using System.Windows.Media;
using static JSL;
@@ -10,8 +9,7 @@ namespace HandheldCompanion.Controllers;
public class DS4Controller : JSController
{
public DS4Controller()
- {
- }
+ { }
public DS4Controller(JOY_SETTINGS settings, PnPDetails details) : base(settings, details)
{
@@ -20,7 +18,10 @@ public DS4Controller(JOY_SETTINGS settings, PnPDetails details) : base(settings,
ColoredButtons.Add(ButtonFlags.B2, new SolidColorBrush(Color.FromArgb(255, 255, 73, 75)));
ColoredButtons.Add(ButtonFlags.B3, new SolidColorBrush(Color.FromArgb(255, 244, 149, 193)));
ColoredButtons.Add(ButtonFlags.B4, new SolidColorBrush(Color.FromArgb(255, 73, 191, 115)));
+ }
+ protected override void InitializeInputOutput()
+ {
// Additional controller specific source buttons
SourceButtons.Add(ButtonFlags.LeftPadClick);
SourceButtons.Add(ButtonFlags.LeftPadTouch);
@@ -39,13 +40,13 @@ public DS4Controller(JOY_SETTINGS settings, PnPDetails details) : base(settings,
TargetAxis.Add(AxisLayoutFlags.RightPad);
}
- public override void UpdateInputs(long ticks)
+ public override void UpdateInputs(long ticks, float delta)
{
// skip if controller isn't connected
if (!IsConnected())
return;
- base.UpdateState();
+ base.UpdateState(delta);
// Left Pad
Inputs.ButtonState[ButtonFlags.LeftPadTouch] = JslGetTouchDown(UserIndex);
@@ -83,7 +84,7 @@ public override void UpdateInputs(long ticks)
Inputs.AxisState[AxisFlags.RightPadY] = 0;
}
- base.UpdateInputs(ticks);
+ base.UpdateInputs(ticks, delta);
}
public override string ToString()
@@ -105,11 +106,7 @@ public override void Unplug()
public override void SetLightColor(byte R, byte G, byte B)
{
- // UI thread
- Application.Current.Dispatcher.Invoke(() =>
- {
- JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B));
- });
+ JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B));
}
public override string GetGlyph(ButtonFlags button)
diff --git a/HandheldCompanion/Controllers/DualSenseController.cs b/HandheldCompanion/Controllers/DualSenseController.cs
index f23bb9eba..8599b4362 100644
--- a/HandheldCompanion/Controllers/DualSenseController.cs
+++ b/HandheldCompanion/Controllers/DualSenseController.cs
@@ -1,7 +1,7 @@
using HandheldCompanion.Inputs;
using HandheldCompanion.Managers;
using HandheldCompanion.Utils;
-using System.Windows;
+using System.Windows.Media;
using static JSL;
namespace HandheldCompanion.Controllers;
@@ -9,10 +9,18 @@ namespace HandheldCompanion.Controllers;
public class DualSenseController : JSController
{
public DualSenseController()
+ { }
+
+ public DualSenseController(JOY_SETTINGS settings, PnPDetails details) : base(settings, details)
{
+ // UI
+ ColoredButtons.Add(ButtonFlags.B1, new SolidColorBrush(Color.FromArgb(255, 116, 139, 255)));
+ ColoredButtons.Add(ButtonFlags.B2, new SolidColorBrush(Color.FromArgb(255, 255, 73, 75)));
+ ColoredButtons.Add(ButtonFlags.B3, new SolidColorBrush(Color.FromArgb(255, 244, 149, 193)));
+ ColoredButtons.Add(ButtonFlags.B4, new SolidColorBrush(Color.FromArgb(255, 73, 191, 115)));
}
- public DualSenseController(JOY_SETTINGS settings, PnPDetails details) : base(settings, details)
+ protected override void InitializeInputOutput()
{
// Additional controller specific source buttons
SourceButtons.Add(ButtonFlags.LeftPadClick);
@@ -32,13 +40,13 @@ public DualSenseController(JOY_SETTINGS settings, PnPDetails details) : base(set
TargetAxis.Add(AxisLayoutFlags.RightPad);
}
- public override void UpdateInputs(long ticks)
+ public override void UpdateInputs(long ticks, float delta)
{
// skip if controller isn't connected
if (!IsConnected())
return;
- base.UpdateState();
+ base.UpdateState(delta);
// Left Pad
Inputs.ButtonState[ButtonFlags.LeftPadTouch] = JslGetTouchDown(UserIndex);
@@ -76,7 +84,7 @@ public override void UpdateInputs(long ticks)
Inputs.AxisState[AxisFlags.RightPadY] = 0;
}
- base.UpdateInputs(ticks);
+ base.UpdateInputs(ticks, delta);
}
public override string ToString()
@@ -98,11 +106,7 @@ public override void Unplug()
public override void SetLightColor(byte R, byte G, byte B)
{
- // UI thread
- Application.Current.Dispatcher.Invoke(() =>
- {
- JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B));
- });
+ JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B));
}
public override string GetGlyph(ButtonFlags button)
diff --git a/HandheldCompanion/Controllers/GordonController.cs b/HandheldCompanion/Controllers/GordonController.cs
index 14dcdac54..1d2ef137e 100644
--- a/HandheldCompanion/Controllers/GordonController.cs
+++ b/HandheldCompanion/Controllers/GordonController.cs
@@ -20,6 +20,9 @@ public class GordonController : SteamController
private const short TrackPadInner = short.MaxValue / 2;
public const ushort MaxRumbleIntensity = 2048;
+ public GordonController()
+ { }
+
public GordonController(PnPDetails details) : base()
{
AttachDetails(details);
@@ -32,7 +35,10 @@ public GordonController(PnPDetails details) : base()
DrawUI();
UpdateUI();
+ }
+ protected override void InitializeInputOutput()
+ {
// Additional controller specific source buttons/axes
SourceButtons.AddRange(new List() { ButtonFlags.L4, ButtonFlags.R4 });
SourceButtons.AddRange(new List() { ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp, ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight });
@@ -65,6 +71,7 @@ public override void AttachDetails(PnPDetails details)
base.AttachDetails(details);
Controller = new(details.VendorID, details.ProductID, details.GetMI());
+ UserIndex = (byte)details.GetMI();
// open controller
Open();
@@ -78,7 +85,7 @@ public override string ToString()
return "Steam Controller Gordon";
}
- public override void UpdateInputs(long ticks)
+ public override void UpdateInputs(long ticks, float delta)
{
if (input is null)
return;
@@ -197,18 +204,24 @@ public override void UpdateInputs(long ticks)
}
// TODO: why Z/Y swapped?
- Inputs.GyroState.Accelerometer.X = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelX] / short.MaxValue * 2.0f;
- Inputs.GyroState.Accelerometer.Y = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f;
- Inputs.GyroState.Accelerometer.Z = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelY] / short.MaxValue * 2.0f;
+ float aX = (float)input.State.AxesState[GordonControllerAxis.GyroAccelX] / short.MaxValue * 2.0f;
+ float aY = (float)input.State.AxesState[GordonControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f;
+ float aZ = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelY] / short.MaxValue * 2.0f;
// TODO: why Roll/Pitch swapped?
- Inputs.GyroState.Gyroscope.X = (float)input.State.AxesState[GordonControllerAxis.GyroPitch] / short.MaxValue * 2048.0f; // Roll
- Inputs.GyroState.Gyroscope.Y = -(float)input.State.AxesState[GordonControllerAxis.GyroRoll] / short.MaxValue * 2048.0f; // Pitch
- Inputs.GyroState.Gyroscope.Z = (float)input.State.AxesState[GordonControllerAxis.GyroYaw] / short.MaxValue * 2048.0f; // Yaw
+ float gX = (float)input.State.AxesState[GordonControllerAxis.GyroPitch] / short.MaxValue * 2000.0f; // Roll
+ float gY = (float)input.State.AxesState[GordonControllerAxis.GyroRoll] / short.MaxValue * 2000.0f; // Pitch
+ float gZ = (float)input.State.AxesState[GordonControllerAxis.GyroYaw] / short.MaxValue * 2000.0f; // Yaw
- base.UpdateInputs(ticks);
- }
+ // store motion
+ Inputs.GyroState.SetGyroscope(gX, gY, gZ);
+ Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
+ // process motion
+ gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
+
+ base.UpdateInputs(ticks, delta);
+ }
private void OnControllerInputReceived(GordonControllerInputEventArgs input)
{
this.input = input;
@@ -233,8 +246,6 @@ public override void Plug()
Controller.SetGyroscope(true);
Controller.SetIdleTimeout(300); // ~5 min
- SetVirtualMuted(SettingsManager.GetBoolean("SteamControllerMute"));
-
TimerManager.Tick += UpdateInputs;
base.Plug();
diff --git a/HandheldCompanion/Controllers/IController.xaml b/HandheldCompanion/Controllers/IController.xaml
index a9e332655..0ec1ea83d 100644
--- a/HandheldCompanion/Controllers/IController.xaml
+++ b/HandheldCompanion/Controllers/IController.xaml
@@ -25,10 +25,16 @@
+
+ TextWrapping="Wrap"
+ Visibility="Collapsed" />
diff --git a/HandheldCompanion/Controllers/IController.xaml.cs b/HandheldCompanion/Controllers/IController.xaml.cs
index 12e3649ce..7bcf82b5b 100644
--- a/HandheldCompanion/Controllers/IController.xaml.cs
+++ b/HandheldCompanion/Controllers/IController.xaml.cs
@@ -1,6 +1,8 @@
using HandheldCompanion.Actions;
+using HandheldCompanion.Helpers;
using HandheldCompanion.Inputs;
using HandheldCompanion.Managers;
+using HandheldCompanion.Misc;
using HandheldCompanion.Utils;
using iNKORE.UI.WPF.Modern.Controls;
using System;
@@ -19,7 +21,6 @@ public enum ControllerCapabilities : ushort
{
None = 0,
MotionSensor = 1,
- Calibration = 2,
}
///
@@ -29,22 +30,22 @@ public partial class IController : UserControl
{
// Buttons and axes we should be able to map to.
// When we have target controllers with different buttons (e.g. in VigEm) this will have to be moved elsewhere.
- public static readonly List TargetButtons = new()
+ protected readonly List TargetButtons = new()
{
- ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4,
+ ButtonFlags.None, ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4,
ButtonFlags.DPadUp, ButtonFlags.DPadDown, ButtonFlags.DPadLeft, ButtonFlags.DPadRight,
ButtonFlags.Start, ButtonFlags.Back, ButtonFlags.Special,
ButtonFlags.L1, ButtonFlags.R1,
ButtonFlags.LeftStickClick, ButtonFlags.RightStickClick,
};
- public static readonly List TargetAxis = new()
+ protected readonly List TargetAxis = new()
{
AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick,
AxisLayoutFlags.L2, AxisLayoutFlags.R2,
};
- protected List SourceAxis = new()
+ protected readonly List SourceAxis = new()
{
// same as target, we assume all controllers have those axes
AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick,
@@ -53,7 +54,7 @@ public partial class IController : UserControl
// Buttons and axes all controllers have that we can map.
// Additional ones can be added per controller.
- protected List SourceButtons = new()
+ protected readonly List SourceButtons = new()
{
// same as target, we assume all controllers have those buttons
ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4,
@@ -79,6 +80,8 @@ public partial class IController : UserControl
public ButtonState InjectedButtons = new();
public ControllerState Inputs = new();
+ protected GamepadMotion gamepadMotion;
+
protected double VibrationStrength = 1.0d;
private byte _UserIndex = 255;
private readonly int MaxUserIndex = 10;
@@ -88,18 +91,21 @@ public partial class IController : UserControl
private bool workingThreadRunning;
public virtual bool IsReady => true;
+ public virtual bool IsWireless => false;
public bool IsBusy
{
get
{
+ bool isBusy = false;
+
// UI thread
Application.Current.Dispatcher.Invoke(() =>
{
- return !IsEnabled;
+ isBusy = !IsEnabled;
});
- return false;
+ return isBusy;
}
set
@@ -165,7 +171,7 @@ protected byte UserIndex
private void SetVirtualControllerVisualIndex(int value)
{
// UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
foreach (FrameworkElement frameworkElement in UserIndexPanel.Children)
{
@@ -203,18 +209,53 @@ private void workingThreadLoop()
public IController()
{
InitializeComponent();
+ InitializeInputOutput();
+
MaxUserIndex = UserIndexPanel.Children.Count;
}
+ protected virtual void UpdateSettings()
+ { }
+
+ protected virtual void InitializeInputOutput()
+ { }
+
public virtual void AttachDetails(PnPDetails details)
{
+ if (details is null)
+ return;
+
this.Details = details;
Details.isHooked = true;
+
+ if (details.isVirtual)
+ return;
+
+ // manage gamepad motion
+ gamepadMotion = new(details.deviceInstanceId, CalibrationMode.Manual | CalibrationMode.SensorFusion);
+
+ // UI thread
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ ControllerType.Glyph = details.isInternal ? "\uE990" : details.isBluetooth ? "\uE702" : "\uECF0";
+ });
+
+ /*
+ // Retrieve the oldest device using LINQ
+ PnPDetails oldest = Details.isXInput ? DeviceManager.GetOldestXInput() : DeviceManager.GetOldestDInput();
+ if (oldest is not null)
+ IsInternal = oldest.deviceInstanceId == Details.deviceInstanceId;
+ */
+ }
+
+ public virtual void UpdateInputs(long ticks, float delta)
+ {
+ InputsUpdated?.Invoke(Inputs, gamepadMotion, delta);
}
- public virtual void UpdateInputs(long ticks)
+ public virtual void UpdateInputs(long ticks, float delta, GamepadMotion gamepadOverwrite)
{
- InputsUpdated?.Invoke(Inputs);
+ InputsUpdated?.Invoke(Inputs, gamepadOverwrite, delta);
}
public bool HasMotionSensor()
@@ -222,6 +263,11 @@ public bool HasMotionSensor()
return Capabilities.HasFlag(ControllerCapabilities.MotionSensor);
}
+ public GamepadMotion GetMotionSensor()
+ {
+ return gamepadMotion;
+ }
+
public bool IsPhysical()
{
return !IsVirtual();
@@ -288,11 +334,11 @@ protected void UpdateUI()
return;
// UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
// ui_button_hook.Content = IsPlugged ? Properties.Resources.Controller_Disconnect : Properties.Resources.Controller_Connect;
ui_button_hide.Content = IsHidden() ? Properties.Resources.Controller_Unhide : Properties.Resources.Controller_Hide;
- ui_button_calibrate.Visibility = Capabilities.HasFlag(ControllerCapabilities.Calibration) ? Visibility.Visible : Visibility.Collapsed;
+ ui_button_calibrate.Visibility = Capabilities.HasFlag(ControllerCapabilities.MotionSensor) ? Visibility.Visible : Visibility.Collapsed;
});
}
@@ -341,8 +387,7 @@ public virtual void SetVibrationStrength(uint value, bool rumble = false)
}
public virtual void SetVibration(byte LargeMotor, byte SmallMotor)
- {
- }
+ { }
// let the controller decide itself what motor to use for a specific button
public virtual void SetHaptic(HapticStrength strength, ButtonFlags button)
@@ -412,7 +457,7 @@ public virtual void Plug()
InjectedButtons.Clear();
// UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
ui_button_hook.IsEnabled = false;
});
@@ -422,7 +467,7 @@ public virtual void Plug()
public virtual void Unplug()
{
// UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
ui_button_hook.IsEnabled = true;
});
@@ -507,21 +552,21 @@ public virtual bool RestoreDrivers()
return true;
}
- protected virtual void Calibrate()
+ public async void Calibrate()
{
+ SensorsManager.Calibrate(gamepadMotion);
}
protected virtual void ui_button_calibrate_Click(object sender, RoutedEventArgs e)
{
+ Calibrate();
}
protected virtual void ui_button_hide_Click(object sender, RoutedEventArgs e)
- {
- }
+ { }
protected virtual void ui_button_hook_Click(object sender, RoutedEventArgs e)
- {
- }
+ { }
public virtual string GetGlyph(ButtonFlags button)
{
@@ -596,6 +641,33 @@ public virtual string GetGlyph(AxisLayoutFlags axis)
return defaultGlyph;
}
+ public GlyphIconInfo GetGlyphIconInfo(ButtonFlags button, int fontIconSize = 14)
+ {
+ var glyph = GetGlyph(button);
+ return new GlyphIconInfo
+ {
+ Name = GetButtonName(button),
+ Glyph = glyph,
+ FontSize = glyph is not null ? 28 : fontIconSize,
+ FontFamily = glyph is not null ? GlyphFontFamily : null,
+ Foreground = GetGlyphColor(button)
+ };
+ }
+
+ public GlyphIconInfo GetGlyphIconInfo(AxisLayoutFlags axis, int fontIconSize = 14)
+ {
+ var glyph = GetGlyph(axis);
+ return new GlyphIconInfo
+ {
+ Name = GetAxisName(axis),
+ Glyph = glyph,
+ FontSize = glyph is not null ? 28 : fontIconSize,
+ FontFamily = glyph is not null ? GlyphFontFamily : null,
+ Foreground = GetGlyphColor(axis)
+ };
+ }
+
+ [Obsolete("GetFontIcon has dependencies on UI and should be avoided. Use GetGlyphIconInfo instead.")]
public FontIcon GetFontIcon(ButtonFlags button, int FontIconSize = 14)
{
var FontIcon = new FontIcon
@@ -614,6 +686,8 @@ public FontIcon GetFontIcon(ButtonFlags button, int FontIconSize = 14)
return FontIcon;
}
+
+ [Obsolete("GetFontIcon has dependencies on UI and should be avoided. Use GetGlyphIconInfo instead.")]
public FontIcon GetFontIcon(AxisLayoutFlags axis, int FontIconSize = 14)
{
var FontIcon = new FontIcon
@@ -653,25 +727,25 @@ private static bool IsTrigger(AxisLayoutFlags axis)
return axis is AxisLayoutFlags.L2 || axis is AxisLayoutFlags.R2;
}
- public static IEnumerable GetTargetButtons()
+ public List GetTargetButtons()
{
- var buttons = Enum.GetValues(typeof(ButtonFlags)).Cast();
+ IEnumerable buttons = Enum.GetValues(typeof(ButtonFlags)).Cast();
- return buttons.Where(a => TargetButtons.Contains(a));
+ return buttons.Where(a => TargetButtons.Contains(a)).ToList();
}
- public static IEnumerable GetTargetAxis()
+ public List GetTargetAxis()
{
- var axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast();
+ IEnumerable axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast();
- return axis.Where(a => TargetAxis.Contains(a) && !IsTrigger(a));
+ return axis.Where(a => TargetAxis.Contains(a) && !IsTrigger(a)).ToList();
}
- public static IEnumerable GetTargetTriggers()
+ public List GetTargetTriggers()
{
- var axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast();
+ IEnumerable axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast();
- return axis.Where(a => TargetAxis.Contains(a) && IsTrigger(a));
+ return axis.Where(a => TargetAxis.Contains(a) && IsTrigger(a)).ToList();
}
public bool HasSourceButton(ButtonFlags button)
@@ -679,11 +753,21 @@ public bool HasSourceButton(ButtonFlags button)
return SourceButtons.Contains(button);
}
+ public bool HasSourceButton(List buttons)
+ {
+ return SourceButtons.Any(buttons.Contains);
+ }
+
public bool HasSourceAxis(AxisLayoutFlags axis)
{
return SourceAxis.Contains(axis);
}
+ public bool HasSourceAxis(List axis)
+ {
+ return SourceAxis.Any(axis.Contains);
+ }
+
public string GetButtonName(ButtonFlags button)
{
return EnumUtils.GetDescriptionFromEnumValue(button, GetType().Name);
@@ -700,7 +784,7 @@ public string GetAxisName(AxisLayoutFlags axis)
public delegate void UserIndexChangedEventHandler(byte UserIndex);
public event InputsUpdatedEventHandler InputsUpdated;
- public delegate void InputsUpdatedEventHandler(ControllerState Inputs);
+ public delegate void InputsUpdatedEventHandler(ControllerState Inputs, GamepadMotion gamepadMotion, float delta);
#endregion
}
diff --git a/HandheldCompanion/Controllers/JSController.cs b/HandheldCompanion/Controllers/JSController.cs
index de0bd42d8..2ee80bb21 100644
--- a/HandheldCompanion/Controllers/JSController.cs
+++ b/HandheldCompanion/Controllers/JSController.cs
@@ -3,9 +3,7 @@
using Nefarius.Utilities.DeviceManagement.PnP;
using System;
using System.Threading.Tasks;
-using System.Windows;
using static JSL;
-using Timer = System.Timers.Timer;
namespace HandheldCompanion.Controllers;
@@ -19,23 +17,16 @@ public class JSController : IController
protected float LeftThumbDeadZone = 0.24f;
protected float RightThumbDeadZone = 0.265f;
- protected Timer calibrateTimer = new Timer(5000) { AutoReset = false };
-
public JSController()
- {
- }
+ { }
public JSController(JOY_SETTINGS settings, PnPDetails details)
{
AttachJoySettings(settings);
AttachDetails(details);
- // timer(s)
- calibrateTimer.Elapsed += CalibrateTimer_Elapsed;
-
// Capabilities
Capabilities |= ControllerCapabilities.MotionSensor;
- Capabilities |= ControllerCapabilities.Calibration;
// UI
DrawUI();
@@ -57,12 +48,12 @@ public override string ToString()
return $"JoyShock Controller {UserIndex}";
}
- public override void UpdateInputs(long ticks)
+ public override void UpdateInputs(long ticks, float delta)
{
- base.UpdateInputs(ticks);
+ base.UpdateInputs(ticks, delta);
}
- public virtual void UpdateState()
+ public virtual void UpdateState(float delta)
{
// skip if controller isn't connected
if (!IsConnected())
@@ -123,13 +114,13 @@ public virtual void UpdateState()
// IMU
iMU_STATE = JslGetIMUState(UserIndex);
- Inputs.GyroState.Accelerometer.X = -iMU_STATE.accelX;
- Inputs.GyroState.Accelerometer.Y = -iMU_STATE.accelY;
- Inputs.GyroState.Accelerometer.Z = iMU_STATE.accelZ;
- Inputs.GyroState.Gyroscope.X = iMU_STATE.gyroX;
- Inputs.GyroState.Gyroscope.Y = -iMU_STATE.gyroY;
- Inputs.GyroState.Gyroscope.Z = iMU_STATE.gyroZ;
+ // store motion
+ Inputs.GyroState.SetGyroscope(iMU_STATE.gyroX, iMU_STATE.gyroY, iMU_STATE.gyroZ);
+ Inputs.GyroState.SetAccelerometer(iMU_STATE.accelX, iMU_STATE.accelY, iMU_STATE.accelZ);
+
+ // process motion
+ gamepadMotion.ProcessMotion(iMU_STATE.gyroX, iMU_STATE.gyroY, iMU_STATE.gyroZ, iMU_STATE.accelX, iMU_STATE.accelY, iMU_STATE.accelZ, delta);
}
public override bool IsConnected()
@@ -152,22 +143,6 @@ public override void SetVibration(byte LargeMotor, byte SmallMotor)
JslSetRumble(UserIndex, (byte)(SmallMotor * VibrationStrength), (byte)(LargeMotor * VibrationStrength));
}
- protected override void Calibrate()
- {
- // start calibration
- JslResetContinuousCalibration(UserIndex);
- JslStartContinuousCalibration(UserIndex);
-
- calibrateTimer.Start();
-
- // UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
- {
- ui_button_calibrate.Content = "Calibrating";
- ui_button_calibrate.IsEnabled = false;
- });
- }
-
public override void CyclePort()
{
string enumerator = Details.GetEnumerator();
@@ -192,32 +167,13 @@ public override void CyclePort()
}
}
- protected override void ui_button_calibrate_Click(object sender, RoutedEventArgs e)
- {
- // start calibration
- Calibrate();
-
- base.ui_button_calibrate_Click(sender, e);
- }
-
- private void CalibrateTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e)
- {
- // stop calibration
- JslPauseContinuousCalibration(UserIndex);
-
- // UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
- {
- ui_button_calibrate.Content = "Calibrate";
- ui_button_calibrate.IsEnabled = true;
- });
- }
-
public void AttachJoySettings(JOY_SETTINGS settings)
{
this.sSETTINGS = settings;
this.UserIndex = (byte)settings.playerNumber;
- JslSetAutomaticCalibration(UserIndex, true);
+ // manage elsewhere
+ JslResetContinuousCalibration(UserIndex);
+ JslPauseContinuousCalibration(UserIndex);
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/LegionController.cs b/HandheldCompanion/Controllers/LegionController.cs
index acaf54137..961b97c8c 100644
--- a/HandheldCompanion/Controllers/LegionController.cs
+++ b/HandheldCompanion/Controllers/LegionController.cs
@@ -1,165 +1,211 @@
-using HandheldCompanion.Devices;
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Utils;
-using HidLibrary;
-using System;
-using System.Collections.Generic;
-using System.Numerics;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Windows.Forms;
-using static HandheldCompanion.Devices.Lenovo.SapientiaUsb;
-
-namespace HandheldCompanion.Controllers
-{
- public class LegionController : XInputController
- {
- // Import the user32.dll library
- [DllImport("user32.dll", SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
- static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref uint pvParam, uint fWinIni);
-
- [Flags]
- private enum FrontEnum
- {
- None = 0,
- LegionR = 64,
- LegionL = 128,
- }
-
- [Flags]
- private enum BackEnum
- {
- None = 0,
- M3 = 4,
- M2 = 8,
- Y3 = 32,
- Y2 = 64,
- Y1 = 128,
- }
-
+using HandheldCompanion.Devices;
+using HandheldCompanion.Helpers;
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
+using HandheldCompanion.Utils;
+using HidLibrary;
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Windows;
+using System.Windows.Forms;
+using static HandheldCompanion.Devices.Lenovo.SapientiaUsb;
+
+namespace HandheldCompanion.Controllers
+{
+ public class LegionController : XInputController
+ {
+ // Import the user32.dll library
+ [DllImport("user32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref uint pvParam, uint fWinIni);
+
+ [Flags]
+ private enum FrontEnum
+ {
+ None = 0,
+ LegionR = 64,
+ LegionL = 128,
+ }
+
+ [Flags]
+ private enum BackEnum
+ {
+ None = 0,
+ M3 = 4,
+ M2 = 8,
+ Y3 = 32,
+ Y2 = 64,
+ Y1 = 128,
+ }
+
private enum ControllerState
{
Unk0 = 0,
Unk1 = 1,
Wired = 2,
Wireless = 3,
- }
-
- private HidDevice hidDevice;
- private const byte FRONT_IDX = 17;
- private const byte BACK_IDX = 19;
- private const byte STATUS_IDX = 0;
- private const byte LCONTROLLER_STATE_IDX = 11;
- private const byte RCONTROLLER_STATE_IDX = 12;
-
- private HashSet READY_STATES = new HashSet() {25, 60};
-
- private Thread dataThread;
- private bool dataThreadRunning;
-
- private byte[] Data = new byte[64];
- public override bool IsReady
- {
- get
- {
- byte status = GetStatus(STATUS_IDX);
- return READY_STATES.Contains(status);
- }
- }
-
- public bool IsWireless
- {
- get
- {
- byte LControllerState = GetStatus(LCONTROLLER_STATE_IDX);
- byte RControllerState = GetStatus(RCONTROLLER_STATE_IDX);
+ }
+
+ private HidDevice hidDevice;
+ private const byte FRONT_IDX = 17;
+ private const byte BACK_IDX = 19;
+ private const byte STATUS_IDX = 0;
+ private const byte LCONTROLLER_STATE_IDX = 11;
+ private const byte RCONTROLLER_STATE_IDX = 12;
+
+ private HashSet READY_STATES = new HashSet() { 25, 60 };
+
+ private Thread dataThread;
+ private bool dataThreadRunning;
+
+ private byte[] Data = new byte[64];
+ public override bool IsReady
+ {
+ get
+ {
+ byte status = GetStatus(STATUS_IDX);
+ return READY_STATES.Contains(status);
+ }
+ }
+
+ public override bool IsWireless
+ {
+ get
+ {
+ byte LControllerState = GetStatus(LCONTROLLER_STATE_IDX);
+ byte RControllerState = GetStatus(RCONTROLLER_STATE_IDX);
return LControllerState == (byte)ControllerState.Wireless || RControllerState == (byte)ControllerState.Wireless;
- }
- }
-
- // Define some constants for the touchpad logic
- private bool IsPassthrough = false;
- private uint LongPressTime = 1000; // The minimum time in milliseconds for a long press
- private const int MaxDistance = 40; // Maximum distance tolerance between touch and untouch in pixels
-
- // Variables to store the touchpad state
- private bool touchpadTouched = false; // Whether the touchpad is currently touched
- private Vector2 touchpadPosition = Vector2.Zero; // The current position of the touchpad
- private Vector2 touchpadFirstPosition = Vector2.Zero; // The first position of the touchpad when touched
- private long touchpadStartTime = 0; // The start time of the touchpad when touched
- private long touchpadEndTime = 0; // The end time of the touchpad when untouched
- private bool touchpadDoubleTapped = false; // Whether the touchpad has been double tapped
- private bool touchpadLongTapped = false; // Whether the touchpad has been long tapped
-
- private long lastTap = 0;
- private Vector2 lastTapPosition = Vector2.Zero; // The current position of the touchpad
-
- public LegionController(PnPDetails details) : base(details)
- {
- // Additional controller specific source buttons
- SourceButtons.Add(ButtonFlags.RightPadTouch);
- SourceButtons.Add(ButtonFlags.RightPadClick);
- SourceButtons.Add(ButtonFlags.RightPadClickDown);
-
- SourceButtons.Add(ButtonFlags.R4);
- SourceButtons.Add(ButtonFlags.R5);
- SourceButtons.Add(ButtonFlags.L4);
- SourceButtons.Add(ButtonFlags.L5);
-
- SourceButtons.Add(ButtonFlags.B5);
- SourceButtons.Add(ButtonFlags.B6);
- SourceButtons.Add(ButtonFlags.B7);
- SourceButtons.Add(ButtonFlags.B8);
-
- SourceAxis.Add(AxisLayoutFlags.RightPad);
- SourceAxis.Add(AxisLayoutFlags.Gyroscope);
-
- // get long press time from system settings
- SystemParametersInfo(0x006A, 0, ref LongPressTime, 0);
- }
-
- public override void AttachDetails(PnPDetails details)
- {
- base.AttachDetails(details);
-
- hidDevice = GetHidDevice();
- if (hidDevice is not null)
- hidDevice.OpenDevice();
- }
-
- private HidDevice GetHidDevice()
- {
- IEnumerable devices = IDevice.GetHidDevices(Details.VendorID, Details.ProductID, 0);
- foreach (HidDevice device in devices)
- {
- if (!device.IsConnected)
- continue;
-
- if (device.Capabilities.InputReportByteLength == 64)
- return device; // HID-compliant vendor-defined device
- }
-
- return null;
- }
-
- private byte GetStatus(int idx)
- {
- if (hidDevice is not null)
- {
- HidReport report = hidDevice.ReadReport();
- if (report.Data is not null)
- return report.Data[idx];
- }
-
- return 0;
- }
-
- public override void Plug()
- {
- hidDevice = GetHidDevice();
- if (hidDevice is not null && hidDevice.IsConnected)
- {
- if (!hidDevice.IsOpen)
+ }
+ }
+
+ private bool IsPassthrough = false;
+ private int GyroIndex = LegionGo.RightJoyconIndex;
+
+ private uint LongPressTime = 1000; // The minimum time in milliseconds for a long press
+ private const int MaxDistance = 40; // Maximum distance tolerance between touch and untouch in pixels
+ private bool touchpadTouched = false; // Whether the touchpad is currently touched
+ private Vector2 touchpadPosition = Vector2.Zero; // The current position of the touchpad
+ private Vector2 touchpadFirstPosition = Vector2.Zero; // The first position of the touchpad when touched
+ private long touchpadStartTime = 0; // The start time of the touchpad when touched
+ private long touchpadEndTime = 0; // The end time of the touchpad when untouched
+ private bool touchpadDoubleTapped = false; // Whether the touchpad has been double tapped
+ private bool touchpadLongTapped = false; // Whether the touchpad has been long tapped
+ private long lastTap = 0;
+ private Vector2 lastTapPosition = Vector2.Zero; // The current position of the touchpad
+
+ private GamepadMotion gamepadMotionR;
+
+ public LegionController() : base()
+ { }
+
+ public LegionController(PnPDetails details) : base(details)
+ {
+ // Capabilities
+ Capabilities |= ControllerCapabilities.MotionSensor;
+
+ // get long press time from system settings
+ SystemParametersInfo(0x006A, 0, ref LongPressTime, 0);
+
+ SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
+ UpdateSettings();
+ }
+
+ public override string ToString()
+ {
+ return $"Legion Controller for Windows";
+ }
+
+ protected override void InitializeInputOutput()
+ {
+ // Additional controller specific source buttons
+ SourceButtons.Add(ButtonFlags.RightPadTouch);
+ SourceButtons.Add(ButtonFlags.RightPadClick);
+ SourceButtons.Add(ButtonFlags.RightPadClickDown);
+
+ SourceButtons.Add(ButtonFlags.R4);
+ SourceButtons.Add(ButtonFlags.R5);
+ SourceButtons.Add(ButtonFlags.L4);
+ SourceButtons.Add(ButtonFlags.L5);
+
+ SourceButtons.Add(ButtonFlags.B5);
+ SourceButtons.Add(ButtonFlags.B6);
+ SourceButtons.Add(ButtonFlags.B7);
+ SourceButtons.Add(ButtonFlags.B8);
+
+ // Legion Controllers do not have the Special button
+ SourceButtons.Remove(ButtonFlags.Special);
+
+ SourceAxis.Add(AxisLayoutFlags.RightPad);
+ SourceAxis.Add(AxisLayoutFlags.Gyroscope);
+ }
+
+ protected override void UpdateSettings()
+ {
+ SetPassthrough(SettingsManager.GetBoolean("LegionControllerPassthrough"));
+ SetGyroIndex(SettingsManager.GetInt("LegionControllerGyroIndex"));
+ }
+
+ private void SettingsManager_SettingValueChanged(string name, object value)
+ {
+ switch (name)
+ {
+ case "LegionControllerPassthrough":
+ SetPassthrough(Convert.ToBoolean(value));
+ break;
+ case "LegionControllerGyroIndex":
+ SetGyroIndex(Convert.ToInt32(value));
+ break;
+ }
+ }
+
+ public override void AttachDetails(PnPDetails details)
+ {
+ base.AttachDetails(details);
+
+ // manage gamepad motion
+ gamepadMotionR = new($"{details.deviceInstanceId}\\{LegionGo.RightJoyconIndex}", CalibrationMode.Manual | CalibrationMode.SensorFusion);
+
+ hidDevice = GetHidDevice();
+ if (hidDevice is not null)
+ hidDevice.OpenDevice();
+ }
+
+ private HidDevice GetHidDevice()
+ {
+ IEnumerable devices = IDevice.GetHidDevices(Details.VendorID, Details.ProductID, 0);
+ foreach (HidDevice device in devices)
+ {
+ if (!device.IsConnected)
+ continue;
+
+ if (device.Capabilities.InputReportByteLength == 64)
+ return device; // HID-compliant vendor-defined device
+ }
+
+ return null;
+ }
+
+ private byte GetStatus(int idx)
+ {
+ if (hidDevice is not null)
+ {
+ HidReport report = hidDevice.ReadReport();
+ if (report.Data is not null)
+ return report.Data[idx];
+ }
+
+ return 0;
+ }
+
+ public override void Plug()
+ {
+ hidDevice = GetHidDevice();
+ if (hidDevice is not null && hidDevice.IsConnected)
+ {
+ if (!hidDevice.IsOpen)
hidDevice.OpenDevice();
// start data thread
@@ -169,13 +215,13 @@ public override void Plug()
dataThread = new Thread(dataThreadLoop);
dataThread.IsBackground = true;
dataThread.Start();
- }
- }
-
- base.Plug();
- }
-
- public override void Unplug()
+ }
+ }
+
+ base.Plug();
+ }
+
+ public override void Unplug()
{
// Kill data thread
if (dataThread is not null)
@@ -185,215 +231,281 @@ public override void Unplug()
if (dataThread.IsAlive)
dataThread.Join();
dataThread = null;
- }
-
- if (hidDevice is not null)
- {
- if (hidDevice.IsConnected && hidDevice.IsOpen)
+ }
+
+ if (hidDevice is not null)
+ {
+ if (hidDevice.IsConnected && hidDevice.IsOpen)
hidDevice.CloseDevice();
hidDevice.Dispose();
- hidDevice = null;
- }
-
- base.Unplug();
- }
-
- public override void UpdateInputs(long ticks, bool commit)
- {
- // skip if controller isn't connected
- if (!IsConnected())
- return;
-
- base.UpdateInputs(ticks, false);
-
- FrontEnum frontButton = (FrontEnum)Data[FRONT_IDX];
- Inputs.ButtonState[ButtonFlags.OEM1] = frontButton.HasFlag(FrontEnum.LegionR);
- Inputs.ButtonState[ButtonFlags.OEM2] = frontButton.HasFlag(FrontEnum.LegionL);
-
- BackEnum backButton = (BackEnum)Data[BACK_IDX];
- Inputs.ButtonState[ButtonFlags.R4] = backButton.HasFlag(BackEnum.M3);
- Inputs.ButtonState[ButtonFlags.R5] = backButton.HasFlag(BackEnum.Y3);
- Inputs.ButtonState[ButtonFlags.L4] = backButton.HasFlag(BackEnum.Y1);
- Inputs.ButtonState[ButtonFlags.L5] = backButton.HasFlag(BackEnum.Y2);
- Inputs.ButtonState[ButtonFlags.B5] = backButton.HasFlag(BackEnum.M2);
- Inputs.ButtonState[ButtonFlags.B6] = Data[20] == 128; // Scroll click
- Inputs.ButtonState[ButtonFlags.B7] = Data[24] == 129; // Scroll up
- Inputs.ButtonState[ButtonFlags.B8] = Data[24] == 255; // Scroll down
-
- // Right Pad
- ushort TouchpadX = (ushort)((Data[25] << 8) | Data[26]);
- ushort TouchpadY = (ushort)((Data[27] << 8) | Data[28]);
-
- bool touched = (TouchpadX != 0 || TouchpadY != 0);
-
- Inputs.ButtonState[ButtonFlags.RightPadTouch] = touched;
- Inputs.ButtonState[ButtonFlags.RightPadClick] = false;
- Inputs.ButtonState[ButtonFlags.RightPadClickDown] = false;
-
- // handle touchpad if passthrough is off
- if (!IsPassthrough)
- HandleTouchpadInput(touched, TouchpadX, TouchpadY);
-
- /*
- Inputs.AxisState[AxisFlags.LeftStickX] += (short)InputUtils.MapRange(Data[29], byte.MinValue, byte.MaxValue, short.MinValue, short.MaxValue);
- Inputs.AxisState[AxisFlags.LeftStickY] -= (short)InputUtils.MapRange(Data[30], byte.MinValue, byte.MaxValue, short.MinValue, short.MaxValue);
-
- Inputs.AxisState[AxisFlags.RightStickX] += (short)InputUtils.MapRange(Data[31], byte.MinValue, byte.MaxValue, short.MinValue, short.MaxValue);
- Inputs.AxisState[AxisFlags.RightStickY] -= (short)InputUtils.MapRange(Data[32], byte.MinValue, byte.MaxValue, short.MinValue, short.MaxValue);
- */
-
- base.UpdateInputs(ticks);
- }
-
- private async void dataThreadLoop(object? obj)
- {
- // pull latest Data
- while (dataThreadRunning)
- {
- if (hidDevice is null)
- continue;
-
+ hidDevice = null;
+ }
+
+ base.Unplug();
+ }
+
+ public override void UpdateInputs(long ticks, float delta, bool commit)
+ {
+ // skip if controller isn't connected
+ if (!IsConnected())
+ return;
+
+ base.UpdateInputs(ticks, delta, false);
+
+ FrontEnum frontButton = (FrontEnum)Data[FRONT_IDX];
+ Inputs.ButtonState[ButtonFlags.OEM1] = frontButton.HasFlag(FrontEnum.LegionR);
+ Inputs.ButtonState[ButtonFlags.OEM2] = frontButton.HasFlag(FrontEnum.LegionL);
+
+ BackEnum backButton = (BackEnum)Data[BACK_IDX];
+ Inputs.ButtonState[ButtonFlags.R4] = backButton.HasFlag(BackEnum.M3);
+ Inputs.ButtonState[ButtonFlags.R5] = backButton.HasFlag(BackEnum.Y3);
+ Inputs.ButtonState[ButtonFlags.L4] = backButton.HasFlag(BackEnum.Y1);
+ Inputs.ButtonState[ButtonFlags.L5] = backButton.HasFlag(BackEnum.Y2);
+ Inputs.ButtonState[ButtonFlags.B5] = backButton.HasFlag(BackEnum.M2);
+ Inputs.ButtonState[ButtonFlags.B6] = Data[20] == 128; // Scroll click
+ Inputs.ButtonState[ButtonFlags.B7] = Data[24] == 129; // Scroll up
+ Inputs.ButtonState[ButtonFlags.B8] = Data[24] == 255; // Scroll down
+
+ // Right Pad
+ ushort TouchpadX = (ushort)((Data[25] << 8) | Data[26]);
+ ushort TouchpadY = (ushort)((Data[27] << 8) | Data[28]);
+
+ bool touched = (TouchpadX != 0 || TouchpadY != 0);
+
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = touched;
+
+ // handle touchpad if passthrough is off
+ if (!IsPassthrough)
+ HandleTouchpadInput(touched, TouchpadX, TouchpadY);
+
+ /*
+ Inputs.AxisState[AxisFlags.LeftStickX] += (short)InputUtils.MapRange(Data[29], byte.MinValue, byte.MaxValue, short.MinValue, short.MaxValue);
+ Inputs.AxisState[AxisFlags.LeftStickY] -= (short)InputUtils.MapRange(Data[30], byte.MinValue, byte.MaxValue, short.MinValue, short.MaxValue);
+
+ Inputs.AxisState[AxisFlags.RightStickX] += (short)InputUtils.MapRange(Data[31], byte.MinValue, byte.MaxValue, short.MinValue, short.MaxValue);
+ Inputs.AxisState[AxisFlags.RightStickY] -= (short)InputUtils.MapRange(Data[32], byte.MinValue, byte.MaxValue, short.MinValue, short.MaxValue);
+ */
+
+ float aX, aZ, aY = 0;
+ float gX, gZ, gY = 0;
+
+ switch (GyroIndex)
+ {
+ default:
+ case LegionGo.LeftJoyconIndex:
+ {
+ aX = (short)(Data[34] << 8 | Data[35]) * -(2.0f / short.MaxValue);
+ aZ = (short)(Data[36] << 8 | Data[37]) * -(2.0f / short.MaxValue);
+ aY = (short)(Data[38] << 8 | Data[39]) * -(2.0f / short.MaxValue);
+
+ gX = (short)(Data[40] << 8 | Data[41]) * -(2000.0f / short.MaxValue);
+ gZ = (short)(Data[42] << 8 | Data[43]) * -(2000.0f / short.MaxValue);
+ gY = (short)(Data[44] << 8 | Data[45]) * -(2000.0f / short.MaxValue);
+ }
+ break;
+
+ case LegionGo.RightJoyconIndex:
+ {
+ aX = (short)(Data[49] << 8 | Data[50]) * -(2.0f / short.MaxValue);
+ aZ = (short)(Data[47] << 8 | Data[48]) * (2.0f / short.MaxValue);
+ aY = (short)(Data[51] << 8 | Data[52]) * -(2.0f / short.MaxValue);
+
+ gX = (short)(Data[55] << 8 | Data[56]) * -(2000.0f / short.MaxValue);
+ gZ = (short)(Data[53] << 8 | Data[54]) * (2000.0f / short.MaxValue);
+ gY = (short)(Data[57] << 8 | Data[58]) * -(2000.0f / short.MaxValue);
+ }
+ break;
+ }
+
+ // store motion
+ Inputs.GyroState.SetGyroscope(gX, gY, gZ);
+ Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
+
+ // process motion
+ switch (GyroIndex)
+ {
+ default:
+ case LegionGo.LeftJoyconIndex:
+ gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
+ base.UpdateInputs(ticks, delta);
+ break;
+ case LegionGo.RightJoyconIndex:
+ gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
+ base.UpdateInputs(ticks, delta, gamepadMotionR);
+ break;
+ }
+ }
+
+ protected override async void ui_button_calibrate_Click(object sender, RoutedEventArgs e)
+ {
+ switch (GyroIndex)
+ {
+ default:
+ case LegionGo.LeftJoyconIndex:
+ SensorsManager.Calibrate(gamepadMotion);
+ break;
+ case LegionGo.RightJoyconIndex:
+ SensorsManager.Calibrate(gamepadMotionR);
+ break;
+ }
+ }
+
+ private async void dataThreadLoop(object? obj)
+ {
+ // pull latest Data
+ while (dataThreadRunning)
+ {
+ if (hidDevice is null)
+ continue;
+
HidReport report = hidDevice.ReadReport();
- if (report is not null)
- {
- // check if packet is safe
+ if (report is not null)
+ {
+ // check if packet is safe
if (READY_STATES.Contains(report.Data[STATUS_IDX]))
- {
Data = report.Data;
- }
- }
- }
- }
-
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.B5:
- return "\u2213"; // M2
- case ButtonFlags.B6:
- return "\u2206"; // Scroll click
- case ButtonFlags.B7:
- return "\u27F0"; // Scroll up
- case ButtonFlags.B8:
- return "\u27F1"; // Scroll down
- }
-
- return base.GetGlyph(button);
- }
-
- public void HandleTouchpadInput(bool touched, ushort x, ushort y)
- {
- // Convert the ushort values to Vector2
- Vector2 position = new Vector2(x, y);
-
- // If the touchpad is touched
- if (touched)
- {
- Inputs.AxisState[AxisFlags.RightPadX] = (short)InputUtils.MapRange((short)x, 0, 1000, short.MinValue, short.MaxValue);
- Inputs.AxisState[AxisFlags.RightPadY] = (short)InputUtils.MapRange((short)-y, 0, 1000, short.MinValue, short.MaxValue);
-
- // If the touchpad was not touched before
- if (!touchpadTouched)
- {
- // Set the touchpad state variables
- touchpadTouched = true;
- touchpadPosition = position;
- touchpadFirstPosition = position;
- touchpadStartTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
-
- // Set the right pad touch flag to true
- Inputs.ButtonState[ButtonFlags.RightPadTouch] = true;
-
- long delay = touchpadStartTime - lastTap;
- float distance = Vector2.Distance(touchpadFirstPosition, lastTapPosition);
-
- if (delay < SystemInformation.DoubleClickTime && distance < MaxDistance * 5)
- {
- Inputs.ButtonState[ButtonFlags.RightPadClick] = true;
- touchpadDoubleTapped = true;
- }
- }
- // If the touchpad was touched before
- else
- {
- // Update the touchpad position
- touchpadPosition = position;
-
- // If the touchpad has been double tapped
- if (touchpadDoubleTapped)
- {
- // Keep the right pad click flag to true
- Inputs.ButtonState[ButtonFlags.RightPadClick] = true;
- }
- else
- {
- // Calculate the duration and the distance of the touchpad
- long duration = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - touchpadStartTime;
- float distance = Vector2.Distance(touchpadFirstPosition, touchpadPosition);
-
- // If the duration is more than the long tap duration and the distance is less than the maximum distance
- if (duration >= LongPressTime && duration < (LongPressTime + 100) && distance < MaxDistance)
- {
- // If the touchpad has not been long tapped before
- if (!touchpadLongTapped)
- {
- // Set the right pad click down flag to true
- Inputs.ButtonState[ButtonFlags.RightPadClickDown] = true;
-
- // Set the touchpad long tapped flag to true
- touchpadLongTapped = true;
- }
- }
- }
- }
- }
- // If the touchpad is not touched
- else
- {
- Inputs.AxisState[AxisFlags.RightPadX] = 0;
- Inputs.AxisState[AxisFlags.RightPadY] = 0;
-
- // If the touchpad was touched before
- if (touchpadTouched)
- {
- // Set the touchpad state variables
- touchpadTouched = false;
- touchpadEndTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
-
- // Set the right pad touch flag to false
- Inputs.ButtonState[ButtonFlags.RightPadTouch] = false;
-
- // Calculate the duration and the distance of the touchpad
- long duration = touchpadEndTime - touchpadStartTime;
- float distance = Vector2.Distance(touchpadFirstPosition, touchpadPosition);
-
- // If the duration is less than the short tap duration and the distance is less than the maximum distance
- if (duration < SystemInformation.DoubleClickTime && distance < MaxDistance)
- {
- // Set the right pad click flag to true
- Inputs.ButtonState[ButtonFlags.RightPadClick] = true;
-
- // Store tap time
- lastTap = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
- lastTapPosition = touchpadPosition;
- }
-
- // Set the touchpad long tapped flag to false
- touchpadLongTapped = false;
-
- // Set the touchpad double tapped flag to false
- touchpadDoubleTapped = false;
- }
- }
- }
-
- internal void SetPassthrough(bool enabled)
- {
+ }
+ }
+ }
+
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.B5:
+ return "\u2213"; // M2
+ case ButtonFlags.B6:
+ return "\u2206"; // Scroll click
+ case ButtonFlags.B7:
+ return "\u27F0"; // Scroll up
+ case ButtonFlags.B8:
+ return "\u27F1"; // Scroll down
+ }
+
+ return base.GetGlyph(button);
+ }
+
+ public void HandleTouchpadInput(bool touched, ushort x, ushort y)
+ {
+ // Convert the ushort values to Vector2
+ Vector2 position = new Vector2(x, y);
+
+ // If the touchpad is touched
+ if (touched)
+ {
+ Inputs.AxisState[AxisFlags.RightPadX] = (short)InputUtils.MapRange((short)x, 0, 1000, short.MinValue, short.MaxValue);
+ Inputs.AxisState[AxisFlags.RightPadY] = (short)InputUtils.MapRange((short)-y, 0, 1000, short.MinValue, short.MaxValue);
+
+ // If the touchpad was not touched before
+ if (!touchpadTouched)
+ {
+ // Set the touchpad state variables
+ touchpadTouched = true;
+ touchpadPosition = position;
+ touchpadFirstPosition = position;
+ touchpadStartTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
+
+ // Set the right pad touch flag to true
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = true;
+
+ long delay = touchpadStartTime - lastTap;
+ float distance = Vector2.Distance(touchpadFirstPosition, lastTapPosition);
+
+ if (delay < SystemInformation.DoubleClickTime && distance < MaxDistance * 5)
+ {
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = true;
+ touchpadDoubleTapped = true;
+ }
+ }
+ // If the touchpad was touched before
+ else
+ {
+ // Update the touchpad position
+ touchpadPosition = position;
+
+ // If the touchpad has been double tapped
+ if (touchpadDoubleTapped)
+ {
+ // Keep the right pad click flag to true
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = true;
+ }
+ else
+ {
+ // Calculate the duration and the distance of the touchpad
+ long duration = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond - touchpadStartTime;
+ float distance = Vector2.Distance(touchpadFirstPosition, touchpadPosition);
+
+ // If the duration is more than the long tap duration and the distance is less than the maximum distance
+ if (duration >= LongPressTime && duration < (LongPressTime + 100) && distance < MaxDistance)
+ {
+ // If the touchpad has not been long tapped before
+ if (!touchpadLongTapped)
+ {
+ // Set the right pad click down flag to true
+ Inputs.ButtonState[ButtonFlags.RightPadClickDown] = true;
+
+ // Set the touchpad long tapped flag to true
+ touchpadLongTapped = true;
+ }
+ }
+ }
+ }
+ }
+ // If the touchpad is not touched
+ else
+ {
+ Inputs.AxisState[AxisFlags.RightPadX] = 0;
+ Inputs.AxisState[AxisFlags.RightPadY] = 0;
+ Inputs.ButtonState[ButtonFlags.RightPadClickDown] = false;
+
+ // If the touchpad was touched before
+ if (touchpadTouched)
+ {
+ // Set the touchpad state variables
+ touchpadTouched = false;
+ touchpadEndTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
+
+ // Set the right pad touch flag to false
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = false;
+
+ // Calculate the duration and the distance of the touchpad
+ long duration = touchpadEndTime - touchpadStartTime;
+ float distance = Vector2.Distance(touchpadFirstPosition, touchpadPosition);
+
+ // If the duration is less than the short tap duration and the distance is less than the maximum distance
+ if (duration < SystemInformation.DoubleClickTime && distance < MaxDistance)
+ {
+ // Set the right pad click flag to true
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = true;
+
+ // Store tap time
+ lastTap = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
+ lastTapPosition = touchpadPosition;
+ }
+ else
+ {
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = false;
+ }
+
+ // Set the touchpad long tapped flag to false
+ touchpadLongTapped = false;
+
+ // Set the touchpad double tapped flag to false
+ touchpadDoubleTapped = false;
+ }
+ }
+ }
+
+ public void SetPassthrough(bool enabled)
+ {
SetTouchPadStatus(enabled ? 1 : 0);
- IsPassthrough = enabled;
- }
-
- }
-}
+ IsPassthrough = enabled;
+ }
+
+ public void SetGyroIndex(int idx)
+ {
+ GyroIndex = idx + LegionGo.LeftJoyconIndex;
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/NeptuneController.cs b/HandheldCompanion/Controllers/NeptuneController.cs
index 412f41153..1d31439da 100644
--- a/HandheldCompanion/Controllers/NeptuneController.cs
+++ b/HandheldCompanion/Controllers/NeptuneController.cs
@@ -28,6 +28,9 @@ public class NeptuneController : SteamController
private Thread rumbleThread;
private bool rumbleThreadRunning;
+ public NeptuneController()
+ { }
+
public NeptuneController(PnPDetails details) : base()
{
AttachDetails(details);
@@ -35,21 +38,15 @@ public NeptuneController(PnPDetails details) : base()
// UI
DrawUI();
UpdateUI();
+ }
+ protected override void InitializeInputOutput()
+ {
// Additional controller specific source buttons/axes
- SourceButtons.AddRange(new List
- { ButtonFlags.L4, ButtonFlags.R4, ButtonFlags.L5, ButtonFlags.R5 });
+ SourceButtons.AddRange(new List { ButtonFlags.L4, ButtonFlags.R4, ButtonFlags.L5, ButtonFlags.R5 });
SourceButtons.AddRange(new List { ButtonFlags.LeftStickTouch, ButtonFlags.RightStickTouch });
- SourceButtons.AddRange(new List
- {
- ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp,
- ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight
- });
- SourceButtons.AddRange(new List
- {
- ButtonFlags.RightPadClick, ButtonFlags.RightPadTouch, ButtonFlags.RightPadClickUp,
- ButtonFlags.RightPadClickDown, ButtonFlags.RightPadClickLeft, ButtonFlags.RightPadClickRight
- });
+ SourceButtons.AddRange(new List { ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp, ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight });
+ SourceButtons.AddRange(new List { ButtonFlags.RightPadClick, ButtonFlags.RightPadTouch, ButtonFlags.RightPadClickUp, ButtonFlags.RightPadClickDown, ButtonFlags.RightPadClickLeft, ButtonFlags.RightPadClickRight });
SourceAxis.Add(AxisLayoutFlags.LeftPad);
SourceAxis.Add(AxisLayoutFlags.RightPad);
@@ -69,6 +66,7 @@ public override void AttachDetails(PnPDetails details)
base.AttachDetails(details);
Controller = new(details.VendorID, details.ProductID, details.GetMI());
+ UserIndex = 0;
// open controller
Open();
@@ -79,7 +77,7 @@ public override string ToString()
return "Valve Software Steam Controller";
}
- public override void UpdateInputs(long ticks)
+ public override void UpdateInputs(long ticks, float delta)
{
if (input is null)
return;
@@ -234,16 +232,23 @@ public override void UpdateInputs(long ticks)
}
// TODO: why Z/Y swapped?
- Inputs.GyroState.Accelerometer.X = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelX] / short.MaxValue * 2.0f;
- Inputs.GyroState.Accelerometer.Y = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f;
- Inputs.GyroState.Accelerometer.Z = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelY] / short.MaxValue * 2.0f;
+ float aX = (float)input.State.AxesState[NeptuneControllerAxis.GyroAccelX] / short.MaxValue * 2.0f;
+ float aY = (float)input.State.AxesState[NeptuneControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f;
+ float aZ = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelY] / short.MaxValue * 2.0f;
// TODO: why Roll/Pitch swapped?
- Inputs.GyroState.Gyroscope.X = (float)input.State.AxesState[NeptuneControllerAxis.GyroPitch] / short.MaxValue * 2048.0f; // Roll
- Inputs.GyroState.Gyroscope.Y = -(float)input.State.AxesState[NeptuneControllerAxis.GyroRoll] / short.MaxValue * 2048.0f; // Pitch
- Inputs.GyroState.Gyroscope.Z = -(float)input.State.AxesState[NeptuneControllerAxis.GyroYaw] / short.MaxValue * 2048.0f; // Yaw
+ float gX = (float)input.State.AxesState[NeptuneControllerAxis.GyroPitch] / short.MaxValue * 2000.0f; // Roll
+ float gY = (float)input.State.AxesState[NeptuneControllerAxis.GyroRoll] / short.MaxValue * 2000.0f; // Pitch
+ float gZ = -(float)input.State.AxesState[NeptuneControllerAxis.GyroYaw] / short.MaxValue * 2000.0f; // Yaw
- base.UpdateInputs(ticks);
+ // store motion
+ Inputs.GyroState.SetGyroscope(gX, gY, gZ);
+ Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
+
+ // process motion
+ gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
+
+ base.UpdateInputs(ticks, delta);
}
private void Open()
@@ -271,7 +276,7 @@ private void Close()
// remove handler
Controller.OnControllerInputReceived = null;
-
+
Controller.Close();
isConnected = false;
}
@@ -318,8 +323,6 @@ public override void Plug()
rumbleThread.IsBackground = true;
rumbleThread.Start();
- SetVirtualMuted(SettingsManager.GetBoolean("SteamControllerMute"));
-
TimerManager.Tick += UpdateInputs;
base.Plug();
diff --git a/HandheldCompanion/Controllers/ProController.cs b/HandheldCompanion/Controllers/ProController.cs
index e3d9b08d9..4838655e9 100644
--- a/HandheldCompanion/Controllers/ProController.cs
+++ b/HandheldCompanion/Controllers/ProController.cs
@@ -7,28 +7,30 @@ namespace HandheldCompanion.Controllers;
public class ProController : JSController
{
- public ProController()
- {
- }
+ public ProController() : base()
+ { }
public ProController(JOY_SETTINGS settings, PnPDetails details) : base(settings, details)
+ { }
+
+ protected override void InitializeInputOutput()
{
// Additional controller specific source buttons
SourceButtons.Add(ButtonFlags.Special2);
SourceAxis.Add(AxisLayoutFlags.Gyroscope);
}
- public override void UpdateInputs(long ticks)
+ public override void UpdateInputs(long ticks, float delta)
{
// skip if controller isn't connected
if (!IsConnected())
return;
- base.UpdateState();
+ base.UpdateState(delta);
Inputs.ButtonState[ButtonFlags.Special2] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskCapture);
- base.UpdateInputs(ticks);
+ base.UpdateInputs(ticks, delta);
}
public override string ToString()
diff --git a/HandheldCompanion/Controllers/SteamController.cs b/HandheldCompanion/Controllers/SteamController.cs
index 9dbb16aa5..a79d773ff 100644
--- a/HandheldCompanion/Controllers/SteamController.cs
+++ b/HandheldCompanion/Controllers/SteamController.cs
@@ -1,5 +1,7 @@
using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
using steam_hidapi.net.Hid;
+using System;
namespace HandheldCompanion.Controllers
{
@@ -11,6 +13,29 @@ public abstract class SteamController : IController
public SteamController() : base()
{
Capabilities |= ControllerCapabilities.MotionSensor;
+
+ SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
+ UpdateSettings();
+ }
+
+ public override void AttachDetails(PnPDetails details)
+ {
+ base.AttachDetails(details);
+ }
+
+ protected override void UpdateSettings()
+ {
+ SetVirtualMuted(SettingsManager.GetBoolean("SteamControllerMute"));
+ }
+
+ private void SettingsManager_SettingValueChanged(string name, object value)
+ {
+ switch (name)
+ {
+ case "SteamControllerMute":
+ SetVirtualMuted(Convert.ToBoolean(value));
+ break;
+ }
}
public override bool IsConnected()
diff --git a/HandheldCompanion/Controllers/XInputController.cs b/HandheldCompanion/Controllers/XInputController.cs
index 819c718c3..3d95f3183 100644
--- a/HandheldCompanion/Controllers/XInputController.cs
+++ b/HandheldCompanion/Controllers/XInputController.cs
@@ -54,7 +54,7 @@ public override string ToString()
return $"XInput Controller {(UserIndex)UserIndex}";
}
- public virtual void UpdateInputs(long ticks, bool commit)
+ public virtual void UpdateInputs(long ticks, float delta, bool commit)
{
// skip if controller isn't connected
if (!IsConnected())
@@ -121,7 +121,7 @@ public virtual void UpdateInputs(long ticks, bool commit)
catch { }
if (commit)
- base.UpdateInputs(ticks);
+ base.UpdateInputs(ticks, delta);
}
public override bool IsConnected()
@@ -149,13 +149,13 @@ public override void SetVibration(byte LargeMotor, byte SmallMotor)
public override void Plug()
{
- TimerManager.Tick += (ticks) => UpdateInputs(ticks, true);
+ TimerManager.Tick += (ticks, delta) => UpdateInputs(ticks, delta, true);
base.Plug();
}
public override void Unplug()
{
- TimerManager.Tick -= (ticks) => UpdateInputs(ticks, true);
+ TimerManager.Tick -= (ticks, delta) => UpdateInputs(ticks, delta, true);
base.Unplug();
}
@@ -198,6 +198,21 @@ public override void Unhide(bool powerCycle = true)
base.Unhide(powerCycle);
}
+ public virtual new bool IsWireless
+ {
+ get
+ {
+ string enumerator = Details.GetEnumerator();
+ switch (enumerator)
+ {
+ default:
+ return false;
+ case "BTHENUM":
+ return true;
+ }
+ }
+ }
+
public override void CyclePort()
{
string enumerator = Details.GetEnumerator();
diff --git a/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs b/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs
index 63ccffbb0..4ea5574c1 100644
--- a/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs
@@ -6,6 +6,7 @@
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Diagnostics;
+using System.Threading.Tasks;
using System.Windows;
namespace HandheldCompanion.Controls.Hints
@@ -41,7 +42,7 @@ private void CheckSettings()
int EmbeddedIntegerScalingSupport = RegistryUtils.GetInt(@"SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000", "DalEmbeddedIntegerScalingSupport");
// UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
this.Visibility = EmbeddedIntegerScalingSupport != 1 ? Visibility.Visible : Visibility.Collapsed;
});
@@ -51,15 +52,18 @@ protected override async void HintActionButton_Click(object sender, RoutedEventA
{
RegistryUtils.SetValue(@"SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000", "DalEmbeddedIntegerScalingSupport", 1);
- var result = Dialog.ShowAsync($"{Properties.Resources.Dialog_ForceRestartTitle}",
- $"{Properties.Resources.Dialog_ForceRestartDesc}",
- ContentDialogButton.Primary, null,
- $"{Properties.Resources.Dialog_Yes}",
- $"{Properties.Resources.Dialog_No}", MainWindow.GetCurrent());
-
- await result;
+ Task dialogTask = new Dialog(MainWindow.GetCurrent())
+ {
+ Title = Properties.Resources.Dialog_ForceRestartTitle,
+ Content = Properties.Resources.Dialog_ForceRestartDesc,
+ DefaultButton = ContentDialogButton.Close,
+ CloseButtonText = Properties.Resources.Dialog_No,
+ PrimaryButtonText = Properties.Resources.Dialog_Yes
+ }.ShowAsync();
+
+ await dialogTask; // sync call
- switch (result.Result)
+ switch (dialogTask.Result)
{
case ContentDialogResult.Primary:
using (Process shutdown = new())
diff --git a/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs b/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs
index 746a1a3ff..fb2b43f9e 100644
--- a/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs
@@ -5,6 +5,7 @@
using System;
using System.Diagnostics;
using System.Management;
+using System.Threading.Tasks;
using System.Windows;
namespace HandheldCompanion.Controls.Hints
@@ -58,7 +59,7 @@ private void CheckSettings()
VulnerableDriverBlocklistEnable = RegistryUtils.GetBoolean(@"SYSTEM\CurrentControlSet\Control\CI\Config", "VulnerableDriverBlocklistEnable");
// UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
this.Visibility = HypervisorEnforcedCodeIntegrityEnabled || VulnerableDriverBlocklistEnable ? Visibility.Visible : Visibility.Collapsed;
});
@@ -69,15 +70,18 @@ protected override async void HintActionButton_Click(object sender, RoutedEventA
RegistryUtils.SetValue(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios", "HypervisorEnforcedCodeIntegrity", 0);
RegistryUtils.SetValue(@"SYSTEM\CurrentControlSet\Control\CI\Config", "VulnerableDriverBlocklistEnable", 0);
- var result = Dialog.ShowAsync($"{Properties.Resources.Dialog_ForceRestartTitle}",
- $"{Properties.Resources.Dialog_ForceRestartDesc}",
- ContentDialogButton.Primary, null,
- $"{Properties.Resources.Dialog_Yes}",
- $"{Properties.Resources.Dialog_No}", MainWindow.GetCurrent());
-
- await result;
+ Task dialogTask = new Dialog(MainWindow.GetCurrent())
+ {
+ Title = Properties.Resources.Dialog_ForceRestartTitle,
+ Content = Properties.Resources.Dialog_ForceRestartDesc,
+ DefaultButton = ContentDialogButton.Close,
+ CloseButtonText = Properties.Resources.Dialog_No,
+ PrimaryButtonText = Properties.Resources.Dialog_Yes
+ }.ShowAsync();
+
+ await dialogTask; // sync call
- switch (result.Result)
+ switch (dialogTask.Result)
{
case ContentDialogResult.Primary:
using (Process shutdown = new())
diff --git a/HandheldCompanion/Controls/Hints/Hint_LegionGoLegionSpace.cs b/HandheldCompanion/Controls/Hints/Hint_LegionGoLegionSpace.cs
index 8f74c2b31..6d1fb83d3 100644
--- a/HandheldCompanion/Controls/Hints/Hint_LegionGoLegionSpace.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_LegionGoLegionSpace.cs
@@ -1,6 +1,5 @@
using HandheldCompanion.Devices;
using HandheldCompanion.Utils;
-using HandheldCompanion.Views;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -31,7 +30,7 @@ public class Hint_LegionGoLegionSpace : IHint
public Hint_LegionGoLegionSpace() : base()
{
- if (MainWindow.CurrentDevice is not LegionGo)
+ if (IDevice.GetCurrent() is not LegionGo)
return;
// Get all the services installed on the local computer
@@ -83,7 +82,7 @@ private void ServiceTimer_Elapsed(object? sender, ElapsedEventArgs e)
}
// UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
this.Visibility = anyRunning ? Visibility.Visible : Visibility.Collapsed;
});
@@ -121,7 +120,7 @@ protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
public override void Stop()
{
- serviceTimer.Stop();
+ serviceTimer?.Stop();
base.Stop();
}
}
diff --git a/HandheldCompanion/Controls/Hints/Hint_MSIClawCenter.cs b/HandheldCompanion/Controls/Hints/Hint_MSIClawCenter.cs
new file mode 100644
index 000000000..24e8b462e
--- /dev/null
+++ b/HandheldCompanion/Controls/Hints/Hint_MSIClawCenter.cs
@@ -0,0 +1,119 @@
+using HandheldCompanion.Devices;
+using Microsoft.Win32.TaskScheduler;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Timers;
+using System.Windows;
+using TaskScheduled = Microsoft.Win32.TaskScheduler.Task;
+
+namespace HandheldCompanion.Controls.Hints
+{
+ public class Hint_MSIClawCenter : IHint
+ {
+ private List serviceNames = new()
+ {
+ "ArmouryCrateSEService",
+ "AsusAppService",
+ "ArmouryCrateControlInterface",
+ };
+
+ private Timer watcherTimer;
+ private const string TaskName = "MSI_Center_M_Server";
+ private const string UIname = "MSI Center M";
+
+ public Hint_MSIClawCenter() : base()
+ {
+ if (IDevice.GetCurrent() is not ClawA1M)
+ return;
+
+ // we'll be checking MSI Claw Task every 4 seconds
+ watcherTimer = new Timer(4000);
+ watcherTimer.Elapsed += WatcherTimer_Elapsed;
+ watcherTimer.Start();
+
+ // default state
+ this.HintActionButton.Visibility = Visibility.Visible;
+
+ this.HintTitle.Text = Properties.Resources.Hint_MSIClawCenterCheck;
+ this.HintDescription.Text = Properties.Resources.Hint_MSIClawCenterCheckDesc;
+ this.HintReadMe.Text = Properties.Resources.Hint_MSIClawCenterCheckReadme;
+
+ this.HintActionButton.Content = Properties.Resources.Hint_MSIClawCenterCheckAction;
+ }
+
+ private TaskScheduled GetTask()
+ {
+ using (TaskService taskService = new TaskService())
+ {
+ TaskScheduled task = taskService.GetTask(TaskName);
+ return task;
+ }
+ }
+
+ private bool HasTask()
+ {
+ TaskScheduled task = GetTask();
+ if (task is null)
+ return false;
+
+ return task.Enabled;
+ }
+
+ private void KillTask()
+ {
+ TaskScheduled task = GetTask();
+ if (task != null)
+ task.Enabled = false;
+ }
+
+ private IEnumerable GetProcesses()
+ {
+ Process[] processes = Process.GetProcesses();
+ foreach (Process process in processes)
+ {
+ if (process.ProcessName.StartsWith(TaskName, StringComparison.InvariantCultureIgnoreCase))
+ yield return process;
+
+ if (process.ProcessName.Contains(UIname, StringComparison.InvariantCultureIgnoreCase))
+ yield return process;
+ }
+ }
+
+ private bool HasProcesses()
+ {
+ return GetProcesses().Any();
+ }
+
+ private void KillProcesses()
+ {
+ foreach (Process process in GetProcesses())
+ process.Kill();
+ }
+
+ private void WatcherTimer_Elapsed(object? sender, ElapsedEventArgs e)
+ {
+ bool hasTask = HasTask();
+ bool hasProcesses = HasProcesses();
+
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ this.Visibility = hasTask || hasProcesses ? Visibility.Visible : Visibility.Collapsed;
+ });
+ }
+
+ protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
+ {
+ KillTask();
+ KillProcesses();
+ }
+
+ public override void Stop()
+ {
+ watcherTimer?.Stop();
+ base.Stop();
+ }
+ }
+}
diff --git a/HandheldCompanion/Controls/Hints/Hint_RogAllyServiceCheck.cs b/HandheldCompanion/Controls/Hints/Hint_RogAllyServiceCheck.cs
index c0de6690d..c0a1a22ae 100644
--- a/HandheldCompanion/Controls/Hints/Hint_RogAllyServiceCheck.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_RogAllyServiceCheck.cs
@@ -1,6 +1,5 @@
using HandheldCompanion.Devices;
using HandheldCompanion.Utils;
-using HandheldCompanion.Views;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -25,7 +24,7 @@ public class Hint_RogAllyServiceCheck : IHint
public Hint_RogAllyServiceCheck() : base()
{
- if (MainWindow.CurrentDevice is not ROGAlly)
+ if (IDevice.GetCurrent() is not ROGAlly)
return;
// Get all the services installed on the local computer
@@ -60,7 +59,7 @@ public Hint_RogAllyServiceCheck() : base()
private void ServiceTimer_Elapsed(object? sender, ElapsedEventArgs e)
{
- if(!serviceControllers.Any())
+ if (!serviceControllers.Any())
return;
// Check if any of the services in the list exist and are running
@@ -77,7 +76,7 @@ private void ServiceTimer_Elapsed(object? sender, ElapsedEventArgs e)
}
// UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
this.Visibility = anyRunning ? Visibility.Visible : Visibility.Collapsed;
});
@@ -102,7 +101,7 @@ protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
public override void Stop()
{
- serviceTimer.Stop();
+ serviceTimer?.Stop();
base.Stop();
}
}
diff --git a/HandheldCompanion/Controls/Hints/Hint_SteamNeptuneDesktop.cs b/HandheldCompanion/Controls/Hints/Hint_SteamNeptuneDesktop.cs
index 2f3d1aea7..a163d929c 100644
--- a/HandheldCompanion/Controls/Hints/Hint_SteamNeptuneDesktop.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_SteamNeptuneDesktop.cs
@@ -28,7 +28,7 @@ private void Steam_Updated(PlatformStatus status)
bool DesktopProfileApplied = PlatformManager.Steam.HasDesktopProfileApplied();
// UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
switch (status)
{
diff --git a/HandheldCompanion/Controls/Hints/Hint_SteamXboxDrivers.cs b/HandheldCompanion/Controls/Hints/Hint_SteamXboxDrivers.cs
index 7772282ea..7676059b3 100644
--- a/HandheldCompanion/Controls/Hints/Hint_SteamXboxDrivers.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_SteamXboxDrivers.cs
@@ -34,7 +34,7 @@ private void CheckDrivers()
bool HasXboxDriversInstalled = PlatformManager.Steam.HasXboxDriversInstalled();
// UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
+ Application.Current.Dispatcher.Invoke(() =>
{
this.Visibility = HasXboxDriversInstalled ? Visibility.Visible : Visibility.Collapsed;
});
diff --git a/HandheldCompanion/Controls/Mapping/ButtonStack.xaml b/HandheldCompanion/Controls/Mapping/ButtonStack.xaml
deleted file mode 100644
index ecb760a33..000000000
--- a/HandheldCompanion/Controls/Mapping/ButtonStack.xaml
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/HandheldCompanion/Controls/Mapping/ButtonStack.xaml.cs b/HandheldCompanion/Controls/Mapping/ButtonStack.xaml.cs
deleted file mode 100644
index dc4d1e16f..000000000
--- a/HandheldCompanion/Controls/Mapping/ButtonStack.xaml.cs
+++ /dev/null
@@ -1,198 +0,0 @@
-using HandheldCompanion.Actions;
-using HandheldCompanion.Inputs;
-using iNKORE.UI.WPF.Modern.Controls;
-using System.Collections.Generic;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Media;
-
-namespace HandheldCompanion.Controls
-{
- ///
- /// Interaction logic for MappingStack.xaml
- ///
- public partial class ButtonStack : SimpleStackPanel
- {
- static Style buttonStyle = (Style)Application.Current.Resources["TabViewButtonStyle"];
- static FontFamily fontFamily = new("Segoe Fluent Icons");
- static Thickness margin = new(0, 0, 1, 0);
- static Thickness padding = new(2, 2, 0, 0);
-
- private ButtonFlags button;
-
- public event UpdatedEventHandler Updated;
- public delegate void UpdatedEventHandler(object sender, List actions);
- public event DeletedEventHandler Deleted;
- public delegate void DeletedEventHandler(object sender);
-
- public ButtonStack() : base()
- {
- InitializeComponent();
- }
-
- public ButtonStack(ButtonFlags button) : this()
- {
- this.button = button;
-
- // remove the xaml reference entry
- getGrid(0).Children.Clear(); Children.Clear();
- // add the first one and never remove it, only modify
- AddMappingToChildren(new ButtonMapping(button));
- }
-
- private Grid getGrid(int index)
- {
- return Children[index] as Grid;
- }
-
- private Button getButton(int index)
- {
- return getGrid(index).Children[0] as Button;
- }
-
- private ButtonMapping getMapping(int index)
- {
- return getGrid(index).Children[1] as ButtonMapping;
- }
-
- public void UpdateIcon(FontIcon newIcon, string newLabel)
- {
- getMapping(0).UpdateIcon(newIcon, newLabel);
- }
-
- public void UpdateSelections()
- {
- getMapping(0).UpdateSelections();
- }
-
- // actions cannot be null or empty
- public void SetActions(List actions)
- {
- int mappingLen = Children.Count;
- int index = 0;
- foreach (var action in actions)
- {
- // reuse existing mappings
- if (index < mappingLen)
- getMapping(index).SetIActions(action);
- // we need more
- else
- {
- ButtonMapping mapping = new(button);
- mapping.SetIActions(action);
- AddMappingToChildren(mapping);
- }
- index++;
- }
-
- // if there were equal or more actions than mappings we're done
- int actionsLen = actions.Count;
- if (mappingLen <= actionsLen)
- return;
-
- // if there were more mappings, remove the remaining ones
- for (int i = actionsLen; i < mappingLen; i++)
- getGrid(i).Children.Clear();
- Children.RemoveRange(actionsLen, mappingLen - actionsLen);
- }
-
- public void Reset()
- {
- for (int i = 0; i < Children.Count; i++)
- {
- if (i == 0)
- getMapping(i).Reset();
- else
- getGrid(i).Children.Clear();
- }
- Children.RemoveRange(1, Children.Count - 1);
- }
-
- private void ButtonMapping_Updated(ButtonFlags button)
- {
- List actions = new();
- for (int i = 0; i < Children.Count; i++)
- {
- IActions action = getMapping(i).GetIActions();
- if (action is null)
- continue;
-
- actions.Add(action);
- }
-
- if (actions.Count > 0)
- Updated?.Invoke(button, actions);
- else
- Deleted?.Invoke(button);
- }
-
- private void AddMappingToChildren(ButtonMapping mapping)
- {
- // doesn't matter if updated or deleted, we need to gather all active
- // we can't map those 1:1 anyway, as there can be more mappings than actions
- mapping.Updated += (sender, action) => ButtonMapping_Updated((ButtonFlags)sender);
- mapping.Deleted += (sender) => ButtonMapping_Updated((ButtonFlags)sender);
-
- int index = Children.Count;
-
- FontIcon fontIcon = new FontIcon();
- fontIcon.HorizontalAlignment = HorizontalAlignment.Center;
- fontIcon.VerticalAlignment = VerticalAlignment.Center;
- fontIcon.FontFamily = fontFamily;
- fontIcon.FontWeight = FontWeights.Bold;
- fontIcon.FontSize = 24;
- if (index == 0)
- fontIcon.Glyph = "\uECC8";
- else
- fontIcon.Glyph = "\uECC9";
-
- Button button = new();
- button.VerticalAlignment = VerticalAlignment.Top;
- button.Height = 48; button.Width = 48;
- button.SetResourceReference(Control.ForegroundProperty, "AccentButtonBackground");
- button.Margin = margin;
- button.Padding = padding; // the glyph is not centered within its box
- button.Style = buttonStyle;
- button.Tag = index;
- button.Click += Button_Click;
- button.Content = fontIcon;
-
- Grid grid = new();
- grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
- grid.ColumnDefinitions.Add(new ColumnDefinition());
- Grid.SetColumn(mapping, 1);
- grid.Children.Add(button);
- grid.Children.Add(mapping);
- Children.Add(grid);
- }
-
- private void Button_Click(object sender, RoutedEventArgs e)
- {
- int index = (int)((Button)sender).Tag;
-
- // Add
- if (index == 0)
- {
- ButtonMapping mapping = new(button);
- AddMappingToChildren(mapping);
- // no need to register new mapping, it's empty, will be updated with event
- }
- // Del
- else
- {
- bool sendEvent = getMapping(index).GetIActions() is not null;
-
- getGrid(index).Children.Clear();
- Children.RemoveAt(index);
-
- // reindex remaining
- for (int i = 0; i < Children.Count; i++)
- getButton(i).Tag = i;
-
- // removal of an actual action needs to be registered as it disappears without event
- if (sendEvent)
- ButtonMapping_Updated(button);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/HandheldCompanion/Controls/Mapping/IMapping.cs b/HandheldCompanion/Controls/Mapping/IMapping.cs
deleted file mode 100644
index 63afa83cf..000000000
--- a/HandheldCompanion/Controls/Mapping/IMapping.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using HandheldCompanion.Actions;
-using System.Windows.Controls;
-
-namespace HandheldCompanion.Controls;
-
-public class IMapping : UserControl
-{
- protected IActions Actions;
- protected object Value;
-
- protected virtual void Update()
- {
- // update axis mapping
- Updated?.Invoke(Value, Actions);
- }
-
- protected virtual void Delete()
- {
- Actions = null;
-
- Deleted?.Invoke(Value);
- }
-
- protected virtual void SetIActions(IActions actions)
- {
- // update mapping IActions
- Actions = actions;
- }
-
- #region events
-
- public event DeletedEventHandler Deleted;
-
- public delegate void DeletedEventHandler(object sender);
-
- public event UpdatedEventHandler Updated;
-
- public delegate void UpdatedEventHandler(object sender, IActions action);
-
- #endregion
-}
\ No newline at end of file
diff --git a/HandheldCompanion/Controls/ProcessEx.cs b/HandheldCompanion/Controls/ProcessEx.cs
new file mode 100644
index 000000000..4847ea3bb
--- /dev/null
+++ b/HandheldCompanion/Controls/ProcessEx.cs
@@ -0,0 +1,214 @@
+using HandheldCompanion.Managers;
+using HandheldCompanion.Platforms;
+using HandheldCompanion.Utils;
+using Microsoft.Win32;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Windows.Media;
+
+namespace HandheldCompanion.Controls;
+
+public class ProcessEx : IDisposable
+{
+ public enum ProcessFilter
+ {
+ Allowed = 0,
+ Restricted = 1,
+ Ignored = 2,
+ HandheldCompanion = 3,
+ Desktop = 4
+ }
+
+ private const string AppCompatRegistry = @"Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers";
+ public static string RunAsAdminRegistryValue = "RUNASADMIN";
+ public static string DisabledMaximizedWindowedValue = "DISABLEDXMAXIMIZEDWINDOWEDMODE";
+ public static string HighDPIAwareValue = "HIGHDPIAWARE";
+
+ public Process Process { get; private set; }
+ public int ProcessId { get; private set; }
+
+ public ProcessFilter Filter { get; set; }
+ public PlatformType Platform { get; set; }
+ public string Path { get; set; }
+ public ImageSource ProcessIcon { get; private set; }
+
+
+ public ProcessThread MainThread { get; set; }
+ public IntPtr MainWindowHandle { get; set; }
+
+ public ConcurrentList Children = new();
+
+ public EventHandler Refreshed;
+
+ private ThreadWaitReason prevThreadWaitReason = ThreadWaitReason.UserRequest;
+
+ public ProcessEx() { }
+ public ProcessEx(Process process, string path, string executable, ProcessFilter filter)
+ {
+ Process = process;
+ ProcessId = process.Id;
+ Path = path;
+ Executable = executable;
+ MainWindowTitle = path;
+ Filter = filter;
+
+ if (!string.IsNullOrEmpty(Path) && File.Exists(Path))
+ {
+ var icon = Icon.ExtractAssociatedIcon(Path);
+ if (icon is not null)
+ {
+ ProcessIcon = icon.ToImageSource();
+ }
+ }
+ }
+
+ public static string GetAppCompatFlags(string Path)
+ {
+ if (string.IsNullOrEmpty(Path))
+ return string.Empty;
+
+ using (var key = Registry.CurrentUser.OpenSubKey(AppCompatRegistry))
+ {
+ string valueStr = (string)key?.GetValue(Path);
+ return valueStr;
+ }
+ }
+
+ public static void SetAppCompatFlag(string Path, string Flag, bool value)
+ {
+ if (string.IsNullOrEmpty(Path))
+ return;
+
+ using (var key = Registry.CurrentUser.CreateSubKey(AppCompatRegistry, RegistryKeyPermissionCheck.ReadWriteSubTree))
+ {
+ if (key != null)
+ {
+ List values = new List { "~" }; ;
+ string valueStr = (string)key.GetValue(Path);
+
+ if (!string.IsNullOrEmpty(valueStr))
+ values = valueStr.Split(' ').ToList();
+
+ values.Remove(Flag);
+
+ if (value)
+ values.Add(Flag);
+
+ if (values.Count == 1 && values[0] == "~" && !string.IsNullOrEmpty(valueStr))
+ key.DeleteValue(Path);
+ else
+ key.SetValue(Path, string.Join(" ", values), RegistryValueKind.String);
+ }
+ }
+ }
+
+ public bool FullScreenOptimization
+ {
+ get
+ {
+ string valueStr = GetAppCompatFlags(Path);
+ return !string.IsNullOrEmpty(valueStr)
+ && valueStr.Split(' ').Any(s => s == DisabledMaximizedWindowedValue);
+ }
+ }
+
+ public bool HighDPIAware
+ {
+ get
+ {
+ string valueStr = GetAppCompatFlags(Path);
+ return !string.IsNullOrEmpty(valueStr)
+ && valueStr.Split(' ').Any(s => s == HighDPIAwareValue);
+ }
+ }
+
+ public string MainWindowTitle { get; private set; }
+
+ public string Executable { get; set; }
+
+ private bool _isSuspended;
+ public bool IsSuspended
+ {
+ get => _isSuspended;
+ set
+ {
+ if (value)
+ {
+ if (prevThreadWaitReason == ThreadWaitReason.Suspended)
+ return;
+
+ ProcessManager.SuspendProcess(this);
+ }
+ else
+ {
+ ProcessManager.ResumeProcess(this);
+ }
+ _isSuspended = value;
+ }
+ }
+
+ public void Refresh()
+ {
+ if (Process.HasExited)
+ return;
+
+ if (MainThread is null)
+ return;
+
+ switch (MainThread.ThreadState)
+ {
+ case ThreadState.Wait:
+ {
+ // monitor if the process main thread was suspended or resumed
+ if (MainThread.WaitReason != prevThreadWaitReason)
+ {
+ prevThreadWaitReason = MainThread.WaitReason;
+ _isSuspended = prevThreadWaitReason == ThreadWaitReason.Suspended;
+ }
+ }
+ break;
+
+ case ThreadState.Terminated:
+ {
+ // dispose from MainThread
+ MainThread.Dispose();
+ MainThread = null;
+ }
+ break;
+ }
+
+ Refreshed?.Invoke(this, EventArgs.Empty);
+ }
+
+ public void RefreshChildProcesses()
+ {
+ // refresh all child processes
+ var childs = ProcessUtils.GetChildIds(Process);
+
+ // remove exited children
+ foreach (var pid in childs)
+ Children.Remove(pid);
+
+ // raise event on new children
+ foreach (var pid in childs)
+ Children.Add(pid);
+ }
+
+ public void Dispose()
+ {
+ Process?.Dispose();
+ MainThread?.Dispose();
+ Children.Dispose();
+
+ GC.SuppressFinalize(this); //now, the finalizer won't be called
+ }
+
+ internal void MainThreadDisposed()
+ {
+ MainThread = ProcessManager.GetMainThread(Process);
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Controls/ProcessEx.xaml.cs b/HandheldCompanion/Controls/ProcessEx.xaml.cs
deleted file mode 100644
index 6482b7ca9..000000000
--- a/HandheldCompanion/Controls/ProcessEx.xaml.cs
+++ /dev/null
@@ -1,327 +0,0 @@
-using HandheldCompanion.Managers;
-using HandheldCompanion.Platforms;
-using HandheldCompanion.Utils;
-using Microsoft.Win32;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Drawing;
-using System.IO;
-using System.Linq;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Media;
-
-namespace HandheldCompanion.Controls;
-
-///
-/// Logique d'interaction pour ProcessEx.xaml
-///
-public partial class ProcessEx : UserControl, IDisposable
-{
- public enum ProcessFilter
- {
- Allowed = 0,
- Restricted = 1,
- Ignored = 2,
- HandheldCompanion = 3,
- Desktop = 4
- }
-
- public string _Executable;
- private string _Title;
- public string Path;
-
- public ProcessFilter Filter;
- public PlatformType Platform { get; set; }
-
- public ImageSource imgSource;
-
- public ProcessThread MainThread;
- public IntPtr MainWindowHandle;
-
- private ThreadState prevThreadState = ThreadState.Terminated;
- private ThreadWaitReason prevThreadWaitReason = ThreadWaitReason.UserRequest;
-
- public Process Process;
- private readonly int ProcessId;
- private LockObject updateLock = new();
-
- public ConcurrentList Children = new();
-
- private const string AppCompatRegistry = @"Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers";
- public static string RunAsAdminRegistryValue = "RUNASADMIN";
- public static string DisabledMaximizedWindowedValue = "DISABLEDXMAXIMIZEDWINDOWEDMODE";
- public static string HighDPIAwareValue = "HIGHDPIAWARE";
-
- public ProcessEx()
- {
- InitializeComponent();
- }
-
- public ProcessEx(Process process, string path, string executable, ProcessFilter filter) : this()
- {
- Process = process;
- ProcessId = process.Id;
- Path = path;
- Executable = executable;
- MainWindowTitle = path;
- Filter = filter;
-
- if (!string.IsNullOrEmpty(path) && File.Exists(path))
- {
- var icon = Icon.ExtractAssociatedIcon(Path);
- if (icon is not null)
- {
- imgSource = icon.ToImageSource();
- ProcessIcon.Source = imgSource;
- }
- }
- }
-
- public static string GetAppCompatFlags(string Path)
- {
- if (string.IsNullOrEmpty(Path))
- return string.Empty;
-
- using (var key = Registry.CurrentUser.OpenSubKey(AppCompatRegistry))
- {
- string valueStr = (string)key?.GetValue(Path);
- return valueStr;
- }
- }
-
- public static void SetAppCompatFlag(string Path, string Flag, bool value)
- {
- if (string.IsNullOrEmpty(Path))
- return;
-
- using (var key = Registry.CurrentUser.CreateSubKey(AppCompatRegistry, RegistryKeyPermissionCheck.ReadWriteSubTree))
- {
- if (key != null)
- {
- List values = new List { "~" }; ;
- string valueStr = (string)key.GetValue(Path);
-
- if (!string.IsNullOrEmpty(valueStr))
- values = valueStr.Split(' ').ToList();
-
- values.Remove(Flag);
-
- if (value)
- values.Add(Flag);
-
- if (values.Count == 1 && values[0] == "~" && !string.IsNullOrEmpty(valueStr))
- key.DeleteValue(Path);
- else
- key.SetValue(Path, string.Join(" ", values), RegistryValueKind.String);
- }
- }
- }
-
- public bool FullScreenOptimization
- {
- get
- {
- string valueStr = GetAppCompatFlags(Path);
- return !string.IsNullOrEmpty(valueStr)
- && valueStr.Split(' ').Any(s => s == DisabledMaximizedWindowedValue);
- }
-
- set
- {
- SetAppCompatFlag(Path, DisabledMaximizedWindowedValue, value);
- }
- }
-
- public bool HighDPIAware
- {
- get
- {
- string valueStr = GetAppCompatFlags(Path);
- return !string.IsNullOrEmpty(valueStr)
- && valueStr.Split(' ').Any(s => s == HighDPIAwareValue);
- }
-
- set
- {
- SetAppCompatFlag(Path, HighDPIAwareValue, value);
- }
- }
-
- public string MainWindowTitle
- {
- get => _Title;
-
- set
- {
- _Title = value;
- TitleTextBlock.Text = value;
- }
- }
-
- public string Executable
- {
- get => _Executable;
-
- set
- {
- _Executable = value;
- ExecutableTextBlock.Text = value;
- }
- }
-
- public int GetProcessId()
- {
- return ProcessId;
- }
-
- public bool HasExited
- {
- get
- {
- if (Process is not null)
- return Process.HasExited;
-
- return true;
- }
- }
-
- public bool IsSuspended
- {
- get
- {
- return prevThreadWaitReason == ThreadWaitReason.Suspended;
- }
- }
-
- public void Refresh()
- {
- if (Process.HasExited)
- return;
-
- // UI thread (async)
- Application.Current.Dispatcher.BeginInvoke(() =>
- {
- using (new ScopedLock(updateLock))
- {
- if (MainThread is null)
- return;
-
- switch (MainThread.ThreadState)
- {
- case ThreadState.Wait:
- {
- // monitor if the process main thread was suspended or resumed
- if (MainThread.WaitReason != prevThreadWaitReason)
- {
- prevThreadWaitReason = MainThread.WaitReason;
-
- switch (prevThreadWaitReason)
- {
- case ThreadWaitReason.Suspended:
- SuspendToggle.IsOn = true;
- break;
-
- default:
- SuspendToggle.IsOn = false;
- break;
- }
- }
- }
- break;
-
- case ThreadState.Terminated:
- {
- // dispose from MainThread
- MainThread.Dispose();
- MainThread = null;
- }
- break;
- }
-
- // update previous state
- prevThreadState = MainThread.ThreadState;
-
- T_FullScreenOptimization.IsOn = !FullScreenOptimization;
- T_HighDPIAware.IsOn = !HighDPIAware;
- }
- });
- }
-
- public void RefreshChildProcesses()
- {
- // refresh all child processes
- var childs = ProcessUtils.GetChildIds(Process);
-
- // remove exited children
- foreach (var pid in childs)
- Children.Remove(pid);
-
- // raise event on new children
- foreach (var pid in childs)
- Children.Add(pid);
- }
-
- private void SuspendToggle_Toggled(object sender, RoutedEventArgs e)
- {
- // wait until lock is released
- if (updateLock)
- return;
-
- switch (SuspendToggle.IsOn)
- {
- case true:
- {
- if (prevThreadWaitReason == ThreadWaitReason.Suspended)
- return;
-
- ProcessManager.SuspendProcess(this);
- }
- break;
- case false:
- ProcessManager.ResumeProcess(this);
- break;
- }
- }
-
- private void B_KillProcess_Clicked(object sender, RoutedEventArgs e)
- {
- if (Process is not null)
- Process.Kill();
- }
-
- public void Dispose()
- {
- if (Process is not null)
- Process.Dispose();
- if (MainThread is not null)
- MainThread.Dispose();
- Children.Dispose();
-
- GC.SuppressFinalize(this); //now, the finalizer won't be called
- }
-
- private void FullScreenOptimization_Toggled(object sender, RoutedEventArgs e)
- {
- // wait until lock is released
- if (updateLock)
- return;
-
- FullScreenOptimization = !T_FullScreenOptimization.IsOn;
- }
-
- private void HighDPIAware_Toggled(object sender, RoutedEventArgs e)
- {
- // wait until lock is released
- if (updateLock)
- return;
-
- HighDPIAware = !T_HighDPIAware.IsOn;
- }
-
- internal void MainThreadDisposed()
- {
- MainThread = ProcessManager.GetMainThread(Process);
- }
-}
\ No newline at end of file
diff --git a/HandheldCompanion/Converters/InverseBooleanToVisibilityConverter.cs b/HandheldCompanion/Converters/InverseBooleanToVisibilityConverter.cs
new file mode 100644
index 000000000..544a42e55
--- /dev/null
+++ b/HandheldCompanion/Converters/InverseBooleanToVisibilityConverter.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Globalization;
+using System.Windows;
+using System.Windows.Data;
+
+namespace HandheldCompanion.Converters;
+
+public sealed class InverseBooleanToVisibilityConverter : IValueConverter
+{
+ ///
+ /// Convert bool or Nullable bool to Visibility
+ ///
+ /// bool or Nullable bool
+ /// Visibility
+ /// null
+ /// null
+ /// Visible or Collapsed
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ var bValue = false;
+ if (value is bool)
+ {
+ bValue = (bool)value;
+ }
+ else if (value is bool?)
+ {
+ var tmp = (bool?)value;
+ bValue = tmp.HasValue ? tmp.Value : false;
+ }
+
+ return !bValue ? Visibility.Visible : Visibility.Collapsed;
+ }
+
+ ///
+ /// Convert Visibility to boolean
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is Visibility)
+ return (Visibility)value != Visibility.Visible;
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Converters/IsNullConverter.cs b/HandheldCompanion/Converters/IsNullConverter.cs
new file mode 100644
index 000000000..c47fa8aa5
--- /dev/null
+++ b/HandheldCompanion/Converters/IsNullConverter.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace HandheldCompanion.Converters;
+
+public class IsNullConverter : IValueConverter
+{
+ public object Convert(object? value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is int intValue)
+ return intValue == 0;
+
+ return value is null;
+ }
+
+ public object ConvertBack(object? value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new InvalidOperationException("IsNullConverter can only be used OneWay.");
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/DSU/DSUServer.cs b/HandheldCompanion/DSU/DSUServer.cs
index dedc8ad03..6000afa92 100644
--- a/HandheldCompanion/DSU/DSUServer.cs
+++ b/HandheldCompanion/DSU/DSUServer.cs
@@ -422,7 +422,7 @@ private void ResetUDPConn()
uint IOC_IN = 0x80000000;
uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
- udpSock.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
+ udpSock?.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
}
public bool Start()
@@ -559,36 +559,35 @@ private bool ReportToBuffer(byte[] outputData, ref int outIdx)
//motion timestamp
Array.Copy(BitConverter.GetBytes((ulong)TimerManager.GetElapsedSeconds()), 0, outputData, outIdx, 8);
-
outIdx += 8;
// Accelerometer
// accelXG
- Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Accelerometer.X), 0, outputData, outIdx, 4);
+ Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Accelerometer[GyroState.SensorState.DSU].X), 0, outputData, outIdx, 4);
outIdx += 4;
// accelYG
- Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Accelerometer.Y), 0, outputData, outIdx, 4);
+ Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Accelerometer[GyroState.SensorState.DSU].Y), 0, outputData, outIdx, 4);
outIdx += 4;
// accelZG
- Array.Copy(BitConverter.GetBytes(-Inputs.GyroState.Accelerometer.Z), 0, outputData, outIdx, 4);
+ Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Accelerometer[GyroState.SensorState.DSU].Z), 0, outputData, outIdx, 4);
outIdx += 4;
// Gyroscope
// angVelPitch
- Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Gyroscope.X), 0, outputData, outIdx, 4);
+ Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Gyroscope[GyroState.SensorState.DSU].X), 0, outputData, outIdx, 4);
outIdx += 4;
// angVelYaw
- Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Gyroscope.Y), 0, outputData, outIdx, 4);
+ Array.Copy(BitConverter.GetBytes(-Inputs.GyroState.Gyroscope[GyroState.SensorState.DSU].Y), 0, outputData, outIdx, 4);
outIdx += 4;
// angVelRoll
- Array.Copy(BitConverter.GetBytes(-Inputs.GyroState.Gyroscope.Z), 0, outputData, outIdx, 4);
+ Array.Copy(BitConverter.GetBytes(-Inputs.GyroState.Gyroscope[GyroState.SensorState.DSU].Z), 0, outputData, outIdx, 4);
outIdx += 4;
}
return true;
}
- public void Tick(long ticks)
+ public void Tick(long ticks, float delta)
{
if (!running)
return;
diff --git a/HandheldCompanion/Devices/AOKZOE/AOKZOEA1.cs b/HandheldCompanion/Devices/AOKZOE/AOKZOEA1.cs
index dcf62849b..f00bb45cd 100644
--- a/HandheldCompanion/Devices/AOKZOE/AOKZOEA1.cs
+++ b/HandheldCompanion/Devices/AOKZOE/AOKZOEA1.cs
@@ -18,8 +18,9 @@ public AOKZOEA1()
nTDP = new double[] { 15, 15, 20 };
cTDP = new double[] { 4, 28 };
GfxClock = new double[] { 100, 2200 };
- CpuClock = 4700;
-
+ CpuClock = 4700;
+
+ GyrometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
@@ -27,7 +28,7 @@ public AOKZOEA1()
{ 'Z', 'Y' }
};
- AccelerometerAxis = new Vector3(-1.0f, 1.0f, -1.0f);
+ AccelerometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
diff --git a/HandheldCompanion/Devices/ASUS/ROGAlly.cs b/HandheldCompanion/Devices/ASUS/ROGAlly.cs
index 7e6c9f516..803598f71 100644
--- a/HandheldCompanion/Devices/ASUS/ROGAlly.cs
+++ b/HandheldCompanion/Devices/ASUS/ROGAlly.cs
@@ -94,7 +94,7 @@ public ROGAlly()
GfxClock = new double[] { 100, 2700 };
CpuClock = 5100;
- GyrometerAxis = new Vector3(-1.0f, 1.0f, 1.0f);
+ GyrometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
@@ -102,7 +102,7 @@ public ROGAlly()
{ 'Z', 'Y' }
};
- AccelerometerAxis = new Vector3(1.0f, 1.0f, 1.0f);
+ AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
@@ -122,31 +122,38 @@ public ROGAlly()
DynamicLightingCapabilities |= LEDLevel.Wheel;
DynamicLightingCapabilities |= LEDLevel.Ambilight;
- DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileROGAllySilentName, Properties.Resources.PowerProfileROGAllySilentDescription)
+ DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileROGAllyBetterBattery, Properties.Resources.PowerProfileROGAllyBetterBatteryDesc)
{
Default = true,
DeviceDefault = true,
OEMPowerMode = (int)AsusMode.Silent,
OSPowerMode = OSPowerMode.BetterBattery,
- Guid = new("961cc777-2547-4f9d-8174-7d86181b8a7a")
+ CPUBoostLevel = CPUBoostLevel.Disabled,
+ Guid = new("961cc777-2547-4f9d-8174-7d86181b8a7a"),
+ TDPOverrideEnabled = true,
+ TDPOverrideValues = new[] { 10.0d, 10.0d, 10.0d }
});
- DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileROGAllyPerformanceName, Properties.Resources.PowerProfileROGAllyPerformanceDescription)
+ DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileROGAllyBetterPerformance, Properties.Resources.PowerProfileROGAllyBetterPerformanceDesc)
{
Default = true,
DeviceDefault = true,
OEMPowerMode = (int)AsusMode.Performance,
OSPowerMode = OSPowerMode.BetterPerformance,
- Guid = new("3af9B8d9-7c97-431d-ad78-34a8bfea439f")
+ Guid = new("3af9B8d9-7c97-431d-ad78-34a8bfea439f"),
+ TDPOverrideEnabled = true,
+ TDPOverrideValues = new[] { 15.0d, 15.0d, 15.0d }
});
- DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileROGAllyTurboName, Properties.Resources.PowerProfileROGAllyTurboDescription)
+ DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileROGAllyBestPerformance, Properties.Resources.PowerProfileROGAllyBestPerformanceDesc)
{
Default = true,
DeviceDefault = true,
OEMPowerMode = (int)AsusMode.Turbo,
OSPowerMode = OSPowerMode.BestPerformance,
- Guid = new("ded574b5-45a0-4f42-8737-46345c09c238")
+ Guid = new("ded574b5-45a0-4f42-8737-46345c09c238"),
+ TDPOverrideEnabled = true,
+ TDPOverrideValues = new[] { 25.0d, 25.0d, 25.0d }
});
OEMChords.Add(new DeviceChord("CC",
@@ -161,14 +168,14 @@ public ROGAlly()
// M1 and M2 do a repeating input when holding the button
OEMChords.Add(new DeviceChord("M1",
- new List { KeyCode.F17 },
- new List { KeyCode.F17 },
+ new List { KeyCode.F18 },
+ new List { KeyCode.F18 },
false, ButtonFlags.OEM3
));
OEMChords.Add(new DeviceChord("M2",
- new List { KeyCode.F18 },
- new List { KeyCode.F18 },
+ new List { KeyCode.F17 },
+ new List { KeyCode.F17 },
false, ButtonFlags.OEM4
));
}
@@ -261,7 +268,7 @@ public ROGAlly()
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
- private byte[] M1F17M2F18 = new byte[64]
+ private byte[] M1F18M2F17 = new byte[64]
{
0x5A, 0xD1, 0x02, 0x08, 0x2C, 0x02, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x30, 0x00, 0x00,
@@ -664,7 +671,7 @@ 23. Commit and reset 4 of 4
SendHidControlWrite(flushBufferWriteChanges); // 16
// Choose the appropriate mapping based on the 'Remap' flag
- SendHidControlWrite(Remap ? M1F17M2F18 : M1M2Default); // Step 17
+ SendHidControlWrite(Remap ? M1F18M2F17 : M1M2Default); // Step 17
SendHidControlWrite(flushBufferWriteChanges); // 18
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEO2.cs b/HandheldCompanion/Devices/AYANEO/AYANEO2.cs
index 77c6ad751..eda197f9a 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEO2.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEO2.cs
@@ -1,93 +1,17 @@
-using HandheldCompanion.Inputs;
-using System.Collections.Generic;
-using System.Numerics;
-using WindowsInput.Events;
-namespace HandheldCompanion.Devices;
-using static HandheldCompanion.Utils.DeviceUtils;
-
-public class AYANEO2 : AYANEO.AYANEODevice
-{
- public AYANEO2()
- {
- // device specific settings
- ProductIllustration = "device_aya_2";
- ProductModel = "AYANEO2";
-
- // https://www.amd.com/en/products/apu/amd-ryzen-7-6800u
- nTDP = new double[] { 15, 15, 20 };
- cTDP = new double[] { 3, 33 };
- GfxClock = new double[] { 100, 2200 };
- CpuClock = 4700;
-
- GyrometerAxis = new Vector3(1.0f, 1.0f, 1.0f);
- GyrometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- AccelerometerAxis = new Vector3(1.0f, 1.0f, 1.0f);
- AccelerometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- // device specific capacities
- Capabilities = DeviceCapabilities.FanControl;
- Capabilities |= DeviceCapabilities.DynamicLighting;
- DynamicLightingCapabilities |= LEDLevel.SolidColor;
-
- ECDetails = new ECDetails
- {
- AddressFanControl = 0x44A,
- AddressFanDuty = 0x44B,
- AddressStatusCommandPort = 0x4E,
- AddressDataPort = 0x4F,
- FanValueMin = 0,
- FanValueMax = 100
- };
-
- OEMChords.Add(new DeviceChord("Custom Key Top Right",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F16 },
- new List { KeyCode.F16, KeyCode.LWin, KeyCode.RControlKey },
- false, ButtonFlags.OEM3
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Top Left",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F15 },
- new List { KeyCode.F15, KeyCode.LWin, KeyCode.RControlKey },
- false, ButtonFlags.OEM4
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Big",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F17 },
- new List { KeyCode.F17, KeyCode.LWin, KeyCode.RControlKey },
- false, ButtonFlags.OEM1
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Small",
- new List { KeyCode.LWin, KeyCode.D },
- new List { KeyCode.LWin, KeyCode.D },
- false, ButtonFlags.OEM2
- ));
+namespace HandheldCompanion.Devices;
+
+public class AYANEO2 : AYANEO.AYANEODeviceCEc
+{
+ public AYANEO2()
+ {
+ // device specific settings
+ this.ProductIllustration = "device_aya_2";
+ this.ProductModel = "AYANEO2";
+
+ // https://www.amd.com/en/products/apu/amd-ryzen-7-6800u
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 3, 33 };
+ this.GfxClock = new double[] { 100, 2200 };
+ this.CpuClock = 4700;
}
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.OEM1:
- return "\uE003";
- case ButtonFlags.OEM2:
- return "\u220B";
- case ButtonFlags.OEM3:
- return "\u220A";
- case ButtonFlags.OEM4:
- return "\u2209";
- }
-
- return defaultGlyph;
- }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEO2021.cs b/HandheldCompanion/Devices/AYANEO/AYANEO2021.cs
index 5f9d66e63..48804a09f 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEO2021.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEO2021.cs
@@ -1,72 +1,75 @@
-using HandheldCompanion.Inputs;
-using System.Collections.Generic;
-using WindowsInput.Events;
-
-namespace HandheldCompanion.Devices;
-
-public class AYANEO2021 : IDevice
-{
- public AYANEO2021()
- {
- // device specific settings
- ProductIllustration = "device_aya_2021";
- ProductModel = "AYANEO2021";
-
- // https://www.amd.com/en/support/apu/amd-ryzen-processors/amd-ryzen-5-mobile-processors-radeon-graphics/amd-ryzen-5-4500u
- nTDP = new double[] { 15, 15, 20 };
- cTDP = new double[] { 3, 25 };
- GfxClock = new double[] { 100, 1500 };
- CpuClock = 4000;
-
- GyrometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- AccelerometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- OEMChords.Add(new DeviceChord("WIN key",
- new List { KeyCode.LWin },
- new List { KeyCode.LWin },
- false, ButtonFlags.OEM1
- ));
-
- // Conflicts with OS
- //listeners.Add("TM key", new ChordClick(KeyCode.RAlt, KeyCode.RControlKey, KeyCode.Delete));
-
- OEMChords.Add(new DeviceChord("ESC key",
- new List { KeyCode.Escape },
- new List { KeyCode.Escape },
- false, ButtonFlags.OEM2
- ));
-
- // Conflicts with Ayaspace when installed
- OEMChords.Add(new DeviceChord("KB key",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.O },
- new List { KeyCode.O, KeyCode.LWin, KeyCode.RControlKey },
- false, ButtonFlags.OEM3
- ));
+using HandheldCompanion.Inputs;
+using System.Collections.Generic;
+using System.Numerics;
+using WindowsInput.Events;
+
+namespace HandheldCompanion.Devices;
+
+public class AYANEO2021 : IDevice
+{
+ public AYANEO2021()
+ {
+ // device specific settings
+ this.ProductIllustration = "device_aya_2021";
+ this.ProductModel = "AYANEO2021";
+
+ // https://www.amd.com/en/support/apu/amd-ryzen-processors/amd-ryzen-5-mobile-processors-radeon-graphics/amd-ryzen-5-4500u
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 3, 25 };
+ this.GfxClock = new double[] { 100, 1500 };
+ this.CpuClock = 4000;
+
+ this.GyrometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
+ this.GyrometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ this.AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
+ this.AccelerometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ this.OEMChords.Add(new DeviceChord("WIN key",
+ new List { KeyCode.LWin },
+ new List { KeyCode.LWin },
+ false, ButtonFlags.OEM1
+ ));
+
+ // Conflicts with OS
+ //listeners.Add("TM key", new ChordClick(KeyCode.RAlt, KeyCode.RControlKey, KeyCode.Delete));
+
+ this.OEMChords.Add(new DeviceChord("ESC key",
+ new List { KeyCode.Escape },
+ new List { KeyCode.Escape },
+ false, ButtonFlags.OEM2
+ ));
+
+ // Conflicts with Ayaspace when installed
+ this.OEMChords.Add(new DeviceChord("KB key",
+ new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.O },
+ new List { KeyCode.O, KeyCode.LWin, KeyCode.RControlKey },
+ false, ButtonFlags.OEM3
+ ));
}
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.OEM1:
- return "\uE008";
- case ButtonFlags.OEM2:
- return "\u242F";
- case ButtonFlags.OEM3:
- return "\u243D";
- }
-
- return defaultGlyph;
- }
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.OEM1:
+ return "\uE008";
+ case ButtonFlags.OEM2:
+ return "\u242F";
+ case ButtonFlags.OEM3:
+ return "\u243D";
+ }
+
+ return defaultGlyph;
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEO2021Pro.cs b/HandheldCompanion/Devices/AYANEO/AYANEO2021Pro.cs
index 208e4b65d..9613bc708 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEO2021Pro.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEO2021Pro.cs
@@ -5,9 +5,9 @@ public class AYANEO2021Pro : AYANEO2021
public AYANEO2021Pro()
{
// https://www.amd.com/en/support/apu/amd-ryzen-processors/amd-ryzen-7-mobile-processors-radeon-graphics/amd-ryzen-7-4800u
- nTDP = new double[] { 15, 15, 20 };
- cTDP = new double[] { 3, 25 };
- GfxClock = new double[] { 100, 1750 };
- CpuClock = 4200;
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 3, 25 };
+ this.GfxClock = new double[] { 100, 1750 };
+ this.CpuClock = 4200;
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEO2S.cs b/HandheldCompanion/Devices/AYANEO/AYANEO2S.cs
index 3c739ff70..a97cf04a0 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEO2S.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEO2S.cs
@@ -5,9 +5,9 @@ public class AYANEO2S : AYANEO2
public AYANEO2S()
{
// https://www.amd.com/en/products/apu/amd-ryzen-7-7840U
- nTDP = new double[] { 15, 15, 20 };
- cTDP = new double[] { 3, 30 };
- GfxClock = new double[] { 100, 2700 };
- CpuClock = 5100;
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 3, 30 };
+ this.GfxClock = new double[] { 100, 2700 };
+ this.CpuClock = 5100;
}
}
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIR.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIR.cs
index 90dace153..431a5d5d5 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOAIR.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIR.cs
@@ -1,93 +1,46 @@
-using HandheldCompanion.Inputs;
-using System.Collections.Generic;
-using System.Numerics;
+using HandheldCompanion.Inputs;
+using System.Collections.Generic;
+using System.Numerics;
using WindowsInput.Events;
-namespace HandheldCompanion.Devices;
-using static HandheldCompanion.Utils.DeviceUtils;
-
-public class AYANEOAIR : AYANEO.AYANEODevice
-{
- public AYANEOAIR()
- {
- // device specific settings
- ProductIllustration = "device_aya_air";
- ProductModel = "AYANEOAir";
-
- // https://www.amd.com/en/products/apu/amd-ryzen-5-5560u
- nTDP = new double[] { 12, 12, 15 };
- cTDP = new double[] { 3, 15 };
- GfxClock = new double[] { 100, 1600 };
- CpuClock = 4000;
-
- GyrometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- AccelerometerAxis = new Vector3(-1.0f, 1.0f, -1.0f);
- AccelerometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- // device specific capacities
- Capabilities = DeviceCapabilities.FanControl;
- Capabilities |= DeviceCapabilities.DynamicLighting;
- DynamicLightingCapabilities |= LEDLevel.SolidColor;
-
- ECDetails = new ECDetails
- {
- AddressFanControl = 0x44A,
- AddressFanDuty = 0x44B,
- AddressStatusCommandPort = 0x4E,
- AddressDataPort = 0x4F,
- FanValueMin = 0,
- FanValueMax = 100
- };
-
- OEMChords.Add(new DeviceChord("Custom Key Top Right",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F10 },
- new List { KeyCode.F10, KeyCode.LWin, KeyCode.RControlKey },
- false, ButtonFlags.OEM3
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Top Left",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F11 },
- new List { KeyCode.F11, KeyCode.LWin, KeyCode.RControlKey },
- false, ButtonFlags.OEM4
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Big",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F12 },
- new List { KeyCode.F12, KeyCode.LWin, KeyCode.RControlKey },
- false, ButtonFlags.OEM1
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Small",
- new List { KeyCode.LWin, KeyCode.D },
- new List { KeyCode.LWin, KeyCode.D },
- false, ButtonFlags.OEM2
- ));
- }
+namespace HandheldCompanion.Devices;
+
+public class AYANEOAIR : AYANEO.AYANEODeviceCEc
+{
+ public AYANEOAIR()
+ {
+ // device specific settings
+ this.ProductIllustration = "device_aya_air";
+ this.ProductModel = "AYANEOAir";
+
+ // https://www.amd.com/en/products/apu/amd-ryzen-5-5560u
+ this.nTDP = new double[] { 12, 12, 15 };
+ this.cTDP = new double[] { 3, 15 };
+ this.GfxClock = new double[] { 100, 1600 };
+ this.CpuClock = 4000;
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.OEM1:
- return "\uE003";
- case ButtonFlags.OEM2:
- return "\u220B";
- case ButtonFlags.OEM3:
- return "\u220A";
- case ButtonFlags.OEM4:
- return "\u2209";
- }
-
- return defaultGlyph;
- }
+ this.GyrometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
+ this.AccelerometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
+
+ this.OEMChords.Clear();
+ this.OEMChords.Add(new DeviceChord("Custom Key Big",
+ new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F12 },
+ new List { KeyCode.F12, KeyCode.LWin, KeyCode.RControlKey },
+ false, ButtonFlags.OEM1
+ ));
+ this.OEMChords.Add(new DeviceChord("Custom Key Small",
+ new List { KeyCode.LWin, KeyCode.D },
+ new List { KeyCode.LWin, KeyCode.D },
+ false, ButtonFlags.OEM2
+ ));
+ this.OEMChords.Add(new DeviceChord("Custom Key Top Left",
+ new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F11 },
+ new List { KeyCode.F11, KeyCode.LWin, KeyCode.RControlKey },
+ false, ButtonFlags.OEM3
+ ));
+ this.OEMChords.Add(new DeviceChord("Custom Key Top Right",
+ new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F10 },
+ new List { KeyCode.F10, KeyCode.LWin, KeyCode.RControlKey },
+ false, ButtonFlags.OEM4
+ ));
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs
index 90aed0744..0ffdbd5c5 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs
@@ -9,32 +9,32 @@ public class AYANEOAIR1S : AYANEOAIR
public AYANEOAIR1S()
{
// https://www.amd.com/en/products/apu/amd-ryzen-7-7840u
- nTDP = new double[] { 15, 15, 20 };
- cTDP = new double[] { 4, 28 };
- GfxClock = new double[] { 100, 2700 };
- CpuClock = 5100;
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 4, 28 };
+ this.GfxClock = new double[] { 100, 2700 };
+ this.CpuClock = 5100;
- OEMChords.Clear();
+ this.OEMChords.Clear();
- OEMChords.Add(new DeviceChord("Custom Key Top Right",
+ this.OEMChords.Add(new DeviceChord("Custom Key Top Right",
new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F16 },
new List { KeyCode.F16, KeyCode.LWin, KeyCode.RControlKey },
false, ButtonFlags.OEM3
));
- OEMChords.Add(new DeviceChord("Custom Key Top Left",
+ this.OEMChords.Add(new DeviceChord("Custom Key Top Left",
new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F15 },
new List { KeyCode.F15, KeyCode.LWin, KeyCode.RControlKey },
false, ButtonFlags.OEM4
));
- OEMChords.Add(new DeviceChord("Custom Key Big",
+ this.OEMChords.Add(new DeviceChord("Custom Key Big",
new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F17 },
new List { KeyCode.F17, KeyCode.LWin, KeyCode.RControlKey },
false, ButtonFlags.OEM1
));
- OEMChords.Add(new DeviceChord("Custom Key Small",
+ this.OEMChords.Add(new DeviceChord("Custom Key Small",
new List { KeyCode.LWin, KeyCode.D },
new List { KeyCode.LWin, KeyCode.D },
false, ButtonFlags.OEM2
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIRLite.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIRLite.cs
index 68ec80101..35c8e6545 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOAIRLite.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIRLite.cs
@@ -5,9 +5,9 @@ public class AYANEOAIRLite : AYANEOAIR
public AYANEOAIRLite()
{
// https://www.amd.com/en/products/apu/amd-ryzen-5-5560u
- nTDP = new double[] { 8, 8, 12 };
- cTDP = new double[] { 3, 12 };
- GfxClock = new double[] { 100, 1600 };
- CpuClock = 4000;
+ this.nTDP = new double[] { 8, 8, 12 };
+ this.cTDP = new double[] { 3, 12 };
+ this.GfxClock = new double[] { 100, 1600 };
+ this.CpuClock = 4000;
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlus.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlus.cs
index 320d36b75..15861866b 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlus.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlus.cs
@@ -1,333 +1,31 @@
using HandheldCompanion.Devices.AYANEO;
-using HandheldCompanion.Inputs;
-using System;
-using System.Collections.Generic;
-using System.Numerics;
-using System.Windows.Media;
-using WindowsInput.Events;
-using static HandheldCompanion.Utils.DeviceUtils;
-
-namespace HandheldCompanion.Devices;
-
-public class AYANEOAIRPlus : AYANEODevice
-{
- public AYANEOAIRPlus()
- {
- // device specific settings
- ProductIllustration = "device_aya_air";
- ProductModel = "AYANEOAir";
-
- GyrometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
- GyrometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- AccelerometerAxis = new Vector3(-1.0f, -1.0f, -1.0f);
- AccelerometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- // device specific capacities
- // todo, missing fan control
- Capabilities |= DeviceCapabilities.DynamicLighting;
- DynamicLightingCapabilities |= LEDLevel.SolidColor;
- DynamicLightingCapabilities |= LEDLevel.Ambilight;
+using System.Collections.Generic;
+using System.Numerics;
- // Ayaneo Air Plus info based on:
- // https://github.com/JustEnoughLinuxOS/distribution/blob/main/packages/hardware/quirks/devices/AYANEO%20AIR%20Plus/bin/ledcontrol
- ECDetails = new ECDetails
- {
- AddressStatusCommandPort = 0x4E,
- AddressDataPort = 0x4F,
- AddressFanControl = 0x0, // Unknown
- AddressFanDuty = 0x0, // Unknown
- FanValueMin = 0, // Unknown
- FanValueMax = 100 // Unknown
- };
-
- OEMChords.Add(new DeviceChord("Custom Key Top Right",
- new List { KeyCode.LControl, KeyCode.LWin, KeyCode.F16 },
- new List { KeyCode.F16, KeyCode.LControl, KeyCode.LWin },
- false, ButtonFlags.OEM3
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Top Left",
- new List { KeyCode.LControl, KeyCode.LWin, KeyCode.F15 },
- new List { KeyCode.F15, KeyCode.LControl, KeyCode.LWin },
- false, ButtonFlags.OEM4
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Big",
- new List { KeyCode.LControl, KeyCode.LWin, KeyCode.F17 },
- new List { KeyCode.F17, KeyCode.LControl, KeyCode.LWin },
- false, ButtonFlags.OEM1
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Small",
- new List { KeyCode.LWin, KeyCode.D },
- new List { KeyCode.D, KeyCode.LWin },
- false, ButtonFlags.OEM2
- ));
- }
-
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.OEM1:
- return "\uE003";
- case ButtonFlags.OEM2:
- return "\u220B";
- case ButtonFlags.OEM3:
- return "\u220A";
- case ButtonFlags.OEM4:
- return "\u2209";
- }
-
- return defaultGlyph;
- }
-
- private void LedOpen()
- {
- ECRamDirectWrite(0x87, ECDetails, 0xA5);
- }
-
- private void LedClose(byte param)
- {
- ECRamDirectWrite(param, ECDetails, 0x01);
- }
-
- private void LedAck()
- {
- ECRamDirectWrite(0x70, ECDetails, 0x00);
- LedClose(0x86);
- }
-
- private void LedEnable()
- {
- // Set LED ON
- LedState(0x37);
-
- LedOpen();
- ECRamDirectWrite(0x70, ECDetails, 0x00);
- LedClose(0x86);
-
- LedOpen();
- ECRamDirectWrite(0xB2, ECDetails, 0xBA);
- LedClose(0xC6);
-
- LedOpen();
- ECRamDirectWrite(0x72, ECDetails, 0xBA);
- LedClose(0x86);
-
- LedAck();
- }
-
- private void LedDisable()
- {
- // Set LED OFF
- LedState(0x31);
- }
-
- private void LedApply()
- {
- LedOpen();
- ECRamDirectWrite(0xBF, ECDetails, 0x00);
- LedClose(0xC6);
-
- LedOpen();
- ECRamDirectWrite(0x7F, ECDetails, 0x00);
- LedClose(0xC6);
-
- LedOpen();
- ECRamDirectWrite(0xC0, ECDetails, 0x00);
- LedClose(0xC6);
-
- LedOpen();
- ECRamDirectWrite(0x80, ECDetails, 0x00);
- LedClose(0x86);
-
- LedOpen();
- ECRamDirectWrite(0xC1, ECDetails, 0x05);
- LedClose(0xC6);
-
- LedOpen();
- ECRamDirectWrite(0x81, ECDetails, 0x05);
- LedClose(0xC6);
-
- LedOpen();
- ECRamDirectWrite(0xC2, ECDetails, 0x05);
- LedClose(0xC6);
-
- LedOpen();
- ECRamDirectWrite(0x82, ECDetails, 0x05);
- LedClose(0x86);
-
- LedOpen();
- ECRamDirectWrite(0xC3, ECDetails, 0x05);
- LedClose(0x86);
-
- LedOpen();
- ECRamDirectWrite(0x83, ECDetails, 0x05);
- LedClose(0x86);
-
- LedOpen();
- ECRamDirectWrite(0xC4, ECDetails, 0x05);
- LedClose(0xC6);
-
- LedOpen();
- ECRamDirectWrite(0x84, ECDetails, 0x05);
- LedClose(0x86);
-
- LedOpen();
- ECRamDirectWrite(0xC5, ECDetails, 0x07);
- LedClose(0xC6);
-
- LedOpen();
- ECRamDirectWrite(0x85, ECDetails, 0x07);
- LedClose(0x86);
-
- LedAck();
- }
-
- private void LedState(byte value)
- {
- // 0x31 = off
- // 0x37 = on
- byte[] zones = { 0xB2, 0x72 };
-
- LedOpen();
- foreach (byte zone in zones)
- {
- ECRamDirectWrite(zone, ECDetails, value);
- ECRamDirectWrite(0xC6, ECDetails, 0x01);
- }
- LedAck();
- }
+namespace HandheldCompanion.Devices;
- private void SetLEDColorL(Color color)
+public class AYANEOAIRPlus : AYANEODeviceCEii
+{
+ public AYANEOAIRPlus()
{
- byte red = color.R;
- byte green = color.G;
- byte blue = color.B;
+ // device specific settings
+ this.ProductIllustration = "device_aya_air";
+ this.ProductModel = "AYANEOAir";
- LedOpen();
- for (byte i = 0xB3; i <= 0xBC; i += 3)
+ this.GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ this.GyrometerAxisSwap = new SortedDictionary
{
- ECRamDirectWrite(i, ECDetails, red);
- ECRamDirectWrite((byte)(i + 1), ECDetails, green);
- ECRamDirectWrite((byte)(i + 2), ECDetails, blue);
- }
- LedAck();
- }
-
- private void SetLEDColorR(Color color)
- {
- byte red = color.R;
- byte green = color.G;
- byte blue = color.B;
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
- LedOpen();
- for (byte i = 0x73; i <= 0x7C; i += 3)
+ this.AccelerometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ this.AccelerometerAxisSwap = new SortedDictionary
{
- ECRamDirectWrite(i, ECDetails, red);
- ECRamDirectWrite((byte)(i + 1), ECDetails, green);
- ECRamDirectWrite((byte)(i + 2), ECDetails, blue);
- }
- LedAck();
- }
-
- private void SetLEDColor(Color color)
- {
- SetLEDColorL(color);
- SetLEDColorR(color);
- }
-
- public override bool ECRamDirectWrite(ushort address, ECDetails details, byte data)
- {
- ushort address2 = BitConverter.ToUInt16(new byte[] { (byte)address, 0xD1 }, 0);
- return base.ECRamDirectWrite(address2, details, data);
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
}
-
- public override bool SetLedStatus(bool status)
- {
- switch(status)
- {
- case true:
- LedEnable();
- break;
- case false:
- LedDisable();
- break;
- }
-
- return true;
- }
-
- public override bool SetLedColor(Color MainColor, Color SecondaryColor, LEDLevel level, int speed)
- {
- if (!DynamicLightingCapabilities.HasFlag(level))
- return false;
-
- switch (level)
- {
- case LEDLevel.SolidColor:
- SetLEDColor(MainColor);
- break;
- case LEDLevel.Ambilight:
- SetLEDColorL(MainColor);
- SetLEDColorR(SecondaryColor);
- break;
- }
-
- LedApply();
-
- return true;
- }
-
- public override bool SetLedBrightness(int brightness)
- {
- // we might want to store colors on SetLedColor() and brightness on SetLedBrightness()
- // so that we can let people mess with brightness slider
- return base.SetLedBrightness(brightness);
- }
-
- /*
- private bool prevWasBlack = true;
- public override bool SetLedColor(Color MainColor, Color SecondaryColor, LEDLevel level)
- {
- if (MainColor == Colors.Black)
- {
- ayaneoLED.LedDisable();
- prevWasBlack = true;
- return true;
- }
- else if (prevWasBlack)
- {
- ayaneoLED.LedEnable();
- prevWasBlack = false;
- }
-
- // Set LED color
- ayaneoLED.SetLEDColor(MainColor);
- ayaneoLED.LedApply();
-
- return true;
- }
-
- public bool SetAmbilight(Color LEDColor1, Color LEDColor2)
- {
- // Set LED color
- ayaneoLED.SetLEDColorL(LEDColor1);
- ayaneoLED.SetLEDColorR(LEDColor2);
- ayaneoLED.LedApply();
- return true;
- }
- */
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMD.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMD.cs
index d42631ca3..7abd21f59 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMD.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMD.cs
@@ -5,9 +5,9 @@ public class AYANEOAIRPlusAMD : AYANEOAIRPlus
public AYANEOAIRPlusAMD()
{
// https://www.amd.com/en/products/apu/amd-ryzen-7-6800u
- nTDP = new double[] { 15, 15, 20 };
- cTDP = new double[] { 3, 33 };
- GfxClock = new double[] { 100, 2200 };
- CpuClock = 4700;
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 3, 33 };
+ this.GfxClock = new double[] { 100, 2200 };
+ this.CpuClock = 4700;
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMDMendocino.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMDMendocino.cs
index be7453c0a..b83816b4d 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMDMendocino.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMDMendocino.cs
@@ -5,9 +5,11 @@ public class AYANEOAIRPlusAMDMendocino : AYANEOAIRPlus
public AYANEOAIRPlusAMDMendocino()
{
// https://www.amd.com/en/products/apu/amd-ryzen-3-7320u
- nTDP = new double[] { 12, 12, 12 };
- cTDP = new double[] { 5, 15 };
- GfxClock = new double[] { 100, 1900 };
- CpuClock = 4100;
+ this.nTDP = new double[] { 12, 12, 12 };
+ this.cTDP = new double[] { 5, 15 };
+ this.GfxClock = new double[] { 100, 1900 };
+ this.CpuClock = 4100;
+
+ this.ECDetails.AddressFanDuty = 0x02;
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusIntel.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusIntel.cs
index 205af5181..0604377d0 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusIntel.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusIntel.cs
@@ -5,9 +5,11 @@ public class AYANEOAIRPlusIntel : AYANEOAIRPlus
public AYANEOAIRPlusIntel()
{
// https://www.intel.com/content/www/us/en/products/sku/226263/intel-core-i31215u-processor-10m-cache-up-to-4-40-ghz/specifications.html
- nTDP = new double[] { 15, 15, 20 };
- cTDP = new double[] { 5, 55 };
- GfxClock = new double[] { 100, 1100 };
- CpuClock = 4400;
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 5, 55 };
+ this.GfxClock = new double[] { 100, 1100 };
+ this.CpuClock = 4400;
+
+ this.ECDetails.AddressFanDuty = 0x02;
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPro.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPro.cs
index 803c97f54..b2ce18b44 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPro.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPro.cs
@@ -5,9 +5,9 @@ public class AYANEOAIRPro : AYANEOAIR
public AYANEOAIRPro()
{
// https://www.amd.com/en/products/apu/amd-ryzen-7-5825u
- nTDP = new double[] { 12, 12, 15 };
- cTDP = new double[] { 3, 18 };
- GfxClock = new double[] { 100, 2000 };
- CpuClock = 4500;
+ this.nTDP = new double[] { 12, 12, 15 };
+ this.cTDP = new double[] { 3, 18 };
+ this.GfxClock = new double[] { 100, 2000 };
+ this.CpuClock = 4500;
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEODeviceCEc.cs b/HandheldCompanion/Devices/AYANEO/AYANEODeviceCEc.cs
new file mode 100644
index 000000000..ac918cde9
--- /dev/null
+++ b/HandheldCompanion/Devices/AYANEO/AYANEODeviceCEc.cs
@@ -0,0 +1,219 @@
+using HandheldCompanion.Inputs;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Threading;
+using System.Windows.Media;
+using WindowsInput.Events;
+using static HandheldCompanion.Utils.DeviceUtils;
+
+namespace HandheldCompanion.Devices.AYANEO
+{
+ // Base class implementing FAN/RGB control for all AYANEO device using the CEc protocol
+ // All devices that arent CEii or CHc (MiniPC)
+ public class AYANEODeviceCEc : AYANEODevice
+ {
+ protected int[] rgbZones = { 1, 2, 3, 4 };
+ protected bool rgbConfirmation = true;
+
+ private bool? ledStatus;
+ private int? ledBrightness;
+ private Color? ledColorSticks;
+ private Color? ledColorAYA;
+ private LEDLevel? ledLevel;
+
+ public AYANEODeviceCEc()
+ {
+ this.Capabilities |= DeviceCapabilities.FanControl;
+ this.Capabilities |= DeviceCapabilities.DynamicLighting;
+ this.Capabilities |= DeviceCapabilities.DynamicLightingBrightness;
+ this.DynamicLightingCapabilities = LEDLevel.SolidColor;
+
+ this.ECDetails = new ECDetails
+ {
+ AddressStatusCommandPort = 0x4E,
+ AddressDataPort = 0x4F,
+ AddressFanControl = 0x44A,
+ AddressFanDuty = 0x44B,
+ FanValueMin = 0,
+ FanValueMax = 100
+ };
+
+ this.GyrometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
+ this.GyrometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ this.AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
+ this.AccelerometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ this.OEMChords.Add(new DeviceChord("Custom Key Big",
+ new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F17 },
+ new List { KeyCode.F17, KeyCode.LWin, KeyCode.RControlKey },
+ false, ButtonFlags.OEM1
+ ));
+ this.OEMChords.Add(new DeviceChord("Custom Key Small",
+ new List { KeyCode.LWin, KeyCode.D },
+ new List { KeyCode.LWin, KeyCode.D },
+ false, ButtonFlags.OEM2
+ ));
+ this.OEMChords.Add(new DeviceChord("Custom Key Top Left",
+ new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F15 },
+ new List { KeyCode.F15, KeyCode.LWin, KeyCode.RControlKey },
+ false, ButtonFlags.OEM3
+ ));
+ this.OEMChords.Add(new DeviceChord("Custom Key Top Right",
+ new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F16 },
+ new List { KeyCode.F16, KeyCode.LWin, KeyCode.RControlKey },
+ false, ButtonFlags.OEM4
+ ));
+ }
+
+ public override bool SetLedStatus(bool status)
+ {
+ lock (this.updateLock)
+ {
+ if (this.ledStatus == status) return true;
+ this.ledStatus = status;
+
+ if ((bool)this.ledStatus)
+ {
+ this.CEcRgb_GlobalOn(LEDGroup.StickBoth);
+ if (this.Capabilities.HasFlag(DeviceCapabilities.DynamicLightingSecondLEDColor)) this.CEcRgb_GlobalOn(LEDGroup.AYA);
+ }
+ else
+ {
+ this.CEcRgb_GlobalOff(LEDGroup.StickBoth);
+ if (this.Capabilities.HasFlag(DeviceCapabilities.DynamicLightingSecondLEDColor)) this.CEcRgb_GlobalOff(LEDGroup.AYA);
+ }
+
+ return true;
+ }
+ }
+
+ public override bool SetLedBrightness(int brightness)
+ {
+ lock (this.updateLock)
+ {
+ if (this.ledBrightness == brightness) return true;
+ this.ledBrightness = brightness;
+
+ if (this.ledColorSticks == null || this.ledColorAYA == null) return true;
+
+ this.CEcRgb_SetColorAll(LEDGroup.StickLeft, (Color)this.ledColorSticks);
+ this.CEcRgb_SetColorAll(LEDGroup.StickRight, (Color)this.ledColorSticks);
+ if (this.Capabilities.HasFlag(DeviceCapabilities.DynamicLightingSecondLEDColor)) this.CEcRgb_SetColorAya((Color)this.ledColorAYA);
+
+ return true;
+ }
+ }
+
+ public override bool SetLedColor(Color colorSticks, Color colorAYA, LEDLevel level, int speed)
+ {
+ lock (this.updateLock)
+ {
+ bool hasChangedSticks = this.ledColorSticks != colorSticks;
+ bool hasChangedAYA = this.ledColorAYA != colorAYA;
+ this.ledColorSticks = colorSticks;
+ this.ledColorAYA = colorAYA;
+ this.ledLevel = level;
+
+ if (this.ledBrightness == null)
+ {
+ return true;
+ }
+
+ switch ((LEDLevel)this.ledLevel)
+ {
+ case LEDLevel.SolidColor:
+ if (hasChangedSticks)
+ {
+ this.CEcRgb_SetColorAll(LEDGroup.StickLeft, (Color)this.ledColorSticks);
+ this.CEcRgb_SetColorAll(LEDGroup.StickRight, (Color)this.ledColorSticks);
+ }
+ if (hasChangedAYA && this.Capabilities.HasFlag(DeviceCapabilities.DynamicLightingSecondLEDColor))
+ {
+ this.CEcRgb_SetColorAya((Color)this.ledColorAYA);
+ }
+ break;
+ }
+
+ return true;
+ }
+ }
+
+ private void CEcRgb_GlobalOn(LEDGroup group, byte speed = 0x00)
+ {
+ this.CEcRgb_I2cWrite(group, 0x02, (byte)(0x80 + speed));
+ }
+
+ private void CEcRgb_GlobalOff(LEDGroup group)
+ {
+ this.CEcRgb_I2cWrite(group, 0x02, 0xc0);
+ }
+
+ private void CEcRgb_SlowOff(LEDGroup group)
+ {
+ this.CEcRgb_I2cWrite(group, 0x02, 0xc0 + (byte)'\a');
+ }
+
+ private void CEcRgb_SetColorAll(LEDGroup group, Color color)
+ {
+ foreach (int zone in this.rgbZones)
+ {
+ byte[] colorBytes = this.MapColorValues(zone, color);
+ this.CEcRgb_SetColorOne(group, zone, colorBytes[0], colorBytes[1], colorBytes[2]);
+ }
+ }
+
+ private void CEcRgb_SetColorAya(Color color)
+ {
+ this.CEcRgb_SetColorOne(LEDGroup.AYA, 4, color.B, color.R, color.G);
+ }
+
+ private void CEcRgb_SetColorOne(LEDGroup group, int zone, int red, int green, int blue)
+ {
+ byte zoneByte = (byte)(zone * 0x03);
+ byte redByte = (byte)this.CEcRgb_GetBrightness(red);
+ byte greenByte = (byte)this.CEcRgb_GetBrightness(green);
+ byte blueByte = (byte)this.CEcRgb_GetBrightness(blue);
+
+ this.CEcRgb_I2cWrite(group, zoneByte, redByte);
+ this.CEcRgb_I2cWrite(group, (byte)(zoneByte + 0x01), greenByte);
+ this.CEcRgb_I2cWrite(group, (byte)(zoneByte + 0x02), blueByte);
+ }
+
+ private int CEcRgb_GetBrightness(int color)
+ {
+ return (int)(((((float)color / 255) * 192) / 100) * (float)this.ledBrightness);
+ }
+
+ protected virtual byte[] MapColorValues(int zone, Color color)
+ {
+ return [color.R, color.G, color.B];
+ }
+
+ protected virtual void CEcRgb_I2cWrite(LEDGroup group, byte command, byte argument)
+ {
+ this.CEcControl_RgbI2cWrite(group, command, argument);
+ if (this.rgbConfirmation) this.CEcControl_RgbI2cWrite(LEDGroup.StickBoth, 0x00, 0x00);
+ }
+
+ protected virtual void CEcControl_RgbI2cWrite(LEDGroup group, byte command, byte argument)
+ {
+ this.ECRAMWrite(0x6d, (byte)group);
+ this.ECRAMWrite(0xb1, command);
+ this.ECRAMWrite(0xb2, argument);
+ this.ECRAMWrite(0xbf, 0x10);
+ Thread.Sleep(5); // AYASpace does this so copied it here
+ this.ECRAMWrite(0xbf, 0xfe);
+ }
+ }
+}
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEODeviceCEii.cs b/HandheldCompanion/Devices/AYANEO/AYANEODeviceCEii.cs
new file mode 100644
index 000000000..f0abb8b43
--- /dev/null
+++ b/HandheldCompanion/Devices/AYANEO/AYANEODeviceCEii.cs
@@ -0,0 +1,113 @@
+using HandheldCompanion.Inputs;
+using System;
+using System.Collections.Generic;
+using System.Numerics;
+using System.Threading;
+using WindowsInput.Events;
+
+namespace HandheldCompanion.Devices.AYANEO
+{
+ // Base class implementing FAN/RGB control for all AYANEO device using the CEii protocol
+ // AIR Plus AMD, AIR Plus Mendocino, AIR Plus Intel, Slide
+ public class AYANEODeviceCEii : AYANEODeviceCEc
+ {
+ public AYANEODeviceCEii()
+ {
+ this.ECDetails = new ECDetails
+ {
+ AddressStatusCommandPort = 0x4e,
+ AddressDataPort = 0x4f,
+ AddressFanControl = 0xc8,
+ AddressFanDuty = 0x04,
+ FanValueMin = 0,
+ FanValueMax = 255
+ };
+
+ this.GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ this.GyrometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ this.AccelerometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ this.AccelerometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ this.OEMChords.Add(new DeviceChord("Custom Key Big",
+ new List { KeyCode.LControl, KeyCode.LWin, KeyCode.F17 },
+ new List { KeyCode.F17, KeyCode.LWin, KeyCode.LControl },
+ false, ButtonFlags.OEM1
+ ));
+ this.OEMChords.Add(new DeviceChord("Custom Key Small",
+ new List { KeyCode.LWin, KeyCode.D },
+ new List { KeyCode.LWin, KeyCode.D },
+ false, ButtonFlags.OEM2
+ ));
+ this.OEMChords.Add(new DeviceChord("Custom Key Top Left",
+ new List { KeyCode.LControl, KeyCode.LWin, KeyCode.F15 },
+ new List { KeyCode.F15, KeyCode.LWin, KeyCode.LControl },
+ false, ButtonFlags.OEM3
+ ));
+ this.OEMChords.Add(new DeviceChord("Custom Key Top Right",
+ new List { KeyCode.LControl, KeyCode.LWin, KeyCode.F16 },
+ new List { KeyCode.F16, KeyCode.LWin, KeyCode.LControl },
+ false, ButtonFlags.OEM4
+ ));
+ }
+
+ public override void SetFanDuty(double percent)
+ {
+ if (ECDetails.AddressFanDuty == 0)
+ return;
+
+ if (!IsOpen)
+ return;
+
+ var duty = percent * (ECDetails.FanValueMax - ECDetails.FanValueMin) / 100 + ECDetails.FanValueMin;
+ var data = Convert.ToByte(duty);
+ this.ECRamDirectWrite((byte)this.ECDetails.AddressFanDuty, data, 0x18);
+ }
+
+
+ public override void SetFanControl(bool enable, int mode = 0)
+ {
+ if (this.ECDetails.AddressFanControl == 0)
+ return;
+
+ if (!this.IsOpen)
+ return;
+
+ byte data = enable ? (byte)0xa5 : (byte)0x00;
+ this.ECRamDirectWrite((byte)this.ECDetails.AddressFanControl, data);
+ }
+
+ // Based on CEiiEcHelper_RgbI2cWrite (AYASpace) but renamed to override existing function
+ protected override void CEcControl_RgbI2cWrite(LEDGroup group, byte command, byte argument)
+ {
+ byte applyAddress = group == LEDGroup.StickLeft ? (byte)0xc6 : (byte)0x86;
+ short groupAddress = group == LEDGroup.StickLeft ? (short)0xb0 : (short)0x70;
+
+ this.CEiiEcHelper_RgbStart();
+ this.ECRamDirectWrite((byte)(groupAddress + command), argument);
+ this.ECRamDirectWrite(applyAddress, 0x01);
+ Thread.Sleep(10); // AYASpace does this so copied it here
+ }
+
+ protected void CEiiEcHelper_RgbStart()
+ {
+ this.ECRamDirectWrite(0x87, 0xa5);
+ }
+
+ public bool ECRamDirectWrite(byte address, byte data, byte offset = 0xd1)
+ {
+ ushort address2 = BitConverter.ToUInt16(new byte[] { (byte)address, offset }, 0);
+ return base.ECRamDirectWrite(address2, this.ECDetails, data);
+ }
+ }
+}
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOKUN.cs b/HandheldCompanion/Devices/AYANEO/AYANEOKUN.cs
index 670929043..aabac800c 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOKUN.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOKUN.cs
@@ -1,103 +1,32 @@
using HandheldCompanion.Inputs;
-using HandheldCompanion.Utils;
using System.Collections.Generic;
-using System.Numerics;
-using System.Threading;
using System.Windows.Media;
using WindowsInput.Events;
-using static HandheldCompanion.Utils.DeviceUtils;
namespace HandheldCompanion.Devices;
-public class AYANEOKUN : AYANEO.AYANEODevice
+public class AYANEOKUN : AYANEO.AYANEODeviceCEc
{
- private enum LEDGroup
- {
- JoystickLeft = 1,
- JoystickRight = 2,
- JoystickBoth = 3,
- AYAButton = 4,
- }
-
- private static byte[] AYA_ZONES = new byte[] { 4 };
- private static byte[] STICK_ZONES = new byte[] { 1, 2, 3, 4 };
-
- private Color color = Color.FromRgb(0, 0, 0);
-
public AYANEOKUN()
{
// device specific settings
- ProductIllustration = "device_aya_kun";
- ProductModel = "AYANEO KUN";
+ this.ProductIllustration = "device_aya_kun";
+ this.ProductModel = "AYANEO KUN";
// https://www.amd.com/en/products/apu/amd-ryzen-7-7840u
- nTDP = new double[] { 15, 15, 20 };
- cTDP = new double[] { 3, 54 };
- GfxClock = new double[] { 100, 2700 };
- CpuClock = 5100;
-
- GyrometerAxis = new Vector3(1.0f, 1.0f, 1.0f);
- GyrometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- AccelerometerAxis = new Vector3(1.0f, 1.0f, 1.0f);
- AccelerometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 3, 54 };
+ this.GfxClock = new double[] { 100, 2700 };
+ this.CpuClock = 5100;
// device specific capacities
- Capabilities = DeviceCapabilities.FanControl;
- Capabilities |= DeviceCapabilities.DynamicLighting;
- DynamicLightingCapabilities |= LEDLevel.SolidColor;
-
- ECDetails = new ECDetails
- {
- AddressFanControl = 0x44A,
- AddressFanDuty = 0x44B,
- AddressStatusCommandPort = 0x4E,
- AddressDataPort = 0x4F,
- FanValueMin = 0,
- FanValueMax = 100
- };
-
- OEMChords.Add(new DeviceChord("Custom Key Big",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F17 },
- new List { KeyCode.F17, KeyCode.LWin, KeyCode.RControlKey },
- false, ButtonFlags.OEM1
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Small",
- new List { KeyCode.LWin, KeyCode.D },
- new List { KeyCode.LWin, KeyCode.D },
- false, ButtonFlags.OEM2
- ));
+ this.Capabilities |= DeviceCapabilities.DynamicLightingSecondLEDColor;
- OEMChords.Add(new DeviceChord("Custom Key Top Left",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F15 },
- new List { KeyCode.F15, KeyCode.LWin, KeyCode.RControlKey },
- false, ButtonFlags.OEM3
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Top Right",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F16 },
- new List { KeyCode.F16, KeyCode.LWin, KeyCode.RControlKey },
-
- false, ButtonFlags.OEM4
- ));
-
- OEMChords.Add(new DeviceChord("T",
+ this.OEMChords.Add(new DeviceChord("T",
new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F18 },
new List { KeyCode.F18, KeyCode.LWin, KeyCode.RControlKey },
false, ButtonFlags.OEM5
));
-
- OEMChords.Add(new DeviceChord("Guide",
+ this.OEMChords.Add(new DeviceChord("Guide",
new List { KeyCode.LButton, KeyCode.XButton2 },
new List { KeyCode.LButton, KeyCode.XButton2 },
false, ButtonFlags.OEM6
@@ -105,114 +34,20 @@ public AYANEOKUN()
}
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.OEM1:
- return "\uE003";
- case ButtonFlags.OEM2:
- return "\u220B";
- case ButtonFlags.OEM3:
- return "\u2209";
- case ButtonFlags.OEM4:
- return "\u220A";
- case ButtonFlags.OEM5:
- return "\u0054";
- case ButtonFlags.OEM6:
- return "\uE001";
- }
-
- return defaultGlyph;
- }
-
- public override bool SetLedStatus(bool status)
- {
- if (status)
- {
- SetLEDGroupEnable(LEDGroup.AYAButton);
- SetLEDGroupColor(LEDGroup.AYAButton, AYA_ZONES, color);
-
- SetLEDGroupEnable(LEDGroup.JoystickBoth);
- SetLEDGroupColor(LEDGroup.JoystickBoth, STICK_ZONES, color);
- }
- else
- {
- SetLEDGroupDisable(LEDGroup.AYAButton);
- SetLEDGroupDisable(LEDGroup.JoystickBoth);
- }
- return true;
- }
-
- public override bool SetLedColor(Color MainColor, Color SecondaryColor, LEDLevel level, int speed)
+ protected override byte[] MapColorValues(int zone, Color color)
{
- if (!DynamicLightingCapabilities.HasFlag(level))
- return false;
-
- color = MainColor;
-
- switch (level)
+ switch (zone)
{
- case LEDLevel.SolidColor:
- SetLEDGroupColor(LEDGroup.AYAButton, AYA_ZONES, color);
- SetLEDGroupColor(LEDGroup.JoystickBoth, STICK_ZONES, color);
- break;
+ case 1:
+ return [color.G, color.R, color.B];
+ case 2:
+ return [color.G, color.B, color.R];
+ case 3:
+ return [color.B, color.R, color.G];
+ case 4:
+ return [color.B, color.G, color.R];
+ default:
+ return [color.R, color.G, color.B];
}
-
- return true;
- }
-
- private void SetLEDGroupEnable(LEDGroup group)
- {
- SendLEDCommand((byte)group, 2, 0x80); // This seems to determine the time between color transitions, 0x80 being a very good balance
- }
-
- private void SetLEDGroupDisable(LEDGroup group)
- {
- SendLEDCommand((byte)group, 2, 0xc0);
- }
-
- private void SetLEDGroupColor(LEDGroup group, byte[] zones, Color color)
- {
- foreach (byte zone in zones)
- {
- byte[] colorValues = GetColorValues(group, zone, color);
- for (byte colorComponentIndex = 0; colorComponentIndex < colorValues.Length; colorComponentIndex++)
- {
- byte zoneColorComponent = (byte)(zone * 3 + colorComponentIndex); // Indicates which Zone and which color component
- byte colorComponentValueBrightness = (byte)(colorValues[colorComponentIndex] * 192 / byte.MaxValue); // Convert 0-255 to 0-100
- SendLEDCommand((byte)group, zoneColorComponent, colorComponentValueBrightness);
- }
- }
- }
-
- private void SendLEDCommand(byte group, byte command, byte argument)
- {
- using (new ScopedLock(updateLock))
- {
- ECRAMWrite(0x6d, group);
- ECRAMWrite(0xb1, command);
- ECRAMWrite(0xb2, argument);
- ECRAMWrite(0xbf, 0x10);
- Thread.Sleep(5); // Sleep here to give the controller enough time. AYASpace does this as well.
- ECRAMWrite(0xbf, 0xfe);
- }
- }
-
- // Get remapped RGB color values for the specific zone
- // 1: R -> G, G -> R, B -> B
- // 2: R -> G, G -> B, B -> R
- // 3: R -> B, G -> R, B -> G
- // 4: R -> B, G -> G, B -> R
- // 4 (AYA): R -> B, G -> R, B -> G
- private byte[] GetColorValues(LEDGroup group, byte zone, Color color)
- {
- if (zone == 1) return new byte[] { color.G, color.R, color.B };
- if (zone == 2) return new byte[] { color.G, color.B, color.R };
- if (zone == 3 || group == LEDGroup.AYAButton) return new byte[] { color.B, color.R, color.G };
- if (zone == 4) return new byte[] { color.B, color.G, color.R };
-
- // Just return the default
- return new byte[] { color.R, color.G, color.B };
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEONEXT.cs b/HandheldCompanion/Devices/AYANEO/AYANEONEXT.cs
index 640e082c3..7cb05f4f8 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEONEXT.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEONEXT.cs
@@ -1,62 +1,64 @@
-using HandheldCompanion.Inputs;
-using System.Collections.Generic;
-
-using WindowsInput.Events;
-
-namespace HandheldCompanion.Devices;
-
-public class AYANEONEXT : IDevice
-{
- public AYANEONEXT()
- {
- // device specific settings
- ProductIllustration = "device_aya_next";
- ProductModel = "AYANEONext";
-
- // https://www.amd.com/fr/products/apu/amd-ryzen-7-5800u
- // https://www.amd.com/fr/products/apu/amd-ryzen-7-5825u
- nTDP = new double[] { 15, 15, 20 };
- cTDP = new double[] { 10, 25 };
- GfxClock = new double[] { 100, 2000 };
- CpuClock = 4500;
-
- GyrometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- AccelerometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- OEMChords.Add(new DeviceChord("Custom key BIG",
- new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F12 },
- new List { KeyCode.F12, KeyCode.LWin, KeyCode.RControlKey },
- false, ButtonFlags.OEM1
- ));
-
- OEMChords.Add(new DeviceChord("Custom key Small",
- new List { KeyCode.LWin, KeyCode.D },
- new List { KeyCode.LWin, KeyCode.D },
- false, ButtonFlags.OEM2
- ));
+using HandheldCompanion.Inputs;
+using System.Collections.Generic;
+using System.Numerics;
+using WindowsInput.Events;
+
+namespace HandheldCompanion.Devices;
+
+public class AYANEONEXT : IDevice
+{
+ public AYANEONEXT()
+ {
+ // device specific settings
+ this.ProductIllustration = "device_aya_next";
+ this.ProductModel = "AYANEONext";
+
+ // https://www.amd.com/fr/products/apu/amd-ryzen-7-5800u
+ // https://www.amd.com/fr/products/apu/amd-ryzen-7-5825u
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 10, 25 };
+ this.GfxClock = new double[] { 100, 2000 };
+ this.CpuClock = 4500;
+
+ this.GyrometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
+ this.GyrometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ this.AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
+ this.AccelerometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ this.OEMChords.Add(new DeviceChord("Custom key BIG",
+ new List { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F12 },
+ new List { KeyCode.F12, KeyCode.LWin, KeyCode.RControlKey },
+ false, ButtonFlags.OEM1
+ ));
+
+ this.OEMChords.Add(new DeviceChord("Custom key Small",
+ new List { KeyCode.LWin, KeyCode.D },
+ new List { KeyCode.LWin, KeyCode.D },
+ false, ButtonFlags.OEM2
+ ));
}
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.OEM1:
- return "\uE003";
- case ButtonFlags.OEM2:
- return "\u220B";
- }
-
- return defaultGlyph;
- }
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.OEM1:
+ return "\uE003";
+ case ButtonFlags.OEM2:
+ return "\u220B";
+ }
+
+ return defaultGlyph;
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEONEXTLite.cs b/HandheldCompanion/Devices/AYANEO/AYANEONEXTLite.cs
new file mode 100644
index 000000000..5e44c5c69
--- /dev/null
+++ b/HandheldCompanion/Devices/AYANEO/AYANEONEXTLite.cs
@@ -0,0 +1,16 @@
+namespace HandheldCompanion.Devices;
+
+public class AYANEONEXTLite : AYANEONEXT
+{
+ public AYANEONEXTLite()
+ {
+ // device specific settings
+ this.ProductModel = "AYANEONext Lite";
+
+ // https://www.amd.com/en/support/apu/amd-ryzen-processors/amd-ryzen-7-mobile-processors-radeon-graphics/amd-ryzen-7-4800u
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 3, 25 };
+ this.GfxClock = new double[] { 100, 1750 };
+ this.CpuClock = 4200;
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEONEXTLite4500U.cs b/HandheldCompanion/Devices/AYANEO/AYANEONEXTLite4500U.cs
new file mode 100644
index 000000000..1b3fc1ff1
--- /dev/null
+++ b/HandheldCompanion/Devices/AYANEO/AYANEONEXTLite4500U.cs
@@ -0,0 +1,11 @@
+namespace HandheldCompanion.Devices;
+
+public class AYANEONEXTLite4500U : AYANEONEXTLite
+{
+ public AYANEONEXTLite4500U()
+ {
+ // https://www.amd.com/en/support/apu/amd-ryzen-processors/amd-ryzen-5-mobile-processors-radeon-graphics/amd-ryzen-5-4500u
+ this.GfxClock = new double[] { 100, 1500 };
+ this.CpuClock = 4000;
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOSLIDE.cs b/HandheldCompanion/Devices/AYANEO/AYANEOSLIDE.cs
index 147a37230..a6c09220d 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOSLIDE.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOSLIDE.cs
@@ -1,103 +1,22 @@
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Utils;
-using System.Collections.Generic;
using System.Numerics;
-using System.Threading;
-using System.Windows.Media;
-using WindowsInput.Events;
-using static HandheldCompanion.Utils.DeviceUtils;
+
namespace HandheldCompanion.Devices;
-public class AYANEOSlide : AYANEO.AYANEODevice
+public class AYANEOSlide : AYANEO.AYANEODeviceCEii
{
-
-
public AYANEOSlide()
{
// device specific settings
- ProductIllustration = "device_aya_slide";
- ProductModel = "AYANEOSlide";
+ this.ProductIllustration = "device_aya_slide";
+ this.ProductModel = "AYANEOSlide";
// https://www.amd.com/en/products/apu/amd-ryzen-7-7840u
- nTDP = new double[] { 15, 15, 20 };
- cTDP = new double[] { 3, 54 };
- GfxClock = new double[] { 100, 2700 };
- CpuClock = 5100;
-
- GyrometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
- GyrometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- AccelerometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
- AccelerometerAxisSwap = new SortedDictionary
- {
- { 'X', 'X' },
- { 'Y', 'Z' },
- { 'Z', 'Y' }
- };
-
- // device specific capacities
- // Capabilities = DeviceCapabilities.FanControl;
- // Capabilities |= DeviceCapabilities.DynamicLighting;
- // DynamicLightingCapabilities |= LEDLevel.SolidColor;
-
-
- ECDetails = new ECDetails
- {
- AddressStatusCommandPort = 0x4E, // unknown
- AddressDataPort = 0x4F, // unknown
- AddressFanControl = 0, // unknown
- AddressFanDuty = 0, // unknown
- FanValueMin = 0, // unknown
- FanValueMax = 100 // unknown
- };
- OEMChords.Clear();
-
- OEMChords.Add(new DeviceChord("Custom Key Big",
- new List { KeyCode.LControl, KeyCode.LWin, KeyCode.F17 },
- new List { KeyCode.F17, KeyCode.LControl, KeyCode.LWin },
- false, ButtonFlags.OEM1
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Small",
- new List { KeyCode.LWin, KeyCode.D },
- new List { KeyCode.D, KeyCode.LWin },
- false, ButtonFlags.OEM2
- ));
-
-
- OEMChords.Add(new DeviceChord("Custom Key Top Left",
- new List { KeyCode.LControl, KeyCode.LWin, KeyCode.F15 },
- new List { KeyCode.F15, KeyCode.LControl, KeyCode.LWin },
- false, ButtonFlags.OEM3
- ));
-
- OEMChords.Add(new DeviceChord("Custom Key Top Right",
- new List { KeyCode.LControl, KeyCode.LWin, KeyCode.F16 },
- new List { KeyCode.F16, KeyCode.LControl, KeyCode.LWin },
- false, ButtonFlags.OEM4
- ));
-
- }
-
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.OEM1:
- return "\uE003";
- case ButtonFlags.OEM2:
- return "\u220B";
- case ButtonFlags.OEM3:
- return "\u2209";
- case ButtonFlags.OEM4:
- return "\u220A";
- }
+ this.nTDP = new double[] { 15, 15, 20 };
+ this.cTDP = new double[] { 3, 54 };
+ this.GfxClock = new double[] { 100, 2700 };
+ this.CpuClock = 5100;
- return defaultGlyph;
+ this.GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ this.AccelerometerAxis = new Vector3(-1.0f, 1.0f, -1.0f);
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/Ayn/AynLoki.cs b/HandheldCompanion/Devices/Ayn/AynLoki.cs
index 79c22e215..fa6bdd612 100644
--- a/HandheldCompanion/Devices/Ayn/AynLoki.cs
+++ b/HandheldCompanion/Devices/Ayn/AynLoki.cs
@@ -26,7 +26,7 @@ public AynLoki()
ProductIllustration = "device_ayn_loki";
ProductModel = "AynLoki";
- GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ GyrometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'Y' },
@@ -34,7 +34,7 @@ public AynLoki()
{ 'Z', 'X' }
};
- AccelerometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
+ AccelerometerAxis = new Vector3(-1.0f, 1.0f, -1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
diff --git a/HandheldCompanion/Devices/DefaultDevice.cs b/HandheldCompanion/Devices/DefaultDevice.cs
index 8a7929ea9..4a91be611 100644
--- a/HandheldCompanion/Devices/DefaultDevice.cs
+++ b/HandheldCompanion/Devices/DefaultDevice.cs
@@ -3,21 +3,5 @@
public class DefaultDevice : IDevice
{
public DefaultDevice()
- {
- // We assume all the devices have those keys
- // Disabled until we implement "turbo" type hotkeys
-
- /*
- OEMChords.Add(new DeviceChord("Volume Up",
- new List { KeyCode.VolumeUp },
- new List { KeyCode.VolumeUp },
- false, ButtonFlags.VolumeUp
- ));
- OEMChords.Add(new DeviceChord("Volume Down",
- new List { KeyCode.VolumeDown },
- new List { KeyCode.VolumeDown },
- false, ButtonFlags.VolumeDown
- ));
- */
- }
+ { }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs b/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs
index 78e6c4504..5a31cf762 100644
--- a/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs
+++ b/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs
@@ -31,7 +31,7 @@ public GPDWin4_2023_7840U()
FanValueMax = 184
};
- GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ GyrometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'Y' },
@@ -39,7 +39,7 @@ public GPDWin4_2023_7840U()
{ 'Z', 'X' }
};
- AccelerometerAxis = new Vector3(1.0f, 1.0f, 1.0f);
+ AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
diff --git a/HandheldCompanion/Devices/GPD/GPDWin4.cs b/HandheldCompanion/Devices/GPD/GPDWin4.cs
index a6fbba664..73587b6b3 100644
--- a/HandheldCompanion/Devices/GPD/GPDWin4.cs
+++ b/HandheldCompanion/Devices/GPD/GPDWin4.cs
@@ -33,14 +33,15 @@ public GPDWin4()
FanValueMax = 127
};
- GyrometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
+ GyrometerAxis = new Vector3(-1.0f, 1.0f, 1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
{ 'Y', 'Z' },
{ 'Z', 'Y' }
- };
-
+ };
+
+ AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
diff --git a/HandheldCompanion/Devices/GPD/GPDWinMax2AMD.cs b/HandheldCompanion/Devices/GPD/GPDWinMax2AMD.cs
index 27e7f7be7..6d0321fd4 100644
--- a/HandheldCompanion/Devices/GPD/GPDWinMax2AMD.cs
+++ b/HandheldCompanion/Devices/GPD/GPDWinMax2AMD.cs
@@ -13,7 +13,7 @@ public GPDWinMax2AMD()
GfxClock = new double[] { 100, 2200 };
CpuClock = 4700;
- GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ GyrometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'Y' },
@@ -21,7 +21,7 @@ public GPDWinMax2AMD()
{ 'Z', 'X' }
};
- AccelerometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
+ AccelerometerAxis = new Vector3(-1.0f, 1.0f, 1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
diff --git a/HandheldCompanion/Devices/GPD/GPDWinMax2Intel.cs b/HandheldCompanion/Devices/GPD/GPDWinMax2Intel.cs
index e2f88f627..80edec143 100644
--- a/HandheldCompanion/Devices/GPD/GPDWinMax2Intel.cs
+++ b/HandheldCompanion/Devices/GPD/GPDWinMax2Intel.cs
@@ -13,7 +13,7 @@ public GPDWinMax2Intel()
GfxClock = new double[] { 100, 1400 };
CpuClock = 4700;
- GyrometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
+ GyrometerAxis = new Vector3(1.0f, 1.0f, 1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
@@ -21,7 +21,7 @@ public GPDWinMax2Intel()
{ 'Z', 'Y' }
};
- AccelerometerAxis = new Vector3(1.0f, 1.0f, 1.0f);
+ AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
diff --git a/HandheldCompanion/Devices/GPD/GPDWinMini-7840U.cs b/HandheldCompanion/Devices/GPD/GPDWinMini-7840U.cs
index 1c8816962..9c63bdddc 100644
--- a/HandheldCompanion/Devices/GPD/GPDWinMini-7840U.cs
+++ b/HandheldCompanion/Devices/GPD/GPDWinMini-7840U.cs
@@ -31,7 +31,7 @@ public GPDWinMini_7840U()
FanValueMax = 244 // 100% ~6000 RPM
};
- GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ GyrometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'Y' },
@@ -39,7 +39,7 @@ public GPDWinMini_7840U()
{ 'Z', 'X' }
};
- AccelerometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
+ AccelerometerAxis = new Vector3(-1.0f, 1.0f, 1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
diff --git a/HandheldCompanion/Devices/IDevice.cs b/HandheldCompanion/Devices/IDevice.cs
index 6359721a4..ddd6934cb 100644
--- a/HandheldCompanion/Devices/IDevice.cs
+++ b/HandheldCompanion/Devices/IDevice.cs
@@ -1,4 +1,5 @@
using HandheldCompanion.Controls;
+using HandheldCompanion.Helpers;
using HandheldCompanion.Inputs;
using HandheldCompanion.Managers;
using HandheldCompanion.Misc;
@@ -12,11 +13,9 @@
using System.Collections.Specialized;
using System.Linq;
using System.Numerics;
-using System.Threading;
using System.Windows.Media;
using Windows.Devices.Sensors;
using WindowsInput.Events;
-using static HandheldCompanion.OneEuroFilter;
using static HandheldCompanion.OpenLibSys;
using static HandheldCompanion.Utils.DeviceUtils;
@@ -64,7 +63,7 @@ public abstract class IDevice
public delegate void KeyReleasedEventHandler(ButtonFlags button);
public delegate void PowerStatusChangedEventHandler(IDevice device);
- private static OpenLibSys openLibSys;
+ protected static OpenLibSys openLibSys;
protected LockObject updateLock = new();
private static IDevice device;
@@ -73,7 +72,6 @@ public abstract class IDevice
public Dictionary hidDevices = new();
public Vector3 AccelerometerAxis = new(1.0f, 1.0f, 1.0f);
-
public SortedDictionary AccelerometerAxisSwap = new()
{
{ 'X', 'X' },
@@ -82,7 +80,6 @@ public abstract class IDevice
};
public Vector3 GyrometerAxis = new(1.0f, 1.0f, 1.0f);
-
public SortedDictionary GyrometerAxisSwap = new()
{
{ 'X', 'X' },
@@ -90,6 +87,8 @@ public abstract class IDevice
{ 'Z', 'Z' }
};
+ public GamepadMotion GamepadMotion;
+
public DeviceCapabilities Capabilities = DeviceCapabilities.None;
public LEDLevel DynamicLightingCapabilities = LEDLevel.SolidColor;
@@ -136,9 +135,6 @@ public abstract class IDevice
// trigger specific settings
public List OEMChords = new();
- // filter settings
- public OneEuroSettings oneEuroSettings = new(0.002d, 0.008d);
-
// UI
protected FontFamily GlyphFontFamily = new("PromptFont");
protected const string defaultGlyph = "\u2753";
@@ -157,10 +153,24 @@ public abstract class IDevice
// key press delay to use for certain scenarios
public short KeyPressDelay = 20;
- protected USBDeviceInfo sensor = new();
-
public IDevice()
{
+ GamepadMotion = new(ProductIllustration, CalibrationMode.Manual | CalibrationMode.SensorFusion);
+
+ VirtualManager.ControllerSelected += VirtualManager_ControllerSelected;
+ DeviceManager.UsbDeviceArrived += GenericDeviceUpdated;
+ DeviceManager.UsbDeviceRemoved += GenericDeviceUpdated;
+ }
+
+ private void VirtualManager_ControllerSelected(HIDmode mode)
+ {
+ SetKeyPressDelay(mode);
+ }
+
+ private void GenericDeviceUpdated(PnPDevice device, DeviceEventArgs obj)
+ {
+ // todo: improve me
+ PullSensors();
}
public IEnumerable OEMButtons => OEMChords.SelectMany(a => a.state.Buttons).Distinct();
@@ -182,13 +192,11 @@ public IDevice()
public string Processor = string.Empty;
public int NumberOfCores = 0;
- public static IDevice GetDefault()
+ public static IDevice GetCurrent()
{
if (device is not null)
return device;
- MotherboardInfo.UpdateMotherboard();
-
var ManufacturerName = MotherboardInfo.Manufacturer.ToUpper();
var ProductName = MotherboardInfo.Product;
var SystemName = MotherboardInfo.SystemName;
@@ -273,6 +281,11 @@ public static IDevice GetDefault()
case "NEXT":
device = new AYANEONEXT();
break;
+ case "NEXT Lite":
+ device = Processor.Contains("4500U")
+ ? new AYANEONEXTLite4500U()
+ : new AYANEONEXTLite();
+ break;
case "AYANEO 2":
case "GEEK":
device = new AYANEO2();
@@ -446,6 +459,16 @@ public static IDevice GetDefault()
}
}
break;
+ case "MICRO-STAR INTERNATIONAL CO., LTD.":
+ {
+ switch (ProductName)
+ {
+ case "MS-1T41":
+ device = new ClawA1M();
+ break;
+ }
+ }
+ break;
}
LogManager.LogInformation("{0} from {1}", ProductName, ManufacturerName);
@@ -544,8 +567,8 @@ public void PullSensors()
if (gyrometer is not null && accelerometer is not null)
{
// check sensor
- var DeviceId = CommonUtils.Between(gyrometer.DeviceId, @"\\?\", @"#{").Replace(@"#", @"\");
- sensor = DeviceUtils.GetUSBDevice(DeviceId);
+ string DeviceId = CommonUtils.Between(gyrometer.DeviceId, @"\\?\", @"#{").Replace(@"#", @"\");
+ USBDeviceInfo sensor = GetUSBDevice(DeviceId);
if (sensor is not null)
InternalSensorName = sensor.Name;
@@ -556,7 +579,7 @@ public void PullSensors()
Capabilities &= ~DeviceCapabilities.InternalSensor;
}
- SerialUSBIMU? USB = SerialUSBIMU.GetDefault();
+ SerialUSBIMU? USB = SerialUSBIMU.GetCurrent();
if (USB is not null)
{
ExternalSensorName = USB.GetName();
@@ -569,14 +592,6 @@ public void PullSensors()
}
}
- public bool RestartSensor()
- {
- if (sensor is null)
- return false;
-
- return PnPUtil.RestartDevice(sensor.DeviceId);
- }
-
public virtual void SetFanDuty(double percent)
{
if (ECDetails.AddressFanDuty == 0)
@@ -718,20 +733,20 @@ public virtual bool ECRamDirectWrite(ushort address, ECDetails details, byte dat
}
}
- protected void ECRAMWrite(byte address, byte data)
+ protected virtual void ECRAMWrite(byte address, byte data)
{
SendECCommand(WR_EC);
SendECData(address);
SendECData(data);
}
- protected void SendECCommand(byte command)
+ protected virtual void SendECCommand(byte command)
{
if (IsECReady())
ECRamWriteByte(EC_SC, command);
}
- protected void SendECData(byte data)
+ protected virtual void SendECData(byte data)
{
if (IsECReady())
ECRamWriteByte(EC_DATA, data);
@@ -739,13 +754,14 @@ protected void SendECData(byte data)
protected bool IsECReady()
{
- DateTime timeout = DateTime.Now.Add(TimeSpan.FromMilliseconds(50));
- while (DateTime.Now < timeout && (ECRamReadByte(EC_SC) & EC_IBF) != 0x0)
- Thread.Sleep(1);
-
- if (DateTime.Now <= timeout)
- return true;
-
+ DateTime timeout = DateTime.Now.AddMilliseconds(250);
+ while (DateTime.Now < timeout)
+ {
+ if ((ECRamReadByte(EC_SC) & EC_IBF) == 0x0)
+ {
+ return true;
+ }
+ }
return false;
}
@@ -831,6 +847,20 @@ public string GetButtonName(ButtonFlags button)
return EnumUtils.GetDescriptionFromEnumValue(button, GetType().Name);
}
+ public GlyphIconInfo GetGlyphIconInfo(ButtonFlags button, int fontIconSize = 14)
+ {
+ var glyph = GetGlyph(button);
+ return new GlyphIconInfo
+ {
+ Name = GetButtonName(button),
+ Glyph = glyph,
+ FontSize = glyph is not null ? 28 : fontIconSize,
+ FontFamily = glyph is not null ? GlyphFontFamily : null,
+ Foreground = null
+ };
+ }
+
+ [Obsolete("GetFontIcon has dependencies on UI and should be avoided. Use GetGlyphIconInfo instead.")]
public FontIcon GetFontIcon(ButtonFlags button, int FontIconSize = 14)
{
var FontIcon = new FontIcon
diff --git a/HandheldCompanion/Devices/Lenovo/LegionGo.cs b/HandheldCompanion/Devices/Lenovo/LegionGo.cs
index 5b3af31aa..508df4eb1 100644
--- a/HandheldCompanion/Devices/Lenovo/LegionGo.cs
+++ b/HandheldCompanion/Devices/Lenovo/LegionGo.cs
@@ -3,6 +3,7 @@
using HandheldCompanion.Inputs;
using HandheldCompanion.Managers;
using HandheldCompanion.Misc;
+using HandheldCompanion.Processors;
using HidLibrary;
using Nefarius.Utilities.DeviceManagement.PnP;
using System;
@@ -100,8 +101,8 @@ private Task SetCPUPowerLimit(CapabilityID capabilityID, int limit) =>
public override bool IsOpen => hidDevices.ContainsKey(INPUT_HID_ID) && hidDevices[INPUT_HID_ID].IsOpen;
- public static int LeftJoyconIndex = 3;
- public static int RightJoyconIndex = 4;
+ public const int LeftJoyconIndex = 3;
+ public const int RightJoyconIndex = 4;
public LegionGo()
{
@@ -112,6 +113,9 @@ public LegionGo()
_vid = 0x17EF;
_pid = 0x6182;
+ // fix for threshold overflow
+ GamepadMotion.SetCalibrationThreshold(124.0f, 2.0f);
+
// https://www.amd.com/en/products/apu/amd-ryzen-z1
// https://www.amd.com/en/products/apu/amd-ryzen-z1-extreme
// https://www.amd.com/en/products/apu/amd-ryzen-7-7840u
@@ -120,7 +124,7 @@ public LegionGo()
GfxClock = new double[] { 100, 2700 };
CpuClock = 5100;
- GyrometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
+ GyrometerAxis = new Vector3(-1.0f, 1.0f, 1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
@@ -128,7 +132,7 @@ public LegionGo()
{ 'Z', 'Y' }
};
- AccelerometerAxis = new Vector3(-1.0f, 1.0f, -1.0f);
+ AccelerometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
@@ -149,19 +153,20 @@ public LegionGo()
DynamicLightingCapabilities |= LEDLevel.Wheel;
// Legion Go - Quiet
- DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileLegionGoQuietName, Properties.Resources.PowerProfileLegionGoQuietDescription)
+ DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileLegionGoBetterBattery, Properties.Resources.PowerProfileLegionGoBetterBatteryDesc)
{
Default = true,
DeviceDefault = true,
OSPowerMode = OSPowerMode.BetterBattery,
- OEMPowerMode = (int) LegionMode.Quiet,
+ CPUBoostLevel = CPUBoostLevel.Disabled,
+ OEMPowerMode = (int)LegionMode.Quiet,
Guid = new("961cc777-2547-4f9d-8174-7d86181b8a7a"),
TDPOverrideEnabled = true,
TDPOverrideValues = new[] { 8.0d, 8.0d, 8.0d }
});
// Legion Go - Balanced
- DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileLegionGoBalancedName, Properties.Resources.PowerProfileLegionGoBalancedDescription)
+ DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileLegionGoBetterPerformance, Properties.Resources.PowerProfileLegionGoBetterPerformanceDesc)
{
Default = true,
DeviceDefault = true,
@@ -173,7 +178,7 @@ public LegionGo()
});
// Legion Go - Performance
- DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileLegionGoPerformanceName, Properties.Resources.PowerProfileLegionGoPerformanceDescription)
+ DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileLegionGoBestPerformance, Properties.Resources.PowerProfileLegionGoBestPerformanceDesc)
{
Default = true,
DeviceDefault = true,
@@ -197,7 +202,7 @@ public LegionGo()
));
// device specific layout
- DefaultLayout.AxisLayout[AxisLayoutFlags.RightPad] = new MouseActions {MouseType = MouseActionsType.Move, Filtering = true, Sensivity = 15 };
+ DefaultLayout.AxisLayout[AxisLayoutFlags.RightPad] = new MouseActions { MouseType = MouseActionsType.Move, Filtering = true, Sensivity = 15 };
DefaultLayout.ButtonLayout[ButtonFlags.RightPadClick] = new List() { new MouseActions { MouseType = MouseActionsType.LeftButton, HapticMode = HapticMode.Down, HapticStrength = HapticStrength.Low } };
DefaultLayout.ButtonLayout[ButtonFlags.RightPadClickDown] = new List() { new MouseActions { MouseType = MouseActionsType.RightButton, HapticMode = HapticMode.Down, HapticStrength = HapticStrength.High } };
@@ -208,12 +213,35 @@ public LegionGo()
Init();
+ // make sure both left and right gyros are enabled
+ SetLeftGyroStatus(1);
+ SetRightGyroStatus(1);
+
+ // make sure both left and right gyros are reporting values
+ SetGyroModeStatus(2, 1, 1);
+ SetGyroModeStatus(2, 2, 2);
+
+ // make sure both left and right gyros are reporting raw values
+ SetGyroSensorDataOnorOff(LeftJoyconIndex, 0x02);
+ SetGyroSensorDataOnorOff(RightJoyconIndex, 0x02);
+
Task task = Task.Run(async () => await GetFanFullSpeedAsync());
bool FanFullSpeed = task.Result;
}
private void PowerProfileManager_Applied(PowerProfile profile, UpdateSource source)
{
+ // tentative: stability fix
+ if (PerformanceManager.GetProcessor() is AMDProcessor AMDProcessor)
+ AMDProcessor.SetCoall(0x100020);
+
+ if (profile.TDPOverrideEnabled && !profile.AutoTDPEnabled)
+ {
+ SetCPUPowerLimit(CapabilityID.CPUShortTermPowerLimit, (int)profile.TDPOverrideValues[0]);
+ SetCPUPowerLimit(CapabilityID.CPULongTermPowerLimit, (int)profile.TDPOverrideValues[1]);
+ SetCPUPowerLimit(CapabilityID.CPUPeakPowerLimit, (int)profile.TDPOverrideValues[2]);
+ }
+
FanTable fanTable = new(new ushort[] { 44, 48, 55, 60, 71, 79, 87, 87, 100, 100 });
if (profile.FanProfile.fanMode != FanMode.Hardware)
{
@@ -233,15 +261,6 @@ private void PowerProfileManager_Applied(PowerProfile profile, UpdateSource sour
SetFanTable(fanTable);
SetSmartFanMode(profile.OEMPowerMode);
-
- /*
- if (profile.TDPOverrideEnabled && !profile.AutoTDPEnabled)
- {
- SetCPUPowerLimit(CapabilityID.CPUShortTermPowerLimit, (int)profile.TDPOverrideValues[0]);
- SetCPUPowerLimit(CapabilityID.CPULongTermPowerLimit, (int)profile.TDPOverrideValues[1]);
- SetCPUPowerLimit(CapabilityID.CPUPeakPowerLimit, (int)profile.TDPOverrideValues[2]);
- }
- */
}
public override bool Open()
@@ -253,6 +272,7 @@ public override bool Open()
SetQuickLightingEffect(0, 1);
SetQuickLightingEffect(3, 1);
SetQuickLightingEffect(4, 1);
+
SetQuickLightingEffectEnable(0, false);
SetQuickLightingEffectEnable(3, false);
SetQuickLightingEffectEnable(4, false);
diff --git a/HandheldCompanion/Devices/Lenovo/SapientiaUsb.cs b/HandheldCompanion/Devices/Lenovo/SapientiaUsb.cs
index e21dd6cf4..df64d93bb 100644
--- a/HandheldCompanion/Devices/Lenovo/SapientiaUsb.cs
+++ b/HandheldCompanion/Devices/Lenovo/SapientiaUsb.cs
@@ -130,6 +130,9 @@ public struct GyroSensorStatus
//获取触摸板状态 tatus:0:关,1:开
[DllImport("SapientiaUsb.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int GetTouchPadStatus();
+ //获取触摸板状态 status:0:关,1:开
+ [DllImport("SapientiaUsb.dll", CallingConvention = CallingConvention.StdCall)]
+ public static extern int GetTrackpadStatus(int device);
//设置触摸板状态 status:0:关,1:开
[DllImport("SapientiaUsb.dll", CallingConvention = CallingConvention.StdCall)]
public static extern bool SetTouchPadStatus(int iSwitch);
@@ -174,7 +177,7 @@ public VERSION(int verPro, int verCMD, int verFir, int verHard)
public static extern bool SetStickCustomDeadzone(int device, int deadzone);
[DllImport("SapientiaUsb.dll", CallingConvention = CallingConvention.StdCall)]
- public static extern bool GetGyroSensorDataOnorOff(int device);
+ public static extern bool SetGyroSensorDataOnorOff(int device, int status);
// Range is 0-99 on Deadzone and Margin
[DllImport("SapientiaUsb.dll", CallingConvention = CallingConvention.StdCall)]
diff --git a/HandheldCompanion/Devices/MSI/ClawA1M.cs b/HandheldCompanion/Devices/MSI/ClawA1M.cs
new file mode 100644
index 000000000..f6504282f
--- /dev/null
+++ b/HandheldCompanion/Devices/MSI/ClawA1M.cs
@@ -0,0 +1,294 @@
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
+using HandheldCompanion.Utils;
+using HidLibrary;
+using System;
+using System.Collections.Generic;
+using System.Management;
+using System.Numerics;
+using System.Threading.Tasks;
+using WindowsInput.Events;
+
+namespace HandheldCompanion.Devices;
+
+public class ClawA1M : IDevice
+{
+ private enum WMIEventCode
+ {
+ LaunchMcxMainUI = 41, // 0x00000029
+ LaunchMcxOSD = 88, // 0x00000058
+ }
+
+ private readonly Dictionary keyMapping = new()
+ {
+ { 0, ButtonFlags.None },
+ { WMIEventCode.LaunchMcxMainUI, ButtonFlags.OEM1 },
+ { WMIEventCode.LaunchMcxOSD, ButtonFlags.OEM2 },
+ };
+
+ private enum GamepadMode
+ {
+ Offline,
+ XInput,
+ DInput,
+ MSI,
+ Desktop,
+ BIOS,
+ TESTING,
+ }
+
+ private enum MKeysFunction
+ {
+ Macro,
+ Combination,
+ }
+
+ private enum CommandType
+ {
+ EnterProfileConfig = 1,
+ ExitProfileConfig = 2,
+ WriteProfile = 3,
+ ReadProfile = 4,
+ ReadProfileAck = 5,
+ Ack = 6,
+ SwitchProfile = 7,
+ WriteProfileToEEPRom = 8,
+ ReadFirmwareVersion = 9,
+ ReadRGBStatusAck = 10, // 0x0000000A
+ ReadCurrentProfile = 11, // 0x0000000B
+ ReadCurrentProfileAck = 12, // 0x0000000C
+ ReadRGBStatus = 13, // 0x0000000D
+ SyncToROM = 34, // 0x00000022
+ RestoreFromROM = 35, // 0x00000023
+ SwitchMode = 36, // 0x00000024
+ ReadGamepadMode = 38, // 0x00000026
+ GamepadModeAck = 39, // 0x00000027
+ ResetDevice = 40, // 0x00000028
+ RGBControl = 224, // 0x000000E0
+ CalibrationControl = 253, // 0x000000FD
+ CalibrationAck = 254, // 0x000000FE
+ }
+
+ private ManagementEventWatcher? specialKeyWatcher;
+ private Dictionary hidDevices = new();
+
+ public ClawA1M()
+ {
+ // device specific settings
+ ProductIllustration = "device_msi_claw";
+
+ // used to monitor OEM specific inputs
+ _vid = 0x0DB0;
+ _pid = 0x1901;
+
+ // https://www.intel.com/content/www/us/en/products/sku/236847/intel-core-ultra-7-processor-155h-24m-cache-up-to-4-80-ghz/specifications.html
+ nTDP = new double[] { 28, 28, 65 };
+ cTDP = new double[] { 20, 65 };
+ GfxClock = new double[] { 100, 2250 };
+ CpuClock = 4800;
+
+ GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ GyrometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
+ AccelerometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ // device specific capacities
+ Capabilities |= DeviceCapabilities.None;
+
+ DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileMSIClawBetterBattery, Properties.Resources.PowerProfileMSIClawBetterBatteryDesc)
+ {
+ Default = true,
+ DeviceDefault = true,
+ OSPowerMode = OSPowerMode.BetterBattery,
+ CPUBoostLevel = CPUBoostLevel.Disabled,
+ Guid = new("961cc777-2547-4f9d-8174-7d86181b8a7a"),
+ TDPOverrideEnabled = true,
+ TDPOverrideValues = new[] { 20.0d, 20.0d, 20.0d }
+ });
+
+ DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileMSIClawBetterPerformance, Properties.Resources.PowerProfileMSIClawBetterPerformanceDesc)
+ {
+ Default = true,
+ DeviceDefault = true,
+ OSPowerMode = OSPowerMode.BetterPerformance,
+ Guid = new("3af9B8d9-7c97-431d-ad78-34a8bfea439f"),
+ TDPOverrideEnabled = true,
+ TDPOverrideValues = new[] { 30.0d, 30.0d, 30.0d }
+ });
+
+ DevicePowerProfiles.Add(new(Properties.Resources.PowerProfileMSIClawBestPerformance, Properties.Resources.PowerProfileMSIClawBestPerformanceDesc)
+ {
+ Default = true,
+ DeviceDefault = true,
+ OSPowerMode = OSPowerMode.BestPerformance,
+ Guid = new("ded574b5-45a0-4f42-8737-46345c09c238"),
+ TDPOverrideEnabled = true,
+ TDPOverrideValues = new[] { 35.0d, 35.0d, 35.0d }
+ });
+
+ OEMChords.Add(new DeviceChord("CLAW",
+ new List(), new List(),
+ false, ButtonFlags.OEM1
+ ));
+
+ OEMChords.Add(new DeviceChord("QS",
+ new List(), new List(),
+ false, ButtonFlags.OEM2
+ ));
+
+ OEMChords.Add(new DeviceChord("M1", // unimplemented
+ new List(), new List(),
+ false, ButtonFlags.OEM3
+ ));
+
+ OEMChords.Add(new DeviceChord("M2", // unimplemented
+ new List(), new List(),
+ false, ButtonFlags.OEM4
+ ));
+ }
+
+ private void StartWatching()
+ {
+ try
+ {
+ var scope = new ManagementScope("\\\\.\\root\\WMI");
+ specialKeyWatcher = new ManagementEventWatcher(scope, (EventQuery)(new WqlEventQuery("SELECT * FROM MSI_Event")));
+ specialKeyWatcher.EventArrived += onWMIEvent;
+ specialKeyWatcher.Start();
+ }
+ catch (Exception ex)
+ {
+ LogManager.LogError("Exception configuring MSI_Event monitor: {0}", ex.Message);
+ }
+ }
+
+ private void StopWatching()
+ {
+ if (specialKeyWatcher == null)
+ {
+ return;
+ }
+
+ try
+ {
+ specialKeyWatcher.EventArrived -= onWMIEvent;
+ specialKeyWatcher.Stop();
+ specialKeyWatcher.Dispose();
+ }
+ catch (Exception ex)
+ {
+ LogManager.LogError("Exception unconfiguring MSI_Event monitor: {0}", ex.Message);
+ }
+
+ specialKeyWatcher = null;
+ }
+
+ private void onWMIEvent(object sender, EventArrivedEventArgs e)
+ {
+ int WMIEvent = Convert.ToInt32(e.NewEvent.Properties["MSIEvt"].Value);
+ WMIEventCode key = (WMIEventCode)(WMIEvent & (int)byte.MaxValue);
+
+ // LogManager.LogInformation("Received MSI WMI Event Code {0}", (int)key);
+
+ if (!keyMapping.ContainsKey(key))
+ return;
+
+ // get button
+ ButtonFlags button = keyMapping[key];
+ switch (key)
+ {
+ default:
+ case WMIEventCode.LaunchMcxMainUI: // MSI Claw: Click
+ case WMIEventCode.LaunchMcxOSD: // Quick Settings: Click
+ {
+ Task.Factory.StartNew(async () =>
+ {
+ KeyPress(button);
+ await Task.Delay(KeyPressDelay);
+ KeyRelease(button);
+ });
+ }
+ break;
+ }
+ }
+
+ public override bool IsReady()
+ {
+ IEnumerable devices = GetHidDevices(_vid, _pid, 0);
+ foreach (HidDevice device in devices)
+ {
+ if (!device.IsConnected)
+ continue;
+
+ // improve detection maybe using if device.ReadFeatureData() ?
+ if (device.Capabilities.InputReportByteLength != 64)
+ continue;
+
+ byte[] msg = { 15, 0, 0, 60, (byte)CommandType.SwitchMode, (byte)GamepadMode.XInput, (byte)MKeysFunction.Macro };
+ device.Write(msg);
+ }
+
+ return true;
+ }
+
+ public override bool Open()
+ {
+ var success = base.Open();
+ if (!success)
+ return false;
+
+ // start WMI event monitor
+ StartWatching();
+
+ return true;
+ }
+
+ public override void Close()
+ {
+ // stop WMI event monitor
+ StopWatching();
+
+ base.Close();
+ }
+
+ public override void SetKeyPressDelay(HIDmode controllerMode)
+ {
+ switch (controllerMode)
+ {
+ case HIDmode.DualShock4Controller:
+ KeyPressDelay = 180;
+ break;
+ default:
+ KeyPressDelay = 20;
+ break;
+ }
+ }
+
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.OEM1:
+ return "\uE010";
+ case ButtonFlags.OEM2:
+ return "\uE011";
+ case ButtonFlags.OEM3:
+ return "\u2212";
+ case ButtonFlags.OEM4:
+ return "\u2213";
+ }
+
+ return defaultGlyph;
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2.cs b/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2.cs
index 52688979a..b82e8a8fe 100644
--- a/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2.cs
+++ b/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2.cs
@@ -20,7 +20,7 @@ public OneXPlayer2() : base()
this.GfxClock = new double[] { 100, 2200 };
this.CpuClock = 4700;
- GyrometerAxis = new Vector3(-1.0f, 1.0f, -1.0f);
+ GyrometerAxis = new Vector3(-1.0f, -1.0f, -1.0f);
this.GyrometerAxisSwap = new()
{
{ 'X', 'X' },
@@ -28,7 +28,7 @@ public OneXPlayer2() : base()
{ 'Z', 'Y' },
};
- AccelerometerAxis = new Vector3(-1.0f, 1.0f, -1.0f);
+ AccelerometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
this.AccelerometerAxisSwap = new()
{
{ 'X', 'X' },
diff --git a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMini.cs b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMini.cs
index 902c9c99f..97ebfed73 100644
--- a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMini.cs
+++ b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMini.cs
@@ -1,6 +1,7 @@
using HandheldCompanion.Inputs;
using HandheldCompanion.Managers;
using System.Collections.Generic;
+using System.Numerics;
using WindowsInput.Events;
namespace HandheldCompanion.Devices;
@@ -11,15 +12,17 @@ public OneXPlayerMini()
{
// device specific settings
ProductIllustration = "device_onexplayer_mini";
- ProductModel = "ONEXPLAYERMini";
-
+ ProductModel = "ONEXPLAYERMini";
+
+ GyrometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
{ 'Y', 'Z' },
{ 'Z', 'Y' }
- };
-
+ };
+
+ AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
diff --git a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniAMD.cs b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniAMD.cs
index 9e6dee08c..cc5c28aae 100644
--- a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniAMD.cs
+++ b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniAMD.cs
@@ -12,6 +12,6 @@ public OneXPlayerMiniAMD()
GfxClock = new double[] { 100, 2000 };
CpuClock = 4400;
- AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
+ AccelerometerAxis = new Vector3(1.0f, 1.0f, 1.0f);
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniIntel.cs b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniIntel.cs
index d40a0ce57..68688f831 100644
--- a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniIntel.cs
+++ b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniIntel.cs
@@ -13,7 +13,7 @@ public OneXPlayerMiniIntel()
GfxClock = new double[] { 100, 1400 };
CpuClock = 4700;
- GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ GyrometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'Y' },
@@ -21,6 +21,6 @@ public OneXPlayerMiniIntel()
{ 'Z', 'X' }
};
- AccelerometerAxis = new Vector3(-1.0f, 1.0f, -1.0f);
+ AccelerometerAxis = new Vector3(1.0f, -1.0f, -1.0f);
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniPro.cs b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniPro.cs
index 709d934b6..0afb8cd53 100644
--- a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniPro.cs
+++ b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniPro.cs
@@ -16,7 +16,7 @@ public OneXPlayerMiniPro()
GfxClock = new double[] { 100, 2200 };
CpuClock = 4700;
- AccelerometerAxis = new Vector3(-1.0f, 1.0f, 1.0f);
+ AccelerometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
OEMChords.Clear();
diff --git a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerOneXFly.cs b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerOneXFly.cs
index f304c87c0..5a2c91b1e 100644
--- a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerOneXFly.cs
+++ b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerOneXFly.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Numerics;
using System.Windows.Media;
using WindowsInput.Events;
namespace HandheldCompanion.Devices;
@@ -25,15 +26,17 @@ public OneXPlayerOneXFly()
nTDP = new double[] { 15, 15, 20 };
cTDP = new double[] { 5, 30 };
GfxClock = new double[] { 100, 2700 };
- CpuClock = 5100;
-
+ CpuClock = 5100;
+
+ GyrometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
GyrometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
{ 'Y', 'Z' },
{ 'Z', 'Y' }
- };
-
+ };
+
+ AccelerometerAxis = new Vector3(-1.0f, -1.0f, 1.0f);
AccelerometerAxisSwap = new SortedDictionary
{
{ 'X', 'X' },
@@ -165,7 +168,7 @@ public override bool SetLedBrightness(int brightness)
// Define the HID message for setting brightness.
byte[] msg = { 0x00, 0x07, 0xFF, 0xFD, 0x01, 0x05, (byte)brightness };
-
+
// Write the HID message to set the LED brightness.
hidDevice.Write(msg);
diff --git a/HandheldCompanion/Extensions/GlyphExtensions.cs b/HandheldCompanion/Extensions/GlyphExtensions.cs
new file mode 100644
index 000000000..4e3ab864c
--- /dev/null
+++ b/HandheldCompanion/Extensions/GlyphExtensions.cs
@@ -0,0 +1,45 @@
+using HandheldCompanion.Utils;
+
+namespace HandheldCompanion.Extensions
+{
+ public static class GlyphExtensions
+ {
+ public static string ToGlyph(this MotionInput motionInput)
+ {
+ switch (motionInput)
+ {
+ default:
+ case MotionInput.LocalSpace:
+ return "\uF272";
+ case MotionInput.PlayerSpace:
+ return "\uF119";
+ case MotionInput.WorldSpace:
+ return "\uE714";
+ /*
+ case MotionInput.AutoRollYawSwap:
+ return "\uE7F8";
+ */
+ case MotionInput.JoystickSteering:
+ return "\uEC47";
+ }
+ }
+
+ public static string ToGlyph(this MotionOuput motionOuput)
+ {
+ switch (motionOuput)
+ {
+ default:
+ case MotionOuput.Disabled:
+ return "\uE8D8";
+ case MotionOuput.RightStick:
+ return "\uF109";
+ case MotionOuput.LeftStick:
+ return "\uF108";
+ case MotionOuput.MoveCursor:
+ return "\uE962";
+ case MotionOuput.ScrollWheel:
+ return "\uEC8F";
+ }
+ }
+ }
+}
diff --git a/HandheldCompanion/Extensions/UIThreadExtensions.cs b/HandheldCompanion/Extensions/UIThreadExtensions.cs
new file mode 100644
index 000000000..685f1601a
--- /dev/null
+++ b/HandheldCompanion/Extensions/UIThreadExtensions.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Windows;
+
+namespace HandheldCompanion.Extensions
+{
+ public static class UIThreadExtensions
+ {
+ public static void ReplaceWith(this ObservableCollection collection, List list)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ collection.Clear();
+ foreach (var item in list)
+ {
+ collection.Add(item);
+ }
+ });
+ }
+
+ public static void SafeAdd(this ObservableCollection collection, T item)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ collection.Add(item);
+ });
+ }
+
+ public static void SafeRemove(this ObservableCollection collection, T item)
+ {
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ collection.Remove(item);
+ });
+ }
+ }
+}
diff --git a/HandheldCompanion/GamepadMotion.dll b/HandheldCompanion/GamepadMotion.dll
new file mode 100644
index 000000000..54260fc83
Binary files /dev/null and b/HandheldCompanion/GamepadMotion.dll differ
diff --git a/HandheldCompanion/GraphicsProcessingUnit/AMDGPU.cs b/HandheldCompanion/GraphicsProcessingUnit/AMDGPU.cs
index bd1dce1b6..6f8b742dd 100644
--- a/HandheldCompanion/GraphicsProcessingUnit/AMDGPU.cs
+++ b/HandheldCompanion/GraphicsProcessingUnit/AMDGPU.cs
@@ -1,7 +1,8 @@
using HandheldCompanion.ADLX;
+using HandheldCompanion.Managers;
+using SharpDX.Direct3D9;
using System;
-using System.Diagnostics;
-using System.Threading;
+using System.Text;
using System.Threading.Tasks;
using System.Timers;
using static HandheldCompanion.ADLX.ADLXBackend;
@@ -27,13 +28,13 @@ public bool HasRSRSupport()
return Execute(ADLXBackend.HasRSRSupport, false);
}
-
+
public override bool HasIntegerScalingSupport()
{
if (!IsInitialized)
return false;
- return Execute(() => ADLXBackend.HasIntegerScalingSupport(0), false);
+ return Execute(() => ADLXBackend.HasIntegerScalingSupport(displayIdx), false);
}
public override bool HasGPUScalingSupport()
@@ -41,7 +42,7 @@ public override bool HasGPUScalingSupport()
if (!IsInitialized)
return false;
- return Execute(() => ADLXBackend.HasGPUScalingSupport(0), false);
+ return Execute(() => ADLXBackend.HasGPUScalingSupport(displayIdx), false);
}
public override bool HasScalingModeSupport()
@@ -49,7 +50,7 @@ public override bool HasScalingModeSupport()
if (!IsInitialized)
return false;
- return Execute(() => ADLXBackend.HasScalingModeSupport(0), false);
+ return Execute(() => ADLXBackend.HasScalingModeSupport(displayIdx), false);
}
public bool GetRSR()
@@ -73,7 +74,7 @@ public override bool GetImageSharpening()
if (!IsInitialized)
return false;
- return Execute(() => ADLXBackend.GetImageSharpening(0), false);
+ return Execute(() => ADLXBackend.GetImageSharpening(deviceIdx), false);
}
public override int GetImageSharpeningSharpness()
@@ -81,7 +82,7 @@ public override int GetImageSharpeningSharpness()
if (!IsInitialized)
return -1;
- return Execute(() => ADLXBackend.GetImageSharpeningSharpness(0), -1);
+ return Execute(() => ADLXBackend.GetImageSharpeningSharpness(deviceIdx), -1);
}
public override bool GetIntegerScaling()
@@ -89,7 +90,7 @@ public override bool GetIntegerScaling()
if (!IsInitialized)
return false;
- return Execute(() => ADLXBackend.GetIntegerScaling(0), false);
+ return Execute(() => ADLXBackend.GetIntegerScaling(displayIdx), false);
}
public override bool GetGPUScaling()
@@ -97,7 +98,7 @@ public override bool GetGPUScaling()
if (!IsInitialized)
return false;
- return Execute(() => ADLXBackend.GetGPUScaling(0), false);
+ return Execute(() => ADLXBackend.GetGPUScaling(displayIdx), false);
}
public override int GetScalingMode()
@@ -105,7 +106,7 @@ public override int GetScalingMode()
if (!IsInitialized)
return -1;
- return Execute(() => ADLXBackend.GetScalingMode(0), -1);
+ return Execute(() => ADLXBackend.GetScalingMode(displayIdx), -1);
}
public bool SetRSRSharpness(int sharpness)
@@ -121,7 +122,7 @@ public override bool SetImageSharpening(bool enable)
if (!IsInitialized)
return false;
- return Execute(() => ADLXBackend.SetImageSharpening(0, enable), false);
+ return Execute(() => ADLXBackend.SetImageSharpening(deviceIdx, enable), false);
}
public bool SetRSR(bool enable)
@@ -134,11 +135,11 @@ public bool SetRSR(bool enable)
// mutually exclusive
if (enable)
{
- if (ADLXBackend.GetIntegerScaling(0))
- ADLXBackend.SetIntegerScaling(0, false);
+ if (ADLXBackend.GetIntegerScaling(displayIdx))
+ ADLXBackend.SetIntegerScaling(displayIdx, false);
- if (ADLXBackend.GetImageSharpening(0))
- ADLXBackend.SetImageSharpening(0, false);
+ if (ADLXBackend.GetImageSharpening(deviceIdx))
+ ADLXBackend.SetImageSharpening(deviceIdx, false);
}
return ADLXBackend.SetRSR(enable);
@@ -150,7 +151,7 @@ public override bool SetImageSharpeningSharpness(int sharpness)
if (!IsInitialized)
return false;
- return Execute(() => ADLXBackend.SetImageSharpeningSharpness(0, sharpness), false);
+ return Execute(() => ADLXBackend.SetImageSharpeningSharpness(deviceIdx, sharpness), false);
}
public override bool SetIntegerScaling(bool enabled, byte type = 0)
@@ -167,7 +168,7 @@ public override bool SetIntegerScaling(bool enabled, byte type = 0)
ADLXBackend.SetRSR(false);
}
- return ADLXBackend.SetIntegerScaling(0, enabled);
+ return ADLXBackend.SetIntegerScaling(displayIdx, enabled);
}, false);
}
@@ -176,7 +177,7 @@ public override bool SetGPUScaling(bool enabled)
if (!IsInitialized)
return false;
- return Execute(() => ADLXBackend.SetGPUScaling(0, enabled), false);
+ return Execute(() => ADLXBackend.SetGPUScaling(displayIdx, enabled), false);
}
public override bool SetScalingMode(int mode)
@@ -184,7 +185,12 @@ public override bool SetScalingMode(int mode)
if (!IsInitialized)
return false;
- return Execute(() => ADLXBackend.SetScalingMode(0, mode), false);
+ return Execute(() => ADLXBackend.SetScalingMode(displayIdx, mode), false);
+ }
+
+ public override float GetClock()
+ {
+ return (float)TelemetryData.gpuClockSpeedValue;
}
public override float GetClock()
@@ -214,9 +220,48 @@ public override float GetVRAMUsage()
protected AdlxTelemetryData TelemetryData = new();
- public AMDGPU()
+ public AMDGPU(AdapterInformation adapterInformation) : base(adapterInformation)
{
- IsInitialized = ADLXBackend.IntializeAdlx();
+ ADLX_RESULT result = ADLX_RESULT.ADLX_FAIL;
+ int adapterCount = 0;
+ int UniqueId = 0;
+ string dispName = string.Empty;
+ string friendlyName = MultimediaManager.GetDisplayFriendlyName(adapterInformation.Details.DeviceName);
+
+ result = GetNumberOfDisplays(ref adapterCount);
+ if (result != ADLX_RESULT.ADLX_OK)
+ return;
+
+ for (int idx = 0; idx < adapterCount; idx++)
+ {
+ StringBuilder displayName = new StringBuilder(256); // Assume display name won't exceed 255 characters
+
+ // skip if failed to retrieve display
+ result = GetDisplayName(idx, displayName, displayName.Capacity);
+ if (result != ADLX_RESULT.ADLX_OK)
+ continue;
+
+ // skip if display is not the one we're looking for
+ if (!displayName.ToString().Equals(friendlyName))
+ continue;
+
+ // update displayIdx
+ displayIdx = idx;
+ break;
+ }
+
+ if (displayIdx != -1)
+ {
+ // get the associated GPU UniqueId
+ result = GetDisplayGPU(displayIdx, ref UniqueId);
+ if (result == ADLX_RESULT.ADLX_OK)
+ {
+ result = GetGPUIndex(UniqueId, ref deviceIdx);
+ if (result == ADLX_RESULT.ADLX_OK)
+ IsInitialized = true;
+ }
+ }
+
if (!IsInitialized)
return;
@@ -231,34 +276,29 @@ public AMDGPU()
private void TelemetryTimer_Elapsed(object? sender, ElapsedEventArgs e)
{
- if (Monitor.TryEnter(telemetryLock))
+ if (telemetryLock.TryEnter())
{
- TelemetryData = ADLXBackend.GetTelemetryData();
- //Debug.WriteLine("W:{0}", TelemetryData.gpuPowerValue);
-
- Monitor.Exit(telemetryLock);
+ TelemetryData = GetTelemetryData();
+ telemetryLock.Exit();
}
}
public override void Start()
{
+ if (!IsInitialized)
+ return;
+
base.Start();
}
public override async void Stop()
{
base.Stop();
-
- // wait until the current ADLX tasks are completed
- while (!Monitor.TryEnter(updateLock) || !Monitor.TryEnter(telemetryLock))
- await Task.Delay(100);
-
- ADLXBackend.CloseAdlx();
}
private async void UpdateTimer_Elapsed(object? sender, ElapsedEventArgs e)
{
- if (Monitor.TryEnter(updateLock))
+ if (updateLock.TryEnter())
{
bool GPUScaling = false;
@@ -300,7 +340,7 @@ private async void UpdateTimer_Elapsed(object? sender, ElapsedEventArgs e)
while (DateTime.Now < timeout && !RSRSupport)
{
RSRSupport = HasRSRSupport();
- Thread.Sleep(250);
+ await Task.Delay(250);
}
RSR = GetRSR();
@@ -326,7 +366,7 @@ private async void UpdateTimer_Elapsed(object? sender, ElapsedEventArgs e)
while (DateTime.Now < timeout && !IntegerScalingSupport)
{
IntegerScalingSupport = HasIntegerScalingSupport();
- Thread.Sleep(250);
+ await Task.Delay(250);
}
IntegerScaling = GetIntegerScaling();
@@ -357,7 +397,7 @@ private async void UpdateTimer_Elapsed(object? sender, ElapsedEventArgs e)
}
catch { }
- Monitor.Exit(updateLock);
+ updateLock.Exit();
}
}
}
diff --git a/HandheldCompanion/GraphicsProcessingUnit/GPU.cs b/HandheldCompanion/GraphicsProcessingUnit/GPU.cs
index 06c8b80ec..0169bfd6d 100644
--- a/HandheldCompanion/GraphicsProcessingUnit/GPU.cs
+++ b/HandheldCompanion/GraphicsProcessingUnit/GPU.cs
@@ -1,11 +1,14 @@
-using System;
+using HandheldCompanion.Utils;
+using SharpDX.Direct3D9;
+using System;
using System.Management;
using System.Threading.Tasks;
-using System.Timers;
+using Task = System.Threading.Tasks.Task;
+using Timer = System.Timers.Timer;
namespace HandheldCompanion.GraphicsProcessingUnit
{
- public class GPU
+ public class GPU : IDisposable
{
#region
public event IntegerScalingChangedEvent IntegerScalingChanged;
@@ -18,10 +21,11 @@ public class GPU
public delegate void GPUScalingChangedEvent(bool Supported, bool Enabled, int Mode);
#endregion
- private static GPU gpu;
- private static string Manufacturer;
+ public AdapterInformation adapterInformation;
+ protected int deviceIdx = -1;
+ protected int displayIdx = -1;
- protected bool IsInitialized = false;
+ public bool IsInitialized = false;
protected const int UpdateInterval = 2000;
protected Timer UpdateTimer;
@@ -40,74 +44,68 @@ public class GPU
protected bool prevImageSharpening = false;
protected int prevImageSharpeningSharpness = -1;
- protected object updateLock = new();
- protected object telemetryLock = new();
+ protected CrossThreadLock updateLock = new();
+ protected CrossThreadLock telemetryLock = new();
+ protected bool halting = false;
- protected static object wrapperLock = new();
- protected static T Execute(Func func, T defaultValue)
+ protected T Execute(Func func, T defaultValue)
{
- lock (wrapperLock)
+ if (halting)
+ return defaultValue;
+
+ try
{
- try
- {
- Task task = Task.Run(func);
- if (task.Wait(TimeSpan.FromSeconds(5)))
- {
- return task.Result;
- }
- }
- catch (AccessViolationException ex)
+ Task task = Task.Run(() =>
{
- // Handle or log the exception as needed
- }
- catch (Exception ex)
- {
- // Handle other exceptions
- }
+ return func();
+ });
- return defaultValue;
+ if (task.Wait(TimeSpan.FromSeconds(3)))
+ return task.Result;
}
- }
+ catch (AccessViolationException)
+ { }
+ catch (Exception)
+ { }
- public GPU()
- {
- Manufacturer = MotherboardInfo.VideoController;
+ return defaultValue;
}
- public static GPU GetCurrent()
- {
- if (gpu is not null)
- return gpu;
-
- switch (Manufacturer)
- {
- case "Advanced Micro Devices, Inc.":
- gpu = new AMDGPU();
- break;
- case "Intel Corporation":
- gpu = new IntelGPU();
- break;
- }
+ public bool IsBusy => (UpdateTimer is not null && UpdateTimer.Enabled) || (TelemetryTimer is not null && TelemetryTimer.Enabled);
- return gpu;
+ public GPU(AdapterInformation adapterInformation)
+ {
+ this.adapterInformation = adapterInformation;
}
public virtual void Start()
{
- if (UpdateTimer != null)
+ // release halting flag
+ halting = false;
+
+ if (UpdateTimer != null && !UpdateTimer.Enabled)
UpdateTimer.Start();
- if (TelemetryTimer != null)
+ if (TelemetryTimer != null && !TelemetryTimer.Enabled)
TelemetryTimer.Start();
}
public virtual void Stop()
{
- if (UpdateTimer != null)
- UpdateTimer.Stop();
+ // set halting flag
+ halting = true;
+
+ try
+ {
+ if (UpdateTimer != null && UpdateTimer.Enabled)
+ UpdateTimer.Stop();
- if (TelemetryTimer != null)
- TelemetryTimer.Stop();
+ if (TelemetryTimer != null && TelemetryTimer.Enabled)
+ TelemetryTimer.Stop();
+ }
+ catch (Exception ex)
+ {
+ }
}
protected virtual void OnIntegerScalingChanged(bool supported, bool enabled)
@@ -228,5 +226,15 @@ public virtual float GetVRAMUsage()
return 0.0f;
}
+
+ public void Dispose()
+ {
+ UpdateTimer?.Dispose();
+ TelemetryTimer?.Dispose();
+ updateLock?.Dispose();
+ telemetryLock?.Dispose();
+
+ GC.SuppressFinalize(this);
+ }
}
}
diff --git a/HandheldCompanion/GraphicsProcessingUnit/IntelGPU.cs b/HandheldCompanion/GraphicsProcessingUnit/IntelGPU.cs
index 31bd60a46..fcd70c9ee 100644
--- a/HandheldCompanion/GraphicsProcessingUnit/IntelGPU.cs
+++ b/HandheldCompanion/GraphicsProcessingUnit/IntelGPU.cs
@@ -1,6 +1,5 @@
using HandheldCompanion.IGCL;
-using System.Threading;
-using System.Threading.Tasks;
+using SharpDX.Direct3D9;
using System.Timers;
using static HandheldCompanion.IGCL.IGCLBackend;
using Timer = System.Timers.Timer;
@@ -17,7 +16,7 @@ public override bool HasIntegerScalingSupport()
if (!IsInitialized)
return false;
- return Execute(() => IGCLBackend.HasIntegerScalingSupport(IGCLBackend.deviceIdx, 0), false);
+ return Execute(() => IGCLBackend.HasIntegerScalingSupport(deviceIdx, 0), false);
}
public override bool HasGPUScalingSupport()
@@ -25,7 +24,7 @@ public override bool HasGPUScalingSupport()
if (!IsInitialized)
return false;
- return Execute(() => IGCLBackend.HasGPUScalingSupport(IGCLBackend.deviceIdx, 0), false);
+ return Execute(() => IGCLBackend.HasGPUScalingSupport(deviceIdx, 0), false);
}
public override bool HasScalingModeSupport()
@@ -33,7 +32,7 @@ public override bool HasScalingModeSupport()
if (!IsInitialized)
return false;
- return Execute(() => IGCLBackend.HasGPUScalingSupport(IGCLBackend.deviceIdx, 0), false);
+ return Execute(() => IGCLBackend.HasGPUScalingSupport(deviceIdx, 0), false);
}
public override bool GetGPUScaling()
@@ -41,7 +40,7 @@ public override bool GetGPUScaling()
if (!IsInitialized)
return false;
- return Execute(() => IGCLBackend.GetGPUScaling(IGCLBackend.deviceIdx, 0), false);
+ return Execute(() => IGCLBackend.GetGPUScaling(deviceIdx, 0), false);
}
public override bool GetImageSharpening()
@@ -49,7 +48,7 @@ public override bool GetImageSharpening()
if (!IsInitialized)
return false;
- return Execute(() => IGCLBackend.GetImageSharpening(IGCLBackend.deviceIdx, 0), false);
+ return Execute(() => IGCLBackend.GetImageSharpening(deviceIdx, 0), false);
}
public override int GetImageSharpeningSharpness()
@@ -57,7 +56,7 @@ public override int GetImageSharpeningSharpness()
if (!IsInitialized)
return 0;
- return Execute(() => IGCLBackend.GetImageSharpeningSharpness(IGCLBackend.deviceIdx, 0), 0);
+ return Execute(() => IGCLBackend.GetImageSharpeningSharpness(deviceIdx, 0), 0);
}
public override bool GetIntegerScaling()
@@ -65,7 +64,7 @@ public override bool GetIntegerScaling()
if (!IsInitialized)
return false;
- return Execute(() => IGCLBackend.GetIntegerScaling(IGCLBackend.deviceIdx), false);
+ return Execute(() => IGCLBackend.GetIntegerScaling(deviceIdx), false);
}
// GPUScaling can't be disabled on Intel GPU ?
@@ -74,7 +73,7 @@ public override bool SetGPUScaling(bool enabled)
if (!IsInitialized)
return false;
- return Execute(() => IGCLBackend.SetGPUScaling(IGCLBackend.deviceIdx, 0), false);
+ return Execute(() => IGCLBackend.SetGPUScaling(deviceIdx, 0), false);
}
public override bool SetImageSharpening(bool enable)
@@ -82,12 +81,12 @@ public override bool SetImageSharpening(bool enable)
if (!IsInitialized)
return false;
- return Execute(() => IGCLBackend.SetImageSharpening(IGCLBackend.deviceIdx, 0, enable), false);
+ return Execute(() => IGCLBackend.SetImageSharpening(deviceIdx, 0, enable), false);
}
public override bool SetImageSharpeningSharpness(int sharpness)
{
- return Execute(() => IGCLBackend.SetImageSharpeningSharpness(IGCLBackend.deviceIdx, 0, sharpness), false);
+ return Execute(() => IGCLBackend.SetImageSharpeningSharpness(deviceIdx, 0, sharpness), false);
}
public override bool SetScalingMode(int mode)
@@ -95,7 +94,7 @@ public override bool SetScalingMode(int mode)
if (!IsInitialized)
return false;
- return Execute(() => IGCLBackend.SetScalingMode(IGCLBackend.deviceIdx, 0, mode), false);
+ return Execute(() => IGCLBackend.SetScalingMode(deviceIdx, 0, mode), false);
}
public override bool SetIntegerScaling(bool enabled, byte type)
@@ -103,7 +102,12 @@ public override bool SetIntegerScaling(bool enabled, byte type)
if (!IsInitialized)
return false;
- return Execute(() => IGCLBackend.SetIntegerScaling(IGCLBackend.deviceIdx, enabled, type), false);
+ return Execute(() => IGCLBackend.SetIntegerScaling(deviceIdx, enabled, type), false);
+ }
+
+ public override float GetClock()
+ {
+ return (float)TelemetryData.GpuCurrentClockFrequencyValue;
}
public override float GetClock()
@@ -128,12 +132,14 @@ public override float GetTemperature()
protected ctl_telemetry_data TelemetryData = new();
- public IntelGPU()
+ public IntelGPU(AdapterInformation adapterInformation) : base(adapterInformation)
{
- IsInitialized = IGCLBackend.Initialize();
- if (!IsInitialized)
+ deviceIdx = GetDeviceIdx(adapterInformation.Details.Description);
+ if (deviceIdx == -1)
return;
+ IsInitialized = true;
+
TelemetryTimer = new Timer(TelemetryInterval);
TelemetryTimer.AutoReset = true;
TelemetryTimer.Elapsed += TelemetryTimer_Elapsed;
@@ -141,28 +147,24 @@ public IntelGPU()
private void TelemetryTimer_Elapsed(object? sender, ElapsedEventArgs e)
{
- if (Monitor.TryEnter(telemetryLock))
+ if (telemetryLock.TryEnter())
{
- TelemetryData = IGCLBackend.GetTelemetryData();
-
- Monitor.Exit(telemetryLock);
+ TelemetryData = GetTelemetry(deviceIdx);
+ telemetryLock.Exit();
}
}
public override void Start()
{
+ if (!IsInitialized)
+ return;
+
base.Start();
}
public override async void Stop()
{
base.Stop();
-
- // wait until the current IGCL tasks are completed
- while (!Monitor.TryEnter(updateLock) || !Monitor.TryEnter(telemetryLock))
- await Task.Delay(100);
-
- IGCLBackend.Terminate();
}
}
}
diff --git a/HandheldCompanion/HandheldCompanion.csproj b/HandheldCompanion/HandheldCompanion.csproj
index c966432a1..8c126e450 100644
--- a/HandheldCompanion/HandheldCompanion.csproj
+++ b/HandheldCompanion/HandheldCompanion.csproj
@@ -1,4 +1,4 @@
-
+
WinExe
@@ -76,6 +76,7 @@
+
@@ -108,8 +109,8 @@
-
+
@@ -124,16 +125,17 @@
-
+
+
@@ -148,10 +150,11 @@
+
-
-
+
+
@@ -161,7 +164,7 @@
-
+
@@ -180,6 +183,7 @@
+
@@ -189,15 +193,6 @@
-
- Code
-
-
- Code
-
-
- Code
-
Resources.resx
True
@@ -231,6 +226,12 @@
PreserveNewest
+
+ PreserveNewest
+
+
+ PreserveNewest
+
PreserveNewest
diff --git a/HandheldCompanion/Helpers/GamepadMotion.cs b/HandheldCompanion/Helpers/GamepadMotion.cs
new file mode 100644
index 000000000..fdca2c067
--- /dev/null
+++ b/HandheldCompanion/Helpers/GamepadMotion.cs
@@ -0,0 +1,327 @@
+using HandheldCompanion.Sensors;
+using System;
+using System.Runtime.InteropServices;
+
+namespace HandheldCompanion.Helpers
+{
+ public enum CalibrationMode
+ {
+ ///
+ /// No auto-calibration. This is the default.
+ ///
+ Manual = 0,
+ ///
+ /// Automatically try to detect when the controller is being held still and update the calibration offset accordingly.
+ ///
+ Stillness = 1,
+ ///
+ /// Calculate an angular velocity from changes in the gravity direction as detected by the accelerometer. If these are steady enough, use them to make corrections to the calibration offset. This will only apply to relevant axes.
+ ///
+ SensorFusion = 2,
+ }
+
+ public class GamepadMotion : IDisposable
+ {
+ private IntPtr handle;
+ private IMUCalibration calibration;
+ private bool thresholdCalibration;
+
+ public float gyroX;
+ public float gyroY;
+ public float gyroZ;
+ public float accelX;
+ public float accelY;
+ public float accelZ;
+ public float deltaTime;
+
+ public const float minGyro = 124.0f; // known minimum
+ public const float minAccel = 2.0f; // known minimum
+
+ public float maxGyro = minGyro;
+ public float maxAccel = minAccel;
+
+ public string deviceInstanceId;
+
+ private const string DllName = "GamepadMotion.dll";
+
+ public GamepadMotion(string deviceInstanceId, CalibrationMode calibrationMode)
+ {
+ handle = CreateGamepadMotion();
+
+ // store device path
+ this.deviceInstanceId = deviceInstanceId;
+
+ // get previous calibration
+ calibration = IMUCalibration.GetCalibration(deviceInstanceId.ToUpper());
+ SetCalibrationOffset(calibration.xOffset, calibration.yOffset, calibration.zOffset, calibration.weight);
+ SetCalibrationMode(calibrationMode);
+ }
+
+ ~GamepadMotion()
+ {
+ Dispose(false);
+ }
+
+ public void Reset()
+ {
+ ResetGamepadMotion(handle);
+ }
+
+ public IMUCalibration GetCalibration()
+ {
+ return calibration;
+ }
+
+ // Implement the ProcessMotion function
+ public void ProcessMotion(float gyroX, float gyroY, float gyroZ, float accelX, float accelY, float accelZ, float deltaTime)
+ {
+ this.gyroX = gyroX;
+ this.gyroY = gyroY;
+ this.gyroZ = gyroZ;
+ this.accelX = accelX;
+ this.accelY = accelY;
+ this.accelZ = accelZ;
+ this.deltaTime = deltaTime;
+
+ if (thresholdCalibration)
+ {
+ if (gyroX > maxGyro)
+ maxGyro = gyroX;
+ if (gyroY > maxGyro)
+ maxGyro = gyroY;
+ if (gyroZ > maxGyro)
+ maxGyro = gyroZ;
+
+ if (accelX > maxAccel)
+ maxAccel = accelX;
+ if (accelY > maxAccel)
+ maxAccel = accelY;
+ if (accelZ > maxAccel)
+ maxAccel = accelZ;
+ }
+
+ ProcessMotion(handle, gyroX, gyroY, gyroZ, accelX, accelY, accelZ, deltaTime);
+ }
+
+ public void GetRawGyro(out float x, out float y, out float z)
+ {
+ x = gyroX;
+ y = gyroY;
+ z = gyroZ;
+ }
+
+ public void GetRawAcceleration(out float x, out float y, out float z)
+ {
+ x = accelX;
+ y = accelY;
+ z = accelZ;
+ }
+
+ // Implement the GetCalibratedGyro function
+ public void GetCalibratedGyro(out float x, out float y, out float z)
+ {
+ GetCalibratedGyro(handle, out x, out y, out z);
+ }
+
+ // Implement the GetGravity function
+ public void GetGravity(out float x, out float y, out float z)
+ {
+ GetGravity(handle, out x, out y, out z);
+ }
+
+ // Implement the GetProcessedAcceleration function
+ public void GetProcessedAcceleration(out float x, out float y, out float z)
+ {
+ GetProcessedAcceleration(handle, out x, out y, out z);
+ }
+
+ // Implement the GetOrientation function
+ public void GetOrientation(out float w, out float x, out float y, out float z)
+ {
+ GetOrientation(handle, out w, out x, out y, out z);
+ }
+
+ // Implement the GetPlayerSpaceGyro function
+ public void GetPlayerSpaceGyro(out float x, out float y, float yawRelaxFactor)
+ {
+ GetPlayerSpaceGyro(handle, out x, out y, yawRelaxFactor);
+ }
+
+ // Implement the GetWorldSpaceGyro function
+ public void GetWorldSpaceGyro(out float x, out float y, float sideReductionThreshold)
+ {
+ GetWorldSpaceGyro(handle, out x, out y, sideReductionThreshold);
+ }
+
+ // Implement the StartContinuousCalibration function
+ public void StartContinuousCalibration()
+ {
+ StartContinuousCalibration(handle);
+ }
+
+ // Implement the PauseContinuousCalibration function
+ public void PauseContinuousCalibration()
+ {
+ PauseContinuousCalibration(handle);
+ }
+
+ // Implement the ResetContinuousCalibration function
+ public void ResetContinuousCalibration()
+ {
+ ResetContinuousCalibration(handle);
+ }
+
+ // Implement the GetCalibrationOffset function
+ public void GetCalibrationOffset(out float xOffset, out float yOffset, out float zOffset)
+ {
+ GetCalibrationOffset(handle, out xOffset, out yOffset, out zOffset);
+ }
+
+ // Implement the SetCalibrationOffset function
+ public void SetCalibrationOffset(float xOffset, float yOffset, float zOffset, int weight)
+ {
+ calibration.xOffset = xOffset;
+ calibration.yOffset = yOffset;
+ calibration.zOffset = zOffset;
+ calibration.weight = weight;
+
+ SetCalibrationOffset(handle, xOffset, yOffset, zOffset, weight);
+ }
+
+ // Implement the GetAutoCalibrationConfidence function
+ public float GetAutoCalibrationConfidence()
+ {
+ return GetAutoCalibrationConfidence(handle);
+ }
+
+ // Implement the SetAutoCalibrationConfidence function
+ public void SetAutoCalibrationConfidence(float newConfidence)
+ {
+ SetAutoCalibrationConfidence(handle, newConfidence);
+ }
+
+ // Implement the GetAutoCalibrationIsSteady function
+ public bool GetAutoCalibrationIsSteady()
+ {
+ return GetAutoCalibrationIsSteady(handle);
+ }
+
+ // Implement the GetCalibrationMode function
+ public CalibrationMode GetCalibrationMode()
+ {
+ return GetCalibrationMode(handle);
+ }
+
+ // Implement the SetCalibrationMode function
+ public void SetCalibrationMode(CalibrationMode calibrationMode)
+ {
+ SetCalibrationMode(handle, calibrationMode);
+ }
+
+ public void StartThresholdCalibration()
+ {
+ thresholdCalibration = true;
+ }
+
+ public void PauseThresholdCalibration()
+ {
+ thresholdCalibration = false;
+ }
+
+ public void ResetThresholdCalibration()
+ {
+ maxGyro = minGyro;
+ maxAccel = minAccel;
+ }
+
+ public void SetCalibrationThreshold(float gyroTreshold, float accelThreshold)
+ {
+ calibration.SetGyroThreshold(Math.Max(minGyro, gyroTreshold));
+ calibration.SetAcceleroThreshold(Math.Max(minAccel, accelThreshold));
+ }
+
+ // Implement the ResetMotion function
+ public void ResetMotion()
+ {
+ ResetMotion(handle);
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (handle != IntPtr.Zero)
+ {
+ DeleteGamepadMotion(handle);
+ handle = IntPtr.Zero;
+ }
+ }
+
+ [DllImport(DllName)]
+ private static extern IntPtr CreateGamepadMotion();
+
+ [DllImport(DllName)]
+ private static extern void DeleteGamepadMotion(IntPtr motion);
+
+ [DllImport(DllName)]
+ private static extern void ResetGamepadMotion(IntPtr motion);
+
+ [DllImport(DllName)]
+ private static extern void ProcessMotion(IntPtr motion, float gyroX, float gyroY, float gyroZ, float accelX, float accelY, float accelZ, float deltaTime);
+
+ [DllImport(DllName)]
+ private static extern void GetCalibratedGyro(IntPtr motion, out float x, out float y, out float z);
+
+ [DllImport(DllName)]
+ private static extern void GetGravity(IntPtr motion, out float x, out float y, out float z);
+
+ [DllImport(DllName)]
+ private static extern void GetProcessedAcceleration(IntPtr motion, out float x, out float y, out float z);
+
+ [DllImport(DllName)]
+ private static extern void GetOrientation(IntPtr motion, out float w, out float x, out float y, out float z);
+
+ [DllImport(DllName)]
+ private static extern void GetPlayerSpaceGyro(IntPtr motion, out float x, out float y, float yawRelaxFactor);
+
+ [DllImport(DllName)]
+ private static extern void GetWorldSpaceGyro(IntPtr motion, out float x, out float y, float sideReductionThreshold);
+
+ [DllImport(DllName)]
+ private static extern void StartContinuousCalibration(IntPtr motion);
+
+ [DllImport(DllName)]
+ private static extern void PauseContinuousCalibration(IntPtr motion);
+
+ [DllImport(DllName)]
+ private static extern void ResetContinuousCalibration(IntPtr motion);
+
+ [DllImport(DllName)]
+ private static extern void GetCalibrationOffset(IntPtr motion, out float xOffset, out float yOffset, out float zOffset);
+
+ [DllImport(DllName)]
+ private static extern void SetCalibrationOffset(IntPtr motion, float xOffset, float yOffset, float zOffset, int weight);
+
+ [DllImport(DllName)]
+ private static extern float GetAutoCalibrationConfidence(IntPtr motion);
+
+ [DllImport(DllName)]
+ private static extern void SetAutoCalibrationConfidence(IntPtr motion, float newConfidence);
+
+ [DllImport(DllName)]
+ private static extern bool GetAutoCalibrationIsSteady(IntPtr motion);
+
+ [DllImport(DllName)]
+ private static extern CalibrationMode GetCalibrationMode(IntPtr motion);
+
+ [DllImport(DllName)]
+ private static extern void SetCalibrationMode(IntPtr motion, CalibrationMode calibrationMode);
+
+ [DllImport(DllName)]
+ private static extern void ResetMotion(IntPtr motion);
+ }
+}
diff --git a/HandheldCompanion/IGCL/IGCLBackend.cs b/HandheldCompanion/IGCL/IGCLBackend.cs
index 8485a0e35..2fcc95d51 100644
--- a/HandheldCompanion/IGCL/IGCLBackend.cs
+++ b/HandheldCompanion/IGCL/IGCLBackend.cs
@@ -384,87 +384,141 @@ public struct ctl_telemetry_data
public double FanSpeedValue;
}
- [DllImport("IGCL_Wrapper.dll")]
- private static extern ctl_result_t IntializeIgcl();
+ [DllImport("kernel32")]
+ public static extern IntPtr LoadLibrary(string lpFileName);
- [DllImport("IGCL_Wrapper.dll")]
- private static extern void CloseIgcl();
-
- [DllImport("IGCL_Wrapper.dll")]
- public static extern uint GetAdapterCounts();
-
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- private static extern IntPtr GetDevices(ref uint pAdapterCount);
-
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- private static extern ctl_result_t GetDeviceProperties(ctl_device_adapter_handle_t hDevice, ref ctl_device_adapter_properties_t StDeviceAdapterProperties);
-
- // RetroScaling
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- static extern ctl_result_t GetRetroScalingCaps(ctl_device_adapter_handle_t hDevice, ref ctl_retro_scaling_caps_t RetroScalingCaps);
-
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- static extern ctl_result_t GetRetroScalingSettings(ctl_device_adapter_handle_t hDevice, ref ctl_retro_scaling_settings_t RetroScalingSettings);
-
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- static extern ctl_result_t SetRetroScalingSettings(ctl_device_adapter_handle_t hDevice, ctl_retro_scaling_settings_t retroScalingSettings);
-
- // Scaling
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- static extern ctl_result_t GetScalingCaps(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_scaling_caps_t ScalingCaps);
-
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- static extern ctl_result_t GetScalingSettings(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_scaling_settings_t ScalingSetting);
-
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- static extern ctl_result_t SetScalingSettings(ctl_device_adapter_handle_t hDevice, uint idx, ctl_scaling_settings_t scalingSettings);
-
- // Sharpness
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- static extern ctl_result_t GetSharpnessCaps(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_sharpness_caps_t SharpnessCaps);
+ [DllImport("kernel32", SetLastError = true)]
+ private static extern bool FreeLibrary(IntPtr hModule);
+
+ [DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
+ private static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string lpProcName);
+
+ // Define the function delegates with the same signatures as the functions in the DLL
+ private delegate ctl_result_t InitializeIgclDelegate();
+ private delegate void CloseIgclDelegate();
+ private delegate IntPtr EnumerateDevicesDelegate(ref uint pAdapterCount);
+ private delegate ctl_result_t GetDevicePropertiesDelegate(ctl_device_adapter_handle_t hDevice, ref ctl_device_adapter_properties_t StDeviceAdapterProperties);
+ private delegate ctl_result_t GetRetroScalingCapsDelegate(ctl_device_adapter_handle_t hDevice, ref ctl_retro_scaling_caps_t RetroScalingCaps);
+ private delegate ctl_result_t GetRetroScalingSettingsDelegate(ctl_device_adapter_handle_t hDevice, ref ctl_retro_scaling_settings_t RetroScalingSettings);
+ private delegate ctl_result_t SetRetroScalingSettingsDelegate(ctl_device_adapter_handle_t hDevice, ctl_retro_scaling_settings_t retroScalingSettings);
+ private delegate ctl_result_t GetScalingCapsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_scaling_caps_t ScalingCaps);
+ private delegate ctl_result_t GetScalingSettingsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_scaling_settings_t ScalingSetting);
+ private delegate ctl_result_t SetScalingSettingsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ctl_scaling_settings_t scalingSettings);
+ private delegate ctl_result_t GetSharpnessCapsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_sharpness_caps_t SharpnessCaps);
+ private delegate ctl_result_t GetSharpnessSettingsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_sharpness_settings_t GetSharpness);
+ private delegate ctl_result_t SetSharpnessSettingsDelegate(ctl_device_adapter_handle_t hDevice, uint idx, ctl_sharpness_settings_t SetSharpness);
+ private delegate ctl_result_t GetTelemetryDataDelegate(ctl_device_adapter_handle_t hDevice, ref ctl_telemetry_data TelemetryData);
+
+ // Define the function pointers
+ private static InitializeIgclDelegate InitializeIgcl;
+ private static CloseIgclDelegate CloseIgcl;
+ private static EnumerateDevicesDelegate EnumerateDevices;
+ private static GetDevicePropertiesDelegate GetDeviceProperties;
+ private static GetRetroScalingCapsDelegate GetRetroScalingCaps;
+ private static GetRetroScalingSettingsDelegate GetRetroScalingSettings;
+ private static SetRetroScalingSettingsDelegate SetRetroScalingSettings;
+ private static GetScalingCapsDelegate GetScalingCaps;
+ private static GetScalingSettingsDelegate GetScalingSettings;
+ private static SetScalingSettingsDelegate SetScalingSettings;
+ private static GetSharpnessCapsDelegate GetSharpnessCaps;
+ private static GetSharpnessSettingsDelegate GetSharpnessSettings;
+ private static SetSharpnessSettingsDelegate SetSharpnessSettings;
+ private static GetTelemetryDataDelegate GetTelemetryData;
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- static extern ctl_result_t GetSharpnessSettings(ctl_device_adapter_handle_t hDevice, uint idx, ref ctl_sharpness_settings_t GetSharpness);
+ public static IntPtr[] devices = new IntPtr[1] { IntPtr.Zero };
+ private static IntPtr pDll = IntPtr.Zero;
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- static extern ctl_result_t SetSharpnessSettings(ctl_device_adapter_handle_t hDevice, uint idx, ctl_sharpness_settings_t SetSharpness);
+ // for this support library
+ public enum IGCLStatus
+ {
+ NO_ERROR = 0,
+ DLL_NOT_FOUND = 1,
+ DLL_INCORRECT_VERSION = 2,
+ DLL_INITIALIZE_ERROR = 3
+ }
- [DllImport("IGCL_Wrapper.dll", CallingConvention = CallingConvention.Cdecl)]
- static extern ctl_result_t GetTelemetryData(ctl_device_adapter_handle_t hDevice, ref ctl_telemetry_data TelemetryData);
+ private const string dllName = "IGCL_Wrapper.dll";
+ private static IGCLStatus status = IGCLStatus.NO_ERROR;
- [DllImport("kernel32", SetLastError = true)]
- static extern IntPtr LocalFree(IntPtr mem);
+ private static Delegate GetDelegate(string procName, Type delegateType)
+ {
+ IntPtr ptr = GetProcAddress(pDll, procName);
+ if (ptr != IntPtr.Zero)
+ {
+ var d = Marshal.GetDelegateForFunctionPointer(ptr, delegateType);
+ return d;
+ }
- public static IntPtr[] devices = new IntPtr[1] { IntPtr.Zero };
- public static nint deviceIdx = 0;
+ var result = Marshal.GetHRForLastWin32Error();
+ throw Marshal.GetExceptionForHR(result);
+ }
public static bool Initialize()
{
+ pDll = LoadLibrary(dllName);
+ if (pDll == IntPtr.Zero)
+ {
+ status = IGCLStatus.DLL_NOT_FOUND;
+ }
+ else
+ {
+ try
+ {
+ // Get the function pointers
+ InitializeIgcl = (InitializeIgclDelegate)GetDelegate("IntializeIgcl", typeof(InitializeIgclDelegate));
+ CloseIgcl = (CloseIgclDelegate)GetDelegate("CloseIgcl", typeof(CloseIgclDelegate));
+ EnumerateDevices = (EnumerateDevicesDelegate)GetDelegate("EnumerateDevices", typeof(EnumerateDevicesDelegate));
+ GetDeviceProperties = (GetDevicePropertiesDelegate)GetDelegate("GetDeviceProperties", typeof(GetDevicePropertiesDelegate));
+ GetRetroScalingCaps = (GetRetroScalingCapsDelegate)GetDelegate("GetRetroScalingCaps", typeof(GetRetroScalingCapsDelegate));
+ GetRetroScalingSettings = (GetRetroScalingSettingsDelegate)GetDelegate("GetRetroScalingSettings", typeof(GetRetroScalingSettingsDelegate));
+ SetRetroScalingSettings = (SetRetroScalingSettingsDelegate)GetDelegate("SetRetroScalingSettings", typeof(SetRetroScalingSettingsDelegate));
+ GetScalingCaps = (GetScalingCapsDelegate)GetDelegate("GetScalingCaps", typeof(GetScalingCapsDelegate));
+ GetScalingSettings = (GetScalingSettingsDelegate)GetDelegate("GetScalingSettings", typeof(GetScalingSettingsDelegate));
+ SetScalingSettings = (SetScalingSettingsDelegate)GetDelegate("SetScalingSettings", typeof(SetScalingSettingsDelegate));
+ GetSharpnessCaps = (GetSharpnessCapsDelegate)GetDelegate("GetSharpnessCaps", typeof(GetSharpnessCapsDelegate));
+ GetSharpnessSettings = (GetSharpnessSettingsDelegate)GetDelegate("GetSharpnessSettings", typeof(GetSharpnessSettingsDelegate));
+ SetSharpnessSettings = (SetSharpnessSettingsDelegate)GetDelegate("SetSharpnessSettings", typeof(SetSharpnessSettingsDelegate));
+ GetTelemetryData = (GetTelemetryDataDelegate)GetDelegate("GetTelemetryData", typeof(GetTelemetryDataDelegate));
+ }
+ catch
+ {
+ status = IGCLStatus.DLL_INITIALIZE_ERROR;
+ }
+ }
+
+ if (status != IGCLStatus.NO_ERROR)
+ return false;
+
ctl_result_t Result = ctl_result_t.CTL_RESULT_SUCCESS;
// Call Init and check the result
- Result = IntializeIgcl();
- if (Result != ctl_result_t.CTL_RESULT_SUCCESS)
- return false;
+ Result = InitializeIgcl();
+ return Result == ctl_result_t.CTL_RESULT_SUCCESS;
+ }
+
+ public static int GetDeviceIdx(string deviceName)
+ {
+ // test
+ Terminate();
+ Initialize();
+ ctl_result_t Result = ctl_result_t.CTL_RESULT_SUCCESS;
uint adapterCount = 0;
// Get the number of Intel devices
- IntPtr hDevices = GetDevices(ref adapterCount);
+ IntPtr hDevices = EnumerateDevices(ref adapterCount);
if (hDevices == IntPtr.Zero)
- return false;
+ return -1;
// Convert the device handles to an array of IntPtr
devices = new IntPtr[adapterCount];
Marshal.Copy(hDevices, devices, 0, (int)adapterCount);
if (devices.Length == 0)
- return false;
+ return -1;
for (int idx = 0; idx < devices.Length; idx++)
{
ctl_device_adapter_properties_t StDeviceAdapterProperties = new();
- ctl_adapter_properties_flag_t adapterFlag;
-
ctl_device_adapter_handle_t hDevice = new()
{
handle = devices[idx]
@@ -474,31 +528,21 @@ public static bool Initialize()
if (Result != ctl_result_t.CTL_RESULT_SUCCESS)
continue;
- switch (adapterCount)
- {
- case 1:
- deviceIdx = idx;
- return true;
-
- default:
- {
- adapterFlag = StDeviceAdapterProperties.graphics_adapter_properties;
- if (!adapterFlag.HasFlag(ctl_adapter_properties_flag_t.CTL_ADAPTER_PROPERTIES_FLAG_INTEGRATED))
- {
- deviceIdx = idx;
- return true;
- }
- }
- break;
- }
+ if (deviceName.Equals(StDeviceAdapterProperties.name))
+ return idx;
}
- return false;
+ return -1;
}
public static void Terminate()
{
- CloseIgcl();
+ if (pDll != IntPtr.Zero)
+ {
+ CloseIgcl();
+ FreeLibrary(pDll);
+ pDll = IntPtr.Zero;
+ }
}
internal static bool HasGPUScalingSupport(nint deviceIdx, uint displayIdx)
@@ -535,7 +579,7 @@ internal static bool GetGPUScaling(nint deviceIdx, uint displayIdx)
{
handle = device
};
-
+
Result = GetScalingSettings(hDevice, displayIdx, ref ScalingSettings);
if (Result != ctl_result_t.CTL_RESULT_SUCCESS)
return false;
@@ -556,7 +600,7 @@ internal static bool SetGPUScaling(nint deviceIdx, uint displayIdx, bool enabled
{
handle = device
};
-
+
Result = GetScalingSettings(hDevice, displayIdx, ref ScalingSettings);
if (Result != ctl_result_t.CTL_RESULT_SUCCESS)
return false;
@@ -642,7 +686,7 @@ internal static bool SetScalingMode(nint deviceIdx, uint displayIdx, int mode)
{
handle = device
};
-
+
Result = GetScalingSettings(hDevice, displayIdx, ref ScalingSettings);
if (Result != ctl_result_t.CTL_RESULT_SUCCESS)
return false;
@@ -831,7 +875,7 @@ internal static bool SetIntegerScaling(nint deviceIdx, bool enabled, byte type)
// fill custom scaling details
RetroScalingSettings.Enable = enabled;
RetroScalingSettings.RetroScalingType = RetroScalingType;
-
+
Result = SetRetroScalingSettings(hDevice, RetroScalingSettings);
if (Result != ctl_result_t.CTL_RESULT_SUCCESS)
return false;
@@ -844,7 +888,7 @@ internal static bool SetIntegerScaling(nint deviceIdx, bool enabled, byte type)
return RetroScalingSettings.Enable == enabled;
}
- internal static ctl_telemetry_data GetTelemetryData()
+ public static ctl_telemetry_data GetTelemetry(nint deviceIdx)
{
ctl_result_t Result = ctl_result_t.CTL_RESULT_SUCCESS;
ctl_telemetry_data TelemetryData = new();
diff --git a/HandheldCompanion/IGCL_Wrapper.dll b/HandheldCompanion/IGCL_Wrapper.dll
index bed77e9f7..d6e228407 100644
Binary files a/HandheldCompanion/IGCL_Wrapper.dll and b/HandheldCompanion/IGCL_Wrapper.dll differ
diff --git a/HandheldCompanion/Inputs/AxisState.cs b/HandheldCompanion/Inputs/AxisState.cs
index b2d2b0c9c..bc45f72f8 100644
--- a/HandheldCompanion/Inputs/AxisState.cs
+++ b/HandheldCompanion/Inputs/AxisState.cs
@@ -9,7 +9,7 @@ namespace HandheldCompanion.Inputs;
[Serializable]
public partial class AxisState : ICloneable
{
- public ConcurrentDictionary State = new(Environment.ProcessorCount * 2, (int)AxisFlags.Max);
+ public ConcurrentDictionary State = new();
public AxisState(ConcurrentDictionary State)
{
diff --git a/HandheldCompanion/Inputs/ButtonState.cs b/HandheldCompanion/Inputs/ButtonState.cs
index db5bc13e2..85274a881 100644
--- a/HandheldCompanion/Inputs/ButtonState.cs
+++ b/HandheldCompanion/Inputs/ButtonState.cs
@@ -9,7 +9,7 @@ namespace HandheldCompanion.Inputs;
[Serializable]
public partial class ButtonState : ICloneable
{
- public ConcurrentDictionary State = new(Environment.ProcessorCount * 2, (int)ButtonFlags.Max);
+ public ConcurrentDictionary State = new();
public ButtonState(ConcurrentDictionary