diff --git a/HandheldCompanion.iss b/HandheldCompanion.iss
index 0743ce7b2..963a15025 100644
--- a/HandheldCompanion.iss
+++ b/HandheldCompanion.iss
@@ -1,17 +1,158 @@
-; -----------
-; CODE
-; -----------
-[Code]
+[Setup]
+; -------------
+; SETUP
+; -------------
+#ifndef Dependency_NoExampleSetup
+
+; requires netcorecheck.exe and netcorecheck_x64.exe (see download link below)
+#define UseNetCoreCheck
+#ifdef UseNetCoreCheck
+ #define UseDotNet80
+#endif
+
+;#define UseVC2005
+;#define UseVC2008
+;#define UseVC2010
+;#define UseVC2012
+;#define UseVC2013
+;#define UseVC2015To2019
+
+#define UseDirectX
+;Install ViGem first
+#define UseViGem
+#define UseHideHide
+#define UseRTSS
+
+#define InstallerVersion '0.2'
+#define MyAppSetupName 'Handheld Companion'
+#define MyBuildId 'HandheldCompanion'
+#define MyAppVersion '0.20.5.6'
+#define MyAppPublisher 'BenjaminLSR'
+#define MyAppCopyright 'Copyright @ BenjaminLSR'
+#define MyAppURL 'https://github.com/Valkirie/HandheldCompanion'
+#define MyAppExeName "HandheldCompanion.exe"
+#define MyConfiguration "Release"
+
+#define RtssExe "RTSS.exe"
+#define EncoderServer64Exe "EncoderServer64.exe"
+#define RTSSHooksLoader64Exe "RTSSHooksLoader64.exe"
+#define EncoderServerExe "EncoderServer.exe"
+#define RTSSHooksLoaderExe "RTSSHooksLoader.exe"
+
+#define DotNetName ".NET Desktop Runtime"
+#define DirectXName "DirectX Runtime"
+#define ViGemName "ViGEmBus Setup"
+#define HidHideName "HidHide Drivers"
+#define RtssName "RTSS Setup"
+
+#define NewDotNetVersion "8.0.1"
+#define NewDirectXVersion "9.29.1974"
+#define NewViGemVersion "1.22.0.0"
+#define NewHidHideVersion "1.5.212"
+#define NewRtssVersion "7.3.6"
+
+//#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.212.0/HidHide_1.5.212_x64.exe"
+#define ViGemDownloadLink "https://github.com/nefarius/ViGEmBus/releases/download/v1.22.0/ViGEmBus_1.22.0_x64_x86_arm64.exe"
+#define RtssDownloadLink "https://github.com/Valkirie/HandheldCompanion/raw/main/redist/RTSSSetup736.exe"
+
+//Registry
+#define RegAppsPath "SOFTWARE\" +MyAppSetupName+ "\"
+#define SoftwareUninstallKey "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
+
+#ifdef UseDotNet80
+ #define MyConfigurationExt "net8.0"
+#endif
+
+AllowNoIcons=yes
+AppName={#MyAppSetupName}
+AppVersion={#MyAppVersion}
+AppVerName={#MyAppSetupName}
+AppCopyright={#MyAppCopyright}
+// remove next line if you only deploy 32-bit binaries and dependencies
+ArchitecturesInstallIn64BitMode=x64
+AppPublisher={#MyAppPublisher}
+AppPublisherURL={#MyAppURL}
+AppSupportURL={#MyAppURL}
+AppUpdatesURL={#MyAppURL}
+CloseApplications = yes
+Compression=lzma
+DefaultGroupName={#MyAppSetupName}
+DefaultDirName={autopf}\{#MyAppSetupName}
+OutputBaseFilename={#MyBuildId}-{#MyAppVersion}
+SetupIconFile="{#SourcePath}\HandheldCompanion\Resources\icon.ico"
+SetupLogging=yes
+MinVersion=6.0
+OutputDir={#SourcePath}\install
+PrivilegesRequired=admin
+SolidCompression=yes
+VersionInfoVersion={#MyAppVersion}
+VersionInfoCompany={#MyAppPublisher}
+UninstallDisplayIcon={app}\{#MyAppExeName}
+
+[Languages]
+Name: en; MessagesFile: "compiler:Default.isl"
+
+[Files]
+#ifdef UseNetCoreCheck
+// download netcorecheck.exe: https://go.microsoft.com/fwlink/?linkid=2135256
+// download netcorecheck_x64.exe: https://go.microsoft.com/fwlink/?linkid=2135504
+Source: "{#SourcePath}\redist\netcorecheck.exe"; Flags: dontcopy noencryption
+Source: "{#SourcePath}\redist\netcorecheck_x64.exe"; Flags: dontcopy noencryption
+#endif
+Source: "{#SourcePath}\bin\{#MyConfiguration}\{#MyConfigurationExt}-windows10.0.19041.0\WinRing0x64.dll"; DestDir: "{app}"; Flags: onlyifdoesntexist
+Source: "{#SourcePath}\bin\{#MyConfiguration}\{#MyConfigurationExt}-windows10.0.19041.0\WinRing0x64.sys"; DestDir: "{app}"; Flags: onlyifdoesntexist
+Source: "{#SourcePath}\bin\{#MyConfiguration}\{#MyConfigurationExt}-windows10.0.19041.0\*"; Excludes: "*WinRing0x64.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
+
+Source: "{#SourcePath}\redist\SegoeIcons.ttf"; DestDir: "{autofonts}"; FontInstall: "Segoe Fluent Icons (TrueType)"; Flags: onlyifdoesntexist uninsneveruninstall
+Source: "{#SourcePath}\redist\PromptFont.otf"; DestDir: "{autofonts}"; FontInstall: "PromptFont"; Flags: uninsneveruninstall
+
+[Icons]
+Name: "{group}\{#MyAppSetupName}"; Filename: "{app}\{#MyAppExeName}"
+Name: "{group}\{cm:UninstallProgram,{#MyAppSetupName}}"; Filename: "{uninstallexe}"
+Name: "{userdesktop}\{#MyAppSetupName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
+
+[Tasks]
+Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"
+
+[Run]
+Filename: "{app}\HandheldCompanion.exe"; Description:"Start Handheld Companion"; Flags: postinstall nowait shellexec skipifsilent;
+
+[InstallDelete]
+Type: files; Name: "{userdesktop}\HidHide Configuration Client.lnk"
+Type: files; Name: "{commondesktop}\HidHide Configuration Client.lnk"
+
+[UninstallRun]
+Filename: "C:\Program Files\Nefarius Software Solutions\HidHide\x64\HidHideCLI.exe"; Parameters: "--cloak-off" ; RunOnceId: "CloakOff"; Flags: runascurrentuser runhidden
+
+[UninstallDelete]
+Type: filesandordirs; Name: "{app}"
+
+[Registry]
+Root: HKA; Subkey: "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps"; Flags: uninsdeletekeyifempty
+Root: HKA; Subkey: "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\HandheldCompanion.exe"; ValueType: string; ValueName: "DumpFolder"; ValueData: "{userdocs}\HandheldCompanion\dumps"; Flags: uninsdeletekey
+
+[Code]
// types and variables
type
+ TIntegerArray = array of integer;
+
TDependency_Entry = record
Filename: String;
+ NewVersion: String;
+ InstalledVersion: String;
Parameters: String;
Title: String;
URL: String;
Checksum: String;
ForceSuccess: Boolean;
- RestartAfter: Boolean;
+ RestartNeeded: Boolean;
end;
var
@@ -20,7 +161,232 @@ var
Dependency_NeedRestart, Dependency_ForceX86: Boolean;
Dependency_DownloadPage: TDownloadWizardPage;
-procedure Dependency_Add(const Filename, Parameters, Title, URL, Checksum: String; const ForceSuccess, RestartAfter: Boolean);
+//Prototypes
+procedure Dependency_Add(const Filename, Parameters, Title, URL, Checksum: String; const ForceSuccess: Boolean); forward;
+procedure Dependency_Add_With_Version(const Filename, NewVersion, InstalledVersion, Parameters, Title, URL, Checksum: String; const ForceSuccess, RestartNeeded: Boolean); forward;
+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_AddDirectX; forward;
+procedure Dependency_AddHideHide; forward;
+procedure Dependency_AddViGem; forward;
+procedure Dependency_AddRTSS; forward;
+function BoolToStr(Value: Boolean): String; forward;
+
+
+#include "./utils/CompareVersions.iss"
+#include "./utils/ApiUtils.iss"
+#include "./utils/RegUtils.iss"
+#include "./utils/UpdateUninstallWizard.iss"
+#include "./utils/Utils.iss"
+
+
+procedure InitializeWizard;
+begin
+ Dependency_DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), nil);
+end;
+
+
+procedure CurStepChanged(CurStep: TSetupStep);
+begin
+ if CurStep = ssPostInstall then
+ begin
+ //TODO - Add firewall entry
+ end;
+end;
+
+
+procedure CurPageChanged(CurPageID: Integer);
+begin
+ if CurPageID = wpFinished then
+ begin
+ if(Dependency_NeedRestart) then
+ WizardForm.RunList.Visible := False;
+ end;
+end;
+
+function NeedRestart: Boolean;
+begin
+ log('***Enter NeedRestart()***');
+ log('NeedRestart: ' +boolToStr(Dependency_NeedRestart));
+ Result := Dependency_NeedRestart;
+ log('!!!Leave NeedRestart()!!!');
+end;
+
+function PrepareToInstall(var NeedsRestart: Boolean): String;
+var
+ PrepareToInstallResult:String;
+begin
+ log('***Enter PrepareToInstall()***');
+ log('Restart needed: ' +boolToStr(NeedsRestart));
+ PrepareToInstallResult:= Dependency_PrepareToInstall(NeedsRestart);
+ log('Result: ' +PrepareToInstallResult);
+ result:= PrepareToInstallResult;
+ log('!!!Leave PrepareToInstall()!!!');
+end;
+
+function UpdateReadyMemo(const Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
+begin
+ Result := Dependency_UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo);
+end;
+
+
+procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
+var
+ resultCode:integer;
+begin
+ if CurUninstallStep = usUninstall then
+ begin
+ if not(checkListBox.checked[keepAllCheck]) then
+ begin
+ if DirExists(ExpandConstant('{userdocs}\{#MyBuildId}\profiles')) then
+ DelTree(ExpandConstant('{userdocs}\{#MyBuildId}\profiles'), True, True, True);
+ if DirExists(ExpandConstant('{userdocs}\{#MyBuildId}\hotkeys')) then
+ DelTree(ExpandConstant('{userdocs}\{#MyBuildId}\hotkeys'), True, True, True);
+ DelTree(ExpandConstant('{localappdata}\HandheldCompanion'), True, True, True);
+ exit;
+ end
+ else
+ begin
+ if not(checkListBox.checked[profilesCheck]) then
+ begin
+ if DirExists(ExpandConstant('{userdocs}\{#MyBuildId}\profiles')) then
+ DelTree(ExpandConstant('{userdocs}\{#MyBuildId}\profiles'), True, True, True);
+ end;
+
+ if not(checkListBox.checked[hotkeysCheck]) then
+ begin
+ if DirExists(ExpandConstant('{userdocs}\{#MyBuildId}\hotkeys')) then
+ DelTree(ExpandConstant('{userdocs}\{#MyBuildId}\hotkeys'), True, True, True);
+ end;
+
+ if not(checkListBox.checked[applicationSettingsCheck]) then
+ begin
+ DelTree(ExpandConstant('{localappdata}\HandheldCompanion'), True, True, True);
+ end;
+ end;
+
+ if not(keepHidhideCheckbox.Checked) then
+ begin
+ uninstallHidHide();
+ end;
+
+ if not(keepVigemCheckbox.Checked) then
+ begin
+ if(ShellExec('', 'msiexec.exe', '/X{966606F3-2745-49E9-BF15-5C3EAA4E9077}', '', SW_SHOW, ewWaitUntilTerminated, resultCode)) then
+ begin
+ log('Successfully executed Vigem uninstaller');
+ if(resultCode = 0) then
+ log('Vigem uninstaller finished successfully')
+ else
+ log('Vigem uninstaller failed with exit code ' +intToStr(resultCode));
+ end
+ else
+ begin
+ log('Failed to execute Vigem uninstaller');
+ end;
+ end;
+ end;
+end;
+
+
+function InitializeSetup: Boolean;
+var
+ installedVersion:string;
+begin
+
+#ifdef UseDotNet80
+ installedVersion:= regGetInstalledVersion('{#DotNetName}');
+ if(compareVersions('{#NewDotNetVersion}', installedVersion, '.', '-') > 0) then
+ begin
+ log('{#DotNetName} {#NewDotNetVersion} needs update.');
+ Dependency_AddDotNet80Desktop;
+ end;
+#endif
+
+#ifdef UseVC2005
+ Dependency_AddVC2005;
+#endif
+#ifdef UseVC2008
+ Dependency_AddVC2008;
+#endif
+#ifdef UseVC2010
+ Dependency_AddVC2010;
+#endif
+#ifdef UseVC2012
+ Dependency_AddVC2012;
+#endif
+#ifdef UseVC2013
+ Dependency_AddVC2013;
+#endif
+#ifdef UseVC2015To2019
+ Dependency_AddVC2015To2019;
+#endif
+
+#ifdef UseDirectX
+ installedVersion:= regGetInstalledVersion('{#DirectXName}');
+ if(compareVersions('{#NewDirectXVersion}', installedVersion, '.', '-') > 0) then
+ begin
+ log('{#DirectXName} {#NewDirectXVersion} needs update.');
+ Dependency_AddDirectX;
+ end;
+#endif
+
+#ifdef UseHideHide
+ if not(isHidHideInstalled()) then
+ begin
+ Dependency_AddHideHide;
+ uninstallHidHide();
+ end
+ else
+ begin
+ installedVersion:= getInstalledHidHideVersion();
+ if(compareVersions('{#NewHidHideVersion}', installedVersion, '.', '-') > 0) then
+ begin
+ log('{#HidHideName} {#NewHidHideVersion} needs update.');
+ Dependency_AddHideHide;
+ uninstallHidHide();
+ end;
+ end;
+#endif
+
+#ifdef UseViGem
+ if not(isViGemInstalled()) then
+ begin
+ Dependency_AddViGem;
+ uninstallViGem();
+ end
+ else
+ begin
+ installedVersion:= regGetInstalledVersion('{#ViGemName}');
+ if(compareVersions('{#NewViGemVersion}', installedVersion, '.', '-') > 0) then
+ begin
+ log('{#ViGemName} {#NewViGemVersion} needs update.');
+ Dependency_AddViGem;
+ uninstallViGem();
+ end;
+ end;
+#endif
+
+#ifdef UseRTSS
+ if(not isRtssInstalled()) then
+ Dependency_AddRTSS
+ else
+ begin
+ installedVersion:= getInstalledRtssVersion();
+ if(compareVersions('{#NewRtssVersion}', installedVersion, '.', '-') > 0) then
+ begin
+ log('{#RtssName} {#NewRtssVersion} needs update.');
+ Dependency_AddRTSS;
+ end;
+ end;
+#endif
+
+ Result := True;
+end;
+#endif
+
+procedure Dependency_Add(const Filename, Parameters, Title, URL, Checksum: String; const ForceSuccess: Boolean);
var
Dependency: TDependency_Entry;
DependencyCount: Integer;
@@ -39,18 +405,42 @@ begin
Dependency.Checksum := Checksum;
Dependency.ForceSuccess := ForceSuccess;
- Dependency.RestartAfter := RestartAfter;
DependencyCount := GetArrayLength(Dependency_List);
SetArrayLength(Dependency_List, DependencyCount + 1);
Dependency_List[DependencyCount] := Dependency;
end;
-procedure Dependency_InitializeWizard;
+
+procedure Dependency_Add_With_Version(const Filename, NewVersion, InstalledVersion, Parameters, Title, URL, Checksum: String; const ForceSuccess, RestartNeeded: Boolean);
+var
+ Dependency: TDependency_Entry;
+ DependencyCount: Integer;
begin
- Dependency_DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), nil);
+ Dependency_Memo := Dependency_Memo + #13#10 + '%1' + Title;
+
+ Dependency.Filename := Filename;
+ Dependency.NewVersion:= NewVersion;
+ Dependency.InstalledVersion:= InstalledVersion;
+ Dependency.Parameters := Parameters;
+ Dependency.Title := Title;
+
+ if FileExists(ExpandConstant('{tmp}{\}') + Filename) then begin
+ Dependency.URL := '';
+ end else begin
+ Dependency.URL := URL;
+ end;
+
+ Dependency.Checksum := Checksum;
+ Dependency.ForceSuccess := ForceSuccess;
+ Dependency.RestartNeeded:= RestartNeeded;
+
+ DependencyCount := GetArrayLength(Dependency_List);
+ SetArrayLength(Dependency_List, DependencyCount + 1);
+ Dependency_List[DependencyCount] := Dependency;
end;
+
function Dependency_PrepareToInstall(var NeedsRestart: Boolean): String;
var
DependencyCount, DependencyIndex, ResultCode: Integer;
@@ -95,28 +485,33 @@ begin
if Result = '' then begin
for DependencyIndex := 0 to DependencyCount - 1 do begin
- Dependency_DownloadPage.SetText(Dependency_List[DependencyIndex].Title, '');
+ Dependency_DownloadPage.SetText(Dependency_List[DependencyIndex].Title + ' ' +Dependency_List[DependencyIndex].NewVersion, '');
Dependency_DownloadPage.SetProgress(DependencyIndex + 1, DependencyCount + 1);
while True do begin
ResultCode := 0;
- if ShellExec('', ExpandConstant('{tmp}{\}') + Dependency_List[DependencyIndex].Filename, Dependency_List[DependencyIndex].Parameters, '', SW_SHOWNORMAL, ewWaitUntilTerminated, ResultCode) then begin
- if Dependency_List[DependencyIndex].RestartAfter then begin
- if DependencyIndex = DependencyCount - 1 then begin
- Dependency_NeedRestart := True;
- end else begin
- NeedsRestart := True;
- Result := Dependency_List[DependencyIndex].Title;
+ if ShellExec('', ExpandConstant('{tmp}{\}') + Dependency_List[DependencyIndex].Filename, Dependency_List[DependencyIndex].Parameters, '', SW_SHOWNORMAL, ewWaitUntilTerminated, ResultCode) then
+ begin
+ log('Successfully executed ' +Dependency_List[DependencyIndex].Filename+ ' with result code: ' +intToStr(ResultCode));
+
+ if (ResultCode = 0) or Dependency_List[DependencyIndex].ForceSuccess then begin // ERROR_SUCCESS (0)
+ begin
+ if(Dependency_List[DependencyIndex].RestartNeeded) then
+ begin
+ log('Restart is needed by ' +Dependency_List[DependencyIndex].Title);
+ Dependency_NeedRestart := True;
end;
+ regSetVersion(Dependency_List[DependencyIndex].Title, Dependency_List[DependencyIndex].NewVersion);
break;
- end else if (ResultCode = 0) or Dependency_List[DependencyIndex].ForceSuccess then begin // ERROR_SUCCESS (0)
- break;
+ end
end else if ResultCode = 1641 then begin // ERROR_SUCCESS_REBOOT_INITIATED (1641)
NeedsRestart := True;
+ log(Dependency_List[DependencyIndex].Title + ' needs restart with result code ' +intToStr(ResultCode));
Result := Dependency_List[DependencyIndex].Title;
break;
end else if ResultCode = 3010 then begin // ERROR_SUCCESS_REBOOT_REQUIRED (3010)
Dependency_NeedRestart := True;
+ log(Dependency_List[DependencyIndex].Title + ' needs restart with result code ' +intToStr(ResultCode));
break;
end;
end;
@@ -178,6 +573,7 @@ begin
end;
Result := Result + FmtMessage(Dependency_Memo, [Space]);
end;
+ log('Ready MemoResult: ' +result);
end;
function Dependency_IsX64: Boolean;
@@ -219,14 +615,12 @@ procedure Dependency_AddDotNet80Desktop;
begin
// https://dotnet.microsoft.com/en-us/download/dotnet/8.0
if not Dependency_IsNetCoreInstalled('Microsoft.WindowsDesktop.App 8.0.0') then begin
- Dependency_Add('dotNet80desktop' + Dependency_ArchSuffix + '.exe',
+ Dependency_Add_With_Version('dotNet80desktop' + Dependency_ArchSuffix + '.exe', '{#NewDotNetVersion}', regGetInstalledVersion('{#DotNetName}'),
'/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart',
- '.NET Desktop Runtime 8.0.0' + Dependency_ArchTitle,
- Dependency_String('https://download.visualstudio.microsoft.com/download/pr/b280d97f-25a9-4ab7-8a12-8291aa3af117/a37ed0e68f51fcd973e9f6cb4f40b1a7/windowsdesktop-runtime-8.0.0-win-x64.exe',
- 'https://download.visualstudio.microsoft.com/download/pr/f9e3b581-059d-429f-9f0d-1d1167ff7e32/bd7661030cd5d66cd3eee0fd20b24540/windowsdesktop-runtime-8.0.0-win-x86.exe'),
- '', False, False);
+ '{#DotNetName}', Dependency_String('{#DotNetX86DownloadLink}', '{#DotNetX64DownloadLink}'), '', False, False);
end;
-end;
+end;
+
procedure Dependency_AddVC2005;
begin
@@ -236,7 +630,7 @@ begin
'/q',
'Visual C++ 2005 Service Pack 1 Redistributable' + Dependency_ArchTitle,
Dependency_String('https://download.microsoft.com/download/8/B/4/8B42259F-5D70-43F4-AC2E-4B208FD8D66A/vcredist_x86.EXE', 'https://download.microsoft.com/download/8/B/4/8B42259F-5D70-43F4-AC2E-4B208FD8D66A/vcredist_x64.EXE'),
- '', False, False);
+ '', False);
end;
end;
@@ -248,7 +642,7 @@ begin
'/q',
'Visual C++ 2008 Service Pack 1 Redistributable' + Dependency_ArchTitle,
Dependency_String('https://download.microsoft.com/download/5/D/8/5D8C65CB-C849-4025-8E95-C3966CAFD8AE/vcredist_x86.exe', 'https://download.microsoft.com/download/5/D/8/5D8C65CB-C849-4025-8E95-C3966CAFD8AE/vcredist_x64.exe'),
- '', False, False);
+ '', False);
end;
end;
@@ -260,7 +654,7 @@ begin
'/passive /norestart',
'Visual C++ 2010 Service Pack 1 Redistributable' + Dependency_ArchTitle,
Dependency_String('https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x86.exe', 'https://download.microsoft.com/download/1/6/5/165255E7-1014-4D0A-B094-B6A430A6BFFC/vcredist_x64.exe'),
- '', False, False);
+ '', False);
end;
end;
@@ -272,7 +666,7 @@ begin
'/passive /norestart',
'Visual C++ 2012 Update 4 Redistributable' + Dependency_ArchTitle,
Dependency_String('https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x86.exe', 'https://download.microsoft.com/download/1/6/B/16B06F60-3B20-4FF2-B699-5E9B7962F9AE/VSU_4/vcredist_x64.exe'),
- '', False, False);
+ '', False);
end;
end;
@@ -284,7 +678,7 @@ begin
'/passive /norestart',
'Visual C++ 2013 Update 5 Redistributable' + Dependency_ArchTitle,
Dependency_String('https://download.visualstudio.microsoft.com/download/pr/10912113/5da66ddebb0ad32ebd4b922fd82e8e25/vcredist_x86.exe', 'https://download.visualstudio.microsoft.com/download/pr/10912041/cee5d6bca2ddbcd039da727bf4acb48a/vcredist_x64.exe'),
- '', False, False);
+ '', False);
end;
end;
@@ -296,288 +690,61 @@ begin
'/passive /norestart',
'Visual C++ 2015-2019 Redistributable' + Dependency_ArchTitle,
Dependency_String('https://aka.ms/vs/16/release/vc_redist.x86.exe', 'https://aka.ms/vs/16/release/vc_redist.x64.exe'),
- '', False, False);
+ '', False);
end;
end;
procedure Dependency_AddDirectX;
begin
// https://www.microsoft.com/en-US/download/details.aspx?id=35
- Dependency_Add('dxwebsetup.exe',
+ Dependency_Add_With_Version('dxwebsetup.exe', '{#NewDirectXVersion}', regGetInstalledVersion('{#DirectXName}'),
'/q',
- 'DirectX Runtime',
- 'https://download.microsoft.com/download/1/7/1/1718CCC4-6315-4D8E-9543-8E28A4E18C4C/dxwebsetup.exe',
+ '{#DirectXName}',
+ '{#DirectXDownloadLink}',
'', True, False);
end;
procedure Dependency_AddHideHide;
begin
- Dependency_Add('HidHide_1.4.202_x64.exe',
+ Dependency_Add_With_Version('HidHide_1.5.212_x64.exe', '{#NewHidHideVersion}', regGetInstalledVersion('{#HidHideName}'),
'/quiet /norestart',
- 'HidHide Drivers',
- 'https://github.com/nefarius/HidHide/releases/download/v1.4.202.0/HidHide_1.4.202_x64.exe',
+ '{#HidHideName}',
+ '{#HidHideDownloadLink}',
'', True, False);
end;
procedure Dependency_AddViGem;
begin
- Dependency_Add('ViGEmBus_1.22.0_x64_x86_arm64.exe',
+ Dependency_Add_With_Version('ViGEmBus_1.22.0_x64_x86_arm64.exe', '{#NewViGemVersion}', regGetInstalledVersion('{#ViGemName}'),
'/quiet /norestart',
- 'ViGEmBus Setup',
- 'https://github.com/nefarius/ViGEmBus/releases/download/v1.22.0/ViGEmBus_1.22.0_x64_x86_arm64.exe',
- '', True, False);
+ '{#ViGemName}',
+ '{#ViGemDownloadLink}',
+ '', True, True);
end;
procedure Dependency_AddRTSS;
begin
- Dependency_Add('RTSSSetup735Beta5.exe',
+ Dependency_Add_With_Version('RTSSSetup735.exe', '{#NewRtssVersion}', regGetInstalledVersion('{#RtssName}'),
'/S',
- 'RTSS Setup v7.3.5 Beta5',
- 'https://github.com/Valkirie/HandheldCompanion/raw/main/redist/RTSSSetup735Beta5.exe',
- '', True, False);
-end;
-
-[Setup]
-; -------------
-; SETUP
-; -------------
-#ifndef Dependency_NoExampleSetup
-
-; requires netcorecheck.exe and netcorecheck_x64.exe (see download link below)
-#define UseNetCoreCheck
-#ifdef UseNetCoreCheck
- #define UseDotNet80
-#endif
-
-;#define UseVC2005
-;#define UseVC2008
-;#define UseVC2010
-;#define UseVC2012
-;#define UseVC2013
-;#define UseVC2015To2019
-
-#define UseDirectX
-; install ViGem first
-#define UseViGem
-#define UseHideHide
-#define UseRTSS
-
-#define MyAppSetupName 'Handheld Companion'
-#define MyBuildId 'HandheldCompanion'
-#define MyAppVersion '0.20.4.1'
-#define MyAppPublisher 'BenjaminLSR'
-#define MyAppCopyright 'Copyright @ BenjaminLSR'
-#define MyAppURL 'https://github.com/Valkirie/HandheldCompanion'
-#define MyAppExeName "HandheldCompanion.exe"
-#define MyConfiguration "Release"
-
-#ifdef UseDotNet80
- #define MyConfigurationExt "net8.0"
-#endif
-
-AppName={#MyAppSetupName}
-AppVersion={#MyAppVersion}
-AppVerName={#MyAppSetupName}
-AppCopyright={#MyAppCopyright}
-VersionInfoVersion={#MyAppVersion}
-VersionInfoCompany={#MyAppPublisher}
-AppPublisher={#MyAppPublisher}
-AppPublisherURL={#MyAppURL}
-AppSupportURL={#MyAppURL}
-AppUpdatesURL={#MyAppURL}
-OutputBaseFilename={#MyBuildId}-{#MyAppVersion}
-DefaultGroupName={#MyAppSetupName}
-DefaultDirName={autopf}\{#MyAppSetupName}
-UninstallDisplayIcon={app}\{#MyAppExeName}
-SetupIconFile="{#SourcePath}\HandheldCompanion\Resources\icon.ico"
-SourceDir=redist
-OutputDir={#SourcePath}\install
-AllowNoIcons=yes
-MinVersion=6.0
-;PrivilegesRequired=admin
-PrivilegesRequiredOverridesAllowed=dialog
-Compression=lzma
-SolidCompression=yes
-
-// remove next line if you only deploy 32-bit binaries and dependencies
-ArchitecturesInstallIn64BitMode=x64
-
-[Languages]
-Name: en; MessagesFile: "compiler:Default.isl"
-
-[Setup]
-AlwaysRestart = yes
-CloseApplications = yes
-
-[Files]
-#ifdef UseNetCoreCheck
-// download netcorecheck.exe: https://go.microsoft.com/fwlink/?linkid=2135256
-// download netcorecheck_x64.exe: https://go.microsoft.com/fwlink/?linkid=2135504
-Source: "netcorecheck.exe"; Flags: dontcopy noencryption
-Source: "netcorecheck_x64.exe"; Flags: dontcopy noencryption
-#endif
-
-Source: "{#SourcePath}\bin\{#MyConfiguration}\{#MyConfigurationExt}-windows10.0.19041.0\WinRing0x64.dll"; DestDir: "{app}"; Flags: onlyifdoesntexist
-Source: "{#SourcePath}\bin\{#MyConfiguration}\{#MyConfigurationExt}-windows10.0.19041.0\WinRing0x64.sys"; DestDir: "{app}"; Flags: onlyifdoesntexist
-Source: "{#SourcePath}\bin\{#MyConfiguration}\{#MyConfigurationExt}-windows10.0.19041.0\*"; Excludes: "*WinRing0x64.*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
-
-Source: "{#SourcePath}\redist\SegoeIcons.ttf"; DestDir: "{autofonts}"; FontInstall: "Segoe Fluent Icons (TrueType)"; Flags: onlyifdoesntexist uninsneveruninstall
-Source: "{#SourcePath}\redist\PromptFont.otf"; DestDir: "{autofonts}"; FontInstall: "PromptFont"; Flags: uninsneveruninstall
-
-[Icons]
-Name: "{group}\{#MyAppSetupName}"; Filename: "{app}\{#MyAppExeName}"
-Name: "{group}\{cm:UninstallProgram,{#MyAppSetupName}}"; Filename: "{uninstallexe}"
-Name: "{commondesktop}\{#MyAppSetupName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
-
-[Tasks]
-Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"
-
-[UninstallRun]
-Filename: "C:\Program Files\Nefarius Software Solutions\HidHide\x64\HidHideCLI.exe"; Parameters: "--cloak-off" ; RunOnceId: "CloakOff"; Flags: runascurrentuser runhidden
-
-[UninstallDelete]
-Type: filesandordirs; Name: "{app}"
-
-[Registry]
-Root: HKLM; Subkey: "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps"; Flags: uninsdeletekeyifempty
-Root: HKLM; Subkey: "Software\Microsoft\Windows\Windows Error Reporting\LocalDumps\HandheldCompanion.exe"; ValueType: string; ValueName: "DumpFolder"; ValueData: "{userdocs}\HandheldCompanion\dumps"; Flags: uninsdeletekey
-
-[Code]
-#include "./UpdateUninstallWizard.iss"
-
-procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
-var
- resultCode:integer;
-begin
- if CurUninstallStep = usUninstall then
- begin
- if not(checkListBox.checked[keepAllCheck]) then
- begin
- if DirExists(ExpandConstant('{userdocs}\{#MyBuildId}\profiles')) then
- DelTree(ExpandConstant('{userdocs}\{#MyBuildId}\profiles'), True, True, True);
- if DirExists(ExpandConstant('{userdocs}\{#MyBuildId}\hotkeys')) then
- DelTree(ExpandConstant('{userdocs}\{#MyBuildId}\hotkeys'), True, True, True);
- DelTree(ExpandConstant('{localappdata}\HandheldCompanion'), True, True, True);
- exit;
- end
- else
- begin
- if not(checkListBox.checked[profilesCheck]) then
- begin
- if DirExists(ExpandConstant('{userdocs}\{#MyBuildId}\profiles')) then
- DelTree(ExpandConstant('{userdocs}\{#MyBuildId}\profiles'), True, True, True);
- end;
-
- if not(checkListBox.checked[hotkeysCheck]) then
- begin
- if DirExists(ExpandConstant('{userdocs}\{#MyBuildId}\hotkeys')) then
- DelTree(ExpandConstant('{userdocs}\{#MyBuildId}\hotkeys'), True, True, True);
- end;
-
- if not(checkListBox.checked[applicationSettingsCheck]) then
- begin
- DelTree(ExpandConstant('{localappdata}\HandheldCompanion'), True, True, True);
- end;
- end;
-
- if not(keepHidhideCheckbox.Checked) then
- begin
- if(ShellExec('', 'msiexec.exe', '/X{41DC2CF5-D952-4EC5-B90B-136E59430EA0} /L*V "C:\HidHide-Uninstall.log"', '', SW_SHOW, ewWaitUntilTerminated, resultCode)) then
- begin
- log('Successfully executed Hidhide uninstaller');
- if(resultCode = 0) then
- log('Hidhide uninstaller finished successfully')
- else
- log('Hidhide uninstaller failed with exit code ' +intToStr(resultCode));
- end
- else
- begin
- log('Failed to execute Hidhide uninstaller');
- end;
- end;
-
- if not(keepVigemCheckbox.Checked) then
- begin
- if(ShellExec('', 'msiexec.exe', '/X{966606F3-2745-49E9-BF15-5C3EAA4E9077}', '', SW_SHOW, ewWaitUntilTerminated, resultCode)) then
- begin
- log('Successfully executed Vigem uninstaller');
- if(resultCode = 0) then
- log('Vigem uninstaller finished successfully')
- else
- log('Vigem uninstaller failed with exit code ' +intToStr(resultCode));
- end
- else
- begin
- log('Failed to execute Vigem uninstaller');
- end;
- end;
+ '{#RtssName}',
+ '{#RtssDownloadLink}',
+ '', True, False);
+
+ stopProcess('{#EncoderServer64Exe}');
+ stopProcess('{#RTSSHooksLoader64Exe}');
+ stopProcess('{#EncoderServerExe}');
+ stopProcess('{#RTSSHooksLoaderExe}');
+
+ if(isProcessRunning('{#RtssExe}')) then
+ begin
+ stopProcess('{#RtssExe}');
end;
end;
-
-
-procedure InitializeWizard;
-begin
- Dependency_InitializeWizard;
-end;
-
-function PrepareToInstall(var NeedsRestart: Boolean): String;
+
+function BoolToStr(Value: Boolean): String;
begin
- Result := Dependency_PrepareToInstall(NeedsRestart);
-end;
-
-function NeedRestart: Boolean;
-begin
- Result := Dependency_NeedRestart;
-end;
-
-function UpdateReadyMemo(const Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
-begin
- Result := Dependency_UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo);
+ if Value then
+ Result := 'Yes'
+ else
+ Result := 'No';
end;
-
-function InitializeSetup: Boolean;
-begin
-
-#ifdef UseDotNet80
- Dependency_AddDotNet80Desktop;
-#endif
-
-#ifdef UseVC2005
- Dependency_AddVC2005;
-#endif
-#ifdef UseVC2008
- Dependency_AddVC2008;
-#endif
-#ifdef UseVC2010
- Dependency_AddVC2010;
-#endif
-#ifdef UseVC2012
- Dependency_AddVC2012;
-#endif
-#ifdef UseVC2013
- Dependency_AddVC2013;
-#endif
-#ifdef UseVC2015To2019
- Dependency_AddVC2015To2019;
-#endif
-
-#ifdef UseDirectX
- Dependency_AddDirectX;
-#endif
-
-#ifdef UseHideHide
- Dependency_AddHideHide;
-#endif
-
-#ifdef UseViGem
- Dependency_AddViGem;
-#endif
-
-#ifdef UseRTSS
- Dependency_AddRTSS;
-#endif
-
- Result := True;
-end;
-
-#endif
diff --git a/HandheldCompanion/ADLX/ADLXBackend.cs b/HandheldCompanion/ADLX/ADLXBackend.cs
index b0353e79e..64d3ca68d 100644
--- a/HandheldCompanion/ADLX/ADLXBackend.cs
+++ b/HandheldCompanion/ADLX/ADLXBackend.cs
@@ -1,134 +1,134 @@
-using System.Runtime.InteropServices;
-using System.Text;
-
-namespace HandheldCompanion.ADLX
-{
- public class ADLXBackend
- {
- public const string ADLX_Wrapper = @"ADLX_Wrapper.dll";
-
- [StructLayout(LayoutKind.Sequential)]
- public struct AdlxTelemetryData
- {
- // GPU Usage
- public bool gpuUsageSupported;
- public double gpuUsageValue;
-
- // GPU Core Frequency
- public bool gpuClockSpeedSupported;
- public double gpuClockSpeedValue;
-
- // GPU VRAM Frequency
- public bool gpuVRAMClockSpeedSupported;
- public double gpuVRAMClockSpeedValue;
-
- // GPU Core Temperature
- public bool gpuTemperatureSupported;
- public double gpuTemperatureValue;
-
- // GPU Hotspot Temperature
- public bool gpuHotspotTemperatureSupported;
- public double gpuHotspotTemperatureValue;
-
- // GPU Power
- public bool gpuPowerSupported;
- public double gpuPowerValue;
-
- // Fan Speed
- public bool gpuFanSpeedSupported;
- public double gpuFanSpeedValue;
-
- // VRAM Usage
- public bool gpuVramSupported;
- public double gpuVramValue;
-
- // GPU Voltage
- public bool gpuVoltageSupported;
- public double gpuVoltageValue;
-
- // GPU TBP
- public bool gpuTotalBoardPowerSupported;
- public double gpuTotalBoardPowerValue;
- }
-
- public enum ADLX_RESULT
- {
- ADLX_OK = 0, /**< @ENG_START_DOX This result indicates success. @ENG_END_DOX */
- ADLX_ALREADY_ENABLED, /**< @ENG_START_DOX This result indicates that the asked action is already enabled. @ENG_END_DOX */
- ADLX_ALREADY_INITIALIZED, /**< @ENG_START_DOX This result indicates that ADLX has a unspecified type of initialization. @ENG_END_DOX */
- ADLX_FAIL, /**< @ENG_START_DOX This result indicates an unspecified failure. @ENG_END_DOX */
- ADLX_INVALID_ARGS, /**< @ENG_START_DOX This result indicates that the arguments are invalid. @ENG_END_DOX */
- ADLX_BAD_VER, /**< @ENG_START_DOX This result indicates that the asked version is incompatible with the current version. @ENG_END_DOX */
- ADLX_UNKNOWN_INTERFACE, /**< @ENG_START_DOX This result indicates that an unknown interface was asked. @ENG_END_DOX */
- ADLX_TERMINATED, /**< @ENG_START_DOX This result indicates that the calls were made in an interface after ADLX was terminated. @ENG_END_DOX */
- ADLX_ADL_INIT_ERROR, /**< @ENG_START_DOX This result indicates that the ADL initialization failed. @ENG_END_DOX */
- ADLX_NOT_FOUND, /**< @ENG_START_DOX This result indicates that the item is not found. @ENG_END_DOX */
- ADLX_INVALID_OBJECT, /**< @ENG_START_DOX This result indicates that the method was called into an invalid object. @ENG_END_DOX */
- ADLX_ORPHAN_OBJECTS, /**< @ENG_START_DOX This result indicates that ADLX was terminated with outstanding ADLX objects. Any interface obtained from ADLX points to invalid memory and calls in their methods will result in unexpected behavior. @ENG_END_DOX */
- ADLX_NOT_SUPPORTED, /**< @ENG_START_DOX This result indicates that the asked feature is not supported. @ENG_END_DOX */
- ADLX_PENDING_OPERATION, /**< @ENG_START_DOX This result indicates a failure due to an operation currently in progress. @ENG_END_DOX */
- ADLX_GPU_INACTIVE /**< @ENG_START_DOX This result indicates that the GPU is inactive. @ENG_END_DOX */
- }
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool IntializeAdlx();
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool CloseAdlx();
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern ADLX_RESULT GetNumberOfDisplays(ref int displayNum);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
- public static extern ADLX_RESULT GetDisplayName(int idx, StringBuilder dispName, int nameLength);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)]
- public static extern ADLX_RESULT GetDisplayGPU(int idx, ref int UniqueId);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)]
- public static extern ADLX_RESULT GetGPUIndex(int UniqueId, ref int idx);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasRSRSupport();
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetRSR();
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetRSR(bool enable);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetRSRSharpness();
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetRSRSharpness(int sharpness);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetAntiLag(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetAntiLag(int GPU, bool enable);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetBoost(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetBoost(int GPU, bool enable);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetBoostResolution(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetBoostResolution(int GPU, int minRes);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetChill(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetChill(int GPU, bool enable);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetChillMinFPS(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetChillMinFPS(int GPU, int minFPS);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetChillMaxFPS(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetChillMaxFPS(int GPU, int maxFPS);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetImageSharpening(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetImageSharpening(int GPU, bool enable);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetImageSharpeningSharpness(int GPU);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetImageSharpeningSharpness(int GPU, int sharpness);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasIntegerScalingSupport(int displayIdx);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetIntegerScaling(int displayIdx);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetIntegerScaling(int displayIdx, bool enabled);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasGPUScalingSupport(int displayIdx);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetGPUScaling(int displayIdx);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetGPUScaling(int displayIdx, bool enabled);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasScalingModeSupport(int displayIdx);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetScalingMode(int displayIdx);
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetScalingMode(int displayIdx, int mode);
-
- [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetAdlxTelemetry(int GPU, ref AdlxTelemetryData adlxTelemetryData);
-
- internal static AdlxTelemetryData GetTelemetryData()
- {
- AdlxTelemetryData TelemetryData = new();
- bool Result = GetAdlxTelemetry(0, ref TelemetryData);
- return TelemetryData;
- }
- }
-}
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace HandheldCompanion.ADLX
+{
+ public class ADLXBackend
+ {
+ public const string ADLX_Wrapper = @"ADLX_Wrapper.dll";
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct AdlxTelemetryData
+ {
+ // GPU Usage
+ public bool gpuUsageSupported;
+ public double gpuUsageValue;
+
+ // GPU Core Frequency
+ public bool gpuClockSpeedSupported;
+ public double gpuClockSpeedValue;
+
+ // GPU VRAM Frequency
+ public bool gpuVRAMClockSpeedSupported;
+ public double gpuVRAMClockSpeedValue;
+
+ // GPU Core Temperature
+ public bool gpuTemperatureSupported;
+ public double gpuTemperatureValue;
+
+ // GPU Hotspot Temperature
+ public bool gpuHotspotTemperatureSupported;
+ public double gpuHotspotTemperatureValue;
+
+ // GPU Power
+ public bool gpuPowerSupported;
+ public double gpuPowerValue;
+
+ // Fan Speed
+ public bool gpuFanSpeedSupported;
+ public double gpuFanSpeedValue;
+
+ // VRAM Usage
+ public bool gpuVramSupported;
+ public double gpuVramValue;
+
+ // GPU Voltage
+ public bool gpuVoltageSupported;
+ public double gpuVoltageValue;
+
+ // GPU TBP
+ public bool gpuTotalBoardPowerSupported;
+ public double gpuTotalBoardPowerValue;
+ }
+
+ public enum ADLX_RESULT
+ {
+ ADLX_OK = 0, /**< @ENG_START_DOX This result indicates success. @ENG_END_DOX */
+ ADLX_ALREADY_ENABLED, /**< @ENG_START_DOX This result indicates that the asked action is already enabled. @ENG_END_DOX */
+ ADLX_ALREADY_INITIALIZED, /**< @ENG_START_DOX This result indicates that ADLX has a unspecified type of initialization. @ENG_END_DOX */
+ ADLX_FAIL, /**< @ENG_START_DOX This result indicates an unspecified failure. @ENG_END_DOX */
+ ADLX_INVALID_ARGS, /**< @ENG_START_DOX This result indicates that the arguments are invalid. @ENG_END_DOX */
+ ADLX_BAD_VER, /**< @ENG_START_DOX This result indicates that the asked version is incompatible with the current version. @ENG_END_DOX */
+ ADLX_UNKNOWN_INTERFACE, /**< @ENG_START_DOX This result indicates that an unknown interface was asked. @ENG_END_DOX */
+ ADLX_TERMINATED, /**< @ENG_START_DOX This result indicates that the calls were made in an interface after ADLX was terminated. @ENG_END_DOX */
+ ADLX_ADL_INIT_ERROR, /**< @ENG_START_DOX This result indicates that the ADL initialization failed. @ENG_END_DOX */
+ ADLX_NOT_FOUND, /**< @ENG_START_DOX This result indicates that the item is not found. @ENG_END_DOX */
+ ADLX_INVALID_OBJECT, /**< @ENG_START_DOX This result indicates that the method was called into an invalid object. @ENG_END_DOX */
+ ADLX_ORPHAN_OBJECTS, /**< @ENG_START_DOX This result indicates that ADLX was terminated with outstanding ADLX objects. Any interface obtained from ADLX points to invalid memory and calls in their methods will result in unexpected behavior. @ENG_END_DOX */
+ ADLX_NOT_SUPPORTED, /**< @ENG_START_DOX This result indicates that the asked feature is not supported. @ENG_END_DOX */
+ ADLX_PENDING_OPERATION, /**< @ENG_START_DOX This result indicates a failure due to an operation currently in progress. @ENG_END_DOX */
+ ADLX_GPU_INACTIVE /**< @ENG_START_DOX This result indicates that the GPU is inactive. @ENG_END_DOX */
+ }
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool IntializeAdlx();
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool CloseAdlx();
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern ADLX_RESULT GetNumberOfDisplays(ref int displayNum);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+ public static extern ADLX_RESULT GetDisplayName(int idx, StringBuilder dispName, int nameLength);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)]
+ public static extern ADLX_RESULT GetDisplayGPU(int idx, ref int UniqueId);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)]
+ public static extern ADLX_RESULT GetGPUIndex(int UniqueId, ref int idx);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasRSRSupport();
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetRSR();
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetRSR(bool enable);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetRSRSharpness();
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetRSRSharpness(int sharpness);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetAntiLag(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetAntiLag(int GPU, bool enable);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetBoost(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetBoost(int GPU, bool enable);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetBoostResolution(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetBoostResolution(int GPU, int minRes);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetChill(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetChill(int GPU, bool enable);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetChillMinFPS(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetChillMinFPS(int GPU, int minFPS);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetChillMaxFPS(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetChillMaxFPS(int GPU, int maxFPS);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetImageSharpening(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetImageSharpening(int GPU, bool enable);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetImageSharpeningSharpness(int GPU);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetImageSharpeningSharpness(int GPU, int sharpness);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasIntegerScalingSupport(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetIntegerScaling(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetIntegerScaling(int displayIdx, bool enabled);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasGPUScalingSupport(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetGPUScaling(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetGPUScaling(int displayIdx, bool enabled);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool HasScalingModeSupport(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern int GetScalingMode(int displayIdx);
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool SetScalingMode(int displayIdx, int mode);
+
+ [DllImport(ADLX_Wrapper, CallingConvention = CallingConvention.Cdecl)] public static extern bool GetAdlxTelemetry(int GPU, ref AdlxTelemetryData adlxTelemetryData);
+
+ internal static AdlxTelemetryData GetTelemetryData()
+ {
+ AdlxTelemetryData TelemetryData = new();
+ bool Result = GetAdlxTelemetry(0, ref TelemetryData);
+ return TelemetryData;
+ }
+ }
+}
diff --git a/HandheldCompanion/Actions/GyroActions.cs b/HandheldCompanion/Actions/GyroActions.cs
index 7b75a9478..b889ba6a6 100644
--- a/HandheldCompanion/Actions/GyroActions.cs
+++ b/HandheldCompanion/Actions/GyroActions.cs
@@ -1,32 +1,32 @@
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Utils;
-using System;
-
-namespace HandheldCompanion.Actions
-{
- [Serializable]
- public class GyroActions : IActions
- {
- public MotionInput MotionInput = MotionInput.LocalSpace;
- public MotionMode MotionMode = MotionMode.Off;
- public bool MotionToggleStatus = false;
- public bool MotionTogglePressed = false; // for debouncing
-
- public ButtonState MotionTrigger = new();
-
- public float gyroWeight = DefaultGyroWeight;
-
- // const vars
- public const int DefaultAxisAntiDeadZone = 15;
- public const AxisLayoutFlags DefaultAxisLayoutFlags = AxisLayoutFlags.RightStick;
-
- public const MouseActionsType DefaultMouseActionsType = MouseActionsType.Move;
- public const int DefaultSensivity = 33;
- public const int DefaultDeadzone = 10;
- public const float DefaultGyroWeight = 1.2f;
-
- public GyroActions()
- {
- }
- }
-}
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Utils;
+using System;
+
+namespace HandheldCompanion.Actions
+{
+ [Serializable]
+ public class GyroActions : IActions
+ {
+ public MotionInput MotionInput = MotionInput.LocalSpace;
+ public MotionMode MotionMode = MotionMode.Off;
+ public bool MotionToggleStatus = false;
+ public bool MotionTogglePressed = false; // for debouncing
+
+ public ButtonState MotionTrigger = new();
+
+ public float gyroWeight = DefaultGyroWeight;
+
+ // const vars
+ public const int DefaultAxisAntiDeadZone = 15;
+ public const AxisLayoutFlags DefaultAxisLayoutFlags = AxisLayoutFlags.RightStick;
+
+ public const MouseActionsType DefaultMouseActionsType = MouseActionsType.Move;
+ public const int DefaultSensivity = 33;
+ public const int DefaultDeadzone = 10;
+ public const float DefaultGyroWeight = 1.2f;
+
+ public GyroActions()
+ {
+ }
+ }
+}
diff --git a/HandheldCompanion/Actions/MouseActions.cs b/HandheldCompanion/Actions/MouseActions.cs
index ae20b4839..97fdc8c00 100644
--- a/HandheldCompanion/Actions/MouseActions.cs
+++ b/HandheldCompanion/Actions/MouseActions.cs
@@ -1,206 +1,206 @@
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Simulators;
-using System;
-using System.ComponentModel;
-using System.Numerics;
-using WindowsInput.Events;
-
-namespace HandheldCompanion.Actions
-{
- [Serializable]
- public enum MouseActionsType
- {
- [Description("Left Button")]
- LeftButton = 1,
- [Description("Right Button")]
- RightButton = 2,
- [Description("Middle Button")]
- MiddleButton = 3,
-
- [Description("Move Cursor")]
- Move = 4,
- [Description("Scroll Wheel")]
- Scroll = 5,
-
- [Description("Scroll Up")]
- ScrollUp = 6,
- [Description("Scroll Down")]
- ScrollDown = 7,
- }
-
- [Serializable]
- public class MouseActions : GyroActions
- {
- public MouseActionsType MouseType;
-
- // const settings
- private const int scrollAmountInClicks = 20;
- private const float FilterBeta = 0.5f;
-
- // runtime variables
- private bool IsCursorDown = false;
- private bool IsTouched = false;
- private Vector2 remainder = new();
- private KeyCode[] pressed;
- private OneEuroFilterPair mouseFilter;
- private Vector2 prevVector = new();
-
- // settings click
- public ModifierSet Modifiers = ModifierSet.None;
-
- // settings axis
- public int Sensivity = 33;
- public float Acceleration = 1.0f;
- public int Deadzone = 10; // stick only
- public bool Filtering = false; // pad only
- public float FilterCutoff = 0.05f; // pad only
- public bool AxisRotated = false;
- public bool AxisInverted = false;
-
- public MouseActions()
- {
- this.actionType = ActionType.Mouse;
-
- this.Value = false;
- this.prevValue = false;
-
- mouseFilter = new(FilterCutoff, FilterBeta);
- }
-
- public MouseActions(MouseActionsType type) : this()
- {
- this.MouseType = type;
- }
-
- public override void Execute(ButtonFlags button, bool value)
- {
- base.Execute(button, value);
-
- switch (this.Value)
- {
- case true:
- {
- if (IsCursorDown)
- return;
-
- IsCursorDown = true;
- pressed = ModifierMap[Modifiers];
- KeyboardSimulator.KeyDown(pressed);
- MouseSimulator.MouseDown(MouseType, scrollAmountInClicks);
- SetHaptic(button, false);
- }
- break;
- case false:
- {
- if (!IsCursorDown)
- return;
-
- IsCursorDown = false;
- MouseSimulator.MouseUp(MouseType);
- KeyboardSimulator.KeyUp(pressed);
- SetHaptic(button, true);
- }
- break;
- }
- }
-
- private bool IsNewTouch(bool value)
- {
- if (value == IsTouched)
- return false;
- if (IsTouched = value)
- return true;
- return false;
- }
-
- public void Execute(AxisLayout layout, bool touched)
- {
- // this line needs to be before the next vector zero check
- bool newTouch = IsNewTouch(touched);
-
- if (layout.vector == Vector2.Zero)
- return;
-
- layout.vector.Y *= -1;
-
- Vector2 deltaVector;
- float sensitivityFinetune;
-
- switch (layout.flags)
- {
- case AxisLayoutFlags.LeftStick:
- case AxisLayoutFlags.RightStick:
- case AxisLayoutFlags.Gyroscope:
- default:
- {
- // convert to <0.0-1.0> values
- deltaVector = layout.vector / short.MaxValue;
- float deadzone = Deadzone / 100.0f;
-
- // apply deadzone
- if (deltaVector.Length() < deadzone)
- return;
-
- deltaVector *= (deltaVector.Length() - deadzone) / deltaVector.Length(); // shorten by deadzone
- deltaVector *= 1.0f / (1.0f - deadzone); // rescale to 0.0 - 1.0
-
- sensitivityFinetune = (MouseType == MouseActionsType.Move ? 0.3f : 0.1f);
- }
- break;
-
- case AxisLayoutFlags.LeftPad:
- case AxisLayoutFlags.RightPad:
- {
- // touchpad was touched, update entry point for delta calculations
- if (newTouch)
- {
- prevVector = layout.vector;
- return;
- }
-
- // calculate delta and convert to <0.0-1.0> values
- deltaVector = (layout.vector - prevVector) / short.MaxValue;
- prevVector = layout.vector;
-
- sensitivityFinetune = (MouseType == MouseActionsType.Move ? 9.0f : 3.0f);
- }
- break;
- }
-
- if (Filtering)
- {
- mouseFilter.SetFilterCutoff(FilterCutoff);
- deltaVector.X = (float)mouseFilter.axis1Filter.Filter(deltaVector.X, 1);
- deltaVector.Y = (float)mouseFilter.axis2Filter.Filter(deltaVector.Y, 1);
- }
-
- if (Acceleration != 1.0f)
- {
- deltaVector.X = (float)(Math.Sign(deltaVector.X) * Math.Pow(Math.Abs(deltaVector.X), Acceleration));
- deltaVector.Y = (float)(Math.Sign(deltaVector.Y) * Math.Pow(Math.Abs(deltaVector.Y), Acceleration));
- sensitivityFinetune = (float)Math.Pow(sensitivityFinetune, Acceleration);
- }
-
- // apply sensitivity, rotation and slider finetune
- deltaVector *= Sensivity * sensitivityFinetune;
- if (AxisRotated)
- deltaVector = new(-deltaVector.Y, deltaVector.X);
- deltaVector *= (AxisInverted ? -1.0f : 1.0f);
-
- // handle the fact that MoveBy()/*Scroll() are int only and we can have movement (0 < abs(delta) < 1)
- deltaVector += remainder; // add partial previous step
- Vector2 intVector = new((int)Math.Truncate(deltaVector.X), (int)Math.Truncate(deltaVector.Y));
- remainder = deltaVector - intVector; // and save the unused rest
-
- if (MouseType == MouseActionsType.Move)
- {
- MouseSimulator.MoveBy((int)intVector.X, (int)intVector.Y);
- }
- else /* if (MouseType == MouseActionsType.Scroll) */
- {
- // MouseSimulator.HorizontalScroll((int)-intVector.X);
- MouseSimulator.VerticalScroll((int)-intVector.Y);
- }
- }
- }
-}
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Simulators;
+using System;
+using System.ComponentModel;
+using System.Numerics;
+using WindowsInput.Events;
+
+namespace HandheldCompanion.Actions
+{
+ [Serializable]
+ public enum MouseActionsType
+ {
+ [Description("Left Button")]
+ LeftButton = 1,
+ [Description("Right Button")]
+ RightButton = 2,
+ [Description("Middle Button")]
+ MiddleButton = 3,
+
+ [Description("Move Cursor")]
+ Move = 4,
+ [Description("Scroll Wheel")]
+ Scroll = 5,
+
+ [Description("Scroll Up")]
+ ScrollUp = 6,
+ [Description("Scroll Down")]
+ ScrollDown = 7,
+ }
+
+ [Serializable]
+ public class MouseActions : GyroActions
+ {
+ public MouseActionsType MouseType;
+
+ // const settings
+ private const int scrollAmountInClicks = 20;
+ private const float FilterBeta = 0.5f;
+
+ // runtime variables
+ private bool IsCursorDown = false;
+ private bool IsTouched = false;
+ private Vector2 remainder = new();
+ private KeyCode[] pressed;
+ private OneEuroFilterPair mouseFilter;
+ private Vector2 prevVector = new();
+
+ // settings click
+ public ModifierSet Modifiers = ModifierSet.None;
+
+ // settings axis
+ public int Sensivity = 33;
+ public float Acceleration = 1.0f;
+ public int Deadzone = 10; // stick only
+ public bool Filtering = false; // pad only
+ public float FilterCutoff = 0.05f; // pad only
+ public bool AxisRotated = false;
+ public bool AxisInverted = false;
+
+ public MouseActions()
+ {
+ this.actionType = ActionType.Mouse;
+
+ this.Value = false;
+ this.prevValue = false;
+
+ mouseFilter = new(FilterCutoff, FilterBeta);
+ }
+
+ public MouseActions(MouseActionsType type) : this()
+ {
+ this.MouseType = type;
+ }
+
+ public override void Execute(ButtonFlags button, bool value)
+ {
+ base.Execute(button, value);
+
+ switch (this.Value)
+ {
+ case true:
+ {
+ if (IsCursorDown)
+ return;
+
+ IsCursorDown = true;
+ pressed = ModifierMap[Modifiers];
+ KeyboardSimulator.KeyDown(pressed);
+ MouseSimulator.MouseDown(MouseType, scrollAmountInClicks);
+ SetHaptic(button, false);
+ }
+ break;
+ case false:
+ {
+ if (!IsCursorDown)
+ return;
+
+ IsCursorDown = false;
+ MouseSimulator.MouseUp(MouseType);
+ KeyboardSimulator.KeyUp(pressed);
+ SetHaptic(button, true);
+ }
+ break;
+ }
+ }
+
+ private bool IsNewTouch(bool value)
+ {
+ if (value == IsTouched)
+ return false;
+ if (IsTouched = value)
+ return true;
+ return false;
+ }
+
+ public void Execute(AxisLayout layout, bool touched)
+ {
+ // this line needs to be before the next vector zero check
+ bool newTouch = IsNewTouch(touched);
+
+ if (layout.vector == Vector2.Zero)
+ return;
+
+ layout.vector.Y *= -1;
+
+ Vector2 deltaVector;
+ float sensitivityFinetune;
+
+ switch (layout.flags)
+ {
+ case AxisLayoutFlags.LeftStick:
+ case AxisLayoutFlags.RightStick:
+ case AxisLayoutFlags.Gyroscope:
+ default:
+ {
+ // convert to <0.0-1.0> values
+ deltaVector = layout.vector / short.MaxValue;
+ float deadzone = Deadzone / 100.0f;
+
+ // apply deadzone
+ if (deltaVector.Length() < deadzone)
+ return;
+
+ deltaVector *= (deltaVector.Length() - deadzone) / deltaVector.Length(); // shorten by deadzone
+ deltaVector *= 1.0f / (1.0f - deadzone); // rescale to 0.0 - 1.0
+
+ sensitivityFinetune = (MouseType == MouseActionsType.Move ? 0.3f : 0.1f);
+ }
+ break;
+
+ case AxisLayoutFlags.LeftPad:
+ case AxisLayoutFlags.RightPad:
+ {
+ // touchpad was touched, update entry point for delta calculations
+ if (newTouch)
+ {
+ prevVector = layout.vector;
+ return;
+ }
+
+ // calculate delta and convert to <0.0-1.0> values
+ deltaVector = (layout.vector - prevVector) / short.MaxValue;
+ prevVector = layout.vector;
+
+ sensitivityFinetune = (MouseType == MouseActionsType.Move ? 9.0f : 3.0f);
+ }
+ break;
+ }
+
+ if (Filtering)
+ {
+ mouseFilter.SetFilterCutoff(FilterCutoff);
+ deltaVector.X = (float)mouseFilter.axis1Filter.Filter(deltaVector.X, 1);
+ deltaVector.Y = (float)mouseFilter.axis2Filter.Filter(deltaVector.Y, 1);
+ }
+
+ if (Acceleration != 1.0f)
+ {
+ deltaVector.X = (float)(Math.Sign(deltaVector.X) * Math.Pow(Math.Abs(deltaVector.X), Acceleration));
+ deltaVector.Y = (float)(Math.Sign(deltaVector.Y) * Math.Pow(Math.Abs(deltaVector.Y), Acceleration));
+ sensitivityFinetune = (float)Math.Pow(sensitivityFinetune, Acceleration);
+ }
+
+ // apply sensitivity, rotation and slider finetune
+ deltaVector *= Sensivity * sensitivityFinetune;
+ if (AxisRotated)
+ deltaVector = new(-deltaVector.Y, deltaVector.X);
+ deltaVector *= (AxisInverted ? -1.0f : 1.0f);
+
+ // handle the fact that MoveBy()/*Scroll() are int only and we can have movement (0 < abs(delta) < 1)
+ deltaVector += remainder; // add partial previous step
+ Vector2 intVector = new((int)Math.Truncate(deltaVector.X), (int)Math.Truncate(deltaVector.Y));
+ remainder = deltaVector - intVector; // and save the unused rest
+
+ if (MouseType == MouseActionsType.Move)
+ {
+ MouseSimulator.MoveBy((int)intVector.X, (int)intVector.Y);
+ }
+ else /* if (MouseType == MouseActionsType.Scroll) */
+ {
+ // MouseSimulator.HorizontalScroll((int)-intVector.X);
+ MouseSimulator.VerticalScroll((int)-intVector.Y);
+ }
+ }
+ }
+}
diff --git a/HandheldCompanion/Actions/SpecialActions.cs b/HandheldCompanion/Actions/SpecialActions.cs
deleted file mode 100644
index 3e1bd7f61..000000000
--- a/HandheldCompanion/Actions/SpecialActions.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Misc;
-using HandheldCompanion.Simulators;
-using System;
-using System.ComponentModel;
-using System.Numerics;
-
-namespace HandheldCompanion.Actions
-{
- [Serializable]
- public enum SpecialActionsType
- {
- [Description("Flick Stick")]
- FlickStick = 0,
- }
-
- [Serializable]
- public class SpecialActions : IActions
- {
- public SpecialActionsType SpecialType;
-
- // runtime variables
- private FlickStick flickStick = new();
- private float remainder = 0;
-
- // settings
- public float FlickSensitivity = 5.0f;
- public float SweepSensitivity = 5.0f;
- public float FlickThreshold = 0.75f;
- public int FlickSpeed = 100;
- public int FlickFrontAngleDeadzone = 15;
-
- public SpecialActions()
- {
- this.actionType = ActionType.Special;
- }
-
- public SpecialActions(SpecialActionsType type) : this()
- {
- this.SpecialType = type;
- }
-
- public void Execute(AxisLayout layout)
- {
- if (layout.vector == Vector2.Zero)
- return;
-
- float delta = flickStick.Handle(layout.vector, FlickSensitivity, SweepSensitivity,
- FlickThreshold, FlickSpeed, FlickFrontAngleDeadzone);
-
- delta += remainder;
- int intDelta = (int)Math.Truncate(delta);
- remainder = delta - intDelta;
-
- MouseSimulator.MoveBy(intDelta, 0);
- }
- }
-}
diff --git a/HandheldCompanion/App.xaml.cs b/HandheldCompanion/App.xaml.cs
index 53a794076..dc1168cf4 100644
--- a/HandheldCompanion/App.xaml.cs
+++ b/HandheldCompanion/App.xaml.cs
@@ -1,130 +1,130 @@
-using HandheldCompanion.Managers;
-using HandheldCompanion.Utils;
-using HandheldCompanion.Views;
-using System;
-using System.Diagnostics;
-using System.Globalization;
-using System.Reflection;
-using System.Threading;
-using System.Windows;
-using static HandheldCompanion.WinAPI;
-
-namespace HandheldCompanion;
-
-///
-/// Interaction logic for App.xaml
-///
-public partial class App : Application
-{
- ///
- /// Initializes the singleton application object. This is the first line of authored code
- /// executed, and as such is the logical equivalent of main() or WinMain().
- ///
- public App()
- {
- InitializeComponent();
- }
-
- ///
- /// Invoked when the application is launched normally by the end user. Other entry points
- /// will be used such as when the application is launched to open a specific file.
- ///
- /// Details about the launch request and process.
- protected override void OnStartup(StartupEventArgs args)
- {
- // get current assembly
- var CurrentAssembly = Assembly.GetExecutingAssembly();
- var fileVersionInfo = FileVersionInfo.GetVersionInfo(CurrentAssembly.Location);
-
- // initialize log
- LogManager.Initialize("HandheldCompanion");
- LogManager.LogInformation("{0} ({1})", CurrentAssembly.GetName(), fileVersionInfo.FileVersion);
-
- using (var process = Process.GetCurrentProcess())
- {
- // force high priority
- SetPriorityClass(process.Handle, (int)PriorityClass.HIGH_PRIORITY_CLASS);
-
- Process[] processes = Process.GetProcessesByName(process.ProcessName);
- if (processes.Length > 1)
- {
- using (Process prevProcess = processes[0])
- {
- nint handle = prevProcess.MainWindowHandle;
- if (ProcessUtils.IsIconic(handle))
- ProcessUtils.ShowWindow(handle, (int)ProcessUtils.ShowWindowCommands.Restored);
-
- // force close this process if we were able to bring previous process to foreground
- // kill previous process otherwise (means it's stalled)
- if (ProcessUtils.SetForegroundWindow(handle))
- process.Kill();
- else
- prevProcess.Kill();
-
- return;
- }
- }
- }
-
- // define culture settings
- var CurrentCulture = SettingsManager.GetString("CurrentCulture");
- var culture = CultureInfo.CurrentCulture;
-
- switch (CurrentCulture)
- {
- default:
- culture = new CultureInfo("en-US");
- break;
- case "fr-FR":
- case "en-US":
- case "zh-CN":
- case "zh-Hant":
- case "de-DE":
- case "it-IT":
- case "pt-BR":
- case "es-ES":
- case "ja-JP":
- case "ru-RU":
- culture = new CultureInfo(CurrentCulture);
- break;
- }
-
- Thread.CurrentThread.CurrentCulture = culture;
- Thread.CurrentThread.CurrentUICulture = culture;
- CultureInfo.DefaultThreadCurrentCulture = culture;
- CultureInfo.DefaultThreadCurrentUICulture = culture;
-
- // handle exceptions nicely
- var currentDomain = default(AppDomain);
- currentDomain = AppDomain.CurrentDomain;
- // Handler for unhandled exceptions.
- currentDomain.UnhandledException += CurrentDomain_UnhandledException;
- // Handler for exceptions in threads behind forms.
- System.Windows.Forms.Application.ThreadException += Application_ThreadException;
-
- MainWindow = new MainWindow(fileVersionInfo, CurrentAssembly);
- MainWindow.Show();
- }
-
- private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
- {
- var ex = default(Exception);
- ex = (Exception)e.Exception;
- if (ex.InnerException != null)
- {
- LogManager.LogCritical(ex.InnerException.Message + "\t" + ex.InnerException.StackTrace);
- }
- LogManager.LogCritical(ex.Message + "\t" + ex.StackTrace);
- }
-
- private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
- {
- var ex = default(Exception);
- ex = (Exception)e.ExceptionObject;
- if (ex.InnerException != null)
- {
- LogManager.LogCritical(ex.InnerException.Message + "\t" + ex.InnerException.StackTrace);
- }
- LogManager.LogCritical(ex.Message + "\t" + ex.StackTrace);
- }
+using HandheldCompanion.Managers;
+using HandheldCompanion.Utils;
+using HandheldCompanion.Views;
+using System;
+using System.Diagnostics;
+using System.Globalization;
+using System.Reflection;
+using System.Threading;
+using System.Windows;
+using static HandheldCompanion.WinAPI;
+
+namespace HandheldCompanion;
+
+///
+/// Interaction logic for App.xaml
+///
+public partial class App : Application
+{
+ ///
+ /// Initializes the singleton application object. This is the first line of authored code
+ /// executed, and as such is the logical equivalent of main() or WinMain().
+ ///
+ public App()
+ {
+ InitializeComponent();
+ }
+
+ ///
+ /// Invoked when the application is launched normally by the end user. Other entry points
+ /// will be used such as when the application is launched to open a specific file.
+ ///
+ /// Details about the launch request and process.
+ protected override void OnStartup(StartupEventArgs args)
+ {
+ // get current assembly
+ var CurrentAssembly = Assembly.GetExecutingAssembly();
+ var fileVersionInfo = FileVersionInfo.GetVersionInfo(CurrentAssembly.Location);
+
+ // initialize log
+ LogManager.Initialize("HandheldCompanion");
+ LogManager.LogInformation("{0} ({1})", CurrentAssembly.GetName(), fileVersionInfo.FileVersion);
+
+ using (var process = Process.GetCurrentProcess())
+ {
+ // force high priority
+ SetPriorityClass(process.Handle, (int)PriorityClass.HIGH_PRIORITY_CLASS);
+
+ Process[] processes = Process.GetProcessesByName(process.ProcessName);
+ if (processes.Length > 1)
+ {
+ using (Process prevProcess = processes[0])
+ {
+ nint handle = prevProcess.MainWindowHandle;
+ if (ProcessUtils.IsIconic(handle))
+ ProcessUtils.ShowWindow(handle, (int)ProcessUtils.ShowWindowCommands.Restored);
+
+ // force close this process if we were able to bring previous process to foreground
+ // kill previous process otherwise (means it's stalled)
+ if (ProcessUtils.SetForegroundWindow(handle))
+ process.Kill();
+ else
+ prevProcess.Kill();
+
+ return;
+ }
+ }
+ }
+
+ // define culture settings
+ var CurrentCulture = SettingsManager.GetString("CurrentCulture");
+ var culture = CultureInfo.CurrentCulture;
+
+ switch (CurrentCulture)
+ {
+ default:
+ culture = new CultureInfo("en-US");
+ break;
+ case "fr-FR":
+ case "en-US":
+ case "zh-CN":
+ case "zh-Hant":
+ case "de-DE":
+ case "it-IT":
+ case "pt-BR":
+ case "es-ES":
+ case "ja-JP":
+ case "ru-RU":
+ culture = new CultureInfo(CurrentCulture);
+ break;
+ }
+
+ Thread.CurrentThread.CurrentCulture = culture;
+ Thread.CurrentThread.CurrentUICulture = culture;
+ CultureInfo.DefaultThreadCurrentCulture = culture;
+ CultureInfo.DefaultThreadCurrentUICulture = culture;
+
+ // handle exceptions nicely
+ var currentDomain = default(AppDomain);
+ currentDomain = AppDomain.CurrentDomain;
+ // Handler for unhandled exceptions.
+ currentDomain.UnhandledException += CurrentDomain_UnhandledException;
+ // Handler for exceptions in threads behind forms.
+ System.Windows.Forms.Application.ThreadException += Application_ThreadException;
+
+ MainWindow = new MainWindow(fileVersionInfo, CurrentAssembly);
+ MainWindow.Show();
+ }
+
+ private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
+ {
+ var ex = default(Exception);
+ ex = (Exception)e.Exception;
+ if (ex.InnerException != null)
+ {
+ LogManager.LogCritical(ex.InnerException.Message + "\t" + ex.InnerException.StackTrace);
+ }
+ LogManager.LogCritical(ex.Message + "\t" + ex.StackTrace);
+ }
+
+ private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ var ex = default(Exception);
+ ex = (Exception)e.ExceptionObject;
+ if (ex.InnerException != null)
+ {
+ LogManager.LogCritical(ex.InnerException.Message + "\t" + ex.InnerException.StackTrace);
+ }
+ LogManager.LogCritical(ex.Message + "\t" + ex.StackTrace);
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/DInputController.cs b/HandheldCompanion/Controllers/DInputController.cs
index 0c83ce3c7..7fa2c3ac2 100644
--- a/HandheldCompanion/Controllers/DInputController.cs
+++ b/HandheldCompanion/Controllers/DInputController.cs
@@ -1,71 +1,71 @@
-using SharpDX.DirectInput;
-
-namespace HandheldCompanion.Controllers;
-
-public class DInputController : IController
-{
- public Joystick joystick;
- protected JoystickState State = new();
-
- public DInputController()
- { }
-
- public DInputController(Joystick joystick, PnPDetails details)
- {
- if (joystick is null)
- return;
-
- this.joystick = joystick;
- UserIndex = (byte)joystick.Properties.JoystickId;
-
- if (details is null)
- return;
-
- Details = details;
- Details.isHooked = true;
-
- // Set BufferSize in order to use buffered data.
- joystick.Properties.BufferSize = 128;
-
- // UI
- DrawUI();
- UpdateUI();
- }
-
- public override string ToString()
- {
- var baseName = base.ToString();
- if (!string.IsNullOrEmpty(baseName))
- return baseName;
- if (!string.IsNullOrEmpty(joystick.Information.ProductName))
- return joystick.Information.ProductName;
- return $"DInput Controller {UserIndex}";
- }
-
- public override void UpdateInputs(long ticks, float delta)
- {
- base.UpdateInputs(ticks, delta);
- }
-
- public override bool IsConnected()
- {
- return (bool)!joystick?.IsDisposed;
- }
-
- public override void Plug()
- {
- if (joystick is not null)
- joystick.Acquire();
-
- base.Plug();
- }
-
- public override void Unplug()
- {
- // Unacquire the joystick
- if (joystick is not null)
- joystick.Unacquire();
-
- base.Unplug();
- }
+using SharpDX.DirectInput;
+
+namespace HandheldCompanion.Controllers;
+
+public class DInputController : IController
+{
+ public Joystick joystick;
+ protected JoystickState State = new();
+
+ public DInputController()
+ { }
+
+ public DInputController(Joystick joystick, PnPDetails details)
+ {
+ if (joystick is null)
+ return;
+
+ this.joystick = joystick;
+ UserIndex = (byte)joystick.Properties.JoystickId;
+
+ if (details is null)
+ return;
+
+ Details = details;
+ Details.isHooked = true;
+
+ // Set BufferSize in order to use buffered data.
+ joystick.Properties.BufferSize = 128;
+
+ // UI
+ DrawUI();
+ UpdateUI();
+ }
+
+ public override string ToString()
+ {
+ var baseName = base.ToString();
+ if (!string.IsNullOrEmpty(baseName))
+ return baseName;
+ if (!string.IsNullOrEmpty(joystick.Information.ProductName))
+ return joystick.Information.ProductName;
+ return $"DInput Controller {UserIndex}";
+ }
+
+ public override void UpdateInputs(long ticks, float delta)
+ {
+ base.UpdateInputs(ticks, delta);
+ }
+
+ public override bool IsConnected()
+ {
+ return (bool)!joystick?.IsDisposed;
+ }
+
+ public override void Plug()
+ {
+ if (joystick is not null)
+ joystick.Acquire();
+
+ base.Plug();
+ }
+
+ public override void Unplug()
+ {
+ // Unacquire the joystick
+ if (joystick is not null)
+ joystick.Unacquire();
+
+ base.Unplug();
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/DS4Controller.cs b/HandheldCompanion/Controllers/DS4Controller.cs
index cb307cc08..4ef3ea723 100644
--- a/HandheldCompanion/Controllers/DS4Controller.cs
+++ b/HandheldCompanion/Controllers/DS4Controller.cs
@@ -1,180 +1,180 @@
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Managers;
-using HandheldCompanion.Utils;
-using System.Windows.Media;
-using static JSL;
-
-namespace HandheldCompanion.Controllers;
-
-public class DS4Controller : JSController
-{
- public DS4Controller()
- { }
-
- public DS4Controller(JOY_SETTINGS settings, PnPDetails details) : base(settings, details)
- {
- // UI
- ColoredButtons.Add(ButtonFlags.B1, new SolidColorBrush(Color.FromArgb(255, 116, 139, 255)));
- ColoredButtons.Add(ButtonFlags.B2, new SolidColorBrush(Color.FromArgb(255, 255, 73, 75)));
- ColoredButtons.Add(ButtonFlags.B3, new SolidColorBrush(Color.FromArgb(255, 244, 149, 193)));
- ColoredButtons.Add(ButtonFlags.B4, new SolidColorBrush(Color.FromArgb(255, 73, 191, 115)));
- }
-
- protected override void InitializeInputOutput()
- {
- // Additional controller specific source buttons
- SourceButtons.Add(ButtonFlags.LeftPadClick);
- SourceButtons.Add(ButtonFlags.LeftPadTouch);
- SourceButtons.Add(ButtonFlags.RightPadClick);
- SourceButtons.Add(ButtonFlags.RightPadTouch);
-
- SourceAxis.Add(AxisLayoutFlags.LeftPad);
- SourceAxis.Add(AxisLayoutFlags.RightPad);
- SourceAxis.Add(AxisLayoutFlags.Gyroscope);
-
- TargetButtons.Add(ButtonFlags.LeftPadClick);
- TargetButtons.Add(ButtonFlags.LeftPadTouch);
- TargetButtons.Add(ButtonFlags.RightPadTouch);
-
- TargetAxis.Add(AxisLayoutFlags.LeftPad);
- TargetAxis.Add(AxisLayoutFlags.RightPad);
- }
-
- public override void UpdateInputs(long ticks, float delta)
- {
- // skip if controller isn't connected
- if (!IsConnected())
- return;
-
- base.UpdateState(delta);
-
- // Left Pad
- Inputs.ButtonState[ButtonFlags.LeftPadTouch] = JslGetTouchDown(UserIndex);
- Inputs.ButtonState[ButtonFlags.LeftPadClick] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskCapture);
-
- if (Inputs.ButtonState[ButtonFlags.LeftPadTouch])
- {
- float joyShockX0 = JslGetTouchX(UserIndex);
- float joyShockY0 = JslGetTouchY(UserIndex);
-
- Inputs.AxisState[AxisFlags.LeftPadX] = (short)InputUtils.MapRange(joyShockX0, 0.0f, 1.0f, short.MinValue, short.MaxValue);
- Inputs.AxisState[AxisFlags.LeftPadY] = (short)InputUtils.MapRange(joyShockY0, 0.0f, 1.0f, short.MaxValue, short.MinValue);
- }
- else
- {
- Inputs.AxisState[AxisFlags.LeftPadX] = 0;
- Inputs.AxisState[AxisFlags.LeftPadY] = 0;
- }
-
- // Right Pad
- Inputs.ButtonState[ButtonFlags.RightPadTouch] = JslGetTouchDown(UserIndex, true);
- Inputs.ButtonState[ButtonFlags.RightPadClick] = Inputs.ButtonState[ButtonFlags.LeftPadClick] && Inputs.ButtonState[ButtonFlags.RightPadTouch];
-
- if (Inputs.ButtonState[ButtonFlags.RightPadTouch])
- {
- float joyShockX1 = JslGetTouchX(UserIndex, true);
- float joyShockY1 = JslGetTouchY(UserIndex, true);
-
- Inputs.AxisState[AxisFlags.RightPadX] = (short)InputUtils.MapRange(joyShockX1, 0.0f, 1.0f, short.MinValue, short.MaxValue);
- Inputs.AxisState[AxisFlags.RightPadY] = (short)InputUtils.MapRange(joyShockY1, 0.0f, 1.0f, short.MaxValue, short.MinValue);
- }
- else
- {
- Inputs.AxisState[AxisFlags.RightPadX] = 0;
- Inputs.AxisState[AxisFlags.RightPadY] = 0;
- }
-
- base.UpdateInputs(ticks, delta);
- }
-
- public override string ToString()
- {
- return "DUALSHOCK®4 Wireless Controller";
- }
-
- public override void Plug()
- {
- TimerManager.Tick += UpdateInputs;
- base.Plug();
- }
-
- public override void Unplug()
- {
- TimerManager.Tick -= UpdateInputs;
- base.Unplug();
- }
-
- public override void SetLightColor(byte R, byte G, byte B)
- {
- JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B));
- }
-
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.B1:
- return "\u21E3"; // Cross
- case ButtonFlags.B2:
- return "\u21E2"; // Circle
- case ButtonFlags.B3:
- return "\u21E0"; // Square
- case ButtonFlags.B4:
- return "\u21E1"; // Triangle
- case ButtonFlags.L1:
- return "\u21B0";
- case ButtonFlags.R1:
- return "\u21B1";
- case ButtonFlags.Back:
- return "\u21E6";
- case ButtonFlags.Start:
- return "\u21E8";
- case ButtonFlags.L2Soft:
- return "\u21B2";
- case ButtonFlags.L2Full:
- return "\u21B2";
- case ButtonFlags.R2Soft:
- return "\u21B3";
- case ButtonFlags.R2Full:
- return "\u21B3";
- case ButtonFlags.Special:
- return "\uE000";
- case ButtonFlags.LeftPadClick:
- case ButtonFlags.RightPadClick:
- case ButtonFlags.LeftPadTouch:
- case ButtonFlags.RightPadTouch:
- return "\u21E7";
- }
-
- return base.GetGlyph(button);
- }
-
- public override string GetGlyph(AxisFlags axis)
- {
- switch (axis)
- {
- case AxisFlags.L2:
- return "\u21B2";
- case AxisFlags.R2:
- return "\u21B3";
- }
-
- return base.GetGlyph(axis);
- }
-
- public override string GetGlyph(AxisLayoutFlags axis)
- {
- switch (axis)
- {
- case AxisLayoutFlags.L2:
- return "\u21B2";
- case AxisLayoutFlags.R2:
- return "\u21B3";
- case AxisLayoutFlags.LeftPad:
- case AxisLayoutFlags.RightPad:
- return "\u21E7";
- }
-
- return base.GetGlyph(axis);
- }
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
+using HandheldCompanion.Utils;
+using System.Windows.Media;
+using static JSL;
+
+namespace HandheldCompanion.Controllers;
+
+public class DS4Controller : JSController
+{
+ public DS4Controller()
+ { }
+
+ public DS4Controller(JOY_SETTINGS settings, PnPDetails details) : base(settings, details)
+ {
+ // UI
+ ColoredButtons.Add(ButtonFlags.B1, new SolidColorBrush(Color.FromArgb(255, 116, 139, 255)));
+ ColoredButtons.Add(ButtonFlags.B2, new SolidColorBrush(Color.FromArgb(255, 255, 73, 75)));
+ ColoredButtons.Add(ButtonFlags.B3, new SolidColorBrush(Color.FromArgb(255, 244, 149, 193)));
+ ColoredButtons.Add(ButtonFlags.B4, new SolidColorBrush(Color.FromArgb(255, 73, 191, 115)));
+ }
+
+ protected override void InitializeInputOutput()
+ {
+ // Additional controller specific source buttons
+ SourceButtons.Add(ButtonFlags.LeftPadClick);
+ SourceButtons.Add(ButtonFlags.LeftPadTouch);
+ SourceButtons.Add(ButtonFlags.RightPadClick);
+ SourceButtons.Add(ButtonFlags.RightPadTouch);
+
+ SourceAxis.Add(AxisLayoutFlags.LeftPad);
+ SourceAxis.Add(AxisLayoutFlags.RightPad);
+ SourceAxis.Add(AxisLayoutFlags.Gyroscope);
+
+ TargetButtons.Add(ButtonFlags.LeftPadClick);
+ TargetButtons.Add(ButtonFlags.LeftPadTouch);
+ TargetButtons.Add(ButtonFlags.RightPadTouch);
+
+ TargetAxis.Add(AxisLayoutFlags.LeftPad);
+ TargetAxis.Add(AxisLayoutFlags.RightPad);
+ }
+
+ public override void UpdateInputs(long ticks, float delta)
+ {
+ // skip if controller isn't connected
+ if (!IsConnected())
+ return;
+
+ base.UpdateState(delta);
+
+ // Left Pad
+ Inputs.ButtonState[ButtonFlags.LeftPadTouch] = JslGetTouchDown(UserIndex);
+ Inputs.ButtonState[ButtonFlags.LeftPadClick] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskCapture);
+
+ if (Inputs.ButtonState[ButtonFlags.LeftPadTouch])
+ {
+ float joyShockX0 = JslGetTouchX(UserIndex);
+ float joyShockY0 = JslGetTouchY(UserIndex);
+
+ Inputs.AxisState[AxisFlags.LeftPadX] = (short)InputUtils.MapRange(joyShockX0, 0.0f, 1.0f, short.MinValue, short.MaxValue);
+ Inputs.AxisState[AxisFlags.LeftPadY] = (short)InputUtils.MapRange(joyShockY0, 0.0f, 1.0f, short.MaxValue, short.MinValue);
+ }
+ else
+ {
+ Inputs.AxisState[AxisFlags.LeftPadX] = 0;
+ Inputs.AxisState[AxisFlags.LeftPadY] = 0;
+ }
+
+ // Right Pad
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = JslGetTouchDown(UserIndex, true);
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = Inputs.ButtonState[ButtonFlags.LeftPadClick] && Inputs.ButtonState[ButtonFlags.RightPadTouch];
+
+ if (Inputs.ButtonState[ButtonFlags.RightPadTouch])
+ {
+ float joyShockX1 = JslGetTouchX(UserIndex, true);
+ float joyShockY1 = JslGetTouchY(UserIndex, true);
+
+ Inputs.AxisState[AxisFlags.RightPadX] = (short)InputUtils.MapRange(joyShockX1, 0.0f, 1.0f, short.MinValue, short.MaxValue);
+ Inputs.AxisState[AxisFlags.RightPadY] = (short)InputUtils.MapRange(joyShockY1, 0.0f, 1.0f, short.MaxValue, short.MinValue);
+ }
+ else
+ {
+ Inputs.AxisState[AxisFlags.RightPadX] = 0;
+ Inputs.AxisState[AxisFlags.RightPadY] = 0;
+ }
+
+ base.UpdateInputs(ticks, delta);
+ }
+
+ public override string ToString()
+ {
+ return "DUALSHOCK®4 Wireless Controller";
+ }
+
+ public override void Plug()
+ {
+ TimerManager.Tick += UpdateInputs;
+ base.Plug();
+ }
+
+ public override void Unplug()
+ {
+ TimerManager.Tick -= UpdateInputs;
+ base.Unplug();
+ }
+
+ public override void SetLightColor(byte R, byte G, byte B)
+ {
+ JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B));
+ }
+
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.B1:
+ return "\u21E3"; // Cross
+ case ButtonFlags.B2:
+ return "\u21E2"; // Circle
+ case ButtonFlags.B3:
+ return "\u21E0"; // Square
+ case ButtonFlags.B4:
+ return "\u21E1"; // Triangle
+ case ButtonFlags.L1:
+ return "\u21B0";
+ case ButtonFlags.R1:
+ return "\u21B1";
+ case ButtonFlags.Back:
+ return "\u21E6";
+ case ButtonFlags.Start:
+ return "\u21E8";
+ case ButtonFlags.L2Soft:
+ return "\u21B2";
+ case ButtonFlags.L2Full:
+ return "\u21B2";
+ case ButtonFlags.R2Soft:
+ return "\u21B3";
+ case ButtonFlags.R2Full:
+ return "\u21B3";
+ case ButtonFlags.Special:
+ return "\uE000";
+ case ButtonFlags.LeftPadClick:
+ case ButtonFlags.RightPadClick:
+ case ButtonFlags.LeftPadTouch:
+ case ButtonFlags.RightPadTouch:
+ return "\u21E7";
+ }
+
+ return base.GetGlyph(button);
+ }
+
+ public override string GetGlyph(AxisFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisFlags.L2:
+ return "\u21B2";
+ case AxisFlags.R2:
+ return "\u21B3";
+ }
+
+ return base.GetGlyph(axis);
+ }
+
+ public override string GetGlyph(AxisLayoutFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisLayoutFlags.L2:
+ return "\u21B2";
+ case AxisLayoutFlags.R2:
+ return "\u21B3";
+ case AxisLayoutFlags.LeftPad:
+ case AxisLayoutFlags.RightPad:
+ return "\u21E7";
+ }
+
+ return base.GetGlyph(axis);
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/DualSenseController.cs b/HandheldCompanion/Controllers/DualSenseController.cs
index 8599b4362..9c3dce616 100644
--- a/HandheldCompanion/Controllers/DualSenseController.cs
+++ b/HandheldCompanion/Controllers/DualSenseController.cs
@@ -1,180 +1,180 @@
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Managers;
-using HandheldCompanion.Utils;
-using System.Windows.Media;
-using static JSL;
-
-namespace HandheldCompanion.Controllers;
-
-public class DualSenseController : JSController
-{
- public DualSenseController()
- { }
-
- public DualSenseController(JOY_SETTINGS settings, PnPDetails details) : base(settings, details)
- {
- // UI
- ColoredButtons.Add(ButtonFlags.B1, new SolidColorBrush(Color.FromArgb(255, 116, 139, 255)));
- ColoredButtons.Add(ButtonFlags.B2, new SolidColorBrush(Color.FromArgb(255, 255, 73, 75)));
- ColoredButtons.Add(ButtonFlags.B3, new SolidColorBrush(Color.FromArgb(255, 244, 149, 193)));
- ColoredButtons.Add(ButtonFlags.B4, new SolidColorBrush(Color.FromArgb(255, 73, 191, 115)));
- }
-
- protected override void InitializeInputOutput()
- {
- // Additional controller specific source buttons
- SourceButtons.Add(ButtonFlags.LeftPadClick);
- SourceButtons.Add(ButtonFlags.LeftPadTouch);
- SourceButtons.Add(ButtonFlags.RightPadClick);
- SourceButtons.Add(ButtonFlags.RightPadTouch);
-
- SourceAxis.Add(AxisLayoutFlags.LeftPad);
- SourceAxis.Add(AxisLayoutFlags.RightPad);
- SourceAxis.Add(AxisLayoutFlags.Gyroscope);
-
- TargetButtons.Add(ButtonFlags.LeftPadClick);
- TargetButtons.Add(ButtonFlags.LeftPadTouch);
- TargetButtons.Add(ButtonFlags.RightPadTouch);
-
- TargetAxis.Add(AxisLayoutFlags.LeftPad);
- TargetAxis.Add(AxisLayoutFlags.RightPad);
- }
-
- public override void UpdateInputs(long ticks, float delta)
- {
- // skip if controller isn't connected
- if (!IsConnected())
- return;
-
- base.UpdateState(delta);
-
- // Left Pad
- Inputs.ButtonState[ButtonFlags.LeftPadTouch] = JslGetTouchDown(UserIndex);
- Inputs.ButtonState[ButtonFlags.LeftPadClick] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskCapture);
-
- if (Inputs.ButtonState[ButtonFlags.LeftPadTouch])
- {
- float joyShockX0 = JslGetTouchX(UserIndex);
- float joyShockY0 = JslGetTouchY(UserIndex);
-
- Inputs.AxisState[AxisFlags.LeftPadX] = (short)InputUtils.MapRange(joyShockX0, 0.0f, 1.0f, short.MinValue, short.MaxValue);
- Inputs.AxisState[AxisFlags.LeftPadY] = (short)InputUtils.MapRange(joyShockY0, 0.0f, 1.0f, short.MaxValue, short.MinValue);
- }
- else
- {
- Inputs.AxisState[AxisFlags.LeftPadX] = 0;
- Inputs.AxisState[AxisFlags.LeftPadY] = 0;
- }
-
- // Right Pad
- Inputs.ButtonState[ButtonFlags.RightPadTouch] = JslGetTouchDown(UserIndex, true);
- Inputs.ButtonState[ButtonFlags.RightPadClick] = Inputs.ButtonState[ButtonFlags.LeftPadClick] && Inputs.ButtonState[ButtonFlags.RightPadTouch];
-
- if (Inputs.ButtonState[ButtonFlags.RightPadTouch])
- {
- float joyShockX1 = JslGetTouchX(UserIndex, true);
- float joyShockY1 = JslGetTouchY(UserIndex, true);
-
- Inputs.AxisState[AxisFlags.RightPadX] = (short)InputUtils.MapRange(joyShockX1, 0.0f, 1.0f, short.MinValue, short.MaxValue);
- Inputs.AxisState[AxisFlags.RightPadY] = (short)InputUtils.MapRange(joyShockY1, 0.0f, 1.0f, short.MaxValue, short.MinValue);
- }
- else
- {
- Inputs.AxisState[AxisFlags.RightPadX] = 0;
- Inputs.AxisState[AxisFlags.RightPadY] = 0;
- }
-
- base.UpdateInputs(ticks, delta);
- }
-
- public override string ToString()
- {
- return "DualSense® Wireless Controller";
- }
-
- public override void Plug()
- {
- TimerManager.Tick += UpdateInputs;
- base.Plug();
- }
-
- public override void Unplug()
- {
- TimerManager.Tick -= UpdateInputs;
- base.Unplug();
- }
-
- public override void SetLightColor(byte R, byte G, byte B)
- {
- JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B));
- }
-
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.B1:
- return "\u21E3"; // Cross
- case ButtonFlags.B2:
- return "\u21E2"; // Circle
- case ButtonFlags.B3:
- return "\u21E0"; // Square
- case ButtonFlags.B4:
- return "\u21E1"; // Triangle
- case ButtonFlags.L1:
- return "\u21B0";
- case ButtonFlags.R1:
- return "\u21B1";
- case ButtonFlags.Back:
- return "\u2206";
- case ButtonFlags.Start:
- return "\u2208";
- case ButtonFlags.L2Soft:
- return "\u21B2";
- case ButtonFlags.L2Full:
- return "\u21B2";
- case ButtonFlags.R2Soft:
- return "\u21B3";
- case ButtonFlags.R2Full:
- return "\u21B3";
- case ButtonFlags.Special:
- return "\uE000";
- case ButtonFlags.LeftPadClick:
- case ButtonFlags.RightPadClick:
- case ButtonFlags.LeftPadTouch:
- case ButtonFlags.RightPadTouch:
- return "\u2207";
- }
-
- return base.GetGlyph(button);
- }
-
- public override string GetGlyph(AxisFlags axis)
- {
- switch (axis)
- {
- case AxisFlags.L2:
- return "\u21B2";
- case AxisFlags.R2:
- return "\u21B3";
- }
-
- return base.GetGlyph(axis);
- }
-
- public override string GetGlyph(AxisLayoutFlags axis)
- {
- switch (axis)
- {
- case AxisLayoutFlags.L2:
- return "\u21B2";
- case AxisLayoutFlags.R2:
- return "\u21B3";
- case AxisLayoutFlags.LeftPad:
- case AxisLayoutFlags.RightPad:
- return "\u2207";
- }
-
- return base.GetGlyph(axis);
- }
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
+using HandheldCompanion.Utils;
+using System.Windows.Media;
+using static JSL;
+
+namespace HandheldCompanion.Controllers;
+
+public class DualSenseController : JSController
+{
+ public DualSenseController()
+ { }
+
+ public DualSenseController(JOY_SETTINGS settings, PnPDetails details) : base(settings, details)
+ {
+ // UI
+ ColoredButtons.Add(ButtonFlags.B1, new SolidColorBrush(Color.FromArgb(255, 116, 139, 255)));
+ ColoredButtons.Add(ButtonFlags.B2, new SolidColorBrush(Color.FromArgb(255, 255, 73, 75)));
+ ColoredButtons.Add(ButtonFlags.B3, new SolidColorBrush(Color.FromArgb(255, 244, 149, 193)));
+ ColoredButtons.Add(ButtonFlags.B4, new SolidColorBrush(Color.FromArgb(255, 73, 191, 115)));
+ }
+
+ protected override void InitializeInputOutput()
+ {
+ // Additional controller specific source buttons
+ SourceButtons.Add(ButtonFlags.LeftPadClick);
+ SourceButtons.Add(ButtonFlags.LeftPadTouch);
+ SourceButtons.Add(ButtonFlags.RightPadClick);
+ SourceButtons.Add(ButtonFlags.RightPadTouch);
+
+ SourceAxis.Add(AxisLayoutFlags.LeftPad);
+ SourceAxis.Add(AxisLayoutFlags.RightPad);
+ SourceAxis.Add(AxisLayoutFlags.Gyroscope);
+
+ TargetButtons.Add(ButtonFlags.LeftPadClick);
+ TargetButtons.Add(ButtonFlags.LeftPadTouch);
+ TargetButtons.Add(ButtonFlags.RightPadTouch);
+
+ TargetAxis.Add(AxisLayoutFlags.LeftPad);
+ TargetAxis.Add(AxisLayoutFlags.RightPad);
+ }
+
+ public override void UpdateInputs(long ticks, float delta)
+ {
+ // skip if controller isn't connected
+ if (!IsConnected())
+ return;
+
+ base.UpdateState(delta);
+
+ // Left Pad
+ Inputs.ButtonState[ButtonFlags.LeftPadTouch] = JslGetTouchDown(UserIndex);
+ Inputs.ButtonState[ButtonFlags.LeftPadClick] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskCapture);
+
+ if (Inputs.ButtonState[ButtonFlags.LeftPadTouch])
+ {
+ float joyShockX0 = JslGetTouchX(UserIndex);
+ float joyShockY0 = JslGetTouchY(UserIndex);
+
+ Inputs.AxisState[AxisFlags.LeftPadX] = (short)InputUtils.MapRange(joyShockX0, 0.0f, 1.0f, short.MinValue, short.MaxValue);
+ Inputs.AxisState[AxisFlags.LeftPadY] = (short)InputUtils.MapRange(joyShockY0, 0.0f, 1.0f, short.MaxValue, short.MinValue);
+ }
+ else
+ {
+ Inputs.AxisState[AxisFlags.LeftPadX] = 0;
+ Inputs.AxisState[AxisFlags.LeftPadY] = 0;
+ }
+
+ // Right Pad
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = JslGetTouchDown(UserIndex, true);
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = Inputs.ButtonState[ButtonFlags.LeftPadClick] && Inputs.ButtonState[ButtonFlags.RightPadTouch];
+
+ if (Inputs.ButtonState[ButtonFlags.RightPadTouch])
+ {
+ float joyShockX1 = JslGetTouchX(UserIndex, true);
+ float joyShockY1 = JslGetTouchY(UserIndex, true);
+
+ Inputs.AxisState[AxisFlags.RightPadX] = (short)InputUtils.MapRange(joyShockX1, 0.0f, 1.0f, short.MinValue, short.MaxValue);
+ Inputs.AxisState[AxisFlags.RightPadY] = (short)InputUtils.MapRange(joyShockY1, 0.0f, 1.0f, short.MaxValue, short.MinValue);
+ }
+ else
+ {
+ Inputs.AxisState[AxisFlags.RightPadX] = 0;
+ Inputs.AxisState[AxisFlags.RightPadY] = 0;
+ }
+
+ base.UpdateInputs(ticks, delta);
+ }
+
+ public override string ToString()
+ {
+ return "DualSense® Wireless Controller";
+ }
+
+ public override void Plug()
+ {
+ TimerManager.Tick += UpdateInputs;
+ base.Plug();
+ }
+
+ public override void Unplug()
+ {
+ TimerManager.Tick -= UpdateInputs;
+ base.Unplug();
+ }
+
+ public override void SetLightColor(byte R, byte G, byte B)
+ {
+ JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B));
+ }
+
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.B1:
+ return "\u21E3"; // Cross
+ case ButtonFlags.B2:
+ return "\u21E2"; // Circle
+ case ButtonFlags.B3:
+ return "\u21E0"; // Square
+ case ButtonFlags.B4:
+ return "\u21E1"; // Triangle
+ case ButtonFlags.L1:
+ return "\u21B0";
+ case ButtonFlags.R1:
+ return "\u21B1";
+ case ButtonFlags.Back:
+ return "\u2206";
+ case ButtonFlags.Start:
+ return "\u2208";
+ case ButtonFlags.L2Soft:
+ return "\u21B2";
+ case ButtonFlags.L2Full:
+ return "\u21B2";
+ case ButtonFlags.R2Soft:
+ return "\u21B3";
+ case ButtonFlags.R2Full:
+ return "\u21B3";
+ case ButtonFlags.Special:
+ return "\uE000";
+ case ButtonFlags.LeftPadClick:
+ case ButtonFlags.RightPadClick:
+ case ButtonFlags.LeftPadTouch:
+ case ButtonFlags.RightPadTouch:
+ return "\u2207";
+ }
+
+ return base.GetGlyph(button);
+ }
+
+ public override string GetGlyph(AxisFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisFlags.L2:
+ return "\u21B2";
+ case AxisFlags.R2:
+ return "\u21B3";
+ }
+
+ return base.GetGlyph(axis);
+ }
+
+ public override string GetGlyph(AxisLayoutFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisLayoutFlags.L2:
+ return "\u21B2";
+ case AxisLayoutFlags.R2:
+ return "\u21B3";
+ case AxisLayoutFlags.LeftPad:
+ case AxisLayoutFlags.RightPad:
+ return "\u2207";
+ }
+
+ return base.GetGlyph(axis);
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/GordonController.cs b/HandheldCompanion/Controllers/GordonController.cs
index 1d2ef137e..46185c1b6 100644
--- a/HandheldCompanion/Controllers/GordonController.cs
+++ b/HandheldCompanion/Controllers/GordonController.cs
@@ -1,322 +1,322 @@
-using HandheldCompanion.Actions;
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Managers;
-using HandheldCompanion.Utils;
-using SharpDX.XInput;
-using steam_hidapi.net;
-using steam_hidapi.net.Hid;
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using System.Windows.Media;
-
-namespace HandheldCompanion.Controllers
-{
- public class GordonController : SteamController
- {
- private steam_hidapi.net.GordonController Controller;
- private GordonControllerInputEventArgs input;
-
- private const short TrackPadInner = short.MaxValue / 2;
- public const ushort MaxRumbleIntensity = 2048;
-
- public GordonController()
- { }
-
- public GordonController(PnPDetails details) : base()
- {
- AttachDetails(details);
-
- // UI
- ColoredButtons.Add(ButtonFlags.B1, new SolidColorBrush(Color.FromArgb(255, 81, 191, 61)));
- ColoredButtons.Add(ButtonFlags.B2, new SolidColorBrush(Color.FromArgb(255, 217, 65, 38)));
- ColoredButtons.Add(ButtonFlags.B3, new SolidColorBrush(Color.FromArgb(255, 26, 159, 255)));
- ColoredButtons.Add(ButtonFlags.B4, new SolidColorBrush(Color.FromArgb(255, 255, 200, 44)));
-
- DrawUI();
- UpdateUI();
- }
-
- protected override void InitializeInputOutput()
- {
- // Additional controller specific source buttons/axes
- SourceButtons.AddRange(new List() { ButtonFlags.L4, ButtonFlags.R4 });
- SourceButtons.AddRange(new List() { ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp, ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight });
- SourceButtons.AddRange(new List() { ButtonFlags.RightPadClick, ButtonFlags.RightPadTouch, ButtonFlags.RightPadClickUp, ButtonFlags.RightPadClickDown, ButtonFlags.RightPadClickLeft, ButtonFlags.RightPadClickRight });
-
- SourceAxis.Add(AxisLayoutFlags.LeftPad);
- SourceAxis.Add(AxisLayoutFlags.RightPad);
- SourceAxis.Add(AxisLayoutFlags.Gyroscope);
-
- TargetButtons.Add(ButtonFlags.LeftPadClick);
- TargetButtons.Add(ButtonFlags.RightPadClick);
- TargetButtons.Add(ButtonFlags.LeftPadTouch);
- TargetButtons.Add(ButtonFlags.RightPadTouch);
-
- TargetAxis.Add(AxisLayoutFlags.LeftPad);
- TargetAxis.Add(AxisLayoutFlags.RightPad);
-
- // This is a very original controller, it doesn't have few things
- SourceButtons.Remove(ButtonFlags.RightStickClick);
- SourceButtons.Remove(ButtonFlags.RightStickUp);
- SourceButtons.Remove(ButtonFlags.RightStickDown);
- SourceButtons.Remove(ButtonFlags.RightStickLeft);
- SourceButtons.Remove(ButtonFlags.RightStickRight);
-
- SourceAxis.Remove(AxisLayoutFlags.RightStick);
- }
-
- public override void AttachDetails(PnPDetails details)
- {
- base.AttachDetails(details);
-
- Controller = new(details.VendorID, details.ProductID, details.GetMI());
- UserIndex = (byte)details.GetMI();
-
- // open controller
- Open();
- }
-
- public override string ToString()
- {
- string baseName = base.ToString();
- if (!string.IsNullOrEmpty(baseName))
- return baseName;
- return "Steam Controller Gordon";
- }
-
- public override void UpdateInputs(long ticks, float delta)
- {
- if (input is null)
- return;
-
- Inputs.ButtonState = InjectedButtons.Clone() as ButtonState;
-
- Inputs.ButtonState[ButtonFlags.B1] = input.State.ButtonState[GordonControllerButton.BtnA];
- Inputs.ButtonState[ButtonFlags.B2] = input.State.ButtonState[GordonControllerButton.BtnB];
- Inputs.ButtonState[ButtonFlags.B3] = input.State.ButtonState[GordonControllerButton.BtnX];
- Inputs.ButtonState[ButtonFlags.B4] = input.State.ButtonState[GordonControllerButton.BtnY];
-
- Inputs.ButtonState[ButtonFlags.DPadUp] = input.State.ButtonState[GordonControllerButton.BtnDpadUp];
- Inputs.ButtonState[ButtonFlags.DPadDown] = input.State.ButtonState[GordonControllerButton.BtnDpadDown];
- Inputs.ButtonState[ButtonFlags.DPadLeft] = input.State.ButtonState[GordonControllerButton.BtnDpadLeft];
- Inputs.ButtonState[ButtonFlags.DPadRight] = input.State.ButtonState[GordonControllerButton.BtnDpadRight];
-
- Inputs.ButtonState[ButtonFlags.Start] = input.State.ButtonState[GordonControllerButton.BtnOptions];
- Inputs.ButtonState[ButtonFlags.Back] = input.State.ButtonState[GordonControllerButton.BtnMenu];
- Inputs.ButtonState[ButtonFlags.Special] = input.State.ButtonState[GordonControllerButton.BtnSteam];
-
- var L2 = input.State.AxesState[GordonControllerAxis.L2];
- var R2 = input.State.AxesState[GordonControllerAxis.R2];
-
- Inputs.ButtonState[ButtonFlags.L2Soft] = L2 > Gamepad.TriggerThreshold;
- Inputs.ButtonState[ButtonFlags.R2Soft] = R2 > Gamepad.TriggerThreshold;
-
- Inputs.ButtonState[ButtonFlags.L2Full] = L2 > Gamepad.TriggerThreshold * 8;
- Inputs.ButtonState[ButtonFlags.R2Full] = R2 > Gamepad.TriggerThreshold * 8;
-
- Inputs.AxisState[AxisFlags.L2] = (short)L2;
- Inputs.AxisState[AxisFlags.R2] = (short)R2;
-
- Inputs.ButtonState[ButtonFlags.L1] = input.State.ButtonState[GordonControllerButton.BtnL1];
- Inputs.ButtonState[ButtonFlags.R1] = input.State.ButtonState[GordonControllerButton.BtnR1];
- Inputs.ButtonState[ButtonFlags.L4] = input.State.ButtonState[GordonControllerButton.BtnL4];
- Inputs.ButtonState[ButtonFlags.R4] = input.State.ButtonState[GordonControllerButton.BtnR4];
-
- // Left Stick
- Inputs.ButtonState[ButtonFlags.LeftStickClick] = input.State.ButtonState[GordonControllerButton.BtnLStickPress];
-
- Inputs.AxisState[AxisFlags.LeftStickX] = input.State.AxesState[GordonControllerAxis.LeftStickX];
- Inputs.AxisState[AxisFlags.LeftStickY] = input.State.AxesState[GordonControllerAxis.LeftStickY];
-
- Inputs.ButtonState[ButtonFlags.LeftStickLeft] = Inputs.AxisState[AxisFlags.LeftStickX] < -Gamepad.LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickRight] = Inputs.AxisState[AxisFlags.LeftStickX] > Gamepad.LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickDown] = Inputs.AxisState[AxisFlags.LeftStickY] < -Gamepad.LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickUp] = Inputs.AxisState[AxisFlags.LeftStickY] > Gamepad.LeftThumbDeadZone;
-
- // TODO: Implement Inner/Outer Ring button mappings for sticks
- // https://github.com/Havner/HandheldCompanion/commit/e1124ceb6c59051201756d5e95b2eb39a3bb24f6
-
- /* float leftLength = new Vector2(Inputs.AxisState[AxisFlags.LeftThumbX], Inputs.AxisState[AxisFlags.LeftThumbY]).Length();
- Inputs.ButtonState[ButtonFlags.LeftStickOuterRing] = leftLength >= (RingThreshold * short.MaxValue);
- Inputs.ButtonState[ButtonFlags.LeftStickInnerRing] = leftLength >= Gamepad.LeftThumbDeadZone && leftLength < (RingThreshold * short.MaxValue); */
-
- // Left Pad
- Inputs.ButtonState[ButtonFlags.LeftPadTouch] = input.State.ButtonState[GordonControllerButton.BtnLPadTouch];
- Inputs.ButtonState[ButtonFlags.LeftPadClick] = input.State.ButtonState[GordonControllerButton.BtnLPadPress];
-
- if (Inputs.ButtonState[ButtonFlags.LeftPadTouch])
- {
- Inputs.AxisState[AxisFlags.LeftPadX] = input.State.AxesState[GordonControllerAxis.LeftPadX];
- Inputs.AxisState[AxisFlags.LeftPadY] = input.State.AxesState[GordonControllerAxis.LeftPadY];
- }
- else
- {
- Inputs.AxisState[AxisFlags.LeftPadX] = 0;
- Inputs.AxisState[AxisFlags.LeftPadY] = 0;
- }
-
- if (Inputs.ButtonState[ButtonFlags.LeftPadClick])
- {
- InputUtils.TouchToDirections(Inputs.AxisState[AxisFlags.LeftPadX], Inputs.AxisState[AxisFlags.LeftPadY], TrackPadInner, 0, out bool[] buttons);
- Inputs.ButtonState[ButtonFlags.LeftPadClickUp] = buttons[0];
- Inputs.ButtonState[ButtonFlags.LeftPadClickRight] = buttons[1];
- Inputs.ButtonState[ButtonFlags.LeftPadClickDown] = buttons[2];
- Inputs.ButtonState[ButtonFlags.LeftPadClickLeft] = buttons[3];
- }
- else
- {
- Inputs.ButtonState[ButtonFlags.LeftPadClickUp] = false;
- Inputs.ButtonState[ButtonFlags.LeftPadClickRight] = false;
- Inputs.ButtonState[ButtonFlags.LeftPadClickDown] = false;
- Inputs.ButtonState[ButtonFlags.LeftPadClickLeft] = false;
- }
-
- // Right Pad
- Inputs.ButtonState[ButtonFlags.RightPadTouch] = input.State.ButtonState[GordonControllerButton.BtnRPadTouch];
- Inputs.ButtonState[ButtonFlags.RightPadClick] = input.State.ButtonState[GordonControllerButton.BtnRPadPress];
-
- if (Inputs.ButtonState[ButtonFlags.RightPadTouch])
- {
- Inputs.AxisState[AxisFlags.RightPadX] = input.State.AxesState[GordonControllerAxis.RightPadX];
- Inputs.AxisState[AxisFlags.RightPadY] = input.State.AxesState[GordonControllerAxis.RightPadY];
- }
- else
- {
- Inputs.AxisState[AxisFlags.RightPadX] = 0;
- Inputs.AxisState[AxisFlags.RightPadY] = 0;
- }
-
- if (Inputs.ButtonState[ButtonFlags.RightPadClick])
- {
- InputUtils.TouchToDirections(Inputs.AxisState[AxisFlags.RightPadX], Inputs.AxisState[AxisFlags.RightPadY], TrackPadInner, 0, out bool[] buttons);
- Inputs.ButtonState[ButtonFlags.RightPadClickUp] = buttons[0];
- Inputs.ButtonState[ButtonFlags.RightPadClickRight] = buttons[1];
- Inputs.ButtonState[ButtonFlags.RightPadClickDown] = buttons[2];
- Inputs.ButtonState[ButtonFlags.RightPadClickLeft] = buttons[3];
- }
- else
- {
- Inputs.ButtonState[ButtonFlags.RightPadClickUp] = false;
- Inputs.ButtonState[ButtonFlags.RightPadClickRight] = false;
- Inputs.ButtonState[ButtonFlags.RightPadClickDown] = false;
- Inputs.ButtonState[ButtonFlags.RightPadClickLeft] = false;
- }
-
- // TODO: why Z/Y swapped?
- float aX = (float)input.State.AxesState[GordonControllerAxis.GyroAccelX] / short.MaxValue * 2.0f;
- float aY = (float)input.State.AxesState[GordonControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f;
- float aZ = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelY] / short.MaxValue * 2.0f;
-
- // TODO: why Roll/Pitch swapped?
- float gX = (float)input.State.AxesState[GordonControllerAxis.GyroPitch] / short.MaxValue * 2000.0f; // Roll
- float gY = (float)input.State.AxesState[GordonControllerAxis.GyroRoll] / short.MaxValue * 2000.0f; // Pitch
- float gZ = (float)input.State.AxesState[GordonControllerAxis.GyroYaw] / short.MaxValue * 2000.0f; // Yaw
-
- // store motion
- Inputs.GyroState.SetGyroscope(gX, gY, gZ);
- Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
-
- // process motion
- gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
-
- base.UpdateInputs(ticks, delta);
- }
- private void OnControllerInputReceived(GordonControllerInputEventArgs input)
- {
- this.input = input;
- }
-
- public override void Plug()
- {
- try
- {
- Controller.OnControllerInputReceived = input => Task.Run(() => OnControllerInputReceived(input));
-
- // open controller
- Open();
- }
- catch (Exception ex)
- {
- LogManager.LogError("Couldn't initialize GordonController. Exception: {0}", ex.Message);
- return;
- }
-
- Controller.SetLizardMode(false);
- Controller.SetGyroscope(true);
- Controller.SetIdleTimeout(300); // ~5 min
-
- TimerManager.Tick += UpdateInputs;
-
- base.Plug();
- }
-
- public override void Unplug()
- {
- try
- {
- // restore lizard state
- Controller.SetLizardMode(true);
- Controller.SetGyroscope(false);
- Controller.SetIdleTimeout(0);
- //Controller.TurnOff(); // TODO: why not?
-
- // close controller
- Close();
- }
- catch
- {
- return;
- }
-
- TimerManager.Tick -= UpdateInputs;
- base.Unplug();
- }
-
- private void Open()
- {
- try
- {
- Controller.Open();
- isConnected = true;
- }
- catch { }
- }
-
- private void Close()
- {
- try
- {
- Controller.Close();
- isConnected = false;
- }
- catch { }
- }
-
- public ushort GetHapticIntensity(byte input, ushort maxIntensity)
- {
- return (ushort)(input * maxIntensity * VibrationStrength / 255);
- }
-
- public override void SetVibration(byte LargeMotor, byte SmallMotor)
- {
- ushort leftAmplitude = GetHapticIntensity(LargeMotor, MaxRumbleIntensity);
- Controller.SetHaptic((byte)SCHapticMotor.Left, leftAmplitude, 0, 1);
-
- ushort rightAmplitude = GetHapticIntensity(SmallMotor, MaxRumbleIntensity);
- Controller.SetHaptic((byte)SCHapticMotor.Right, rightAmplitude, 0, 1);
- }
-
- public override void SetHaptic(HapticStrength strength, ButtonFlags button)
- {
- ushort value = strength switch
- {
- HapticStrength.Low => 512,
- HapticStrength.Medium => 1024,
- HapticStrength.High => 2048,
- _ => 0,
- };
- Controller.SetHaptic((byte)GetMotorForButton(button), value, 0, 1);
- }
- }
+using HandheldCompanion.Actions;
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
+using HandheldCompanion.Utils;
+using SharpDX.XInput;
+using steam_hidapi.net;
+using steam_hidapi.net.Hid;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using System.Windows.Media;
+
+namespace HandheldCompanion.Controllers
+{
+ public class GordonController : SteamController
+ {
+ private steam_hidapi.net.GordonController Controller;
+ private GordonControllerInputEventArgs input;
+
+ private const short TrackPadInner = short.MaxValue / 2;
+ public const ushort MaxRumbleIntensity = 2048;
+
+ public GordonController()
+ { }
+
+ public GordonController(PnPDetails details) : base()
+ {
+ AttachDetails(details);
+
+ // UI
+ ColoredButtons.Add(ButtonFlags.B1, new SolidColorBrush(Color.FromArgb(255, 81, 191, 61)));
+ ColoredButtons.Add(ButtonFlags.B2, new SolidColorBrush(Color.FromArgb(255, 217, 65, 38)));
+ ColoredButtons.Add(ButtonFlags.B3, new SolidColorBrush(Color.FromArgb(255, 26, 159, 255)));
+ ColoredButtons.Add(ButtonFlags.B4, new SolidColorBrush(Color.FromArgb(255, 255, 200, 44)));
+
+ DrawUI();
+ UpdateUI();
+ }
+
+ protected override void InitializeInputOutput()
+ {
+ // Additional controller specific source buttons/axes
+ SourceButtons.AddRange(new List() { ButtonFlags.L4, ButtonFlags.R4 });
+ SourceButtons.AddRange(new List() { ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp, ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight });
+ SourceButtons.AddRange(new List() { ButtonFlags.RightPadClick, ButtonFlags.RightPadTouch, ButtonFlags.RightPadClickUp, ButtonFlags.RightPadClickDown, ButtonFlags.RightPadClickLeft, ButtonFlags.RightPadClickRight });
+
+ SourceAxis.Add(AxisLayoutFlags.LeftPad);
+ SourceAxis.Add(AxisLayoutFlags.RightPad);
+ SourceAxis.Add(AxisLayoutFlags.Gyroscope);
+
+ TargetButtons.Add(ButtonFlags.LeftPadClick);
+ TargetButtons.Add(ButtonFlags.RightPadClick);
+ TargetButtons.Add(ButtonFlags.LeftPadTouch);
+ TargetButtons.Add(ButtonFlags.RightPadTouch);
+
+ TargetAxis.Add(AxisLayoutFlags.LeftPad);
+ TargetAxis.Add(AxisLayoutFlags.RightPad);
+
+ // This is a very original controller, it doesn't have few things
+ SourceButtons.Remove(ButtonFlags.RightStickClick);
+ SourceButtons.Remove(ButtonFlags.RightStickUp);
+ SourceButtons.Remove(ButtonFlags.RightStickDown);
+ SourceButtons.Remove(ButtonFlags.RightStickLeft);
+ SourceButtons.Remove(ButtonFlags.RightStickRight);
+
+ SourceAxis.Remove(AxisLayoutFlags.RightStick);
+ }
+
+ public override void AttachDetails(PnPDetails details)
+ {
+ base.AttachDetails(details);
+
+ Controller = new(details.VendorID, details.ProductID, details.GetMI());
+ UserIndex = (byte)details.GetMI();
+
+ // open controller
+ Open();
+ }
+
+ public override string ToString()
+ {
+ string baseName = base.ToString();
+ if (!string.IsNullOrEmpty(baseName))
+ return baseName;
+ return "Steam Controller Gordon";
+ }
+
+ public override void UpdateInputs(long ticks, float delta)
+ {
+ if (input is null)
+ return;
+
+ Inputs.ButtonState = InjectedButtons.Clone() as ButtonState;
+
+ Inputs.ButtonState[ButtonFlags.B1] = input.State.ButtonState[GordonControllerButton.BtnA];
+ Inputs.ButtonState[ButtonFlags.B2] = input.State.ButtonState[GordonControllerButton.BtnB];
+ Inputs.ButtonState[ButtonFlags.B3] = input.State.ButtonState[GordonControllerButton.BtnX];
+ Inputs.ButtonState[ButtonFlags.B4] = input.State.ButtonState[GordonControllerButton.BtnY];
+
+ Inputs.ButtonState[ButtonFlags.DPadUp] = input.State.ButtonState[GordonControllerButton.BtnDpadUp];
+ Inputs.ButtonState[ButtonFlags.DPadDown] = input.State.ButtonState[GordonControllerButton.BtnDpadDown];
+ Inputs.ButtonState[ButtonFlags.DPadLeft] = input.State.ButtonState[GordonControllerButton.BtnDpadLeft];
+ Inputs.ButtonState[ButtonFlags.DPadRight] = input.State.ButtonState[GordonControllerButton.BtnDpadRight];
+
+ Inputs.ButtonState[ButtonFlags.Start] = input.State.ButtonState[GordonControllerButton.BtnOptions];
+ Inputs.ButtonState[ButtonFlags.Back] = input.State.ButtonState[GordonControllerButton.BtnMenu];
+ Inputs.ButtonState[ButtonFlags.Special] = input.State.ButtonState[GordonControllerButton.BtnSteam];
+
+ var L2 = input.State.AxesState[GordonControllerAxis.L2];
+ var R2 = input.State.AxesState[GordonControllerAxis.R2];
+
+ Inputs.ButtonState[ButtonFlags.L2Soft] = L2 > Gamepad.TriggerThreshold;
+ Inputs.ButtonState[ButtonFlags.R2Soft] = R2 > Gamepad.TriggerThreshold;
+
+ Inputs.ButtonState[ButtonFlags.L2Full] = L2 > Gamepad.TriggerThreshold * 8;
+ Inputs.ButtonState[ButtonFlags.R2Full] = R2 > Gamepad.TriggerThreshold * 8;
+
+ Inputs.AxisState[AxisFlags.L2] = (short)L2;
+ Inputs.AxisState[AxisFlags.R2] = (short)R2;
+
+ Inputs.ButtonState[ButtonFlags.L1] = input.State.ButtonState[GordonControllerButton.BtnL1];
+ Inputs.ButtonState[ButtonFlags.R1] = input.State.ButtonState[GordonControllerButton.BtnR1];
+ Inputs.ButtonState[ButtonFlags.L4] = input.State.ButtonState[GordonControllerButton.BtnL4];
+ Inputs.ButtonState[ButtonFlags.R4] = input.State.ButtonState[GordonControllerButton.BtnR4];
+
+ // Left Stick
+ Inputs.ButtonState[ButtonFlags.LeftStickClick] = input.State.ButtonState[GordonControllerButton.BtnLStickPress];
+
+ Inputs.AxisState[AxisFlags.LeftStickX] = input.State.AxesState[GordonControllerAxis.LeftStickX];
+ Inputs.AxisState[AxisFlags.LeftStickY] = input.State.AxesState[GordonControllerAxis.LeftStickY];
+
+ Inputs.ButtonState[ButtonFlags.LeftStickLeft] = Inputs.AxisState[AxisFlags.LeftStickX] < -Gamepad.LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickRight] = Inputs.AxisState[AxisFlags.LeftStickX] > Gamepad.LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickDown] = Inputs.AxisState[AxisFlags.LeftStickY] < -Gamepad.LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickUp] = Inputs.AxisState[AxisFlags.LeftStickY] > Gamepad.LeftThumbDeadZone;
+
+ // TODO: Implement Inner/Outer Ring button mappings for sticks
+ // https://github.com/Havner/HandheldCompanion/commit/e1124ceb6c59051201756d5e95b2eb39a3bb24f6
+
+ /* float leftLength = new Vector2(Inputs.AxisState[AxisFlags.LeftThumbX], Inputs.AxisState[AxisFlags.LeftThumbY]).Length();
+ Inputs.ButtonState[ButtonFlags.LeftStickOuterRing] = leftLength >= (RingThreshold * short.MaxValue);
+ Inputs.ButtonState[ButtonFlags.LeftStickInnerRing] = leftLength >= Gamepad.LeftThumbDeadZone && leftLength < (RingThreshold * short.MaxValue); */
+
+ // Left Pad
+ Inputs.ButtonState[ButtonFlags.LeftPadTouch] = input.State.ButtonState[GordonControllerButton.BtnLPadTouch];
+ Inputs.ButtonState[ButtonFlags.LeftPadClick] = input.State.ButtonState[GordonControllerButton.BtnLPadPress];
+
+ if (Inputs.ButtonState[ButtonFlags.LeftPadTouch])
+ {
+ Inputs.AxisState[AxisFlags.LeftPadX] = input.State.AxesState[GordonControllerAxis.LeftPadX];
+ Inputs.AxisState[AxisFlags.LeftPadY] = input.State.AxesState[GordonControllerAxis.LeftPadY];
+ }
+ else
+ {
+ Inputs.AxisState[AxisFlags.LeftPadX] = 0;
+ Inputs.AxisState[AxisFlags.LeftPadY] = 0;
+ }
+
+ if (Inputs.ButtonState[ButtonFlags.LeftPadClick])
+ {
+ InputUtils.TouchToDirections(Inputs.AxisState[AxisFlags.LeftPadX], Inputs.AxisState[AxisFlags.LeftPadY], TrackPadInner, 0, out bool[] buttons);
+ Inputs.ButtonState[ButtonFlags.LeftPadClickUp] = buttons[0];
+ Inputs.ButtonState[ButtonFlags.LeftPadClickRight] = buttons[1];
+ Inputs.ButtonState[ButtonFlags.LeftPadClickDown] = buttons[2];
+ Inputs.ButtonState[ButtonFlags.LeftPadClickLeft] = buttons[3];
+ }
+ else
+ {
+ Inputs.ButtonState[ButtonFlags.LeftPadClickUp] = false;
+ Inputs.ButtonState[ButtonFlags.LeftPadClickRight] = false;
+ Inputs.ButtonState[ButtonFlags.LeftPadClickDown] = false;
+ Inputs.ButtonState[ButtonFlags.LeftPadClickLeft] = false;
+ }
+
+ // Right Pad
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = input.State.ButtonState[GordonControllerButton.BtnRPadTouch];
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = input.State.ButtonState[GordonControllerButton.BtnRPadPress];
+
+ if (Inputs.ButtonState[ButtonFlags.RightPadTouch])
+ {
+ Inputs.AxisState[AxisFlags.RightPadX] = input.State.AxesState[GordonControllerAxis.RightPadX];
+ Inputs.AxisState[AxisFlags.RightPadY] = input.State.AxesState[GordonControllerAxis.RightPadY];
+ }
+ else
+ {
+ Inputs.AxisState[AxisFlags.RightPadX] = 0;
+ Inputs.AxisState[AxisFlags.RightPadY] = 0;
+ }
+
+ if (Inputs.ButtonState[ButtonFlags.RightPadClick])
+ {
+ InputUtils.TouchToDirections(Inputs.AxisState[AxisFlags.RightPadX], Inputs.AxisState[AxisFlags.RightPadY], TrackPadInner, 0, out bool[] buttons);
+ Inputs.ButtonState[ButtonFlags.RightPadClickUp] = buttons[0];
+ Inputs.ButtonState[ButtonFlags.RightPadClickRight] = buttons[1];
+ Inputs.ButtonState[ButtonFlags.RightPadClickDown] = buttons[2];
+ Inputs.ButtonState[ButtonFlags.RightPadClickLeft] = buttons[3];
+ }
+ else
+ {
+ Inputs.ButtonState[ButtonFlags.RightPadClickUp] = false;
+ Inputs.ButtonState[ButtonFlags.RightPadClickRight] = false;
+ Inputs.ButtonState[ButtonFlags.RightPadClickDown] = false;
+ Inputs.ButtonState[ButtonFlags.RightPadClickLeft] = false;
+ }
+
+ // TODO: why Z/Y swapped?
+ float aX = (float)input.State.AxesState[GordonControllerAxis.GyroAccelX] / short.MaxValue * 2.0f;
+ float aY = (float)input.State.AxesState[GordonControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f;
+ float aZ = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelY] / short.MaxValue * 2.0f;
+
+ // TODO: why Roll/Pitch swapped?
+ float gX = (float)input.State.AxesState[GordonControllerAxis.GyroPitch] / short.MaxValue * 2000.0f; // Roll
+ float gY = (float)input.State.AxesState[GordonControllerAxis.GyroRoll] / short.MaxValue * 2000.0f; // Pitch
+ float gZ = (float)input.State.AxesState[GordonControllerAxis.GyroYaw] / short.MaxValue * 2000.0f; // Yaw
+
+ // store motion
+ Inputs.GyroState.SetGyroscope(gX, gY, gZ);
+ Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
+
+ // process motion
+ gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
+
+ base.UpdateInputs(ticks, delta);
+ }
+ private void OnControllerInputReceived(GordonControllerInputEventArgs input)
+ {
+ this.input = input;
+ }
+
+ public override void Plug()
+ {
+ try
+ {
+ Controller.OnControllerInputReceived = input => Task.Run(() => OnControllerInputReceived(input));
+
+ // open controller
+ Open();
+ }
+ catch (Exception ex)
+ {
+ LogManager.LogError("Couldn't initialize GordonController. Exception: {0}", ex.Message);
+ return;
+ }
+
+ Controller.SetLizardMode(false);
+ Controller.SetGyroscope(true);
+ Controller.SetIdleTimeout(300); // ~5 min
+
+ TimerManager.Tick += UpdateInputs;
+
+ base.Plug();
+ }
+
+ public override void Unplug()
+ {
+ try
+ {
+ // restore lizard state
+ Controller.SetLizardMode(true);
+ Controller.SetGyroscope(false);
+ Controller.SetIdleTimeout(0);
+ //Controller.TurnOff(); // TODO: why not?
+
+ // close controller
+ Close();
+ }
+ catch
+ {
+ return;
+ }
+
+ TimerManager.Tick -= UpdateInputs;
+ base.Unplug();
+ }
+
+ private void Open()
+ {
+ try
+ {
+ Controller.Open();
+ isConnected = true;
+ }
+ catch { }
+ }
+
+ private void Close()
+ {
+ try
+ {
+ Controller.Close();
+ isConnected = false;
+ }
+ catch { }
+ }
+
+ public ushort GetHapticIntensity(byte input, ushort maxIntensity)
+ {
+ return (ushort)(input * maxIntensity * VibrationStrength / 255);
+ }
+
+ public override void SetVibration(byte LargeMotor, byte SmallMotor)
+ {
+ ushort leftAmplitude = GetHapticIntensity(LargeMotor, MaxRumbleIntensity);
+ Controller.SetHaptic((byte)SCHapticMotor.Left, leftAmplitude, 0, 1);
+
+ ushort rightAmplitude = GetHapticIntensity(SmallMotor, MaxRumbleIntensity);
+ Controller.SetHaptic((byte)SCHapticMotor.Right, rightAmplitude, 0, 1);
+ }
+
+ public override void SetHaptic(HapticStrength strength, ButtonFlags button)
+ {
+ ushort value = strength switch
+ {
+ HapticStrength.Low => 512,
+ HapticStrength.Medium => 1024,
+ HapticStrength.High => 2048,
+ _ => 0,
+ };
+ Controller.SetHaptic((byte)GetMotorForButton(button), value, 0, 1);
+ }
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/IController.xaml b/HandheldCompanion/Controllers/IController.xaml
index 0ec1ea83d..ce6ea74f6 100644
--- a/HandheldCompanion/Controllers/IController.xaml
+++ b/HandheldCompanion/Controllers/IController.xaml
@@ -1,144 +1,144 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HandheldCompanion/Controllers/IController.xaml.cs b/HandheldCompanion/Controllers/IController.xaml.cs
index 7bcf82b5b..0d79258ce 100644
--- a/HandheldCompanion/Controllers/IController.xaml.cs
+++ b/HandheldCompanion/Controllers/IController.xaml.cs
@@ -1,791 +1,791 @@
-using HandheldCompanion.Actions;
-using HandheldCompanion.Helpers;
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Managers;
-using HandheldCompanion.Misc;
-using HandheldCompanion.Utils;
-using iNKORE.UI.WPF.Modern.Controls;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Media;
-
-namespace HandheldCompanion.Controllers
-{
- [Flags]
- public enum ControllerCapabilities : ushort
- {
- None = 0,
- MotionSensor = 1,
- }
-
- ///
- /// Logique d'interaction pour IController.xaml
- ///
- public partial class IController : UserControl
- {
- // Buttons and axes we should be able to map to.
- // When we have target controllers with different buttons (e.g. in VigEm) this will have to be moved elsewhere.
- protected readonly List TargetButtons = new()
- {
- ButtonFlags.None, ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4,
- ButtonFlags.DPadUp, ButtonFlags.DPadDown, ButtonFlags.DPadLeft, ButtonFlags.DPadRight,
- ButtonFlags.Start, ButtonFlags.Back, ButtonFlags.Special,
- ButtonFlags.L1, ButtonFlags.R1,
- ButtonFlags.LeftStickClick, ButtonFlags.RightStickClick,
- };
-
- protected readonly List TargetAxis = new()
- {
- AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick,
- AxisLayoutFlags.L2, AxisLayoutFlags.R2,
- };
-
- protected readonly List SourceAxis = new()
- {
- // same as target, we assume all controllers have those axes
- AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick,
- AxisLayoutFlags.L2, AxisLayoutFlags.R2
- };
-
- // Buttons and axes all controllers have that we can map.
- // Additional ones can be added per controller.
- protected readonly List SourceButtons = new()
- {
- // same as target, we assume all controllers have those buttons
- ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4,
- ButtonFlags.DPadUp, ButtonFlags.DPadDown, ButtonFlags.DPadLeft, ButtonFlags.DPadRight,
- ButtonFlags.Start, ButtonFlags.Back, ButtonFlags.Special,
- ButtonFlags.L1, ButtonFlags.R1,
- ButtonFlags.LeftStickClick, ButtonFlags.RightStickClick,
- // additional buttons calculated from the above
- ButtonFlags.L2Soft, ButtonFlags.R2Soft, ButtonFlags.L2Full, ButtonFlags.R2Full,
- ButtonFlags.LeftStickUp, ButtonFlags.LeftStickDown, ButtonFlags.LeftStickLeft, ButtonFlags.LeftStickRight,
- ButtonFlags.RightStickUp, ButtonFlags.RightStickDown, ButtonFlags.RightStickLeft, ButtonFlags.RightStickRight
- };
-
- protected FontFamily GlyphFontFamily = new("PromptFont");
- public static readonly string defaultGlyph = "\u2753";
- public ControllerCapabilities Capabilities = ControllerCapabilities.None;
- protected SortedDictionary ColoredAxis = new();
- protected SortedDictionary ColoredButtons = new();
- protected FontFamily DefaultFontFamily = new("Segeo WP");
-
- public PnPDetails Details;
-
- public ButtonState InjectedButtons = new();
- public ControllerState Inputs = new();
-
- protected GamepadMotion gamepadMotion;
-
- protected double VibrationStrength = 1.0d;
- private byte _UserIndex = 255;
- private readonly int MaxUserIndex = 10;
-
- private int workingIdx = 0;
- private Thread workingThread;
- private bool workingThreadRunning;
-
- public virtual bool IsReady => true;
- public virtual bool IsWireless => false;
-
- public bool IsBusy
- {
- get
- {
- bool isBusy = false;
-
- // UI thread
- Application.Current.Dispatcher.Invoke(() =>
- {
- isBusy = !IsEnabled;
- });
-
- return isBusy;
- }
-
- set
- {
- // UI thread
- Application.Current.Dispatcher.Invoke(() =>
- {
- IsEnabled = !value;
- ProgressBarWarning.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
- });
-
- switch (value)
- {
- case false:
- {
- // kill working thread
- if (workingThread is not null)
- {
- workingThreadRunning = false;
- // Ensure the thread has finished execution
- if (workingThread.IsAlive)
- workingThread.Join();
- workingThread = null;
- }
-
- // visually update user index
- SetVirtualControllerVisualIndex(UserIndex);
- }
- break;
-
- case true:
- {
- workingThreadRunning = true;
- workingThread = new Thread(workingThreadLoop);
- workingThread.IsBackground = true;
- workingThread.Start();
- }
- return;
- }
- }
- }
-
- protected byte UserIndex
- {
- get
- {
- return _UserIndex;
- }
-
- set
- {
- _UserIndex = value;
- UserIndexChanged?.Invoke(value);
-
- if (IsBusy)
- return;
-
- // visually update user index
- SetVirtualControllerVisualIndex(value);
- }
- }
-
- private void SetVirtualControllerVisualIndex(int value)
- {
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- foreach (FrameworkElement frameworkElement in UserIndexPanel.Children)
- {
- if (frameworkElement is not Border)
- continue;
-
- Border border = (Border)frameworkElement;
- int idx = UserIndexPanel.Children.IndexOf(border);
-
- if (idx == value)
- border.SetResourceReference(BackgroundProperty, "AccentAAFillColorDefaultBrush");
- else
- border.SetResourceReference(BackgroundProperty, "SystemControlForegroundBaseLowBrush");
- }
- });
- }
-
- private void workingThreadLoop()
- {
- int direction = 1; // 1 for increasing, -1 for decreasing
- workingIdx = 0;
-
- while (workingThreadRunning)
- {
- workingIdx += direction; // increment or decrement the index
- if (workingIdx == MaxUserIndex - 1 || workingIdx == 0) // if the index reaches the limit or zero
- direction = -direction; // reverse the direction
-
- SetVirtualControllerVisualIndex(workingIdx);
-
- Thread.Sleep(100);
- }
- }
-
- public IController()
- {
- InitializeComponent();
- InitializeInputOutput();
-
- MaxUserIndex = UserIndexPanel.Children.Count;
- }
-
- protected virtual void UpdateSettings()
- { }
-
- protected virtual void InitializeInputOutput()
- { }
-
- public virtual void AttachDetails(PnPDetails details)
- {
- if (details is null)
- return;
-
- this.Details = details;
- Details.isHooked = true;
-
- if (details.isVirtual)
- return;
-
- // manage gamepad motion
- gamepadMotion = new(details.deviceInstanceId, CalibrationMode.Manual | CalibrationMode.SensorFusion);
-
- // UI thread
- Application.Current.Dispatcher.Invoke(() =>
- {
- ControllerType.Glyph = details.isInternal ? "\uE990" : details.isBluetooth ? "\uE702" : "\uECF0";
- });
-
- /*
- // Retrieve the oldest device using LINQ
- PnPDetails oldest = Details.isXInput ? DeviceManager.GetOldestXInput() : DeviceManager.GetOldestDInput();
- if (oldest is not null)
- IsInternal = oldest.deviceInstanceId == Details.deviceInstanceId;
- */
- }
-
- public virtual void UpdateInputs(long ticks, float delta)
- {
- InputsUpdated?.Invoke(Inputs, gamepadMotion, delta);
- }
-
- public virtual void UpdateInputs(long ticks, float delta, GamepadMotion gamepadOverwrite)
- {
- InputsUpdated?.Invoke(Inputs, gamepadOverwrite, delta);
- }
-
- public bool HasMotionSensor()
- {
- return Capabilities.HasFlag(ControllerCapabilities.MotionSensor);
- }
-
- public GamepadMotion GetMotionSensor()
- {
- return gamepadMotion;
- }
-
- public bool IsPhysical()
- {
- return !IsVirtual();
- }
-
- public bool IsVirtual()
- {
- if (Details is not null)
- return Details.isVirtual;
- return true;
- }
-
- public bool IsGaming()
- {
- if (Details is not null)
- return Details.isGaming;
- return false;
- }
-
- public int GetUserIndex()
- {
- return UserIndex;
- }
-
- public string GetInstancePath()
- {
- if (Details is not null)
- return Details.deviceInstanceId;
- return string.Empty;
- }
-
- public string GetContainerInstancePath()
- {
- if (Details is not null)
- return Details.baseContainerDeviceInstanceId;
- return string.Empty;
- }
-
- public override string ToString()
- {
- if (Details is not null)
- return Details.Name;
- return string.Empty;
- }
-
- protected void DrawUI()
- {
- // update name
- ControllerName.Text = (IsVirtual() ? Properties.Resources.Controller_Virtual : string.Empty) + ToString();
-
- // some elements of virtual controllers shouldn't be visible
- if (IsVirtual())
- {
- ui_button_hook.Visibility = Visibility.Collapsed;
- ui_button_hide.Visibility = Visibility.Collapsed;
- ui_button_calibrate.Visibility = Visibility.Collapsed;
- }
- }
-
- protected void UpdateUI()
- {
- // some elements of virtual controllers shouldn't be visible
- if (IsVirtual())
- return;
-
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- // ui_button_hook.Content = IsPlugged ? Properties.Resources.Controller_Disconnect : Properties.Resources.Controller_Connect;
- ui_button_hide.Content = IsHidden() ? Properties.Resources.Controller_Unhide : Properties.Resources.Controller_Hide;
- ui_button_calibrate.Visibility = Capabilities.HasFlag(ControllerCapabilities.MotionSensor) ? Visibility.Visible : Visibility.Collapsed;
- });
- }
-
- public Button GetButtonHook()
- {
- return ui_button_hook;
- }
-
- public Button GetButtonHide()
- {
- return ui_button_hide;
- }
-
- public Button GetButtonCalibrate()
- {
- return ui_button_calibrate;
- }
-
- public void InjectState(ButtonState State, bool IsKeyDown, bool IsKeyUp)
- {
- if (State.IsEmpty())
- return;
-
- foreach (var button in State.Buttons)
- InjectedButtons[button] = IsKeyDown;
-
- LogManager.LogDebug("Injecting {0} (IsKeyDown:{1}) (IsKeyUp:{2}) to {3}", string.Join(',', State.Buttons),
- IsKeyDown, IsKeyUp, ToString());
- }
-
- public void InjectButton(ButtonFlags button, bool IsKeyDown, bool IsKeyUp)
- {
- if (button == ButtonFlags.None)
- return;
-
- InjectedButtons[button] = IsKeyDown;
-
- LogManager.LogDebug("Injecting {0} (IsKeyDown:{1}) (IsKeyUp:{2}) to {3}", button, IsKeyDown, IsKeyUp,
- ToString());
- }
-
- public virtual void SetVibrationStrength(uint value, bool rumble = false)
- {
- VibrationStrength = value / 100.0d;
- if (rumble) Rumble();
- }
-
- public virtual void SetVibration(byte LargeMotor, byte SmallMotor)
- { }
-
- // let the controller decide itself what motor to use for a specific button
- public virtual void SetHaptic(HapticStrength strength, ButtonFlags button)
- {
- int delay;
- switch (strength)
- {
- default:
- case HapticStrength.Low:
- delay = 85;
- break;
-
- case HapticStrength.Medium:
- delay = 105;
- break;
-
- case HapticStrength.High:
- delay = 125;
- break;
- }
-
- switch (button)
- {
- case ButtonFlags.B1:
- case ButtonFlags.B2:
- case ButtonFlags.B3:
- case ButtonFlags.B4:
- case ButtonFlags.L1:
- case ButtonFlags.L2Soft:
- case ButtonFlags.Start:
- case ButtonFlags.RightStickClick:
- case ButtonFlags.RightPadClick:
- Rumble(delay, 0, byte.MaxValue);
- break;
- default:
- Rumble(delay, byte.MaxValue, 0);
- break;
- }
- }
-
- public virtual bool IsConnected()
- {
- return false;
- }
-
- private Task rumbleTask;
- public virtual void Rumble(int delay = 125, byte LargeMotor = byte.MaxValue, byte SmallMotor = byte.MaxValue)
- {
- // If the current task is not null and not completed
- if (rumbleTask != null && !rumbleTask.IsCompleted)
- SetVibration(0, 0);
-
- // Create a new task that executes the following code
- rumbleTask = Task.Run(async () =>
- {
- SetVibration(LargeMotor, SmallMotor);
- await Task.Delay(delay);
- SetVibration(0, 0);
- });
- }
-
- // this function cannot be called twice
- public virtual void Plug()
- {
- SetVibrationStrength(SettingsManager.GetUInt("VibrationStrength"));
-
- InjectedButtons.Clear();
-
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- ui_button_hook.IsEnabled = false;
- });
- }
-
- // this function cannot be called twice
- public virtual void Unplug()
- {
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- ui_button_hook.IsEnabled = true;
- });
- }
-
- public bool IsHidden()
- {
- // bool hide_device = HidHide.IsRegistered(Details.deviceInstanceId);
- bool hide_base = HidHide.IsRegistered(Details.baseContainerDeviceInstanceId);
- return /* hide_device || */ hide_base;
- }
-
- public virtual void Hide(bool powerCycle = true)
- {
- HideHID();
-
- if (powerCycle)
- {
- IsBusy = true;
-
- ControllerManager.PowerCyclers[Details.baseContainerDeviceInstanceId] = true;
- CyclePort();
- }
-
- UpdateUI();
- }
-
- public virtual void Unhide(bool powerCycle = true)
- {
- UnhideHID();
-
- if (powerCycle)
- {
- IsBusy = true;
-
- ControllerManager.PowerCyclers[Details.baseContainerDeviceInstanceId] = true;
- CyclePort();
- }
-
- UpdateUI();
- }
-
- public virtual void CyclePort()
- {
- Details.CyclePort();
- }
-
- public virtual void SetLightColor(byte R, byte G, byte B)
- {
- }
-
- protected void HideHID()
- {
- HidHide.HidePath(Details.baseContainerDeviceInstanceId);
- HidHide.HidePath(Details.deviceInstanceId);
-
- /*
- // get HidHideDevice
- HidHideDevice hideDevice = HidHide.GetHidHideDevice(Details.baseContainerDeviceInstanceId);
- if (hideDevice is not null)
- foreach (HidHideSubDevice subDevice in hideDevice.Devices)
- HidHide.HidePath(subDevice.DeviceInstancePath);
- */
- }
-
- protected void UnhideHID()
- {
- HidHide.UnhidePath(Details.baseContainerDeviceInstanceId);
- HidHide.UnhidePath(Details.deviceInstanceId);
-
- /*
- // get HidHideDevice
- HidHideDevice hideDevice = HidHide.GetHidHideDevice(Details.baseContainerDeviceInstanceId);
- if (hideDevice is not null)
- foreach (HidHideSubDevice subDevice in hideDevice.Devices)
- HidHide.UnhidePath(subDevice.DeviceInstancePath);
- */
- }
-
- public virtual bool RestoreDrivers()
- {
- return true;
- }
-
- public async void Calibrate()
- {
- SensorsManager.Calibrate(gamepadMotion);
- }
-
- protected virtual void ui_button_calibrate_Click(object sender, RoutedEventArgs e)
- {
- Calibrate();
- }
-
- protected virtual void ui_button_hide_Click(object sender, RoutedEventArgs e)
- { }
-
- protected virtual void ui_button_hook_Click(object sender, RoutedEventArgs e)
- { }
-
- public virtual string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.DPadUp:
- return "\u219F"; // Button A
- case ButtonFlags.DPadDown:
- return "\u21A1"; // Button B
- case ButtonFlags.DPadLeft:
- return "\u219E"; // Button X
- case ButtonFlags.DPadRight:
- return "\u21A0"; // Button Y
- case ButtonFlags.LeftStickClick:
- return "\u21BA";
- case ButtonFlags.RightStickClick:
- return "\u21BB";
- case ButtonFlags.LeftStickUp:
- return "\u21BE";
- case ButtonFlags.LeftStickDown:
- return "\u21C2";
- case ButtonFlags.LeftStickLeft:
- return "\u21BC";
- case ButtonFlags.LeftStickRight:
- return "\u21C0";
- case ButtonFlags.RightStickUp:
- return "\u21BF";
- case ButtonFlags.RightStickDown:
- return "\u21C3";
- case ButtonFlags.RightStickLeft:
- return "\u21BD";
- case ButtonFlags.RightStickRight:
- return "\u21C1";
- case ButtonFlags.VolumeUp:
- return "\u21fe";
- case ButtonFlags.VolumeDown:
- return "\u21fd";
- }
-
- return defaultGlyph;
- }
-
- public virtual string GetGlyph(AxisFlags axis)
- {
- switch (axis)
- {
- case AxisFlags.LeftStickX:
- return "\u21C4";
- case AxisFlags.LeftStickY:
- return "\u21C5";
- case AxisFlags.RightStickX:
- return "\u21C6";
- case AxisFlags.RightStickY:
- return "\u21F5";
- }
-
- return defaultGlyph;
- }
-
- public virtual string GetGlyph(AxisLayoutFlags axis)
- {
- switch (axis)
- {
- case AxisLayoutFlags.LeftStick:
- return "\u21CB";
- case AxisLayoutFlags.RightStick:
- return "\u21CC";
- case AxisLayoutFlags.Gyroscope:
- return "\u2B94";
- }
-
- return defaultGlyph;
- }
-
- public GlyphIconInfo GetGlyphIconInfo(ButtonFlags button, int fontIconSize = 14)
- {
- var glyph = GetGlyph(button);
- return new GlyphIconInfo
- {
- Name = GetButtonName(button),
- Glyph = glyph,
- FontSize = glyph is not null ? 28 : fontIconSize,
- FontFamily = glyph is not null ? GlyphFontFamily : null,
- Foreground = GetGlyphColor(button)
- };
- }
-
- public GlyphIconInfo GetGlyphIconInfo(AxisLayoutFlags axis, int fontIconSize = 14)
- {
- var glyph = GetGlyph(axis);
- return new GlyphIconInfo
- {
- Name = GetAxisName(axis),
- Glyph = glyph,
- FontSize = glyph is not null ? 28 : fontIconSize,
- FontFamily = glyph is not null ? GlyphFontFamily : null,
- Foreground = GetGlyphColor(axis)
- };
- }
-
- [Obsolete("GetFontIcon has dependencies on UI and should be avoided. Use GetGlyphIconInfo instead.")]
- public FontIcon GetFontIcon(ButtonFlags button, int FontIconSize = 14)
- {
- var FontIcon = new FontIcon
- {
- Glyph = GetGlyph(button),
- FontSize = FontIconSize,
- Foreground = GetGlyphColor(button)
- };
-
- if (FontIcon.Glyph is not null)
- {
- FontIcon.FontFamily = GlyphFontFamily;
- FontIcon.FontSize = 28;
- }
-
- return FontIcon;
- }
-
-
- [Obsolete("GetFontIcon has dependencies on UI and should be avoided. Use GetGlyphIconInfo instead.")]
- public FontIcon GetFontIcon(AxisLayoutFlags axis, int FontIconSize = 14)
- {
- var FontIcon = new FontIcon
- {
- Glyph = GetGlyph(axis),
- FontSize = FontIconSize,
- Foreground = GetGlyphColor(axis)
- };
-
- if (FontIcon.Glyph is not null)
- {
- FontIcon.FontFamily = GlyphFontFamily;
- FontIcon.FontSize = 28;
- }
-
- return FontIcon;
- }
-
- public Brush GetGlyphColor(ButtonFlags button)
- {
- if (ColoredButtons.TryGetValue(button, out var brush))
- return brush;
-
- return null;
- }
-
- public Brush GetGlyphColor(AxisLayoutFlags axis)
- {
- /* if (AxisBrush.TryGetValue(axis, out Brush brush))
- return brush; */
-
- return null;
- }
-
- private static bool IsTrigger(AxisLayoutFlags axis)
- {
- return axis is AxisLayoutFlags.L2 || axis is AxisLayoutFlags.R2;
- }
-
- public List GetTargetButtons()
- {
- IEnumerable buttons = Enum.GetValues(typeof(ButtonFlags)).Cast();
-
- return buttons.Where(a => TargetButtons.Contains(a)).ToList();
- }
-
- public List GetTargetAxis()
- {
- IEnumerable axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast();
-
- return axis.Where(a => TargetAxis.Contains(a) && !IsTrigger(a)).ToList();
- }
-
- public List GetTargetTriggers()
- {
- IEnumerable axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast();
-
- return axis.Where(a => TargetAxis.Contains(a) && IsTrigger(a)).ToList();
- }
-
- public bool HasSourceButton(ButtonFlags button)
- {
- return SourceButtons.Contains(button);
- }
-
- public bool HasSourceButton(List buttons)
- {
- return SourceButtons.Any(buttons.Contains);
- }
-
- public bool HasSourceAxis(AxisLayoutFlags axis)
- {
- return SourceAxis.Contains(axis);
- }
-
- public bool HasSourceAxis(List axis)
- {
- return SourceAxis.Any(axis.Contains);
- }
-
- public string GetButtonName(ButtonFlags button)
- {
- return EnumUtils.GetDescriptionFromEnumValue(button, GetType().Name);
- }
-
- public string GetAxisName(AxisLayoutFlags axis)
- {
- return EnumUtils.GetDescriptionFromEnumValue(axis, GetType().Name);
- }
-
- #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);
-
- #endregion
- }
-}
+using HandheldCompanion.Actions;
+using HandheldCompanion.Helpers;
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
+using HandheldCompanion.Misc;
+using HandheldCompanion.Utils;
+using iNKORE.UI.WPF.Modern.Controls;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+
+namespace HandheldCompanion.Controllers
+{
+ [Flags]
+ public enum ControllerCapabilities : ushort
+ {
+ None = 0,
+ MotionSensor = 1,
+ }
+
+ ///
+ /// Logique d'interaction pour IController.xaml
+ ///
+ public partial class IController : UserControl
+ {
+ // Buttons and axes we should be able to map to.
+ // When we have target controllers with different buttons (e.g. in VigEm) this will have to be moved elsewhere.
+ protected readonly List TargetButtons = new()
+ {
+ ButtonFlags.None, ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4,
+ ButtonFlags.DPadUp, ButtonFlags.DPadDown, ButtonFlags.DPadLeft, ButtonFlags.DPadRight,
+ ButtonFlags.Start, ButtonFlags.Back, ButtonFlags.Special,
+ ButtonFlags.L1, ButtonFlags.R1,
+ ButtonFlags.LeftStickClick, ButtonFlags.RightStickClick,
+ };
+
+ protected readonly List TargetAxis = new()
+ {
+ AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick,
+ AxisLayoutFlags.L2, AxisLayoutFlags.R2,
+ };
+
+ protected readonly List SourceAxis = new()
+ {
+ // same as target, we assume all controllers have those axes
+ AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick,
+ AxisLayoutFlags.L2, AxisLayoutFlags.R2
+ };
+
+ // Buttons and axes all controllers have that we can map.
+ // Additional ones can be added per controller.
+ protected readonly List SourceButtons = new()
+ {
+ // same as target, we assume all controllers have those buttons
+ ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4,
+ ButtonFlags.DPadUp, ButtonFlags.DPadDown, ButtonFlags.DPadLeft, ButtonFlags.DPadRight,
+ ButtonFlags.Start, ButtonFlags.Back, ButtonFlags.Special,
+ ButtonFlags.L1, ButtonFlags.R1,
+ ButtonFlags.LeftStickClick, ButtonFlags.RightStickClick,
+ // additional buttons calculated from the above
+ ButtonFlags.L2Soft, ButtonFlags.R2Soft, ButtonFlags.L2Full, ButtonFlags.R2Full,
+ ButtonFlags.LeftStickUp, ButtonFlags.LeftStickDown, ButtonFlags.LeftStickLeft, ButtonFlags.LeftStickRight,
+ ButtonFlags.RightStickUp, ButtonFlags.RightStickDown, ButtonFlags.RightStickLeft, ButtonFlags.RightStickRight
+ };
+
+ protected FontFamily GlyphFontFamily = new("PromptFont");
+ public static readonly string defaultGlyph = "\u2753";
+ public ControllerCapabilities Capabilities = ControllerCapabilities.None;
+ protected SortedDictionary ColoredAxis = new();
+ protected SortedDictionary ColoredButtons = new();
+ protected FontFamily DefaultFontFamily = new("Segeo WP");
+
+ public PnPDetails Details;
+
+ public ButtonState InjectedButtons = new();
+ public ControllerState Inputs = new();
+
+ protected GamepadMotion gamepadMotion;
+
+ protected double VibrationStrength = 1.0d;
+ private byte _UserIndex = 255;
+ private readonly int MaxUserIndex = 10;
+
+ private int workingIdx = 0;
+ private Thread workingThread;
+ private bool workingThreadRunning;
+
+ public virtual bool IsReady => true;
+ public virtual bool IsWireless => false;
+
+ public bool IsBusy
+ {
+ get
+ {
+ bool isBusy = false;
+
+ // UI thread
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ isBusy = !IsEnabled;
+ });
+
+ return isBusy;
+ }
+
+ set
+ {
+ // UI thread
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ IsEnabled = !value;
+ ProgressBarWarning.Visibility = value ? Visibility.Visible : Visibility.Collapsed;
+ });
+
+ switch (value)
+ {
+ case false:
+ {
+ // kill working thread
+ if (workingThread is not null)
+ {
+ workingThreadRunning = false;
+ // Ensure the thread has finished execution
+ if (workingThread.IsAlive)
+ workingThread.Join();
+ workingThread = null;
+ }
+
+ // visually update user index
+ SetVirtualControllerVisualIndex(UserIndex);
+ }
+ break;
+
+ case true:
+ {
+ workingThreadRunning = true;
+ workingThread = new Thread(workingThreadLoop);
+ workingThread.IsBackground = true;
+ workingThread.Start();
+ }
+ return;
+ }
+ }
+ }
+
+ protected byte UserIndex
+ {
+ get
+ {
+ return _UserIndex;
+ }
+
+ set
+ {
+ _UserIndex = value;
+ UserIndexChanged?.Invoke(value);
+
+ if (IsBusy)
+ return;
+
+ // visually update user index
+ SetVirtualControllerVisualIndex(value);
+ }
+ }
+
+ private void SetVirtualControllerVisualIndex(int value)
+ {
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ foreach (FrameworkElement frameworkElement in UserIndexPanel.Children)
+ {
+ if (frameworkElement is not Border)
+ continue;
+
+ Border border = (Border)frameworkElement;
+ int idx = UserIndexPanel.Children.IndexOf(border);
+
+ if (idx == value)
+ border.SetResourceReference(BackgroundProperty, "AccentAAFillColorDefaultBrush");
+ else
+ border.SetResourceReference(BackgroundProperty, "SystemControlForegroundBaseLowBrush");
+ }
+ });
+ }
+
+ private void workingThreadLoop()
+ {
+ int direction = 1; // 1 for increasing, -1 for decreasing
+ workingIdx = 0;
+
+ while (workingThreadRunning)
+ {
+ workingIdx += direction; // increment or decrement the index
+ if (workingIdx == MaxUserIndex - 1 || workingIdx == 0) // if the index reaches the limit or zero
+ direction = -direction; // reverse the direction
+
+ SetVirtualControllerVisualIndex(workingIdx);
+
+ Thread.Sleep(100);
+ }
+ }
+
+ public IController()
+ {
+ InitializeComponent();
+ InitializeInputOutput();
+
+ MaxUserIndex = UserIndexPanel.Children.Count;
+ }
+
+ protected virtual void UpdateSettings()
+ { }
+
+ protected virtual void InitializeInputOutput()
+ { }
+
+ public virtual void AttachDetails(PnPDetails details)
+ {
+ if (details is null)
+ return;
+
+ this.Details = details;
+ Details.isHooked = true;
+
+ if (details.isVirtual)
+ return;
+
+ // manage gamepad motion
+ gamepadMotion = new(details.deviceInstanceId, CalibrationMode.Manual | CalibrationMode.SensorFusion);
+
+ // UI thread
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ ControllerType.Glyph = details.isInternal ? "\uE990" : details.isBluetooth ? "\uE702" : "\uECF0";
+ });
+
+ /*
+ // Retrieve the oldest device using LINQ
+ PnPDetails oldest = Details.isXInput ? DeviceManager.GetOldestXInput() : DeviceManager.GetOldestDInput();
+ if (oldest is not null)
+ IsInternal = oldest.deviceInstanceId == Details.deviceInstanceId;
+ */
+ }
+
+ public virtual void UpdateInputs(long ticks, float delta)
+ {
+ InputsUpdated?.Invoke(Inputs, gamepadMotion, delta);
+ }
+
+ public virtual void UpdateInputs(long ticks, float delta, GamepadMotion gamepadOverwrite)
+ {
+ InputsUpdated?.Invoke(Inputs, gamepadOverwrite, delta);
+ }
+
+ public bool HasMotionSensor()
+ {
+ return Capabilities.HasFlag(ControllerCapabilities.MotionSensor);
+ }
+
+ public GamepadMotion GetMotionSensor()
+ {
+ return gamepadMotion;
+ }
+
+ public bool IsPhysical()
+ {
+ return !IsVirtual();
+ }
+
+ public bool IsVirtual()
+ {
+ if (Details is not null)
+ return Details.isVirtual;
+ return true;
+ }
+
+ public bool IsGaming()
+ {
+ if (Details is not null)
+ return Details.isGaming;
+ return false;
+ }
+
+ public int GetUserIndex()
+ {
+ return UserIndex;
+ }
+
+ public string GetInstancePath()
+ {
+ if (Details is not null)
+ return Details.deviceInstanceId;
+ return string.Empty;
+ }
+
+ public string GetContainerInstancePath()
+ {
+ if (Details is not null)
+ return Details.baseContainerDeviceInstanceId;
+ return string.Empty;
+ }
+
+ public override string ToString()
+ {
+ if (Details is not null)
+ return Details.Name;
+ return string.Empty;
+ }
+
+ protected void DrawUI()
+ {
+ // update name
+ ControllerName.Text = (IsVirtual() ? Properties.Resources.Controller_Virtual : string.Empty) + ToString();
+
+ // some elements of virtual controllers shouldn't be visible
+ if (IsVirtual())
+ {
+ ui_button_hook.Visibility = Visibility.Collapsed;
+ ui_button_hide.Visibility = Visibility.Collapsed;
+ ui_button_calibrate.Visibility = Visibility.Collapsed;
+ }
+ }
+
+ protected void UpdateUI()
+ {
+ // some elements of virtual controllers shouldn't be visible
+ if (IsVirtual())
+ return;
+
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ // ui_button_hook.Content = IsPlugged ? Properties.Resources.Controller_Disconnect : Properties.Resources.Controller_Connect;
+ ui_button_hide.Content = IsHidden() ? Properties.Resources.Controller_Unhide : Properties.Resources.Controller_Hide;
+ ui_button_calibrate.Visibility = Capabilities.HasFlag(ControllerCapabilities.MotionSensor) ? Visibility.Visible : Visibility.Collapsed;
+ });
+ }
+
+ public Button GetButtonHook()
+ {
+ return ui_button_hook;
+ }
+
+ public Button GetButtonHide()
+ {
+ return ui_button_hide;
+ }
+
+ public Button GetButtonCalibrate()
+ {
+ return ui_button_calibrate;
+ }
+
+ public void InjectState(ButtonState State, bool IsKeyDown, bool IsKeyUp)
+ {
+ if (State.IsEmpty())
+ return;
+
+ foreach (var button in State.Buttons)
+ InjectedButtons[button] = IsKeyDown;
+
+ LogManager.LogDebug("Injecting {0} (IsKeyDown:{1}) (IsKeyUp:{2}) to {3}", string.Join(',', State.Buttons),
+ IsKeyDown, IsKeyUp, ToString());
+ }
+
+ public void InjectButton(ButtonFlags button, bool IsKeyDown, bool IsKeyUp)
+ {
+ if (button == ButtonFlags.None)
+ return;
+
+ InjectedButtons[button] = IsKeyDown;
+
+ LogManager.LogDebug("Injecting {0} (IsKeyDown:{1}) (IsKeyUp:{2}) to {3}", button, IsKeyDown, IsKeyUp,
+ ToString());
+ }
+
+ public virtual void SetVibrationStrength(uint value, bool rumble = false)
+ {
+ VibrationStrength = value / 100.0d;
+ if (rumble) Rumble();
+ }
+
+ public virtual void SetVibration(byte LargeMotor, byte SmallMotor)
+ { }
+
+ // let the controller decide itself what motor to use for a specific button
+ public virtual void SetHaptic(HapticStrength strength, ButtonFlags button)
+ {
+ int delay;
+ switch (strength)
+ {
+ default:
+ case HapticStrength.Low:
+ delay = 85;
+ break;
+
+ case HapticStrength.Medium:
+ delay = 105;
+ break;
+
+ case HapticStrength.High:
+ delay = 125;
+ break;
+ }
+
+ switch (button)
+ {
+ case ButtonFlags.B1:
+ case ButtonFlags.B2:
+ case ButtonFlags.B3:
+ case ButtonFlags.B4:
+ case ButtonFlags.L1:
+ case ButtonFlags.L2Soft:
+ case ButtonFlags.Start:
+ case ButtonFlags.RightStickClick:
+ case ButtonFlags.RightPadClick:
+ Rumble(delay, 0, byte.MaxValue);
+ break;
+ default:
+ Rumble(delay, byte.MaxValue, 0);
+ break;
+ }
+ }
+
+ public virtual bool IsConnected()
+ {
+ return false;
+ }
+
+ private Task rumbleTask;
+ public virtual void Rumble(int delay = 125, byte LargeMotor = byte.MaxValue, byte SmallMotor = byte.MaxValue)
+ {
+ // If the current task is not null and not completed
+ if (rumbleTask != null && !rumbleTask.IsCompleted)
+ SetVibration(0, 0);
+
+ // Create a new task that executes the following code
+ rumbleTask = Task.Run(async () =>
+ {
+ SetVibration(LargeMotor, SmallMotor);
+ await Task.Delay(delay);
+ SetVibration(0, 0);
+ });
+ }
+
+ // this function cannot be called twice
+ public virtual void Plug()
+ {
+ SetVibrationStrength(SettingsManager.GetUInt("VibrationStrength"));
+
+ InjectedButtons.Clear();
+
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ ui_button_hook.IsEnabled = false;
+ });
+ }
+
+ // this function cannot be called twice
+ public virtual void Unplug()
+ {
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ ui_button_hook.IsEnabled = true;
+ });
+ }
+
+ public bool IsHidden()
+ {
+ // bool hide_device = HidHide.IsRegistered(Details.deviceInstanceId);
+ bool hide_base = HidHide.IsRegistered(Details.baseContainerDeviceInstanceId);
+ return /* hide_device || */ hide_base;
+ }
+
+ public virtual void Hide(bool powerCycle = true)
+ {
+ HideHID();
+
+ if (powerCycle)
+ {
+ IsBusy = true;
+
+ ControllerManager.PowerCyclers[Details.baseContainerDeviceInstanceId] = true;
+ CyclePort();
+ }
+
+ UpdateUI();
+ }
+
+ public virtual void Unhide(bool powerCycle = true)
+ {
+ UnhideHID();
+
+ if (powerCycle)
+ {
+ IsBusy = true;
+
+ ControllerManager.PowerCyclers[Details.baseContainerDeviceInstanceId] = true;
+ CyclePort();
+ }
+
+ UpdateUI();
+ }
+
+ public virtual void CyclePort()
+ {
+ Details.CyclePort();
+ }
+
+ public virtual void SetLightColor(byte R, byte G, byte B)
+ {
+ }
+
+ protected void HideHID()
+ {
+ HidHide.HidePath(Details.baseContainerDeviceInstanceId);
+ HidHide.HidePath(Details.deviceInstanceId);
+
+ /*
+ // get HidHideDevice
+ HidHideDevice hideDevice = HidHide.GetHidHideDevice(Details.baseContainerDeviceInstanceId);
+ if (hideDevice is not null)
+ foreach (HidHideSubDevice subDevice in hideDevice.Devices)
+ HidHide.HidePath(subDevice.DeviceInstancePath);
+ */
+ }
+
+ protected void UnhideHID()
+ {
+ HidHide.UnhidePath(Details.baseContainerDeviceInstanceId);
+ HidHide.UnhidePath(Details.deviceInstanceId);
+
+ /*
+ // get HidHideDevice
+ HidHideDevice hideDevice = HidHide.GetHidHideDevice(Details.baseContainerDeviceInstanceId);
+ if (hideDevice is not null)
+ foreach (HidHideSubDevice subDevice in hideDevice.Devices)
+ HidHide.UnhidePath(subDevice.DeviceInstancePath);
+ */
+ }
+
+ public virtual bool RestoreDrivers()
+ {
+ return true;
+ }
+
+ public async void Calibrate()
+ {
+ SensorsManager.Calibrate(gamepadMotion);
+ }
+
+ protected virtual void ui_button_calibrate_Click(object sender, RoutedEventArgs e)
+ {
+ Calibrate();
+ }
+
+ protected virtual void ui_button_hide_Click(object sender, RoutedEventArgs e)
+ { }
+
+ protected virtual void ui_button_hook_Click(object sender, RoutedEventArgs e)
+ { }
+
+ public virtual string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.DPadUp:
+ return "\u219F"; // Button A
+ case ButtonFlags.DPadDown:
+ return "\u21A1"; // Button B
+ case ButtonFlags.DPadLeft:
+ return "\u219E"; // Button X
+ case ButtonFlags.DPadRight:
+ return "\u21A0"; // Button Y
+ case ButtonFlags.LeftStickClick:
+ return "\u21BA";
+ case ButtonFlags.RightStickClick:
+ return "\u21BB";
+ case ButtonFlags.LeftStickUp:
+ return "\u21BE";
+ case ButtonFlags.LeftStickDown:
+ return "\u21C2";
+ case ButtonFlags.LeftStickLeft:
+ return "\u21BC";
+ case ButtonFlags.LeftStickRight:
+ return "\u21C0";
+ case ButtonFlags.RightStickUp:
+ return "\u21BF";
+ case ButtonFlags.RightStickDown:
+ return "\u21C3";
+ case ButtonFlags.RightStickLeft:
+ return "\u21BD";
+ case ButtonFlags.RightStickRight:
+ return "\u21C1";
+ case ButtonFlags.VolumeUp:
+ return "\u21fe";
+ case ButtonFlags.VolumeDown:
+ return "\u21fd";
+ }
+
+ return defaultGlyph;
+ }
+
+ public virtual string GetGlyph(AxisFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisFlags.LeftStickX:
+ return "\u21C4";
+ case AxisFlags.LeftStickY:
+ return "\u21C5";
+ case AxisFlags.RightStickX:
+ return "\u21C6";
+ case AxisFlags.RightStickY:
+ return "\u21F5";
+ }
+
+ return defaultGlyph;
+ }
+
+ public virtual string GetGlyph(AxisLayoutFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisLayoutFlags.LeftStick:
+ return "\u21CB";
+ case AxisLayoutFlags.RightStick:
+ return "\u21CC";
+ case AxisLayoutFlags.Gyroscope:
+ return "\u2B94";
+ }
+
+ return defaultGlyph;
+ }
+
+ public GlyphIconInfo GetGlyphIconInfo(ButtonFlags button, int fontIconSize = 14)
+ {
+ var glyph = GetGlyph(button);
+ return new GlyphIconInfo
+ {
+ Name = GetButtonName(button),
+ Glyph = glyph,
+ FontSize = glyph is not null ? 28 : fontIconSize,
+ FontFamily = glyph is not null ? GlyphFontFamily : null,
+ Foreground = GetGlyphColor(button)
+ };
+ }
+
+ public GlyphIconInfo GetGlyphIconInfo(AxisLayoutFlags axis, int fontIconSize = 14)
+ {
+ var glyph = GetGlyph(axis);
+ return new GlyphIconInfo
+ {
+ Name = GetAxisName(axis),
+ Glyph = glyph,
+ FontSize = glyph is not null ? 28 : fontIconSize,
+ FontFamily = glyph is not null ? GlyphFontFamily : null,
+ Foreground = GetGlyphColor(axis)
+ };
+ }
+
+ [Obsolete("GetFontIcon has dependencies on UI and should be avoided. Use GetGlyphIconInfo instead.")]
+ public FontIcon GetFontIcon(ButtonFlags button, int FontIconSize = 14)
+ {
+ var FontIcon = new FontIcon
+ {
+ Glyph = GetGlyph(button),
+ FontSize = FontIconSize,
+ Foreground = GetGlyphColor(button)
+ };
+
+ if (FontIcon.Glyph is not null)
+ {
+ FontIcon.FontFamily = GlyphFontFamily;
+ FontIcon.FontSize = 28;
+ }
+
+ return FontIcon;
+ }
+
+
+ [Obsolete("GetFontIcon has dependencies on UI and should be avoided. Use GetGlyphIconInfo instead.")]
+ public FontIcon GetFontIcon(AxisLayoutFlags axis, int FontIconSize = 14)
+ {
+ var FontIcon = new FontIcon
+ {
+ Glyph = GetGlyph(axis),
+ FontSize = FontIconSize,
+ Foreground = GetGlyphColor(axis)
+ };
+
+ if (FontIcon.Glyph is not null)
+ {
+ FontIcon.FontFamily = GlyphFontFamily;
+ FontIcon.FontSize = 28;
+ }
+
+ return FontIcon;
+ }
+
+ public Brush GetGlyphColor(ButtonFlags button)
+ {
+ if (ColoredButtons.TryGetValue(button, out var brush))
+ return brush;
+
+ return null;
+ }
+
+ public Brush GetGlyphColor(AxisLayoutFlags axis)
+ {
+ /* if (AxisBrush.TryGetValue(axis, out Brush brush))
+ return brush; */
+
+ return null;
+ }
+
+ private static bool IsTrigger(AxisLayoutFlags axis)
+ {
+ return axis is AxisLayoutFlags.L2 || axis is AxisLayoutFlags.R2;
+ }
+
+ public List GetTargetButtons()
+ {
+ IEnumerable buttons = Enum.GetValues(typeof(ButtonFlags)).Cast();
+
+ return buttons.Where(a => TargetButtons.Contains(a)).ToList();
+ }
+
+ public List GetTargetAxis()
+ {
+ IEnumerable axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast();
+
+ return axis.Where(a => TargetAxis.Contains(a) && !IsTrigger(a)).ToList();
+ }
+
+ public List GetTargetTriggers()
+ {
+ IEnumerable axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast();
+
+ return axis.Where(a => TargetAxis.Contains(a) && IsTrigger(a)).ToList();
+ }
+
+ public bool HasSourceButton(ButtonFlags button)
+ {
+ return SourceButtons.Contains(button);
+ }
+
+ public bool HasSourceButton(List buttons)
+ {
+ return SourceButtons.Any(buttons.Contains);
+ }
+
+ public bool HasSourceAxis(AxisLayoutFlags axis)
+ {
+ return SourceAxis.Contains(axis);
+ }
+
+ public bool HasSourceAxis(List axis)
+ {
+ return SourceAxis.Any(axis.Contains);
+ }
+
+ public string GetButtonName(ButtonFlags button)
+ {
+ return EnumUtils.GetDescriptionFromEnumValue(button, GetType().Name);
+ }
+
+ public string GetAxisName(AxisLayoutFlags axis)
+ {
+ return EnumUtils.GetDescriptionFromEnumValue(axis, GetType().Name);
+ }
+
+ #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);
+
+ #endregion
+ }
+}
diff --git a/HandheldCompanion/Controllers/JSController.cs b/HandheldCompanion/Controllers/JSController.cs
index 2ee80bb21..9dc80a911 100644
--- a/HandheldCompanion/Controllers/JSController.cs
+++ b/HandheldCompanion/Controllers/JSController.cs
@@ -1,179 +1,179 @@
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Utils;
-using Nefarius.Utilities.DeviceManagement.PnP;
-using System;
-using System.Threading.Tasks;
-using static JSL;
-
-namespace HandheldCompanion.Controllers;
-
-public class JSController : IController
-{
- protected JOY_SETTINGS sSETTINGS;
- protected JOY_SHOCK_STATE sTATE;
- protected IMU_STATE iMU_STATE;
-
- protected float TriggerThreshold = 0.12f;
- protected float LeftThumbDeadZone = 0.24f;
- protected float RightThumbDeadZone = 0.265f;
-
- public JSController()
- { }
-
- public JSController(JOY_SETTINGS settings, PnPDetails details)
- {
- AttachJoySettings(settings);
- AttachDetails(details);
-
- // Capabilities
- Capabilities |= ControllerCapabilities.MotionSensor;
-
- // UI
- DrawUI();
- UpdateUI();
- }
-
- public override string ToString()
- {
- var baseName = base.ToString();
- if (!string.IsNullOrEmpty(baseName))
- return baseName;
-
- switch ((JOY_TYPE)sSETTINGS.controllerType)
- {
- case JOY_TYPE.DualShock4:
- return "DualShock 4";
- }
-
- return $"JoyShock Controller {UserIndex}";
- }
-
- public override void UpdateInputs(long ticks, float delta)
- {
- base.UpdateInputs(ticks, delta);
- }
-
- public virtual void UpdateState(float delta)
- {
- // skip if controller isn't connected
- if (!IsConnected())
- return;
-
- Inputs.ButtonState = InjectedButtons.Clone() as ButtonState;
-
- // pull state
- sTATE = JslGetSimpleState(UserIndex);
-
- Inputs.ButtonState[ButtonFlags.B1] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskS);
- Inputs.ButtonState[ButtonFlags.B2] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskE);
- Inputs.ButtonState[ButtonFlags.B3] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskW);
- Inputs.ButtonState[ButtonFlags.B4] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskN);
-
- Inputs.ButtonState[ButtonFlags.Back] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskMinus);
- Inputs.ButtonState[ButtonFlags.Start] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskPlus);
-
- Inputs.ButtonState[ButtonFlags.DPadUp] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskUp);
- Inputs.ButtonState[ButtonFlags.DPadDown] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskDown);
- Inputs.ButtonState[ButtonFlags.DPadLeft] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskLeft);
- Inputs.ButtonState[ButtonFlags.DPadRight] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskRight);
-
- Inputs.ButtonState[ButtonFlags.Special] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskHome);
-
- // Triggers
- Inputs.ButtonState[ButtonFlags.L1] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskL);
- Inputs.ButtonState[ButtonFlags.R1] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskR);
-
- Inputs.ButtonState[ButtonFlags.L2Soft] = sTATE.lTrigger > TriggerThreshold;
- Inputs.ButtonState[ButtonFlags.R2Soft] = sTATE.rTrigger > TriggerThreshold;
-
- Inputs.ButtonState[ButtonFlags.L2Full] = sTATE.lTrigger > TriggerThreshold * 8;
- Inputs.ButtonState[ButtonFlags.R2Full] = sTATE.rTrigger > TriggerThreshold * 8;
-
- Inputs.AxisState[AxisFlags.L2] = (short)InputUtils.MapRange(sTATE.lTrigger, 0.0f, 1.0f, byte.MinValue, byte.MaxValue);
- Inputs.AxisState[AxisFlags.R2] = (short)InputUtils.MapRange(sTATE.rTrigger, 0.0f, 1.0f, byte.MinValue, byte.MaxValue);
-
- // Left Stick
- Inputs.ButtonState[ButtonFlags.LeftStickLeft] = sTATE.stickLX < -LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickRight] = sTATE.stickLX > LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickDown] = sTATE.stickLY < -LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickUp] = sTATE.stickLY > LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickClick] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskLClick);
-
- Inputs.AxisState[AxisFlags.LeftStickX] = (short)InputUtils.MapRange(sTATE.stickLX, -1.0f, 1.0f, short.MinValue, short.MaxValue);
- Inputs.AxisState[AxisFlags.LeftStickY] = (short)InputUtils.MapRange(sTATE.stickLY, -1.0f, 1.0f, short.MinValue, short.MaxValue);
-
- // Right Stick
- Inputs.ButtonState[ButtonFlags.RightStickLeft] = sTATE.stickRX < -LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.RightStickRight] = sTATE.stickRX > LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.RightStickDown] = sTATE.stickRY < -LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.RightStickUp] = sTATE.stickRY > LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.RightStickClick] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskRClick);
-
- Inputs.AxisState[AxisFlags.RightStickX] = (short)InputUtils.MapRange(sTATE.stickRX, -1.0f, 1.0f, short.MinValue, short.MaxValue);
- Inputs.AxisState[AxisFlags.RightStickY] = (short)InputUtils.MapRange(sTATE.stickRY, -1.0f, 1.0f, short.MinValue, short.MaxValue);
-
- // IMU
- iMU_STATE = JslGetIMUState(UserIndex);
-
- // store motion
- Inputs.GyroState.SetGyroscope(iMU_STATE.gyroX, iMU_STATE.gyroY, iMU_STATE.gyroZ);
- Inputs.GyroState.SetAccelerometer(iMU_STATE.accelX, iMU_STATE.accelY, iMU_STATE.accelZ);
-
- // process motion
- gamepadMotion.ProcessMotion(iMU_STATE.gyroX, iMU_STATE.gyroY, iMU_STATE.gyroZ, iMU_STATE.accelX, iMU_STATE.accelY, iMU_STATE.accelZ, delta);
- }
-
- public override bool IsConnected()
- {
- return JslStillConnected(UserIndex);
- }
-
- public override void Plug()
- {
- base.Plug();
- }
-
- public override void Unplug()
- {
- base.Unplug();
- }
-
- public override void SetVibration(byte LargeMotor, byte SmallMotor)
- {
- JslSetRumble(UserIndex, (byte)(SmallMotor * VibrationStrength), (byte)(LargeMotor * VibrationStrength));
- }
-
- public override void CyclePort()
- {
- string enumerator = Details.GetEnumerator();
- switch (enumerator)
- {
- default:
- case "BTHENUM":
- Task.Run(async () =>
- {
- // Details.InstallNullDrivers();
- // await Task.Delay(1000);
- // Details.InstallCustomDriver("hidbth.inf");
-
- Details.Uninstall(false);
- await Task.Delay(1000);
- Devcon.Refresh();
- });
- break;
- case "USB":
- base.CyclePort();
- break;
- }
- }
-
- public void AttachJoySettings(JOY_SETTINGS settings)
- {
- this.sSETTINGS = settings;
- this.UserIndex = (byte)settings.playerNumber;
-
- // manage elsewhere
- JslResetContinuousCalibration(UserIndex);
- JslPauseContinuousCalibration(UserIndex);
- }
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Utils;
+using Nefarius.Utilities.DeviceManagement.PnP;
+using System;
+using System.Threading.Tasks;
+using static JSL;
+
+namespace HandheldCompanion.Controllers;
+
+public class JSController : IController
+{
+ protected JOY_SETTINGS sSETTINGS;
+ protected JOY_SHOCK_STATE sTATE;
+ protected IMU_STATE iMU_STATE;
+
+ protected float TriggerThreshold = 0.12f;
+ protected float LeftThumbDeadZone = 0.24f;
+ protected float RightThumbDeadZone = 0.265f;
+
+ public JSController()
+ { }
+
+ public JSController(JOY_SETTINGS settings, PnPDetails details)
+ {
+ AttachJoySettings(settings);
+ AttachDetails(details);
+
+ // Capabilities
+ Capabilities |= ControllerCapabilities.MotionSensor;
+
+ // UI
+ DrawUI();
+ UpdateUI();
+ }
+
+ public override string ToString()
+ {
+ var baseName = base.ToString();
+ if (!string.IsNullOrEmpty(baseName))
+ return baseName;
+
+ switch ((JOY_TYPE)sSETTINGS.controllerType)
+ {
+ case JOY_TYPE.DualShock4:
+ return "DualShock 4";
+ }
+
+ return $"JoyShock Controller {UserIndex}";
+ }
+
+ public override void UpdateInputs(long ticks, float delta)
+ {
+ base.UpdateInputs(ticks, delta);
+ }
+
+ public virtual void UpdateState(float delta)
+ {
+ // skip if controller isn't connected
+ if (!IsConnected())
+ return;
+
+ Inputs.ButtonState = InjectedButtons.Clone() as ButtonState;
+
+ // pull state
+ sTATE = JslGetSimpleState(UserIndex);
+
+ Inputs.ButtonState[ButtonFlags.B1] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskS);
+ Inputs.ButtonState[ButtonFlags.B2] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskE);
+ Inputs.ButtonState[ButtonFlags.B3] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskW);
+ Inputs.ButtonState[ButtonFlags.B4] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskN);
+
+ Inputs.ButtonState[ButtonFlags.Back] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskMinus);
+ Inputs.ButtonState[ButtonFlags.Start] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskPlus);
+
+ Inputs.ButtonState[ButtonFlags.DPadUp] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskUp);
+ Inputs.ButtonState[ButtonFlags.DPadDown] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskDown);
+ Inputs.ButtonState[ButtonFlags.DPadLeft] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskLeft);
+ Inputs.ButtonState[ButtonFlags.DPadRight] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskRight);
+
+ Inputs.ButtonState[ButtonFlags.Special] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskHome);
+
+ // Triggers
+ Inputs.ButtonState[ButtonFlags.L1] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskL);
+ Inputs.ButtonState[ButtonFlags.R1] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskR);
+
+ Inputs.ButtonState[ButtonFlags.L2Soft] = sTATE.lTrigger > TriggerThreshold;
+ Inputs.ButtonState[ButtonFlags.R2Soft] = sTATE.rTrigger > TriggerThreshold;
+
+ Inputs.ButtonState[ButtonFlags.L2Full] = sTATE.lTrigger > TriggerThreshold * 8;
+ Inputs.ButtonState[ButtonFlags.R2Full] = sTATE.rTrigger > TriggerThreshold * 8;
+
+ Inputs.AxisState[AxisFlags.L2] = (short)InputUtils.MapRange(sTATE.lTrigger, 0.0f, 1.0f, byte.MinValue, byte.MaxValue);
+ Inputs.AxisState[AxisFlags.R2] = (short)InputUtils.MapRange(sTATE.rTrigger, 0.0f, 1.0f, byte.MinValue, byte.MaxValue);
+
+ // Left Stick
+ Inputs.ButtonState[ButtonFlags.LeftStickLeft] = sTATE.stickLX < -LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickRight] = sTATE.stickLX > LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickDown] = sTATE.stickLY < -LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickUp] = sTATE.stickLY > LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickClick] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskLClick);
+
+ Inputs.AxisState[AxisFlags.LeftStickX] = (short)InputUtils.MapRange(sTATE.stickLX, -1.0f, 1.0f, short.MinValue, short.MaxValue);
+ Inputs.AxisState[AxisFlags.LeftStickY] = (short)InputUtils.MapRange(sTATE.stickLY, -1.0f, 1.0f, short.MinValue, short.MaxValue);
+
+ // Right Stick
+ Inputs.ButtonState[ButtonFlags.RightStickLeft] = sTATE.stickRX < -LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.RightStickRight] = sTATE.stickRX > LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.RightStickDown] = sTATE.stickRY < -LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.RightStickUp] = sTATE.stickRY > LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.RightStickClick] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskRClick);
+
+ Inputs.AxisState[AxisFlags.RightStickX] = (short)InputUtils.MapRange(sTATE.stickRX, -1.0f, 1.0f, short.MinValue, short.MaxValue);
+ Inputs.AxisState[AxisFlags.RightStickY] = (short)InputUtils.MapRange(sTATE.stickRY, -1.0f, 1.0f, short.MinValue, short.MaxValue);
+
+ // IMU
+ iMU_STATE = JslGetIMUState(UserIndex);
+
+ // store motion
+ Inputs.GyroState.SetGyroscope(iMU_STATE.gyroX, iMU_STATE.gyroY, iMU_STATE.gyroZ);
+ Inputs.GyroState.SetAccelerometer(iMU_STATE.accelX, iMU_STATE.accelY, iMU_STATE.accelZ);
+
+ // process motion
+ gamepadMotion.ProcessMotion(iMU_STATE.gyroX, iMU_STATE.gyroY, iMU_STATE.gyroZ, iMU_STATE.accelX, iMU_STATE.accelY, iMU_STATE.accelZ, delta);
+ }
+
+ public override bool IsConnected()
+ {
+ return JslStillConnected(UserIndex);
+ }
+
+ public override void Plug()
+ {
+ base.Plug();
+ }
+
+ public override void Unplug()
+ {
+ base.Unplug();
+ }
+
+ public override void SetVibration(byte LargeMotor, byte SmallMotor)
+ {
+ JslSetRumble(UserIndex, (byte)(SmallMotor * VibrationStrength), (byte)(LargeMotor * VibrationStrength));
+ }
+
+ public override void CyclePort()
+ {
+ string enumerator = Details.GetEnumerator();
+ switch (enumerator)
+ {
+ default:
+ case "BTHENUM":
+ Task.Run(async () =>
+ {
+ // Details.InstallNullDrivers();
+ // await Task.Delay(1000);
+ // Details.InstallCustomDriver("hidbth.inf");
+
+ Details.Uninstall(false);
+ await Task.Delay(1000);
+ Devcon.Refresh();
+ });
+ break;
+ case "USB":
+ base.CyclePort();
+ break;
+ }
+ }
+
+ public void AttachJoySettings(JOY_SETTINGS settings)
+ {
+ this.sSETTINGS = settings;
+ this.UserIndex = (byte)settings.playerNumber;
+
+ // manage elsewhere
+ JslResetContinuousCalibration(UserIndex);
+ JslPauseContinuousCalibration(UserIndex);
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/NeptuneController.cs b/HandheldCompanion/Controllers/NeptuneController.cs
index 1d31439da..e56dd4ab8 100644
--- a/HandheldCompanion/Controllers/NeptuneController.cs
+++ b/HandheldCompanion/Controllers/NeptuneController.cs
@@ -1,400 +1,400 @@
-using HandheldCompanion.Actions;
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Managers;
-using SharpDX.XInput;
-using steam_hidapi.net;
-using steam_hidapi.net.Hid;
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace HandheldCompanion.Controllers;
-
-public class NeptuneController : SteamController
-{
- private steam_hidapi.net.NeptuneController Controller;
- private NeptuneControllerInputEventArgs input;
-
- private const short TrackPadInner = 21844;
-
- public byte FeedbackLargeMotor;
- public byte FeedbackSmallMotor;
-
- public const sbyte MinIntensity = -2;
- public const sbyte MaxIntensity = 10;
-
- // TODO: why not use TimerManager.Tick?
- private Thread rumbleThread;
- private bool rumbleThreadRunning;
-
- public NeptuneController()
- { }
-
- public NeptuneController(PnPDetails details) : base()
- {
- AttachDetails(details);
-
- // UI
- DrawUI();
- UpdateUI();
- }
-
- protected override void InitializeInputOutput()
- {
- // Additional controller specific source buttons/axes
- SourceButtons.AddRange(new List { ButtonFlags.L4, ButtonFlags.R4, ButtonFlags.L5, ButtonFlags.R5 });
- SourceButtons.AddRange(new List { ButtonFlags.LeftStickTouch, ButtonFlags.RightStickTouch });
- SourceButtons.AddRange(new List { ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp, ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight });
- SourceButtons.AddRange(new List { ButtonFlags.RightPadClick, ButtonFlags.RightPadTouch, ButtonFlags.RightPadClickUp, ButtonFlags.RightPadClickDown, ButtonFlags.RightPadClickLeft, ButtonFlags.RightPadClickRight });
-
- SourceAxis.Add(AxisLayoutFlags.LeftPad);
- SourceAxis.Add(AxisLayoutFlags.RightPad);
- SourceAxis.Add(AxisLayoutFlags.Gyroscope);
-
- TargetButtons.Add(ButtonFlags.LeftPadClick);
- TargetButtons.Add(ButtonFlags.RightPadClick);
- TargetButtons.Add(ButtonFlags.LeftPadTouch);
- TargetButtons.Add(ButtonFlags.RightPadTouch);
-
- TargetAxis.Add(AxisLayoutFlags.LeftPad);
- TargetAxis.Add(AxisLayoutFlags.RightPad);
- }
-
- public override void AttachDetails(PnPDetails details)
- {
- base.AttachDetails(details);
-
- Controller = new(details.VendorID, details.ProductID, details.GetMI());
- UserIndex = 0;
-
- // open controller
- Open();
- }
-
- public override string ToString()
- {
- return "Valve Software Steam Controller";
- }
-
- public override void UpdateInputs(long ticks, float delta)
- {
- if (input is null)
- return;
-
- Inputs.ButtonState = InjectedButtons.Clone() as ButtonState;
-
- Inputs.ButtonState[ButtonFlags.B1] = input.State.ButtonState[NeptuneControllerButton.BtnA];
- Inputs.ButtonState[ButtonFlags.B2] = input.State.ButtonState[NeptuneControllerButton.BtnB];
- Inputs.ButtonState[ButtonFlags.B3] = input.State.ButtonState[NeptuneControllerButton.BtnX];
- Inputs.ButtonState[ButtonFlags.B4] = input.State.ButtonState[NeptuneControllerButton.BtnY];
-
- Inputs.ButtonState[ButtonFlags.DPadUp] = input.State.ButtonState[NeptuneControllerButton.BtnDpadUp];
- Inputs.ButtonState[ButtonFlags.DPadDown] = input.State.ButtonState[NeptuneControllerButton.BtnDpadDown];
- Inputs.ButtonState[ButtonFlags.DPadLeft] = input.State.ButtonState[NeptuneControllerButton.BtnDpadLeft];
- Inputs.ButtonState[ButtonFlags.DPadRight] = input.State.ButtonState[NeptuneControllerButton.BtnDpadRight];
-
- Inputs.ButtonState[ButtonFlags.Start] = input.State.ButtonState[NeptuneControllerButton.BtnOptions];
- Inputs.ButtonState[ButtonFlags.Back] = input.State.ButtonState[NeptuneControllerButton.BtnMenu];
-
- Inputs.ButtonState[ButtonFlags.Special] = input.State.ButtonState[NeptuneControllerButton.BtnSteam];
- Inputs.ButtonState[ButtonFlags.OEM1] = input.State.ButtonState[NeptuneControllerButton.BtnQuickAccess];
-
- var L2 = input.State.AxesState[NeptuneControllerAxis.L2] * byte.MaxValue / short.MaxValue;
- var R2 = input.State.AxesState[NeptuneControllerAxis.R2] * byte.MaxValue / short.MaxValue;
-
- Inputs.ButtonState[ButtonFlags.L2Soft] = L2 > Gamepad.TriggerThreshold;
- Inputs.ButtonState[ButtonFlags.R2Soft] = R2 > Gamepad.TriggerThreshold;
-
- Inputs.ButtonState[ButtonFlags.L2Full] = L2 > Gamepad.TriggerThreshold * 8;
- Inputs.ButtonState[ButtonFlags.R2Full] = R2 > Gamepad.TriggerThreshold * 8;
-
- Inputs.AxisState[AxisFlags.L2] = (short)L2;
- Inputs.AxisState[AxisFlags.R2] = (short)R2;
-
- Inputs.ButtonState[ButtonFlags.LeftStickTouch] =
- input.State.ButtonState[NeptuneControllerButton.BtnLStickTouch];
- Inputs.ButtonState[ButtonFlags.RightStickTouch] =
- input.State.ButtonState[NeptuneControllerButton.BtnRStickTouch];
-
- Inputs.ButtonState[ButtonFlags.LeftStickClick] = input.State.ButtonState[NeptuneControllerButton.BtnLStickPress];
- Inputs.ButtonState[ButtonFlags.RightStickClick] = input.State.ButtonState[NeptuneControllerButton.BtnRStickPress];
-
- Inputs.ButtonState[ButtonFlags.L1] = input.State.ButtonState[NeptuneControllerButton.BtnL1];
- Inputs.ButtonState[ButtonFlags.R1] = input.State.ButtonState[NeptuneControllerButton.BtnR1];
- Inputs.ButtonState[ButtonFlags.L4] = input.State.ButtonState[NeptuneControllerButton.BtnL4];
- Inputs.ButtonState[ButtonFlags.R4] = input.State.ButtonState[NeptuneControllerButton.BtnR4];
- Inputs.ButtonState[ButtonFlags.L5] = input.State.ButtonState[NeptuneControllerButton.BtnL5];
- Inputs.ButtonState[ButtonFlags.R5] = input.State.ButtonState[NeptuneControllerButton.BtnR5];
-
- // Left Stick
- Inputs.ButtonState[ButtonFlags.LeftStickUp] = input.State.ButtonState[NeptuneControllerButton.BtnLStickTouch];
- Inputs.ButtonState[ButtonFlags.LeftStickClick] = input.State.ButtonState[NeptuneControllerButton.BtnLStickPress];
-
- Inputs.AxisState[AxisFlags.LeftStickX] = input.State.AxesState[NeptuneControllerAxis.LeftStickX];
- Inputs.AxisState[AxisFlags.LeftStickY] = input.State.AxesState[NeptuneControllerAxis.LeftStickY];
-
- Inputs.ButtonState[ButtonFlags.LeftStickLeft] =
- input.State.AxesState[NeptuneControllerAxis.LeftStickX] < -Gamepad.LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickRight] =
- input.State.AxesState[NeptuneControllerAxis.LeftStickX] > Gamepad.LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickDown] =
- input.State.AxesState[NeptuneControllerAxis.LeftStickY] < -Gamepad.LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickUp] =
- input.State.AxesState[NeptuneControllerAxis.LeftStickY] > Gamepad.LeftThumbDeadZone;
-
- // TODO: Implement Inner/Outer Ring button mappings for sticks
- // https://github.com/Havner/HandheldCompanion/commit/e1124ceb6c59051201756d5e95b2eb39a3bb24f6
-
- /* float leftLength = new Vector2(Inputs.AxisState[AxisFlags.LeftStickX], Inputs.AxisState[AxisFlags.LeftStickY]).Length();
- Inputs.ButtonState[ButtonFlags.LeftStickOuterRing] = leftLength >= (RingThreshold * short.MaxValue);
- Inputs.ButtonState[ButtonFlags.LeftStickInnerRing] = leftLength >= Gamepad.LeftThumbDeadZone && leftLength < (RingThreshold * short.MaxValue); */
-
- // Right Stick
- Inputs.ButtonState[ButtonFlags.RightStickTouch] = input.State.ButtonState[NeptuneControllerButton.BtnRStickTouch];
- Inputs.ButtonState[ButtonFlags.RightStickClick] = input.State.ButtonState[NeptuneControllerButton.BtnRStickPress];
-
- Inputs.AxisState[AxisFlags.RightStickX] = input.State.AxesState[NeptuneControllerAxis.RightStickX];
- Inputs.AxisState[AxisFlags.RightStickY] = input.State.AxesState[NeptuneControllerAxis.RightStickY];
-
- Inputs.ButtonState[ButtonFlags.RightStickLeft] =
- input.State.AxesState[NeptuneControllerAxis.RightStickX] < -Gamepad.RightThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.RightStickRight] =
- input.State.AxesState[NeptuneControllerAxis.RightStickX] > Gamepad.RightThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.RightStickDown] =
- input.State.AxesState[NeptuneControllerAxis.RightStickY] < -Gamepad.RightThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.RightStickUp] =
- input.State.AxesState[NeptuneControllerAxis.RightStickY] > Gamepad.RightThumbDeadZone;
-
- // TODO: Implement Inner/Outer Ring button mappings for sticks
- // https://github.com/Havner/HandheldCompanion/commit/e1124ceb6c59051201756d5e95b2eb39a3bb24f6
-
- /* float rightLength = new Vector2(Inputs.AxisState[AxisFlags.RightStickX], Inputs.AxisState[AxisFlags.RightStickY]).Length();
- Inputs.ButtonState[ButtonFlags.RightStickOuterRing] = rightLength >= (RingThreshold * short.MaxValue);
- Inputs.ButtonState[ButtonFlags.RightStickInnerRing] = rightLength >= Gamepad.RightThumbDeadZone && rightLength < (RingThreshold * short.MaxValue); */
-
- // Left Pad
- Inputs.ButtonState[ButtonFlags.LeftPadTouch] = input.State.ButtonState[NeptuneControllerButton.BtnLPadTouch];
- if (input.State.ButtonState[NeptuneControllerButton.BtnLPadTouch])
- {
- Inputs.AxisState[AxisFlags.LeftPadX] = input.State.AxesState[NeptuneControllerAxis.LeftPadX];
- Inputs.AxisState[AxisFlags.LeftPadY] = input.State.AxesState[NeptuneControllerAxis.LeftPadY];
- }
- else
- {
- Inputs.AxisState[AxisFlags.LeftPadX] = 0;
- Inputs.AxisState[AxisFlags.LeftPadY] = 0;
- }
-
- Inputs.ButtonState[ButtonFlags.LeftPadClick] = input.State.ButtonState[NeptuneControllerButton.BtnLPadPress];
- if (Inputs.ButtonState[ButtonFlags.LeftPadClick])
- {
- Inputs.ButtonState[ButtonFlags.LeftPadClickUp] = Inputs.AxisState[AxisFlags.LeftPadY] >= TrackPadInner;
- Inputs.ButtonState[ButtonFlags.LeftPadClickDown] = Inputs.AxisState[AxisFlags.LeftPadY] <= -TrackPadInner;
- Inputs.ButtonState[ButtonFlags.LeftPadClickRight] = Inputs.AxisState[AxisFlags.LeftPadX] >= TrackPadInner;
- Inputs.ButtonState[ButtonFlags.LeftPadClickLeft] = Inputs.AxisState[AxisFlags.LeftPadX] <= -TrackPadInner;
- }
- else
- {
- Inputs.ButtonState[ButtonFlags.LeftPadClickUp] = false;
- Inputs.ButtonState[ButtonFlags.LeftPadClickDown] = false;
- Inputs.ButtonState[ButtonFlags.LeftPadClickRight] = false;
- Inputs.ButtonState[ButtonFlags.LeftPadClickLeft] = false;
- }
-
- // Right Pad
- Inputs.ButtonState[ButtonFlags.RightPadTouch] = input.State.ButtonState[NeptuneControllerButton.BtnRPadTouch];
- if (input.State.ButtonState[NeptuneControllerButton.BtnRPadTouch])
- {
- Inputs.AxisState[AxisFlags.RightPadX] = input.State.AxesState[NeptuneControllerAxis.RightPadX];
- Inputs.AxisState[AxisFlags.RightPadY] = input.State.AxesState[NeptuneControllerAxis.RightPadY];
- }
- else
- {
- Inputs.AxisState[AxisFlags.RightPadX] = 0;
- Inputs.AxisState[AxisFlags.RightPadY] = 0;
- }
-
- Inputs.ButtonState[ButtonFlags.RightPadClick] = input.State.ButtonState[NeptuneControllerButton.BtnRPadPress];
- if (Inputs.ButtonState[ButtonFlags.RightPadClick])
- {
- Inputs.ButtonState[ButtonFlags.RightPadClickUp] = Inputs.AxisState[AxisFlags.RightPadY] >= TrackPadInner;
- Inputs.ButtonState[ButtonFlags.RightPadClickDown] = Inputs.AxisState[AxisFlags.RightPadY] <= -TrackPadInner;
- Inputs.ButtonState[ButtonFlags.RightPadClickRight] = Inputs.AxisState[AxisFlags.RightPadX] >= TrackPadInner;
- Inputs.ButtonState[ButtonFlags.RightPadClickLeft] = Inputs.AxisState[AxisFlags.RightPadX] <= -TrackPadInner;
- }
- else
- {
- Inputs.ButtonState[ButtonFlags.RightPadClickUp] = false;
- Inputs.ButtonState[ButtonFlags.RightPadClickDown] = false;
- Inputs.ButtonState[ButtonFlags.RightPadClickRight] = false;
- Inputs.ButtonState[ButtonFlags.RightPadClickLeft] = false;
- }
-
- // TODO: why Z/Y swapped?
- float aX = (float)input.State.AxesState[NeptuneControllerAxis.GyroAccelX] / short.MaxValue * 2.0f;
- float aY = (float)input.State.AxesState[NeptuneControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f;
- float aZ = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelY] / short.MaxValue * 2.0f;
-
- // TODO: why Roll/Pitch swapped?
- float gX = (float)input.State.AxesState[NeptuneControllerAxis.GyroPitch] / short.MaxValue * 2000.0f; // Roll
- float gY = (float)input.State.AxesState[NeptuneControllerAxis.GyroRoll] / short.MaxValue * 2000.0f; // Pitch
- float gZ = -(float)input.State.AxesState[NeptuneControllerAxis.GyroYaw] / short.MaxValue * 2000.0f; // Yaw
-
- // store motion
- Inputs.GyroState.SetGyroscope(gX, gY, gZ);
- Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
-
- // process motion
- gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
-
- base.UpdateInputs(ticks, delta);
- }
-
- private void Open()
- {
- try
- {
- Controller.Open();
- isConnected = true;
-
- // disable lizard state
- Controller.RequestLizardMode(false);
-
- // create handler
- Controller.OnControllerInputReceived += input => Task.Run(() => OnControllerInputReceived(input));
- }
- catch { }
- }
-
- private void Close()
- {
- try
- {
- // disable lizard state
- Controller.RequestLizardMode(true);
-
- // remove handler
- Controller.OnControllerInputReceived = null;
-
- Controller.Close();
- isConnected = false;
- }
- catch { }
- }
-
- public override void Hide(bool powerCycle = true)
- {
- Close();
- base.Hide(powerCycle);
- if (!powerCycle)
- Open();
- }
-
- public override void Unhide(bool powerCycle = true)
- {
- Close();
- base.Unhide(powerCycle);
- if (!powerCycle)
- Open();
- }
-
- private void OnControllerInputReceived(NeptuneControllerInputEventArgs input)
- {
- this.input = input;
- }
-
- public override void Plug()
- {
- try
- {
- // open controller
- Open();
- }
- catch (Exception ex)
- {
- LogManager.LogError("Couldn't initialize GordonController. Exception: {0}", ex.Message);
- return;
- }
-
- // manage rumble thread
- rumbleThreadRunning = true;
- rumbleThread = new Thread(RumbleThreadLoop);
- rumbleThread.IsBackground = true;
- rumbleThread.Start();
-
- TimerManager.Tick += UpdateInputs;
-
- base.Plug();
- }
-
- public override void Unplug()
- {
- try
- {
- // kill rumble thread
- if (rumbleThread is not null)
- {
- rumbleThreadRunning = false;
- // Ensure the thread has finished execution
- if (rumbleThread.IsAlive)
- rumbleThread.Join();
- rumbleThread = null;
- }
-
- // close controller
- Close();
- }
- catch
- {
- return;
- }
-
- TimerManager.Tick -= UpdateInputs;
-
- base.Unplug();
- }
-
- public bool GetHapticIntensity(byte? input, sbyte minIntensity, sbyte maxIntensity, out sbyte output)
- {
- output = default;
- if (input is null || input.Value == 0)
- return false;
-
- var value = minIntensity + (maxIntensity - minIntensity) * input.Value * VibrationStrength / 255;
- output = (sbyte)(value - 5); // convert from dB to values
- return true;
- }
-
- public override void SetVibration(byte LargeMotor, byte SmallMotor)
- {
- this.FeedbackLargeMotor = LargeMotor;
- this.FeedbackSmallMotor = SmallMotor;
- }
-
- private async void RumbleThreadLoop(object? obj)
- {
- while (rumbleThreadRunning)
- {
- if (GetHapticIntensity(FeedbackLargeMotor, MinIntensity, MaxIntensity, out var leftIntensity))
- Controller.SetHaptic2(SCHapticMotor.Left, NCHapticStyle.Weak, leftIntensity);
-
- if (GetHapticIntensity(FeedbackSmallMotor, MinIntensity, MaxIntensity, out var rightIntensity))
- Controller.SetHaptic2(SCHapticMotor.Right, NCHapticStyle.Weak, rightIntensity);
-
- await Task.Delay(TimerManager.GetPeriod() * 2);
- }
- }
-
- public override void SetHaptic(HapticStrength strength, ButtonFlags button)
- {
- ushort value = strength switch
- {
- HapticStrength.Low => 512,
- HapticStrength.Medium => 1024,
- HapticStrength.High => 2048,
- _ => 0,
- };
- Controller.SetHaptic((byte)GetMotorForButton(button), value, 0, 1);
- }
+using HandheldCompanion.Actions;
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
+using SharpDX.XInput;
+using steam_hidapi.net;
+using steam_hidapi.net.Hid;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace HandheldCompanion.Controllers;
+
+public class NeptuneController : SteamController
+{
+ private steam_hidapi.net.NeptuneController Controller;
+ private NeptuneControllerInputEventArgs input;
+
+ private const short TrackPadInner = 21844;
+
+ public byte FeedbackLargeMotor;
+ public byte FeedbackSmallMotor;
+
+ public const sbyte MinIntensity = -2;
+ public const sbyte MaxIntensity = 10;
+
+ // TODO: why not use TimerManager.Tick?
+ private Thread rumbleThread;
+ private bool rumbleThreadRunning;
+
+ public NeptuneController()
+ { }
+
+ public NeptuneController(PnPDetails details) : base()
+ {
+ AttachDetails(details);
+
+ // UI
+ DrawUI();
+ UpdateUI();
+ }
+
+ protected override void InitializeInputOutput()
+ {
+ // Additional controller specific source buttons/axes
+ SourceButtons.AddRange(new List { ButtonFlags.L4, ButtonFlags.R4, ButtonFlags.L5, ButtonFlags.R5 });
+ SourceButtons.AddRange(new List { ButtonFlags.LeftStickTouch, ButtonFlags.RightStickTouch });
+ SourceButtons.AddRange(new List { ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp, ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight });
+ SourceButtons.AddRange(new List { ButtonFlags.RightPadClick, ButtonFlags.RightPadTouch, ButtonFlags.RightPadClickUp, ButtonFlags.RightPadClickDown, ButtonFlags.RightPadClickLeft, ButtonFlags.RightPadClickRight });
+
+ SourceAxis.Add(AxisLayoutFlags.LeftPad);
+ SourceAxis.Add(AxisLayoutFlags.RightPad);
+ SourceAxis.Add(AxisLayoutFlags.Gyroscope);
+
+ TargetButtons.Add(ButtonFlags.LeftPadClick);
+ TargetButtons.Add(ButtonFlags.RightPadClick);
+ TargetButtons.Add(ButtonFlags.LeftPadTouch);
+ TargetButtons.Add(ButtonFlags.RightPadTouch);
+
+ TargetAxis.Add(AxisLayoutFlags.LeftPad);
+ TargetAxis.Add(AxisLayoutFlags.RightPad);
+ }
+
+ public override void AttachDetails(PnPDetails details)
+ {
+ base.AttachDetails(details);
+
+ Controller = new(details.VendorID, details.ProductID, details.GetMI());
+ UserIndex = 0;
+
+ // open controller
+ Open();
+ }
+
+ public override string ToString()
+ {
+ return "Valve Software Steam Controller";
+ }
+
+ public override void UpdateInputs(long ticks, float delta)
+ {
+ if (input is null)
+ return;
+
+ Inputs.ButtonState = InjectedButtons.Clone() as ButtonState;
+
+ Inputs.ButtonState[ButtonFlags.B1] = input.State.ButtonState[NeptuneControllerButton.BtnA];
+ Inputs.ButtonState[ButtonFlags.B2] = input.State.ButtonState[NeptuneControllerButton.BtnB];
+ Inputs.ButtonState[ButtonFlags.B3] = input.State.ButtonState[NeptuneControllerButton.BtnX];
+ Inputs.ButtonState[ButtonFlags.B4] = input.State.ButtonState[NeptuneControllerButton.BtnY];
+
+ Inputs.ButtonState[ButtonFlags.DPadUp] = input.State.ButtonState[NeptuneControllerButton.BtnDpadUp];
+ Inputs.ButtonState[ButtonFlags.DPadDown] = input.State.ButtonState[NeptuneControllerButton.BtnDpadDown];
+ Inputs.ButtonState[ButtonFlags.DPadLeft] = input.State.ButtonState[NeptuneControllerButton.BtnDpadLeft];
+ Inputs.ButtonState[ButtonFlags.DPadRight] = input.State.ButtonState[NeptuneControllerButton.BtnDpadRight];
+
+ Inputs.ButtonState[ButtonFlags.Start] = input.State.ButtonState[NeptuneControllerButton.BtnOptions];
+ Inputs.ButtonState[ButtonFlags.Back] = input.State.ButtonState[NeptuneControllerButton.BtnMenu];
+
+ Inputs.ButtonState[ButtonFlags.Special] = input.State.ButtonState[NeptuneControllerButton.BtnSteam];
+ Inputs.ButtonState[ButtonFlags.OEM1] = input.State.ButtonState[NeptuneControllerButton.BtnQuickAccess];
+
+ var L2 = input.State.AxesState[NeptuneControllerAxis.L2] * byte.MaxValue / short.MaxValue;
+ var R2 = input.State.AxesState[NeptuneControllerAxis.R2] * byte.MaxValue / short.MaxValue;
+
+ Inputs.ButtonState[ButtonFlags.L2Soft] = L2 > Gamepad.TriggerThreshold;
+ Inputs.ButtonState[ButtonFlags.R2Soft] = R2 > Gamepad.TriggerThreshold;
+
+ Inputs.ButtonState[ButtonFlags.L2Full] = L2 > Gamepad.TriggerThreshold * 8;
+ Inputs.ButtonState[ButtonFlags.R2Full] = R2 > Gamepad.TriggerThreshold * 8;
+
+ Inputs.AxisState[AxisFlags.L2] = (short)L2;
+ Inputs.AxisState[AxisFlags.R2] = (short)R2;
+
+ Inputs.ButtonState[ButtonFlags.LeftStickTouch] =
+ input.State.ButtonState[NeptuneControllerButton.BtnLStickTouch];
+ Inputs.ButtonState[ButtonFlags.RightStickTouch] =
+ input.State.ButtonState[NeptuneControllerButton.BtnRStickTouch];
+
+ Inputs.ButtonState[ButtonFlags.LeftStickClick] = input.State.ButtonState[NeptuneControllerButton.BtnLStickPress];
+ Inputs.ButtonState[ButtonFlags.RightStickClick] = input.State.ButtonState[NeptuneControllerButton.BtnRStickPress];
+
+ Inputs.ButtonState[ButtonFlags.L1] = input.State.ButtonState[NeptuneControllerButton.BtnL1];
+ Inputs.ButtonState[ButtonFlags.R1] = input.State.ButtonState[NeptuneControllerButton.BtnR1];
+ Inputs.ButtonState[ButtonFlags.L4] = input.State.ButtonState[NeptuneControllerButton.BtnL4];
+ Inputs.ButtonState[ButtonFlags.R4] = input.State.ButtonState[NeptuneControllerButton.BtnR4];
+ Inputs.ButtonState[ButtonFlags.L5] = input.State.ButtonState[NeptuneControllerButton.BtnL5];
+ Inputs.ButtonState[ButtonFlags.R5] = input.State.ButtonState[NeptuneControllerButton.BtnR5];
+
+ // Left Stick
+ Inputs.ButtonState[ButtonFlags.LeftStickUp] = input.State.ButtonState[NeptuneControllerButton.BtnLStickTouch];
+ Inputs.ButtonState[ButtonFlags.LeftStickClick] = input.State.ButtonState[NeptuneControllerButton.BtnLStickPress];
+
+ Inputs.AxisState[AxisFlags.LeftStickX] = input.State.AxesState[NeptuneControllerAxis.LeftStickX];
+ Inputs.AxisState[AxisFlags.LeftStickY] = input.State.AxesState[NeptuneControllerAxis.LeftStickY];
+
+ Inputs.ButtonState[ButtonFlags.LeftStickLeft] =
+ input.State.AxesState[NeptuneControllerAxis.LeftStickX] < -Gamepad.LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickRight] =
+ input.State.AxesState[NeptuneControllerAxis.LeftStickX] > Gamepad.LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickDown] =
+ input.State.AxesState[NeptuneControllerAxis.LeftStickY] < -Gamepad.LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickUp] =
+ input.State.AxesState[NeptuneControllerAxis.LeftStickY] > Gamepad.LeftThumbDeadZone;
+
+ // TODO: Implement Inner/Outer Ring button mappings for sticks
+ // https://github.com/Havner/HandheldCompanion/commit/e1124ceb6c59051201756d5e95b2eb39a3bb24f6
+
+ /* float leftLength = new Vector2(Inputs.AxisState[AxisFlags.LeftStickX], Inputs.AxisState[AxisFlags.LeftStickY]).Length();
+ Inputs.ButtonState[ButtonFlags.LeftStickOuterRing] = leftLength >= (RingThreshold * short.MaxValue);
+ Inputs.ButtonState[ButtonFlags.LeftStickInnerRing] = leftLength >= Gamepad.LeftThumbDeadZone && leftLength < (RingThreshold * short.MaxValue); */
+
+ // Right Stick
+ Inputs.ButtonState[ButtonFlags.RightStickTouch] = input.State.ButtonState[NeptuneControllerButton.BtnRStickTouch];
+ Inputs.ButtonState[ButtonFlags.RightStickClick] = input.State.ButtonState[NeptuneControllerButton.BtnRStickPress];
+
+ Inputs.AxisState[AxisFlags.RightStickX] = input.State.AxesState[NeptuneControllerAxis.RightStickX];
+ Inputs.AxisState[AxisFlags.RightStickY] = input.State.AxesState[NeptuneControllerAxis.RightStickY];
+
+ Inputs.ButtonState[ButtonFlags.RightStickLeft] =
+ input.State.AxesState[NeptuneControllerAxis.RightStickX] < -Gamepad.RightThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.RightStickRight] =
+ input.State.AxesState[NeptuneControllerAxis.RightStickX] > Gamepad.RightThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.RightStickDown] =
+ input.State.AxesState[NeptuneControllerAxis.RightStickY] < -Gamepad.RightThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.RightStickUp] =
+ input.State.AxesState[NeptuneControllerAxis.RightStickY] > Gamepad.RightThumbDeadZone;
+
+ // TODO: Implement Inner/Outer Ring button mappings for sticks
+ // https://github.com/Havner/HandheldCompanion/commit/e1124ceb6c59051201756d5e95b2eb39a3bb24f6
+
+ /* float rightLength = new Vector2(Inputs.AxisState[AxisFlags.RightStickX], Inputs.AxisState[AxisFlags.RightStickY]).Length();
+ Inputs.ButtonState[ButtonFlags.RightStickOuterRing] = rightLength >= (RingThreshold * short.MaxValue);
+ Inputs.ButtonState[ButtonFlags.RightStickInnerRing] = rightLength >= Gamepad.RightThumbDeadZone && rightLength < (RingThreshold * short.MaxValue); */
+
+ // Left Pad
+ Inputs.ButtonState[ButtonFlags.LeftPadTouch] = input.State.ButtonState[NeptuneControllerButton.BtnLPadTouch];
+ if (input.State.ButtonState[NeptuneControllerButton.BtnLPadTouch])
+ {
+ Inputs.AxisState[AxisFlags.LeftPadX] = input.State.AxesState[NeptuneControllerAxis.LeftPadX];
+ Inputs.AxisState[AxisFlags.LeftPadY] = input.State.AxesState[NeptuneControllerAxis.LeftPadY];
+ }
+ else
+ {
+ Inputs.AxisState[AxisFlags.LeftPadX] = 0;
+ Inputs.AxisState[AxisFlags.LeftPadY] = 0;
+ }
+
+ Inputs.ButtonState[ButtonFlags.LeftPadClick] = input.State.ButtonState[NeptuneControllerButton.BtnLPadPress];
+ if (Inputs.ButtonState[ButtonFlags.LeftPadClick])
+ {
+ Inputs.ButtonState[ButtonFlags.LeftPadClickUp] = Inputs.AxisState[AxisFlags.LeftPadY] >= TrackPadInner;
+ Inputs.ButtonState[ButtonFlags.LeftPadClickDown] = Inputs.AxisState[AxisFlags.LeftPadY] <= -TrackPadInner;
+ Inputs.ButtonState[ButtonFlags.LeftPadClickRight] = Inputs.AxisState[AxisFlags.LeftPadX] >= TrackPadInner;
+ Inputs.ButtonState[ButtonFlags.LeftPadClickLeft] = Inputs.AxisState[AxisFlags.LeftPadX] <= -TrackPadInner;
+ }
+ else
+ {
+ Inputs.ButtonState[ButtonFlags.LeftPadClickUp] = false;
+ Inputs.ButtonState[ButtonFlags.LeftPadClickDown] = false;
+ Inputs.ButtonState[ButtonFlags.LeftPadClickRight] = false;
+ Inputs.ButtonState[ButtonFlags.LeftPadClickLeft] = false;
+ }
+
+ // Right Pad
+ Inputs.ButtonState[ButtonFlags.RightPadTouch] = input.State.ButtonState[NeptuneControllerButton.BtnRPadTouch];
+ if (input.State.ButtonState[NeptuneControllerButton.BtnRPadTouch])
+ {
+ Inputs.AxisState[AxisFlags.RightPadX] = input.State.AxesState[NeptuneControllerAxis.RightPadX];
+ Inputs.AxisState[AxisFlags.RightPadY] = input.State.AxesState[NeptuneControllerAxis.RightPadY];
+ }
+ else
+ {
+ Inputs.AxisState[AxisFlags.RightPadX] = 0;
+ Inputs.AxisState[AxisFlags.RightPadY] = 0;
+ }
+
+ Inputs.ButtonState[ButtonFlags.RightPadClick] = input.State.ButtonState[NeptuneControllerButton.BtnRPadPress];
+ if (Inputs.ButtonState[ButtonFlags.RightPadClick])
+ {
+ Inputs.ButtonState[ButtonFlags.RightPadClickUp] = Inputs.AxisState[AxisFlags.RightPadY] >= TrackPadInner;
+ Inputs.ButtonState[ButtonFlags.RightPadClickDown] = Inputs.AxisState[AxisFlags.RightPadY] <= -TrackPadInner;
+ Inputs.ButtonState[ButtonFlags.RightPadClickRight] = Inputs.AxisState[AxisFlags.RightPadX] >= TrackPadInner;
+ Inputs.ButtonState[ButtonFlags.RightPadClickLeft] = Inputs.AxisState[AxisFlags.RightPadX] <= -TrackPadInner;
+ }
+ else
+ {
+ Inputs.ButtonState[ButtonFlags.RightPadClickUp] = false;
+ Inputs.ButtonState[ButtonFlags.RightPadClickDown] = false;
+ Inputs.ButtonState[ButtonFlags.RightPadClickRight] = false;
+ Inputs.ButtonState[ButtonFlags.RightPadClickLeft] = false;
+ }
+
+ // TODO: why Z/Y swapped?
+ float aX = (float)input.State.AxesState[NeptuneControllerAxis.GyroAccelX] / short.MaxValue * 2.0f;
+ float aY = (float)input.State.AxesState[NeptuneControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f;
+ float aZ = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelY] / short.MaxValue * 2.0f;
+
+ // TODO: why Roll/Pitch swapped?
+ float gX = (float)input.State.AxesState[NeptuneControllerAxis.GyroPitch] / short.MaxValue * 2000.0f; // Roll
+ float gY = (float)input.State.AxesState[NeptuneControllerAxis.GyroRoll] / short.MaxValue * 2000.0f; // Pitch
+ float gZ = -(float)input.State.AxesState[NeptuneControllerAxis.GyroYaw] / short.MaxValue * 2000.0f; // Yaw
+
+ // store motion
+ Inputs.GyroState.SetGyroscope(gX, gY, gZ);
+ Inputs.GyroState.SetAccelerometer(aX, aY, aZ);
+
+ // process motion
+ gamepadMotion.ProcessMotion(gX, gY, gZ, aX, aY, aZ, delta);
+
+ base.UpdateInputs(ticks, delta);
+ }
+
+ private void Open()
+ {
+ try
+ {
+ Controller.Open();
+ isConnected = true;
+
+ // disable lizard state
+ Controller.RequestLizardMode(false);
+
+ // create handler
+ Controller.OnControllerInputReceived += input => Task.Run(() => OnControllerInputReceived(input));
+ }
+ catch { }
+ }
+
+ private void Close()
+ {
+ try
+ {
+ // disable lizard state
+ Controller.RequestLizardMode(true);
+
+ // remove handler
+ Controller.OnControllerInputReceived = null;
+
+ Controller.Close();
+ isConnected = false;
+ }
+ catch { }
+ }
+
+ public override void Hide(bool powerCycle = true)
+ {
+ Close();
+ base.Hide(powerCycle);
+ if (!powerCycle)
+ Open();
+ }
+
+ public override void Unhide(bool powerCycle = true)
+ {
+ Close();
+ base.Unhide(powerCycle);
+ if (!powerCycle)
+ Open();
+ }
+
+ private void OnControllerInputReceived(NeptuneControllerInputEventArgs input)
+ {
+ this.input = input;
+ }
+
+ public override void Plug()
+ {
+ try
+ {
+ // open controller
+ Open();
+ }
+ catch (Exception ex)
+ {
+ LogManager.LogError("Couldn't initialize GordonController. Exception: {0}", ex.Message);
+ return;
+ }
+
+ // manage rumble thread
+ rumbleThreadRunning = true;
+ rumbleThread = new Thread(RumbleThreadLoop);
+ rumbleThread.IsBackground = true;
+ rumbleThread.Start();
+
+ TimerManager.Tick += UpdateInputs;
+
+ base.Plug();
+ }
+
+ public override void Unplug()
+ {
+ try
+ {
+ // kill rumble thread
+ if (rumbleThread is not null)
+ {
+ rumbleThreadRunning = false;
+ // Ensure the thread has finished execution
+ if (rumbleThread.IsAlive)
+ rumbleThread.Join();
+ rumbleThread = null;
+ }
+
+ // close controller
+ Close();
+ }
+ catch
+ {
+ return;
+ }
+
+ TimerManager.Tick -= UpdateInputs;
+
+ base.Unplug();
+ }
+
+ public bool GetHapticIntensity(byte? input, sbyte minIntensity, sbyte maxIntensity, out sbyte output)
+ {
+ output = default;
+ if (input is null || input.Value == 0)
+ return false;
+
+ var value = minIntensity + (maxIntensity - minIntensity) * input.Value * VibrationStrength / 255;
+ output = (sbyte)(value - 5); // convert from dB to values
+ return true;
+ }
+
+ public override void SetVibration(byte LargeMotor, byte SmallMotor)
+ {
+ this.FeedbackLargeMotor = LargeMotor;
+ this.FeedbackSmallMotor = SmallMotor;
+ }
+
+ private async void RumbleThreadLoop(object? obj)
+ {
+ while (rumbleThreadRunning)
+ {
+ if (GetHapticIntensity(FeedbackLargeMotor, MinIntensity, MaxIntensity, out var leftIntensity))
+ Controller.SetHaptic2(SCHapticMotor.Left, NCHapticStyle.Weak, leftIntensity);
+
+ if (GetHapticIntensity(FeedbackSmallMotor, MinIntensity, MaxIntensity, out var rightIntensity))
+ Controller.SetHaptic2(SCHapticMotor.Right, NCHapticStyle.Weak, rightIntensity);
+
+ await Task.Delay(TimerManager.GetPeriod() * 2);
+ }
+ }
+
+ public override void SetHaptic(HapticStrength strength, ButtonFlags button)
+ {
+ ushort value = strength switch
+ {
+ HapticStrength.Low => 512,
+ HapticStrength.Medium => 1024,
+ HapticStrength.High => 2048,
+ _ => 0,
+ };
+ Controller.SetHaptic((byte)GetMotorForButton(button), value, 0, 1);
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/ProController.cs b/HandheldCompanion/Controllers/ProController.cs
index 4838655e9..43a990032 100644
--- a/HandheldCompanion/Controllers/ProController.cs
+++ b/HandheldCompanion/Controllers/ProController.cs
@@ -1,120 +1,120 @@
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Managers;
-using HandheldCompanion.Utils;
-using static JSL;
-
-namespace HandheldCompanion.Controllers;
-
-public class ProController : JSController
-{
- public ProController() : base()
- { }
-
- public ProController(JOY_SETTINGS settings, PnPDetails details) : base(settings, details)
- { }
-
- protected override void InitializeInputOutput()
- {
- // Additional controller specific source buttons
- SourceButtons.Add(ButtonFlags.Special2);
- SourceAxis.Add(AxisLayoutFlags.Gyroscope);
- }
-
- public override void UpdateInputs(long ticks, float delta)
- {
- // skip if controller isn't connected
- if (!IsConnected())
- return;
-
- base.UpdateState(delta);
-
- Inputs.ButtonState[ButtonFlags.Special2] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskCapture);
-
- base.UpdateInputs(ticks, delta);
- }
-
- public override string ToString()
- {
- return "Nintendo Pro Controller";
- }
-
- public override void Plug()
- {
- TimerManager.Tick += UpdateInputs;
- base.Plug();
- }
-
- public override void Unplug()
- {
- TimerManager.Tick -= UpdateInputs;
- base.Unplug();
- }
-
- public override void SetVibration(byte LargeMotor, byte SmallMotor)
- {
- // HD rumble isn't yet supported
- }
-
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.B1:
- return "\u21D2"; // B
- case ButtonFlags.B2:
- return "\u21D3"; // A
- case ButtonFlags.B3:
- return "\u21D1"; // Y
- case ButtonFlags.B4:
- return "\u21D0"; // X
- case ButtonFlags.L1:
- return "\u219C";
- case ButtonFlags.R1:
- return "\u219D";
- case ButtonFlags.Back:
- return "\u21FD";
- case ButtonFlags.Start:
- return "\u21FE";
- case ButtonFlags.L2Soft:
- return "\u219A";
- case ButtonFlags.L2Full:
- return "\u219A";
- case ButtonFlags.R2Soft:
- return "\u219B";
- case ButtonFlags.R2Full:
- return "\u219B";
- case ButtonFlags.Special:
- return "\u21F9";
- case ButtonFlags.Special2:
- return "\u21FA";
- }
-
- return base.GetGlyph(button);
- }
-
- public override string GetGlyph(AxisFlags axis)
- {
- switch (axis)
- {
- case AxisFlags.L2:
- return "\u219A";
- case AxisFlags.R2:
- return "\u219B";
- }
-
- return base.GetGlyph(axis);
- }
-
- public override string GetGlyph(AxisLayoutFlags axis)
- {
- switch (axis)
- {
- case AxisLayoutFlags.L2:
- return "\u219A";
- case AxisLayoutFlags.R2:
- return "\u219B";
- }
-
- return base.GetGlyph(axis);
- }
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
+using HandheldCompanion.Utils;
+using static JSL;
+
+namespace HandheldCompanion.Controllers;
+
+public class ProController : JSController
+{
+ public ProController() : base()
+ { }
+
+ public ProController(JOY_SETTINGS settings, PnPDetails details) : base(settings, details)
+ { }
+
+ protected override void InitializeInputOutput()
+ {
+ // Additional controller specific source buttons
+ SourceButtons.Add(ButtonFlags.Special2);
+ SourceAxis.Add(AxisLayoutFlags.Gyroscope);
+ }
+
+ public override void UpdateInputs(long ticks, float delta)
+ {
+ // skip if controller isn't connected
+ if (!IsConnected())
+ return;
+
+ base.UpdateState(delta);
+
+ Inputs.ButtonState[ButtonFlags.Special2] = BitwiseUtils.HasByteSet(sTATE.buttons, ButtonMaskCapture);
+
+ base.UpdateInputs(ticks, delta);
+ }
+
+ public override string ToString()
+ {
+ return "Nintendo Pro Controller";
+ }
+
+ public override void Plug()
+ {
+ TimerManager.Tick += UpdateInputs;
+ base.Plug();
+ }
+
+ public override void Unplug()
+ {
+ TimerManager.Tick -= UpdateInputs;
+ base.Unplug();
+ }
+
+ public override void SetVibration(byte LargeMotor, byte SmallMotor)
+ {
+ // HD rumble isn't yet supported
+ }
+
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.B1:
+ return "\u21D2"; // B
+ case ButtonFlags.B2:
+ return "\u21D3"; // A
+ case ButtonFlags.B3:
+ return "\u21D1"; // Y
+ case ButtonFlags.B4:
+ return "\u21D0"; // X
+ case ButtonFlags.L1:
+ return "\u219C";
+ case ButtonFlags.R1:
+ return "\u219D";
+ case ButtonFlags.Back:
+ return "\u21FD";
+ case ButtonFlags.Start:
+ return "\u21FE";
+ case ButtonFlags.L2Soft:
+ return "\u219A";
+ case ButtonFlags.L2Full:
+ return "\u219A";
+ case ButtonFlags.R2Soft:
+ return "\u219B";
+ case ButtonFlags.R2Full:
+ return "\u219B";
+ case ButtonFlags.Special:
+ return "\u21F9";
+ case ButtonFlags.Special2:
+ return "\u21FA";
+ }
+
+ return base.GetGlyph(button);
+ }
+
+ public override string GetGlyph(AxisFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisFlags.L2:
+ return "\u219A";
+ case AxisFlags.R2:
+ return "\u219B";
+ }
+
+ return base.GetGlyph(axis);
+ }
+
+ public override string GetGlyph(AxisLayoutFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisLayoutFlags.L2:
+ return "\u219A";
+ case AxisLayoutFlags.R2:
+ return "\u219B";
+ }
+
+ return base.GetGlyph(axis);
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/SteamController.cs b/HandheldCompanion/Controllers/SteamController.cs
index a79d773ff..a50f0f0e4 100644
--- a/HandheldCompanion/Controllers/SteamController.cs
+++ b/HandheldCompanion/Controllers/SteamController.cs
@@ -1,228 +1,228 @@
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Managers;
-using steam_hidapi.net.Hid;
-using System;
-
-namespace HandheldCompanion.Controllers
-{
- public abstract class SteamController : IController
- {
- protected bool isConnected = false;
- protected bool isVirtualMuted = false;
-
- public SteamController() : base()
- {
- Capabilities |= ControllerCapabilities.MotionSensor;
-
- SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
- UpdateSettings();
- }
-
- public override void AttachDetails(PnPDetails details)
- {
- base.AttachDetails(details);
- }
-
- protected override void UpdateSettings()
- {
- SetVirtualMuted(SettingsManager.GetBoolean("SteamControllerMute"));
- }
-
- private void SettingsManager_SettingValueChanged(string name, object value)
- {
- switch (name)
- {
- case "SteamControllerMute":
- SetVirtualMuted(Convert.ToBoolean(value));
- break;
- }
- }
-
- public override bool IsConnected()
- {
- return isConnected;
- }
-
- public virtual bool IsVirtualMuted()
- {
- return isVirtualMuted;
- }
-
- public virtual void SetVirtualMuted(bool mute)
- {
- isVirtualMuted = mute;
- }
-
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.B1:
- return "\u21D3"; // Button A
- case ButtonFlags.B2:
- return "\u21D2"; // Button B
- case ButtonFlags.B3:
- return "\u21D0"; // Button X
- case ButtonFlags.B4:
- return "\u21D1"; // Button Y
- case ButtonFlags.L1:
- return "\u21B0";
- case ButtonFlags.R1:
- return "\u21B1";
- case ButtonFlags.Back:
- return "\u21FA";
- case ButtonFlags.Start:
- return "\u21FB";
- case ButtonFlags.L2Soft:
- case ButtonFlags.L2Full:
- return "\u21B2";
- case ButtonFlags.R2Soft:
- case ButtonFlags.R2Full:
- return "\u21B3";
- case ButtonFlags.L4:
- return "\u2276";
- case ButtonFlags.L5:
- return "\u2278";
- case ButtonFlags.R4:
- return "\u2277";
- case ButtonFlags.R5:
- return "\u2279";
- case ButtonFlags.Special:
- return "\u21E4";
- case ButtonFlags.OEM1:
- return "\u21E5";
- case ButtonFlags.LeftStickTouch:
- return "\u21DA";
- case ButtonFlags.RightStickTouch:
- return "\u21DB";
- case ButtonFlags.LeftPadTouch:
- return "\u2268";
- case ButtonFlags.RightPadTouch:
- return "\u2269";
- case ButtonFlags.LeftPadClick:
- return "\u2266";
- case ButtonFlags.RightPadClick:
- return "\u2267";
- case ButtonFlags.LeftPadClickUp:
- return "\u2270";
- case ButtonFlags.LeftPadClickDown:
- return "\u2274";
- case ButtonFlags.LeftPadClickLeft:
- return "\u226E";
- case ButtonFlags.LeftPadClickRight:
- return "\u2272";
- case ButtonFlags.RightPadClickUp:
- return "\u2271";
- case ButtonFlags.RightPadClickDown:
- return "\u2275";
- case ButtonFlags.RightPadClickLeft:
- return "\u226F";
- case ButtonFlags.RightPadClickRight:
- return "\u2273";
- }
-
- return base.GetGlyph(button);
- }
-
- public override string GetGlyph(AxisFlags axis)
- {
- switch (axis)
- {
- case AxisFlags.L2:
- return "\u2196";
- case AxisFlags.R2:
- return "\u2197";
- }
-
- return base.GetGlyph(axis);
- }
-
- public override string GetGlyph(AxisLayoutFlags axis)
- {
- switch (axis)
- {
- case AxisLayoutFlags.L2:
- return "\u2196";
- case AxisLayoutFlags.R2:
- return "\u2197";
- case AxisLayoutFlags.LeftPad:
- return "\u2264";
- case AxisLayoutFlags.RightPad:
- return "\u2265";
- case AxisLayoutFlags.Gyroscope:
- return "\u2B94";
- }
-
- return base.GetGlyph(axis);
- }
-
- protected virtual SCHapticMotor GetMotorForButton(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.DPadUp:
- case ButtonFlags.DPadDown:
- case ButtonFlags.DPadLeft:
- case ButtonFlags.DPadRight:
- case ButtonFlags.L1:
- case ButtonFlags.L2Soft:
- case ButtonFlags.L2Full:
- case ButtonFlags.L4:
- case ButtonFlags.L5:
-
- case ButtonFlags.Back:
- case ButtonFlags.Special:
-
- case ButtonFlags.LeftStickTouch:
- case ButtonFlags.LeftStickClick:
- case ButtonFlags.LeftStickUp:
- case ButtonFlags.LeftStickDown:
- case ButtonFlags.LeftStickLeft:
- case ButtonFlags.LeftStickRight:
-
- case ButtonFlags.LeftPadTouch:
- case ButtonFlags.LeftPadClick:
- case ButtonFlags.LeftPadClickUp:
- case ButtonFlags.LeftPadClickDown:
- case ButtonFlags.LeftPadClickLeft:
- case ButtonFlags.LeftPadClickRight:
-
- return SCHapticMotor.Left;
-
- case ButtonFlags.B1:
- case ButtonFlags.B2:
- case ButtonFlags.B3:
- case ButtonFlags.B4:
-
- case ButtonFlags.R1:
- case ButtonFlags.R2Soft:
- case ButtonFlags.R2Full:
- case ButtonFlags.R4:
- case ButtonFlags.R5:
-
- case ButtonFlags.Start:
- case ButtonFlags.OEM1: // STEAM
-
- case ButtonFlags.RightStickTouch:
- case ButtonFlags.RightStickClick:
- case ButtonFlags.RightStickUp:
- case ButtonFlags.RightStickDown:
- case ButtonFlags.RightStickLeft:
- case ButtonFlags.RightStickRight:
-
- case ButtonFlags.RightPadTouch:
- case ButtonFlags.RightPadClick:
- case ButtonFlags.RightPadClickUp:
- case ButtonFlags.RightPadClickDown:
- case ButtonFlags.RightPadClickLeft:
- case ButtonFlags.RightPadClickRight:
-
- return SCHapticMotor.Right;
-
- default:
- throw new System.Exception(string.Format("Button 2 Rumble button missing: {0}", button.ToString()));
-
- }
- }
- }
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
+using steam_hidapi.net.Hid;
+using System;
+
+namespace HandheldCompanion.Controllers
+{
+ public abstract class SteamController : IController
+ {
+ protected bool isConnected = false;
+ protected bool isVirtualMuted = false;
+
+ public SteamController() : base()
+ {
+ Capabilities |= ControllerCapabilities.MotionSensor;
+
+ SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged;
+ UpdateSettings();
+ }
+
+ public override void AttachDetails(PnPDetails details)
+ {
+ base.AttachDetails(details);
+ }
+
+ protected override void UpdateSettings()
+ {
+ SetVirtualMuted(SettingsManager.GetBoolean("SteamControllerMute"));
+ }
+
+ private void SettingsManager_SettingValueChanged(string name, object value)
+ {
+ switch (name)
+ {
+ case "SteamControllerMute":
+ SetVirtualMuted(Convert.ToBoolean(value));
+ break;
+ }
+ }
+
+ public override bool IsConnected()
+ {
+ return isConnected;
+ }
+
+ public virtual bool IsVirtualMuted()
+ {
+ return isVirtualMuted;
+ }
+
+ public virtual void SetVirtualMuted(bool mute)
+ {
+ isVirtualMuted = mute;
+ }
+
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.B1:
+ return "\u21D3"; // Button A
+ case ButtonFlags.B2:
+ return "\u21D2"; // Button B
+ case ButtonFlags.B3:
+ return "\u21D0"; // Button X
+ case ButtonFlags.B4:
+ return "\u21D1"; // Button Y
+ case ButtonFlags.L1:
+ return "\u21B0";
+ case ButtonFlags.R1:
+ return "\u21B1";
+ case ButtonFlags.Back:
+ return "\u21FA";
+ case ButtonFlags.Start:
+ return "\u21FB";
+ case ButtonFlags.L2Soft:
+ case ButtonFlags.L2Full:
+ return "\u21B2";
+ case ButtonFlags.R2Soft:
+ case ButtonFlags.R2Full:
+ return "\u21B3";
+ case ButtonFlags.L4:
+ return "\u2276";
+ case ButtonFlags.L5:
+ return "\u2278";
+ case ButtonFlags.R4:
+ return "\u2277";
+ case ButtonFlags.R5:
+ return "\u2279";
+ case ButtonFlags.Special:
+ return "\u21E4";
+ case ButtonFlags.OEM1:
+ return "\u21E5";
+ case ButtonFlags.LeftStickTouch:
+ return "\u21DA";
+ case ButtonFlags.RightStickTouch:
+ return "\u21DB";
+ case ButtonFlags.LeftPadTouch:
+ return "\u2268";
+ case ButtonFlags.RightPadTouch:
+ return "\u2269";
+ case ButtonFlags.LeftPadClick:
+ return "\u2266";
+ case ButtonFlags.RightPadClick:
+ return "\u2267";
+ case ButtonFlags.LeftPadClickUp:
+ return "\u2270";
+ case ButtonFlags.LeftPadClickDown:
+ return "\u2274";
+ case ButtonFlags.LeftPadClickLeft:
+ return "\u226E";
+ case ButtonFlags.LeftPadClickRight:
+ return "\u2272";
+ case ButtonFlags.RightPadClickUp:
+ return "\u2271";
+ case ButtonFlags.RightPadClickDown:
+ return "\u2275";
+ case ButtonFlags.RightPadClickLeft:
+ return "\u226F";
+ case ButtonFlags.RightPadClickRight:
+ return "\u2273";
+ }
+
+ return base.GetGlyph(button);
+ }
+
+ public override string GetGlyph(AxisFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisFlags.L2:
+ return "\u2196";
+ case AxisFlags.R2:
+ return "\u2197";
+ }
+
+ return base.GetGlyph(axis);
+ }
+
+ public override string GetGlyph(AxisLayoutFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisLayoutFlags.L2:
+ return "\u2196";
+ case AxisLayoutFlags.R2:
+ return "\u2197";
+ case AxisLayoutFlags.LeftPad:
+ return "\u2264";
+ case AxisLayoutFlags.RightPad:
+ return "\u2265";
+ case AxisLayoutFlags.Gyroscope:
+ return "\u2B94";
+ }
+
+ return base.GetGlyph(axis);
+ }
+
+ protected virtual SCHapticMotor GetMotorForButton(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.DPadUp:
+ case ButtonFlags.DPadDown:
+ case ButtonFlags.DPadLeft:
+ case ButtonFlags.DPadRight:
+ case ButtonFlags.L1:
+ case ButtonFlags.L2Soft:
+ case ButtonFlags.L2Full:
+ case ButtonFlags.L4:
+ case ButtonFlags.L5:
+
+ case ButtonFlags.Back:
+ case ButtonFlags.Special:
+
+ case ButtonFlags.LeftStickTouch:
+ case ButtonFlags.LeftStickClick:
+ case ButtonFlags.LeftStickUp:
+ case ButtonFlags.LeftStickDown:
+ case ButtonFlags.LeftStickLeft:
+ case ButtonFlags.LeftStickRight:
+
+ case ButtonFlags.LeftPadTouch:
+ case ButtonFlags.LeftPadClick:
+ case ButtonFlags.LeftPadClickUp:
+ case ButtonFlags.LeftPadClickDown:
+ case ButtonFlags.LeftPadClickLeft:
+ case ButtonFlags.LeftPadClickRight:
+
+ return SCHapticMotor.Left;
+
+ case ButtonFlags.B1:
+ case ButtonFlags.B2:
+ case ButtonFlags.B3:
+ case ButtonFlags.B4:
+
+ case ButtonFlags.R1:
+ case ButtonFlags.R2Soft:
+ case ButtonFlags.R2Full:
+ case ButtonFlags.R4:
+ case ButtonFlags.R5:
+
+ case ButtonFlags.Start:
+ case ButtonFlags.OEM1: // STEAM
+
+ case ButtonFlags.RightStickTouch:
+ case ButtonFlags.RightStickClick:
+ case ButtonFlags.RightStickUp:
+ case ButtonFlags.RightStickDown:
+ case ButtonFlags.RightStickLeft:
+ case ButtonFlags.RightStickRight:
+
+ case ButtonFlags.RightPadTouch:
+ case ButtonFlags.RightPadClick:
+ case ButtonFlags.RightPadClickUp:
+ case ButtonFlags.RightPadClickDown:
+ case ButtonFlags.RightPadClickLeft:
+ case ButtonFlags.RightPadClickRight:
+
+ return SCHapticMotor.Right;
+
+ default:
+ throw new System.Exception(string.Format("Button 2 Rumble button missing: {0}", button.ToString()));
+
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controllers/XInputController.cs b/HandheldCompanion/Controllers/XInputController.cs
index 3d95f3183..d22c0452d 100644
--- a/HandheldCompanion/Controllers/XInputController.cs
+++ b/HandheldCompanion/Controllers/XInputController.cs
@@ -1,445 +1,445 @@
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Managers;
-using SharpDX.XInput;
-using System;
-using System.Linq;
-using System.Runtime.InteropServices;
-using System.Threading.Tasks;
-using System.Windows.Media;
-
-namespace HandheldCompanion.Controllers;
-
-public class XInputController : IController
-{
- private Controller Controller;
- private Gamepad Gamepad;
-
- private XInputStateSecret State;
-
- public XInputController()
- { }
-
- public XInputController(PnPDetails details)
- {
- AttachController(details.XInputUserIndex);
- AttachDetails(details);
-
- // UI
- ColoredButtons.Add(ButtonFlags.B1, new SolidColorBrush(Color.FromArgb(255, 81, 191, 61)));
- ColoredButtons.Add(ButtonFlags.B2, new SolidColorBrush(Color.FromArgb(255, 217, 65, 38)));
- ColoredButtons.Add(ButtonFlags.B3, new SolidColorBrush(Color.FromArgb(255, 26, 159, 255)));
- ColoredButtons.Add(ButtonFlags.B4, new SolidColorBrush(Color.FromArgb(255, 255, 200, 44)));
-
- DrawUI();
- UpdateUI();
-
- string enumerator = Details.GetEnumerator();
- switch (enumerator)
- {
- default:
- case "BTHENUM":
- ProgressBarWarning.Text = Properties.Resources.XInputController_Warning_BTH;
- break;
- case "USB":
- ProgressBarWarning.Text = Properties.Resources.XInputController_Warning_USB;
- break;
- }
- }
-
- public override string ToString()
- {
- var baseName = base.ToString();
- if (!string.IsNullOrEmpty(baseName))
- return baseName;
- return $"XInput Controller {(UserIndex)UserIndex}";
- }
-
- public virtual void UpdateInputs(long ticks, float delta, bool commit)
- {
- // skip if controller isn't connected
- if (!IsConnected())
- return;
-
- try
- {
- // update gamepad state
- Gamepad = Controller.GetState().Gamepad;
-
- // update secret state
- XInputGetStateSecret14(UserIndex, out State);
-
- Inputs.ButtonState = InjectedButtons.Clone() as ButtonState;
-
- Inputs.ButtonState[ButtonFlags.B1] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.A);
- Inputs.ButtonState[ButtonFlags.B2] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.B);
- Inputs.ButtonState[ButtonFlags.B3] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.X);
- Inputs.ButtonState[ButtonFlags.B4] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.Y);
-
- Inputs.ButtonState[ButtonFlags.Start] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.Start);
- Inputs.ButtonState[ButtonFlags.Back] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.Back);
-
- Inputs.ButtonState[ButtonFlags.L2Soft] = Gamepad.LeftTrigger > Gamepad.TriggerThreshold;
- Inputs.ButtonState[ButtonFlags.R2Soft] = Gamepad.RightTrigger > Gamepad.TriggerThreshold;
-
- Inputs.ButtonState[ButtonFlags.L2Full] = Gamepad.LeftTrigger > Gamepad.TriggerThreshold * 8;
- Inputs.ButtonState[ButtonFlags.R2Full] = Gamepad.RightTrigger > Gamepad.TriggerThreshold * 8;
-
- Inputs.ButtonState[ButtonFlags.LeftStickClick] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.LeftThumb);
- Inputs.ButtonState[ButtonFlags.RightStickClick] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.RightThumb);
-
- Inputs.ButtonState[ButtonFlags.L1] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.LeftShoulder);
- Inputs.ButtonState[ButtonFlags.R1] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.RightShoulder);
-
- Inputs.ButtonState[ButtonFlags.DPadUp] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadUp);
- Inputs.ButtonState[ButtonFlags.DPadDown] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadDown);
- Inputs.ButtonState[ButtonFlags.DPadLeft] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadLeft);
- Inputs.ButtonState[ButtonFlags.DPadRight] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadRight);
-
- // Left Stick
- Inputs.ButtonState[ButtonFlags.LeftStickLeft] = Gamepad.LeftThumbX < -Gamepad.LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickRight] = Gamepad.LeftThumbX > Gamepad.LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickDown] = Gamepad.LeftThumbY < -Gamepad.LeftThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.LeftStickUp] = Gamepad.LeftThumbY > Gamepad.LeftThumbDeadZone;
-
- Inputs.AxisState[AxisFlags.LeftStickX] = Gamepad.LeftThumbX;
- Inputs.AxisState[AxisFlags.LeftStickY] = Gamepad.LeftThumbY;
-
- // Right Stick
- Inputs.ButtonState[ButtonFlags.RightStickLeft] = Gamepad.RightThumbX < -Gamepad.RightThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.RightStickRight] = Gamepad.RightThumbX > Gamepad.RightThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.RightStickDown] = Gamepad.RightThumbY < -Gamepad.RightThumbDeadZone;
- Inputs.ButtonState[ButtonFlags.RightStickUp] = Gamepad.RightThumbY > Gamepad.RightThumbDeadZone;
-
- Inputs.AxisState[AxisFlags.RightStickX] = Gamepad.RightThumbX;
- Inputs.AxisState[AxisFlags.RightStickY] = Gamepad.RightThumbY;
-
- Inputs.ButtonState[ButtonFlags.Special] = State.wButtons.HasFlag(XInputStateButtons.Xbox);
-
- Inputs.AxisState[AxisFlags.L2] = Gamepad.LeftTrigger;
- Inputs.AxisState[AxisFlags.R2] = Gamepad.RightTrigger;
- }
- catch { }
-
- if (commit)
- base.UpdateInputs(ticks, delta);
- }
-
- public override bool IsConnected()
- {
- if (Controller is not null)
- return Controller.IsConnected;
- return false;
- }
-
- public override void SetVibration(byte LargeMotor, byte SmallMotor)
- {
- if (!IsConnected())
- return;
-
- try
- {
- ushort LeftMotorSpeed = (ushort)((double)LargeMotor / byte.MaxValue * ushort.MaxValue * VibrationStrength);
- ushort RightMotorSpeed = (ushort)((double)SmallMotor / byte.MaxValue * ushort.MaxValue * VibrationStrength);
-
- Vibration vibration = new Vibration { LeftMotorSpeed = LeftMotorSpeed, RightMotorSpeed = RightMotorSpeed };
- Controller.SetVibration(vibration);
- }
- catch { }
- }
-
- public override void Plug()
- {
- TimerManager.Tick += (ticks, delta) => UpdateInputs(ticks, delta, true);
- base.Plug();
- }
-
- public override void Unplug()
- {
- TimerManager.Tick -= (ticks, delta) => UpdateInputs(ticks, delta, true);
- base.Unplug();
- }
-
- public static UserIndex TryGetUserIndex(PnPDetails details)
- {
- XInputCapabilitiesEx capabilitiesEx = new();
-
- for (int idx = 0; idx < 4; idx++)
- {
- if (XInputGetCapabilitiesEx(1, idx, 0, ref capabilitiesEx) == 0)
- {
- if (capabilitiesEx.ProductId != details.ProductID || capabilitiesEx.VendorId != details.VendorID)
- continue;
-
- var devices = DeviceManager.GetDetails(capabilitiesEx.VendorId, capabilitiesEx.ProductId);
- if (devices.FirstOrDefault() is not null)
- return (UserIndex)idx;
- }
- }
-
- return SharpDX.XInput.UserIndex.Any;
- }
-
- public virtual void AttachController(byte userIndex)
- {
- if (UserIndex == userIndex)
- return;
-
- UserIndex = userIndex;
- Controller = new((UserIndex)userIndex);
- }
-
- public override void Hide(bool powerCycle = true)
- {
- base.Hide(powerCycle);
- }
-
- public override void Unhide(bool powerCycle = true)
- {
- base.Unhide(powerCycle);
- }
-
- public virtual new bool IsWireless
- {
- get
- {
- string enumerator = Details.GetEnumerator();
- switch (enumerator)
- {
- default:
- return false;
- case "BTHENUM":
- return true;
- }
- }
- }
-
- public override void CyclePort()
- {
- string enumerator = Details.GetEnumerator();
- switch (enumerator)
- {
- default:
- case "BTHENUM":
- Task.Run(async () =>
- {
- /*
- // Bluetooth HID Device
- Details.InstallNullDrivers(false);
- Details.InstallCustomDriver("hidbth.inf", false);
-
- // Bluetooth XINPUT compatible input device
- Details.InstallNullDrivers(true);
- Details.InstallCustomDriver("xinputhid.inf", true);
- */
-
- /*
- Details.Uninstall(false); // Bluetooth HID Device
- Details.Uninstall(true); // Bluetooth XINPUT compatible input device
- await Task.Delay(1000);
- Devcon.Refresh();
- */
- });
- break;
- case "USB":
- base.CyclePort();
- break;
- }
- }
-
- public override string GetGlyph(ButtonFlags button)
- {
- switch (button)
- {
- case ButtonFlags.B1:
- return "\u21D3"; // Button A
- case ButtonFlags.B2:
- return "\u21D2"; // Button B
- case ButtonFlags.B3:
- return "\u21D0"; // Button X
- case ButtonFlags.B4:
- return "\u21D1"; // Button Y
- case ButtonFlags.L1:
- return "\u2198";
- case ButtonFlags.R1:
- return "\u2199";
- case ButtonFlags.Back:
- return "\u21FA";
- case ButtonFlags.Start:
- return "\u21FB";
- case ButtonFlags.L2Soft:
- return "\u21DC";
- case ButtonFlags.L2Full:
- return "\u2196";
- case ButtonFlags.R2Soft:
- return "\u21DD";
- case ButtonFlags.R2Full:
- return "\u2197";
- case ButtonFlags.Special:
- return "\uE001";
- }
-
- return base.GetGlyph(button);
- }
-
- public override string GetGlyph(AxisFlags axis)
- {
- switch (axis)
- {
- case AxisFlags.L2:
- return "\u2196";
- case AxisFlags.R2:
- return "\u2197";
- }
-
- return base.GetGlyph(axis);
- }
-
- public override string GetGlyph(AxisLayoutFlags axis)
- {
- switch (axis)
- {
- case AxisLayoutFlags.L2:
- return "\u2196";
- case AxisLayoutFlags.R2:
- return "\u2197";
- }
-
- return base.GetGlyph(axis);
- }
-
- #region struct
-
- [StructLayout(LayoutKind.Explicit)]
- protected struct XInputGamepad
- {
- [MarshalAs(UnmanagedType.I2)]
- [FieldOffset(0)]
- public short wButtons;
-
- [MarshalAs(UnmanagedType.I1)]
- [FieldOffset(2)]
- public byte bLeftTrigger;
-
- [MarshalAs(UnmanagedType.I1)]
- [FieldOffset(3)]
- public byte bRightTrigger;
-
- [MarshalAs(UnmanagedType.I2)]
- [FieldOffset(4)]
- public short sThumbLX;
-
- [MarshalAs(UnmanagedType.I2)]
- [FieldOffset(6)]
- public short sThumbLY;
-
- [MarshalAs(UnmanagedType.I2)]
- [FieldOffset(8)]
- public short sThumbRX;
-
- [MarshalAs(UnmanagedType.I2)]
- [FieldOffset(10)]
- public short sThumbRY;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- protected struct XInputVibration
- {
- [MarshalAs(UnmanagedType.I2)] public ushort LeftMotorSpeed;
-
- [MarshalAs(UnmanagedType.I2)] public ushort RightMotorSpeed;
- }
-
- [StructLayout(LayoutKind.Explicit)]
- protected struct XInputCapabilities
- {
- [MarshalAs(UnmanagedType.I1)]
- [FieldOffset(0)]
- private readonly byte Type;
-
- [MarshalAs(UnmanagedType.I1)]
- [FieldOffset(1)]
- public byte SubType;
-
- [MarshalAs(UnmanagedType.I2)]
- [FieldOffset(2)]
- public short Flags;
-
- [FieldOffset(4)] public XInputGamepad Gamepad;
-
- [FieldOffset(16)] public XInputVibration Vibration;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- protected struct XInputCapabilitiesEx
- {
- public XInputCapabilities Capabilities;
- [MarshalAs(UnmanagedType.U2)] public ushort VendorId;
- [MarshalAs(UnmanagedType.U2)] public ushort ProductId;
- [MarshalAs(UnmanagedType.U2)] public ushort REV;
- [MarshalAs(UnmanagedType.U4)] public uint XID;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- protected struct XInputStateSecret
- {
- public uint eventCount;
- public XInputStateButtons wButtons;
- public byte bLeftTrigger;
- public byte bRightTrigger;
- public short sThumbLX;
- public short sThumbLY;
- public short sThumbRX;
- public short sThumbRY;
- }
-
- [StructLayout(LayoutKind.Sequential)]
- protected struct XInputBaseBusInformation
- {
- [MarshalAs(UnmanagedType.U2)]
- UInt16 VID;
- [MarshalAs(UnmanagedType.U2)]
- UInt16 PID;
- [MarshalAs(UnmanagedType.U4)]
- UInt32 a3;
- [MarshalAs(UnmanagedType.U4)]
- UInt32 Flags; // probably
- [MarshalAs(UnmanagedType.U1)]
- byte a4;
- [MarshalAs(UnmanagedType.U1)]
- byte a5;
- [MarshalAs(UnmanagedType.U1)]
- byte a6;
- [MarshalAs(UnmanagedType.U1)]
- byte reserved;
- }
-
- [Flags]
- protected enum XInputStateButtons : ushort
- {
- None = 0,
- Xbox = 1024
- }
-
- #endregion
-
- #region imports
-
- [DllImport("xinput1_4.dll", EntryPoint = "#108")]
- protected static extern int XInputGetCapabilitiesEx
- (
- int a1, // [in] unknown, should probably be 1
- int dwUserIndex, // [in] Index of the gamer associated with the device
- int dwFlags, // [in] Input flags that identify the device type
- ref XInputCapabilitiesEx pCapabilities // [out] Receives the capabilities
- );
-
- [DllImport("xinput1_3.dll", EntryPoint = "#100")]
- protected static extern int XInputGetStateSecret13(int playerIndex, out XInputStateSecret struc);
-
- [DllImport("xinput1_4.dll", EntryPoint = "#100")]
- protected static extern int XInputGetStateSecret14(int playerIndex, out XInputStateSecret struc);
-
- [DllImport("xinput1_4.dll", EntryPoint = "#104")]
- protected static extern int XInputGetBaseBusInformation(int dwUserIndex, ref XInputBaseBusInformation pInfo);
- #endregion
+using HandheldCompanion.Inputs;
+using HandheldCompanion.Managers;
+using SharpDX.XInput;
+using System;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using System.Windows.Media;
+
+namespace HandheldCompanion.Controllers;
+
+public class XInputController : IController
+{
+ private Controller Controller;
+ private Gamepad Gamepad;
+
+ private XInputStateSecret State;
+
+ public XInputController()
+ { }
+
+ public XInputController(PnPDetails details)
+ {
+ AttachController(details.XInputUserIndex);
+ AttachDetails(details);
+
+ // UI
+ ColoredButtons.Add(ButtonFlags.B1, new SolidColorBrush(Color.FromArgb(255, 81, 191, 61)));
+ ColoredButtons.Add(ButtonFlags.B2, new SolidColorBrush(Color.FromArgb(255, 217, 65, 38)));
+ ColoredButtons.Add(ButtonFlags.B3, new SolidColorBrush(Color.FromArgb(255, 26, 159, 255)));
+ ColoredButtons.Add(ButtonFlags.B4, new SolidColorBrush(Color.FromArgb(255, 255, 200, 44)));
+
+ DrawUI();
+ UpdateUI();
+
+ string enumerator = Details.GetEnumerator();
+ switch (enumerator)
+ {
+ default:
+ case "BTHENUM":
+ ProgressBarWarning.Text = Properties.Resources.XInputController_Warning_BTH;
+ break;
+ case "USB":
+ ProgressBarWarning.Text = Properties.Resources.XInputController_Warning_USB;
+ break;
+ }
+ }
+
+ public override string ToString()
+ {
+ var baseName = base.ToString();
+ if (!string.IsNullOrEmpty(baseName))
+ return baseName;
+ return $"XInput Controller {(UserIndex)UserIndex}";
+ }
+
+ public virtual void UpdateInputs(long ticks, float delta, bool commit)
+ {
+ // skip if controller isn't connected
+ if (!IsConnected())
+ return;
+
+ try
+ {
+ // update gamepad state
+ Gamepad = Controller.GetState().Gamepad;
+
+ // update secret state
+ XInputGetStateSecret14(UserIndex, out State);
+
+ Inputs.ButtonState = InjectedButtons.Clone() as ButtonState;
+
+ Inputs.ButtonState[ButtonFlags.B1] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.A);
+ Inputs.ButtonState[ButtonFlags.B2] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.B);
+ Inputs.ButtonState[ButtonFlags.B3] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.X);
+ Inputs.ButtonState[ButtonFlags.B4] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.Y);
+
+ Inputs.ButtonState[ButtonFlags.Start] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.Start);
+ Inputs.ButtonState[ButtonFlags.Back] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.Back);
+
+ Inputs.ButtonState[ButtonFlags.L2Soft] = Gamepad.LeftTrigger > Gamepad.TriggerThreshold;
+ Inputs.ButtonState[ButtonFlags.R2Soft] = Gamepad.RightTrigger > Gamepad.TriggerThreshold;
+
+ Inputs.ButtonState[ButtonFlags.L2Full] = Gamepad.LeftTrigger > Gamepad.TriggerThreshold * 8;
+ Inputs.ButtonState[ButtonFlags.R2Full] = Gamepad.RightTrigger > Gamepad.TriggerThreshold * 8;
+
+ Inputs.ButtonState[ButtonFlags.LeftStickClick] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.LeftThumb);
+ Inputs.ButtonState[ButtonFlags.RightStickClick] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.RightThumb);
+
+ Inputs.ButtonState[ButtonFlags.L1] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.LeftShoulder);
+ Inputs.ButtonState[ButtonFlags.R1] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.RightShoulder);
+
+ Inputs.ButtonState[ButtonFlags.DPadUp] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadUp);
+ Inputs.ButtonState[ButtonFlags.DPadDown] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadDown);
+ Inputs.ButtonState[ButtonFlags.DPadLeft] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadLeft);
+ Inputs.ButtonState[ButtonFlags.DPadRight] = Gamepad.Buttons.HasFlag(GamepadButtonFlags.DPadRight);
+
+ // Left Stick
+ Inputs.ButtonState[ButtonFlags.LeftStickLeft] = Gamepad.LeftThumbX < -Gamepad.LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickRight] = Gamepad.LeftThumbX > Gamepad.LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickDown] = Gamepad.LeftThumbY < -Gamepad.LeftThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.LeftStickUp] = Gamepad.LeftThumbY > Gamepad.LeftThumbDeadZone;
+
+ Inputs.AxisState[AxisFlags.LeftStickX] = Gamepad.LeftThumbX;
+ Inputs.AxisState[AxisFlags.LeftStickY] = Gamepad.LeftThumbY;
+
+ // Right Stick
+ Inputs.ButtonState[ButtonFlags.RightStickLeft] = Gamepad.RightThumbX < -Gamepad.RightThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.RightStickRight] = Gamepad.RightThumbX > Gamepad.RightThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.RightStickDown] = Gamepad.RightThumbY < -Gamepad.RightThumbDeadZone;
+ Inputs.ButtonState[ButtonFlags.RightStickUp] = Gamepad.RightThumbY > Gamepad.RightThumbDeadZone;
+
+ Inputs.AxisState[AxisFlags.RightStickX] = Gamepad.RightThumbX;
+ Inputs.AxisState[AxisFlags.RightStickY] = Gamepad.RightThumbY;
+
+ Inputs.ButtonState[ButtonFlags.Special] = State.wButtons.HasFlag(XInputStateButtons.Xbox);
+
+ Inputs.AxisState[AxisFlags.L2] = Gamepad.LeftTrigger;
+ Inputs.AxisState[AxisFlags.R2] = Gamepad.RightTrigger;
+ }
+ catch { }
+
+ if (commit)
+ base.UpdateInputs(ticks, delta);
+ }
+
+ public override bool IsConnected()
+ {
+ if (Controller is not null)
+ return Controller.IsConnected;
+ return false;
+ }
+
+ public override void SetVibration(byte LargeMotor, byte SmallMotor)
+ {
+ if (!IsConnected())
+ return;
+
+ try
+ {
+ ushort LeftMotorSpeed = (ushort)((double)LargeMotor / byte.MaxValue * ushort.MaxValue * VibrationStrength);
+ ushort RightMotorSpeed = (ushort)((double)SmallMotor / byte.MaxValue * ushort.MaxValue * VibrationStrength);
+
+ Vibration vibration = new Vibration { LeftMotorSpeed = LeftMotorSpeed, RightMotorSpeed = RightMotorSpeed };
+ Controller.SetVibration(vibration);
+ }
+ catch { }
+ }
+
+ public override void Plug()
+ {
+ TimerManager.Tick += (ticks, delta) => UpdateInputs(ticks, delta, true);
+ base.Plug();
+ }
+
+ public override void Unplug()
+ {
+ TimerManager.Tick -= (ticks, delta) => UpdateInputs(ticks, delta, true);
+ base.Unplug();
+ }
+
+ public static UserIndex TryGetUserIndex(PnPDetails details)
+ {
+ XInputCapabilitiesEx capabilitiesEx = new();
+
+ for (int idx = 0; idx < 4; idx++)
+ {
+ if (XInputGetCapabilitiesEx(1, idx, 0, ref capabilitiesEx) == 0)
+ {
+ if (capabilitiesEx.ProductId != details.ProductID || capabilitiesEx.VendorId != details.VendorID)
+ continue;
+
+ var devices = DeviceManager.GetDetails(capabilitiesEx.VendorId, capabilitiesEx.ProductId);
+ if (devices.FirstOrDefault() is not null)
+ return (UserIndex)idx;
+ }
+ }
+
+ return SharpDX.XInput.UserIndex.Any;
+ }
+
+ public virtual void AttachController(byte userIndex)
+ {
+ if (UserIndex == userIndex)
+ return;
+
+ UserIndex = userIndex;
+ Controller = new((UserIndex)userIndex);
+ }
+
+ public override void Hide(bool powerCycle = true)
+ {
+ base.Hide(powerCycle);
+ }
+
+ public override void Unhide(bool powerCycle = true)
+ {
+ base.Unhide(powerCycle);
+ }
+
+ public virtual new bool IsWireless
+ {
+ get
+ {
+ string enumerator = Details.GetEnumerator();
+ switch (enumerator)
+ {
+ default:
+ return false;
+ case "BTHENUM":
+ return true;
+ }
+ }
+ }
+
+ public override void CyclePort()
+ {
+ string enumerator = Details.GetEnumerator();
+ switch (enumerator)
+ {
+ default:
+ case "BTHENUM":
+ Task.Run(async () =>
+ {
+ /*
+ // Bluetooth HID Device
+ Details.InstallNullDrivers(false);
+ Details.InstallCustomDriver("hidbth.inf", false);
+
+ // Bluetooth XINPUT compatible input device
+ Details.InstallNullDrivers(true);
+ Details.InstallCustomDriver("xinputhid.inf", true);
+ */
+
+ /*
+ Details.Uninstall(false); // Bluetooth HID Device
+ Details.Uninstall(true); // Bluetooth XINPUT compatible input device
+ await Task.Delay(1000);
+ Devcon.Refresh();
+ */
+ });
+ break;
+ case "USB":
+ base.CyclePort();
+ break;
+ }
+ }
+
+ public override string GetGlyph(ButtonFlags button)
+ {
+ switch (button)
+ {
+ case ButtonFlags.B1:
+ return "\u21D3"; // Button A
+ case ButtonFlags.B2:
+ return "\u21D2"; // Button B
+ case ButtonFlags.B3:
+ return "\u21D0"; // Button X
+ case ButtonFlags.B4:
+ return "\u21D1"; // Button Y
+ case ButtonFlags.L1:
+ return "\u2198";
+ case ButtonFlags.R1:
+ return "\u2199";
+ case ButtonFlags.Back:
+ return "\u21FA";
+ case ButtonFlags.Start:
+ return "\u21FB";
+ case ButtonFlags.L2Soft:
+ return "\u21DC";
+ case ButtonFlags.L2Full:
+ return "\u2196";
+ case ButtonFlags.R2Soft:
+ return "\u21DD";
+ case ButtonFlags.R2Full:
+ return "\u2197";
+ case ButtonFlags.Special:
+ return "\uE001";
+ }
+
+ return base.GetGlyph(button);
+ }
+
+ public override string GetGlyph(AxisFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisFlags.L2:
+ return "\u2196";
+ case AxisFlags.R2:
+ return "\u2197";
+ }
+
+ return base.GetGlyph(axis);
+ }
+
+ public override string GetGlyph(AxisLayoutFlags axis)
+ {
+ switch (axis)
+ {
+ case AxisLayoutFlags.L2:
+ return "\u2196";
+ case AxisLayoutFlags.R2:
+ return "\u2197";
+ }
+
+ return base.GetGlyph(axis);
+ }
+
+ #region struct
+
+ [StructLayout(LayoutKind.Explicit)]
+ protected struct XInputGamepad
+ {
+ [MarshalAs(UnmanagedType.I2)]
+ [FieldOffset(0)]
+ public short wButtons;
+
+ [MarshalAs(UnmanagedType.I1)]
+ [FieldOffset(2)]
+ public byte bLeftTrigger;
+
+ [MarshalAs(UnmanagedType.I1)]
+ [FieldOffset(3)]
+ public byte bRightTrigger;
+
+ [MarshalAs(UnmanagedType.I2)]
+ [FieldOffset(4)]
+ public short sThumbLX;
+
+ [MarshalAs(UnmanagedType.I2)]
+ [FieldOffset(6)]
+ public short sThumbLY;
+
+ [MarshalAs(UnmanagedType.I2)]
+ [FieldOffset(8)]
+ public short sThumbRX;
+
+ [MarshalAs(UnmanagedType.I2)]
+ [FieldOffset(10)]
+ public short sThumbRY;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ protected struct XInputVibration
+ {
+ [MarshalAs(UnmanagedType.I2)] public ushort LeftMotorSpeed;
+
+ [MarshalAs(UnmanagedType.I2)] public ushort RightMotorSpeed;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ protected struct XInputCapabilities
+ {
+ [MarshalAs(UnmanagedType.I1)]
+ [FieldOffset(0)]
+ private readonly byte Type;
+
+ [MarshalAs(UnmanagedType.I1)]
+ [FieldOffset(1)]
+ public byte SubType;
+
+ [MarshalAs(UnmanagedType.I2)]
+ [FieldOffset(2)]
+ public short Flags;
+
+ [FieldOffset(4)] public XInputGamepad Gamepad;
+
+ [FieldOffset(16)] public XInputVibration Vibration;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ protected struct XInputCapabilitiesEx
+ {
+ public XInputCapabilities Capabilities;
+ [MarshalAs(UnmanagedType.U2)] public ushort VendorId;
+ [MarshalAs(UnmanagedType.U2)] public ushort ProductId;
+ [MarshalAs(UnmanagedType.U2)] public ushort REV;
+ [MarshalAs(UnmanagedType.U4)] public uint XID;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ protected struct XInputStateSecret
+ {
+ public uint eventCount;
+ public XInputStateButtons wButtons;
+ public byte bLeftTrigger;
+ public byte bRightTrigger;
+ public short sThumbLX;
+ public short sThumbLY;
+ public short sThumbRX;
+ public short sThumbRY;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ protected struct XInputBaseBusInformation
+ {
+ [MarshalAs(UnmanagedType.U2)]
+ UInt16 VID;
+ [MarshalAs(UnmanagedType.U2)]
+ UInt16 PID;
+ [MarshalAs(UnmanagedType.U4)]
+ UInt32 a3;
+ [MarshalAs(UnmanagedType.U4)]
+ UInt32 Flags; // probably
+ [MarshalAs(UnmanagedType.U1)]
+ byte a4;
+ [MarshalAs(UnmanagedType.U1)]
+ byte a5;
+ [MarshalAs(UnmanagedType.U1)]
+ byte a6;
+ [MarshalAs(UnmanagedType.U1)]
+ byte reserved;
+ }
+
+ [Flags]
+ protected enum XInputStateButtons : ushort
+ {
+ None = 0,
+ Xbox = 1024
+ }
+
+ #endregion
+
+ #region imports
+
+ [DllImport("xinput1_4.dll", EntryPoint = "#108")]
+ protected static extern int XInputGetCapabilitiesEx
+ (
+ int a1, // [in] unknown, should probably be 1
+ int dwUserIndex, // [in] Index of the gamer associated with the device
+ int dwFlags, // [in] Input flags that identify the device type
+ ref XInputCapabilitiesEx pCapabilities // [out] Receives the capabilities
+ );
+
+ [DllImport("xinput1_3.dll", EntryPoint = "#100")]
+ protected static extern int XInputGetStateSecret13(int playerIndex, out XInputStateSecret struc);
+
+ [DllImport("xinput1_4.dll", EntryPoint = "#100")]
+ protected static extern int XInputGetStateSecret14(int playerIndex, out XInputStateSecret struc);
+
+ [DllImport("xinput1_4.dll", EntryPoint = "#104")]
+ protected static extern int XInputGetBaseBusInformation(int dwUserIndex, ref XInputBaseBusInformation pInfo);
+ #endregion
}
\ No newline at end of file
diff --git a/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs b/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs
index 4ea5574c1..60ba88369 100644
--- a/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_AMD_IntegerScalingCheck.cs
@@ -1,84 +1,84 @@
-using HandheldCompanion.Managers;
-using HandheldCompanion.Misc;
-using HandheldCompanion.Processors;
-using HandheldCompanion.Utils;
-using HandheldCompanion.Views;
-using iNKORE.UI.WPF.Modern.Controls;
-using System;
-using System.Diagnostics;
-using System.Threading.Tasks;
-using System.Windows;
-
-namespace HandheldCompanion.Controls.Hints
-{
- public class Hint_AMD_IntegerScalingCheck : IHint
- {
- public Hint_AMD_IntegerScalingCheck() : base()
- {
- // default state
- this.HintActionButton.Visibility = Visibility.Visible;
-
- this.HintTitle.Text = Properties.Resources.Hint_AMD_IntegerScalingCheck;
- this.HintDescription.Text = Properties.Resources.Hint_AMD_IntegerScalingCheckDesc;
- this.HintReadMe.Text = Properties.Resources.Hint_AMD_IntegerScalingCheckReadme;
-
- this.HintActionButton.Content = Properties.Resources.Hint_AMD_IntegerScalingCheckAction;
-
- // manage events
- PerformanceManager.Initialized += PerformanceManager_Initialized;
- }
-
- private void PerformanceManager_Initialized()
- {
- Processor processor = PerformanceManager.GetProcessor();
-
- if (processor is not null && processor is AMDProcessor)
- CheckSettings();
- }
-
- private void CheckSettings()
- {
- // read OS specific values
- int EmbeddedIntegerScalingSupport = RegistryUtils.GetInt(@"SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000", "DalEmbeddedIntegerScalingSupport");
-
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- this.Visibility = EmbeddedIntegerScalingSupport != 1 ? Visibility.Visible : Visibility.Collapsed;
- });
- }
-
- protected override async void HintActionButton_Click(object sender, RoutedEventArgs e)
- {
- RegistryUtils.SetValue(@"SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000", "DalEmbeddedIntegerScalingSupport", 1);
-
- Task dialogTask = new Dialog(MainWindow.GetCurrent())
- {
- Title = Properties.Resources.Dialog_ForceRestartTitle,
- Content = Properties.Resources.Dialog_ForceRestartDesc,
- DefaultButton = ContentDialogButton.Close,
- CloseButtonText = Properties.Resources.Dialog_No,
- PrimaryButtonText = Properties.Resources.Dialog_Yes
- }.ShowAsync();
-
- await dialogTask; // sync call
-
- switch (dialogTask.Result)
- {
- case ContentDialogResult.Primary:
- using (Process shutdown = new())
- {
- shutdown.StartInfo.FileName = "shutdown.exe";
- shutdown.StartInfo.Arguments = "-r -t 3";
-
- shutdown.StartInfo.UseShellExecute = false;
- shutdown.StartInfo.CreateNoWindow = true;
- shutdown.Start();
- }
- break;
- case ContentDialogResult.Secondary:
- break;
- }
- }
- }
-}
+using HandheldCompanion.Managers;
+using HandheldCompanion.Misc;
+using HandheldCompanion.Processors;
+using HandheldCompanion.Utils;
+using HandheldCompanion.Views;
+using iNKORE.UI.WPF.Modern.Controls;
+using System;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace HandheldCompanion.Controls.Hints
+{
+ public class Hint_AMD_IntegerScalingCheck : IHint
+ {
+ public Hint_AMD_IntegerScalingCheck() : base()
+ {
+ // default state
+ this.HintActionButton.Visibility = Visibility.Visible;
+
+ this.HintTitle.Text = Properties.Resources.Hint_AMD_IntegerScalingCheck;
+ this.HintDescription.Text = Properties.Resources.Hint_AMD_IntegerScalingCheckDesc;
+ this.HintReadMe.Text = Properties.Resources.Hint_AMD_IntegerScalingCheckReadme;
+
+ this.HintActionButton.Content = Properties.Resources.Hint_AMD_IntegerScalingCheckAction;
+
+ // manage events
+ PerformanceManager.Initialized += PerformanceManager_Initialized;
+ }
+
+ private void PerformanceManager_Initialized()
+ {
+ Processor processor = PerformanceManager.GetProcessor();
+
+ if (processor is not null && processor is AMDProcessor)
+ CheckSettings();
+ }
+
+ private void CheckSettings()
+ {
+ // read OS specific values
+ int EmbeddedIntegerScalingSupport = RegistryUtils.GetInt(@"SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000", "DalEmbeddedIntegerScalingSupport");
+
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ this.Visibility = EmbeddedIntegerScalingSupport != 1 ? Visibility.Visible : Visibility.Collapsed;
+ });
+ }
+
+ protected override async void HintActionButton_Click(object sender, RoutedEventArgs e)
+ {
+ RegistryUtils.SetValue(@"SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}\0000", "DalEmbeddedIntegerScalingSupport", 1);
+
+ Task dialogTask = new Dialog(MainWindow.GetCurrent())
+ {
+ Title = Properties.Resources.Dialog_ForceRestartTitle,
+ Content = Properties.Resources.Dialog_ForceRestartDesc,
+ DefaultButton = ContentDialogButton.Close,
+ CloseButtonText = Properties.Resources.Dialog_No,
+ PrimaryButtonText = Properties.Resources.Dialog_Yes
+ }.ShowAsync();
+
+ await dialogTask; // sync call
+
+ switch (dialogTask.Result)
+ {
+ case ContentDialogResult.Primary:
+ using (Process shutdown = new())
+ {
+ shutdown.StartInfo.FileName = "shutdown.exe";
+ shutdown.StartInfo.Arguments = "-r -t 3";
+
+ shutdown.StartInfo.UseShellExecute = false;
+ shutdown.StartInfo.CreateNoWindow = true;
+ shutdown.Start();
+ }
+ break;
+ case ContentDialogResult.Secondary:
+ break;
+ }
+ }
+ }
+}
diff --git a/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs b/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs
index fb2b43f9e..3899e4d8e 100644
--- a/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_CoreIsolationCheck.cs
@@ -1,113 +1,113 @@
-using HandheldCompanion.Misc;
-using HandheldCompanion.Utils;
-using HandheldCompanion.Views;
-using iNKORE.UI.WPF.Modern.Controls;
-using System;
-using System.Diagnostics;
-using System.Management;
-using System.Threading.Tasks;
-using System.Windows;
-
-namespace HandheldCompanion.Controls.Hints
-{
- public class Hint_CoreIsolationCheck : IHint
- {
- private static WqlEventQuery HypervisorQuery = new WqlEventQuery(@"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_LOCAL_MACHINE' AND KeyPath = 'SYSTEM\\CurrentControlSet\\Control\\DeviceGuard\\Scenarios' AND ValueName='HypervisorEnforcedCodeIntegrity'");
- private static WqlEventQuery VulnerableDriverQuery = new WqlEventQuery(@"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_LOCAL_MACHINE' AND KeyPath = 'SYSTEM\\CurrentControlSet\\Control\\CI\\Config' AND ValueName='VulnerableDriverBlocklistEnable'");
-
- private ManagementEventWatcher VulnerableDriverWatcher = new ManagementEventWatcher(VulnerableDriverQuery);
- private ManagementEventWatcher HypervisorWatcher = new ManagementEventWatcher(HypervisorQuery);
-
- bool HypervisorEnforcedCodeIntegrityEnabled = true;
- bool VulnerableDriverBlocklistEnable = true;
-
- public Hint_CoreIsolationCheck() : base()
- {
- if (RegistryUtils.KeyExists(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios", "HypervisorEnforcedCodeIntegrity"))
- {
- HypervisorWatcher.EventArrived += new EventArrivedEventHandler(HandleEvent);
- HypervisorWatcher.Start();
- }
-
- if (RegistryUtils.KeyExists(@"SYSTEM\CurrentControlSet\Control\CI\Config", "VulnerableDriverBlocklistEnable"))
- {
- VulnerableDriverWatcher.EventArrived += new EventArrivedEventHandler(HandleEvent);
- VulnerableDriverWatcher.Start();
- }
-
- // default state
- this.HintActionButton.Visibility = Visibility.Visible;
-
- this.HintTitle.Text = Properties.Resources.Hint_CoreIsolationCheck;
- this.HintDescription.Text = Properties.Resources.Hint_CoreIsolationCheckDesc;
- this.HintReadMe.Text = Properties.Resources.Hint_CoreIsolationCheckReadme;
-
- this.HintActionButton.Content = Properties.Resources.Hint_CoreIsolationCheckAction;
-
- CheckSettings();
- }
-
- private void HandleEvent(object sender, EventArrivedEventArgs e)
- {
- CheckSettings();
- }
-
- private void CheckSettings()
- {
- // read OS specific values
- HypervisorEnforcedCodeIntegrityEnabled = RegistryUtils.GetBoolean(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios", "HypervisorEnforcedCodeIntegrity");
- VulnerableDriverBlocklistEnable = RegistryUtils.GetBoolean(@"SYSTEM\CurrentControlSet\Control\CI\Config", "VulnerableDriverBlocklistEnable");
-
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- this.Visibility = HypervisorEnforcedCodeIntegrityEnabled || VulnerableDriverBlocklistEnable ? Visibility.Visible : Visibility.Collapsed;
- });
- }
-
- protected override async void HintActionButton_Click(object sender, RoutedEventArgs e)
- {
- RegistryUtils.SetValue(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios", "HypervisorEnforcedCodeIntegrity", 0);
- RegistryUtils.SetValue(@"SYSTEM\CurrentControlSet\Control\CI\Config", "VulnerableDriverBlocklistEnable", 0);
-
- Task dialogTask = new Dialog(MainWindow.GetCurrent())
- {
- Title = Properties.Resources.Dialog_ForceRestartTitle,
- Content = Properties.Resources.Dialog_ForceRestartDesc,
- DefaultButton = ContentDialogButton.Close,
- CloseButtonText = Properties.Resources.Dialog_No,
- PrimaryButtonText = Properties.Resources.Dialog_Yes
- }.ShowAsync();
-
- await dialogTask; // sync call
-
- switch (dialogTask.Result)
- {
- case ContentDialogResult.Primary:
- using (Process shutdown = new())
- {
- shutdown.StartInfo.FileName = "shutdown.exe";
- shutdown.StartInfo.Arguments = "-r -t 3";
-
- shutdown.StartInfo.UseShellExecute = false;
- shutdown.StartInfo.CreateNoWindow = true;
- shutdown.Start();
- }
- break;
- case ContentDialogResult.Secondary:
- break;
- }
- }
-
- public override void Stop()
- {
- if (RegistryUtils.KeyExists(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios", "HypervisorEnforcedCodeIntegrity"))
- HypervisorWatcher.Stop();
-
- if (RegistryUtils.KeyExists(@"SYSTEM\CurrentControlSet\Control\CI\Config", "VulnerableDriverBlocklistEnable"))
- VulnerableDriverWatcher.Stop();
-
- base.Stop();
- }
- }
-}
+using HandheldCompanion.Misc;
+using HandheldCompanion.Utils;
+using HandheldCompanion.Views;
+using iNKORE.UI.WPF.Modern.Controls;
+using System;
+using System.Diagnostics;
+using System.Management;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace HandheldCompanion.Controls.Hints
+{
+ public class Hint_CoreIsolationCheck : IHint
+ {
+ private static WqlEventQuery HypervisorQuery = new WqlEventQuery(@"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_LOCAL_MACHINE' AND KeyPath = 'SYSTEM\\CurrentControlSet\\Control\\DeviceGuard\\Scenarios' AND ValueName='HypervisorEnforcedCodeIntegrity'");
+ private static WqlEventQuery VulnerableDriverQuery = new WqlEventQuery(@"SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_LOCAL_MACHINE' AND KeyPath = 'SYSTEM\\CurrentControlSet\\Control\\CI\\Config' AND ValueName='VulnerableDriverBlocklistEnable'");
+
+ private ManagementEventWatcher VulnerableDriverWatcher = new ManagementEventWatcher(VulnerableDriverQuery);
+ private ManagementEventWatcher HypervisorWatcher = new ManagementEventWatcher(HypervisorQuery);
+
+ bool HypervisorEnforcedCodeIntegrityEnabled = true;
+ bool VulnerableDriverBlocklistEnable = true;
+
+ public Hint_CoreIsolationCheck() : base()
+ {
+ if (RegistryUtils.KeyExists(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios", "HypervisorEnforcedCodeIntegrity"))
+ {
+ HypervisorWatcher.EventArrived += new EventArrivedEventHandler(HandleEvent);
+ HypervisorWatcher.Start();
+ }
+
+ if (RegistryUtils.KeyExists(@"SYSTEM\CurrentControlSet\Control\CI\Config", "VulnerableDriverBlocklistEnable"))
+ {
+ VulnerableDriverWatcher.EventArrived += new EventArrivedEventHandler(HandleEvent);
+ VulnerableDriverWatcher.Start();
+ }
+
+ // default state
+ this.HintActionButton.Visibility = Visibility.Visible;
+
+ this.HintTitle.Text = Properties.Resources.Hint_CoreIsolationCheck;
+ this.HintDescription.Text = Properties.Resources.Hint_CoreIsolationCheckDesc;
+ this.HintReadMe.Text = Properties.Resources.Hint_CoreIsolationCheckReadme;
+
+ this.HintActionButton.Content = Properties.Resources.Hint_CoreIsolationCheckAction;
+
+ CheckSettings();
+ }
+
+ private void HandleEvent(object sender, EventArrivedEventArgs e)
+ {
+ CheckSettings();
+ }
+
+ private void CheckSettings()
+ {
+ // read OS specific values
+ HypervisorEnforcedCodeIntegrityEnabled = RegistryUtils.GetBoolean(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios", "HypervisorEnforcedCodeIntegrity");
+ VulnerableDriverBlocklistEnable = RegistryUtils.GetBoolean(@"SYSTEM\CurrentControlSet\Control\CI\Config", "VulnerableDriverBlocklistEnable");
+
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ this.Visibility = HypervisorEnforcedCodeIntegrityEnabled || VulnerableDriverBlocklistEnable ? Visibility.Visible : Visibility.Collapsed;
+ });
+ }
+
+ protected override async void HintActionButton_Click(object sender, RoutedEventArgs e)
+ {
+ RegistryUtils.SetValue(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios", "HypervisorEnforcedCodeIntegrity", 0);
+ RegistryUtils.SetValue(@"SYSTEM\CurrentControlSet\Control\CI\Config", "VulnerableDriverBlocklistEnable", 0);
+
+ Task dialogTask = new Dialog(MainWindow.GetCurrent())
+ {
+ Title = Properties.Resources.Dialog_ForceRestartTitle,
+ Content = Properties.Resources.Dialog_ForceRestartDesc,
+ DefaultButton = ContentDialogButton.Close,
+ CloseButtonText = Properties.Resources.Dialog_No,
+ PrimaryButtonText = Properties.Resources.Dialog_Yes
+ }.ShowAsync();
+
+ await dialogTask; // sync call
+
+ switch (dialogTask.Result)
+ {
+ case ContentDialogResult.Primary:
+ using (Process shutdown = new())
+ {
+ shutdown.StartInfo.FileName = "shutdown.exe";
+ shutdown.StartInfo.Arguments = "-r -t 3";
+
+ shutdown.StartInfo.UseShellExecute = false;
+ shutdown.StartInfo.CreateNoWindow = true;
+ shutdown.Start();
+ }
+ break;
+ case ContentDialogResult.Secondary:
+ break;
+ }
+ }
+
+ public override void Stop()
+ {
+ if (RegistryUtils.KeyExists(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios", "HypervisorEnforcedCodeIntegrity"))
+ HypervisorWatcher.Stop();
+
+ if (RegistryUtils.KeyExists(@"SYSTEM\CurrentControlSet\Control\CI\Config", "VulnerableDriverBlocklistEnable"))
+ VulnerableDriverWatcher.Stop();
+
+ base.Stop();
+ }
+ }
+}
diff --git a/HandheldCompanion/Controls/Hints/Hint_LegionGoLegionSpace.cs b/HandheldCompanion/Controls/Hints/Hint_LegionGoLegionSpace.cs
index 6d1fb83d3..ae102ede0 100644
--- a/HandheldCompanion/Controls/Hints/Hint_LegionGoLegionSpace.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_LegionGoLegionSpace.cs
@@ -1,127 +1,127 @@
-using HandheldCompanion.Devices;
-using HandheldCompanion.Utils;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.ServiceProcess;
-using System.Threading.Tasks;
-using System.Timers;
-using System.Windows;
-
-namespace HandheldCompanion.Controls.Hints
-{
- public class Hint_LegionGoLegionSpace : IHint
- {
- private List serviceNames = new()
- {
- "DAService",
- };
-
- private List taskNames = new()
- {
- "LegionGoQuickSettings",
- "LegionSpace",
- "LSDaemon"
- };
-
- private List serviceControllers = new();
- private Timer serviceTimer;
-
- public Hint_LegionGoLegionSpace() : base()
- {
- if (IDevice.GetCurrent() is not LegionGo)
- return;
-
- // Get all the services installed on the local computer
- ServiceController[] services = ServiceController.GetServices();
- foreach (string serviceName in serviceNames)
- {
- if (services.Any(s => serviceNames.Contains(s.ServiceName)))
- {
- // Create a service controller object for the specified service
- ServiceController serviceController = new ServiceController(serviceName);
- serviceControllers.Add(serviceController);
- }
- }
-
- // Check if any of the services in the list exist
- if (!serviceControllers.Any())
- return;
-
- serviceTimer = new Timer(4000);
- serviceTimer.Elapsed += ServiceTimer_Elapsed;
- serviceTimer.Start();
-
- // default state
- this.HintActionButton.Visibility = Visibility.Visible;
-
- this.HintTitle.Text = Properties.Resources.Hint_LegionGoServices;
- this.HintDescription.Text = Properties.Resources.Hint_LegionGoServicesDesc;
- this.HintReadMe.Text = Properties.Resources.Hint_LegionGoServicesReadme;
-
- this.HintActionButton.Content = Properties.Resources.Hint_LegionGoServicesAction;
- }
-
- private void ServiceTimer_Elapsed(object? sender, ElapsedEventArgs e)
- {
- if (!serviceControllers.Any())
- return;
-
- // Check if any of the services in the list exist and are running
- bool anyRunning = false;
-
- foreach (ServiceController serviceController in serviceControllers)
- {
- serviceController.Refresh();
- if (serviceController.Status == ServiceControllerStatus.Running)
- {
- anyRunning = true;
- break;
- }
- }
-
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- this.Visibility = anyRunning ? Visibility.Visible : Visibility.Collapsed;
- });
- }
-
- protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
- {
- if (!serviceControllers.Any())
- return;
-
- Task.Run(async () =>
- {
- // Disable services
- foreach (ServiceController serviceController in serviceControllers)
- {
- // Disable service from doing anything anymore
- ServiceUtils.ChangeStartMode(serviceController, ServiceStartMode.Disabled, out string error);
-
- // Stop tasks related to service
- foreach (string taskName in taskNames)
- {
- var taskProcess = Process.GetProcessesByName(taskName).FirstOrDefault();
- if (taskProcess != null && !taskProcess.HasExited)
- {
- taskProcess.Kill();
- }
- }
-
- // Stop running service
- if (serviceController.Status == ServiceControllerStatus.Running)
- serviceController.Stop();
- }
- });
- }
-
- public override void Stop()
- {
- serviceTimer?.Stop();
- base.Stop();
- }
- }
-}
+using HandheldCompanion.Devices;
+using HandheldCompanion.Utils;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.ServiceProcess;
+using System.Threading.Tasks;
+using System.Timers;
+using System.Windows;
+
+namespace HandheldCompanion.Controls.Hints
+{
+ public class Hint_LegionGoLegionSpace : IHint
+ {
+ private List serviceNames = new()
+ {
+ "DAService",
+ };
+
+ private List taskNames = new()
+ {
+ "LegionGoQuickSettings",
+ "LegionSpace",
+ "LSDaemon"
+ };
+
+ private List serviceControllers = new();
+ private Timer serviceTimer;
+
+ public Hint_LegionGoLegionSpace() : base()
+ {
+ if (IDevice.GetCurrent() is not LegionGo)
+ return;
+
+ // Get all the services installed on the local computer
+ ServiceController[] services = ServiceController.GetServices();
+ foreach (string serviceName in serviceNames)
+ {
+ if (services.Any(s => serviceNames.Contains(s.ServiceName)))
+ {
+ // Create a service controller object for the specified service
+ ServiceController serviceController = new ServiceController(serviceName);
+ serviceControllers.Add(serviceController);
+ }
+ }
+
+ // Check if any of the services in the list exist
+ if (!serviceControllers.Any())
+ return;
+
+ serviceTimer = new Timer(4000);
+ serviceTimer.Elapsed += ServiceTimer_Elapsed;
+ serviceTimer.Start();
+
+ // default state
+ this.HintActionButton.Visibility = Visibility.Visible;
+
+ this.HintTitle.Text = Properties.Resources.Hint_LegionGoServices;
+ this.HintDescription.Text = Properties.Resources.Hint_LegionGoServicesDesc;
+ this.HintReadMe.Text = Properties.Resources.Hint_LegionGoServicesReadme;
+
+ this.HintActionButton.Content = Properties.Resources.Hint_LegionGoServicesAction;
+ }
+
+ private void ServiceTimer_Elapsed(object? sender, ElapsedEventArgs e)
+ {
+ if (!serviceControllers.Any())
+ return;
+
+ // Check if any of the services in the list exist and are running
+ bool anyRunning = false;
+
+ foreach (ServiceController serviceController in serviceControllers)
+ {
+ serviceController.Refresh();
+ if (serviceController.Status == ServiceControllerStatus.Running)
+ {
+ anyRunning = true;
+ break;
+ }
+ }
+
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ this.Visibility = anyRunning ? Visibility.Visible : Visibility.Collapsed;
+ });
+ }
+
+ protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (!serviceControllers.Any())
+ return;
+
+ Task.Run(async () =>
+ {
+ // Disable services
+ foreach (ServiceController serviceController in serviceControllers)
+ {
+ // Disable service from doing anything anymore
+ ServiceUtils.ChangeStartMode(serviceController, ServiceStartMode.Disabled, out string error);
+
+ // Stop tasks related to service
+ foreach (string taskName in taskNames)
+ {
+ var taskProcess = Process.GetProcessesByName(taskName).FirstOrDefault();
+ if (taskProcess != null && !taskProcess.HasExited)
+ {
+ taskProcess.Kill();
+ }
+ }
+
+ // Stop running service
+ if (serviceController.Status == ServiceControllerStatus.Running)
+ serviceController.Stop();
+ }
+ });
+ }
+
+ public override void Stop()
+ {
+ serviceTimer?.Stop();
+ base.Stop();
+ }
+ }
+}
diff --git a/HandheldCompanion/Controls/Hints/Hint_MSIClawCenter.cs b/HandheldCompanion/Controls/Hints/Hint_MSIClawCenter.cs
index 24e8b462e..0e6fee166 100644
--- a/HandheldCompanion/Controls/Hints/Hint_MSIClawCenter.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_MSIClawCenter.cs
@@ -1,119 +1,119 @@
-using HandheldCompanion.Devices;
-using Microsoft.Win32.TaskScheduler;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Timers;
-using System.Windows;
-using TaskScheduled = Microsoft.Win32.TaskScheduler.Task;
-
-namespace HandheldCompanion.Controls.Hints
-{
- public class Hint_MSIClawCenter : IHint
- {
- private List serviceNames = new()
- {
- "ArmouryCrateSEService",
- "AsusAppService",
- "ArmouryCrateControlInterface",
- };
-
- private Timer watcherTimer;
- private const string TaskName = "MSI_Center_M_Server";
- private const string UIname = "MSI Center M";
-
- public Hint_MSIClawCenter() : base()
- {
- if (IDevice.GetCurrent() is not ClawA1M)
- return;
-
- // we'll be checking MSI Claw Task every 4 seconds
- watcherTimer = new Timer(4000);
- watcherTimer.Elapsed += WatcherTimer_Elapsed;
- watcherTimer.Start();
-
- // default state
- this.HintActionButton.Visibility = Visibility.Visible;
-
- this.HintTitle.Text = Properties.Resources.Hint_MSIClawCenterCheck;
- this.HintDescription.Text = Properties.Resources.Hint_MSIClawCenterCheckDesc;
- this.HintReadMe.Text = Properties.Resources.Hint_MSIClawCenterCheckReadme;
-
- this.HintActionButton.Content = Properties.Resources.Hint_MSIClawCenterCheckAction;
- }
-
- private TaskScheduled GetTask()
- {
- using (TaskService taskService = new TaskService())
- {
- TaskScheduled task = taskService.GetTask(TaskName);
- return task;
- }
- }
-
- private bool HasTask()
- {
- TaskScheduled task = GetTask();
- if (task is null)
- return false;
-
- return task.Enabled;
- }
-
- private void KillTask()
- {
- TaskScheduled task = GetTask();
- if (task != null)
- task.Enabled = false;
- }
-
- private IEnumerable GetProcesses()
- {
- Process[] processes = Process.GetProcesses();
- foreach (Process process in processes)
- {
- if (process.ProcessName.StartsWith(TaskName, StringComparison.InvariantCultureIgnoreCase))
- yield return process;
-
- if (process.ProcessName.Contains(UIname, StringComparison.InvariantCultureIgnoreCase))
- yield return process;
- }
- }
-
- private bool HasProcesses()
- {
- return GetProcesses().Any();
- }
-
- private void KillProcesses()
- {
- foreach (Process process in GetProcesses())
- process.Kill();
- }
-
- private void WatcherTimer_Elapsed(object? sender, ElapsedEventArgs e)
- {
- bool hasTask = HasTask();
- bool hasProcesses = HasProcesses();
-
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- this.Visibility = hasTask || hasProcesses ? Visibility.Visible : Visibility.Collapsed;
- });
- }
-
- protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
- {
- KillTask();
- KillProcesses();
- }
-
- public override void Stop()
- {
- watcherTimer?.Stop();
- base.Stop();
- }
- }
-}
+using HandheldCompanion.Devices;
+using Microsoft.Win32.TaskScheduler;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Timers;
+using System.Windows;
+using TaskScheduled = Microsoft.Win32.TaskScheduler.Task;
+
+namespace HandheldCompanion.Controls.Hints
+{
+ public class Hint_MSIClawCenter : IHint
+ {
+ private List serviceNames = new()
+ {
+ "ArmouryCrateSEService",
+ "AsusAppService",
+ "ArmouryCrateControlInterface",
+ };
+
+ private Timer watcherTimer;
+ private const string TaskName = "MSI_Center_M_Server";
+ private const string UIname = "MSI Center M";
+
+ public Hint_MSIClawCenter() : base()
+ {
+ if (IDevice.GetCurrent() is not ClawA1M)
+ return;
+
+ // we'll be checking MSI Claw Task every 4 seconds
+ watcherTimer = new Timer(4000);
+ watcherTimer.Elapsed += WatcherTimer_Elapsed;
+ watcherTimer.Start();
+
+ // default state
+ this.HintActionButton.Visibility = Visibility.Visible;
+
+ this.HintTitle.Text = Properties.Resources.Hint_MSIClawCenterCheck;
+ this.HintDescription.Text = Properties.Resources.Hint_MSIClawCenterCheckDesc;
+ this.HintReadMe.Text = Properties.Resources.Hint_MSIClawCenterCheckReadme;
+
+ this.HintActionButton.Content = Properties.Resources.Hint_MSIClawCenterCheckAction;
+ }
+
+ private TaskScheduled GetTask()
+ {
+ using (TaskService taskService = new TaskService())
+ {
+ TaskScheduled task = taskService.GetTask(TaskName);
+ return task;
+ }
+ }
+
+ private bool HasTask()
+ {
+ TaskScheduled task = GetTask();
+ if (task is null)
+ return false;
+
+ return task.Enabled;
+ }
+
+ private void KillTask()
+ {
+ TaskScheduled task = GetTask();
+ if (task != null)
+ task.Enabled = false;
+ }
+
+ private IEnumerable GetProcesses()
+ {
+ Process[] processes = Process.GetProcesses();
+ foreach (Process process in processes)
+ {
+ if (process.ProcessName.StartsWith(TaskName, StringComparison.InvariantCultureIgnoreCase))
+ yield return process;
+
+ if (process.ProcessName.Contains(UIname, StringComparison.InvariantCultureIgnoreCase))
+ yield return process;
+ }
+ }
+
+ private bool HasProcesses()
+ {
+ return GetProcesses().Any();
+ }
+
+ private void KillProcesses()
+ {
+ foreach (Process process in GetProcesses())
+ process.Kill();
+ }
+
+ private void WatcherTimer_Elapsed(object? sender, ElapsedEventArgs e)
+ {
+ bool hasTask = HasTask();
+ bool hasProcesses = HasProcesses();
+
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ this.Visibility = hasTask || hasProcesses ? Visibility.Visible : Visibility.Collapsed;
+ });
+ }
+
+ protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
+ {
+ KillTask();
+ KillProcesses();
+ }
+
+ public override void Stop()
+ {
+ watcherTimer?.Stop();
+ base.Stop();
+ }
+ }
+}
diff --git a/HandheldCompanion/Controls/Hints/Hint_RogAllyServiceCheck.cs b/HandheldCompanion/Controls/Hints/Hint_RogAllyServiceCheck.cs
index c0a1a22ae..c77a8e6e9 100644
--- a/HandheldCompanion/Controls/Hints/Hint_RogAllyServiceCheck.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_RogAllyServiceCheck.cs
@@ -1,108 +1,108 @@
-using HandheldCompanion.Devices;
-using HandheldCompanion.Utils;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.ServiceProcess;
-using System.Threading.Tasks;
-using System.Timers;
-using System.Windows;
-
-namespace HandheldCompanion.Controls.Hints
-{
- public class Hint_RogAllyServiceCheck : IHint
- {
- private List serviceNames = new()
- {
- "ArmouryCrateSEService",
- "AsusAppService",
- "ArmouryCrateControlInterface",
- };
-
- private List serviceControllers = new();
- private Timer serviceTimer;
-
- public Hint_RogAllyServiceCheck() : base()
- {
- if (IDevice.GetCurrent() is not ROGAlly)
- return;
-
- // Get all the services installed on the local computer
- ServiceController[] services = ServiceController.GetServices();
- foreach (string serviceName in serviceNames)
- {
- if (services.Any(s => serviceNames.Contains(s.ServiceName)))
- {
- // Create a service controller object for the specified service
- ServiceController serviceController = new ServiceController(serviceName);
- serviceControllers.Add(serviceController);
- }
- }
-
- // Check if any of the services in the list exist
- if (!serviceControllers.Any())
- return;
-
- serviceTimer = new Timer(4000);
- serviceTimer.Elapsed += ServiceTimer_Elapsed;
- serviceTimer.Start();
-
- // default state
- this.HintActionButton.Visibility = Visibility.Visible;
-
- this.HintTitle.Text = Properties.Resources.Hint_RogAllyServiceCheck;
- this.HintDescription.Text = Properties.Resources.Hint_RogAllyServiceCheckDesc;
- this.HintReadMe.Text = Properties.Resources.Hint_RogAllyServiceCheckReadme;
-
- this.HintActionButton.Content = Properties.Resources.Hint_RogAllyServiceCheckAction;
- }
-
- private void ServiceTimer_Elapsed(object? sender, ElapsedEventArgs e)
- {
- if (!serviceControllers.Any())
- return;
-
- // Check if any of the services in the list exist and are running
- bool anyRunning = false;
-
- foreach (ServiceController serviceController in serviceControllers)
- {
- serviceController.Refresh();
- if (serviceController.Status == ServiceControllerStatus.Running)
- {
- anyRunning = true;
- break;
- }
- }
-
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- this.Visibility = anyRunning ? Visibility.Visible : Visibility.Collapsed;
- });
- }
-
- protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
- {
- if (!serviceControllers.Any())
- return;
-
- Task.Run(async () =>
- {
- foreach (ServiceController serviceController in serviceControllers)
- {
- if (serviceController.Status == ServiceControllerStatus.Running)
- serviceController.Stop();
- serviceController.WaitForStatus(ServiceControllerStatus.Stopped);
- ServiceUtils.ChangeStartMode(serviceController, ServiceStartMode.Disabled, out _);
- }
- });
- }
-
- public override void Stop()
- {
- serviceTimer?.Stop();
- base.Stop();
- }
- }
-}
+using HandheldCompanion.Devices;
+using HandheldCompanion.Utils;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.ServiceProcess;
+using System.Threading.Tasks;
+using System.Timers;
+using System.Windows;
+
+namespace HandheldCompanion.Controls.Hints
+{
+ public class Hint_RogAllyServiceCheck : IHint
+ {
+ private List serviceNames = new()
+ {
+ "ArmouryCrateSEService",
+ "AsusAppService",
+ "ArmouryCrateControlInterface",
+ };
+
+ private List serviceControllers = new();
+ private Timer serviceTimer;
+
+ public Hint_RogAllyServiceCheck() : base()
+ {
+ if (IDevice.GetCurrent() is not ROGAlly)
+ return;
+
+ // Get all the services installed on the local computer
+ ServiceController[] services = ServiceController.GetServices();
+ foreach (string serviceName in serviceNames)
+ {
+ if (services.Any(s => serviceNames.Contains(s.ServiceName)))
+ {
+ // Create a service controller object for the specified service
+ ServiceController serviceController = new ServiceController(serviceName);
+ serviceControllers.Add(serviceController);
+ }
+ }
+
+ // Check if any of the services in the list exist
+ if (!serviceControllers.Any())
+ return;
+
+ serviceTimer = new Timer(4000);
+ serviceTimer.Elapsed += ServiceTimer_Elapsed;
+ serviceTimer.Start();
+
+ // default state
+ this.HintActionButton.Visibility = Visibility.Visible;
+
+ this.HintTitle.Text = Properties.Resources.Hint_RogAllyServiceCheck;
+ this.HintDescription.Text = Properties.Resources.Hint_RogAllyServiceCheckDesc;
+ this.HintReadMe.Text = Properties.Resources.Hint_RogAllyServiceCheckReadme;
+
+ this.HintActionButton.Content = Properties.Resources.Hint_RogAllyServiceCheckAction;
+ }
+
+ private void ServiceTimer_Elapsed(object? sender, ElapsedEventArgs e)
+ {
+ if (!serviceControllers.Any())
+ return;
+
+ // Check if any of the services in the list exist and are running
+ bool anyRunning = false;
+
+ foreach (ServiceController serviceController in serviceControllers)
+ {
+ serviceController.Refresh();
+ if (serviceController.Status == ServiceControllerStatus.Running)
+ {
+ anyRunning = true;
+ break;
+ }
+ }
+
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ this.Visibility = anyRunning ? Visibility.Visible : Visibility.Collapsed;
+ });
+ }
+
+ protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
+ {
+ if (!serviceControllers.Any())
+ return;
+
+ Task.Run(async () =>
+ {
+ foreach (ServiceController serviceController in serviceControllers)
+ {
+ if (serviceController.Status == ServiceControllerStatus.Running)
+ serviceController.Stop();
+ serviceController.WaitForStatus(ServiceControllerStatus.Stopped);
+ ServiceUtils.ChangeStartMode(serviceController, ServiceStartMode.Disabled, out _);
+ }
+ });
+ }
+
+ public override void Stop()
+ {
+ serviceTimer?.Stop();
+ base.Stop();
+ }
+ }
+}
diff --git a/HandheldCompanion/Controls/Hints/Hint_SteamNeptuneDesktop.cs b/HandheldCompanion/Controls/Hints/Hint_SteamNeptuneDesktop.cs
index a163d929c..00a9bc9c9 100644
--- a/HandheldCompanion/Controls/Hints/Hint_SteamNeptuneDesktop.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_SteamNeptuneDesktop.cs
@@ -1,70 +1,70 @@
-using HandheldCompanion.Managers;
-using HandheldCompanion.Platforms;
-using System;
-using System.Threading.Tasks;
-using System.Windows;
-
-namespace HandheldCompanion.Controls.Hints
-{
- public class Hint_SteamNeptuneDesktop : IHint
- {
- public Hint_SteamNeptuneDesktop() : base()
- {
- PlatformManager.Steam.Updated += Steam_Updated;
- PlatformManager.Initialized += PlatformManager_Initialized;
-
- // default state
- this.HintActionButton.Visibility = Visibility.Visible;
-
- this.HintTitle.Text = Properties.Resources.Hint_SteamNeptuneDesktop;
- this.HintDescription.Text = Properties.Resources.Hint_SteamNeptuneDesktopDesc;
- this.HintReadMe.Text = Properties.Resources.Hint_SteamNeptuneReadme;
-
- this.HintActionButton.Content = Properties.Resources.Hint_SteamNeptuneAction;
- }
-
- private void Steam_Updated(PlatformStatus status)
- {
- bool DesktopProfileApplied = PlatformManager.Steam.HasDesktopProfileApplied();
-
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- switch (status)
- {
- default:
- case PlatformStatus.Stopping:
- case PlatformStatus.Stopped:
- this.Visibility = Visibility.Collapsed;
- break;
- case PlatformStatus.Started:
- this.Visibility = DesktopProfileApplied ? Visibility.Visible : Visibility.Collapsed;
- break;
- }
- });
- }
-
- private void PlatformManager_Initialized()
- {
- Steam_Updated(PlatformManager.Steam.Status);
- }
-
- protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
- {
- Task.Run(async () =>
- {
- PlatformManager.Steam.StopProcess();
-
- while (PlatformManager.Steam.IsRunning)
- await Task.Delay(1000);
-
- PlatformManager.Steam.StartProcess();
- });
- }
-
- public override void Stop()
- {
- base.Stop();
- }
- }
-}
+using HandheldCompanion.Managers;
+using HandheldCompanion.Platforms;
+using System;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace HandheldCompanion.Controls.Hints
+{
+ public class Hint_SteamNeptuneDesktop : IHint
+ {
+ public Hint_SteamNeptuneDesktop() : base()
+ {
+ PlatformManager.Steam.Updated += Steam_Updated;
+ PlatformManager.Initialized += PlatformManager_Initialized;
+
+ // default state
+ this.HintActionButton.Visibility = Visibility.Visible;
+
+ this.HintTitle.Text = Properties.Resources.Hint_SteamNeptuneDesktop;
+ this.HintDescription.Text = Properties.Resources.Hint_SteamNeptuneDesktopDesc;
+ this.HintReadMe.Text = Properties.Resources.Hint_SteamNeptuneReadme;
+
+ this.HintActionButton.Content = Properties.Resources.Hint_SteamNeptuneAction;
+ }
+
+ private void Steam_Updated(PlatformStatus status)
+ {
+ bool DesktopProfileApplied = PlatformManager.Steam.HasDesktopProfileApplied();
+
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ switch (status)
+ {
+ default:
+ case PlatformStatus.Stopping:
+ case PlatformStatus.Stopped:
+ this.Visibility = Visibility.Collapsed;
+ break;
+ case PlatformStatus.Started:
+ this.Visibility = DesktopProfileApplied ? Visibility.Visible : Visibility.Collapsed;
+ break;
+ }
+ });
+ }
+
+ private void PlatformManager_Initialized()
+ {
+ Steam_Updated(PlatformManager.Steam.Status);
+ }
+
+ protected override void HintActionButton_Click(object sender, RoutedEventArgs e)
+ {
+ Task.Run(async () =>
+ {
+ PlatformManager.Steam.StopProcess();
+
+ while (PlatformManager.Steam.IsRunning)
+ await Task.Delay(1000);
+
+ PlatformManager.Steam.StartProcess();
+ });
+ }
+
+ public override void Stop()
+ {
+ base.Stop();
+ }
+ }
+}
diff --git a/HandheldCompanion/Controls/Hints/Hint_SteamXboxDrivers.cs b/HandheldCompanion/Controls/Hints/Hint_SteamXboxDrivers.cs
index 7676059b3..9da43cd4a 100644
--- a/HandheldCompanion/Controls/Hints/Hint_SteamXboxDrivers.cs
+++ b/HandheldCompanion/Controls/Hints/Hint_SteamXboxDrivers.cs
@@ -1,48 +1,48 @@
-using HandheldCompanion.Managers;
-using HandheldCompanion.Platforms;
-using System.Windows;
-
-namespace HandheldCompanion.Controls.Hints
-{
- public class Hint_SteamXboxDrivers : IHint
- {
- public Hint_SteamXboxDrivers() : base()
- {
- PlatformManager.Steam.Updated += Steam_Updated;
- PlatformManager.Initialized += PlatformManager_Initialized;
-
- // default state
- this.HintActionButton.Visibility = Visibility.Collapsed;
-
- this.HintTitle.Text = Properties.Resources.Hint_SteamXboxDrivers;
- this.HintDescription.Text = Properties.Resources.Hint_SteamXboxDriversDesc;
- this.HintReadMe.Text = Properties.Resources.Hint_SteamXboxDriversReadme;
- }
-
- private void Steam_Updated(PlatformStatus status)
- {
- CheckDrivers();
- }
-
- private void PlatformManager_Initialized()
- {
- CheckDrivers();
- }
-
- private void CheckDrivers()
- {
- bool HasXboxDriversInstalled = PlatformManager.Steam.HasXboxDriversInstalled();
-
- // UI thread (async)
- Application.Current.Dispatcher.Invoke(() =>
- {
- this.Visibility = HasXboxDriversInstalled ? Visibility.Visible : Visibility.Collapsed;
- });
- }
-
- public override void Stop()
- {
- base.Stop();
- }
- }
-}
+using HandheldCompanion.Managers;
+using HandheldCompanion.Platforms;
+using System.Windows;
+
+namespace HandheldCompanion.Controls.Hints
+{
+ public class Hint_SteamXboxDrivers : IHint
+ {
+ public Hint_SteamXboxDrivers() : base()
+ {
+ PlatformManager.Steam.Updated += Steam_Updated;
+ PlatformManager.Initialized += PlatformManager_Initialized;
+
+ // default state
+ this.HintActionButton.Visibility = Visibility.Collapsed;
+
+ this.HintTitle.Text = Properties.Resources.Hint_SteamXboxDrivers;
+ this.HintDescription.Text = Properties.Resources.Hint_SteamXboxDriversDesc;
+ this.HintReadMe.Text = Properties.Resources.Hint_SteamXboxDriversReadme;
+ }
+
+ private void Steam_Updated(PlatformStatus status)
+ {
+ CheckDrivers();
+ }
+
+ private void PlatformManager_Initialized()
+ {
+ CheckDrivers();
+ }
+
+ private void CheckDrivers()
+ {
+ bool HasXboxDriversInstalled = PlatformManager.Steam.HasXboxDriversInstalled();
+
+ // UI thread (async)
+ Application.Current.Dispatcher.Invoke(() =>
+ {
+ this.Visibility = HasXboxDriversInstalled ? Visibility.Visible : Visibility.Collapsed;
+ });
+ }
+
+ public override void Stop()
+ {
+ base.Stop();
+ }
+ }
+}
diff --git a/HandheldCompanion/Controls/Hints/IHint.xaml b/HandheldCompanion/Controls/Hints/IHint.xaml
index b93cf86f0..b5a0c4f01 100644
--- a/HandheldCompanion/Controls/Hints/IHint.xaml
+++ b/HandheldCompanion/Controls/Hints/IHint.xaml
@@ -1,61 +1,61 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/HandheldCompanion/Controls/Hints/IHint.xaml.cs b/HandheldCompanion/Controls/Hints/IHint.xaml.cs
index b92cbf8a5..b918affdd 100644
--- a/HandheldCompanion/Controls/Hints/IHint.xaml.cs
+++ b/HandheldCompanion/Controls/Hints/IHint.xaml.cs
@@ -1,22 +1,22 @@
-using System.Windows;
-using System.Windows.Controls;
-
-namespace HandheldCompanion.Controls.Hints
-{
- ///
- /// Interaction logic for IHint.xaml
- ///
- public partial class IHint : UserControl
- {
- public IHint()
- {
- InitializeComponent();
- }
-
- protected virtual void HintActionButton_Click(object sender, RoutedEventArgs e)
- { }
-
- public virtual void Stop()
- { }
- }
-}
+using System.Windows;
+using System.Windows.Controls;
+
+namespace HandheldCompanion.Controls.Hints
+{
+ ///
+ /// Interaction logic for IHint.xaml
+ ///
+ public partial class IHint : UserControl
+ {
+ public IHint()
+ {
+ InitializeComponent();
+ }
+
+ protected virtual void HintActionButton_Click(object sender, RoutedEventArgs e)
+ { }
+
+ public virtual void Stop()
+ { }
+ }
+}
diff --git a/HandheldCompanion/Controls/Mapping/AxisMapping.xaml b/HandheldCompanion/Controls/Mapping/AxisMapping.xaml
deleted file mode 100644
index b959c4cb9..000000000
--- a/HandheldCompanion/Controls/Mapping/AxisMapping.xaml
+++ /dev/null
@@ -1,624 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/HandheldCompanion/Controls/Mapping/AxisMapping.xaml.cs b/HandheldCompanion/Controls/Mapping/AxisMapping.xaml.cs
deleted file mode 100644
index 17ca8a0ae..000000000
--- a/HandheldCompanion/Controls/Mapping/AxisMapping.xaml.cs
+++ /dev/null
@@ -1,389 +0,0 @@
-using HandheldCompanion.Actions;
-using HandheldCompanion.Controllers;
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Managers;
-using HandheldCompanion.Utils;
-using iNKORE.UI.WPF.Modern.Controls;
-using System;
-using System.Windows;
-using System.Windows.Controls;
-
-namespace HandheldCompanion.Controls;
-
-///
-/// Interaction logic for AxisMapping.xaml
-///
-public partial class AxisMapping : IMapping
-{
- public AxisMapping()
- {
- InitializeComponent();
- }
-
- public AxisMapping(AxisLayoutFlags axis) : this()
- {
- Value = axis;
-
- Icon.Glyph = axis.ToString();
- }
-
- public void UpdateIcon(FontIcon newIcon, string newLabel)
- {
- Name.Text = newLabel;
-
- Icon.Glyph = newIcon.Glyph;
- Icon.FontFamily = newIcon.FontFamily;
- Icon.FontSize = newIcon.FontSize;
-
- if (newIcon.Foreground is not null)
- Icon.Foreground = newIcon.Foreground;
- else
- Icon.SetResourceReference(ForegroundProperty, "SystemControlForegroundBaseMediumBrush");
- }
-
- public void UpdateSelections()
- {
- Action_SelectionChanged(null, null);
- }
-
- internal void SetIActions(IActions actions)
- {
- // reset and update mapping IActions
- Reset();
- base.SetIActions(actions);
-
- // update UI
- ActionComboBox.SelectedIndex = (int)actions.actionType;
- }
-
- private void Action_SelectionChanged(object sender, SelectionChangedEventArgs e)
- {
- if (ActionComboBox.SelectedItem is null)
- return;
-
- // we're not ready yet
- if (TargetComboBox is null)
- return;
-
- // clear current dropdown values
- TargetComboBox.Items.Clear();
- TargetComboBox.IsEnabled = ActionComboBox.SelectedIndex != 0;
-
- // get current controller
- IController? controller = ControllerManager.GetEmulatedController();
-
- // populate target dropdown based on action type
- ActionType type = (ActionType)ActionComboBox.SelectedIndex;
-
- if (type == ActionType.Disabled)
- {
- if (Actions is not null)
- Delete();
- return;
- }
-
- if (type == ActionType.Joystick)
- {
- if (Actions is null || Actions is not AxisActions)
- Actions = new AxisActions();
-
- // we need a controller to get compatible buttons
- if (controller is null)
- return;
-
- foreach (AxisLayoutFlags axis in IController.GetTargetAxis())
- {
- // create a label, store ButtonFlags as Tag and Label as controller specific string
- Label buttonLabel = new Label { Tag = axis, Content = controller.GetAxisName(axis) };
- TargetComboBox.Items.Add(buttonLabel);
-
- if (axis.Equals(((AxisActions)Actions).Axis))
- TargetComboBox.SelectedItem = buttonLabel;
- }
-
- // settings
- Axis2AxisImproveCircularity.IsOn = ((AxisActions)Actions).ImproveCircularity;
- Axis2AxisAutoRotate.IsOn = ((AxisActions)Actions).AutoRotate;
- Axis2AxisRotation.Value = (((AxisActions)Actions).AxisInverted ? 180 : 0) +
- (((AxisActions)Actions).AxisRotated ? 90 : 0);
- Axis2AxisInnerDeadzone.Value = ((AxisActions)Actions).AxisDeadZoneInner;
- Axis2AxisOuterDeadzone.Value = ((AxisActions)Actions).AxisDeadZoneOuter;
- Axis2AxisAntiDeadzone.Value = ((AxisActions)Actions).AxisAntiDeadZone;
- }
- else if (type == ActionType.Mouse)
- {
- if (Actions is null || Actions is not MouseActions)
- Actions = new MouseActions();
-
- foreach (MouseActionsType mouseType in Enum.GetValues(typeof(MouseActionsType)))
- {
- // skip specific scenarios
- switch (mouseType)
- {
- case MouseActionsType.LeftButton:
- case MouseActionsType.RightButton:
- case MouseActionsType.MiddleButton:
- case MouseActionsType.ScrollUp:
- case MouseActionsType.ScrollDown:
- continue;
- }
-
- // create a label, store MouseActionsType as Tag and Label as controller specific string
- Label buttonLabel = new Label { Tag = mouseType, Content = EnumUtils.GetDescriptionFromEnumValue(mouseType) };
- TargetComboBox.Items.Add(buttonLabel);
-
- if (mouseType.Equals(((MouseActions)Actions).MouseType))
- TargetComboBox.SelectedItem = buttonLabel;
- }
-
- // settings
- Axis2MousePointerSpeed.Value = ((MouseActions)Actions).Sensivity;
- Axis2MouseAutoRotate.IsOn = ((MouseActions)Actions).AutoRotate;
- Axis2MouseRotation.Value = (((MouseActions)Actions).AxisInverted ? 180 : 0) +
- (((MouseActions)Actions).AxisRotated ? 90 : 0);
- Axis2MouseDeadzone.Value = ((MouseActions)Actions).Deadzone;
- Axis2MouseAcceleration.Value = ((MouseActions)this.Actions).Acceleration;
- Axis2MouseFiltering.IsOn = ((MouseActions)this.Actions).Filtering;
- Axis2MouseFilterCutoff.Value = ((MouseActions)this.Actions).FilterCutoff;
- }
-
- // if no target element was selected, pick the first one
- if (TargetComboBox.SelectedItem is null)
- TargetComboBox.SelectedIndex = 0;
-
- base.Update();
- }
-
- private void Target_SelectionChanged(object sender, SelectionChangedEventArgs e)
- {
- if (Actions is null)
- return;
-
- if (TargetComboBox.SelectedItem is null)
- return;
-
- // generate IActions based on settings
- switch (Actions.actionType)
- {
- case ActionType.Joystick:
- {
- var buttonLabel = TargetComboBox.SelectedItem as Label;
- ((AxisActions)Actions).Axis = (AxisLayoutFlags)buttonLabel.Tag;
- }
- break;
-
- case ActionType.Mouse:
- {
- var buttonLabel = TargetComboBox.SelectedItem as Label;
- ((MouseActions)Actions).MouseType = (MouseActionsType)buttonLabel.Tag;
- }
- break;
- }
-
- base.Update();
- }
-
- public void Reset()
- {
- ActionComboBox.SelectedIndex = 0;
- TargetComboBox.SelectedItem = null;
- }
-
- private void Axis2AxisAutoRotate_Toggled(object sender, RoutedEventArgs e)
- {
- if (Actions is null)
- return;
-
- switch (Actions.actionType)
- {
- case ActionType.Joystick:
- ((AxisActions)Actions).AutoRotate = Axis2AxisAutoRotate.IsOn;
- break;
- }
-
- base.Update();
- }
-
- private void Axis_Rotation_Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
- {
- if (Actions is null)
- return;
-
- switch (Actions.actionType)
- {
- case ActionType.Joystick:
- ((AxisActions)Actions).AxisInverted = (((int)Axis2AxisRotation.Value / 90) & 2) == 2;
- ((AxisActions)Actions).AxisRotated = (((int)Axis2AxisRotation.Value / 90) & 1) == 1;
- break;
- }
-
- base.Update();
- }
-
- private void Axis_InnerDeadzone_Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
- {
- if (Actions is null)
- return;
-
- switch (Actions.actionType)
- {
- case ActionType.Joystick:
- ((AxisActions)Actions).AxisDeadZoneInner = (int)Axis2AxisInnerDeadzone.Value;
- break;
- }
-
- base.Update();
- }
-
- private void Axis_OuterDeadzone_Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
- {
- if (Actions is null)
- return;
-
- switch (Actions.actionType)
- {
- case ActionType.Joystick:
- ((AxisActions)Actions).AxisDeadZoneOuter = (int)Axis2AxisOuterDeadzone.Value;
- break;
- }
-
- base.Update();
- }
-
- private void Axis_AntiDeadZone_Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
- {
- if (Actions is null)
- return;
-
- switch (Actions.actionType)
- {
- case ActionType.Joystick:
- ((AxisActions)Actions).AxisAntiDeadZone = (int)Axis2AxisAntiDeadzone.Value;
- break;
- }
-
- base.Update();
- }
-
- private void Axis2AxisImproveCircularity_Toggled(object sender, RoutedEventArgs e)
- {
- if (Actions is null)
- return;
-
- switch (Actions.actionType)
- {
- case ActionType.Joystick:
- ((AxisActions)Actions).ImproveCircularity = Axis2AxisImproveCircularity.IsOn;
- break;
- }
-
- base.Update();
- }
-
- private void Axis2MousePointerSpeed_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
- {
- if (Actions is null)
- return;
-
- switch (Actions.actionType)
- {
- case ActionType.Mouse:
- ((MouseActions)Actions).Sensivity = (int)Axis2MousePointerSpeed.Value;
- break;
- }
-
- base.Update();
- }
-
- private void Axis2MouseAutoRotate_Toggled(object sender, RoutedEventArgs e)
- {
- if (Actions is null)
- return;
-
- switch (Actions.actionType)
- {
- case ActionType.Mouse:
- ((MouseActions)Actions).AutoRotate = Axis2MouseAutoRotate.IsOn;
- break;
- }
-
- base.Update();
- }
-
- private void Axis2MouseRotation_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
- {
- if (Actions is null)
- return;
-
- switch (Actions.actionType)
- {
- case ActionType.Mouse:
- ((MouseActions)Actions).AxisInverted = (((int)Axis2MouseRotation.Value / 90) & 2) == 2;
- ((MouseActions)Actions).AxisRotated = (((int)Axis2MouseRotation.Value / 90) & 1) == 1;
- break;
- }
-
- base.Update();
- }
-
- private void Axis2MouseDeadzone_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
- {
- if (Actions is null)
- return;
-
- switch (Actions.actionType)
- {
- case ActionType.Mouse:
- ((MouseActions)Actions).Deadzone = (int)Axis2MouseDeadzone.Value;
- break;
- }
-
- base.Update();
- }
-
- private void Axis2MouseAcceleration_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
- {
- if (this.Actions is null)
- return;
-
- switch (this.Actions.actionType)
- {
- case ActionType.Mouse:
- ((MouseActions)this.Actions).Acceleration = (float)Axis2MouseAcceleration.Value;
- break;
- }
-
- base.Update();
- }
-
- // TODO: artificially convert to something more human readable?
- private void Axis2MouseFiltering_Toggled(object sender, RoutedEventArgs e)
- {
- if (this.Actions is null)
- return;
-
- switch (this.Actions.actionType)
- {
- case ActionType.Mouse:
- ((MouseActions)this.Actions).Filtering = Axis2MouseFiltering.IsOn;
- break;
- }
-
- base.Update();
- }
-
- private void Axis2MouseFilterCutoff_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
- {
- if (this.Actions is null)
- return;
-
- switch (this.Actions.actionType)
- {
- case ActionType.Mouse:
- ((MouseActions)this.Actions).FilterCutoff = (float)Axis2MouseFilterCutoff.Value;
- break;
- }
-
- base.Update();
- }
-}
\ No newline at end of file
diff --git a/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml b/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml
deleted file mode 100644
index 075697336..000000000
--- a/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml
+++ /dev/null
@@ -1,370 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml.cs b/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml.cs
deleted file mode 100644
index 828e32269..000000000
--- a/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml.cs
+++ /dev/null
@@ -1,332 +0,0 @@
-using GregsStack.InputSimulatorStandard.Native;
-using HandheldCompanion.Actions;
-using HandheldCompanion.Controllers;
-using HandheldCompanion.Inputs;
-using HandheldCompanion.Managers;
-using HandheldCompanion.Utils;
-using iNKORE.UI.WPF.Modern.Controls;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Windows;
-using System.Windows.Controls;
-
-namespace HandheldCompanion.Controls;
-
-///
-/// Interaction logic for ButtonMapping.xaml
-///
-public partial class ButtonMapping : IMapping
-{
- private static List