diff --git a/HandheldCompanion.iss b/HandheldCompanion.iss
index 6f0653221..e1d8b6583 100644
--- a/HandheldCompanion.iss
+++ b/HandheldCompanion.iss
@@ -312,10 +312,10 @@ end;
procedure Dependency_AddHideHide;
begin
- Dependency_Add('HidHide_1.4.192_x64.exe',
+ Dependency_Add('HidHide_1.4.202_x64.exe',
'/quiet /norestart',
'HidHide Drivers',
- 'https://github.com/nefarius/HidHide/releases/download/v1.4.192.0/HidHide_1.4.192_x64.exe',
+ 'https://github.com/nefarius/HidHide/releases/download/v1.4.202.0/HidHide_1.4.202_x64.exe',
'', True, False);
end;
@@ -364,7 +364,7 @@ end;
#define MyAppSetupName 'Handheld Companion'
#define MyBuildId 'HandheldCompanion'
-#define MyAppVersion '0.20.3.1'
+#define MyAppVersion '0.20.4.0'
#define MyAppPublisher 'BenjaminLSR'
#define MyAppCopyright 'Copyright @ BenjaminLSR'
#define MyAppURL 'https://github.com/Valkirie/HandheldCompanion'
@@ -482,7 +482,7 @@ begin
if not(keepHidhideCheckbox.Checked) then
begin
- if(ShellExec('', 'msiexec.exe', '/X{BE49B9DE-F8EB-4F54-B312-DD4B601985FC}', '', SW_SHOW, ewWaitUntilTerminated, resultCode)) then
+ if(ShellExec('', 'msiexec.exe', '/X{50D7EB6D-6A4A-4A38-B09C-CC28F75F082E} /L*V "C:\HidHide-Uninstall.log"', '', SW_SHOW, ewWaitUntilTerminated, resultCode)) then
begin
log('Successfully executed Hidhide uninstaller');
if(resultCode = 0) then
@@ -580,4 +580,4 @@ begin
Result := True;
end;
-#endif
+#endif
\ No newline at end of file
diff --git a/HandheldCompanion/ADLX/ADLXBackend.cs b/HandheldCompanion/ADLX/ADLXBackend.cs
new file mode 100644
index 000000000..a3702e47e
--- /dev/null
+++ b/HandheldCompanion/ADLX/ADLXBackend.cs
@@ -0,0 +1,103 @@
+using System.Runtime.InteropServices;
+
+namespace HandheldCompanion.ADLX
+{
+ public class ADLXBackend
+ {
+ public const string ADLX_Wrapper = @"ADLX_Wrapper.dll";
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct AdlxTelemetryData
+ {
+ // GPU Usage
+ public bool gpuUsageSupported;
+ public double gpuUsageValue;
+
+ // GPU Core Frequency
+ public bool gpuClockSpeedSupported;
+ public double gpuClockSpeedValue;
+
+ // GPU VRAM Frequency
+ public bool gpuVRAMClockSpeedSupported;
+ public double gpuVRAMClockSpeedValue;
+
+ // GPU Core Temperature
+ public bool gpuTemperatureSupported;
+ public double gpuTemperatureValue;
+
+ // GPU Hotspot Temperature
+ public bool gpuHotspotTemperatureSupported;
+ public double gpuHotspotTemperatureValue;
+
+ // GPU Power
+ public bool gpuPowerSupported;
+ public double gpuPowerValue;
+
+ // Fan Speed
+ public bool gpuFanSpeedSupported;
+ public double gpuFanSpeedValue;
+
+ // VRAM Usage
+ public bool gpuVramSupported;
+ public double gpuVramValue;
+
+ // GPU Voltage
+ public bool gpuVoltageSupported;
+ public double gpuVoltageValue;
+
+ // GPU TBP
+ public bool gpuTotalBoardPowerSupported;
+ public double gpuTotalBoardPowerValue;
+ }
+
+ [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 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);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetRSRSharpness();
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetRSRSharpness(int sharpness);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetAntiLag(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetAntiLag(int GPU, bool enable);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetBoost(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetBoost(int GPU, bool enable);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetBoostResolution(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetBoostResolution(int GPU, int minRes);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetChill(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetChill(int GPU, bool enable);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetChillMinFPS(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetChillMinFPS(int GPU, int minFPS);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetChillMaxFPS(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetChillMaxFPS(int GPU, int maxFPS);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetImageSharpening(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetImageSharpening(int GPU, bool enable);
+ [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 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 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 GetAdlxTelemetry(int GPU, ref AdlxTelemetryData adlxTelemetryData);
+
+ internal static AdlxTelemetryData GetTelemetryData()
+ {
+ AdlxTelemetryData TelemetryData = new();
+ bool Result = GetAdlxTelemetry(0, ref TelemetryData);
+ return TelemetryData;
+ }
+ }
+}
diff --git a/HandheldCompanion/ADLX_3DSettings.dll b/HandheldCompanion/ADLX_3DSettings.dll
deleted file mode 100644
index 7cdeefa6f..000000000
Binary files a/HandheldCompanion/ADLX_3DSettings.dll and /dev/null differ
diff --git a/HandheldCompanion/ADLX_DisplaySettings.dll b/HandheldCompanion/ADLX_DisplaySettings.dll
deleted file mode 100644
index dc8b70eae..000000000
Binary files a/HandheldCompanion/ADLX_DisplaySettings.dll and /dev/null differ
diff --git a/HandheldCompanion/ADLX_Wrapper.dll b/HandheldCompanion/ADLX_Wrapper.dll
new file mode 100644
index 000000000..9a61d0678
Binary files /dev/null and b/HandheldCompanion/ADLX_Wrapper.dll differ
diff --git a/HandheldCompanion/App.config b/HandheldCompanion/App.config
index a3ffdcfcf..a6c1d1472 100644
--- a/HandheldCompanion/App.config
+++ b/HandheldCompanion/App.config
@@ -11,246 +11,276 @@
-
- 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
+
+
+ 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
+
+
\ No newline at end of file
diff --git a/HandheldCompanion/App.xaml b/HandheldCompanion/App.xaml
index e41d783e8..6e2074da8 100644
--- a/HandheldCompanion/App.xaml
+++ b/HandheldCompanion/App.xaml
@@ -65,6 +65,7 @@
+
diff --git a/HandheldCompanion/Controllers/IController.xaml b/HandheldCompanion/Controllers/IController.xaml
index 595e0f0fd..a9e332655 100644
--- a/HandheldCompanion/Controllers/IController.xaml
+++ b/HandheldCompanion/Controllers/IController.xaml
@@ -4,8 +4,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:HandheldCompanion.Controllers"
- xmlns:resx="clr-namespace:HandheldCompanion.Properties"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+ xmlns:resx="clr-namespace:HandheldCompanion.Properties"
xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern"
d:DesignHeight="450"
d:DesignWidth="800"
@@ -30,20 +30,67 @@
FontSize="30"
Glyph="" />
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -53,8 +100,8 @@
Name="ui_button_hook"
Width="100"
Click="ui_button_hook_Click"
- FontSize="14"
Content="{x:Static resx:Resources.Controller_Connect}"
+ FontSize="14"
Style="{DynamicResource AccentButtonStyle}" />
diff --git a/HandheldCompanion/Controllers/IController.xaml.cs b/HandheldCompanion/Controllers/IController.xaml.cs
index 7e3f74323..12e3649ce 100644
--- a/HandheldCompanion/Controllers/IController.xaml.cs
+++ b/HandheldCompanion/Controllers/IController.xaml.cs
@@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
@@ -43,101 +44,166 @@ public partial class IController : UserControl
AxisLayoutFlags.L2, AxisLayoutFlags.R2,
};
- public static readonly string defaultGlyph = "\u2753";
+ protected List SourceAxis = new()
+ {
+ // same as target, we assume all controllers have those axes
+ AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick,
+ AxisLayoutFlags.L2, AxisLayoutFlags.R2
+ };
+
+ // Buttons and axes all controllers have that we can map.
+ // Additional ones can be added per controller.
+ protected List SourceButtons = new()
+ {
+ // same as target, we assume all controllers have those buttons
+ 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,
+ // additional buttons calculated from the above
+ ButtonFlags.L2Soft, ButtonFlags.R2Soft, ButtonFlags.L2Full, ButtonFlags.R2Full,
+ ButtonFlags.LeftStickUp, ButtonFlags.LeftStickDown, ButtonFlags.LeftStickLeft, ButtonFlags.LeftStickRight,
+ ButtonFlags.RightStickUp, ButtonFlags.RightStickDown, ButtonFlags.RightStickLeft, ButtonFlags.RightStickRight
+ };
+ protected FontFamily GlyphFontFamily = new("PromptFont");
+ public static readonly string defaultGlyph = "\u2753";
public ControllerCapabilities Capabilities = ControllerCapabilities.None;
protected SortedDictionary ColoredAxis = new();
-
protected SortedDictionary ColoredButtons = new();
protected FontFamily DefaultFontFamily = new("Segeo WP");
public PnPDetails Details;
- // UI
- protected FontFamily GlyphFontFamily = new("PromptFont");
-
public ButtonState InjectedButtons = new();
-
public ControllerState Inputs = new();
+
+ protected double VibrationStrength = 1.0d;
+ private byte _UserIndex = 255;
+ private readonly int MaxUserIndex = 10;
+
+ private int workingIdx = 0;
+ private Thread workingThread;
+ private bool workingThreadRunning;
+
public virtual bool IsReady => true;
public bool IsBusy
{
get
{
- return !IsEnabled;
+ // UI thread
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ return !IsEnabled;
+ });
+
+ return false;
}
+
set
{
// UI thread
Application.Current.Dispatcher.Invoke(() =>
{
IsEnabled = !value;
- ProgressBarPanel.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
+ ProgressBarWarning.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
});
+
+ switch (value)
+ {
+ case false:
+ {
+ // kill working thread
+ if (workingThread is not null)
+ {
+ workingThreadRunning = false;
+ // Ensure the thread has finished execution
+ if (workingThread.IsAlive)
+ workingThread.Join();
+ workingThread = null;
+ }
+
+ // visually update user index
+ SetVirtualControllerVisualIndex(UserIndex);
+ }
+ break;
+
+ case true:
+ {
+ workingThreadRunning = true;
+ workingThread = new Thread(workingThreadLoop);
+ workingThread.IsBackground = true;
+ workingThread.Start();
+ }
+ return;
+ }
}
}
- protected List SourceAxis = new()
- {
- // same as target, we assume all controllers have those axes
- AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick,
- AxisLayoutFlags.L2, AxisLayoutFlags.R2
- };
-
- // Buttons and axes all controllers have that we can map.
- // Additional ones can be added per controller.
- protected List SourceButtons = new()
- {
- // same as target, we assume all controllers have those buttons
- 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,
- // additional buttons calculated from the above
- ButtonFlags.L2Soft, ButtonFlags.R2Soft, ButtonFlags.L2Full, ButtonFlags.R2Full,
- ButtonFlags.LeftStickUp, ButtonFlags.LeftStickDown, ButtonFlags.LeftStickLeft, ButtonFlags.LeftStickRight,
- ButtonFlags.RightStickUp, ButtonFlags.RightStickDown, ButtonFlags.RightStickLeft, ButtonFlags.RightStickRight
- };
-
- private byte _UserIndex = 255;
protected byte UserIndex
{
get
{
return _UserIndex;
}
+
set
{
_UserIndex = value;
UserIndexChanged?.Invoke(value);
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- foreach(FrameworkElement frameworkElement in UserIndexPanel.Children)
- {
- if (frameworkElement is not Border)
- continue;
-
- Border border = (Border)frameworkElement;
- int idx = UserIndexPanel.Children.IndexOf(border);
-
- if (idx == value)
- border.SetResourceReference(BackgroundProperty, "AccentAAFillColorDefaultBrush");
- else
- border.SetResourceReference(BackgroundProperty, "SystemControlForegroundBaseLowBrush");
- }
- });
+ if (IsBusy)
+ return;
+
+ // visually update user index
+ SetVirtualControllerVisualIndex(value);
}
}
- protected double VibrationStrength = 1.0d;
+ private void SetVirtualControllerVisualIndex(int value)
+ {
+ // UI thread (async)
+ Application.Current.Dispatcher.BeginInvoke(() =>
+ {
+ foreach (FrameworkElement frameworkElement in UserIndexPanel.Children)
+ {
+ if (frameworkElement is not Border)
+ continue;
+
+ Border border = (Border)frameworkElement;
+ int idx = UserIndexPanel.Children.IndexOf(border);
+
+ if (idx == value)
+ border.SetResourceReference(BackgroundProperty, "AccentAAFillColorDefaultBrush");
+ else
+ border.SetResourceReference(BackgroundProperty, "SystemControlForegroundBaseLowBrush");
+ }
+ });
+ }
+
+ private void workingThreadLoop()
+ {
+ int direction = 1; // 1 for increasing, -1 for decreasing
+ workingIdx = 0;
+
+ while (workingThreadRunning)
+ {
+ workingIdx += direction; // increment or decrement the index
+ if (workingIdx == MaxUserIndex - 1 || workingIdx == 0) // if the index reaches the limit or zero
+ direction = -direction; // reverse the direction
+
+ SetVirtualControllerVisualIndex(workingIdx);
+
+ Thread.Sleep(100);
+ }
+ }
public IController()
{
InitializeComponent();
+ MaxUserIndex = UserIndexPanel.Children.Count;
}
public virtual void AttachDetails(PnPDetails details)
@@ -206,13 +272,21 @@ protected void DrawUI()
// update name
ControllerName.Text = (IsVirtual() ? Properties.Resources.Controller_Virtual : string.Empty) + ToString();
- // virtual controller shouldn't be visible
+ // some elements of virtual controllers shouldn't be visible
if (IsVirtual())
- this.Visibility = Visibility.Collapsed;
+ {
+ ui_button_hook.Visibility = Visibility.Collapsed;
+ ui_button_hide.Visibility = Visibility.Collapsed;
+ ui_button_calibrate.Visibility = Visibility.Collapsed;
+ }
}
protected void UpdateUI()
{
+ // some elements of virtual controllers shouldn't be visible
+ if (IsVirtual())
+ return;
+
// UI thread (async)
Application.Current.Dispatcher.BeginInvoke(() =>
{
diff --git a/HandheldCompanion/Controllers/LegionController.cs b/HandheldCompanion/Controllers/LegionController.cs
index d870afbef..acaf54137 100644
--- a/HandheldCompanion/Controllers/LegionController.cs
+++ b/HandheldCompanion/Controllers/LegionController.cs
@@ -166,7 +166,6 @@ public override void Plug()
if (dataThread is null)
{
dataThreadRunning = true;
-
dataThread = new Thread(dataThreadLoop);
dataThread.IsBackground = true;
dataThread.Start();
@@ -178,11 +177,13 @@ public override void Plug()
public override void Unplug()
{
- // kill data thread
+ // Kill data thread
if (dataThread is not null)
{
dataThreadRunning = false;
- dataThread.Join();
+ // Ensure the thread has finished execution
+ if (dataThread.IsAlive)
+ dataThread.Join();
dataThread = null;
}
diff --git a/HandheldCompanion/Controllers/NeptuneController.cs b/HandheldCompanion/Controllers/NeptuneController.cs
index 9587c7685..412f41153 100644
--- a/HandheldCompanion/Controllers/NeptuneController.cs
+++ b/HandheldCompanion/Controllers/NeptuneController.cs
@@ -330,8 +330,14 @@ public override void Unplug()
try
{
// kill rumble thread
- rumbleThreadRunning = false;
- rumbleThread.Join();
+ if (rumbleThread is not null)
+ {
+ rumbleThreadRunning = false;
+ // Ensure the thread has finished execution
+ if (rumbleThread.IsAlive)
+ rumbleThread.Join();
+ rumbleThread = null;
+ }
// close controller
Close();
diff --git a/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs b/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs
index 74f017a12..63ccffbb0 100644
--- a/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs
@@ -2,10 +2,10 @@
using HandheldCompanion.Misc;
using HandheldCompanion.Processors;
using HandheldCompanion.Utils;
+using HandheldCompanion.Views;
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Diagnostics;
-using System.Management;
using System.Windows;
namespace HandheldCompanion.Controls.Hints
@@ -55,7 +55,7 @@ protected override async void HintActionButton_Click(object sender, RoutedEventA
$"{Properties.Resources.Dialog_ForceRestartDesc}",
ContentDialogButton.Primary, null,
$"{Properties.Resources.Dialog_Yes}",
- $"{Properties.Resources.Dialog_No}");
+ $"{Properties.Resources.Dialog_No}", MainWindow.GetCurrent());
await result;
diff --git a/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs b/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs
index 8630a0700..746a1a3ff 100644
--- a/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs
@@ -1,5 +1,6 @@
using HandheldCompanion.Misc;
using HandheldCompanion.Utils;
+using HandheldCompanion.Views;
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Diagnostics;
@@ -72,7 +73,7 @@ protected override async void HintActionButton_Click(object sender, RoutedEventA
$"{Properties.Resources.Dialog_ForceRestartDesc}",
ContentDialogButton.Primary, null,
$"{Properties.Resources.Dialog_Yes}",
- $"{Properties.Resources.Dialog_No}");
+ $"{Properties.Resources.Dialog_No}", MainWindow.GetCurrent());
await result;
diff --git a/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml.cs b/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml.cs
index 0f910bb6d..828e32269 100644
--- a/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml.cs
+++ b/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml.cs
@@ -7,9 +7,9 @@
using iNKORE.UI.WPF.Modern.Controls;
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Windows;
using System.Windows.Controls;
-using System.Linq;
namespace HandheldCompanion.Controls;
diff --git a/HandheldCompanion/Controls/ProcessEx.xaml.cs b/HandheldCompanion/Controls/ProcessEx.xaml.cs
index f4a5d6b9b..6482b7ca9 100644
--- a/HandheldCompanion/Controls/ProcessEx.xaml.cs
+++ b/HandheldCompanion/Controls/ProcessEx.xaml.cs
@@ -2,19 +2,15 @@
using HandheldCompanion.Platforms;
using HandheldCompanion.Utils;
using Microsoft.Win32;
-using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
-using System.Reflection;
-using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
-using Windows.Foundation.Collections;
namespace HandheldCompanion.Controls;
@@ -209,6 +205,9 @@ public void Refresh()
{
using (new ScopedLock(updateLock))
{
+ if (MainThread is null)
+ return;
+
switch (MainThread.ThreadState)
{
case ThreadState.Wait:
@@ -320,4 +319,9 @@ private void HighDPIAware_Toggled(object sender, RoutedEventArgs e)
HighDPIAware = !T_HighDPIAware.IsOn;
}
+
+ internal void MainThreadDisposed()
+ {
+ MainThread = ProcessManager.GetMainThread(Process);
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Converters/InvertPercentageConverter.cs b/HandheldCompanion/Converters/InvertPercentageConverter.cs
new file mode 100644
index 000000000..d3c939aa7
--- /dev/null
+++ b/HandheldCompanion/Converters/InvertPercentageConverter.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Globalization;
+using System.Windows.Data;
+
+namespace HandheldCompanion.Converters;
+
+public class InvertPercentageConverter : IValueConverter
+{
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return 100 - (double)value;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return 100 - (double)value;
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEO2021Pro.cs b/HandheldCompanion/Devices/AYANEO/AYANEO2021Pro.cs
index 5508910dd..208e4b65d 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEO2021Pro.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEO2021Pro.cs
@@ -1,9 +1,4 @@
-using HandheldCompanion.Inputs;
-using System.Collections.Generic;
-
-using WindowsInput.Events;
-
-namespace HandheldCompanion.Devices;
+namespace HandheldCompanion.Devices;
public class AYANEO2021Pro : AYANEO2021
{
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEODevice.cs b/HandheldCompanion/Devices/AYANEO/AYANEODevice.cs
index dc02918b6..62421e7d4 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEODevice.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEODevice.cs
@@ -25,7 +25,7 @@ public AYANEODevice()
{
prevPowerStatus = SystemInformation.PowerStatus.PowerLineStatus;
prevBatteryLevelPercentage = (int)(SystemInformation.PowerStatus.BatteryLifePercent * 100);
- PowerManager.PowerStatusChanged += PowerManager_PowerStatusChanged;
+ SystemManager.PowerStatusChanged += PowerManager_PowerStatusChanged;
}
private void PowerManager_PowerStatusChanged(PowerStatus powerStatus)
@@ -101,7 +101,7 @@ public override bool SetLedBrightness(int brightness)
// so that we can let people mess with brightness slider
return base.SetLedBrightness(brightness);
}
-
+
private void SetLEDColor(Color color)
{
using (new ScopedLock(updateLock))
diff --git a/HandheldCompanion/Devices/GPD/GPDWinMini-7640U.cs b/HandheldCompanion/Devices/GPD/GPDWinMini-7640U.cs
new file mode 100644
index 000000000..22876efb8
--- /dev/null
+++ b/HandheldCompanion/Devices/GPD/GPDWinMini-7640U.cs
@@ -0,0 +1,11 @@
+namespace HandheldCompanion.Devices;
+
+public class GPDWinMini_7640U : GPDWinMini_7840U
+{
+ public GPDWinMini_7640U()
+ {
+ // https://www.amd.com/en/products/apu/amd-ryzen-5-7640u
+ GfxClock = new double[] { 200, 2600 };
+ CpuClock = 4900;
+ }
+}
diff --git a/HandheldCompanion/Devices/GPD/GPDWinMini-7840U.cs b/HandheldCompanion/Devices/GPD/GPDWinMini-7840U.cs
new file mode 100644
index 000000000..1c8816962
--- /dev/null
+++ b/HandheldCompanion/Devices/GPD/GPDWinMini-7840U.cs
@@ -0,0 +1,110 @@
+using HandheldCompanion.Inputs;
+using System.Collections.Generic;
+using System.Numerics;
+using WindowsInput.Events;
+
+namespace HandheldCompanion.Devices;
+
+public class GPDWinMini_7840U : IDevice
+{
+ public GPDWinMini_7840U()
+ {
+ // https://www.amd.com/en/products/apu/amd-ryzen-7-7840u
+ nTDP = new double[] { 15, 15, 18 };
+ cTDP = new double[] { 5, 18 };
+ GfxClock = new double[] { 200, 2700 };
+ CpuClock = 5100;
+
+ // device specific settings
+ ProductIllustration = "device_gpd_winmini";
+
+ // device specific capacities
+ Capabilities = DeviceCapabilities.FanControl;
+
+ ECDetails = new ECDetails
+ {
+ AddressFanControl = 0x47A, // Fan % setpoint address, 0 for off, 1 to 244 for 0 - 100%
+ AddressFanDuty = 0x47A, // Fan duty and control are the same for this device
+ AddressStatusCommandPort = 0x4E, // Unverified
+ AddressDataPort = 0x4F, // Unverified
+ FanValueMin = 0, // 0 is off, but functions do + lowest value, so 0 required
+ FanValueMax = 244 // 100% ~6000 RPM
+ };
+
+ GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f);
+ GyrometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'Y' },
+ { 'Y', 'Z' },
+ { 'Z', 'X' }
+ };
+
+ AccelerometerAxis = new Vector3(1.0f, -1.0f, 1.0f);
+ AccelerometerAxisSwap = new SortedDictionary
+ {
+ { 'X', 'X' },
+ { 'Y', 'Z' },
+ { 'Z', 'Y' }
+ };
+
+ // Disabled this one as Win Max 2 also sends an Xbox guide input when Menu key is pressed.
+ OEMChords.Add(new DeviceChord("Menu",
+ new List { KeyCode.LButton | KeyCode.XButton2 },
+ new List { KeyCode.LButton | KeyCode.XButton2 },
+ true, ButtonFlags.OEM1
+ ));
+
+ // note, need to manually configured in GPD app
+ OEMChords.Add(new DeviceChord("L4",
+ new List { KeyCode.F11, KeyCode.L },
+ new List { KeyCode.F11, KeyCode.L },
+ false, ButtonFlags.OEM2
+ ));
+
+ OEMChords.Add(new DeviceChord("R4",
+ new List { KeyCode.F12, KeyCode.R },
+ new List { KeyCode.F12, KeyCode.R },
+ false, ButtonFlags.OEM3
+ ));
+ }
+
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.OEM2:
+ return "\u2276";
+ case ButtonFlags.OEM3:
+ return "\u2277";
+ }
+
+ return defaultGlyph;
+ }
+
+ public override float ReadFanDuty()
+ {
+ // Does not work, reads 255 255
+ // Define memory addresses for fan speed data
+ byte Address1 = 0x78;
+ byte Address2 = 0x79;
+
+ // Initialize the fan speed percentage
+ int fanSpeedPercentageActual = 0;
+
+ // Read the two bytes from memory (assumed to represent fan speed)
+ uint data1 = ECRamReadByte(Address1);
+ uint data2 = ECRamReadByte(Address2);
+
+ //LogManager.LogDebug("ReadFanDuty data1 {0} data2 {1}", data1, data2);
+
+ // Combine the two bytes into a 16-bit integer (fanSpeed)
+ short fanSpeed = (short)((data2 << 8) | data1);
+
+ // Assign the fan speed as a percentage to fanSpeedPercentageActual
+ fanSpeedPercentageActual = fanSpeed;
+
+ //LogManager.LogDebug("ReadFanDuty percentage actual {0}", fanSpeedPercentageActual);
+
+ return fanSpeedPercentageActual;
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/IDevice.cs b/HandheldCompanion/Devices/IDevice.cs
index 5c78e32fa..cdc4bd8f6 100644
--- a/HandheldCompanion/Devices/IDevice.cs
+++ b/HandheldCompanion/Devices/IDevice.cs
@@ -6,7 +6,6 @@
using HandheldCompanion.Utils;
using HidLibrary;
using iNKORE.UI.WPF.Modern.Controls;
-using LibreHardwareMonitor.Hardware.Motherboard;
using Nefarius.Utilities.DeviceManagement.PnP;
using System;
using System.Collections.Generic;
@@ -310,6 +309,17 @@ public static IDevice GetDefault()
case "WIN2":
device = new GPDWin2();
break;
+ case "G1617-01":
+ switch (Processor)
+ {
+ case "AMD Ryzen 5 7640U w/ Radeon 760M Graphics":
+ device = new GPDWinMini_7640U();
+ break;
+ case "AMD Ryzen 7 7840U w/ Radeon 780M Graphics":
+ device = new GPDWinMini_7840U();
+ break;
+ }
+ break;
case "G1618-03":
device = new GPDWin3();
break;
diff --git a/HandheldCompanion/Devices/Lenovo/LegionGo.cs b/HandheldCompanion/Devices/Lenovo/LegionGo.cs
index 49076e93f..5b3af31aa 100644
--- a/HandheldCompanion/Devices/Lenovo/LegionGo.cs
+++ b/HandheldCompanion/Devices/Lenovo/LegionGo.cs
@@ -7,8 +7,6 @@
using Nefarius.Utilities.DeviceManagement.PnP;
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Management;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;
@@ -29,12 +27,82 @@ public enum LegionMode
Custom = 0xFF,
}
- private FanTable fanTable = new();
+ public enum CapabilityID
+ {
+ IGPUMode = 0x00010000,
+ FlipToStart = 0x00030000,
+ NvidiaGPUDynamicDisplaySwitching = 0x00040000,
+ AMDSmartShiftMode = 0x00050001,
+ AMDSkinTemperatureTracking = 0x00050002,
+ SupportedPowerModes = 0x00070000,
+ LegionZoneSupportVersion = 0x00090000,
+ IGPUModeChangeStatus = 0x000F0000,
+ CPUShortTermPowerLimit = 0x0101FF00,
+ CPULongTermPowerLimit = 0x0102FF00,
+ CPUPeakPowerLimit = 0x0103FF00,
+ CPUTemperatureLimit = 0x0104FF00,
+ APUsPPTPowerLimit = 0x0105FF00,
+ CPUCrossLoadingPowerLimit = 0x0106FF00,
+ CPUPL1Tau = 0x0107FF00,
+ GPUPowerBoost = 0x0201FF00,
+ GPUConfigurableTGP = 0x0202FF00,
+ GPUTemperatureLimit = 0x0203FF00,
+ GPUTotalProcessingPowerTargetOnAcOffsetFromBaseline = 0x0204FF00,
+ GPUStatus = 0x02070000,
+ GPUDidVid = 0x02090000,
+ InstantBootAc = 0x03010001,
+ InstantBootUsbPowerDelivery = 0x03010002,
+ FanFullSpeed = 0x04020000,
+ CpuCurrentFanSpeed = 0x04030001,
+ GpuCurrentFanSpeed = 0x04030002,
+ CpuCurrentTemperature = 0x05040000,
+ GpuCurrentTemperature = 0x05050000
+ }
+
+ private Task GetFanFullSpeedAsync() =>
+ WMI.CallAsync("root\\WMI",
+ $"SELECT * FROM LENOVO_OTHER_METHOD",
+ "GetFeatureValue",
+ new() { { "IDs", (int)CapabilityID.FanFullSpeed } },
+ pdc => Convert.ToInt32(pdc["Value"].Value) == 1);
+
+ public Task SetFanFullSpeedAsync(bool enabled) =>
+ WMI.CallAsync("root\\WMI",
+ $"SELECT * FROM LENOVO_OTHER_METHOD",
+ "SetFeatureValue",
+ new()
+ {
+ { "IDs", (int)CapabilityID.FanFullSpeed },
+ { "value", enabled ? 1 : 0 },
+ });
+
+ private Task SetFanTable(FanTable fanTable) => WMI.CallAsync("root\\WMI",
+ $"SELECT * FROM LENOVO_FAN_METHOD",
+ "Fan_Set_Table",
+ new() { { "FanTable", fanTable.GetBytes() } });
+
+ private Task SetSmartFanMode(int fanMode) => WMI.CallAsync("root\\WMI",
+ $"SELECT * FROM LENOVO_GAMEZONE_DATA",
+ "SetSmartFanMode",
+ new() { { "Data", fanMode } });
+
+ private Task SetCPUPowerLimit(CapabilityID capabilityID, int limit) =>
+ WMI.CallAsync("root\\WMI",
+ $"SELECT * FROM LENOVO_OTHER_METHOD",
+ "SetFeatureValue",
+ new()
+ {
+ { "IDs", (int)capabilityID },
+ { "value", limit },
+ });
public const byte INPUT_HID_ID = 0x04;
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 LegionGo()
{
// device specific settings
@@ -70,7 +138,7 @@ public LegionGo()
// device specific capacities
Capabilities |= DeviceCapabilities.None;
- // Capabilities |= DeviceCapabilities.FanControl;
+ Capabilities |= DeviceCapabilities.FanControl;
Capabilities |= DeviceCapabilities.DynamicLighting;
Capabilities |= DeviceCapabilities.DynamicLightingBrightness;
@@ -139,29 +207,16 @@ public LegionGo()
DefaultLayout.ButtonLayout[ButtonFlags.B8] = new List() { new MouseActions { MouseType = MouseActionsType.ScrollDown } };
Init();
- }
-
- public override void SetFanControl(bool enable, int mode = 0)
- {
- // do something
- }
- public void SetFanFullSpeed(bool enabled)
- {
- // Fan control: Default, Full (0, 1)
- ECRAMWrite(0x8A, (byte)(enabled ? 1 : 0));
- }
-
- public override void SetFanDuty(double percent)
- {
- // do something
+ Task task = Task.Run(async () => await GetFanFullSpeedAsync());
+ bool FanFullSpeed = task.Result;
}
private void PowerProfileManager_Applied(PowerProfile profile, UpdateSource source)
{
- if (profile.FanProfile.fanMode == FanMode.Hardware)
- fanTable = new(new ushort[] { 44, 48, 55, 60, 71, 79, 87, 87, 100, 100 });
- else
+ FanTable fanTable = new(new ushort[] { 44, 48, 55, 60, 71, 79, 87, 87, 100, 100 });
+ if (profile.FanProfile.fanMode != FanMode.Hardware)
+ {
fanTable = new(new ushort[] {
(ushort)profile.FanProfile.fanSpeeds[1],
(ushort)profile.FanProfile.fanSpeeds[2],
@@ -174,81 +229,19 @@ private void PowerProfileManager_Applied(PowerProfile profile, UpdateSource sour
(ushort)profile.FanProfile.fanSpeeds[9],
(ushort)profile.FanProfile.fanSpeeds[10],
});
+ }
- try
- {
- // Fan control
- ManagementScope managementScope = new ManagementScope("root\\WMI");
- managementScope.Connect();
- ObjectQuery objectQuery = new ObjectQuery("SELECT * FROM LENOVO_FAN_METHOD");
- using (ManagementObjectCollection searcher = new ManagementObjectSearcher(managementScope, objectQuery).Get())
- {
- var obj = searcher.Cast