diff --git a/HandheldCompanion.iss b/HandheldCompanion.iss
index 82349543c..41ee38364 100644
--- a/HandheldCompanion.iss
+++ b/HandheldCompanion.iss
@@ -8,6 +8,7 @@
#define UseNetCoreCheck
#ifdef UseNetCoreCheck
#define UseDotNet80
+ ;#define UseDotNet90
#endif
;#define UseVC2005
@@ -26,7 +27,7 @@
#define InstallerVersion '0.2'
#define MyAppSetupName 'Handheld Companion'
#define MyBuildId 'HandheldCompanion'
-#define MyAppVersion '0.21.6.1'
+#define MyAppVersion '0.21.7.0'
#define MyAppPublisher 'BenjaminLSR'
#define MyAppCopyright 'Copyright @ BenjaminLSR'
#define MyAppURL 'https://github.com/Valkirie/HandheldCompanion'
@@ -52,12 +53,6 @@
; RTSS 7.3.6
#define NewRtssVersion "7.3.5.28010"
-//#define DotNetX64DownloadLink "https://download.visualstudio.microsoft.com/download/pr/b280d97f-25a9-4ab7-8a12-8291aa3af117/a37ed0e68f51fcd973e9f6cb4f40b1a7/windowsdesktop-runtime-8.0.0-win-x64.exe"
-//#define DotNetX86DownloadLink "https://download.visualstudio.microsoft.com/download/pr/f9e3b581-059d-429f-9f0d-1d1167ff7e32/bd7661030cd5d66cd3eee0fd20b24540/windowsdesktop-runtime-8.0.0-win-x86.exe"
-
-#define DotNetX64DownloadLink "https://download.visualstudio.microsoft.com/download/pr/f18288f6-1732-415b-b577-7fb46510479a/a98239f751a7aed31bc4aa12f348a9bf/windowsdesktop-runtime-8.0.1-win-x64.exe"
-#define DotNetX86DownloadLink "https://download.visualstudio.microsoft.com/download/pr/ca725693-6de7-4a4d-b8a4-4390b0387c66/ce13f2f016152d9b5f2d3c6537cc415b/windowsdesktop-runtime-8.0.1-win-x86.exe"
-
#define DirectXDownloadLink "https://download.microsoft.com/download/1/7/1/1718CCC4-6315-4D8E-9543-8E28A4E18C4C/dxwebsetup.exe"
#define HidHideDownloadLink "https://github.com/nefarius/HidHide/releases/download/v1.5.230.0/HidHide_1.5.230_x64.exe"
#define ViGemDownloadLink "https://github.com/nefarius/ViGEmBus/releases/download/v1.22.0/ViGEmBus_1.22.0_x64_x86_arm64.exe"
@@ -69,6 +64,14 @@
#ifdef UseDotNet80
#define MyConfigurationExt "net8.0"
+ #define DotNetX64DownloadLink "https://download.visualstudio.microsoft.com/download/pr/f18288f6-1732-415b-b577-7fb46510479a/a98239f751a7aed31bc4aa12f348a9bf/windowsdesktop-runtime-8.0.1-win-x64.exe"
+ #define DotNetX86DownloadLink "https://download.visualstudio.microsoft.com/download/pr/ca725693-6de7-4a4d-b8a4-4390b0387c66/ce13f2f016152d9b5f2d3c6537cc415b/windowsdesktop-runtime-8.0.1-win-x86.exe"
+#endif
+
+#ifdef UseDotNet90
+ #define MyConfigurationExt "net9.0"
+ #define DotNetX64DownloadLink "https://download.visualstudio.microsoft.com/download/pr/30d1fcdb-8bf1-4b6e-ad06-f66ed68017bf/20abf38df849587b0a2de99a31f5c1c8/windowsdesktop-runtime-9.0.0-rc.2.24474.4-win-x64.exe"
+ #define DotNetX86DownloadLink "https://download.visualstudio.microsoft.com/download/pr/f6a4c462-a2a6-4488-9448-574b659c31e5/7eb8840cb5e42e0fd41a57964fe3472c/windowsdesktop-runtime-9.0.0-rc.2.24474.4-win-x86.exe"
#endif
#define WindowsVersion "10.0.19041"
@@ -169,6 +172,7 @@ procedure Dependency_Add_With_Version(const Filename, NewVersion, InstalledVersi
function Dependency_PrepareToInstall(var NeedsRestart: Boolean): String; forward;
function Dependency_UpdateReadyMemo(const Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String; forward;
procedure Dependency_AddDotNet80Desktop; forward;
+procedure Dependency_AddDotNet90Desktop; forward;
procedure Dependency_AddDirectX; forward;
procedure Dependency_AddHideHide; forward;
procedure Dependency_AddViGem; forward;
@@ -306,6 +310,15 @@ begin
end;
#endif
+#ifdef UseDotNet90
+ installedVersion:= regGetInstalledVersion('{#DotNetName}');
+ if(compareVersions('{#NewDotNetVersion}', installedVersion, '.', '-') > 0) then
+ begin
+ log('{#DotNetName} {#NewDotNetVersion} needs update.');
+ Dependency_AddDotNet90Desktop;
+ end;
+#endif
+
#ifdef UseVC2005
Dependency_AddVC2005;
#endif
@@ -621,8 +634,17 @@ begin
'/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
'{#DotNetName}', Dependency_String('{#DotNetX86DownloadLink}', '{#DotNetX64DownloadLink}'), '', False, False);
end;
-end;
+end;
+procedure Dependency_AddDotNet90Desktop;
+begin
+ // https://dotnet.microsoft.com/fr-fr/download/dotnet/9.0
+ if not Dependency_IsNetCoreInstalled('Microsoft.WindowsDesktop.App 9.0.0') then begin
+ Dependency_Add_With_Version('dotNet90desktop' + Dependency_ArchSuffix + '.exe', '{#NewDotNetVersion}', regGetInstalledVersion('{#DotNetName}'),
+ '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
+ '{#DotNetName}', Dependency_String('{#DotNetX86DownloadLink}', '{#DotNetX64DownloadLink}'), '', False, False);
+ end;
+end;
procedure Dependency_AddVC2005;
begin
diff --git a/HandheldCompanion/Actions/MouseActions.cs b/HandheldCompanion/Actions/MouseActions.cs
index 97fdc8c00..dfcdc52bc 100644
--- a/HandheldCompanion/Actions/MouseActions.cs
+++ b/HandheldCompanion/Actions/MouseActions.cs
@@ -51,7 +51,7 @@ public class MouseActions : GyroActions
// settings axis
public int Sensivity = 33;
public float Acceleration = 1.0f;
- public int Deadzone = 10; // stick only
+ public int Deadzone = 15; // stick only
public bool Filtering = false; // pad only
public float FilterCutoff = 0.05f; // pad only
public bool AxisRotated = false;
diff --git a/HandheldCompanion/App.config b/HandheldCompanion/App.config
index 40c87ebd8..69abc7893 100644
--- a/HandheldCompanion/App.config
+++ b/HandheldCompanion/App.config
@@ -69,7 +69,7 @@
en-US
- 0
+ 20
False
@@ -305,6 +305,9 @@
100
+
+ False
+
\ No newline at end of file
diff --git a/HandheldCompanion/Commands/EmptyCommands.cs b/HandheldCompanion/Commands/EmptyCommands.cs
index f45fc32e3..4e847c913 100644
--- a/HandheldCompanion/Commands/EmptyCommands.cs
+++ b/HandheldCompanion/Commands/EmptyCommands.cs
@@ -12,9 +12,9 @@ public EmptyCommands()
base.OnKeyDown = true;
}
- public virtual void Execute(bool IsKeyDown, bool IsKeyUp)
+ public virtual void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, IsBackground);
}
}
}
diff --git a/HandheldCompanion/Commands/ExecutableCommands.cs b/HandheldCompanion/Commands/ExecutableCommands.cs
index 51f55c522..8b1c694ed 100644
--- a/HandheldCompanion/Commands/ExecutableCommands.cs
+++ b/HandheldCompanion/Commands/ExecutableCommands.cs
@@ -23,7 +23,7 @@ public ExecutableCommands()
base.OnKeyUp = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
if (!File.Exists(this.Path))
return;
@@ -45,7 +45,7 @@ public override void Execute(bool IsKeyDown, bool IsKeyUp)
process.Start();
});
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/FunctionCommands.cs b/HandheldCompanion/Commands/FunctionCommands.cs
index f4f0cbc8e..c52efc3c4 100644
--- a/HandheldCompanion/Commands/FunctionCommands.cs
+++ b/HandheldCompanion/Commands/FunctionCommands.cs
@@ -1,5 +1,7 @@
using HandheldCompanion.Commands.Functions.HC;
using HandheldCompanion.Commands.Functions.Multimedia;
+using HandheldCompanion.Commands.Functions.Multitasking;
+using HandheldCompanion.Commands.Functions.Performance;
using HandheldCompanion.Commands.Functions.Windows;
using System;
using System.Collections.Generic;
@@ -19,18 +21,28 @@ public class FunctionCommands : ICommands
typeof(HIDModeCommands),
typeof(DesktopLayoutCommands),
typeof(CycleSubProfileCommands),
+ typeof(QuickOverlayCommands),
+ "Power & battery",
+ typeof(TDPIncrease),
+ typeof(TDPDecrease),
"Windows",
typeof(OnScreenKeyboardCommands),
typeof(OnScreenKeyboardLegacyCommands),
- typeof(KillForegroundCommands),
typeof(ActionCenterCommands),
typeof(SettingsCommands),
typeof(ScreenshotCommands),
- "Multimedia",
+ typeof(GameBarCommands),
+ "Multitasking",
+ typeof(KillForegroundCommands),
+ typeof(TaskManagerCommands),
+ typeof(SwapScreenCommands),
+ "Display",
typeof(BrightnessIncrease),
typeof(BrightnessDecrease),
+ "Sound",
typeof(VolumeIncrease),
typeof(VolumeDecrease),
+ typeof(VolumeMute),
];
public FunctionCommands()
@@ -38,9 +50,9 @@ public FunctionCommands()
base.commandType = CommandType.Function;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, IsBackground);
}
}
}
diff --git a/HandheldCompanion/Commands/Functions/HC/CycleSubProfileCommands.cs b/HandheldCompanion/Commands/Functions/HC/CycleSubProfileCommands.cs
index 2eb4d32c4..84cfa8310 100644
--- a/HandheldCompanion/Commands/Functions/HC/CycleSubProfileCommands.cs
+++ b/HandheldCompanion/Commands/Functions/HC/CycleSubProfileCommands.cs
@@ -16,7 +16,7 @@ public CycleSubProfileCommands()
base.OnKeyUp = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
switch (CycleIndex)
{
@@ -28,7 +28,7 @@ public override void Execute(bool IsKeyDown, bool IsKeyUp)
break;
}
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/HC/DesktopLayoutCommands.cs b/HandheldCompanion/Commands/Functions/HC/DesktopLayoutCommands.cs
index 9f805ca24..57bffa663 100644
--- a/HandheldCompanion/Commands/Functions/HC/DesktopLayoutCommands.cs
+++ b/HandheldCompanion/Commands/Functions/HC/DesktopLayoutCommands.cs
@@ -18,22 +18,22 @@ public DesktopLayoutCommands()
SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
case SettingsName:
- base.Execute(OnKeyDown, OnKeyUp);
+ base.Execute(OnKeyDown, OnKeyUp, true);
break;
}
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
bool value = !SettingsManager.GetBoolean(SettingsName, true);
SettingsManager.SetProperty(SettingsName, value, false, true);
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override bool IsToggled => SettingsManager.GetBoolean(SettingsName, true);
diff --git a/HandheldCompanion/Commands/Functions/HC/HIDModeCommands.cs b/HandheldCompanion/Commands/Functions/HC/HIDModeCommands.cs
index 78d5f9749..3628990e9 100644
--- a/HandheldCompanion/Commands/Functions/HC/HIDModeCommands.cs
+++ b/HandheldCompanion/Commands/Functions/HC/HIDModeCommands.cs
@@ -17,6 +17,19 @@ public HIDModeCommands()
base.FontFamily = "PromptFont";
base.Glyph = "\u243C";
+ Update();
+
+ ProfileManager.Applied += ProfileManager_Applied;
+ }
+
+ private void ProfileManager_Applied(Profile profile, UpdateSource source)
+ {
+ IsEnabled = profile.HID == HIDmode.NotSelected;
+ Update();
+ }
+
+ public override void Update()
+ {
HIDmode currentHIDmode = (HIDmode)SettingsManager.GetInt(SettingsName, true);
switch (currentHIDmode)
{
@@ -28,16 +41,10 @@ public HIDModeCommands()
break;
}
- ProfileManager.Applied += ProfileManager_Applied;
- }
-
- private void ProfileManager_Applied(Profile profile, UpdateSource source)
- {
- IsEnabled = profile.HID == HIDmode.NotSelected;
base.Update();
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
if (IsEnabled)
{
@@ -46,19 +53,17 @@ public override void Execute(bool IsKeyDown, bool IsKeyUp)
{
case HIDmode.Xbox360Controller:
SettingsManager.SetProperty(SettingsName, (int)HIDmode.DualShock4Controller);
- LiveGlyph = "\uE000";
break;
case HIDmode.DualShock4Controller:
SettingsManager.SetProperty(SettingsName, (int)HIDmode.Xbox360Controller);
- LiveGlyph = "\uE001";
break;
default:
break;
}
}
- base.Update();
- base.Execute(IsKeyDown, IsKeyUp);
+ Update();
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/HC/MainWindowCommands.cs b/HandheldCompanion/Commands/Functions/HC/MainWindowCommands.cs
index 835b94da8..3c693fd3b 100644
--- a/HandheldCompanion/Commands/Functions/HC/MainWindowCommands.cs
+++ b/HandheldCompanion/Commands/Functions/HC/MainWindowCommands.cs
@@ -18,14 +18,14 @@ public MainWindowCommands()
private void StateChanged(object? sender, EventArgs e)
{
- base.Execute(OnKeyDown, OnKeyUp);
+ base.Execute(OnKeyDown, OnKeyUp, true);
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
MainWindow.GetCurrent().SwapWindowState();
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override bool IsToggled => MainWindow.GetCurrent().WindowState != System.Windows.WindowState.Minimized;
diff --git a/HandheldCompanion/Commands/Functions/HC/OverlayGamepadCommands.cs b/HandheldCompanion/Commands/Functions/HC/OverlayGamepadCommands.cs
index a8b38b353..7a68577eb 100644
--- a/HandheldCompanion/Commands/Functions/HC/OverlayGamepadCommands.cs
+++ b/HandheldCompanion/Commands/Functions/HC/OverlayGamepadCommands.cs
@@ -18,14 +18,14 @@ public OverlayGamepadCommands()
private void IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
- base.Execute(OnKeyDown, OnKeyUp);
+ base.Execute(OnKeyDown, OnKeyUp, true);
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
MainWindow.overlayModel.ToggleVisibility();
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override bool IsToggled => MainWindow.overlayModel.Visibility == System.Windows.Visibility.Visible;
diff --git a/HandheldCompanion/Commands/Functions/HC/OverlayTrackpadCommands.cs b/HandheldCompanion/Commands/Functions/HC/OverlayTrackpadCommands.cs
index 24d56f719..51a78e6d0 100644
--- a/HandheldCompanion/Commands/Functions/HC/OverlayTrackpadCommands.cs
+++ b/HandheldCompanion/Commands/Functions/HC/OverlayTrackpadCommands.cs
@@ -18,14 +18,14 @@ public OverlayTrackpadCommands()
private void IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
- base.Execute(OnKeyDown, OnKeyUp);
+ base.Execute(OnKeyDown, OnKeyUp, true);
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
MainWindow.overlayTrackpad.ToggleVisibility();
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override bool IsToggled => MainWindow.overlayTrackpad.Visibility == System.Windows.Visibility.Visible;
diff --git a/HandheldCompanion/Commands/Functions/HC/QuickOverlayCommands.cs b/HandheldCompanion/Commands/Functions/HC/QuickOverlayCommands.cs
new file mode 100644
index 000000000..f40627514
--- /dev/null
+++ b/HandheldCompanion/Commands/Functions/HC/QuickOverlayCommands.cs
@@ -0,0 +1,78 @@
+using HandheldCompanion.Managers;
+using System;
+
+namespace HandheldCompanion.Commands.Functions.HC
+{
+ [Serializable]
+ public class QuickOverlayCommands : FunctionCommands
+ {
+ private const string SettingsName = "OnScreenDisplayLevel";
+
+ private bool _IsToggled = false;
+ private int prevDisplaylevel = 0;
+
+ public QuickOverlayCommands()
+ {
+ base.Name = Properties.Resources.Hotkey_OnScreenDisplayToggle;
+ base.Description = Properties.Resources.Hotkey_OnScreenDisplayToggleDesc;
+ base.Glyph = "\uE78B";
+ base.OnKeyUp = true;
+
+ prevDisplaylevel = SettingsManager.GetInt(Settings.OnScreenDisplayLevel);
+
+ SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
+ }
+
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
+ {
+ switch (name)
+ {
+ case SettingsName:
+ if (!temporary)
+ prevDisplaylevel = Convert.ToInt16(value);
+ break;
+ }
+ }
+
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
+ {
+ switch (_IsToggled)
+ {
+ case true:
+ SettingsManager.SetProperty(SettingsName, prevDisplaylevel, false, true);
+ break;
+ case false:
+ SettingsManager.SetProperty(SettingsName, 0, false, true);
+ break;
+ }
+
+ // invert toggle
+ _IsToggled = !_IsToggled;
+
+ base.Execute(IsKeyDown, IsKeyUp, false);
+ }
+
+ public override bool IsToggled => !_IsToggled;
+
+ public override object Clone()
+ {
+ QuickOverlayCommands commands = new()
+ {
+ commandType = this.commandType,
+ Name = this.Name,
+ Description = this.Description,
+ Glyph = this.Glyph,
+ OnKeyUp = this.OnKeyUp,
+ OnKeyDown = this.OnKeyDown
+ };
+
+ return commands;
+ }
+
+ public override void Dispose()
+ {
+ SettingsManager.SettingValueChanged -= SettingsManager_SettingValueChanged;
+ base.Dispose();
+ }
+ }
+}
diff --git a/HandheldCompanion/Commands/Functions/HC/QuickToolsCommands.cs b/HandheldCompanion/Commands/Functions/HC/QuickToolsCommands.cs
index 9395c15ac..1e27ece53 100644
--- a/HandheldCompanion/Commands/Functions/HC/QuickToolsCommands.cs
+++ b/HandheldCompanion/Commands/Functions/HC/QuickToolsCommands.cs
@@ -18,14 +18,14 @@ public QuickToolsCommands()
private void IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
- base.Execute(OnKeyDown, OnKeyUp);
+ base.Execute(OnKeyDown, OnKeyUp, true);
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
OverlayQuickTools.GetCurrent().ToggleVisibility();
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override bool IsToggled => OverlayQuickTools.GetCurrent().Visibility == System.Windows.Visibility.Visible;
diff --git a/HandheldCompanion/Commands/Functions/Multimedia/BrightnessDecrease.cs b/HandheldCompanion/Commands/Functions/Multimedia/BrightnessDecrease.cs
index e9b8598ae..29a9ceab9 100644
--- a/HandheldCompanion/Commands/Functions/Multimedia/BrightnessDecrease.cs
+++ b/HandheldCompanion/Commands/Functions/Multimedia/BrightnessDecrease.cs
@@ -14,11 +14,11 @@ public BrightnessDecrease()
OnKeyDown = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
MultimediaManager.DecreaseBrightness();
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/Multimedia/BrightnessIncrease.cs b/HandheldCompanion/Commands/Functions/Multimedia/BrightnessIncrease.cs
index 100112f50..124dd144f 100644
--- a/HandheldCompanion/Commands/Functions/Multimedia/BrightnessIncrease.cs
+++ b/HandheldCompanion/Commands/Functions/Multimedia/BrightnessIncrease.cs
@@ -14,11 +14,11 @@ public BrightnessIncrease()
OnKeyDown = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
MultimediaManager.IncreaseBrightness();
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/Multimedia/VolumeDecrease.cs b/HandheldCompanion/Commands/Functions/Multimedia/VolumeDecrease.cs
index f5468fceb..596431067 100644
--- a/HandheldCompanion/Commands/Functions/Multimedia/VolumeDecrease.cs
+++ b/HandheldCompanion/Commands/Functions/Multimedia/VolumeDecrease.cs
@@ -14,11 +14,11 @@ public VolumeDecrease()
OnKeyDown = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
MultimediaManager.DecreaseVolume();
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/Multimedia/VolumeIncrease.cs b/HandheldCompanion/Commands/Functions/Multimedia/VolumeIncrease.cs
index ddcf94358..b9e1bdbb2 100644
--- a/HandheldCompanion/Commands/Functions/Multimedia/VolumeIncrease.cs
+++ b/HandheldCompanion/Commands/Functions/Multimedia/VolumeIncrease.cs
@@ -14,11 +14,11 @@ public VolumeIncrease()
OnKeyDown = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
MultimediaManager.IncreaseVolume();
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/Multimedia/VolumeMute.cs b/HandheldCompanion/Commands/Functions/Multimedia/VolumeMute.cs
new file mode 100644
index 000000000..e3e7b121b
--- /dev/null
+++ b/HandheldCompanion/Commands/Functions/Multimedia/VolumeMute.cs
@@ -0,0 +1,73 @@
+using HandheldCompanion.Managers;
+using System;
+
+namespace HandheldCompanion.Commands.Functions.Multimedia
+{
+ [Serializable]
+ public class VolumeMute : FunctionCommands
+ {
+ public VolumeMute()
+ {
+ Name = Properties.Resources.Hotkey_muteVolume;
+ Description = Properties.Resources.Hotkey_muteVolumeDesc;
+ Glyph = "\uE74F";
+ OnKeyDown = true;
+
+ Update();
+
+ MultimediaManager.VolumeNotification += MultimediaManager_VolumeNotification;
+ }
+
+ private void MultimediaManager_VolumeNotification(float volume)
+ {
+ Update();
+ }
+
+ public override void Update()
+ {
+ switch (IsToggled)
+ {
+ case true:
+ LiveGlyph = "\uE74F";
+ break;
+ case false:
+ LiveGlyph = "\uE767";
+ break;
+ }
+
+ base.Update();
+ }
+
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
+ {
+ MultimediaManager.ToggleMute();
+
+ Update();
+ base.Execute(IsKeyDown, IsKeyUp, false);
+ }
+
+ public override bool IsToggled => MultimediaManager.IsMuted();
+
+ public override object Clone()
+ {
+ VolumeMute commands = new()
+ {
+ commandType = commandType,
+ Name = Name,
+ Description = Description,
+ Glyph = Glyph,
+ LiveGlyph = LiveGlyph,
+ OnKeyUp = OnKeyUp,
+ OnKeyDown = OnKeyDown
+ };
+
+ return commands;
+ }
+
+ public override void Dispose()
+ {
+ MultimediaManager.VolumeNotification -= MultimediaManager_VolumeNotification;
+ base.Dispose();
+ }
+ }
+}
diff --git a/HandheldCompanion/Commands/Functions/Windows/KillForegroundCommands.cs b/HandheldCompanion/Commands/Functions/Multitasking/KillForegroundCommands.cs
similarity index 77%
rename from HandheldCompanion/Commands/Functions/Windows/KillForegroundCommands.cs
rename to HandheldCompanion/Commands/Functions/Multitasking/KillForegroundCommands.cs
index 757376045..2d0934dd3 100644
--- a/HandheldCompanion/Commands/Functions/Windows/KillForegroundCommands.cs
+++ b/HandheldCompanion/Commands/Functions/Multitasking/KillForegroundCommands.cs
@@ -2,7 +2,7 @@
using HandheldCompanion.Managers;
using System;
-namespace HandheldCompanion.Commands.Functions.Windows
+namespace HandheldCompanion.Commands.Functions.Multitasking
{
[Serializable]
public class KillForegroundCommands : FunctionCommands
@@ -15,19 +15,18 @@ public KillForegroundCommands()
OnKeyUp = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
- // get current foreground process
- ProcessEx fProcess = ProcessManager.GetForegroundProcess();
-
- // kill if is alive
try
{
+ // get current foreground process
+ ProcessEx fProcess = ProcessManager.GetForegroundProcess();
+ // kill if is alive
fProcess?.Process?.Kill();
}
catch { }
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/Multitasking/SwapScreenCommands.cs b/HandheldCompanion/Commands/Functions/Multitasking/SwapScreenCommands.cs
new file mode 100644
index 000000000..c5cc2d03d
--- /dev/null
+++ b/HandheldCompanion/Commands/Functions/Multitasking/SwapScreenCommands.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Linq;
+using System.Windows.Forms;
+using WpfScreenHelper.Enum;
+
+namespace HandheldCompanion.Commands.Functions.Multitasking
+{
+ [Serializable]
+ public class SwapScreenCommands : FunctionCommands
+ {
+ private bool HasTwoScreen => Screen.AllScreens.Length > 1;
+
+ public SwapScreenCommands()
+ {
+ Name = Properties.Resources.Hotkey_SwapScreen;
+ Description = Properties.Resources.Hotkey_SwapScreenDesc;
+ Glyph = "\ue8a7";
+ OnKeyUp = true;
+ }
+
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
+ {
+ if (HasTwoScreen)
+ {
+ // get foreground window
+ IntPtr hWnd = WinAPI.GetforegroundWindow();
+
+ // get the other screen
+ Screen currentScreen = Screen.FromHandle(hWnd);
+ Screen nextScreen = Screen.AllScreens.Where(screen => screen.DeviceName != currentScreen.DeviceName).FirstOrDefault();
+ if (nextScreen is not null)
+ {
+ // move window
+ WinAPI.MoveWindow(hWnd, nextScreen, WindowPositions.Maximize);
+ WinAPI.SetForegroundWindow(hWnd);
+ }
+ }
+
+ base.Execute(IsKeyDown, IsKeyUp, false);
+ }
+
+ public override object Clone()
+ {
+ SwapScreenCommands commands = new()
+ {
+ commandType = commandType,
+ Name = Name,
+ Description = Description,
+ Glyph = Glyph,
+ OnKeyUp = OnKeyUp,
+ OnKeyDown = OnKeyDown
+ };
+
+ return commands;
+ }
+ }
+}
diff --git a/HandheldCompanion/Commands/Functions/Multitasking/TaskManagerCommands.cs b/HandheldCompanion/Commands/Functions/Multitasking/TaskManagerCommands.cs
new file mode 100644
index 000000000..464334579
--- /dev/null
+++ b/HandheldCompanion/Commands/Functions/Multitasking/TaskManagerCommands.cs
@@ -0,0 +1,40 @@
+using GregsStack.InputSimulatorStandard.Native;
+using HandheldCompanion.Simulators;
+using System;
+
+namespace HandheldCompanion.Commands.Functions.Multitasking
+{
+ [Serializable]
+ public class TaskManagerCommands : FunctionCommands
+ {
+ public TaskManagerCommands()
+ {
+ Name = Properties.Resources.Hotkey_TaskManager;
+ Description = Properties.Resources.Hotkey_TaskManagerDesc;
+ Glyph = "\uE9D9";
+ OnKeyUp = true;
+ }
+
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
+ {
+ KeyboardSimulator.KeyPress(new[] { VirtualKeyCode.LCONTROL, VirtualKeyCode.LSHIFT, VirtualKeyCode.ESCAPE });
+
+ base.Execute(IsKeyDown, IsKeyUp, false);
+ }
+
+ public override object Clone()
+ {
+ TaskManagerCommands commands = new()
+ {
+ commandType = commandType,
+ Name = Name,
+ Description = Description,
+ Glyph = Glyph,
+ OnKeyUp = OnKeyUp,
+ OnKeyDown = OnKeyDown
+ };
+
+ return commands;
+ }
+ }
+}
diff --git a/HandheldCompanion/Commands/Functions/Performance/TDPDecrease.cs b/HandheldCompanion/Commands/Functions/Performance/TDPDecrease.cs
new file mode 100644
index 000000000..6fcb96ac1
--- /dev/null
+++ b/HandheldCompanion/Commands/Functions/Performance/TDPDecrease.cs
@@ -0,0 +1,51 @@
+using HandheldCompanion.Managers;
+using HandheldCompanion.Misc;
+using HandheldCompanion.Processors;
+using System;
+
+namespace HandheldCompanion.Commands.Functions.Performance
+{
+ [Serializable]
+ public class TDPDecrease : FunctionCommands
+ {
+ public TDPDecrease()
+ {
+ Name = Properties.Resources.Hotkey_decreaseTDP;
+ Description = Properties.Resources.Hotkey_decreaseTDPDesc;
+ FontFamily = "Segoe UI Symbol";
+ Glyph = "\u2796";
+ OnKeyDown = true;
+ }
+
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
+ {
+ PowerProfile powerProfile = PowerProfileManager.GetCurrent();
+ if (powerProfile.TDPOverrideEnabled && !powerProfile.DeviceDefault)
+ {
+ double TPDMin = PerformanceManager.GetMinimumTDP();
+ for (int idx = (int)PowerType.Slow; idx <= (int)PowerType.Fast; idx++)
+ powerProfile.TDPOverrideValues[idx] = Math.Max(TPDMin, powerProfile.TDPOverrideValues[idx] - 1);
+
+ PowerProfileManager.UpdateOrCreateProfile(powerProfile, UpdateSource.Background);
+ }
+
+ base.Execute(IsKeyDown, IsKeyUp, false);
+ }
+
+ public override object Clone()
+ {
+ TDPDecrease commands = new()
+ {
+ commandType = commandType,
+ Name = Name,
+ Description = Description,
+ FontFamily = FontFamily,
+ Glyph = Glyph,
+ OnKeyUp = OnKeyUp,
+ OnKeyDown = OnKeyDown
+ };
+
+ return commands;
+ }
+ }
+}
diff --git a/HandheldCompanion/Commands/Functions/Performance/TDPIncrease.cs b/HandheldCompanion/Commands/Functions/Performance/TDPIncrease.cs
new file mode 100644
index 000000000..a1a0c5cae
--- /dev/null
+++ b/HandheldCompanion/Commands/Functions/Performance/TDPIncrease.cs
@@ -0,0 +1,51 @@
+using HandheldCompanion.Managers;
+using HandheldCompanion.Misc;
+using HandheldCompanion.Processors;
+using System;
+
+namespace HandheldCompanion.Commands.Functions.Performance
+{
+ [Serializable]
+ public class TDPIncrease : FunctionCommands
+ {
+ public TDPIncrease()
+ {
+ Name = Properties.Resources.Hotkey_increaseTDP;
+ Description = Properties.Resources.Hotkey_increaseTDPDesc;
+ FontFamily = "Segoe UI Symbol";
+ Glyph = "\u2795";
+ OnKeyDown = true;
+ }
+
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
+ {
+ PowerProfile powerProfile = PowerProfileManager.GetCurrent();
+ if (powerProfile.TDPOverrideEnabled && !powerProfile.DeviceDefault)
+ {
+ double TDPMax = PerformanceManager.GetMaximumTDP();
+ for (int idx = (int)PowerType.Slow; idx <= (int)PowerType.Fast; idx++)
+ powerProfile.TDPOverrideValues[idx] = Math.Min(TDPMax, powerProfile.TDPOverrideValues[idx] + 1);
+
+ PowerProfileManager.UpdateOrCreateProfile(powerProfile, UpdateSource.Background);
+ }
+
+ base.Execute(IsKeyDown, IsKeyUp, false);
+ }
+
+ public override object Clone()
+ {
+ TDPIncrease commands = new()
+ {
+ commandType = commandType,
+ Name = Name,
+ Description = Description,
+ FontFamily = FontFamily,
+ Glyph = Glyph,
+ OnKeyUp = OnKeyUp,
+ OnKeyDown = OnKeyDown
+ };
+
+ return commands;
+ }
+ }
+}
diff --git a/HandheldCompanion/Commands/Functions/Windows/ActionCenterCommands.cs b/HandheldCompanion/Commands/Functions/Windows/ActionCenterCommands.cs
index 234af8d4d..d45e74774 100644
--- a/HandheldCompanion/Commands/Functions/Windows/ActionCenterCommands.cs
+++ b/HandheldCompanion/Commands/Functions/Windows/ActionCenterCommands.cs
@@ -15,14 +15,14 @@ public ActionCenterCommands()
OnKeyUp = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
Task.Run(() =>
{
Process.Start(new ProcessStartInfo("ms-actioncenter://") { UseShellExecute = true });
});
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/Windows/GameBarCommands.cs b/HandheldCompanion/Commands/Functions/Windows/GameBarCommands.cs
new file mode 100644
index 000000000..7b477d8ad
--- /dev/null
+++ b/HandheldCompanion/Commands/Functions/Windows/GameBarCommands.cs
@@ -0,0 +1,40 @@
+using GregsStack.InputSimulatorStandard.Native;
+using HandheldCompanion.Simulators;
+using System;
+
+namespace HandheldCompanion.Commands.Functions.Windows
+{
+ [Serializable]
+ public class GameBarCommands : FunctionCommands
+ {
+ public GameBarCommands()
+ {
+ Name = Properties.Resources.Hotkey_GameBar;
+ Description = Properties.Resources.Hotkey_GameBarDesc;
+ Glyph = "\uE713";
+ OnKeyUp = true;
+ }
+
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
+ {
+ KeyboardSimulator.KeyPress(new[] { VirtualKeyCode.LWIN, VirtualKeyCode.VK_G });
+
+ base.Execute(IsKeyDown, IsKeyUp, false);
+ }
+
+ public override object Clone()
+ {
+ GameBarCommands commands = new()
+ {
+ commandType = commandType,
+ Name = Name,
+ Description = Description,
+ Glyph = Glyph,
+ OnKeyUp = OnKeyUp,
+ OnKeyDown = OnKeyDown
+ };
+
+ return commands;
+ }
+ }
+}
diff --git a/HandheldCompanion/Commands/Functions/Windows/OnScreenKeyboardCommands.cs b/HandheldCompanion/Commands/Functions/Windows/OnScreenKeyboardCommands.cs
index 9931a1894..700b6ede0 100644
--- a/HandheldCompanion/Commands/Functions/Windows/OnScreenKeyboardCommands.cs
+++ b/HandheldCompanion/Commands/Functions/Windows/OnScreenKeyboardCommands.cs
@@ -14,11 +14,11 @@ public OnScreenKeyboardCommands()
OnKeyUp = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
OnScreenKeyboard.ToggleVisibility();
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/Windows/OnScreenKeyboardLegacyCommands.cs b/HandheldCompanion/Commands/Functions/Windows/OnScreenKeyboardLegacyCommands.cs
index a6191e651..a838284c0 100644
--- a/HandheldCompanion/Commands/Functions/Windows/OnScreenKeyboardLegacyCommands.cs
+++ b/HandheldCompanion/Commands/Functions/Windows/OnScreenKeyboardLegacyCommands.cs
@@ -25,7 +25,7 @@ public OnScreenKeyboardLegacyCommands()
OnKeyUp = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
Task.Run(async () =>
{
@@ -60,7 +60,7 @@ public override void Execute(bool IsKeyDown, bool IsKeyUp)
}
});
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/Windows/ScreenshotCommands.cs b/HandheldCompanion/Commands/Functions/Windows/ScreenshotCommands.cs
index 187ace054..3fb64680a 100644
--- a/HandheldCompanion/Commands/Functions/Windows/ScreenshotCommands.cs
+++ b/HandheldCompanion/Commands/Functions/Windows/ScreenshotCommands.cs
@@ -15,11 +15,11 @@ public ScreenshotCommands()
OnKeyUp = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
KeyboardSimulator.KeyPress(new[] { VirtualKeyCode.LWIN, VirtualKeyCode.LSHIFT, VirtualKeyCode.VK_S });
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/Functions/Windows/SettingsCommands.cs b/HandheldCompanion/Commands/Functions/Windows/SettingsCommands.cs
index 41965021a..e99714cdf 100644
--- a/HandheldCompanion/Commands/Functions/Windows/SettingsCommands.cs
+++ b/HandheldCompanion/Commands/Functions/Windows/SettingsCommands.cs
@@ -15,14 +15,14 @@ public SettingsCommands()
OnKeyUp = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
Task.Run(() =>
{
Process.Start(new ProcessStartInfo("ms-settings://") { UseShellExecute = true });
});
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Commands/ICommands.cs b/HandheldCompanion/Commands/ICommands.cs
index 39fb5df31..151473afb 100644
--- a/HandheldCompanion/Commands/ICommands.cs
+++ b/HandheldCompanion/Commands/ICommands.cs
@@ -56,7 +56,7 @@ public string LiveGlyph
public ICommands() { }
- public virtual void Execute(bool IsKeyDown, bool IsKeyUp)
+ public virtual void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
Executed?.Invoke(this);
}
diff --git a/HandheldCompanion/Commands/KeyboardCommands.cs b/HandheldCompanion/Commands/KeyboardCommands.cs
index 22f3a1f41..6fd863fbf 100644
--- a/HandheldCompanion/Commands/KeyboardCommands.cs
+++ b/HandheldCompanion/Commands/KeyboardCommands.cs
@@ -22,7 +22,7 @@ public KeyboardCommands()
base.OnKeyDown = true;
}
- public override void Execute(bool IsKeyDown, bool IsKeyUp)
+ public override void Execute(bool IsKeyDown, bool IsKeyUp, bool IsBackground)
{
if (OnKeyDown && OnKeyUp)
{
@@ -59,7 +59,7 @@ public override void Execute(bool IsKeyDown, bool IsKeyUp)
}
}
- base.Execute(IsKeyDown, IsKeyUp);
+ base.Execute(IsKeyDown, IsKeyUp, false);
}
public override object Clone()
diff --git a/HandheldCompanion/Controllers/DInputController.cs b/HandheldCompanion/Controllers/DInputController.cs
index 0c83ce3c7..99cea2bdc 100644
--- a/HandheldCompanion/Controllers/DInputController.cs
+++ b/HandheldCompanion/Controllers/DInputController.cs
@@ -34,10 +34,10 @@ public DInputController(Joystick joystick, PnPDetails details)
public override string ToString()
{
- var baseName = base.ToString();
+ string baseName = base.ToString();
if (!string.IsNullOrEmpty(baseName))
return baseName;
- if (!string.IsNullOrEmpty(joystick.Information.ProductName))
+ if (!string.IsNullOrEmpty(joystick?.Information.ProductName))
return joystick.Information.ProductName;
return $"DInput Controller {UserIndex}";
}
diff --git a/HandheldCompanion/Controllers/GordonController.cs b/HandheldCompanion/Controllers/GordonController.cs
index a22a9d4d3..f17d93264 100644
--- a/HandheldCompanion/Controllers/GordonController.cs
+++ b/HandheldCompanion/Controllers/GordonController.cs
@@ -1,4 +1,5 @@
using HandheldCompanion.Actions;
+using HandheldCompanion.Helpers;
using HandheldCompanion.Inputs;
using HandheldCompanion.Managers;
using HandheldCompanion.Utils;
@@ -217,7 +218,8 @@ public override void UpdateInputs(long ticks, float delta)
Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
// process motion
- gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
+ if (gamepadMotions.TryGetValue(gamepadIndex, out GamepadMotion gamepadMotion))
+ gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
base.UpdateInputs(ticks, delta);
}
diff --git a/HandheldCompanion/Controllers/IController.xaml.cs b/HandheldCompanion/Controllers/IController.xaml.cs
index f4683422c..506500983 100644
--- a/HandheldCompanion/Controllers/IController.xaml.cs
+++ b/HandheldCompanion/Controllers/IController.xaml.cs
@@ -80,7 +80,8 @@ public partial class IController : UserControl
public ButtonState InjectedButtons = new();
public ControllerState Inputs = new();
- protected GamepadMotion gamepadMotion = new(string.Empty, CalibrationMode.Manual);
+ protected byte gamepadIndex = 0;
+ protected Dictionary gamepadMotions = new();
protected double VibrationStrength = 1.0d;
private byte _UserIndex = 255;
@@ -93,7 +94,9 @@ public partial class IController : UserControl
protected object hidLock = new();
public virtual bool IsReady => true;
- public virtual bool IsWireless => false;
+ public virtual bool IsWireless => Details.isBluetooth;
+ public virtual bool IsDongle => Details.isDongle;
+
public bool isPlaceholder;
public bool IsBusy
@@ -216,6 +219,8 @@ public IController()
InitializeComponent();
InitializeInputOutput();
+ gamepadMotions[gamepadIndex] = new(string.Empty, CalibrationMode.Manual);
+
MaxUserIndex = UserIndexPanel.Children.Count;
}
@@ -237,12 +242,12 @@ public virtual void AttachDetails(PnPDetails details)
return;
// manage gamepad motion
- gamepadMotion = new(details.deviceInstanceId, CalibrationMode.Manual | CalibrationMode.SensorFusion);
+ gamepadMotions[gamepadIndex] = new(details.deviceInstanceId, CalibrationMode.Manual | CalibrationMode.SensorFusion);
// UI thread
Application.Current.Dispatcher.Invoke(() =>
{
- ControllerType.Glyph = details.isInternal ? "\uE990" : details.isBluetooth ? "\uE702" : "\uECF0";
+ ControllerType.Glyph = details.isInternal ? "\uE990" : IsWireless ? "\uE702" : IsDongle ? "\uECF1" : "\uECF0";
});
/*
@@ -255,12 +260,7 @@ public virtual void AttachDetails(PnPDetails details)
public virtual void UpdateInputs(long ticks, float delta)
{
- InputsUpdated?.Invoke(Inputs, gamepadMotion, delta);
- }
-
- public virtual void UpdateInputs(long ticks, float delta, GamepadMotion gamepadOverwrite)
- {
- InputsUpdated?.Invoke(Inputs, gamepadOverwrite, delta);
+ InputsUpdated?.Invoke(Inputs, gamepadMotions, delta, gamepadIndex);
}
public bool HasMotionSensor()
@@ -270,7 +270,7 @@ public bool HasMotionSensor()
public GamepadMotion GetMotionSensor()
{
- return gamepadMotion;
+ return gamepadMotions[gamepadIndex];
}
public bool IsPhysical()
@@ -557,9 +557,9 @@ public virtual bool RestoreDrivers()
return true;
}
- public async void Calibrate()
+ public virtual async void Calibrate()
{
- SensorsManager.Calibrate(gamepadMotion);
+ SensorsManager.Calibrate(gamepadMotions);
}
protected virtual void ui_button_calibrate_Click(object sender, RoutedEventArgs e)
@@ -832,13 +832,11 @@ public string GetAxisName(AxisLayoutFlags axis)
}
#region events
-
public event UserIndexChangedEventHandler UserIndexChanged;
public delegate void UserIndexChangedEventHandler(byte UserIndex);
public event InputsUpdatedEventHandler InputsUpdated;
- public delegate void InputsUpdatedEventHandler(ControllerState Inputs, GamepadMotion gamepadMotion, float delta);
-
+ public delegate void InputsUpdatedEventHandler(ControllerState Inputs, Dictionary gamepadMotions, float delta, byte gamepadIndex);
#endregion
}
}
diff --git a/HandheldCompanion/Controllers/JSController.cs b/HandheldCompanion/Controllers/JSController.cs
index ebc21dac3..564190800 100644
--- a/HandheldCompanion/Controllers/JSController.cs
+++ b/HandheldCompanion/Controllers/JSController.cs
@@ -1,4 +1,5 @@
-using HandheldCompanion.Inputs;
+using HandheldCompanion.Helpers;
+using HandheldCompanion.Inputs;
using HandheldCompanion.Utils;
using Nefarius.Utilities.DeviceManagement.PnP;
using System;
@@ -119,7 +120,8 @@ public virtual void UpdateState(float delta)
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);
+ if (gamepadMotions.TryGetValue(gamepadIndex, out GamepadMotion gamepadMotion))
+ gamepadMotion.ProcessMotion(iMU_STATE.gyroX, iMU_STATE.gyroY, iMU_STATE.gyroZ, iMU_STATE.accelX, iMU_STATE.accelY, iMU_STATE.accelZ, delta);
}
}
diff --git a/HandheldCompanion/Controllers/LegionController.cs b/HandheldCompanion/Controllers/LegionController.cs
index ded7a01e6..60f367166 100644
--- a/HandheldCompanion/Controllers/LegionController.cs
+++ b/HandheldCompanion/Controllers/LegionController.cs
@@ -9,8 +9,6 @@
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
@@ -60,8 +58,26 @@ private enum ControllerState
private Thread dataThread;
private bool dataThreadRunning;
-
private byte[] Data = new byte[64];
+
+ #region TouchVariables
+ private bool IsPassthrough = false;
+ private bool touchpadTouched = false;
+ private DateTime touchStartTime;
+ private DateTime lastTapTime = DateTime.MinValue;
+ private Vector2 lastTapPosition;
+ private Vector2 touchStartPosition;
+ private const int doubleTapMaxDistance = 100; // Example threshold distance
+ private const int doubleTapMaxTime = 300; // Maximum time between taps in milliseconds
+ private uint longTapDuration = 500; // Threshold for a long tap in milliseconds
+ private const int longTapMaxMovement = 50; // Maximum movement allowed for a long tap
+ private bool longTapTriggered = false;
+ private bool validTapPosition = false;
+ private bool doubleTapPending = false;
+ private bool doubleTapped = false;
+ private Vector2 lastKnownPosition;
+ #endregion
+
public override bool IsReady
{
get
@@ -81,22 +97,6 @@ public override bool IsWireless
}
}
- 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()
{ }
@@ -107,7 +107,7 @@ public LegionController(PnPDetails details) : base(details)
Capabilities |= ControllerCapabilities.MotionSensor;
// get long press time from system settings
- SystemParametersInfo(0x006A, 0, ref LongPressTime, 0);
+ SystemParametersInfo(0x006A, 0, ref longTapDuration, 0);
SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
UpdateSettings();
@@ -148,7 +148,7 @@ protected override void UpdateSettings()
SetGyroIndex(SettingsManager.GetInt("LegionControllerGyroIndex"));
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
@@ -165,8 +165,8 @@ public override void AttachDetails(PnPDetails details)
{
base.AttachDetails(details);
- // manage gamepad motion
- gamepadMotionR = new($"{details.deviceInstanceId}\\{LegionGo.RightJoyconIndex}", CalibrationMode.Manual | CalibrationMode.SensorFusion);
+ // manage gamepad motion from right controller
+ gamepadMotions[1] = new($"{details.deviceInstanceId}\\{LegionGo.RightJoyconIndex}", CalibrationMode.Manual | CalibrationMode.SensorFusion);
hidDevice = GetHidDevice();
if (hidDevice is not null)
@@ -226,6 +226,8 @@ public override void Plug()
public override void Unplug()
{
+ SetPassthrough(true);
+
// Kill data thread
if (dataThread is not null)
{
@@ -248,6 +250,9 @@ public override void Unplug()
base.Unplug();
}
+ protected float aX = 0.0f, aZ = 0.0f, aY = 0.0f;
+ protected float gX = 0.0f, gZ = 0.0f, gY = 0.0f;
+
public override void UpdateInputs(long ticks, float delta, bool commit)
{
// skip if controller isn't connected
@@ -270,88 +275,60 @@ public override void UpdateInputs(long ticks, float delta, bool commit)
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);
- */
+ {
+ // Right Pad
+ ushort TouchpadX = (ushort)((Data[25] << 8) | Data[26]);
+ ushort TouchpadY = (ushort)((Data[27] << 8) | Data[28]);
+ bool touched = (TouchpadX != 0 || TouchpadY != 0);
- float aX, aZ, aY = 0;
- float gX, gZ, gY = 0;
+ HandleTouchpadInput(touched, TouchpadX, TouchpadY);
+ }
- switch (GyroIndex)
+ for (byte idx = 0; idx <= 1; ++idx)
{
- default:
- case LegionGo.LeftJoyconIndex:
- {
- aX = (short)(Data[34] << 8 | Data[35]) * -(4.0f / short.MaxValue);
- aZ = (short)(Data[36] << 8 | Data[37]) * -(4.0f / short.MaxValue);
- aY = (short)(Data[38] << 8 | Data[39]) * -(4.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;
+ switch (idx)
+ {
+ default:
+ case 0: // LeftJoycon
+ {
+ aX = (short)(Data[34] << 8 | Data[35]) * -(4.0f / short.MaxValue);
+ aZ = (short)(Data[36] << 8 | Data[37]) * -(4.0f / short.MaxValue);
+ aY = (short)(Data[38] << 8 | Data[39]) * -(4.0f / short.MaxValue);
- case LegionGo.RightJoyconIndex:
- {
- aX = (short)(Data[49] << 8 | Data[50]) * -(4.0f / short.MaxValue);
- aZ = (short)(Data[47] << 8 | Data[48]) * (4.0f / short.MaxValue);
- aY = (short)(Data[51] << 8 | Data[52]) * -(4.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;
- 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;
- }
+ case 1: // RightJoycon
+ {
+ aX = (short)(Data[49] << 8 | Data[50]) * -(4.0f / short.MaxValue);
+ aZ = (short)(Data[47] << 8 | Data[48]) * (4.0f / short.MaxValue);
+ aY = (short)(Data[51] << 8 | Data[52]) * -(4.0f / short.MaxValue);
- // store motion
- Inputs.GyroState.SetGyroscope(gX, gY, gZ);
- Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
+ 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;
+ }
- // process motion
- switch (GyroIndex)
- {
- default:
- case LegionGo.LeftJoyconIndex:
+ // compute motion from controller
+ if (gamepadMotions.TryGetValue(idx, out GamepadMotion gamepadMotion))
gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
- base.UpdateInputs(ticks, delta);
- break;
- case LegionGo.RightJoyconIndex:
- gamepadMotionR.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;
+ // store motion from user selected gyro (left, right)
+ if (idx == gamepadIndex)
+ {
+ Inputs.GyroState.SetGyroscope(gX, gY, gZ);
+ Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
+ }
}
+
+ base.UpdateInputs(ticks, delta);
}
private void dataThreadLoop(object? obj)
@@ -382,117 +359,110 @@ public override string GetGlyph(ButtonFlags button)
return base.GetGlyph(button);
}
- public void HandleTouchpadInput(bool touched, ushort x, ushort y)
+ public void HandleTouchpadInput(bool touched, ushort TouchpadX, ushort TouchpadY)
{
// Convert the ushort values to Vector2
- Vector2 position = new Vector2(x, y);
+ Vector2 position = new Vector2(TouchpadX, TouchpadY);
+
+ if (touched)
+ {
+ lastKnownPosition = position;
+ }
+
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = touched;
// 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);
+ Inputs.AxisState[AxisFlags.RightPadX] = (short)InputUtils.MapRange((short)TouchpadX, 0, 1000, short.MinValue, short.MaxValue);
+ Inputs.AxisState[AxisFlags.RightPadY] = (short)InputUtils.MapRange((short)-TouchpadY, 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)
+ touchStartTime = DateTime.Now;
+ touchStartPosition = position;
+ longTapTriggered = false;
+ validTapPosition = true;
+
+ // Trigger double tap if pending
+ if (doubleTapPending && (DateTime.Now - lastTapTime).TotalMilliseconds <= doubleTapMaxTime &&
+ Vector2.Distance(lastTapPosition, lastKnownPosition) <= doubleTapMaxDistance)
{
- Inputs.ButtonState[ButtonFlags.RightPadClick] = true;
- touchpadDoubleTapped = true;
+ HandleDoubleTap(lastKnownPosition);
+ doubleTapPending = false;
+ doubleTapped = true;
+ lastTapTime = DateTime.MinValue; // Reset lastTapTime after double tap
}
}
- // If the touchpad was touched before
- else
+ else if (!longTapTriggered && !doubleTapped && (DateTime.Now - touchStartTime).TotalMilliseconds >= longTapDuration)
{
- // Update the touchpad position
- touchpadPosition = position;
-
- // If the touchpad has been double tapped
- if (touchpadDoubleTapped)
+ // Check if the touch moved too much for a long tap
+ if (Vector2.Distance(touchStartPosition, position) <= longTapMaxMovement)
{
- // 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;
- }
- }
+ // Trigger long tap while the touch is held
+ HandleLongTap(position);
+ longTapTriggered = true;
}
}
+ else if (Vector2.Distance(touchStartPosition, position) > longTapMaxMovement)
+ {
+ validTapPosition = false;
+ }
}
// If the touchpad is not touched
else
{
Inputs.AxisState[AxisFlags.RightPadX] = 0;
Inputs.AxisState[AxisFlags.RightPadY] = 0;
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = false;
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);
+ doubleTapped = false;
+ DateTime touchEndTime = DateTime.Now;
+ double touchDuration = (touchEndTime - touchStartTime).TotalMilliseconds;
- // 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)
+ // Handle short tap
+ if (touchDuration < longTapDuration && !longTapTriggered && validTapPosition &&
+ Vector2.Distance(touchStartPosition, lastKnownPosition) <= doubleTapMaxDistance)
{
- // 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;
+ // Single short tap detected
+ HandleShortTap(lastKnownPosition);
+ lastTapTime = touchEndTime;
+ lastTapPosition = lastKnownPosition;
+ doubleTapPending = true;
}
-
- // Set the touchpad long tapped flag to false
- touchpadLongTapped = false;
-
- // Set the touchpad double tapped flag to false
- touchpadDoubleTapped = false;
}
}
}
+ private void HandleShortTap(Vector2 position)
+ {
+ // Handle short tap action here
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = true;
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = true;
+ }
+
+ private void HandleDoubleTap(Vector2 position)
+ {
+ // Handle double tap action here
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = true;
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = true;
+ }
+
+ private void HandleLongTap(Vector2 position)
+ {
+ // Handle long tap action here
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = true;
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = true;
+ Inputs.ButtonState[ButtonFlags.RightPadClickDown] = true;
+ }
+
public void SetPassthrough(bool enabled)
{
SetTouchPadStatus(enabled ? 1 : 0);
@@ -501,7 +471,7 @@ public void SetPassthrough(bool enabled)
public void SetGyroIndex(int idx)
{
- GyroIndex = idx + LegionGo.LeftJoyconIndex;
+ gamepadIndex = (byte)idx;
}
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/NeptuneController.cs b/HandheldCompanion/Controllers/NeptuneController.cs
index c58672cf9..32fca6f9a 100644
--- a/HandheldCompanion/Controllers/NeptuneController.cs
+++ b/HandheldCompanion/Controllers/NeptuneController.cs
@@ -1,4 +1,5 @@
using HandheldCompanion.Actions;
+using HandheldCompanion.Helpers;
using HandheldCompanion.Inputs;
using HandheldCompanion.Managers;
using SharpDX.XInput;
@@ -245,7 +246,8 @@ public override void UpdateInputs(long ticks, float delta)
Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
// process motion
- gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
+ if (gamepadMotions.TryGetValue(gamepadIndex, out GamepadMotion gamepadMotion))
+ gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
base.UpdateInputs(ticks, delta);
}
diff --git a/HandheldCompanion/Controllers/TatantulaProController.cs b/HandheldCompanion/Controllers/TatantulaProController.cs
new file mode 100644
index 000000000..5fb05e451
--- /dev/null
+++ b/HandheldCompanion/Controllers/TatantulaProController.cs
@@ -0,0 +1,326 @@
+using HandheldCompanion.Devices;
+using HandheldCompanion.Helpers;
+using HandheldCompanion.Inputs;
+using HidLibrary;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Threading;
+
+namespace HandheldCompanion.Controllers
+{
+ public class TatantulaProController : XInputController
+ {
+ private HidDevice hidDevice;
+
+ private Thread dataThread;
+ private bool dataThreadRunning;
+ private byte[] Data = new byte[64];
+
+ protected float aX = 0.0f, aZ = 0.0f, aY = 0.0f;
+ protected float gX = 0.0f, gZ = 0.0f, gY = 0.0f;
+ private const byte EXTRABUTTON0_IDX = 11;
+ private const byte EXTRABUTTON1_IDX = 12;
+ private const byte EXTRABUTTON2_IDX = 13;
+
+ [Flags]
+ private enum Button0Enum
+ {
+ None = 0,
+ M = 4
+ }
+
+ [Flags]
+ private enum Button1Enum
+ {
+ None = 0,
+ M1 = 1,
+ M2 = 2,
+ T1 = 4,
+ T2 = 8,
+ T3 = 16,
+ C1 = 32,
+ C2 = 64,
+ C3 = 128
+ }
+
+ [Flags]
+ private enum Button2Enum
+ {
+ None = 0,
+ C4 = 1
+ }
+
+ [Flags]
+ private enum ButtonLayout
+ {
+ Xbox = 64,
+ Nintendo = 128,
+ }
+
+ public TatantulaProController() : base()
+ { }
+
+ public TatantulaProController(PnPDetails details) : base(details)
+ {
+ // Capabilities
+ Capabilities |= ControllerCapabilities.MotionSensor;
+ }
+
+ public override string ToString()
+ {
+ return $"GameSir Tarantula Pro PC Controller";
+ }
+
+ protected override void InitializeInputOutput()
+ {
+ SourceButtons.Add(ButtonFlags.R4);
+ SourceButtons.Add(ButtonFlags.L4);
+ SourceButtons.Add(ButtonFlags.L5);
+
+ SourceButtons.Add(ButtonFlags.B5);
+ SourceButtons.Add(ButtonFlags.B6);
+ SourceButtons.Add(ButtonFlags.B7);
+ SourceButtons.Add(ButtonFlags.B8);
+ SourceButtons.Add(ButtonFlags.B9);
+ SourceButtons.Add(ButtonFlags.B10);
+ SourceButtons.Add(ButtonFlags.B11);
+
+ SourceAxis.Add(AxisLayoutFlags.Gyroscope);
+ }
+
+ 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;
+ }
+
+ // (test: 0x01, normal: 0x02)
+ private byte[] ControllerMode = new byte[33] { 0x7, 0x04, 0x0a, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+
+ // brigthness mode
+ private byte[] LEDMode = new byte[33] { 0x7, 0x06, 0x07, 0x01, 0x64, 0x32, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+
+ // H V S
+ private byte[] LEDColor = new byte[33] { 0x7, 0x10, 0x07, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+
+ // button action button
+ private byte[] ButtonMode = new byte[33] { 0x7, 0x13, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+
+ // T3 C1 C2 T1 T2 C3 C4 M1 M2
+ private byte[] ExtraButtons = new byte[] { 0x28, 0x29, 0x2a, 0x26, 0x27, 0x2b, 0x2c, 0x24, 0x25 };
+
+ // (nintendo: 0x02, xbox: 0x01)
+ private byte[] ControllerLayout = new byte[] { 0x07, 0x07, 0x09, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
+
+ private ButtonLayout GetLayout()
+ {
+ ButtonLayout layout = (ButtonLayout)Data[EXTRABUTTON2_IDX];
+ return layout.HasFlag(ButtonLayout.Xbox) ? ButtonLayout.Xbox : ButtonLayout.Nintendo;
+ }
+
+ private void SetVerboseMode()
+ {
+ // unlock raw hid report
+ ControllerMode[4] = 0x02;
+ hidDevice.Write(ControllerMode);
+ }
+
+ private void SetTestMode()
+ {
+ // unlock raw hid report
+ ControllerMode[4] = 0x01;
+ hidDevice.Write(ControllerMode);
+ }
+
+ public override void Plug()
+ {
+ hidDevice = GetHidDevice();
+ if (hidDevice is not null && hidDevice.IsConnected)
+ {
+ if (!hidDevice.IsOpen)
+ hidDevice.OpenDevice();
+
+ // unlock raw hid report
+ SetTestMode();
+
+ foreach (byte button in ExtraButtons)
+ {
+ ButtonMode[10] = button;
+ hidDevice.Write(ButtonMode);
+ }
+
+ /*
+ ControllerLayout[6] = 0x02; // Nintendo
+ hidDevice.Write(ControllerLayout);
+
+ ButtonLayout layout = GetLayout();
+
+ ControllerLayout[6] = 0x01; // XBOX
+ hidDevice.Write(ControllerLayout);
+
+ layout = GetLayout();
+ */
+
+ /*
+ // LED ON
+ LEDMode[6] = 1;
+ hidDevice.Write(LEDMode);
+ */
+
+ // start data thread
+ if (dataThread is null)
+ {
+ dataThreadRunning = true;
+ dataThread = new Thread(dataThreadLoop)
+ {
+ IsBackground = true,
+ Priority = ThreadPriority.Highest
+ };
+ dataThread.Start();
+ }
+ }
+
+ base.Plug();
+ }
+
+ public override void Unplug()
+ {
+ // Kill data thread
+ if (dataThread is not null)
+ {
+ dataThreadRunning = false;
+ // Ensure the thread has finished execution
+ if (dataThread.IsAlive)
+ dataThread.Join();
+ dataThread = null;
+ }
+
+ if (hidDevice is not null)
+ {
+ if (hidDevice.IsConnected && hidDevice.IsOpen)
+ hidDevice.CloseDevice();
+
+ hidDevice.Dispose();
+ hidDevice = null;
+ }
+
+ base.Unplug();
+ }
+
+ public void ColorToHSV(Color color, out double hue, out double saturation, out double value)
+ {
+ // Convert RGB values to a scale of 0 to 1
+ float rScaled = color.R / 255f;
+ float gScaled = color.G / 255f;
+ float bScaled = color.B / 255f;
+
+ // Find the maximum and minimum values of R, G and B
+ float max = Math.Max(rScaled, Math.Max(gScaled, bScaled));
+ float min = Math.Min(rScaled, Math.Min(gScaled, bScaled));
+ float delta = max - min;
+
+ // Calculate V (value/brightness) - scaled to 100%
+ value = max * 100;
+
+ // Calculate S (saturation) - scaled to 100%
+ saturation = (max == 0) ? 0 : (delta / max) * 100;
+
+ // Calculate H (hue)
+ hue = color.GetHue();
+ hue = hue / 360.0f * 255f;
+ }
+
+ public override void SetLightColor(byte R, byte G, byte B)
+ {
+ if (hidDevice is null || !hidDevice.IsConnected || !hidDevice.IsOpen)
+ return;
+
+ Color color = Color.FromArgb(R, G, B);
+ ColorToHSV(color, out double hue, out double saturation, out double value);
+
+ LEDColor[5] = (byte)hue;
+ LEDColor[6] = (byte)saturation;
+ LEDColor[7] = (byte)value;
+
+ hidDevice.Write(LEDColor);
+ }
+
+ public override void UpdateInputs(long ticks, float delta, bool commit)
+ {
+ // skip if controller isn't connected
+ if (!IsConnected())
+ return;
+
+ base.UpdateInputs(ticks, delta, false);
+
+ Button0Enum extraButtons0 = (Button0Enum)Data[EXTRABUTTON0_IDX];
+ Inputs.ButtonState[ButtonFlags.L5] = extraButtons0.HasFlag(Button0Enum.M);
+
+ Button1Enum extraButtons1 = (Button1Enum)Data[EXTRABUTTON1_IDX];
+ Inputs.ButtonState[ButtonFlags.L4] = extraButtons1.HasFlag(Button1Enum.M1);
+ Inputs.ButtonState[ButtonFlags.R4] = extraButtons1.HasFlag(Button1Enum.M2);
+
+ Button2Enum extraButtons2 = (Button2Enum)Data[EXTRABUTTON2_IDX];
+ Inputs.ButtonState[ButtonFlags.B5] = extraButtons1.HasFlag(Button1Enum.C1);
+ Inputs.ButtonState[ButtonFlags.B6] = extraButtons1.HasFlag(Button1Enum.C2);
+ Inputs.ButtonState[ButtonFlags.B7] = extraButtons1.HasFlag(Button1Enum.C3);
+ Inputs.ButtonState[ButtonFlags.B8] = extraButtons2.HasFlag(Button2Enum.C4);
+
+ Inputs.ButtonState[ButtonFlags.B9] = extraButtons1.HasFlag(Button1Enum.T1);
+ Inputs.ButtonState[ButtonFlags.B10] = extraButtons1.HasFlag(Button1Enum.T2);
+ Inputs.ButtonState[ButtonFlags.B11] = extraButtons1.HasFlag(Button1Enum.T3);
+
+ aX = (short)(Data[14] << 8 | Data[15]) * (4.0f / short.MaxValue);
+ aZ = (short)(Data[16] << 8 | Data[17]) * -(4.0f / short.MaxValue);
+ aY = (short)(Data[18] << 8 | Data[19]) * (4.0f / short.MaxValue);
+
+ gX = (short)(Data[20] << 8 | Data[21]) * (2000.0f / short.MaxValue);
+ gZ = (short)(Data[22] << 8 | Data[23]) * -(2000.0f / short.MaxValue);
+ gY = (short)(Data[24] << 8 | Data[25]) * (2000.0f / short.MaxValue);
+
+ // compute motion from controller
+ if (gamepadMotions.TryGetValue(gamepadIndex, out GamepadMotion gamepadMotion))
+ gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
+
+ Inputs.GyroState.SetGyroscope(gX, gY, gZ);
+ Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
+
+ base.UpdateInputs(ticks, delta);
+ }
+
+ private void dataThreadLoop(object? obj)
+ {
+ // pull latest Data
+ while (dataThreadRunning)
+ {
+ HidDeviceData report = hidDevice?.ReadData(0);
+ if (report is not null)
+ Buffer.BlockCopy(report.Data, 1, Data, 0, report.Data.Length - 1);
+ }
+ }
+
+ public override string GetGlyph(ButtonFlags button)
+ {
+ return base.GetGlyph(button);
+ }
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/DSU/ClientRequestTimes.cs b/HandheldCompanion/DSU/ClientRequestTimes.cs
new file mode 100644
index 000000000..192a440d6
--- /dev/null
+++ b/HandheldCompanion/DSU/ClientRequestTimes.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Net.NetworkInformation;
+
+namespace HandheldCompanion.DSU
+{
+ public class ClientRequestTimes
+ {
+ DateTime allPads;
+ DateTime[] padIds;
+ Dictionary padMacs;
+
+ public DateTime AllPadsTime { get { return allPads; } }
+ public DateTime[] PadIdsTime { get { return padIds; } }
+ public Dictionary PadMacsTime { get { return padMacs; } }
+
+ public ClientRequestTimes()
+ {
+ allPads = DateTime.MinValue;
+ padIds = new DateTime[4];
+
+ for (int i = 0; i < padIds.Length; i++)
+ padIds[i] = DateTime.MinValue;
+
+ padMacs = [];
+ }
+
+ public void RequestPadInfo(byte regFlags, byte idToReg, PhysicalAddress macToReg)
+ {
+ if (regFlags == 0)
+ allPads = DateTime.UtcNow;
+ else
+ {
+ if ((regFlags & 0x01) != 0) //id valid
+ {
+ if (idToReg < padIds.Length)
+ padIds[idToReg] = DateTime.UtcNow;
+ }
+ if ((regFlags & 0x02) != 0) //mac valid
+ {
+ padMacs[macToReg] = DateTime.UtcNow;
+ }
+ }
+ }
+ }
+}
diff --git a/HandheldCompanion/DSU/DSUServer.cs b/HandheldCompanion/DSU/DSUServer.cs
index 597b2435e..4b40713b2 100644
--- a/HandheldCompanion/DSU/DSUServer.cs
+++ b/HandheldCompanion/DSU/DSUServer.cs
@@ -1,5 +1,7 @@
using Force.Crc32;
using HandheldCompanion.Controllers;
+using HandheldCompanion.DSU;
+using HandheldCompanion.Helpers;
using HandheldCompanion.Inputs;
using HandheldCompanion.Managers;
using HandheldCompanion.Utils;
@@ -9,8 +11,10 @@
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
+using System.Numerics;
using System.Threading;
using System.Windows.Forms;
+using static HandheldCompanion.Inputs.GyroState;
namespace HandheldCompanion;
@@ -48,56 +52,37 @@ public enum DsBattery : byte
Charged = 0xEF
}
-public struct DualShockPadMeta
+public static class DSUServer
{
- public byte PadId;
- public DsState PadState;
- public DsConnection ConnectionType;
- public DsModel Model;
- public PhysicalAddress PadMacAddress;
- public DsBattery BatteryStatus;
- public bool IsActive;
-}
-
-public class DSUServer
-{
- public delegate void GetPadDetail(int padIdx, ref DualShockPadMeta meta);
-
- public delegate void StartedEventHandler(DSUServer server);
-
- public delegate void StoppedEventHandler(DSUServer server);
-
- public const int NUMBER_SLOTS = 4;
+ private const byte NUMBER_SLOTS = 4;
private const int ARG_BUFFER_LEN = 80;
-
private const ushort MaxProtocolVersion = 1001;
- private SemaphoreSlim _pool;
- //private SocketAsyncEventArgs[] argsList;
- private byte[][] dataBuffers;
-
- private readonly Dictionary clients = [];
-
- private ControllerState Inputs = new();
- private int listInd;
- private readonly PhysicalAddress PadMacAddress;
-
- public DualShockPadMeta padMeta;
- private readonly ReaderWriterLockSlim poolLock = new();
-
- public int port = 26760;
-
- private readonly GetPadDetail portInfoGet;
- private readonly byte[] recvBuffer = new byte[1024];
- public bool running;
- private uint serverId;
- private int udpPacketCount;
- private Socket udpSock;
-
- public DSUServer()
- {
- PadMacAddress = new PhysicalAddress(new byte[] { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 });
- portInfoGet = GetPadDetailForIdx;
+ private static SemaphoreSlim _pool;
+ private static byte[][] dataBuffers;
+
+ private static readonly Dictionary clients = [];
+ private static readonly DualShockPadMeta[] padMetas = new DualShockPadMeta[NUMBER_SLOTS];
+ private static readonly ReaderWriterLockSlim poolLock = new();
+ private static readonly byte[] recvBuffer = new byte[1024];
+
+ private const int serverPort = 26760;
+ private static uint serverId;
+ private static int udpPacketCount;
+ private static Socket udpSock;
+
+ public static bool IsInitialized;
+
+ #region events
+ public delegate void StartedEventHandler();
+ public static event StartedEventHandler Started;
+
+ public delegate void StoppedEventHandler();
+ public static event StoppedEventHandler Stopped;
+ #endregion
+
+ static DSUServer()
+ {
_pool = new SemaphoreSlim(ARG_BUFFER_LEN);
dataBuffers = new byte[ARG_BUFFER_LEN][];
for (int num = 0; num < ARG_BUFFER_LEN; num++)
@@ -107,45 +92,41 @@ public DSUServer()
dataBuffers[num] = new byte[100];
}
- padMeta = new DualShockPadMeta()
- {
- BatteryStatus = DsBattery.Full,
- ConnectionType = DsConnection.Usb,
- IsActive = true,
- PadId = 0,
- PadMacAddress = PadMacAddress,
- Model = DsModel.DS4,
- PadState = DsState.Connected
- };
- }
-
- private void GetPadDetailForIdx(int padIdx, ref DualShockPadMeta meta)
- {
- meta = padMeta;
+ for (byte padIdx = 0; padIdx < NUMBER_SLOTS; padIdx++)
+ {
+ byte address = (byte)(0x10 + padIdx);
+
+ padMetas[padIdx] = new DualShockPadMeta()
+ {
+ BatteryStatus = DsBattery.Full,
+ ConnectionType = DsConnection.Usb,
+ IsActive = true,
+ PadId = padIdx,
+ PadMacAddress = new PhysicalAddress([address, address, address, address, address, address]),
+ Model = DsModel.DS4,
+ PadState = DsState.Connected
+ };
+ }
}
- public event StartedEventHandler Started;
-
- public event StoppedEventHandler Stopped;
-
- public override string ToString()
+ private static void GetPadDetailForIdx(int padIdx, ref DualShockPadMeta meta)
{
- return GetType().Name;
+ meta = padMetas[padIdx];
}
- private void SocketEvent_AsyncCompleted(object sender, SocketAsyncEventArgs e)
+ private static void SocketEvent_AsyncCompleted(object sender, SocketAsyncEventArgs e)
{
_pool.Release();
e.Dispose();
}
- private void CompletedSynchronousSocketEvent(SocketAsyncEventArgs args)
+ private static void CompletedSynchronousSocketEvent(SocketAsyncEventArgs args)
{
_pool.Release();
args.Dispose();
}
- private int BeginPacket(byte[] packetBuf, ushort reqProtocolVersion = MaxProtocolVersion)
+ private static int BeginPacket(byte[] packetBuf, ushort reqProtocolVersion = MaxProtocolVersion)
{
var currIdx = 0;
packetBuf[currIdx++] = (byte)'D';
@@ -168,7 +149,7 @@ private int BeginPacket(byte[] packetBuf, ushort reqProtocolVersion = MaxProtoco
return currIdx;
}
- private void FinishPacket(byte[] packetBuf)
+ private static void FinishPacket(byte[] packetBuf)
{
Array.Clear(packetBuf, 8, 4);
@@ -176,7 +157,8 @@ private void FinishPacket(byte[] packetBuf)
Array.Copy(BitConverter.GetBytes(crcCalc), 0, packetBuf, 8, 4);
}
- private void SendPacket(IPEndPoint clientEP, byte[] usefulData, ushort reqProtocolVersion = MaxProtocolVersion)
+ private static int listInd;
+ private static void SendPacket(IPEndPoint clientEP, byte[] usefulData, ushort reqProtocolVersion = MaxProtocolVersion)
{
byte[] packetData = new byte[usefulData.Length + 16];
int currIdx = BeginPacket(packetData, reqProtocolVersion);
@@ -212,7 +194,7 @@ private void SendPacket(IPEndPoint clientEP, byte[] usefulData, ushort reqProtoc
}
}
- private void ProcessIncoming(byte[] localMsg, IPEndPoint clientEP)
+ private static void ProcessIncoming(byte[] localMsg, IPEndPoint clientEP)
{
try
{
@@ -293,7 +275,7 @@ private void ProcessIncoming(byte[] localMsg, IPEndPoint clientEP)
{
var currRequest = localMsg[requestsIdx + i];
var padData = new DualShockPadMeta();
- portInfoGet(currRequest, ref padData);
+ GetPadDetailForIdx(currRequest, ref padData);
var outIdx = 0;
Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PortInfo), 0, outputData, outIdx, 4);
@@ -359,7 +341,7 @@ private void ProcessIncoming(byte[] localMsg, IPEndPoint clientEP)
}
}
- private void ReceiveCallback(IAsyncResult iar)
+ private static void ReceiveCallback(IAsyncResult iar)
{
byte[] localMsg = null;
EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0);
@@ -375,7 +357,7 @@ private void ReceiveCallback(IAsyncResult iar)
}
catch (SocketException)
{
- if (running)
+ if (IsInitialized)
{
ResetUDPConn();
}
@@ -390,11 +372,11 @@ private void ReceiveCallback(IAsyncResult iar)
ProcessIncoming(localMsg, (IPEndPoint)clientEP);
}
- private void StartReceive()
+ private static void StartReceive()
{
try
{
- if (running)
+ if (IsInitialized)
{
//Start listening for a new message.
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
@@ -403,7 +385,7 @@ private void StartReceive()
}
catch (SocketException /*ex*/)
{
- if (running)
+ if (IsInitialized)
{
ResetUDPConn();
StartReceive();
@@ -417,7 +399,7 @@ private void StartReceive()
/// Frees Socket from potentially firing SocketException instances after a client
/// connection is terminated. Avoids memory leak
///
- private void ResetUDPConn()
+ private static void ResetUDPConn()
{
if (udpSock is null)
return;
@@ -433,18 +415,18 @@ private void ResetUDPConn()
catch (ObjectDisposedException) { }
}
- public bool Start()
+ public static bool Start()
{
try
{
udpSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
- udpSock.Bind(new IPEndPoint(IPAddress.Any, port));
+ udpSock.Bind(new IPEndPoint(IPAddress.Any, serverPort));
}
catch (SocketException)
{
- LogManager.LogCritical("{0} couldn't listen to port: {1}", ToString(), port);
+ LogManager.LogCritical("DSUServer couldn't listen to port: {0}", serverPort);
Stop();
- return running;
+ return IsInitialized;
}
catch (Exception /*ex*/) { }
@@ -454,17 +436,17 @@ public bool Start()
TimerManager.Tick += Tick;
- running = true;
+ IsInitialized = true;
StartReceive();
- LogManager.LogInformation("{0} has started. Listening to port: {1}", ToString(), port);
- Started?.Invoke(this);
+ LogManager.LogInformation("DSUServer has started. Listening to port: {0}", serverPort);
+ Started?.Invoke();
- return running;
+ return IsInitialized;
}
- public void Stop()
+ public static void Stop()
{
if (udpSock is not null)
{
@@ -476,20 +458,24 @@ public void Stop()
udpSock = null;
}
- running = false;
+ IsInitialized = false;
TimerManager.Tick -= Tick;
- LogManager.LogInformation("{0} has stopped", ToString());
- Stopped?.Invoke(this);
- }
+ LogManager.LogInformation("DSUServer has stopped");
+ Stopped?.Invoke();
+ }
+
+ private static ControllerState Inputs = new();
+ private static Dictionary GamepadMotions = new();
- public void UpdateInputs(ControllerState inputs)
+ public static void UpdateInputs(ControllerState inputs, Dictionary gamepadMotions)
{
Inputs = inputs;
- }
+ GamepadMotions = gamepadMotions;
+ }
- private bool ReportToBuffer(byte[] outputData, ref int outIdx)
+ private static bool ReportToBuffer(byte[] outputData, ref int outIdx, byte padIdx)
{
unchecked
{
@@ -518,11 +504,8 @@ private bool ReportToBuffer(byte[] outputData, ref int outIdx)
if (Inputs.AxisState[AxisFlags.L2] == byte.MaxValue) outputData[outIdx] |= 0x01;
outputData[++outIdx] =
- Convert.ToByte(Inputs.ButtonState[ButtonFlags.Special]); // (hidReport.PS) ? (byte)1 :
- outputData[++outIdx] = Convert.ToByte(Inputs.ButtonState[ButtonFlags.LeftPadClick] ||
- Inputs.ButtonState[
- ButtonFlags
- .RightPadClick]); // (hidReport.TouchButton) ? (byte)1 :
+ Convert.ToByte(Inputs.ButtonState[ButtonFlags.Special]); // (hidReport.PS) ? (byte)1 : (byte)0
+ outputData[++outIdx] = Convert.ToByte(Inputs.ButtonState[ButtonFlags.LeftPadClick] || Inputs.ButtonState[ButtonFlags.RightPadClick]); // (hidReport.TouchButton) ? (byte)1 : (byte)0
//Left stick
outputData[++outIdx] = InputUtils.NormalizeXboxInput(Inputs.AxisState[AxisFlags.LeftStickX]);
@@ -567,235 +550,225 @@ 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[GyroState.SensorState.DSU].X), 0, outputData, outIdx, 4);
- outIdx += 4;
- // accelYG
- 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[GyroState.SensorState.DSU].Z), 0, outputData, outIdx, 4);
- outIdx += 4;
-
- // Gyroscope
- // angVelPitch
- 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[GyroState.SensorState.DSU].Y), 0, outputData, outIdx, 4);
- outIdx += 4;
- // angVelRoll
- Array.Copy(BitConverter.GetBytes(-Inputs.GyroState.Gyroscope[GyroState.SensorState.DSU].Z), 0, outputData, outIdx, 4);
+ outIdx += 8;
+
+ float gyroX = 0.0f, gyroY = 0.0f, gyroZ = 0.0f;
+ float accelX = 0.0f, accelY = 0.0f, accelZ = 0.0f;
+ switch (padIdx)
+ {
+ default:
+ {
+ if (Inputs.GyroState.Gyroscope.TryGetValue(SensorState.DSU, out Vector3 gyrometer))
+ {
+ gyroX = gyrometer.X;
+ gyroY = gyrometer.Y;
+ gyroZ = gyrometer.Z;
+ }
+
+ if (Inputs.GyroState.Accelerometer.TryGetValue(SensorState.DSU, out Vector3 accelerometer))
+ {
+ accelX = accelerometer.X;
+ accelY = accelerometer.Y;
+ accelZ = accelerometer.Z;
+ }
+ }
+ break;
+
+ case 1:
+ case 2:
+ byte gamepadMotionIdx = (byte)(padIdx - 1);
+ if (GamepadMotions.TryGetValue(gamepadMotionIdx, out GamepadMotion gamepadMotion))
+ {
+ gamepadMotion.GetRawGyro(out gyroX, out gyroY, out gyroZ);
+ gamepadMotion.GetRawAcceleration(out accelX, out accelY, out accelZ);
+ }
+ break;
+
+ case 3:
+ // do nothing
+ return true;
+ }
+
+ accelX = accelX * -1.0f;
+ accelY = accelY * -1.0f;
+ accelZ = accelZ * -1.0f;
+
+ // Accelerometer
+ Array.Copy(BitConverter.GetBytes(accelX), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ Array.Copy(BitConverter.GetBytes(accelY), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ Array.Copy(BitConverter.GetBytes(accelZ), 0, outputData, outIdx, 4);
+ outIdx += 4;
+
+ // Gyroscope
+ Array.Copy(BitConverter.GetBytes(gyroX), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ Array.Copy(BitConverter.GetBytes(-gyroY), 0, outputData, outIdx, 4);
+ outIdx += 4;
+ Array.Copy(BitConverter.GetBytes(-gyroZ), 0, outputData, outIdx, 4);
outIdx += 4;
}
return true;
}
- public void Tick(long ticks, float delta)
+ public static void Tick(long ticks, float delta)
{
- if (!running)
- return;
-
- // only update every one second
- if (ticks % 1000 == 0)
- {
- var ChargeStatus = SystemInformation.PowerStatus.BatteryChargeStatus;
-
- if (ChargeStatus.HasFlag(BatteryChargeStatus.Charging))
- padMeta.BatteryStatus = DsBattery.Charging;
- else if (ChargeStatus.HasFlag(BatteryChargeStatus.NoSystemBattery))
- padMeta.BatteryStatus = DsBattery.None;
- else if (ChargeStatus.HasFlag(BatteryChargeStatus.High))
- padMeta.BatteryStatus = DsBattery.High;
- else if (ChargeStatus.HasFlag(BatteryChargeStatus.Low))
- padMeta.BatteryStatus = DsBattery.Low;
- else if (ChargeStatus.HasFlag(BatteryChargeStatus.Critical))
- padMeta.BatteryStatus = DsBattery.Dying;
- else
- padMeta.BatteryStatus = DsBattery.Medium;
- }
-
- // update status
- padMeta.IsActive = true; // fixme ?
-
- var clientsList = new List();
- var now = DateTime.UtcNow;
- lock (clients)
- {
- var clientsToDelete = new List();
-
- foreach (var cl in clients)
- {
- const double TimeoutLimit = 5;
-
- if ((now - cl.Value.AllPadsTime).TotalSeconds < TimeoutLimit)
- {
- clientsList.Add(cl.Key);
- }
- else if (padMeta.PadId < cl.Value.PadIdsTime.Length &&
- (now - cl.Value.PadIdsTime[padMeta.PadId]).TotalSeconds < TimeoutLimit)
- {
- clientsList.Add(cl.Key);
- }
- else if (cl.Value.PadMacsTime.TryGetValue(padMeta.PadMacAddress, out var padTime) &&
- (now - padTime).TotalSeconds < TimeoutLimit)
- {
- clientsList.Add(cl.Key);
- }
- else //check if this client is totally dead, and remove it if so
- {
- var clientOk = false;
- foreach (var t in cl.Value.PadIdsTime)
- {
- var dur = (now - t).TotalSeconds;
- if (dur < TimeoutLimit)
- {
- clientOk = true;
- break;
- }
- }
-
- if (!clientOk)
- {
- foreach (var dict in cl.Value.PadMacsTime)
- {
- var dur = (now - dict.Value).TotalSeconds;
- if (dur < TimeoutLimit)
- {
- clientOk = true;
- break;
- }
- }
-
- if (!clientOk)
- clientsToDelete.Add(cl.Key);
- }
- }
- }
-
- foreach (var delCl in clientsToDelete) clients.Remove(delCl);
- clientsToDelete.Clear();
- clientsToDelete = null;
- }
-
- if (clientsList.Count <= 0)
- return;
-
- unchecked
+ if (!IsInitialized)
+ return;
+
+ // only update every one second
+ for (byte padIdx = 0; padIdx < NUMBER_SLOTS; padIdx++)
{
- var outputData = new byte[100];
- var outIdx = BeginPacket(outputData);
- Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PadDataRsp), 0, outputData, outIdx, 4);
- outIdx += 4;
-
- outputData[outIdx++] = padMeta.PadId;
- outputData[outIdx++] = (byte)padMeta.PadState;
- outputData[outIdx++] = (byte)padMeta.Model;
- outputData[outIdx++] = (byte)padMeta.ConnectionType;
- {
- var padMac = padMeta.PadMacAddress.GetAddressBytes();
- outputData[outIdx++] = padMac[0];
- outputData[outIdx++] = padMac[1];
- outputData[outIdx++] = padMac[2];
- outputData[outIdx++] = padMac[3];
- outputData[outIdx++] = padMac[4];
- outputData[outIdx++] = padMac[5];
- }
- outputData[outIdx++] = (byte)padMeta.BatteryStatus;
- outputData[outIdx++] = padMeta.IsActive ? (byte)1 : (byte)0;
-
- Array.Copy(BitConverter.GetBytes((uint)udpPacketCount++), 0, outputData, outIdx, 4);
- outIdx += 4;
+ DualShockPadMeta padMeta = padMetas[padIdx];
- if (!ReportToBuffer(outputData, ref outIdx))
- return;
- FinishPacket(outputData);
-
- foreach (var cl in clientsList)
- {
- int temp = 0;
- poolLock.EnterWriteLock();
- temp = listInd;
- listInd = ++listInd % ARG_BUFFER_LEN;
- SocketAsyncEventArgs args = new SocketAsyncEventArgs()
+ if (ticks % 1000 == 0)
+ {
+ BatteryChargeStatus ChargeStatus = SystemInformation.PowerStatus.BatteryChargeStatus;
+
+ if (ChargeStatus.HasFlag(BatteryChargeStatus.Charging))
+ padMeta.BatteryStatus = DsBattery.Charging;
+ else if (ChargeStatus.HasFlag(BatteryChargeStatus.NoSystemBattery))
+ padMeta.BatteryStatus = DsBattery.None;
+ else if (ChargeStatus.HasFlag(BatteryChargeStatus.High))
+ padMeta.BatteryStatus = DsBattery.High;
+ else if (ChargeStatus.HasFlag(BatteryChargeStatus.Low))
+ padMeta.BatteryStatus = DsBattery.Low;
+ else if (ChargeStatus.HasFlag(BatteryChargeStatus.Critical))
+ padMeta.BatteryStatus = DsBattery.Dying;
+ else
+ padMeta.BatteryStatus = DsBattery.Medium;
+ }
+
+ // update status
+ padMeta.IsActive = true; // fixme ?
+
+ List? clientsList = new List();
+ DateTime now = DateTime.UtcNow;
+ lock (clients)
+ {
+ List? clientsToDelete = new List();
+
+ foreach (var cl in clients)
{
- RemoteEndPoint = cl,
- };
- args.SetBuffer(dataBuffers[temp], 0, 100);
- args.Completed += SocketEvent_AsyncCompleted;
- poolLock.ExitWriteLock();
-
- _pool.Wait();
- Array.Copy(outputData, args.Buffer, outputData.Length);
- bool sentAsync = false;
- try
- {
- bool sendAsync = udpSock.SendToAsync(args);
- }
- catch (SocketException /*ex*/) { }
- catch (Exception /*ex*/) { }
- finally
- {
- if (!sentAsync) CompletedSynchronousSocketEvent(args);
- }
- }
- }
-
- clientsList.Clear();
- clientsList = null;
- }
-
- private enum MessageType
- {
- DSUC_VersionReq = 0x100000,
- DSUS_VersionRsp = 0x100000,
- DSUC_ListPorts = 0x100001,
- DSUS_PortInfo = 0x100001,
- DSUC_PadDataReq = 0x100002,
- DSUS_PadDataRsp = 0x100002
- }
-
- class ClientRequestTimes
- {
- DateTime allPads;
- DateTime[] padIds;
- Dictionary padMacs;
-
- public DateTime AllPadsTime { get { return allPads; } }
- public DateTime[] PadIdsTime { get { return padIds; } }
- public Dictionary PadMacsTime { get { return padMacs; } }
-
- public ClientRequestTimes()
- {
- allPads = DateTime.MinValue;
- padIds = new DateTime[4];
-
- for (int i = 0; i < padIds.Length; i++)
- padIds[i] = DateTime.MinValue;
-
- padMacs = [];
- }
-
- public void RequestPadInfo(byte regFlags, byte idToReg, PhysicalAddress macToReg)
- {
- if (regFlags == 0)
- allPads = DateTime.UtcNow;
- else
- {
- if ((regFlags & 0x01) != 0) //id valid
- {
- if (idToReg < padIds.Length)
- padIds[idToReg] = DateTime.UtcNow;
- }
- if ((regFlags & 0x02) != 0) //mac valid
- {
- padMacs[macToReg] = DateTime.UtcNow;
- }
- }
+ const double TimeoutLimit = 5;
+
+ if ((now - cl.Value.AllPadsTime).TotalSeconds < TimeoutLimit)
+ {
+ clientsList.Add(cl.Key);
+ }
+ else if (padMeta.PadId < cl.Value.PadIdsTime.Length &&
+ (now - cl.Value.PadIdsTime[padMeta.PadId]).TotalSeconds < TimeoutLimit)
+ {
+ clientsList.Add(cl.Key);
+ }
+ else if (cl.Value.PadMacsTime.TryGetValue(padMeta.PadMacAddress, out var padTime) &&
+ (now - padTime).TotalSeconds < TimeoutLimit)
+ {
+ clientsList.Add(cl.Key);
+ }
+ else //check if this client is totally dead, and remove it if so
+ {
+ var clientOk = false;
+ foreach (var t in cl.Value.PadIdsTime)
+ {
+ var dur = (now - t).TotalSeconds;
+ if (dur < TimeoutLimit)
+ {
+ clientOk = true;
+ break;
+ }
+ }
+
+ if (!clientOk)
+ {
+ foreach (var dict in cl.Value.PadMacsTime)
+ {
+ var dur = (now - dict.Value).TotalSeconds;
+ if (dur < TimeoutLimit)
+ {
+ clientOk = true;
+ break;
+ }
+ }
+
+ if (!clientOk)
+ clientsToDelete.Add(cl.Key);
+ }
+ }
+ }
+
+ foreach (var delCl in clientsToDelete) clients.Remove(delCl);
+ clientsToDelete.Clear();
+ clientsToDelete = null;
+ }
+
+ if (clientsList.Count <= 0)
+ return;
+
+ unchecked
+ {
+ var outputData = new byte[100];
+ var outIdx = BeginPacket(outputData);
+ Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PadDataRsp), 0, outputData, outIdx, 4);
+ outIdx += 4;
+
+ outputData[outIdx++] = padMeta.PadId;
+ outputData[outIdx++] = (byte)padMeta.PadState;
+ outputData[outIdx++] = (byte)padMeta.Model;
+ outputData[outIdx++] = (byte)padMeta.ConnectionType;
+ {
+ var padMac = padMeta.PadMacAddress.GetAddressBytes();
+ outputData[outIdx++] = padMac[0];
+ outputData[outIdx++] = padMac[1];
+ outputData[outIdx++] = padMac[2];
+ outputData[outIdx++] = padMac[3];
+ outputData[outIdx++] = padMac[4];
+ outputData[outIdx++] = padMac[5];
+ }
+ outputData[outIdx++] = (byte)padMeta.BatteryStatus;
+ outputData[outIdx++] = padMeta.IsActive ? (byte)1 : (byte)0;
+
+ Array.Copy(BitConverter.GetBytes((uint)udpPacketCount++), 0, outputData, outIdx, 4);
+ outIdx += 4;
+
+ if (!ReportToBuffer(outputData, ref outIdx, padIdx))
+ return;
+ FinishPacket(outputData);
+
+ foreach (var cl in clientsList)
+ {
+ int temp = 0;
+ poolLock.EnterWriteLock();
+ temp = listInd;
+ listInd = ++listInd % ARG_BUFFER_LEN;
+ SocketAsyncEventArgs args = new SocketAsyncEventArgs()
+ {
+ RemoteEndPoint = cl,
+ };
+ args.SetBuffer(dataBuffers[temp], 0, 100);
+ args.Completed += SocketEvent_AsyncCompleted;
+ poolLock.ExitWriteLock();
+
+ _pool.Wait();
+ Array.Copy(outputData, args.Buffer, outputData.Length);
+ bool sentAsync = false;
+ try
+ {
+ bool sendAsync = udpSock.SendToAsync(args);
+ }
+ catch (SocketException /*ex*/) { }
+ catch (Exception /*ex*/) { }
+ finally
+ {
+ if (!sentAsync) CompletedSynchronousSocketEvent(args);
+ }
+ }
+ }
+
+ clientsList.Clear();
+ clientsList = null;
}
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/DSU/DualShockPadMeta.cs b/HandheldCompanion/DSU/DualShockPadMeta.cs
new file mode 100644
index 000000000..9110c1212
--- /dev/null
+++ b/HandheldCompanion/DSU/DualShockPadMeta.cs
@@ -0,0 +1,15 @@
+using System.Net.NetworkInformation;
+
+namespace HandheldCompanion.DSU
+{
+ public struct DualShockPadMeta
+ {
+ public byte PadId;
+ public DsState PadState;
+ public DsConnection ConnectionType;
+ public DsModel Model;
+ public PhysicalAddress PadMacAddress;
+ public DsBattery BatteryStatus;
+ public bool IsActive;
+ }
+}
diff --git a/HandheldCompanion/DSU/MessageType.cs b/HandheldCompanion/DSU/MessageType.cs
new file mode 100644
index 000000000..51b46a0b9
--- /dev/null
+++ b/HandheldCompanion/DSU/MessageType.cs
@@ -0,0 +1,12 @@
+namespace HandheldCompanion.DSU
+{
+ public enum MessageType
+ {
+ DSUC_VersionReq = 0x100000,
+ DSUS_VersionRsp = 0x100000,
+ DSUC_ListPorts = 0x100001,
+ DSUS_PortInfo = 0x100001,
+ DSUC_PadDataReq = 0x100002,
+ DSUS_PadDataRsp = 0x100002
+ }
+}
diff --git a/HandheldCompanion/Devices/ASUS/ROGAlly.cs b/HandheldCompanion/Devices/ASUS/ROGAlly.cs
index 38f4a3fb7..97fc37f66 100644
--- a/HandheldCompanion/Devices/ASUS/ROGAlly.cs
+++ b/HandheldCompanion/Devices/ASUS/ROGAlly.cs
@@ -30,6 +30,8 @@ public class ROGAlly : IDevice
private AsusACPI? asusACPI;
+ private static bool customFanControl = false;
+
private const byte INPUT_HID_ID = 0x5a;
private const byte AURA_HID_ID = 0x5d;
private const int ASUS_ID = 0x0b05;
@@ -430,8 +432,17 @@ public override void SetFanControl(bool enable, int mode = 0)
switch (enable)
{
case false:
- asusACPI?.DeviceSet(AsusACPI.PerformanceMode, mode);
- return;
+ {
+ if (customFanControl)
+ {
+ customFanControl = false;
+ asusACPI?.DeviceSet(AsusACPI.PerformanceMode, mode);
+ }
+ }
+ break;
+ case true:
+ customFanControl = true;
+ break;
}
}
@@ -724,7 +735,7 @@ public void SetBatteryChargeLimit(int chargeLimit)
asusACPI?.DeviceSet(AsusACPI.BatteryLimit, chargeLimit);
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOFlipDS.cs b/HandheldCompanion/Devices/AYANEO/AYANEOFlipDS.cs
index 522f0060e..e8c476f78 100644
--- a/HandheldCompanion/Devices/AYANEO/AYANEOFlipDS.cs
+++ b/HandheldCompanion/Devices/AYANEO/AYANEOFlipDS.cs
@@ -49,7 +49,7 @@ private void ControllerManager_InputsUpdated(ControllerState Inputs)
}
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/Devices/GPD/GPDWinMax2AMD.cs b/HandheldCompanion/Devices/GPD/GPDWinMax2-2022-6800U.cs
similarity index 78%
rename from HandheldCompanion/Devices/GPD/GPDWinMax2AMD.cs
rename to HandheldCompanion/Devices/GPD/GPDWinMax2-2022-6800U.cs
index 6d0321fd4..5ab46f823 100644
--- a/HandheldCompanion/Devices/GPD/GPDWinMax2AMD.cs
+++ b/HandheldCompanion/Devices/GPD/GPDWinMax2-2022-6800U.cs
@@ -3,13 +3,13 @@
namespace HandheldCompanion.Devices;
-public class GPDWinMax2AMD : GPDWinMax2
+public class GPDWinMax2_2022_6800U : GPDWinMax2
{
- public GPDWinMax2AMD()
+ public GPDWinMax2_2022_6800U()
{
// https://www.amd.com/fr/products/apu/amd-ryzen-7-6800u
nTDP = new double[] { 15, 15, 28 };
- cTDP = new double[] { 15, 28 };
+ cTDP = new double[] { 3, 28 };
GfxClock = new double[] { 100, 2200 };
CpuClock = 4700;
@@ -20,8 +20,7 @@ public GPDWinMax2AMD()
{ 'Y', 'Z' },
{ '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/GPDWinMax2-2023-7640U.cs b/HandheldCompanion/Devices/GPD/GPDWinMax2-2023-7640U.cs
new file mode 100644
index 000000000..eb9aaa694
--- /dev/null
+++ b/HandheldCompanion/Devices/GPD/GPDWinMax2-2023-7640U.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Numerics;
+
+namespace HandheldCompanion.Devices;
+
+public class GPDWinMax2_2023_7640U : GPDWinMax2
+{
+ public GPDWinMax2_2023_7640U()
+ {
+ // https://www.amd.com/en/products/processors/laptop/ryzen/7000-series/amd-ryzen-5-7640u.html
+ nTDP = new double[] { 15, 15, 28 };
+ cTDP = new double[] { 3, 28 };
+ GfxClock = new double[] { 100, 2600 };
+ CpuClock = 4900;
+
+ 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' }
+ };
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/GPD/GPDWinMax2-2023-7840U.cs b/HandheldCompanion/Devices/GPD/GPDWinMax2-2023-7840U.cs
new file mode 100644
index 000000000..5f6bd2ad5
--- /dev/null
+++ b/HandheldCompanion/Devices/GPD/GPDWinMax2-2023-7840U.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Numerics;
+
+namespace HandheldCompanion.Devices;
+
+public class GPDWinMax2_2023_7840U : GPDWinMax2
+{
+ public GPDWinMax2_2023_7840U()
+ {
+ // https://www.amd.com/en/products/processors/laptop/ryzen/7000-series/amd-ryzen-7-7840u.html
+ nTDP = new double[] { 15, 15, 28 };
+ cTDP = new double[] { 3, 28 };
+ GfxClock = new double[] { 100, 2700 };
+ CpuClock = 5200;
+
+ 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' }
+ };
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/GPD/GPDWinMax2-2024-8640U.cs b/HandheldCompanion/Devices/GPD/GPDWinMax2-2024-8640U.cs
new file mode 100644
index 000000000..5b9a88201
--- /dev/null
+++ b/HandheldCompanion/Devices/GPD/GPDWinMax2-2024-8640U.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Numerics;
+
+namespace HandheldCompanion.Devices;
+
+public class GPDWinMax2_2024_8640U : GPDWinMax2
+{
+ public GPDWinMax2_2024_8640U()
+ {
+ // https://www.amd.com/en/products/processors/laptop/ryzen/8000-series/amd-ryzen-5-8640u.html
+ nTDP = new double[] { 15, 15, 28 };
+ cTDP = new double[] { 3, 28 };
+ GfxClock = new double[] { 100, 2600 };
+ CpuClock = 4900;
+
+ 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' }
+ };
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/GPD/GPDWinMax2-2024-8840U.cs b/HandheldCompanion/Devices/GPD/GPDWinMax2-2024-8840U.cs
new file mode 100644
index 000000000..830fca97e
--- /dev/null
+++ b/HandheldCompanion/Devices/GPD/GPDWinMax2-2024-8840U.cs
@@ -0,0 +1,32 @@
+using System.Collections.Generic;
+using System.Numerics;
+
+namespace HandheldCompanion.Devices;
+
+public class GPDWinMax2_2024_8840U : GPDWinMax2
+{
+ public GPDWinMax2_2024_8840U()
+ {
+ // https://www.amd.com/en/products/processors/laptop/ryzen/8000-series/amd-ryzen-7-8840u.html
+ nTDP = new double[] { 15, 15, 28 };
+ cTDP = new double[] { 3, 28 };
+ GfxClock = new double[] { 100, 2700 };
+ CpuClock = 5200;
+
+ 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' }
+ };
+ }
+}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/GPD/GPDWinMax2.cs b/HandheldCompanion/Devices/GPD/GPDWinMax2.cs
index c57cf8d8c..26b4c57c8 100644
--- a/HandheldCompanion/Devices/GPD/GPDWinMax2.cs
+++ b/HandheldCompanion/Devices/GPD/GPDWinMax2.cs
@@ -1,59 +1,59 @@
-using HandheldCompanion.Inputs;
-using WindowsInput.Events;
-
-namespace HandheldCompanion.Devices;
-
-public class GPDWinMax2 : IDevice
-{
- public GPDWinMax2()
- {
- // device specific settings
- ProductIllustration = "device_gpd_winmax2";
-
- // device specific capacities
- Capabilities = DeviceCapabilities.FanControl;
-
- ECDetails = new ECDetails
- {
- AddressFanControl = 0x275,
- AddressFanDuty = 0x1809,
- AddressStatusCommandPort = 0x4E,
- AddressDataPort = 0x4F,
- FanValueMin = 0,
- FanValueMax = 184
- };
-
- // Disabled this one as Win Max 2 also sends an Xbox guide input when Menu key is pressed.
- OEMChords.Add(new KeyboardChord("Menu",
- [KeyCode.LButton | KeyCode.XButton2],
- [KeyCode.LButton | KeyCode.XButton2],
- true, ButtonFlags.OEM1
- ));
-
- // note, need to manually configured in GPD app
- OEMChords.Add(new KeyboardChord("Bottom button left",
- [KeyCode.F11, KeyCode.L],
- [KeyCode.F11, KeyCode.L],
- false, ButtonFlags.OEM2
- ));
-
- OEMChords.Add(new KeyboardChord("Bottom button right",
- [KeyCode.F12, KeyCode.R],
- [KeyCode.F12, KeyCode.R],
- false, ButtonFlags.OEM3
- ));
+using HandheldCompanion.Inputs;
+using WindowsInput.Events;
+
+namespace HandheldCompanion.Devices;
+
+public class GPDWinMax2 : IDevice
+{
+ public GPDWinMax2()
+ {
+ // device specific settings
+ ProductIllustration = "device_gpd_winmax2";
+
+ // device specific capacities
+ Capabilities = DeviceCapabilities.FanControl;
+
+ ECDetails = new ECDetails
+ {
+ AddressFanControl = 0x275,
+ AddressFanDuty = 0x1809,
+ AddressStatusCommandPort = 0x4E,
+ AddressDataPort = 0x4F,
+ FanValueMin = 0,
+ FanValueMax = 184
+ };
+
+ // Disabled this one as Win Max 2 also sends an Xbox guide input when Menu key is pressed.
+ OEMChords.Add(new KeyboardChord("Menu",
+ [KeyCode.LButton | KeyCode.XButton2],
+ [KeyCode.LButton | KeyCode.XButton2],
+ true, ButtonFlags.OEM1
+ ));
+
+ // note, need to manually configured in GPD app
+ OEMChords.Add(new KeyboardChord("Bottom button left",
+ [KeyCode.F11, KeyCode.L],
+ [KeyCode.F11, KeyCode.L],
+ false, ButtonFlags.OEM2
+ ));
+
+ OEMChords.Add(new KeyboardChord("Bottom button right",
+ [KeyCode.F12, KeyCode.R],
+ [KeyCode.F12, KeyCode.R],
+ false, ButtonFlags.OEM3
+ ));
}
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.OEM2:
- return "\u220E";
- case ButtonFlags.OEM3:
- return "\u220F";
- }
-
- return defaultGlyph;
- }
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.OEM2:
+ return "\u220E";
+ case ButtonFlags.OEM3:
+ return "\u220F";
+ }
+
+ return defaultGlyph;
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Devices/IDevice.cs b/HandheldCompanion/Devices/IDevice.cs
index 82681834e..7336cd3da 100644
--- a/HandheldCompanion/Devices/IDevice.cs
+++ b/HandheldCompanion/Devices/IDevice.cs
@@ -146,7 +146,7 @@ public abstract class IDevice
public string ProductModel = "default";
// minimum delay before trying to emulate a virtual controller on system resume (milliseconds)
- public short ResumeDelay = 1000;
+ public short ResumeDelay = 2000;
// key press delay to use for certain scenarios
public short KeyPressDelay = 20;
@@ -397,7 +397,28 @@ public static IDevice GetCurrent()
device = new GPDWinMax2Intel();
break;
case "G1619-04":
- device = new GPDWinMax2AMD();
+ switch (Processor)
+ {
+ case "AMD Ryzen 7 6800U with Radeon Graphics":
+ device = new GPDWinMax2_2022_6800U();
+ break;
+ case "AMD Ryzen 5 7640U w/ Radeon 760M Graphics":
+ device = new GPDWinMax2_2023_7640U();
+ break;
+ case "AMD Ryzen 7 7840U w/ Radeon 780M Graphics":
+ device = new GPDWinMax2_2023_7840U();
+ break;
+ case "AMD Ryzen 5 8640U w/ Radeon 760M Graphics":
+ device = new GPDWinMax2_2024_8640U();
+ break;
+ case "AMD Ryzen 7 8840U w/ Radeon 780M Graphics":
+ device = new GPDWinMax2_2024_8840U();
+ break;
+ // if none of those
+ default:
+ device = new GPDWinMax2_2024_8840U(); // Assume newer device is similar to latest as of this commit
+ break;
+ }
break;
}
}
diff --git a/HandheldCompanion/Devices/Lenovo/LegionGo.cs b/HandheldCompanion/Devices/Lenovo/LegionGo.cs
index abef4e090..305c4405f 100644
--- a/HandheldCompanion/Devices/Lenovo/LegionGo.cs
+++ b/HandheldCompanion/Devices/Lenovo/LegionGo.cs
@@ -534,7 +534,7 @@ public override string GetGlyph(ButtonFlags button)
return defaultGlyph;
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/Devices/Valve/SteamDeck.cs b/HandheldCompanion/Devices/Valve/SteamDeck.cs
index 3ced6f085..ca0adad1d 100644
--- a/HandheldCompanion/Devices/Valve/SteamDeck.cs
+++ b/HandheldCompanion/Devices/Valve/SteamDeck.cs
@@ -246,7 +246,7 @@ public void SetMaxBatteryCharge(int chargeLimit)
inpOut?.WriteMemory(MCBL, data);
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/HandheldCompanion.csproj b/HandheldCompanion/HandheldCompanion.csproj
index 3ab4ad427..3fb8f3688 100644
--- a/HandheldCompanion/HandheldCompanion.csproj
+++ b/HandheldCompanion/HandheldCompanion.csproj
@@ -2,7 +2,7 @@
WinExe
- net8.0-windows10.0.19041.0
+ net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0
true
true
x64
@@ -12,7 +12,7 @@
HandheldCompanion.App
$(SolutionDir)bin\$(Configuration)
Resources\icon.ico
- 0.21.6.1
+ 0.21.7.0
app.manifest
AnyCPU;x64;x86
true
@@ -229,9 +229,11 @@
+
+
diff --git a/HandheldCompanion/Inputs/ButtonFlags.cs b/HandheldCompanion/Inputs/ButtonFlags.cs
index 39d3d0aad..48fa8c5ef 100644
--- a/HandheldCompanion/Inputs/ButtonFlags.cs
+++ b/HandheldCompanion/Inputs/ButtonFlags.cs
@@ -87,6 +87,9 @@ public enum ButtonFlags : byte
[Description("Volume Down")] VolumeDown = 61,
Special2 = 62,
+ B9 = 63,
+ B10 = 64,
+ B11 = 65,
HOTKEY_START = 70,
HOTKEY_GYRO_ACTIVATION = 140,
diff --git a/HandheldCompanion/Inputs/GyroState.cs b/HandheldCompanion/Inputs/GyroState.cs
index 669382392..d3615ca8e 100644
--- a/HandheldCompanion/Inputs/GyroState.cs
+++ b/HandheldCompanion/Inputs/GyroState.cs
@@ -9,12 +9,10 @@ public class GyroState : ICloneable
public Dictionary Accelerometer = [];
public Dictionary Gyroscope = [];
- public const byte SENSOR_MAX = 4;
public enum SensorState
{
- Raw,
Default,
- GMH,
+ GamepadMotion,
DSU
}
diff --git a/HandheldCompanion/Managers/ControllerManager.cs b/HandheldCompanion/Managers/ControllerManager.cs
index 5c478f57a..f7c569cfb 100644
--- a/HandheldCompanion/Managers/ControllerManager.cs
+++ b/HandheldCompanion/Managers/ControllerManager.cs
@@ -55,7 +55,7 @@ public static class ControllerManager
private static object targetLock = new object();
public static ControllerManagerStatus managerStatus = ControllerManagerStatus.Pending;
- private static Timer scenarioTimer = new(1000) { AutoReset = true };
+ private static Timer scenarioTimer = new(100) { AutoReset = false };
public static bool IsInitialized;
@@ -84,7 +84,7 @@ public static Task Start()
DriversStore = DeserializeDriverStore();
// Flushing possible JoyShocks...
- JslDisconnectAndDisposeAll();
+ JslDisconnect();
DeviceManager.XUsbDeviceArrived += XUsbDeviceArrived;
DeviceManager.XUsbDeviceRemoved += XUsbDeviceRemoved;
@@ -95,10 +95,8 @@ public static Task Start()
SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
- /*
UIGamepad.GotFocus += GamepadFocusManager_GotFocus;
UIGamepad.LostFocus += GamepadFocusManager_LostFocus;
- */
ProcessManager.ForegroundChanged += ProcessManager_ForegroundChanged;
@@ -152,7 +150,7 @@ public static void Stop()
controller.Unhide(false);
// Flushing possible JoyShocks...
- JslDisconnectAndDisposeAll();
+ JslDisconnect();
LogManager.LogInformation("{0} has stopped", "ControllerManager");
}
@@ -173,7 +171,6 @@ private enum FocusedWindow
Quicktools
}
- /*
private static void GamepadFocusManager_LostFocus(string Name)
{
switch (Name)
@@ -207,7 +204,6 @@ private static void GamepadFocusManager_GotFocus(string Name)
// check applicable scenarios
CheckControllerScenario();
}
- */
private static void ProcessManager_ForegroundChanged(ProcessEx? processEx, ProcessEx? backgroundEx)
{
@@ -281,11 +277,9 @@ private static void ScenarioTimer_Elapsed(object? sender, ElapsedEventArgs e)
}
}
- /*
// either main window or quicktools are focused
if (focusedWindows != FocusedWindow.None)
ControllerMuted = true;
- */
}
private static void CheckControllerScenario()
@@ -295,7 +289,7 @@ private static void CheckControllerScenario()
scenarioTimer.Start();
}
- private static void SettingsManager_SettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
@@ -594,8 +588,7 @@ private static async void HidDeviceArrived(PnPDetails details, DeviceEventArgs o
// unsupported controller
if (controller is null)
{
- LogManager.LogError("Unsupported Generic controller: VID:{0} and PID:{1}", details.GetVendorID(),
- details.GetProductID());
+ LogManager.LogError("Unsupported Generic controller: VID:{0} and PID:{1}", details.GetVendorID(), details.GetProductID());
return;
}
@@ -845,10 +838,36 @@ private static async void XUsbDeviceArrived(PnPDetails details, DeviceEventArgs
case "0x17EF":
controller = new LegionController(details);
break;
+
+ // GameSir
+ case "0x3537":
+ {
+ switch (details.GetProductID())
+ {
+ // Tarantula Pro (Dongle)
+ case "0x1099":
+ case "0x103E":
+ details.isDongle = true;
+ goto case "0x1050";
+ // Tarantula Pro
+ default:
+ case "0x1050":
+ controller = new TatantulaProController(details);
+ break;
+ }
+ }
+ break;
}
});
}
+ // unsupported controller
+ if (controller is null)
+ {
+ LogManager.LogError("Unsupported XInput controller: VID:{0} and PID:{1}", details.GetVendorID(), details.GetProductID());
+ return;
+ }
+
while (!controller.IsReady && controller.IsConnected())
await Task.Delay(250);
@@ -1184,11 +1203,14 @@ public static List GetControllers()
}
private static ControllerState mutedState = new ControllerState();
- private static void UpdateInputs(ControllerState controllerState, GamepadMotion gamepadMotion, float deltaTimeSeconds)
+ private static void UpdateInputs(ControllerState controllerState, Dictionary gamepadMotions, float deltaTimeSeconds, byte gamepadIndex)
{
// raise event
InputsUpdated?.Invoke(controllerState);
+ // get main motion
+ GamepadMotion gamepadMotion = gamepadMotions[gamepadIndex];
+
switch (sensorSelection)
{
case SensorFamily.Windows:
@@ -1205,21 +1227,19 @@ private static void UpdateInputs(ControllerState controllerState, GamepadMotion
MainWindow.overlayModel.UpdateReport(controllerState, gamepadMotion, deltaTimeSeconds);
}
+ // compute layout
+ controllerState = LayoutManager.MapController(controllerState);
+ InputsUpdated2?.Invoke(controllerState);
+
// controller is muted
if (ControllerMuted)
{
mutedState.ButtonState[ButtonFlags.Special] = controllerState.ButtonState[ButtonFlags.Special];
-
- // swap states
controllerState = mutedState;
}
- else
- {
- // compute layout
- controllerState = LayoutManager.MapController(controllerState);
- InputsUpdated2?.Invoke(controllerState);
- }
+ DS4Touch.UpdateInputs(controllerState);
+ DSUServer.UpdateInputs(controllerState, gamepadMotions);
VirtualManager.UpdateInputs(controllerState, gamepadMotion);
}
diff --git a/HandheldCompanion/Managers/DynamicLightingManager.cs b/HandheldCompanion/Managers/DynamicLightingManager.cs
index fab7028f1..1cd6faaf0 100644
--- a/HandheldCompanion/Managers/DynamicLightingManager.cs
+++ b/HandheldCompanion/Managers/DynamicLightingManager.cs
@@ -146,7 +146,7 @@ private static void ReleaseDirect3DDevice()
device = null;
}
- private static void SettingsManager_SettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/Managers/HotkeysManager.cs b/HandheldCompanion/Managers/HotkeysManager.cs
index 1c826363e..bd423fa1f 100644
--- a/HandheldCompanion/Managers/HotkeysManager.cs
+++ b/HandheldCompanion/Managers/HotkeysManager.cs
@@ -1,6 +1,8 @@
using HandheldCompanion.Commands;
using HandheldCompanion.Commands.Functions.HC;
using HandheldCompanion.Commands.Functions.Multimedia;
+using HandheldCompanion.Commands.Functions.Multitasking;
+using HandheldCompanion.Commands.Functions.Performance;
using HandheldCompanion.Commands.Functions.Windows;
using HandheldCompanion.Inputs;
using HandheldCompanion.Utils;
@@ -186,16 +188,16 @@ private static void ProcessHotkey(string fileName)
command = new OverlayTrackpadCommands();
break;
case 03:
- // "OnScreenDisplayToggle"
+ command = new QuickOverlayCommands();
break;
case 10:
command = new QuickToolsCommands();
break;
case 11:
- // "increaseTDP"
+ command = new TDPIncrease();
break;
case 12:
- // "decreaseTDP"
+ command = new TDPDecrease();
break;
case 13:
// "suspendResumeTask"
diff --git a/HandheldCompanion/Managers/InputsManager.cs b/HandheldCompanion/Managers/InputsManager.cs
index b72e5adbd..7c799e5d6 100644
--- a/HandheldCompanion/Managers/InputsManager.cs
+++ b/HandheldCompanion/Managers/InputsManager.cs
@@ -180,7 +180,7 @@ private static bool CheckForSequence(bool IsKeyDown, bool IsKeyUp)
}
// execute command
- hotkey.command.Execute(IsKeyDown, IsKeyUp);
+ hotkey.Execute(IsKeyDown, IsKeyUp, false);
// raise event
CommandExecuted?.Invoke(hotkey, hotkey.command);
diff --git a/HandheldCompanion/Managers/LayoutManager.cs b/HandheldCompanion/Managers/LayoutManager.cs
index eab93b252..c618aca4e 100644
--- a/HandheldCompanion/Managers/LayoutManager.cs
+++ b/HandheldCompanion/Managers/LayoutManager.cs
@@ -236,7 +236,7 @@ public static void SerializeLayoutTemplate(LayoutTemplate layoutTemplate)
File.WriteAllText(fileName, jsonString);
}
- private static void SettingsManager_SettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/Managers/MotionManager.cs b/HandheldCompanion/Managers/MotionManager.cs
index d5269d610..05ee69250 100644
--- a/HandheldCompanion/Managers/MotionManager.cs
+++ b/HandheldCompanion/Managers/MotionManager.cs
@@ -6,6 +6,7 @@
using HandheldCompanion.Utils;
using HandheldCompanion.Views;
using System;
+using System.Collections.Generic;
using System.Numerics;
using static HandheldCompanion.Utils.DeviceUtils;
using SensorState = HandheldCompanion.Inputs.GyroState.SensorState;
@@ -53,72 +54,121 @@ public static void UpdateReport(ControllerState controllerState, GamepadMotion g
// and is enough for DS4/DSU gyroscope handling
private static void SetupMotion(ControllerState controllerState, GamepadMotion gamepadMotion)
{
- Profile current = ProfileManager.GetCurrent();
+ // GamepadMotion: calibrated/filtered outputs from JoyShockLibrary
+ if (controllerState.GyroState.Gyroscope.TryGetValue(SensorState.GamepadMotion, out Vector3 gyrometer))
+ {
+ gamepadMotion.GetCalibratedGyro(out float gyroX, out float gyroY, out float gyroZ);
+
+ gyrometer.X = gyroX;
+ gyrometer.Y = gyroZ;
+ gyrometer.Z = gyroY;
+ }
+ if (controllerState.GyroState.Accelerometer.TryGetValue(SensorState.GamepadMotion, out Vector3 acceleromter))
+ {
+ gamepadMotion.GetGravity(out float accelX, out float accelY, out float accelZ);
- // GMH: based on GamepadMotionHelpers values
- gamepadMotion.GetCalibratedGyro(out float gyroX, out float gyroY, out float gyroZ);
- gamepadMotion.GetGravity(out float accelX, out float accelY, out float accelZ);
+ acceleromter.X = accelX;
+ acceleromter.Y = accelY;
+ acceleromter.Z = accelZ;
+ }
- controllerState.GyroState.Gyroscope[SensorState.GMH] = new() { X = gyroX, Y = gyroY, Z = gyroZ };
- controllerState.GyroState.Accelerometer[SensorState.GMH] = new() { X = accelX, Y = accelY, Z = accelZ };
+ // DSU: unfiltered outputs from sensors
+ if (controllerState.GyroState.Gyroscope.TryGetValue(SensorState.DSU, out gyrometer))
+ {
+ gamepadMotion.GetRawGyro(out float gyroX, out float gyroY, out float gyroZ);
- // Default: based on GamepadMotionHelpers values with multipliers applied
- controllerState.GyroState.Gyroscope[SensorState.Default] = controllerState.GyroState.Gyroscope[SensorState.GMH] * current.GyrometerMultiplier;
- controllerState.GyroState.Accelerometer[SensorState.Default] = controllerState.GyroState.Accelerometer[SensorState.GMH] * current.AccelerometerMultiplier;
+ gyrometer.X = gyroX;
+ gyrometer.Y = gyroZ;
+ gyrometer.Z = gyroY;
+ }
+ if (controllerState.GyroState.Accelerometer.TryGetValue(SensorState.DSU, out acceleromter))
+ {
+ gamepadMotion.GetRawAcceleration(out float accelX, out float accelY, out float accelZ);
+
+ acceleromter.X = accelX;
+ acceleromter.Y = accelY;
+ acceleromter.Z = accelZ;
+ }
+
+ // Default: based on GamepadMotionHelpers values with profile settings applied
+ Profile current = ProfileManager.GetCurrent();
+
+ controllerState.GyroState.Gyroscope[SensorState.Default] = controllerState.GyroState.Gyroscope[SensorState.GamepadMotion] * current.GyrometerMultiplier;
+ controllerState.GyroState.Accelerometer[SensorState.Default] = controllerState.GyroState.Accelerometer[SensorState.GamepadMotion] * current.AccelerometerMultiplier;
// Default: swap roll/yaw/auto
+ SteeringAxis steeringAxis = DetermineSteeringAxis(current, controllerState);
+ if (steeringAxis == SteeringAxis.Yaw)
+ {
+ SwapYawRoll(controllerState.GyroState.Gyroscope, SensorState.Default);
+ SwapYawRoll(controllerState.GyroState.Accelerometer, SensorState.Default);
+ SwapYawRoll(controllerState.GyroState.Gyroscope, SensorState.DSU);
+ SwapYawRoll(controllerState.GyroState.Accelerometer, SensorState.DSU);
+ }
+
+ // DSU: invert axes if needed
+ if (current.MotionInvertHorizontal)
+ {
+ InvertAxis(controllerState.GyroState.Gyroscope, SensorState.DSU, Axis.Y, Axis.Z);
+ InvertAxis(controllerState.GyroState.Accelerometer, SensorState.DSU, Axis.Y, Axis.Z);
+ }
+ if (current.MotionInvertVertical)
+ {
+ InvertAxis(controllerState.GyroState.Gyroscope, SensorState.DSU, Axis.X, Axis.Y);
+ InvertAxis(controllerState.GyroState.Accelerometer, SensorState.DSU, Axis.X, Axis.Y);
+ }
+ }
+
+ private static SteeringAxis DetermineSteeringAxis(Profile current, ControllerState controllerState)
+ {
SteeringAxis steeringAxis = current.SteeringAxis;
if (steeringAxis == SteeringAxis.Auto)
{
SensorFamily sensorSelection = (SensorFamily)SettingsManager.GetInt("SensorSelection");
- switch (sensorSelection)
+ if (sensorSelection == SensorFamily.Windows || sensorSelection == SensorFamily.SerialUSBIMU)
{
- case SensorFamily.Windows:
- case SensorFamily.SerialUSBIMU:
- steeringAxis = SteeringAxis.Yaw;
- break;
-
- case SensorFamily.Controller:
- if (Math.Abs(accelZ) > Math.Abs(accelY))
- steeringAxis = SteeringAxis.Yaw;
- break;
+ return SteeringAxis.Yaw;
+ }
+ if (sensorSelection == SensorFamily.Controller &&
+ Math.Abs(controllerState.GyroState.Accelerometer[SensorState.Default].Z) > Math.Abs(controllerState.GyroState.Accelerometer[SensorState.Default].Y))
+ {
+ return SteeringAxis.Yaw;
}
}
+ return steeringAxis;
+ }
- switch (steeringAxis)
+ private static void SwapYawRoll(Dictionary sensorDictionary, SensorState state)
+ {
+ if (sensorDictionary.TryGetValue(state, out Vector3 sensor))
{
- case SteeringAxis.Yaw:
- controllerState.GyroState.Gyroscope[SensorState.Default] = new(controllerState.GyroState.Gyroscope[SensorState.Default].X, -controllerState.GyroState.Gyroscope[SensorState.Default].Z, -controllerState.GyroState.Gyroscope[SensorState.Default].Y);
- controllerState.GyroState.Accelerometer[SensorState.Default] = new(controllerState.GyroState.Accelerometer[SensorState.Default].X, -controllerState.GyroState.Accelerometer[SensorState.Default].Z, -controllerState.GyroState.Accelerometer[SensorState.Default].Y);
- break;
+ sensor = new Vector3(sensor.X, -sensor.Z, -sensor.Y);
+ sensorDictionary[state] = sensor;
}
+ }
- // update all states (except Default, GMH)
- foreach (SensorState state in Enum.GetValues(typeof(SensorState)))
+ private static void InvertAxis(Dictionary sensorDictionary, SensorState state, Axis axis1, Axis axis2)
+ {
+ if (sensorDictionary.TryGetValue(state, out Vector3 sensor))
{
- if (state == SensorState.Default || state == SensorState.GMH)
- continue;
-
- // set to default
- controllerState.GyroState.Gyroscope[state] = controllerState.GyroState.Gyroscope[SensorState.Default];
- controllerState.GyroState.Accelerometer[state] = controllerState.GyroState.Accelerometer[SensorState.Default];
-
- // DSU: invert horizontal axis
- if (current.MotionInvertHorizontal)
+ switch (axis1)
{
- controllerState.GyroState.Gyroscope[state] = new(controllerState.GyroState.Gyroscope[state].X, -controllerState.GyroState.Gyroscope[state].Y, -controllerState.GyroState.Gyroscope[state].Z);
- controllerState.GyroState.Accelerometer[state] = new(controllerState.GyroState.Accelerometer[state].X, -controllerState.GyroState.Accelerometer[state].Y, -controllerState.GyroState.Accelerometer[state].Z);
+ case Axis.X: sensor.X = -sensor.X; break;
+ case Axis.Y: sensor.Y = -sensor.Y; break;
+ case Axis.Z: sensor.Z = -sensor.Z; break;
}
-
- // DSU: invert vertical axis
- if (current.MotionInvertVertical)
+ switch (axis2)
{
- controllerState.GyroState.Gyroscope[state] = new(-controllerState.GyroState.Gyroscope[state].X, -controllerState.GyroState.Gyroscope[state].Y, controllerState.GyroState.Gyroscope[state].Z);
- controllerState.GyroState.Accelerometer[state] = new(-controllerState.GyroState.Accelerometer[state].X, -controllerState.GyroState.Accelerometer[state].Y, controllerState.GyroState.Accelerometer[state].Z);
+ case Axis.X: sensor.X = -sensor.X; break;
+ case Axis.Y: sensor.Y = -sensor.Y; break;
+ case Axis.Z: sensor.Z = -sensor.Z; break;
}
+ sensorDictionary[state] = sensor;
}
}
+ private enum Axis { X, Y, Z }
+
// this function is used for advanced motion calculations used by
// gyro to joy/mouse mappings and by UI that configures them
private static void ProcessMotion(ControllerState controllerState, GamepadMotion gamepadMotion)
@@ -230,6 +280,4 @@ private static void ProcessMotion(ControllerState controllerState, GamepadMotion
controllerState.AxisState[AxisFlags.GyroY] = (short)Math.Clamp(output.Y, short.MinValue, short.MaxValue);
}
}
-
-
}
\ No newline at end of file
diff --git a/HandheldCompanion/Managers/MultimediaManager.cs b/HandheldCompanion/Managers/MultimediaManager.cs
index be0d565f1..919eb6335 100644
--- a/HandheldCompanion/Managers/MultimediaManager.cs
+++ b/HandheldCompanion/Managers/MultimediaManager.cs
@@ -90,7 +90,7 @@ private static void SetDefaultAudioEndPoint()
}
}
- private static void SettingsManager_SettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
@@ -479,6 +479,38 @@ public static void DecreaseVolume()
SetVolume(volume);
}
+ public static void Mute()
+ {
+ if (!VolumeSupport)
+ return;
+
+ multimediaDevice.AudioEndpointVolume.Mute = true;
+ }
+
+ public static void Unmute()
+ {
+ if (!VolumeSupport)
+ return;
+
+ multimediaDevice.AudioEndpointVolume.Mute = false;
+ }
+
+ public static void ToggleMute()
+ {
+ if (!VolumeSupport)
+ return;
+
+ multimediaDevice.AudioEndpointVolume.Mute = !multimediaDevice.AudioEndpointVolume.Mute;
+ }
+
+ public static bool IsMuted()
+ {
+ if (!VolumeSupport)
+ return true;
+
+ return multimediaDevice.AudioEndpointVolume.Mute;
+ }
+
public static short GetBrightness()
{
try
diff --git a/HandheldCompanion/Managers/OSDManager.cs b/HandheldCompanion/Managers/OSDManager.cs
index b9d548080..78489ab7d 100644
--- a/HandheldCompanion/Managers/OSDManager.cs
+++ b/HandheldCompanion/Managers/OSDManager.cs
@@ -413,7 +413,7 @@ private static void AddElementIfNotNull(OverlayEntry entry, float? value, String
}
}
- private static void SettingsManager_SettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/Managers/PerformanceManager.cs b/HandheldCompanion/Managers/PerformanceManager.cs
index b9aaf243b..6201d4ab6 100644
--- a/HandheldCompanion/Managers/PerformanceManager.cs
+++ b/HandheldCompanion/Managers/PerformanceManager.cs
@@ -22,8 +22,7 @@ public static class OSPowerMode
///
/// Better Performance mode.
///
- // public static Guid BetterPerformance = new Guid("3af9B8d9-7c97-431d-ad78-34a8bfea439f");
- public static Guid BetterPerformance = new();
+ public static Guid BetterPerformance = Guid.Empty;
///
/// Best Performance mode.
@@ -43,95 +42,108 @@ public enum CPUBoostLevel
public static class PerformanceManager
{
private const short INTERVAL_DEFAULT = 3000; // default interval between value scans
- private const short INTERVAL_AUTO = 1010; // default interval between value scans
+ private const short INTERVAL_AUTO = 1010; // default interval between value scans for AutoTDP
private const short INTERVAL_DEGRADED = 5000; // degraded interval between value scans
- public static readonly Guid[] PowerModes = new Guid[3] { OSPowerMode.BetterBattery, OSPowerMode.BetterPerformance, OSPowerMode.BestPerformance };
+ private const int COUNTER_DEFAULT = 3; // default counter value
+ private const int COUNTER_AUTO = 5; // default counter value for AutoTDP
- private static readonly Timer autoWatchdog;
- private static readonly Timer cpuWatchdog;
+ public static readonly Guid[] PowerModes = { OSPowerMode.BetterBattery, OSPowerMode.BetterPerformance, OSPowerMode.BestPerformance };
+
+ private static readonly Timer autotdpWatchdog;
+ private static readonly Timer tdpWatchdog;
private static readonly Timer gfxWatchdog;
- private static readonly Timer powerWatchdog;
+ private static readonly Timer cpuWatchdog;
- private static CrossThreadLock autoLock = new();
- private static CrossThreadLock cpuLock = new();
+ private static CrossThreadLock autotdpLock = new();
+ private static CrossThreadLock tdpLock = new();
private static CrossThreadLock gfxLock = new();
- private static CrossThreadLock powerLock = new();
+ private static CrossThreadLock cpuLock = new();
+
+ private static PowerProfile? currentProfile = null;
+
+ // used to determine relevant TDP and MSR values
+ private static Processor? processor;
// AutoTDP
- private static double AutoTDP;
- private static double AutoTDPPrev;
private static bool AutoTDPFirstRun = true;
+ private static double AutoTDPTargetFPS;
private static int AutoTDPFPSSetpointMetCounter;
private static int AutoTDPFPSSmallDipCounter;
+ private static readonly double[] FPSHistory = new double[6];
+ private static double ProcessValueFPSPrevious;
+ private static double AutoTDP;
+ private static double AutoTDPPrev;
private static double AutoTDPMax;
- private static double TDPMax;
- private static double TDPMin;
- private static double AutoTDPTargetFPS;
- private static bool cpuWatchdogPendingStop;
- private static uint currentEPP = 50;
- private static int currentCoreCount;
+ private static bool autotdpWatchdogPendingStop;
+ private static int autotdpWatchdogCounter;
// powercfg
- private static uint currentPerfBoostMode;
private static Guid currentPowerMode = new("FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF");
- private static readonly double[] CurrentTDP = new double[5]; // used to store current TDP
// GPU limits
private static double FallbackGfxClock;
- private static readonly double[] FPSHistory = new double[6];
- private static bool gfxWatchdogPendingStop;
-
- private static Processor? processor;
- private static double ProcessValueFPSPrevious;
private static double StoredGfxClock;
+ private static bool gfxWatchdogPendingStop;
+ private static int gfxWatchdogCounter;
// TDP limits
+ private static double TDPMin;
+ private static double TDPMax;
+ private static bool tdpWatchdogPendingStop;
+ private static readonly double[] CurrentTDP = new double[5]; // used to store current TDP
private static readonly double[] StoredTDP = new double[3]; // used to store TDP
+ private static int tdpWatchdogCounter;
private static bool IsInitialized;
-
public static event InitializedEventHandler Initialized;
public delegate void InitializedEventHandler();
static PerformanceManager()
{
// initialize timer(s)
- powerWatchdog = new Timer { Interval = INTERVAL_DEFAULT, AutoReset = true, Enabled = false };
- powerWatchdog.Elapsed += powerWatchdog_Elapsed;
-
cpuWatchdog = new Timer { Interval = INTERVAL_DEFAULT, AutoReset = true, Enabled = false };
cpuWatchdog.Elapsed += cpuWatchdog_Elapsed;
+ tdpWatchdog = new Timer { Interval = INTERVAL_DEFAULT, AutoReset = true, Enabled = false };
+ tdpWatchdog.Elapsed += tdpWatchdog_Elapsed;
+
gfxWatchdog = new Timer { Interval = INTERVAL_DEFAULT, AutoReset = true, Enabled = false };
gfxWatchdog.Elapsed += gfxWatchdog_Elapsed;
- autoWatchdog = new Timer { Interval = INTERVAL_AUTO, AutoReset = true, Enabled = false };
- autoWatchdog.Elapsed += AutoTDPWatchdog_Elapsed;
+ autotdpWatchdog = new Timer { Interval = INTERVAL_AUTO, AutoReset = true, Enabled = false };
+ autotdpWatchdog.Elapsed += autotdpWatchdog_Elapsed;
// manage events
PowerProfileManager.Applied += PowerProfileManager_Applied;
PowerProfileManager.Discarded += PowerProfileManager_Discarded;
- SettingsManager.SettingValueChanged += SettingsManagerOnSettingValueChanged;
+ SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
+ }
- currentCoreCount = MotherboardInfo.NumberOfCores;
+ public static double GetMinimumTDP()
+ {
+ return TDPMin;
+ }
+
+ public static double GetMaximumTDP()
+ {
+ return TDPMax;
}
- private static void SettingsManagerOnSettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
case "ConfigurableTDPOverrideDown":
{
TDPMin = Convert.ToDouble(value);
- AutoTDP = (TDPMax + TDPMin) / 2.0d;
+ if (AutoTDPMax != 0d && AutoTDPMax < TDPMin) AutoTDPMax = TDPMin;
}
break;
case "ConfigurableTDPOverrideUp":
{
TDPMax = Convert.ToDouble(value);
- if (AutoTDPMax == 0d) AutoTDPMax = TDPMax;
- AutoTDP = (TDPMax + TDPMin) / 2.0d;
+ if (AutoTDPMax == 0d || AutoTDPMax > TDPMax) AutoTDPMax = TDPMax;
}
break;
}
@@ -139,57 +151,61 @@ private static void SettingsManagerOnSettingValueChanged(string name, object val
private static void PowerProfileManager_Applied(PowerProfile profile, UpdateSource source)
{
+ currentProfile = profile;
+
// apply profile defined TDP
if (profile.TDPOverrideEnabled)
{
if (!profile.AutoTDPEnabled)
{
- // Manual TDP is set, use it and set max limit
+ // AutoTDP is off and manual TDP is set
+ // stop AutoTDP watchdog and apply manual TDP
+ StopAutoTDPWatchdog(true);
RequestTDP(profile.TDPOverrideValues);
- if (!cpuWatchdog.Enabled)
+ if (!tdpWatchdog.Enabled)
StartTDPWatchdog();
-
- AutoTDPMax = SettingsManager.GetInt("ConfigurableTDPOverrideUp");
}
- else if (profile.TDPOverrideValues is not null)
+ else
{
- // Both manual TDP and AutoTDP are on,
- // use manual slider as the max limit for AutoTDP
- AutoTDPMax = profile.TDPOverrideValues[0];
+ // Both manual TDP and AutoTDP are on
+ // use AutoTDP watchdog to adjust TDP
StopTDPWatchdog(true);
+ RestoreTDP(true);
}
+
+ // use manual slider as the starting value
+ // and max limit for AutoTDP
+ if (profile.TDPOverrideValues is not null)
+ AutoTDP = AutoTDPMax = profile.TDPOverrideValues[0];
+
}
else
{
- if (cpuWatchdog.Enabled)
+ if (tdpWatchdog.Enabled)
StopTDPWatchdog(true);
if (!profile.AutoTDPEnabled)
{
+ if (autotdpWatchdog.Enabled)
+ StopAutoTDPWatchdog(true);
+
// Neither manual TDP nor AutoTDP is enabled, restore default TDP
RestoreTDP(true);
}
- else
- {
- // AutoTDP is enabled but manual override is not, use the settings max limit
- AutoTDPMax = SettingsManager.GetInt("ConfigurableTDPOverrideUp");
- }
+
+ // manual TDP override is not set
+ // use the settings max limit for AutoTDP
+ AutoTDP = AutoTDPMax = SettingsManager.GetInt("ConfigurableTDPOverrideUp");
}
// apply profile defined AutoTDP
if (profile.AutoTDPEnabled)
{
AutoTDPTargetFPS = profile.AutoTDPRequestedFPS;
- StartAutoTDPWatchdog();
- }
- else if (autoWatchdog.Enabled)
- {
- StopAutoTDPWatchdog(true);
- // restore default TDP (if not manual TDP is enabled)
- if (!profile.TDPOverrideEnabled)
- RestoreTDP(true);
+ if (!autotdpWatchdog.Enabled)
+ StartAutoTDPWatchdog();
}
// apply profile defined CPU
@@ -200,7 +216,7 @@ private static void PowerProfileManager_Applied(PowerProfile profile, UpdateSour
else
{
// restore default GPU clock
- RestoreCPUClock(true);
+ RestoreCPUClock();
}
// apply profile defined GPU
@@ -209,10 +225,12 @@ private static void PowerProfileManager_Applied(PowerProfile profile, UpdateSour
RequestGPUClock(profile.GPUOverrideValue);
StartGPUWatchdog();
}
- else if (gfxWatchdog.Enabled)
+ else
{
+ if (gfxWatchdog.Enabled)
+ StopGPUWatchdog(true);
+
// restore default GPU clock
- StopGPUWatchdog(true);
RestoreGPUClock(true);
}
@@ -221,7 +239,7 @@ private static void PowerProfileManager_Applied(PowerProfile profile, UpdateSour
{
RequestEPP(profile.EPPOverrideValue);
}
- else if (currentEPP != 0x00000032)
+ else
{
// restore default EPP
RequestEPP(0x00000032);
@@ -232,7 +250,7 @@ private static void PowerProfileManager_Applied(PowerProfile profile, UpdateSour
{
RequestCPUCoreCount(profile.CPUCoreCount);
}
- else if (currentCoreCount != MotherboardInfo.NumberOfCores)
+ else
{
// restore default CPU Core Count
RequestCPUCoreCount(MotherboardInfo.NumberOfCores);
@@ -259,6 +277,8 @@ private static void PowerProfileManager_Applied(PowerProfile profile, UpdateSour
private static void PowerProfileManager_Discarded(PowerProfile profile)
{
+ currentProfile = null;
+
// restore default TDP
if (profile.TDPOverrideEnabled)
{
@@ -270,14 +290,13 @@ private static void PowerProfileManager_Discarded(PowerProfile profile)
if (profile.AutoTDPEnabled)
{
StopAutoTDPWatchdog(true);
- StopTDPWatchdog(true);
RestoreTDP(true);
}
// restore default CPU frequency
if (profile.CPUOverrideEnabled)
{
- RestoreCPUClock(true);
+ RestoreCPUClock();
}
// restore default GPU frequency
@@ -312,15 +331,17 @@ private static void PowerProfileManager_Discarded(PowerProfile profile)
private static void RestoreTDP(bool immediate)
{
- // On power status change, force refresh TDP
+ // On power status change, force refresh TDP and AutoTDP
PowerProfile profile = PowerProfileManager.GetDefault();
RequestTDP(profile.TDPOverrideValues, immediate);
+
+ if (profile.TDPOverrideValues is not null)
+ AutoTDP = profile.TDPOverrideValues[0];
}
- private static void RestoreCPUClock(bool immediate)
+ private static void RestoreCPUClock()
{
- uint maxClock = MotherboardInfo.ProcessorMaxTurboSpeed;
- RequestCPUClock(maxClock);
+ RequestCPUClock(0);
}
private static void RestoreGPUClock(bool immediate)
@@ -328,51 +349,110 @@ private static void RestoreGPUClock(bool immediate)
RequestGPUClock(255 * 50, immediate);
}
- private static void AutoTDPWatchdog_Elapsed(object? sender, ElapsedEventArgs e)
+ private static void autotdpWatchdog_Elapsed(object? sender, ElapsedEventArgs e)
{
+ if (processor is null || !processor.IsInitialized)
+ return;
+
if (!PlatformManager.RTSS.HasHook())
+ {
+ autotdpWatchdog.Interval = INTERVAL_DEGRADED;
+ RestoreTDP(true);
return;
+ }
+ else
+ autotdpWatchdog.Interval = INTERVAL_AUTO;
- if (autoLock.TryEnter())
+ if (autotdpLock.TryEnter())
{
- // todo: Store fps for data gathering from multiple points (OSD, Performance)
- double processValueFPS = PlatformManager.RTSS.GetFramerate(true);
+ try
+ {
+ autotdpWatchdogCounter++;
- // Ensure realistic process values, prevent divide by 0
- processValueFPS = Math.Clamp(processValueFPS, 5, 500);
+ bool TDPdone = false;
+ bool MSRdone = true;
+ bool forcedUpdate = false;
- // Determine error amount, include target, actual and dipper modifier
- double controllerError = AutoTDPTargetFPS - processValueFPS - AutoTDPDipper(processValueFPS, AutoTDPTargetFPS);
+ // todo: Store fps for data gathering from multiple points (OSD, Performance)
+ double processValueFPS = PlatformManager.RTSS.GetFramerate(true);
- // Clamp error amount corrected within a single cycle
- // Adjust clamp if actual FPS is 2.5x requested FPS
- double clampLowerLimit = processValueFPS >= 2.5 * AutoTDPTargetFPS ? -100 : -5;
- controllerError = Math.Clamp(controllerError, clampLowerLimit, 15);
+ // Ensure realistic process values, prevent divide by 0
+ processValueFPS = Math.Clamp(processValueFPS, 5, 500);
- double TDPAdjustment = controllerError * AutoTDP / processValueFPS;
- TDPAdjustment *= 0.9; // Always have a little undershoot
+ // Determine error amount, include target, actual and dipper modifier
+ double controllerError = AutoTDPTargetFPS - processValueFPS - AutoTDPDipper(processValueFPS, AutoTDPTargetFPS);
- // Determine final setpoint
- if (!AutoTDPFirstRun)
- AutoTDP += TDPAdjustment + AutoTDPDamper(processValueFPS);
- else
- AutoTDPFirstRun = false;
+ // Clamp error amount corrected within a single cycle
+ // Adjust clamp if actual FPS is 2.5x requested FPS
+ double clampLowerLimit = processValueFPS >= 2.5 * AutoTDPTargetFPS ? -100 : -5;
+ controllerError = Math.Clamp(controllerError, clampLowerLimit, 15);
- AutoTDP = Math.Clamp(AutoTDP, TDPMin, AutoTDPMax);
+ double TDPAdjustment = controllerError * AutoTDP / processValueFPS;
+ TDPAdjustment *= 0.9; // Always have a little undershoot
- // Only update if we have a different TDP value to set
- if (AutoTDP != AutoTDPPrev)
- {
- double[] values = new double[3] { AutoTDP, AutoTDP, AutoTDP };
- RequestTDP(values, true);
- }
- AutoTDPPrev = AutoTDP;
+ // Determine final setpoint
+ if (!AutoTDPFirstRun)
+ AutoTDP += TDPAdjustment + AutoTDPDamper(processValueFPS);
+ else
+ AutoTDPFirstRun = false;
+
+ AutoTDP = Math.Clamp(AutoTDP, TDPMin, AutoTDPMax);
- // LogManager.LogTrace("TDPSet;;;;;{0:0.0};{1:0.000};{2:0.0000};{3:0.0000};{4:0.0000}", AutoTDPTargetFPS, AutoTDP, TDPAdjustment, ProcessValueFPS, TDPDamping);
+ // LogManager.LogTrace("TDPSet;;;;;{0:0.0};{1:0.000};{2:0.0000};{3:0.0000};{4:0.0000}", AutoTDPTargetFPS, AutoTDP, TDPAdjustment, ProcessValueFPS, TDPDamping);
+
+ // force update TDP periodically since we don't actually read current TDP
+ if (autotdpWatchdogCounter > COUNTER_AUTO)
+ {
+ forcedUpdate = true;
+ autotdpWatchdogCounter = 0;
+ }
- // release lock
- Exit:
- autoLock.Exit();
+ // Only update if we have a different TDP value to set
+ // or a forced update is requested
+ if (AutoTDP != AutoTDPPrev || forcedUpdate)
+ {
+ double[] values = new double[3] { AutoTDP, AutoTDP, AutoTDP };
+ RequestTDP(values, true);
+ AutoTDPPrev = AutoTDP;
+ }
+
+ // are we done ?
+ TDPdone = CurrentTDP[0] == StoredTDP[0] && CurrentTDP[1] == StoredTDP[1] && CurrentTDP[2] == StoredTDP[2];
+
+ // processor specific
+ if (processor is IntelProcessor)
+ {
+ double TDPslow = StoredTDP[(int)PowerType.Slow];
+ double TDPfast = StoredTDP[(int)PowerType.Fast];
+
+ if (TDPslow != 0.0d && TDPfast != 0.0d)
+ // only request an update if current limit is different than stored
+ if (CurrentTDP[(int)PowerType.MsrSlow] != TDPslow || CurrentTDP[(int)PowerType.MsrFast] != TDPfast || forcedUpdate)
+ {
+ MSRdone = false;
+ RequestMSR(TDPslow, TDPfast);
+ }
+ }
+
+ // user requested to halt AutoTDP watchdog
+ if (autotdpWatchdogPendingStop)
+ {
+ if (autotdpWatchdog.Interval == INTERVAL_AUTO)
+ {
+ if (TDPdone && MSRdone)
+ autotdpWatchdog.Stop();
+ }
+ else if (autotdpWatchdog.Interval == INTERVAL_DEGRADED)
+ {
+ autotdpWatchdog.Stop();
+ }
+ }
+ }
+ finally
+ {
+ // release lock
+ autotdpLock.Exit();
+ }
}
}
@@ -435,140 +515,126 @@ private static double AutoTDPDamper(double FPSActual)
return TDPDamping;
}
- // todo: update this function to force (re)apply profile settings
- private static void powerWatchdog_Elapsed(object? sender, ElapsedEventArgs e)
+ private static void cpuWatchdog_Elapsed(object? sender, ElapsedEventArgs e)
{
- if (powerLock.TryEnter())
+ if (cpuLock.TryEnter())
{
- // Checking if active power shceme has changed to reflect that
- if (PowerGetEffectiveOverlayScheme(out Guid activeScheme) == 0)
+ try
{
- if (activeScheme != currentPowerMode)
+ if (currentProfile is not null)
{
- currentPowerMode = activeScheme;
- int idx = Array.IndexOf(PowerModes, activeScheme);
- if (idx != -1)
- PowerModeChanged?.Invoke(idx);
- }
- }
+ // Check if CPU clock speed has changed and apply if needed
+ if (currentProfile.CPUOverrideEnabled)
+ RequestCPUClock(Convert.ToUInt32(currentProfile.CPUOverrideValue));
- // read perfboostmode
- uint[] result = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFBOOSTMODE);
- uint perfboostmode = result[(int)PowerIndexType.AC];
- if (perfboostmode != currentPerfBoostMode)
- {
- currentPerfBoostMode = perfboostmode;
- PerfBoostModeChanged?.Invoke(perfboostmode);
- }
+ // Check if CPU core count has changed and apply if needed
+ if (currentProfile.CPUCoreEnabled)
+ RequestCPUCoreCount(currentProfile.CPUCoreCount);
- // Checking if current EPP value has changed to reflect that
- uint[] EPP = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP);
- uint DCvalue = EPP[(int)PowerIndexType.DC];
+ // Check if current EPP value has changed and apply if needed
+ if (currentProfile.EPPOverrideEnabled)
+ RequestEPP(currentProfile.EPPOverrideValue);
- if (DCvalue != currentEPP)
+ // Check if active power shceme has changed and apply if needed
+ RequestPowerMode(currentProfile.OSPowerMode);
+
+ // Check if PerfBoostMode value has changed and apply if needed
+ RequestPerfBoostMode((uint)currentProfile.CPUBoostLevel);
+ }
+ }
+ finally
{
- currentEPP = DCvalue;
- EPPChanged?.Invoke(DCvalue);
+ // release lock
+ cpuLock.Exit();
}
-
- // release lock
- Exit:
- powerLock.Exit();
}
}
- private static async void cpuWatchdog_Elapsed(object? sender, ElapsedEventArgs e)
+ private static async void tdpWatchdog_Elapsed(object? sender, ElapsedEventArgs e)
{
if (processor is null || !processor.IsInitialized)
return;
- if (cpuLock.TryEnter())
+ if (tdpLock.TryEnter())
{
- bool TDPdone = false;
- bool MSRdone = false;
-
- // read current values and (re)apply requested TDP if needed
- foreach (PowerType type in (PowerType[])Enum.GetValues(typeof(PowerType)))
+ try
{
- var idx = (int)type;
-
- // skip msr
- if (idx >= StoredTDP.Length)
- break;
+ tdpWatchdogCounter++;
- double TDP = StoredTDP[idx];
- if (TDP == 0.0d)
- continue;
+ bool TDPdone = false;
+ bool MSRdone = true;
+ bool forcedUpdate = false;
- if (processor is AMDProcessor)
+ // force update TDP periodically since we don't actually read current TDP
+ if (tdpWatchdogCounter > COUNTER_DEFAULT)
{
- // AMD reduces TDP by 10% when OS power mode is set to Best power efficiency
- if (currentPowerMode == OSPowerMode.BetterBattery)
- TDP = (int)Math.Truncate(TDP * 0.9);
-
- // AMD doesn't have MSR
- if (type == PowerType.MsrSlow || type == PowerType.MsrFast)
- continue;
+ forcedUpdate = true;
+ tdpWatchdogCounter = 0;
}
- else if (processor is IntelProcessor)
- {
- // Intel doesn't have stapm
- if (type == PowerType.Stapm)
- continue;
- }
-
- // todo: find a way to read TDP limits
- double ReadTDP = CurrentTDP[idx];
- if (ReadTDP != 0)
- cpuWatchdog.Interval = INTERVAL_DEFAULT;
- else
- cpuWatchdog.Interval = INTERVAL_DEGRADED;
- // only request an update if current limit is different than stored
- if (ReadTDP != TDP)
+ // read current values and (re)apply requested TDP if needed
+ for (int idx = (int)PowerType.Slow; idx <= (int)PowerType.Fast; idx++)
{
- processor.SetTDPLimit(type, TDP);
+ double TDP = StoredTDP[idx];
+ if (TDP == 0.0d)
+ continue;
- // update TDP limits (temporary)
- CurrentTDP[idx] = TDP;
- }
+ // AMD reduces TDP by 10% when OS power mode is set to Best power efficiency
+ if (processor is AMDProcessor && currentPowerMode == OSPowerMode.BetterBattery)
+ TDP = (int)Math.Truncate(TDP * 0.9);
- await Task.Delay(20);
- }
+ // todo: find a way to read TDP limits
+ double ReadTDP = CurrentTDP[idx];
+ if (ReadTDP != 0)
+ tdpWatchdog.Interval = INTERVAL_DEFAULT;
+ else
+ tdpWatchdog.Interval = INTERVAL_DEGRADED;
- // are we done ?
- TDPdone = CurrentTDP[0] == StoredTDP[0] && CurrentTDP[1] == StoredTDP[1] && CurrentTDP[2] == StoredTDP[2];
+ // only request an update if current limit is different than stored
+ // or a forced update is requested
+ if (ReadTDP != TDP || forcedUpdate)
+ RequestTDP((PowerType)idx, TDP, true);
- // processor specific
- if (processor is IntelProcessor)
- {
- int TDPslow = (int)StoredTDP[(int)PowerType.Slow];
- int TDPfast = (int)StoredTDP[(int)PowerType.Fast];
+ await Task.Delay(20);
+ }
- // only request an update if current limit is different than stored
- if (CurrentTDP[(int)PowerType.MsrSlow] != TDPslow || CurrentTDP[(int)PowerType.MsrFast] != TDPfast)
- ((IntelProcessor)processor).SetMSRLimit(TDPslow, TDPfast);
- else
- MSRdone = true;
- }
+ // are we done ?
+ TDPdone = CurrentTDP[0] == StoredTDP[0] && CurrentTDP[1] == StoredTDP[1] && CurrentTDP[2] == StoredTDP[2];
- // user requested to halt cpu watchdog
- if (cpuWatchdogPendingStop)
- {
- if (cpuWatchdog.Interval == INTERVAL_DEFAULT)
+ // processor specific
+ if (processor is IntelProcessor)
{
- if (TDPdone && MSRdone)
- cpuWatchdog.Stop();
+ double TDPslow = StoredTDP[(int)PowerType.Slow];
+ double TDPfast = StoredTDP[(int)PowerType.Fast];
+
+ if (TDPslow != 0.0d && TDPfast != 0.0d)
+ // only request an update if current limit is different than stored
+ if (CurrentTDP[(int)PowerType.MsrSlow] != TDPslow || CurrentTDP[(int)PowerType.MsrFast] != TDPfast || forcedUpdate)
+ {
+ MSRdone = false;
+ RequestMSR(TDPslow, TDPfast);
+ }
}
- else if (cpuWatchdog.Interval == INTERVAL_DEGRADED)
+
+ // user requested to halt TDP watchdog
+ if (tdpWatchdogPendingStop)
{
- cpuWatchdog.Stop();
+ if (tdpWatchdog.Interval == INTERVAL_DEFAULT)
+ {
+ if (TDPdone && MSRdone)
+ tdpWatchdog.Stop();
+ }
+ else if (tdpWatchdog.Interval == INTERVAL_DEGRADED)
+ {
+ tdpWatchdog.Stop();
+ }
}
}
-
- // release lock
- Exit:
- cpuLock.Exit();
+ finally
+ {
+ // release lock
+ tdpLock.Exit();
+ }
}
}
@@ -577,61 +643,67 @@ private static void gfxWatchdog_Elapsed(object? sender, ElapsedEventArgs e)
if (processor is null || !processor.IsInitialized)
return;
+ GPU GPU = GPUManager.GetCurrent();
+ if (GPU is null || !GPU.IsInitialized)
+ return;
+
if (gfxLock.TryEnter())
{
- bool GPUdone = false;
- GPU GPU = GPUManager.GetCurrent();
- if (GPU is null)
+ try
{
- // release lock
- goto Exit;
- }
+ gfxWatchdogCounter++;
- float CurrentGfxClock = GPUManager.GetCurrent().GetClock();
+ bool GPUdone = true;
+ bool forcedUpdate = false;
- if (CurrentGfxClock != 0)
- gfxWatchdog.Interval = INTERVAL_DEFAULT;
- else
- gfxWatchdog.Interval = INTERVAL_DEGRADED;
+ // not ready yet
+ if (StoredGfxClock == 0)
+ return;
- // not ready yet
- if (StoredGfxClock == 0)
- {
- // release lock
- goto Exit;
- }
+ float CurrentGfxClock = GPUManager.GetCurrent().GetClock();
- // only request an update if current gfx clock is different than stored
- if (CurrentGfxClock != StoredGfxClock)
- {
- // disabling
- if (StoredGfxClock == 12750)
- GPUdone = true;
+ if (CurrentGfxClock != 0)
+ gfxWatchdog.Interval = INTERVAL_DEFAULT;
else
- processor.SetGPUClock(StoredGfxClock);
- }
- else
- {
- GPUdone = true;
- }
+ gfxWatchdog.Interval = INTERVAL_DEGRADED;
- // user requested to halt gpu watchdog
- if (gfxWatchdogPendingStop)
- {
- if (gfxWatchdog.Interval == INTERVAL_DEFAULT)
+ if (gfxWatchdogCounter > COUNTER_DEFAULT)
{
- if (GPUdone)
- gfxWatchdog.Stop();
+ forcedUpdate = true;
+ gfxWatchdogCounter = 0;
+ }
+
+ // only request an update if current gfx clock is different than stored
+ // or a forced update is requested
+ if (CurrentGfxClock != StoredGfxClock || forcedUpdate)
+ {
+ // disabling
+ if (StoredGfxClock != 12750)
+ {
+ GPUdone = false;
+ RequestGPUClock(StoredGfxClock, true);
+ }
}
- else if (gfxWatchdog.Interval == INTERVAL_DEGRADED)
+
+ // user requested to halt gpu watchdog
+ if (gfxWatchdogPendingStop)
{
- gfxWatchdog.Stop();
+ if (gfxWatchdog.Interval == INTERVAL_DEFAULT)
+ {
+ if (GPUdone)
+ gfxWatchdog.Stop();
+ }
+ else if (gfxWatchdog.Interval == INTERVAL_DEGRADED)
+ {
+ gfxWatchdog.Stop();
+ }
}
}
-
- // release lock
- Exit:
- gfxLock.Exit();
+ finally
+ {
+ // release lock
+ gfxLock.Exit();
+ }
}
}
@@ -651,33 +723,34 @@ private static void StopGPUWatchdog(bool immediate = false)
private static void StartTDPWatchdog()
{
- cpuWatchdogPendingStop = false;
- cpuWatchdog.Interval = INTERVAL_DEFAULT;
- cpuWatchdog.Start();
+ tdpWatchdogPendingStop = false;
+ tdpWatchdog.Interval = INTERVAL_DEFAULT;
+ tdpWatchdog.Start();
}
private static void StopTDPWatchdog(bool immediate = false)
{
- cpuWatchdogPendingStop = true;
+ tdpWatchdogPendingStop = true;
if (immediate)
- cpuWatchdog.Stop();
+ tdpWatchdog.Stop();
}
private static void StartAutoTDPWatchdog()
{
- autoWatchdog.Start();
+ autotdpWatchdogPendingStop = false;
+ autotdpWatchdog.Interval = INTERVAL_AUTO;
+ autotdpWatchdog.Start();
}
private static void StopAutoTDPWatchdog(bool immediate = false)
{
- autoWatchdog.Stop();
+ autotdpWatchdogPendingStop = true;
+ if (immediate)
+ autotdpWatchdog.Stop();
}
private static void RequestTDP(PowerType type, double value, bool immediate = false)
{
- if (processor is null || !processor.IsInitialized)
- return;
-
// make sure we're not trying to run below or above specs
value = Math.Min(TDPMax, Math.Max(TDPMin, value));
@@ -685,11 +758,20 @@ private static void RequestTDP(PowerType type, double value, bool immediate = fa
int idx = (int)type;
StoredTDP[idx] = value;
+ if (processor is null || !processor.IsInitialized)
+ return;
+
// immediately apply
if (immediate)
{
- processor.SetTDPLimit((PowerType)idx, value, immediate);
CurrentTDP[idx] = value;
+
+ if (processor is IntelProcessor)
+ // Intel doesn't have stapm
+ if (type == PowerType.Stapm)
+ return;
+
+ processor.SetTDPLimit((PowerType)idx, value, immediate);
}
}
@@ -706,32 +788,64 @@ private static async void RequestTDP(double[] values, bool immediate = false)
}
}
- private static void RequestGPUClock(double value, bool immediate = false)
+ private static void RequestMSR(double PL1, double PL2)
{
if (processor is null || !processor.IsInitialized)
return;
+ if (processor is IntelProcessor)
+ {
+ // make sure we're not trying to run below or above specs
+ double TDPslow = Math.Min(TDPMax, Math.Max(TDPMin, PL1));
+ double TDPfast = Math.Min(TDPMax, Math.Max(TDPMin, PL2));
+
+ CurrentTDP[(int)PowerType.MsrSlow] = TDPslow;
+ CurrentTDP[(int)PowerType.MsrFast] = TDPfast;
+ ((IntelProcessor)processor).SetMSRLimit(TDPslow, TDPfast);
+ }
+ }
+
+ private static void RequestGPUClock(double value, bool immediate = false)
+ {
// update value read by timer
StoredGfxClock = value;
+ if (processor is null || !processor.IsInitialized)
+ return;
+
// immediately apply
if (immediate)
- processor.SetGPUClock(value);
+ {
+ int result = 0;
+ processor.SetGPUClock(StoredGfxClock, ref result);
+
+ if (result != 0)
+ LogManager.LogWarning("Failed to set requested GPU clock: {0}, error code: {1}", StoredGfxClock, result);
+ }
}
private static void RequestPowerMode(Guid guid)
{
currentPowerMode = guid;
+
+ if (PowerGetEffectiveOverlayScheme(out Guid activeScheme) == 0)
+ if (activeScheme == currentPowerMode)
+ return;
+
LogManager.LogDebug("User requested power scheme: {0}", currentPowerMode);
if (PowerSetActiveOverlayScheme(currentPowerMode) != 0)
LogManager.LogWarning("Failed to set requested power scheme: {0}", currentPowerMode);
+ else
+ {
+ int idx = Array.IndexOf(PowerModes, currentPowerMode);
+ if (idx != -1)
+ PowerModeChanged?.Invoke(idx);
+ }
}
private static void RequestEPP(uint EPPOverrideValue)
{
- currentEPP = EPPOverrideValue;
-
var requestedEPP = new uint[2]
{
(uint)Math.Max(0, (int)EPPOverrideValue - 17),
@@ -753,12 +867,12 @@ private static void RequestEPP(uint EPPOverrideValue)
EPP = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP);
if (EPP[0] != requestedEPP[0] || EPP[1] != requestedEPP[1])
LogManager.LogWarning("Failed to set requested EPP");
+ else
+ EPPChanged?.Invoke(EPPOverrideValue);
}
private static void RequestCPUCoreCount(int CoreCount)
{
- currentCoreCount = CoreCount;
-
uint currentCoreCountPercent = (uint)((100.0d / MotherboardInfo.NumberOfCores) * CoreCount);
// Is the CPMINCORES value already correct?
@@ -791,17 +905,26 @@ private static void RequestCPUCoreCount(int CoreCount)
private static void RequestPerfBoostMode(uint value)
{
- currentPerfBoostMode = value;
+ // Is the PerfBoostMode value already correct?
+ uint[] perfBoostMode = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFBOOSTMODE);
+ bool IsReady = (perfBoostMode[0] == value && perfBoostMode[1] == value);
+ if (IsReady)
+ return;
+
+ // Set profile PerfBoostMode
PowerScheme.WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFBOOSTMODE, value, value);
LogManager.LogDebug("User requested perfboostmode: {0}", value);
+
+ // Has the value been applied?
+ perfBoostMode = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFBOOSTMODE);
+ if (perfBoostMode[0] != value || perfBoostMode[1] != value)
+ LogManager.LogWarning("Failed to set requested perfboostmode");
}
private static void RequestCPUClock(uint cpuClock)
{
- double maxClock = MotherboardInfo.ProcessorMaxTurboSpeed;
-
// Is the PROCFREQMAX value already correct?
uint[] currentClock = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PROCFREQMAX);
bool IsReady = (currentClock[0] == cpuClock && currentClock[1] == cpuClock);
@@ -809,8 +932,11 @@ private static void RequestCPUClock(uint cpuClock)
if (IsReady)
return;
+ // Set profile max processor frequency
PowerScheme.WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PROCFREQMAX, cpuClock, cpuClock);
+ PowerScheme.WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PROCFREQMAX1, cpuClock, cpuClock);
+ double maxClock = MotherboardInfo.ProcessorMaxTurboSpeed;
double cpuPercentage = cpuClock / maxClock * 100.0d;
LogManager.LogDebug("User requested PROCFREQMAX: {0} ({1}%)", cpuClock, cpuPercentage);
@@ -823,7 +949,7 @@ private static void RequestCPUClock(uint cpuClock)
public static void Start()
{
// initialize watchdog(s)
- powerWatchdog.Start();
+ cpuWatchdog.Start();
// initialize processor
processor = Processor.GetCurrent();
@@ -852,14 +978,14 @@ public static void Stop()
if (processor is not null && processor.IsInitialized)
processor.Stop();
- powerWatchdog.Stop();
- cpuWatchdog.Stop();
+ autotdpWatchdog.Stop();
+ tdpWatchdog.Stop();
gfxWatchdog.Stop();
- autoWatchdog.Stop();
+ cpuWatchdog.Stop();
IsInitialized = false;
- LogManager.LogInformation("{0} has started", "PerformanceManager");
+ LogManager.LogInformation("{0} has stopped", "PerformanceManager");
}
public static void Resume(bool OS)
diff --git a/HandheldCompanion/Managers/PlatformManager.cs b/HandheldCompanion/Managers/PlatformManager.cs
index 96416fd9e..b934a5c02 100644
--- a/HandheldCompanion/Managers/PlatformManager.cs
+++ b/HandheldCompanion/Managers/PlatformManager.cs
@@ -9,8 +9,6 @@ namespace HandheldCompanion.Managers;
public static class PlatformManager
{
- private const int UpdateInterval = 1000;
-
// gaming platforms
public static readonly Steam Steam = new();
public static readonly GOGGalaxy GOGGalaxy = new();
@@ -20,7 +18,8 @@ public static class PlatformManager
public static RTSS RTSS = new();
public static Platforms.LibreHardwareMonitor LibreHardwareMonitor = new();
- private static Timer UpdateTimer;
+ private const int UpdateInterval = 1000;
+ private static Timer UpdateTimer = new() { Interval = UpdateInterval, AutoReset = false };
private static bool IsInitialized;
@@ -52,18 +51,14 @@ public static void Start()
if (LibreHardwareMonitor.IsInstalled)
LibreHardwareMonitor.Start();
+
+ UpdateTimer.Elapsed += (sender, e) => MonitorPlatforms();
+ UpdateTimer.Start();
SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
ProfileManager.Applied += ProfileManager_Applied;
PowerProfileManager.Applied += PowerProfileManager_Applied;
- UpdateTimer = new Timer(UpdateInterval)
- {
- AutoReset = false
- };
- UpdateTimer.Elapsed += (sender, e) => MonitorPlatforms();
- UpdateTimer.Start();
-
IsInitialized = true;
Initialized?.Invoke();
@@ -94,7 +89,7 @@ private static void ProfileManager_Applied(Profile profile, UpdateSource source)
UpdateTimer.Start();
}
- private static void SettingsManager_SettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
// UI thread
Application.Current.Dispatcher.Invoke(() =>
@@ -206,6 +201,10 @@ public static void Stop()
if (LibreHardwareMonitor.IsInstalled)
LibreHardwareMonitor.Stop();
+ SettingsManager.SettingValueChanged -= SettingsManager_SettingValueChanged;
+ ProfileManager.Applied -= ProfileManager_Applied;
+ PowerProfileManager.Applied -= PowerProfileManager_Applied;
+
IsInitialized = false;
LogManager.LogInformation("{0} has stopped", "PlatformManager");
diff --git a/HandheldCompanion/Managers/PowerProfileManager.cs b/HandheldCompanion/Managers/PowerProfileManager.cs
index f235f9fe7..a44a12399 100644
--- a/HandheldCompanion/Managers/PowerProfileManager.cs
+++ b/HandheldCompanion/Managers/PowerProfileManager.cs
@@ -206,13 +206,13 @@ public static PowerProfile GetProfile(Guid guid)
private static bool HasDefault()
{
- return profiles.Values.Count(a => a.Default) != 0;
+ return profiles.Values.Count(a => a.Default && a.Guid == Guid.Empty) != 0;
}
public static PowerProfile GetDefault()
{
if (HasDefault())
- return profiles.Values.FirstOrDefault(a => a.Default);
+ return profiles.Values.FirstOrDefault(a => a.Default && a.Guid == Guid.Empty);
return new PowerProfile();
}
diff --git a/HandheldCompanion/Managers/SensorsManager.cs b/HandheldCompanion/Managers/SensorsManager.cs
index 9ccfa7515..da49ea7f5 100644
--- a/HandheldCompanion/Managers/SensorsManager.cs
+++ b/HandheldCompanion/Managers/SensorsManager.cs
@@ -6,6 +6,7 @@
using HandheldCompanion.Views;
using Nefarius.Utilities.DeviceManagement.PnP;
using System;
+using System.Collections.Generic;
using System.Numerics;
using System.Threading.Tasks;
using static HandheldCompanion.Utils.DeviceUtils;
@@ -95,7 +96,7 @@ private static void DeviceManager_UsbDeviceArrived(PnPDevice device, DeviceEvent
SettingsManager.SetProperty("SensorSelection", (int)SensorFamily.SerialUSBIMU);
}
- private static void SettingsManager_SettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
@@ -244,6 +245,11 @@ public static void SetSensorFamily(SensorFamily sensorFamily)
}
public static async void Calibrate(GamepadMotion gamepadMotion)
+ {
+ Calibrate(new Dictionary { { 0, gamepadMotion } });
+ }
+
+ public static async void Calibrate(Dictionary gamepadMotions)
{
Dialog dialog = new Dialog(MainWindow.GetCurrent())
{
@@ -255,8 +261,8 @@ public static async void Calibrate(GamepadMotion gamepadMotion)
// display calibration dialog
dialog.Show();
- // skip if null
- if (gamepadMotion is null)
+ // skip if empty
+ if (gamepadMotions.Count == 0)
goto Close;
for (int i = 4; i > 0; i--)
@@ -265,83 +271,91 @@ public static async void Calibrate(GamepadMotion gamepadMotion)
await Task.Delay(1000);
}
- dialog.UpdateContent("Calibrating stationary sensor noise and drift correction...");
+ foreach (GamepadMotion gamepadMotion in gamepadMotions.Values)
+ {
+ dialog.UpdateContent($"Calibrating {gamepadMotion.deviceInstanceId} stationary sensor noise and drift correction...");
- // reset motion values
- gamepadMotion.ResetMotion();
+ // reset motion values
+ gamepadMotion.ResetMotion();
- // wait until device is steady
- DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(3));
- while (DateTime.Now < timeout && !gamepadMotion.GetAutoCalibrationIsSteady())
- await Task.Delay(100);
+ // wait until device is steady
+ DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(3));
+ while (DateTime.Now < timeout && !gamepadMotion.GetAutoCalibrationIsSteady())
+ await Task.Delay(100);
- // device is either too shaky or stalled
- bool IsSteady = gamepadMotion.GetAutoCalibrationIsSteady();
- if (!IsSteady)
- {
- gamepadMotion.GetCalibratedGyro(out float x, out float y, out float z);
+ // device is either too shaky or stalled
+ bool IsSteady = gamepadMotion.GetAutoCalibrationIsSteady();
+ if (!IsSteady)
+ {
+ gamepadMotion.GetCalibratedGyro(out float x, out float y, out float z);
- // display message
- if (x == 0 && y == 0 && z == 0)
- dialog.UpdateContent("Calibration failed: device is silent.");
- else
- dialog.UpdateContent("Calibration failed: device is silent or unsteady.");
+ // display message
+ if (x == 0 && y == 0 && z == 0)
+ dialog.UpdateContent($"Calibration failed: device is silent.");
+ else
+ dialog.UpdateContent($"Calibration device is silent or unsteady.");
- goto Close;
- }
+ // wait a bit
+ await Task.Delay(2000);
- // start continuous calibration
- gamepadMotion.StartContinuousCalibration();
+ break;
+ }
- // give gamepad motion 3 seconds to get values
- timeout = DateTime.Now.Add(TimeSpan.FromSeconds(3));
- while (DateTime.Now < timeout)
- await Task.Delay(100);
+ // start continuous calibration
+ gamepadMotion.StartContinuousCalibration();
- // halt continuous calibration
- gamepadMotion.PauseContinuousCalibration();
+ // give gamepad motion 3 seconds to get values
+ timeout = DateTime.Now.Add(TimeSpan.FromSeconds(3));
+ while (DateTime.Now < timeout)
+ await Task.Delay(100);
- // get continuous calibration confidence
- float confidence = gamepadMotion.GetAutoCalibrationConfidence();
+ // halt continuous calibration
+ gamepadMotion.PauseContinuousCalibration();
- // get/set calibration offsets
- gamepadMotion.GetCalibrationOffset(out float xOffset, out float yOffset, out float zOffset);
- gamepadMotion.SetCalibrationOffset(xOffset, yOffset, zOffset, (int)(confidence * 10.0f));
+ // get continuous calibration confidence
+ float confidence = gamepadMotion.GetAutoCalibrationConfidence();
- /*
- dialog.UpdateTitle("Please take back the controller in hands and get ready to shake it.");
+ // get/set calibration offsets
+ gamepadMotion.GetCalibrationOffset(out float xOffset, out float yOffset, out float zOffset);
+ gamepadMotion.SetCalibrationOffset(xOffset, yOffset, zOffset, (int)(confidence * 10.0f));
- for (int i = 4; i > 0; i--)
- {
- dialog.UpdateContent($"Threshold calibration will start in {i} seconds.");
- await Task.Delay(1000);
- }
+ /*
+ dialog.UpdateTitle("Please take back the controller in hands and get ready to shake it.");
- dialog.UpdateContent("Shake the device in all direction...");
+ for (int i = 4; i > 0; i--)
+ {
+ dialog.UpdateContent($"Threshold calibration will start in {i} seconds.");
+ await Task.Delay(1000);
+ }
- // reset motion values
- gamepadMotion.ResetThresholdCalibration();
- gamepadMotion.StartThresholdCalibration();
+ dialog.UpdateContent("Shake the device in all direction...");
- // wait until device is steady
- timeout = DateTime.Now.Add(TimeSpan.FromSeconds(3));
- while (DateTime.Now < timeout)
- await Task.Delay(100);
+ // reset motion values
+ gamepadMotion.ResetThresholdCalibration();
+ gamepadMotion.StartThresholdCalibration();
- gamepadMotion.PauseThresholdCalibration();
+ // wait until device is steady
+ timeout = DateTime.Now.Add(TimeSpan.FromSeconds(3));
+ while (DateTime.Now < timeout)
+ await Task.Delay(100);
- // get calibration offsets
- gamepadMotion.SetCalibrationThreshold(gamepadMotion.maxGyro, gamepadMotion.maxAccel);
- */
+ gamepadMotion.PauseThresholdCalibration();
- // store calibration offsets
- IMUCalibration.StoreCalibration(gamepadMotion.deviceInstanceId, gamepadMotion.GetCalibration());
+ // get calibration offsets
+ gamepadMotion.SetCalibrationThreshold(gamepadMotion.maxGyro, gamepadMotion.maxAccel);
+ */
- // display message
- dialog.UpdateContent($"Calibration succeeded: stationary sensor noise recorded. Drift correction found. Confidence: {confidence * 100.0f}%");
+ // store calibration offsets
+ IMUCalibration.StoreCalibration(gamepadMotion.deviceInstanceId, gamepadMotion.GetCalibration());
+
+ // display message
+ dialog.UpdateContent($"Calibration succeeded: stationary sensor noise recorded. Drift correction found. Confidence: {confidence * 100.0f}%");
+
+ // wait a bit
+ await Task.Delay(2000);
+ }
Close:
- await Task.Delay(2000);
dialog.Hide();
}
}
diff --git a/HandheldCompanion/Managers/SettingsManager.cs b/HandheldCompanion/Managers/SettingsManager.cs
index fa77eeaee..219f5fcc2 100644
--- a/HandheldCompanion/Managers/SettingsManager.cs
+++ b/HandheldCompanion/Managers/SettingsManager.cs
@@ -32,7 +32,7 @@ public static class SettingsManager
{
public delegate void InitializedEventHandler();
- public delegate void SettingValueChangedEventHandler(string name, object value);
+ public delegate void SettingValueChangedEventHandler(string name, object value, bool temporary);
private static readonly Dictionary Settings = [];
@@ -55,7 +55,7 @@ public static void Start()
.OrderBy(s => s.Name);
foreach (var property in properties)
- SettingValueChanged(property.Name, GetProperty(property.Name));
+ SettingValueChanged(property.Name, GetProperty(property.Name), false);
if (GetBoolean("FirstStart"))
SetProperty("FirstStart", false);
@@ -117,7 +117,7 @@ public static void SetProperty(string name, object value, bool force = false, bo
Settings[name] = value;
// raise event
- SettingValueChanged?.Invoke(name, value);
+ SettingValueChanged?.Invoke(name, value, temporary);
LogManager.LogDebug("Settings {0} set to {1}", name, value);
}
diff --git a/HandheldCompanion/Managers/SystemManager.cs b/HandheldCompanion/Managers/SystemManager.cs
index 02a76d09c..823702665 100644
--- a/HandheldCompanion/Managers/SystemManager.cs
+++ b/HandheldCompanion/Managers/SystemManager.cs
@@ -9,10 +9,28 @@ namespace HandheldCompanion.Managers;
public static class SystemManager
{
- // Import SetThreadExecutionState Win32 API and define flags
+ #region PInvoke
+
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint SetThreadExecutionState(uint esFlags);
+ [DllImport("user32.dll", SetLastError = true)]
+ private static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, uint dwDesiredAccess);
+
+ #endregion
+
+ #region Events
+
+ public static event SystemStatusChangedEventHandler SystemStatusChanged;
+ public static event PowerStatusChangedEventHandler PowerStatusChanged;
+ public static event InitializedEventHandler Initialized;
+
+ public delegate void SystemStatusChangedEventHandler(SystemStatus status, SystemStatus prevStatus);
+ public delegate void PowerStatusChangedEventHandler(PowerStatus status);
+ public delegate void InitializedEventHandler();
+
+ #endregion
+
public const uint ES_CONTINUOUS = 0x80000000;
public const uint ES_SYSTEM_REQUIRED = 0x00000001;
@@ -73,6 +91,11 @@ public enum SystemStatus
static SystemManager()
{
// listen to system events
+ SubscribeToSystemEvents();
+ }
+
+ private static void SubscribeToSystemEvents()
+ {
SystemEvents.PowerModeChanged += OnPowerChange;
SystemEvents.SessionSwitch += OnSessionSwitch;
@@ -83,12 +106,17 @@ static SystemManager()
SystemPowerManager.RemainingDischargeTimeChanged += BatteryStatusChanged;
}
- #region import
-
- [DllImport("user32.dll", SetLastError = true)]
- private static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, uint dwDesiredAccess);
+ private static void UnsubscribeFromSystemEvents()
+ {
+ SystemEvents.PowerModeChanged -= OnPowerChange;
+ SystemEvents.SessionSwitch -= OnSessionSwitch;
- #endregion
+ SystemPowerManager.BatteryStatusChanged -= BatteryStatusChanged;
+ SystemPowerManager.EnergySaverStatusChanged -= BatteryStatusChanged;
+ SystemPowerManager.PowerSupplyStatusChanged -= BatteryStatusChanged;
+ SystemPowerManager.RemainingChargePercentChanged -= BatteryStatusChanged;
+ SystemPowerManager.RemainingDischargeTimeChanged -= BatteryStatusChanged;
+ }
private static void BatteryStatusChanged(object sender, object e)
{
@@ -97,11 +125,13 @@ private static void BatteryStatusChanged(object sender, object e)
public static void Start()
{
- // check if current session is locked
- var handle = OpenInputDesktop(0, false, 0);
- IsSessionLocked = handle == IntPtr.Zero;
+ if (IsInitialized)
+ return;
- SystemRoutine();
+ // Check if current session is locked
+ IsSessionLocked = OpenInputDesktop(0, false, 0) == IntPtr.Zero;
+
+ PerformSystemRoutine();
IsInitialized = true;
Initialized?.Invoke();
@@ -119,8 +149,7 @@ public static void Stop()
IsInitialized = false;
// stop listening to system events
- SystemEvents.PowerModeChanged -= OnPowerChange;
- SystemEvents.SessionSwitch -= OnSessionSwitch;
+ UnsubscribeFromSystemEvents();
LogManager.LogInformation("{0} has stopped", "PowerManager");
}
@@ -132,13 +161,16 @@ private static void OnPowerChange(object s, PowerModeChangedEventArgs e)
case PowerModes.Resume:
IsPowerSuspended = false;
break;
+
case PowerModes.Suspend:
IsPowerSuspended = true;
// Prevent system sleep
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
+
LogManager.LogDebug("System is trying to suspend. Performing tasks...");
break;
+
default:
case PowerModes.StatusChange:
PowerStatusChanged?.Invoke(SystemInformation.PowerStatus);
@@ -147,7 +179,7 @@ private static void OnPowerChange(object s, PowerModeChangedEventArgs e)
LogManager.LogDebug("Device power mode set to {0}", e.Mode);
- SystemRoutine();
+ PerformSystemRoutine();
}
private static void OnSessionSwitch(object sender, SessionSwitchEventArgs e)
@@ -166,10 +198,10 @@ private static void OnSessionSwitch(object sender, SessionSwitchEventArgs e)
LogManager.LogDebug("Session switched to {0}", e.Reason);
- SystemRoutine();
+ PerformSystemRoutine();
}
- private static void SystemRoutine()
+ private static void PerformSystemRoutine()
{
if (!IsPowerSuspended && !IsSessionLocked)
currentSystemStatus = SystemStatus.SystemReady;
@@ -177,28 +209,12 @@ private static void SystemRoutine()
currentSystemStatus = SystemStatus.SystemPending;
// only raise event is system status has changed
- if (previousSystemStatus != currentSystemStatus)
- {
- LogManager.LogInformation("System status set to {0}", currentSystemStatus);
- SystemStatusChanged?.Invoke(currentSystemStatus, previousSystemStatus);
-
- previousSystemStatus = currentSystemStatus;
- }
- }
-
- #region events
-
- public static event SystemStatusChangedEventHandler SystemStatusChanged;
-
- public delegate void SystemStatusChangedEventHandler(SystemStatus status, SystemStatus prevStatus);
-
- public static event PowerStatusChangedEventHandler PowerStatusChanged;
-
- public delegate void PowerStatusChangedEventHandler(PowerStatus status);
-
- public static event InitializedEventHandler Initialized;
+ if (previousSystemStatus == currentSystemStatus)
+ return;
- public delegate void InitializedEventHandler();
+ LogManager.LogInformation("System status set to {0}", currentSystemStatus);
+ SystemStatusChanged?.Invoke(currentSystemStatus, previousSystemStatus);
- #endregion
+ previousSystemStatus = currentSystemStatus;
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Managers/TaskManager.cs b/HandheldCompanion/Managers/TaskManager.cs
index f3a06d8ad..14cbb1ed1 100644
--- a/HandheldCompanion/Managers/TaskManager.cs
+++ b/HandheldCompanion/Managers/TaskManager.cs
@@ -24,7 +24,7 @@ static TaskManager()
SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
}
- private static void SettingsManager_SettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/Managers/UpdateManager.cs b/HandheldCompanion/Managers/UpdateManager.cs
index 341d192ba..37217db07 100644
--- a/HandheldCompanion/Managers/UpdateManager.cs
+++ b/HandheldCompanion/Managers/UpdateManager.cs
@@ -74,7 +74,7 @@ static UpdateManager()
SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
}
- private static void SettingsManager_SettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/Managers/VirtualManager.cs b/HandheldCompanion/Managers/VirtualManager.cs
index 4f383b3a1..fe701cd52 100644
--- a/HandheldCompanion/Managers/VirtualManager.cs
+++ b/HandheldCompanion/Managers/VirtualManager.cs
@@ -18,8 +18,6 @@ public static class VirtualManager
public static ViGEmClient vClient;
public static ViGEmTarget vTarget;
- private static DSUServer DSUServer;
-
// settings vars
public static HIDmode HIDmode = HIDmode.NoController;
private static HIDmode defaultHIDmode = HIDmode.NoController;
@@ -59,9 +57,6 @@ static VirtualManager()
MessageBox.Show("Unable to start Handheld Companion, the ViGEm application is missing.\n\nPlease get it from: https://github.com/ViGEm/ViGEmBus/releases", "Error");
throw new InvalidOperationException();
}
-
- // initialize DSUClient
- DSUServer = new DSUServer();
}
public static void Start()
@@ -139,7 +134,7 @@ public static void Suspend(bool OS)
}
}
- private static void SettingsManager_SettingValueChanged(string name, object value)
+ private static void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
@@ -322,11 +317,7 @@ private static void OnTargetVibrated(byte LargeMotor, byte SmallMotor)
public static void UpdateInputs(ControllerState controllerState, GamepadMotion gamepadMotion)
{
- // DS4Touch is used by both targets below, update first
- DS4Touch.UpdateInputs(controllerState);
-
vTarget?.UpdateInputs(controllerState, gamepadMotion);
- DSUServer?.UpdateInputs(controllerState);
}
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Misc/Hotkey.cs b/HandheldCompanion/Misc/Hotkey.cs
index 4f05196b7..97b44ecc0 100644
--- a/HandheldCompanion/Misc/Hotkey.cs
+++ b/HandheldCompanion/Misc/Hotkey.cs
@@ -4,6 +4,7 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
+using System.Data;
using System.Linq;
using WindowsInput.Events;
@@ -58,5 +59,14 @@ public object Clone()
return hotkey;
}
+
+ public void Execute(bool onKeyDown, bool onKeyUp, bool IsBackground)
+ {
+ bool Rumble = SettingsManager.GetBoolean("HotkeyRumbleOnExecution");
+ if (Rumble && !IsBackground && !IsInternal)
+ ControllerManager.GetTargetController()?.Rumble();
+
+ command?.Execute(command.OnKeyDown, command.OnKeyUp, IsBackground);
+ }
}
}
diff --git a/HandheldCompanion/Misc/JoyShockLibrary.cs b/HandheldCompanion/Misc/JoyShockLibrary.cs
index 2895233b6..045894eb9 100644
--- a/HandheldCompanion/Misc/JoyShockLibrary.cs
+++ b/HandheldCompanion/Misc/JoyShockLibrary.cs
@@ -1,4 +1,7 @@
-using System.Runtime.InteropServices;
+using HandheldCompanion.Managers;
+using System;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
public static class JSL
{
@@ -98,8 +101,20 @@ public delegate void EventCallback(int handle, JOY_SHOCK_STATE state, JOY_SHOCK_
public static extern int JslConnectDevices();
[DllImport("JoyShockLibrary")]
public static extern int JslGetConnectedDeviceHandles(int[] deviceHandleArray, int size);
+
[DllImport("JoyShockLibrary")]
public static extern void JslDisconnectAndDisposeAll();
+
+ public static void JslDisconnect()
+ {
+ // Flushing possible JoyShocks...
+ Task jslTask = Task.Run(() => JslDisconnectAndDisposeAll());
+
+ bool completedInTime = jslTask.Wait(TimeSpan.FromSeconds(2));
+ if (!completedInTime)
+ LogManager.LogWarning("JslDisconnectAndDisposeAll() timed out.");
+ }
+
[DllImport("JoyShockLibrary")]
public static extern bool JslStillConnected(int deviceId);
[DllImport("JoyShockLibrary")]
diff --git a/HandheldCompanion/Misc/PnPDetails.cs b/HandheldCompanion/Misc/PnPDetails.cs
index 5f69b902a..287d3c85c 100644
--- a/HandheldCompanion/Misc/PnPDetails.cs
+++ b/HandheldCompanion/Misc/PnPDetails.cs
@@ -20,6 +20,7 @@ public class PnPDetails
public bool isPhysical => !isVirtual;
public bool isBluetooth => EnumeratorName.Equals("BTHENUM");
public bool isUSB => EnumeratorName.Equals("USB");
+ public bool isDongle = false;
public string devicePath = string.Empty;
public string baseContainerDevicePath = string.Empty;
diff --git a/HandheldCompanion/Misc/PowerProfile.cs b/HandheldCompanion/Misc/PowerProfile.cs
index 8f10745b0..fdc5aa1c1 100644
--- a/HandheldCompanion/Misc/PowerProfile.cs
+++ b/HandheldCompanion/Misc/PowerProfile.cs
@@ -66,7 +66,7 @@ public string GetFileName()
public bool IsDefault()
{
- return Default;
+ return Default && Guid == Guid.Empty;
}
public override string ToString()
diff --git a/HandheldCompanion/Misc/PowerScheme.cs b/HandheldCompanion/Misc/PowerScheme.cs
index e55f42e63..be26c6bd1 100644
--- a/HandheldCompanion/Misc/PowerScheme.cs
+++ b/HandheldCompanion/Misc/PowerScheme.cs
@@ -25,6 +25,10 @@ public static Guid
PROCFREQMAX =
new("75b0ae3f-bce0-45a7-8c89-c9611c25e100"); // Maximum processor frequency in MHz, 0 for no limit (default)
+ public static Guid
+ PROCFREQMAX1 =
+ new("75b0ae3f-bce0-45a7-8c89-c9611c25e101"); // Maximum processor frequency for processor power efficiency class 1 in MHz, 0 for no limit (default)
+
public static Guid
CPMINCORES =
new("0cc5b647-c1df-4637-891a-dec35c318583"); // Processor performance core parking min cores, expressed as a percent from 0 - 100
@@ -121,9 +125,9 @@ public static bool SetValue(PowerIndexType powerType, Guid SchemeGuid, Guid SubG
return false;
}
- public static bool SetAttribute(Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, bool hide)
+ public static bool SetAttribute(Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, uint value)
{
- return PowerWriteSettingAttributes(SubGroupOfPowerSettingsGuid, PowerSettingGuid, (uint)(hide ? 1 : 0)) == 0;
+ return PowerWriteSettingAttributes(SubGroupOfPowerSettingsGuid, PowerSettingGuid, value) == 0;
}
public static uint[] ReadPowerCfg(Guid SubGroup, Guid Settings)
@@ -147,7 +151,7 @@ public static void WritePowerCfg(Guid SubGroup, Guid Settings, uint ACValue, uin
if (GetActiveScheme(out var currentScheme))
{
// unhide attribute
- SetAttribute(SubGroup, Settings, false);
+ SetAttribute(SubGroup, Settings, 2);
// set value(s)
SetValue(PowerIndexType.AC, currentScheme, SubGroup, Settings, ACValue);
diff --git a/HandheldCompanion/Misc/Profile.cs b/HandheldCompanion/Misc/Profile.cs
index 6a44b1d13..3049dec62 100644
--- a/HandheldCompanion/Misc/Profile.cs
+++ b/HandheldCompanion/Misc/Profile.cs
@@ -49,19 +49,20 @@ public partial class Profile : ICloneable, IComparable
public string Path { get; set; } = string.Empty;
public string Arguments { get; set; } = string.Empty;
- public bool IsSubProfile { get; set; } = false;
- public bool IsFavoriteSubProfile { get; set; } = false;
+ public bool IsSubProfile { get; set; }
+ public bool IsFavoriteSubProfile { get; set; }
public Guid Guid { get; set; } = Guid.NewGuid();
public string Executable { get; set; } = string.Empty;
public bool Enabled { get; set; }
+ public bool IsPinned { get; set; } = true;
public bool Default { get; set; }
public Version Version { get; set; } = new();
public string LayoutTitle { get; set; } = string.Empty;
- public bool LayoutEnabled { get; set; } = false;
+ public bool LayoutEnabled { get; set; }
public Layout Layout { get; set; } = new();
public bool Whitelisted { get; set; } // if true, can see through the HidHide cloak
@@ -101,16 +102,16 @@ public partial class Profile : ICloneable, IComparable
};
public int FramerateValue { get; set; } = 0; // default RTSS value
- public bool GPUScaling { get; set; } = false;
+ public bool GPUScaling { get; set; }
public int ScalingMode { get; set; } = 0; // default AMD value
- public bool RSREnabled { get; set; } = false;
+ public bool RSREnabled { get; set; }
public int RSRSharpness { get; set; } = 20; // default AMD value
- public bool IntegerScalingEnabled { get; set; } = false;
+ public bool IntegerScalingEnabled { get; set; }
public int IntegerScalingDivider { get; set; } = 1;
public byte IntegerScalingType { get; set; } = 0;
- public bool RISEnabled { get; set; } = false;
+ public bool RISEnabled { get; set; }
public int RISSharpness { get; set; } = 80; // default AMD value
- public bool AFMFEnabled { get; set; } = false;
+ public bool AFMFEnabled { get; set; }
// AppCompatFlags
public bool FullScreenOptimization { get; set; } = true;
diff --git a/HandheldCompanion/Platforms/LibreHardwareMonitor.cs b/HandheldCompanion/Platforms/LibreHardwareMonitor.cs
index b73d02530..3e8f5011c 100644
--- a/HandheldCompanion/Platforms/LibreHardwareMonitor.cs
+++ b/HandheldCompanion/Platforms/LibreHardwareMonitor.cs
@@ -47,7 +47,7 @@ public LibreHardwareMonitor()
SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/Platforms/RTSS.cs b/HandheldCompanion/Platforms/RTSS.cs
index 021d1d8ef..1d0c80a06 100644
--- a/HandheldCompanion/Platforms/RTSS.cs
+++ b/HandheldCompanion/Platforms/RTSS.cs
@@ -246,7 +246,11 @@ public void RefreshAppEntry()
{
// refresh appEntry
int processId = appEntry is not null ? appEntry.ProcessId : 0;
- appEntry = OSD.GetAppEntries().Where(x => (x.Flags & AppFlags.MASK) != AppFlags.None).FirstOrDefault(a => a.ProcessId == processId);
+ try
+ {
+ appEntry = OSD.GetAppEntries().Where(x => (x.Flags & AppFlags.MASK) != AppFlags.None).FirstOrDefault(a => a.ProcessId == processId);
+ }
+ catch (FileNotFoundException) { }
}
public double GetFramerate(bool refresh = false)
diff --git a/HandheldCompanion/Processors/AMDProcessor.cs b/HandheldCompanion/Processors/AMDProcessor.cs
index 036acdbec..dc23ac291 100644
--- a/HandheldCompanion/Processors/AMDProcessor.cs
+++ b/HandheldCompanion/Processors/AMDProcessor.cs
@@ -114,7 +114,7 @@ public override void SetTDPLimit(PowerType type, double limit, bool immediate, i
}
}
- public override void SetGPUClock(double clock, int result)
+ public override void SetGPUClock(double clock, ref int result)
{
lock (updateLock)
{
@@ -147,8 +147,8 @@ public override void SetGPUClock(double clock, int result)
if (clock == 12750)
return;
- int error = RyzenAdj.set_gfx_clk(ry, (uint)clock);
- base.SetGPUClock(clock, error);
+ result = RyzenAdj.set_gfx_clk(ry, (uint)clock);
+ base.SetGPUClock(clock, ref result);
}
break;
}
diff --git a/HandheldCompanion/Processors/IntelProcessor.cs b/HandheldCompanion/Processors/IntelProcessor.cs
index 134c16b63..2751cb7f0 100644
--- a/HandheldCompanion/Processors/IntelProcessor.cs
+++ b/HandheldCompanion/Processors/IntelProcessor.cs
@@ -65,13 +65,13 @@ public void SetMSRLimit(double PL1, double PL2)
platform.set_msr_limits((int)PL1, (int)PL2);
}
- public override void SetGPUClock(double clock, int result)
+ public override void SetGPUClock(double clock, ref int result)
{
lock (updateLock)
{
- var error = platform.set_gfx_clk((int)clock);
+ result = platform.set_gfx_clk((int)clock);
- base.SetGPUClock(clock, error);
+ base.SetGPUClock(clock, ref result);
}
}
}
\ No newline at end of file
diff --git a/HandheldCompanion/Processors/Processor.cs b/HandheldCompanion/Processors/Processor.cs
index 9d78751fe..46d8cc626 100644
--- a/HandheldCompanion/Processors/Processor.cs
+++ b/HandheldCompanion/Processors/Processor.cs
@@ -69,7 +69,7 @@ public virtual void SetTDPLimit(PowerType type, double limit, bool immediate = f
LogManager.LogDebug("User requested {0} TDP limit: {1}, error code: {2}", type, (uint)limit, result);
}
- public virtual void SetGPUClock(double clock, int result = 0)
+ public virtual void SetGPUClock(double clock, ref int result)
{
/*
* #define ADJ_ERR_FAM_UNSUPPORTED -1
diff --git a/HandheldCompanion/Properties/Resources.Designer.cs b/HandheldCompanion/Properties/Resources.Designer.cs
index d5aa5c937..0cb12065b 100644
--- a/HandheldCompanion/Properties/Resources.Designer.cs
+++ b/HandheldCompanion/Properties/Resources.Designer.cs
@@ -3255,6 +3255,78 @@ public static string Enum_SteamDeck_ButtonFlags_OEM1 {
}
}
+ ///
+ /// Looks up a localized string similar to T2.
+ ///
+ public static string Enum_TatantulaProController_ButtonFlags_B10 {
+ get {
+ return ResourceManager.GetString("Enum_TatantulaProController_ButtonFlags_B10", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to T3.
+ ///
+ public static string Enum_TatantulaProController_ButtonFlags_B11 {
+ get {
+ return ResourceManager.GetString("Enum_TatantulaProController_ButtonFlags_B11", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to C1.
+ ///
+ public static string Enum_TatantulaProController_ButtonFlags_B5 {
+ get {
+ return ResourceManager.GetString("Enum_TatantulaProController_ButtonFlags_B5", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to C2.
+ ///
+ public static string Enum_TatantulaProController_ButtonFlags_B6 {
+ get {
+ return ResourceManager.GetString("Enum_TatantulaProController_ButtonFlags_B6", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to C3.
+ ///
+ public static string Enum_TatantulaProController_ButtonFlags_B7 {
+ get {
+ return ResourceManager.GetString("Enum_TatantulaProController_ButtonFlags_B7", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to C4.
+ ///
+ public static string Enum_TatantulaProController_ButtonFlags_B8 {
+ get {
+ return ResourceManager.GetString("Enum_TatantulaProController_ButtonFlags_B8", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to T1.
+ ///
+ public static string Enum_TatantulaProController_ButtonFlags_B9 {
+ get {
+ return ResourceManager.GetString("Enum_TatantulaProController_ButtonFlags_B9", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to M.
+ ///
+ public static string Enum_TatantulaProController_ButtonFlags_L5 {
+ get {
+ return ResourceManager.GetString("Enum_TatantulaProController_ButtonFlags_L5", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Gyroscope.
///
@@ -4182,6 +4254,24 @@ public static string Hotkey_FunctionDesc {
}
}
+ ///
+ /// Looks up a localized string similar to Open Game Bar.
+ ///
+ public static string Hotkey_GameBar {
+ get {
+ return ResourceManager.GetString("Hotkey_GameBar", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Press this key: Windows + G.
+ ///
+ public static string Hotkey_GameBarDesc {
+ get {
+ return ResourceManager.GetString("Hotkey_GameBarDesc", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Guide or PS button.
///
@@ -4344,6 +4434,24 @@ public static string Hotkey_MainwindowDesc {
}
}
+ ///
+ /// Looks up a localized string similar to Mute volume.
+ ///
+ public static string Hotkey_muteVolume {
+ get {
+ return ResourceManager.GetString("Hotkey_muteVolume", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Mute/unmute volume.
+ ///
+ public static string Hotkey_muteVolumeDesc {
+ get {
+ return ResourceManager.GetString("Hotkey_muteVolumeDesc", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to None.
///
@@ -4524,6 +4632,24 @@ public static string Hotkey_suspendResumeTaskDesc {
}
}
+ ///
+ /// Looks up a localized string similar to Swap foreground window.
+ ///
+ public static string Hotkey_SwapScreen {
+ get {
+ return ResourceManager.GetString("Hotkey_SwapScreen", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Move the foreground window to next available screen.
+ ///
+ public static string Hotkey_SwapScreenDesc {
+ get {
+ return ResourceManager.GetString("Hotkey_SwapScreenDesc", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Open Task Manager.
///
@@ -6798,7 +6924,7 @@ public static string ProfilesPage_EmulatedControllerXbox {
}
///
- /// Looks up a localized string similar to User per-game profile.
+ /// Looks up a localized string similar to Use per-game profile.
///
public static string ProfilesPage_EnableProfile {
get {
@@ -7148,6 +7274,15 @@ public static string ProfilesPage_OK {
}
}
+ ///
+ /// Looks up a localized string similar to Display the profile in Quick start section from the Quicktools Applications page.
+ ///
+ public static string ProfilesPage_Pinned {
+ get {
+ return ResourceManager.GetString("ProfilesPage_Pinned", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Power limit target.
///
diff --git a/HandheldCompanion/Properties/Resources.resx b/HandheldCompanion/Properties/Resources.resx
index 96a47c302..9f1dd6d4a 100644
--- a/HandheldCompanion/Properties/Resources.resx
+++ b/HandheldCompanion/Properties/Resources.resx
@@ -665,7 +665,7 @@
Delete profile
- User per-game profile
+ Use per-game profile
The profile will be automatically applied when the associated application is detected
@@ -3309,4 +3309,49 @@ Thank you for using HC and helping us make it better.
UNUSED
+
+ Mute volume
+
+
+ Mute/unmute volume
+
+
+ Open Game Bar
+
+
+ Press this key: Windows + G
+
+
+ C1
+
+
+ C2
+
+
+ C3
+
+
+ C4
+
+
+ T1
+
+
+ T2
+
+
+ T3
+
+
+ M
+
+
+ Display the profile in Quick start section from the Quicktools Applications page
+
+
+ Swap foreground window
+
+
+ Move the foreground window to next available screen
+
\ No newline at end of file
diff --git a/HandheldCompanion/Properties/Settings.Designer.cs b/HandheldCompanion/Properties/Settings.Designer.cs
index ce0a6666b..5bc2c5f73 100644
--- a/HandheldCompanion/Properties/Settings.Designer.cs
+++ b/HandheldCompanion/Properties/Settings.Designer.cs
@@ -243,7 +243,7 @@ public string CurrentCulture {
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Configuration.DefaultSettingValueAttribute("0")]
+ [global::System.Configuration.DefaultSettingValueAttribute("20")]
public double OverlayControllerRestingPitch {
get {
return ((double)(this["OverlayControllerRestingPitch"]));
@@ -1188,5 +1188,17 @@ public double AYANEOFlipScreenBrightness {
this["AYANEOFlipScreenBrightness"] = value;
}
}
+
+ [global::System.Configuration.UserScopedSettingAttribute()]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Configuration.DefaultSettingValueAttribute("False")]
+ public bool HotkeyRumbleOnExecution {
+ get {
+ return ((bool)(this["HotkeyRumbleOnExecution"]));
+ }
+ set {
+ this["HotkeyRumbleOnExecution"] = value;
+ }
+ }
}
}
diff --git a/HandheldCompanion/Properties/Settings.settings b/HandheldCompanion/Properties/Settings.settings
index 9228258d2..a424c0382 100644
--- a/HandheldCompanion/Properties/Settings.settings
+++ b/HandheldCompanion/Properties/Settings.settings
@@ -57,7 +57,7 @@
en-US
- 0
+ 20
False
@@ -293,5 +293,8 @@
100
+
+ False
+
\ No newline at end of file
diff --git a/HandheldCompanion/Resources/libryzenadj.dll b/HandheldCompanion/Resources/libryzenadj.dll
index d32dbe66e..075576f19 100644
Binary files a/HandheldCompanion/Resources/libryzenadj.dll and b/HandheldCompanion/Resources/libryzenadj.dll differ
diff --git a/HandheldCompanion/Targets/DualShock4Target.cs b/HandheldCompanion/Targets/DualShock4Target.cs
index b09e012a1..c4a0161ac 100644
--- a/HandheldCompanion/Targets/DualShock4Target.cs
+++ b/HandheldCompanion/Targets/DualShock4Target.cs
@@ -8,7 +8,9 @@
using Nefarius.ViGEm.Client.Targets;
using Nefarius.ViGEm.Client.Targets.DualShock4;
using System;
+using System.Numerics;
using System.Threading;
+using static HandheldCompanion.Inputs.GyroState;
namespace HandheldCompanion.Targets
{
@@ -177,13 +179,19 @@ public override unsafe void UpdateInputs(ControllerState Inputs, GamepadMotion g
calibration = gamepadMotion.GetCalibration();
// Use gyro sensor data, map to proper range, invert where needed
- outDS4Report.wGyroX = (short)InputUtils.rangeMap(Inputs.GyroState.Gyroscope[GyroState.SensorState.Raw].X, -2000.0f, 2000.0f, short.MinValue, short.MaxValue);
- outDS4Report.wGyroY = (short)InputUtils.rangeMap(Inputs.GyroState.Gyroscope[GyroState.SensorState.Raw].Y, -2000.0f, 2000.0f, short.MinValue, short.MaxValue);
- outDS4Report.wGyroZ = (short)InputUtils.rangeMap(Inputs.GyroState.Gyroscope[GyroState.SensorState.Raw].Z, -2000.0f, 2000.0f, short.MinValue, short.MaxValue);
+ if (Inputs.GyroState.Gyroscope.TryGetValue(SensorState.GamepadMotion, out Vector3 gyrometer))
+ {
+ outDS4Report.wGyroX = (short)InputUtils.rangeMap(gyrometer.X, -2000.0f, 2000.0f, short.MinValue, short.MaxValue);
+ outDS4Report.wGyroY = (short)InputUtils.rangeMap(gyrometer.Y, -2000.0f, 2000.0f, short.MinValue, short.MaxValue);
+ outDS4Report.wGyroZ = (short)InputUtils.rangeMap(gyrometer.Z, -2000.0f, 2000.0f, short.MinValue, short.MaxValue);
+ }
- outDS4Report.wAccelX = (short)InputUtils.rangeMap(Inputs.GyroState.Accelerometer[GyroState.SensorState.Raw].X, -4.0f, 4.0f, short.MinValue, short.MaxValue);
- outDS4Report.wAccelY = (short)InputUtils.rangeMap(Inputs.GyroState.Accelerometer[GyroState.SensorState.Raw].Y, -4.0f, 4.0f, short.MinValue, short.MaxValue);
- outDS4Report.wAccelZ = (short)InputUtils.rangeMap(Inputs.GyroState.Accelerometer[GyroState.SensorState.Raw].Z, -4.0f, 4.0f, short.MinValue, short.MaxValue);
+ if (Inputs.GyroState.Accelerometer.TryGetValue(SensorState.GamepadMotion, out Vector3 accelerometer))
+ {
+ outDS4Report.wAccelX = (short)InputUtils.rangeMap(accelerometer.X, -4.0f, 4.0f, short.MinValue, short.MaxValue);
+ outDS4Report.wAccelY = (short)InputUtils.rangeMap(accelerometer.Y, -4.0f, 4.0f, short.MinValue, short.MaxValue);
+ outDS4Report.wAccelZ = (short)InputUtils.rangeMap(accelerometer.Z, -4.0f, 4.0f, short.MinValue, short.MaxValue);
+ }
// todo: implement battery value based on device
outDS4Report.bBatteryLvlSpecial = 11;
diff --git a/HandheldCompanion/ViewModels/HotkeyViewModel.cs b/HandheldCompanion/ViewModels/HotkeyViewModel.cs
index 6bb795f07..aeb3b20e4 100644
--- a/HandheldCompanion/ViewModels/HotkeyViewModel.cs
+++ b/HandheldCompanion/ViewModels/HotkeyViewModel.cs
@@ -110,6 +110,7 @@ private void Command_Updated(ICommands command)
{
OnPropertyChanged(nameof(LiveGlyph));
OnPropertyChanged(nameof(IsEnabled));
+ OnPropertyChanged(nameof(IsToggled));
}
public override void Dispose()
@@ -501,8 +502,7 @@ public HotkeyViewModel(Hotkey hotkey)
ExecuteCommand = new DelegateCommand(async () =>
{
- if (Hotkey.command is not null)
- Hotkey.command.Execute(Hotkey.command.OnKeyDown, Hotkey.command.OnKeyUp);
+ Hotkey.Execute(Hotkey.command.OnKeyDown, Hotkey.command.OnKeyUp, false);
});
EraseButtonCommand = new DelegateCommand(async () =>
diff --git a/HandheldCompanion/ViewModels/Layout/Pages/ButtonsPageViewModel.cs b/HandheldCompanion/ViewModels/Layout/Pages/ButtonsPageViewModel.cs
index cdb089f26..9edb69fc4 100644
--- a/HandheldCompanion/ViewModels/Layout/Pages/ButtonsPageViewModel.cs
+++ b/HandheldCompanion/ViewModels/Layout/Pages/ButtonsPageViewModel.cs
@@ -8,7 +8,7 @@ namespace HandheldCompanion.ViewModels
{
public class ButtonsPageViewModel : ILayoutPageViewModel
{
- private static readonly List _ABXY = [ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4, ButtonFlags.B5, ButtonFlags.B6, ButtonFlags.B7, ButtonFlags.B8];
+ private static readonly List _ABXY = [ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4, ButtonFlags.B5, ButtonFlags.B6, ButtonFlags.B7, ButtonFlags.B8, ButtonFlags.B9, ButtonFlags.B10, ButtonFlags.B11];
private static readonly List _BUMPERS = [ButtonFlags.L1, ButtonFlags.R1];
private static readonly List _MENU = [ButtonFlags.Back, ButtonFlags.Start, ButtonFlags.Special, ButtonFlags.Special2];
private static readonly List _BACKGRIPS = [ButtonFlags.L4, ButtonFlags.L5, ButtonFlags.R4, ButtonFlags.R5];
diff --git a/HandheldCompanion/ViewModels/Pages/HotkeyPageViewModel.cs b/HandheldCompanion/ViewModels/Pages/HotkeyPageViewModel.cs
index 45fc00fbe..e618b5ba9 100644
--- a/HandheldCompanion/ViewModels/Pages/HotkeyPageViewModel.cs
+++ b/HandheldCompanion/ViewModels/Pages/HotkeyPageViewModel.cs
@@ -12,6 +12,18 @@ public class HotkeyPageViewModel : BaseViewModel
public ObservableCollection HotkeysList { get; set; } = [];
public ICommand CreateHotkeyCommand { get; private set; }
+ public bool Rumble
+ {
+ get
+ {
+ return SettingsManager.GetBoolean("HotkeyRumbleOnExecution");
+ }
+ set
+ {
+ SettingsManager.SetProperty("HotkeyRumbleOnExecution", value);
+ }
+ }
+
public HotkeyPageViewModel()
{
HotkeysManager.Updated += HotkeysManager_Updated;
diff --git a/HandheldCompanion/ViewModels/Pages/OverlayPageViewModel.cs b/HandheldCompanion/ViewModels/Pages/OverlayPageViewModel.cs
index 9d0ebc1d0..bf69fe06e 100644
--- a/HandheldCompanion/ViewModels/Pages/OverlayPageViewModel.cs
+++ b/HandheldCompanion/ViewModels/Pages/OverlayPageViewModel.cs
@@ -526,7 +526,7 @@ public override void Dispose()
base.Dispose();
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/ViewModels/Pages/PerformancePageViewModel.cs b/HandheldCompanion/ViewModels/Pages/PerformancePageViewModel.cs
index 5eea8af30..b72713f96 100644
--- a/HandheldCompanion/ViewModels/Pages/PerformancePageViewModel.cs
+++ b/HandheldCompanion/ViewModels/Pages/PerformancePageViewModel.cs
@@ -489,7 +489,7 @@ public PerformancePageViewModel(bool isQuickTools)
#region General Setup
- SettingsManager.SettingValueChanged += SettingsManager_SettingsValueChanged;
+ SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
MultimediaManager.PrimaryScreenChanged += MultimediaManager_PrimaryScreenChanged;
PerformanceManager.ProcessorStatusChanged += PerformanceManager_ProcessorStatusChanged;
PerformanceManager.EPPChanged += PerformanceManager_EPPChanged;
@@ -668,7 +668,7 @@ public PerformancePageViewModel(bool isQuickTools)
public override void Dispose()
{
- SettingsManager.SettingValueChanged -= SettingsManager_SettingsValueChanged;
+ SettingsManager.SettingValueChanged -= SettingsManager_SettingValueChanged;
MultimediaManager.PrimaryScreenChanged -= MultimediaManager_PrimaryScreenChanged;
PerformanceManager.ProcessorStatusChanged -= PerformanceManager_ProcessorStatusChanged;
PerformanceManager.EPPChanged += PerformanceManager_EPPChanged;
@@ -690,7 +690,7 @@ public override void Dispose()
#region Events
- private void SettingsManager_SettingsValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
switch (name)
{
diff --git a/HandheldCompanion/ViewModels/Pages/QuickApplicationPageViewModel.cs b/HandheldCompanion/ViewModels/Pages/QuickApplicationPageViewModel.cs
index 146ed9667..5b3e086eb 100644
--- a/HandheldCompanion/ViewModels/Pages/QuickApplicationPageViewModel.cs
+++ b/HandheldCompanion/ViewModels/Pages/QuickApplicationPageViewModel.cs
@@ -99,11 +99,15 @@ private void ProfileManager_Updated(Profile profile, UpdateSource source, bool i
ProfileViewModel? foundProfile = Profiles.ToList().FirstOrDefault(p => p.Profile == profile || p.Profile.Guid == profile.Guid);
if (foundProfile is null)
{
- Profiles.SafeAdd(new ProfileViewModel(profile, this));
+ if (profile.IsPinned)
+ Profiles.SafeAdd(new ProfileViewModel(profile, this));
}
else
{
- foundProfile.Profile = profile;
+ if (profile.IsPinned)
+ foundProfile.Profile = profile;
+ else
+ ProfileManager_Deleted(profile);
}
}
diff --git a/HandheldCompanion/ViewModels/ProcessWindowViewModel.cs b/HandheldCompanion/ViewModels/ProcessWindowViewModel.cs
index 4f67dcb56..71e095a4c 100644
--- a/HandheldCompanion/ViewModels/ProcessWindowViewModel.cs
+++ b/HandheldCompanion/ViewModels/ProcessWindowViewModel.cs
@@ -66,6 +66,9 @@ public ProcessWindowViewModel(ProcessWindow processWindow, ProcessExViewModel pr
SwapScreenCommand = new DelegateCommand(async () =>
{
Screen screen = Screen.AllScreens.Where(screen => screen.DeviceName != CurrentScreen.DeviceName).FirstOrDefault();
+ if (screen is null)
+ return;
+
WinAPI.MoveWindow(ProcessWindow.Hwnd, screen, WpfScreenHelper.Enum.WindowPositions.Maximize);
WinAPI.SetForegroundWindow(ProcessWindow.Hwnd);
diff --git a/HandheldCompanion/Views/Pages/ControllerPage.xaml.cs b/HandheldCompanion/Views/Pages/ControllerPage.xaml.cs
index 365d03d0d..a06f5d0dc 100644
--- a/HandheldCompanion/Views/Pages/ControllerPage.xaml.cs
+++ b/HandheldCompanion/Views/Pages/ControllerPage.xaml.cs
@@ -63,7 +63,7 @@ private void ProfileManager_Applied(Profile profile, UpdateSource source)
}
});
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
// UI thread
Application.Current.Dispatcher.Invoke(() =>
diff --git a/HandheldCompanion/Views/Pages/DevicePage.xaml.cs b/HandheldCompanion/Views/Pages/DevicePage.xaml.cs
index c6b1ec11b..951a98820 100644
--- a/HandheldCompanion/Views/Pages/DevicePage.xaml.cs
+++ b/HandheldCompanion/Views/Pages/DevicePage.xaml.cs
@@ -116,7 +116,7 @@ private void Page_Loaded(object? sender, RoutedEventArgs? e)
public void Page_Closed()
{ }
- private void SettingsManager_SettingValueChanged(string? name, object value)
+ private void SettingsManager_SettingValueChanged(string? name, object value, bool temporary)
{
// UI thread
Application.Current.Dispatcher.Invoke(() =>
diff --git a/HandheldCompanion/Views/Pages/HotkeysPage.xaml b/HandheldCompanion/Views/Pages/HotkeysPage.xaml
index ef6de194c..1536c0af6 100644
--- a/HandheldCompanion/Views/Pages/HotkeysPage.xaml
+++ b/HandheldCompanion/Views/Pages/HotkeysPage.xaml
@@ -23,264 +23,276 @@
mc:Ignorable="d">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Text="{Binding Description, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
+ TextWrapping="Wrap" />
-
+
-
+ Orientation="Horizontal">
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
+
+
+
+
+
+
+
-
-
+
+
-
-
@@ -288,46 +300,233 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ Text="{Binding CustomName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
+ Text="{Binding ExecutableArguments, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+ TextAlignment="Left" />
-
-
-
-
+
@@ -389,129 +583,44 @@
Margin="12,0,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
- SelectedIndex="{Binding CyclingDirection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
-
-
+ SelectedIndex="{Binding ExecutableWindowStyle, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
+ Normal
+ Hidden
+ Minimized
+ Maximized
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
+ Text="Specifies if the application should start with adminstrator elevation"
+ TextWrapping="Wrap" />
+
-
+ VerticalAlignment="Center"
+ IsOn="{Binding ExecutableRunAs, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
+ Style="{DynamicResource InvertedToggleSwitchStyle}" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
- Normal
- Hidden
- Minimized
- Maximized
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ Command="{Binding DeleteHotkeyCommand}"
+ Content="Delete hotkey"
+ Style="{StaticResource AccentButtonStyle}" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HandheldCompanion/Views/Pages/LayoutPage.xaml.cs b/HandheldCompanion/Views/Pages/LayoutPage.xaml.cs
index a01410bd6..013084a0b 100644
--- a/HandheldCompanion/Views/Pages/LayoutPage.xaml.cs
+++ b/HandheldCompanion/Views/Pages/LayoutPage.xaml.cs
@@ -193,7 +193,7 @@ private void LayoutManager_Updated(LayoutTemplate layoutTemplate)
});
}
- private void SettingsManager_SettingValueChanged(string? name, object value)
+ private void SettingsManager_SettingValueChanged(string? name, object value, bool temporary)
{
// UI thread
Application.Current.Dispatcher.Invoke(() =>
diff --git a/HandheldCompanion/Views/Pages/OverlayPage.xaml b/HandheldCompanion/Views/Pages/OverlayPage.xaml
index 412ab24ca..401554ec2 100644
--- a/HandheldCompanion/Views/Pages/OverlayPage.xaml
+++ b/HandheldCompanion/Views/Pages/OverlayPage.xaml
@@ -787,7 +787,7 @@
IsSnapToTickEnabled="True"
LargeChange="10"
Maximum="90"
- Minimum="0"
+ Minimum="-90"
SmallChange="5"
Style="{DynamicResource SliderStyle1}"
TickFrequency="5"
diff --git a/HandheldCompanion/Views/Pages/OverlayPage.xaml.cs b/HandheldCompanion/Views/Pages/OverlayPage.xaml.cs
index 5db751222..2109df3fa 100644
--- a/HandheldCompanion/Views/Pages/OverlayPage.xaml.cs
+++ b/HandheldCompanion/Views/Pages/OverlayPage.xaml.cs
@@ -52,7 +52,7 @@ private void RTSS_Updated(PlatformStatus status)
});
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
// UI thread
Application.Current.Dispatcher.Invoke(() =>
diff --git a/HandheldCompanion/Views/Pages/ProfilesPage.xaml b/HandheldCompanion/Views/Pages/ProfilesPage.xaml
index 95af8cb3c..0caabd3e8 100644
--- a/HandheldCompanion/Views/Pages/ProfilesPage.xaml
+++ b/HandheldCompanion/Views/Pages/ProfilesPage.xaml
@@ -385,6 +385,18 @@
Content="{l:Static resx:Resources.ProfilesPage_Whitelist}"
Unchecked="cB_Whitelist_Checked" />
+
+
+
+
diff --git a/HandheldCompanion/Views/QuickPages/QuickDevicePage.xaml b/HandheldCompanion/Views/QuickPages/QuickDevicePage.xaml
index d1012c9fe..d33e1cb56 100644
--- a/HandheldCompanion/Views/QuickPages/QuickDevicePage.xaml
+++ b/HandheldCompanion/Views/QuickPages/QuickDevicePage.xaml
@@ -241,49 +241,6 @@
Toggled="NightLightToggle_Toggled" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/HandheldCompanion/Views/QuickPages/QuickDevicePage.xaml.cs b/HandheldCompanion/Views/QuickPages/QuickDevicePage.xaml.cs
index 5aa719bc3..9e8ee018d 100644
--- a/HandheldCompanion/Views/QuickPages/QuickDevicePage.xaml.cs
+++ b/HandheldCompanion/Views/QuickPages/QuickDevicePage.xaml.cs
@@ -111,7 +111,7 @@ private void NightLight_Toggled(bool enabled)
});
}
- private void SettingsManager_SettingValueChanged(string? name, object value)
+ private void SettingsManager_SettingValueChanged(string? name, object value, bool temporary)
{
// UI thread
Application.Current.Dispatcher.Invoke(() =>
diff --git a/HandheldCompanion/Views/QuickPages/QuickHomePage.xaml.cs b/HandheldCompanion/Views/QuickPages/QuickHomePage.xaml.cs
index 2015546fb..0a60a4e5e 100644
--- a/HandheldCompanion/Views/QuickPages/QuickHomePage.xaml.cs
+++ b/HandheldCompanion/Views/QuickPages/QuickHomePage.xaml.cs
@@ -160,7 +160,7 @@ private void ProfileManager_Applied(Profile profile, UpdateSource source)
});
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
string[] onScreenDisplayLevels = {
Properties.Resources.OverlayPage_OverlayDisplayLevel_Disabled,
diff --git a/HandheldCompanion/Views/Windows/MainWindow.xaml b/HandheldCompanion/Views/Windows/MainWindow.xaml
index ad16d8ac1..fa2951385 100644
--- a/HandheldCompanion/Views/Windows/MainWindow.xaml
+++ b/HandheldCompanion/Views/Windows/MainWindow.xaml
@@ -172,6 +172,7 @@
+
@@ -191,11 +192,10 @@
diff --git a/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml.cs b/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml.cs
index ef205e550..7dfd097d8 100644
--- a/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml.cs
+++ b/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml.cs
@@ -163,7 +163,7 @@ public static OverlayQuickTools GetCurrent()
return CurrentWindow;
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
// UI thread
Application.Current.Dispatcher.Invoke(() =>
diff --git a/HandheldCompanion/Views/Windows/OverlayTrackpad.xaml.cs b/HandheldCompanion/Views/Windows/OverlayTrackpad.xaml.cs
index 292c46a89..ddb70a3da 100644
--- a/HandheldCompanion/Views/Windows/OverlayTrackpad.xaml.cs
+++ b/HandheldCompanion/Views/Windows/OverlayTrackpad.xaml.cs
@@ -36,7 +36,7 @@ public OverlayTrackpad()
rightInput = new TouchInput();
}
- private void SettingsManager_SettingValueChanged(string name, object value)
+ private void SettingsManager_SettingValueChanged(string name, object value, bool temporary)
{
// UI thread
Application.Current.Dispatcher.Invoke(() =>
diff --git a/redist/windowsdesktop-runtime-9.0.0-rc.2.24474.4-win-x64.exe b/redist/windowsdesktop-runtime-9.0.0-rc.2.24474.4-win-x64.exe
new file mode 100644
index 000000000..7dc614e44
Binary files /dev/null and b/redist/windowsdesktop-runtime-9.0.0-rc.2.24474.4-win-x64.exe differ