diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 0a22a429f..7933b35bd 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -14,6 +14,7 @@ labels: bug - [ ] GPD - [ ] ONEXPLAYER - [ ] VALVE +- [ ] LENOVO **Device model** Your device model diff --git a/.github/workflows/publish-hc.yml b/.github/workflows/publish-hc.yml index 9ae122118..d9fb469cb 100644 --- a/.github/workflows/publish-hc.yml +++ b/.github/workflows/publish-hc.yml @@ -1,106 +1,110 @@ -name: Build and Release HandheldCompanion - -on: - pull_request: - branches: - - main - workflow_dispatch: - inputs: - releaseVersion: - description: 'Release version to create' - required: false - type: string - -defaults: - run: - shell: pwsh - -permissions: - actions: write - checks: write - contents: write - deployments: none - id-token: none - issues: none - discussions: none - packages: none - pages: none - pull-requests: write - repository-projects: none - security-events: none - statuses: none - -jobs: - build: - name: Build and Release - runs-on: windows-latest - - env: - SOLUTION_NAME: HandheldCompanion - INNO_VERSION: 6.2.2 - - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Validate Release - if: inputs.releaseVersion != '' - run: | - if ('${{ inputs.releaseVersion }}' -notmatch '^\d+(?:\.\d+){3}$') { - Write-Error "Version ${{ inputs.releaseVersion }} is not valid, please use format Major.Minor.Revision.Build" - Exit 1 - } - - if ('${{ github.ref }}' -ne 'refs/heads/main') { - Write-Error "Release may only be created from main" - Exit 1 - } - - # This step sets release version in source control. Uses pwsh for lack of an available github action. - - name: Set Release Version in Repo - if: inputs.releaseVersion != '' - run: | - (Get-Content -Path ./HandheldCompanion.iss) -replace "#define MyAppVersion `'\d+(?:\.\d+){3}`'", "#define MyAppVersion '${{ inputs.releaseVersion }}'" | Out-File ./HandheldCompanion.iss - (Get-Content -Path ./HandheldCompanion/HandheldCompanion.csproj) -replace "<Version>\d+(?:\.\d+){3}</Version>", "<Version>${{ inputs.releaseVersion }}</Version>" | Out-File ./HandheldCompanion/HandheldCompanion.csproj - - - name: Install Innosetup - run: | - choco install innosetup --version=${{ env.INNO_VERSION }} --force - - - name: Setup dotnet 8 SDK - uses: actions/setup-dotnet@v3 - with: - dotnet-version: '8.x' - - - name: Restore Solution - run: dotnet restore ${{ env.SOLUTION_NAME }}.sln - - - name: Build Solution - run: dotnet build ${{ env.SOLUTION_NAME }}.sln /p:Configuration=Release - - - name: Create Installer - run: | - iscc.exe ${{ env.SOLUTION_NAME }}.iss - - - name: Create Release Pull Request - if: inputs.releaseVersion != '' - uses: peter-evans/create-pull-request@v5 - with: - branch: release/${{ inputs.releaseVersion }} - title: 'Release ${{ inputs.releaseVersion }}' - commit-message: 'Release ${{ inputs.releaseVersion }}' - body: 'Release ${{ inputs.releaseVersion }}. Triggered by ${{ github.actor }}' - - - name: Create Release - if: inputs.releaseVersion != '' - uses: softprops/action-gh-release@v0.1.15 - with: - body_path: .github/workflows/RELEASE_FORMAT.txt - tag_name: ${{ inputs.releaseVersion }} - name: "Build ${{ inputs.releaseVersion }}" - draft: true - fail_on_unmatched_files: true - files: | - ./install/HandheldCompanion-${{ inputs.releaseVersion }}.exe \ No newline at end of file +name: Build and Release HandheldCompanion + +on: + pull_request: + branches: + - main + workflow_dispatch: + inputs: + releaseVersion: + description: 'Release version to create' + required: false + type: string + +defaults: + run: + shell: pwsh + +permissions: + actions: write + checks: write + contents: write + deployments: none + id-token: none + issues: none + discussions: none + packages: none + pages: none + pull-requests: write + repository-projects: none + security-events: none + statuses: none + +jobs: + build: + name: Build and Release + runs-on: windows-latest + + env: + SOLUTION_NAME: HandheldCompanion + INNO_VERSION: 6.2.2 + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Validate Release + if: inputs.releaseVersion != '' + run: | + if ('${{ inputs.releaseVersion }}' -notmatch '^\d+(?:\.\d+){3}$') { + Write-Error "Version ${{ inputs.releaseVersion }} is not valid, please use format Major.Minor.Revision.Build" + Exit 1 + } + + if ('${{ github.ref }}' -ne 'refs/heads/main') { + Write-Error "Release may only be created from main" + Exit 1 + } + + # This step sets release version in source control. Uses pwsh for lack of an available github action. + - name: Set Release Version in Repo + if: inputs.releaseVersion != '' + run: | + (Get-Content -Path ./HandheldCompanion.iss) -replace "#define MyAppVersion `'\d+(?:\.\d+){3}`'", "#define MyAppVersion '${{ inputs.releaseVersion }}'" | Out-File ./HandheldCompanion.iss + (Get-Content -Path ./HandheldCompanion/HandheldCompanion.csproj) -replace "<Version>\d+(?:\.\d+){3}</Version>", "<Version>${{ inputs.releaseVersion }}</Version>" | Out-File ./HandheldCompanion/HandheldCompanion.csproj + + - name: Install Innosetup + run: | + choco install innosetup --version=${{ env.INNO_VERSION }} --force + + - name: Setup dotnet 8 SDK + uses: actions/setup-dotnet@v3 + with: + dotnet-version: '8.x' + + - name: Restore Solution + run: dotnet restore ${{ env.SOLUTION_NAME }}.sln + + - name: Build Solution + run: dotnet build ${{ env.SOLUTION_NAME }}.sln /p:Configuration=Release + + - name: Create Installer + run: | + iscc.exe ${{ env.SOLUTION_NAME }}.iss + + - name: Create Release Pull Request + if: inputs.releaseVersion != '' + uses: peter-evans/create-pull-request@v5 + with: + branch: release/${{ inputs.releaseVersion }} + title: 'Release ${{ inputs.releaseVersion }}' + commit-message: 'Release ${{ inputs.releaseVersion }}' + body: 'Release ${{ inputs.releaseVersion }}. Triggered by ${{ github.actor }}' + + - name: Create Release + if: inputs.releaseVersion != '' + uses: softprops/action-gh-release@v0.1.15 + with: + body_path: .github/workflows/RELEASE_FORMAT.txt + tag_name: ${{ inputs.releaseVersion }} + name: "Build ${{ inputs.releaseVersion }}" + draft: true + fail_on_unmatched_files: true + files: | +<<<<<<< HEAD + ./install/HandheldCompanion-${{ inputs.releaseVersion }}.exe +======= + ./install/HandheldCompanion-${{ inputs.releaseVersion }}.exe +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d diff --git a/HandheldCompanion.iss b/HandheldCompanion.iss index 7e5be6f7b..d252804f4 100644 --- a/HandheldCompanion.iss +++ b/HandheldCompanion.iss @@ -1,597 +1,653 @@ -; ----------- -; CODE -; ----------- -[Code] -// types and variables -type - TDependency_Entry = record - Filename: String; - Parameters: String; - Title: String; - URL: String; - Checksum: String; - ForceSuccess: Boolean; - RestartAfter: Boolean; - end; - -var - Dependency_Memo: String; - Dependency_List: array of TDependency_Entry; - Dependency_NeedRestart, Dependency_ForceX86: Boolean; - Dependency_DownloadPage: TDownloadWizardPage; - -procedure Dependency_Add(const Filename, Parameters, Title, URL, Checksum: String; const ForceSuccess, RestartAfter: Boolean); -var - Dependency: TDependency_Entry; - DependencyCount: Integer; -begin - Dependency_Memo := Dependency_Memo + #13#10 + '%1' + Title; - - Dependency.Filename := Filename; - 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.RestartAfter := RestartAfter; - - DependencyCount := GetArrayLength(Dependency_List); - SetArrayLength(Dependency_List, DependencyCount + 1); - Dependency_List[DependencyCount] := Dependency; -end; - -procedure Dependency_InitializeWizard; -begin - Dependency_DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), nil); -end; - -function Dependency_PrepareToInstall(var NeedsRestart: Boolean): String; -var - DependencyCount, DependencyIndex, ResultCode: Integer; - Retry: Boolean; - TempValue: String; -begin - DependencyCount := GetArrayLength(Dependency_List); - - if DependencyCount > 0 then begin - Dependency_DownloadPage.Show; - - for DependencyIndex := 0 to DependencyCount - 1 do begin - if Dependency_List[DependencyIndex].URL <> '' then begin - Dependency_DownloadPage.Clear; - Dependency_DownloadPage.Add(Dependency_List[DependencyIndex].URL, Dependency_List[DependencyIndex].Filename, Dependency_List[DependencyIndex].Checksum); - - Retry := True; - while Retry do begin - Retry := False; - - try - Dependency_DownloadPage.Download; - except - if Dependency_DownloadPage.AbortedByUser then begin - Result := Dependency_List[DependencyIndex].Title; - DependencyIndex := DependencyCount; - end else begin - case SuppressibleMsgBox(AddPeriod(GetExceptionMessage), mbError, MB_ABORTRETRYIGNORE, IDIGNORE) of - IDABORT: begin - Result := Dependency_List[DependencyIndex].Title; - DependencyIndex := DependencyCount; - end; - IDRETRY: begin - Retry := True; - end; - end; - end; - end; - end; - end; - end; - - if Result = '' then begin - for DependencyIndex := 0 to DependencyCount - 1 do begin - Dependency_DownloadPage.SetText(Dependency_List[DependencyIndex].Title, ''); - 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; - end; - break; - end else if (ResultCode = 0) or Dependency_List[DependencyIndex].ForceSuccess then begin // ERROR_SUCCESS (0) - break; - end else if ResultCode = 1641 then begin // ERROR_SUCCESS_REBOOT_INITIATED (1641) - NeedsRestart := True; - Result := Dependency_List[DependencyIndex].Title; - break; - end else if ResultCode = 3010 then begin // ERROR_SUCCESS_REBOOT_REQUIRED (3010) - Dependency_NeedRestart := True; - break; - end; - end; - - case SuppressibleMsgBox(FmtMessage(SetupMessage(msgErrorFunctionFailed), [Dependency_List[DependencyIndex].Title, IntToStr(ResultCode)]), mbError, MB_ABORTRETRYIGNORE, IDIGNORE) of - IDABORT: begin - Result := Dependency_List[DependencyIndex].Title; - break; - end; - IDIGNORE: begin - break; - end; - end; - end; - - if Result <> '' then begin - break; - end; - end; - - if NeedsRestart then begin - TempValue := '"' + ExpandConstant('{srcexe}') + '" /restart=1 /LANG="' + ExpandConstant('{language}') + '" /DIR="' + WizardDirValue + '" /GROUP="' + WizardGroupValue + '" /TYPE="' + WizardSetupType(False) + '" /COMPONENTS="' + WizardSelectedComponents(False) + '" /TASKS="' + WizardSelectedTasks(False) + '"'; - if WizardNoIcons then begin - TempValue := TempValue + ' /NOICONS'; - end; - RegWriteStringValue(HKA, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', '{#SetupSetting("AppName")}', TempValue); - end; - end; - - Dependency_DownloadPage.Hide; - end; -end; - -function Dependency_UpdateReadyMemo(const Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String; -begin - Result := ''; - if MemoUserInfoInfo <> '' then begin - Result := Result + MemoUserInfoInfo + Newline + NewLine; - end; - if MemoDirInfo <> '' then begin - Result := Result + MemoDirInfo + Newline + NewLine; - end; - if MemoTypeInfo <> '' then begin - Result := Result + MemoTypeInfo + Newline + NewLine; - end; - if MemoComponentsInfo <> '' then begin - Result := Result + MemoComponentsInfo + Newline + NewLine; - end; - if MemoGroupInfo <> '' then begin - Result := Result + MemoGroupInfo + Newline + NewLine; - end; - if MemoTasksInfo <> '' then begin - Result := Result + MemoTasksInfo; - end; - - if Dependency_Memo <> '' then begin - if MemoTasksInfo = '' then begin - Result := Result + SetupMessage(msgReadyMemoTasks); - end; - Result := Result + FmtMessage(Dependency_Memo, [Space]); - end; -end; - -function Dependency_IsX64: Boolean; -begin - Result := not Dependency_ForceX86 and Is64BitInstallMode; -end; - -function Dependency_String(const x86, x64: String): String; -begin - if Dependency_IsX64 then begin - Result := x64; - end else begin - Result := x86; - end; -end; - -function Dependency_ArchSuffix: String; -begin - Result := Dependency_String('', '_x64'); -end; - -function Dependency_ArchTitle: String; -begin - Result := Dependency_String(' (x86)', ' (x64)'); -end; - -function Dependency_IsNetCoreInstalled(const Version: String): Boolean; -var - ResultCode: Integer; -begin - // source code: https://github.com/dotnet/deployment-tools/tree/master/src/clickonce/native/projects/NetCoreCheck - if not FileExists(ExpandConstant('{tmp}{\}') + 'netcorecheck' + Dependency_ArchSuffix + '.exe') then begin - ExtractTemporaryFile('netcorecheck' + Dependency_ArchSuffix + '.exe'); - end; - Result := ShellExec('', ExpandConstant('{tmp}{\}') + 'netcorecheck' + Dependency_ArchSuffix + '.exe', Version, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0); -end; - -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', - '/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); - end; -end; - -procedure Dependency_AddVC2005; -begin - // https://www.microsoft.com/en-US/download/details.aspx?id=26347 - if not IsMsiProductInstalled(Dependency_String('{86C9D5AA-F00C-4921-B3F2-C60AF92E2844}', '{A8D19029-8E5C-4E22-8011-48070F9E796E}'), PackVersionComponents(8, 0, 61000, 0)) then begin - Dependency_Add('vcredist2005' + Dependency_ArchSuffix + '.exe', - '/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); - end; -end; - -procedure Dependency_AddVC2008; -begin - // https://www.microsoft.com/en-US/download/details.aspx?id=26368 - if not IsMsiProductInstalled(Dependency_String('{DE2C306F-A067-38EF-B86C-03DE4B0312F9}', '{FDA45DDF-8E17-336F-A3ED-356B7B7C688A}'), PackVersionComponents(9, 0, 30729, 6161)) then begin - Dependency_Add('vcredist2008' + Dependency_ArchSuffix + '.exe', - '/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); - end; -end; - -procedure Dependency_AddVC2010; -begin - // https://www.microsoft.com/en-US/download/details.aspx?id=26999 - if not IsMsiProductInstalled(Dependency_String('{1F4F1D2A-D9DA-32CF-9909-48485DA06DD5}', '{5B75F761-BAC8-33BC-A381-464DDDD813A3}'), PackVersionComponents(10, 0, 40219, 0)) then begin - Dependency_Add('vcredist2010' + Dependency_ArchSuffix + '.exe', - '/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); - end; -end; - -procedure Dependency_AddVC2012; -begin - // https://www.microsoft.com/en-US/download/details.aspx?id=30679 - if not IsMsiProductInstalled(Dependency_String('{4121ED58-4BD9-3E7B-A8B5-9F8BAAE045B7}', '{EFA6AFA1-738E-3E00-8101-FD03B86B29D1}'), PackVersionComponents(11, 0, 61030, 0)) then begin - Dependency_Add('vcredist2012' + Dependency_ArchSuffix + '.exe', - '/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); - end; -end; - -procedure Dependency_AddVC2013; -begin - // https://support.microsoft.com/en-US/help/4032938 - if not IsMsiProductInstalled(Dependency_String('{B59F5BF1-67C8-3802-8E59-2CE551A39FC5}', '{20400CF0-DE7C-327E-9AE4-F0F38D9085F8}'), PackVersionComponents(12, 0, 40664, 0)) then begin - Dependency_Add('vcredist2013' + Dependency_ArchSuffix + '.exe', - '/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); - end; -end; - -procedure Dependency_AddVC2015To2019; -begin - // https://support.microsoft.com/en-US/help/2977003/the-latest-supported-visual-c-downloads - if not IsMsiProductInstalled(Dependency_String('{65E5BD06-6392-3027-8C26-853107D3CF1A}', '{36F68A90-239C-34DF-B58C-64B30153CE35}'), PackVersionComponents(14, 29, 30037, 0)) then begin - Dependency_Add('vcredist2019' + Dependency_ArchSuffix + '.exe', - '/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); - end; -end; - -procedure Dependency_AddDirectX; -begin - // https://www.microsoft.com/en-US/download/details.aspx?id=35 - Dependency_Add('dxwebsetup.exe', - '/q', - 'DirectX Runtime', - 'https://download.microsoft.com/download/1/7/1/1718CCC4-6315-4D8E-9543-8E28A4E18C4C/dxwebsetup.exe', - '', True, False); -end; - -procedure Dependency_AddHideHide; -begin - Dependency_Add('HidHide_1.4.192_x64.exe', - '/quiet /norestart', - 'HidHide Drivers', - 'https://github.com/nefarius/HidHide/releases/download/v1.4.192.0/HidHide_1.4.192_x64.exe', - '', True, False); -end; - -procedure Dependency_AddViGem; -begin - Dependency_Add('ViGEmBus_1.22.0_x64_x86_arm64.exe', - '/quiet /norestart', - 'ViGEmBus Setup', - 'https://github.com/nefarius/ViGEmBus/releases/download/v1.22.0/ViGEmBus_1.22.0_x64_x86_arm64.exe', - '', True, False); -end; - -procedure Dependency_AddRTSS; -begin - Dependency_Add('RTSSSetup735Beta5.exe', - '/S', - 'RTSS Setup v7.3.5 Beta5', - 'https://github.com/Valkirie/HandheldCompanion/raw/main/redist/RTSSSetup735Beta5.exe', - '', True, False); -end; - -procedure Dependency_AddHWiNFO; -begin - Dependency_Add('hwi_766.exe', - '/silent', - 'HWiNFO v7.6.6', - 'https://github.com/Valkirie/HandheldCompanion/raw/main/redist/hwi_766.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 UseHWiNFO - -#define MyAppSetupName 'Handheld Companion' -#define MyBuildId 'HandheldCompanion' -#define MyAppVersion '0.19.1.2' -#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{BE49B9DE-F8EB-4F54-B312-DD4B601985FC}', '', 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; - end; -end; - - -procedure InitializeWizard; -begin - Dependency_InitializeWizard; -end; - -function PrepareToInstall(var NeedsRestart: 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); -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 - -#ifdef UseHWiNFO - Dependency_AddHWiNFO; -#endif - - Result := True; -end; - -#endif \ No newline at end of file +; ----------- +; CODE +; ----------- +[Code] +// types and variables +type + TDependency_Entry = record + Filename: String; + Parameters: String; + Title: String; + URL: String; + Checksum: String; + ForceSuccess: Boolean; + RestartAfter: Boolean; + end; + +var + Dependency_Memo: String; + Dependency_List: array of TDependency_Entry; + Dependency_NeedRestart, Dependency_ForceX86: Boolean; + Dependency_DownloadPage: TDownloadWizardPage; + +procedure Dependency_Add(const Filename, Parameters, Title, URL, Checksum: String; const ForceSuccess, RestartAfter: Boolean); +var + Dependency: TDependency_Entry; + DependencyCount: Integer; +begin + Dependency_Memo := Dependency_Memo + #13#10 + '%1' + Title; + + Dependency.Filename := Filename; + 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.RestartAfter := RestartAfter; + + DependencyCount := GetArrayLength(Dependency_List); + SetArrayLength(Dependency_List, DependencyCount + 1); + Dependency_List[DependencyCount] := Dependency; +end; + +procedure Dependency_InitializeWizard; +begin + Dependency_DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), nil); +end; + +function Dependency_PrepareToInstall(var NeedsRestart: Boolean): String; +var + DependencyCount, DependencyIndex, ResultCode: Integer; + Retry: Boolean; + TempValue: String; +begin + DependencyCount := GetArrayLength(Dependency_List); + + if DependencyCount > 0 then begin + Dependency_DownloadPage.Show; + + for DependencyIndex := 0 to DependencyCount - 1 do begin + if Dependency_List[DependencyIndex].URL <> '' then begin + Dependency_DownloadPage.Clear; + Dependency_DownloadPage.Add(Dependency_List[DependencyIndex].URL, Dependency_List[DependencyIndex].Filename, Dependency_List[DependencyIndex].Checksum); + + Retry := True; + while Retry do begin + Retry := False; + + try + Dependency_DownloadPage.Download; + except + if Dependency_DownloadPage.AbortedByUser then begin + Result := Dependency_List[DependencyIndex].Title; + DependencyIndex := DependencyCount; + end else begin + case SuppressibleMsgBox(AddPeriod(GetExceptionMessage), mbError, MB_ABORTRETRYIGNORE, IDIGNORE) of + IDABORT: begin + Result := Dependency_List[DependencyIndex].Title; + DependencyIndex := DependencyCount; + end; + IDRETRY: begin + Retry := True; + end; + end; + end; + end; + end; + end; + end; + + if Result = '' then begin + for DependencyIndex := 0 to DependencyCount - 1 do begin + Dependency_DownloadPage.SetText(Dependency_List[DependencyIndex].Title, ''); + 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; + end; + break; + end else if (ResultCode = 0) or Dependency_List[DependencyIndex].ForceSuccess then begin // ERROR_SUCCESS (0) + break; + end else if ResultCode = 1641 then begin // ERROR_SUCCESS_REBOOT_INITIATED (1641) + NeedsRestart := True; + Result := Dependency_List[DependencyIndex].Title; + break; + end else if ResultCode = 3010 then begin // ERROR_SUCCESS_REBOOT_REQUIRED (3010) + Dependency_NeedRestart := True; + break; + end; + end; + + case SuppressibleMsgBox(FmtMessage(SetupMessage(msgErrorFunctionFailed), [Dependency_List[DependencyIndex].Title, IntToStr(ResultCode)]), mbError, MB_ABORTRETRYIGNORE, IDIGNORE) of + IDABORT: begin + Result := Dependency_List[DependencyIndex].Title; + break; + end; + IDIGNORE: begin + break; + end; + end; + end; + + if Result <> '' then begin + break; + end; + end; + + if NeedsRestart then begin + TempValue := '"' + ExpandConstant('{srcexe}') + '" /restart=1 /LANG="' + ExpandConstant('{language}') + '" /DIR="' + WizardDirValue + '" /GROUP="' + WizardGroupValue + '" /TYPE="' + WizardSetupType(False) + '" /COMPONENTS="' + WizardSelectedComponents(False) + '" /TASKS="' + WizardSelectedTasks(False) + '"'; + if WizardNoIcons then begin + TempValue := TempValue + ' /NOICONS'; + end; + RegWriteStringValue(HKA, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', '{#SetupSetting("AppName")}', TempValue); + end; + end; + + Dependency_DownloadPage.Hide; + end; +end; + +function Dependency_UpdateReadyMemo(const Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String; +begin + Result := ''; + if MemoUserInfoInfo <> '' then begin + Result := Result + MemoUserInfoInfo + Newline + NewLine; + end; + if MemoDirInfo <> '' then begin + Result := Result + MemoDirInfo + Newline + NewLine; + end; + if MemoTypeInfo <> '' then begin + Result := Result + MemoTypeInfo + Newline + NewLine; + end; + if MemoComponentsInfo <> '' then begin + Result := Result + MemoComponentsInfo + Newline + NewLine; + end; + if MemoGroupInfo <> '' then begin + Result := Result + MemoGroupInfo + Newline + NewLine; + end; + if MemoTasksInfo <> '' then begin + Result := Result + MemoTasksInfo; + end; + + if Dependency_Memo <> '' then begin + if MemoTasksInfo = '' then begin + Result := Result + SetupMessage(msgReadyMemoTasks); + end; + Result := Result + FmtMessage(Dependency_Memo, [Space]); + end; +end; + +function Dependency_IsX64: Boolean; +begin + Result := not Dependency_ForceX86 and Is64BitInstallMode; +end; + +function Dependency_String(const x86, x64: String): String; +begin + if Dependency_IsX64 then begin + Result := x64; + end else begin + Result := x86; + end; +end; + +function Dependency_ArchSuffix: String; +begin + Result := Dependency_String('', '_x64'); +end; + +function Dependency_ArchTitle: String; +begin + Result := Dependency_String(' (x86)', ' (x64)'); +end; + +function Dependency_IsNetCoreInstalled(const Version: String): Boolean; +var + ResultCode: Integer; +begin + // source code: https://github.com/dotnet/deployment-tools/tree/master/src/clickonce/native/projects/NetCoreCheck + if not FileExists(ExpandConstant('{tmp}{\}') + 'netcorecheck' + Dependency_ArchSuffix + '.exe') then begin + ExtractTemporaryFile('netcorecheck' + Dependency_ArchSuffix + '.exe'); + end; + Result := ShellExec('', ExpandConstant('{tmp}{\}') + 'netcorecheck' + Dependency_ArchSuffix + '.exe', Version, '', SW_HIDE, ewWaitUntilTerminated, ResultCode) and (ResultCode = 0); +end; + +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', + '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart', +<<<<<<< HEAD + '.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'), +======= + '.NET Runtime 7.0.0' + Dependency_ArchTitle, + Dependency_String('https://download.visualstudio.microsoft.com/download/pr/75c0d7c7-9f30-46fd-9675-a301f0e051f4/ec04d5cc40aa6537a4af21fad6bf8ba9/dotnet-runtime-7.0.0-win-x86.exe', + 'https://download.visualstudio.microsoft.com/download/pr/87bc5966-97cc-498c-8381-bff4c43aafc6/baca88b989e7d2871e989d33a667d8e9/dotnet-runtime-7.0.0-win-x64.exe'), + '', False, False); + end; +end; + +procedure Dependency_AddDotNet70Desktop; +begin + // https://dotnet.microsoft.com/download/dotnet/7.0 + if not Dependency_IsNetCoreInstalled('Microsoft.WindowsDesktop.App 7.0.10') then begin + Dependency_Add('dotnet70desktop' + Dependency_ArchSuffix + '.exe', + '/lcid ' + IntToStr(GetUILanguage) + ' /passive /norestart', + '.NET Desktop Runtime 7.0.0' + Dependency_ArchTitle, + Dependency_String('https://download.visualstudio.microsoft.com/download/pr/9812249d-fc42-41ab-bd2e-6e858d5dd5a7/95fa5a1a77eace4482bcb98ede190003/windowsdesktop-runtime-7.0.10-win-x86.exe', + 'https://download.visualstudio.microsoft.com/download/pr/747f4a98-2586-4bc6-b828-34f35e384a7d/44225cfd9d365855ec77d00c4812133c/windowsdesktop-runtime-7.0.10-win-x64.exe'), +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + '', False, False); + end; +end; + +procedure Dependency_AddVC2005; +begin + // https://www.microsoft.com/en-US/download/details.aspx?id=26347 + if not IsMsiProductInstalled(Dependency_String('{86C9D5AA-F00C-4921-B3F2-C60AF92E2844}', '{A8D19029-8E5C-4E22-8011-48070F9E796E}'), PackVersionComponents(8, 0, 61000, 0)) then begin + Dependency_Add('vcredist2005' + Dependency_ArchSuffix + '.exe', + '/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); + end; +end; + +procedure Dependency_AddVC2008; +begin + // https://www.microsoft.com/en-US/download/details.aspx?id=26368 + if not IsMsiProductInstalled(Dependency_String('{DE2C306F-A067-38EF-B86C-03DE4B0312F9}', '{FDA45DDF-8E17-336F-A3ED-356B7B7C688A}'), PackVersionComponents(9, 0, 30729, 6161)) then begin + Dependency_Add('vcredist2008' + Dependency_ArchSuffix + '.exe', + '/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); + end; +end; + +procedure Dependency_AddVC2010; +begin + // https://www.microsoft.com/en-US/download/details.aspx?id=26999 + if not IsMsiProductInstalled(Dependency_String('{1F4F1D2A-D9DA-32CF-9909-48485DA06DD5}', '{5B75F761-BAC8-33BC-A381-464DDDD813A3}'), PackVersionComponents(10, 0, 40219, 0)) then begin + Dependency_Add('vcredist2010' + Dependency_ArchSuffix + '.exe', + '/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); + end; +end; + +procedure Dependency_AddVC2012; +begin + // https://www.microsoft.com/en-US/download/details.aspx?id=30679 + if not IsMsiProductInstalled(Dependency_String('{4121ED58-4BD9-3E7B-A8B5-9F8BAAE045B7}', '{EFA6AFA1-738E-3E00-8101-FD03B86B29D1}'), PackVersionComponents(11, 0, 61030, 0)) then begin + Dependency_Add('vcredist2012' + Dependency_ArchSuffix + '.exe', + '/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); + end; +end; + +procedure Dependency_AddVC2013; +begin + // https://support.microsoft.com/en-US/help/4032938 + if not IsMsiProductInstalled(Dependency_String('{B59F5BF1-67C8-3802-8E59-2CE551A39FC5}', '{20400CF0-DE7C-327E-9AE4-F0F38D9085F8}'), PackVersionComponents(12, 0, 40664, 0)) then begin + Dependency_Add('vcredist2013' + Dependency_ArchSuffix + '.exe', + '/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); + end; +end; + +procedure Dependency_AddVC2015To2019; +begin + // https://support.microsoft.com/en-US/help/2977003/the-latest-supported-visual-c-downloads + if not IsMsiProductInstalled(Dependency_String('{65E5BD06-6392-3027-8C26-853107D3CF1A}', '{36F68A90-239C-34DF-B58C-64B30153CE35}'), PackVersionComponents(14, 29, 30037, 0)) then begin + Dependency_Add('vcredist2019' + Dependency_ArchSuffix + '.exe', + '/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); + end; +end; + +procedure Dependency_AddDirectX; +begin + // https://www.microsoft.com/en-US/download/details.aspx?id=35 + Dependency_Add('dxwebsetup.exe', + '/q', + 'DirectX Runtime', + 'https://download.microsoft.com/download/1/7/1/1718CCC4-6315-4D8E-9543-8E28A4E18C4C/dxwebsetup.exe', + '', True, False); +end; + +procedure Dependency_AddHideHide; +begin +<<<<<<< HEAD + Dependency_Add('HidHide_1.4.192_x64.exe', + '/quiet /norestart', + 'HidHide Drivers', +======= + // https://www.microsoft.com/en-US/download/details.aspx?id=35 + Dependency_Add('HidHide_1.4.192_x64.exe', + '/quiet /norestart', + 'HidHide Drivers v1.4.192', +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + 'https://github.com/nefarius/HidHide/releases/download/v1.4.192.0/HidHide_1.4.192_x64.exe', + '', True, False); +end; + +procedure Dependency_AddViGem; +begin +<<<<<<< HEAD + Dependency_Add('ViGEmBus_1.22.0_x64_x86_arm64.exe', + '/quiet /norestart', + 'ViGEmBus Setup', + 'https://github.com/nefarius/ViGEmBus/releases/download/v1.22.0/ViGEmBus_1.22.0_x64_x86_arm64.exe', +======= + // https://www.microsoft.com/en-US/download/details.aspx?id=35 + Dependency_Add('ViGEmBus_1.22.0_x64_x86_arm64.exe', + '/quiet /norestart', + 'ViGEmBus Setup 1.22.0', + 'https://github.com/Valkirie/HandheldCompanion/raw/main/redist/ViGEmBus_1.22.0_x64_x86_arm64.exe', +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + '', True, False); +end; + +procedure Dependency_AddRTSS; +begin + Dependency_Add('RTSSSetup735Beta5.exe', + '/S', + 'RTSS Setup v7.3.5 Beta5', + 'https://github.com/Valkirie/HandheldCompanion/raw/main/redist/RTSSSetup735Beta5.exe', + '', True, False); +end; + +procedure Dependency_AddHWiNFO; +begin + Dependency_Add('hwi_766.exe', + '/silent', + 'HWiNFO v7.6.6', + 'https://github.com/Valkirie/HandheldCompanion/raw/main/redist/hwi_766.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 UseHWiNFO + +#define MyAppSetupName 'Handheld Companion' +#define MyBuildId 'HandheldCompanion' +<<<<<<< HEAD +#define MyAppVersion '0.19.1.2' +======= +#define MyAppVersion '0.18.0.6' +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +#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}" + +<<<<<<< HEAD +[UninstallRun] +Filename: "C:\Program Files\Nefarius Software Solutions\HidHide\x64\HidHideCLI.exe"; Parameters: "--cloak-off" ; RunOnceId: "CloakOff"; Flags: runascurrentuser runhidden +======= +[Run] +Filename: "{sys}\sc.exe"; Parameters: "stop ControllerService" ; Flags: runascurrentuser runhidden +Filename: "{sys}\sc.exe"; Parameters: "delete ControllerService" ; Flags: runascurrentuser runhidden + +[UninstallRun] +Filename: "C:\Program Files\Nefarius Software Solutions e.U\HidHideCLI\HidHideCLI.exe"; Parameters: "--cloak-off" ; RunOnceId: "CloakOff"; Flags: runascurrentuser runhidden +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + +[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 +<<<<<<< HEAD + if(ShellExec('', 'msiexec.exe', '/X{BE49B9DE-F8EB-4F54-B312-DD4B601985FC}', '', SW_SHOW, ewWaitUntilTerminated, resultCode)) then +======= + if(ShellExec('', 'msiexec.exe', '/X{50D7EB6D-6A4A-4A38-B09C-CC28F75F082E} /qn /norestart', '', SW_SHOW, ewWaitUntilTerminated, resultCode)) then +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + 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; + end; +end; + + +procedure InitializeWizard; +begin + Dependency_InitializeWizard; +end; + +function PrepareToInstall(var NeedsRestart: 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); +end; + +function InitializeSetup: Boolean; +begin + +<<<<<<< HEAD +#ifdef UseDotNet80 + Dependency_AddDotNet80Desktop; +======= +#ifdef UseDotNet70 + Dependency_AddDotNet70Desktop; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +#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 + +#ifdef UseHWiNFO + Dependency_AddHWiNFO; +#endif + + Result := True; +end; + +#endif diff --git a/HandheldCompanion/Actions/ButtonActions.cs b/HandheldCompanion/Actions/ButtonActions.cs index 5e56ea707..d6d643e5e 100644 --- a/HandheldCompanion/Actions/ButtonActions.cs +++ b/HandheldCompanion/Actions/ButtonActions.cs @@ -1,59 +1,65 @@ -using HandheldCompanion.Inputs; -using System; - -namespace HandheldCompanion.Actions -{ - [Serializable] - public class ButtonActions : IActions - { - public ButtonFlags Button; - - // runtime variables - private bool IsKeyDown = false; - - public ButtonActions() - { - this.ActionType = ActionType.Button; - - this.Value = false; - this.prevValue = false; - } - - public ButtonActions(ButtonFlags button) : this() - { - this.Button = button; - } - - public override void Execute(ButtonFlags button, bool value) - { - base.Execute(button, value); - - switch (this.Value) - { - case true: - { - if (IsKeyDown) - return; - - IsKeyDown = true; - SetHaptic(button, false); - } - break; - case false: - { - if (!IsKeyDown) - return; - - IsKeyDown = false; - SetHaptic(button, true); - } - break; - } - } - - public bool GetValue() - { - return (bool)this.Value; - } - } -} +using HandheldCompanion.Inputs; +using System; + +namespace HandheldCompanion.Actions +{ + [Serializable] + public class ButtonActions : IActions + { + public ButtonFlags Button; + + // runtime variables + private bool IsKeyDown = false; + + public ButtonActions() + { + this.ActionType = ActionType.Button; + + this.Value = false; + this.prevValue = false; + } + + public ButtonActions(ButtonFlags button) : this() + { + this.Button = button; + } + +<<<<<<< HEAD + public override void Execute(ButtonFlags button, bool value) + { + base.Execute(button, value); +======= + public override void Execute(ButtonFlags button, bool value, int longTime) + { + base.Execute(button, value, longTime); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + switch (this.Value) + { + case true: + { + if (IsKeyDown) + return; + + IsKeyDown = true; + SetHaptic(button, false); + } + break; + case false: + { + if (!IsKeyDown) + return; + + IsKeyDown = false; + SetHaptic(button, true); + } + break; + } + } + + public bool GetValue() + { + return (bool)this.Value; + } + } +} diff --git a/HandheldCompanion/Actions/IActions.cs b/HandheldCompanion/Actions/IActions.cs index 98e03a224..81679ca7b 100644 --- a/HandheldCompanion/Actions/IActions.cs +++ b/HandheldCompanion/Actions/IActions.cs @@ -1,181 +1,235 @@ -using HandheldCompanion.Inputs; -using HandheldCompanion.Managers; -using System; -using System.Collections.Generic; -using System.Windows.Forms; -using WindowsInput.Events; - -namespace HandheldCompanion.Actions -{ - [Serializable] - public enum ActionType - { - Disabled = 0, - Button = 1, - Joystick = 2, - Keyboard = 3, - Mouse = 4, - Trigger = 5, - Special = 6, - } - - [Serializable] - public enum ModifierSet - { - None = 0, - Shift = 1, - Control = 2, - Alt = 3, - ShiftControl = 4, - ShiftAlt = 5, - ControlAlt = 6, - ShiftControlAlt = 7, - } - - [Serializable] - public enum PressType - { - Short = 0, - Long = 1, - } - - [Serializable] - public enum HapticMode - { - Off = 0, - Down = 1, - Up = 2, - Both = 3, - } - - [Serializable] - public enum HapticStrength - { - Low = 0, - Medium = 1, - High = 2, - } - - [Serializable] - public abstract class IActions : ICloneable - { - public static Dictionary<ModifierSet, KeyCode[]> ModifierMap = new() - { - { ModifierSet.None, new KeyCode[] { } }, - { ModifierSet.Shift, new KeyCode[] { KeyCode.LShift } }, - { ModifierSet.Control, new KeyCode[] { KeyCode.LControl } }, - { ModifierSet.Alt, new KeyCode[] { KeyCode.LMenu } }, - { ModifierSet.ShiftControl, new KeyCode[] { KeyCode.LShift, KeyCode.LControl } }, - { ModifierSet.ShiftAlt, new KeyCode[] { KeyCode.LShift, KeyCode.LMenu } }, - { ModifierSet.ControlAlt, new KeyCode[] { KeyCode.LControl, KeyCode.LMenu } }, - { ModifierSet.ShiftControlAlt, new KeyCode[] { KeyCode.LShift, KeyCode.LControl, KeyCode.LMenu } }, - }; - - public ActionType ActionType = ActionType.Disabled; - - protected object Value; - protected object prevValue; - - // TODO: multiple delay, delay ranges - public PressType PressType = PressType.Short; - public int LongPressTime = 450; // default value for steam - protected int pressTimer = -1; // -1 inactive, >= 0 active - - public bool Turbo; - public int TurboDelay = 30; - protected int TurboIdx; - protected bool IsTurboed; - - public bool Toggle; - protected bool IsToggled; - - public HapticMode HapticMode = HapticMode.Off; - public HapticStrength HapticStrength = HapticStrength.Low; - - protected ScreenOrientation Orientation = ScreenOrientation.Angle0; - public bool AutoRotate { get; set; } = false; - - public IActions() - { - } - - public virtual void SetHaptic(ButtonFlags button, bool up) - { - if (this.HapticMode == HapticMode.Off) return; - if (this.HapticMode == HapticMode.Down && up) return; - if (this.HapticMode == HapticMode.Up && !up) return; - - ControllerManager.GetTargetController()?.SetHaptic(this.HapticStrength, button); - } - - public virtual void Execute(ButtonFlags button, bool value) - { - switch(PressType) - { - case PressType.Long: - { - if (value || (pressTimer <= LongPressTime && pressTimer >= 0)) - { - pressTimer += TimerManager.GetPeriod(); - value = true; - } - else if(pressTimer >= LongPressTime) - { - pressTimer = -1; - } - } - break; - } - - if (Toggle) - { - if ((bool)prevValue != value && value) - IsToggled = !IsToggled; - } - else - IsToggled = false; - - if (Turbo) - { - if (value || IsToggled) - { - if (TurboIdx % TurboDelay == 0) - IsTurboed = !IsTurboed; - - TurboIdx += TimerManager.GetPeriod(); - } - else - { - IsTurboed = false; - TurboIdx = 0; - } - } - else - IsTurboed = false; - - // update previous value - prevValue = value; - - // update value - if (Toggle && Turbo) - this.Value = IsToggled && IsTurboed; - else if (Toggle) - this.Value = IsToggled; - else if (Turbo) - this.Value = IsTurboed; - else - this.Value = value; - } - - public virtual void SetOrientation(ScreenOrientation orientation) - { - Orientation = orientation; - } - - // Improve me ! - public object Clone() - { - return MemberwiseClone(); - } - } -} +using HandheldCompanion.Inputs; +using HandheldCompanion.Managers; +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using WindowsInput.Events; + +namespace HandheldCompanion.Actions +{ + [Serializable] + public enum ActionType + { + Disabled = 0, + Button = 1, + Joystick = 2, + Keyboard = 3, + Mouse = 4, + Trigger = 5, + Special = 6, + } + + [Serializable] + public enum ModifierSet + { + None = 0, + Shift = 1, + Control = 2, + Alt = 3, + ShiftControl = 4, + ShiftAlt = 5, + ControlAlt = 6, + ShiftControlAlt = 7, + } + + [Serializable] + public enum PressType + { + Short = 0, + Long = 1, + } + + [Serializable] + public enum HapticMode + { + Off = 0, + Down = 1, + Up = 2, + Both = 3, + } + + [Serializable] + public enum HapticStrength + { + Low = 0, + Medium = 1, + High = 2, + } + + [Serializable] + public abstract class IActions : ICloneable + { + public static Dictionary<ModifierSet, KeyCode[]> ModifierMap = new() + { + { ModifierSet.None, new KeyCode[] { } }, + { ModifierSet.Shift, new KeyCode[] { KeyCode.LShift } }, + { ModifierSet.Control, new KeyCode[] { KeyCode.LControl } }, + { ModifierSet.Alt, new KeyCode[] { KeyCode.LMenu } }, + { ModifierSet.ShiftControl, new KeyCode[] { KeyCode.LShift, KeyCode.LControl } }, + { ModifierSet.ShiftAlt, new KeyCode[] { KeyCode.LShift, KeyCode.LMenu } }, + { ModifierSet.ControlAlt, new KeyCode[] { KeyCode.LControl, KeyCode.LMenu } }, + { ModifierSet.ShiftControlAlt, new KeyCode[] { KeyCode.LShift, KeyCode.LControl, KeyCode.LMenu } }, + }; + + public ActionType ActionType = ActionType.Disabled; + + protected object Value; + protected object prevValue; + +<<<<<<< HEAD +======= + // values below are common for button type actions + + protected int Period; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + // TODO: multiple delay, delay ranges + public PressType PressType = PressType.Short; + public int LongPressTime = 450; // default value for steam + protected int pressTimer = -1; // -1 inactive, >= 0 active + + public bool Turbo; + public int TurboDelay = 30; + protected int TurboIdx; + protected bool IsTurboed; + + public bool Toggle; + protected bool IsToggled; + + public HapticMode HapticMode = HapticMode.Off; + public HapticStrength HapticStrength = HapticStrength.Low; + + protected ScreenOrientation Orientation = ScreenOrientation.Angle0; + public bool AutoRotate { get; set; } = false; + + public IActions() + { +<<<<<<< HEAD +======= + Period = TimerManager.GetPeriod(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public virtual void SetHaptic(ButtonFlags button, bool up) + { + if (this.HapticMode == HapticMode.Off) return; + if (this.HapticMode == HapticMode.Down && up) return; + if (this.HapticMode == HapticMode.Up && !up) return; + + ControllerManager.GetTargetController()?.SetHaptic(this.HapticStrength, button); + } + +<<<<<<< HEAD + public virtual void Execute(ButtonFlags button, bool value) + { + switch(PressType) + { + case PressType.Long: + { + if (value || (pressTimer <= LongPressTime && pressTimer >= 0)) + { + pressTimer += TimerManager.GetPeriod(); + value = true; + } + else if(pressTimer >= LongPressTime) + { + pressTimer = -1; + } + } + break; +======= + // if longDelay == 0 no new logic will be executed + public virtual void Execute(ButtonFlags button, bool value, int longTime) + { + // reset failed attempts on button release + if (pressTimer >= 0 && !value && + ((PressType == PressType.Short && pressTimer >= longTime) || + (PressType == PressType.Long && pressTimer < longTime))) + { + pressTimer = -1; + prevValue = false; + return; + } + + // some long presses exist and button was just pressed, start the timer and quit + if (longTime > 0 && value && !(bool)prevValue) + { + pressTimer = 0; + prevValue = true; + return; + } + + if (pressTimer >= 0) + { + pressTimer += Period; + + // conditions were met to trigger either short or long, reset state, press buttons + if ((!value && PressType == PressType.Short && pressTimer < longTime) || + (value && PressType == PressType.Long && pressTimer >= longTime)) + { + pressTimer = -1; + prevValue = false; // simulate a situation where the button was just pressed + value = true; // prev = false, current = true, this way toggle works + } + // timer active, conditions not met, carry on, maybe smth happens, maybe failed attempt + else + return; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + if (Toggle) + { + if ((bool)prevValue != value && value) + IsToggled = !IsToggled; + } + else + IsToggled = false; + + if (Turbo) + { + if (value || IsToggled) + { + if (TurboIdx % TurboDelay == 0) + IsTurboed = !IsTurboed; + +<<<<<<< HEAD + TurboIdx += TimerManager.GetPeriod(); +======= + TurboIdx += Period; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + else + { + IsTurboed = false; + TurboIdx = 0; + } + } + else + IsTurboed = false; + + // update previous value + prevValue = value; + + // update value + if (Toggle && Turbo) + this.Value = IsToggled && IsTurboed; + else if (Toggle) + this.Value = IsToggled; + else if (Turbo) + this.Value = IsTurboed; + else + this.Value = value; + } + + public virtual void SetOrientation(ScreenOrientation orientation) + { + Orientation = orientation; + } + + // Improve me ! + public object Clone() + { + return MemberwiseClone(); + } + } +} diff --git a/HandheldCompanion/Actions/KeyboardActions.cs b/HandheldCompanion/Actions/KeyboardActions.cs index d58a37739..953730a04 100644 --- a/HandheldCompanion/Actions/KeyboardActions.cs +++ b/HandheldCompanion/Actions/KeyboardActions.cs @@ -1,66 +1,72 @@ -using GregsStack.InputSimulatorStandard.Native; -using HandheldCompanion.Inputs; -using HandheldCompanion.Simulators; -using System; -using WindowsInput.Events; - -namespace HandheldCompanion.Actions -{ - [Serializable] - public class KeyboardActions : IActions - { - public VirtualKeyCode Key; - - // runtime variables - private bool IsKeyDown = false; - private KeyCode[] pressed; - - // settings - public ModifierSet Modifiers = ModifierSet.None; - - public KeyboardActions() - { - this.ActionType = ActionType.Keyboard; - - this.Value = false; - this.prevValue = false; - } - - public KeyboardActions(VirtualKeyCode key) : this() - { - this.Key = key; - } - - public override void Execute(ButtonFlags button, bool value) - { - base.Execute(button, value); - - switch (this.Value) - { - case true: - { - if (IsKeyDown) - return; - - IsKeyDown = true; - pressed = ModifierMap[Modifiers]; - KeyboardSimulator.KeyDown(pressed); - KeyboardSimulator.KeyDown(Key); - SetHaptic(button, false); - } - break; - case false: - { - if (!IsKeyDown) - return; - - IsKeyDown = false; - KeyboardSimulator.KeyUp(Key); - KeyboardSimulator.KeyUp(pressed); - SetHaptic(button, true); - } - break; - } - } - } -} +using GregsStack.InputSimulatorStandard.Native; +using HandheldCompanion.Inputs; +using HandheldCompanion.Simulators; +using System; +using WindowsInput.Events; + +namespace HandheldCompanion.Actions +{ + [Serializable] + public class KeyboardActions : IActions + { + public VirtualKeyCode Key; + + // runtime variables + private bool IsKeyDown = false; + private KeyCode[] pressed; + + // settings + public ModifierSet Modifiers = ModifierSet.None; + + public KeyboardActions() + { + this.ActionType = ActionType.Keyboard; + + this.Value = false; + this.prevValue = false; + } + + public KeyboardActions(VirtualKeyCode key) : this() + { + this.Key = key; + } + +<<<<<<< HEAD + public override void Execute(ButtonFlags button, bool value) + { + base.Execute(button, value); +======= + public override void Execute(ButtonFlags button, bool value, int longTime) + { + base.Execute(button, value, longTime); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + switch (this.Value) + { + case true: + { + if (IsKeyDown) + return; + + IsKeyDown = true; + pressed = ModifierMap[Modifiers]; + KeyboardSimulator.KeyDown(pressed); + KeyboardSimulator.KeyDown(Key); + SetHaptic(button, false); + } + break; + case false: + { + if (!IsKeyDown) + return; + + IsKeyDown = false; + KeyboardSimulator.KeyUp(Key); + KeyboardSimulator.KeyUp(pressed); + SetHaptic(button, true); + } + break; + } + } + } +} diff --git a/HandheldCompanion/Actions/MouseActions.cs b/HandheldCompanion/Actions/MouseActions.cs index fdc4941be..85bf7df86 100644 --- a/HandheldCompanion/Actions/MouseActions.cs +++ b/HandheldCompanion/Actions/MouseActions.cs @@ -1,206 +1,212 @@ -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; + } + +<<<<<<< HEAD + public override void Execute(ButtonFlags button, bool value) + { + base.Execute(button, value); +======= + public override void Execute(ButtonFlags button, bool value, int longTime) + { + base.Execute(button, value, longTime); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + 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/App.config b/HandheldCompanion/App.config index bf7669c66..3b80e60c9 100644 --- a/HandheldCompanion/App.config +++ b/HandheldCompanion/App.config @@ -141,7 +141,11 @@ <value>True</value> </setting> <setting name="SteamControllerMute" serializeAs="String"> +<<<<<<< HEAD <value>False</value> +======= + <value>True</value> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </setting> <setting name="HIDcloakonconnect" serializeAs="String"> <value>True</value> @@ -195,6 +199,7 @@ <value>1</value> </setting> <setting name="QuickToolsAutoHide" serializeAs="String"> +<<<<<<< HEAD <value>False</value> </setting> <setting name="ControllerManagement" serializeAs="String"> @@ -243,8 +248,31 @@ <value>50</value> </setting> <setting name="LegionControllerPassthrough" serializeAs="String"> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + <value>False</value> + </setting> + <setting name="VirtualControllerForceOrder" serializeAs="String"> + <value>False</value> + </setting> + <setting name="HidModeDoNotShowAgain" serializeAs="String"> <value>False</value> </setting> + <setting name="HIDmode" serializeAs="String"> + <value>0</value> + </setting> + <setting name="HIDstatus" serializeAs="String"> + <value>1</value> + </setting> + <setting name="DSUEnabled" serializeAs="String"> + <value>True</value> + </setting> + <setting name="DSUport" serializeAs="String"> + <value>26760</value> + </setting> + <setting name="DSUip" serializeAs="String"> + <value>127.0.0.1</value> + </setting> </HandheldCompanion.Properties.Settings> </userSettings> </configuration> \ No newline at end of file diff --git a/HandheldCompanion/Controllers/DS4Controller.cs b/HandheldCompanion/Controllers/DS4Controller.cs index 0156be2f7..c99233de9 100644 --- a/HandheldCompanion/Controllers/DS4Controller.cs +++ b/HandheldCompanion/Controllers/DS4Controller.cs @@ -1,183 +1,187 @@ -using HandheldCompanion.Inputs; -using HandheldCompanion.Managers; -using HandheldCompanion.Utils; -using System.Windows; -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))); - - // 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) - { - // skip if controller isn't connected - if (!IsConnected()) - return; - - base.UpdateState(); - - // 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); - } - - 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) - { - // UI thread - Application.Current.Dispatcher.Invoke(() => - { - JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B)); - }); - } - - public override void Cleanup() - { - TimerManager.Tick -= UpdateInputs; - } - - 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; +<<<<<<< HEAD +======= +using Inkore.UI.WPF.Modern; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using System.Windows; +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))); + + // 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) + { + // skip if controller isn't connected + if (!IsConnected()) + return; + + base.UpdateState(); + + // 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); + } + + 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) + { + // UI thread + Application.Current.Dispatcher.Invoke(() => + { + JslSetLightColour(UserIndex, CommonUtils.rgb_to_int(R, G, B)); + }); + } + + public override void Cleanup() + { + TimerManager.Tick -= UpdateInputs; + } + + 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/GordonController.cs b/HandheldCompanion/Controllers/GordonController.cs index 637c007f8..c252b78f6 100644 --- a/HandheldCompanion/Controllers/GordonController.cs +++ b/HandheldCompanion/Controllers/GordonController.cs @@ -1,316 +1,338 @@ -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(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(); - - // Additional controller specific source buttons/axes - SourceButtons.AddRange(new List<ButtonFlags>() { ButtonFlags.L4, ButtonFlags.R4 }); - SourceButtons.AddRange(new List<ButtonFlags>() { ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp, ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight }); - SourceButtons.AddRange(new List<ButtonFlags>() { 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()); - - // 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) - { - 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? - Inputs.GyroState.Accelerometer.X = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelX] / short.MaxValue * 2.0f; - Inputs.GyroState.Accelerometer.Y = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f; - Inputs.GyroState.Accelerometer.Z = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelY] / short.MaxValue * 2.0f; - - // TODO: why Roll/Pitch swapped? - Inputs.GyroState.Gyroscope.X = (float)input.State.AxesState[GordonControllerAxis.GyroPitch] / short.MaxValue * 2048.0f; // Roll - Inputs.GyroState.Gyroscope.Y = -(float)input.State.AxesState[GordonControllerAxis.GyroRoll] / short.MaxValue * 2048.0f; // Pitch - Inputs.GyroState.Gyroscope.Z = (float)input.State.AxesState[GordonControllerAxis.GyroYaw] / short.MaxValue * 2048.0f; // Yaw - - base.UpdateInputs(ticks); - } - - 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 - - SetVirtualMuted(SettingsManager.GetBoolean("SteamControllerMute")); - - 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 override void Cleanup() - { - TimerManager.Tick -= UpdateInputs; - } - - 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(PnPDetails details) : base() + { +<<<<<<< HEAD + AttachDetails(details); +======= + if (details is null) + return; + + Controller = new(details.attributes.VendorID, details.attributes.ProductID, details.GetMI()); + + // open controller + Open(); + + Details = details; + Details.isHooked = true; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // 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))); + +<<<<<<< HEAD + DrawUI(); + UpdateUI(); +======= + InitializeComponent(); + DrawControls(); + RefreshControls(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // Additional controller specific source buttons/axes + SourceButtons.AddRange(new List<ButtonFlags>() { ButtonFlags.L4, ButtonFlags.R4 }); + SourceButtons.AddRange(new List<ButtonFlags>() { ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp, ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight }); + SourceButtons.AddRange(new List<ButtonFlags>() { 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); + } + +<<<<<<< HEAD + public override void AttachDetails(PnPDetails details) + { + base.AttachDetails(details); + + Controller = new(details.VendorID, details.ProductID, details.GetMI()); + + // open controller + Open(); + } + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public override string ToString() + { + string baseName = base.ToString(); + if (!string.IsNullOrEmpty(baseName)) + return baseName; + return "Steam Controller Gordon"; + } + + public override void UpdateInputs(long ticks) + { + 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? + Inputs.GyroState.Accelerometer.X = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelX] / short.MaxValue * 2.0f; + Inputs.GyroState.Accelerometer.Y = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f; + Inputs.GyroState.Accelerometer.Z = -(float)input.State.AxesState[GordonControllerAxis.GyroAccelY] / short.MaxValue * 2.0f; + + // TODO: why Roll/Pitch swapped? + Inputs.GyroState.Gyroscope.X = (float)input.State.AxesState[GordonControllerAxis.GyroPitch] / short.MaxValue * 2048.0f; // Roll + Inputs.GyroState.Gyroscope.Y = -(float)input.State.AxesState[GordonControllerAxis.GyroRoll] / short.MaxValue * 2048.0f; // Pitch + Inputs.GyroState.Gyroscope.Z = (float)input.State.AxesState[GordonControllerAxis.GyroYaw] / short.MaxValue * 2048.0f; // Yaw + + base.UpdateInputs(ticks); + } + + 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 + + SetVirtualMuted(SettingsManager.GetBoolean("SteamControllerMute")); + + 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 override void Cleanup() + { + TimerManager.Tick -= UpdateInputs; + } + + 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 595e0f0fd..87c039ba7 100644 --- a/HandheldCompanion/Controllers/IController.xaml +++ b/HandheldCompanion/Controllers/IController.xaml @@ -1,93 +1,122 @@ -<UserControl - x:Class="HandheldCompanion.Controllers.IController" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:HandheldCompanion.Controllers" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - d:DesignHeight="450" - d:DesignWidth="800" - mc:Ignorable="d"> - - <Border - Padding="15,12,12,12" - Background="{DynamicResource SystemControlPageBackgroundAltHighBrush}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <ui:SimpleStackPanel Spacing="12"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - VerticalAlignment="Center" - FontFamily="PromptFont" - FontSize="30" - Glyph="␼" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center" Spacing="2"> - <TextBlock Name="ControllerName" Style="{StaticResource BodyTextBlockStyle}" /> - - <ui:SimpleStackPanel Spacing="2" HorizontalAlignment="Left" Orientation="Horizontal" Name="UserIndexPanel"> - <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> - <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> - <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> - <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> - <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> - <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> - <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> - <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> - <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> - <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </DockPanel> - - <DockPanel Grid.Column="1" HorizontalAlignment="Right"> - <Button - Name="ui_button_hook" - Width="100" - Click="ui_button_hook_Click" - FontSize="14" - Content="{x:Static resx:Resources.Controller_Connect}" - Style="{DynamicResource AccentButtonStyle}" /> - <Button - Name="ui_button_hide" - Width="100" - Margin="6,0,0,0" - Click="ui_button_hide_Click" - FontSize="14" - Style="{DynamicResource AccentButtonStyle}" /> - <Button - Name="ui_button_calibrate" - Width="100" - Margin="6,0,0,0" - Click="ui_button_calibrate_Click" - Content="Calibrate" - FontSize="14" - Style="{DynamicResource AccentButtonStyle}" - Visibility="Collapsed" /> - </DockPanel> - </Grid> - - <ui:SimpleStackPanel - Name="ProgressBarPanel" - d:Visibility="Visible" - Spacing="6" - Visibility="Collapsed"> - <ui:ProgressBar Name="ProgressBarUpdate" IsIndeterminate="True" /> - <TextBlock - Name="ProgressBarWarning" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - TextAlignment="Center" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Border> -</UserControl> +<UserControl + x:Class="HandheldCompanion.Controllers.IController" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:HandheldCompanion.Controllers" +<<<<<<< HEAD + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" +======= + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + d:DesignHeight="450" + d:DesignWidth="800" + mc:Ignorable="d"> + + <Border + Padding="15,12,12,12" + Background="{DynamicResource SystemControlPageBackgroundAltHighBrush}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + +<<<<<<< HEAD + <ui:SimpleStackPanel Spacing="12"> +======= + <StackPanel> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon +<<<<<<< HEAD + VerticalAlignment="Center" + FontFamily="PromptFont" + FontSize="30" + Glyph="␼" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center" Spacing="2"> + <TextBlock Name="ControllerName" Style="{StaticResource BodyTextBlockStyle}" /> + + <ui:SimpleStackPanel Spacing="2" HorizontalAlignment="Left" Orientation="Horizontal" Name="UserIndexPanel"> + <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> + <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> + <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> + <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> + <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> + <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> + <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> + <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> + <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> + <Border CornerRadius="2" Background="{DynamicResource SystemControlForegroundBaseLowBrush}" Width="12" Height="12"></Border> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> +======= + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <TextBlock + Name="ui_name" + Margin="12,0,0,0" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" /> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + </DockPanel> + + <DockPanel Grid.Column="1" HorizontalAlignment="Right"> + <Button + Name="ui_button_hook" + Width="100" + Click="ui_button_hook_Click" + FontSize="14" +<<<<<<< HEAD + Content="{x:Static resx:Resources.Controller_Connect}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Style="{DynamicResource AccentButtonStyle}" /> + <Button + Name="ui_button_hide" + Width="100" + Margin="6,0,0,0" + Click="ui_button_hide_Click" + FontSize="14" + Style="{DynamicResource AccentButtonStyle}" /> + <Button + Name="ui_button_calibrate" + Width="100" + Margin="6,0,0,0" + Click="ui_button_calibrate_Click" + Content="Calibrate" + FontSize="14" + Style="{DynamicResource AccentButtonStyle}" + Visibility="Collapsed" /> + </DockPanel> + </Grid> + + <ui:SimpleStackPanel + Name="ProgressBarPanel" + d:Visibility="Visible" + Spacing="6" + Visibility="Collapsed"> + <ui:ProgressBar Name="ProgressBarUpdate" IsIndeterminate="True" /> + <TextBlock + Name="ProgressBarWarning" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + TextAlignment="Center" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> +<<<<<<< HEAD + </ui:SimpleStackPanel> +======= + </StackPanel> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + </Border> +</UserControl> diff --git a/HandheldCompanion/Controllers/IController.xaml.cs b/HandheldCompanion/Controllers/IController.xaml.cs index 1d81c958c..81fac8a45 100644 --- a/HandheldCompanion/Controllers/IController.xaml.cs +++ b/HandheldCompanion/Controllers/IController.xaml.cs @@ -1,650 +1,825 @@ -using HandheldCompanion.Actions; -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.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, - Calibration = 2, - } - - /// <summary> - /// Logique d'interaction pour IController.xaml - /// </summary> - public partial class IController : UserControl - { - // Buttons and axes we should be able to map to. - // When we have target controllers with different buttons (e.g. in VigEm) this will have to be moved elsewhere. - public static readonly List<ButtonFlags> TargetButtons = new() - { - ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4, - ButtonFlags.DPadUp, ButtonFlags.DPadDown, ButtonFlags.DPadLeft, ButtonFlags.DPadRight, - ButtonFlags.Start, ButtonFlags.Back, ButtonFlags.Special, - ButtonFlags.L1, ButtonFlags.R1, - ButtonFlags.LeftStickClick, ButtonFlags.RightStickClick, - }; - - public static readonly List<AxisLayoutFlags> TargetAxis = new() - { - AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick, - AxisLayoutFlags.L2, AxisLayoutFlags.R2, - }; - - public static readonly string defaultGlyph = "\u2753"; - - public ControllerCapabilities Capabilities = ControllerCapabilities.None; - protected SortedDictionary<AxisLayoutFlags, Brush> ColoredAxis = new(); - - protected SortedDictionary<ButtonFlags, Brush> ColoredButtons = new(); - protected FontFamily DefaultFontFamily = new("Segeo WP"); - - public PnPDetails Details; - - // UI - protected FontFamily GlyphFontFamily = new("PromptFont"); - - public ButtonState InjectedButtons = new(); - - public ControllerState Inputs = new(); - public virtual bool IsReady => true; - - public bool IsBusy - { - get - { - return IsEnabled; - } - set - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - IsEnabled = !value; - ProgressBarPanel.Visibility = value ? Visibility.Visible : Visibility.Collapsed; - }); - } - } - - protected List<AxisLayoutFlags> SourceAxis = new() - { - // same as target, we assume all controllers have those axes - AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick, - AxisLayoutFlags.L2, AxisLayoutFlags.R2 - }; - - // Buttons and axes all controllers have that we can map. - // Additional ones can be added per controller. - protected List<ButtonFlags> SourceButtons = new() - { - // same as target, we assume all controllers have those buttons - ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4, - ButtonFlags.DPadUp, ButtonFlags.DPadDown, ButtonFlags.DPadLeft, ButtonFlags.DPadRight, - ButtonFlags.Start, ButtonFlags.Back, ButtonFlags.Special, - ButtonFlags.L1, ButtonFlags.R1, - ButtonFlags.LeftStickClick, ButtonFlags.RightStickClick, - // additional buttons calculated from the above - ButtonFlags.L2Soft, ButtonFlags.R2Soft, ButtonFlags.L2Full, ButtonFlags.R2Full, - ButtonFlags.LeftStickUp, ButtonFlags.LeftStickDown, ButtonFlags.LeftStickLeft, ButtonFlags.LeftStickRight, - ButtonFlags.RightStickUp, ButtonFlags.RightStickDown, ButtonFlags.RightStickLeft, ButtonFlags.RightStickRight - }; - - private byte _UserIndex = 255; - protected byte UserIndex - { - get - { - return _UserIndex; - } - set - { - _UserIndex = value; - UserIndexChanged?.Invoke(value); - - // UI thread (async) - Application.Current.Dispatcher.Invoke(() => - { - foreach(FrameworkElement frameworkElement in UserIndexPanel.Children) - { - if (frameworkElement is not Border) - continue; - - Border border = (Border)frameworkElement; - int idx = UserIndexPanel.Children.IndexOf(border); - - if (idx == value) - border.SetResourceReference(BackgroundProperty, "AccentAAFillColorDefaultBrush"); - else - border.SetResourceReference(BackgroundProperty, "SystemControlForegroundBaseLowBrush"); - } - }); - } - } - - protected double VibrationStrength = 1.0d; - - public IController() - { - InitializeComponent(); - } - - public virtual void AttachDetails(PnPDetails details) - { - this.Details = details; - Details.isHooked = true; - } - - public virtual void UpdateInputs(long ticks) - { - InputsUpdated?.Invoke(Inputs); - } - - public bool HasMotionSensor() - { - return Capabilities.HasFlag(ControllerCapabilities.MotionSensor); - } - - 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(); - - // virtual controller shouldn't be visible - if (IsVirtual()) - this.Visibility = Visibility.Collapsed; - } - - protected void UpdateUI() - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // ui_button_hook.Content = IsPlugged ? Properties.Resources.Controller_Disconnect : Properties.Resources.Controller_Connect; - ui_button_hide.Content = IsHidden() ? Properties.Resources.Controller_Unhide : Properties.Resources.Controller_Hide; - ui_button_calibrate.Visibility = Capabilities.HasFlag(ControllerCapabilities.Calibration) ? Visibility.Visible : Visibility.Collapsed; - }); - } - - 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.BeginInvoke(() => - { - ui_button_hook.IsEnabled = false; - }); - } - - // this function cannot be called twice - public virtual void Unplug() - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - ui_button_hook.IsEnabled = true; - }); - } - - // like Unplug but one that can be safely called when controller is already removed - public virtual void Cleanup() - { - } - - 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; - } - - protected virtual void Calibrate() - { - } - - protected virtual void ui_button_calibrate_Click(object sender, RoutedEventArgs e) - { - CalibrateClicked?.Invoke(this); - } - - protected virtual void ui_button_hide_Click(object sender, RoutedEventArgs e) - { - HideClicked?.Invoke(this); - } - - protected virtual void ui_button_hook_Click(object sender, RoutedEventArgs e) - { - HookClicked?.Invoke(this); - } - - 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 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; - } - - 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 static IEnumerable<ButtonFlags> GetTargetButtons() - { - var buttons = Enum.GetValues(typeof(ButtonFlags)).Cast<ButtonFlags>(); - - return buttons.Where(a => TargetButtons.Contains(a)); - } - - public static IEnumerable<AxisLayoutFlags> GetTargetAxis() - { - var axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast<AxisLayoutFlags>(); - - return axis.Where(a => TargetAxis.Contains(a) && !IsTrigger(a)); - } - - public static IEnumerable<AxisLayoutFlags> GetTargetTriggers() - { - var axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast<AxisLayoutFlags>(); - - return axis.Where(a => TargetAxis.Contains(a) && IsTrigger(a)); - } - - public bool HasSourceButton(ButtonFlags button) - { - return SourceButtons.Contains(button); - } - - public bool HasSourceAxis(AxisLayoutFlags axis) - { - return SourceAxis.Contains(axis); - } - - 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); - - public event HookClickedEventHandler HookClicked; - public delegate void HookClickedEventHandler(IController controller); - - public event HideClickedEventHandler HideClicked; - public delegate void HideClickedEventHandler(IController controller); - - public event CalibrateClickedEventHandler CalibrateClicked; - public delegate void CalibrateClickedEventHandler(IController controller); - - #endregion - } -} +using HandheldCompanion.Actions; +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.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, + Calibration = 2, + } + + /// <summary> + /// Logique d'interaction pour IController.xaml + /// </summary> + public partial class IController : UserControl + { + // Buttons and axes we should be able to map to. + // When we have target controllers with different buttons (e.g. in VigEm) this will have to be moved elsewhere. + public static readonly List<ButtonFlags> TargetButtons = new() +<<<<<<< HEAD + { + ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4, + ButtonFlags.DPadUp, ButtonFlags.DPadDown, ButtonFlags.DPadLeft, ButtonFlags.DPadRight, + ButtonFlags.Start, ButtonFlags.Back, ButtonFlags.Special, + ButtonFlags.L1, ButtonFlags.R1, + ButtonFlags.LeftStickClick, ButtonFlags.RightStickClick, + }; + + public static readonly List<AxisLayoutFlags> TargetAxis = new() + { + AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick, + AxisLayoutFlags.L2, AxisLayoutFlags.R2, + }; +======= + { + ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4, + ButtonFlags.DPadUp, ButtonFlags.DPadDown, ButtonFlags.DPadLeft, ButtonFlags.DPadRight, + ButtonFlags.Start, ButtonFlags.Back, ButtonFlags.Special, + ButtonFlags.L1, ButtonFlags.R1, + ButtonFlags.LeftStickClick, ButtonFlags.RightStickClick, + }; + + public static readonly List<AxisLayoutFlags> TargetAxis = new() + { + AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick, + AxisLayoutFlags.L2, AxisLayoutFlags.R2, + }; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + public static readonly string defaultGlyph = "\u2753"; + + public ControllerCapabilities Capabilities = ControllerCapabilities.None; + protected SortedDictionary<AxisLayoutFlags, Brush> ColoredAxis = new(); + + protected SortedDictionary<ButtonFlags, Brush> ColoredButtons = new(); + protected FontFamily DefaultFontFamily = new("Segeo WP"); + + public PnPDetails Details; + + // UI + protected FontFamily GlyphFontFamily = new("PromptFont"); + + public ButtonState InjectedButtons = new(); + + public ControllerState Inputs = new(); +<<<<<<< HEAD + public virtual bool IsReady => true; + + public bool IsBusy + { + get + { + return IsEnabled; + } + set + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + IsEnabled = !value; + ProgressBarPanel.Visibility = value ? Visibility.Visible : Visibility.Collapsed; + }); + } + } + + protected List<AxisLayoutFlags> SourceAxis = new() + { + // same as target, we assume all controllers have those axes + AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick, + AxisLayoutFlags.L2, AxisLayoutFlags.R2 + }; +======= + protected bool isPlugged; + + protected List<AxisLayoutFlags> SourceAxis = new() + { + // same as target, we assume all controllers have those axes + AxisLayoutFlags.LeftStick, AxisLayoutFlags.RightStick, + AxisLayoutFlags.L2, AxisLayoutFlags.R2 + }; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // Buttons and axes all controllers have that we can map. + // Additional ones can be added per controller. + protected List<ButtonFlags> SourceButtons = new() +<<<<<<< HEAD + { + // same as target, we assume all controllers have those buttons + ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4, + ButtonFlags.DPadUp, ButtonFlags.DPadDown, ButtonFlags.DPadLeft, ButtonFlags.DPadRight, + ButtonFlags.Start, ButtonFlags.Back, ButtonFlags.Special, + ButtonFlags.L1, ButtonFlags.R1, + ButtonFlags.LeftStickClick, ButtonFlags.RightStickClick, + // additional buttons calculated from the above + ButtonFlags.L2Soft, ButtonFlags.R2Soft, ButtonFlags.L2Full, ButtonFlags.R2Full, + ButtonFlags.LeftStickUp, ButtonFlags.LeftStickDown, ButtonFlags.LeftStickLeft, ButtonFlags.LeftStickRight, + ButtonFlags.RightStickUp, ButtonFlags.RightStickDown, ButtonFlags.RightStickLeft, ButtonFlags.RightStickRight + }; + + private byte _UserIndex = 255; + protected byte UserIndex + { + get + { + return _UserIndex; + } + set + { + _UserIndex = value; + UserIndexChanged?.Invoke(value); + + // UI thread (async) + Application.Current.Dispatcher.Invoke(() => + { + foreach(FrameworkElement frameworkElement in UserIndexPanel.Children) + { + if (frameworkElement is not Border) + continue; + + Border border = (Border)frameworkElement; + int idx = UserIndexPanel.Children.IndexOf(border); + + if (idx == value) + border.SetResourceReference(BackgroundProperty, "AccentAAFillColorDefaultBrush"); + else + border.SetResourceReference(BackgroundProperty, "SystemControlForegroundBaseLowBrush"); + } + }); + } + } + +======= + { + // 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 int UserIndex; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + protected double VibrationStrength = 1.0d; + + public IController() + { +<<<<<<< HEAD + InitializeComponent(); + } + + public virtual void AttachDetails(PnPDetails details) + { + this.Details = details; + Details.isHooked = true; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public virtual void UpdateInputs(long ticks) + { + InputsUpdated?.Invoke(Inputs); + } + + public bool HasMotionSensor() + { + return Capabilities.HasFlag(ControllerCapabilities.MotionSensor); + } + +<<<<<<< HEAD + public bool IsPhysical() + { + return !IsVirtual(); + } + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + 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; + } + +<<<<<<< HEAD + protected void DrawUI() + { + // update name + ControllerName.Text = (IsVirtual() ? Properties.Resources.Controller_Virtual : string.Empty) + ToString(); +======= + protected void DrawControls() + { + // update name + ui_name.Text = (IsVirtual() ? Properties.Resources.Controller_Virtual : string.Empty) + ToString(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // virtual controller shouldn't be visible + if (IsVirtual()) + this.Visibility = Visibility.Collapsed; + } + +<<<<<<< HEAD + protected void UpdateUI() +======= + protected void RefreshControls() +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { +<<<<<<< HEAD + // ui_button_hook.Content = IsPlugged ? Properties.Resources.Controller_Disconnect : Properties.Resources.Controller_Connect; +======= + if (!IsEnabled) + return; + + ui_button_hook.Content = IsPlugged() ? Properties.Resources.Controller_Disconnect : Properties.Resources.Controller_Connect; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ui_button_hide.Content = IsHidden() ? Properties.Resources.Controller_Unhide : Properties.Resources.Controller_Hide; + ui_button_calibrate.Visibility = Capabilities.HasFlag(ControllerCapabilities.Calibration) ? Visibility.Visible : Visibility.Collapsed; + }); + } + + 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) +<<<<<<< HEAD + { + 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; + } + } +======= + { } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + public virtual bool IsConnected() + { + return false; + } + +<<<<<<< HEAD + 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); +======= + public virtual void Rumble(int delay = 125) + { + Task.Run(async () => + { + SetVibration(byte.MaxValue, byte.MaxValue); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + await Task.Delay(delay); + SetVibration(0, 0); + }); + } + +<<<<<<< HEAD +======= + public virtual bool IsPlugged() + { + return isPlugged; + } + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + // this function cannot be called twice + public virtual void Plug() + { + SetVibrationStrength(SettingsManager.GetUInt("VibrationStrength")); + +<<<<<<< HEAD + InjectedButtons.Clear(); + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + ui_button_hook.IsEnabled = false; + }); +======= + isPlugged = true; + + InjectedButtons.Clear(); + + RefreshControls(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + // this function cannot be called twice + public virtual void Unplug() + { +<<<<<<< HEAD + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + ui_button_hook.IsEnabled = true; + }); +======= + isPlugged = false; + + RefreshControls(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + // like Unplug but one that can be safely called when controller is already removed + public virtual void Cleanup() + { + } + + public bool IsHidden() + { +<<<<<<< HEAD + // bool hide_device = HidHide.IsRegistered(Details.deviceInstanceId); + bool hide_base = HidHide.IsRegistered(Details.baseContainerDeviceInstanceId); +======= + // var hide_device = HidHide.IsRegistered(Details.deviceInstanceId); + var hide_base = HidHide.IsRegistered(Details.baseContainerDeviceInstanceId); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + return /* hide_device || */ hide_base; + } + + public virtual void Hide(bool powerCycle = true) + { + HideHID(); + + if (powerCycle) + { +<<<<<<< HEAD + IsBusy = true; + + ControllerManager.PowerCyclers[Details.baseContainerDeviceInstanceId] = true; + CyclePort(); + } + + UpdateUI(); +======= + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + IsEnabled = false; + ProgressBarPanel.Visibility = Visibility.Visible; + }); + + ControllerManager.PowerCyclers[Details.baseContainerDeviceInstanceId] = true; + + CyclePort(); + return; + } + + RefreshControls(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public virtual void Unhide(bool powerCycle = true) + { + UnhideHID(); + + if (powerCycle) + { +<<<<<<< HEAD + IsBusy = true; + + ControllerManager.PowerCyclers[Details.baseContainerDeviceInstanceId] = true; + CyclePort(); + } + + UpdateUI(); +======= + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + IsEnabled = false; + ProgressBarPanel.Visibility = Visibility.Visible; + }); + + ControllerManager.PowerCyclers[Details.baseContainerDeviceInstanceId] = true; + + CyclePort(); + return; + } + + RefreshControls(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public virtual void CyclePort() + { + Details.CyclePort(); + } + + public virtual void SetLightColor(byte R, byte G, byte B) + { + } + +<<<<<<< HEAD + protected void HideHID() +======= + public void HideHID() +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + 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); + */ + } + +<<<<<<< HEAD + protected void UnhideHID() +======= + public void UnhideHID() +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + 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; + } + + protected virtual void Calibrate() + { + } + + protected virtual void ui_button_calibrate_Click(object sender, RoutedEventArgs e) + { + CalibrateClicked?.Invoke(this); + } + + protected virtual void ui_button_hide_Click(object sender, RoutedEventArgs e) + { + HideClicked?.Invoke(this); + } + + protected virtual void ui_button_hook_Click(object sender, RoutedEventArgs e) + { + HookClicked?.Invoke(this); + } + + 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"; +<<<<<<< HEAD +======= + case ButtonFlags.OEM1: + return "\u2780"; + case ButtonFlags.OEM2: + return "\u2781"; + case ButtonFlags.OEM3: + return "\u2782"; + case ButtonFlags.OEM4: + return "\u2783"; + case ButtonFlags.OEM5: + return "\u2784"; + case ButtonFlags.OEM6: + return "\u2785"; + case ButtonFlags.OEM7: + return "\u2786"; + case ButtonFlags.OEM8: + return "\u2787"; + case ButtonFlags.OEM9: + return "\u2788"; + case ButtonFlags.OEM10: + return "\u2789"; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + 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 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; + } + + 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 static IEnumerable<ButtonFlags> GetTargetButtons() + { + var buttons = Enum.GetValues(typeof(ButtonFlags)).Cast<ButtonFlags>(); + + return buttons.Where(a => TargetButtons.Contains(a)); + } + + public static IEnumerable<AxisLayoutFlags> GetTargetAxis() + { + var axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast<AxisLayoutFlags>(); + + return axis.Where(a => TargetAxis.Contains(a) && !IsTrigger(a)); + } + + public static IEnumerable<AxisLayoutFlags> GetTargetTriggers() + { + var axis = Enum.GetValues(typeof(AxisLayoutFlags)).Cast<AxisLayoutFlags>(); + + return axis.Where(a => TargetAxis.Contains(a) && IsTrigger(a)); + } + + public bool HasSourceButton(ButtonFlags button) + { + return SourceButtons.Contains(button); + } + + public bool HasSourceAxis(AxisLayoutFlags axis) + { + return SourceAxis.Contains(axis); + } + + public string GetButtonName(ButtonFlags button) + { + return EnumUtils.GetDescriptionFromEnumValue(button, GetType().Name); + } + + public string GetAxisName(AxisLayoutFlags axis) + { + return EnumUtils.GetDescriptionFromEnumValue(axis, GetType().Name); + } + + #region events + +<<<<<<< HEAD + public event UserIndexChangedEventHandler UserIndexChanged; + public delegate void UserIndexChangedEventHandler(byte UserIndex); + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public event InputsUpdatedEventHandler InputsUpdated; + public delegate void InputsUpdatedEventHandler(ControllerState Inputs); + + public event HookClickedEventHandler HookClicked; + public delegate void HookClickedEventHandler(IController controller); + + public event HideClickedEventHandler HideClicked; + public delegate void HideClickedEventHandler(IController controller); + + public event CalibrateClickedEventHandler CalibrateClicked; + public delegate void CalibrateClickedEventHandler(IController controller); + + #endregion + } +} diff --git a/HandheldCompanion/Controllers/JSController.cs b/HandheldCompanion/Controllers/JSController.cs index de0bd42d8..509512a10 100644 --- a/HandheldCompanion/Controllers/JSController.cs +++ b/HandheldCompanion/Controllers/JSController.cs @@ -1,223 +1,251 @@ -using HandheldCompanion.Inputs; -using HandheldCompanion.Utils; -using Nefarius.Utilities.DeviceManagement.PnP; -using System; -using System.Threading.Tasks; -using System.Windows; -using static JSL; -using Timer = System.Timers.Timer; - -namespace HandheldCompanion.Controllers; - -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; - - protected Timer calibrateTimer = new Timer(5000) { AutoReset = false }; - - public JSController() - { - } - - public JSController(JOY_SETTINGS settings, PnPDetails details) - { - AttachJoySettings(settings); - AttachDetails(details); - - // timer(s) - calibrateTimer.Elapsed += CalibrateTimer_Elapsed; - - // Capabilities - Capabilities |= ControllerCapabilities.MotionSensor; - Capabilities |= ControllerCapabilities.Calibration; - - // UI - DrawUI(); - 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) - { - base.UpdateInputs(ticks); - } - - public virtual void UpdateState() - { - // 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); - Inputs.GyroState.Accelerometer.X = -iMU_STATE.accelX; - Inputs.GyroState.Accelerometer.Y = -iMU_STATE.accelY; - Inputs.GyroState.Accelerometer.Z = iMU_STATE.accelZ; - - Inputs.GyroState.Gyroscope.X = iMU_STATE.gyroX; - Inputs.GyroState.Gyroscope.Y = -iMU_STATE.gyroY; - Inputs.GyroState.Gyroscope.Z = iMU_STATE.gyroZ; - } - - 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)); - } - - protected override void Calibrate() - { - // start calibration - JslResetContinuousCalibration(UserIndex); - JslStartContinuousCalibration(UserIndex); - - calibrateTimer.Start(); - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - ui_button_calibrate.Content = "Calibrating"; - ui_button_calibrate.IsEnabled = false; - }); - } - - public override void CyclePort() - { - string enumerator = Details.GetEnumerator(); - 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; - } - } - - protected override void ui_button_calibrate_Click(object sender, RoutedEventArgs e) - { - // start calibration - Calibrate(); - - base.ui_button_calibrate_Click(sender, e); - } - - private void CalibrateTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e) - { - // stop calibration - JslPauseContinuousCalibration(UserIndex); - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - ui_button_calibrate.Content = "Calibrate"; - ui_button_calibrate.IsEnabled = true; - }); - } - - public void AttachJoySettings(JOY_SETTINGS settings) - { - this.sSETTINGS = settings; - this.UserIndex = (byte)settings.playerNumber; - - JslSetAutomaticCalibration(UserIndex, true); - } +using HandheldCompanion.Inputs; +using HandheldCompanion.Utils; +using Nefarius.Utilities.DeviceManagement.PnP; +<<<<<<< HEAD +using System; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using System.Threading.Tasks; +using System.Windows; +using static JSL; +using Timer = System.Timers.Timer; + +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; + + protected Timer calibrateTimer = new Timer(5000) { AutoReset = false }; + + public JSController() + { + } + + public JSController(JOY_SETTINGS settings, PnPDetails details) + { +<<<<<<< HEAD + AttachJoySettings(settings); + AttachDetails(details); +======= + if (string.IsNullOrEmpty(settings.path)) + return; + + this.sSETTINGS = settings; + this.UserIndex = settings.playerNumber; + + if (details is null) + return; + + Details = details; + Details.isHooked = true; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // timer(s) + calibrateTimer.Elapsed += CalibrateTimer_Elapsed; + + // Capabilities + Capabilities |= ControllerCapabilities.MotionSensor; + Capabilities |= ControllerCapabilities.Calibration; + + // UI +<<<<<<< HEAD + DrawUI(); + UpdateUI(); +======= + InitializeComponent(); + DrawControls(); + RefreshControls(); + + JslSetAutomaticCalibration(UserIndex, true); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + 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) + { + base.UpdateInputs(ticks); + } + + public virtual void UpdateState() + { + // 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); + Inputs.GyroState.Accelerometer.X = -iMU_STATE.accelX; + Inputs.GyroState.Accelerometer.Y = -iMU_STATE.accelY; + Inputs.GyroState.Accelerometer.Z = iMU_STATE.accelZ; + + Inputs.GyroState.Gyroscope.X = iMU_STATE.gyroX; + Inputs.GyroState.Gyroscope.Y = -iMU_STATE.gyroY; + Inputs.GyroState.Gyroscope.Z = iMU_STATE.gyroZ; + } + + 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)); + } + + protected override void Calibrate() + { + // start calibration + JslResetContinuousCalibration(UserIndex); + JslStartContinuousCalibration(UserIndex); + + calibrateTimer.Start(); + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + ui_button_calibrate.Content = "Calibrating"; + ui_button_calibrate.IsEnabled = false; + }); + } + + public override void CyclePort() + { + string enumerator = Details.GetEnumerator(); + 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; + } + } + + protected override void ui_button_calibrate_Click(object sender, RoutedEventArgs e) + { + // start calibration + Calibrate(); + + base.ui_button_calibrate_Click(sender, e); + } + + private void CalibrateTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e) + { + // stop calibration + JslPauseContinuousCalibration(UserIndex); + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + ui_button_calibrate.Content = "Calibrate"; + ui_button_calibrate.IsEnabled = true; + }); + } +<<<<<<< HEAD + + public void AttachJoySettings(JOY_SETTINGS settings) + { + this.sSETTINGS = settings; + this.UserIndex = (byte)settings.playerNumber; + + JslSetAutomaticCalibration(UserIndex, true); + } +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } \ No newline at end of file diff --git a/HandheldCompanion/Controllers/NeptuneController.cs b/HandheldCompanion/Controllers/NeptuneController.cs index 0438e5a77..f988b4eec 100644 --- a/HandheldCompanion/Controllers/NeptuneController.cs +++ b/HandheldCompanion/Controllers/NeptuneController.cs @@ -1,392 +1,427 @@ -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(PnPDetails details) : base() - { - AttachDetails(details); - - // UI - DrawUI(); - UpdateUI(); - - // Additional controller specific source buttons/axes - SourceButtons.AddRange(new List<ButtonFlags> - { ButtonFlags.L4, ButtonFlags.R4, ButtonFlags.L5, ButtonFlags.R5 }); - SourceButtons.AddRange(new List<ButtonFlags> { ButtonFlags.LeftStickTouch, ButtonFlags.RightStickTouch }); - SourceButtons.AddRange(new List<ButtonFlags> - { - ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp, - ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight - }); - SourceButtons.AddRange(new List<ButtonFlags> - { - 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()); - - // open controller - Open(); - } - - public override string ToString() - { - return "Valve Software Steam Controller"; - } - - public override void UpdateInputs(long ticks) - { - 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? - Inputs.GyroState.Accelerometer.X = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelX] / short.MaxValue * 2.0f; - Inputs.GyroState.Accelerometer.Y = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f; - Inputs.GyroState.Accelerometer.Z = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelY] / short.MaxValue * 2.0f; - - // TODO: why Roll/Pitch swapped? - Inputs.GyroState.Gyroscope.X = (float)input.State.AxesState[NeptuneControllerAxis.GyroPitch] / short.MaxValue * 2048.0f; // Roll - Inputs.GyroState.Gyroscope.Y = -(float)input.State.AxesState[NeptuneControllerAxis.GyroRoll] / short.MaxValue * 2048.0f; // Pitch - Inputs.GyroState.Gyroscope.Z = -(float)input.State.AxesState[NeptuneControllerAxis.GyroYaw] / short.MaxValue * 2048.0f; // Yaw - - base.UpdateInputs(ticks); - } - - private void Open() - { - try - { - Controller.Open(); - isConnected = true; - } - catch { } - } - - private void Close() - { - try - { - 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 - { - Controller.OnControllerInputReceived = input => Task.Run(() => OnControllerInputReceived(input)); - - // open controller - Open(); - } - catch (Exception ex) - { - LogManager.LogError("Couldn't initialize GordonController. Exception: {0}", ex.Message); - return; - } - - // disable lizard state - Controller.RequestLizardMode(false); - - // manage rumble thread - rumbleThreadRunning = true; - rumbleThread = new Thread(RumbleThreadLoop); - rumbleThread.IsBackground = true; - rumbleThread.Start(); - - SetVirtualMuted(SettingsManager.GetBoolean("SteamControllerMute")); - - TimerManager.Tick += UpdateInputs; - - base.Plug(); - } - - public override void Unplug() - { - try - { - // restore lizard state - Controller.RequestLizardMode(true); - - // kill rumble thread - rumbleThreadRunning = false; - rumbleThread.Join(); - - // close controller - Close(); - } - catch - { - return; - } - - TimerManager.Tick -= UpdateInputs; - - base.Unplug(); - } - - public override void Cleanup() - { - TimerManager.Tick -= UpdateInputs; - } - - 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(PnPDetails details) : base() + { +<<<<<<< HEAD + AttachDetails(details); + + // UI + DrawUI(); + UpdateUI(); +======= + if (details is null) + return; + + Details = details; + Details.isHooked = true; + + try + { + Controller = new(details.attributes.VendorID, details.attributes.ProductID, details.GetMI()); + + // open controller + Open(); + } + catch (Exception ex) + { + LogManager.LogError("Couldn't initialize NeptuneController. Exception: {0}", ex.Message); + return; + } + + // UI + InitializeComponent(); + DrawControls(); + RefreshControls(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // Additional controller specific source buttons/axes + SourceButtons.AddRange(new List<ButtonFlags> + { ButtonFlags.L4, ButtonFlags.R4, ButtonFlags.L5, ButtonFlags.R5 }); + SourceButtons.AddRange(new List<ButtonFlags> { ButtonFlags.LeftStickTouch, ButtonFlags.RightStickTouch }); + SourceButtons.AddRange(new List<ButtonFlags> + { + ButtonFlags.LeftPadClick, ButtonFlags.LeftPadTouch, ButtonFlags.LeftPadClickUp, + ButtonFlags.LeftPadClickDown, ButtonFlags.LeftPadClickLeft, ButtonFlags.LeftPadClickRight + }); + SourceButtons.AddRange(new List<ButtonFlags> + { + 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); +<<<<<<< HEAD + + 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()); + + // open controller + Open(); +======= + + TargetAxis.Add(AxisLayoutFlags.LeftPad); + TargetAxis.Add(AxisLayoutFlags.RightPad); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public override string ToString() + { + return "Valve Software Steam Controller"; + } + + public override void UpdateInputs(long ticks) + { + 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? + Inputs.GyroState.Accelerometer.X = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelX] / short.MaxValue * 2.0f; + Inputs.GyroState.Accelerometer.Y = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelZ] / short.MaxValue * 2.0f; + Inputs.GyroState.Accelerometer.Z = -(float)input.State.AxesState[NeptuneControllerAxis.GyroAccelY] / short.MaxValue * 2.0f; + + // TODO: why Roll/Pitch swapped? + Inputs.GyroState.Gyroscope.X = (float)input.State.AxesState[NeptuneControllerAxis.GyroPitch] / short.MaxValue * 2048.0f; // Roll + Inputs.GyroState.Gyroscope.Y = -(float)input.State.AxesState[NeptuneControllerAxis.GyroRoll] / short.MaxValue * 2048.0f; // Pitch + Inputs.GyroState.Gyroscope.Z = -(float)input.State.AxesState[NeptuneControllerAxis.GyroYaw] / short.MaxValue * 2048.0f; // Yaw + + base.UpdateInputs(ticks); + } + + private void Open() + { + try + { + Controller.Open(); + isConnected = true; + } + catch { } + } + + private void Close() + { + try + { + Controller.Close(); + isConnected = false; + } + catch { } + } + +<<<<<<< HEAD + 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(); + } + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + private void OnControllerInputReceived(NeptuneControllerInputEventArgs 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; + } + + // disable lizard state + Controller.RequestLizardMode(false); + + // manage rumble thread + rumbleThreadRunning = true; + rumbleThread = new Thread(RumbleThreadLoop); + rumbleThread.IsBackground = true; + rumbleThread.Start(); + + SetVirtualMuted(SettingsManager.GetBoolean("SteamControllerMute")); + + TimerManager.Tick += UpdateInputs; + + base.Plug(); + } + + public override void Unplug() + { + try + { + // restore lizard state + Controller.RequestLizardMode(true); + + // kill rumble thread + rumbleThreadRunning = false; + rumbleThread.Join(); + + // close controller + Close(); + } + catch + { + return; + } + + TimerManager.Tick -= UpdateInputs; + + base.Unplug(); + } + + public override void Cleanup() + { + TimerManager.Tick -= UpdateInputs; + } + + 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/SteamController.cs b/HandheldCompanion/Controllers/SteamController.cs index 9dbb16aa5..3e90bd82f 100644 --- a/HandheldCompanion/Controllers/SteamController.cs +++ b/HandheldCompanion/Controllers/SteamController.cs @@ -1,203 +1,230 @@ -using HandheldCompanion.Inputs; -using steam_hidapi.net.Hid; - -namespace HandheldCompanion.Controllers -{ - public abstract class SteamController : IController - { - protected bool isConnected = false; - protected bool isVirtualMuted = false; - - public SteamController() : base() - { - Capabilities |= ControllerCapabilities.MotionSensor; - } - - 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 steam_hidapi.net.Hid; + +namespace HandheldCompanion.Controllers +{ + public abstract class SteamController : IController + { + protected bool isConnected = false; + protected bool isVirtualMuted = false; + + public SteamController() : base() + { + Capabilities |= ControllerCapabilities.MotionSensor; + } + + public override bool IsConnected() + { + return isConnected; + } + + public virtual bool IsVirtualMuted() + { + return isVirtualMuted; + } + + public virtual void SetVirtualMuted(bool mute) + { + isVirtualMuted = mute; + } + +<<<<<<< HEAD +======= + public override void Hide(bool powerCycle = true) + { + HideHID(); + + RefreshControls(); + } + + public override void Unhide(bool powerCycle = true) + { + UnhideHID(); + + RefreshControls(); + } + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + 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: +<<<<<<< HEAD + return "\u2276"; + case ButtonFlags.L5: + return "\u2278"; + case ButtonFlags.R4: + return "\u2277"; + case ButtonFlags.R5: + return "\u2279"; +======= + return "\u219c\u24f8"; + case ButtonFlags.L5: + return "\u219c\u24f9"; + case ButtonFlags.R4: + return "\u219d\u24f8"; + case ButtonFlags.R5: + return "\u219d\u24f9"; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + 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 4e06b6faf..1527f8fb8 100644 --- a/HandheldCompanion/Controllers/XInputController.cs +++ b/HandheldCompanion/Controllers/XInputController.cs @@ -1,435 +1,524 @@ -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, 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); - } - - 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) => UpdateInputs(ticks, true); - base.Plug(); - } - - public override void Unplug() - { - TimerManager.Tick -= (ticks) => UpdateInputs(ticks, true); - base.Unplug(); - } - - public override void Cleanup() - { - TimerManager.Tick -= UpdateInputs; - } - - 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 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; +<<<<<<< HEAD +======= +using Nefarius.Utilities.DeviceManagement.PnP; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using SharpDX.XInput; +using System; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using System.Windows; +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) + { +<<<<<<< HEAD + 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 XInputController(Controller controller, PnPDetails details) : this() + { + Controller = controller; + UserIndex = (int)controller.UserIndex; + + if (!IsConnected()) + return; + + this.Details = details; + if (Details is null) + return; + + Details.isHooked = true; + + // 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))); + + InitializeComponent(); + DrawControls(); + RefreshControls(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public void UpdateController(Controller controller) + { + Controller = controller; + UserIndex = (int)controller.UserIndex; + } + + 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, 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 { } + +<<<<<<< HEAD + if (commit) + base.UpdateInputs(ticks); +======= + base.UpdateInputs(ticks); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + 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; + +<<<<<<< HEAD + 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 { } +======= + var LeftMotorSpeed = (ushort)((double)LargeMotor / byte.MaxValue * ushort.MaxValue * VibrationStrength); + var RightMotorSpeed = (ushort)((double)SmallMotor / byte.MaxValue * ushort.MaxValue * VibrationStrength); + + var vibration = new Vibration { LeftMotorSpeed = LeftMotorSpeed, RightMotorSpeed = RightMotorSpeed }; + Controller.SetVibration(vibration); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public override void Plug() + { +<<<<<<< HEAD + TimerManager.Tick += (ticks) => UpdateInputs(ticks, true); +======= + TimerManager.Tick += UpdateInputs; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + base.Plug(); + } + + public override void Unplug() + { +<<<<<<< HEAD + TimerManager.Tick -= (ticks) => UpdateInputs(ticks, true); +======= + TimerManager.Tick -= UpdateInputs; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + base.Unplug(); + } + + public override void Cleanup() + { + TimerManager.Tick -= UpdateInputs; + } + + public static UserIndex TryGetUserIndex(PnPDetails details) + { + XInputCapabilitiesEx capabilitiesEx = new(); + + for (int idx = 0; idx < 4; idx++) + { + if (XInputGetCapabilitiesEx(1, idx, 0, ref capabilitiesEx) == 0) + { +<<<<<<< HEAD + if (capabilitiesEx.ProductId != details.ProductID || capabilitiesEx.VendorId != details.VendorID) +======= + if (capabilitiesEx.ProductId != details.attributes.ProductID || capabilitiesEx.VendorId != details.attributes.VendorID) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + continue; + + var devices = DeviceManager.GetDetails(capabilitiesEx.VendorId, capabilitiesEx.ProductId); + if (devices.FirstOrDefault() is not null) + return (UserIndex)idx; + } + } + + return SharpDX.XInput.UserIndex.Any; + } + +<<<<<<< HEAD + public virtual void AttachController(byte userIndex) + { + if (UserIndex == userIndex) + return; + + UserIndex = userIndex; + Controller = new((UserIndex)userIndex); + } + + public override void Hide(bool powerCycle = true) + { +======= + public override void Hide(bool powerCycle = true) + { + if (powerCycle) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + ProgressBarWarning.Text = Properties.Resources.ControllerPage_XInputControllerWarning; + }); + } + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + base.Hide(powerCycle); + } + + public override void Unhide(bool powerCycle = true) + { +<<<<<<< HEAD +======= + if (powerCycle) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + ProgressBarWarning.Text = Properties.Resources.ControllerPage_XInputControllerWarning; + }); + } + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + base.Unhide(powerCycle); + } + + 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/Mapping/AxisMapping.xaml b/HandheldCompanion/Controls/Mapping/AxisMapping.xaml index 1bac32689..5617eadea 100644 --- a/HandheldCompanion/Controls/Mapping/AxisMapping.xaml +++ b/HandheldCompanion/Controls/Mapping/AxisMapping.xaml @@ -1,534 +1,546 @@ -<local:IMapping - x:Class="HandheldCompanion.Controls.AxisMapping" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:HandheldCompanion.Controls" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - d:Background="White" - d:DesignHeight="1500" - d:DesignWidth="800" - mc:Ignorable="d"> - - <Expander HorizontalAlignment="Stretch"> - <Expander.Header> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition /> - </Grid.ColumnDefinitions> - - <ui:FontIcon - Name="Icon" - Width="40" - Height="40" - HorizontalAlignment="Left" - VerticalAlignment="Center" - FontFamily="{DynamicResource PhoneFontFamilyNormal}" - FontSize="14" /> - <TextBlock - Name="Name" - Grid.Column="1" - HorizontalAlignment="Left" - VerticalAlignment="Center" - FontSize="14" /> - - <DockPanel Grid.Column="2" HorizontalAlignment="Right"> - <ComboBox - Name="ActionComboBox" - Width="120" - VerticalAlignment="Center" - SelectedIndex="0" - SelectionChanged="Action_SelectionChanged"> - <ComboBoxItem Content="Disabled" IsEnabled="True" /> - <ComboBoxItem Content="Button" IsEnabled="False" /> - <ComboBoxItem Content="Joystick" IsEnabled="True" /> - <ComboBoxItem Content="Keyboard" IsEnabled="False" /> - <ComboBoxItem Content="Mouse" IsEnabled="True" /> - <ComboBoxItem Content="Trigger" IsEnabled="False" /> - </ComboBox> - - <ComboBox - Name="TargetComboBox" - Width="140" - Margin="12,0,0,0" - VerticalAlignment="Center" - IsEnabled="False" - SelectionChanged="Target_SelectionChanged" /> - </DockPanel> - </Grid> - </Expander.Header> - <Expander.Content> - <StackPanel> - <!-- Axis 2 Axis --> - <Grid - Name="Axis2Axis" - d:Visibility="Visible" - Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=2}"> - - <ui:SimpleStackPanel Spacing="6"> - <ui:SimpleStackPanel Spacing="6"> - - <!-- Improve circularity --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="9*" MinWidth="200" /> - <ColumnDefinition MinWidth="80" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Improve circularity" /> - - <ui:ToggleSwitch - Name="Axis2AxisImproveCircularity" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Axis2AxisImproveCircularity_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Auto rotate --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="9*" MinWidth="200" /> - <ColumnDefinition MinWidth="80" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Rotate to match screen orientation" /> - - <ui:ToggleSwitch - Name="Axis2AxisAutoRotate" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Axis2AxisAutoRotate_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Axis rotation --> - <Grid IsEnabled="{Binding IsOn, Converter={StaticResource InvertBooleanConverter}, ElementName=Axis2AxisAutoRotate}"> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Rotate axis (degrees)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2AxisRotation, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2AxisRotation" - FlowDirection="LeftToRight" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="270" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="90" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis_Rotation_Slider_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Inner deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Inner deadzone (%)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2AxisInnerDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2AxisInnerDeadzone" - FlowDirection="LeftToRight" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis_InnerDeadzone_Slider_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Outer deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Outer deadzone (%)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2AxisOuterDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2AxisOuterDeadzone" - FlowDirection="RightToLeft" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis_OuterDeadzone_Slider_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Anti deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Anti deadzone (%)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2AxisAntiDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2AxisAntiDeadzone" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis_AntiDeadZone_Slider_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - - <!-- Axis 2 Mouse --> - <Grid - Name="Axis2Mouse" - d:Visibility="Visible" - Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=4}"> - - <ui:SimpleStackPanel Spacing="6"> - <ui:SimpleStackPanel Spacing="6"> - - <!-- Auto rotate --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="9*" MinWidth="200" /> - <ColumnDefinition MinWidth="80" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Rotate to match screen orientation" /> - - <ui:ToggleSwitch - Name="Axis2MouseAutoRotate" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Axis2MouseAutoRotate_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Axis rotation --> - <Grid IsEnabled="{Binding IsOn, Converter={StaticResource InvertBooleanConverter}, ElementName=Axis2MouseAutoRotate}"> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Rotate axis (degrees)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2MouseRotation, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2MouseRotation" - FlowDirection="LeftToRight" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="270" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="90" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis2MouseRotation_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Sensitivity --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Sensitivity" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2MousePointerSpeed, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2MousePointerSpeed" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="5" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis2MousePointerSpeed_ValueChanged" - Value="33" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Acceleration --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Acceleration" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, StringFormat=N2, ElementName=Axis2MouseAcceleration, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2MouseAcceleration" - AutoToolTipPrecision="2" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="2.00" - Minimum="1.00" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="0.05" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis2MouseAcceleration_ValueChanged" - Value="1.00" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Filtering --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="2*" MinWidth="200" /> - <ColumnDefinition Width="8*" MinWidth="120" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Filtering (touchpad only)" /> - - <ui:ToggleSwitch - Name="Axis2MouseFiltering" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Axis2MouseFiltering_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Filtering (hidden) --> - <Grid d:Visibility="Visible" Visibility="{Binding ElementName=Axis2MouseFiltering, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Filtering cutoff (less is more filter)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, StringFormat=N3, ElementName=Axis2MouseFilterCutoff, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2MouseFilterCutoff" - AutoToolTipPrecision="3" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="0.1" - Minimum="0.01" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="0.005" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis2MouseFilterCutoff_ValueChanged" - Value="0.05" /> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </Grid> - - <!-- Deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Deadzone (joystick only)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2MouseDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2MouseDeadzone" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis2MouseDeadzone_ValueChanged" - Value="10" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - </StackPanel> - </Expander.Content> - </Expander> +<local:IMapping + x:Class="HandheldCompanion.Controls.AxisMapping" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:HandheldCompanion.Controls" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + d:Background="White" + d:DesignHeight="1500" + d:DesignWidth="800" + mc:Ignorable="d"> + + <Expander HorizontalAlignment="Stretch"> + <Expander.Header> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition /> + </Grid.ColumnDefinitions> + + <ui:FontIcon + Name="Icon" + Width="40" + Height="40" + HorizontalAlignment="Left" + VerticalAlignment="Center" + FontFamily="{DynamicResource PhoneFontFamilyNormal}" + FontSize="14" /> + <TextBlock + Name="Name" + Grid.Column="1" + HorizontalAlignment="Left" + VerticalAlignment="Center" + FontSize="14" /> + + <DockPanel Grid.Column="2" HorizontalAlignment="Right"> + <ComboBox + Name="ActionComboBox" + Width="120" + VerticalAlignment="Center" + SelectedIndex="0" + SelectionChanged="Action_SelectionChanged"> + <ComboBoxItem Content="Disabled" IsEnabled="True" /> + <ComboBoxItem Content="Button" IsEnabled="False" /> + <ComboBoxItem Content="Joystick" IsEnabled="True" /> + <ComboBoxItem Content="Keyboard" IsEnabled="False" /> + <ComboBoxItem Content="Mouse" IsEnabled="True" /> + <ComboBoxItem Content="Trigger" IsEnabled="False" /> + </ComboBox> + + <ComboBox + Name="TargetComboBox" + Width="140" + Margin="12,0,0,0" + VerticalAlignment="Center" + IsEnabled="False" + SelectionChanged="Target_SelectionChanged" /> + </DockPanel> + </Grid> + </Expander.Header> + <Expander.Content> + <StackPanel> + <!-- Axis 2 Axis --> + <Grid + Name="Axis2Axis" + d:Visibility="Visible" + Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=2}"> + + <ui:SimpleStackPanel Spacing="6"> + <ui:SimpleStackPanel Spacing="6"> + + <!-- Improve circularity --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="80" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Improve circularity" /> + + <ui:ToggleSwitch + Name="Axis2AxisImproveCircularity" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Axis2AxisImproveCircularity_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Auto rotate --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="80" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Rotate to match screen orientation" /> + + <ui:ToggleSwitch + Name="Axis2AxisAutoRotate" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Axis2AxisAutoRotate_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Axis rotation --> + <Grid IsEnabled="{Binding IsOn, Converter={StaticResource InvertBooleanConverter}, ElementName=Axis2AxisAutoRotate}"> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Rotate axis (degrees)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2AxisRotation, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2AxisRotation" + FlowDirection="LeftToRight" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="270" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="90" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="Axis_Rotation_Slider_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Inner deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Inner deadzone (%)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2AxisInnerDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2AxisInnerDeadzone" + FlowDirection="LeftToRight" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="Axis_InnerDeadzone_Slider_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Outer deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Outer deadzone (%)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2AxisOuterDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2AxisOuterDeadzone" + FlowDirection="RightToLeft" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="Axis_OuterDeadzone_Slider_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Anti deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Anti deadzone (%)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2AxisAntiDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2AxisAntiDeadzone" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="Axis_AntiDeadZone_Slider_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + + <!-- Axis 2 Mouse --> + <Grid + Name="Axis2Mouse" + d:Visibility="Visible" + Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=4}"> + + <ui:SimpleStackPanel Spacing="6"> + <ui:SimpleStackPanel Spacing="6"> + + <!-- Auto rotate --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="80" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Rotate to match screen orientation" /> + + <ui:ToggleSwitch + Name="Axis2MouseAutoRotate" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Axis2MouseAutoRotate_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Axis rotation --> + <Grid IsEnabled="{Binding IsOn, Converter={StaticResource InvertBooleanConverter}, ElementName=Axis2MouseAutoRotate}"> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Rotate axis (degrees)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2MouseRotation, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2MouseRotation" + FlowDirection="LeftToRight" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="270" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="90" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="Axis2MouseRotation_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Sensitivity --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Sensitivity" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2MousePointerSpeed, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2MousePointerSpeed" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="5" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis2MousePointerSpeed_ValueChanged" + Value="33" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Acceleration --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Acceleration" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, StringFormat=N2, ElementName=Axis2MouseAcceleration, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2MouseAcceleration" + AutoToolTipPrecision="2" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="2.00" + Minimum="1.00" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="0.05" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis2MouseAcceleration_ValueChanged" + Value="1.00" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Filtering --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="2*" MinWidth="200" /> + <ColumnDefinition Width="8*" MinWidth="120" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Filtering (touchpad only)" /> + + <ui:ToggleSwitch + Name="Axis2MouseFiltering" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Axis2MouseFiltering_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Filtering (hidden) --> + <Grid d:Visibility="Visible" Visibility="{Binding ElementName=Axis2MouseFiltering, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Filtering cutoff (less is more filter)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, StringFormat=N3, ElementName=Axis2MouseFilterCutoff, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2MouseFilterCutoff" + AutoToolTipPrecision="3" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="0.1" + Minimum="0.01" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="0.005" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis2MouseFilterCutoff_ValueChanged" + Value="0.05" /> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </Grid> + + <!-- Deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Deadzone (joystick only)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2MouseDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2MouseDeadzone" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis2MouseDeadzone_ValueChanged" + Value="10" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + </StackPanel> + </Expander.Content> + </Expander> </local:IMapping> \ No newline at end of file diff --git a/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml b/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml index 1f4394781..16ebd6c68 100644 --- a/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml +++ b/HandheldCompanion/Controls/Mapping/ButtonMapping.xaml @@ -1,336 +1,339 @@ -<local:IMapping - x:Class="HandheldCompanion.Controls.ButtonMapping" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:HandheldCompanion.Controls" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - d:Background="White" - d:DesignHeight="1000" - d:DesignWidth="800" - mc:Ignorable="d"> - - <Expander HorizontalAlignment="Stretch"> - <Expander.Header> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition /> - </Grid.ColumnDefinitions> - - <DockPanel MinWidth="150"> - <ui:FontIcon - Name="Icon" - Height="40" - Margin="0,0,5,0" - HorizontalAlignment="Left" - VerticalAlignment="Center" - FontFamily="{DynamicResource PhoneFontFamilyNormal}" - FontSize="14" /> - <TextBlock - Name="Name" - HorizontalAlignment="Left" - VerticalAlignment="Center" - FontSize="14" /> - </DockPanel> - - <ComboBox - Name="PressComboBox" - Grid.Column="1" - Width="120" - VerticalAlignment="Center" - SelectedIndex="0" - SelectionChanged="Press_SelectionChanged"> - <ComboBoxItem Content="Short press" /> - <ComboBoxItem Content="Long press" /> - </ComboBox> - - <DockPanel Grid.Column="2" HorizontalAlignment="Right"> - <ComboBox - Name="ActionComboBox" - Width="120" - Margin="12,0,0,0" - VerticalAlignment="Center" - SelectedIndex="0" - SelectionChanged="Action_SelectionChanged"> - <ComboBoxItem Content="Disabled" IsEnabled="True" /> - <ComboBoxItem Content="Button" IsEnabled="True" /> - <ComboBoxItem Content="Joystick" IsEnabled="False" /> - <ComboBoxItem Content="Keyboard" IsEnabled="True" /> - <ComboBoxItem Content="Mouse" IsEnabled="True" /> - <ComboBoxItem Content="Trigger" IsEnabled="False" /> - </ComboBox> - - <ComboBox - Name="TargetComboBox" - Width="140" - Margin="12,0,0,0" - VerticalAlignment="Center" - IsEnabled="False" - SelectionChanged="Target_SelectionChanged" /> - </DockPanel> - </Grid> - </Expander.Header> - <Expander.Content> - <ui:SimpleStackPanel Spacing="6"> - <!-- Button 2 Button, Long press --> - <Grid d:Visibility="Visible" Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=1|3|4}"> - - <ui:SimpleStackPanel Name="Button2ButtonPressDelay" Spacing="6"> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Long press delay (ms, longest applies)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=LongPressDelaySlider, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="LongPressDelaySlider" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="2000" - Minimum="100" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="50" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="LongPressDelaySlider_ValueChanged" - Value="450" /> - - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - - <!-- Button 2 Keyboard/Mouse, modifiers --> - <Grid - Name="Button2ButtonWithModifier" - d:Visibility="Visible" - Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=3|4}"> - - <ui:SimpleStackPanel Spacing="6"> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="1*" MinWidth="200" /> - <ColumnDefinition Width="4*" MinWidth="180" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Modifier(s)" /> - - <DockPanel Grid.Column="1" HorizontalAlignment="Right"> - <ComboBox - Name="ModifierComboBox" - Width="180" - VerticalAlignment="Center" - SelectedIndex="0" - SelectionChanged="Modifier_SelectionChanged"> - <ComboBoxItem Content="None" /> - <ComboBoxItem Content="Shift" /> - <ComboBoxItem Content="Control" /> - <ComboBoxItem Content="Alt" /> - <ComboBoxItem Content="Shift + Control" /> - <ComboBoxItem Content="Shift + Alt" /> - <ComboBoxItem Content="Control + Alt" /> - <ComboBoxItem Content="Shift + Control + Alt" /> - </ComboBox> - </DockPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - - <!-- Button 2 Button/Keyboard/Mouse --> - <Grid - Name="Button2Button" - d:Visibility="Visible" - Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=1|3|4}"> - - <ui:SimpleStackPanel Spacing="6"> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="2*" MinWidth="200" /> - <ColumnDefinition Width="8*" MinWidth="120" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Hold to repeat" /> - - <ui:ToggleSwitch - Name="Toggle_Turbo" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_Turbo_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Repeat rate (hidden) --> - <Grid d:Visibility="Visible" Visibility="{Binding ElementName=Toggle_Turbo, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> - - <ui:SimpleStackPanel Spacing="6"> - - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Repeat rate (ms)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Turbo_Slider, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Turbo_Slider" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="10" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="5" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Turbo_Slider_ValueChanged" - Value="30" /> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </Grid> - </ui:SimpleStackPanel> - - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="2*" MinWidth="200" /> - <ColumnDefinition Width="8*" MinWidth="120" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Press to toggle" /> - - <ui:ToggleSwitch - Name="Toggle_Toggle" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_Toggle_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - - <!-- Button 2 Button, haptic --> - <Grid - Name="Button2ButtonHaptic" - d:Visibility="Visible" - Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=1|3|4}"> - - <ui:SimpleStackPanel Spacing="6"> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="2*" MinWidth="200" /> - <ColumnDefinition Width="8*" MinWidth="120" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Haptic feedback" /> - - <DockPanel Grid.Column="1" HorizontalAlignment="Right"> - <ComboBox - Name="HapticModeComboBox" - Width="105" - Margin="6,0,0,0" - VerticalAlignment="Center" - SelectedIndex="0" - SelectionChanged="HapticMode_SelectionChanged"> - <ComboBoxItem Content="Off" /> - <ComboBoxItem Content="Down" /> - <ComboBoxItem Content="Up" /> - <ComboBoxItem Content="Both" /> - </ComboBox> - <ComboBox - Name="HapticStrengthComboBox" - Width="105" - Margin="6,0,0,0" - VerticalAlignment="Center" - IsEnabled="False" - SelectedIndex="0" - SelectionChanged="HapticStrength_SelectionChanged"> - <ComboBoxItem Content="Low" /> - <ComboBoxItem Content="Medium" /> - <ComboBoxItem Content="High" /> - </ComboBox> - </DockPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - - </ui:SimpleStackPanel> - </Expander.Content> - </Expander> +<local:IMapping + x:Class="HandheldCompanion.Controls.ButtonMapping" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:HandheldCompanion.Controls" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + d:Background="White" + d:DesignHeight="1000" + d:DesignWidth="800" + mc:Ignorable="d"> + + <Expander HorizontalAlignment="Stretch"> + <Expander.Header> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition /> + </Grid.ColumnDefinitions> + + <DockPanel MinWidth="150"> + <ui:FontIcon + Name="Icon" + Height="40" + Margin="0,0,5,0" + HorizontalAlignment="Left" + VerticalAlignment="Center" + FontFamily="{DynamicResource PhoneFontFamilyNormal}" + FontSize="14" /> + <TextBlock + Name="Name" + HorizontalAlignment="Left" + VerticalAlignment="Center" + FontSize="14" /> + </DockPanel> + + <ComboBox + Name="PressComboBox" + Grid.Column="1" + Width="120" + VerticalAlignment="Center" + SelectedIndex="0" + SelectionChanged="Press_SelectionChanged"> + <ComboBoxItem Content="Short press" /> + <ComboBoxItem Content="Long press" /> + </ComboBox> + + <DockPanel Grid.Column="2" HorizontalAlignment="Right"> + <ComboBox + Name="ActionComboBox" + Width="120" + Margin="12,0,0,0" + VerticalAlignment="Center" + SelectedIndex="0" + SelectionChanged="Action_SelectionChanged"> + <ComboBoxItem Content="Disabled" IsEnabled="True" /> + <ComboBoxItem Content="Button" IsEnabled="True" /> + <ComboBoxItem Content="Joystick" IsEnabled="False" /> + <ComboBoxItem Content="Keyboard" IsEnabled="True" /> + <ComboBoxItem Content="Mouse" IsEnabled="True" /> + <ComboBoxItem Content="Trigger" IsEnabled="False" /> + </ComboBox> + + <ComboBox + Name="TargetComboBox" + Width="140" + Margin="12,0,0,0" + VerticalAlignment="Center" + IsEnabled="False" + SelectionChanged="Target_SelectionChanged" /> + </DockPanel> + </Grid> + </Expander.Header> + <Expander.Content> + <ui:SimpleStackPanel Spacing="6"> + <!-- Button 2 Button, Long press --> + <Grid d:Visibility="Visible" Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=1|3|4}"> + + <ui:SimpleStackPanel Name="Button2ButtonPressDelay" Spacing="6"> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Long press delay (ms, longest applies)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=LongPressDelaySlider, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="LongPressDelaySlider" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="2000" + Minimum="100" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="50" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="LongPressDelaySlider_ValueChanged" + Value="450" /> + + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + + <!-- Button 2 Keyboard/Mouse, modifiers --> + <Grid + Name="Button2ButtonWithModifier" + d:Visibility="Visible" + Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=3|4}"> + + <ui:SimpleStackPanel Spacing="6"> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="1*" MinWidth="200" /> + <ColumnDefinition Width="4*" MinWidth="180" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Modifier(s)" /> + + <DockPanel Grid.Column="1" HorizontalAlignment="Right"> + <ComboBox + Name="ModifierComboBox" + Width="180" + VerticalAlignment="Center" + SelectedIndex="0" + SelectionChanged="Modifier_SelectionChanged"> + <ComboBoxItem Content="None" /> + <ComboBoxItem Content="Shift" /> + <ComboBoxItem Content="Control" /> + <ComboBoxItem Content="Alt" /> + <ComboBoxItem Content="Shift + Control" /> + <ComboBoxItem Content="Shift + Alt" /> + <ComboBoxItem Content="Control + Alt" /> + <ComboBoxItem Content="Shift + Control + Alt" /> + </ComboBox> + </DockPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + + <!-- Button 2 Button/Keyboard/Mouse --> + <Grid + Name="Button2Button" + d:Visibility="Visible" + Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=1|3|4}"> + + <ui:SimpleStackPanel Spacing="6"> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="2*" MinWidth="200" /> + <ColumnDefinition Width="8*" MinWidth="120" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Hold to repeat" /> + + <ui:ToggleSwitch + Name="Toggle_Turbo" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_Turbo_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Repeat rate (hidden) --> + <Grid d:Visibility="Visible" Visibility="{Binding ElementName=Toggle_Turbo, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + + <ui:SimpleStackPanel Spacing="6"> + + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Repeat rate (ms)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Turbo_Slider, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Turbo_Slider" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="10" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="5" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="Turbo_Slider_ValueChanged" + Value="30" /> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </Grid> + </ui:SimpleStackPanel> + + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="2*" MinWidth="200" /> + <ColumnDefinition Width="8*" MinWidth="120" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Press to toggle" /> + + <ui:ToggleSwitch + Name="Toggle_Toggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_Toggle_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + + <!-- Button 2 Button, haptic --> + <Grid + Name="Button2ButtonHaptic" + d:Visibility="Visible" + Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=1|3|4}"> + + <ui:SimpleStackPanel Spacing="6"> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="2*" MinWidth="200" /> + <ColumnDefinition Width="8*" MinWidth="120" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Haptic feedback" /> + + <DockPanel Grid.Column="1" HorizontalAlignment="Right"> + <ComboBox + Name="HapticModeComboBox" + Width="105" + Margin="6,0,0,0" + VerticalAlignment="Center" + SelectedIndex="0" + SelectionChanged="HapticMode_SelectionChanged"> + <ComboBoxItem Content="Off" /> + <ComboBoxItem Content="Down" /> + <ComboBoxItem Content="Up" /> + <ComboBoxItem Content="Both" /> + </ComboBox> + <ComboBox + Name="HapticStrengthComboBox" + Width="105" + Margin="6,0,0,0" + VerticalAlignment="Center" + IsEnabled="False" + SelectedIndex="0" + SelectionChanged="HapticStrength_SelectionChanged"> + <ComboBoxItem Content="Low" /> + <ComboBoxItem Content="Medium" /> + <ComboBoxItem Content="High" /> + </ComboBox> + </DockPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + + </ui:SimpleStackPanel> + </Expander.Content> + </Expander> </local:IMapping> \ No newline at end of file diff --git a/HandheldCompanion/Controls/Mapping/GyroMapping.xaml b/HandheldCompanion/Controls/Mapping/GyroMapping.xaml index 206ee2186..54a23a41a 100644 --- a/HandheldCompanion/Controls/Mapping/GyroMapping.xaml +++ b/HandheldCompanion/Controls/Mapping/GyroMapping.xaml @@ -1,610 +1,637 @@ -<local:IMapping - x:Class="HandheldCompanion.Controls.GyroMapping" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:HandheldCompanion.Controls" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - d:Background="White" - d:DesignHeight="1500" - d:DesignWidth="800" - mc:Ignorable="d"> - - <Expander HorizontalAlignment="Stretch"> - <Expander.Header> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition /> - </Grid.ColumnDefinitions> - - <ui:FontIcon - Name="Icon" - Width="40" - Height="40" - HorizontalAlignment="Left" - VerticalAlignment="Center" - FontFamily="{DynamicResource PhoneFontFamilyNormal}" - FontSize="14" /> - <TextBlock - Name="Name" - Grid.Column="1" - HorizontalAlignment="Left" - VerticalAlignment="Center" - FontSize="14" /> - - <DockPanel Grid.Column="2" HorizontalAlignment="Right"> - <ComboBox - Name="ActionComboBox" - Width="120" - VerticalAlignment="Center" - SelectedIndex="0" - SelectionChanged="Action_SelectionChanged"> - <ComboBoxItem Content="Disabled" IsEnabled="True" /> - <ComboBoxItem Content="Button" IsEnabled="False" /> - <ComboBoxItem Content="Joystick" IsEnabled="True" /> - <ComboBoxItem Content="Keyboard" IsEnabled="False" /> - <ComboBoxItem Content="Mouse" IsEnabled="True" /> - <ComboBoxItem Content="Trigger" IsEnabled="False" /> - </ComboBox> - - <ComboBox - Name="TargetComboBox" - Width="140" - Margin="12,0,0,0" - VerticalAlignment="Center" - IsEnabled="False" - SelectionChanged="Target_SelectionChanged" /> - </DockPanel> - </Grid> - </Expander.Header> - <Expander.Content> - <ui:SimpleStackPanel Spacing="6"> - <!-- Shared --> - <Grid Name="Shared"> - <ui:SimpleStackPanel Spacing="6"> - <ui:SimpleStackPanel Spacing="6"> - - <!-- Gyro input --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <StackPanel Orientation="Vertical"> - <TextBlock - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.ProfilesPage_StyleofInput}" - ToolTip="{x:Static resx:Resources.ProfilesPage_StyleofInputTooltip}" /> - <TextBlock - Name="Text_InputHint" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - TextWrapping="Wrap" /> - </StackPanel> - - <ComboBox - Name="cB_Input" - Grid.Column="1" - Margin="12,0,0,0" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - HorizontalContentAlignment="Left" - SelectionChanged="cB_Input_SelectionChanged" /> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Gyro activator --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <StackPanel Orientation="Vertical"> - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.ProfilesPage_UMCMotionOnOff}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ProfilesPage_UMCMotionOnOffDesc}" - TextWrapping="Wrap" /> - </StackPanel> - - <ui:SimpleStackPanel - Name="UMC_Activator" - Grid.Column="1" - Margin="12,0,0,0" - HorizontalAlignment="Stretch" - Orientation="Vertical" - Spacing="6"> - <ComboBox - Name="cB_UMC_MotionDefaultOffOn" - HorizontalAlignment="Stretch" - HorizontalContentAlignment="Left" - SelectionChanged="cB_UMC_MotionDefaultOffOn_SelectionChanged"> - <ComboBoxItem Content="{x:Static resx:Resources.ProfilesPage_UMCMotionOff}" /> - <ComboBoxItem Content="{x:Static resx:Resources.ProfilesPage_UMCMotionOn}" /> - <ComboBoxItem Content="{x:Static resx:Resources.ProfilesPage_UMCMotionToggle}" /> - </ComboBox> - </ui:SimpleStackPanel> - </Grid> - - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Axis 2 Axis --> - <Grid - Name="Axis2Axis" - d:Visibility="Visible" - Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=2}"> - - <ui:SimpleStackPanel Spacing="6"> - <ui:SimpleStackPanel Spacing="6"> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Gyro Weight --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <StackPanel Orientation="Vertical"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Gyrometer weight" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="Modify the gyrometer weight when applied against joystick movements" - TextWrapping="Wrap" /> - </StackPanel> - - <DockPanel - Grid.Column="1" - Margin="12,0,0,0" - ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N1, ElementName=Slider_GyroWeight, Mode=OneWay}" - TextAlignment="Center" /> - <Slider - x:Name="Slider_GyroWeight" - Margin="6,0,0,0" - VerticalAlignment="Center" - AutoToolTipPrecision="1" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="2.0" - Minimum="1.0" - Style="{DynamicResource SliderStyle1}" - TickFrequency="0.1" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Slider_GyroWeight_ValueChanged" /> - </DockPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Improve circularity --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="9*" MinWidth="200" /> - <ColumnDefinition MinWidth="80" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Improve circularity" /> - - <ui:ToggleSwitch - Name="Axis2AxisImproveCircularity" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Axis2AxisImproveCircularity_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Auto rotate --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="9*" MinWidth="200" /> - <ColumnDefinition MinWidth="80" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Rotate to match screen orientation" /> - - <ui:ToggleSwitch - Name="Axis2AxisAutoRotate" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Axis2AxisAutoRotate_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Axis rotation --> - <Grid IsEnabled="{Binding IsOn, Converter={StaticResource InvertBooleanConverter}, ElementName=Axis2AxisAutoRotate}"> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Rotate axis (degrees)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2AxisRotation, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2AxisRotation" - FlowDirection="LeftToRight" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="270" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="90" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis_Rotation_Slider_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Inner deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Inner deadzone (%)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2AxisInnerDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2AxisInnerDeadzone" - FlowDirection="LeftToRight" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis_InnerDeadzone_Slider_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Outer deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Outer deadzone (%)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2AxisOuterDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2AxisOuterDeadzone" - FlowDirection="RightToLeft" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis_OuterDeadzone_Slider_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Anti deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Anti deadzone (%)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2AxisAntiDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2AxisAntiDeadzone" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis_AntiDeadZone_Slider_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - - <!-- Axis 2 Mouse --> - <Grid - Name="Axis2Mouse" - d:Visibility="Visible" - Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=4}"> - - <ui:SimpleStackPanel Spacing="6"> - <ui:SimpleStackPanel Spacing="6"> - - <!-- Auto rotate --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="9*" MinWidth="200" /> - <ColumnDefinition MinWidth="80" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Rotate to match screen orientation" /> - - <ui:ToggleSwitch - Name="Axis2MouseAutoRotate" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Axis2MouseAutoRotate_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Axis rotation --> - <Grid IsEnabled="{Binding IsOn, Converter={StaticResource InvertBooleanConverter}, ElementName=Axis2MouseAutoRotate}"> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Rotate axis (degrees)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2MouseRotation, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2MouseRotation" - FlowDirection="LeftToRight" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="270" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="90" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis2MouseRotation_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Sensitivity --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Sensitivity" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2MousePointerSpeed, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2MousePointerSpeed" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="5" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis2MousePointerSpeed_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Acceleration --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Acceleration" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, StringFormat=N2, ElementName=Axis2MouseAcceleration, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2MouseAcceleration" - AutoToolTipPrecision="2" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="2.00" - Minimum="1.00" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="0.05" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis2MouseAcceleration_ValueChanged" - Value="1.00" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Deadzone" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Axis2MouseDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Axis2MouseDeadzone" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Axis2MouseDeadzone_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - </ui:SimpleStackPanel> - </Expander.Content> - </Expander> +<local:IMapping + x:Class="HandheldCompanion.Controls.GyroMapping" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:HandheldCompanion.Controls" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + d:Background="White" + d:DesignHeight="1500" + d:DesignWidth="800" + mc:Ignorable="d"> + + <Expander HorizontalAlignment="Stretch"> + <Expander.Header> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition /> + </Grid.ColumnDefinitions> + + <ui:FontIcon + Name="Icon" + Width="40" + Height="40" + HorizontalAlignment="Left" + VerticalAlignment="Center" + FontFamily="{DynamicResource PhoneFontFamilyNormal}" + FontSize="14" /> + <TextBlock + Name="Name" + Grid.Column="1" + HorizontalAlignment="Left" + VerticalAlignment="Center" + FontSize="14" /> + + <DockPanel Grid.Column="2" HorizontalAlignment="Right"> + <ComboBox + Name="ActionComboBox" + Width="120" + VerticalAlignment="Center" + SelectedIndex="0" + SelectionChanged="Action_SelectionChanged"> + <ComboBoxItem Content="Disabled" IsEnabled="True" /> + <ComboBoxItem Content="Button" IsEnabled="False" /> + <ComboBoxItem Content="Joystick" IsEnabled="True" /> + <ComboBoxItem Content="Keyboard" IsEnabled="False" /> + <ComboBoxItem Content="Mouse" IsEnabled="True" /> + <ComboBoxItem Content="Trigger" IsEnabled="False" /> + </ComboBox> + + <ComboBox + Name="TargetComboBox" + Width="140" + Margin="12,0,0,0" + VerticalAlignment="Center" + IsEnabled="False" + SelectionChanged="Target_SelectionChanged" /> + </DockPanel> + </Grid> + </Expander.Header> + <Expander.Content> + <ui:SimpleStackPanel Spacing="6"> + <!-- Shared --> + <Grid Name="Shared"> + <ui:SimpleStackPanel Spacing="6"> + <ui:SimpleStackPanel Spacing="6"> + + <!-- Gyro input --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel Orientation="Vertical"> + <TextBlock + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_StyleofInput}" + ToolTip="{x:Static resx:Resources.ProfilesPage_StyleofInputTooltip}" /> + <TextBlock + Name="Text_InputHint" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + TextWrapping="Wrap" /> + </StackPanel> + + <ComboBox + Name="cB_Input" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + HorizontalContentAlignment="Left" + SelectionChanged="cB_Input_SelectionChanged" /> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Gyro activator --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel Orientation="Vertical"> + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_UMCMotionOnOff}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_UMCMotionOnOffDesc}" + TextWrapping="Wrap" /> + </StackPanel> + + <ui:SimpleStackPanel + Name="UMC_Activator" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Stretch" + Orientation="Vertical" + Spacing="6"> + <ComboBox + Name="cB_UMC_MotionDefaultOffOn" + HorizontalAlignment="Stretch" + HorizontalContentAlignment="Left" + SelectionChanged="cB_UMC_MotionDefaultOffOn_SelectionChanged"> + <ComboBoxItem Content="{x:Static resx:Resources.ProfilesPage_UMCMotionOff}" /> + <ComboBoxItem Content="{x:Static resx:Resources.ProfilesPage_UMCMotionOn}" /> + <ComboBoxItem Content="{x:Static resx:Resources.ProfilesPage_UMCMotionToggle}" /> + </ComboBox> + </ui:SimpleStackPanel> + </Grid> + + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Axis 2 Axis --> + <Grid + Name="Axis2Axis" + d:Visibility="Visible" + Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=2}"> + + <ui:SimpleStackPanel Spacing="6"> + <ui:SimpleStackPanel Spacing="6"> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Gyro Weight --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Gyrometer weight" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="Modify the gyrometer weight when applied against joystick movements" + TextWrapping="Wrap" /> + </StackPanel> + + <DockPanel + Grid.Column="1" + Margin="12,0,0,0" + ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N1, ElementName=Slider_GyroWeight, Mode=OneWay}" + TextAlignment="Center" /> + <Slider + x:Name="Slider_GyroWeight" + Margin="6,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="1" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="2.0" + Minimum="1.0" + Style="{DynamicResource SliderStyle1}" + TickFrequency="0.1" + TickPlacement="BottomRight" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Slider_GyroWeight_ValueChanged" /> + </DockPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Improve circularity --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="80" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Improve circularity" /> + + <ui:ToggleSwitch + Name="Axis2AxisImproveCircularity" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Axis2AxisImproveCircularity_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Auto rotate --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="80" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Rotate to match screen orientation" /> + + <ui:ToggleSwitch + Name="Axis2AxisAutoRotate" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Axis2AxisAutoRotate_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Axis rotation --> + <Grid IsEnabled="{Binding IsOn, Converter={StaticResource InvertBooleanConverter}, ElementName=Axis2AxisAutoRotate}"> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Rotate axis (degrees)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2AxisRotation, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2AxisRotation" + FlowDirection="LeftToRight" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="270" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="90" + TickPlacement="BottomRight" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis_Rotation_Slider_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Inner deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Inner deadzone (%)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2AxisInnerDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2AxisInnerDeadzone" + FlowDirection="LeftToRight" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis_InnerDeadzone_Slider_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Outer deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Outer deadzone (%)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2AxisOuterDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2AxisOuterDeadzone" + FlowDirection="RightToLeft" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis_OuterDeadzone_Slider_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Anti deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Anti deadzone (%)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2AxisAntiDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2AxisAntiDeadzone" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis_AntiDeadZone_Slider_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + + <!-- Axis 2 Mouse --> + <Grid + Name="Axis2Mouse" + d:Visibility="Visible" + Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=4}"> + + <ui:SimpleStackPanel Spacing="6"> + <ui:SimpleStackPanel Spacing="6"> + + <!-- Auto rotate --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="80" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Rotate to match screen orientation" /> + + <ui:ToggleSwitch + Name="Axis2MouseAutoRotate" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Axis2MouseAutoRotate_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Axis rotation --> + <Grid IsEnabled="{Binding IsOn, Converter={StaticResource InvertBooleanConverter}, ElementName=Axis2MouseAutoRotate}"> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Rotate axis (degrees)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2MouseRotation, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2MouseRotation" + FlowDirection="LeftToRight" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="270" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="90" + TickPlacement="BottomRight" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis2MouseRotation_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Sensitivity --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Sensitivity" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2MousePointerSpeed, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2MousePointerSpeed" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="5" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis2MousePointerSpeed_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Acceleration --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Acceleration" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, StringFormat=N2, ElementName=Axis2MouseAcceleration, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2MouseAcceleration" + AutoToolTipPrecision="2" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="2.00" + Minimum="1.00" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="0.05" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis2MouseAcceleration_ValueChanged" + Value="1.00" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Deadzone" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Axis2MouseDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Axis2MouseDeadzone" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Axis2MouseDeadzone_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + </ui:SimpleStackPanel> + </Expander.Content> + </Expander> </local:IMapping> \ No newline at end of file diff --git a/HandheldCompanion/Controls/Mapping/GyroMapping.xaml.cs b/HandheldCompanion/Controls/Mapping/GyroMapping.xaml.cs index 5dae39bec..be4bbde4e 100644 --- a/HandheldCompanion/Controls/Mapping/GyroMapping.xaml.cs +++ b/HandheldCompanion/Controls/Mapping/GyroMapping.xaml.cs @@ -1,504 +1,508 @@ -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; - -/// <summary> -/// Interaction logic for GyroMapping.xaml -/// </summary> -public partial class GyroMapping : IMapping -{ - private Hotkey GyroHotkey = new(60); - - public GyroMapping() - { - InitializeComponent(); - - - // draw input modes - foreach (var mode in (MotionInput[])Enum.GetValues(typeof(MotionInput))) - { - // create panel - var panel = new SimpleStackPanel - { Spacing = 6, Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center }; - - // create icon - var icon = new FontIcon { Glyph = "" }; - - switch (mode) - { - default: - case MotionInput.PlayerSpace: - icon.Glyph = "\uF119"; - break; - case MotionInput.JoystickCamera: - icon.Glyph = "\uE714"; - break; - case MotionInput.AutoRollYawSwap: - icon.Glyph = "\uE7F8"; - break; - case MotionInput.JoystickSteering: - icon.Glyph = "\uEC47"; - break; - } - - if (icon.Glyph != "") - panel.Children.Add(icon); - - // create textblock - var description = EnumUtils.GetDescriptionFromEnumValue(mode); - var text = new TextBlock { Text = description }; - panel.Children.Add(text); - - cB_Input.Items.Add(panel); - } - - HotkeysManager.HotkeyCreated += TriggerCreated; - InputsManager.TriggerUpdated += TriggerUpdated; - } - - public GyroMapping(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 - GyroHotkey.inputsChord.State = ((GyroActions)actions).MotionTrigger.Clone() as ButtonState; - GyroHotkey.DrawInput(); - - 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 - var 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) - { - // default values - Actions = new AxisActions() - { - Axis = GyroActions.DefaultAxisLayoutFlags, - AxisAntiDeadZone = GyroActions.DefaultAxisAntiDeadZone, - MotionTrigger = GyroHotkey.inputsChord.State.Clone() as ButtonState - }; - } - - // we need a controller to get compatible buttons - if (controller is null) - return; - - foreach (var axis in IController.GetTargetAxis()) - { - // create a label, store ButtonFlags as Tag and Label as controller specific string - var buttonLabel = new Label { Tag = axis, Content = controller.GetAxisName(axis) }; - TargetComboBox.Items.Add(buttonLabel); - - if (axis.Equals(((AxisActions)Actions).Axis)) - TargetComboBox.SelectedItem = buttonLabel; - } - - if (TargetComboBox.SelectedItem is null) - TargetComboBox.SelectedIndex = 0; - - // settings - Slider_GyroWeight.Value = ((AxisActions)Actions).gyroWeight; - 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() - { - MouseType = GyroActions.DefaultMouseActionsType, - Sensivity = GyroActions.DefaultSensivity, - Deadzone = GyroActions.DefaultDeadzone, - MotionTrigger = GyroHotkey.inputsChord.State.Clone() as ButtonState - }; - } - - 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 - var buttonLabel = new Label - { Tag = mouseType, Content = EnumUtils.GetDescriptionFromEnumValue(mouseType) }; - TargetComboBox.Items.Add(buttonLabel); - - if (mouseType.Equals(((MouseActions)Actions).MouseType)) - TargetComboBox.SelectedItem = buttonLabel; - } - - if (TargetComboBox.SelectedItem is null) - TargetComboBox.SelectedIndex = 0; - - // 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; - } - - cB_Input.SelectedIndex = (int)((GyroActions)this.Actions).MotionInput; - cB_UMC_MotionDefaultOffOn.SelectedIndex = (int)((GyroActions)this.Actions).MotionMode; - - 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> e) - { - if (this.Actions is null) - return; - - switch (this.Actions.ActionType) - { - case ActionType.Mouse: - ((MouseActions)this.Actions).Acceleration = (float)Axis2MouseAcceleration.Value; - break; - } - - base.Update(); - } - - private void cB_Input_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (cB_Input.SelectedIndex == -1) - return; - - if (this.Actions is null) - return; - - MotionInput input = (MotionInput)cB_Input.SelectedIndex; - Text_InputHint.Text = EnumUtils.GetDescriptionFromEnumValue(input, string.Empty, "Desc"); - - ((GyroActions)this.Actions).MotionInput = (MotionInput)cB_Input.SelectedIndex; - - base.Update(); - } - - private void cB_UMC_MotionDefaultOffOn_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (cB_UMC_MotionDefaultOffOn.SelectedIndex == -1) - return; - - if (this.Actions is null) - return; - - ((GyroActions)this.Actions).MotionMode = (MotionMode)cB_UMC_MotionDefaultOffOn.SelectedIndex; - - base.Update(); - } - - private void Slider_GyroWeight_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (this.Actions is null) - return; - - ((GyroActions)this.Actions).gyroWeight = (float)Slider_GyroWeight.Value; - - base.Update(); - } - - private void TriggerCreated(Hotkey hotkey) - { - switch (hotkey.inputsHotkey.Listener) - { - case "shortcutProfilesPage@": - { - var hotkeyBorder = hotkey.GetControl(); - if (hotkeyBorder is null || hotkeyBorder.Parent is not null) - return; - - // pull hotkey - GyroHotkey = hotkey; - - UMC_Activator.Children.Add(hotkeyBorder); - } - break; - } - } - - private void TriggerUpdated(string listener, InputsChord inputs, InputsManager.ListenerType type) - { - if (this.Actions is null) - return; - - switch (listener) - { - case "shortcutProfilesPage@": - case "shortcutProfilesPage@@": - ((GyroActions)this.Actions).MotionTrigger = inputs.State.Clone() as ButtonState; - - // recover previous motion trigger, if any - GyroHotkey.inputsChord.State = ((GyroActions)Actions).MotionTrigger.Clone() as ButtonState; - GyroHotkey.DrawInput(); - break; - } - - base.Update(); - } +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; + +/// <summary> +/// Interaction logic for GyroMapping.xaml +/// </summary> +public partial class GyroMapping : IMapping +{ + private Hotkey GyroHotkey = new(60); + + public GyroMapping() + { + InitializeComponent(); + + + // draw input modes + foreach (var mode in (MotionInput[])Enum.GetValues(typeof(MotionInput))) + { + // create panel + var panel = new SimpleStackPanel + { Spacing = 6, Orientation = Orientation.Horizontal, VerticalAlignment = VerticalAlignment.Center }; + + // create icon + var icon = new FontIcon { Glyph = "" }; + + switch (mode) + { + default: + case MotionInput.PlayerSpace: + icon.Glyph = "\uF119"; + break; + case MotionInput.JoystickCamera: + icon.Glyph = "\uE714"; + break; + case MotionInput.AutoRollYawSwap: + icon.Glyph = "\uE7F8"; + break; + case MotionInput.JoystickSteering: + icon.Glyph = "\uEC47"; + break; + } + + if (icon.Glyph != "") + panel.Children.Add(icon); + + // create textblock + var description = EnumUtils.GetDescriptionFromEnumValue(mode); + var text = new TextBlock { Text = description }; + panel.Children.Add(text); + + cB_Input.Items.Add(panel); + } + + HotkeysManager.HotkeyCreated += TriggerCreated; + InputsManager.TriggerUpdated += TriggerUpdated; + } + + public GyroMapping(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 + GyroHotkey.inputsChord.State = ((GyroActions)actions).MotionTrigger.Clone() as ButtonState; + GyroHotkey.DrawInput(); + + 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 + var 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) + { + // default values + Actions = new AxisActions() + { + Axis = GyroActions.DefaultAxisLayoutFlags, + AxisAntiDeadZone = GyroActions.DefaultAxisAntiDeadZone, + MotionTrigger = GyroHotkey.inputsChord.State.Clone() as ButtonState + }; + } + + // we need a controller to get compatible buttons + if (controller is null) + return; + + foreach (var axis in IController.GetTargetAxis()) + { + // create a label, store ButtonFlags as Tag and Label as controller specific string + var buttonLabel = new Label { Tag = axis, Content = controller.GetAxisName(axis) }; + TargetComboBox.Items.Add(buttonLabel); + + if (axis.Equals(((AxisActions)Actions).Axis)) + TargetComboBox.SelectedItem = buttonLabel; + } + + if (TargetComboBox.SelectedItem is null) + TargetComboBox.SelectedIndex = 0; + + // settings + Slider_GyroWeight.Value = ((AxisActions)Actions).gyroWeight; + 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() + { + MouseType = GyroActions.DefaultMouseActionsType, + Sensivity = GyroActions.DefaultSensivity, + Deadzone = GyroActions.DefaultDeadzone, + MotionTrigger = GyroHotkey.inputsChord.State.Clone() as ButtonState + }; + } + + 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 + var buttonLabel = new Label + { Tag = mouseType, Content = EnumUtils.GetDescriptionFromEnumValue(mouseType) }; + TargetComboBox.Items.Add(buttonLabel); + + if (mouseType.Equals(((MouseActions)Actions).MouseType)) + TargetComboBox.SelectedItem = buttonLabel; + } + + if (TargetComboBox.SelectedItem is null) + TargetComboBox.SelectedIndex = 0; + + // 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; + } + + cB_Input.SelectedIndex = (int)((GyroActions)this.Actions).MotionInput; + cB_UMC_MotionDefaultOffOn.SelectedIndex = (int)((GyroActions)this.Actions).MotionMode; + + 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> 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<double> e) + { + if (this.Actions is null) + return; + + switch (this.Actions.ActionType) + { + case ActionType.Mouse: + ((MouseActions)this.Actions).Acceleration = (float)Axis2MouseAcceleration.Value; + break; + } + + base.Update(); + } + + private void cB_Input_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (cB_Input.SelectedIndex == -1) + return; + + if (this.Actions is null) + return; + + MotionInput input = (MotionInput)cB_Input.SelectedIndex; +<<<<<<< HEAD + Text_InputHint.Text = EnumUtils.GetDescriptionFromEnumValue(input, string.Empty, "Desc"); +======= + Text_InputHint.Text = Profile.InputDescription[input]; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + ((GyroActions)this.Actions).MotionInput = (MotionInput)cB_Input.SelectedIndex; + + base.Update(); + } + + private void cB_UMC_MotionDefaultOffOn_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (cB_UMC_MotionDefaultOffOn.SelectedIndex == -1) + return; + + if (this.Actions is null) + return; + + ((GyroActions)this.Actions).MotionMode = (MotionMode)cB_UMC_MotionDefaultOffOn.SelectedIndex; + + base.Update(); + } + + private void Slider_GyroWeight_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (this.Actions is null) + return; + + ((GyroActions)this.Actions).gyroWeight = (float)Slider_GyroWeight.Value; + + base.Update(); + } + + private void TriggerCreated(Hotkey hotkey) + { + switch (hotkey.inputsHotkey.Listener) + { + case "shortcutProfilesPage@": + { + var hotkeyBorder = hotkey.GetControl(); + if (hotkeyBorder is null || hotkeyBorder.Parent is not null) + return; + + // pull hotkey + GyroHotkey = hotkey; + + UMC_Activator.Children.Add(hotkeyBorder); + } + break; + } + } + + private void TriggerUpdated(string listener, InputsChord inputs, InputsManager.ListenerType type) + { + if (this.Actions is null) + return; + + switch (listener) + { + case "shortcutProfilesPage@": + case "shortcutProfilesPage@@": + ((GyroActions)this.Actions).MotionTrigger = inputs.State.Clone() as ButtonState; + + // recover previous motion trigger, if any + GyroHotkey.inputsChord.State = ((GyroActions)Actions).MotionTrigger.Clone() as ButtonState; + GyroHotkey.DrawInput(); + break; + } + + base.Update(); + } } \ No newline at end of file diff --git a/HandheldCompanion/Controls/Mapping/TriggerMapping.xaml b/HandheldCompanion/Controls/Mapping/TriggerMapping.xaml index 0d1c04207..193fb574e 100644 --- a/HandheldCompanion/Controls/Mapping/TriggerMapping.xaml +++ b/HandheldCompanion/Controls/Mapping/TriggerMapping.xaml @@ -1,191 +1,200 @@ -<local:IMapping - x:Class="HandheldCompanion.Controls.TriggerMapping" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:local="clr-namespace:HandheldCompanion.Controls" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - d:Background="White" - d:DesignHeight="1000" - d:DesignWidth="800" - mc:Ignorable="d"> - - <Expander HorizontalAlignment="Stretch"> - <Expander.Header> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition Width="Auto" /> - <ColumnDefinition /> - </Grid.ColumnDefinitions> - - <ui:FontIcon - Name="Icon" - Width="40" - Height="40" - HorizontalAlignment="Left" - VerticalAlignment="Center" - FontFamily="{DynamicResource PhoneFontFamilyNormal}" - FontSize="14" /> - <TextBlock - Name="Name" - Grid.Column="1" - HorizontalAlignment="Left" - VerticalAlignment="Center" - FontSize="14" /> - - <DockPanel Grid.Column="2" HorizontalAlignment="Right"> - <ComboBox - Name="ActionComboBox" - Width="120" - VerticalAlignment="Center" - SelectedIndex="0" - SelectionChanged="Action_SelectionChanged"> - <ComboBoxItem Content="Disabled" IsEnabled="True" /> - <ComboBoxItem Content="Button" IsEnabled="False" /> - <ComboBoxItem Content="Joystick" IsEnabled="False" /> - <ComboBoxItem Content="Keyboard" IsEnabled="False" /> - <ComboBoxItem Content="Mouse" IsEnabled="False" /> - <ComboBoxItem Content="Trigger" IsEnabled="True" /> - </ComboBox> - - <ComboBox - Name="TargetComboBox" - Width="140" - Margin="12,0,0,0" - VerticalAlignment="Center" - IsEnabled="False" - SelectionChanged="Target_SelectionChanged" /> - </DockPanel> - </Grid> - </Expander.Header> - <Expander.Content> - <StackPanel> - <!-- Trigger 2 Trigger --> - <Grid - Name="Trigger2Trigger" - d:Visibility="Visible" - Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=5}"> - - <ui:SimpleStackPanel Spacing="6"> - <ui:SimpleStackPanel Spacing="6"> - - <!-- Inner deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Inner deadzone (%)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Trigger2TriggerInnerDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Trigger2TriggerInnerDeadzone" - FlowDirection="LeftToRight" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Trigger2TriggerInnerDeadzone_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Outer deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Outer deadzone (%)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Trigger2TriggerOuterDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Trigger2TriggerOuterDeadzone" - FlowDirection="RightToLeft" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Trigger2TriggerOuterDeadzone_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <!-- Inner deadzone --> - <Grid> - <ui:SimpleStackPanel Spacing="6"> - <Grid> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Anti deadzone (%)" /> - <TextBox - HorizontalAlignment="Right" - HorizontalContentAlignment="Center" - IsReadOnly="True" - Text="{Binding Value, ElementName=Trigger2TriggerAntiDeadzone, Mode=OneWay}" /> - </Grid> - - <Slider - x:Name="Trigger2TriggerAntiDeadzone" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Maximum="100" - Minimum="0" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Trigger2TriggerAntiDeadzone_ValueChanged" /> - </ui:SimpleStackPanel> - </Grid> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - </StackPanel> - </Expander.Content> - </Expander> +<local:IMapping + x:Class="HandheldCompanion.Controls.TriggerMapping" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="clr-namespace:HandheldCompanion.Controls" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + d:Background="White" + d:DesignHeight="1000" + d:DesignWidth="800" + mc:Ignorable="d"> + + <Expander HorizontalAlignment="Stretch"> + <Expander.Header> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition Width="Auto" /> + <ColumnDefinition /> + </Grid.ColumnDefinitions> + + <ui:FontIcon + Name="Icon" + Width="40" + Height="40" + HorizontalAlignment="Left" + VerticalAlignment="Center" + FontFamily="{DynamicResource PhoneFontFamilyNormal}" + FontSize="14" /> + <TextBlock + Name="Name" + Grid.Column="1" + HorizontalAlignment="Left" + VerticalAlignment="Center" + FontSize="14" /> + + <DockPanel Grid.Column="2" HorizontalAlignment="Right"> + <ComboBox + Name="ActionComboBox" + Width="120" + VerticalAlignment="Center" + SelectedIndex="0" + SelectionChanged="Action_SelectionChanged"> + <ComboBoxItem Content="Disabled" IsEnabled="True" /> + <ComboBoxItem Content="Button" IsEnabled="False" /> + <ComboBoxItem Content="Joystick" IsEnabled="False" /> + <ComboBoxItem Content="Keyboard" IsEnabled="False" /> + <ComboBoxItem Content="Mouse" IsEnabled="False" /> + <ComboBoxItem Content="Trigger" IsEnabled="True" /> + </ComboBox> + + <ComboBox + Name="TargetComboBox" + Width="140" + Margin="12,0,0,0" + VerticalAlignment="Center" + IsEnabled="False" + SelectionChanged="Target_SelectionChanged" /> + </DockPanel> + </Grid> + </Expander.Header> + <Expander.Content> + <StackPanel> + <!-- Trigger 2 Trigger --> + <Grid + Name="Trigger2Trigger" + d:Visibility="Visible" + Visibility="{Binding ElementName=ActionComboBox, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=5}"> + + <ui:SimpleStackPanel Spacing="6"> + <ui:SimpleStackPanel Spacing="6"> + + <!-- Inner deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Inner deadzone (%)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Trigger2TriggerInnerDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Trigger2TriggerInnerDeadzone" + FlowDirection="LeftToRight" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Trigger2TriggerInnerDeadzone_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Outer deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Outer deadzone (%)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Trigger2TriggerOuterDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Trigger2TriggerOuterDeadzone" + FlowDirection="RightToLeft" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Trigger2TriggerOuterDeadzone_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <!-- Inner deadzone --> + <Grid> + <ui:SimpleStackPanel Spacing="6"> + <Grid> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Anti deadzone (%)" /> + <TextBox + HorizontalAlignment="Right" + HorizontalContentAlignment="Center" + IsReadOnly="True" + Text="{Binding Value, ElementName=Trigger2TriggerAntiDeadzone, Mode=OneWay}" /> + </Grid> + + <Slider + x:Name="Trigger2TriggerAntiDeadzone" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="0" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" +<<<<<<< HEAD + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ValueChanged="Trigger2TriggerAntiDeadzone_ValueChanged" /> + </ui:SimpleStackPanel> + </Grid> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + </StackPanel> + </Expander.Content> + </Expander> </local:IMapping> \ No newline at end of file diff --git a/HandheldCompanion/Controls/ProcessEx.xaml b/HandheldCompanion/Controls/ProcessEx.xaml index 1d2382797..29dd21d0f 100644 --- a/HandheldCompanion/Controls/ProcessEx.xaml +++ b/HandheldCompanion/Controls/ProcessEx.xaml @@ -1,74 +1,87 @@ -<UserControl - x:Class="HandheldCompanion.Controls.ProcessEx" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - d:Background="White" - d:Width="400" - mc:Ignorable="d"> - - <Expander - Padding="20,12,12,12" - HorizontalAlignment="Stretch" - d:IsExpanded="True"> - <Expander.Header> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="24" /> - <ColumnDefinition Width="1*" /> - </Grid.ColumnDefinitions> - - <Image - Name="ProcessIcon" - Grid.Column="0" - Width="24" - Height="24" /> - <StackPanel Grid.Column="1" Margin="12,0,0,0"> - <TextBlock x:Name="ExecutableTextBlock" Style="{StaticResource BodyTextBlockStyle}" /> - <TextBlock - x:Name="TitleTextBlock" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - TextWrapping="NoWrap" /> - </StackPanel> - </Grid> - </Expander.Header> - - <Expander.Content> - <ui:SimpleStackPanel Spacing="6"> - <!-- Suspend --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition /> - <ColumnDefinition Width="0.2*" MinWidth="80" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Suspend process" /> - - <ui:ToggleSwitch - Name="SuspendToggle" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="SuspendToggle_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> - - <!-- Kill Process --> - <Button - Name="B_KillProcess" - HorizontalAlignment="Stretch" - Click="B_KillProcess_Clicked" - Content="Kill process" - Style="{DynamicResource AccentButtonStyle}" /> - </ui:SimpleStackPanel> - </Expander.Content> - </Expander> +<UserControl + x:Class="HandheldCompanion.Controls.ProcessEx" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + d:Background="White" + d:Width="400" + mc:Ignorable="d"> + + <Expander + Padding="20,12,12,12" + HorizontalAlignment="Stretch" + d:IsExpanded="True"> + <Expander.Header> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="24" /> + <ColumnDefinition Width="1*" /> + </Grid.ColumnDefinitions> + + <Image + Name="ProcessIcon" + Grid.Column="0" + Width="24" + Height="24" /> + <StackPanel Grid.Column="1" Margin="12,0,0,0"> + <TextBlock x:Name="ExecutableTextBlock" Style="{StaticResource BodyTextBlockStyle}" /> + <TextBlock + x:Name="TitleTextBlock" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + TextWrapping="NoWrap" /> + </StackPanel> + </Grid> + </Expander.Header> + + <Expander.Content> + <ui:SimpleStackPanel Spacing="6"> + <!-- Suspend --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition /> + <ColumnDefinition Width="0.2*" MinWidth="80" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Suspend process" /> + + <ui:ToggleSwitch + Name="SuspendToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="SuspendToggle_Toggled" /> + </Grid> + +<<<<<<< HEAD + <!-- Separator --> + <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> + + <!-- Kill Process --> + <Button + Name="B_KillProcess" + HorizontalAlignment="Stretch" + Click="B_KillProcess_Clicked" + Content="Kill process" + Style="{DynamicResource AccentButtonStyle}" /> +======= + <!-- Separator --> + <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> + + <!-- Kill Process --> + <Button + Name="B_KillProcess" + Click="B_KillProcess_Clicked" + Content="Kill process" + HorizontalAlignment="Stretch" + Style="{DynamicResource AccentButtonStyle}"/> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + </ui:SimpleStackPanel> + </Expander.Content> + </Expander> </UserControl> \ No newline at end of file diff --git a/HandheldCompanion/Controls/ProcessEx.xaml.cs b/HandheldCompanion/Controls/ProcessEx.xaml.cs index 2f5475195..aeabc082c 100644 --- a/HandheldCompanion/Controls/ProcessEx.xaml.cs +++ b/HandheldCompanion/Controls/ProcessEx.xaml.cs @@ -1,216 +1,220 @@ -using HandheldCompanion.Managers; -using HandheldCompanion.Platforms; -using HandheldCompanion.Utils; -using System; -using System.Diagnostics; -using System.Drawing; -using System.IO; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; - -namespace HandheldCompanion.Controls; - -/// <summary> -/// Logique d'interaction pour ProcessEx.xaml -/// </summary> -public partial class ProcessEx : UserControl, IDisposable -{ - public enum ProcessFilter - { - Allowed = 0, - Restricted = 1, - Ignored = 2, - HandheldCompanion = 3, - Desktop = 4 - } - - public string _Executable; - - private string _Title; - - public ConcurrentList<int> Children = new(); - - public ProcessFilter Filter; - - public ImageSource imgSource; - - public ProcessThread MainThread; - - public IntPtr MainWindowHandle; - - public string Path; - - private ThreadState prevThreadState = ThreadState.Terminated; - private ThreadWaitReason prevThreadWaitReason = ThreadWaitReason.UserRequest; - - public Process Process; - private readonly int ProcessId; - - public ProcessEx() - { - InitializeComponent(); - } - - public ProcessEx(Process process, string path, string executable, ProcessFilter filter) : this() - { - Process = process; - ProcessId = process.Id; - - Path = path; - - Executable = executable; - MainWindowTitle = executable; // temporary, will be overwritten by ProcessManager - - Filter = filter; - Platform = PlatformManager.GetPlatform(Process); - - if (!string.IsNullOrEmpty(path) && File.Exists(path)) - { - var icon = Icon.ExtractAssociatedIcon(Path); - if (icon is not null) - { - imgSource = icon.ToImageSource(); - ProcessIcon.Source = imgSource; - } - } - } - - public string MainWindowTitle - { - get => _Title; - - set - { - _Title = value; - TitleTextBlock.Text = value; - } - } - - public string Executable - { - get => _Executable; - - set - { - _Executable = value; - ExecutableTextBlock.Text = value; - } - } - - public PlatformType Platform { get; set; } - - public void Dispose() - { - if (Process is not null) - Process.Dispose(); - if (MainThread is not null) - MainThread.Dispose(); - Children.Dispose(); - - GC.SuppressFinalize(this); //now, the finalizer won't be called - } - - public int GetProcessId() - { - return ProcessId; - } - - public bool HasExited() - { - if (Process is not null) - return Process.HasExited; - - return true; - } - - public void Refresh() - { - if (Process.HasExited) - return; - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (MainThread.ThreadState) - { - case ThreadState.Wait: - { - // monitor if the process main thread was suspended or resumed - if (MainThread.WaitReason != prevThreadWaitReason) - { - prevThreadWaitReason = MainThread.WaitReason; - - switch (prevThreadWaitReason) - { - case ThreadWaitReason.Suspended: - SuspendToggle.IsOn = true; - break; - - default: - SuspendToggle.IsOn = false; - break; - } - } - } - break; - - case ThreadState.Terminated: - { - // dispose from MainThread - MainThread.Dispose(); - MainThread = null; - } - break; - } - - // update previous state - prevThreadState = MainThread.ThreadState; - }); - } - - public bool IsSuspended() - { - return prevThreadWaitReason == ThreadWaitReason.Suspended; - } - - public void RefreshChildProcesses() - { - // refresh all child processes - var childs = ProcessUtils.GetChildIds(Process); - - // remove exited children - foreach (var pid in childs) - Children.Remove(pid); - - // raise event on new children - foreach (var pid in childs) - Children.Add(pid); - } - - private void SuspendToggle_Toggled(object sender, RoutedEventArgs e) - { - switch (SuspendToggle.IsOn) - { - case true: - { - if (prevThreadWaitReason == ThreadWaitReason.Suspended) - return; - - ProcessManager.SuspendProcess(this); - } - break; - case false: - ProcessManager.ResumeProcess(this); - break; - } - - Refresh(); - } - - private void B_KillProcess_Clicked(object sender, RoutedEventArgs e) - { - if (Process is not null) - Process.Kill(); - } +using HandheldCompanion.Managers; +using HandheldCompanion.Platforms; +using HandheldCompanion.Utils; +using System; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; + +namespace HandheldCompanion.Controls; + +/// <summary> +/// Logique d'interaction pour ProcessEx.xaml +/// </summary> +public partial class ProcessEx : UserControl, IDisposable +{ + public enum ProcessFilter + { + Allowed = 0, + Restricted = 1, + Ignored = 2, + HandheldCompanion = 3, + Desktop = 4 + } + + public string _Executable; + + private string _Title; + + public ConcurrentList<int> Children = new(); + + public ProcessFilter Filter; + + public ImageSource imgSource; + + public ProcessThread MainThread; + + public IntPtr MainWindowHandle; + + public string Path; + + private ThreadState prevThreadState = ThreadState.Terminated; + private ThreadWaitReason prevThreadWaitReason = ThreadWaitReason.UserRequest; + + public Process Process; + private readonly int ProcessId; + + public ProcessEx() + { + InitializeComponent(); + } + + public ProcessEx(Process process, string path, string executable, ProcessFilter filter) : this() + { + Process = process; + ProcessId = process.Id; + + Path = path; + + Executable = executable; + MainWindowTitle = executable; // temporary, will be overwritten by ProcessManager + + Filter = filter; + Platform = PlatformManager.GetPlatform(Process); + + if (!string.IsNullOrEmpty(path) && File.Exists(path)) + { + var icon = Icon.ExtractAssociatedIcon(Path); + if (icon is not null) + { + imgSource = icon.ToImageSource(); + ProcessIcon.Source = imgSource; + } + } + } + + public string MainWindowTitle + { + get => _Title; + + set + { + _Title = value; + TitleTextBlock.Text = value; + } + } + + public string Executable + { + get => _Executable; + + set + { + _Executable = value; + ExecutableTextBlock.Text = value; + } + } + + public PlatformType Platform { get; set; } + + public void Dispose() + { + if (Process is not null) + Process.Dispose(); + if (MainThread is not null) + MainThread.Dispose(); + Children.Dispose(); + + GC.SuppressFinalize(this); //now, the finalizer won't be called + } + + public int GetProcessId() + { + return ProcessId; + } + + public bool HasExited() + { + if (Process is not null) + return Process.HasExited; + + return true; + } + + public void Refresh() + { + if (Process.HasExited) + return; + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (MainThread.ThreadState) + { + case ThreadState.Wait: + { + // monitor if the process main thread was suspended or resumed + if (MainThread.WaitReason != prevThreadWaitReason) + { + prevThreadWaitReason = MainThread.WaitReason; + + switch (prevThreadWaitReason) + { + case ThreadWaitReason.Suspended: + SuspendToggle.IsOn = true; + break; + + default: + SuspendToggle.IsOn = false; + break; + } + } + } + break; + + case ThreadState.Terminated: + { + // dispose from MainThread + MainThread.Dispose(); + MainThread = null; + } + break; + } + + // update previous state + prevThreadState = MainThread.ThreadState; + }); + } + + public bool IsSuspended() + { + return prevThreadWaitReason == ThreadWaitReason.Suspended; + } + + public void RefreshChildProcesses() + { + // refresh all child processes + var childs = ProcessUtils.GetChildIds(Process); + + // remove exited children + foreach (var pid in childs) + Children.Remove(pid); + + // raise event on new children + foreach (var pid in childs) + Children.Add(pid); + } + + private void SuspendToggle_Toggled(object sender, RoutedEventArgs e) + { + switch (SuspendToggle.IsOn) + { + case true: + { + if (prevThreadWaitReason == ThreadWaitReason.Suspended) + return; + + ProcessManager.SuspendProcess(this); + } + break; + case false: + ProcessManager.ResumeProcess(this); + break; + } + + Refresh(); + } + + private void B_KillProcess_Clicked(object sender, RoutedEventArgs e) + { +<<<<<<< HEAD + if (Process is not null) + Process.Kill(); +======= + Process.Kill(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } } \ No newline at end of file diff --git a/HandheldCompanion/DSU/DSUServer.cs b/HandheldCompanion/DSU/DSUServer.cs index 18355ab96..417218674 100644 --- a/HandheldCompanion/DSU/DSUServer.cs +++ b/HandheldCompanion/DSU/DSUServer.cs @@ -1,816 +1,831 @@ -using Force.Crc32; -using HandheldCompanion.Controllers; -using HandheldCompanion.Inputs; -using HandheldCompanion.Managers; -using HandheldCompanion.Utils; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; -using System.Threading; -using System.Windows.Forms; -using Timer = System.Timers.Timer; - -namespace HandheldCompanion; - -public enum DsState : byte -{ - [Description("Disconnected")] Disconnected = 0x00, - [Description("Reserved")] Reserved = 0x01, - [Description("Connected")] Connected = 0x02 -} - -public enum DsConnection : byte -{ - [Description("None")] None = 0x00, - [Description("Usb")] Usb = 0x01, - [Description("Bluetooth")] Bluetooth = 0x02 -} - -public enum DsModel : byte -{ - [Description("None")] None = 0, - [Description("DualShock 3")] DS3 = 1, - [Description("DualShock 4")] DS4 = 2, - [Description("Generic Gamepad")] Generic = 3 -} - -public enum DsBattery : byte -{ - None = 0x00, - Dying = 0x01, - Low = 0x02, - Medium = 0x03, - High = 0x04, - Full = 0x05, - Charging = 0xEE, - Charged = 0xEF -} - -public struct DualShockPadMeta -{ - public byte PadId; - public DsState PadState; - public DsConnection ConnectionType; - public DsModel Model; - public PhysicalAddress PadMacAddress; - public DsBattery BatteryStatus; - public bool IsActive; -} - -public class DSUServer -{ - public delegate void GetPadDetail(int padIdx, ref DualShockPadMeta meta); - - public delegate void StartedEventHandler(DSUServer server); - - public delegate void StoppedEventHandler(DSUServer server); - - public const int NUMBER_SLOTS = 4; - private const int ARG_BUFFER_LEN = 80; - - private const ushort MaxProtocolVersion = 1001; - private SemaphoreSlim _pool; - private SocketAsyncEventArgs[] argsList; - - private readonly Dictionary<IPEndPoint, ClientRequestTimes> clients = new(); - - private ControllerState Inputs = new(); - - private int listInd; - private readonly PhysicalAddress PadMacAddress; - - public DualShockPadMeta padMeta; - private readonly ReaderWriterLockSlim poolLock = new(); - - public int port = 26760; - - private readonly GetPadDetail portInfoGet; - private readonly byte[] recvBuffer = new byte[1024]; - public bool running; - private uint serverId; - private int udpPacketCount; - private Socket udpSock; - private Timer udpTimer; - - public DSUServer() - { - PadMacAddress = new PhysicalAddress(new byte[] { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }); - portInfoGet = GetPadDetailForIdx; - - padMeta = new DualShockPadMeta() - { - BatteryStatus = DsBattery.Full, - ConnectionType = DsConnection.Usb, - IsActive = true, - PadId = (byte)0, - PadMacAddress = PadMacAddress, - Model = DsModel.DS4, - PadState = DsState.Connected - }; - - udpTimer = new(5000); - udpTimer.AutoReset = false; - udpTimer.Elapsed += UdpTimer_Elapsed; - } - - private void ResetPool() - { - if (_pool is not null) - _pool.Release(); - - _pool = new SemaphoreSlim(ARG_BUFFER_LEN); - - if (argsList is not null && argsList.Length != 0) - foreach(SocketAsyncEventArgs args in argsList) - args.Dispose(); - - argsList = new SocketAsyncEventArgs[ARG_BUFFER_LEN]; - - for (int num = 0; num < ARG_BUFFER_LEN; num++) - { - SocketAsyncEventArgs args = new SocketAsyncEventArgs(); - args.SetBuffer(new byte[100], 0, 100); - args.Completed += SocketEvent_Completed; - argsList[num] = args; - } - } - - private void GetPadDetailForIdx(int padIdx, ref DualShockPadMeta meta) - { - meta = padMeta; - } - - public event StartedEventHandler Started; - - public event StoppedEventHandler Stopped; - - public override string ToString() - { - return GetType().Name; - } - - private void SocketEvent_Completed(object sender, SocketAsyncEventArgs e) - { - _pool.Release(); - } - - private void CompletedSynchronousSocketEvent() - { - _pool.Release(); - } - - private int BeginPacket(byte[] packetBuf, ushort reqProtocolVersion = MaxProtocolVersion) - { - var currIdx = 0; - packetBuf[currIdx++] = (byte)'D'; - packetBuf[currIdx++] = (byte)'S'; - packetBuf[currIdx++] = (byte)'U'; - packetBuf[currIdx++] = (byte)'S'; - - Array.Copy(BitConverter.GetBytes(reqProtocolVersion), 0, packetBuf, currIdx, 2); - currIdx += 2; - - Array.Copy(BitConverter.GetBytes((ushort)packetBuf.Length - 16), 0, packetBuf, currIdx, 2); - currIdx += 2; - - Array.Clear(packetBuf, currIdx, 4); //place for crc - currIdx += 4; - - Array.Copy(BitConverter.GetBytes(serverId), 0, packetBuf, currIdx, 4); - currIdx += 4; - - return currIdx; - } - - private void FinishPacket(byte[] packetBuf) - { - Array.Clear(packetBuf, 8, 4); - - var crcCalc = Crc32Algorithm.Compute(packetBuf); - Array.Copy(BitConverter.GetBytes(crcCalc), 0, packetBuf, 8, 4); - } - - private void SendPacket(IPEndPoint clientEP, byte[] usefulData, ushort reqProtocolVersion = MaxProtocolVersion) - { - var packetData = new byte[usefulData.Length + 16]; - var currIdx = BeginPacket(packetData, reqProtocolVersion); - Array.Copy(usefulData, 0, packetData, currIdx, usefulData.Length); - FinishPacket(packetData); - poolLock.EnterWriteLock(); - //try { udpSock.SendTo(packetData, clientEP); } - var temp = listInd; - listInd = ++listInd % ARG_BUFFER_LEN; - var args = argsList[temp]; - poolLock.ExitWriteLock(); - - _pool.Wait(); - args.RemoteEndPoint = clientEP; - Array.Copy(packetData, args.Buffer, packetData.Length); - //args.SetBuffer(packetData, 0, packetData.Length); - var sentAsync = false; - try - { - sentAsync = udpSock.SendToAsync(args); - if (!sentAsync) CompletedSynchronousSocketEvent(); - } - catch (Exception /*e*/) - { - } - finally - { - if (!sentAsync) CompletedSynchronousSocketEvent(); - } - } - - private void ProcessIncoming(byte[] localMsg, IPEndPoint clientEP) - { - try - { - var currIdx = 0; - if (localMsg[0] != 'D' || localMsg[1] != 'S' || localMsg[2] != 'U' || localMsg[3] != 'C') - return; - currIdx += 4; - - uint protocolVer = BitConverter.ToUInt16(localMsg, currIdx); - currIdx += 2; - - if (protocolVer > MaxProtocolVersion) - return; - - uint packetSize = BitConverter.ToUInt16(localMsg, currIdx); - currIdx += 2; - - if (packetSize < 0) - return; - - packetSize += 16; //size of header - if (packetSize > localMsg.Length) - return; - else if (packetSize < localMsg.Length) - { - byte[] newMsg = new byte[packetSize]; - Array.Copy(localMsg, newMsg, packetSize); - localMsg = newMsg; - } - - var crcValue = BitConverter.ToUInt32(localMsg, currIdx); - //zero out the crc32 in the packet once we got it since that's whats needed for calculation - localMsg[currIdx++] = 0; - localMsg[currIdx++] = 0; - localMsg[currIdx++] = 0; - localMsg[currIdx++] = 0; - - var crcCalc = Crc32Algorithm.Compute(localMsg); - if (crcValue != crcCalc) - return; - - var clientId = BitConverter.ToUInt32(localMsg, currIdx); - currIdx += 4; - - var messageType = BitConverter.ToUInt32(localMsg, currIdx); - currIdx += 4; - - if (messageType == (uint)MessageType.DSUC_VersionReq) - { - var outputData = new byte[8]; - var outIdx = 0; - Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_VersionRsp), 0, outputData, outIdx, 4); - outIdx += 4; - Array.Copy(BitConverter.GetBytes(MaxProtocolVersion), 0, outputData, outIdx, 2); - outIdx += 2; - outputData[outIdx++] = 0; - outputData[outIdx++] = 0; - - SendPacket(clientEP, outputData); - } - else if (messageType == (uint)MessageType.DSUC_ListPorts) - { - var numPadRequests = BitConverter.ToInt32(localMsg, currIdx); - currIdx += 4; - if (numPadRequests < 0 || numPadRequests > NUMBER_SLOTS) - return; - - var requestsIdx = currIdx; - for (var i = 0; i < numPadRequests; i++) - { - var currRequest = localMsg[requestsIdx + i]; - if (currRequest >= NUMBER_SLOTS) - return; - } - - var outputData = new byte[16]; - for (byte i = 0; i < numPadRequests; i++) - { - var currRequest = localMsg[requestsIdx + i]; - var padData = new DualShockPadMeta(); - portInfoGet(currRequest, ref padData); - - var outIdx = 0; - Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PortInfo), 0, outputData, outIdx, 4); - outIdx += 4; - - outputData[outIdx++] = padData.PadId; - outputData[outIdx++] = (byte)padData.PadState; - outputData[outIdx++] = (byte)padData.Model; - outputData[outIdx++] = (byte)padData.ConnectionType; - - byte[] addressBytes = null; - if (padData.PadMacAddress is not null) - addressBytes = padData.PadMacAddress.GetAddressBytes(); - - if (addressBytes is not null && addressBytes.Length == 6) - { - outputData[outIdx++] = addressBytes[0]; - outputData[outIdx++] = addressBytes[1]; - outputData[outIdx++] = addressBytes[2]; - outputData[outIdx++] = addressBytes[3]; - outputData[outIdx++] = addressBytes[4]; - outputData[outIdx++] = addressBytes[5]; - } - else - { - outputData[outIdx++] = 0; - outputData[outIdx++] = 0; - outputData[outIdx++] = 0; - outputData[outIdx++] = 0; - outputData[outIdx++] = 0; - outputData[outIdx++] = 0; - } - - outputData[outIdx++] = (byte)padData.BatteryStatus; - outputData[outIdx++] = 0; - - SendPacket(clientEP, outputData); - } - } - else if (messageType == (uint)MessageType.DSUC_PadDataReq) - { - var regFlags = localMsg[currIdx++]; - var idToReg = localMsg[currIdx++]; - var macToReg = new PhysicalAddress(new byte[] { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }); - - lock (clients) - { - if (clients.TryGetValue(clientEP, out var client)) - { - client.RequestPadInfo(regFlags, idToReg, macToReg); - } - else - { - var clientTimes = new ClientRequestTimes(); - clientTimes.RequestPadInfo(regFlags, idToReg, macToReg); - clients[clientEP] = clientTimes; - } - } - } - } - catch - { - } - } - - private void ReceiveCallback(IAsyncResult iar) - { - byte[] localMsg = null; - EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0); - - try - { - //Get the received message. - Socket recvSock = (Socket)iar.AsyncState; - int msgLen = recvSock.EndReceiveFrom(iar, ref clientEP); - - localMsg = new byte[msgLen]; - Array.Copy(recvBuffer, localMsg, msgLen); - } - catch (SocketException) - { - if (running) - { - ResetUDPConn(); - return; - } - } - catch (Exception /*e*/) { } - - //Start another receive as soon as we copied the data - StartReceive(); - - //Process the data if its valid - if (localMsg != null) - ProcessIncoming(localMsg, (IPEndPoint)clientEP); - } - - private void StartReceive() - { - try - { - if (running) - { - //Start listening for a new message. - EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0); - udpSock.BeginReceiveFrom(recvBuffer, 0, recvBuffer.Length, SocketFlags.None, ref newClientEP, ReceiveCallback, udpSock); - } - } - catch (SocketException /*ex*/) - { - if (running) - { - ResetUDPConn(); - return; - } - } - catch (Exception /*ex*/) { } - } - - /// <summary> - /// Used to send CONNRESET ioControlCode to Socket used for UDP Server. - /// Frees Socket from potentially firing SocketException instances after a client - /// connection is terminated. Avoids memory leak - /// </summary> - private void ResetUDPConn() - { - // suspend UDP - if (running) - Stop(); - - udpTimer.Stop(); - udpTimer.Start(); - } - - private void UdpTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e) - { - // resume UDP - Start(); - } - - public bool Start() - { - ResetPool(); - - try - { - udpSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); - udpSock.Bind(new IPEndPoint(IPAddress.Any, port)); - } - catch (SocketException) - { - LogManager.LogCritical("{0} couldn't listen to port: {1}", ToString(), port); - Stop(); - return running; - } - catch (Exception /*ex*/) { } - - var randomBuf = new byte[4]; - new Random().NextBytes(randomBuf); - serverId = BitConverter.ToUInt32(randomBuf, 0); - - TimerManager.Tick += Tick; - - running = true; - - StartReceive(); - - LogManager.LogInformation("{0} has started. Listening to port: {1}", ToString(), port); - Started?.Invoke(this); - - return running; - } - - public void Stop() - { - if (udpSock is not null) - { - if (udpSock.Connected) - udpSock.Disconnect(true); - - udpSock.Close(); - udpSock.Dispose(); - udpSock = null; - } - - running = false; - - TimerManager.Tick -= Tick; - - LogManager.LogInformation("{0} has stopped", ToString()); - Stopped?.Invoke(this); - } - - public void UpdateInputs(ControllerState inputs) - { - Inputs = inputs; - } - - private bool ReportToBuffer(byte[] outputData, ref int outIdx) - { - unchecked - { - outputData[outIdx] = 0; - - if (Inputs.ButtonState[ButtonFlags.DPadLeft]) outputData[outIdx] |= 0x80; - if (Inputs.ButtonState[ButtonFlags.DPadDown]) outputData[outIdx] |= 0x40; - if (Inputs.ButtonState[ButtonFlags.DPadRight]) outputData[outIdx] |= 0x20; - if (Inputs.ButtonState[ButtonFlags.DPadUp]) outputData[outIdx] |= 0x10; - - if (Inputs.ButtonState[ButtonFlags.Start]) outputData[outIdx] |= 0x08; - if (Inputs.ButtonState[ButtonFlags.RightStickClick]) outputData[outIdx] |= 0x04; - if (Inputs.ButtonState[ButtonFlags.LeftStickClick]) outputData[outIdx] |= 0x02; - if (Inputs.ButtonState[ButtonFlags.Back]) outputData[outIdx] |= 0x01; - - outputData[++outIdx] = 0; - - if (Inputs.ButtonState[ButtonFlags.B1]) outputData[outIdx] |= 0x40; - if (Inputs.ButtonState[ButtonFlags.B2]) outputData[outIdx] |= 0x20; - if (Inputs.ButtonState[ButtonFlags.B3]) outputData[outIdx] |= 0x80; - if (Inputs.ButtonState[ButtonFlags.B4]) outputData[outIdx] |= 0x10; - - if (Inputs.ButtonState[ButtonFlags.R1]) outputData[outIdx] |= 0x08; - if (Inputs.ButtonState[ButtonFlags.L1]) outputData[outIdx] |= 0x04; - if (Inputs.AxisState[AxisFlags.R2] == byte.MaxValue) outputData[outIdx] |= 0x02; - if (Inputs.AxisState[AxisFlags.L2] == byte.MaxValue) outputData[outIdx] |= 0x01; - - outputData[++outIdx] = - Convert.ToByte(Inputs.ButtonState[ButtonFlags.Special]); // (hidReport.PS) ? (byte)1 : - outputData[++outIdx] = Convert.ToByte(Inputs.ButtonState[ButtonFlags.LeftPadClick] || - Inputs.ButtonState[ - ButtonFlags - .RightPadClick]); // (hidReport.TouchButton) ? (byte)1 : - - //Left stick - outputData[++outIdx] = InputUtils.NormalizeXboxInput(Inputs.AxisState[AxisFlags.LeftStickX]); - outputData[++outIdx] = InputUtils.NormalizeXboxInput(Inputs.AxisState[AxisFlags.LeftStickY]); - outputData[outIdx] = (byte)(byte.MaxValue - outputData[outIdx]); //invert Y by convention - - //Right stick - outputData[++outIdx] = InputUtils.NormalizeXboxInput(Inputs.AxisState[AxisFlags.RightStickX]); - outputData[++outIdx] = InputUtils.NormalizeXboxInput(Inputs.AxisState[AxisFlags.RightStickY]); - outputData[outIdx] = (byte)(byte.MaxValue - outputData[outIdx]); //invert Y by convention - - outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.DPadLeft] ? (byte)0xFF : (byte)0x00; - outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.DPadDown] ? (byte)0xFF : (byte)0x00; - outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.DPadRight] ? (byte)0xFF : (byte)0x00; - outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.DPadUp] ? (byte)0xFF : (byte)0x00; - - outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.B1] ? (byte)0xFF : (byte)0x00; - outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.B2] ? (byte)0xFF : (byte)0x00; - outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.B3] ? (byte)0xFF : (byte)0x00; - outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.B4] ? (byte)0xFF : (byte)0x00; - - outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.R1] ? (byte)0xFF : (byte)0x00; - outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.L1] ? (byte)0xFF : (byte)0x00; - - outputData[++outIdx] = (byte)Inputs.AxisState[AxisFlags.R2]; - outputData[++outIdx] = (byte)Inputs.AxisState[AxisFlags.L2]; - - outIdx++; - - //DS4 only: touchpad points - for (var i = 0; i < 2; i++) - { - var tpad = i == 0 ? DS4Touch.LeftPadTouch : DS4Touch.RightPadTouch; - - outputData[outIdx++] = tpad.IsActive ? (byte)1 : (byte)0; - outputData[outIdx++] = (byte)tpad.RawTrackingNum; - Array.Copy(BitConverter.GetBytes((ushort)tpad.X), 0, outputData, outIdx, 2); - outIdx += 2; - Array.Copy(BitConverter.GetBytes((ushort)tpad.Y), 0, outputData, outIdx, 2); - outIdx += 2; - } - - //motion timestamp - Array.Copy(BitConverter.GetBytes((ulong)TimerManager.GetElapsedSeconds()), 0, outputData, outIdx, 8); - - outIdx += 8; - - // Accelerometer - // accelXG - Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Accelerometer.X), 0, outputData, outIdx, 4); - outIdx += 4; - // accelYG - Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Accelerometer.Y), 0, outputData, outIdx, 4); - outIdx += 4; - // accelZG - Array.Copy(BitConverter.GetBytes(-Inputs.GyroState.Accelerometer.Z), 0, outputData, outIdx, 4); - outIdx += 4; - - // Gyroscope - // angVelPitch - Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Gyroscope.X), 0, outputData, outIdx, 4); - outIdx += 4; - // angVelYaw - Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Gyroscope.Y), 0, outputData, outIdx, 4); - outIdx += 4; - // angVelRoll - Array.Copy(BitConverter.GetBytes(-Inputs.GyroState.Gyroscope.Z), 0, outputData, outIdx, 4); - outIdx += 4; - } - - return true; - } - - public void Tick(long ticks) - { - if (!running) - return; - - // only update every one second - if (ticks % 1000 == 0) - { - var ChargeStatus = SystemInformation.PowerStatus.BatteryChargeStatus; - - if (ChargeStatus.HasFlag(BatteryChargeStatus.Charging)) - padMeta.BatteryStatus = DsBattery.Charging; - else if (ChargeStatus.HasFlag(BatteryChargeStatus.NoSystemBattery)) - padMeta.BatteryStatus = DsBattery.None; - else if (ChargeStatus.HasFlag(BatteryChargeStatus.High)) - padMeta.BatteryStatus = DsBattery.High; - else if (ChargeStatus.HasFlag(BatteryChargeStatus.Low)) - padMeta.BatteryStatus = DsBattery.Low; - else if (ChargeStatus.HasFlag(BatteryChargeStatus.Critical)) - padMeta.BatteryStatus = DsBattery.Dying; - else - padMeta.BatteryStatus = DsBattery.Medium; - } - - // update status - padMeta.IsActive = true; // fixme ? - - var clientsList = new List<IPEndPoint>(); - var now = DateTime.UtcNow; - lock (clients) - { - var clientsToDelete = new List<IPEndPoint>(); - - foreach (var cl in clients) - { - const double TimeoutLimit = 5; - - if ((now - cl.Value.AllPadsTime).TotalSeconds < TimeoutLimit) - { - clientsList.Add(cl.Key); - } - else if (padMeta.PadId < cl.Value.PadIdsTime.Length && - (now - cl.Value.PadIdsTime[padMeta.PadId]).TotalSeconds < TimeoutLimit) - { - clientsList.Add(cl.Key); - } - else if (cl.Value.PadMacsTime.TryGetValue(padMeta.PadMacAddress, out var padTime) && - (now - padTime).TotalSeconds < TimeoutLimit) - { - clientsList.Add(cl.Key); - } - else //check if this client is totally dead, and remove it if so - { - var clientOk = false; - foreach (var t in cl.Value.PadIdsTime) - { - var dur = (now - t).TotalSeconds; - if (dur < TimeoutLimit) - { - clientOk = true; - break; - } - } - - if (!clientOk) - { - foreach (var dict in cl.Value.PadMacsTime) - { - var dur = (now - dict.Value).TotalSeconds; - if (dur < TimeoutLimit) - { - clientOk = true; - break; - } - } - - if (!clientOk) - clientsToDelete.Add(cl.Key); - } - } - } - - foreach (var delCl in clientsToDelete) clients.Remove(delCl); - clientsToDelete.Clear(); - clientsToDelete = null; - } - - if (clientsList.Count <= 0) - return; - - unchecked - { - var outputData = new byte[100]; - var outIdx = BeginPacket(outputData); - Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PadDataRsp), 0, outputData, outIdx, 4); - outIdx += 4; - - outputData[outIdx++] = padMeta.PadId; - outputData[outIdx++] = (byte)padMeta.PadState; - outputData[outIdx++] = (byte)padMeta.Model; - outputData[outIdx++] = (byte)padMeta.ConnectionType; - { - var padMac = padMeta.PadMacAddress.GetAddressBytes(); - outputData[outIdx++] = padMac[0]; - outputData[outIdx++] = padMac[1]; - outputData[outIdx++] = padMac[2]; - outputData[outIdx++] = padMac[3]; - outputData[outIdx++] = padMac[4]; - outputData[outIdx++] = padMac[5]; - } - outputData[outIdx++] = (byte)padMeta.BatteryStatus; - outputData[outIdx++] = padMeta.IsActive ? (byte)1 : (byte)0; - - Array.Copy(BitConverter.GetBytes((uint)udpPacketCount++), 0, outputData, outIdx, 4); - outIdx += 4; - - if (!ReportToBuffer(outputData, ref outIdx)) - return; - FinishPacket(outputData); - - foreach (var cl in clientsList) - { - //try { udpSock.SendTo(outputData, cl); } - int temp = 0; - poolLock.EnterWriteLock(); - temp = listInd; - listInd = ++listInd % ARG_BUFFER_LEN; - SocketAsyncEventArgs args = argsList[temp]; - poolLock.ExitWriteLock(); - - _pool.Wait(); - args.RemoteEndPoint = cl; - Array.Copy(outputData, args.Buffer, outputData.Length); - bool sentAsync = false; - try - { - bool sendAsync = udpSock.SendToAsync(args); - if (!sendAsync) CompletedSynchronousSocketEvent(); - } - catch (SocketException /*ex*/) { } - catch (Exception /*ex*/) { } - finally - { - if (!sentAsync) CompletedSynchronousSocketEvent(); - } - } - } - - clientsList.Clear(); - clientsList = null; - } - - private enum MessageType - { - DSUC_VersionReq = 0x100000, - DSUS_VersionRsp = 0x100000, - DSUC_ListPorts = 0x100001, - DSUS_PortInfo = 0x100001, - DSUC_PadDataReq = 0x100002, - DSUS_PadDataRsp = 0x100002 - } - - class ClientRequestTimes - { - DateTime allPads; - DateTime[] padIds; - Dictionary<PhysicalAddress, DateTime> padMacs; - - public DateTime AllPadsTime { get { return allPads; } } - public DateTime[] PadIdsTime { get { return padIds; } } - public Dictionary<PhysicalAddress, DateTime> PadMacsTime { get { return padMacs; } } - - public ClientRequestTimes() - { - allPads = DateTime.MinValue; - padIds = new DateTime[4]; - - for (int i = 0; i < padIds.Length; i++) - padIds[i] = DateTime.MinValue; - - padMacs = new Dictionary<PhysicalAddress, DateTime>(); - } - - public void RequestPadInfo(byte regFlags, byte idToReg, PhysicalAddress macToReg) - { - if (regFlags == 0) - allPads = DateTime.UtcNow; - else - { - if ((regFlags & 0x01) != 0) //id valid - { - if (idToReg < padIds.Length) - padIds[idToReg] = DateTime.UtcNow; - } - if ((regFlags & 0x02) != 0) //mac valid - { - padMacs[macToReg] = DateTime.UtcNow; - } - } - } - } +using Force.Crc32; +using HandheldCompanion.Controllers; +using HandheldCompanion.Inputs; +using HandheldCompanion.Managers; +using HandheldCompanion.Utils; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Threading; +using System.Windows.Forms; +<<<<<<< HEAD +using Timer = System.Timers.Timer; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + +namespace HandheldCompanion; + +public enum DsState : byte +{ + [Description("Disconnected")] Disconnected = 0x00, + [Description("Reserved")] Reserved = 0x01, + [Description("Connected")] Connected = 0x02 +} + +public enum DsConnection : byte +{ + [Description("None")] None = 0x00, + [Description("Usb")] Usb = 0x01, + [Description("Bluetooth")] Bluetooth = 0x02 +} + +public enum DsModel : byte +{ + [Description("None")] None = 0, + [Description("DualShock 3")] DS3 = 1, + [Description("DualShock 4")] DS4 = 2, + [Description("Generic Gamepad")] Generic = 3 +} + +public enum DsBattery : byte +{ + None = 0x00, + Dying = 0x01, + Low = 0x02, + Medium = 0x03, + High = 0x04, + Full = 0x05, + Charging = 0xEE, + Charged = 0xEF +} + +public struct DualShockPadMeta +{ + public byte PadId; + public DsState PadState; + public DsConnection ConnectionType; + public DsModel Model; + public PhysicalAddress PadMacAddress; + public DsBattery BatteryStatus; + public bool IsActive; +} + +public class DSUServer +{ + public delegate void GetPadDetail(int padIdx, ref DualShockPadMeta meta); + + public delegate void StartedEventHandler(DSUServer server); + + public delegate void StoppedEventHandler(DSUServer server); + + public const int NUMBER_SLOTS = 4; + private const int ARG_BUFFER_LEN = 80; + + private const ushort MaxProtocolVersion = 1001; + private SemaphoreSlim _pool; + private SocketAsyncEventArgs[] argsList; + + private readonly Dictionary<IPEndPoint, ClientRequestTimes> clients = new(); + + private ControllerState Inputs = new(); + + private int listInd; + private readonly PhysicalAddress PadMacAddress; + + public DualShockPadMeta padMeta; + private readonly ReaderWriterLockSlim poolLock = new(); + + public int port = 26760; + + private readonly GetPadDetail portInfoGet; + private readonly byte[] recvBuffer = new byte[1024]; + public bool running; + private uint serverId; + private int udpPacketCount; + private Socket udpSock; + private Timer udpTimer; + + public DSUServer() + { + PadMacAddress = new PhysicalAddress(new byte[] { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }); + portInfoGet = GetPadDetailForIdx; + + padMeta = new DualShockPadMeta() + { + BatteryStatus = DsBattery.Full, + ConnectionType = DsConnection.Usb, + IsActive = true, + PadId = (byte)0, + PadMacAddress = PadMacAddress, + Model = DsModel.DS4, + PadState = DsState.Connected + }; + + udpTimer = new(5000); + udpTimer.AutoReset = false; + udpTimer.Elapsed += UdpTimer_Elapsed; + } + + private void ResetPool() + { + if (_pool is not null) + _pool.Release(); + + _pool = new SemaphoreSlim(ARG_BUFFER_LEN); + + if (argsList is not null && argsList.Length != 0) + foreach(SocketAsyncEventArgs args in argsList) + args.Dispose(); + + argsList = new SocketAsyncEventArgs[ARG_BUFFER_LEN]; +<<<<<<< HEAD + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + for (int num = 0; num < ARG_BUFFER_LEN; num++) + { + SocketAsyncEventArgs args = new SocketAsyncEventArgs(); + args.SetBuffer(new byte[100], 0, 100); + args.Completed += SocketEvent_Completed; + argsList[num] = args; + } + } + + private void GetPadDetailForIdx(int padIdx, ref DualShockPadMeta meta) + { + meta = padMeta; + } + + public event StartedEventHandler Started; + + public event StoppedEventHandler Stopped; + + public override string ToString() + { + return GetType().Name; + } + + private void SocketEvent_Completed(object sender, SocketAsyncEventArgs e) + { + _pool.Release(); + } + + private void CompletedSynchronousSocketEvent() + { + _pool.Release(); + } + + private int BeginPacket(byte[] packetBuf, ushort reqProtocolVersion = MaxProtocolVersion) + { + var currIdx = 0; + packetBuf[currIdx++] = (byte)'D'; + packetBuf[currIdx++] = (byte)'S'; + packetBuf[currIdx++] = (byte)'U'; + packetBuf[currIdx++] = (byte)'S'; + + Array.Copy(BitConverter.GetBytes(reqProtocolVersion), 0, packetBuf, currIdx, 2); + currIdx += 2; + + Array.Copy(BitConverter.GetBytes((ushort)packetBuf.Length - 16), 0, packetBuf, currIdx, 2); + currIdx += 2; + + Array.Clear(packetBuf, currIdx, 4); //place for crc + currIdx += 4; + + Array.Copy(BitConverter.GetBytes(serverId), 0, packetBuf, currIdx, 4); + currIdx += 4; + + return currIdx; + } + + private void FinishPacket(byte[] packetBuf) + { + Array.Clear(packetBuf, 8, 4); + + var crcCalc = Crc32Algorithm.Compute(packetBuf); + Array.Copy(BitConverter.GetBytes(crcCalc), 0, packetBuf, 8, 4); + } + + private void SendPacket(IPEndPoint clientEP, byte[] usefulData, ushort reqProtocolVersion = MaxProtocolVersion) + { + var packetData = new byte[usefulData.Length + 16]; + var currIdx = BeginPacket(packetData, reqProtocolVersion); + Array.Copy(usefulData, 0, packetData, currIdx, usefulData.Length); + FinishPacket(packetData); + poolLock.EnterWriteLock(); + //try { udpSock.SendTo(packetData, clientEP); } + var temp = listInd; + listInd = ++listInd % ARG_BUFFER_LEN; + var args = argsList[temp]; + poolLock.ExitWriteLock(); + + _pool.Wait(); + args.RemoteEndPoint = clientEP; + Array.Copy(packetData, args.Buffer, packetData.Length); + //args.SetBuffer(packetData, 0, packetData.Length); + var sentAsync = false; + try + { + sentAsync = udpSock.SendToAsync(args); + if (!sentAsync) CompletedSynchronousSocketEvent(); + } + catch (Exception /*e*/) + { + } + finally + { + if (!sentAsync) CompletedSynchronousSocketEvent(); + } + } + + private void ProcessIncoming(byte[] localMsg, IPEndPoint clientEP) + { + try + { + var currIdx = 0; + if (localMsg[0] != 'D' || localMsg[1] != 'S' || localMsg[2] != 'U' || localMsg[3] != 'C') + return; + currIdx += 4; + + uint protocolVer = BitConverter.ToUInt16(localMsg, currIdx); + currIdx += 2; + + if (protocolVer > MaxProtocolVersion) + return; + + uint packetSize = BitConverter.ToUInt16(localMsg, currIdx); + currIdx += 2; + + if (packetSize < 0) + return; + + packetSize += 16; //size of header + if (packetSize > localMsg.Length) + return; + else if (packetSize < localMsg.Length) + { + byte[] newMsg = new byte[packetSize]; + Array.Copy(localMsg, newMsg, packetSize); + localMsg = newMsg; + } + + var crcValue = BitConverter.ToUInt32(localMsg, currIdx); + //zero out the crc32 in the packet once we got it since that's whats needed for calculation + localMsg[currIdx++] = 0; + localMsg[currIdx++] = 0; + localMsg[currIdx++] = 0; + localMsg[currIdx++] = 0; + + var crcCalc = Crc32Algorithm.Compute(localMsg); + if (crcValue != crcCalc) + return; + + var clientId = BitConverter.ToUInt32(localMsg, currIdx); + currIdx += 4; + + var messageType = BitConverter.ToUInt32(localMsg, currIdx); + currIdx += 4; + + if (messageType == (uint)MessageType.DSUC_VersionReq) + { + var outputData = new byte[8]; + var outIdx = 0; + Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_VersionRsp), 0, outputData, outIdx, 4); + outIdx += 4; + Array.Copy(BitConverter.GetBytes(MaxProtocolVersion), 0, outputData, outIdx, 2); + outIdx += 2; + outputData[outIdx++] = 0; + outputData[outIdx++] = 0; + + SendPacket(clientEP, outputData); + } + else if (messageType == (uint)MessageType.DSUC_ListPorts) + { + var numPadRequests = BitConverter.ToInt32(localMsg, currIdx); + currIdx += 4; + if (numPadRequests < 0 || numPadRequests > NUMBER_SLOTS) + return; + + var requestsIdx = currIdx; + for (var i = 0; i < numPadRequests; i++) + { + var currRequest = localMsg[requestsIdx + i]; + if (currRequest >= NUMBER_SLOTS) + return; + } + + var outputData = new byte[16]; + for (byte i = 0; i < numPadRequests; i++) + { + var currRequest = localMsg[requestsIdx + i]; + var padData = new DualShockPadMeta(); + portInfoGet(currRequest, ref padData); + + var outIdx = 0; + Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PortInfo), 0, outputData, outIdx, 4); + outIdx += 4; + + outputData[outIdx++] = padData.PadId; + outputData[outIdx++] = (byte)padData.PadState; + outputData[outIdx++] = (byte)padData.Model; + outputData[outIdx++] = (byte)padData.ConnectionType; + + byte[] addressBytes = null; + if (padData.PadMacAddress is not null) + addressBytes = padData.PadMacAddress.GetAddressBytes(); + + if (addressBytes is not null && addressBytes.Length == 6) + { + outputData[outIdx++] = addressBytes[0]; + outputData[outIdx++] = addressBytes[1]; + outputData[outIdx++] = addressBytes[2]; + outputData[outIdx++] = addressBytes[3]; + outputData[outIdx++] = addressBytes[4]; + outputData[outIdx++] = addressBytes[5]; + } + else + { + outputData[outIdx++] = 0; + outputData[outIdx++] = 0; + outputData[outIdx++] = 0; + outputData[outIdx++] = 0; + outputData[outIdx++] = 0; + outputData[outIdx++] = 0; + } + + outputData[outIdx++] = (byte)padData.BatteryStatus; + outputData[outIdx++] = 0; + + SendPacket(clientEP, outputData); + } + } + else if (messageType == (uint)MessageType.DSUC_PadDataReq) + { + var regFlags = localMsg[currIdx++]; + var idToReg = localMsg[currIdx++]; + var macToReg = new PhysicalAddress(new byte[] { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 }); + + lock (clients) + { + if (clients.TryGetValue(clientEP, out var client)) + { + client.RequestPadInfo(regFlags, idToReg, macToReg); + } + else + { + var clientTimes = new ClientRequestTimes(); + clientTimes.RequestPadInfo(regFlags, idToReg, macToReg); + clients[clientEP] = clientTimes; + } + } + } + } + catch + { + } + } + + private void ReceiveCallback(IAsyncResult iar) + { + byte[] localMsg = null; + EndPoint clientEP = new IPEndPoint(IPAddress.Any, 0); + + try + { + //Get the received message. + Socket recvSock = (Socket)iar.AsyncState; + int msgLen = recvSock.EndReceiveFrom(iar, ref clientEP); + + localMsg = new byte[msgLen]; + Array.Copy(recvBuffer, localMsg, msgLen); + } + catch (SocketException) + { + if (running) + { + ResetUDPConn(); + return; + } + } + catch (Exception /*e*/) { } + + //Start another receive as soon as we copied the data + StartReceive(); + + //Process the data if its valid + if (localMsg != null) + ProcessIncoming(localMsg, (IPEndPoint)clientEP); + } + + private void StartReceive() + { + try + { + if (running) + { + //Start listening for a new message. + EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0); + udpSock.BeginReceiveFrom(recvBuffer, 0, recvBuffer.Length, SocketFlags.None, ref newClientEP, ReceiveCallback, udpSock); + } + } + catch (SocketException /*ex*/) + { + if (running) + { + ResetUDPConn(); + return; + } + } + catch (Exception /*ex*/) { } + } + + /// <summary> + /// Used to send CONNRESET ioControlCode to Socket used for UDP Server. + /// Frees Socket from potentially firing SocketException instances after a client + /// connection is terminated. Avoids memory leak + /// </summary> + private void ResetUDPConn() + { + // suspend UDP + if (running) + Stop(); + + udpTimer.Stop(); + udpTimer.Start(); + } + + private void UdpTimer_Elapsed(object? sender, System.Timers.ElapsedEventArgs e) + { + // resume UDP + Start(); + } + + public bool Start() + { + ResetPool(); + + try + { + udpSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + udpSock.Bind(new IPEndPoint(IPAddress.Any, port)); + } + catch (SocketException) + { + LogManager.LogCritical("{0} couldn't listen to port: {1}", ToString(), port); + Stop(); + return running; + } + catch (Exception /*ex*/) { } + + var randomBuf = new byte[4]; + new Random().NextBytes(randomBuf); + serverId = BitConverter.ToUInt32(randomBuf, 0); + + TimerManager.Tick += Tick; + + running = true; + + StartReceive(); + + LogManager.LogInformation("{0} has started. Listening to port: {1}", ToString(), port); + Started?.Invoke(this); + + return running; + } + + public void Stop() + { + if (udpSock is not null) + { + if (udpSock.Connected) + udpSock.Disconnect(true); + + udpSock.Close(); + udpSock.Dispose(); + udpSock = null; + } + + running = false; + + TimerManager.Tick -= Tick; + + LogManager.LogInformation("{0} has stopped", ToString()); + Stopped?.Invoke(this); + } + + public void UpdateInputs(ControllerState inputs) + { + Inputs = inputs; + } + + private bool ReportToBuffer(byte[] outputData, ref int outIdx) + { + unchecked + { + outputData[outIdx] = 0; + + if (Inputs.ButtonState[ButtonFlags.DPadLeft]) outputData[outIdx] |= 0x80; + if (Inputs.ButtonState[ButtonFlags.DPadDown]) outputData[outIdx] |= 0x40; + if (Inputs.ButtonState[ButtonFlags.DPadRight]) outputData[outIdx] |= 0x20; + if (Inputs.ButtonState[ButtonFlags.DPadUp]) outputData[outIdx] |= 0x10; + + if (Inputs.ButtonState[ButtonFlags.Start]) outputData[outIdx] |= 0x08; + if (Inputs.ButtonState[ButtonFlags.RightStickClick]) outputData[outIdx] |= 0x04; + if (Inputs.ButtonState[ButtonFlags.LeftStickClick]) outputData[outIdx] |= 0x02; + if (Inputs.ButtonState[ButtonFlags.Back]) outputData[outIdx] |= 0x01; + + outputData[++outIdx] = 0; + + if (Inputs.ButtonState[ButtonFlags.B1]) outputData[outIdx] |= 0x40; + if (Inputs.ButtonState[ButtonFlags.B2]) outputData[outIdx] |= 0x20; + if (Inputs.ButtonState[ButtonFlags.B3]) outputData[outIdx] |= 0x80; + if (Inputs.ButtonState[ButtonFlags.B4]) outputData[outIdx] |= 0x10; + + if (Inputs.ButtonState[ButtonFlags.R1]) outputData[outIdx] |= 0x08; + if (Inputs.ButtonState[ButtonFlags.L1]) outputData[outIdx] |= 0x04; + if (Inputs.AxisState[AxisFlags.R2] == byte.MaxValue) outputData[outIdx] |= 0x02; + if (Inputs.AxisState[AxisFlags.L2] == byte.MaxValue) outputData[outIdx] |= 0x01; + + outputData[++outIdx] = + Convert.ToByte(Inputs.ButtonState[ButtonFlags.Special]); // (hidReport.PS) ? (byte)1 : + outputData[++outIdx] = Convert.ToByte(Inputs.ButtonState[ButtonFlags.LeftPadClick] || + Inputs.ButtonState[ + ButtonFlags + .RightPadClick]); // (hidReport.TouchButton) ? (byte)1 : + + //Left stick + outputData[++outIdx] = InputUtils.NormalizeXboxInput(Inputs.AxisState[AxisFlags.LeftStickX]); + outputData[++outIdx] = InputUtils.NormalizeXboxInput(Inputs.AxisState[AxisFlags.LeftStickY]); + outputData[outIdx] = (byte)(byte.MaxValue - outputData[outIdx]); //invert Y by convention + + //Right stick + outputData[++outIdx] = InputUtils.NormalizeXboxInput(Inputs.AxisState[AxisFlags.RightStickX]); + outputData[++outIdx] = InputUtils.NormalizeXboxInput(Inputs.AxisState[AxisFlags.RightStickY]); + outputData[outIdx] = (byte)(byte.MaxValue - outputData[outIdx]); //invert Y by convention + + outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.DPadLeft] ? (byte)0xFF : (byte)0x00; + outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.DPadDown] ? (byte)0xFF : (byte)0x00; + outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.DPadRight] ? (byte)0xFF : (byte)0x00; + outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.DPadUp] ? (byte)0xFF : (byte)0x00; + + outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.B1] ? (byte)0xFF : (byte)0x00; + outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.B2] ? (byte)0xFF : (byte)0x00; + outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.B3] ? (byte)0xFF : (byte)0x00; + outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.B4] ? (byte)0xFF : (byte)0x00; + + outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.R1] ? (byte)0xFF : (byte)0x00; + outputData[++outIdx] = Inputs.ButtonState[ButtonFlags.L1] ? (byte)0xFF : (byte)0x00; + + outputData[++outIdx] = (byte)Inputs.AxisState[AxisFlags.R2]; + outputData[++outIdx] = (byte)Inputs.AxisState[AxisFlags.L2]; + + outIdx++; + + //DS4 only: touchpad points + for (var i = 0; i < 2; i++) + { + var tpad = i == 0 ? DS4Touch.LeftPadTouch : DS4Touch.RightPadTouch; + + outputData[outIdx++] = tpad.IsActive ? (byte)1 : (byte)0; + outputData[outIdx++] = (byte)tpad.RawTrackingNum; + Array.Copy(BitConverter.GetBytes((ushort)tpad.X), 0, outputData, outIdx, 2); + outIdx += 2; + Array.Copy(BitConverter.GetBytes((ushort)tpad.Y), 0, outputData, outIdx, 2); + outIdx += 2; + } + + //motion timestamp + Array.Copy(BitConverter.GetBytes((ulong)TimerManager.GetElapsedSeconds()), 0, outputData, outIdx, 8); + + outIdx += 8; + + // Accelerometer + // accelXG + Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Accelerometer.X), 0, outputData, outIdx, 4); + outIdx += 4; + // accelYG + Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Accelerometer.Y), 0, outputData, outIdx, 4); + outIdx += 4; + // accelZG + Array.Copy(BitConverter.GetBytes(-Inputs.GyroState.Accelerometer.Z), 0, outputData, outIdx, 4); + outIdx += 4; + + // Gyroscope + // angVelPitch + Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Gyroscope.X), 0, outputData, outIdx, 4); + outIdx += 4; + // angVelYaw + Array.Copy(BitConverter.GetBytes(Inputs.GyroState.Gyroscope.Y), 0, outputData, outIdx, 4); + outIdx += 4; + // angVelRoll + Array.Copy(BitConverter.GetBytes(-Inputs.GyroState.Gyroscope.Z), 0, outputData, outIdx, 4); + outIdx += 4; + } + + return true; + } + + public void Tick(long ticks) + { + if (!running) + return; + + // only update every one second + if (ticks % 1000 == 0) + { + var ChargeStatus = SystemInformation.PowerStatus.BatteryChargeStatus; + + if (ChargeStatus.HasFlag(BatteryChargeStatus.Charging)) + padMeta.BatteryStatus = DsBattery.Charging; + else if (ChargeStatus.HasFlag(BatteryChargeStatus.NoSystemBattery)) + padMeta.BatteryStatus = DsBattery.None; + else if (ChargeStatus.HasFlag(BatteryChargeStatus.High)) + padMeta.BatteryStatus = DsBattery.High; + else if (ChargeStatus.HasFlag(BatteryChargeStatus.Low)) + padMeta.BatteryStatus = DsBattery.Low; + else if (ChargeStatus.HasFlag(BatteryChargeStatus.Critical)) + padMeta.BatteryStatus = DsBattery.Dying; + else + padMeta.BatteryStatus = DsBattery.Medium; + } + + // update status + padMeta.IsActive = true; // fixme ? + + var clientsList = new List<IPEndPoint>(); + var now = DateTime.UtcNow; + lock (clients) + { + var clientsToDelete = new List<IPEndPoint>(); + + foreach (var cl in clients) + { + const double TimeoutLimit = 5; + + if ((now - cl.Value.AllPadsTime).TotalSeconds < TimeoutLimit) + { + clientsList.Add(cl.Key); + } + else if (padMeta.PadId < cl.Value.PadIdsTime.Length && + (now - cl.Value.PadIdsTime[padMeta.PadId]).TotalSeconds < TimeoutLimit) + { + clientsList.Add(cl.Key); + } + else if (cl.Value.PadMacsTime.TryGetValue(padMeta.PadMacAddress, out var padTime) && + (now - padTime).TotalSeconds < TimeoutLimit) + { + clientsList.Add(cl.Key); + } + else //check if this client is totally dead, and remove it if so + { + var clientOk = false; + foreach (var t in cl.Value.PadIdsTime) + { + var dur = (now - t).TotalSeconds; + if (dur < TimeoutLimit) + { + clientOk = true; + break; + } + } + + if (!clientOk) + { + foreach (var dict in cl.Value.PadMacsTime) + { + var dur = (now - dict.Value).TotalSeconds; + if (dur < TimeoutLimit) + { + clientOk = true; + break; + } + } + + if (!clientOk) + clientsToDelete.Add(cl.Key); + } + } + } + + foreach (var delCl in clientsToDelete) clients.Remove(delCl); + clientsToDelete.Clear(); + clientsToDelete = null; + } + + if (clientsList.Count <= 0) + return; + + unchecked + { + var outputData = new byte[100]; + var outIdx = BeginPacket(outputData); + Array.Copy(BitConverter.GetBytes((uint)MessageType.DSUS_PadDataRsp), 0, outputData, outIdx, 4); + outIdx += 4; + + outputData[outIdx++] = padMeta.PadId; + outputData[outIdx++] = (byte)padMeta.PadState; + outputData[outIdx++] = (byte)padMeta.Model; + outputData[outIdx++] = (byte)padMeta.ConnectionType; + { + var padMac = padMeta.PadMacAddress.GetAddressBytes(); + outputData[outIdx++] = padMac[0]; + outputData[outIdx++] = padMac[1]; + outputData[outIdx++] = padMac[2]; + outputData[outIdx++] = padMac[3]; + outputData[outIdx++] = padMac[4]; + outputData[outIdx++] = padMac[5]; + } + outputData[outIdx++] = (byte)padMeta.BatteryStatus; + outputData[outIdx++] = padMeta.IsActive ? (byte)1 : (byte)0; + + Array.Copy(BitConverter.GetBytes((uint)udpPacketCount++), 0, outputData, outIdx, 4); + outIdx += 4; + + if (!ReportToBuffer(outputData, ref outIdx)) + return; + FinishPacket(outputData); + + foreach (var cl in clientsList) + { + //try { udpSock.SendTo(outputData, cl); } + int temp = 0; + poolLock.EnterWriteLock(); + temp = listInd; + listInd = ++listInd % ARG_BUFFER_LEN; + SocketAsyncEventArgs args = argsList[temp]; + poolLock.ExitWriteLock(); + + _pool.Wait(); + args.RemoteEndPoint = cl; + Array.Copy(outputData, args.Buffer, outputData.Length); + bool sentAsync = false; + try + { + bool sendAsync = udpSock.SendToAsync(args); + if (!sendAsync) CompletedSynchronousSocketEvent(); + } +<<<<<<< HEAD + catch (SocketException /*ex*/) { } + catch (Exception /*ex*/) { } +======= + catch (ObjectDisposedException) + { + } + catch (NullReferenceException) + { + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + finally + { + if (!sentAsync) CompletedSynchronousSocketEvent(); + } + } + } + + clientsList.Clear(); + clientsList = null; + } + + private enum MessageType + { + DSUC_VersionReq = 0x100000, + DSUS_VersionRsp = 0x100000, + DSUC_ListPorts = 0x100001, + DSUS_PortInfo = 0x100001, + DSUC_PadDataReq = 0x100002, + DSUS_PadDataRsp = 0x100002 + } + + class ClientRequestTimes + { + DateTime allPads; + DateTime[] padIds; + Dictionary<PhysicalAddress, DateTime> padMacs; + + public DateTime AllPadsTime { get { return allPads; } } + public DateTime[] PadIdsTime { get { return padIds; } } + public Dictionary<PhysicalAddress, DateTime> PadMacsTime { get { return padMacs; } } + + public ClientRequestTimes() + { + allPads = DateTime.MinValue; + padIds = new DateTime[4]; + + for (int i = 0; i < padIds.Length; i++) + padIds[i] = DateTime.MinValue; + + padMacs = new Dictionary<PhysicalAddress, DateTime>(); + } + + public void RequestPadInfo(byte regFlags, byte idToReg, PhysicalAddress macToReg) + { + if (regFlags == 0) + allPads = DateTime.UtcNow; + else + { + if ((regFlags & 0x01) != 0) //id valid + { + if (idToReg < padIds.Length) + padIds[idToReg] = DateTime.UtcNow; + } + if ((regFlags & 0x02) != 0) //mac valid + { + padMacs[macToReg] = DateTime.UtcNow; + } + } + } + } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/AOKZOE/AOKZOEA1Pro.cs b/HandheldCompanion/Devices/AOKZOE/AOKZOEA1Pro.cs index 8ef12d4ae..5066f7b35 100644 --- a/HandheldCompanion/Devices/AOKZOE/AOKZOEA1Pro.cs +++ b/HandheldCompanion/Devices/AOKZOE/AOKZOEA1Pro.cs @@ -1,17 +1,20 @@ -namespace HandheldCompanion.Devices; - -public class AOKZOEA1Pro : AOKZOEA1 -{ - public AOKZOEA1Pro() - { - // device specific settings - ProductIllustration = "device_aokzoe_a1"; - ProductModel = "AOKZOEA1Pro"; - - // https://www.amd.com/en/products/apu/amd-ryzen-7-7840u - nTDP = new double[] { 15, 15, 20 }; - cTDP = new double[] { 4, 28 }; - GfxClock = new double[] { 100, 2700 }; - CpuClock = 5100; - } +namespace HandheldCompanion.Devices; + +public class AOKZOEA1Pro : AOKZOEA1 +{ + public AOKZOEA1Pro() + { + // device specific settings + ProductIllustration = "device_aokzoe_a1"; + ProductModel = "AOKZOEA1Pro"; + + // https://www.amd.com/en/products/apu/amd-ryzen-7-7840u + nTDP = new double[] { 15, 15, 20 }; + cTDP = new double[] { 4, 28 }; + GfxClock = new double[] { 100, 2700 }; +<<<<<<< HEAD + CpuClock = 5100; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/ASUS/AsusACPI.cs b/HandheldCompanion/Devices/ASUS/AsusACPI.cs index 801fb70e5..217232427 100644 --- a/HandheldCompanion/Devices/ASUS/AsusACPI.cs +++ b/HandheldCompanion/Devices/ASUS/AsusACPI.cs @@ -1,511 +1,523 @@ -using HandheldCompanion.Managers; -using System; -using System.Management; -using System.Runtime.InteropServices; -using System.Threading; - -public enum AsusFan -{ - CPU = 0, - GPU = 1, - Mid = 2, - XGM = 3 -} - -public enum AsusMode -{ - Performance = 0, - Turbo = 1, - Silent = 2 -} - -public enum AsusGPU -{ - Eco = 0, - Standard = 1, - Ultimate = 2 -} - -namespace HandheldCompanion.Devices.ASUS -{ - public class AsusACPI - { - const string FILE_NAME = @"\\.\\ATKACPI"; - const uint CONTROL_CODE = 0x0022240C; - - const uint DSTS = 0x53545344; - const uint DEVS = 0x53564544; - const uint INIT = 0x54494E49; - - public const uint UniversalControl = 0x00100021; - - public const int KB_Light_Up = 0xc4; - public const int KB_Light_Down = 0xc5; - public const int Brightness_Down = 0x10; - public const int Brightness_Up = 0x20; - public const int KB_Sleep = 0x6c; - public const int KB_DUO_PgUpDn = 0x4B; - public const int KB_DUO_SecondDisplay = 0x6A; - - - public const int Touchpad_Toggle = 0x6B; - - public const int ChargerMode = 0x0012006C; - - public const int ChargerUSB = 2; - public const int ChargerBarrel = 1; - - public const uint CPU_Fan = 0x00110013; - public const uint GPU_Fan = 0x00110014; - public const uint Mid_Fan = 0x00110031; - - public const uint PerformanceMode = 0x00120075; // Performance modes - public const uint VivoBookMode = 0x00110019; // Vivobook performance modes - - public const uint GPUEco = 0x00090020; - public const uint GPUXGConnected = 0x00090018; - public const uint GPUXG = 0x00090019; - public const uint GPUMux = 0x00090016; - - public const uint BatteryLimit = 0x00120057; - public const uint ScreenOverdrive = 0x00050019; - public const uint ScreenMiniled = 0x0005001E; - - public const uint DevsCPUFan = 0x00110022; - public const uint DevsGPUFan = 0x00110023; - - public const uint DevsCPUFanCurve = 0x00110024; - public const uint DevsGPUFanCurve = 0x00110025; - public const uint DevsMidFanCurve = 0x00110032; - - public const int Temp_CPU = 0x00120094; - public const int Temp_GPU = 0x00120097; - - public const int PPT_TotalA0 = 0x001200A0; // SPL (Total limit for all-AMD models) / PL1 - public const int PPT_EDCA1 = 0x001200A1; // CPU EDC - public const int PPT_TDCA2 = 0x001200A2; // CPU TDC - public const int PPT_APUA3 = 0x001200A3; // sPPT (long boost limit) / PL2 - - public const int PPT_CPUB0 = 0x001200B0; // CPU PPT on 2022 (PPT_LIMIT_APU) - public const int PPT_CPUB1 = 0x001200B1; // Total PPT on 2022 (PPT_LIMIT_SLOW) - - public const int PPT_GPUC0 = 0x001200C0; // NVIDIA GPU Boost - public const int PPT_APUC1 = 0x001200C1; // fPPT (fast boost limit) - public const int PPT_GPUC2 = 0x001200C2; // NVIDIA GPU Temp Target (75.. 87 C) - - public const int TUF_KB_BRIGHTNESS = 0x00050021; - public const int TUF_KB = 0x00100056; - public const int TUF_KB_STATE = 0x00100057; - - public const int MICMUTE_LED = 0x00040017; - - public const int TabletState = 0x00060077; - public const int FnLock = 0x00100023; - - public const int ScreenPadToggle = 0x00050031; - public const int ScreenPadBrightness = 0x00050032; - - public const int Tablet_Notebook = 0; - public const int Tablet_Tablet = 1; - public const int Tablet_Tent = 2; - public const int Tablet_Rotated = 3; - - public const int PerformanceBalanced = 0; - public const int PerformanceTurbo = 1; - public const int PerformanceSilent = 2; - public const int PerformanceManual = 4; - - public const int GPUModeEco = 0; - public const int GPUModeStandard = 1; - public const int GPUModeUltimate = 2; - - public const int MinTotal = 5; - - public static int MaxTotal = 150; - public static int DefaultTotal = 125; - - public const int MinCPU = 5; - public const int MaxCPU = 100; - public const int DefaultCPU = 80; - - public const int MinGPUBoost = 5; - public const int MaxGPUBoost = 25; - - public const int MinGPUTemp = 75; - public const int MaxGPUTemp = 87; - - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - private static extern IntPtr CreateFile( - string lpFileName, - uint dwDesiredAccess, - uint dwShareMode, - IntPtr lpSecurityAttributes, - uint dwCreationDisposition, - uint dwFlagsAndAttributes, - IntPtr hTemplateFile - ); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool DeviceIoControl( - IntPtr hDevice, - uint dwIoControlCode, - byte[] lpInBuffer, - uint nInBufferSize, - byte[] lpOutBuffer, - uint nOutBufferSize, - ref uint lpBytesReturned, - IntPtr lpOverlapped - ); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool CloseHandle(IntPtr hObject); - - private const uint GENERIC_READ = 0x80000000; - private const uint GENERIC_WRITE = 0x40000000; - private const uint OPEN_EXISTING = 3; - private const uint FILE_ATTRIBUTE_NORMAL = 0x80; - private const uint FILE_SHARE_READ = 1; - private const uint FILE_SHARE_WRITE = 2; - - private const int ASUS_WMI_KEYBOARD_POWER_BOOT = 0x03 << 16; - private const int ASUS_WMI_KEYBOARD_POWER_AWAKE = 0x0C << 16; - private const int ASUS_WMI_KEYBOARD_POWER_SLEEP = 0x30 << 16; - private const int ASUS_WMI_KEYBOARD_POWER_SHUTDOWN = 0xC0 << 16; - - private IntPtr handle; - - // Event handling attempt - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool WaitForSingleObject(IntPtr hHandle, int dwMilliseconds); - - private IntPtr eventHandle; - - // still works only with asus optimization service on , if someone knows how to get ACPI events from asus without that - let me know - public void RunListener() - { - - eventHandle = CreateEvent(IntPtr.Zero, false, false, "ATK4001"); - - byte[] outBuffer = new byte[16]; - byte[] data = new byte[8]; - bool result; - - data[0] = BitConverter.GetBytes(eventHandle.ToInt32())[0]; - data[1] = BitConverter.GetBytes(eventHandle.ToInt32())[1]; - - Control(0x222400, data, outBuffer); - - while (true) - { - WaitForSingleObject(eventHandle, Timeout.Infinite); - Control(0x222408, new byte[0], outBuffer); - int code = BitConverter.ToInt32(outBuffer, 0); - } - } - - - public AsusACPI() - { - handle = CreateFile( - FILE_NAME, - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_READ | FILE_SHARE_WRITE, - IntPtr.Zero, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - IntPtr.Zero - ); - - if (!IsOpen()) - { - LogManager.LogError("Can't connect to Asus ACPI"); - return; - } - - MaxTotal = 50; - DefaultTotal = 30; - } - - public bool IsOpen() - { - return handle != new IntPtr(-1); - } - - public void Control(uint dwIoControlCode, byte[] lpInBuffer, byte[] lpOutBuffer) - { - - uint lpBytesReturned = 0; - DeviceIoControl( - handle, - dwIoControlCode, - lpInBuffer, - (uint)lpInBuffer.Length, - lpOutBuffer, - (uint)lpOutBuffer.Length, - ref lpBytesReturned, - IntPtr.Zero - ); - } - - public void Close() - { - CloseHandle(handle); - } - - protected byte[] CallMethod(uint MethodID, byte[] args) - { - byte[] acpiBuf = new byte[8 + args.Length]; - byte[] outBuffer = new byte[16]; - - BitConverter.GetBytes((uint)MethodID).CopyTo(acpiBuf, 0); - BitConverter.GetBytes((uint)args.Length).CopyTo(acpiBuf, 4); - Array.Copy(args, 0, acpiBuf, 8, args.Length); - - // if (MethodID == DEVS) Debug.WriteLine(BitConverter.ToString(acpiBuf, 0, acpiBuf.Length)); - - Control(CONTROL_CODE, acpiBuf, outBuffer); - - return outBuffer; - - } - - public byte[] DeviceInit() - { - byte[] args = new byte[8]; - return CallMethod(INIT, args); - } - - public int DeviceSet(uint DeviceID, int Status) - { - byte[] args = new byte[8]; - BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); - BitConverter.GetBytes((uint)Status).CopyTo(args, 4); - - byte[] status = CallMethod(DEVS, args); - int result = BitConverter.ToInt32(status, 0); - - return result; - } - - public int DeviceSet(uint DeviceID, byte[] Params, string logName) - { - byte[] args = new byte[4 + Params.Length]; - BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); - Params.CopyTo(args, 4); - - byte[] status = CallMethod(DEVS, args); - int result = BitConverter.ToInt32(status, 0); - - return BitConverter.ToInt32(status, 0); - } - - public int DeviceGet(uint DeviceID) - { - byte[] args = new byte[8]; - BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); - byte[] status = CallMethod(DSTS, args); - - return BitConverter.ToInt32(status, 0) - 65536; - - } - - public byte[] DeviceGetBuffer(uint DeviceID, uint Status = 0) - { - byte[] args = new byte[8]; - BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); - BitConverter.GetBytes((uint)Status).CopyTo(args, 4); - - return CallMethod(DSTS, args); - } - - public int SetGPUEco(int eco) - { - int ecoFlag = DeviceGet(GPUEco); - if (ecoFlag < 0) return -1; - - if (ecoFlag == 1 && eco == 0) - return DeviceSet(GPUEco, eco); - - if (ecoFlag == 0 && eco == 1) - return DeviceSet(GPUEco, eco); - - return -1; - } - - public int SetFanRange(AsusFan device, byte[] curve) - { - byte min = (byte)(curve[8] * 255 / 100); - byte max = (byte)(curve[15] * 255 / 100); - byte[] range = { min, max }; - - int result; - switch (device) - { - case AsusFan.GPU: - result = DeviceSet(DevsGPUFan, range, "FanRangeGPU"); - break; - default: - result = DeviceSet(DevsCPUFan, range, "FanRangeCPU"); - break; - } - return result; - } - - public int SetFanSpeed(AsusFan device, byte speed) - { - byte[] curve = new byte[] { 0x1E, 0x28, 0x32, 0x3C, 0x46, 0x50, 0x5A, 0x5A, speed, speed, speed, speed, speed, speed, speed, speed }; - int result; - switch (device) - { - case AsusFan.GPU: - result = DeviceSet(DevsGPUFanCurve, curve, "FanGPU"); - break; - case AsusFan.Mid: - result = DeviceSet(DevsMidFanCurve, curve, "FanMid"); - break; - default: - result = DeviceSet(DevsCPUFanCurve, curve, "FanCPU"); - break; - } - return result; - } - - public int SetFanCurve(AsusFan device, byte[] curve) - { - - if (curve.Length != 16) return -1; - // if (curve.All(singleByte => singleByte == 0)) return -1; - - int result; - int fanScale = 100; //AppConfig.Get("fan_scale", 100); - - // if (fanScale != 100 && device == AsusFan.CPU) Logger.WriteLine("Custom fan scale: " + fanScale); - - // it seems to be a bug, when some old model's bios can go nuts if fan is set to 100% - - for (int i = 8; i < curve.Length; i++) curve[i] = (byte)(Math.Max((byte)0, Math.Min((byte)99, curve[i])) * fanScale / 100); - - switch (device) - { - case AsusFan.GPU: - result = DeviceSet(DevsGPUFanCurve, curve, "FanGPU"); - break; - case AsusFan.Mid: - result = DeviceSet(DevsMidFanCurve, curve, "FanMid"); - break; - default: - result = DeviceSet(DevsCPUFanCurve, curve, "FanCPU"); - break; - } - - return result; - } - - public byte[] GetFanCurve(AsusFan device, int mode = 0) - { - uint fan_mode; - - // because it's asus, and modes are swapped here - switch (mode) - { - case 1: fan_mode = 2; break; - case 2: fan_mode = 1; break; - default: fan_mode = 0; break; - } - - switch (device) - { - case AsusFan.GPU: - return DeviceGetBuffer(DevsGPUFanCurve, fan_mode); - case AsusFan.Mid: - return DeviceGetBuffer(DevsMidFanCurve, fan_mode); - default: - return DeviceGetBuffer(DevsCPUFanCurve, fan_mode); - } - - } - - public static bool IsInvalidCurve(byte[] curve) - { - return curve.Length != 16 || IsEmptyCurve(curve); - } - - public static bool IsEmptyCurve(byte[] curve) - { - return false; - } - - public static byte[] FixFanCurve(byte[] curve) - { - return curve; - - } - - public bool IsXGConnected() - { - //return true; - return DeviceGet(GPUXGConnected) == 1; - } - - public bool IsAllAmdPPT() - { - return DeviceGet(PPT_CPUB0) >= 0 && DeviceGet(PPT_GPUC0) < 0; - } - - public void TUFKeyboardBrightness(int brightness) - { - int param = 0x80 | (brightness & 0x7F); - DeviceSet(TUF_KB_BRIGHTNESS, param); - } - - public void TUFKeyboardRGB(int mode, System.Drawing.Color color, int speed) - { - - byte[] setting = new byte[12]; - setting[0] = (byte)0xB4; - setting[1] = (byte)mode; - setting[2] = color.R; - setting[3] = color.G; - setting[4] = color.B; - setting[5] = (byte)speed; - - DeviceSet(TUF_KB, setting, "TUF RGB"); - //Debug.WriteLine(BitConverter.ToString(setting)); - } - - public void TUFKeyboardPower(bool awake = true, bool boot = false, bool sleep = false, bool shutdown = false) - { - int state = 0xbd; - - if (boot) state = state | ASUS_WMI_KEYBOARD_POWER_BOOT; - if (awake) state = state | ASUS_WMI_KEYBOARD_POWER_AWAKE; - if (sleep) state = state | ASUS_WMI_KEYBOARD_POWER_SLEEP; - if (shutdown) state = state | ASUS_WMI_KEYBOARD_POWER_SHUTDOWN; - - state = state | 0x01 << 8; - - DeviceSet(TUF_KB_STATE, state); - } - - public void SubscribeToEvents(Action<object, EventArrivedEventArgs> EventHandler) - { - try - { - ManagementEventWatcher watcher = new ManagementEventWatcher(); - watcher.EventArrived += new EventArrivedEventHandler(EventHandler); - watcher.Scope = new ManagementScope("root\\wmi"); - watcher.Query = new WqlEventQuery("SELECT * FROM AsusAtkWmiEvent"); - watcher.Start(); - } - catch - { - - } - } - } -} +<<<<<<< HEAD +using HandheldCompanion.Managers; +using System; +using System.Management; +using System.Runtime.InteropServices; +using System.Threading; +======= +using System.Management; +using System.Runtime.InteropServices; +using System; +using System.Threading; +using HandheldCompanion.Managers; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + +public enum AsusFan +{ + CPU = 0, + GPU = 1, + Mid = 2, + XGM = 3 +} + +public enum AsusMode +{ +<<<<<<< HEAD + Performance = 0, +======= + Balanced = 0, +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Turbo = 1, + Silent = 2 +} + +public enum AsusGPU +{ + Eco = 0, + Standard = 1, + Ultimate = 2 +} + +namespace HandheldCompanion.Devices.ASUS +{ + public class AsusACPI + { + const string FILE_NAME = @"\\.\\ATKACPI"; + const uint CONTROL_CODE = 0x0022240C; + + const uint DSTS = 0x53545344; + const uint DEVS = 0x53564544; + const uint INIT = 0x54494E49; + + public const uint UniversalControl = 0x00100021; + + public const int KB_Light_Up = 0xc4; + public const int KB_Light_Down = 0xc5; + public const int Brightness_Down = 0x10; + public const int Brightness_Up = 0x20; + public const int KB_Sleep = 0x6c; + public const int KB_DUO_PgUpDn = 0x4B; + public const int KB_DUO_SecondDisplay = 0x6A; + + + public const int Touchpad_Toggle = 0x6B; + + public const int ChargerMode = 0x0012006C; + + public const int ChargerUSB = 2; + public const int ChargerBarrel = 1; + + public const uint CPU_Fan = 0x00110013; + public const uint GPU_Fan = 0x00110014; + public const uint Mid_Fan = 0x00110031; + + public const uint PerformanceMode = 0x00120075; // Performance modes + public const uint VivoBookMode = 0x00110019; // Vivobook performance modes + + public const uint GPUEco = 0x00090020; + public const uint GPUXGConnected = 0x00090018; + public const uint GPUXG = 0x00090019; + public const uint GPUMux = 0x00090016; + + public const uint BatteryLimit = 0x00120057; + public const uint ScreenOverdrive = 0x00050019; + public const uint ScreenMiniled = 0x0005001E; + + public const uint DevsCPUFan = 0x00110022; + public const uint DevsGPUFan = 0x00110023; + + public const uint DevsCPUFanCurve = 0x00110024; + public const uint DevsGPUFanCurve = 0x00110025; + public const uint DevsMidFanCurve = 0x00110032; + + public const int Temp_CPU = 0x00120094; + public const int Temp_GPU = 0x00120097; + + public const int PPT_TotalA0 = 0x001200A0; // SPL (Total limit for all-AMD models) / PL1 + public const int PPT_EDCA1 = 0x001200A1; // CPU EDC + public const int PPT_TDCA2 = 0x001200A2; // CPU TDC + public const int PPT_APUA3 = 0x001200A3; // sPPT (long boost limit) / PL2 + + public const int PPT_CPUB0 = 0x001200B0; // CPU PPT on 2022 (PPT_LIMIT_APU) + public const int PPT_CPUB1 = 0x001200B1; // Total PPT on 2022 (PPT_LIMIT_SLOW) + + public const int PPT_GPUC0 = 0x001200C0; // NVIDIA GPU Boost + public const int PPT_APUC1 = 0x001200C1; // fPPT (fast boost limit) + public const int PPT_GPUC2 = 0x001200C2; // NVIDIA GPU Temp Target (75.. 87 C) + + public const int TUF_KB_BRIGHTNESS = 0x00050021; + public const int TUF_KB = 0x00100056; + public const int TUF_KB_STATE = 0x00100057; + + public const int MICMUTE_LED = 0x00040017; + + public const int TabletState = 0x00060077; + public const int FnLock = 0x00100023; + + public const int ScreenPadToggle = 0x00050031; + public const int ScreenPadBrightness = 0x00050032; + + public const int Tablet_Notebook = 0; + public const int Tablet_Tablet = 1; + public const int Tablet_Tent = 2; + public const int Tablet_Rotated = 3; + + public const int PerformanceBalanced = 0; + public const int PerformanceTurbo = 1; + public const int PerformanceSilent = 2; + public const int PerformanceManual = 4; + + public const int GPUModeEco = 0; + public const int GPUModeStandard = 1; + public const int GPUModeUltimate = 2; + + public const int MinTotal = 5; + + public static int MaxTotal = 150; + public static int DefaultTotal = 125; + + public const int MinCPU = 5; + public const int MaxCPU = 100; + public const int DefaultCPU = 80; + + public const int MinGPUBoost = 5; + public const int MaxGPUBoost = 25; + + public const int MinGPUTemp = 75; + public const int MaxGPUTemp = 87; + + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern IntPtr CreateFile( + string lpFileName, + uint dwDesiredAccess, + uint dwShareMode, + IntPtr lpSecurityAttributes, + uint dwCreationDisposition, + uint dwFlagsAndAttributes, + IntPtr hTemplateFile + ); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool DeviceIoControl( + IntPtr hDevice, + uint dwIoControlCode, + byte[] lpInBuffer, + uint nInBufferSize, + byte[] lpOutBuffer, + uint nOutBufferSize, + ref uint lpBytesReturned, + IntPtr lpOverlapped + ); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool CloseHandle(IntPtr hObject); + + private const uint GENERIC_READ = 0x80000000; + private const uint GENERIC_WRITE = 0x40000000; + private const uint OPEN_EXISTING = 3; + private const uint FILE_ATTRIBUTE_NORMAL = 0x80; + private const uint FILE_SHARE_READ = 1; + private const uint FILE_SHARE_WRITE = 2; + + private const int ASUS_WMI_KEYBOARD_POWER_BOOT = 0x03 << 16; + private const int ASUS_WMI_KEYBOARD_POWER_AWAKE = 0x0C << 16; + private const int ASUS_WMI_KEYBOARD_POWER_SLEEP = 0x30 << 16; + private const int ASUS_WMI_KEYBOARD_POWER_SHUTDOWN = 0xC0 << 16; + + private IntPtr handle; + + // Event handling attempt + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool WaitForSingleObject(IntPtr hHandle, int dwMilliseconds); + + private IntPtr eventHandle; + + // still works only with asus optimization service on , if someone knows how to get ACPI events from asus without that - let me know + public void RunListener() + { + + eventHandle = CreateEvent(IntPtr.Zero, false, false, "ATK4001"); + + byte[] outBuffer = new byte[16]; + byte[] data = new byte[8]; + bool result; + + data[0] = BitConverter.GetBytes(eventHandle.ToInt32())[0]; + data[1] = BitConverter.GetBytes(eventHandle.ToInt32())[1]; + + Control(0x222400, data, outBuffer); + + while (true) + { + WaitForSingleObject(eventHandle, Timeout.Infinite); + Control(0x222408, new byte[0], outBuffer); + int code = BitConverter.ToInt32(outBuffer, 0); + } + } + + + public AsusACPI() + { + handle = CreateFile( + FILE_NAME, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + IntPtr.Zero, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + IntPtr.Zero + ); + + if (!IsOpen()) + { + LogManager.LogError("Can't connect to Asus ACPI"); + return; + } + + MaxTotal = 50; + DefaultTotal = 30; + } + + public bool IsOpen() + { + return handle != new IntPtr(-1); + } + + public void Control(uint dwIoControlCode, byte[] lpInBuffer, byte[] lpOutBuffer) + { + + uint lpBytesReturned = 0; + DeviceIoControl( + handle, + dwIoControlCode, + lpInBuffer, + (uint)lpInBuffer.Length, + lpOutBuffer, + (uint)lpOutBuffer.Length, + ref lpBytesReturned, + IntPtr.Zero + ); + } + + public void Close() + { + CloseHandle(handle); + } + + protected byte[] CallMethod(uint MethodID, byte[] args) + { + byte[] acpiBuf = new byte[8 + args.Length]; + byte[] outBuffer = new byte[16]; + + BitConverter.GetBytes((uint)MethodID).CopyTo(acpiBuf, 0); + BitConverter.GetBytes((uint)args.Length).CopyTo(acpiBuf, 4); + Array.Copy(args, 0, acpiBuf, 8, args.Length); + + // if (MethodID == DEVS) Debug.WriteLine(BitConverter.ToString(acpiBuf, 0, acpiBuf.Length)); + + Control(CONTROL_CODE, acpiBuf, outBuffer); + + return outBuffer; + + } + + public byte[] DeviceInit() + { + byte[] args = new byte[8]; + return CallMethod(INIT, args); + } + + public int DeviceSet(uint DeviceID, int Status) + { + byte[] args = new byte[8]; + BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); + BitConverter.GetBytes((uint)Status).CopyTo(args, 4); + + byte[] status = CallMethod(DEVS, args); + int result = BitConverter.ToInt32(status, 0); + + return result; + } + + public int DeviceSet(uint DeviceID, byte[] Params, string logName) + { + byte[] args = new byte[4 + Params.Length]; + BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); + Params.CopyTo(args, 4); + + byte[] status = CallMethod(DEVS, args); + int result = BitConverter.ToInt32(status, 0); + + return BitConverter.ToInt32(status, 0); + } + + public int DeviceGet(uint DeviceID) + { + byte[] args = new byte[8]; + BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); + byte[] status = CallMethod(DSTS, args); + + return BitConverter.ToInt32(status, 0) - 65536; + + } + + public byte[] DeviceGetBuffer(uint DeviceID, uint Status = 0) + { + byte[] args = new byte[8]; + BitConverter.GetBytes((uint)DeviceID).CopyTo(args, 0); + BitConverter.GetBytes((uint)Status).CopyTo(args, 4); + + return CallMethod(DSTS, args); + } + + public int SetGPUEco(int eco) + { + int ecoFlag = DeviceGet(GPUEco); + if (ecoFlag < 0) return -1; + + if (ecoFlag == 1 && eco == 0) + return DeviceSet(GPUEco, eco); + + if (ecoFlag == 0 && eco == 1) + return DeviceSet(GPUEco, eco); + + return -1; + } + + public int SetFanRange(AsusFan device, byte[] curve) + { + byte min = (byte)(curve[8] * 255 / 100); + byte max = (byte)(curve[15] * 255 / 100); + byte[] range = { min, max }; + + int result; + switch (device) + { + case AsusFan.GPU: + result = DeviceSet(DevsGPUFan, range, "FanRangeGPU"); + break; + default: + result = DeviceSet(DevsCPUFan, range, "FanRangeCPU"); + break; + } + return result; + } + + public int SetFanSpeed(AsusFan device, byte speed) + { + byte[] curve = new byte[] { 0x1E, 0x28, 0x32, 0x3C, 0x46, 0x50, 0x5A, 0x5A, speed, speed, speed, speed, speed, speed, speed, speed }; + int result; + switch (device) + { + case AsusFan.GPU: + result = DeviceSet(DevsGPUFanCurve, curve, "FanGPU"); + break; + case AsusFan.Mid: + result = DeviceSet(DevsMidFanCurve, curve, "FanMid"); + break; + default: + result = DeviceSet(DevsCPUFanCurve, curve, "FanCPU"); + break; + } + return result; + } + + public int SetFanCurve(AsusFan device, byte[] curve) + { + + if (curve.Length != 16) return -1; + // if (curve.All(singleByte => singleByte == 0)) return -1; + + int result; + int fanScale = 100; //AppConfig.Get("fan_scale", 100); + + // if (fanScale != 100 && device == AsusFan.CPU) Logger.WriteLine("Custom fan scale: " + fanScale); + + // it seems to be a bug, when some old model's bios can go nuts if fan is set to 100% + + for (int i = 8; i < curve.Length; i++) curve[i] = (byte)(Math.Max((byte)0, Math.Min((byte)99, curve[i])) * fanScale / 100); + + switch (device) + { + case AsusFan.GPU: + result = DeviceSet(DevsGPUFanCurve, curve, "FanGPU"); + break; + case AsusFan.Mid: + result = DeviceSet(DevsMidFanCurve, curve, "FanMid"); + break; + default: + result = DeviceSet(DevsCPUFanCurve, curve, "FanCPU"); + break; + } + + return result; + } + + public byte[] GetFanCurve(AsusFan device, int mode = 0) + { + uint fan_mode; + + // because it's asus, and modes are swapped here + switch (mode) + { + case 1: fan_mode = 2; break; + case 2: fan_mode = 1; break; + default: fan_mode = 0; break; + } + + switch (device) + { + case AsusFan.GPU: + return DeviceGetBuffer(DevsGPUFanCurve, fan_mode); + case AsusFan.Mid: + return DeviceGetBuffer(DevsMidFanCurve, fan_mode); + default: + return DeviceGetBuffer(DevsCPUFanCurve, fan_mode); + } + + } + + public static bool IsInvalidCurve(byte[] curve) + { + return curve.Length != 16 || IsEmptyCurve(curve); + } + + public static bool IsEmptyCurve(byte[] curve) + { + return false; + } + + public static byte[] FixFanCurve(byte[] curve) + { + return curve; + + } + + public bool IsXGConnected() + { + //return true; + return DeviceGet(GPUXGConnected) == 1; + } + + public bool IsAllAmdPPT() + { + return DeviceGet(PPT_CPUB0) >= 0 && DeviceGet(PPT_GPUC0) < 0; + } + + public void TUFKeyboardBrightness(int brightness) + { + int param = 0x80 | (brightness & 0x7F); + DeviceSet(TUF_KB_BRIGHTNESS, param); + } + + public void TUFKeyboardRGB(int mode, System.Drawing.Color color, int speed) + { + + byte[] setting = new byte[12]; + setting[0] = (byte)0xB4; + setting[1] = (byte)mode; + setting[2] = color.R; + setting[3] = color.G; + setting[4] = color.B; + setting[5] = (byte)speed; + + DeviceSet(TUF_KB, setting, "TUF RGB"); + //Debug.WriteLine(BitConverter.ToString(setting)); + } + + public void TUFKeyboardPower(bool awake = true, bool boot = false, bool sleep = false, bool shutdown = false) + { + int state = 0xbd; + + if (boot) state = state | ASUS_WMI_KEYBOARD_POWER_BOOT; + if (awake) state = state | ASUS_WMI_KEYBOARD_POWER_AWAKE; + if (sleep) state = state | ASUS_WMI_KEYBOARD_POWER_SLEEP; + if (shutdown) state = state | ASUS_WMI_KEYBOARD_POWER_SHUTDOWN; + + state = state | 0x01 << 8; + + DeviceSet(TUF_KB_STATE, state); + } + + public void SubscribeToEvents(Action<object, EventArrivedEventArgs> EventHandler) + { + try + { + ManagementEventWatcher watcher = new ManagementEventWatcher(); + watcher.EventArrived += new EventArrivedEventHandler(EventHandler); + watcher.Scope = new ManagementScope("root\\wmi"); + watcher.Query = new WqlEventQuery("SELECT * FROM AsusAtkWmiEvent"); + watcher.Start(); + } + catch + { + + } + } + } +} diff --git a/HandheldCompanion/Devices/ASUS/ROGAlly.cs b/HandheldCompanion/Devices/ASUS/ROGAlly.cs index 787d36292..3d563272e 100644 --- a/HandheldCompanion/Devices/ASUS/ROGAlly.cs +++ b/HandheldCompanion/Devices/ASUS/ROGAlly.cs @@ -1,3 +1,4 @@ +<<<<<<< HEAD using HandheldCompanion.Devices.ASUS; using HandheldCompanion.Inputs; using HandheldCompanion.Managers; @@ -683,4 +684,244 @@ public void SendHidControlWrite(byte[] data) } } +======= +using HandheldCompanion.Devices.ASUS; +using HandheldCompanion.Inputs; +using HandheldCompanion.Utils; +using HidLibrary; +using Nefarius.Utilities.DeviceManagement.PnP; +using System; +using System.Collections.Generic; +using System.Numerics; +using WindowsInput.Events; +using Task = System.Threading.Tasks.Task; +using System.Threading.Tasks; + +namespace HandheldCompanion.Devices; + +public class ROGAlly : IDevice +{ + private readonly Dictionary<byte, ButtonFlags> keyMapping = new() + { + { 0, ButtonFlags.None }, + { 166, ButtonFlags.OEM1 }, + { 56, ButtonFlags.OEM2 }, + { 165, ButtonFlags.OEM3 }, + { 167, ButtonFlags.OEM4 }, + { 168, ButtonFlags.OEM4 }, + }; + + private Dictionary<byte, HidDevice> hidDevices = new(); + private AsusACPI asusACPI; + + private const byte INPUT_HID_ID = 0x5a; + + public override bool IsOpen => hidDevices.ContainsKey(INPUT_HID_ID) && hidDevices[INPUT_HID_ID].IsOpen && asusACPI is not null && asusACPI.IsOpen(); + + public ROGAlly() + { + // device specific settings + ProductIllustration = "device_rog_ally"; + + // used to monitor OEM specific inputs + _vid = 0x0B05; + _pid = 0x1ABE; + + // https://www.amd.com/en/products/apu/amd-ryzen-z1 + // https://www.amd.com/en/products/apu/amd-ryzen-z1-extreme + // https://www.amd.com/en/products/apu/amd-ryzen-7-7840u + nTDP = new double[] { 15, 15, 20 }; + cTDP = new double[] { 5, 30 }; + GfxClock = new double[] { 100, 2700 }; + + AngularVelocityAxis = new Vector3(-1.0f, 1.0f, 1.0f); + AngularVelocityAxisSwap = new SortedDictionary<char, char> + { + { 'X', 'X' }, + { 'Y', 'Z' }, + { 'Z', 'Y' } + }; + + AccelerationAxis = new Vector3(1.0f, 1.0f, 1.0f); + AccelerationAxisSwap = new SortedDictionary<char, char> + { + { 'X', 'X' }, + { 'Y', 'Z' }, + { 'Z', 'Y' } + }; + + // device specific capacities + Capabilities = DeviceCapabilities.FanControl; + + OEMChords.Add(new DeviceChord("CC", + new List<KeyCode>(), new List<KeyCode>(), + false, ButtonFlags.OEM1 + )); + + OEMChords.Add(new DeviceChord("AC", + new List<KeyCode>(), new List<KeyCode>(), + false, ButtonFlags.OEM2 + )); + + OEMChords.Add(new DeviceChord("M1/M2", + new List<KeyCode>(), new List<KeyCode>(), + false, ButtonFlags.OEM3 + )); + } + + public override bool Open() + { + var success = base.Open(); + if (!success) + return false; + + // try open asus ACPI + asusACPI = new AsusACPI(); + if (asusACPI is null) + return false; + + return true; + } + + public override void Close() + { + // close Asus ACPI + if (asusACPI is not null) + asusACPI.Close(); + + // close devices + foreach (HidDevice hidDevice in hidDevices.Values) + hidDevice.CloseDevice(); + + base.Close(); + } + + public override bool IsReady() + { + IEnumerable<HidDevice> devices = GetHidDevices(_vid, _pid); + foreach (HidDevice device in devices) + { + if (!device.IsConnected) + continue; + + if (device.ReadFeatureData(out byte[] data, INPUT_HID_ID)) + { + device.OpenDevice(); + device.MonitorDeviceEvents = true; + + hidDevices[INPUT_HID_ID] = device; + + Task<HidReport> ReportDevice = Task.Run(async () => await device.ReadReportAsync()); + ReportDevice.ContinueWith(t => OnReport(ReportDevice.Result, device)); + } + } + + hidDevices.TryGetValue(INPUT_HID_ID, out HidDevice hidDevice); + if (hidDevice is null || !hidDevice.IsConnected) + return false; + + PnPDevice pnpDevice = PnPDevice.GetDeviceByInterfaceId(hidDevice.DevicePath); + string device_parent = pnpDevice.GetProperty<string>(DevicePropertyKey.Device_Parent); + + PnPDevice pnpParent = PnPDevice.GetDeviceByInstanceId(device_parent); + Guid parent_guid = pnpParent.GetProperty<Guid>(DevicePropertyKey.Device_ClassGuid); + string parent_instanceId = pnpParent.GetProperty<string>(DevicePropertyKey.Device_InstanceId); + + return DeviceHelper.IsDeviceAvailable(parent_guid, parent_instanceId); + } + + private void OnReport(HidReport result, HidDevice device) + { + Task<HidReport> ReportDevice = Task.Run(async () => await device.ReadReportAsync()); + ReportDevice.ContinueWith(t => OnReport(ReportDevice.Result, device)); + + // get key + byte key = result.Data[0]; + HandleEvent(key); + } + + public override void SetFanControl(bool enable) + { + if (!IsOpen) + return; + + switch (enable) + { + case false: + asusACPI.DeviceSet(AsusACPI.PerformanceMode, (int)AsusMode.Turbo); + return; + } + } + + public override void SetFanDuty(double percent) + { + if (!IsOpen) + return; + + asusACPI.SetFanSpeed(AsusFan.CPU, Convert.ToByte(percent)); + asusACPI.SetFanSpeed(AsusFan.GPU, Convert.ToByte(percent)); + } + + public override float ReadFanDuty() + { + if (!IsOpen) + return 100.0f; + + int cpuFan = asusACPI.DeviceGet(AsusACPI.CPU_Fan); + int gpuFan = asusACPI.DeviceGet(AsusACPI.GPU_Fan); + return (cpuFan + gpuFan) / 2 * 100; + } + + public override void SetKeyPressDelay(HIDmode controllerMode) + { + switch (controllerMode) + { + case HIDmode.DualShock4Controller: + KeyPressDelay = 180; + break; + default: + KeyPressDelay = 20; + break; + } + } + + private void HandleEvent(byte key) + { + if (!keyMapping.ContainsKey(key)) + return; + + // get button + ButtonFlags button = keyMapping[key]; + switch (key) + { + case 0: // Back paddles: Release + { + KeyRelease(ButtonFlags.OEM3); + } + return; + + case 165: // Back paddles: Press + case 167: // Armory crate: Hold + KeyPress(button); + break; + + case 168: // Armory crate: Hold, released + KeyRelease(button); + break; + + default: + case 56: // Armory crate: Click + case 166: // Command center: Click + { + Task.Factory.StartNew(async () => + { + KeyPress(button); + await Task.Delay(KeyPressDelay); + KeyRelease(button); + }); + } + break; + } + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } \ No newline at end of file diff --git a/HandheldCompanion/Devices/AYANEO/AYANEO2.cs b/HandheldCompanion/Devices/AYANEO/AYANEO2.cs index 73a22b506..5495be78a 100644 --- a/HandheldCompanion/Devices/AYANEO/AYANEO2.cs +++ b/HandheldCompanion/Devices/AYANEO/AYANEO2.cs @@ -1,4 +1,8 @@ +<<<<<<< HEAD using HandheldCompanion.Inputs; +======= +using HandheldCompanion.Inputs; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d using System.Collections.Generic; using System.Numerics; using WindowsInput.Events; @@ -36,8 +40,12 @@ public AYANEO2() }; // device specific capacities +<<<<<<< HEAD Capabilities = DeviceCapabilities.FanControl; Capabilities |= DeviceCapabilities.DynamicLighting; +======= + Capabilities = DeviceCapabilities.FanControl; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d ECDetails = new ECDetails { diff --git a/HandheldCompanion/Devices/AYANEO/AYANEO2S.cs b/HandheldCompanion/Devices/AYANEO/AYANEO2S.cs index 3c739ff70..0a80ceea0 100644 --- a/HandheldCompanion/Devices/AYANEO/AYANEO2S.cs +++ b/HandheldCompanion/Devices/AYANEO/AYANEO2S.cs @@ -1,13 +1,94 @@ -namespace HandheldCompanion.Devices; - -public class AYANEO2S : AYANEO2 -{ - public AYANEO2S() - { - // https://www.amd.com/en/products/apu/amd-ryzen-7-7840U - nTDP = new double[] { 15, 15, 20 }; - cTDP = new double[] { 3, 30 }; - GfxClock = new double[] { 100, 2700 }; - CpuClock = 5100; - } -} +<<<<<<< HEAD +namespace HandheldCompanion.Devices; + +public class AYANEO2S : AYANEO2 +{ + public AYANEO2S() + { + // https://www.amd.com/en/products/apu/amd-ryzen-7-7840U + nTDP = new double[] { 15, 15, 20 }; + cTDP = new double[] { 3, 30 }; + GfxClock = new double[] { 100, 2700 }; + CpuClock = 5100; + } +} +======= +using HandheldCompanion.Inputs; +using System.Collections.Generic; +<<<<<<<< HEAD:HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs +======== +using System.Numerics; +>>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d:HandheldCompanion/Devices/AYANEO/AYANEO2S.cs +using WindowsInput.Events; + +namespace HandheldCompanion.Devices; + +public class AYANEOAIR1S : AYANEOAIR +{ + public AYANEOAIR1S() + { + // https://www.amd.com/en/products/apu/amd-ryzen-7-7840u + nTDP = new double[] { 15, 15, 20 }; + cTDP = new double[] { 4, 28 }; + GfxClock = new double[] { 100, 2700 }; + CpuClock = 5100; + +<<<<<<<< HEAD:HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs + OEMChords.Clear(); +======== + AngularVelocityAxis = new Vector3(1.0f, 1.0f, 1.0f); + AngularVelocityAxisSwap = new SortedDictionary<char, char> + { + { 'X', 'X' }, + { 'Y', 'Z' }, + { 'Z', 'Y' } + }; + + AccelerationAxis = new Vector3(1.0f, 1.0f, 1.0f); + AccelerationAxisSwap = new SortedDictionary<char, char> + { + { 'X', 'X' }, + { 'Y', 'Z' }, + { 'Z', 'Y' } + }; + + // device specific capacities + Capabilities = DeviceCapabilities.FanControl; + + ECDetails = new ECDetails + { + AddressControl = 0x44A, + AddressDuty = 0x44B, + AddressRegistry = 0x4E, + AddressData = 0x4F, + ValueMin = 0, + ValueMax = 100 + }; +>>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d:HandheldCompanion/Devices/AYANEO/AYANEO2S.cs + + OEMChords.Add(new DeviceChord("Custom Key Top Right", + new List<KeyCode> { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F16 }, + new List<KeyCode> { KeyCode.F16, KeyCode.LWin, KeyCode.RControlKey }, + false, ButtonFlags.OEM3 + )); + + OEMChords.Add(new DeviceChord("Custom Key Top Left", + new List<KeyCode> { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F15 }, + new List<KeyCode> { KeyCode.F15, KeyCode.LWin, KeyCode.RControlKey }, + false, ButtonFlags.OEM4 + )); + + OEMChords.Add(new DeviceChord("Custom Key Big", + new List<KeyCode> { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F17 }, + new List<KeyCode> { KeyCode.F17, KeyCode.LWin, KeyCode.RControlKey }, + false, ButtonFlags.OEM1 + )); + + OEMChords.Add(new DeviceChord("Custom Key Small", + new List<KeyCode> { KeyCode.LWin, KeyCode.D }, + new List<KeyCode> { KeyCode.LWin, KeyCode.D }, + false, ButtonFlags.OEM2 + )); + } +} +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIR.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIR.cs index c0c053192..d69bf523f 100644 --- a/HandheldCompanion/Devices/AYANEO/AYANEOAIR.cs +++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIR.cs @@ -1,10 +1,19 @@ using HandheldCompanion.Inputs; using System.Collections.Generic; using System.Numerics; +<<<<<<< HEAD using WindowsInput.Events; namespace HandheldCompanion.Devices; public class AYANEOAIR : AYANEO.AYANEODevice +======= + +using WindowsInput.Events; + +namespace HandheldCompanion.Devices; + +public class AYANEOAIR : IDevice +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { public AYANEOAIR() { @@ -35,7 +44,10 @@ public AYANEOAIR() // device specific capacities Capabilities = DeviceCapabilities.FanControl; +<<<<<<< HEAD Capabilities |= DeviceCapabilities.DynamicLighting; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d ECDetails = new ECDetails { diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs index 90aed0744..2446c2872 100644 --- a/HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs +++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs @@ -1,43 +1,86 @@ -using HandheldCompanion.Inputs; -using System.Collections.Generic; -using WindowsInput.Events; - -namespace HandheldCompanion.Devices; - -public class AYANEOAIR1S : AYANEOAIR -{ - public AYANEOAIR1S() - { - // https://www.amd.com/en/products/apu/amd-ryzen-7-7840u - nTDP = new double[] { 15, 15, 20 }; - cTDP = new double[] { 4, 28 }; - GfxClock = new double[] { 100, 2700 }; - CpuClock = 5100; - - OEMChords.Clear(); - - OEMChords.Add(new DeviceChord("Custom Key Top Right", - new List<KeyCode> { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F16 }, - new List<KeyCode> { KeyCode.F16, KeyCode.LWin, KeyCode.RControlKey }, - false, ButtonFlags.OEM3 - )); - - OEMChords.Add(new DeviceChord("Custom Key Top Left", - new List<KeyCode> { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F15 }, - new List<KeyCode> { KeyCode.F15, KeyCode.LWin, KeyCode.RControlKey }, - false, ButtonFlags.OEM4 - )); - - OEMChords.Add(new DeviceChord("Custom Key Big", - new List<KeyCode> { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F17 }, - new List<KeyCode> { KeyCode.F17, KeyCode.LWin, KeyCode.RControlKey }, - false, ButtonFlags.OEM1 - )); - - OEMChords.Add(new DeviceChord("Custom Key Small", - new List<KeyCode> { KeyCode.LWin, KeyCode.D }, - new List<KeyCode> { KeyCode.LWin, KeyCode.D }, - false, ButtonFlags.OEM2 - )); - } +using HandheldCompanion.Inputs; +using System.Collections.Generic; +<<<<<<< HEAD +<<<<<<<< HEAD:HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs +======== +using System.Numerics; +>>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d:HandheldCompanion/Devices/AYANEO/AYANEO2S.cs +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using WindowsInput.Events; + +namespace HandheldCompanion.Devices; + +public class AYANEOAIR1S : AYANEOAIR +{ + public AYANEOAIR1S() + { + // https://www.amd.com/en/products/apu/amd-ryzen-7-7840u + nTDP = new double[] { 15, 15, 20 }; + cTDP = new double[] { 4, 28 }; + GfxClock = new double[] { 100, 2700 }; +<<<<<<< HEAD + CpuClock = 5100; + +<<<<<<<< HEAD:HandheldCompanion/Devices/AYANEO/AYANEOAIR1S.cs + OEMChords.Clear(); +======== + AngularVelocityAxis = new Vector3(1.0f, 1.0f, 1.0f); + AngularVelocityAxisSwap = new SortedDictionary<char, char> + { + { 'X', 'X' }, + { 'Y', 'Z' }, + { 'Z', 'Y' } + }; + + AccelerationAxis = new Vector3(1.0f, 1.0f, 1.0f); + AccelerationAxisSwap = new SortedDictionary<char, char> + { + { 'X', 'X' }, + { 'Y', 'Z' }, + { 'Z', 'Y' } + }; + + // device specific capacities + Capabilities = DeviceCapabilities.FanControl; + + ECDetails = new ECDetails + { + AddressControl = 0x44A, + AddressDuty = 0x44B, + AddressRegistry = 0x4E, + AddressData = 0x4F, + ValueMin = 0, + ValueMax = 100 + }; +>>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d:HandheldCompanion/Devices/AYANEO/AYANEO2S.cs +======= + + OEMChords.Clear(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + OEMChords.Add(new DeviceChord("Custom Key Top Right", + new List<KeyCode> { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F16 }, + new List<KeyCode> { KeyCode.F16, KeyCode.LWin, KeyCode.RControlKey }, + false, ButtonFlags.OEM3 + )); + + OEMChords.Add(new DeviceChord("Custom Key Top Left", + new List<KeyCode> { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F15 }, + new List<KeyCode> { KeyCode.F15, KeyCode.LWin, KeyCode.RControlKey }, + false, ButtonFlags.OEM4 + )); + + OEMChords.Add(new DeviceChord("Custom Key Big", + new List<KeyCode> { KeyCode.RControlKey, KeyCode.LWin, KeyCode.F17 }, + new List<KeyCode> { KeyCode.F17, KeyCode.LWin, KeyCode.RControlKey }, + false, ButtonFlags.OEM1 + )); + + OEMChords.Add(new DeviceChord("Custom Key Small", + new List<KeyCode> { KeyCode.LWin, KeyCode.D }, + new List<KeyCode> { KeyCode.LWin, KeyCode.D }, + false, ButtonFlags.OEM2 + )); + } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlus.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlus.cs index 320d36b75..bc50f72ee 100644 --- a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlus.cs +++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlus.cs @@ -1,3 +1,4 @@ +<<<<<<< HEAD using HandheldCompanion.Devices.AYANEO; using HandheldCompanion.Inputs; using System; @@ -10,6 +11,17 @@ namespace HandheldCompanion.Devices; public class AYANEOAIRPlus : AYANEODevice +======= +using HandheldCompanion.Inputs; +using System.Collections.Generic; +using System.Numerics; + +using WindowsInput.Events; + +namespace HandheldCompanion.Devices; + +public class AYANEOAIRPlus : IDevice +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { public AYANEOAIRPlus() { @@ -17,22 +29,33 @@ public AYANEOAIRPlus() ProductIllustration = "device_aya_air"; ProductModel = "AYANEOAir"; +<<<<<<< HEAD GyrometerAxis = new Vector3(1.0f, -1.0f, -1.0f); GyrometerAxisSwap = new SortedDictionary<char, char> +======= + AngularVelocityAxis = new Vector3(1.0f, -1.0f, -1.0f); + AngularVelocityAxisSwap = new SortedDictionary<char, char> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { { 'X', 'X' }, { 'Y', 'Z' }, { 'Z', 'Y' } }; +<<<<<<< HEAD AccelerometerAxis = new Vector3(-1.0f, -1.0f, -1.0f); AccelerometerAxisSwap = new SortedDictionary<char, char> +======= + AccelerationAxis = new Vector3(-1.0f, -1.0f, -1.0f); + AccelerationAxisSwap = new SortedDictionary<char, char> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { { 'X', 'X' }, { 'Y', 'Z' }, { 'Z', 'Y' } }; +<<<<<<< HEAD // device specific capacities // todo, missing fan control Capabilities |= DeviceCapabilities.DynamicLighting; @@ -51,6 +74,8 @@ public AYANEOAIRPlus() FanValueMax = 100 // Unknown }; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d OEMChords.Add(new DeviceChord("Custom Key Top Right", new List<KeyCode> { KeyCode.LControl, KeyCode.LWin, KeyCode.F16 }, new List<KeyCode> { KeyCode.F16, KeyCode.LControl, KeyCode.LWin }, @@ -74,6 +99,7 @@ public AYANEOAIRPlus() new List<KeyCode> { KeyCode.D, KeyCode.LWin }, false, ButtonFlags.OEM2 )); +<<<<<<< HEAD } public override string GetGlyph(ButtonFlags button) @@ -330,4 +356,7 @@ public bool SetAmbilight(Color LEDColor1, Color LEDColor2) return true; } */ +======= + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } \ No newline at end of file diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMD.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMD.cs index d42631ca3..544092c59 100644 --- a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMD.cs +++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMD.cs @@ -1,13 +1,16 @@ -namespace HandheldCompanion.Devices; - -public class AYANEOAIRPlusAMD : AYANEOAIRPlus -{ - public AYANEOAIRPlusAMD() - { - // https://www.amd.com/en/products/apu/amd-ryzen-7-6800u - nTDP = new double[] { 15, 15, 20 }; - cTDP = new double[] { 3, 33 }; - GfxClock = new double[] { 100, 2200 }; - CpuClock = 4700; - } +namespace HandheldCompanion.Devices; + +public class AYANEOAIRPlusAMD : AYANEOAIRPlus +{ + public AYANEOAIRPlusAMD() + { + // https://www.amd.com/en/products/apu/amd-ryzen-7-6800u + nTDP = new double[] { 15, 15, 20 }; + cTDP = new double[] { 3, 33 }; + GfxClock = new double[] { 100, 2200 }; +<<<<<<< HEAD + CpuClock = 4700; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMDMendocino.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMDMendocino.cs index be7453c0a..2c98a041b 100644 --- a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMDMendocino.cs +++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusAMDMendocino.cs @@ -1,13 +1,16 @@ -namespace HandheldCompanion.Devices; - -public class AYANEOAIRPlusAMDMendocino : AYANEOAIRPlus -{ - public AYANEOAIRPlusAMDMendocino() - { - // https://www.amd.com/en/products/apu/amd-ryzen-3-7320u - nTDP = new double[] { 12, 12, 12 }; - cTDP = new double[] { 5, 15 }; - GfxClock = new double[] { 100, 1900 }; - CpuClock = 4100; - } +namespace HandheldCompanion.Devices; + +public class AYANEOAIRPlusAMDMendocino : AYANEOAIRPlus +{ + public AYANEOAIRPlusAMDMendocino() + { + // https://www.amd.com/en/products/apu/amd-ryzen-3-7320u + nTDP = new double[] { 12, 12, 12 }; + cTDP = new double[] { 5, 15 }; + GfxClock = new double[] { 100, 1900 }; +<<<<<<< HEAD + CpuClock = 4100; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusIntel.cs b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusIntel.cs index 205af5181..b48b8a3a5 100644 --- a/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusIntel.cs +++ b/HandheldCompanion/Devices/AYANEO/AYANEOAIRPlusIntel.cs @@ -1,13 +1,16 @@ -namespace HandheldCompanion.Devices; - -public class AYANEOAIRPlusIntel : AYANEOAIRPlus -{ - public AYANEOAIRPlusIntel() - { - // https://www.intel.com/content/www/us/en/products/sku/226263/intel-core-i31215u-processor-10m-cache-up-to-4-40-ghz/specifications.html - nTDP = new double[] { 15, 15, 20 }; - cTDP = new double[] { 5, 55 }; - GfxClock = new double[] { 100, 1100 }; - CpuClock = 4400; - } +namespace HandheldCompanion.Devices; + +public class AYANEOAIRPlusIntel : AYANEOAIRPlus +{ + public AYANEOAIRPlusIntel() + { + // https://www.intel.com/content/www/us/en/products/sku/226263/intel-core-i31215u-processor-10m-cache-up-to-4-40-ghz/specifications.html + nTDP = new double[] { 15, 15, 20 }; + cTDP = new double[] { 5, 55 }; + GfxClock = new double[] { 100, 1100 }; +<<<<<<< HEAD + CpuClock = 4400; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/Ayn/AynLoki.cs b/HandheldCompanion/Devices/Ayn/AynLoki.cs index d4a506b64..cc2a8799b 100644 --- a/HandheldCompanion/Devices/Ayn/AynLoki.cs +++ b/HandheldCompanion/Devices/Ayn/AynLoki.cs @@ -1,3 +1,4 @@ +<<<<<<< HEAD using HandheldCompanion.Inputs; using System; using System.Collections.Generic; @@ -173,4 +174,65 @@ public override string GetGlyph(ButtonFlags button) return defaultGlyph; } +======= +using HandheldCompanion.Inputs; +using System.Collections.Generic; +using System.Numerics; +using WindowsInput.Events; + +namespace HandheldCompanion.Devices; + +public class AynLoki : IDevice +{ + public AynLoki() + { + // Ayn Loki device generic settings + ProductIllustration = "device_ayn_loki"; + ProductModel = "AynLoki"; + + AngularVelocityAxis = new Vector3(1.0f, 1.0f, -1.0f); + AngularVelocityAxisSwap = new SortedDictionary<char, char> + { + { 'X', 'Y' }, + { 'Y', 'Z' }, + { 'Z', 'X' } + }; + + AccelerationAxis = new Vector3(1.0f, -1.0f, -1.0f); + AccelerationAxisSwap = new SortedDictionary<char, char> + { + { 'X', 'X' }, + { 'Y', 'Z' }, + { 'Z', 'Y' } + }; + + // device specific capacities + /* + Capacities = DeviceCapacities.FanControl; + + ECDetails = new ECDetails + { + AddressControl = 0x12C, + AddressDuty = 0x11, + AddressRegistry = 0x12, + AddressData = 0x4F, + ValueMin = 0, + ValueMax = 128 + }; + + */ + + OEMChords.Add(new DeviceChord("Guide", + new List<KeyCode> { KeyCode.LButton, KeyCode.XButton2 }, + new List<KeyCode> { KeyCode.LButton, KeyCode.XButton2 }, + false, ButtonFlags.OEM1 + )); + + OEMChords.Add(new DeviceChord("LCC", + new List<KeyCode> { KeyCode.LControl, KeyCode.LShift, KeyCode.LMenu, KeyCode.T }, + new List<KeyCode> { KeyCode.T, KeyCode.LMenu, KeyCode.LShift, KeyCode.LControl }, + false, ButtonFlags.OEM2 + )); + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } \ No newline at end of file diff --git a/HandheldCompanion/Devices/Ayn/AynLokiMax6600U.cs b/HandheldCompanion/Devices/Ayn/AynLokiMax6600U.cs index eea9a37fc..4a726b974 100644 --- a/HandheldCompanion/Devices/Ayn/AynLokiMax6600U.cs +++ b/HandheldCompanion/Devices/Ayn/AynLokiMax6600U.cs @@ -1,13 +1,16 @@ -namespace HandheldCompanion.Devices; - -public class LokiMax6600U : AynLoki -{ - public LokiMax6600U() - { - // https://www.amd.com/en/products/apu/amd-ryzen-5-6600u - nTDP = new double[] { 15, 15, 20 }; - cTDP = new double[] { 5, 28 }; - GfxClock = new double[] { 100, 1900 }; - CpuClock = 4500; - } +namespace HandheldCompanion.Devices; + +public class LokiMax6600U : AynLoki +{ + public LokiMax6600U() + { + // https://www.amd.com/en/products/apu/amd-ryzen-5-6600u + nTDP = new double[] { 15, 15, 20 }; + cTDP = new double[] { 5, 28 }; + GfxClock = new double[] { 100, 1900 }; +<<<<<<< HEAD + CpuClock = 4500; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/Ayn/AynLokiMax6800U.cs b/HandheldCompanion/Devices/Ayn/AynLokiMax6800U.cs index d4f0233ca..3709e9a40 100644 --- a/HandheldCompanion/Devices/Ayn/AynLokiMax6800U.cs +++ b/HandheldCompanion/Devices/Ayn/AynLokiMax6800U.cs @@ -1,13 +1,16 @@ -namespace HandheldCompanion.Devices; - -public class LokiMax6800U : AynLoki -{ - public LokiMax6800U() - { - // https://www.amd.com/en/products/apu/amd-ryzen-7-6800u - nTDP = new double[] { 15, 15, 20 }; - cTDP = new double[] { 5, 28 }; - GfxClock = new double[] { 100, 2200 }; - CpuClock = 4700; - } +namespace HandheldCompanion.Devices; + +public class LokiMax6800U : AynLoki +{ + public LokiMax6800U() + { + // https://www.amd.com/en/products/apu/amd-ryzen-7-6800u + nTDP = new double[] { 15, 15, 20 }; + cTDP = new double[] { 5, 28 }; + GfxClock = new double[] { 100, 2200 }; +<<<<<<< HEAD + CpuClock = 4700; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/Ayn/AynLokiMiniPro.cs b/HandheldCompanion/Devices/Ayn/AynLokiMiniPro.cs index af4c3820b..27216394c 100644 --- a/HandheldCompanion/Devices/Ayn/AynLokiMiniPro.cs +++ b/HandheldCompanion/Devices/Ayn/AynLokiMiniPro.cs @@ -1,13 +1,16 @@ -namespace HandheldCompanion.Devices; - -public class LokiMiniPro : AynLoki -{ - public LokiMiniPro() - { - // https://www.amd.com/en/products/apu/amd-ryzen-3-7320u - nTDP = new double[] { 12, 12, 12 }; - cTDP = new double[] { 5, 15 }; - GfxClock = new double[] { 100, 1900 }; - CpuClock = 4100; - } +namespace HandheldCompanion.Devices; + +public class LokiMiniPro : AynLoki +{ + public LokiMiniPro() + { + // https://www.amd.com/en/products/apu/amd-ryzen-3-7320u + nTDP = new double[] { 12, 12, 12 }; + cTDP = new double[] { 5, 15 }; + GfxClock = new double[] { 100, 1900 }; +<<<<<<< HEAD + CpuClock = 4100; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/Ayn/AynLokiZero.cs b/HandheldCompanion/Devices/Ayn/AynLokiZero.cs index 4a9ea29a7..63fb56a78 100644 --- a/HandheldCompanion/Devices/Ayn/AynLokiZero.cs +++ b/HandheldCompanion/Devices/Ayn/AynLokiZero.cs @@ -1,13 +1,16 @@ -namespace HandheldCompanion.Devices; - -public class LokiZero : AynLoki -{ - public LokiZero() - { - // https://www.amd.com/en/products/apu/amd-athlon-silver-3050u - nTDP = new double[] { 10, 10, 10 }; - cTDP = new double[] { 5, 15 }; - GfxClock = new double[] { 100, 1100 }; - CpuClock = 3200; - } +namespace HandheldCompanion.Devices; + +public class LokiZero : AynLoki +{ + public LokiZero() + { + // https://www.amd.com/en/products/apu/amd-athlon-silver-3050u + nTDP = new double[] { 10, 10, 10 }; + cTDP = new double[] { 5, 15 }; + GfxClock = new double[] { 100, 1100 }; +<<<<<<< HEAD + CpuClock = 3200; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } } \ No newline at end of file diff --git a/HandheldCompanion/Devices/GPD/GPDWin4-2023-7640U.cs b/HandheldCompanion/Devices/GPD/GPDWin4-2023-7640U.cs index 7f6b1925e..7321f9a34 100644 --- a/HandheldCompanion/Devices/GPD/GPDWin4-2023-7640U.cs +++ b/HandheldCompanion/Devices/GPD/GPDWin4-2023-7640U.cs @@ -1,3 +1,4 @@ +<<<<<<< HEAD namespace HandheldCompanion.Devices; public class GPDWin4_2023_7640U : GPDWin4_2023_7840U @@ -7,5 +8,79 @@ public GPDWin4_2023_7640U() // https://www.amd.com/en/products/apu/amd-ryzen-5-7640u GfxClock = new double[] { 200, 2600 }; CpuClock = 4900; +======= +using HandheldCompanion.Inputs; +using System.Collections.Generic; +using System.Numerics; +using WindowsInput.Events; + +namespace HandheldCompanion.Devices; + +public class GPDWin4_2023_7640U : IDevice +{ + public GPDWin4_2023_7640U() + { + // device specific settings + ProductIllustration = "device_gpd4"; + + // https://www.amd.com/en/products/apu/amd-ryzen-5-7640u + nTDP = new double[] { 15, 15, 28 }; + cTDP = new double[] { 5, 28 }; + GfxClock = new double[] { 200, 2600 }; + + AngularVelocityAxis = new Vector3(1.0f, 1.0f, -1.0f); + AngularVelocityAxisSwap = new SortedDictionary<char, char> + { + { 'X', 'Y' }, + { 'Y', 'Z' }, + { 'Z', 'X' } + }; + + AccelerationAxis = new Vector3(1.0f, 1.0f, 1.0f); + AccelerationAxisSwap = new SortedDictionary<char, char> + { + { 'X', 'X' }, + { 'Y', 'Z' }, + { 'Z', 'Y' } + }; + + // device specific capacities + Capabilities = DeviceCapabilities.FanControl; + + ECDetails = new ECDetails + { + AddressControl = 0x275, + AddressDuty = 0x1809, + AddressRegistry = 0x4E, + AddressData = 0x4F, + ValueMin = 0, + ValueMax = 184 + }; + + // Note, OEM1 not configured as this device has it's own Menu button for guide button + + // Note, chords need to be manually configured in GPD app first by end user + + // GPD Back buttons do not have a "hold", configured buttons are key down and up immediately + // Holding back buttons will result in same key down and up input every 2-3 seconds + // Configured chords in GPD app need unique characters otherwise this leads to a + // "mixed" result when pressing both buttons at the same time + OEMChords.Add(new DeviceChord("Bottom button left", + new List<KeyCode> { KeyCode.F11, KeyCode.L }, + new List<KeyCode> { KeyCode.F11, KeyCode.L }, + false, ButtonFlags.OEM2 + )); + + OEMChords.Add(new DeviceChord("Bottom button right", + new List<KeyCode> { KeyCode.F12, KeyCode.R }, + new List<KeyCode> { KeyCode.F12, KeyCode.R }, + false, ButtonFlags.OEM3 + )); + } + + public override void Close() + { + base.Close(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } diff --git a/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs b/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs index 78e6c4504..d5e3e72e3 100644 --- a/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs +++ b/HandheldCompanion/Devices/GPD/GPDWin4-2023-7840U.cs @@ -14,6 +14,7 @@ public GPDWin4_2023_7840U() // https://www.amd.com/en/products/apu/amd-ryzen-7-7840u nTDP = new double[] { 15, 15, 28 }; +<<<<<<< HEAD cTDP = new double[] { 5, 30 }; GfxClock = new double[] { 200, 2700 }; CpuClock = 5100; @@ -33,20 +34,48 @@ public GPDWin4_2023_7840U() GyrometerAxis = new Vector3(1.0f, 1.0f, -1.0f); GyrometerAxisSwap = new SortedDictionary<char, char> +======= + cTDP = new double[] { 5, 28 }; + GfxClock = new double[] { 200, 2700 }; + + AngularVelocityAxis = new Vector3(1.0f, 1.0f, -1.0f); + AngularVelocityAxisSwap = new SortedDictionary<char, char> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { { 'X', 'Y' }, { 'Y', 'Z' }, { 'Z', 'X' } }; +<<<<<<< HEAD AccelerometerAxis = new Vector3(1.0f, 1.0f, 1.0f); AccelerometerAxisSwap = new SortedDictionary<char, char> +======= + AccelerationAxis = new Vector3(1.0f, 1.0f, 1.0f); + AccelerationAxisSwap = new SortedDictionary<char, char> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { { 'X', 'X' }, { 'Y', 'Z' }, { 'Z', 'Y' } }; +<<<<<<< HEAD +======= + // device specific capacities + Capabilities = DeviceCapabilities.FanControl; + + ECDetails = new ECDetails + { + AddressControl = 0x275, + AddressDuty = 0x1809, + AddressRegistry = 0x4E, + AddressData = 0x4F, + ValueMin = 0, + ValueMax = 184 + }; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d // Note, OEM1 not configured as this device has it's own Menu button for guide button // Note, chords need to be manually configured in GPD app first by end user @@ -66,6 +95,7 @@ public GPDWin4_2023_7840U() new List<KeyCode> { KeyCode.F12, KeyCode.R }, false, ButtonFlags.OEM3 )); +<<<<<<< HEAD } public override string GetGlyph(ButtonFlags button) @@ -79,6 +109,8 @@ public override string GetGlyph(ButtonFlags button) } return defaultGlyph; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } public override void Close() diff --git a/HandheldCompanion/Devices/IDevice.cs b/HandheldCompanion/Devices/IDevice.cs index d5691ebd2..4207ee824 100644 --- a/HandheldCompanion/Devices/IDevice.cs +++ b/HandheldCompanion/Devices/IDevice.cs @@ -1,3 +1,4 @@ +<<<<<<< HEAD using HandheldCompanion.Controls; using HandheldCompanion.Inputs; using HandheldCompanion.Managers; @@ -847,4 +848,589 @@ public virtual string GetGlyph(ButtonFlags button) return defaultGlyph; } +======= +using HandheldCompanion.Inputs; +using HandheldCompanion.Managers; +using HandheldCompanion.Sensors; +using HandheldCompanion.Utils; +using HidLibrary; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using Windows.Devices.Sensors; +using static HandheldCompanion.OneEuroFilter; +using static HandheldCompanion.OpenLibSys; + +namespace HandheldCompanion.Devices; + +[Flags] +public enum DeviceCapabilities : ushort +{ + None = 0, + InternalSensor = 1, + ExternalSensor = 2, + FanControl = 4 +} + +public struct ECDetails +{ + public ushort AddressRegistry; + public ushort AddressData; + public ushort AddressControl; + public ushort AddressDuty; + + public short ValueMin; + public short ValueMax; +} + +public abstract class IDevice +{ + public delegate void KeyPressedEventHandler(ButtonFlags button); + public delegate void KeyReleasedEventHandler(ButtonFlags button); + + private static OpenLibSys openLibSys; + + private static IDevice device; + + protected ushort _vid, _pid; + + public Vector3 AccelerationAxis = new(1.0f, 1.0f, 1.0f); + + public SortedDictionary<char, char> AccelerationAxisSwap = new() + { + { 'X', 'X' }, + { 'Y', 'Y' }, + { 'Z', 'Z' } + }; + + public Vector3 AngularVelocityAxis = new(1.0f, 1.0f, 1.0f); + + public SortedDictionary<char, char> AngularVelocityAxisSwap = new() + { + { 'X', 'X' }, + { 'Y', 'Y' }, + { 'Z', 'Z' } + }; + + public DeviceCapabilities Capabilities = DeviceCapabilities.None; + + // device configurable TDP (down, up) + public double[] cTDP = { 10, 25 }; + + public ECDetails ECDetails; + + public string ExternalSensorName = string.Empty; + + // device GfxClock frequency limits + public double[] GfxClock = { 100, 1800 }; + public string InternalSensorName = string.Empty; + + // device nominal TDP (slow, fast) + public double[] nTDP = { 15, 15, 20 }; + + // trigger specific settings + public List<DeviceChord> OEMChords = new(); + + // filter settings + public OneEuroSettings oneEuroSettings = new(0.002d, 0.008d); + + public string ProductIllustration = "device_generic"; + public string ProductModel = "default"; + + // mininum delay before trying to emulate a virtual controller on system resume (milliseconds) + public short ResumeDelay = 1000; + + // key press delay to use for certain scenarios + public short KeyPressDelay = 20; + + protected USBDeviceInfo sensor = new(); + + public IDevice() + { + } + + public IEnumerable<ButtonFlags> OEMButtons => OEMChords.SelectMany(a => a.state.Buttons).Distinct(); + + public virtual bool IsOpen => openLibSys is not null; + + public virtual bool IsSupported => true; + + public event KeyPressedEventHandler KeyPressed; + public event KeyReleasedEventHandler KeyReleased; + + public string ManufacturerName = string.Empty; + public string ProductName = string.Empty; + public string SystemName = string.Empty; + public string Version = string.Empty; + public string Processor = string.Empty; + public int NumberOfCores = 0; + + public static IDevice GetDefault() + { + if (device is not null) + return device; + + var ManufacturerName = MotherboardInfo.Manufacturer.ToUpper(); + var ProductName = MotherboardInfo.Product; + var SystemName = MotherboardInfo.SystemName; + var Version = MotherboardInfo.Version; + var Processor = MotherboardInfo.Processor; + var NumberOfCores = MotherboardInfo.NumberOfCores; + + switch (ManufacturerName) + { + case "AYN": + { + switch (ProductName) + { + case "Loki MiniPro": + device = new LokiMiniPro(); + break; + case "Loki Zero": + device = new LokiZero(); + break; + case "Loki Max": + switch (Processor) + { + case "AMD Ryzen 5 6600U with Radeon Graphics": + device = new LokiMax6600U(); + break; + case "AMD Ryzen 7 6800U with Radeon Graphics": + device = new LokiMax6800U(); + break; + } + break; + } + } + break; + case "AOKZOE": + { + switch (ProductName) + { + case "AOKZOE A1 AR07": + device = new AOKZOEA1(); + break; + case "AOKZOE A1 Pro": + device = new AOKZOEA1Pro(); + break; + } + } + break; + case "AYADEVICE": + case "AYANEO": + { + switch (ProductName) + { + case "AIR": + device = new AYANEOAIR(); + break; + case "AIR Pro": + device = new AYANEOAIRPro(); + break; + case "AIR 1S": + device = new AYANEOAIR1S(); + break; + case "AIR Lite": + device = new AYANEOAIRLite(); + break; + case "AYA NEO FOUNDER": + case "AYANEO 2021": + device = new AYANEO2021(); + break; + case "AYANEO 2021 Pro": + case "AYANEO 2021 Pro Retro Power": + device = new AYANEO2021Pro(); + break; + case "NEXT Pro": + case "NEXT Advance": + case "NEXT": + device = new AYANEONEXT(); + break; + case "AYANEO 2": + case "GEEK": + device = new AYANEO2(); + break; + case "AB05-AMD": + device = new AYANEOAIRPlusAMD(); + break; + case "AB05-Mendocino": + device = new AYANEOAIRPlusAMDMendocino(); + break; + case "AB05-Intel": + device = new AYANEOAIRPlusIntel(); + break; + case "AYANEO 2S": + case "GEEK 1S": + device = new AYANEO2S(); + break; + } + } + break; + + case "GPD": + { + switch (ProductName) + { + case "WIN2": + device = new GPDWin2(); + break; + case "G1618-03": + device = new GPDWin3(); + break; + case "G1618-04": + switch (Processor) + { + case "AMD Ryzen 7 6800U with Radeon Graphics": + device = new GPDWin4(); + break; + case "AMD Ryzen 5 7640U w/ Radeon 760M Graphics": + device = new GPDWin4_2023_7640U(); + break; + case "AMD Ryzen 7 7840U w/ Radeon 780M Graphics": + device = new GPDWin4_2023_7840U(); + break; + } + break; + case "G1619-03": + device = new GPDWinMax2Intel(); + break; + case "G1619-04": + device = new GPDWinMax2AMD(); + break; + } + } + break; + + case "ONE-NETBOOK TECHNOLOGY CO., LTD.": + case "ONE-NETBOOK": + { + switch (ProductName) + { + case "ONE XPLAYER": + case "ONEXPLAYER Mini Pro": + { + switch (Version) + { + default: + case "V01": + device = new OneXPlayerMiniAMD(); + break; + case "1002-C": + device = new OneXPlayerMiniIntel(); + break; + case "V03": + device = new OneXPlayerMiniPro(); + break; + } + break; + } + case "ONEXPLAYER mini A07": + device = new OneXPlayerMiniAMD(); + break; + case "ONEXPLAYER 2 ARP23": + { + switch (Version) + { + default: + case "Ver.1.0": + device = new OneXPlayer2(); + break; + } + break; + } + case "ONEXPLAYER 2 PRO ARP23P": + case "ONEXPLAYER 2 PRO ARP23P EVA-01": + switch (Version) + { + default: + case "Version 1.0": + device = new OneXPlayer2_7840U(); + break; + } + break; + } + } + break; + + case "ASUSTEK COMPUTER INC.": + { + switch (ProductName) + { + // Todo, figure out if theres a diff between Z1 and Z1 extreme versions + case "RC71L": + device = new ROGAlly(); + break; + } + } + break; + + case "VALVE": + { + switch (ProductName) + { + case "Jupiter": + device = new SteamDeck(); + break; + } + } + break; + } + + LogManager.LogInformation("{0} from {1}", ProductName, ManufacturerName); + + if (device is null) + { + device = new DefaultDevice(); + LogManager.LogWarning("Device not yet supported. The behavior of the application will be unpredictable"); + } + + // get the actual handheld device + device.ManufacturerName = ManufacturerName; + device.ProductName = ProductName; + + return device; + } + + public bool HasMotionSensor() + { + return Capabilities.HasFlag(DeviceCapabilities.InternalSensor) || Capabilities.HasFlag(DeviceCapabilities.ExternalSensor); + } + + public virtual bool Open() + { + if (openLibSys != null) + return true; + + try + { + // initialize OpenLibSys + openLibSys = new OpenLibSys(); + + // Check support library sutatus + var status = openLibSys.GetStatus(); + switch (status) + { + case (uint)OlsStatus.NO_ERROR: + break; + default: + LogManager.LogError("Couldn't initialize OpenLibSys. ErrorCode: {0}", status); + return false; + } + + // Check WinRing0 status + var dllstatus = (OlsDllStatus)openLibSys.GetDllStatus(); + switch (dllstatus) + { + case (uint)OlsDllStatus.OLS_DLL_NO_ERROR: + break; + default: + LogManager.LogError("Couldn't initialize OpenLibSys. ErrorCode: {0}", dllstatus); + return false; + } + } + catch (Exception ex) + { + LogManager.LogError("Couldn't initialize OpenLibSys. ErrorCode: {0}", ex.Message); + Close(); + return false; + } + + return true; + } + + public virtual void Close() + { + if (openLibSys is null) + return; + + SetFanControl(false); + + openLibSys.Dispose(); + openLibSys = null; + } + + public virtual bool IsReady() + { + return true; + } + + public virtual void SetKeyPressDelay(HIDmode controllerMode) + { + switch (controllerMode) + { + default: + KeyPressDelay = 20; + break; + } + } + + public void PullSensors() + { + Gyrometer? gyrometer = Gyrometer.GetDefault(); + Accelerometer? accelerometer = Accelerometer.GetDefault(); + + if (gyrometer is not null && accelerometer is not null) + { + // check sensor + var DeviceId = CommonUtils.Between(gyrometer.DeviceId, @"\\?\", @"#{").Replace(@"#", @"\"); + sensor = DeviceUtils.GetUSBDevice(DeviceId); + if (sensor is not null) + InternalSensorName = sensor.Name; + + Capabilities |= DeviceCapabilities.InternalSensor; + } + else if (Capabilities.HasFlag(DeviceCapabilities.InternalSensor)) + { + Capabilities &= ~DeviceCapabilities.InternalSensor; + } + + SerialUSBIMU? USB = SerialUSBIMU.GetDefault(); + if (USB is not null) + { + ExternalSensorName = USB.GetName(); + + Capabilities |= DeviceCapabilities.ExternalSensor; + } + else if (Capabilities.HasFlag(DeviceCapabilities.ExternalSensor)) + { + Capabilities &= ~DeviceCapabilities.ExternalSensor; + } + } + + public bool RestartSensor() + { + if (sensor is null) + return false; + + return PnPUtil.RestartDevice(sensor.DeviceId); + } + + public string GetButtonName(ButtonFlags button) + { + return EnumUtils.GetDescriptionFromEnumValue(button, GetType().Name); + } + + public virtual void SetFanDuty(double percent) + { + if (ECDetails.AddressDuty == 0) + return; + + var duty = percent * (ECDetails.ValueMax - ECDetails.ValueMin) / 100 + ECDetails.ValueMin; + var data = Convert.ToByte(duty); + + ECRamDirectWrite(ECDetails.AddressDuty, ECDetails, data); + } + + public virtual void SetFanControl(bool enable) + { + if (ECDetails.AddressControl == 0) + return; + + var data = Convert.ToByte(enable); + ECRamDirectWrite(ECDetails.AddressControl, ECDetails, data); + } + + public virtual float ReadFanDuty() + { + if (ECDetails.AddressControl == 0) + return 0; + + // todo: implement me + return 0; + } + + [Obsolete("ECRamReadByte is deprecated, please use ECRamReadByte with ECDetails instead.")] + public static byte ECRamReadByte(ushort address) + { + try + { + return openLibSys.ReadIoPortByte(address); + } + catch (Exception ex) + { + LogManager.LogError("Couldn't read byte from address {0} using OpenLibSys. ErrorCode: {1}", address, + ex.Message); + return 0; + } + } + + public static byte ECRamReadByte(ushort address, ECDetails details) + { + var addr_upper = (byte)((address >> 8) & byte.MaxValue); + var addr_lower = (byte)(address & byte.MaxValue); + + try + { + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2E); + openLibSys.WriteIoPortByte(details.AddressData, 0x11); + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2F); + openLibSys.WriteIoPortByte(details.AddressData, addr_upper); + + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2E); + openLibSys.WriteIoPortByte(details.AddressData, 0x10); + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2F); + openLibSys.WriteIoPortByte(details.AddressData, addr_lower); + + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2E); + openLibSys.WriteIoPortByte(details.AddressData, 0x12); + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2F); + + return openLibSys.ReadIoPortByte(details.AddressData); + } + catch (Exception ex) + { + LogManager.LogError("Couldn't read to port using OpenLibSys. ErrorCode: {0}", ex.Message); + return 0; + } + } + + public static bool ECRamDirectWrite(ushort address, ECDetails details, byte data) + { + var addr_upper = (byte)((address >> 8) & byte.MaxValue); + var addr_lower = (byte)(address & byte.MaxValue); + + try + { + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2E); + openLibSys.WriteIoPortByte(details.AddressData, 0x11); + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2F); + openLibSys.WriteIoPortByte(details.AddressData, addr_upper); + + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2E); + openLibSys.WriteIoPortByte(details.AddressData, 0x10); + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2F); + openLibSys.WriteIoPortByte(details.AddressData, addr_lower); + + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2E); + openLibSys.WriteIoPortByte(details.AddressData, 0x12); + openLibSys.WriteIoPortByte(details.AddressRegistry, 0x2F); + openLibSys.WriteIoPortByte(details.AddressData, data); + return true; + } + catch (Exception ex) + { + LogManager.LogError("Couldn't write to port using OpenLibSys. ErrorCode: {0}", ex.Message); + return false; + } + } + + protected void KeyPress(ButtonFlags button) + { + KeyPressed?.Invoke(button); + } + + protected void KeyRelease(ButtonFlags button) + { + KeyReleased?.Invoke(button); + } + + protected static IEnumerable<HidDevice> GetHidDevices(int vendorId, int deviceId, int minFeatures = 1) + { + HidDevice[] HidDeviceList = HidDevices.Enumerate(vendorId, new int[] { deviceId }).ToArray(); + foreach (HidDevice device in HidDeviceList) + if (device.IsConnected && device.Capabilities.FeatureReportByteLength >= minFeatures) + yield return device; + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } \ No newline at end of file diff --git a/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2-7840U.cs b/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2-7840U.cs new file mode 100644 index 000000000..f3126450c --- /dev/null +++ b/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2-7840U.cs @@ -0,0 +1,16 @@ +namespace HandheldCompanion.Devices; + +public class OneXPlayer2_7840U : OneXPlayer2 +{ + public OneXPlayer2_7840U() + { + // device specific settings + ProductIllustration = "device_onexplayer_2"; + ProductModel = "ONEXPLAYER 2 7840U"; + + // https://www.amd.com/en/products/apu/amd-ryzen-7-7840u + nTDP = new double[] { 15, 15, 20 }; + cTDP = new double[] { 4, 30 }; + GfxClock = new double[] { 100, 2700 }; + } +} \ No newline at end of file diff --git a/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2.cs b/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2.cs index 95143987b..e980ca7f2 100644 --- a/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2.cs +++ b/HandheldCompanion/Devices/OneXPlayer/OneXPlayer2.cs @@ -17,19 +17,31 @@ public OneXPlayer2() : base() // https://www.amd.com/en/products/apu/amd-ryzen-7-6800u this.nTDP = new double[] { 15, 15, 20 }; this.cTDP = new double[] { 4, 28 }; +<<<<<<< HEAD this.GfxClock = new double[] { 100, 2200 }; this.CpuClock = 4700; GyrometerAxis = new Vector3(-1.0f, 1.0f, -1.0f); this.GyrometerAxisSwap = new() +======= + this.GfxClock = new double[] { 100, 2200 }; + + AngularVelocityAxis = new Vector3(-1.0f, 1.0f, -1.0f); + this.AngularVelocityAxisSwap = new() +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { { 'X', 'X' }, { 'Y', 'Z' }, { 'Z', 'Y' }, }; +<<<<<<< HEAD AccelerometerAxis = new Vector3(-1.0f, 1.0f, -1.0f); this.AccelerometerAxisSwap = new() +======= + AccelerationAxis = new Vector3(-1.0f, 1.0f, -1.0f); + this.AccelerationAxisSwap = new() +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { { 'X', 'X' }, { 'Y', 'Z' }, @@ -41,12 +53,21 @@ public OneXPlayer2() : base() ECDetails = new ECDetails { +<<<<<<< HEAD AddressFanControl = 0x44A, AddressFanDuty = 0x44B, AddressStatusCommandPort = 0x4E, AddressDataPort = 0x4F, FanValueMin = 0, FanValueMax = 184 +======= + AddressControl = 0x44A, + AddressDuty = 0x44B, + AddressRegistry = 0x4E, + AddressData = 0x4F, + ValueMin = 0, + ValueMax = 184 +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d }; // Choose OEM2 due to presense of physical Xbox Guide button @@ -56,6 +77,7 @@ public OneXPlayer2() : base() new List<KeyCode>() { KeyCode.LWin, KeyCode.LMenu, KeyCode.LControl }, false, ButtonFlags.OEM2 )); +<<<<<<< HEAD } public override string GetGlyph(ButtonFlags button) { @@ -66,6 +88,8 @@ public override string GetGlyph(ButtonFlags button) } return defaultGlyph; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } public override bool Open() @@ -77,9 +101,15 @@ public override bool Open() // allow OneX button to pass key inputs LogManager.LogInformation("Unlocked {0} OEM button", ButtonFlags.OEM2); +<<<<<<< HEAD ECRamDirectWrite(0x4EB, ECDetails, 0xEB); return ECRamReadByte(0x4EB, ECDetails) == 0xEB; +======= + ECRamDirectWrite(0x4EB, ECDetails, 0x40); + + return (ECRamReadByte(0x4EB, ECDetails) == 0x40); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } public override void Close() diff --git a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniPro.cs b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniPro.cs index 709d934b6..b66c8bb51 100644 --- a/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniPro.cs +++ b/HandheldCompanion/Devices/OneXPlayer/OneXPlayerMiniPro.cs @@ -13,10 +13,16 @@ public OneXPlayerMiniPro() // https://www.amd.com/en/products/apu/amd-ryzen-7-6800u nTDP = new double[] { 15, 15, 20 }; cTDP = new double[] { 4, 28 }; +<<<<<<< HEAD GfxClock = new double[] { 100, 2200 }; CpuClock = 4700; AccelerometerAxis = new Vector3(-1.0f, 1.0f, 1.0f); +======= + GfxClock = new double[] { 100, 2200 }; + + AccelerationAxis = new Vector3(-1.0f, 1.0f, 1.0f); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d OEMChords.Clear(); @@ -37,6 +43,7 @@ public OneXPlayerMiniPro() new List<KeyCode> { KeyCode.LWin, KeyCode.D }, false, ButtonFlags.OEM3 )); +<<<<<<< HEAD } public override string GetGlyph(ButtonFlags button) @@ -52,6 +59,8 @@ public override string GetGlyph(ButtonFlags button) } return defaultGlyph; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } public override bool Open() diff --git a/HandheldCompanion/Devices/Valve/SteamDeck.cs b/HandheldCompanion/Devices/Valve/SteamDeck.cs index 023e1bc78..d9f0e4183 100644 --- a/HandheldCompanion/Devices/Valve/SteamDeck.cs +++ b/HandheldCompanion/Devices/Valve/SteamDeck.cs @@ -70,8 +70,12 @@ public SteamDeck() cTDP = new double[] { 4, 15 }; // https://www.techpowerup.com/gpu-specs/steam-deck-gpu.c3897 +<<<<<<< HEAD GfxClock = new double[] { 200, 1600 }; CpuClock = 3500; +======= + GfxClock = new double[] { 200, 1600 }; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d OEMChords.Add(new DeviceChord("...", new List<KeyCode>(), new List<KeyCode>(), diff --git a/HandheldCompanion/HandheldCompanion.csproj b/HandheldCompanion/HandheldCompanion.csproj index 153dcbe5a..80ac07edd 100644 --- a/HandheldCompanion/HandheldCompanion.csproj +++ b/HandheldCompanion/HandheldCompanion.csproj @@ -2,7 +2,11 @@ <PropertyGroup> <OutputType>WinExe</OutputType> +<<<<<<< HEAD <TargetFrameworks>net8.0-windows10.0.19041.0</TargetFrameworks> +======= + <TargetFrameworks>net8.0-windows10.0.19041.0;net7.0-windows10.0.19041.0</TargetFrameworks> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <GenerateRuntimeConfigDevFile>true</GenerateRuntimeConfigDevFile> <AppendTargetFrameworkToOutputPath>true</AppendTargetFrameworkToOutputPath> <PlatformTarget>x64</PlatformTarget> @@ -12,7 +16,11 @@ <StartupObject>HandheldCompanion.App</StartupObject> <OutputPath>$(SolutionDir)bin\$(Configuration)</OutputPath> <ApplicationIcon>Resources\icon.ico</ApplicationIcon> +<<<<<<< HEAD <Version>0.19.1.2</Version> +======= + <Version>0.18.0.6</Version> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <ApplicationManifest>app.manifest</ApplicationManifest> <Platforms>AnyCPU;x64;x86</Platforms> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> @@ -78,7 +86,10 @@ <None Remove="Resources\device_gpd4.png" /> <None Remove="Resources\device_gpd_win2.png" /> <None Remove="Resources\device_gpd_winmax2.png" /> +<<<<<<< HEAD <None Remove="Resources\device_legion_go.png" /> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <None Remove="Resources\device_onexplayer_2.png" /> <None Remove="Resources\device_onexplayer_mini.png" /> <None Remove="Resources\device_onexplayer_onexfly.png" /> @@ -154,7 +165,11 @@ <PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" /> <PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" /> <PackageReference Include="NAudio" Version="2.2.0" /> +<<<<<<< HEAD <PackageReference Include="Nefarius.Drivers.HidHide" Version="1.12.2" /> +======= + <PackageReference Include="Nefarius.Drivers.HidHide" Version="1.8.60" /> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <PackageReference Include="Nefarius.Utilities.DeviceManagement" Version="3.17.406" /> <PackageReference Include="Nefarius.ViGEm.Client" Version="1.21.256" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> @@ -166,13 +181,19 @@ <PackageReference Include="Serilog.Settings.Configuration" Version="7.0.1" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> +<<<<<<< HEAD <PackageReference Include="SharpDX" Version="4.2.0" /> <PackageReference Include="SharpDX.Direct3D9" Version="4.2.0" /> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <PackageReference Include="SharpDX.DirectInput" Version="4.2.0" /> <PackageReference Include="SharpDX.XInput" Version="4.2.0" /> <PackageReference Include="System.IO.Ports" Version="7.0.0" /> <PackageReference Include="System.Management" Version="7.0.2" /> +<<<<<<< HEAD <PackageReference Include="System.ServiceProcess.ServiceController" Version="8.0.0" /> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <PackageReference Include="WindowsAPICodePack" Version="7.0.4" /> <PackageReference Include="WindowsInput" Version="6.4.1" /> <PackageReference Include="WpfScreenHelper" Version="2.1.0" /> @@ -2257,9 +2278,12 @@ <None Update="Resources\RTSSSharedMemoryNET.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> +<<<<<<< HEAD <None Update="SapientiaUsb.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <None Update="ucrtbased.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </None> diff --git a/HandheldCompanion/Managers/ControllerManager.cs b/HandheldCompanion/Managers/ControllerManager.cs index 84d00cad5..a280f80fd 100644 --- a/HandheldCompanion/Managers/ControllerManager.cs +++ b/HandheldCompanion/Managers/ControllerManager.cs @@ -1,1115 +1,1500 @@ -using HandheldCompanion.Controllers; -using HandheldCompanion.Controls; -using HandheldCompanion.Inputs; -using HandheldCompanion.Platforms; -using HandheldCompanion.Utils; -using HandheldCompanion.Views; -using HandheldCompanion.Views.Classes; -using Nefarius.Utilities.DeviceManagement.Drivers; -using Nefarius.Utilities.DeviceManagement.Extensions; -using Nefarius.Utilities.DeviceManagement.PnP; -using SharpDX.DirectInput; -using SharpDX.XInput; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using Windows.UI; -using Windows.UI.ViewManagement; -using static HandheldCompanion.Utils.DeviceUtils; -using static JSL; -using DeviceType = SharpDX.DirectInput.DeviceType; - -namespace HandheldCompanion.Managers; - -public static class ControllerManager -{ - private static readonly ConcurrentDictionary<string, IController> Controllers = new(); - public static readonly ConcurrentDictionary<string, bool> PowerCyclers = new(); - - private static Thread watchdogThread; - private static bool watchdogThreadRunning; - private static bool ControllerManagement; - - private static bool ControllerManagementSuccess = false; - private static int ControllerManagementAttempts = 0; - private const int ControllerManagementMaxAttempts = 3; - - private static readonly XInputController? emptyXInput = new(); - private static readonly DS4Controller? emptyDS4 = new(); - - private static IController? targetController; - private static FocusedWindow focusedWindows = FocusedWindow.None; - private static ProcessEx? foregroundProcess; - private static bool ControllerMuted; - - public static bool IsInitialized; - - static ControllerManager() - { - watchdogThread = new Thread(watchdogThreadLoop); - watchdogThread.IsBackground = true; - } - - public static void Start() - { - // Flushing possible JoyShocks... - JslDisconnectAndDisposeAll(); - - DeviceManager.XUsbDeviceArrived += XUsbDeviceArrived; - DeviceManager.XUsbDeviceRemoved += XUsbDeviceRemoved; - - DeviceManager.HidDeviceArrived += HidDeviceArrived; - DeviceManager.HidDeviceRemoved += HidDeviceRemoved; - - DeviceManager.Initialized += DeviceManager_Initialized; - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - - GamepadFocusManager.GotFocus += GamepadFocusManager_GotFocus; - GamepadFocusManager.LostFocus += GamepadFocusManager_LostFocus; - - ProcessManager.ForegroundChanged += ProcessManager_ForegroundChanged; - - VirtualManager.Vibrated += VirtualManager_Vibrated; - - MainWindow.CurrentDevice.KeyPressed += CurrentDevice_KeyPressed; - MainWindow.CurrentDevice.KeyReleased += CurrentDevice_KeyReleased; - - MainWindow.uiSettings.ColorValuesChanged += OnColorValuesChanged; - - // enable HidHide - HidHide.SetCloaking(true); - - IsInitialized = true; - Initialized?.Invoke(); - - // summon an empty controller, used to feed Layout UI - // todo: improve me - ControllerSelected?.Invoke(GetEmulatedController()); - - LogManager.LogInformation("{0} has started", "ControllerManager"); - } - - public static void Stop() - { - if (!IsInitialized) - return; - - IsInitialized = false; - - // unplug on close - ClearTargetController(); - - DeviceManager.XUsbDeviceArrived -= XUsbDeviceArrived; - DeviceManager.XUsbDeviceRemoved -= XUsbDeviceRemoved; - - DeviceManager.HidDeviceArrived -= HidDeviceArrived; - DeviceManager.HidDeviceRemoved -= HidDeviceRemoved; - - SettingsManager.SettingValueChanged -= SettingsManager_SettingValueChanged; - - // uncloak on close, if requested - if (SettingsManager.GetBoolean("HIDuncloakonclose")) - foreach (var controller in GetPhysicalControllers()) - controller.Unhide(false); - - // Flushing possible JoyShocks... - JslDisconnectAndDisposeAll(); - - LogManager.LogInformation("{0} has stopped", "ControllerManager"); - } - - private static void OnColorValuesChanged(UISettings sender, object args) - { - var _systemBackground = MainWindow.uiSettings.GetColorValue(UIColorType.Background); - var _systemAccent = MainWindow.uiSettings.GetColorValue(UIColorType.Accent); - - targetController?.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); - } - - [Flags] - private enum FocusedWindow - { - None, - MainWindow, - Quicktools - } - - private static void GamepadFocusManager_LostFocus(Control control) - { - GamepadWindow gamepadWindow = (GamepadWindow)control; - - switch (gamepadWindow.Title) - { - case "QuickTools": - focusedWindows &= ~FocusedWindow.Quicktools; - break; - default: - focusedWindows &= ~FocusedWindow.MainWindow; - break; - } - - // check applicable scenarios - CheckControllerScenario(); - } - - private static void GamepadFocusManager_GotFocus(Control control) - { - GamepadWindow gamepadWindow = (GamepadWindow)control; - switch (gamepadWindow.Title) - { - case "QuickTools": - focusedWindows |= FocusedWindow.Quicktools; - break; - default: - focusedWindows |= FocusedWindow.MainWindow; - break; - } - - // check applicable scenarios - CheckControllerScenario(); - } - - private static void ProcessManager_ForegroundChanged(ProcessEx processEx, ProcessEx backgroundEx) - { - foregroundProcess = processEx; - - // check applicable scenarios - CheckControllerScenario(); - } - - private static void CurrentDevice_KeyReleased(ButtonFlags button) - { - // calls current controller (if connected) - var controller = GetTargetController(); - controller?.InjectButton(button, false, true); - } - - private static void CurrentDevice_KeyPressed(ButtonFlags button) - { - // calls current controller (if connected) - var controller = GetTargetController(); - controller?.InjectButton(button, true, false); - } - - private static void CheckControllerScenario() - { - ControllerMuted = false; - - // platform specific scenarios - if (foregroundProcess?.Platform == PlatformType.Steam) - { - // mute virtual controller if foreground process is Steam or Steam-related and user a toggle the mute setting - // Controller specific scenarios - if (targetController is SteamController) - { - SteamController steamController = (SteamController)targetController; - if (steamController.IsVirtualMuted()) - ControllerMuted = true; - } - } - - // either main window or quicktools are focused - if (focusedWindows != FocusedWindow.None) - ControllerMuted = true; - } - - private static void SettingsManager_SettingValueChanged(string name, object value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (name) - { - case "VibrationStrength": - uint VibrationStrength = Convert.ToUInt32(value); - targetController?.SetVibrationStrength(VibrationStrength, MainWindow.GetCurrent().IsLoaded); - break; - - case "SteamControllerMute": - { - IController target = GetTargetController(); - if (target is null) - return; - - if (target is not SteamController) - return; - - bool Muted = Convert.ToBoolean(value); - ((SteamController)target).SetVirtualMuted(Muted); - } - break; - - case "ControllerManagement": - { - ControllerManagement = Convert.ToBoolean(value); - switch(ControllerManagement) - { - case true: - { - if (!watchdogThreadRunning) - { - watchdogThreadRunning = true; - - watchdogThread = new Thread(watchdogThreadLoop); - watchdogThread.IsBackground = true; - watchdogThread.Start(); - } - } - break; - case false: - { - if (watchdogThreadRunning) - { - watchdogThreadRunning = false; - watchdogThread.Join(); - } - } - break; - } - } - break; - - case "LegionControllerPassthrough": - { - IController target = GetTargetController(); - if (target is null) - return; - - if (target is not LegionController) - return; - - bool enabled = Convert.ToBoolean(value); - ((LegionController)target).SetPassthrough(enabled); - } - break; - } - }); - } - - private static void DeviceManager_Initialized() - { - // search for last known controller and connect - var path = SettingsManager.GetString("HIDInstancePath"); - - if (Controllers.ContainsKey(path)) - { - // last known controller still is plugged, set as target - SetTargetController(path, false); - } - else if (HasPhysicalController()) - { - // no known controller, connect to first available - path = GetPhysicalControllers().FirstOrDefault().GetContainerInstancePath(); - SetTargetController(path, false); - } - } - - private static void VirtualManager_Vibrated(byte LargeMotor, byte SmallMotor) - { - targetController?.SetVibration(LargeMotor, SmallMotor); - } - - private static async void HidDeviceArrived(PnPDetails details, DeviceEventArgs obj) - { - if (!details.isGaming) - return; - - Controllers.TryGetValue(details.baseContainerDeviceInstanceId, out IController controller); - - // are we power cycling ? - PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); - - // JoyShockLibrary - int connectedJoys = JslConnectDevices(); - if (connectedJoys != 0) - { - int[] joysHandle = new int[connectedJoys]; - JslGetConnectedDeviceHandles(joysHandle, connectedJoys); - - // scroll handles until we find matching device path - int joyShockId = -1; - JOY_SETTINGS settings = new(); - - foreach (int i in joysHandle) - { - settings = JslGetControllerInfoAndSettings(i); - - string joyShockpath = settings.path; - string detailsPath = details.devicePath; - - if (detailsPath.Equals(joyShockpath, StringComparison.InvariantCultureIgnoreCase)) - { - joyShockId = i; - break; - } - } - - // device found - if (joyShockId != -1) - { - // use handle - settings.playerNumber = joyShockId; - - JOY_TYPE joyShockType = (JOY_TYPE)JslGetControllerType(joyShockId); - - if (controller is not null) - { - ((JSController)controller).AttachDetails(details); - ((JSController)controller).AttachJoySettings(settings); - - // hide new InstanceID (HID) - if (controller.IsHidden()) - controller.Hide(false); - - IsPowerCycling = true; - } - else - { - // UI thread (sync) - Application.Current.Dispatcher.Invoke(() => - { - switch (joyShockType) - { - case JOY_TYPE.DualSense: - controller = new DualSenseController(settings, details); - break; - case JOY_TYPE.DualShock4: - controller = new DS4Controller(settings, details); - break; - case JOY_TYPE.ProController: - controller = new ProController(settings, details); - break; - } - }); - } - } - else - { - // unsupported controller - LogManager.LogError("Couldn't find matching JoyShock controller: VID:{0} and PID:{1}", - details.GetVendorID(), details.GetProductID()); - } - } - else - { - // DInput - var directInput = new DirectInput(); - int VendorId = details.VendorID; - int ProductId = details.ProductID; - - // initialize controller vars - Joystick joystick = null; - - // search for the plugged controller - foreach (var deviceInstance in directInput.GetDevices(DeviceType.Gamepad, DeviceEnumerationFlags.AllDevices)) - { - try - { - // Instantiate the joystick - var lookup_joystick = new Joystick(directInput, deviceInstance.InstanceGuid); - var SymLink = DeviceManager.SymLinkToInstanceId(lookup_joystick.Properties.InterfacePath, - obj.InterfaceGuid.ToString()); - - if (SymLink.Equals(details.SymLink, StringComparison.InvariantCultureIgnoreCase)) - { - joystick = lookup_joystick; - break; - } - } - catch - { - } - } - - if (joystick is not null) - { - // supported controller - VendorId = joystick.Properties.VendorId; - ProductId = joystick.Properties.ProductId; - } - else - { - // unsupported controller - LogManager.LogError("Couldn't find matching DInput controller: VID:{0} and PID:{1}", - details.GetVendorID(), details.GetProductID()); - } - - if (controller is not null) - { - controller.AttachDetails(details); - - // hide new InstanceID (HID) - if (controller.IsHidden()) - controller.Hide(false); - - IsPowerCycling = true; - } - else - { - // UI thread (sync) - Application.Current.Dispatcher.Invoke(() => - { - // search for a supported controller - switch (VendorId) - { - // STEAM - case 0x28DE: - { - switch (ProductId) - { - // WIRED STEAM CONTROLLER - case 0x1102: - // MI == 0 is virtual keyboards - // MI == 1 is virtual mouse - // MI == 2 is controller proper - // No idea what's in case of more than one controller connected - if (details.GetMI() == 2) - controller = new GordonController(details); - break; - // WIRELESS STEAM CONTROLLER - case 0x1142: - // MI == 0 is virtual keyboards - // MI == 1-4 are 4 controllers - // TODO: The dongle registers 4 controller devices, regardless how many are - // actually connected. There is no easy way to check for connection without - // actually talking to each controller. Handle only the first for now. - if (details.GetMI() == 1) - controller = new GordonController(details); - break; - - // STEAM DECK - case 0x1205: - controller = new NeptuneController(details); - break; - } - } - break; - - // NINTENDO - case 0x057E: - { - switch (ProductId) - { - // Nintendo Wireless Gamepad - case 0x2009: - break; - } - } - break; - - // LENOVO - case 0x17EF: - { - switch (ProductId) - { - case 0x6184: - break; - } - } - break; - } - }); - } - } - - // unsupported controller - if (controller is null) - { - LogManager.LogError("Unsupported Generic controller: VID:{0} and PID:{1}", details.GetVendorID(), - details.GetProductID()); - return; - } - - while (!controller.IsReady && controller.IsConnected()) - await Task.Delay(250); - - // set (un)busy - controller.IsBusy = false; - - // update or create controller - var path = controller.GetContainerInstancePath(); - Controllers[path] = controller; - - LogManager.LogDebug("Generic controller {0} plugged", controller.ToString()); - - // raise event - ControllerPlugged?.Invoke(controller, IsPowerCycling); - - ToastManager.SendToast(controller.ToString(), "detected"); - - // remove controller from powercyclers - PowerCyclers.TryRemove(controller.GetContainerInstancePath(), out _); - - // new controller logic - if (DeviceManager.IsInitialized) - { - if (controller.IsPhysical() && targetController is null) - SetTargetController(controller.GetContainerInstancePath(), IsPowerCycling); - - if (targetController is not null) - { - Color _systemBackground = MainWindow.uiSettings.GetColorValue(UIColorType.Background); - Color _systemAccent = MainWindow.uiSettings.GetColorValue(UIColorType.Accent); - targetController.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); - } - } - } - - private static async void HidDeviceRemoved(PnPDetails details, DeviceEventArgs obj) - { - IController controller = null; - - DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(10)); - while (DateTime.Now < timeout && controller is null) - { - if (Controllers.TryGetValue(details.baseContainerDeviceInstanceId, out controller)) - break; - - await Task.Delay(100); - } - - if (controller is null) - return; - - // XInput controller are handled elsewhere - if (controller is XInputController) - return; - - if (controller is JSController) - JslDisconnect(controller.GetUserIndex()); - - // are we power cycling ? - PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); - - // unhide on remove - if (!IsPowerCycling) - { - controller.Unhide(false); - - // unplug controller, if needed - if (GetTargetController()?.GetContainerInstancePath() == details.baseContainerDeviceInstanceId) - ClearTargetController(); - - // controller was unplugged - Controllers.TryRemove(details.baseContainerDeviceInstanceId, out _); - } - - LogManager.LogDebug("Generic controller {0} unplugged", controller.ToString()); - - // raise event - ControllerUnplugged?.Invoke(controller, IsPowerCycling); - } - - private static void watchdogThreadLoop(object? obj) - { - while(watchdogThreadRunning) - { - // monitoring unexpected slot changes - HashSet<byte> UserIndexes = new(); - bool XInputDrunk = false; - - foreach (XInputController xInputController in Controllers.Values.Where(c => c.Details is not null && c.Details.isXInput)) - { - byte UserIndex = DeviceManager.GetXInputIndexAsync(xInputController.Details.baseContainerDevicePath); - - // controller is not ready yet - if (UserIndex == byte.MaxValue) - continue; - - // that's not possible, XInput is drunk - if (!UserIndexes.Add(UserIndex)) - XInputDrunk = true; - - xInputController.AttachController(UserIndex); - } - - if (XInputDrunk) - { - foreach (XInputController xInputController in Controllers.Values.Where(c => c.Details is not null && c.Details.isXInput)) - xInputController.AttachController(byte.MaxValue); - } - - if (VirtualManager.HIDmode == HIDmode.Xbox360Controller && VirtualManager.HIDstatus == HIDstatus.Connected) - { - if (HasVirtualController()) - { - // check if it is first controller - IController controller = GetControllerFromSlot(UserIndex.One, false); - if (controller is null) - { - // disable that setting if we failed too many times - if (ControllerManagementAttempts == ControllerManagementMaxAttempts) - { - SettingsManager.SetProperty("ControllerManagement", false); - - // resume all physical controllers - StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers"); - if (deviceInstanceIds is not null && deviceInstanceIds.Count != 0) - ResumeControllers(); - - ControllerManagementSuccess = false; - ControllerManagementAttempts = 0; - - Working?.Invoke(2); - - // suspend watchdog - watchdogThreadRunning = false; - watchdogThread.Join(); - } - else - { - Working?.Invoke(0); - ControllerManagementSuccess = false; - ControllerManagementAttempts++; - - // suspend virtual controller - VirtualManager.Suspend(); - - // suspend all physical controllers - foreach (XInputController xInputController in GetPhysicalControllers().OfType<XInputController>()) - SuspendController(xInputController.Details.baseContainerDeviceInstanceId); - - // resume virtual controller - VirtualManager.Resume(); - - // resume all physical controllers - StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers"); - if (deviceInstanceIds is not null && deviceInstanceIds.Count != 0) - ResumeControllers(); - - // suspend and resume virtual controller - VirtualManager.Suspend(); - VirtualManager.Resume(); - } - } - else - { - // resume all physical controllers - StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers"); - if (deviceInstanceIds is not null && deviceInstanceIds.Count != 0) - ResumeControllers(); - - // give us one extra loop to make sure we're good - if (!ControllerManagementSuccess) - { - ControllerManagementSuccess = true; - Working?.Invoke(1); - } - else - ControllerManagementAttempts = 0; - } - } - } - - Thread.Sleep(2000); - } - } - - private static async void XUsbDeviceArrived(PnPDetails details, DeviceEventArgs obj) - { - Controllers.TryGetValue(details.baseContainerDeviceInstanceId, out IController controller); - - // are we power cycling ? - PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); - - // get details passed UserIndex - UserIndex userIndex = (UserIndex)details.XInputUserIndex; - - // device manager failed to retrieve actual userIndex - // use backup method - if (userIndex == UserIndex.Any) - userIndex = XInputController.TryGetUserIndex(details); - - if (controller is not null) - { - ((XInputController)controller).AttachDetails(details); - ((XInputController)controller).AttachController((byte)userIndex); - - // hide new InstanceID (HID) - if (controller.IsHidden()) - controller.Hide(false); - - IsPowerCycling = true; - } - else - { - Application.Current.Dispatcher.Invoke(() => - { - switch (details.GetVendorID()) - { - default: - controller = new XInputController(details); - break; - - // LegionGo - case "0x17EF": - controller = new LegionController(details); - break; - } - }); - } - - while (!controller.IsReady && controller.IsConnected()) - await Task.Delay(250); - - // set (un)busy - controller.IsBusy = false; - - // update or create controller - string path = details.baseContainerDeviceInstanceId; - Controllers[path] = controller; - - LogManager.LogDebug("XInput controller {0} plugged", controller.ToString()); - - // raise event - ControllerPlugged?.Invoke(controller, IsPowerCycling); - - ToastManager.SendToast(controller.ToString(), "detected"); - - // remove controller from powercyclers - PowerCyclers.TryRemove(controller.GetContainerInstancePath(), out _); - - // new controller logic - if (DeviceManager.IsInitialized) - { - if (controller.IsPhysical() && targetController is null) - SetTargetController(controller.GetContainerInstancePath(), IsPowerCycling); - - if (targetController is not null) - { - Color _systemBackground = MainWindow.uiSettings.GetColorValue(UIColorType.Background); - Color _systemAccent = MainWindow.uiSettings.GetColorValue(UIColorType.Accent); - targetController.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); - - string ManufacturerName = MotherboardInfo.Manufacturer.ToUpper(); - switch (ManufacturerName) - { - case "AOKZOE": - case "ONE-NETBOOK TECHNOLOGY CO., LTD.": - case "ONE-NETBOOK": - targetController.Rumble(); - break; - } - } - } - } - - private static async void XUsbDeviceRemoved(PnPDetails details, DeviceEventArgs obj) - { - IController controller = null; - - DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(10)); - while (DateTime.Now < timeout && controller is null) - { - if (Controllers.TryGetValue(details.baseContainerDeviceInstanceId, out controller)) - break; - - await Task.Delay(100); - } - - if (controller is null) - return; - - // are we power cycling ? - PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); - - // controller was unplugged - if (!IsPowerCycling) - { - controller.Unhide(false); - Controllers.TryRemove(details.baseContainerDeviceInstanceId, out _); - - // controller is current target - if (targetController?.GetContainerInstancePath() == details.baseContainerDeviceInstanceId) - ClearTargetController(); - } - - LogManager.LogDebug("XInput controller {0} unplugged", controller.ToString()); - - // raise event - ControllerUnplugged?.Invoke(controller, IsPowerCycling); - } - - private static void ClearTargetController() - { - // unplug previous controller - if (targetController is not null) - { - targetController.InputsUpdated -= UpdateInputs; - targetController.SetLightColor(0, 0, 0); - targetController.Cleanup(); - targetController.Unplug(); - targetController = null; - - // update HIDInstancePath - SettingsManager.SetProperty("HIDInstancePath", string.Empty); - } - } - - public static void SetTargetController(string baseContainerDeviceInstanceId, bool IsPowerCycling) - { - // look for new controller - if (!Controllers.TryGetValue(baseContainerDeviceInstanceId, out IController controller)) - return; - - if (controller.IsVirtual()) - return; - - // clear current target - ClearTargetController(); - - // update target controller - targetController = controller; - targetController.InputsUpdated += UpdateInputs; - targetController.Plug(); - - Color _systemBackground = MainWindow.uiSettings.GetColorValue(UIColorType.Background); - Color _systemAccent = MainWindow.uiSettings.GetColorValue(UIColorType.Accent); - targetController.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); - - // update HIDInstancePath - SettingsManager.SetProperty("HIDInstancePath", baseContainerDeviceInstanceId); - - if (!IsPowerCycling) - { - if (SettingsManager.GetBoolean("HIDcloakonconnect")) - { - bool powerCycle = true; - - if (targetController is LegionController) - { - // todo: Look for a byte within hid report that'd tend to mean both controllers are synced. - // Then I guess we could try and power cycle them. - powerCycle = !((LegionController)targetController).IsWireless; - } - - if (!targetController.IsHidden()) - targetController.Hide(powerCycle); - } - } - - // check applicable scenarios - CheckControllerScenario(); - - // check if controller is about to power cycle - PowerCyclers.TryGetValue(baseContainerDeviceInstanceId, out IsPowerCycling); - - if (!IsPowerCycling) - { - if (SettingsManager.GetBoolean("HIDvibrateonconnect")) - targetController.Rumble(); - } - - ControllerSelected?.Invoke(targetController); - } - - public static bool SuspendController(string baseContainerDeviceInstanceId) - { - // PnPUtil.StartPnPUtil(@"/delete-driver C:\Windows\INF\xusb22.inf /uninstall /force"); - StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers"); - - if (deviceInstanceIds is null) - deviceInstanceIds = new(); - - try - { - PnPDevice pnPDevice = PnPDevice.GetDeviceByInstanceId(baseContainerDeviceInstanceId); - UsbPnPDevice usbPnPDevice = pnPDevice.ToUsbPnPDevice(); - DriverMeta pnPDriver = null; - - try - { - pnPDriver = pnPDevice.GetCurrentDriver(); - } - catch { } - - string enumerator = pnPDevice.GetProperty<string>(DevicePropertyKey.Device_EnumeratorName); - switch (enumerator) - { - case "USB": - if (pnPDriver is not null) - { - pnPDevice.InstallNullDriver(out bool rebootRequired); - usbPnPDevice.CyclePort(); - } - - if (!deviceInstanceIds.Contains(baseContainerDeviceInstanceId)) - deviceInstanceIds.Add(baseContainerDeviceInstanceId); - - SettingsManager.SetProperty("SuspendedControllers", deviceInstanceIds); - PowerCyclers[baseContainerDeviceInstanceId] = true; - return true; - } - } - catch { } - - - return false; - } - - public static bool ResumeControllers() - { - // PnPUtil.StartPnPUtil(@"/add-driver C:\Windows\INF\xusb22.inf /install"); - StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers"); - - if (deviceInstanceIds is null || deviceInstanceIds.Count == 0) - return true; - - foreach (string baseContainerDeviceInstanceId in deviceInstanceIds) - { - try - { - PnPDevice pnPDevice = PnPDevice.GetDeviceByInstanceId(baseContainerDeviceInstanceId); - UsbPnPDevice usbPnPDevice = pnPDevice.ToUsbPnPDevice(); - DriverMeta pnPDriver = null; - - try - { - pnPDriver = pnPDevice.GetCurrentDriver(); - } - catch { } - - string enumerator = pnPDevice.GetProperty<string>(DevicePropertyKey.Device_EnumeratorName); - switch (enumerator) - { - case "USB": - if (pnPDriver is null || pnPDriver.InfPath != "xusb22.inf") - { - pnPDevice.RemoveAndSetup(); - pnPDevice.InstallCustomDriver("xusb22.inf", out bool rebootRequired); - } - - if (deviceInstanceIds.Contains(baseContainerDeviceInstanceId)) - deviceInstanceIds.Remove(baseContainerDeviceInstanceId); - - SettingsManager.SetProperty("SuspendedControllers", deviceInstanceIds); - PowerCyclers.TryRemove(baseContainerDeviceInstanceId, out _); - return true; - } - } - catch { } - } - - return false; - } - - public static IController GetTargetController() - { - return targetController; - } - - public static bool HasPhysicalController() - { - return GetPhysicalControllers().Count() != 0; - } - - public static bool HasVirtualController() - { - return GetVirtualControllers().Count() != 0; - } - - public static IEnumerable<IController> GetPhysicalControllers() - { - return Controllers.Values.Where(a => !a.IsVirtual()).ToList(); - } - - public static IEnumerable<IController> GetVirtualControllers() - { - return Controllers.Values.Where(a => a.IsVirtual()).ToList(); - } - - public static XInputController GetControllerFromSlot(UserIndex userIndex = 0, bool physical = true) - { - return Controllers.Values.FirstOrDefault(c => c is XInputController && ((physical && c.IsPhysical()) || !physical && c.IsVirtual()) && c.GetUserIndex() == (int)userIndex) as XInputController; - } - - public static List<IController> GetControllers() - { - return Controllers.Values.ToList(); - } - - private static void UpdateInputs(ControllerState controllerState) - { - ButtonState buttonState = controllerState.ButtonState.Clone() as ButtonState; - - // raise event - InputsUpdated?.Invoke(controllerState); - - // pass inputs to Inputs manager - InputsManager.UpdateReport(buttonState); - - // pass to SensorsManager for sensors value reading - SensorFamily sensorSelection = (SensorFamily)SettingsManager.GetInt("SensorSelection"); - switch (sensorSelection) - { - case SensorFamily.Windows: - case SensorFamily.SerialUSBIMU: - SensorsManager.UpdateReport(controllerState); - break; - } - - // pass to MotionManager for calculations - MotionManager.UpdateReport(controllerState); - - // pass inputs to Overlay Model - MainWindow.overlayModel.UpdateReport(controllerState); - - // pass inputs to Layout manager - controllerState = LayoutManager.MapController(controllerState); - - // controller is muted - if (ControllerMuted) - { - ControllerState emptyState = new ControllerState(); - emptyState.ButtonState[ButtonFlags.Special] = controllerState.ButtonState[ButtonFlags.Special]; - - controllerState = emptyState; - } - - VirtualManager.UpdateInputs(controllerState); - } - - internal static IController GetEmulatedController() - { - var HIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); - switch (HIDmode) - { - default: - case HIDmode.NoController: - case HIDmode.Xbox360Controller: - return emptyXInput; - - case HIDmode.DualShock4Controller: - return emptyDS4; - } - } - - #region events - - public static event ControllerPluggedEventHandler ControllerPlugged; - public delegate void ControllerPluggedEventHandler(IController Controller, bool IsPowerCycling); - - public static event ControllerUnpluggedEventHandler ControllerUnplugged; - public delegate void ControllerUnpluggedEventHandler(IController Controller, bool IsPowerCycling); - - public static event ControllerSelectedEventHandler ControllerSelected; - public delegate void ControllerSelectedEventHandler(IController Controller); - - public static event InputsUpdatedEventHandler InputsUpdated; - public delegate void InputsUpdatedEventHandler(ControllerState Inputs); - - public static event WorkingEventHandler Working; - public delegate void WorkingEventHandler(int status); - - public static event InitializedEventHandler Initialized; - public delegate void InitializedEventHandler(); - - #endregion +using HandheldCompanion.Controllers; +using HandheldCompanion.Controls; +using HandheldCompanion.Inputs; +using HandheldCompanion.Platforms; +using HandheldCompanion.Utils; +using HandheldCompanion.Views; +using HandheldCompanion.Views.Classes; +<<<<<<< HEAD +using Nefarius.Utilities.DeviceManagement.Drivers; +using Nefarius.Utilities.DeviceManagement.Extensions; +======= +using Inkore.UI.WPF.Modern; +using Microsoft.Win32; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using Nefarius.Utilities.DeviceManagement.PnP; +using SharpDX.DirectInput; +using SharpDX.XInput; +using System; +<<<<<<< HEAD +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using Windows.UI; +======= +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using Windows.UI.ViewManagement; +using static HandheldCompanion.Utils.DeviceUtils; +using static JSL; +using DeviceType = SharpDX.DirectInput.DeviceType; + +namespace HandheldCompanion.Managers; + +public static class ControllerManager +{ +<<<<<<< HEAD + private static readonly ConcurrentDictionary<string, IController> Controllers = new(); + public static readonly ConcurrentDictionary<string, bool> PowerCyclers = new(); + + private static Thread watchdogThread; + private static bool watchdogThreadRunning; + private static bool ControllerManagement; + + private static bool ControllerManagementSuccess = false; + private static int ControllerManagementAttempts = 0; + private const int ControllerManagementMaxAttempts = 3; +======= + private static readonly Dictionary<string, IController> Controllers = new(); + public static readonly Dictionary<string, bool> PowerCyclers = new(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + private static readonly XInputController? emptyXInput = new(); + private static readonly DS4Controller? emptyDS4 = new(); + + private static IController? targetController; + private static FocusedWindow focusedWindows = FocusedWindow.None; + private static ProcessEx? foregroundProcess; + private static bool ControllerMuted; + + private static UISettings uiSettings; + + public static bool IsInitialized; + +<<<<<<< HEAD + static ControllerManager() + { + watchdogThread = new Thread(watchdogThreadLoop); + watchdogThread.IsBackground = true; + } +======= + private static bool virtualControllerCreated; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + public static void Start() + { + // Flushing possible JoyShocks... + JslDisconnectAndDisposeAll(); + + DeviceManager.XUsbDeviceArrived += XUsbDeviceArrived; + DeviceManager.XUsbDeviceRemoved += XUsbDeviceRemoved; + + DeviceManager.HidDeviceArrived += HidDeviceArrived; + DeviceManager.HidDeviceRemoved += HidDeviceRemoved; + + DeviceManager.Initialized += DeviceManager_Initialized; + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + + GamepadFocusManager.GotFocus += GamepadFocusManager_GotFocus; + GamepadFocusManager.LostFocus += GamepadFocusManager_LostFocus; + + ProcessManager.ForegroundChanged += ProcessManager_ForegroundChanged; + +<<<<<<< HEAD +======= + VirtualManager.ControllerSelected += VirtualManager_ControllerSelected; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + VirtualManager.Vibrated += VirtualManager_Vibrated; + + MainWindow.CurrentDevice.KeyPressed += CurrentDevice_KeyPressed; + MainWindow.CurrentDevice.KeyReleased += CurrentDevice_KeyReleased; + + MainWindow.uiSettings.ColorValuesChanged += OnColorValuesChanged; + + // enable HidHide + HidHide.SetCloaking(true); + + uiSettings = new UISettings(); + uiSettings.ColorValuesChanged += OnColorValuesChanged; + + IsInitialized = true; + Initialized?.Invoke(); + + // summon an empty controller, used to feed Layout UI + // todo: improve me + ControllerSelected?.Invoke(GetEmulatedController()); + + LogManager.LogInformation("{0} has started", "ControllerManager"); + } + +<<<<<<< HEAD + public static void Stop() + { + if (!IsInitialized) + return; + + IsInitialized = false; + + // unplug on close + ClearTargetController(); + + DeviceManager.XUsbDeviceArrived -= XUsbDeviceArrived; + DeviceManager.XUsbDeviceRemoved -= XUsbDeviceRemoved; + + DeviceManager.HidDeviceArrived -= HidDeviceArrived; + DeviceManager.HidDeviceRemoved -= HidDeviceRemoved; + + SettingsManager.SettingValueChanged -= SettingsManager_SettingValueChanged; + + // uncloak on close, if requested + if (SettingsManager.GetBoolean("HIDuncloakonclose")) + foreach (var controller in GetPhysicalControllers()) + controller.Unhide(false); + + // Flushing possible JoyShocks... + JslDisconnectAndDisposeAll(); + + LogManager.LogInformation("{0} has stopped", "ControllerManager"); + } + + private static void OnColorValuesChanged(UISettings sender, object args) + { + var _systemBackground = MainWindow.uiSettings.GetColorValue(UIColorType.Background); + var _systemAccent = MainWindow.uiSettings.GetColorValue(UIColorType.Accent); +======= + private static void OnColorValuesChanged(UISettings sender, object args) + { + var _systemBackground = uiSettings.GetColorValue(UIColorType.Background); + var _systemAccent = uiSettings.GetColorValue(UIColorType.Accent); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + targetController?.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); + } + + [Flags] + private enum FocusedWindow + { + None, + MainWindow, + Quicktools + } + + private static void GamepadFocusManager_LostFocus(Control control) + { + GamepadWindow gamepadWindow = (GamepadWindow)control; + + switch (gamepadWindow.Title) + { + case "QuickTools": + focusedWindows &= ~FocusedWindow.Quicktools; + break; + default: + focusedWindows &= ~FocusedWindow.MainWindow; + break; + } + + // check applicable scenarios + CheckControllerScenario(); + } + + private static void GamepadFocusManager_GotFocus(Control control) + { + GamepadWindow gamepadWindow = (GamepadWindow)control; + switch (gamepadWindow.Title) + { + case "QuickTools": + focusedWindows |= FocusedWindow.Quicktools; + break; + default: + focusedWindows |= FocusedWindow.MainWindow; + break; + } + + // check applicable scenarios + CheckControllerScenario(); + } + + private static void ProcessManager_ForegroundChanged(ProcessEx processEx, ProcessEx backgroundEx) + { + foregroundProcess = processEx; + + // check applicable scenarios + CheckControllerScenario(); + } + + private static void CurrentDevice_KeyReleased(ButtonFlags button) + { + // calls current controller (if connected) + var controller = GetTargetController(); + controller?.InjectButton(button, false, true); + } + + private static void CurrentDevice_KeyPressed(ButtonFlags button) + { + // calls current controller (if connected) + var controller = GetTargetController(); + controller?.InjectButton(button, true, false); + } + + private static void CheckControllerScenario() + { + ControllerMuted = false; + + // platform specific scenarios + if (foregroundProcess?.Platform == PlatformType.Steam) + { + // mute virtual controller if foreground process is Steam or Steam-related and user a toggle the mute setting + // Controller specific scenarios + if (targetController is SteamController) + { + SteamController steamController = (SteamController)targetController; + if (steamController.IsVirtualMuted()) + ControllerMuted = true; + } + } + + // either main window or quicktools are focused + if (focusedWindows != FocusedWindow.None) + ControllerMuted = true; + } + +<<<<<<< HEAD +======= + public static void Stop() + { + if (!IsInitialized) + return; + + IsInitialized = false; + + // unplug on close + ClearTargetController(); + + DeviceManager.XUsbDeviceArrived -= XUsbDeviceArrived; + DeviceManager.XUsbDeviceRemoved -= XUsbDeviceRemoved; + + DeviceManager.HidDeviceArrived -= HidDeviceArrived; + DeviceManager.HidDeviceRemoved -= HidDeviceRemoved; + + SettingsManager.SettingValueChanged -= SettingsManager_SettingValueChanged; + + // uncloak on close, if requested + if (SettingsManager.GetBoolean("HIDuncloakonclose")) + foreach (var controller in GetPhysicalControllers()) + controller.Unhide(false); + + // Flushing possible JoyShocks... + JslDisconnectAndDisposeAll(); + + LogManager.LogInformation("{0} has stopped", "ControllerManager"); + } + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + private static void SettingsManager_SettingValueChanged(string name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { + case "VibrationStrength": + uint VibrationStrength = Convert.ToUInt32(value); + targetController?.SetVibrationStrength(VibrationStrength, MainWindow.GetCurrent().IsLoaded); + break; + + case "SteamControllerMute": + { + IController target = GetTargetController(); + if (target is null) + return; + + if (target is not SteamController) + return; + + bool Muted = Convert.ToBoolean(value); + ((SteamController)target).SetVirtualMuted(Muted); + } +<<<<<<< HEAD + break; + + case "ControllerManagement": + { + ControllerManagement = Convert.ToBoolean(value); + switch(ControllerManagement) + { + case true: + { + if (!watchdogThreadRunning) + { + watchdogThreadRunning = true; + + watchdogThread = new Thread(watchdogThreadLoop); + watchdogThread.IsBackground = true; + watchdogThread.Start(); + } + } + break; + case false: + { + if (watchdogThreadRunning) + { + watchdogThreadRunning = false; + watchdogThread.Join(); + } + } + break; + } + } + break; + + case "LegionControllerPassthrough": + { + IController target = GetTargetController(); + if (target is null) + return; + + if (target is not LegionController) + return; + + bool enabled = Convert.ToBoolean(value); + ((LegionController)target).SetPassthrough(enabled); + } +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + break; + } + }); + } + + private static void DeviceManager_Initialized() + { + // search for last known controller and connect + var path = SettingsManager.GetString("HIDInstancePath"); + + if (Controllers.ContainsKey(path)) + { + // last known controller still is plugged, set as target + SetTargetController(path, false); + } + else if (HasPhysicalController()) + { + // no known controller, connect to first available + path = GetPhysicalControllers().FirstOrDefault().GetContainerInstancePath(); + SetTargetController(path, false); + } + } + + private static void VirtualManager_Vibrated(byte LargeMotor, byte SmallMotor) + { + targetController?.SetVibration(LargeMotor, SmallMotor); + } + + private static async void HidDeviceArrived(PnPDetails details, DeviceEventArgs obj) + { + if (!details.isGaming) + return; + +<<<<<<< HEAD + Controllers.TryGetValue(details.baseContainerDeviceInstanceId, out IController controller); + + // are we power cycling ? + PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); +======= + // initialize controller vars + IController controller = null; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // JoyShockLibrary + int connectedJoys = JslConnectDevices(); + if (connectedJoys != 0) + { + int[] joysHandle = new int[connectedJoys]; + JslGetConnectedDeviceHandles(joysHandle, connectedJoys); + + // scroll handles until we find matching device path + int joyShockId = -1; + JOY_SETTINGS settings = new(); + + foreach (int i in joysHandle) + { + settings = JslGetControllerInfoAndSettings(i); + + string joyShockpath = settings.path; +<<<<<<< HEAD + string detailsPath = details.devicePath; +======= + string detailsPath = details.Path; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + if (detailsPath.Equals(joyShockpath, StringComparison.InvariantCultureIgnoreCase)) + { + joyShockId = i; + break; + } + } + + // device found + if (joyShockId != -1) + { + // use handle + settings.playerNumber = joyShockId; + + JOY_TYPE joyShockType = (JOY_TYPE)JslGetControllerType(joyShockId); + +<<<<<<< HEAD + if (controller is not null) + { + ((JSController)controller).AttachDetails(details); + ((JSController)controller).AttachJoySettings(settings); + + // hide new InstanceID (HID) + if (controller.IsHidden()) + controller.Hide(false); + + IsPowerCycling = true; + } + else + { + // UI thread (sync) + Application.Current.Dispatcher.Invoke(() => + { + switch (joyShockType) + { + case JOY_TYPE.DualSense: + controller = new DualSenseController(settings, details); + break; + case JOY_TYPE.DualShock4: + controller = new DS4Controller(settings, details); + break; + case JOY_TYPE.ProController: + controller = new ProController(settings, details); + break; + } + }); + } +======= + // UI thread (sync) + Application.Current.Dispatcher.Invoke(() => + { + switch (joyShockType) + { + case JOY_TYPE.DualSense: + controller = new DualSenseController(settings, details); + break; + case JOY_TYPE.DualShock4: + controller = new DS4Controller(settings, details); + break; + case JOY_TYPE.ProController: + controller = new ProController(settings, details); + break; + } + }); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + else + { + // unsupported controller + LogManager.LogError("Couldn't find matching JoyShock controller: VID:{0} and PID:{1}", + details.GetVendorID(), details.GetProductID()); + } + } + else + { +<<<<<<< HEAD + // DInput + var directInput = new DirectInput(); + int VendorId = details.VendorID; + int ProductId = details.ProductID; +======= + + // DInput + var directInput = new DirectInput(); + int VendorId = details.attributes.VendorID; + int ProductId = details.attributes.ProductID; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // initialize controller vars + Joystick joystick = null; + + // search for the plugged controller + foreach (var deviceInstance in directInput.GetDevices(DeviceType.Gamepad, DeviceEnumerationFlags.AllDevices)) + { + try + { + // Instantiate the joystick + var lookup_joystick = new Joystick(directInput, deviceInstance.InstanceGuid); + var SymLink = DeviceManager.SymLinkToInstanceId(lookup_joystick.Properties.InterfacePath, + obj.InterfaceGuid.ToString()); + + if (SymLink.Equals(details.SymLink, StringComparison.InvariantCultureIgnoreCase)) + { + joystick = lookup_joystick; + break; + } + } + catch + { + } + } + + if (joystick is not null) + { + // supported controller + VendorId = joystick.Properties.VendorId; + ProductId = joystick.Properties.ProductId; + } + else + { + // unsupported controller + LogManager.LogError("Couldn't find matching DInput controller: VID:{0} and PID:{1}", + details.GetVendorID(), details.GetProductID()); + } + +<<<<<<< HEAD + if (controller is not null) + { + controller.AttachDetails(details); + + // hide new InstanceID (HID) + if (controller.IsHidden()) + controller.Hide(false); + + IsPowerCycling = true; + } + else + { + // UI thread (sync) + Application.Current.Dispatcher.Invoke(() => + { + // search for a supported controller + switch (VendorId) + { + // STEAM + case 0x28DE: + { + switch (ProductId) + { + // WIRED STEAM CONTROLLER + case 0x1102: + // MI == 0 is virtual keyboards + // MI == 1 is virtual mouse + // MI == 2 is controller proper + // No idea what's in case of more than one controller connected + if (details.GetMI() == 2) + controller = new GordonController(details); + break; + // WIRELESS STEAM CONTROLLER + case 0x1142: + // MI == 0 is virtual keyboards + // MI == 1-4 are 4 controllers + // TODO: The dongle registers 4 controller devices, regardless how many are + // actually connected. There is no easy way to check for connection without + // actually talking to each controller. Handle only the first for now. + if (details.GetMI() == 1) + controller = new GordonController(details); + break; + + // STEAM DECK + case 0x1205: + controller = new NeptuneController(details); + break; + } + } + break; + + // NINTENDO + case 0x057E: + { + switch (ProductId) + { + // Nintendo Wireless Gamepad + case 0x2009: + break; + } + } + break; + + // LENOVO + case 0x17EF: + { + switch (ProductId) + { + case 0x6184: + break; + } + } + break; + } + }); + } + } + + // unsupported controller + if (controller is null) + { + LogManager.LogError("Unsupported Generic controller: VID:{0} and PID:{1}", details.GetVendorID(), + details.GetProductID()); + return; + } + + while (!controller.IsReady && controller.IsConnected()) + await Task.Delay(250); + + // set (un)busy + controller.IsBusy = false; + + // update or create controller + var path = controller.GetContainerInstancePath(); + Controllers[path] = controller; + + LogManager.LogDebug("Generic controller {0} plugged", controller.ToString()); + + // raise event + ControllerPlugged?.Invoke(controller, IsPowerCycling); + + ToastManager.SendToast(controller.ToString(), "detected"); + + // remove controller from powercyclers + PowerCyclers.TryRemove(controller.GetContainerInstancePath(), out _); + + // new controller logic + if (DeviceManager.IsInitialized) + { + if (controller.IsPhysical() && targetController is null) + SetTargetController(controller.GetContainerInstancePath(), IsPowerCycling); + + if (targetController is not null) + { + Color _systemBackground = MainWindow.uiSettings.GetColorValue(UIColorType.Background); + Color _systemAccent = MainWindow.uiSettings.GetColorValue(UIColorType.Accent); + targetController.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); + } + } +======= + // UI thread (sync) + Application.Current.Dispatcher.Invoke(() => + { + // search for a supported controller + switch (VendorId) + { + // STEAM + case 0x28DE: + { + switch (ProductId) + { + // WIRED STEAM CONTROLLER + case 0x1102: + // MI == 0 is virtual keyboards + // MI == 1 is virtual mouse + // MI == 2 is controller proper + // No idea what's in case of more than one controller connected + if (details.GetMI() == 2) + controller = new GordonController(details); + break; + // WIRELESS STEAM CONTROLLER + case 0x1142: + // MI == 0 is virtual keyboards + // MI == 1-4 are 4 controllers + // TODO: The dongle registers 4 controller devices, regardless how many are + // actually connected. There is no easy way to check for connection without + // actually talking to each controller. Handle only the first for now. + if (details.GetMI() == 1) + controller = new GordonController(details); + break; + + // STEAM DECK + case 0x1205: + controller = new NeptuneController(details); + break; + } + } + break; + + // NINTENDO + case 0x057E: + { + switch (ProductId) + { + // Nintendo Wireless Gamepad + case 0x2009: + break; + } + } + break; + } + }); + } + + // unsupported controller + if (controller is null) + { + LogManager.LogError("Unsupported Generic controller: VID:{0} and PID:{1}", details.GetVendorID(), + details.GetProductID()); + return; + } + + // failed to initialize + if (controller.Details is null) + return; + + if (!controller.IsConnected()) + return; + + // update or create controller + var path = controller.GetContainerInstancePath(); + Controllers[path] = controller; + + // are we power cycling ? + PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); + + // power cycling logic + // hide new InstanceID (HID) + if (IsPowerCycling && controller.IsHidden()) + controller.HideHID(); + + LogManager.LogDebug("Generic controller {0} plugged", controller.ToString()); + + // raise event + ControllerPlugged?.Invoke(controller, IsHCVirtualController(controller), IsPowerCycling); + + ToastManager.SendToast(controller.ToString(), "detected"); + + // remove controller from powercyclers + PowerCyclers.Remove(controller.GetContainerInstancePath()); + + // first controller logic + if (!controller.IsVirtual() && GetTargetController() is null && DeviceManager.IsInitialized) + SetTargetController(controller.GetContainerInstancePath(), IsPowerCycling); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private static async void HidDeviceRemoved(PnPDetails details, DeviceEventArgs obj) + { +<<<<<<< HEAD + IController controller = null; + + DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(10)); + while (DateTime.Now < timeout && controller is null) + { + if (Controllers.TryGetValue(details.baseContainerDeviceInstanceId, out controller)) + break; + + await Task.Delay(100); + } + + if (controller is null) +======= + if (!Controllers.TryGetValue(details.baseContainerDeviceInstanceId, out IController controller)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + return; + + // XInput controller are handled elsewhere + if (controller is XInputController) + return; + + if (controller is JSController) + JslDisconnect(controller.GetUserIndex()); +<<<<<<< HEAD +======= + + // are we power cycling ? + PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); + + // unhide on remove + if (!IsPowerCycling) + controller.UnhideHID(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // are we power cycling ? + PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); + + // unhide on remove + if (!IsPowerCycling) + { + controller.Unhide(false); + + // unplug controller, if needed + if (GetTargetController()?.GetContainerInstancePath() == details.baseContainerDeviceInstanceId) + ClearTargetController(); + + // controller was unplugged + Controllers.TryRemove(details.baseContainerDeviceInstanceId, out _); + } + + LogManager.LogDebug("Generic controller {0} unplugged", controller.ToString()); + + LogManager.LogDebug("Generic controller {0} unplugged", controller.ToString()); + + // raise event + ControllerUnplugged?.Invoke(controller, IsPowerCycling); + } + + private static void watchdogThreadLoop(object? obj) + { +<<<<<<< HEAD + while(watchdogThreadRunning) + { + // monitoring unexpected slot changes + HashSet<byte> UserIndexes = new(); + bool XInputDrunk = false; + + foreach (XInputController xInputController in Controllers.Values.Where(c => c.Details is not null && c.Details.isXInput)) + { + byte UserIndex = DeviceManager.GetXInputIndexAsync(xInputController.Details.baseContainerDevicePath); + + // controller is not ready yet + if (UserIndex == byte.MaxValue) + continue; + + // that's not possible, XInput is drunk + if (!UserIndexes.Add(UserIndex)) + XInputDrunk = true; + + xInputController.AttachController(UserIndex); + } + + if (XInputDrunk) + { + foreach (XInputController xInputController in Controllers.Values.Where(c => c.Details is not null && c.Details.isXInput)) + xInputController.AttachController(byte.MaxValue); + } + + if (VirtualManager.HIDmode == HIDmode.Xbox360Controller && VirtualManager.HIDstatus == HIDstatus.Connected) + { + if (HasVirtualController()) + { + // check if it is first controller + IController controller = GetControllerFromSlot(UserIndex.One, false); + if (controller is null) + { + // disable that setting if we failed too many times + if (ControllerManagementAttempts == ControllerManagementMaxAttempts) + { + SettingsManager.SetProperty("ControllerManagement", false); + + // resume all physical controllers + StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers"); + if (deviceInstanceIds is not null && deviceInstanceIds.Count != 0) + ResumeControllers(); + + ControllerManagementSuccess = false; + ControllerManagementAttempts = 0; + + Working?.Invoke(2); + + // suspend watchdog + watchdogThreadRunning = false; + watchdogThread.Join(); + } + else + { + Working?.Invoke(0); + ControllerManagementSuccess = false; + ControllerManagementAttempts++; + + // suspend virtual controller + VirtualManager.Suspend(); + + // suspend all physical controllers + foreach (XInputController xInputController in GetPhysicalControllers().OfType<XInputController>()) + SuspendController(xInputController.Details.baseContainerDeviceInstanceId); + + // resume virtual controller + VirtualManager.Resume(); + + // resume all physical controllers + StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers"); + if (deviceInstanceIds is not null && deviceInstanceIds.Count != 0) + ResumeControllers(); + + // suspend and resume virtual controller + VirtualManager.Suspend(); + VirtualManager.Resume(); + } + } + else + { + // resume all physical controllers + StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers"); + if (deviceInstanceIds is not null && deviceInstanceIds.Count != 0) + ResumeControllers(); + + // give us one extra loop to make sure we're good + if (!ControllerManagementSuccess) + { + ControllerManagementSuccess = true; + Working?.Invoke(1); + } + else + ControllerManagementAttempts = 0; + } + } + } + + Thread.Sleep(2000); + } + } + + private static async void XUsbDeviceArrived(PnPDetails details, DeviceEventArgs obj) + { + Controllers.TryGetValue(details.baseContainerDeviceInstanceId, out IController controller); +======= + // get details passed UserIndex + UserIndex userIndex = (UserIndex)details.XInputUserIndex; + + // device manager failed to retrieve actual userIndex + // use backup method + if (userIndex == UserIndex.Any) + userIndex = XInputController.TryGetUserIndex(details); + + // A XInput controller + Controller _controller = new(userIndex); + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + XInputController controller = new(_controller, details); + + // failed to initialize + if (controller.Details is null) + return; + + if (!controller.IsConnected()) + return; + + // update or create controller + string path = controller.GetContainerInstancePath(); + Controllers[path] = controller; + + // are we power cycling ? + PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); + + // power cycling logic + // hide new InstanceID (HID) + if (IsPowerCycling && controller.IsHidden()) + controller.HideHID(); + + LogManager.LogDebug("XInput controller {0} plugged", controller.ToString()); + + // raise event + ControllerPlugged?.Invoke(controller, IsHCVirtualController(controller), IsPowerCycling); + + ToastManager.SendToast(controller.ToString(), "detected"); + + // remove controller from powercyclers + PowerCyclers.Remove(controller.GetContainerInstancePath()); + + // first controller logic + if (!controller.IsVirtual() && GetTargetController() is null && DeviceManager.IsInitialized) + SetTargetController(controller.GetContainerInstancePath(), IsPowerCycling); + }); + } + + private static void XUsbDeviceRemoved(PnPDetails details, DeviceEventArgs obj) + { + if (!Controllers.TryGetValue(details.baseContainerDeviceInstanceId, out IController controller)) + return; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // are we power cycling ? + PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); + +<<<<<<< HEAD + // get details passed UserIndex + UserIndex userIndex = (UserIndex)details.XInputUserIndex; + + // device manager failed to retrieve actual userIndex + // use backup method + if (userIndex == UserIndex.Any) + userIndex = XInputController.TryGetUserIndex(details); + + if (controller is not null) + { + ((XInputController)controller).AttachDetails(details); + ((XInputController)controller).AttachController((byte)userIndex); + + // hide new InstanceID (HID) + if (controller.IsHidden()) + controller.Hide(false); + + IsPowerCycling = true; + } + else + { + Application.Current.Dispatcher.Invoke(() => + { + switch (details.GetVendorID()) + { + default: + controller = new XInputController(details); + break; + + // LegionGo + case "0x17EF": + controller = new LegionController(details); + break; + } + }); + } + + while (!controller.IsReady && controller.IsConnected()) + await Task.Delay(250); + + // set (un)busy + controller.IsBusy = false; + + // update or create controller + string path = details.baseContainerDeviceInstanceId; + Controllers[path] = controller; + + LogManager.LogDebug("XInput controller {0} plugged", controller.ToString()); +======= + // unhide on remove + if (!IsPowerCycling) + controller.UnhideHID(); + + // controller was unplugged + Controllers.Remove(details.baseContainerDeviceInstanceId); + + // unplug controller, if needed + if (GetTargetController()?.GetContainerInstancePath() == controller.GetContainerInstancePath()) + ClearTargetController(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + LogManager.LogDebug("XInput controller {0} unplugged", controller.ToString()); + + // raise event +<<<<<<< HEAD + ControllerPlugged?.Invoke(controller, IsPowerCycling); + + ToastManager.SendToast(controller.ToString(), "detected"); + + // remove controller from powercyclers + PowerCyclers.TryRemove(controller.GetContainerInstancePath(), out _); + + // new controller logic + if (DeviceManager.IsInitialized) + { + if (controller.IsPhysical() && targetController is null) + SetTargetController(controller.GetContainerInstancePath(), IsPowerCycling); + + if (targetController is not null) + { + Color _systemBackground = MainWindow.uiSettings.GetColorValue(UIColorType.Background); + Color _systemAccent = MainWindow.uiSettings.GetColorValue(UIColorType.Accent); + targetController.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); + + string ManufacturerName = MotherboardInfo.Manufacturer.ToUpper(); + switch (ManufacturerName) + { + case "AOKZOE": + case "ONE-NETBOOK TECHNOLOGY CO., LTD.": + case "ONE-NETBOOK": + targetController.Rumble(); + break; + } + } + } + } + + private static async void XUsbDeviceRemoved(PnPDetails details, DeviceEventArgs obj) + { + IController controller = null; + + DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(10)); + while (DateTime.Now < timeout && controller is null) + { + if (Controllers.TryGetValue(details.baseContainerDeviceInstanceId, out controller)) + break; + + await Task.Delay(100); + } + + if (controller is null) + return; + + // are we power cycling ? + PowerCyclers.TryGetValue(details.baseContainerDeviceInstanceId, out bool IsPowerCycling); + + // controller was unplugged + if (!IsPowerCycling) + { + controller.Unhide(false); + Controllers.TryRemove(details.baseContainerDeviceInstanceId, out _); + + // controller is current target + if (targetController?.GetContainerInstancePath() == details.baseContainerDeviceInstanceId) + ClearTargetController(); + } + + LogManager.LogDebug("XInput controller {0} unplugged", controller.ToString()); + + // raise event +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ControllerUnplugged?.Invoke(controller, IsPowerCycling); + } + + private static void ClearTargetController() + { + // unplug previous controller + if (targetController is not null) + { + targetController.InputsUpdated -= UpdateInputs; + targetController.SetLightColor(0, 0, 0); + targetController.Cleanup(); + targetController.Unplug(); + targetController = null; + + // update HIDInstancePath + SettingsManager.SetProperty("HIDInstancePath", string.Empty); + } + } + + public static void SetTargetController(string baseContainerDeviceInstanceId, bool IsPowerCycling) + { +<<<<<<< HEAD +======= + // unplug current controller + if (targetController is not null) + { + string targetPath = targetController.GetContainerInstancePath(); + + ClearTargetController(); + + // if we're setting currently selected, it's unplugged, there is none plugged + // reset the UI to the default controller and stop + if (targetPath == baseContainerDeviceInstanceId) + { + // reset layout UI + ControllerSelected?.Invoke(GetEmulatedController()); + return; + } + } + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + // look for new controller + if (!Controllers.TryGetValue(baseContainerDeviceInstanceId, out IController controller)) + return; + +<<<<<<< HEAD + if (controller.IsVirtual()) + return; + + // clear current target + ClearTargetController(); +======= + if (controller is null) + return; + + if (controller.IsVirtual()) + return; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // update target controller + targetController = controller; + targetController.InputsUpdated += UpdateInputs; + targetController.Plug(); + + Color _systemBackground = MainWindow.uiSettings.GetColorValue(UIColorType.Background); + Color _systemAccent = MainWindow.uiSettings.GetColorValue(UIColorType.Accent); + targetController.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); + +<<<<<<< HEAD +======= + var _systemBackground = uiSettings.GetColorValue(UIColorType.Background); + var _systemAccent = uiSettings.GetColorValue(UIColorType.Accent); + targetController.SetLightColor(_systemAccent.R, _systemAccent.G, _systemAccent.B); + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + // update HIDInstancePath + SettingsManager.SetProperty("HIDInstancePath", baseContainerDeviceInstanceId); + + if (!IsPowerCycling) + { + if (SettingsManager.GetBoolean("HIDcloakonconnect")) +<<<<<<< HEAD + { + bool powerCycle = true; + + if (targetController is LegionController) + { + // todo: Look for a byte within hid report that'd tend to mean both controllers are synced. + // Then I guess we could try and power cycle them. + powerCycle = !((LegionController)targetController).IsWireless; + } + + if (!targetController.IsHidden()) + targetController.Hide(powerCycle); + } +======= + if (!targetController.IsHidden()) + targetController.Hide(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + // check applicable scenarios + CheckControllerScenario(); + + // check if controller is about to power cycle + PowerCyclers.TryGetValue(baseContainerDeviceInstanceId, out IsPowerCycling); + + if (!IsPowerCycling) + { + if (SettingsManager.GetBoolean("HIDvibrateonconnect")) + targetController.Rumble(); + } +<<<<<<< HEAD +======= + else + { + // stop listening to device while it's power cycled + // only usefull for Xbox One bluetooth controllers + targetController.Unplug(); + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + ControllerSelected?.Invoke(targetController); + } + +<<<<<<< HEAD + public static bool SuspendController(string baseContainerDeviceInstanceId) + { + // PnPUtil.StartPnPUtil(@"/delete-driver C:\Windows\INF\xusb22.inf /uninstall /force"); + StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers"); + + if (deviceInstanceIds is null) + deviceInstanceIds = new(); + + try + { + PnPDevice pnPDevice = PnPDevice.GetDeviceByInstanceId(baseContainerDeviceInstanceId); + UsbPnPDevice usbPnPDevice = pnPDevice.ToUsbPnPDevice(); + DriverMeta pnPDriver = null; + + try + { + pnPDriver = pnPDevice.GetCurrentDriver(); + } + catch { } + + string enumerator = pnPDevice.GetProperty<string>(DevicePropertyKey.Device_EnumeratorName); + switch (enumerator) + { + case "USB": + if (pnPDriver is not null) + { + pnPDevice.InstallNullDriver(out bool rebootRequired); + usbPnPDevice.CyclePort(); + } + + if (!deviceInstanceIds.Contains(baseContainerDeviceInstanceId)) + deviceInstanceIds.Add(baseContainerDeviceInstanceId); + + SettingsManager.SetProperty("SuspendedControllers", deviceInstanceIds); + PowerCyclers[baseContainerDeviceInstanceId] = true; + return true; + } + } + catch { } + + + return false; + } + + public static bool ResumeControllers() + { + // PnPUtil.StartPnPUtil(@"/add-driver C:\Windows\INF\xusb22.inf /install"); + StringCollection deviceInstanceIds = SettingsManager.GetStringCollection("SuspendedControllers"); + + if (deviceInstanceIds is null || deviceInstanceIds.Count == 0) + return true; + + foreach (string baseContainerDeviceInstanceId in deviceInstanceIds) + { + try + { + PnPDevice pnPDevice = PnPDevice.GetDeviceByInstanceId(baseContainerDeviceInstanceId); + UsbPnPDevice usbPnPDevice = pnPDevice.ToUsbPnPDevice(); + DriverMeta pnPDriver = null; + + try + { + pnPDriver = pnPDevice.GetCurrentDriver(); + } + catch { } + + string enumerator = pnPDevice.GetProperty<string>(DevicePropertyKey.Device_EnumeratorName); + switch (enumerator) + { + case "USB": + if (pnPDriver is null || pnPDriver.InfPath != "xusb22.inf") + { + pnPDevice.RemoveAndSetup(); + pnPDevice.InstallCustomDriver("xusb22.inf", out bool rebootRequired); + } + + if (deviceInstanceIds.Contains(baseContainerDeviceInstanceId)) + deviceInstanceIds.Remove(baseContainerDeviceInstanceId); + + SettingsManager.SetProperty("SuspendedControllers", deviceInstanceIds); + PowerCyclers.TryRemove(baseContainerDeviceInstanceId, out _); + return true; + } + } + catch { } + } + + return false; + } + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public static IController GetTargetController() + { + return targetController; + } + + public static bool HasPhysicalController() + { + return GetPhysicalControllers().Count() != 0; + } + + public static bool HasVirtualController() + { + return GetVirtualControllers().Count() != 0; + } + + public static IEnumerable<IController> GetPhysicalControllers() + { + return Controllers.Values.Where(a => !a.IsVirtual()).ToList(); + } + + public static IEnumerable<IController> GetVirtualControllers() + { + return Controllers.Values.Where(a => a.IsVirtual()).ToList(); + } + + public static XInputController GetControllerFromSlot(UserIndex userIndex = 0, bool physical = true) + { + return Controllers.Values.FirstOrDefault(c => c is XInputController && ((physical && c.IsPhysical()) || !physical && c.IsVirtual()) && c.GetUserIndex() == (int)userIndex) as XInputController; + } + + public static List<IController> GetControllers() + { + return Controllers.Values.ToList(); + } + + private static void UpdateInputs(ControllerState controllerState) + { + ButtonState buttonState = controllerState.ButtonState.Clone() as ButtonState; + + // raise event + InputsUpdated?.Invoke(controllerState); + + // pass inputs to Inputs manager + InputsManager.UpdateReport(buttonState); + + // pass to SensorsManager for sensors value reading + SensorFamily sensorSelection = (SensorFamily)SettingsManager.GetInt("SensorSelection"); + switch (sensorSelection) + { + case SensorFamily.Windows: + case SensorFamily.SerialUSBIMU: + SensorsManager.UpdateReport(controllerState); + break; + } + + // pass to MotionManager for calculations + MotionManager.UpdateReport(controllerState); + + // pass inputs to Overlay Model + MainWindow.overlayModel.UpdateReport(controllerState); + + // pass inputs to Layout manager + controllerState = LayoutManager.MapController(controllerState); + + // controller is muted + if (ControllerMuted) + { + ControllerState emptyState = new ControllerState(); + emptyState.ButtonState[ButtonFlags.Special] = controllerState.ButtonState[ButtonFlags.Special]; + +<<<<<<< HEAD + controllerState = emptyState; + } + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + VirtualManager.UpdateInputs(controllerState); + } + + internal static IController GetEmulatedController() + { + var HIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); + switch (HIDmode) + { + default: + case HIDmode.NoController: + case HIDmode.Xbox360Controller: + return emptyXInput; + + case HIDmode.DualShock4Controller: + return emptyDS4; + } + } + + private static bool IsHCVirtualController(XInputController controller) + { + if (controller.IsVirtual() && virtualControllerCreated) + { + virtualControllerCreated = false; + return true; + } + return false; + } + + private static bool IsHCVirtualController(IController controller) + { + if (controller.IsVirtual() && virtualControllerCreated) + { + virtualControllerCreated = false; + return true; + } + return false; + } + + private static void VirtualManager_ControllerSelected(IController Controller) + { + virtualControllerCreated = true; + } + + #region events + + public static event ControllerPluggedEventHandler ControllerPlugged; +<<<<<<< HEAD + public delegate void ControllerPluggedEventHandler(IController Controller, bool IsPowerCycling); + + public static event ControllerUnpluggedEventHandler ControllerUnplugged; +======= + + public delegate void ControllerPluggedEventHandler(IController Controller, bool isHCVirtualController, bool IsPowerCycling); + + public static event ControllerUnpluggedEventHandler ControllerUnplugged; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public delegate void ControllerUnpluggedEventHandler(IController Controller, bool IsPowerCycling); + + public static event ControllerSelectedEventHandler ControllerSelected; + public delegate void ControllerSelectedEventHandler(IController Controller); + + public static event InputsUpdatedEventHandler InputsUpdated; + public delegate void InputsUpdatedEventHandler(ControllerState Inputs); + + public static event WorkingEventHandler Working; + public delegate void WorkingEventHandler(int status); + + public static event InitializedEventHandler Initialized; + public delegate void InitializedEventHandler(); + + #endregion } \ No newline at end of file diff --git a/HandheldCompanion/Managers/DeviceManager.cs b/HandheldCompanion/Managers/DeviceManager.cs index d6827a116..516a7c273 100644 --- a/HandheldCompanion/Managers/DeviceManager.cs +++ b/HandheldCompanion/Managers/DeviceManager.cs @@ -1,886 +1,1166 @@ -using HandheldCompanion.Managers.Hid; -using HandheldCompanion.Sensors; -using HandheldCompanion.Utils; -using Microsoft.Win32.SafeHandles; -using Nefarius.Utilities.DeviceManagement.PnP; -using PInvoke; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading.Tasks; - -namespace HandheldCompanion.Managers; - -public static class DeviceManager -{ - public static Guid HidDevice; - private static readonly DeviceNotificationListener UsbDeviceListener = new(); - private static readonly DeviceNotificationListener XUsbDeviceListener = new(); - private static readonly DeviceNotificationListener HidDeviceListener = new(); - - private static readonly ConcurrentDictionary<string, PnPDetails> PnPDevices = new(); - - const ulong GENERIC_READ = (0x80000000L); - const ulong GENERIC_WRITE = (0x40000000L); - const ulong GENERIC_EXECUTE = (0x20000000L); - const ulong GENERIC_ALL = (0x10000000L); - - const uint FILE_SHARE_READ = 0x00000001; - const uint FILE_SHARE_WRITE = 0x00000002; - const uint FILE_SHARE_DELETE = 0x00000004; - - const uint CREATE_NEW = 1; - const uint CREATE_ALWAYS = 2; - const uint OPEN_EXISTING = 3; - const uint OPEN_ALWAYS = 4; - const uint TRUNCATE_EXISTING = 5; - - const ulong IOCTL_XUSB_GET_LED_STATE = 0x8000E008; - - static byte[] XINPUT_LED_TO_PORT_MAP = new byte[16] - { - 255, // All off - 255, // All blinking, then previous setting - 0, // 1 flashes, then on - 1, // 2 flashes, then on - 2, // 3 flashes, then on - 3, // 4 flashes, then on - 0, // 1 on - 1, // 2 on - 2, // 3 on - 3, // 4 on - 255, // Rotate - 255, // Blink, based on previous setting - 255, // Slow blink, based on previous setting - 255, // Rotate with two lights - 255, // Persistent slow all blink - 255, // Blink once, then previous setting - }; - - public static bool IsInitialized; - - static DeviceManager() - { - // initialize hid - HidD_GetHidGuidMethod(out HidDevice); - } - - public static void Start() - { - // fail-safe - PnPUtil.StartPnPUtil(@"/add-driver C:\Windows\INF\xusb22.inf /install"); - - UsbDeviceListener.StartListen(DeviceInterfaceIds.UsbDevice); - UsbDeviceListener.DeviceArrived += UsbDevice_DeviceArrived; - UsbDeviceListener.DeviceRemoved += UsbDevice_DeviceRemoved; - - XUsbDeviceListener.StartListen(DeviceInterfaceIds.XUsbDevice); - XUsbDeviceListener.DeviceArrived += XUsbDevice_DeviceArrived; - XUsbDeviceListener.DeviceRemoved += XUsbDevice_DeviceRemoved; - - HidDeviceListener.StartListen(DeviceInterfaceIds.HidDevice); - HidDeviceListener.DeviceArrived += HidDevice_DeviceArrived; - HidDeviceListener.DeviceRemoved += HidDevice_DeviceRemoved; - - RefreshXInput(); - RefreshDInput(); - - IsInitialized = true; - Initialized?.Invoke(); - - LogManager.LogInformation("{0} has started", "DeviceManager"); - } - - public static void Stop() - { - if (!IsInitialized) - return; - - IsInitialized = false; - - UsbDeviceListener.StopListen(DeviceInterfaceIds.UsbDevice); - UsbDeviceListener.DeviceArrived -= UsbDevice_DeviceArrived; - UsbDeviceListener.DeviceRemoved -= UsbDevice_DeviceRemoved; - - XUsbDeviceListener.StopListen(DeviceInterfaceIds.XUsbDevice); - XUsbDeviceListener.DeviceArrived -= XUsbDevice_DeviceArrived; - XUsbDeviceListener.DeviceRemoved -= XUsbDevice_DeviceRemoved; - - HidDeviceListener.StopListen(DeviceInterfaceIds.HidDevice); - HidDeviceListener.DeviceArrived -= HidDevice_DeviceArrived; - HidDeviceListener.DeviceRemoved -= HidDevice_DeviceRemoved; - - // fail-safe - PnPUtil.StartPnPUtil(@"/add-driver C:\Windows\INF\xusb22.inf /install"); - - LogManager.LogInformation("{0} has stopped", "DeviceManager"); - } - - private static void RefreshXInput() - { - var deviceIndex = 0; - Dictionary<string, DateTimeOffset> devices = new(); - - while (Devcon.FindByInterfaceGuid(DeviceInterfaceIds.XUsbDevice, out var path, out var instanceId, - deviceIndex++)) - { - var device = PnPDevice.GetDeviceByInterfaceId(path); - var arrival = device.GetProperty<DateTimeOffset>(DevicePropertyKey.Device_LastArrivalDate); - - // add new device - devices.Add(path, arrival); - } - - // sort devices list - devices = devices.OrderBy(device => device.Value).ToDictionary(x => x.Key, x => x.Value); - foreach (var pair in devices) - XUsbDevice_DeviceArrived(new DeviceEventArgs - { InterfaceGuid = DeviceInterfaceIds.XUsbDevice, SymLink = pair.Key }); - } - - private static void RefreshDInput() - { - var deviceIndex = 0; - Dictionary<string, DateTimeOffset> devices = new(); - - while (Devcon.FindByInterfaceGuid(DeviceInterfaceIds.HidDevice, out var path, out var instanceId, - deviceIndex++)) - { - var device = PnPDevice.GetDeviceByInterfaceId(path); - var arrival = device.GetProperty<DateTimeOffset>(DevicePropertyKey.Device_LastArrivalDate); - - // add new device - devices.Add(path, arrival); - } - - // sort devices list - devices = devices.OrderBy(device => device.Value).ToDictionary(x => x.Key, x => x.Value); - foreach (var pair in devices) - HidDevice_DeviceArrived(new DeviceEventArgs - { InterfaceGuid = DeviceInterfaceIds.HidDevice, SymLink = pair.Key }); - } - - private static PnPDetails FindDevice(string InstanceId) - { - if (InstanceId.StartsWith(@"USB\")) - return FindDeviceFromUSB(InstanceId); - if (InstanceId.StartsWith(@"HID\")) - return FindDeviceFromHID(InstanceId); - return null; - } - - public static PnPDetails FindDeviceFromUSB(string InstanceId) - { - PnPDetails details = null; - while(details is null) - { - details = PnPDevices.Values.FirstOrDefault(device => device.baseContainerDeviceInstanceId.Equals(InstanceId, StringComparison.InvariantCultureIgnoreCase)); - - if (details is not null) - return details; - - // look for parent - try - { - PnPDevice pnPDevice = PnPDevice.GetDeviceByInstanceId(InstanceId); - if (pnPDevice.Parent is null) - break; - - InstanceId = pnPDevice.Parent.InstanceId; - } - catch - { - break; - } - } - - return details; - } - - public static PnPDetails FindDeviceFromHID(string InstanceId) - { - PnPDevices.TryGetValue(InstanceId, out var device); - return device; - } - - private static int GetDeviceIndex(string path) - { - int deviceIndex = 0; - - while (Devcon.FindByInterfaceGuid(DeviceInterfaceIds.HidDevice, out var symlink, out var instanceId, deviceIndex++)) - if (symlink == path) - return deviceIndex; - - return 0; - } - - private static PnPDetails GetDetails(string path) - { - try - { - string SymLink = SymLinkToInstanceId(path, DeviceInterfaceIds.HidDevice.ToString()); - - PnPDevice children = PnPDevice.GetDeviceByInterfaceId(path); - - // get attributes - Attributes? attributes = GetHidAttributes(path); - Capabilities? capabilities = GetHidCapabilities(path); - - if (attributes is null || capabilities is null) - return null; - - string ProductID = ((Attributes)attributes).ProductID.ToString("X4"); - string VendorID = ((Attributes)attributes).VendorID.ToString("X4"); - - IPnPDevice? parent = children; - string parentId = parent.InstanceId; - - while (parent.Parent is not null) - { - // update parent InstanceId - parentId = parent.Parent.InstanceId; - - if (parentId.Equals(@"HTREE\ROOT\0", StringComparison.InvariantCultureIgnoreCase)) - break; - - if (parentId.Contains(@"USB\ROOT", StringComparison.InvariantCultureIgnoreCase)) - break; - - if (parentId.Contains(@"ROOT\SYSTEM", StringComparison.InvariantCultureIgnoreCase)) - break; - - if (parentId.Contains(@"HID\", StringComparison.InvariantCultureIgnoreCase)) - break; - - if (!parentId.Contains(ProductID, StringComparison.InvariantCultureIgnoreCase)) - break; - - if (!parentId.Contains(VendorID, StringComparison.InvariantCultureIgnoreCase)) - break; - - // we want the closest parent - PnPDevice check = PnPDevice.GetDeviceByInstanceId(parentId); - if (check.Children is not null) - { - if (check.Children.Count() > 1) - break; - } - - // update parent - parent = check; - } - - // get details - PnPDetails details = new PnPDetails - { - devicePath = path, - SymLink = SymLink, - Name = parent.GetProperty<string>(DevicePropertyKey.Device_DeviceDesc), - Enumerator = parent.GetProperty<string>(DevicePropertyKey.Device_EnumeratorName), - deviceInstanceId = children.InstanceId.ToUpper(), - baseContainerDeviceInstanceId = parent.InstanceId.ToUpper(), - isVirtual = parent.IsVirtual() || children.IsVirtual(), - isGaming = IsGaming((Attributes)attributes, (Capabilities)capabilities), - ProductID = ((Attributes)attributes).ProductID, - VendorID = ((Attributes)attributes).VendorID, - isXInput = children.InstanceId.Contains("IG_", StringComparison.InvariantCultureIgnoreCase), - }; - - // get name - string DeviceDesc = parent.GetProperty<string>(DevicePropertyKey.Device_DeviceDesc); - string FriendlyName = parent.GetProperty<string>(DevicePropertyKey.Device_FriendlyName); - - if (!string.IsNullOrEmpty(FriendlyName)) - details.Name = FriendlyName; - else if (!string.IsNullOrEmpty(DeviceDesc)) - details.Name = DeviceDesc; - - // add or update device - PnPDevices[details.SymLink] = details; - - return details; - } - catch { } - - return null; - } - - public static List<PnPDetails> GetDetails(ushort VendorId = 0, ushort ProductId = 0) - { - return PnPDevices.Values.OrderBy(device => device.XInputDeviceIdx).Where(device => - device.VendorID == VendorId && device.ProductID == ProductId && !device.isHooked).ToList(); - } - - public static PnPDetails GetDeviceByInterfaceId(string path) - { - var device = PnPDevice.GetDeviceByInterfaceId(path); - if (device is null) - return null; - - return new PnPDetails - { - devicePath = path, - SymLink = SymLinkToInstanceId(path, DeviceInterfaceIds.UsbDevice.ToString()), - - deviceInstanceId = device.InstanceId, - baseContainerDeviceInstanceId = device.InstanceId - }; - } - - public static string GetManufacturerString(string path) - { - using var handle = Kernel32.CreateFile(path, - Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | - Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, - Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, - IntPtr.Zero, Kernel32.CreationDisposition.OPEN_EXISTING, - Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL - | Kernel32.CreateFileFlags.FILE_FLAG_NO_BUFFERING - | Kernel32.CreateFileFlags.FILE_FLAG_WRITE_THROUGH, - Kernel32.SafeObjectHandle.Null - ); - - return GetString(handle.DangerousGetHandle(), HidD_GetManufacturerString); - } - - public static string GetProductString(string path) - { - using var handle = Kernel32.CreateFile(path, - Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | - Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, - Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, - IntPtr.Zero, Kernel32.CreationDisposition.OPEN_EXISTING, - Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL - | Kernel32.CreateFileFlags.FILE_FLAG_NO_BUFFERING - | Kernel32.CreateFileFlags.FILE_FLAG_WRITE_THROUGH, - Kernel32.SafeObjectHandle.Null - ); - - return GetString(handle.DangerousGetHandle(), HidD_GetProductString); - } - - private static string GetString(IntPtr handle, Func<IntPtr, byte[], uint, bool> proc) - { - var buf = new byte[256]; - - if (!proc(handle, buf, (uint)buf.Length)) - return null; - - var str = Encoding.Unicode.GetString(buf, 0, buf.Length); - - return str.Contains("\0") ? str.Substring(0, str.IndexOf('\0')) : str; - } - - private static Attributes? GetHidAttributes(string path) - { - using var handle = Kernel32.CreateFile(path, - Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, - Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, - IntPtr.Zero, - Kernel32.CreationDisposition.OPEN_EXISTING, - Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL, - Kernel32.SafeObjectHandle.Null - ); - - return GetAttributes.Get(handle.DangerousGetHandle()); - } - - private static Capabilities? GetHidCapabilities(string path) - { - using var handle = Kernel32.CreateFile(path, - Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, - Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, - IntPtr.Zero, - Kernel32.CreationDisposition.OPEN_EXISTING, - Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL, - Kernel32.SafeObjectHandle.Null - ); - - return GetCapabilities.Get(handle.DangerousGetHandle()); - } - - private static bool IsGaming(Attributes attributes, Capabilities capabilities) - { - return ( - ((attributes.VendorID == 0x28DE) && (attributes.ProductID == 0x1102)) || // STEAM CONTROLLER - // ((attributes.VendorID == 0x28DE) && (attributes.ProductID == 0x1106)) || // STEAM CONTROLLER BLUETOOTH - ((attributes.VendorID == 0x28DE) && (attributes.ProductID == 0x1142)) || // STEAM CONTROLLER WIRELESS - ((attributes.VendorID == 0x28DE) && (attributes.ProductID == 0x1205)) || // STEAM DECK - (0x05 == capabilities.UsagePage) || (0x01 == capabilities.UsagePage) && ((0x04 == capabilities.Usage) || (0x05 == capabilities.Usage))); - } - - public static PnPDetails GetPnPDeviceEx(string SymLink) - { - PnPDevices.TryGetValue(SymLink, out var details); - return details; - } - - public static string SymLinkToInstanceId(string SymLink, string InterfaceGuid) - { - string InstanceId = SymLink.ToUpper().Replace(InterfaceGuid, "", StringComparison.InvariantCultureIgnoreCase); - InstanceId = InstanceId.Replace("#", @"\"); - InstanceId = InstanceId.Replace(@"\\?\", ""); - InstanceId = InstanceId.Replace(@"\{}", ""); - return InstanceId; - } - - private static void XUsbDevice_DeviceRemoved(DeviceEventArgs obj) - { - try - { - Task.Run(async () => - { - string InstanceId = SymLinkToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); - - PnPDetails deviceEx = null; - DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(8)); - while (DateTime.Now < timeout && deviceEx is null) - { - deviceEx = FindDevice(InstanceId); - await Task.Delay(100); - } - - if (deviceEx is null) - return; - - if (PnPDevices.TryRemove(deviceEx.SymLink, out var value)) - { - LogManager.LogDebug("XUsbDevice {1} removed from slot {2}: {0}", deviceEx.Name, deviceEx.isVirtual ? "virtual" : "physical", deviceEx.XInputUserIndex); - XUsbDeviceRemoved?.Invoke(deviceEx, obj); - } - }); - } - catch { } - } - - private static void XUsbDevice_DeviceArrived(DeviceEventArgs obj) - { - try - { - Task.Run(async () => - { - string InstanceId = SymLinkToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); - - PnPDetails deviceEx = null; - - DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(8)); - while (DateTime.Now < timeout && deviceEx is null) - { - deviceEx = FindDevice(InstanceId); - await Task.Delay(100); - } - - if (deviceEx is not null && deviceEx.isGaming) - { - deviceEx.isXInput = true; - deviceEx.baseContainerDevicePath = obj.SymLink; - - if (deviceEx.Enumerator.Equals("USB")) - deviceEx.XInputUserIndex = GetXInputIndexAsync(obj.SymLink); - - if (deviceEx.XInputUserIndex == byte.MaxValue) - deviceEx.XInputDeviceIdx = GetDeviceIndex(obj.SymLink); - - LogManager.LogDebug("XUsbDevice {4} arrived on slot {5}: {0} (VID:{1}, PID:{2}) {3}", deviceEx.Name, - deviceEx.GetVendorID(), deviceEx.GetProductID(), deviceEx.deviceInstanceId, deviceEx.isVirtual ? "virtual" : "physical", deviceEx.XInputUserIndex); - - // raise event - Event: - XUsbDeviceArrived?.Invoke(deviceEx, obj); - } - }); - } - catch - { - } - } - - private static void HidDevice_DeviceRemoved(DeviceEventArgs obj) - { - try - { - Task.Run(async () => - { - string InstanceId = SymLinkToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); - - PnPDetails deviceEx = null; - DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(8)); - while (DateTime.Now < timeout && deviceEx is null) - { - deviceEx = FindDevice(InstanceId); - await Task.Delay(100); - } - - // skip if XInput - if (deviceEx is null || deviceEx.isXInput) - return; - - if (PnPDevices.TryRemove(deviceEx.SymLink, out var value)) - { - LogManager.LogDebug("HidDevice removed: {0}", deviceEx.Name); - HidDeviceRemoved?.Invoke(deviceEx, obj); - } - }); - } - catch - { - } - } - - private static void HidDevice_DeviceArrived(DeviceEventArgs obj) - { - try - { - Task.Run(async () => - { - string InstanceId = SymLinkToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); - PnPDetails deviceEx = null; - - DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(8)); - while (DateTime.Now < timeout && deviceEx is null) - { - deviceEx = GetDetails(obj.SymLink); - await Task.Delay(100); - } - - // skip if XInput - if (deviceEx is null || deviceEx.isXInput) - return; - - LogManager.LogDebug("HidDevice arrived: {0} (VID:{1}, PID:{2}) {3}", deviceEx.Name, deviceEx.GetVendorID(), - deviceEx.GetProductID(), deviceEx.deviceInstanceId); - HidDeviceArrived?.Invoke(deviceEx, obj); - }); - } - catch { } - } - - private static void UsbDevice_DeviceRemoved(DeviceEventArgs obj) - { - try - { - var symLink = CommonUtils.Between(obj.SymLink, "#", "#") + "&"; - var VendorID = CommonUtils.Between(symLink, "VID_", "&"); - var ProductID = CommonUtils.Between(symLink, "PID_", "&"); - - if (SerialUSBIMU.vendors.ContainsKey(new KeyValuePair<string, string>(VendorID, ProductID))) - UsbDeviceRemoved?.Invoke(null, obj); - } - catch - { - } - } - - private static void UsbDevice_DeviceArrived(DeviceEventArgs obj) - { - try - { - var symLink = CommonUtils.Between(obj.SymLink, "#", "#") + "&"; - var VendorID = CommonUtils.Between(symLink, "VID_", "&"); - var ProductID = CommonUtils.Between(symLink, "PID_", "&"); - - if (SerialUSBIMU.vendors.ContainsKey(new KeyValuePair<string, string>(VendorID, ProductID))) - UsbDeviceArrived?.Invoke(null, obj); - } - catch - { - } - } - - public static byte GetXInputIndexAsync(string SymLink) - { - byte ledState = 0; - - DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(4)); - while (DateTime.Now < timeout && (ledState < 2 || ledState > 9)) - { - using (SafeFileHandle handle = CreateFileW(SymLink, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)) - { - if (handle.IsInvalid) - return byte.MaxValue; - - byte[] gamepadStateRequest0101 = new byte[3] { 0x01, 0x01, 0x00 }; - byte[] ledStateData = new byte[3]; - uint len = 0; - - if (!DeviceIoControl(handle, IOCTL_XUSB_GET_LED_STATE, gamepadStateRequest0101, gamepadStateRequest0101.Length, ledStateData, ledStateData.Length, ref len, 0)) - return byte.MaxValue; - - ledState = ledStateData[2]; - } - - Task.Delay(1000); - } - - return XINPUT_LED_TO_PORT_MAP[ledState]; - } - - public static string[]? GetDevices(Guid? classGuid) - { - string? filter = null; - int flags = CM_GETIDLIST_FILTER_PRESENT; - - if (classGuid is not null) - { - filter = classGuid?.ToString("B").ToUpper(); - flags |= CM_GETIDLIST_FILTER_CLASS; - } - - var res = CM_Get_Device_ID_List_Size(out var size, filter, flags); - if (res != CR_SUCCESS) - return null; - - char[] data = new char[size]; - res = CM_Get_Device_ID_List(filter, data, size, flags); - if (res != CR_SUCCESS) - return null; - - var result = new string(data); - var devices = result.Split('\0', StringSplitOptions.RemoveEmptyEntries); - return devices.ToArray(); - } - - public static string? GetDeviceDesc(String PNPString) - { - if (CM_Locate_DevNode(out var devInst, PNPString, 0) != 0) - return null; - - if (!CM_Get_DevNode_Property(devInst, DEVPKEY_Device_DeviceDesc, out var deviceDesc, 0)) - return null; - - return deviceDesc; - } - - public static IList<Tuple<UIntPtr, UIntPtr>>? GetDeviceMemResources(string PNPString) - { - int res = CM_Locate_DevNode(out var devInst, PNPString, 0); - if (res != CR_SUCCESS) - return null; - - res = CM_Get_First_Log_Conf(out var logConf, devInst, ALLOC_LOG_CONF); - if (res != CR_SUCCESS) - res = CM_Get_First_Log_Conf(out logConf, devInst, BOOT_LOG_CONF); - if (res != CR_SUCCESS) - return null; - - var ranges = new List<Tuple<UIntPtr, UIntPtr>>(); - - while (CM_Get_Next_Res_Des(out var newResDes, logConf, ResType_Mem, out _, 0) == 0) - { - CM_Free_Res_Des_Handle(logConf); - logConf = newResDes; - - if (!CM_Get_Res_Des_Data<MEM_RESOURCE>(logConf, out var memResource, 0)) - continue; - - ranges.Add(new Tuple<UIntPtr, UIntPtr>( - memResource.MEM_Header.MD_Alloc_Base, memResource.MEM_Header.MD_Alloc_End)); - } - - CM_Free_Res_Des_Handle(logConf); - return ranges; - } - - static bool CM_Get_DevNode_Property(IntPtr devInst, DEVPROPKEY propertyKey, out string result, int flags) - { - result = default; - - // int length = 0; - // int res = CM_Get_DevNode_Property(devInst, ref propertyKey, out var propertyType, null, ref length, flags); - // if (res != CR_SUCCESS && res != CR_BUFFER_TOO_SMALL) - // return false; - - char[] buffer = new char[2048]; - int length = buffer.Length; - int res = CM_Get_DevNode_Property(devInst, ref propertyKey, out var propertyType, buffer, ref length, flags); - if (res != CR_SUCCESS) - return false; - if (propertyType != DEVPROP_TYPE_STRING) - return false; - - result = new String(buffer, 0, length).Split('\0').First(); - return true; - } - - static bool CM_Get_Res_Des_Data<T>(IntPtr rdResDes, out T buffer, int ulFlags) where T : struct - { - buffer = default; - - int res = CM_Get_Res_Des_Data_Size(out var size, rdResDes, ulFlags); - if (res != CR_SUCCESS) - return false; - - int sizeOf = Marshal.SizeOf<T>(); - if (sizeOf < size) - return false; - - var addr = Marshal.AllocHGlobal(sizeOf); - try - { - res = CM_Get_Res_Des_Data(rdResDes, addr, size, 0); - if (res != CR_SUCCESS) - return false; - - buffer = Marshal.PtrToStructure<T>(addr); - return true; - } - finally - { - Marshal.FreeHGlobal(addr); - } - } - - #region struct - - [StructLayout(LayoutKind.Sequential)] - struct MEM_DES - { - internal uint MD_Count; - internal uint MD_Type; - internal UIntPtr MD_Alloc_Base; - internal UIntPtr MD_Alloc_End; - internal uint MD_Flags; - internal uint MD_Reserved; - }; - - [StructLayout(LayoutKind.Sequential, Pack = 4)] - struct MEM_RANGE - { - internal UIntPtr MR_Align; // specifies mask for base alignment - internal uint MR_nBytes; // specifies number of bytes required - internal UIntPtr MR_Min; // specifies minimum address of the range - internal UIntPtr MR_Max; // specifies maximum address of the range - internal uint MR_Flags; // specifies flags describing range (fMD flags) - internal uint MR_Reserved; - }; - - [StructLayout(LayoutKind.Sequential)] - struct MEM_RESOURCE - { - internal MEM_DES MEM_Header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - internal MEM_RANGE[] MEM_Data; - }; - - [StructLayout(LayoutKind.Sequential)] - struct DEVPROPKEY - { - public Guid Guid; - public uint Pid; - - public DEVPROPKEY(String guid, uint pid) - { - this.Guid = new Guid(guid); - this.Pid = pid; - } - }; - - const int ALLOC_LOG_CONF = 0x00000002; // Specifies the Alloc Element. - const int BOOT_LOG_CONF = 0x00000003; // Specifies the RM Alloc Element. - const int ResType_Mem = (0x00000001); // Physical address resource - - const int CM_GETIDLIST_FILTER_PRESENT = 0x00000100; - const int CM_GETIDLIST_FILTER_CLASS = 0x00000200; - const int CR_SUCCESS = 0x0; - const int CR_BUFFER_TOO_SMALL = 0x1A; - - const int DEVPROP_TYPE_STRING = 0x00000012; - - static readonly DEVPROPKEY DEVPKEY_Device_DeviceDesc = new DEVPROPKEY("a45c254e-df1c-4efd-8020-67d146a850e0", 2); - - internal static readonly Guid GUID_DISPLAY = new Guid("{4d36e968-e325-11ce-bfc1-08002be10318}"); - - #endregion - - #region import - - [DllImport("hid.dll", EntryPoint = "HidD_GetHidGuid")] - internal static extern void HidD_GetHidGuidMethod(out Guid hidGuid); - - [DllImport("hid", CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.U1)] - private static extern bool HidD_GetManufacturerString(IntPtr HidDeviceObject, [Out] byte[] Buffer, - uint BufferLength); - - [DllImport("hid", CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.U1)] - private static extern bool HidD_GetProductString(IntPtr HidDeviceObject, [Out] byte[] Buffer, uint BufferLength); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern SafeFileHandle CreateFileW( - [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, - ulong dwDesiredAccess, - ulong dwShareMode, - IntPtr lpSecurityAttributes, - uint dwCreationDisposition, - uint dwFlagsAndAttributes, - IntPtr hTemplateFile); - - [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - private static extern bool DeviceIoControl( - SafeFileHandle hDevice, - ulong ioControlCode, - byte[] inBuffer, - int nInBufferSize, - byte[] outBuffer, - int nOutBufferSize, - ref uint pBytesReturned, - IntPtr overlapped); - - [DllImport("setupapi.dll", CharSet = CharSet.Auto)] - static extern int CM_Locate_DevNode(out IntPtr pdnDevInst, string pDeviceID, int ulFlags); - - [DllImport("setupapi.dll", CharSet = CharSet.Unicode)] - static extern int CM_Get_Device_ID_List_Size(out int idListlen, string? filter, int ulFlags); - - [DllImport("setupapi.dll", CharSet = CharSet.Unicode)] - static extern int CM_Get_Device_ID_List(string? filter, char[] bffr, int bffrLen, int ulFlags); - - [DllImport("CfgMgr32.dll", CharSet = CharSet.Unicode)] - static extern int CM_Get_DevNode_Property(IntPtr devInst, ref DEVPROPKEY propertyKey, out int propertyType, char[]? bffr, ref int bffrLen, int flags); - - [DllImport("setupapi.dll")] - static extern int CM_Free_Res_Des_Handle(IntPtr rdResDes); - - [DllImport("setupapi.dll")] - static extern int CM_Get_First_Log_Conf(out IntPtr rdResDes, IntPtr pdnDevInst, int ulFlags); - - [DllImport("setupapi.dll")] - static extern int CM_Get_Next_Res_Des(out IntPtr newResDes, IntPtr rdResDes, int resType, out int resourceID, int ulFlags); - - [DllImport("setupapi.dll")] - static extern int CM_Get_Res_Des_Data_Size(out int size, IntPtr rdResDes, int ulFlags); - - [DllImport("setupapi.dll")] - static extern int CM_Get_Res_Des_Data(IntPtr rdResDes, IntPtr buffer, int size, int ulFlags); - - #endregion - - #region events - - public static event XInputDeviceArrivedEventHandler XUsbDeviceArrived; - public delegate void XInputDeviceArrivedEventHandler(PnPDetails device, DeviceEventArgs obj); - - public static event XInputDeviceRemovedEventHandler XUsbDeviceRemoved; - public delegate void XInputDeviceRemovedEventHandler(PnPDetails device, DeviceEventArgs obj); - - public static event GenericDeviceArrivedEventHandler UsbDeviceArrived; - public delegate void GenericDeviceArrivedEventHandler(PnPDevice device, DeviceEventArgs obj); - - public static event GenericDeviceRemovedEventHandler UsbDeviceRemoved; - public delegate void GenericDeviceRemovedEventHandler(PnPDevice device, DeviceEventArgs obj); - - public static event DInputDeviceArrivedEventHandler HidDeviceArrived; - public delegate void DInputDeviceArrivedEventHandler(PnPDetails device, DeviceEventArgs obj); - - public static event DInputDeviceRemovedEventHandler HidDeviceRemoved; - public delegate void DInputDeviceRemovedEventHandler(PnPDetails device, DeviceEventArgs obj); - - public static event InitializedEventHandler Initialized; - public delegate void InitializedEventHandler(); - - #endregion +using HandheldCompanion.Managers.Hid; +using HandheldCompanion.Sensors; +using HandheldCompanion.Utils; +using Microsoft.Win32.SafeHandles; +using Nefarius.Utilities.DeviceManagement.PnP; +using PInvoke; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +namespace HandheldCompanion.Managers; + +public static class DeviceManager +{ + public static Guid HidDevice; + private static readonly DeviceNotificationListener UsbDeviceListener = new(); + private static readonly DeviceNotificationListener XUsbDeviceListener = new(); + private static readonly DeviceNotificationListener HidDeviceListener = new(); + + private static readonly ConcurrentDictionary<string, PnPDetails> PnPDevices = new(); + + const ulong GENERIC_READ = (0x80000000L); + const ulong GENERIC_WRITE = (0x40000000L); + const ulong GENERIC_EXECUTE = (0x20000000L); + const ulong GENERIC_ALL = (0x10000000L); + + const uint FILE_SHARE_READ = 0x00000001; + const uint FILE_SHARE_WRITE = 0x00000002; + const uint FILE_SHARE_DELETE = 0x00000004; + + const uint CREATE_NEW = 1; + const uint CREATE_ALWAYS = 2; + const uint OPEN_EXISTING = 3; + const uint OPEN_ALWAYS = 4; + const uint TRUNCATE_EXISTING = 5; + + const ulong IOCTL_XUSB_GET_LED_STATE = 0x8000E008; + + static byte[] XINPUT_LED_TO_PORT_MAP = new byte[16] + { + 255, // All off + 255, // All blinking, then previous setting + 0, // 1 flashes, then on + 1, // 2 flashes, then on + 2, // 3 flashes, then on + 3, // 4 flashes, then on + 0, // 1 on + 1, // 2 on + 2, // 3 on + 3, // 4 on + 255, // Rotate + 255, // Blink, based on previous setting + 255, // Slow blink, based on previous setting + 255, // Rotate with two lights + 255, // Persistent slow all blink + 255, // Blink once, then previous setting + }; + + public static bool IsInitialized; + + static DeviceManager() + { + // initialize hid + HidD_GetHidGuidMethod(out HidDevice); + } + + public static void Start() + { +<<<<<<< HEAD + // fail-safe + PnPUtil.StartPnPUtil(@"/add-driver C:\Windows\INF\xusb22.inf /install"); + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + UsbDeviceListener.StartListen(DeviceInterfaceIds.UsbDevice); + UsbDeviceListener.DeviceArrived += UsbDevice_DeviceArrived; + UsbDeviceListener.DeviceRemoved += UsbDevice_DeviceRemoved; + + XUsbDeviceListener.StartListen(DeviceInterfaceIds.XUsbDevice); + XUsbDeviceListener.DeviceArrived += XUsbDevice_DeviceArrived; + XUsbDeviceListener.DeviceRemoved += XUsbDevice_DeviceRemoved; + + HidDeviceListener.StartListen(DeviceInterfaceIds.HidDevice); + HidDeviceListener.DeviceArrived += HidDevice_DeviceArrived; + HidDeviceListener.DeviceRemoved += HidDevice_DeviceRemoved; + +<<<<<<< HEAD + RefreshXInput(); + RefreshDInput(); +======= + Refresh(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + IsInitialized = true; + Initialized?.Invoke(); + + LogManager.LogInformation("{0} has started", "DeviceManager"); + } + + public static void Stop() + { + if (!IsInitialized) + return; + + IsInitialized = false; + + UsbDeviceListener.StopListen(DeviceInterfaceIds.UsbDevice); + UsbDeviceListener.DeviceArrived -= UsbDevice_DeviceArrived; + UsbDeviceListener.DeviceRemoved -= UsbDevice_DeviceRemoved; + + XUsbDeviceListener.StopListen(DeviceInterfaceIds.XUsbDevice); + XUsbDeviceListener.DeviceArrived -= XUsbDevice_DeviceArrived; + XUsbDeviceListener.DeviceRemoved -= XUsbDevice_DeviceRemoved; + + HidDeviceListener.StopListen(DeviceInterfaceIds.HidDevice); + HidDeviceListener.DeviceArrived -= HidDevice_DeviceArrived; + HidDeviceListener.DeviceRemoved -= HidDevice_DeviceRemoved; + +<<<<<<< HEAD + // fail-safe + PnPUtil.StartPnPUtil(@"/add-driver C:\Windows\INF\xusb22.inf /install"); + + LogManager.LogInformation("{0} has stopped", "DeviceManager"); + } + +======= + LogManager.LogInformation("{0} has stopped", "DeviceManager"); + } + + public static void Refresh() + { + RefreshHID(); + RefreshXInput(); + RefreshDInput(); + } + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + private static void RefreshXInput() + { + var deviceIndex = 0; + Dictionary<string, DateTimeOffset> devices = new(); + + while (Devcon.FindByInterfaceGuid(DeviceInterfaceIds.XUsbDevice, out var path, out var instanceId, + deviceIndex++)) + { + var device = PnPDevice.GetDeviceByInterfaceId(path); + var arrival = device.GetProperty<DateTimeOffset>(DevicePropertyKey.Device_LastArrivalDate); + + // add new device + devices.Add(path, arrival); + } + + // sort devices list + devices = devices.OrderBy(device => device.Value).ToDictionary(x => x.Key, x => x.Value); + foreach (var pair in devices) + XUsbDevice_DeviceArrived(new DeviceEventArgs + { InterfaceGuid = DeviceInterfaceIds.XUsbDevice, SymLink = pair.Key }); + } + + private static void RefreshDInput() + { + var deviceIndex = 0; + Dictionary<string, DateTimeOffset> devices = new(); + + while (Devcon.FindByInterfaceGuid(DeviceInterfaceIds.HidDevice, out var path, out var instanceId, + deviceIndex++)) + { + var device = PnPDevice.GetDeviceByInterfaceId(path); + var arrival = device.GetProperty<DateTimeOffset>(DevicePropertyKey.Device_LastArrivalDate); + + // add new device + devices.Add(path, arrival); + } + + // sort devices list + devices = devices.OrderBy(device => device.Value).ToDictionary(x => x.Key, x => x.Value); + foreach (var pair in devices) + HidDevice_DeviceArrived(new DeviceEventArgs + { InterfaceGuid = DeviceInterfaceIds.HidDevice, SymLink = pair.Key }); + } + +<<<<<<< HEAD + private static PnPDetails FindDevice(string InstanceId) + { + if (InstanceId.StartsWith(@"USB\")) + return FindDeviceFromUSB(InstanceId); + if (InstanceId.StartsWith(@"HID\")) + return FindDeviceFromHID(InstanceId); + return null; + } + + public static PnPDetails FindDeviceFromUSB(string InstanceId) + { + PnPDetails details = null; + while(details is null) + { + details = PnPDevices.Values.FirstOrDefault(device => device.baseContainerDeviceInstanceId.Equals(InstanceId, StringComparison.InvariantCultureIgnoreCase)); + + if (details is not null) + return details; + + // look for parent + try + { + PnPDevice pnPDevice = PnPDevice.GetDeviceByInstanceId(InstanceId); + if (pnPDevice.Parent is null) + break; + + InstanceId = pnPDevice.Parent.InstanceId; + } + catch + { + break; +======= + private static PnPDetails FindDevice(string SymLink, bool Removed = false) + { + if (SymLink.StartsWith(@"USB\")) + return FindDeviceFromUSB(SymLink, Removed); + if (SymLink.StartsWith(@"HID\")) + return FindDeviceFromHID(SymLink, Removed); + return null; + } + + public static PnPDetails FindDeviceFromUSB(string SymLink, bool Removed) + { + var details = PnPDevices.Values + .FirstOrDefault(device => device.baseContainerDeviceInstanceId.Equals(SymLink, StringComparison.InvariantCultureIgnoreCase)); + + // backup plan + if (details is null) + { + var deviceIndex = 0; + while (Devcon.FindByInterfaceGuid(DeviceInterfaceIds.UsbDevice, out var path, out var instanceId, + deviceIndex++)) + { + var parent = PnPDevice.GetDeviceByInterfaceId(path); + + path = PathToInstanceId(path, DeviceInterfaceIds.UsbDevice.ToString()); + if (path == SymLink) + { + details = PnPDevices.Values.FirstOrDefault(device => device.baseContainerDeviceInstanceId.Equals(parent.InstanceId, + StringComparison.InvariantCultureIgnoreCase)); + break; + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + } + + return details; + } + +<<<<<<< HEAD + public static PnPDetails FindDeviceFromHID(string InstanceId) + { + PnPDevices.TryGetValue(InstanceId, out var device); + return device; + } + + private static int GetDeviceIndex(string path) + { + int deviceIndex = 0; + + while (Devcon.FindByInterfaceGuid(DeviceInterfaceIds.HidDevice, out var symlink, out var instanceId, deviceIndex++)) + if (symlink == path) + return deviceIndex; + + return 0; + } + + private static PnPDetails GetDetails(string path) + { + try + { + string SymLink = SymLinkToInstanceId(path, DeviceInterfaceIds.HidDevice.ToString()); + + PnPDevice children = PnPDevice.GetDeviceByInterfaceId(path); + + // get attributes + Attributes? attributes = GetHidAttributes(path); + Capabilities? capabilities = GetHidCapabilities(path); + + if (attributes is null || capabilities is null) + return null; + + string ProductID = ((Attributes)attributes).ProductID.ToString("X4"); + string VendorID = ((Attributes)attributes).VendorID.ToString("X4"); + + IPnPDevice? parent = children; + string parentId = parent.InstanceId; + + while (parent.Parent is not null) + { + // update parent InstanceId + parentId = parent.Parent.InstanceId; +======= + public static PnPDetails FindDeviceFromHID(string SymLink, bool Removed) + { + PnPDevices.TryGetValue(SymLink, out var device); + return device; + } + + private static void RefreshHID() + { + var deviceIndex = 0; + while (Devcon.FindByInterfaceGuid(DeviceInterfaceIds.HidDevice, out var path, out var instanceId, + deviceIndex++)) + { + var children = PnPDevice.GetDeviceByInterfaceId(path); + + var parent = children; + var parentId = string.Empty; + + // get attributes + var attributes = GetHidAttributes(path); + var capabilities = GetHidCapabilities(path); + + if (attributes is null || capabilities is null) + continue; + + var ProductID = ((Attributes)attributes).ProductID.ToString("X4"); + var VendorID = ((Attributes)attributes).VendorID.ToString("X4"); + var FriendlyName = string.Empty; + + while (parent is not null) + { + if (string.IsNullOrEmpty(FriendlyName)) + FriendlyName = parent.GetProperty<string>(DevicePropertyKey.Device_FriendlyName); + + parentId = parent.GetProperty<string>(DevicePropertyKey.Device_Parent); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + if (parentId.Equals(@"HTREE\ROOT\0", StringComparison.InvariantCultureIgnoreCase)) + break; + + if (parentId.Contains(@"USB\ROOT", StringComparison.InvariantCultureIgnoreCase)) + break; + + if (parentId.Contains(@"ROOT\SYSTEM", StringComparison.InvariantCultureIgnoreCase)) + break; + + if (parentId.Contains(@"HID\", StringComparison.InvariantCultureIgnoreCase)) + break; + + if (!parentId.Contains(ProductID, StringComparison.InvariantCultureIgnoreCase)) + break; + + if (!parentId.Contains(VendorID, StringComparison.InvariantCultureIgnoreCase)) + break; + +<<<<<<< HEAD + // we want the closest parent + PnPDevice check = PnPDevice.GetDeviceByInstanceId(parentId); + if (check.Children is not null) + { + if (check.Children.Count() > 1) + break; + } + + // update parent + parent = check; +======= + parent = PnPDevice.GetDeviceByInstanceId(parentId); + } + + if (string.IsNullOrEmpty(FriendlyName)) + { + var product = GetProductString(path); + var vendor = GetManufacturerString(path); + + FriendlyName = string.Join(' ', vendor, product).Trim(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + // get details + PnPDetails details = new PnPDetails + { +<<<<<<< HEAD + devicePath = path, + SymLink = SymLink, + Name = parent.GetProperty<string>(DevicePropertyKey.Device_DeviceDesc), + Enumerator = parent.GetProperty<string>(DevicePropertyKey.Device_EnumeratorName), + deviceInstanceId = children.InstanceId.ToUpper(), + baseContainerDeviceInstanceId = parent.InstanceId.ToUpper(), + isVirtual = parent.IsVirtual() || children.IsVirtual(), + isGaming = IsGaming((Attributes)attributes, (Capabilities)capabilities), + ProductID = ((Attributes)attributes).ProductID, + VendorID = ((Attributes)attributes).VendorID, + isXInput = children.InstanceId.Contains("IG_", StringComparison.InvariantCultureIgnoreCase), + }; + + // get name + string DeviceDesc = parent.GetProperty<string>(DevicePropertyKey.Device_DeviceDesc); + string FriendlyName = parent.GetProperty<string>(DevicePropertyKey.Device_FriendlyName); + + if (!string.IsNullOrEmpty(FriendlyName)) + details.Name = FriendlyName; + else if (!string.IsNullOrEmpty(DeviceDesc)) + details.Name = DeviceDesc; + + // add or update device + PnPDevices[details.SymLink] = details; + + return details; + } + catch { } + + return null; +======= + Path = path, + SymLink = PathToInstanceId(path, DeviceInterfaceIds.HidDevice.ToString()), + + deviceInstanceId = children.InstanceId, + baseContainerDeviceInstanceId = parent.InstanceId, + + Name = FriendlyName, + + isVirtual = parent.IsVirtual() || children.IsVirtual(), + isGaming = IsGaming((Attributes)attributes, (Capabilities)capabilities), + + arrivalDate = children.GetProperty<DateTimeOffset>(DevicePropertyKey.Device_LastArrivalDate), + + attributes = (Attributes)attributes, + capabilities = (Capabilities)capabilities, + + DeviceIdx = deviceIndex + }; + + // add or update device + if (!PnPDevices.ContainsKey(details.SymLink)) + PnPDevices.TryAdd(details.SymLink, details); + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public static List<PnPDetails> GetDetails(ushort VendorId = 0, ushort ProductId = 0) + { +<<<<<<< HEAD + return PnPDevices.Values.OrderBy(device => device.XInputDeviceIdx).Where(device => + device.VendorID == VendorId && device.ProductID == ProductId && !device.isHooked).ToList(); +======= + return PnPDevices.Values.OrderBy(a => a.DeviceIdx).Where(a => + a.attributes.VendorID == VendorId && a.attributes.ProductID == ProductId && !a.isHooked).ToList(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public static PnPDetails GetDeviceByInterfaceId(string path) + { + var device = PnPDevice.GetDeviceByInterfaceId(path); + if (device is null) + return null; + + return new PnPDetails + { +<<<<<<< HEAD + devicePath = path, + SymLink = SymLinkToInstanceId(path, DeviceInterfaceIds.UsbDevice.ToString()), +======= + Path = path, + SymLink = PathToInstanceId(path, DeviceInterfaceIds.UsbDevice.ToString()), +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + deviceInstanceId = device.InstanceId, + baseContainerDeviceInstanceId = device.InstanceId + }; + } + + public static string GetManufacturerString(string path) + { + using var handle = Kernel32.CreateFile(path, + Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | + Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, + Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, + IntPtr.Zero, Kernel32.CreationDisposition.OPEN_EXISTING, + Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL + | Kernel32.CreateFileFlags.FILE_FLAG_NO_BUFFERING + | Kernel32.CreateFileFlags.FILE_FLAG_WRITE_THROUGH, + Kernel32.SafeObjectHandle.Null + ); + + return GetString(handle.DangerousGetHandle(), HidD_GetManufacturerString); + } + + public static string GetProductString(string path) + { + using var handle = Kernel32.CreateFile(path, + Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | + Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, + Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, + IntPtr.Zero, Kernel32.CreationDisposition.OPEN_EXISTING, + Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL + | Kernel32.CreateFileFlags.FILE_FLAG_NO_BUFFERING + | Kernel32.CreateFileFlags.FILE_FLAG_WRITE_THROUGH, + Kernel32.SafeObjectHandle.Null + ); + + return GetString(handle.DangerousGetHandle(), HidD_GetProductString); + } + + private static string GetString(IntPtr handle, Func<IntPtr, byte[], uint, bool> proc) + { + var buf = new byte[256]; + + if (!proc(handle, buf, (uint)buf.Length)) + return null; + + var str = Encoding.Unicode.GetString(buf, 0, buf.Length); + + return str.Contains("\0") ? str.Substring(0, str.IndexOf('\0')) : str; + } + + private static Attributes? GetHidAttributes(string path) + { + using var handle = Kernel32.CreateFile(path, +<<<<<<< HEAD + Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, + Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, + IntPtr.Zero, + Kernel32.CreationDisposition.OPEN_EXISTING, + Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL, +======= + Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | + Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, + Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, + IntPtr.Zero, Kernel32.CreationDisposition.OPEN_EXISTING, + Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL + | Kernel32.CreateFileFlags.FILE_FLAG_NO_BUFFERING + | Kernel32.CreateFileFlags.FILE_FLAG_WRITE_THROUGH, +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Kernel32.SafeObjectHandle.Null + ); + + return GetAttributes.Get(handle.DangerousGetHandle()); + } + + private static Capabilities? GetHidCapabilities(string path) + { + using var handle = Kernel32.CreateFile(path, +<<<<<<< HEAD + Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, + Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, + IntPtr.Zero, + Kernel32.CreationDisposition.OPEN_EXISTING, + Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL, +======= + Kernel32.ACCESS_MASK.GenericRight.GENERIC_READ | + Kernel32.ACCESS_MASK.GenericRight.GENERIC_WRITE, + Kernel32.FileShare.FILE_SHARE_READ | Kernel32.FileShare.FILE_SHARE_WRITE, + IntPtr.Zero, Kernel32.CreationDisposition.OPEN_EXISTING, + Kernel32.CreateFileFlags.FILE_ATTRIBUTE_NORMAL + | Kernel32.CreateFileFlags.FILE_FLAG_NO_BUFFERING + | Kernel32.CreateFileFlags.FILE_FLAG_WRITE_THROUGH, +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Kernel32.SafeObjectHandle.Null + ); + + return GetCapabilities.Get(handle.DangerousGetHandle()); + } + + private static bool IsGaming(Attributes attributes, Capabilities capabilities) + { + return ( + ((attributes.VendorID == 0x28DE) && (attributes.ProductID == 0x1102)) || // STEAM CONTROLLER + // ((attributes.VendorID == 0x28DE) && (attributes.ProductID == 0x1106)) || // STEAM CONTROLLER BLUETOOTH + ((attributes.VendorID == 0x28DE) && (attributes.ProductID == 0x1142)) || // STEAM CONTROLLER WIRELESS + ((attributes.VendorID == 0x28DE) && (attributes.ProductID == 0x1205)) || // STEAM DECK + (0x05 == capabilities.UsagePage) || (0x01 == capabilities.UsagePage) && ((0x04 == capabilities.Usage) || (0x05 == capabilities.Usage))); + } + + public static PnPDetails GetPnPDeviceEx(string SymLink) + { +<<<<<<< HEAD + PnPDevices.TryGetValue(SymLink, out var details); + return details; + } + + public static string SymLinkToInstanceId(string SymLink, string InterfaceGuid) + { + string InstanceId = SymLink.ToUpper().Replace(InterfaceGuid, "", StringComparison.InvariantCultureIgnoreCase); + InstanceId = InstanceId.Replace("#", @"\"); + InstanceId = InstanceId.Replace(@"\\?\", ""); + InstanceId = InstanceId.Replace(@"\{}", ""); + return InstanceId; + } + + private static void XUsbDevice_DeviceRemoved(DeviceEventArgs obj) + { + try + { + Task.Run(async () => + { + string InstanceId = SymLinkToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); + + PnPDetails deviceEx = null; + DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(8)); + while (DateTime.Now < timeout && deviceEx is null) + { + deviceEx = FindDevice(InstanceId); + await Task.Delay(100); + } + + if (deviceEx is null) + return; + + if (PnPDevices.TryRemove(deviceEx.SymLink, out var value)) + { + LogManager.LogDebug("XUsbDevice {1} removed from slot {2}: {0}", deviceEx.Name, deviceEx.isVirtual ? "virtual" : "physical", deviceEx.XInputUserIndex); + XUsbDeviceRemoved?.Invoke(deviceEx, obj); + } + }); + } + catch { } + } + + private static void XUsbDevice_DeviceArrived(DeviceEventArgs obj) + { + try + { + Task.Run(async () => + { + string InstanceId = SymLinkToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); + + PnPDetails deviceEx = null; + + DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(8)); + while (DateTime.Now < timeout && deviceEx is null) + { + deviceEx = FindDevice(InstanceId); + await Task.Delay(100); + } + + if (deviceEx is not null && deviceEx.isGaming) + { + deviceEx.isXInput = true; + deviceEx.baseContainerDevicePath = obj.SymLink; + + if (deviceEx.Enumerator.Equals("USB")) + deviceEx.XInputUserIndex = GetXInputIndexAsync(obj.SymLink); + + if (deviceEx.XInputUserIndex == byte.MaxValue) + deviceEx.XInputDeviceIdx = GetDeviceIndex(obj.SymLink); + + LogManager.LogDebug("XUsbDevice {4} arrived on slot {5}: {0} (VID:{1}, PID:{2}) {3}", deviceEx.Name, + deviceEx.GetVendorID(), deviceEx.GetProductID(), deviceEx.deviceInstanceId, deviceEx.isVirtual ? "virtual" : "physical", deviceEx.XInputUserIndex); +======= + if (PnPDevices.TryGetValue(SymLink, out var details)) + return details; + + return null; + } + + public static string PathToInstanceId(string SymLink, string InterfaceGuid) + { + var output = SymLink.ToUpper().Replace(InterfaceGuid, "", StringComparison.InvariantCultureIgnoreCase); + output = output.Replace("#", @"\"); + output = output.Replace(@"\\?\", ""); + output = output.Replace(@"\{}", ""); + return output; + } + + private static async void XUsbDevice_DeviceRemoved(DeviceEventArgs obj) + { + var SymLink = PathToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); + + var deviceEx = FindDevice(SymLink, true); + if (deviceEx is null) + return; + + // give system at least one second to initialize device + await Task.Delay(1000); + if (PnPDevices.TryRemove(deviceEx.SymLink, out var value)) + { + // RefreshHID(); + LogManager.LogDebug("XUsbDevice {1} removed: {0}", deviceEx.Name, deviceEx.isVirtual ? "virtual" : "physical"); + + // raise event + XUsbDeviceRemoved?.Invoke(deviceEx, obj); + } + } + + private static async void XUsbDevice_DeviceArrived(DeviceEventArgs obj) + { + try + { + var SymLink = PathToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); + + if (IsInitialized) + { + // give system at least one second to initialize device + await Task.Delay(1000); + RefreshHID(); + } + + var deviceEx = FindDevice(SymLink); + if (deviceEx is not null && deviceEx.isGaming) + { + deviceEx.isXInput = true; + + using (SafeFileHandle handle = CreateFileW(obj.SymLink, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)) + { + if (handle.IsInvalid) + goto Event; + + byte[] gamepadStateRequest0101 = new byte[3] { 0x01, 0x01, 0x00 }; + byte[] ledStateData = new byte[3]; + uint len = 0; + + if (!DeviceIoControl(handle, IOCTL_XUSB_GET_LED_STATE, gamepadStateRequest0101, gamepadStateRequest0101.Length, ledStateData, ledStateData.Length, ref len, 0)) + goto Event; + + byte ledState = ledStateData[2]; + deviceEx.XInputUserIndex = XINPUT_LED_TO_PORT_MAP[ledState]; + } + + LogManager.LogDebug("XUsbDevice {4} arrived on slot {5}: {0} (VID:{1}, PID:{2}) {3}", deviceEx.Name, + deviceEx.GetVendorID(), deviceEx.GetProductID(), deviceEx.deviceInstanceId, deviceEx.isVirtual ? "virtual" : "physical", deviceEx.XInputUserIndex); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // raise event + Event: + XUsbDeviceArrived?.Invoke(deviceEx, obj); +<<<<<<< HEAD + } + }); +======= + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + catch + { + } + } + +<<<<<<< HEAD + private static void HidDevice_DeviceRemoved(DeviceEventArgs obj) + { + try + { + Task.Run(async () => + { + string InstanceId = SymLinkToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); + + PnPDetails deviceEx = null; + DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(8)); + while (DateTime.Now < timeout && deviceEx is null) + { + deviceEx = FindDevice(InstanceId); + await Task.Delay(100); + } + + // skip if XInput + if (deviceEx is null || deviceEx.isXInput) + return; + + if (PnPDevices.TryRemove(deviceEx.SymLink, out var value)) + { + LogManager.LogDebug("HidDevice removed: {0}", deviceEx.Name); + HidDeviceRemoved?.Invoke(deviceEx, obj); + } + }); +======= + private static async void HidDevice_DeviceRemoved(DeviceEventArgs obj) + { + try + { + var SymLink = PathToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); + + var deviceEx = FindDevice(SymLink, true); + if (deviceEx is null) + return; + + // give system at least one second to initialize device (+500 ms to give XInput priority) + await Task.Delay(1500); + PnPDevices.TryRemove(deviceEx.SymLink, out var value); + + // RefreshHID(); + LogManager.LogDebug("HidDevice removed: {0}", deviceEx.Name); + HidDeviceRemoved?.Invoke(deviceEx, obj); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + catch + { + } + } + +<<<<<<< HEAD + private static void HidDevice_DeviceArrived(DeviceEventArgs obj) + { + try + { + Task.Run(async () => + { + string InstanceId = SymLinkToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); + PnPDetails deviceEx = null; + + DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(8)); + while (DateTime.Now < timeout && deviceEx is null) + { + deviceEx = GetDetails(obj.SymLink); + await Task.Delay(100); + } + + // skip if XInput + if (deviceEx is null || deviceEx.isXInput) + return; + + LogManager.LogDebug("HidDevice arrived: {0} (VID:{1}, PID:{2}) {3}", deviceEx.Name, deviceEx.GetVendorID(), + deviceEx.GetProductID(), deviceEx.deviceInstanceId); + HidDeviceArrived?.Invoke(deviceEx, obj); + }); + } + catch { } +======= + private static async void HidDevice_DeviceArrived(DeviceEventArgs obj) + { + var SymLink = PathToInstanceId(obj.SymLink, obj.InterfaceGuid.ToString()); + + if (IsInitialized) + { + // give system at least one second to initialize device (+500 ms to give XInput priority) + await Task.Delay(1500); + RefreshHID(); + } + + var deviceEx = FindDevice(SymLink); + if (deviceEx is not null && !deviceEx.isXInput) + { + LogManager.LogDebug("HidDevice arrived: {0} (VID:{1}, PID:{2}) {3}", deviceEx.Name, deviceEx.GetVendorID(), + deviceEx.GetProductID(), deviceEx.deviceInstanceId); + HidDeviceArrived?.Invoke(deviceEx, obj); + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private static void UsbDevice_DeviceRemoved(DeviceEventArgs obj) + { + try + { + var symLink = CommonUtils.Between(obj.SymLink, "#", "#") + "&"; + var VendorID = CommonUtils.Between(symLink, "VID_", "&"); + var ProductID = CommonUtils.Between(symLink, "PID_", "&"); + + if (SerialUSBIMU.vendors.ContainsKey(new KeyValuePair<string, string>(VendorID, ProductID))) + UsbDeviceRemoved?.Invoke(null, obj); + } + catch + { + } + } + + private static void UsbDevice_DeviceArrived(DeviceEventArgs obj) + { + try + { + var symLink = CommonUtils.Between(obj.SymLink, "#", "#") + "&"; + var VendorID = CommonUtils.Between(symLink, "VID_", "&"); + var ProductID = CommonUtils.Between(symLink, "PID_", "&"); + + if (SerialUSBIMU.vendors.ContainsKey(new KeyValuePair<string, string>(VendorID, ProductID))) + UsbDeviceArrived?.Invoke(null, obj); + } + catch + { + } + } + +<<<<<<< HEAD + public static byte GetXInputIndexAsync(string SymLink) + { + byte ledState = 0; + + DateTime timeout = DateTime.Now.Add(TimeSpan.FromSeconds(4)); + while (DateTime.Now < timeout && (ledState < 2 || ledState > 9)) + { + using (SafeFileHandle handle = CreateFileW(SymLink, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)) + { + if (handle.IsInvalid) + return byte.MaxValue; + + byte[] gamepadStateRequest0101 = new byte[3] { 0x01, 0x01, 0x00 }; + byte[] ledStateData = new byte[3]; + uint len = 0; + + if (!DeviceIoControl(handle, IOCTL_XUSB_GET_LED_STATE, gamepadStateRequest0101, gamepadStateRequest0101.Length, ledStateData, ledStateData.Length, ref len, 0)) + return byte.MaxValue; + + ledState = ledStateData[2]; + } + + Task.Delay(1000); + } + + return XINPUT_LED_TO_PORT_MAP[ledState]; + } + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public static string[]? GetDevices(Guid? classGuid) + { + string? filter = null; + int flags = CM_GETIDLIST_FILTER_PRESENT; + + if (classGuid is not null) + { + filter = classGuid?.ToString("B").ToUpper(); + flags |= CM_GETIDLIST_FILTER_CLASS; + } + + var res = CM_Get_Device_ID_List_Size(out var size, filter, flags); + if (res != CR_SUCCESS) + return null; + + char[] data = new char[size]; + res = CM_Get_Device_ID_List(filter, data, size, flags); + if (res != CR_SUCCESS) + return null; + + var result = new string(data); + var devices = result.Split('\0', StringSplitOptions.RemoveEmptyEntries); + return devices.ToArray(); + } + + public static string? GetDeviceDesc(String PNPString) + { + if (CM_Locate_DevNode(out var devInst, PNPString, 0) != 0) + return null; + + if (!CM_Get_DevNode_Property(devInst, DEVPKEY_Device_DeviceDesc, out var deviceDesc, 0)) + return null; + + return deviceDesc; + } + + public static IList<Tuple<UIntPtr, UIntPtr>>? GetDeviceMemResources(string PNPString) + { + int res = CM_Locate_DevNode(out var devInst, PNPString, 0); + if (res != CR_SUCCESS) + return null; + + res = CM_Get_First_Log_Conf(out var logConf, devInst, ALLOC_LOG_CONF); + if (res != CR_SUCCESS) + res = CM_Get_First_Log_Conf(out logConf, devInst, BOOT_LOG_CONF); + if (res != CR_SUCCESS) + return null; + + var ranges = new List<Tuple<UIntPtr, UIntPtr>>(); + + while (CM_Get_Next_Res_Des(out var newResDes, logConf, ResType_Mem, out _, 0) == 0) + { + CM_Free_Res_Des_Handle(logConf); + logConf = newResDes; + + if (!CM_Get_Res_Des_Data<MEM_RESOURCE>(logConf, out var memResource, 0)) + continue; + + ranges.Add(new Tuple<UIntPtr, UIntPtr>( + memResource.MEM_Header.MD_Alloc_Base, memResource.MEM_Header.MD_Alloc_End)); + } + + CM_Free_Res_Des_Handle(logConf); + return ranges; + } + + static bool CM_Get_DevNode_Property(IntPtr devInst, DEVPROPKEY propertyKey, out string result, int flags) + { + result = default; + + // int length = 0; + // int res = CM_Get_DevNode_Property(devInst, ref propertyKey, out var propertyType, null, ref length, flags); + // if (res != CR_SUCCESS && res != CR_BUFFER_TOO_SMALL) + // return false; + + char[] buffer = new char[2048]; + int length = buffer.Length; + int res = CM_Get_DevNode_Property(devInst, ref propertyKey, out var propertyType, buffer, ref length, flags); + if (res != CR_SUCCESS) + return false; + if (propertyType != DEVPROP_TYPE_STRING) + return false; + + result = new String(buffer, 0, length).Split('\0').First(); + return true; + } + + static bool CM_Get_Res_Des_Data<T>(IntPtr rdResDes, out T buffer, int ulFlags) where T : struct + { + buffer = default; + + int res = CM_Get_Res_Des_Data_Size(out var size, rdResDes, ulFlags); + if (res != CR_SUCCESS) + return false; + + int sizeOf = Marshal.SizeOf<T>(); + if (sizeOf < size) + return false; + + var addr = Marshal.AllocHGlobal(sizeOf); + try + { + res = CM_Get_Res_Des_Data(rdResDes, addr, size, 0); + if (res != CR_SUCCESS) + return false; + + buffer = Marshal.PtrToStructure<T>(addr); + return true; + } + finally + { + Marshal.FreeHGlobal(addr); + } + } + + #region struct + + [StructLayout(LayoutKind.Sequential)] + struct MEM_DES + { + internal uint MD_Count; + internal uint MD_Type; + internal UIntPtr MD_Alloc_Base; + internal UIntPtr MD_Alloc_End; + internal uint MD_Flags; + internal uint MD_Reserved; + }; + + [StructLayout(LayoutKind.Sequential, Pack = 4)] + struct MEM_RANGE + { + internal UIntPtr MR_Align; // specifies mask for base alignment + internal uint MR_nBytes; // specifies number of bytes required + internal UIntPtr MR_Min; // specifies minimum address of the range + internal UIntPtr MR_Max; // specifies maximum address of the range + internal uint MR_Flags; // specifies flags describing range (fMD flags) + internal uint MR_Reserved; + }; + + [StructLayout(LayoutKind.Sequential)] + struct MEM_RESOURCE + { + internal MEM_DES MEM_Header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + internal MEM_RANGE[] MEM_Data; + }; + + [StructLayout(LayoutKind.Sequential)] + struct DEVPROPKEY + { + public Guid Guid; + public uint Pid; + + public DEVPROPKEY(String guid, uint pid) + { + this.Guid = new Guid(guid); + this.Pid = pid; + } + }; + + const int ALLOC_LOG_CONF = 0x00000002; // Specifies the Alloc Element. + const int BOOT_LOG_CONF = 0x00000003; // Specifies the RM Alloc Element. + const int ResType_Mem = (0x00000001); // Physical address resource + + const int CM_GETIDLIST_FILTER_PRESENT = 0x00000100; + const int CM_GETIDLIST_FILTER_CLASS = 0x00000200; + const int CR_SUCCESS = 0x0; + const int CR_BUFFER_TOO_SMALL = 0x1A; + + const int DEVPROP_TYPE_STRING = 0x00000012; + + static readonly DEVPROPKEY DEVPKEY_Device_DeviceDesc = new DEVPROPKEY("a45c254e-df1c-4efd-8020-67d146a850e0", 2); + + internal static readonly Guid GUID_DISPLAY = new Guid("{4d36e968-e325-11ce-bfc1-08002be10318}"); + + #endregion + + #region import + + [DllImport("hid.dll", EntryPoint = "HidD_GetHidGuid")] + internal static extern void HidD_GetHidGuidMethod(out Guid hidGuid); + + [DllImport("hid", CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.U1)] + private static extern bool HidD_GetManufacturerString(IntPtr HidDeviceObject, [Out] byte[] Buffer, + uint BufferLength); + + [DllImport("hid", CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.U1)] + private static extern bool HidD_GetProductString(IntPtr HidDeviceObject, [Out] byte[] Buffer, uint BufferLength); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern SafeFileHandle CreateFileW( + [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, + ulong dwDesiredAccess, + ulong dwShareMode, + IntPtr lpSecurityAttributes, + uint dwCreationDisposition, + uint dwFlagsAndAttributes, + IntPtr hTemplateFile); + + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + private static extern bool DeviceIoControl( + SafeFileHandle hDevice, + ulong ioControlCode, + byte[] inBuffer, + int nInBufferSize, + byte[] outBuffer, + int nOutBufferSize, + ref uint pBytesReturned, + IntPtr overlapped); + + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] + static extern int CM_Locate_DevNode(out IntPtr pdnDevInst, string pDeviceID, int ulFlags); + + [DllImport("setupapi.dll", CharSet = CharSet.Unicode)] + static extern int CM_Get_Device_ID_List_Size(out int idListlen, string? filter, int ulFlags); + + [DllImport("setupapi.dll", CharSet = CharSet.Unicode)] + static extern int CM_Get_Device_ID_List(string? filter, char[] bffr, int bffrLen, int ulFlags); + + [DllImport("CfgMgr32.dll", CharSet = CharSet.Unicode)] + static extern int CM_Get_DevNode_Property(IntPtr devInst, ref DEVPROPKEY propertyKey, out int propertyType, char[]? bffr, ref int bffrLen, int flags); + + [DllImport("setupapi.dll")] + static extern int CM_Free_Res_Des_Handle(IntPtr rdResDes); + + [DllImport("setupapi.dll")] + static extern int CM_Get_First_Log_Conf(out IntPtr rdResDes, IntPtr pdnDevInst, int ulFlags); + + [DllImport("setupapi.dll")] + static extern int CM_Get_Next_Res_Des(out IntPtr newResDes, IntPtr rdResDes, int resType, out int resourceID, int ulFlags); + + [DllImport("setupapi.dll")] + static extern int CM_Get_Res_Des_Data_Size(out int size, IntPtr rdResDes, int ulFlags); + + [DllImport("setupapi.dll")] + static extern int CM_Get_Res_Des_Data(IntPtr rdResDes, IntPtr buffer, int size, int ulFlags); + + #endregion + + #region events + + public static event XInputDeviceArrivedEventHandler XUsbDeviceArrived; + public delegate void XInputDeviceArrivedEventHandler(PnPDetails device, DeviceEventArgs obj); + + public static event XInputDeviceRemovedEventHandler XUsbDeviceRemoved; + public delegate void XInputDeviceRemovedEventHandler(PnPDetails device, DeviceEventArgs obj); + + public static event GenericDeviceArrivedEventHandler UsbDeviceArrived; + public delegate void GenericDeviceArrivedEventHandler(PnPDevice device, DeviceEventArgs obj); + + public static event GenericDeviceRemovedEventHandler UsbDeviceRemoved; + public delegate void GenericDeviceRemovedEventHandler(PnPDevice device, DeviceEventArgs obj); + + public static event DInputDeviceArrivedEventHandler HidDeviceArrived; + public delegate void DInputDeviceArrivedEventHandler(PnPDetails device, DeviceEventArgs obj); + + public static event DInputDeviceRemovedEventHandler HidDeviceRemoved; + public delegate void DInputDeviceRemovedEventHandler(PnPDetails device, DeviceEventArgs obj); + + public static event InitializedEventHandler Initialized; + public delegate void InitializedEventHandler(); + + #endregion } \ No newline at end of file diff --git a/HandheldCompanion/Managers/GamepadFocusManager.cs b/HandheldCompanion/Managers/GamepadFocusManager.cs index b1ea5af8e..2f57f0085 100644 --- a/HandheldCompanion/Managers/GamepadFocusManager.cs +++ b/HandheldCompanion/Managers/GamepadFocusManager.cs @@ -1,649 +1,665 @@ -using GregsStack.InputSimulatorStandard.Native; -using HandheldCompanion.Controllers; -using HandheldCompanion.Inputs; -using HandheldCompanion.Utils; -using HandheldCompanion.Views; -using HandheldCompanion.Views.Classes; -using HandheldCompanion.Views.Windows; -using Inkore.UI.WPF.Modern.Controls; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Input; -using System.Windows.Navigation; -using System.Xml.Linq; -using Frame = Inkore.UI.WPF.Modern.Controls.Frame; -using Page = System.Windows.Controls.Page; -using Timer = System.Timers.Timer; - -namespace HandheldCompanion.Managers -{ - public class GamepadFocusManager - { - #region events - public static event GotFocusEventHandler GotFocus; - public delegate void GotFocusEventHandler(Control control); - - public static event LostFocusEventHandler LostFocus; - public delegate void LostFocusEventHandler(Control control); - #endregion - - private GamepadWindow _currentWindow; - private Frame _gamepadFrame; - private Page _gamepadPage; - private Timer _gamepadTimer; - - private bool _goingBack; - private bool _goingForward; - - private bool _rendered; - private bool _focused; - - private ButtonState prevButtonState = new(); - - // key: Windows, value: NavigationViewItem - private Control prevNavigation; - // key: Page - private ConcurrentDictionary<object, Control> prevControl = new(); - - public GamepadFocusManager(GamepadWindow gamepadWindow, Frame contentFrame) - { - // set current window - _currentWindow = gamepadWindow; - _currentWindow.GotFocus += _currentWindow_GotFocus; - _currentWindow.GotKeyboardFocus += _currentWindow_GotFocus; - _currentWindow.LostFocus += _currentWindow_LostFocus; - - //_currentWindow.IsVisibleChanged += _currentWindow_IsVisibleChanged; - - _currentWindow.GotGamepadWindowFocus += _currentWindow_GotGamepadWindowFocus; - _currentWindow.LostGamepadWindowFocus += _currentWindow_LostGamepadWindowFocus; - - _currentWindow.Activated += (sender, e) => _currentWindow_GotFocus(sender, null); - _currentWindow.Deactivated += (sender, e) => _currentWindow_LostFocus(sender, null); - - _gamepadFrame = contentFrame; - _gamepadFrame.Navigated += ContentFrame_Navigated; - - // start listening to inputs - switch (SettingsManager.GetBoolean("DesktopProfileOnStart")) - { - case true: - ControllerManager.InputsUpdated -= InputsUpdated; - break; - case false: - ControllerManager.InputsUpdated += InputsUpdated; - break; - } - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - - _gamepadTimer = new Timer(250) { AutoReset = false }; - _gamepadTimer.Elapsed += _gamepadFrame_PageRendered; - } - - private void _currentWindow_GotGamepadWindowFocus() - { - _focused = true; - GotFocus?.Invoke(_currentWindow); - } - - private void _currentWindow_LostGamepadWindowFocus() - { - // halt timer - _gamepadTimer.Stop(); - - _focused = false; - - LostFocus?.Invoke(_currentWindow); - } - private void _currentWindow_GotFocus(object sender, RoutedEventArgs e) - { - // already has focus - if (_focused) - return; - - // set focus - _focused = true; - - // raise event - GotFocus?.Invoke(_currentWindow); - } - - private void _currentWindow_LostFocus(object sender, RoutedEventArgs e) - { - // doesn't have focus - if (!_focused) - return; - - // check if sender is part of current window - if (e is not null && e.OriginalSource is not null) - { - Window yourParentWindow = Window.GetWindow((DependencyObject)e.OriginalSource); - - // sender is part of parent window, return - if (yourParentWindow == _currentWindow) - return; - } - - // unset focus - _focused = false; - - // halt timer - _gamepadTimer.Stop(); - - // raise event - LostFocus?.Invoke(_currentWindow); - } - - private void SettingsManager_SettingValueChanged(string name, object value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (name) - { - case "DesktopLayoutEnabled": - { - var value = SettingsManager.GetBoolean(name, true); - switch (value) - { - case true: - ControllerManager.InputsUpdated -= InputsUpdated; - break; - case false: - ControllerManager.InputsUpdated += InputsUpdated; - break; - } - } - break; - } - }); - } - - private void ContentFrame_Navigated(object sender, NavigationEventArgs e) - { - // set rendering state - _rendered = false; - - // remove state - _goingForward = false; - - // halt timer - _gamepadTimer.Stop(); - - // store current Frame - _gamepadFrame = (Frame)sender; - _gamepadFrame.ContentRendered += _gamepadFrame_ContentRendered; - - // store current Page - _gamepadPage = (Page)_gamepadFrame.Content; - } - - private void _gamepadFrame_ContentRendered(object? sender, EventArgs e) - { - _gamepadTimer.Stop(); - _gamepadTimer.Start(); - } - - private void _gamepadFrame_PageRendered(object? sender, System.Timers.ElapsedEventArgs e) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // specific-cases - switch (_gamepadPage.Tag) - { - case "layout": - case "SettingsMode0": - case "SettingsMode1": - - // quicktools - case "quickhome": - case "quicksettings": - case "quickdevice": - case "quickperformance": - case "quickprofiles": - case "quicksuspender": - _goingForward = true; - break; - } - - if (_goingBack && prevControl.TryGetValue(_gamepadPage.Tag, out Control control)) - { - Focus(control); - - // remove state - _goingBack = false; - } - else if (_goingForward) - { - if (prevControl.TryGetValue(_gamepadPage.Tag, out control)) - Focus(control); - else - { - control = WPFUtils.GetTopLeftControl<Control>(_currentWindow.elements); - Focus(control); - } - } - /* - else if (prevNavigation is null && _currentWindow.IsVisible && _currentWindow.WindowState != WindowState.Minimized) - { - NavigationViewItem currentNavigationViewItem = (NavigationViewItem)WPFUtils.GetTopLeftControl<NavigationViewItem>(_currentWindow.elements); - prevNavigation = currentNavigationViewItem; - Focus(currentNavigationViewItem); - } - */ - - // clear history - if (_gamepadPage is not null) - prevControl.Remove(_gamepadPage.Tag, out _); - - // set rendering state - _rendered = true; - }); - } - - public void Focus(Control control) - { - if (control is null) - return; - - // set focus to control - Keyboard.Focus(control); - control.Focus(); - - ToolTipService.SetShowsToolTipOnKeyboardFocus(control, true); - control.BringIntoView(); - } - - public Control FocusedElement(GamepadWindow window) - { - IInputElement keyboardFocused = null; - - if (Keyboard.FocusedElement is not null) - if (Keyboard.FocusedElement.GetType().IsSubclassOf(typeof(Control))) - keyboardFocused = Keyboard.FocusedElement; - - if (keyboardFocused is null) - { - if (window is not null) - keyboardFocused = window; - else - keyboardFocused = _currentWindow; - } - - if (keyboardFocused.Focusable) - { - Control controlFocused = (Control)keyboardFocused; - - string keyboardType = controlFocused.GetType().Name; - - switch (keyboardType) - { - case "MainWindow": - case "OverlayQuickTools": - case "TouchScrollViewer": - { - if (prevNavigation is not null) - { - // a new page opened - controlFocused = WPFUtils.GetTopLeftControl<Control>(window.elements); - } - else - { - // first start - prevNavigation = controlFocused = WPFUtils.GetTopLeftControl<NavigationViewItem>(window.elements); - } - } - break; - - case "NavigationViewItem": - { - switch (controlFocused.Name) - { - case "b_ServiceStart": - case "b_ServiceStop": - case "b_ServiceInstall": - case "b_ServiceDelete": - break; - default: - { - // update navigation - prevNavigation = (NavigationViewItem)controlFocused; - } - break; - } - } - break; - - default: - { - // store current control - if (_gamepadPage is not null) - prevControl[_gamepadPage.Tag] = controlFocused; - } - break; - } - - if (controlFocused is not null) - { - // pick the last known Control - return controlFocused; - } - else if (window is MainWindow) - { - // pick the top left NavigationViewItem - return WPFUtils.GetTopLeftControl<NavigationViewItem>(window.elements); - } - else if (window is OverlayQuickTools) - { - // pick the top left Control - return WPFUtils.GetTopLeftControl<Control>(window.elements); - } - } - - return null; - } - - private void InputsUpdated(ControllerState controllerState) - { - if (!_rendered || !_focused) - return; - - // stop gamepad navigation when InputsManager is listening - if (InputsManager.IsListening) - return; - - if (controllerState.ButtonState.Equals(prevButtonState)) - return; - - prevButtonState = controllerState.ButtonState.Clone() as ButtonState; - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // get current focused element - Control focusedElement = FocusedElement(_currentWindow); - if (focusedElement is null) - return; - - string elementType = focusedElement.GetType().Name; - - // set direction - WPFUtils.Direction direction = WPFUtils.Direction.None; - - // force display keyboard focus rectangle - WPFUtils.MakeFocusVisible(_currentWindow); - - if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.B1)) - { - // lazy - // todo: implement proper RoutedEvent call - switch (elementType) - { - case "Button": - WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.RETURN); - break; - case "ToggleSwitch": - ((ToggleSwitch)focusedElement).IsOn = !((ToggleSwitch)focusedElement).IsOn; - break; - case "ToggleButton": - WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.RETURN); - break; - case "CheckBox": - ((CheckBox)focusedElement).IsChecked = !((CheckBox)focusedElement).IsChecked; - break; - - case "NavigationViewItem": - { - // set state - _goingForward = true; - - if (prevControl.TryGetValue(_gamepadPage.Tag, out Control control)) - Focus(control); - else - { - // get the nearest non-navigation control - focusedElement = WPFUtils.GetTopLeftControl<Control>(_currentWindow.elements); - Focus(focusedElement); - } - } - break; - - case "ComboBox": - { - ComboBox comboBox = (ComboBox)focusedElement; - comboBox.IsDropDownOpen = true; - } - return; - - case "ComboBoxItem": - WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.RETURN); - return; - } - } - else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.B2)) - { - // lazy - // todo: implement proper RoutedEvent call - switch (elementType) - { - default: - { - switch (_gamepadPage.Tag) - { - default: - { - //TODO: this sometimes happens. we need to handle this. - if (prevNavigation is null) - LogManager.LogInformation("prevNav is null"); - - // restore previous NavigationViewItem - Focus(prevNavigation); - } - return; - - case "layout": - case "SettingsMode0": - case "SettingsMode1": - - // todo: shouldn't be hardcoded - case "quickhome": - case "quicksettings": - case "quickdevice": - case "quickperformance": - case "quickprofiles": - case "quicksuspender": - { - // set state - _goingBack = true; - - // go back to previous page - if (_gamepadFrame.CanGoBack) - _gamepadFrame.GoBack(); - } - return; - } - } - break; - - case "ComboBox": - { - ComboBox comboBox = (ComboBox)focusedElement; - switch (comboBox.IsDropDownOpen) - { - case true: - comboBox.IsDropDownOpen = false; - break; - case false: - { - // restore previous NavigationViewItem - if (prevNavigation is not null) - Focus(prevNavigation); - else - { - switch (_gamepadPage.Tag) - { - // todo: shouldn't be hardcoded - case "quickhome": - case "quicksettings": - case "quickdevice": - case "quickperformance": - case "quickprofiles": - case "quicksuspender": - { - // set state - _goingBack = true; - - // go back to previous page - if (_gamepadFrame.CanGoBack) - _gamepadFrame.GoBack(); - } - break; - } - } - } - break; - } - } - return; - - case "ComboBoxItem": - { - ComboBox comboBox = ItemsControl.ItemsControlFromItemContainer(focusedElement) as ComboBox; - comboBox.IsDropDownOpen = false; - } - return; - - case "NavigationViewItem": - { - if (_currentWindow is OverlayQuickTools) - WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.ESCAPE); - } - break; - } - } - else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.B4)) - { - switch (elementType) - { - case "Button": - { - // To get the first RadioButton in the list, if any - RadioButton firstRadioButton = WPFUtils.FindChildren(focusedElement).FirstOrDefault(c => c is RadioButton) as RadioButton; - if (firstRadioButton is not null) - firstRadioButton.IsChecked = true; - } - break; - } - } - else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.L1)) - { - if (prevNavigation is not null) - { - elementType = prevNavigation.GetType().Name; - direction = WPFUtils.Direction.Left; - } - } - else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.R1)) - { - if (prevNavigation is not null) - { - elementType = prevNavigation.GetType().Name; - direction = WPFUtils.Direction.Right; - } - } - else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.DPadUp) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftStickUp) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftPadClickUp)) - { - direction = WPFUtils.Direction.Up; - } - else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.DPadDown) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftStickDown) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftPadClickDown)) - { - direction = WPFUtils.Direction.Down; - } - else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.DPadLeft) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftStickLeft) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftPadClickLeft)) - { - direction = WPFUtils.Direction.Left; - } - else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.DPadRight) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftStickRight) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftPadClickRight)) - { - direction = WPFUtils.Direction.Right; - } - - // navigation - if (direction != WPFUtils.Direction.None) - { - switch (elementType) - { - case "NavigationViewItem": - { - focusedElement = WPFUtils.GetClosestControl<NavigationViewItem>(focusedElement, _currentWindow.elements, direction); - Focus(focusedElement); - } - return; - - case "ComboBox": - { - ComboBox comboBox = (ComboBox)focusedElement; - if (comboBox.IsDropDownOpen) - { - switch (direction) - { - case WPFUtils.Direction.Up: - WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.UP); - return; - case WPFUtils.Direction.Down: - WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.DOWN); - return; - } - } - } - break; - - case "ComboBoxItem": - { - switch (direction) - { - case WPFUtils.Direction.Up: - WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.UP); - return; - case WPFUtils.Direction.Down: - WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.DOWN); - return; - } - } - break; - - case "Slider": - { - switch (direction) - { - case WPFUtils.Direction.Up: - case WPFUtils.Direction.Down: - focusedElement = WPFUtils.GetClosestControl<Control>(focusedElement, _currentWindow.elements, direction, new List<Type>() { typeof(NavigationViewItem) }); - Focus(focusedElement); - return; - - case WPFUtils.Direction.Left: - WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.LEFT); - return; - case WPFUtils.Direction.Right: - WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.RIGHT); - return; - } - } - break; - } - - // default - focusedElement = WPFUtils.GetClosestControl<Control>(focusedElement, _currentWindow.elements, direction, new List<Type>() { typeof(NavigationViewItem) }); - Focus(focusedElement); - } - }); - } - } -} +using GregsStack.InputSimulatorStandard.Native; +using HandheldCompanion.Controllers; +using HandheldCompanion.Inputs; +using HandheldCompanion.Utils; +using HandheldCompanion.Views; +using HandheldCompanion.Views.Classes; +using HandheldCompanion.Views.Windows; +using Inkore.UI.WPF.Modern.Controls; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Navigation; +using System.Xml.Linq; +using Frame = Inkore.UI.WPF.Modern.Controls.Frame; +using Page = System.Windows.Controls.Page; +using Timer = System.Timers.Timer; + +namespace HandheldCompanion.Managers +{ + public class GamepadFocusManager + { + #region events + public static event GotFocusEventHandler GotFocus; + public delegate void GotFocusEventHandler(Control control); + + public static event LostFocusEventHandler LostFocus; + public delegate void LostFocusEventHandler(Control control); + #endregion + + private GamepadWindow _currentWindow; + private Frame _gamepadFrame; + private Page _gamepadPage; + private Timer _gamepadTimer; + + private bool _goingBack; + private bool _goingForward; + + private bool _rendered; + private bool _focused; + + private ButtonState prevButtonState = new(); + + // key: Windows, value: NavigationViewItem + private Control prevNavigation; + // key: Page + private ConcurrentDictionary<object, Control> prevControl = new(); + + public GamepadFocusManager(GamepadWindow gamepadWindow, Frame contentFrame) + { + // set current window + _currentWindow = gamepadWindow; + _currentWindow.GotFocus += _currentWindow_GotFocus; + _currentWindow.GotKeyboardFocus += _currentWindow_GotFocus; + _currentWindow.LostFocus += _currentWindow_LostFocus; + + //_currentWindow.IsVisibleChanged += _currentWindow_IsVisibleChanged; + + _currentWindow.GotGamepadWindowFocus += _currentWindow_GotGamepadWindowFocus; + _currentWindow.LostGamepadWindowFocus += _currentWindow_LostGamepadWindowFocus; + + _currentWindow.Activated += (sender, e) => _currentWindow_GotFocus(sender, null); + _currentWindow.Deactivated += (sender, e) => _currentWindow_LostFocus(sender, null); + + _gamepadFrame = contentFrame; + _gamepadFrame.Navigated += ContentFrame_Navigated; + + // start listening to inputs + switch (SettingsManager.GetBoolean("DesktopProfileOnStart")) + { + case true: + ControllerManager.InputsUpdated -= InputsUpdated; + break; + case false: + ControllerManager.InputsUpdated += InputsUpdated; + break; + } + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + + _gamepadTimer = new Timer(250) { AutoReset = false }; + _gamepadTimer.Elapsed += _gamepadFrame_PageRendered; + } + + private void _currentWindow_GotGamepadWindowFocus() + { + _focused = true; + GotFocus?.Invoke(_currentWindow); + } + + private void _currentWindow_LostGamepadWindowFocus() + { + // halt timer + _gamepadTimer.Stop(); + + _focused = false; + + LostFocus?.Invoke(_currentWindow); + } + private void _currentWindow_GotFocus(object sender, RoutedEventArgs e) + { + // already has focus + if (_focused) + return; + + // set focus + _focused = true; + + // raise event + GotFocus?.Invoke(_currentWindow); + } + + private void _currentWindow_LostFocus(object sender, RoutedEventArgs e) + { + // doesn't have focus + if (!_focused) + return; + + // check if sender is part of current window + if (e is not null && e.OriginalSource is not null) + { + Window yourParentWindow = Window.GetWindow((DependencyObject)e.OriginalSource); + + // sender is part of parent window, return + if (yourParentWindow == _currentWindow) + return; + } + + // unset focus + _focused = false; + + // halt timer + _gamepadTimer.Stop(); + + // raise event + LostFocus?.Invoke(_currentWindow); + } + + private void SettingsManager_SettingValueChanged(string name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { + case "DesktopLayoutEnabled": + { + var value = SettingsManager.GetBoolean(name, true); + switch (value) + { + case true: + ControllerManager.InputsUpdated -= InputsUpdated; + break; + case false: + ControllerManager.InputsUpdated += InputsUpdated; + break; + } + } + break; + } + }); + } + + private void ContentFrame_Navigated(object sender, NavigationEventArgs e) + { + // set rendering state + _rendered = false; + + // remove state + _goingForward = false; + + // halt timer + _gamepadTimer.Stop(); + + // store current Frame + _gamepadFrame = (Frame)sender; + _gamepadFrame.ContentRendered += _gamepadFrame_ContentRendered; + + // store current Page + _gamepadPage = (Page)_gamepadFrame.Content; + } + + private void _gamepadFrame_ContentRendered(object? sender, EventArgs e) + { + _gamepadTimer.Stop(); + _gamepadTimer.Start(); + } + + private void _gamepadFrame_PageRendered(object? sender, System.Timers.ElapsedEventArgs e) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // specific-cases + switch (_gamepadPage.Tag) + { + case "layout": + case "SettingsMode0": + case "SettingsMode1": + + // quicktools + case "quickhome": + case "quicksettings": + case "quickdevice": + case "quickperformance": + case "quickprofiles": + case "quicksuspender": + _goingForward = true; + break; + } + + if (_goingBack && prevControl.TryGetValue(_gamepadPage.Tag, out Control control)) + { + Focus(control); + + // remove state + _goingBack = false; + } + else if (_goingForward) + { + if (prevControl.TryGetValue(_gamepadPage.Tag, out control)) + Focus(control); + else + { + control = WPFUtils.GetTopLeftControl<Control>(_currentWindow.elements); + Focus(control); + } + } + /* + else if (prevNavigation is null && _currentWindow.IsVisible && _currentWindow.WindowState != WindowState.Minimized) + { + NavigationViewItem currentNavigationViewItem = (NavigationViewItem)WPFUtils.GetTopLeftControl<NavigationViewItem>(_currentWindow.elements); + prevNavigation = currentNavigationViewItem; + Focus(currentNavigationViewItem); + } + */ + + // clear history + if (_gamepadPage is not null) + prevControl.Remove(_gamepadPage.Tag, out _); + + // set rendering state + _rendered = true; + }); + } + + public void Focus(Control control) + { + if (control is null) + return; + + // set focus to control + Keyboard.Focus(control); + control.Focus(); + + ToolTipService.SetShowsToolTipOnKeyboardFocus(control, true); + control.BringIntoView(); + } + + public Control FocusedElement(GamepadWindow window) + { + IInputElement keyboardFocused = null; + + if (Keyboard.FocusedElement is not null) + if (Keyboard.FocusedElement.GetType().IsSubclassOf(typeof(Control))) + keyboardFocused = Keyboard.FocusedElement; + + if (keyboardFocused is null) + { + if (window is not null) + keyboardFocused = window; + else + keyboardFocused = _currentWindow; + } + + if (keyboardFocused.Focusable) + { + Control controlFocused = (Control)keyboardFocused; + + string keyboardType = controlFocused.GetType().Name; + + switch (keyboardType) + { + case "MainWindow": + case "OverlayQuickTools": + case "TouchScrollViewer": + { + if (prevNavigation is not null) + { + // a new page opened + controlFocused = WPFUtils.GetTopLeftControl<Control>(window.elements); + } + else + { + // first start + prevNavigation = controlFocused = WPFUtils.GetTopLeftControl<NavigationViewItem>(window.elements); + } + } + break; + + case "NavigationViewItem": + { + switch (controlFocused.Name) + { + case "b_ServiceStart": + case "b_ServiceStop": + case "b_ServiceInstall": + case "b_ServiceDelete": + break; + default: + { + // update navigation + prevNavigation = (NavigationViewItem)controlFocused; + } + break; + } + } + break; + + default: + { + // store current control + if (_gamepadPage is not null) + prevControl[_gamepadPage.Tag] = controlFocused; + } + break; + } + + if (controlFocused is not null) + { + // pick the last known Control + return controlFocused; + } + else if (window is MainWindow) + { + // pick the top left NavigationViewItem + return WPFUtils.GetTopLeftControl<NavigationViewItem>(window.elements); + } + else if (window is OverlayQuickTools) + { + // pick the top left Control + return WPFUtils.GetTopLeftControl<Control>(window.elements); + } + } + + return null; + } + + private void InputsUpdated(ControllerState controllerState) + { + if (!_rendered || !_focused) + return; + + // stop gamepad navigation when InputsManager is listening + if (InputsManager.IsListening) + return; + + if (controllerState.ButtonState.Equals(prevButtonState)) + return; + + prevButtonState = controllerState.ButtonState.Clone() as ButtonState; + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // get current focused element + Control focusedElement = FocusedElement(_currentWindow); + if (focusedElement is null) + return; + + string elementType = focusedElement.GetType().Name; + + // set direction + WPFUtils.Direction direction = WPFUtils.Direction.None; + + // force display keyboard focus rectangle + WPFUtils.MakeFocusVisible(_currentWindow); + + if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.B1)) + { + // lazy + // todo: implement proper RoutedEvent call + switch (elementType) + { + case "Button": + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.RETURN); + break; + case "ToggleSwitch": + ((ToggleSwitch)focusedElement).IsOn = !((ToggleSwitch)focusedElement).IsOn; + break; + case "ToggleButton": + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.RETURN); + break; + case "CheckBox": + ((CheckBox)focusedElement).IsChecked = !((CheckBox)focusedElement).IsChecked; + break; + + case "NavigationViewItem": + { +<<<<<<< HEAD + // set state + _goingForward = true; +======= + switch (focusedElement.Name) + { + // deprecated, used for ui:NavigationView.FooterMenuItem + case "b_ServiceStart": + case "b_ServiceStop": + case "b_ServiceInstall": + case "b_ServiceDelete": + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.RETURN); + return; + default: + { + // set state + _goingForward = true; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + if (prevControl.TryGetValue(_gamepadPage.Tag, out Control control)) + Focus(control); + else + { + // get the nearest non-navigation control + focusedElement = WPFUtils.GetTopLeftControl<Control>(_currentWindow.elements); + Focus(focusedElement); + } + } + break; + + case "ComboBox": + { + ComboBox comboBox = (ComboBox)focusedElement; + comboBox.IsDropDownOpen = true; + } + return; + + case "ComboBoxItem": + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.RETURN); + return; + } + } + else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.B2)) + { + // lazy + // todo: implement proper RoutedEvent call + switch (elementType) + { + default: + { + switch (_gamepadPage.Tag) + { + default: + { + //TODO: this sometimes happens. we need to handle this. + if (prevNavigation is null) + LogManager.LogInformation("prevNav is null"); + + // restore previous NavigationViewItem + Focus(prevNavigation); + } + return; + + case "layout": + case "SettingsMode0": + case "SettingsMode1": + + // todo: shouldn't be hardcoded + case "quickhome": + case "quicksettings": + case "quickdevice": + case "quickperformance": + case "quickprofiles": + case "quicksuspender": + { + // set state + _goingBack = true; + + // go back to previous page + if (_gamepadFrame.CanGoBack) + _gamepadFrame.GoBack(); + } + return; + } + } + break; + + case "ComboBox": + { + ComboBox comboBox = (ComboBox)focusedElement; + switch (comboBox.IsDropDownOpen) + { + case true: + comboBox.IsDropDownOpen = false; + break; + case false: + { + // restore previous NavigationViewItem + if (prevNavigation is not null) + Focus(prevNavigation); + else + { + switch (_gamepadPage.Tag) + { + // todo: shouldn't be hardcoded + case "quickhome": + case "quicksettings": + case "quickdevice": + case "quickperformance": + case "quickprofiles": + case "quicksuspender": + { + // set state + _goingBack = true; + + // go back to previous page + if (_gamepadFrame.CanGoBack) + _gamepadFrame.GoBack(); + } + break; + } + } + } + break; + } + } + return; + + case "ComboBoxItem": + { + ComboBox comboBox = ItemsControl.ItemsControlFromItemContainer(focusedElement) as ComboBox; + comboBox.IsDropDownOpen = false; + } + return; + + case "NavigationViewItem": + { + if (_currentWindow is OverlayQuickTools) + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.ESCAPE); + } + break; + } + } + else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.B4)) + { + switch (elementType) + { + case "Button": + { + // To get the first RadioButton in the list, if any + RadioButton firstRadioButton = WPFUtils.FindChildren(focusedElement).FirstOrDefault(c => c is RadioButton) as RadioButton; + if (firstRadioButton is not null) + firstRadioButton.IsChecked = true; + } + break; + } + } + else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.L1)) + { + if (prevNavigation is not null) + { + elementType = prevNavigation.GetType().Name; + direction = WPFUtils.Direction.Left; + } + } + else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.R1)) + { + if (prevNavigation is not null) + { + elementType = prevNavigation.GetType().Name; + direction = WPFUtils.Direction.Right; + } + } + else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.DPadUp) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftStickUp) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftPadClickUp)) + { + direction = WPFUtils.Direction.Up; + } + else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.DPadDown) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftStickDown) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftPadClickDown)) + { + direction = WPFUtils.Direction.Down; + } + else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.DPadLeft) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftStickLeft) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftPadClickLeft)) + { + direction = WPFUtils.Direction.Left; + } + else if (controllerState.ButtonState.Buttons.Contains(ButtonFlags.DPadRight) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftStickRight) || controllerState.ButtonState.Buttons.Contains(ButtonFlags.LeftPadClickRight)) + { + direction = WPFUtils.Direction.Right; + } + + // navigation + if (direction != WPFUtils.Direction.None) + { + switch (elementType) + { + case "NavigationViewItem": + { + focusedElement = WPFUtils.GetClosestControl<NavigationViewItem>(focusedElement, _currentWindow.elements, direction); + Focus(focusedElement); + } + return; + + case "ComboBox": + { + ComboBox comboBox = (ComboBox)focusedElement; + if (comboBox.IsDropDownOpen) + { + switch (direction) + { + case WPFUtils.Direction.Up: + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.UP); + return; + case WPFUtils.Direction.Down: + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.DOWN); + return; + } + } + } + break; + + case "ComboBoxItem": + { + switch (direction) + { + case WPFUtils.Direction.Up: + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.UP); + return; + case WPFUtils.Direction.Down: + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.DOWN); + return; + } + } + break; + + case "Slider": + { + switch (direction) + { + case WPFUtils.Direction.Up: + case WPFUtils.Direction.Down: + focusedElement = WPFUtils.GetClosestControl<Control>(focusedElement, _currentWindow.elements, direction, new List<Type>() { typeof(NavigationViewItem) }); + Focus(focusedElement); + return; + + case WPFUtils.Direction.Left: + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.LEFT); + return; + case WPFUtils.Direction.Right: + WPFUtils.SendKeyToControl(focusedElement, (int)VirtualKeyCode.RIGHT); + return; + } + } + break; + } + + // default + focusedElement = WPFUtils.GetClosestControl<Control>(focusedElement, _currentWindow.elements, direction, new List<Type>() { typeof(NavigationViewItem) }); + Focus(focusedElement); + } + }); + } + } +} diff --git a/HandheldCompanion/Managers/Hotkeys/Hotkey.cs b/HandheldCompanion/Managers/Hotkeys/Hotkey.cs index 5d4a99773..714ac5e1c 100644 --- a/HandheldCompanion/Managers/Hotkeys/Hotkey.cs +++ b/HandheldCompanion/Managers/Hotkeys/Hotkey.cs @@ -9,7 +9,11 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +<<<<<<< HEAD using System.Windows.Media.Animation; +======= +using System.Windows.Media.Animation; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d using static HandheldCompanion.Managers.InputsHotkey; using static HandheldCompanion.Managers.InputsManager; @@ -302,6 +306,7 @@ public void DrawInput() foreach (var button in inputsChord.State.Buttons) { +<<<<<<< HEAD UIElement? label = null; var fontIcon = new FontIcon(); @@ -336,6 +341,18 @@ public void DrawInput() fontIcon.SetResourceReference(Control.ForegroundProperty, "SystemControlForegroundBaseMediumBrush"); +======= + UIElement? label = null; + + var fontIcon = controller.GetFontIcon(button); + // we display only one label, default one is not enough + if (fontIcon.Glyph != IController.defaultGlyph) + { + if (fontIcon.Foreground is null) + fontIcon.SetResourceReference(Control.ForegroundProperty, + "SystemControlForegroundBaseMediumBrush"); + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d label = fontIcon; } diff --git a/HandheldCompanion/Managers/HotkeysManager.cs b/HandheldCompanion/Managers/HotkeysManager.cs index 60db88d58..0e6973b4f 100644 --- a/HandheldCompanion/Managers/HotkeysManager.cs +++ b/HandheldCompanion/Managers/HotkeysManager.cs @@ -1,514 +1,526 @@ -using GregsStack.InputSimulatorStandard.Native; -using HandheldCompanion.Controllers; -using HandheldCompanion.Controls; -using HandheldCompanion.Misc; -using HandheldCompanion.Properties; -using HandheldCompanion.Simulators; -using HandheldCompanion.Utils; -using HandheldCompanion.Views; -using Inkore.UI.WPF.Modern.Controls; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using System.Windows; -using Windows.System; -using static HandheldCompanion.Managers.InputsHotkey; -using static HandheldCompanion.Managers.InputsManager; - -namespace HandheldCompanion.Managers; - -public static class HotkeysManager -{ - public delegate void CommandExecutedEventHandler(string listener); - - public delegate void HotkeyCreatedEventHandler(Hotkey hotkey); - - public delegate void HotkeyTypeCreatedEventHandler(InputsHotkeyType type); - - public delegate void HotkeyUpdatedEventHandler(Hotkey hotkey); - - public delegate void InitializedEventHandler(); - - private const short PIN_LIMIT = 18; - private static readonly string InstallPath; - private static bool hasProfileHID = false; - public static SortedDictionary<ushort, Hotkey> Hotkeys = new(); - - private static bool IsInitialized; - - static HotkeysManager() - { - // initialize path - InstallPath = Path.Combine(MainWindow.SettingsPath, "hotkeys"); - if (!Directory.Exists(InstallPath)) - Directory.CreateDirectory(InstallPath); - - InputsManager.TriggerUpdated += TriggerUpdated; - InputsManager.TriggerRaised += TriggerRaised; - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - ControllerManager.ControllerSelected += ControllerManager_ControllerSelected; - ControllerManager.ControllerPlugged += ControllerManager_ControllerPlugged; - ControllerManager.ControllerUnplugged += ControllerManager_ControllerUnplugged; - ProfileManager.Applied += ProfileManager_Applied; - VirtualManager.ControllerSelected += VirtualManager_ControllerSelected; - } - - public static event HotkeyTypeCreatedEventHandler HotkeyTypeCreated; - - public static event HotkeyCreatedEventHandler HotkeyCreated; - - public static event HotkeyUpdatedEventHandler HotkeyUpdated; - - public static event CommandExecutedEventHandler CommandExecuted; - - public static event InitializedEventHandler Initialized; - - private static void ControllerManager_ControllerSelected(IController Controller) - { - foreach (var hotkey in Hotkeys.Values) - hotkey.ControllerSelected(Controller); - } - - private static void ControllerManager_ControllerPlugged(IController Controller, bool IsPowerCycling) - { - // when the target emulated controller is Dualshock - // only enable HIDmode switch hotkey when controller is plugged (last stage of HIDmode change in this case) - var targetHIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); - if (targetHIDmode == HIDmode.DualShock4Controller) - { - var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals("shortcutChangeHIDMode")); - foreach (var hotkey in hotkeys) - { - Application.Current.Dispatcher.BeginInvoke(() => { hotkey.IsEnabled = !hasProfileHID; }); - } - } - } - - private static void ControllerManager_ControllerUnplugged(IController Controller, bool IsPowerCycling) - { - // when the target emulated controller is Xbox Controller - // only enable HIDmode switch hotkey when controller is unplugged (last stage of HIDmode change in this case) - var targetHIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); - - if (targetHIDmode == HIDmode.Xbox360Controller) - { - var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals("shortcutChangeHIDMode")); - foreach (var hotkey in hotkeys) - { - Application.Current.Dispatcher.Invoke(() => { hotkey.IsEnabled = !hasProfileHID; }); - } - } - } - - private static void ProfileManager_Applied(Profile profile, UpdateSource source) - { - // check if profile-specific HIDmode -> disable emulated controller hotkey, else -> enable it - HIDmode HIDmode; - - switch ((HIDmode)profile.HID) - { - case HIDmode.Xbox360Controller: - case HIDmode.DualShock4Controller: - { - hasProfileHID = true; - HIDmode = (HIDmode)profile.HID; // Applies profile-specific HID - break; - } - - default: // Default - { - HIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); // Applies default HID from settings - hasProfileHID = false; - break; - } - } - - // enable/disable hotkey based on profile HIDmode - var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals("shortcutChangeHIDMode")); - foreach (var hotkey in hotkeys) - Application.Current.Dispatcher.Invoke(() => - { - hotkey.IsEnabled = !hasProfileHID; - }); - - // change glyph at startup only - if (!IsInitialized) - { - VirtualManager_ControllerSelected(HIDmode); - } - } - - private static void VirtualManager_ControllerSelected(HIDmode HIDmode) - { - // change glyph of shortcutChangeHIDMode to the corresponding target emulated controller - var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals("shortcutChangeHIDMode")); - foreach (var hotkey in hotkeys) - { - switch (HIDmode) - { - case HIDmode.Xbox360Controller: - hotkey.inputsHotkey.Glyph = "\uE001"; - break; - case HIDmode.DualShock4Controller: - hotkey.inputsHotkey.Glyph = "\uE000"; - break; - default: - break; - } - - // redraw to change glyph - hotkey.Draw(); - } - } - - - public static void Start() - { - // process hotkeys types - foreach (var type in (InputsHotkeyType[])Enum.GetValues(typeof(InputsHotkeyType))) - HotkeyTypeCreated?.Invoke(type); - - // process hotkeys - foreach (var pair in InputsHotkeys) - { - var Id = pair.Key; - var inputsHotkey = pair.Value; - - Hotkey hotkey = null; - - var fileName = Path.Combine(InstallPath, $"{inputsHotkey.Listener}.json"); - - // check for existing hotkey - if (File.Exists(fileName)) - hotkey = ProcessHotkey(fileName); - - // no hotkey found or failed parsing - if (hotkey is null) - { - hotkey = new Hotkey(Id); - hotkey.IsPinned = inputsHotkey.DefaultPinned; - } - - // hotkey is outdated and using an unknown inputs hotkey - if (!InputsHotkeys.TryGetValue(hotkey.hotkeyId, out var foundHotkey)) - continue; - - // pull inputs hotkey - hotkey.SetInputsHotkey(foundHotkey); - hotkey.Draw(); - - if (!Hotkeys.ContainsKey(hotkey.hotkeyId)) - Hotkeys.Add(hotkey.hotkeyId, hotkey); - } - - foreach (var hotkey in Hotkeys.Values) - { - hotkey.Listening += StartListening; - hotkey.Pinning += PinOrUnpinHotkey; - hotkey.Summoned += hotkey => InvokeTrigger(hotkey, false, true); - hotkey.Updated += hotkey => SerializeHotkey(hotkey, true); - - if (!string.IsNullOrEmpty(hotkey.inputsHotkey.Settings)) - hotkey.IsEnabled = SettingsManager.GetBoolean(hotkey.inputsHotkey.Settings); - - HotkeyCreated?.Invoke(hotkey); - } - - IsInitialized = true; - Initialized?.Invoke(); - - LogManager.LogInformation("{0} has started", "HotkeysManager"); - } - - public static void Stop() - { - if (!IsInitialized) - return; - - IsInitialized = false; - - ControllerManager.ControllerPlugged -= ControllerManager_ControllerPlugged; - ControllerManager.ControllerUnplugged -= ControllerManager_ControllerUnplugged; - ProfileManager.Applied -= ProfileManager_Applied; - VirtualManager.ControllerSelected -= VirtualManager_ControllerSelected; - - LogManager.LogInformation("{0} has stopped", "HotkeysManager"); - } - - private static void SettingsManager_SettingValueChanged(string name, object value) - { - // manage toggle type hotkeys - foreach (var hotkey in Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals(name))) - { - if (!hotkey.inputsHotkey.IsToggle) - continue; - - var toggle = Convert.ToBoolean(value); - hotkey.SetToggle(toggle); - } - - // manage settings type hotkeys - foreach (var hotkey in Hotkeys.Values.Where(item => item.inputsHotkey.Settings.Contains(name))) - { - var enabled = SettingsManager.GetBoolean(hotkey.inputsHotkey.Settings); - hotkey.IsEnabled = enabled; - } - } - - private static void StartListening(Hotkey hotkey, ListenerType type) - { - InputsManager.StartListening(hotkey, type); - hotkey.StartListening(type); - } - - private static void PinOrUnpinHotkey(Hotkey hotkey) - { - switch (hotkey.IsPinned) - { - case false: - { - var count = CountPinned(); - - if (count >= PIN_LIMIT) - { - _ = Dialog.ShowAsync($"{Resources.SettingsPage_UpdateWarning}", - $"You can't pin more than {PIN_LIMIT} hotkeys", - ContentDialogButton.Primary, string.Empty, $"{Resources.ProfilesPage_OK}"); - - return; - } - - hotkey.IsPinned = true; - } - break; - case true: - hotkey.IsPinned = false; - break; - } - - // overwrite current file - SerializeHotkey(hotkey, true); - } - - private static int CountPinned() - { - return Hotkeys.Values.Count(item => item.IsPinned); - } - - private static void TriggerUpdated(string listener, InputsChord inputs, ListenerType type) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // we use @ as a special character to link two ore more listeners together - listener = listener.TrimEnd('@'); - - var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Contains(listener)); - foreach (var hotkey in hotkeys) - { - hotkey.StopListening(inputs, type); - - // overwrite current file - SerializeHotkey(hotkey, true); - } - }); - } - - private static Hotkey ProcessHotkey(string fileName) - { - Hotkey hotkey = null; - try - { - var outputraw = File.ReadAllText(fileName); - hotkey = JsonConvert.DeserializeObject<Hotkey>(outputraw); - } - catch (Exception ex) - { - LogManager.LogError("Could not parse hotkey {0}. {1}", fileName, ex.Message); - } - - return hotkey; - } - - public static void SerializeHotkey(Hotkey hotkey, bool overwrite = false) - { - var listener = hotkey.inputsHotkey.Listener; - - var settingsPath = Path.Combine(InstallPath, $"{listener}.json"); - if (!File.Exists(settingsPath) || overwrite) - { - var jsonString = JsonConvert.SerializeObject(hotkey, Formatting.Indented); - if (FileUtils.IsFileWritable(settingsPath)) - File.WriteAllText(settingsPath, jsonString); - } - - // raise event - HotkeyUpdated?.Invoke(hotkey); - } - - public static void TriggerRaised(string listener, InputsChord input, InputsHotkeyType type, bool IsKeyDown, - bool IsKeyUp) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // we use @ as a special character to link two ore more listeners together - var trimmed = listener.TrimEnd('@'); - var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Contains(trimmed)); - foreach (var hotkey in hotkeys) - hotkey.Highlight(); - }); - - // These are special shortcut keys with no related events - switch (type) - { - case InputsHotkeyType.Embedded: - return; - } - - var fProcess = ProcessManager.GetForegroundProcess(); - - try - { - switch (listener) - { - case "shortcutKeyboard": - { - var uiHostNoLaunch = new ProcessUtils.UIHostNoLaunch(); - var tipInvocation = (ProcessUtils.ITipInvocation)uiHostNoLaunch; - tipInvocation.Toggle(ProcessUtils.GetDesktopWindow()); - Marshal.ReleaseComObject(uiHostNoLaunch); - } - break; - case "shortcutDesktop": - KeyboardSimulator.KeyPress(new[] { VirtualKeyCode.LWIN, VirtualKeyCode.VK_D }); - break; - case "shortcutESC": - if (fProcess is not null) - { - ProcessUtils.SetForegroundWindow(fProcess.MainWindowHandle); - KeyboardSimulator.KeyPress(VirtualKeyCode.ESCAPE); - } - - break; - case "shortcutExpand": - if (fProcess is not null) - { - var Placement = ProcessUtils.GetPlacement(fProcess.MainWindowHandle); - - switch (Placement.showCmd) - { - case ProcessUtils.ShowWindowCommands.Normal: - case ProcessUtils.ShowWindowCommands.Minimized: - ProcessUtils.ShowWindow(fProcess.MainWindowHandle, - (int)ProcessUtils.ShowWindowCommands.Maximized); - break; - case ProcessUtils.ShowWindowCommands.Maximized: - ProcessUtils.ShowWindow(fProcess.MainWindowHandle, - (int)ProcessUtils.ShowWindowCommands.Restored); - break; - } - } - - break; - case "shortcutTaskview": - KeyboardSimulator.KeyPress(new[] { VirtualKeyCode.LWIN, VirtualKeyCode.TAB }); - break; - case "shortcutTaskManager": - KeyboardSimulator.KeyPress(new[] - { VirtualKeyCode.LCONTROL, VirtualKeyCode.LSHIFT, VirtualKeyCode.ESCAPE }); - break; - case "shortcutActionCenter": - { - var uri = new Uri("ms-actioncenter"); - var success = Launcher.LaunchUriAsync(uri); - } - break; - case "shortcutControlCenter": - { - var uri = new Uri( - "ms-actioncenter:controlcenter/&suppressAnimations=false&showFooter=true&allowPageNavigation=true"); - var success = Launcher.LaunchUriAsync(uri); - } - break; - case "shortcutPrintScreen": - KeyboardSimulator.KeyPress( - new[] { VirtualKeyCode.LWIN, VirtualKeyCode.LSHIFT, VirtualKeyCode.VK_S }); - break; - case "suspendResumeTask": - { - var sProcess = ProcessManager.GetLastSuspendedProcess(); - - if (sProcess is null || sProcess.Filter != ProcessEx.ProcessFilter.Allowed) - break; - - if (sProcess.IsSuspended()) - ProcessManager.ResumeProcess(sProcess); - else - ProcessManager.SuspendProcess(fProcess); - } - break; - case "shortcutKillApp": - if (fProcess is not null) fProcess.Process.Kill(); - break; - case "OnScreenDisplayLevel": - { - var value = !SettingsManager.GetBoolean(listener); - SettingsManager.SetProperty(listener, value); - } - break; - - // temporary settings - case "DesktopLayoutEnabled": - { - var value = !SettingsManager.GetBoolean(listener, true); - SettingsManager.SetProperty(listener, value, false, true); - - ToastManager.SendToast("Desktop layout", $"is now {(value ? "enabled" : "disabled")}"); - } - break; - - case "shortcutChangeHIDMode": - { - var currentHIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); - switch (currentHIDmode) - { - case HIDmode.Xbox360Controller: - SettingsManager.SetProperty("HIDmode", (int)HIDmode.DualShock4Controller); - break; - case HIDmode.DualShock4Controller: - SettingsManager.SetProperty("HIDmode", (int)HIDmode.Xbox360Controller); - break; - default: - break; - } - break; - } - - default: - KeyboardSimulator.KeyPress(input.OutputKeys.ToArray()); - break; - } - - LogManager.LogDebug("Executed Hotkey: {0}", listener); - - // play a tune to notify a command was executed - SystemManager.PlayWindowsMedia("Windows Navigation Start.wav"); - - // raise an event - CommandExecuted?.Invoke(listener); - } - catch (Exception ex) - { - LogManager.LogError("Failed to parse trigger {0}, {1}", listener, ex.Message); - } - } - - internal static void ClearHotkey(Hotkey hotkey) - { - // do something - } +using GregsStack.InputSimulatorStandard.Native; +using HandheldCompanion.Controllers; +using HandheldCompanion.Controls; +using HandheldCompanion.Misc; +using HandheldCompanion.Properties; +using HandheldCompanion.Simulators; +using HandheldCompanion.Utils; +using HandheldCompanion.Views; +using Inkore.UI.WPF.Modern.Controls; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Windows; +using Windows.System; +using static HandheldCompanion.Managers.InputsHotkey; +using static HandheldCompanion.Managers.InputsManager; + +namespace HandheldCompanion.Managers; + +public static class HotkeysManager +{ + public delegate void CommandExecutedEventHandler(string listener); + + public delegate void HotkeyCreatedEventHandler(Hotkey hotkey); + + public delegate void HotkeyTypeCreatedEventHandler(InputsHotkeyType type); + + public delegate void HotkeyUpdatedEventHandler(Hotkey hotkey); + + public delegate void InitializedEventHandler(); + + private const short PIN_LIMIT = 18; + private static readonly string InstallPath; + private static bool hasProfileHID = false; + public static SortedDictionary<ushort, Hotkey> Hotkeys = new(); + + private static bool IsInitialized; + + static HotkeysManager() + { + // initialize path + InstallPath = Path.Combine(MainWindow.SettingsPath, "hotkeys"); + if (!Directory.Exists(InstallPath)) + Directory.CreateDirectory(InstallPath); + + InputsManager.TriggerUpdated += TriggerUpdated; + InputsManager.TriggerRaised += TriggerRaised; + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + ControllerManager.ControllerSelected += ControllerManager_ControllerSelected; + ControllerManager.ControllerPlugged += ControllerManager_ControllerPlugged; + ControllerManager.ControllerUnplugged += ControllerManager_ControllerUnplugged; + ProfileManager.Applied += ProfileManager_Applied; + VirtualManager.ControllerSelected += VirtualManager_ControllerSelected; + } + + public static event HotkeyTypeCreatedEventHandler HotkeyTypeCreated; + + public static event HotkeyCreatedEventHandler HotkeyCreated; + + public static event HotkeyUpdatedEventHandler HotkeyUpdated; + + public static event CommandExecutedEventHandler CommandExecuted; + + public static event InitializedEventHandler Initialized; + + private static void ControllerManager_ControllerSelected(IController Controller) + { + foreach (var hotkey in Hotkeys.Values) + hotkey.ControllerSelected(Controller); + } + + private static void ControllerManager_ControllerPlugged(IController Controller, bool IsPowerCycling) + { + // when the target emulated controller is Dualshock + // only enable HIDmode switch hotkey when controller is plugged (last stage of HIDmode change in this case) + var targetHIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); + if (targetHIDmode == HIDmode.DualShock4Controller) + { + var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals("shortcutChangeHIDMode")); + foreach (var hotkey in hotkeys) + { + Application.Current.Dispatcher.BeginInvoke(() => { hotkey.IsEnabled = !hasProfileHID; }); + } + } + } + + private static void ControllerManager_ControllerUnplugged(IController Controller, bool IsPowerCycling) + { + // when the target emulated controller is Xbox Controller + // only enable HIDmode switch hotkey when controller is unplugged (last stage of HIDmode change in this case) + var targetHIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); + + if (targetHIDmode == HIDmode.Xbox360Controller) + { + var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals("shortcutChangeHIDMode")); + foreach (var hotkey in hotkeys) + { + Application.Current.Dispatcher.Invoke(() => { hotkey.IsEnabled = !hasProfileHID; }); + } + } + } + + private static void ProfileManager_Applied(Profile profile, UpdateSource source) + { + // check if profile-specific HIDmode -> disable emulated controller hotkey, else -> enable it + HIDmode HIDmode; + + switch ((HIDmode)profile.HID) + { + case HIDmode.Xbox360Controller: + case HIDmode.DualShock4Controller: + { + hasProfileHID = true; + HIDmode = (HIDmode)profile.HID; // Applies profile-specific HID + break; + } + + default: // Default + { + HIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); // Applies default HID from settings + hasProfileHID = false; + break; + } + } + + // enable/disable hotkey based on profile HIDmode + var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals("shortcutChangeHIDMode")); + foreach (var hotkey in hotkeys) + Application.Current.Dispatcher.Invoke(() => + { + hotkey.IsEnabled = !hasProfileHID; + }); + + // change glyph at startup only + if (!IsInitialized) + { + VirtualManager_ControllerSelected(HIDmode); + } + } + + private static void VirtualManager_ControllerSelected(HIDmode HIDmode) + { + // change glyph of shortcutChangeHIDMode to the corresponding target emulated controller + var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals("shortcutChangeHIDMode")); + foreach (var hotkey in hotkeys) + { + switch (HIDmode) + { + case HIDmode.Xbox360Controller: + hotkey.inputsHotkey.Glyph = "\uE001"; + break; + case HIDmode.DualShock4Controller: + hotkey.inputsHotkey.Glyph = "\uE000"; + break; + default: + break; + } + + // redraw to change glyph + hotkey.Draw(); + } + } + + + public static void Start() + { + // process hotkeys types + foreach (var type in (InputsHotkeyType[])Enum.GetValues(typeof(InputsHotkeyType))) + HotkeyTypeCreated?.Invoke(type); + + // process hotkeys + foreach (var pair in InputsHotkeys) + { + var Id = pair.Key; + var inputsHotkey = pair.Value; + + Hotkey hotkey = null; + + var fileName = Path.Combine(InstallPath, $"{inputsHotkey.Listener}.json"); + + // check for existing hotkey + if (File.Exists(fileName)) + hotkey = ProcessHotkey(fileName); + + // no hotkey found or failed parsing + if (hotkey is null) + { + hotkey = new Hotkey(Id); + hotkey.IsPinned = inputsHotkey.DefaultPinned; + } + + // hotkey is outdated and using an unknown inputs hotkey + if (!InputsHotkeys.TryGetValue(hotkey.hotkeyId, out var foundHotkey)) + continue; + + // pull inputs hotkey + hotkey.SetInputsHotkey(foundHotkey); + hotkey.Draw(); + + if (!Hotkeys.ContainsKey(hotkey.hotkeyId)) + Hotkeys.Add(hotkey.hotkeyId, hotkey); + } + + foreach (var hotkey in Hotkeys.Values) + { + hotkey.Listening += StartListening; + hotkey.Pinning += PinOrUnpinHotkey; + hotkey.Summoned += hotkey => InvokeTrigger(hotkey, false, true); + hotkey.Updated += hotkey => SerializeHotkey(hotkey, true); + + if (!string.IsNullOrEmpty(hotkey.inputsHotkey.Settings)) + hotkey.IsEnabled = SettingsManager.GetBoolean(hotkey.inputsHotkey.Settings); + + HotkeyCreated?.Invoke(hotkey); + } + + IsInitialized = true; + Initialized?.Invoke(); + + LogManager.LogInformation("{0} has started", "HotkeysManager"); + } + + public static void Stop() + { + if (!IsInitialized) + return; + + IsInitialized = false; + + ControllerManager.ControllerPlugged -= ControllerManager_ControllerPlugged; + ControllerManager.ControllerUnplugged -= ControllerManager_ControllerUnplugged; + ProfileManager.Applied -= ProfileManager_Applied; + VirtualManager.ControllerSelected -= VirtualManager_ControllerSelected; + + LogManager.LogInformation("{0} has stopped", "HotkeysManager"); + } + + private static void SettingsManager_SettingValueChanged(string name, object value) + { + // manage toggle type hotkeys + foreach (var hotkey in Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals(name))) + { + if (!hotkey.inputsHotkey.IsToggle) + continue; + + var toggle = Convert.ToBoolean(value); + hotkey.SetToggle(toggle); + } + + // manage settings type hotkeys + foreach (var hotkey in Hotkeys.Values.Where(item => item.inputsHotkey.Settings.Contains(name))) + { + var enabled = SettingsManager.GetBoolean(hotkey.inputsHotkey.Settings); + hotkey.IsEnabled = enabled; + } + } + + private static void StartListening(Hotkey hotkey, ListenerType type) + { + InputsManager.StartListening(hotkey, type); + hotkey.StartListening(type); + } + + private static void PinOrUnpinHotkey(Hotkey hotkey) + { + switch (hotkey.IsPinned) + { + case false: + { + var count = CountPinned(); + + if (count >= PIN_LIMIT) + { + _ = Dialog.ShowAsync($"{Resources.SettingsPage_UpdateWarning}", + $"You can't pin more than {PIN_LIMIT} hotkeys", + ContentDialogButton.Primary, string.Empty, $"{Resources.ProfilesPage_OK}"); + + return; + } + + hotkey.IsPinned = true; + } + break; + case true: + hotkey.IsPinned = false; + break; + } + + // overwrite current file + SerializeHotkey(hotkey, true); + } + + private static int CountPinned() + { + return Hotkeys.Values.Count(item => item.IsPinned); + } + + private static void TriggerUpdated(string listener, InputsChord inputs, ListenerType type) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // we use @ as a special character to link two ore more listeners together + listener = listener.TrimEnd('@'); + + var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Contains(listener)); + foreach (var hotkey in hotkeys) + { + hotkey.StopListening(inputs, type); + + // overwrite current file + SerializeHotkey(hotkey, true); + } + }); + } + + private static Hotkey ProcessHotkey(string fileName) + { + Hotkey hotkey = null; + try + { + var outputraw = File.ReadAllText(fileName); + hotkey = JsonConvert.DeserializeObject<Hotkey>(outputraw); + } + catch (Exception ex) + { + LogManager.LogError("Could not parse hotkey {0}. {1}", fileName, ex.Message); + } + + return hotkey; + } + + public static void SerializeHotkey(Hotkey hotkey, bool overwrite = false) + { + var listener = hotkey.inputsHotkey.Listener; + + var settingsPath = Path.Combine(InstallPath, $"{listener}.json"); + if (!File.Exists(settingsPath) || overwrite) + { + var jsonString = JsonConvert.SerializeObject(hotkey, Formatting.Indented); + if (FileUtils.IsFileWritable(settingsPath)) + File.WriteAllText(settingsPath, jsonString); + } + + // raise event + HotkeyUpdated?.Invoke(hotkey); + } + + public static void TriggerRaised(string listener, InputsChord input, InputsHotkeyType type, bool IsKeyDown, + bool IsKeyUp) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // we use @ as a special character to link two ore more listeners together + var trimmed = listener.TrimEnd('@'); + var hotkeys = Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Contains(trimmed)); + foreach (var hotkey in hotkeys) + hotkey.Highlight(); + }); + + // These are special shortcut keys with no related events + switch (type) + { + case InputsHotkeyType.Embedded: + return; + } + + var fProcess = ProcessManager.GetForegroundProcess(); + + try + { + switch (listener) + { + case "shortcutKeyboard": + { + var uiHostNoLaunch = new ProcessUtils.UIHostNoLaunch(); + var tipInvocation = (ProcessUtils.ITipInvocation)uiHostNoLaunch; + tipInvocation.Toggle(ProcessUtils.GetDesktopWindow()); + Marshal.ReleaseComObject(uiHostNoLaunch); + } + break; + case "shortcutDesktop": + KeyboardSimulator.KeyPress(new[] { VirtualKeyCode.LWIN, VirtualKeyCode.VK_D }); + break; + case "shortcutESC": + if (fProcess is not null) + { + ProcessUtils.SetForegroundWindow(fProcess.MainWindowHandle); + KeyboardSimulator.KeyPress(VirtualKeyCode.ESCAPE); + } + + break; + case "shortcutExpand": + if (fProcess is not null) + { + var Placement = ProcessUtils.GetPlacement(fProcess.MainWindowHandle); + + switch (Placement.showCmd) + { + case ProcessUtils.ShowWindowCommands.Normal: + case ProcessUtils.ShowWindowCommands.Minimized: + ProcessUtils.ShowWindow(fProcess.MainWindowHandle, + (int)ProcessUtils.ShowWindowCommands.Maximized); + break; + case ProcessUtils.ShowWindowCommands.Maximized: + ProcessUtils.ShowWindow(fProcess.MainWindowHandle, + (int)ProcessUtils.ShowWindowCommands.Restored); + break; + } + } + + break; + case "shortcutTaskview": + KeyboardSimulator.KeyPress(new[] { VirtualKeyCode.LWIN, VirtualKeyCode.TAB }); + break; + case "shortcutTaskManager": + KeyboardSimulator.KeyPress(new[] + { VirtualKeyCode.LCONTROL, VirtualKeyCode.LSHIFT, VirtualKeyCode.ESCAPE }); + break; + case "shortcutActionCenter": + { + var uri = new Uri("ms-actioncenter"); + var success = Launcher.LaunchUriAsync(uri); + } + break; + case "shortcutControlCenter": + { + var uri = new Uri( + "ms-actioncenter:controlcenter/&suppressAnimations=false&showFooter=true&allowPageNavigation=true"); + var success = Launcher.LaunchUriAsync(uri); + } + break; + case "shortcutPrintScreen": + KeyboardSimulator.KeyPress( + new[] { VirtualKeyCode.LWIN, VirtualKeyCode.LSHIFT, VirtualKeyCode.VK_S }); + break; + case "suspendResumeTask": + { + var sProcess = ProcessManager.GetLastSuspendedProcess(); + + if (sProcess is null || sProcess.Filter != ProcessEx.ProcessFilter.Allowed) + break; + + if (sProcess.IsSuspended()) + ProcessManager.ResumeProcess(sProcess); + else + ProcessManager.SuspendProcess(fProcess); + } + break; + case "shortcutKillApp": + if (fProcess is not null) fProcess.Process.Kill(); + break; +<<<<<<< HEAD +======= + case "QuietModeToggled": + { + var value = !SettingsManager.GetBoolean(listener); + SettingsManager.SetProperty(listener, value); + + ToastManager.SendToast("Quiet mode", $"is now {(value ? "enabled" : "disabled")}"); + } + break; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + case "OnScreenDisplayLevel": + { + var value = !SettingsManager.GetBoolean(listener); + SettingsManager.SetProperty(listener, value); + } + break; + + // temporary settings + case "DesktopLayoutEnabled": + { + var value = !SettingsManager.GetBoolean(listener, true); + SettingsManager.SetProperty(listener, value, false, true); + + ToastManager.SendToast("Desktop layout", $"is now {(value ? "enabled" : "disabled")}"); + } + break; + + case "shortcutChangeHIDMode": + { + var currentHIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); + switch (currentHIDmode) + { + case HIDmode.Xbox360Controller: + SettingsManager.SetProperty("HIDmode", (int)HIDmode.DualShock4Controller); + break; + case HIDmode.DualShock4Controller: + SettingsManager.SetProperty("HIDmode", (int)HIDmode.Xbox360Controller); + break; + default: + break; + } + break; + } + + default: + KeyboardSimulator.KeyPress(input.OutputKeys.ToArray()); + break; + } + + LogManager.LogDebug("Executed Hotkey: {0}", listener); + + // play a tune to notify a command was executed + SystemManager.PlayWindowsMedia("Windows Navigation Start.wav"); + + // raise an event + CommandExecuted?.Invoke(listener); + } + catch (Exception ex) + { + LogManager.LogError("Failed to parse trigger {0}, {1}", listener, ex.Message); + } + } + + internal static void ClearHotkey(Hotkey hotkey) + { + // do something + } } \ No newline at end of file diff --git a/HandheldCompanion/Managers/InputsManager.cs b/HandheldCompanion/Managers/InputsManager.cs index 0c6add3db..d014830d2 100644 --- a/HandheldCompanion/Managers/InputsManager.cs +++ b/HandheldCompanion/Managers/InputsManager.cs @@ -1,700 +1,703 @@ -using Gma.System.MouseKeyHook; -using GregsStack.InputSimulatorStandard.Native; -using HandheldCompanion.Controllers; -using HandheldCompanion.Inputs; -using HandheldCompanion.Simulators; -using HandheldCompanion.Views; -using PrecisionTiming; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows.Forms; -using WindowsInput.Events; -using static HandheldCompanion.Managers.InputsHotkey; -using ButtonState = HandheldCompanion.Inputs.ButtonState; -using Timer = System.Timers.Timer; - -namespace HandheldCompanion.Managers; - -public static class InputsManager -{ - public delegate void InitializedEventHandler(); - - public delegate void TriggerRaisedEventHandler(string listener, InputsChord inputs, InputsHotkeyType type, - bool IsKeyDown, bool IsKeyUp); - - public delegate void TriggerUpdatedEventHandler(string listener, InputsChord inputs, ListenerType type); - - public enum ListenerType - { - Default, - Output, - UI - } - - private const short TIME_FLUSH = 5; // default interval between buffer flush - private const short TIME_SPAM = 50; // default interval between two allowed inputs - private const short TIME_FLUSH_EXTENDED = 150; // extended buffer flush interval when expecting another chord key - - private const short TIME_NEXT = 500; // default interval before submitting output keys used in combo - - private const short TIME_LONG = 600; // default interval between two inputs from a chord - // default interval before considering a chord as hold - - private const short - TIME_EXPIRED = 3000; // default interval before considering a chord as expired if no input is detected - - private const uint LLKHF_INJECTED = 0x00000010; - private const uint LLKHF_LOWER_IL_INJECTED = 0x00000002; - - // Gamepad variables - private static readonly PrecisionTimer KeyboardResetTimer; - - private static ButtonState prevState = new(); - - // InputsChord variables - private static InputsChord currentChord = new(); - private static InputsChord prevChord = new(); - private static readonly InputsChord storedChord = new(); - private static string SpecialKey; - - private static readonly Timer InputsChordHoldTimer; - private static readonly Timer InputsChordInputTimer; - - private static readonly Dictionary<KeyValuePair<KeyCode, bool>, int> prevKeys = new(); - - // Global variables - private static readonly Timer ListenerTimer; - - private static ListenerType currentType; - private static InputsHotkey currentHotkey = new(); - - private static readonly List<KeyEventArgsExt> BufferKeys = new(); - - private static readonly Dictionary<string, InputsChord> Triggers = new(); - - // Keyboard vars - private static IKeyboardMouseEvents m_GlobalHook; - - private static short KeyIndex; - private static bool KeyUsed; - - public static bool IsInitialized; - - private static bool IsKeyDown; - private static bool IsKeyUp; - - /* - * InputsManager v3 - * Note: I'd like to modify the InputSimulator library to extend its capacities and ModifiedKeyDown and ModifiedKeyUp - * https://github.com/GregsStack/InputSimulatorStandard - */ - - static InputsManager() - { - KeyboardResetTimer = new PrecisionTimer(); - KeyboardResetTimer.SetInterval(new Action(ReleaseKeyboardBuffer), TIME_FLUSH, false, 0, TimerMode.OneShot, true); - - ListenerTimer = new Timer(TIME_EXPIRED); - ListenerTimer.AutoReset = false; - ListenerTimer.Elapsed += (sender, e) => ListenerExpired(); - - InputsChordHoldTimer = new Timer(TIME_LONG); - InputsChordHoldTimer.AutoReset = false; - InputsChordHoldTimer.Elapsed += (sender, e) => InputsChordHold_Elapsed(); - - InputsChordInputTimer = new Timer(TIME_NEXT); - InputsChordInputTimer.AutoReset = false; - InputsChordInputTimer.Elapsed += (sender, e) => InputsChordInput_Elapsed(); - - HotkeysManager.HotkeyCreated += TriggerCreated; - } - - public static event TriggerRaisedEventHandler TriggerRaised; - - public static event TriggerUpdatedEventHandler TriggerUpdated; - - public static event InitializedEventHandler Initialized; - - private static void InputsChordHold_Elapsed() - { - // triggered when key is pressed for a long time - currentChord.InputsType = InputsChordType.Long; - CheckForSequence(true, false); - } - - private static void InputsChordInput_Elapsed() - { - // triggered after a key has been pressed (used by combo exclusively) - CheckForSequence(false, true); - } - - private static bool CheckForSequence(bool IsKeyDown, bool IsKeyUp) - { - if (currentChord.State.IsEmpty() && - currentChord.OutputKeys.Count == 0) - return false; - - // stop timers on KeyUp - if (IsKeyUp) - { - InputsChordHoldTimer.Stop(); - InputsChordInputTimer.Stop(); - } - - if (!IsListening) - { - var keys = GetTriggersFromChord(currentChord); - - if (keys.Count != 0) - { - LogManager.LogDebug("Captured: Buttons: {0}, Type: {1}, IsKeyDown: {2}", string.Join(',', currentChord.State.Buttons), - currentChord.InputsType, IsKeyDown); - - foreach (var key in keys) - { - var hotkey = InputsHotkeys.Values.FirstOrDefault(item => item.Listener == key); - if (hotkey is null) - continue; - - // special case HIDmode switch hotkey - // required here in order to (immediatly) disable hotkey while HIDmode is being changed to avoid duplicates - // this takes care of repeated keybinds actions - if (hotkey.Listener == "shortcutChangeHIDMode") - { - var inputType = currentChord.InputsType; - if ((inputType == InputsChordType.Click && IsKeyUp) || (inputType == InputsChordType.Long && IsKeyDown)) - { - var hidHotkeys = HotkeysManager.Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals("shortcutChangeHIDMode")); - foreach (var hidHotkey in hidHotkeys) - { - if (!hidHotkey.IsEnabled) - return false; - System.Windows.Application.Current.Dispatcher.Invoke(() => { hidHotkey.IsEnabled = false; }); - } - } - } - - var chord = Triggers[key]; - switch (chord.InputsType) - { - case InputsChordType.Click: - { - if (!hotkey.OnKeyDown && IsKeyDown) - continue; - - if (!hotkey.OnKeyUp && IsKeyUp) - continue; - } - break; - - case InputsChordType.Long: - { - if (IsKeyUp) - continue; - } - break; - } - - TriggerRaised?.Invoke(key, chord, hotkey.hotkeyType, IsKeyDown, IsKeyUp); - } - - return true; - } - - // get the associated keys - foreach (var chord in MainWindow.CurrentDevice.OEMChords.Where(a => currentChord.State.Contains(a.state))) - { - // it could be the currentChord isn't mapped but a InputsChordType.Long is - currentChord.InputsType = InputsChordType.Long; - keys = GetTriggersFromChord(currentChord); - if (keys.Count != 0) - return false; - - var layout = LayoutManager.GetCurrent(); - if (layout is not null) - foreach (var button in chord.state.Buttons) - if (layout.ButtonLayout.ContainsKey(button)) - return false; - - var chords = chord.chords[IsKeyDown]; - LogManager.LogDebug("Released: KeyCodes: {0}, IsKeyDown: {1}", string.Join(',', chords), IsKeyDown); - - if (IsKeyDown) - { - KeyboardSimulator.KeyDown(chords.ToArray()); - - // stop hold timer - InputsChordHoldTimer.Stop(); - } - - // else if (IsKeyUp) - KeyboardSimulator.KeyUp(chords.ToArray()); - - return true; - } - } - else - { - if (IsKeyDown) - switch (currentChord.InputsType) - { - case InputsChordType.Click: - return false; - } - - StopListening(currentChord); - } - - return false; - } - - private static List<KeyEventArgsExt> InjectModifiers(KeyEventArgsExt args) - { - List<KeyEventArgsExt> mods = new(); - - if (args.Modifiers == Keys.None) - return mods; - - foreach (var mode in ((Keys[])Enum.GetValues(typeof(Keys))).Where(a => a != Keys.None)) - if (args.Modifiers.HasFlag(mode)) - { - var mod = new KeyEventArgsExt(mode, args.ScanCode, args.Timestamp, args.IsKeyDown, args.IsKeyUp, true, - args.Flags); - mods.Add(mod); - } - - return mods; - } - - private static void SetInterval(PrecisionTimer timer, short interval) - { - if (timer.GetPeriod() == interval) - return; - - if (timer.IsRunning()) - timer.Stop(); - - timer.SetPeriod(interval); - } - - private static void M_GlobalHook_KeyEvent(object? sender, KeyEventArgs e) - { - KeyEventArgsExt args = (KeyEventArgsExt)e; - - bool Injected = (args.Flags & LLKHF_INJECTED) > 0; - bool InjectedLL = (args.Flags & LLKHF_LOWER_IL_INJECTED) > 0; - - if ((Injected || InjectedLL) && currentType != ListenerType.Output) - return; - - KeyCode hookKey = (KeyCode)args.KeyValue; - - KeyboardResetTimer.Stop(); - KeyUsed = false; - - // are we listening for keyboards inputs as part of a custom hotkey ? - if (currentType == ListenerType.Output) - { - args.SuppressKeyPress = true; - - // add key to InputsChord - currentChord.AddKey(args); - - InputsChordInputTimer.Stop(); - InputsChordInputTimer.Start(); - - return; - } - - foreach (DeviceChord? pair in MainWindow.CurrentDevice.OEMChords.Where(a => !a.silenced)) - { - List<KeyCode> chord = pair.chords[args.IsKeyDown]; - if (KeyIndex >= chord.Count) - continue; - - // simplified process for single key chords - if (chord.Count == 1) - { - if (chord[0] == hookKey) - { - // calls current controller (if connected) - IController controller = ControllerManager.GetTargetController(); - controller?.InjectState(pair.state, args.IsKeyDown, args.IsKeyUp); - return; - } - } - - KeyCode chordKey = chord[KeyIndex]; - if (chordKey == hookKey) - { - KeyUsed = true; - KeyIndex++; - - // increase interval as we're expecting a new chord key - SetInterval(KeyboardResetTimer, TIME_FLUSH_EXTENDED); - - break; // leave loop - } - - // restore default interval - SetInterval(KeyboardResetTimer, TIME_FLUSH); - } - - // if key is used or previous key was, we need to maintain key(s) order - if (KeyUsed || KeyIndex > 0) - { - args.SuppressKeyPress = true; - - // add key to buffer - BufferKeys.Add(args); - - // search for matching triggers - List<KeyCode> buffer_keys = GetChord(BufferKeys); - - foreach (DeviceChord? chord in MainWindow.CurrentDevice.OEMChords.Where(a => - a.chords[args.IsKeyDown].Count == BufferKeys.Count)) - { - // compare ordered enumerable - List<KeyCode> chord_keys = chord.GetChord(args.IsKeyDown); - - bool existsCheck = chord_keys.All(x => buffer_keys.Any(y => x == y)); - if (existsCheck) - { - // reset index - KeyIndex = 0; - - // check if inputs timestamp are too close from one to another - bool IsKeyUnexpected = args.IsKeyUp && string.IsNullOrEmpty(SpecialKey); - - // do not bother checking timing if key is already unexpected - if (!IsKeyUnexpected) - { - KeyValuePair<KeyCode, bool> pair = new KeyValuePair<KeyCode, bool>(hookKey, args.IsKeyDown); - int prevTimestamp = prevKeys.TryGetValue(pair, out var key) ? key : TIME_SPAM; - prevKeys[pair] = args.Timestamp; - - // spamming - if (args.Timestamp - prevTimestamp < TIME_SPAM) - IsKeyUnexpected = true; - } - - // clear buffer - BufferKeys.Clear(); - - // leave if inputs are too close - if (IsKeyUnexpected) - return; - - // calls current controller (if connected) - IController controller = ControllerManager.GetTargetController(); - controller?.InjectState(chord.state, args.IsKeyDown, args.IsKeyUp); - - if (args.IsKeyDown) - SpecialKey = chord.name; - else if (args.IsKeyUp) - SpecialKey = string.Empty; - - return; - } - } - } - else - { - // manage AltGr - if (args.IsKeyUp) - { - switch (args.KeyValue) - { - case 165: - KeyboardSimulator.KeyUp((VirtualKeyCode)162); - break; - } - } - } - - KeyboardResetTimer.Start(); - } - - private static List<string> GetTriggersFromChord(InputsChord lookup) - { - List<string> keys = new(); - - foreach (var pair in Triggers) - { - var key = pair.Key; - var chord = pair.Value; - - var InputsType = chord.InputsType; - var State = chord.State; - - if (InputsType.HasFlag(lookup.InputsType) && State.Buttons.Count() != 0 && lookup.State.Equals(State)) - keys.Add(key); - } - - return keys; - } - - private static void ReleaseKeyboardBuffer() - { - if (BufferKeys.Count == 0) - return; - - // reset index - KeyIndex = 0; - - var keys = BufferKeys.OrderBy(a => a.Timestamp).ToList(); - for (var i = 0; i < keys.Count; i++) - { - var args = keys[i]; - - switch (args.IsKeyDown) - { - case true: - KeyboardSimulator.KeyDown(args); - break; - case false: - KeyboardSimulator.KeyUp(args); - break; - } - - // clear buffer - BufferKeys.Remove(args); - } - - // clear buffer - BufferKeys.Clear(); - } - - private static List<KeyCode> GetChord(List<KeyEventArgsExt> args) - { - return args.Select(a => (KeyCode)a.KeyValue).OrderBy(key => key).ToList(); - } - - public static void Start() - { - if (MainWindow.CurrentDevice.HasKey()) - InitGlobalHook(); - - IsInitialized = true; - Initialized?.Invoke(); - - LogManager.LogInformation("{0} has started", "InputsManager"); - } - - public static void Stop() - { - if (!IsInitialized) - return; - - IsInitialized = false; - - DisposeGlobalHook(); - - LogManager.LogInformation("{0} has stopped", "InputsManager"); - } - - private static void InitGlobalHook() - { - if (m_GlobalHook is not null) - return; - - m_GlobalHook = Hook.GlobalEvents(); - m_GlobalHook.KeyDown += M_GlobalHook_KeyEvent; - m_GlobalHook.KeyUp += M_GlobalHook_KeyEvent; - } - - private static void DisposeGlobalHook() - { - if (m_GlobalHook is null) - return; - - m_GlobalHook.KeyDown -= M_GlobalHook_KeyEvent; - m_GlobalHook.KeyUp -= M_GlobalHook_KeyEvent; - m_GlobalHook.Dispose(); - m_GlobalHook = null; - } - - public static void UpdateReport(ButtonState buttonState) - { - // half-press should be removed if full-press is also present - if (currentChord.State[ButtonFlags.L2Full]) - { - currentChord.State[ButtonFlags.L2Soft] = false; - storedChord.State[ButtonFlags.L2Soft] = false; - buttonState[ButtonFlags.L2Soft] = false; - } - - if (currentChord.State[ButtonFlags.R2Full]) - { - currentChord.State[ButtonFlags.R2Soft] = false; - storedChord.State[ButtonFlags.R2Soft] = false; - buttonState[ButtonFlags.R2Soft] = false; - } - - if (currentChord.State[ButtonFlags.LeftStickClick]) - { - currentChord.State[ButtonFlags.LeftStickTouch] = false; - storedChord.State[ButtonFlags.LeftStickTouch] = false; - buttonState[ButtonFlags.LeftStickTouch] = false; - } - - if (currentChord.State[ButtonFlags.RightStickClick]) - { - currentChord.State[ButtonFlags.RightStickTouch] = false; - storedChord.State[ButtonFlags.RightStickTouch] = false; - buttonState[ButtonFlags.RightStickTouch] = false; - } - - if (currentChord.State[ButtonFlags.LeftPadClick]) - { - currentChord.State[ButtonFlags.LeftPadTouch] = false; - storedChord.State[ButtonFlags.LeftPadTouch] = false; - buttonState[ButtonFlags.LeftPadTouch] = false; - } - - if (currentChord.State[ButtonFlags.RightPadClick]) - { - currentChord.State[ButtonFlags.RightPadTouch] = false; - storedChord.State[ButtonFlags.RightPadTouch] = false; - buttonState[ButtonFlags.RightPadTouch] = false; - } - - if (prevState.Equals(buttonState)) - return; - - // reset hold timer - InputsChordHoldTimer.Stop(); - InputsChordHoldTimer.Start(); - - // IsKeyDown - if (!buttonState.IsEmpty()) - { - currentChord.State = buttonState.Clone() as ButtonState; - storedChord.State.AddRange(buttonState); - - currentChord.InputsType = InputsChordType.Click; - - IsKeyDown = true; - IsKeyUp = false; - } - // IsKeyUp - else if (IsKeyDown && !currentChord.State.Equals(buttonState)) - { - IsKeyUp = true; - IsKeyDown = false; - - currentChord.State = storedChord.State.Clone() as ButtonState; - } - - var success = CheckForSequence(IsKeyDown, IsKeyUp); - - if (buttonState.IsEmpty() && IsKeyUp) - { - currentChord.State.Clear(); - storedChord.State.Clear(); - } - - prevState = buttonState.Clone() as ButtonState; - - // GamepadResetTimer.Start(); - } - - public static bool IsListening => !string.IsNullOrEmpty(currentHotkey.Listener); - - public static void StartListening(Hotkey hotkey, ListenerType type) - { - if (!MainWindow.CurrentDevice.HasKey()) - InitGlobalHook(); - - // force expiration on previous listener, if any - if (IsListening) - ListenerExpired(); - - // store current hotkey values - prevChord = new InputsChord(hotkey.inputsChord.State, hotkey.inputsChord.OutputKeys, - hotkey.inputsChord.InputsType); - - currentHotkey = hotkey.inputsHotkey; - currentChord = hotkey.inputsChord; - currentType = type; - - switch (type) - { - case ListenerType.Output: - currentChord.OutputKeys.Clear(); - break; - default: - case ListenerType.UI: - case ListenerType.Default: - currentChord.State.Clear(); - break; - } - - BufferKeys.Clear(); - - ListenerTimer.Start(); - } - - private static void StopListening(InputsChord inputsChord = null) - { - if (!MainWindow.CurrentDevice.HasKey()) - DisposeGlobalHook(); - - if (inputsChord is null) - inputsChord = new InputsChord(); - - switch (currentType) - { - case ListenerType.Default: - case ListenerType.Output: - case ListenerType.UI: - Triggers[currentHotkey.Listener] = - new InputsChord(inputsChord.State, inputsChord.OutputKeys, inputsChord.InputsType); - break; - } - - TriggerUpdated?.Invoke(currentHotkey.Listener, inputsChord, currentType); - - LogManager.LogDebug("Trigger: {0} updated. buttons: {1}, type: {2}", currentHotkey.Listener, - string.Join(",", inputsChord.State.Buttons), inputsChord.InputsType); - - currentHotkey = new InputsHotkey(); - currentChord = new InputsChord(); - currentType = ListenerType.Default; - - ListenerTimer.Stop(); - InputsChordHoldTimer.Stop(); - InputsChordInputTimer.Stop(); - } - - private static void ListenerExpired() - { - // restore previous chord - StopListening(prevChord); - } - - public static void ClearListening(Hotkey hotkey) - { - currentHotkey = hotkey.inputsHotkey; - StopListening(); - } - - private static void TriggerCreated(Hotkey hotkey) - { - var listener = hotkey.inputsHotkey.Listener; - - Triggers.TryAdd(listener, hotkey.inputsChord); - } - - internal static void InvokeTrigger(Hotkey hotkey, bool IsKeyDown, bool IsKeyUp) - { - if (IsKeyDown && hotkey.inputsHotkey.OnKeyDown) - TriggerRaised?.Invoke(hotkey.inputsHotkey.Listener, hotkey.inputsChord, hotkey.inputsHotkey.hotkeyType, - true, false); - - if (IsKeyUp && hotkey.inputsHotkey.OnKeyUp) - TriggerRaised?.Invoke(hotkey.inputsHotkey.Listener, hotkey.inputsChord, hotkey.inputsHotkey.hotkeyType, - false, true); - } +using Gma.System.MouseKeyHook; +using GregsStack.InputSimulatorStandard.Native; +<<<<<<< HEAD +using HandheldCompanion.Controllers; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using HandheldCompanion.Inputs; +using HandheldCompanion.Simulators; +using HandheldCompanion.Views; +using PrecisionTiming; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; +using WindowsInput.Events; +using static HandheldCompanion.Managers.InputsHotkey; +using ButtonState = HandheldCompanion.Inputs.ButtonState; +using Timer = System.Timers.Timer; + +namespace HandheldCompanion.Managers; + +public static class InputsManager +{ + public delegate void InitializedEventHandler(); + + public delegate void TriggerRaisedEventHandler(string listener, InputsChord inputs, InputsHotkeyType type, + bool IsKeyDown, bool IsKeyUp); + + public delegate void TriggerUpdatedEventHandler(string listener, InputsChord inputs, ListenerType type); + + public enum ListenerType + { + Default, + Output, + UI + } + + private const short TIME_FLUSH = 5; // default interval between buffer flush + private const short TIME_SPAM = 50; // default interval between two allowed inputs + private const short TIME_FLUSH_EXTENDED = 150; // extended buffer flush interval when expecting another chord key + + private const short TIME_NEXT = 500; // default interval before submitting output keys used in combo + + private const short TIME_LONG = 600; // default interval between two inputs from a chord + // default interval before considering a chord as hold + + private const short + TIME_EXPIRED = 3000; // default interval before considering a chord as expired if no input is detected + + private const uint LLKHF_INJECTED = 0x00000010; + private const uint LLKHF_LOWER_IL_INJECTED = 0x00000002; + + // Gamepad variables + private static readonly PrecisionTimer KeyboardResetTimer; + + private static ButtonState prevState = new(); + + // InputsChord variables + private static InputsChord currentChord = new(); + private static InputsChord prevChord = new(); + private static readonly InputsChord storedChord = new(); + private static string SpecialKey; + + private static readonly Timer InputsChordHoldTimer; + private static readonly Timer InputsChordInputTimer; + + private static readonly Dictionary<KeyValuePair<KeyCode, bool>, int> prevKeys = new(); + + // Global variables + private static readonly Timer ListenerTimer; + + private static ListenerType currentType; + private static InputsHotkey currentHotkey = new(); + + private static readonly List<KeyEventArgsExt> BufferKeys = new(); + + private static readonly Dictionary<string, InputsChord> Triggers = new(); + + // Keyboard vars + private static IKeyboardMouseEvents m_GlobalHook; + + private static short KeyIndex; + private static bool KeyUsed; + + public static bool IsInitialized; + + private static bool IsKeyDown; + private static bool IsKeyUp; + + /* + * InputsManager v3 + * Note: I'd like to modify the InputSimulator library to extend its capacities and ModifiedKeyDown and ModifiedKeyUp + * https://github.com/GregsStack/InputSimulatorStandard + */ + + static InputsManager() + { + KeyboardResetTimer = new PrecisionTimer(); + KeyboardResetTimer.SetInterval(new Action(ReleaseKeyboardBuffer), TIME_FLUSH, false, 0, TimerMode.OneShot, true); + + ListenerTimer = new Timer(TIME_EXPIRED); + ListenerTimer.AutoReset = false; + ListenerTimer.Elapsed += (sender, e) => ListenerExpired(); + + InputsChordHoldTimer = new Timer(TIME_LONG); + InputsChordHoldTimer.AutoReset = false; + InputsChordHoldTimer.Elapsed += (sender, e) => InputsChordHold_Elapsed(); + + InputsChordInputTimer = new Timer(TIME_NEXT); + InputsChordInputTimer.AutoReset = false; + InputsChordInputTimer.Elapsed += (sender, e) => InputsChordInput_Elapsed(); + + HotkeysManager.HotkeyCreated += TriggerCreated; + } + + public static event TriggerRaisedEventHandler TriggerRaised; + + public static event TriggerUpdatedEventHandler TriggerUpdated; + + public static event InitializedEventHandler Initialized; + + private static void InputsChordHold_Elapsed() + { + // triggered when key is pressed for a long time + currentChord.InputsType = InputsChordType.Long; + CheckForSequence(true, false); + } + + private static void InputsChordInput_Elapsed() + { + // triggered after a key has been pressed (used by combo exclusively) + CheckForSequence(false, true); + } + + private static bool CheckForSequence(bool IsKeyDown, bool IsKeyUp) + { + if (currentChord.State.IsEmpty() && + currentChord.OutputKeys.Count == 0) + return false; + + // stop timers on KeyUp + if (IsKeyUp) + { + InputsChordHoldTimer.Stop(); + InputsChordInputTimer.Stop(); + } + + if (!IsListening) + { + var keys = GetTriggersFromChord(currentChord); + + if (keys.Count != 0) + { + LogManager.LogDebug("Captured: Buttons: {0}, Type: {1}, IsKeyDown: {2}", string.Join(',', currentChord.State.Buttons), + currentChord.InputsType, IsKeyDown); + + foreach (var key in keys) + { + var hotkey = InputsHotkeys.Values.FirstOrDefault(item => item.Listener == key); + if (hotkey is null) + continue; + + // special case HIDmode switch hotkey + // required here in order to (immediatly) disable hotkey while HIDmode is being changed to avoid duplicates + // this takes care of repeated keybinds actions + if (hotkey.Listener == "shortcutChangeHIDMode") + { + var inputType = currentChord.InputsType; + if ((inputType == InputsChordType.Click && IsKeyUp) || (inputType == InputsChordType.Long && IsKeyDown)) + { + var hidHotkeys = HotkeysManager.Hotkeys.Values.Where(item => item.inputsHotkey.Listener.Equals("shortcutChangeHIDMode")); + foreach (var hidHotkey in hidHotkeys) + { + if (!hidHotkey.IsEnabled) + return false; + System.Windows.Application.Current.Dispatcher.Invoke(() => { hidHotkey.IsEnabled = false; }); + } + } + } + + var chord = Triggers[key]; + switch (chord.InputsType) + { + case InputsChordType.Click: + { + if (!hotkey.OnKeyDown && IsKeyDown) + continue; + + if (!hotkey.OnKeyUp && IsKeyUp) + continue; + } + break; + + case InputsChordType.Long: + { + if (IsKeyUp) + continue; + } + break; + } + + TriggerRaised?.Invoke(key, chord, hotkey.hotkeyType, IsKeyDown, IsKeyUp); + } + + return true; + } + + // get the associated keys + foreach (var chord in MainWindow.CurrentDevice.OEMChords.Where(a => currentChord.State.Contains(a.state))) + { + // it could be the currentChord isn't mapped but a InputsChordType.Long is + currentChord.InputsType = InputsChordType.Long; + keys = GetTriggersFromChord(currentChord); + if (keys.Count != 0) + return false; + + var layout = LayoutManager.GetCurrent(); + if (layout is not null) + foreach (var button in chord.state.Buttons) + if (layout.ButtonLayout.ContainsKey(button)) + return false; + + var chords = chord.chords[IsKeyDown]; + LogManager.LogDebug("Released: KeyCodes: {0}, IsKeyDown: {1}", string.Join(',', chords), IsKeyDown); + + if (IsKeyDown) + { + KeyboardSimulator.KeyDown(chords.ToArray()); + + // stop hold timer + InputsChordHoldTimer.Stop(); + } + + // else if (IsKeyUp) + KeyboardSimulator.KeyUp(chords.ToArray()); + + return true; + } + } + else + { + if (IsKeyDown) + switch (currentChord.InputsType) + { + case InputsChordType.Click: + return false; + } + + StopListening(currentChord); + } + + return false; + } + + private static List<KeyEventArgsExt> InjectModifiers(KeyEventArgsExt args) + { + List<KeyEventArgsExt> mods = new(); + + if (args.Modifiers == Keys.None) + return mods; + + foreach (var mode in ((Keys[])Enum.GetValues(typeof(Keys))).Where(a => a != Keys.None)) + if (args.Modifiers.HasFlag(mode)) + { + var mod = new KeyEventArgsExt(mode, args.ScanCode, args.Timestamp, args.IsKeyDown, args.IsKeyUp, true, + args.Flags); + mods.Add(mod); + } + + return mods; + } + + private static void SetInterval(PrecisionTimer timer, short interval) + { + if (timer.GetPeriod() == interval) + return; + + if (timer.IsRunning()) + timer.Stop(); + + timer.SetPeriod(interval); + } + + private static void M_GlobalHook_KeyEvent(object? sender, KeyEventArgs e) + { + KeyEventArgsExt args = (KeyEventArgsExt)e; + + bool Injected = (args.Flags & LLKHF_INJECTED) > 0; + bool InjectedLL = (args.Flags & LLKHF_LOWER_IL_INJECTED) > 0; + + if ((Injected || InjectedLL) && currentType != ListenerType.Output) + return; + + KeyCode hookKey = (KeyCode)args.KeyValue; + + KeyboardResetTimer.Stop(); + KeyUsed = false; + + // are we listening for keyboards inputs as part of a custom hotkey ? + if (currentType == ListenerType.Output) + { + args.SuppressKeyPress = true; + + // add key to InputsChord + currentChord.AddKey(args); + + InputsChordInputTimer.Stop(); + InputsChordInputTimer.Start(); + + return; + } + + foreach (DeviceChord? pair in MainWindow.CurrentDevice.OEMChords.Where(a => !a.silenced)) + { + List<KeyCode> chord = pair.chords[args.IsKeyDown]; + if (KeyIndex >= chord.Count) + continue; + + // simplified process for single key chords + if (chord.Count == 1) + { + if (chord[0] == hookKey) + { + // calls current controller (if connected) + IController controller = ControllerManager.GetTargetController(); + controller?.InjectState(pair.state, args.IsKeyDown, args.IsKeyUp); + return; + } + } + + KeyCode chordKey = chord[KeyIndex]; + if (chordKey == hookKey) + { + KeyUsed = true; + KeyIndex++; + + // increase interval as we're expecting a new chord key + SetInterval(KeyboardResetTimer, TIME_FLUSH_EXTENDED); + + break; // leave loop + } + + // restore default interval + SetInterval(KeyboardResetTimer, TIME_FLUSH); + } + + // if key is used or previous key was, we need to maintain key(s) order + if (KeyUsed || KeyIndex > 0) + { + args.SuppressKeyPress = true; + + // add key to buffer + BufferKeys.Add(args); + + // search for matching triggers + List<KeyCode> buffer_keys = GetChord(BufferKeys); + + foreach (DeviceChord? chord in MainWindow.CurrentDevice.OEMChords.Where(a => + a.chords[args.IsKeyDown].Count == BufferKeys.Count)) + { + // compare ordered enumerable + List<KeyCode> chord_keys = chord.GetChord(args.IsKeyDown); + + bool existsCheck = chord_keys.All(x => buffer_keys.Any(y => x == y)); + if (existsCheck) + { + // reset index + KeyIndex = 0; + + // check if inputs timestamp are too close from one to another + bool IsKeyUnexpected = args.IsKeyUp && string.IsNullOrEmpty(SpecialKey); + + // do not bother checking timing if key is already unexpected + if (!IsKeyUnexpected) + { + KeyValuePair<KeyCode, bool> pair = new KeyValuePair<KeyCode, bool>(hookKey, args.IsKeyDown); + int prevTimestamp = prevKeys.TryGetValue(pair, out var key) ? key : TIME_SPAM; + prevKeys[pair] = args.Timestamp; + + // spamming + if (args.Timestamp - prevTimestamp < TIME_SPAM) + IsKeyUnexpected = true; + } + + // clear buffer + BufferKeys.Clear(); + + // leave if inputs are too close + if (IsKeyUnexpected) + return; + + // calls current controller (if connected) + IController controller = ControllerManager.GetTargetController(); + controller?.InjectState(chord.state, args.IsKeyDown, args.IsKeyUp); + + if (args.IsKeyDown) + SpecialKey = chord.name; + else if (args.IsKeyUp) + SpecialKey = string.Empty; + + return; + } + } + } + else + { + // manage AltGr + if (args.IsKeyUp) + { + switch (args.KeyValue) + { + case 165: + KeyboardSimulator.KeyUp((VirtualKeyCode)162); + break; + } + } + } + + KeyboardResetTimer.Start(); + } + + private static List<string> GetTriggersFromChord(InputsChord lookup) + { + List<string> keys = new(); + + foreach (var pair in Triggers) + { + var key = pair.Key; + var chord = pair.Value; + + var InputsType = chord.InputsType; + var State = chord.State; + + if (InputsType.HasFlag(lookup.InputsType) && State.Buttons.Count() != 0 && lookup.State.Equals(State)) + keys.Add(key); + } + + return keys; + } + + private static void ReleaseKeyboardBuffer() + { + if (BufferKeys.Count == 0) + return; + + // reset index + KeyIndex = 0; + + var keys = BufferKeys.OrderBy(a => a.Timestamp).ToList(); + for (var i = 0; i < keys.Count; i++) + { + var args = keys[i]; + + switch (args.IsKeyDown) + { + case true: + KeyboardSimulator.KeyDown(args); + break; + case false: + KeyboardSimulator.KeyUp(args); + break; + } + + // clear buffer + BufferKeys.Remove(args); + } + + // clear buffer + BufferKeys.Clear(); + } + + private static List<KeyCode> GetChord(List<KeyEventArgsExt> args) + { + return args.Select(a => (KeyCode)a.KeyValue).OrderBy(key => key).ToList(); + } + + public static void Start() + { + if (MainWindow.CurrentDevice.HasKey()) + InitGlobalHook(); + + IsInitialized = true; + Initialized?.Invoke(); + + LogManager.LogInformation("{0} has started", "InputsManager"); + } + + public static void Stop() + { + if (!IsInitialized) + return; + + IsInitialized = false; + + DisposeGlobalHook(); + + LogManager.LogInformation("{0} has stopped", "InputsManager"); + } + + private static void InitGlobalHook() + { + if (m_GlobalHook is not null) + return; + + m_GlobalHook = Hook.GlobalEvents(); + m_GlobalHook.KeyDown += M_GlobalHook_KeyEvent; + m_GlobalHook.KeyUp += M_GlobalHook_KeyEvent; + } + + private static void DisposeGlobalHook() + { + if (m_GlobalHook is null) + return; + + m_GlobalHook.KeyDown -= M_GlobalHook_KeyEvent; + m_GlobalHook.KeyUp -= M_GlobalHook_KeyEvent; + m_GlobalHook.Dispose(); + m_GlobalHook = null; + } + + public static void UpdateReport(ButtonState buttonState) + { + // half-press should be removed if full-press is also present + if (currentChord.State[ButtonFlags.L2Full]) + { + currentChord.State[ButtonFlags.L2Soft] = false; + storedChord.State[ButtonFlags.L2Soft] = false; + buttonState[ButtonFlags.L2Soft] = false; + } + + if (currentChord.State[ButtonFlags.R2Full]) + { + currentChord.State[ButtonFlags.R2Soft] = false; + storedChord.State[ButtonFlags.R2Soft] = false; + buttonState[ButtonFlags.R2Soft] = false; + } + + if (currentChord.State[ButtonFlags.LeftStickClick]) + { + currentChord.State[ButtonFlags.LeftStickTouch] = false; + storedChord.State[ButtonFlags.LeftStickTouch] = false; + buttonState[ButtonFlags.LeftStickTouch] = false; + } + + if (currentChord.State[ButtonFlags.RightStickClick]) + { + currentChord.State[ButtonFlags.RightStickTouch] = false; + storedChord.State[ButtonFlags.RightStickTouch] = false; + buttonState[ButtonFlags.RightStickTouch] = false; + } + + if (currentChord.State[ButtonFlags.LeftPadClick]) + { + currentChord.State[ButtonFlags.LeftPadTouch] = false; + storedChord.State[ButtonFlags.LeftPadTouch] = false; + buttonState[ButtonFlags.LeftPadTouch] = false; + } + + if (currentChord.State[ButtonFlags.RightPadClick]) + { + currentChord.State[ButtonFlags.RightPadTouch] = false; + storedChord.State[ButtonFlags.RightPadTouch] = false; + buttonState[ButtonFlags.RightPadTouch] = false; + } + + if (prevState.Equals(buttonState)) + return; + + // reset hold timer + InputsChordHoldTimer.Stop(); + InputsChordHoldTimer.Start(); + + // IsKeyDown + if (!buttonState.IsEmpty()) + { + currentChord.State = buttonState.Clone() as ButtonState; + storedChord.State.AddRange(buttonState); + + currentChord.InputsType = InputsChordType.Click; + + IsKeyDown = true; + IsKeyUp = false; + } + // IsKeyUp + else if (IsKeyDown && !currentChord.State.Equals(buttonState)) + { + IsKeyUp = true; + IsKeyDown = false; + + currentChord.State = storedChord.State.Clone() as ButtonState; + } + + var success = CheckForSequence(IsKeyDown, IsKeyUp); + + if (buttonState.IsEmpty() && IsKeyUp) + { + currentChord.State.Clear(); + storedChord.State.Clear(); + } + + prevState = buttonState.Clone() as ButtonState; + + // GamepadResetTimer.Start(); + } + + public static bool IsListening => !string.IsNullOrEmpty(currentHotkey.Listener); + + public static void StartListening(Hotkey hotkey, ListenerType type) + { + if (!MainWindow.CurrentDevice.HasKey()) + InitGlobalHook(); + + // force expiration on previous listener, if any + if (IsListening) + ListenerExpired(); + + // store current hotkey values + prevChord = new InputsChord(hotkey.inputsChord.State, hotkey.inputsChord.OutputKeys, + hotkey.inputsChord.InputsType); + + currentHotkey = hotkey.inputsHotkey; + currentChord = hotkey.inputsChord; + currentType = type; + + switch (type) + { + case ListenerType.Output: + currentChord.OutputKeys.Clear(); + break; + default: + case ListenerType.UI: + case ListenerType.Default: + currentChord.State.Clear(); + break; + } + + BufferKeys.Clear(); + + ListenerTimer.Start(); + } + + private static void StopListening(InputsChord inputsChord = null) + { + if (!MainWindow.CurrentDevice.HasKey()) + DisposeGlobalHook(); + + if (inputsChord is null) + inputsChord = new InputsChord(); + + switch (currentType) + { + case ListenerType.Default: + case ListenerType.Output: + case ListenerType.UI: + Triggers[currentHotkey.Listener] = + new InputsChord(inputsChord.State, inputsChord.OutputKeys, inputsChord.InputsType); + break; + } + + TriggerUpdated?.Invoke(currentHotkey.Listener, inputsChord, currentType); + + LogManager.LogDebug("Trigger: {0} updated. buttons: {1}, type: {2}", currentHotkey.Listener, + string.Join(",", inputsChord.State.Buttons), inputsChord.InputsType); + + currentHotkey = new InputsHotkey(); + currentChord = new InputsChord(); + currentType = ListenerType.Default; + + ListenerTimer.Stop(); + InputsChordHoldTimer.Stop(); + InputsChordInputTimer.Stop(); + } + + private static void ListenerExpired() + { + // restore previous chord + StopListening(prevChord); + } + + public static void ClearListening(Hotkey hotkey) + { + currentHotkey = hotkey.inputsHotkey; + StopListening(); + } + + private static void TriggerCreated(Hotkey hotkey) + { + var listener = hotkey.inputsHotkey.Listener; + + Triggers.TryAdd(listener, hotkey.inputsChord); + } + + internal static void InvokeTrigger(Hotkey hotkey, bool IsKeyDown, bool IsKeyUp) + { + if (IsKeyDown && hotkey.inputsHotkey.OnKeyDown) + TriggerRaised?.Invoke(hotkey.inputsHotkey.Listener, hotkey.inputsChord, hotkey.inputsHotkey.hotkeyType, + true, false); + + if (IsKeyUp && hotkey.inputsHotkey.OnKeyUp) + TriggerRaised?.Invoke(hotkey.inputsHotkey.Listener, hotkey.inputsChord, hotkey.inputsHotkey.hotkeyType, + false, true); + } } \ No newline at end of file diff --git a/HandheldCompanion/Managers/LayoutManager.cs b/HandheldCompanion/Managers/LayoutManager.cs index c84928001..11423327c 100644 --- a/HandheldCompanion/Managers/LayoutManager.cs +++ b/HandheldCompanion/Managers/LayoutManager.cs @@ -1,495 +1,523 @@ -using HandheldCompanion.Actions; -using HandheldCompanion.Controllers; -using HandheldCompanion.Controls; -using HandheldCompanion.Inputs; -using HandheldCompanion.Managers.Desktop; -using HandheldCompanion.Utils; -using HandheldCompanion.Views; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; -using System.Numerics; -using System.Threading.Tasks; -using System.Windows; - -namespace HandheldCompanion.Managers; - -internal static class LayoutManager -{ - public static List<LayoutTemplate> Templates = new() - { - LayoutTemplate.DefaultLayout, - LayoutTemplate.DesktopLayout, - LayoutTemplate.NintendoLayout, - LayoutTemplate.KeyboardLayout, - LayoutTemplate.GamepadMouseLayout, - LayoutTemplate.GamepadJoystickLayout - }; - - private static bool updateLock; - private static Layout currentLayout; - private static ScreenRotation currentOrientation = new(); - private static Layout profileLayout; - private static Layout desktopLayout; - private static readonly string desktopLayoutFile = "desktop"; - - public static string LayoutsPath; - public static string TemplatesPath; - - private static bool IsInitialized; - - static LayoutManager() - { - // initialiaze path - LayoutsPath = Path.Combine(MainWindow.SettingsPath, "layouts"); - if (!Directory.Exists(LayoutsPath)) - Directory.CreateDirectory(LayoutsPath); - - TemplatesPath = Path.Combine(MainWindow.SettingsPath, "templates"); - if (!Directory.Exists(TemplatesPath)) - Directory.CreateDirectory(TemplatesPath); - - // monitor layout files - layoutWatcher = new FileSystemWatcher - { - Path = TemplatesPath, - EnableRaisingEvents = true, - IncludeSubdirectories = true, - Filter = "*.json", - NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite, - }; - - ProfileManager.Applied += ProfileManager_Applied; - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - - SystemManager.DisplayOrientationChanged += DesktopManager_DisplayOrientationChanged; - } - - public static FileSystemWatcher layoutWatcher { get; set; } - - public static void Start() - { - // process community templates - var fileEntries = Directory.GetFiles(TemplatesPath, "*.json", SearchOption.AllDirectories); - foreach (var fileName in fileEntries) - ProcessLayoutTemplate(fileName); - - foreach (var layoutTemplate in Templates) - Updated?.Invoke(layoutTemplate); - - var desktopFile = Path.Combine(LayoutsPath, $"{desktopLayoutFile}.json"); - desktopLayout = ProcessLayout(desktopFile); - if (desktopLayout is null) - { - desktopLayout = LayoutTemplate.DesktopLayout.Layout.Clone() as Layout; - DesktopLayout_Updated(desktopLayout); - } - - desktopLayout.Updated += DesktopLayout_Updated; - - // TODO: overwritten layout will have different GUID so it will duplicate - layoutWatcher.Created += LayoutWatcher_Template; - layoutWatcher.Changed += LayoutWatcher_Template; - - IsInitialized = true; - Initialized?.Invoke(); - - LogManager.LogInformation("{0} has started", "LayoutManager"); - } - - public static void Stop() - { - if (!IsInitialized) - return; - - IsInitialized = false; - - LogManager.LogInformation("{0} has stopped", "LayoutManager"); - } - - // this event is called from non main thread and it creates LayoutTemplate which is a WPF element - private static void LayoutWatcher_Template(object sender, FileSystemEventArgs e) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - ProcessLayoutTemplate(e.FullPath); - }); - } - - private static Layout? ProcessLayout(string fileName) - { - Layout layout = null; - - try - { - string outputraw = File.ReadAllText(fileName); - layout = JsonConvert.DeserializeObject<Layout>(outputraw, new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All - }); - } - catch (Exception ex) - { - LogManager.LogError("Could not parse Layout {0}. {1}", fileName, ex.Message); - } - - // failed to parse - if (layout is null) - LogManager.LogError("Could not parse Layout {0}", fileName); - - return layout; - } - - private static void ProcessLayoutTemplate(string fileName) - { - LayoutTemplate layoutTemplate = null; - - try - { - string outputraw = File.ReadAllText(fileName); - layoutTemplate = JsonConvert.DeserializeObject<LayoutTemplate>(outputraw, new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All - }); - } - catch (Exception ex) - { - LogManager.LogError("Could not parse LayoutTemplate {0}. {1}", fileName, ex.Message); - } - - // failed to parse - if (layoutTemplate is null || layoutTemplate.Layout is null) - { - LogManager.LogError("Could not parse LayoutTemplate {0}", fileName); - return; - } - - // todo: implement deduplication - Templates.Add(layoutTemplate); - Updated?.Invoke(layoutTemplate); - } - - private static void DesktopLayout_Updated(Layout layout) - { - SerializeLayout(layout, desktopLayoutFile); - } - - private static void ProfileManager_Applied(Profile profile, UpdateSource source) - { - SetProfileLayout(profile); - } - - private static void SetProfileLayout(Profile profile = null) - { - var defaultProfile = ProfileManager.GetDefault(); - - if (profile.LayoutEnabled) - // use profile layout if enabled - profileLayout = profile.Layout.Clone() as Layout; - else if (defaultProfile.LayoutEnabled) - // fallback to default profile layout if enabled - profileLayout = defaultProfile.Layout.Clone() as Layout; - else - // this should not happen, defaultProfile LayoutEnabled should always be true - profileLayout = null; - - // only update current layout if we're not into desktop layout mode - if (!SettingsManager.GetBoolean("DesktopLayoutEnabled", true)) - SetActiveLayout(profileLayout); - } - - public static Layout GetCurrent() - { - return currentLayout; - } - - public static Layout GetDesktop() - { - return desktopLayout; - } - - public static void SerializeLayout(Layout layout, string fileName) - { - var jsonString = JsonConvert.SerializeObject(layout, Formatting.Indented, new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All - }); - - fileName = Path.Combine(LayoutsPath, $"{fileName}.json"); - if (FileUtils.IsFileWritable(fileName)) - File.WriteAllText(fileName, jsonString); - } - - public static void SerializeLayoutTemplate(LayoutTemplate layoutTemplate) - { - var jsonString = JsonConvert.SerializeObject(layoutTemplate, Formatting.Indented, new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All - }); - - string fileName = Path.Combine(TemplatesPath, $"{layoutTemplate.Name}_{layoutTemplate.Author}.json"); - if (FileUtils.IsFileWritable(fileName)) - File.WriteAllText(fileName, jsonString); - } - - private static void SettingsManager_SettingValueChanged(string name, object value) - { - switch (name) - { - case "DesktopLayoutEnabled": - { - switch (Convert.ToBoolean(value)) - { - case true: - SetActiveLayout(desktopLayout); - break; - case false: - SetActiveLayout(profileLayout); - break; - } - } - break; - } - } - - private static void DesktopManager_DisplayOrientationChanged(ScreenRotation rotation) - { - currentOrientation = rotation; - - // apply orientation - UpdateOrientation(); - } - - private static void UpdateOrientation() - { - if (currentLayout is null) - return; - - foreach (var axisLayout in currentLayout.AxisLayout) - { - // pull action - var action = axisLayout.Value; - - if (action is null) - continue; - - if (action.AutoRotate) - action.SetOrientation(currentOrientation); - } - } - - private static async void SetActiveLayout(Layout layout) - { - while (updateLock) - await Task.Delay(5); - - currentLayout = layout; - - // (re)apply orientation - UpdateOrientation(); - } - - public static ControllerState MapController(ControllerState controllerState) - { - // when no profile active and default is disabled, do 1:1 controller mapping - if (currentLayout is null) - return controllerState; - - // TODO: this call is not from the main thread, SetActiveLayout is. - // proper lock needed here? volatile? Interlocked.Exchange()? - // set lock - updateLock = true; - - // clean output state, there should be no leaking of current controller state, - // only buttons/axes mapped from the layout should be passed on - ControllerState outputState = new(); - - // except the main gyroscope state that's not re-mappable (6 values) - outputState.GyroState = controllerState.GyroState; - - foreach (var buttonState in controllerState.ButtonState.State) - { - ButtonFlags button = buttonState.Key; - bool value = buttonState.Value; - - // skip, if not mapped - if (!currentLayout.ButtonLayout.TryGetValue(button, out List<IActions> actions)) - continue; - - foreach (var action in actions) - { - switch (action.ActionType) - { - // button to button - case ActionType.Button: - { - ButtonActions bAction = action as ButtonActions; - bAction.Execute(button, value); - - bool outVal = bAction.GetValue() || outputState.ButtonState[bAction.Button]; - outputState.ButtonState[bAction.Button] = outVal; - } - break; - - // button to keyboard key - case ActionType.Keyboard: - { - KeyboardActions kAction = action as KeyboardActions; - kAction.Execute(button, value); - } - break; - - // button to mouse click - case ActionType.Mouse: - { - MouseActions mAction = action as MouseActions; - mAction.Execute(button, value); - } - break; - } - } - } - - foreach (var axisLayout in currentLayout.AxisLayout) - { - AxisLayoutFlags flags = axisLayout.Key; - - // read origin values - AxisLayout InLayout = AxisLayout.Layouts[flags]; - AxisFlags InAxisX = InLayout.GetAxisFlags('X'); - AxisFlags InAxisY = InLayout.GetAxisFlags('Y'); - - InLayout.vector.X = controllerState.AxisState[InAxisX]; - InLayout.vector.Y = controllerState.AxisState[InAxisY]; - - // pull action - IActions action = axisLayout.Value; - - if (action is null) - continue; - - switch (action.ActionType) - { - case ActionType.Joystick: - { - AxisActions aAction = action as AxisActions; - aAction.Execute(InLayout); - - // read output axis - AxisLayout OutLayout = AxisLayout.Layouts[aAction.Axis]; - AxisFlags OutAxisX = OutLayout.GetAxisFlags('X'); - AxisFlags OutAxisY = OutLayout.GetAxisFlags('Y'); - - outputState.AxisState[OutAxisX] = - (short)Math.Clamp(outputState.AxisState[OutAxisX] + aAction.GetValue().X, short.MinValue, short.MaxValue); - outputState.AxisState[OutAxisY] = - (short)Math.Clamp(outputState.AxisState[OutAxisY] + aAction.GetValue().Y, short.MinValue, short.MaxValue); - } - break; - - case ActionType.Trigger: - { - TriggerActions tAction = action as TriggerActions; - tAction.Execute(InAxisY, (short)InLayout.vector.Y); - - // read output axis - AxisLayout OutLayout = AxisLayout.Layouts[tAction.Axis]; - AxisFlags OutAxisY = OutLayout.GetAxisFlags('Y'); - - outputState.AxisState[OutAxisY] = (short)tAction.GetValue(); - } - break; - - case ActionType.Mouse: - { - MouseActions mAction = action as MouseActions; - - // This buttonState check won't work here if UpdateInputs is event based, might need a rework in the future - bool touched = false; - if (ControllerState.AxisTouchButtons.TryGetValue(InLayout.flags, out ButtonFlags touchButton)) - touched = controllerState.ButtonState[touchButton]; - - mAction.Execute(InLayout, touched); - } - break; - } - } - - foreach (var axisLayout in currentLayout.GyroLayout) - { - AxisLayoutFlags flags = axisLayout.Key; - - // read origin values - AxisLayout InLayout = AxisLayout.Layouts[flags]; - AxisFlags InAxisX = InLayout.GetAxisFlags('X'); - AxisFlags InAxisY = InLayout.GetAxisFlags('Y'); - - InLayout.vector.X = controllerState.AxisState[InAxisX]; - InLayout.vector.Y = controllerState.AxisState[InAxisY]; - - // pull action - IActions action = axisLayout.Value; - - if (action is null) - continue; - - switch (action.ActionType) - { - case ActionType.Joystick: - { - AxisActions aAction = action as AxisActions; - aAction.Execute(InLayout); - - // Read output axis - AxisLayout OutLayout = AxisLayout.Layouts[aAction.Axis]; - AxisFlags OutAxisX = OutLayout.GetAxisFlags('X'); - AxisFlags OutAxisY = OutLayout.GetAxisFlags('Y'); - - Vector2 joystick = new Vector2(outputState.AxisState[OutAxisX], outputState.AxisState[OutAxisY]); - - // Reduce motion weight based on joystick position - // Get the distance of the joystick from the center - float joystickLength = Math.Clamp(joystick.Length() / short.MaxValue, 0, 1); - float weightFactor = aAction.gyroWeight - joystickLength; - Vector2 result = joystick + aAction.GetValue() * weightFactor; - - // Apply clamping to the result to stay in range of joystick - outputState.AxisState[OutAxisX] = (short)Math.Clamp(result.X, short.MinValue, short.MaxValue); - outputState.AxisState[OutAxisY] = (short)Math.Clamp(result.Y, short.MinValue, short.MaxValue); - } - break; - - case ActionType.Mouse: - { - MouseActions mAction = action as MouseActions; - - // This buttonState check won't work here if UpdateInputs is event based, might need a rework in the future - bool touched = false; - if (ControllerState.AxisTouchButtons.TryGetValue(InLayout.flags, out ButtonFlags touchButton)) - touched = controllerState.ButtonState[touchButton]; - - mAction.Execute(InLayout, touched); - } - break; - } - } - - // release lock - updateLock = false; - - return outputState; - } - - #region events - - public static event InitializedEventHandler Initialized; - public delegate void InitializedEventHandler(); - - public static event UpdatedEventHandler Updated; - public delegate void UpdatedEventHandler(LayoutTemplate layoutTemplate); - - #endregion +using HandheldCompanion.Actions; +using HandheldCompanion.Controllers; +using HandheldCompanion.Controls; +using HandheldCompanion.Inputs; +using HandheldCompanion.Managers.Desktop; +using HandheldCompanion.Utils; +using HandheldCompanion.Views; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Numerics; +using System.Threading.Tasks; +using System.Windows; + +namespace HandheldCompanion.Managers; + +internal static class LayoutManager +{ + public static List<LayoutTemplate> Templates = new() + { + LayoutTemplate.DefaultLayout, + LayoutTemplate.DesktopLayout, + LayoutTemplate.NintendoLayout, + LayoutTemplate.KeyboardLayout, + LayoutTemplate.GamepadMouseLayout, + LayoutTemplate.GamepadJoystickLayout + }; + + private static bool updateLock; + private static Layout currentLayout; + private static ScreenRotation currentOrientation = new(); + private static Layout profileLayout; + private static Layout desktopLayout; + private static readonly string desktopLayoutFile = "desktop"; + + public static string LayoutsPath; + public static string TemplatesPath; + + private static bool IsInitialized; + + static LayoutManager() + { + // initialiaze path + LayoutsPath = Path.Combine(MainWindow.SettingsPath, "layouts"); + if (!Directory.Exists(LayoutsPath)) + Directory.CreateDirectory(LayoutsPath); + + TemplatesPath = Path.Combine(MainWindow.SettingsPath, "templates"); + if (!Directory.Exists(TemplatesPath)) + Directory.CreateDirectory(TemplatesPath); + + // monitor layout files + layoutWatcher = new FileSystemWatcher + { + Path = TemplatesPath, + EnableRaisingEvents = true, + IncludeSubdirectories = true, + Filter = "*.json", + NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite, + }; + + ProfileManager.Applied += ProfileManager_Applied; + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + + SystemManager.DisplayOrientationChanged += DesktopManager_DisplayOrientationChanged; + } + + public static FileSystemWatcher layoutWatcher { get; set; } + + public static void Start() + { + // process community templates + var fileEntries = Directory.GetFiles(TemplatesPath, "*.json", SearchOption.AllDirectories); + foreach (var fileName in fileEntries) + ProcessLayoutTemplate(fileName); + + foreach (var layoutTemplate in Templates) + Updated?.Invoke(layoutTemplate); + + var desktopFile = Path.Combine(LayoutsPath, $"{desktopLayoutFile}.json"); + desktopLayout = ProcessLayout(desktopFile); + if (desktopLayout is null) + { + desktopLayout = LayoutTemplate.DesktopLayout.Layout.Clone() as Layout; + DesktopLayout_Updated(desktopLayout); + } + + desktopLayout.Updated += DesktopLayout_Updated; + + // TODO: overwritten layout will have different GUID so it will duplicate + layoutWatcher.Created += LayoutWatcher_Template; + layoutWatcher.Changed += LayoutWatcher_Template; + + IsInitialized = true; + Initialized?.Invoke(); + + LogManager.LogInformation("{0} has started", "LayoutManager"); + } + + public static void Stop() + { + if (!IsInitialized) + return; + + IsInitialized = false; + + LogManager.LogInformation("{0} has stopped", "LayoutManager"); + } + + // this event is called from non main thread and it creates LayoutTemplate which is a WPF element + private static void LayoutWatcher_Template(object sender, FileSystemEventArgs e) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + ProcessLayoutTemplate(e.FullPath); + }); + } + + private static Layout? ProcessLayout(string fileName) + { + Layout layout = null; + + try + { + string outputraw = File.ReadAllText(fileName); + layout = JsonConvert.DeserializeObject<Layout>(outputraw, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + } + catch (Exception ex) + { + LogManager.LogError("Could not parse Layout {0}. {1}", fileName, ex.Message); + } + + // failed to parse + if (layout is null) + LogManager.LogError("Could not parse Layout {0}", fileName); + + return layout; + } + + private static void ProcessLayoutTemplate(string fileName) + { + LayoutTemplate layoutTemplate = null; + + try + { + string outputraw = File.ReadAllText(fileName); + layoutTemplate = JsonConvert.DeserializeObject<LayoutTemplate>(outputraw, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + } + catch (Exception ex) + { + LogManager.LogError("Could not parse LayoutTemplate {0}. {1}", fileName, ex.Message); + } + + // failed to parse + if (layoutTemplate is null || layoutTemplate.Layout is null) + { + LogManager.LogError("Could not parse LayoutTemplate {0}", fileName); + return; + } + + // todo: implement deduplication + Templates.Add(layoutTemplate); + Updated?.Invoke(layoutTemplate); + } + + private static void DesktopLayout_Updated(Layout layout) + { + SerializeLayout(layout, desktopLayoutFile); + } + + private static void ProfileManager_Applied(Profile profile, UpdateSource source) + { + SetProfileLayout(profile); + } + + private static void SetProfileLayout(Profile profile = null) + { + var defaultProfile = ProfileManager.GetDefault(); + + if (profile.LayoutEnabled) + // use profile layout if enabled + profileLayout = profile.Layout.Clone() as Layout; + else if (defaultProfile.LayoutEnabled) + // fallback to default profile layout if enabled + profileLayout = defaultProfile.Layout.Clone() as Layout; + else + // this should not happen, defaultProfile LayoutEnabled should always be true + profileLayout = null; + + // only update current layout if we're not into desktop layout mode + if (!SettingsManager.GetBoolean("DesktopLayoutEnabled", true)) + SetActiveLayout(profileLayout); + } + + public static Layout GetCurrent() + { + return currentLayout; + } + + public static Layout GetDesktop() + { + return desktopLayout; + } + + public static void SerializeLayout(Layout layout, string fileName) + { + var jsonString = JsonConvert.SerializeObject(layout, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + + fileName = Path.Combine(LayoutsPath, $"{fileName}.json"); + if (FileUtils.IsFileWritable(fileName)) + File.WriteAllText(fileName, jsonString); + } + + public static void SerializeLayoutTemplate(LayoutTemplate layoutTemplate) + { + var jsonString = JsonConvert.SerializeObject(layoutTemplate, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + + string fileName = Path.Combine(TemplatesPath, $"{layoutTemplate.Name}_{layoutTemplate.Author}.json"); + if (FileUtils.IsFileWritable(fileName)) + File.WriteAllText(fileName, jsonString); + } + + private static void SettingsManager_SettingValueChanged(string name, object value) + { + switch (name) + { + case "DesktopLayoutEnabled": + { + switch (Convert.ToBoolean(value)) + { + case true: + SetActiveLayout(desktopLayout); + break; + case false: + SetActiveLayout(profileLayout); + break; + } + } + break; + } + } + + private static void DesktopManager_DisplayOrientationChanged(ScreenRotation rotation) + { + currentOrientation = rotation; + + // apply orientation + UpdateOrientation(); + } + + private static void UpdateOrientation() + { + if (currentLayout is null) + return; + + foreach (var axisLayout in currentLayout.AxisLayout) + { + // pull action + var action = axisLayout.Value; + + if (action is null) + continue; + + if (action.AutoRotate) + action.SetOrientation(currentOrientation); + } + } + + private static async void SetActiveLayout(Layout layout) + { + while (updateLock) + await Task.Delay(5); + + currentLayout = layout; + + // (re)apply orientation + UpdateOrientation(); + } + + public static ControllerState MapController(ControllerState controllerState) + { + // when no profile active and default is disabled, do 1:1 controller mapping + if (currentLayout is null) + return controllerState; + + // TODO: this call is not from the main thread, SetActiveLayout is. + // proper lock needed here? volatile? Interlocked.Exchange()? + // set lock + updateLock = true; + + // clean output state, there should be no leaking of current controller state, + // only buttons/axes mapped from the layout should be passed on + ControllerState outputState = new(); + + // except the main gyroscope state that's not re-mappable (6 values) + outputState.GyroState = controllerState.GyroState; + + foreach (var buttonState in controllerState.ButtonState.State) + { + ButtonFlags button = buttonState.Key; + bool value = buttonState.Value; + + // skip, if not mapped + if (!currentLayout.ButtonLayout.TryGetValue(button, out List<IActions> actions)) + continue; + +<<<<<<< HEAD +======= + // Some long press logic. Unfortunately in case of long press actions are not 100% + // independent of eachother. When button is pressed that has long press mapped, short + // press should not be triggered on key down. It should only be triggered on keyup, but + // only if released before the long timer. If timer passed, short is ignored, long is + // pressed. Long story short :-), short press needs to be aware if long press exists. + + // TODO: change to set of ranges so several independent long presses are possible + // if there are no long presses nothing changes + int maxLongTime = 0; + foreach (var action in actions) + if (action.PressType == PressType.Long) + maxLongTime = Math.Max(maxLongTime, action.LongPressTime); + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + foreach (var action in actions) + { + switch (action.ActionType) + { + // button to button + case ActionType.Button: + { + ButtonActions bAction = action as ButtonActions; +<<<<<<< HEAD + bAction.Execute(button, value); +======= + bAction.Execute(button, value, maxLongTime); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + bool outVal = bAction.GetValue() || outputState.ButtonState[bAction.Button]; + outputState.ButtonState[bAction.Button] = outVal; + } + break; + + // button to keyboard key + case ActionType.Keyboard: + { + KeyboardActions kAction = action as KeyboardActions; +<<<<<<< HEAD + kAction.Execute(button, value); +======= + kAction.Execute(button, value, maxLongTime); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + break; + + // button to mouse click + case ActionType.Mouse: + { + MouseActions mAction = action as MouseActions; +<<<<<<< HEAD + mAction.Execute(button, value); +======= + mAction.Execute(button, value, maxLongTime); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + break; + } + } + } + + foreach (var axisLayout in currentLayout.AxisLayout) + { + AxisLayoutFlags flags = axisLayout.Key; + + // read origin values + AxisLayout InLayout = AxisLayout.Layouts[flags]; + AxisFlags InAxisX = InLayout.GetAxisFlags('X'); + AxisFlags InAxisY = InLayout.GetAxisFlags('Y'); + + InLayout.vector.X = controllerState.AxisState[InAxisX]; + InLayout.vector.Y = controllerState.AxisState[InAxisY]; + + // pull action + IActions action = axisLayout.Value; + + if (action is null) + continue; + + switch (action.ActionType) + { + case ActionType.Joystick: + { + AxisActions aAction = action as AxisActions; + aAction.Execute(InLayout); + + // read output axis + AxisLayout OutLayout = AxisLayout.Layouts[aAction.Axis]; + AxisFlags OutAxisX = OutLayout.GetAxisFlags('X'); + AxisFlags OutAxisY = OutLayout.GetAxisFlags('Y'); + + outputState.AxisState[OutAxisX] = + (short)Math.Clamp(outputState.AxisState[OutAxisX] + aAction.GetValue().X, short.MinValue, short.MaxValue); + outputState.AxisState[OutAxisY] = + (short)Math.Clamp(outputState.AxisState[OutAxisY] + aAction.GetValue().Y, short.MinValue, short.MaxValue); + } + break; + + case ActionType.Trigger: + { + TriggerActions tAction = action as TriggerActions; + tAction.Execute(InAxisY, (short)InLayout.vector.Y); + + // read output axis + AxisLayout OutLayout = AxisLayout.Layouts[tAction.Axis]; + AxisFlags OutAxisY = OutLayout.GetAxisFlags('Y'); + + outputState.AxisState[OutAxisY] = (short)tAction.GetValue(); + } + break; + + case ActionType.Mouse: + { + MouseActions mAction = action as MouseActions; + + // This buttonState check won't work here if UpdateInputs is event based, might need a rework in the future + bool touched = false; + if (ControllerState.AxisTouchButtons.TryGetValue(InLayout.flags, out ButtonFlags touchButton)) + touched = controllerState.ButtonState[touchButton]; + + mAction.Execute(InLayout, touched); + } + break; + } + } + + foreach (var axisLayout in currentLayout.GyroLayout) + { + AxisLayoutFlags flags = axisLayout.Key; + + // read origin values + AxisLayout InLayout = AxisLayout.Layouts[flags]; + AxisFlags InAxisX = InLayout.GetAxisFlags('X'); + AxisFlags InAxisY = InLayout.GetAxisFlags('Y'); + + InLayout.vector.X = controllerState.AxisState[InAxisX]; + InLayout.vector.Y = controllerState.AxisState[InAxisY]; + + // pull action + IActions action = axisLayout.Value; + + if (action is null) + continue; + + switch (action.ActionType) + { + case ActionType.Joystick: + { + AxisActions aAction = action as AxisActions; + aAction.Execute(InLayout); + + // Read output axis + AxisLayout OutLayout = AxisLayout.Layouts[aAction.Axis]; + AxisFlags OutAxisX = OutLayout.GetAxisFlags('X'); + AxisFlags OutAxisY = OutLayout.GetAxisFlags('Y'); + + Vector2 joystick = new Vector2(outputState.AxisState[OutAxisX], outputState.AxisState[OutAxisY]); + + // Reduce motion weight based on joystick position + // Get the distance of the joystick from the center + float joystickLength = Math.Clamp(joystick.Length() / short.MaxValue, 0, 1); + float weightFactor = aAction.gyroWeight - joystickLength; + Vector2 result = joystick + aAction.GetValue() * weightFactor; + + // Apply clamping to the result to stay in range of joystick + outputState.AxisState[OutAxisX] = (short)Math.Clamp(result.X, short.MinValue, short.MaxValue); + outputState.AxisState[OutAxisY] = (short)Math.Clamp(result.Y, short.MinValue, short.MaxValue); + } + break; + + case ActionType.Mouse: + { + MouseActions mAction = action as MouseActions; + + // This buttonState check won't work here if UpdateInputs is event based, might need a rework in the future + bool touched = false; + if (ControllerState.AxisTouchButtons.TryGetValue(InLayout.flags, out ButtonFlags touchButton)) + touched = controllerState.ButtonState[touchButton]; + + mAction.Execute(InLayout, touched); + } + break; + } + } + + // release lock + updateLock = false; + + return outputState; + } + + #region events + + public static event InitializedEventHandler Initialized; + public delegate void InitializedEventHandler(); + + public static event UpdatedEventHandler Updated; + public delegate void UpdatedEventHandler(LayoutTemplate layoutTemplate); + + #endregion } \ No newline at end of file diff --git a/HandheldCompanion/Managers/OSDManager.cs b/HandheldCompanion/Managers/OSDManager.cs index b0e081105..27ff1e6ff 100644 --- a/HandheldCompanion/Managers/OSDManager.cs +++ b/HandheldCompanion/Managers/OSDManager.cs @@ -1,459 +1,538 @@ -using PrecisionTiming; -using RTSSSharedMemoryNET; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using static HandheldCompanion.Platforms.HWiNFO; - -namespace HandheldCompanion.Managers; - -public static class OSDManager -{ - public delegate void InitializedEventHandler(); - - // C1: GPU - // C2: CPU - // C3: RAM - // C4: VRAM - // C5: BATT - // C6: FPS - private const string Header = - "<C0=FFFFFF><C1=458A6E><C2=4C8DB2><C3=AD7B95><C4=A369A6><C5=F19F86><C6=D76D76><A0=-4><A1=5><A2=-2><A3=-3><A4=-4><A5=-5><S0=-50><S1=50>"; - - private static bool IsInitialized; - public static short OverlayLevel; - - private static readonly PrecisionTimer RefreshTimer; - private static int RefreshInterval = 100; - - private static readonly ConcurrentDictionary<int, OSD> OnScreenDisplay = new(); - private static AppEntry OnScreenAppEntry; - private static List<string> Content; - - static OSDManager() - { - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - - PlatformManager.RTSS.Hooked += RTSS_Hooked; - PlatformManager.RTSS.Unhooked += RTSS_Unhooked; - - // timer used to monitor foreground application framerate - RefreshInterval = SettingsManager.GetInt("OnScreenDisplayRefreshRate"); - - RefreshTimer = new PrecisionTimer(); - RefreshTimer.SetInterval(new Action(UpdateOSD), RefreshInterval, false, 0, TimerMode.Periodic, true); - } - - public static event InitializedEventHandler Initialized; - - private static void RTSS_Unhooked(int processId) - { - try - { - // clear previous display - if (OnScreenDisplay.TryGetValue(processId, out var OSD)) - { - OSD.Update(""); - OSD.Dispose(); - - OnScreenDisplay.TryRemove(new KeyValuePair<int, OSD>(processId, OSD)); - } - } - catch - { - } - } - - private static void RTSS_Hooked(AppEntry appEntry) - { - try - { - // update foreground id - OnScreenAppEntry = appEntry; - - // only create a new OSD if needed - if (OnScreenDisplay.ContainsKey(appEntry.ProcessId)) - return; - - OnScreenDisplay[OnScreenAppEntry.ProcessId] = new OSD(OnScreenAppEntry.Name); - } - catch - { - } - } - - public static void Start() - { - IsInitialized = true; - Initialized?.Invoke(); - - LogManager.LogInformation("{0} has started", "OSDManager"); - } - - private static uint OSDIndex(this OSD? osd) - { - if (osd is null) - return uint.MaxValue; - - var osdSlot = typeof(OSD).GetField("m_osdSlot", - BindingFlags.NonPublic | BindingFlags.Instance); - var value = osdSlot.GetValue(osd); - if (value is null) - return uint.MaxValue; - - return (uint)value; - } - - private static uint OSDIndex(string name) - { - var entries = OSD.GetOSDEntries().ToList(); - for (var i = 0; i < entries.Count(); i++) - if (entries[i].Owner == name) - return (uint)i; - return 0; - } - - private static void UpdateOSD() - { - if (OverlayLevel == 0) - return; - - foreach (var pair in OnScreenDisplay) - { - var processId = pair.Key; - var processOSD = pair.Value; - - try - { - if (processId == OnScreenAppEntry.ProcessId) - { - var content = Draw(processId); - processOSD.Update(content); - } - else - { - processOSD.Update(""); - } - } - catch - { - } - } - } - - public static string Draw(int processId) - { - SensorElement sensor; - Content = new List<string>(); - - switch (OverlayLevel) - { - default: - case 0: // Disabled - break; - - case 1: // Minimal - { - OverlayRow row1 = new(); - - OverlayEntry FPSentry = new("<APP>", "C6"); - FPSentry.elements.Add(new OverlayEntryElement - { - Value = "<FR>", - SzUnit = "FPS" - }); - FPSentry.elements.Add(new OverlayEntryElement - { - Value = "<FT>", - SzUnit = "ms" - }); - row1.entries.Add(FPSentry); - - // add header to row1 - Content.Add(Header + row1); - } - break; - - case 2: // Extended - { - OverlayRow row1 = new(); - - OverlayEntry BATTentry = new("BATT", "C5"); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryChargeLevel, - out sensor)) - BATTentry.elements.Add(new OverlayEntryElement(sensor)); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryRemainingCapacity, - out sensor)) - BATTentry.elements.Add(new OverlayEntryElement(sensor)); - row1.entries.Add(BATTentry); - - OverlayEntry GPUentry = new("GPU", "C1"); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUUsage, out sensor)) - GPUentry.elements.Add(new OverlayEntryElement(sensor)); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUPower, out sensor)) - GPUentry.elements.Add(new OverlayEntryElement(sensor)); - row1.entries.Add(GPUentry); - - OverlayEntry CPUentry = new("CPU", "C2"); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUUsage, out sensor)) - CPUentry.elements.Add(new OverlayEntryElement(sensor)); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUPower, out sensor)) - CPUentry.elements.Add(new OverlayEntryElement(sensor)); - row1.entries.Add(CPUentry); - - OverlayEntry RAMentry = new("RAM", "C3"); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.PhysicalMemoryUsage, - out sensor)) - RAMentry.elements.Add(new OverlayEntryElement(sensor)); - row1.entries.Add(RAMentry); - - OverlayEntry FPSentry = new("<APP>", "C6"); - FPSentry.elements.Add(new OverlayEntryElement - { - Value = "<FR>", - SzUnit = "FPS" - }); - FPSentry.elements.Add(new OverlayEntryElement - { - Value = "<FT>", - SzUnit = "ms" - }); - row1.entries.Add(FPSentry); - - // add header to row1 - Content.Add(Header + row1); - } - break; - - case 3: // Full - { - OverlayRow row1 = new(); - OverlayRow row2 = new(); - OverlayRow row3 = new(); - OverlayRow row4 = new(); - OverlayRow row5 = new(); - OverlayRow row6 = new(); - - OverlayEntry GPUentry = new("GPU", "C1", true); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUUsage, out sensor)) - GPUentry.elements.Add(new OverlayEntryElement(sensor)); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUPower, out sensor)) - GPUentry.elements.Add(new OverlayEntryElement(sensor)); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUTemperature, out sensor)) - GPUentry.elements.Add(new OverlayEntryElement(sensor)); - row1.entries.Add(GPUentry); - - OverlayEntry CPUentry = new("CPU", "C2", true); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUUsage, out sensor)) - CPUentry.elements.Add(new OverlayEntryElement(sensor)); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUPower, out sensor)) - CPUentry.elements.Add(new OverlayEntryElement(sensor)); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUTemperature, out sensor)) - CPUentry.elements.Add(new OverlayEntryElement(sensor)); - row2.entries.Add(CPUentry); - - OverlayEntry RAMentry = new("RAM", "C3", true); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.PhysicalMemoryUsage, - out sensor)) - RAMentry.elements.Add(new OverlayEntryElement(sensor)); - row3.entries.Add(RAMentry); - - OverlayEntry VRAMentry = new("VRAM", "C4", true); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUMemoryUsage, out sensor)) - VRAMentry.elements.Add(new OverlayEntryElement(sensor)); - row4.entries.Add(VRAMentry); - - OverlayEntry BATTentry = new("BATT", "C5", true); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryChargeLevel, - out sensor)) - BATTentry.elements.Add(new OverlayEntryElement(sensor)); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryRemainingCapacity, - out sensor)) - BATTentry.elements.Add(new OverlayEntryElement(sensor)); - if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryRemainingTime, - out sensor)) - BATTentry.elements.Add(new OverlayEntryElement(sensor)); - row5.entries.Add(BATTentry); - - OverlayEntry FPSentry = new("<APP>", "C6"); - FPSentry.elements.Add(new OverlayEntryElement - { - Value = "<FR>", - SzUnit = "FPS" - }); - FPSentry.elements.Add(new OverlayEntryElement - { - Value = "<FT>", - SzUnit = "ms" - }); - row6.entries.Add(FPSentry); - - // add header to row1 - Content.Add(Header + row1); - Content.Add(row2.ToString()); - Content.Add(row3.ToString()); - Content.Add(row4.ToString()); - Content.Add(row5.ToString()); - Content.Add(row6.ToString()); - } - break; - - case 4: // External - { - /* - * Intended to simply allow RTSS/HWINFO to run, and let the user configure the overlay within those - * tools as they wish - */ - } - break; - } - - return string.Join("\n", Content); - } - - public static void Stop() - { - if (!IsInitialized) - return; - - RefreshTimer.Stop(); - - // unhook all processes - foreach (var processId in OnScreenDisplay.Keys) - RTSS_Unhooked(processId); - - IsInitialized = false; - - LogManager.LogInformation("{0} has stopped", "OSDManager"); - } - - private static void SettingsManager_SettingValueChanged(string name, object value) - { - switch (name) - { - case "OnScreenDisplayLevel": - { - OverlayLevel = Convert.ToInt16(value); - - if (OverlayLevel > 0) - { - if (OverlayLevel == 4) - { - // No need to update OSD in External - RefreshTimer.Stop(); - - // Remove previous UI in External - foreach (var pair in OnScreenDisplay) - { - var processOSD = pair.Value; - processOSD.Update(""); - } - } - else - { - // Other modes need the refresh timer to update OSD - if (!RefreshTimer.IsRunning()) - RefreshTimer.Start(); - } - } - else - { - RefreshTimer.Stop(); - - // clear UI on stop - foreach (var pair in OnScreenDisplay) - { - var processOSD = pair.Value; - processOSD.Update(""); - } - } - } - break; - - case "OnScreenDisplayRefreshRate": - { - RefreshInterval = Convert.ToInt32(value); - - if (RefreshTimer.IsRunning()) - { - RefreshTimer.Stop(); - RefreshTimer.SetPeriod(RefreshInterval); - RefreshTimer.Start(); - } - } - break; - } - } -} - -public struct OverlayEntryElement -{ - public string Value { get; set; } - public string SzUnit { get; set; } - - public override string ToString() - { - return string.Format("<C0>{0:00}<S1>{1}<S><C>", Value, SzUnit); - } - - public OverlayEntryElement(SensorElement sensor) - { - Value = string.Format("{0:00}", sensor.Value); - SzUnit = sensor.szUnit; - } -} - -public class OverlayEntry : IDisposable -{ - public List<OverlayEntryElement> elements = new(); - - public OverlayEntry(string name, string colorScheme = "", bool indent = false) - { - Name = indent ? name + "\t" : name; - - if (!string.IsNullOrEmpty(colorScheme)) - Name = "<" + colorScheme + ">" + Name + "<C>"; - } - - public string Name { get; set; } - - public void Dispose() - { - elements.Clear(); - elements = null; - } -} - -public class OverlayRow : IDisposable -{ - public List<OverlayEntry> entries = new(); - - public void Dispose() - { - entries.Clear(); - entries = null; - } - - public override string ToString() - { - List<string> rowStr = new(); - - foreach (var entry in entries) - { - if (entry.elements is null || entry.elements.Count == 0) - continue; - - List<string> entriesStr = new() { entry.Name }; - - foreach (var element in entry.elements) - entriesStr.Add(element.ToString()); - - var ItemStr = string.Join(" ", entriesStr); - rowStr.Add(ItemStr); - } - - return string.Join(" | ", rowStr); - } +using PrecisionTiming; +using RTSSSharedMemoryNET; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using static HandheldCompanion.Platforms.HWiNFO; + +namespace HandheldCompanion.Managers; + +public static class OSDManager +{ + public delegate void InitializedEventHandler(); + + // C1: GPU + // C2: CPU + // C3: RAM + // C4: VRAM + // C5: BATT + // C6: FPS + private const string Header = + "<C0=FFFFFF><C1=458A6E><C2=4C8DB2><C3=AD7B95><C4=A369A6><C5=F19F86><C6=D76D76><A0=-4><A1=5><A2=-2><A3=-3><A4=-4><A5=-5><S0=-50><S1=50>"; + + private static bool IsInitialized; + public static short OverlayLevel; + + private static readonly PrecisionTimer RefreshTimer; + private static int RefreshInterval = 100; + + private static readonly ConcurrentDictionary<int, OSD> OnScreenDisplay = new(); + private static AppEntry OnScreenAppEntry; + private static List<string> Content; + + static OSDManager() + { + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + + PlatformManager.RTSS.Hooked += RTSS_Hooked; + PlatformManager.RTSS.Unhooked += RTSS_Unhooked; + + // timer used to monitor foreground application framerate + RefreshInterval = SettingsManager.GetInt("OnScreenDisplayRefreshRate"); + + RefreshTimer = new PrecisionTimer(); + RefreshTimer.SetInterval(new Action(UpdateOSD), RefreshInterval, false, 0, TimerMode.Periodic, true); + } + + public static event InitializedEventHandler Initialized; + + private static void RTSS_Unhooked(int processId) + { + try + { + // clear previous display + if (OnScreenDisplay.TryGetValue(processId, out var OSD)) + { + OSD.Update(""); + OSD.Dispose(); + + OnScreenDisplay.TryRemove(new KeyValuePair<int, OSD>(processId, OSD)); + } + } + catch + { + } + } + + private static void RTSS_Hooked(AppEntry appEntry) + { + try + { + // update foreground id + OnScreenAppEntry = appEntry; + + // only create a new OSD if needed + if (OnScreenDisplay.ContainsKey(appEntry.ProcessId)) + return; + + OnScreenDisplay[OnScreenAppEntry.ProcessId] = new OSD(OnScreenAppEntry.Name); + } + catch + { + } + } + + public static void Start() + { + IsInitialized = true; + Initialized?.Invoke(); + + LogManager.LogInformation("{0} has started", "OSDManager"); + } + + private static uint OSDIndex(this OSD? osd) + { + if (osd is null) + return uint.MaxValue; + + var osdSlot = typeof(OSD).GetField("m_osdSlot", + BindingFlags.NonPublic | BindingFlags.Instance); + var value = osdSlot.GetValue(osd); + if (value is null) + return uint.MaxValue; + + return (uint)value; + } + + private static uint OSDIndex(string name) + { + var entries = OSD.GetOSDEntries().ToList(); + for (var i = 0; i < entries.Count(); i++) + if (entries[i].Owner == name) + return (uint)i; + return 0; + } + + private static void UpdateOSD() + { + if (OverlayLevel == 0) + return; + + foreach (var pair in OnScreenDisplay) + { + var processId = pair.Key; + var processOSD = pair.Value; + + try + { + if (processId == OnScreenAppEntry.ProcessId) + { + var content = Draw(processId); + processOSD.Update(content); + } + else + { + processOSD.Update(""); + } + } + catch + { + } + } + } + + public static string Draw(int processId) + { + SensorElement sensor; + Content = new List<string>(); + + switch (OverlayLevel) + { + default: + case 0: // Disabled + break; + + case 1: // Minimal + { + OverlayRow row1 = new(); + +<<<<<<< HEAD + OverlayEntry FPSentry = new("<APP>", "C6"); + FPSentry.elements.Add(new OverlayEntryElement + { + Value = "<FR>", + SzUnit = "FPS" + }); + FPSentry.elements.Add(new OverlayEntryElement + { + Value = "<FT>", + SzUnit = "ms" +======= + OverlayEntry FPSentry = new(AppFlag, "C6"); + FPSentry.elements.Add(new SensorElement + { + Value = PlatformManager.RTSS.GetFramerate(processId), + szUnit = "FPS" +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + }); + row1.entries.Add(FPSentry); + + // add header to row1 + Content.Add(Header + row1); + } + break; + + case 2: // Extended + { + OverlayRow row1 = new(); + + OverlayEntry BATTentry = new("BATT", "C5"); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryChargeLevel, + out sensor)) +<<<<<<< HEAD + BATTentry.elements.Add(new OverlayEntryElement(sensor)); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryRemainingCapacity, + out sensor)) + BATTentry.elements.Add(new OverlayEntryElement(sensor)); +======= + BATTentry.elements.Add(sensor); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryRemainingCapacity, + out sensor)) + BATTentry.elements.Add(sensor); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + row1.entries.Add(BATTentry); + + OverlayEntry GPUentry = new("GPU", "C1"); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUUsage, out sensor)) +<<<<<<< HEAD + GPUentry.elements.Add(new OverlayEntryElement(sensor)); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUPower, out sensor)) + GPUentry.elements.Add(new OverlayEntryElement(sensor)); +======= + GPUentry.elements.Add(sensor); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUPower, out sensor)) + GPUentry.elements.Add(sensor); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + row1.entries.Add(GPUentry); + + OverlayEntry CPUentry = new("CPU", "C2"); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUUsage, out sensor)) +<<<<<<< HEAD + CPUentry.elements.Add(new OverlayEntryElement(sensor)); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUPower, out sensor)) + CPUentry.elements.Add(new OverlayEntryElement(sensor)); +======= + CPUentry.elements.Add(sensor); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUPower, out sensor)) + CPUentry.elements.Add(sensor); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + row1.entries.Add(CPUentry); + + OverlayEntry RAMentry = new("RAM", "C3"); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.PhysicalMemoryUsage, + out sensor)) +<<<<<<< HEAD + RAMentry.elements.Add(new OverlayEntryElement(sensor)); + row1.entries.Add(RAMentry); + + OverlayEntry FPSentry = new("<APP>", "C6"); + FPSentry.elements.Add(new OverlayEntryElement + { + Value = "<FR>", + SzUnit = "FPS" + }); + FPSentry.elements.Add(new OverlayEntryElement + { + Value = "<FT>", + SzUnit = "ms" +======= + RAMentry.elements.Add(sensor); + row1.entries.Add(RAMentry); + + OverlayEntry FPSentry = new(AppFlag, "C6"); + FPSentry.elements.Add(new SensorElement + { + Value = PlatformManager.RTSS.GetFramerate(processId), + szUnit = "FPS" +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + }); + row1.entries.Add(FPSentry); + + // add header to row1 + Content.Add(Header + row1); + } + break; + + case 3: // Full + { + OverlayRow row1 = new(); + OverlayRow row2 = new(); + OverlayRow row3 = new(); + OverlayRow row4 = new(); + OverlayRow row5 = new(); + OverlayRow row6 = new(); + + OverlayEntry GPUentry = new("GPU", "C1", true); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUUsage, out sensor)) +<<<<<<< HEAD + GPUentry.elements.Add(new OverlayEntryElement(sensor)); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUPower, out sensor)) + GPUentry.elements.Add(new OverlayEntryElement(sensor)); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUTemperature, out sensor)) + GPUentry.elements.Add(new OverlayEntryElement(sensor)); +======= + GPUentry.elements.Add(sensor); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUPower, out sensor)) + GPUentry.elements.Add(sensor); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUTemperature, out sensor)) + GPUentry.elements.Add(sensor); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + row1.entries.Add(GPUentry); + + OverlayEntry CPUentry = new("CPU", "C2", true); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUUsage, out sensor)) +<<<<<<< HEAD + CPUentry.elements.Add(new OverlayEntryElement(sensor)); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUPower, out sensor)) + CPUentry.elements.Add(new OverlayEntryElement(sensor)); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUTemperature, out sensor)) + CPUentry.elements.Add(new OverlayEntryElement(sensor)); +======= + CPUentry.elements.Add(sensor); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUPower, out sensor)) + CPUentry.elements.Add(sensor); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.CPUTemperature, out sensor)) + CPUentry.elements.Add(sensor); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + row2.entries.Add(CPUentry); + + OverlayEntry RAMentry = new("RAM", "C3", true); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.PhysicalMemoryUsage, + out sensor)) +<<<<<<< HEAD + RAMentry.elements.Add(new OverlayEntryElement(sensor)); +======= + RAMentry.elements.Add(sensor); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + row3.entries.Add(RAMentry); + + OverlayEntry VRAMentry = new("VRAM", "C4", true); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.GPUMemoryUsage, out sensor)) +<<<<<<< HEAD + VRAMentry.elements.Add(new OverlayEntryElement(sensor)); +======= + VRAMentry.elements.Add(sensor); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + row4.entries.Add(VRAMentry); + + OverlayEntry BATTentry = new("BATT", "C5", true); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryChargeLevel, + out sensor)) +<<<<<<< HEAD + BATTentry.elements.Add(new OverlayEntryElement(sensor)); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryRemainingCapacity, + out sensor)) + BATTentry.elements.Add(new OverlayEntryElement(sensor)); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryRemainingTime, + out sensor)) + BATTentry.elements.Add(new OverlayEntryElement(sensor)); + row5.entries.Add(BATTentry); + + OverlayEntry FPSentry = new("<APP>", "C6"); + FPSentry.elements.Add(new OverlayEntryElement + { + Value = "<FR>", + SzUnit = "FPS" + }); + FPSentry.elements.Add(new OverlayEntryElement + { + Value = "<FT>", + SzUnit = "ms" +======= + BATTentry.elements.Add(sensor); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryRemainingCapacity, + out sensor)) + BATTentry.elements.Add(sensor); + if (PlatformManager.HWiNFO.MonitoredSensors.TryGetValue(SensorElementType.BatteryRemainingTime, + out sensor)) + BATTentry.elements.Add(sensor); + row5.entries.Add(BATTentry); + + OverlayEntry FPSentry = new(AppFlag, "C6", true); + FPSentry.elements.Add(new SensorElement + { + Value = PlatformManager.RTSS.GetFramerate(processId), + szUnit = "FPS" +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + }); + row6.entries.Add(FPSentry); + + // add header to row1 + Content.Add(Header + row1); + Content.Add(row2.ToString()); + Content.Add(row3.ToString()); + Content.Add(row4.ToString()); + Content.Add(row5.ToString()); + Content.Add(row6.ToString()); + } + break; + + case 4: // External + { + /* + * Intended to simply allow RTSS/HWINFO to run, and let the user configure the overlay within those + * tools as they wish + */ + } + break; + } + + return string.Join("\n", Content); + } + + public static void Stop() + { + if (!IsInitialized) + return; + + RefreshTimer.Stop(); + + // unhook all processes + foreach (var processId in OnScreenDisplay.Keys) + RTSS_Unhooked(processId); + + IsInitialized = false; + + LogManager.LogInformation("{0} has stopped", "OSDManager"); + } + + private static void SettingsManager_SettingValueChanged(string name, object value) + { + switch (name) + { + case "OnScreenDisplayLevel": + { + OverlayLevel = Convert.ToInt16(value); + + if (OverlayLevel > 0) + { + if (OverlayLevel == 4) + { + // No need to update OSD in External + RefreshTimer.Stop(); + + // Remove previous UI in External + foreach (var pair in OnScreenDisplay) + { + var processOSD = pair.Value; + processOSD.Update(""); + } + } + else + { + // Other modes need the refresh timer to update OSD + if (!RefreshTimer.IsRunning()) + RefreshTimer.Start(); + } + } + else + { + RefreshTimer.Stop(); + + // clear UI on stop + foreach (var pair in OnScreenDisplay) + { + var processOSD = pair.Value; + processOSD.Update(""); + } + } + } + break; + + case "OnScreenDisplayRefreshRate": + { + RefreshInterval = Convert.ToInt32(value); + + if (RefreshTimer.IsRunning()) + { + RefreshTimer.Stop(); + RefreshTimer.SetPeriod(RefreshInterval); + RefreshTimer.Start(); + } + } + break; + } + } +} + +public struct OverlayEntryElement +{ + public string Value { get; set; } + public string SzUnit { get; set; } + + public override string ToString() + { + return string.Format("<C0>{0:00}<S1>{1}<S><C>", Value, SzUnit); + } + + public OverlayEntryElement(SensorElement sensor) + { + Value = string.Format("{0:00}", sensor.Value); + SzUnit = sensor.szUnit; + } +} + +public class OverlayEntry : IDisposable +{ + public List<OverlayEntryElement> elements = new(); + + public OverlayEntry(string name, string colorScheme = "", bool indent = false) + { + Name = indent ? name + "\t" : name; + + if (!string.IsNullOrEmpty(colorScheme)) + Name = "<" + colorScheme + ">" + Name + "<C>"; + } + + public string Name { get; set; } + + public void Dispose() + { + elements.Clear(); + elements = null; + } +} + +public class OverlayRow : IDisposable +{ + public List<OverlayEntry> entries = new(); + + public void Dispose() + { + entries.Clear(); + entries = null; + } + + public override string ToString() + { + List<string> rowStr = new(); + + foreach (var entry in entries) + { + if (entry.elements is null || entry.elements.Count == 0) + continue; + + List<string> entriesStr = new() { entry.Name }; + + foreach (var element in entry.elements) + entriesStr.Add(element.ToString()); + + var ItemStr = string.Join(" ", entriesStr); + rowStr.Add(ItemStr); + } + + return string.Join(" | ", rowStr); + } } \ No newline at end of file diff --git a/HandheldCompanion/Managers/PerformanceManager.cs b/HandheldCompanion/Managers/PerformanceManager.cs index 8f935bb56..0ff4265d8 100644 --- a/HandheldCompanion/Managers/PerformanceManager.cs +++ b/HandheldCompanion/Managers/PerformanceManager.cs @@ -1,5 +1,9 @@ using HandheldCompanion.Misc; using HandheldCompanion.Processors; +<<<<<<< HEAD +======= +using HandheldCompanion.Utils; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d using HandheldCompanion.Views; using RTSSSharedMemoryNET; using System; @@ -22,7 +26,11 @@ public static class PowerMode /// Better Performance mode. /// </summary> // public static Guid BetterPerformance = new Guid("3af9B8d9-7c97-431d-ad78-34a8bfea439f"); +<<<<<<< HEAD public static Guid BetterPerformance = new(); +======= + public static Guid BetterPerformance = new("00000000-0000-0000-0000-000000000000"); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// <summary> /// Best Performance mode. @@ -37,7 +45,12 @@ public class PerformanceManager : Manager private const short INTERVAL_DEGRADED = 5000; // degraded interval between value scans public static int MaxDegreeOfParallelism = 4; +<<<<<<< HEAD public static readonly Guid[] PowerModes = new Guid[3] { PowerMode.BetterBattery, PowerMode.BetterPerformance, PowerMode.BestPerformance }; +======= + private static readonly Guid[] PowerModes = new Guid[3] + { PowerMode.BetterBattery, PowerMode.BetterPerformance, PowerMode.BestPerformance }; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d private readonly Timer autoWatchdog; private readonly Timer cpuWatchdog; @@ -100,9 +113,12 @@ public PerformanceManager() ProfileManager.Applied += ProfileManager_Applied; ProfileManager.Discarded += ProfileManager_Discarded; +<<<<<<< HEAD PowerProfileManager.Applied += PowerProfileManager_Applied; PowerProfileManager.Discarded += PowerProfileManager_Discarded; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d PlatformManager.HWiNFO.PowerLimitChanged += HWiNFO_PowerLimitChanged; PlatformManager.HWiNFO.GPUFrequencyChanged += HWiNFO_GPUFrequencyChanged; @@ -136,6 +152,7 @@ private void SettingsManagerOnSettingValueChanged(string name, object value) } } +<<<<<<< HEAD private void ProfileManager_Applied(Profile profile, UpdateSource source) { // apply profile define RSR @@ -170,6 +187,9 @@ private void ProfileManager_Discarded(Profile profile) } private void PowerProfileManager_Applied(PowerProfile profile, UpdateSource source) +======= + private void ProfileManager_Applied(Profile profile, ProfileUpdateSource source) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { // apply profile defined TDP if (profile.TDPOverrideEnabled && profile.TDPOverrideValues is not null) @@ -220,6 +240,7 @@ private void PowerProfileManager_Applied(PowerProfile profile, UpdateSource sour RestoreTDP(true); } +<<<<<<< HEAD // apply profile defined CPU if (profile.CPUOverrideEnabled) { @@ -231,6 +252,8 @@ private void PowerProfileManager_Applied(PowerProfile profile, UpdateSource sour RestoreCPUClock(true); } +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d // apply profile defined GPU if (profile.GPUOverrideEnabled) { @@ -260,6 +283,7 @@ private void PowerProfileManager_Applied(PowerProfile profile, UpdateSource sour { RequestCPUCoreCount(profile.CPUCoreCount); } +<<<<<<< HEAD else if (currentCoreCount != MotherboardInfo.NumberOfCores) { // restore default CPU Core Count @@ -274,6 +298,32 @@ private void PowerProfileManager_Applied(PowerProfile profile, UpdateSource sour } private void PowerProfileManager_Discarded(PowerProfile profile) +======= + else if (currentCoreCount != Environment.ProcessorCount) + { + // restore default CPU Core Count + RequestCPUCoreCount(Environment.ProcessorCount); + } + + // apply profile define RSR + try + { + if (profile.RSREnabled) + { + ADLXBackend.SetRSR(true); + ADLXBackend.SetRSRSharpness(profile.RSRSharpness); + } + else if (ADLXBackend.GetRSRState() == 1) + { + ADLXBackend.SetRSR(false); + ADLXBackend.SetRSRSharpness(20); + } + } + catch { } + } + + private void ProfileManager_Discarded(Profile profile) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { // restore default TDP if (profile.TDPOverrideEnabled) @@ -288,12 +338,15 @@ private void PowerProfileManager_Discarded(PowerProfile profile) StopAutoTDPWatchdog(true); StopTDPWatchdog(true); RestoreTDP(true); +<<<<<<< HEAD } // restore default CPU frequency if (profile.CPUOverrideEnabled) { RestoreCPUClock(true); +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } // restore default GPU frequency @@ -310,6 +363,7 @@ private void PowerProfileManager_Discarded(PowerProfile profile) RequestEPP(0x00000032); } +<<<<<<< HEAD // unapply profile defined CPU Core Count if (profile.CPUCoreEnabled) { @@ -324,6 +378,24 @@ private void PowerProfileManager_Discarded(PowerProfile profile) // restore PowerMode.BetterPerformance RequestPowerMode(PowerMode.BetterPerformance); +======= + // (un)apply profile defined CPU Core Count + if (profile.CPUCoreEnabled) + { + RequestCPUCoreCount(100); + } + + try + { + // restore default RSR + if (profile.RSREnabled) + { + ADLXBackend.SetRSR(false); + ADLXBackend.SetRSRSharpness(20); + } + } + catch { } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void RestoreTDP(bool immediate) @@ -332,12 +404,15 @@ private void RestoreTDP(bool immediate) RequestTDP(pType, MainWindow.CurrentDevice.cTDP[1], immediate); } +<<<<<<< HEAD private void RestoreCPUClock(bool immediate) { uint maxClock = MotherboardInfo.ProcessorMaxTurboSpeed; RequestCPUClock(maxClock); } +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d private void RestoreGPUClock(bool immediate) { RequestGPUClock(255 * 50, immediate); @@ -481,7 +556,11 @@ private void powerWatchdog_Elapsed(object? sender, ElapsedEventArgs e) } // read perfboostmode +<<<<<<< HEAD var result = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFBOOSTMODE); +======= + var result = ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFBOOSTMODE); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d var perfboostmode = result[(int)PowerIndexType.AC] == (uint)PerfBoostMode.Aggressive && result[(int)PowerIndexType.DC] == (uint)PerfBoostMode.Aggressive; @@ -492,7 +571,11 @@ private void powerWatchdog_Elapsed(object? sender, ElapsedEventArgs e) } // Checking if current EPP value has changed to reflect that +<<<<<<< HEAD var EPP = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP); +======= + var EPP = ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d var DCvalue = EPP[(int)PowerIndexType.DC]; if (DCvalue != currentEPP) @@ -530,13 +613,21 @@ private async void cpuWatchdog_Elapsed(object? sender, ElapsedEventArgs e) var TDP = StoredTDP[idx]; +<<<<<<< HEAD if (processor is AMDProcessor) +======= + if (processor.GetType() == typeof(AMDProcessor)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { // AMD reduces TDP by 10% when OS power mode is set to Best power efficiency if (currentPowerMode == PowerMode.BetterBattery) TDP = (int)Math.Truncate(TDP * 0.9); } +<<<<<<< HEAD else if (processor is IntelProcessor) +======= + else if (processor.GetType() == typeof(IntelProcessor)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { // Intel doesn't have stapm if (type == PowerType.Stapm) @@ -561,7 +652,11 @@ private async void cpuWatchdog_Elapsed(object? sender, ElapsedEventArgs e) TDPdone = CurrentTDP[0] == StoredTDP[0] && CurrentTDP[1] == StoredTDP[1] && CurrentTDP[2] == StoredTDP[2]; // processor specific +<<<<<<< HEAD if (processor is IntelProcessor) +======= + if (processor.GetType() == typeof(IntelProcessor)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d { var TDPslow = (int)StoredTDP[(int)PowerType.Slow]; var TDPfast = (int)StoredTDP[(int)PowerType.Fast]; @@ -741,9 +836,15 @@ public void RequestGPUClock(double value, bool immediate = false) processor.SetGPUClock(value); } +<<<<<<< HEAD public void RequestPowerMode(Guid guid) { currentPowerMode = guid; +======= + public void RequestPowerMode(int idx) + { + currentPowerMode = PowerModes[idx]; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d LogManager.LogInformation("User requested power scheme: {0}", currentPowerMode); if (PowerSetActiveOverlayScheme(currentPowerMode) != 0) LogManager.LogWarning("Failed to set requested power scheme: {0}", currentPowerMode); @@ -760,18 +861,30 @@ public void RequestEPP(uint EPPOverrideValue) }; // Is the EPP value already correct? +<<<<<<< HEAD uint[] EPP = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP); +======= + uint[] EPP = ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d if (EPP[0] == requestedEPP[0] && EPP[1] == requestedEPP[1]) return; LogManager.LogInformation("User requested EPP AC: {0}, DC: {1}", requestedEPP[0], requestedEPP[1]); // Set profile EPP +<<<<<<< HEAD PowerScheme.WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP, requestedEPP[0], requestedEPP[1]); PowerScheme.WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP1, requestedEPP[0], requestedEPP[1]); // Has the EPP value been applied? EPP = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP); +======= + WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP, requestedEPP[0], requestedEPP[1]); + WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP1, requestedEPP[0], requestedEPP[1]); + + // Has the EPP value been applied? + EPP = ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFEPP); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d if (EPP[0] != requestedEPP[0] || EPP[1] != requestedEPP[1]) LogManager.LogWarning("Failed to set requested EPP"); } @@ -783,29 +896,50 @@ public void RequestCPUCoreCount(int CoreCount) uint currentCoreCountPercent = (uint)((100.0d / MotherboardInfo.NumberOfCores) * CoreCount); // Is the CPMINCORES value already correct? +<<<<<<< HEAD uint[] CPMINCORES = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMINCORES); bool CPMINCORESReady = (CPMINCORES[0] == currentCoreCountPercent && CPMINCORES[1] == currentCoreCountPercent); // Is the CPMAXCORES value already correct? uint[] CPMAXCORES = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMAXCORES); +======= + uint[] CPMINCORES = ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMINCORES); + bool CPMINCORESReady = (CPMINCORES[0] == currentCoreCountPercent && CPMINCORES[1] == currentCoreCountPercent); + + // Is the CPMAXCORES value already correct? + uint[] CPMAXCORES = ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMAXCORES); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d bool CPMAXCORESReady = (CPMAXCORES[0] == currentCoreCountPercent && CPMAXCORES[1] == currentCoreCountPercent); if (CPMINCORESReady && CPMAXCORESReady) return; // Set profile CPMINCORES and CPMAXCORES +<<<<<<< HEAD PowerScheme.WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMINCORES, currentCoreCountPercent, currentCoreCountPercent); PowerScheme.WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMAXCORES, currentCoreCountPercent, currentCoreCountPercent); +======= + WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMINCORES, currentCoreCountPercent, currentCoreCountPercent); + WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMAXCORES, currentCoreCountPercent, currentCoreCountPercent); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d LogManager.LogInformation("User requested CoreCount: {0} ({1}%)", CoreCount, currentCoreCountPercent); // Has the CPMINCORES value been applied? +<<<<<<< HEAD CPMINCORES = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMINCORES); +======= + CPMINCORES = ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMINCORES); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d if (CPMINCORES[0] != currentCoreCountPercent || CPMINCORES[1] != currentCoreCountPercent) LogManager.LogWarning("Failed to set requested CPMINCORES"); // Has the CPMAXCORES value been applied? +<<<<<<< HEAD CPMAXCORES = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMAXCORES); +======= + CPMAXCORES = ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.CPMAXCORES); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d if (CPMAXCORES[0] != currentCoreCountPercent || CPMAXCORES[1] != currentCoreCountPercent) LogManager.LogWarning("Failed to set requested CPMAXCORES"); } @@ -814,12 +948,18 @@ public void RequestPerfBoostMode(bool value) { currentPerfBoostMode = value; +<<<<<<< HEAD var perfboostmode = value ? (uint)PerfBoostMode.Aggressive : (uint)PerfBoostMode.Enabled; PowerScheme.WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFBOOSTMODE, perfboostmode, perfboostmode); +======= + var perfboostmode = value ? (uint)PerfBoostMode.Aggressive : (uint)PerfBoostMode.Disabled; + WritePowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PERFBOOSTMODE, perfboostmode, perfboostmode); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d LogManager.LogInformation("User requested perfboostmode: {0}", value); } +<<<<<<< HEAD private void RequestCPUClock(uint cpuClock) { double maxClock = MotherboardInfo.ProcessorMaxTurboSpeed; @@ -840,6 +980,38 @@ private void RequestCPUClock(uint cpuClock) currentClock = PowerScheme.ReadPowerCfg(PowerSubGroup.SUB_PROCESSOR, PowerSetting.PROCFREQMAX); if (currentClock[0] != cpuClock || currentClock[1] != cpuClock) LogManager.LogWarning("Failed to set requested PROCFREQMAX"); +======= + private uint[] ReadPowerCfg(Guid SubGroup, Guid Settings) + { + var results = new uint[2]; + + if (PowerProfile.GetActiveScheme(out var currentScheme)) + { + // read AC/DC values + PowerProfile.GetValue(PowerIndexType.AC, currentScheme, SubGroup, Settings, + out results[(int)PowerIndexType.AC]); + PowerProfile.GetValue(PowerIndexType.DC, currentScheme, SubGroup, Settings, + out results[(int)PowerIndexType.DC]); + } + + return results; + } + + private void WritePowerCfg(Guid SubGroup, Guid Settings, uint ACValue, uint DCValue) + { + if (PowerProfile.GetActiveScheme(out var currentScheme)) + { + // unhide attribute + PowerProfile.SetAttribute(SubGroup, Settings, false); + + // set value(s) + PowerProfile.SetValue(PowerIndexType.AC, currentScheme, SubGroup, Settings, ACValue); + PowerProfile.SetValue(PowerIndexType.DC, currentScheme, SubGroup, Settings, DCValue); + + // activate scheme + PowerProfile.SetActiveScheme(currentScheme); + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } public override void Start() @@ -850,6 +1022,18 @@ public override void Start() // initialize processor processor = Processor.GetCurrent(); +<<<<<<< HEAD +======= + // read OS specific values + var HypervisorEnforcedCodeIntegrityEnabled = RegistryUtils.GetBoolean(@"SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios", + "HypervisorEnforcedCodeIntegrity"); + var VulnerableDriverBlocklistEnable = RegistryUtils.GetBoolean(@"SYSTEM\CurrentControlSet\Control\CI\Config", + "VulnerableDriverBlocklistEnable"); + + if (VulnerableDriverBlocklistEnable || HypervisorEnforcedCodeIntegrityEnabled) + LogManager.LogWarning("Core isolation settings are turned on. TDP read/write and fan control might be disabled"); + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d if (processor.IsInitialized) { processor.StatusChanged += Processor_StatusChanged; diff --git a/HandheldCompanion/Managers/PlatformManager.cs b/HandheldCompanion/Managers/PlatformManager.cs index e0ddf1a8b..a282ce97f 100644 --- a/HandheldCompanion/Managers/PlatformManager.cs +++ b/HandheldCompanion/Managers/PlatformManager.cs @@ -1,270 +1,277 @@ -using HandheldCompanion.Platforms; -using System; -using System.Diagnostics; -using System.Timers; -using System.Windows; - -namespace HandheldCompanion.Managers; - -public static class PlatformManager -{ - private const int UpdateInterval = 1000; - - // gaming platforms - public static readonly Steam Steam = new(); - public static readonly GOGGalaxy GOGGalaxy = new(); - public static readonly UbisoftConnect UbisoftConnect = new(); - - // misc platforms - public static RTSS RTSS = new(); - public static HWiNFO HWiNFO = new(); - public static OpenHardwareMonitor OpenHardwareMonitor = new(); - - private static Timer UpdateTimer; - - private static bool IsInitialized; - - private static PlatformNeeds CurrentNeeds = PlatformNeeds.None; - private static PlatformNeeds PreviousNeeds = PlatformNeeds.None; - - public static event InitializedEventHandler Initialized; - public delegate void InitializedEventHandler(); - - public static void Start() - { - if (Steam.IsInstalled) - Steam.Start(); - - if (GOGGalaxy.IsInstalled) - { - // do something - } - - if (UbisoftConnect.IsInstalled) - { - // do something - } - - if (RTSS.IsInstalled) - { - // do something - } - - if (HWiNFO.IsInstalled) - { - // do something - } - - if (OpenHardwareMonitor.IsInstalled) - OpenHardwareMonitor.Start(); - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - ProfileManager.Applied += ProfileManager_Applied; - PowerProfileManager.Applied += PowerProfileManager_Applied; - - UpdateTimer = new Timer(UpdateInterval); - UpdateTimer.AutoReset = false; - UpdateTimer.Elapsed += (sender, e) => MonitorPlatforms(); - - IsInitialized = true; - Initialized?.Invoke(); - - LogManager.LogInformation("{0} has started", "PlatformManager"); - } - - private static void PowerProfileManager_Applied(Misc.PowerProfile profile, UpdateSource source) - { - // AutoTDP - if (profile.AutoTDPEnabled) - CurrentNeeds |= PlatformNeeds.AutoTDP; - else - CurrentNeeds &= ~PlatformNeeds.AutoTDP; - - UpdateTimer.Stop(); - UpdateTimer.Start(); - } - - private static void ProfileManager_Applied(Profile profile, UpdateSource source) - { - // Framerate limiter - if (profile.FramerateEnabled) - CurrentNeeds |= PlatformNeeds.FramerateLimiter; - else - CurrentNeeds &= ~PlatformNeeds.FramerateLimiter; - - UpdateTimer.Stop(); - UpdateTimer.Start(); - } - - private static void SettingsManager_SettingValueChanged(string name, object value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (name) - { - case "OnScreenDisplayLevel": - { - var level = Convert.ToInt16(value); - - switch (level) - { - case 0: // Disabled - CurrentNeeds &= ~PlatformNeeds.OnScreenDisplay; - CurrentNeeds &= ~PlatformNeeds.OnScreenDisplayComplex; - break; - default: - case 1: // Minimal - CurrentNeeds |= PlatformNeeds.OnScreenDisplay; - CurrentNeeds &= ~PlatformNeeds.OnScreenDisplayComplex; - break; - case 2: // Extended - case 3: // Full - case 4: // External - CurrentNeeds |= PlatformNeeds.OnScreenDisplay; - CurrentNeeds |= PlatformNeeds.OnScreenDisplayComplex; - break; - } - - UpdateTimer.Stop(); - UpdateTimer.Start(); - } - break; - } - }); - } - - private static void MonitorPlatforms() - { - /* - * Dependencies: - * HWInfo: OSD - * RTSS: AutoTDP, framerate limiter, OSD - */ - - // Check if the current needs are the same as the previous needs - if (CurrentNeeds == PreviousNeeds) return; - - // Start or stop HWiNFO and RTSS based on the current and previous needs - if (CurrentNeeds.HasFlag(PlatformNeeds.OnScreenDisplay)) - { - // If OSD is needed, start RTSS and start HWiNFO only if OnScreenDisplayComplex is true - if (!PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplay)) - // Only start RTSS if it was not running before and if it is installed - if (RTSS.IsInstalled) - { - // Start RTSS - RTSS.Start(); - } - if (CurrentNeeds.HasFlag(PlatformNeeds.OnScreenDisplayComplex)) - { - // This condition checks if OnScreenDisplayComplex is true - // OnScreenDisplayComplex is a new flag that indicates if the OSD needs more information from HWiNFO - if (!PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplay) || - !PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplayComplex)) - // Only start HWiNFO if it was not running before or if OnScreenDisplayComplex was false and if it is installed - if (HWiNFO.IsInstalled) - HWiNFO.Start(); - } - else - { - // If OnScreenDisplayComplex is false, stop HWiNFO if it was running before - if (PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplay) && - PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplayComplex)) - // Only stop HWiNFO if it is installed - if (HWiNFO.IsInstalled) - HWiNFO.Stop(true); - } - } - else if (CurrentNeeds.HasFlag(PlatformNeeds.AutoTDP) || CurrentNeeds.HasFlag(PlatformNeeds.FramerateLimiter)) - { - // If AutoTDP or framerate limiter is needed, start only RTSS and stop HWiNFO - if (!PreviousNeeds.HasFlag(PlatformNeeds.AutoTDP) && !PreviousNeeds.HasFlag(PlatformNeeds.FramerateLimiter)) - // Only start RTSS if it was not running before and if it is installed - if (RTSS.IsInstalled) - RTSS.Start(); - - // Only stop HWiNFO if it was running before - // Only stop HWiNFO if it is installed - if (PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplay)) - if (HWiNFO.IsInstalled) - HWiNFO.Stop(true); - } - else - { - // If none of the needs are present, stop both HWiNFO and RTSS - if (PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplay) || PreviousNeeds.HasFlag(PlatformNeeds.AutoTDP) || - PreviousNeeds.HasFlag(PlatformNeeds.FramerateLimiter)) - { - // Only stop HWiNFO and RTSS if they were running before and if they are installed - if (HWiNFO.IsInstalled) HWiNFO.Stop(true); - if (RTSS.IsInstalled) - { - // Stop RTSS - RTSS.Stop(); - } - } - } - - // Store the current needs in the previous needs variable - PreviousNeeds = CurrentNeeds; - } - - public static void Stop() - { - if (Steam.IsInstalled) - Steam.Stop(); - - if (GOGGalaxy.IsInstalled) - GOGGalaxy.Dispose(); - - if (UbisoftConnect.IsInstalled) - UbisoftConnect.Dispose(); - - if (RTSS.IsInstalled) - { - var killRTSS = SettingsManager.GetBoolean("PlatformRTSSEnabled"); - RTSS.Stop(killRTSS); - RTSS.Dispose(); - } - - if (HWiNFO.IsInstalled) - { - var killHWiNFO = SettingsManager.GetBoolean("PlatformHWiNFOEnabled"); - HWiNFO.Stop(killHWiNFO); - HWiNFO.Dispose(); - } - - if (OpenHardwareMonitor.IsInstalled) - OpenHardwareMonitor.Stop(); - - IsInitialized = false; - - LogManager.LogInformation("{0} has stopped", "PlatformManager"); - } - - public static PlatformType GetPlatform(Process proc) - { - if (!IsInitialized) - return PlatformType.Windows; - - // is this process part of a specific platform - if (Steam.IsRelated(proc)) - return Steam.PlatformType; - if (GOGGalaxy.IsRelated(proc)) - return GOGGalaxy.PlatformType; - if (UbisoftConnect.IsRelated(proc)) - return UbisoftConnect.PlatformType; - return PlatformType.Windows; - } - - [Flags] - private enum PlatformNeeds - { - None = 0, - AutoTDP = 1, - FramerateLimiter = 2, - OnScreenDisplay = 4, - OnScreenDisplayComplex = 8 - } +using HandheldCompanion.Platforms; +using System; +using System.Diagnostics; +using System.Timers; +using System.Windows; + +namespace HandheldCompanion.Managers; + +public static class PlatformManager +{ + private const int UpdateInterval = 1000; + + // gaming platforms +<<<<<<< HEAD + public static readonly Steam Steam = new(); +======= + public static readonly SteamPlatform Steam = new(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public static readonly GOGGalaxy GOGGalaxy = new(); + public static readonly UbisoftConnect UbisoftConnect = new(); + + // misc platforms + public static RTSS RTSS = new(); + public static HWiNFO HWiNFO = new(); +<<<<<<< HEAD + public static OpenHardwareMonitor OpenHardwareMonitor = new(); +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + private static Timer UpdateTimer; + + private static bool IsInitialized; + + private static PlatformNeeds CurrentNeeds = PlatformNeeds.None; + private static PlatformNeeds PreviousNeeds = PlatformNeeds.None; + + public static event InitializedEventHandler Initialized; + public delegate void InitializedEventHandler(); + + public static void Start() + { + if (Steam.IsInstalled) + Steam.Start(); + + if (GOGGalaxy.IsInstalled) + { + // do something + } + + if (UbisoftConnect.IsInstalled) + { + // do something + } + + if (RTSS.IsInstalled) + { + // do something + } + + if (HWiNFO.IsInstalled) + { + // do something + } + + if (OpenHardwareMonitor.IsInstalled) + OpenHardwareMonitor.Start(); + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + ProfileManager.Applied += ProfileManager_Applied; + PowerProfileManager.Applied += PowerProfileManager_Applied; + + UpdateTimer = new Timer(UpdateInterval); + UpdateTimer.AutoReset = false; + UpdateTimer.Elapsed += (sender, e) => MonitorPlatforms(); + + IsInitialized = true; + Initialized?.Invoke(); + + LogManager.LogInformation("{0} has started", "PlatformManager"); + } + + private static void PowerProfileManager_Applied(Misc.PowerProfile profile, UpdateSource source) + { + // AutoTDP + if (profile.AutoTDPEnabled) + CurrentNeeds |= PlatformNeeds.AutoTDP; + else + CurrentNeeds &= ~PlatformNeeds.AutoTDP; + + UpdateTimer.Stop(); + UpdateTimer.Start(); + } + + private static void ProfileManager_Applied(Profile profile, UpdateSource source) + { + // Framerate limiter + if (profile.FramerateEnabled) + CurrentNeeds |= PlatformNeeds.FramerateLimiter; + else + CurrentNeeds &= ~PlatformNeeds.FramerateLimiter; + + UpdateTimer.Stop(); + UpdateTimer.Start(); + } + + private static void SettingsManager_SettingValueChanged(string name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { + case "OnScreenDisplayLevel": + { + var level = Convert.ToInt16(value); + + switch (level) + { + case 0: // Disabled + CurrentNeeds &= ~PlatformNeeds.OnScreenDisplay; + CurrentNeeds &= ~PlatformNeeds.OnScreenDisplayComplex; + break; + default: + case 1: // Minimal + CurrentNeeds |= PlatformNeeds.OnScreenDisplay; + CurrentNeeds &= ~PlatformNeeds.OnScreenDisplayComplex; + break; + case 2: // Extended + case 3: // Full + case 4: // External + CurrentNeeds |= PlatformNeeds.OnScreenDisplay; + CurrentNeeds |= PlatformNeeds.OnScreenDisplayComplex; + break; + } + + UpdateTimer.Stop(); + UpdateTimer.Start(); + } + break; + } + }); + } + + private static void MonitorPlatforms() + { + /* + * Dependencies: + * HWInfo: OSD + * RTSS: AutoTDP, framerate limiter, OSD + */ + + // Check if the current needs are the same as the previous needs + if (CurrentNeeds == PreviousNeeds) return; + + // Start or stop HWiNFO and RTSS based on the current and previous needs + if (CurrentNeeds.HasFlag(PlatformNeeds.OnScreenDisplay)) + { + // If OSD is needed, start RTSS and start HWiNFO only if OnScreenDisplayComplex is true + if (!PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplay)) + // Only start RTSS if it was not running before and if it is installed + if (RTSS.IsInstalled) + { + // Start RTSS + RTSS.Start(); + } + if (CurrentNeeds.HasFlag(PlatformNeeds.OnScreenDisplayComplex)) + { + // This condition checks if OnScreenDisplayComplex is true + // OnScreenDisplayComplex is a new flag that indicates if the OSD needs more information from HWiNFO + if (!PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplay) || + !PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplayComplex)) + // Only start HWiNFO if it was not running before or if OnScreenDisplayComplex was false and if it is installed + if (HWiNFO.IsInstalled) + HWiNFO.Start(); + } + else + { + // If OnScreenDisplayComplex is false, stop HWiNFO if it was running before + if (PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplay) && + PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplayComplex)) + // Only stop HWiNFO if it is installed + if (HWiNFO.IsInstalled) + HWiNFO.Stop(true); + } + } + else if (CurrentNeeds.HasFlag(PlatformNeeds.AutoTDP) || CurrentNeeds.HasFlag(PlatformNeeds.FramerateLimiter)) + { + // If AutoTDP or framerate limiter is needed, start only RTSS and stop HWiNFO + if (!PreviousNeeds.HasFlag(PlatformNeeds.AutoTDP) && !PreviousNeeds.HasFlag(PlatformNeeds.FramerateLimiter)) + // Only start RTSS if it was not running before and if it is installed + if (RTSS.IsInstalled) + RTSS.Start(); + + // Only stop HWiNFO if it was running before + // Only stop HWiNFO if it is installed + if (PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplay)) + if (HWiNFO.IsInstalled) + HWiNFO.Stop(true); + } + else + { + // If none of the needs are present, stop both HWiNFO and RTSS + if (PreviousNeeds.HasFlag(PlatformNeeds.OnScreenDisplay) || PreviousNeeds.HasFlag(PlatformNeeds.AutoTDP) || + PreviousNeeds.HasFlag(PlatformNeeds.FramerateLimiter)) + { + // Only stop HWiNFO and RTSS if they were running before and if they are installed + if (HWiNFO.IsInstalled) HWiNFO.Stop(true); + if (RTSS.IsInstalled) + { + // Stop RTSS + RTSS.Stop(); + } + } + } + + // Store the current needs in the previous needs variable + PreviousNeeds = CurrentNeeds; + } + + public static void Stop() + { + if (Steam.IsInstalled) + Steam.Stop(); + + if (GOGGalaxy.IsInstalled) + GOGGalaxy.Dispose(); + + if (UbisoftConnect.IsInstalled) + UbisoftConnect.Dispose(); + + if (RTSS.IsInstalled) + { + var killRTSS = SettingsManager.GetBoolean("PlatformRTSSEnabled"); + RTSS.Stop(killRTSS); + RTSS.Dispose(); + } + + if (HWiNFO.IsInstalled) + { + var killHWiNFO = SettingsManager.GetBoolean("PlatformHWiNFOEnabled"); + HWiNFO.Stop(killHWiNFO); + HWiNFO.Dispose(); + } + + if (OpenHardwareMonitor.IsInstalled) + OpenHardwareMonitor.Stop(); + + IsInitialized = false; + + LogManager.LogInformation("{0} has stopped", "PlatformManager"); + } + + public static PlatformType GetPlatform(Process proc) + { + if (!IsInitialized) + return PlatformType.Windows; + + // is this process part of a specific platform + if (Steam.IsRelated(proc)) + return Steam.PlatformType; + if (GOGGalaxy.IsRelated(proc)) + return GOGGalaxy.PlatformType; + if (UbisoftConnect.IsRelated(proc)) + return UbisoftConnect.PlatformType; + return PlatformType.Windows; + } + + [Flags] + private enum PlatformNeeds + { + None = 0, + AutoTDP = 1, + FramerateLimiter = 2, + OnScreenDisplay = 4, + OnScreenDisplayComplex = 8 + } } \ No newline at end of file diff --git a/HandheldCompanion/Managers/ProcessManager.cs b/HandheldCompanion/Managers/ProcessManager.cs index 74e82f96a..e91c69449 100644 --- a/HandheldCompanion/Managers/ProcessManager.cs +++ b/HandheldCompanion/Managers/ProcessManager.cs @@ -55,6 +55,12 @@ static ProcessManager() TreeScope.Children, OnWindowOpened); +<<<<<<< HEAD +======= + // list all current windows + EnumWindows(OnWindowDiscovered, 0); + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d ForegroundTimer = new Timer(1000); ForegroundTimer.Elapsed += ForegroundCallback; } diff --git a/HandheldCompanion/Managers/ProfileManager.cs b/HandheldCompanion/Managers/ProfileManager.cs index 0d056e434..ba03cd3c6 100644 --- a/HandheldCompanion/Managers/ProfileManager.cs +++ b/HandheldCompanion/Managers/ProfileManager.cs @@ -1,586 +1,600 @@ -using HandheldCompanion.Controllers; -using HandheldCompanion.Controls; -using HandheldCompanion.Misc; -using HandheldCompanion.Utils; -using HandheldCompanion.Views; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using static HandheldCompanion.Utils.XInputPlusUtils; - -namespace HandheldCompanion.Managers; - -public static class ProfileManager -{ - public const string DefaultName = "Default"; - - public static Dictionary<string, Profile> profiles = new(StringComparer.InvariantCultureIgnoreCase); - - private static Profile currentProfile; - - private static string ProfilesPath; - - public static bool IsInitialized; - - static ProfileManager() - { - // initialiaze path(s) - ProfilesPath = Path.Combine(MainWindow.SettingsPath, "profiles"); - if (!Directory.Exists(ProfilesPath)) - Directory.CreateDirectory(ProfilesPath); - - ProcessManager.ForegroundChanged += ProcessManager_ForegroundChanged; - ProcessManager.ProcessStarted += ProcessManager_ProcessStarted; - ProcessManager.ProcessStopped += ProcessManager_ProcessStopped; - - PowerProfileManager.Deleted += PowerProfileManager_Deleted; - - ControllerManager.ControllerPlugged += ControllerManager_ControllerPlugged; - } - - public static FileSystemWatcher profileWatcher { get; set; } - - public static void Start() - { - // monitor profile files - profileWatcher = new FileSystemWatcher - { - Path = ProfilesPath, - EnableRaisingEvents = true, - IncludeSubdirectories = true, - Filter = "*.json", - NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size - }; - profileWatcher.Deleted += ProfileDeleted; - - // process existing profiles - var fileEntries = Directory.GetFiles(ProfilesPath, "*.json", SearchOption.AllDirectories); - foreach (var fileName in fileEntries) - ProcessProfile(fileName); - - // check for default profile - if (!HasDefault()) - { - Layout deviceLayout = MainWindow.CurrentDevice.DefaultLayout.Clone() as Layout; - Profile defaultProfile = new() - { - Name = DefaultName, - Default = true, - Enabled = false, - Layout = deviceLayout, - LayoutTitle = LayoutTemplate.DefaultLayout.Name, - LayoutEnabled = true - }; - - UpdateOrCreateProfile(defaultProfile, UpdateSource.Creation); - } - - // force apply default - ApplyProfile(GetDefault()); - - IsInitialized = true; - Initialized?.Invoke(); - - LogManager.LogInformation("{0} has started", "ProfileManager"); - } - - public static void Stop() - { - if (!IsInitialized) - return; - - IsInitialized = false; - - profileWatcher.Deleted -= ProfileDeleted; - profileWatcher.Dispose(); - - LogManager.LogInformation("{0} has stopped", "ProfileManager"); - } - - public static bool Contains(Profile profile) - { - foreach (var pr in profiles.Values) - if (pr.Path.Equals(profile.Path, StringComparison.InvariantCultureIgnoreCase)) - return true; - - return false; - } - - public static bool Contains(string fileName) - { - foreach (var pr in profiles.Values) - if (pr.Path.Equals(fileName, StringComparison.InvariantCultureIgnoreCase)) - return true; - - return false; - } - - public static Profile GetProfileFromPath(string path, bool ignoreStatus) - { - // get profile from path - Profile profile = profiles.Values.FirstOrDefault(a => a.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase)); - - if (profile is null) - { - // otherwise, get profile from executable - string fileName = Path.GetFileName(path); - profile = profiles.Values.FirstOrDefault(a => a.Executable.Equals(fileName, StringComparison.InvariantCultureIgnoreCase)); - - if (profile is null) - return GetDefault(); - } - - // ignore profile status (enabled/disabled) - if (ignoreStatus) - return profile; - - return profile.Enabled ? profile : GetDefault(); - } - - private static void ApplyProfile(Profile profile, UpdateSource source = UpdateSource.Background, - bool announce = true) - { - // might not be the same anymore if disabled - profile = GetProfileFromPath(profile.Path, false); - - // we've already announced this profile - if (currentProfile is not null) - if (currentProfile.Path.Equals(profile.Path, StringComparison.InvariantCultureIgnoreCase)) - announce = false; - - // update current profile before invoking event - currentProfile = profile; - - // raise event - Applied?.Invoke(profile, source); - - // send toast - // todo: localize me - if (announce) - { - LogManager.LogInformation("Profile {0} applied", profile.Name); - ToastManager.SendToast($"Profile {profile.Name} applied"); - } - } - - private static void PowerProfileManager_Deleted(PowerProfile powerProfile) - { - foreach(Profile profile in profiles.Values) - { - bool isCurrent = profile.PowerProfile == powerProfile.Guid; - if (isCurrent) - { - // sanitize profile - SanitizeProfile(profile); - - // update profile - UpdateOrCreateProfile(profile); - - if (currentProfile.Path.Equals(profile.Path, StringComparison.InvariantCultureIgnoreCase)) - ApplyProfile(profile); - } - } - } - - private static void ProcessManager_ProcessStopped(ProcessEx processEx) - { - try - { - var profile = GetProfileFromPath(processEx.Path, true); - - // do not discard default profile - if (profile is null || profile.Default) - return; - - // raise event - Discarded?.Invoke(profile); - - if (profile.ErrorCode.HasFlag(ProfileErrorCode.Running)) - { - // update profile - UpdateOrCreateProfile(profile); - - // restore default profile - ApplyProfile(GetDefault()); - } - } - catch - { - } - } - - private static void ProcessManager_ProcessStarted(ProcessEx processEx, bool OnStartup) - { - try - { - var profile = GetProfileFromPath(processEx.Path, true); - - if (profile is null || profile.Default) - return; - - // update profile executable path - profile.Path = processEx.Path; - - // update profile - UpdateOrCreateProfile(profile); - } - catch - { - } - } - - private static void ProcessManager_ForegroundChanged(ProcessEx proc, ProcessEx back) - { - try - { - var profile = GetProfileFromPath(proc.Path, false); - - // update profile executable path - if (!profile.Default) - { - profile.Path = proc.Path; - UpdateOrCreateProfile(profile); - } - - // raise event - if (back is not null) - { - var backProfile = GetProfileFromPath(back.Path, false); - - if (backProfile != profile) - Discarded?.Invoke(backProfile); - } - - ApplyProfile(profile); - } - catch - { - } - } - - private static void ProfileDeleted(object sender, FileSystemEventArgs e) - { - // not ideal - var ProfileName = e.Name.Replace(".json", ""); - var profile = profiles.Values.FirstOrDefault(p => p.Name.Equals(ProfileName, StringComparison.InvariantCultureIgnoreCase)); - - // couldn't find a matching profile - if (profile is null) - return; - - // you can't delete default profile ! - if (profile.Default) - { - SerializeProfile(profile); - return; - } - - DeleteProfile(profile); - } - - private static bool HasDefault() - { - return profiles.Values.Count(a => a.Default) != 0; - } - - public static Profile GetDefault() - { - if (HasDefault()) - return profiles.Values.FirstOrDefault(a => a.Default); - return new Profile(); - } - - public static Profile GetCurrent() - { - if (currentProfile is not null) - return currentProfile; - - return GetDefault(); - } - - private static void ProcessProfile(string fileName) - { - Profile profile = null; - try - { - var outputraw = File.ReadAllText(fileName); - var jObject = JObject.Parse(outputraw); - - // latest pre-versionning release - Version version = new("0.15.0.4"); - if (jObject.TryGetValue("Version", out var value)) - version = new Version(value.ToString()); - - switch (version.ToString()) - { - case "0.15.0.4": - { - outputraw = CommonUtils.RegexReplace(outputraw, "Generic.Dictionary(.*)System.Private.CoreLib\"", - "Generic.SortedDictionary$1System.Collections\""); - jObject = JObject.Parse(outputraw); - jObject.Remove("MotionSensivityArray"); - outputraw = jObject.ToString(); - } - break; - case "0.16.0.5": - { - outputraw = outputraw.Replace( - "\"System.Collections.Generic.SortedDictionary`2[[HandheldCompanion.Inputs.ButtonFlags, HandheldCompanion],[System.Boolean, System.Private.CoreLib]], System.Collections\"", - "\"System.Collections.Concurrent.ConcurrentDictionary`2[[HandheldCompanion.Inputs.ButtonFlags, HandheldCompanion],[System.Boolean, System.Private.CoreLib]], System.Collections.Concurrent\""); - } - break; - } - - profile = JsonConvert.DeserializeObject<Profile>(outputraw, new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All - }); - } - catch (Exception ex) - { - LogManager.LogError("Could not parse profile {0}. {1}", fileName, ex.Message); - } - - // failed to parse - if (profile is null || profile.Name is null || profile.Path is null) - { - LogManager.LogError("Failed to parse profile {0}", fileName); - return; - } - - UpdateOrCreateProfile(profile, UpdateSource.Serializer); - - // default specific - if (profile.Default) - ApplyProfile(profile, UpdateSource.Serializer); - } - - public static void DeleteProfile(Profile profile) - { - var profilePath = Path.Combine(ProfilesPath, profile.GetFileName()); - - if (profiles.ContainsKey(profile.Path)) - { - // Unregister application from HidHide - HidHide.UnregisterApplication(profile.Path); - - // Remove XInputPlus (extended compatibility) - XInputPlus.UnregisterApplication(profile); - - profiles.Remove(profile.Path); - - // warn owner - var isCurrent = profile.Path.Equals(currentProfile.Path, StringComparison.InvariantCultureIgnoreCase); - - // raise event - Discarded?.Invoke(profile); - - // raise event(s) - Deleted?.Invoke(profile); - - // send toast - // todo: localize me - ToastManager.SendToast($"Profile {profile.Name} deleted"); - - LogManager.LogInformation("Deleted profile {0}", profilePath); - - // restore default profile - if (isCurrent) - ApplyProfile(GetDefault()); - } - - FileUtils.FileDelete(profilePath); - } - - public static void SerializeProfile(Profile profile) - { - // update profile version to current build - profile.Version = new Version(MainWindow.fileVersionInfo.FileVersion); - - var jsonString = JsonConvert.SerializeObject(profile, Formatting.Indented, new JsonSerializerSettings - { - TypeNameHandling = TypeNameHandling.All - }); - - // prepare for writing - var profilePath = Path.Combine(ProfilesPath, profile.GetFileName()); - - try - { - if (FileUtils.IsFileWritable(profilePath)) - File.WriteAllText(profilePath, jsonString); - } - catch { } - } - - private static void SanitizeProfile(Profile profile) - { - profile.ErrorCode = ProfileErrorCode.None; - - if (profile.Default) - { - profile.ErrorCode |= ProfileErrorCode.Default; - } - else - { - var processpath = Path.GetDirectoryName(profile.Path); - - if (!Directory.Exists(processpath)) - profile.ErrorCode |= ProfileErrorCode.MissingPath; - - if (!File.Exists(profile.Path)) - profile.ErrorCode |= ProfileErrorCode.MissingExecutable; - - if (!FileUtils.IsDirectoryWritable(processpath)) - profile.ErrorCode |= ProfileErrorCode.MissingPermission; - - if (ProcessManager.GetProcesses(profile.Executable).Capacity > 0) - profile.ErrorCode |= ProfileErrorCode.Running; - } - - // looks like profile power profile was deleted, restore balanced - if (!PowerProfileManager.Contains(profile.PowerProfile)) - profile.PowerProfile = PowerMode.BetterPerformance; - } - - public static void UpdateOrCreateProfile(Profile profile, UpdateSource source = UpdateSource.Background) - { - bool isCurrent = false; - switch (source) - { - // update current profile on creation - case UpdateSource.Creation: - case UpdateSource.QuickProfilesPage: - isCurrent = true; - break; - default: - // check if this is current profile - isCurrent = currentProfile is null - ? false - : profile.Path.Equals(currentProfile.Path, StringComparison.InvariantCultureIgnoreCase); - break; - } - - // refresh error code - SanitizeProfile(profile); - - // used to get and store a few previous values - XInputPlusMethod prevWrapper = XInputPlusMethod.Disabled; - if (profiles.TryGetValue(profile.Path, out Profile prevProfile)) - { - prevWrapper = prevProfile.XInputPlus; - } - - // update database - profiles[profile.Path] = profile; - - // raise event(s) - Updated?.Invoke(profile, source, isCurrent); - - if (source == UpdateSource.Serializer) - return; - - // do not update wrapper and cloaking from default profile - if (!profile.Default) - { - // update wrapper - if (!UpdateProfileWrapper(profile)) - { - // restore previous XInputPlus mode if failed to update - profile.XInputPlus = prevWrapper; - source = UpdateSource.Background; - } - - // update cloaking - UpdateProfileCloaking(profile); - } - - // apply profile (silently) - if (isCurrent) - ApplyProfile(profile, source); - - // serialize profile - SerializeProfile(profile); - } - - public static bool UpdateProfileCloaking(Profile profile) - { - switch (profile.ErrorCode) - { - case ProfileErrorCode.MissingExecutable: - case ProfileErrorCode.MissingPath: - case ProfileErrorCode.Default: - return false; - } - - switch (profile.Whitelisted) - { - case true: - return HidHide.RegisterApplication(profile.Path); - default: - case false: - return HidHide.UnregisterApplication(profile.Path); - } - } - - public static bool UpdateProfileWrapper(Profile profile) - { - switch (profile.ErrorCode) - { - case ProfileErrorCode.MissingPermission: - case ProfileErrorCode.MissingPath: - case ProfileErrorCode.Running: - case ProfileErrorCode.Default: - return false; - } - - switch (profile.XInputPlus) - { - case XInputPlusMethod.Redirection: - return XInputPlus.RegisterApplication(profile); - default: - case XInputPlusMethod.Disabled: - case XInputPlusMethod.Injection: - return XInputPlus.UnregisterApplication(profile); - } - } - - private static void ControllerManager_ControllerPlugged(IController Controller, bool IsPowerCycling) - { - // we're only interest in virtual, XInput controllers - if (Controller is not XInputController || !Controller.IsVirtual()) - return; - - foreach (var profile in profiles.Values) - UpdateProfileWrapper(profile); - } - - public static Profile? GetProfileWithDefaultLayout() => profiles.Values.FirstOrDefault(p => p.Layout.IsDefaultLayout); - - #region events - - public static event DeletedEventHandler Deleted; - - public delegate void DeletedEventHandler(Profile profile); - - public static event UpdatedEventHandler Updated; - - public delegate void UpdatedEventHandler(Profile profile, UpdateSource source, bool isCurrent); - - public static event AppliedEventHandler Applied; - - public delegate void AppliedEventHandler(Profile profile, UpdateSource source); - - public static event DiscardedEventHandler Discarded; - - public delegate void DiscardedEventHandler(Profile profile); - - public static event InitializedEventHandler Initialized; - - public delegate void InitializedEventHandler(); - - #endregion +using HandheldCompanion.Controllers; +using HandheldCompanion.Controls; +<<<<<<< HEAD +using HandheldCompanion.Misc; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using HandheldCompanion.Utils; +using HandheldCompanion.Views; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using static HandheldCompanion.Utils.XInputPlusUtils; + +namespace HandheldCompanion.Managers; + +public static class ProfileManager +{ + public const string DefaultName = "Default"; + + public static Dictionary<string, Profile> profiles = new(StringComparer.InvariantCultureIgnoreCase); + + private static Profile currentProfile; + + private static string ProfilesPath; + + public static bool IsInitialized; + + static ProfileManager() + { + // initialiaze path(s) + ProfilesPath = Path.Combine(MainWindow.SettingsPath, "profiles"); + if (!Directory.Exists(ProfilesPath)) + Directory.CreateDirectory(ProfilesPath); + + ProcessManager.ForegroundChanged += ProcessManager_ForegroundChanged; + ProcessManager.ProcessStarted += ProcessManager_ProcessStarted; + ProcessManager.ProcessStopped += ProcessManager_ProcessStopped; + + PowerProfileManager.Deleted += PowerProfileManager_Deleted; + + ControllerManager.ControllerPlugged += ControllerManager_ControllerPlugged; + } + + public static FileSystemWatcher profileWatcher { get; set; } + + public static void Start() + { + // monitor profile files + profileWatcher = new FileSystemWatcher + { + Path = ProfilesPath, + EnableRaisingEvents = true, + IncludeSubdirectories = true, + Filter = "*.json", + NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size + }; + profileWatcher.Deleted += ProfileDeleted; + + // process existing profiles + var fileEntries = Directory.GetFiles(ProfilesPath, "*.json", SearchOption.AllDirectories); + foreach (var fileName in fileEntries) + ProcessProfile(fileName); + + // check for default profile + if (!HasDefault()) + { + Layout deviceLayout = MainWindow.CurrentDevice.DefaultLayout.Clone() as Layout; + Profile defaultProfile = new() + { + Name = DefaultName, + Default = true, + Enabled = false, + Layout = deviceLayout, + LayoutTitle = LayoutTemplate.DefaultLayout.Name, + LayoutEnabled = true + }; + + UpdateOrCreateProfile(defaultProfile, UpdateSource.Creation); + } + + // force apply default + ApplyProfile(GetDefault()); + + IsInitialized = true; + Initialized?.Invoke(); + + LogManager.LogInformation("{0} has started", "ProfileManager"); + } + + public static void Stop() + { + if (!IsInitialized) + return; + + IsInitialized = false; + + profileWatcher.Deleted -= ProfileDeleted; + profileWatcher.Dispose(); + + LogManager.LogInformation("{0} has stopped", "ProfileManager"); + } + + public static bool Contains(Profile profile) + { + foreach (var pr in profiles.Values) + if (pr.Path.Equals(profile.Path, StringComparison.InvariantCultureIgnoreCase)) + return true; + + return false; + } + + public static bool Contains(string fileName) + { + foreach (var pr in profiles.Values) + if (pr.Path.Equals(fileName, StringComparison.InvariantCultureIgnoreCase)) + return true; + + return false; + } + + public static Profile GetProfileFromPath(string path, bool ignoreStatus) + { + // get profile from path + Profile profile = profiles.Values.FirstOrDefault(a => a.Path.Equals(path, StringComparison.InvariantCultureIgnoreCase)); + + if (profile is null) + { + // otherwise, get profile from executable + string fileName = Path.GetFileName(path); + profile = profiles.Values.FirstOrDefault(a => a.Executable.Equals(fileName, StringComparison.InvariantCultureIgnoreCase)); + + if (profile is null) + return GetDefault(); + } + + // ignore profile status (enabled/disabled) + if (ignoreStatus) + return profile; + + return profile.Enabled ? profile : GetDefault(); + } + + private static void ApplyProfile(Profile profile, UpdateSource source = UpdateSource.Background, + bool announce = true) + { + // might not be the same anymore if disabled + profile = GetProfileFromPath(profile.Path, false); + + // we've already announced this profile + if (currentProfile is not null) + if (currentProfile.Path.Equals(profile.Path, StringComparison.InvariantCultureIgnoreCase)) + announce = false; + + // update current profile before invoking event + currentProfile = profile; + + // raise event + Applied?.Invoke(profile, source); + + // send toast + // todo: localize me + if (announce) + { + LogManager.LogInformation("Profile {0} applied", profile.Name); + ToastManager.SendToast($"Profile {profile.Name} applied"); + } +<<<<<<< HEAD + } + + private static void PowerProfileManager_Deleted(PowerProfile powerProfile) + { + foreach(Profile profile in profiles.Values) + { + bool isCurrent = profile.PowerProfile == powerProfile.Guid; + if (isCurrent) + { + // sanitize profile + SanitizeProfile(profile); + + // update profile + UpdateOrCreateProfile(profile); + + if (currentProfile.Path.Equals(profile.Path, StringComparison.InvariantCultureIgnoreCase)) + ApplyProfile(profile); + } + } +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private static void ProcessManager_ProcessStopped(ProcessEx processEx) + { + try + { + var profile = GetProfileFromPath(processEx.Path, true); + + // do not discard default profile + if (profile is null || profile.Default) + return; + + // raise event + Discarded?.Invoke(profile); + + if (profile.ErrorCode.HasFlag(ProfileErrorCode.Running)) + { + // update profile + UpdateOrCreateProfile(profile); + + // restore default profile + ApplyProfile(GetDefault()); + } + } + catch + { + } + } + + private static void ProcessManager_ProcessStarted(ProcessEx processEx, bool OnStartup) + { + try + { + var profile = GetProfileFromPath(processEx.Path, true); + + if (profile is null || profile.Default) + return; + + // update profile executable path + profile.Path = processEx.Path; + + // update profile + UpdateOrCreateProfile(profile); + } + catch + { + } + } + + private static void ProcessManager_ForegroundChanged(ProcessEx proc, ProcessEx back) + { + try + { + var profile = GetProfileFromPath(proc.Path, false); + + // update profile executable path + if (!profile.Default) + { + profile.Path = proc.Path; + UpdateOrCreateProfile(profile); + } + + // raise event + if (back is not null) + { + var backProfile = GetProfileFromPath(back.Path, false); + + if (backProfile != profile) + Discarded?.Invoke(backProfile); + } + + ApplyProfile(profile); + } + catch + { + } + } + + private static void ProfileDeleted(object sender, FileSystemEventArgs e) + { + // not ideal + var ProfileName = e.Name.Replace(".json", ""); + var profile = profiles.Values.FirstOrDefault(p => p.Name.Equals(ProfileName, StringComparison.InvariantCultureIgnoreCase)); + + // couldn't find a matching profile + if (profile is null) + return; + + // you can't delete default profile ! + if (profile.Default) + { + SerializeProfile(profile); + return; + } + + DeleteProfile(profile); + } + + private static bool HasDefault() + { + return profiles.Values.Count(a => a.Default) != 0; + } + + public static Profile GetDefault() + { + if (HasDefault()) + return profiles.Values.FirstOrDefault(a => a.Default); + return new Profile(); + } + + public static Profile GetCurrent() + { + if (currentProfile is not null) + return currentProfile; + + return GetDefault(); + } + + private static void ProcessProfile(string fileName) + { + Profile profile = null; + try + { + var outputraw = File.ReadAllText(fileName); + var jObject = JObject.Parse(outputraw); + + // latest pre-versionning release + Version version = new("0.15.0.4"); + if (jObject.TryGetValue("Version", out var value)) + version = new Version(value.ToString()); + + switch (version.ToString()) + { + case "0.15.0.4": + { + outputraw = CommonUtils.RegexReplace(outputraw, "Generic.Dictionary(.*)System.Private.CoreLib\"", + "Generic.SortedDictionary$1System.Collections\""); + jObject = JObject.Parse(outputraw); + jObject.Remove("MotionSensivityArray"); + outputraw = jObject.ToString(); + } + break; + case "0.16.0.5": + { + outputraw = outputraw.Replace( + "\"System.Collections.Generic.SortedDictionary`2[[HandheldCompanion.Inputs.ButtonFlags, HandheldCompanion],[System.Boolean, System.Private.CoreLib]], System.Collections\"", + "\"System.Collections.Concurrent.ConcurrentDictionary`2[[HandheldCompanion.Inputs.ButtonFlags, HandheldCompanion],[System.Boolean, System.Private.CoreLib]], System.Collections.Concurrent\""); + } + break; + } + + profile = JsonConvert.DeserializeObject<Profile>(outputraw, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + } + catch (Exception ex) + { + LogManager.LogError("Could not parse profile {0}. {1}", fileName, ex.Message); + } + + // failed to parse + if (profile is null || profile.Name is null || profile.Path is null) + { + LogManager.LogError("Failed to parse profile {0}", fileName); + return; + } + + UpdateOrCreateProfile(profile, UpdateSource.Serializer); + + // default specific + if (profile.Default) + ApplyProfile(profile, UpdateSource.Serializer); + } + + public static void DeleteProfile(Profile profile) + { + var profilePath = Path.Combine(ProfilesPath, profile.GetFileName()); + + if (profiles.ContainsKey(profile.Path)) + { + // Unregister application from HidHide + HidHide.UnregisterApplication(profile.Path); + + // Remove XInputPlus (extended compatibility) + XInputPlus.UnregisterApplication(profile); + + profiles.Remove(profile.Path); + + // warn owner + var isCurrent = profile.Path.Equals(currentProfile.Path, StringComparison.InvariantCultureIgnoreCase); + + // raise event + Discarded?.Invoke(profile); + + // raise event(s) + Deleted?.Invoke(profile); + + // send toast + // todo: localize me + ToastManager.SendToast($"Profile {profile.Name} deleted"); + + LogManager.LogInformation("Deleted profile {0}", profilePath); + + // restore default profile + if (isCurrent) + ApplyProfile(GetDefault()); + } + + FileUtils.FileDelete(profilePath); + } + + public static void SerializeProfile(Profile profile) + { + // update profile version to current build + profile.Version = new Version(MainWindow.fileVersionInfo.FileVersion); + + var jsonString = JsonConvert.SerializeObject(profile, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All + }); + + // prepare for writing + var profilePath = Path.Combine(ProfilesPath, profile.GetFileName()); + + try + { + if (FileUtils.IsFileWritable(profilePath)) + File.WriteAllText(profilePath, jsonString); + } + catch { } + } + + private static void SanitizeProfile(Profile profile) + { + profile.ErrorCode = ProfileErrorCode.None; + + if (profile.Default) + { + profile.ErrorCode |= ProfileErrorCode.Default; + } + else + { + var processpath = Path.GetDirectoryName(profile.Path); + + if (!Directory.Exists(processpath)) + profile.ErrorCode |= ProfileErrorCode.MissingPath; + + if (!File.Exists(profile.Path)) + profile.ErrorCode |= ProfileErrorCode.MissingExecutable; + + if (!FileUtils.IsDirectoryWritable(processpath)) + profile.ErrorCode |= ProfileErrorCode.MissingPermission; + + if (ProcessManager.GetProcesses(profile.Executable).Capacity > 0) + profile.ErrorCode |= ProfileErrorCode.Running; + } + + // looks like profile power profile was deleted, restore balanced + if (!PowerProfileManager.Contains(profile.PowerProfile)) + profile.PowerProfile = PowerMode.BetterPerformance; + } + + public static void UpdateOrCreateProfile(Profile profile, UpdateSource source = UpdateSource.Background) + { + bool isCurrent = false; + switch (source) + { + // update current profile on creation + case UpdateSource.Creation: + case UpdateSource.QuickProfilesPage: + isCurrent = true; + break; + default: + // check if this is current profile + isCurrent = currentProfile is null + ? false + : profile.Path.Equals(currentProfile.Path, StringComparison.InvariantCultureIgnoreCase); + break; + } + + // refresh error code + SanitizeProfile(profile); + + // used to get and store a few previous values + XInputPlusMethod prevWrapper = XInputPlusMethod.Disabled; + if (profiles.TryGetValue(profile.Path, out Profile prevProfile)) + { + prevWrapper = prevProfile.XInputPlus; + } + + // update database + profiles[profile.Path] = profile; + + // raise event(s) + Updated?.Invoke(profile, source, isCurrent); + + if (source == UpdateSource.Serializer) + return; + + // do not update wrapper and cloaking from default profile + if (!profile.Default) + { + // update wrapper + if (!UpdateProfileWrapper(profile)) + { + // restore previous XInputPlus mode if failed to update + profile.XInputPlus = prevWrapper; +<<<<<<< HEAD + source = UpdateSource.Background; +======= + source = ProfileUpdateSource.Background; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + // update cloaking + UpdateProfileCloaking(profile); + } + + // apply profile (silently) + if (isCurrent) + ApplyProfile(profile, source); + + // serialize profile + SerializeProfile(profile); + } + + public static bool UpdateProfileCloaking(Profile profile) + { + switch (profile.ErrorCode) + { + case ProfileErrorCode.MissingExecutable: + case ProfileErrorCode.MissingPath: + case ProfileErrorCode.Default: + return false; + } + + switch (profile.Whitelisted) + { + case true: + return HidHide.RegisterApplication(profile.Path); + default: + case false: + return HidHide.UnregisterApplication(profile.Path); + } + } + + public static bool UpdateProfileWrapper(Profile profile) + { + switch (profile.ErrorCode) + { + case ProfileErrorCode.MissingPermission: + case ProfileErrorCode.MissingPath: + case ProfileErrorCode.Running: + case ProfileErrorCode.Default: + return false; + } + + switch (profile.XInputPlus) + { + case XInputPlusMethod.Redirection: + return XInputPlus.RegisterApplication(profile); + default: + case XInputPlusMethod.Disabled: + case XInputPlusMethod.Injection: + return XInputPlus.UnregisterApplication(profile); + } + } + +<<<<<<< HEAD + private static void ControllerManager_ControllerPlugged(IController Controller, bool IsPowerCycling) +======= + private static void ControllerManager_ControllerPlugged(IController Controller, bool isHCVirtualController, bool IsPowerCycling) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + // we're only interest in virtual, XInput controllers + if (Controller is not XInputController || !Controller.IsVirtual()) + return; + + foreach (var profile in profiles.Values) + UpdateProfileWrapper(profile); + } + + public static Profile? GetProfileWithDefaultLayout() => profiles.Values.FirstOrDefault(p => p.Layout.IsDefaultLayout); + + #region events + + public static event DeletedEventHandler Deleted; + + public delegate void DeletedEventHandler(Profile profile); + + public static event UpdatedEventHandler Updated; + + public delegate void UpdatedEventHandler(Profile profile, UpdateSource source, bool isCurrent); + + public static event AppliedEventHandler Applied; + + public delegate void AppliedEventHandler(Profile profile, UpdateSource source); + + public static event DiscardedEventHandler Discarded; + + public delegate void DiscardedEventHandler(Profile profile); + + public static event InitializedEventHandler Initialized; + + public delegate void InitializedEventHandler(); + + #endregion } \ No newline at end of file diff --git a/HandheldCompanion/Managers/SettingsManager.cs b/HandheldCompanion/Managers/SettingsManager.cs index 003834c93..9ef1beb3a 100644 --- a/HandheldCompanion/Managers/SettingsManager.cs +++ b/HandheldCompanion/Managers/SettingsManager.cs @@ -5,8 +5,12 @@ using System.Collections.Specialized; using System.Configuration; using System.Linq; +<<<<<<< HEAD using System.Windows.Media; +======= + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d namespace HandheldCompanion.Managers; public static class SettingsManager diff --git a/HandheldCompanion/Managers/SystemManager.cs b/HandheldCompanion/Managers/SystemManager.cs index 4288f3456..db04a86e2 100644 --- a/HandheldCompanion/Managers/SystemManager.cs +++ b/HandheldCompanion/Managers/SystemManager.cs @@ -1,3 +1,4 @@ +<<<<<<< HEAD using HandheldCompanion.Devices; using HandheldCompanion.Managers.Desktop; using HandheldCompanion.Misc; @@ -627,4 +628,673 @@ private static extern int EnumDisplayDevices(string lpDevice, int iDevNum, ref D public delegate void InitializedEventHandler(); #endregion +======= +using HandheldCompanion.Devices; +using HandheldCompanion.Managers.Desktop; +using HandheldCompanion.Misc; +using HandheldCompanion.Views; +using Microsoft.Win32; +using NAudio.CoreAudioApi; +using NAudio.CoreAudioApi.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Management; +using System.Media; +using System.Runtime.InteropServices; +using System.Timers; +using System.Windows.Forms; +using Timer = System.Timers.Timer; + +namespace HandheldCompanion.Managers; + +public static class SystemManager +{ + private static DesktopScreen DesktopScreen; + private static ScreenRotation ScreenOrientation; + + private static readonly MMDeviceEnumerator DevEnum; + private static MMDevice multimediaDevice; + private static readonly MMDeviceNotificationClient notificationClient; + + private static readonly ManagementEventWatcher BrightnessWatcher; + private static readonly ManagementScope Scope; + + private static bool VolumeSupport; + private static readonly bool BrightnessSupport; + private static bool FanControlSupport; + + private const int UpdateInterval = 1000; + private static readonly Timer ADLXTimer; + private static int prevRSRState = -2; + private static int prevRSRSharpness = -1; + + public static bool IsInitialized; + + static SystemManager() + { + // ADLX + ADLXTimer = new Timer(UpdateInterval); + ADLXTimer.AutoReset = true; + ADLXTimer.Elapsed += ADLXTimer_Elapsed; + + // setup the multimedia device and get current volume value + notificationClient = new MMDeviceNotificationClient(); + DevEnum = new MMDeviceEnumerator(); + DevEnum.RegisterEndpointNotificationCallback(notificationClient); + SetDefaultAudioEndPoint(); + + // get current brightness value + Scope = new ManagementScope(@"\\.\root\wmi"); + Scope.Connect(); + + // creating the watcher + BrightnessWatcher = new ManagementEventWatcher(Scope, new EventQuery("Select * From WmiMonitorBrightnessEvent")); + BrightnessWatcher.EventArrived += onWMIEvent; + + SystemEvents.DisplaySettingsChanged += SystemEvents_DisplaySettingsChanged; + + // check if we have control over brightness + BrightnessSupport = GetBrightness() != -1; + + if (MainWindow.CurrentDevice.IsOpen && MainWindow.CurrentDevice.IsSupported) + if (MainWindow.CurrentDevice.Capabilities.HasFlag(DeviceCapabilities.FanControl)) + FanControlSupport = true; + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + HotkeysManager.CommandExecuted += HotkeysManager_CommandExecuted; + } + + private static void ADLXTimer_Elapsed(object? sender, ElapsedEventArgs e) + { + try + { + var RSRState = ADLXBackend.GetRSRState(); + var RSRSharpness = ADLXBackend.GetRSRSharpness(); + + if ((RSRState != prevRSRState) || (RSRSharpness != prevRSRSharpness)) + { + RSRStateChanged?.Invoke(RSRState, RSRSharpness); + + prevRSRState = RSRState; + prevRSRSharpness = RSRSharpness; + } + } + catch { } + } + + private static void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data) + { + VolumeNotification?.Invoke(data.MasterVolume * 100.0f); + } + + private static void SetDefaultAudioEndPoint() + { + try + { + if (multimediaDevice is not null && multimediaDevice.AudioEndpointVolume is not null) + { + VolumeSupport = false; + multimediaDevice.AudioEndpointVolume.OnVolumeNotification -= AudioEndpointVolume_OnVolumeNotification; + } + + multimediaDevice = DevEnum.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia); + + if (multimediaDevice is not null && multimediaDevice.AudioEndpointVolume is not null) + { + VolumeSupport = true; + multimediaDevice.AudioEndpointVolume.OnVolumeNotification += AudioEndpointVolume_OnVolumeNotification; + } + + // do this even when no device found, to set to 0 + VolumeNotification?.Invoke((float)GetVolume()); + } + catch (Exception) + { + LogManager.LogError("No AudioEndpoint available"); + } + } + + private static void SettingsManager_SettingValueChanged(string name, object value) + { + switch (name) + { + case "NativeDisplayOrientation": + { + var nativeOrientation = (ScreenRotation.Rotations)Convert.ToInt32(value); + + if (!IsInitialized) + return; + + var oldOrientation = ScreenOrientation.rotation; + ScreenOrientation = new ScreenRotation(ScreenOrientation.rotationUnnormalized, nativeOrientation); + + if (oldOrientation != ScreenOrientation.rotation) + // Though the real orientation didn't change, raise event because the interpretation of it changed + DisplayOrientationChanged?.Invoke(ScreenOrientation); + } + break; + case "QuietModeEnabled": + { + var enabled = Convert.ToBoolean(value); + var toggled = SettingsManager.GetBoolean("QuietModeToggled"); + + if (!enabled && toggled) + SettingsManager.SetProperty("QuietModeToggled", false); + } + break; + case "QuietModeToggled": + { + var toggled = Convert.ToBoolean(value); + + // do not send command to device on startup if toggle is off + if (!SettingsManager.IsInitialized && !toggled) + return; + + MainWindow.CurrentDevice.SetFanControl(toggled); + + if (!toggled) + return; + + var duty = SettingsManager.GetDouble("QuietModeDuty"); + MainWindow.CurrentDevice.SetFanDuty(duty); + } + break; + case "QuietModeDuty": + { + var enabled = SettingsManager.GetBoolean("QuietModeEnabled"); + var toggled = SettingsManager.GetBoolean("QuietModeToggled"); + + if (!enabled || !toggled) + return; + + var duty = Convert.ToDouble(value); + MainWindow.CurrentDevice.SetFanDuty(duty); + } + break; + } + } + + private static void HotkeysManager_CommandExecuted(string listener) + { + switch (listener) + { + case "increaseBrightness": + { + var stepRoundDn = (int)Math.Floor(GetBrightness() / 5.0d); + var brightness = stepRoundDn * 5 + 5; + SetBrightness(brightness); + } + break; + case "decreaseBrightness": + { + var stepRoundUp = (int)Math.Ceiling(GetBrightness() / 5.0d); + var brightness = stepRoundUp * 5 - 5; + SetBrightness(brightness); + } + break; + case "increaseVolume": + { + var stepRoundDn = (int)Math.Floor(Math.Round(GetVolume() / 5.0d, 2)); + var volume = stepRoundDn * 5 + 5; + SetVolume(volume); + } + break; + case "decreaseVolume": + { + var stepRoundUp = (int)Math.Ceiling(Math.Round(GetVolume() / 5.0d, 2)); + var volume = stepRoundUp * 5 - 5; + SetVolume(volume); + } + break; + } + } + + private static void onWMIEvent(object sender, EventArrivedEventArgs e) + { + var brightness = Convert.ToInt32(e.NewEvent.Properties["Brightness"].Value); + BrightnessNotification?.Invoke(brightness); + } + + private static void SystemEvents_DisplaySettingsChanged(object? sender, EventArgs e) + { + var PrimaryScreen = Screen.PrimaryScreen; + + if (DesktopScreen is null || DesktopScreen.PrimaryScreen.DeviceName != PrimaryScreen.DeviceName) + { + // update current desktop screen + DesktopScreen = new DesktopScreen(PrimaryScreen); + + // pull resolutions details + var resolutions = GetResolutions(DesktopScreen.PrimaryScreen.DeviceName); + + foreach (var mode in resolutions) + { + var res = new ScreenResolution(mode.dmPelsWidth, mode.dmPelsHeight, mode.dmBitsPerPel); + + var frequencies = resolutions + .Where(a => a.dmPelsWidth == mode.dmPelsWidth && a.dmPelsHeight == mode.dmPelsHeight) + .Select(b => b.dmDisplayFrequency).Distinct().ToList(); + foreach (var frequency in frequencies) + res.Frequencies[frequency] = new ScreenFrequency(frequency); + + if (!DesktopScreen.HasResolution(res)) + DesktopScreen.resolutions.Add(res); + } + + // sort resolutions + DesktopScreen.SortResolutions(); + + // raise event + PrimaryScreenChanged?.Invoke(DesktopScreen); + } + + // update current desktop resolution + DesktopScreen.devMode = GetDisplay(DesktopScreen.PrimaryScreen.DeviceName); + var ScreenResolution = + DesktopScreen.GetResolution(DesktopScreen.devMode.dmPelsWidth, DesktopScreen.devMode.dmPelsHeight); + + var oldOrientation = ScreenOrientation.rotation; + + if (!IsInitialized) + { + var nativeScreenRotation = (ScreenRotation.Rotations)SettingsManager.GetInt("NativeDisplayOrientation"); + ScreenOrientation = new ScreenRotation((ScreenRotation.Rotations)DesktopScreen.devMode.dmDisplayOrientation, + nativeScreenRotation); + oldOrientation = ScreenRotation.Rotations.UNSET; + + if (nativeScreenRotation == ScreenRotation.Rotations.UNSET) + SettingsManager.SetProperty("NativeDisplayOrientation", (int)ScreenOrientation.rotationNativeBase, + true); + } + else + { + ScreenOrientation = new ScreenRotation((ScreenRotation.Rotations)DesktopScreen.devMode.dmDisplayOrientation, + ScreenOrientation.rotationNativeBase); + } + + // raise event + DisplaySettingsChanged?.Invoke(ScreenResolution); + + if (oldOrientation != ScreenOrientation.rotation) + // raise event + DisplayOrientationChanged?.Invoke(ScreenOrientation); + } + + public static DesktopScreen GetDesktopScreen() + { + return DesktopScreen; + } + + public static ScreenRotation GetScreenOrientation() + { + return ScreenOrientation; + } + + public static void Start() + { + // start brightness watcher + BrightnessWatcher.Start(); + ADLXTimer.Start(); + + // force trigger events + SystemEvents_DisplaySettingsChanged(null, null); + + IsInitialized = true; + Initialized?.Invoke(); + + LogManager.LogInformation("{0} has started", "SystemManager"); + } + + public static void Stop() + { + if (!IsInitialized) + return; + + // stop brightness watcher + BrightnessWatcher.Stop(); + ADLXTimer.Stop(); + + DevEnum.UnregisterEndpointNotificationCallback(notificationClient); + + IsInitialized = false; + + LogManager.LogInformation("{0} has stopped", "SystemManager"); + } + + public static bool SetResolution(int width, int height, int displayFrequency) + { + if (!IsInitialized) + return false; + + var ret = false; + long RetVal = 0; + var dm = new Display(); + dm.dmSize = (short)Marshal.SizeOf(typeof(Display)); + dm.dmPelsWidth = width; + dm.dmPelsHeight = height; + dm.dmDisplayFrequency = displayFrequency; + dm.dmFields = Display.DM_PELSWIDTH | Display.DM_PELSHEIGHT | Display.DM_DISPLAYFREQUENCY; + RetVal = ChangeDisplaySettings(ref dm, CDS_TEST); + if (RetVal == 0) + { + RetVal = ChangeDisplaySettings(ref dm, 0); + ret = true; + } + + return ret; + } + + public static bool SetResolution(int width, int height, int displayFrequency, int bitsPerPel) + { + if (!IsInitialized) + return false; + + var ret = false; + long RetVal = 0; + var dm = new Display(); + dm.dmSize = (short)Marshal.SizeOf(typeof(Display)); + dm.dmPelsWidth = width; + dm.dmPelsHeight = height; + dm.dmDisplayFrequency = displayFrequency; + dm.dmBitsPerPel = bitsPerPel; + dm.dmFields = Display.DM_PELSWIDTH | Display.DM_PELSHEIGHT | Display.DM_DISPLAYFREQUENCY; + RetVal = ChangeDisplaySettings(ref dm, CDS_TEST); + if (RetVal == 0) + { + RetVal = ChangeDisplaySettings(ref dm, 0); + ret = true; + } + + return ret; + } + + public static Display GetDisplay(string DeviceName) + { + var dm = new Display(); + dm.dmSize = (short)Marshal.SizeOf(typeof(Display)); + bool mybool; + mybool = EnumDisplaySettings(DeviceName, -1, ref dm); + return dm; + } + + public static List<Display> GetResolutions(string DeviceName) + { + var allMode = new List<Display>(); + var dm = new Display(); + dm.dmSize = (short)Marshal.SizeOf(typeof(Display)); + var index = 0; + while (EnumDisplaySettings(DeviceName, index, ref dm)) + { + allMode.Add(dm); + index++; + } + + return allMode; + } + + public static void PlayWindowsMedia(string file) + { + var path = Path.Combine(@"c:\Windows\Media\", file); + if (File.Exists(path)) + new SoundPlayer(path).Play(); + } + + public static bool HasVolumeSupport() + { + return VolumeSupport; + } + + public static void SetVolume(double volume) + { + if (!VolumeSupport) + return; + + multimediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar = (float)(volume / 100.0d); + } + + public static double GetVolume() + { + if (!VolumeSupport) + return 0.0d; + + return multimediaDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100.0d; + } + + public static bool HasBrightnessSupport() + { + return BrightnessSupport; + } + + public static void SetBrightness(double brightness) + { + if (!BrightnessSupport) + return; + + try + { + using (var mclass = new ManagementClass("WmiMonitorBrightnessMethods")) + { + mclass.Scope = new ManagementScope(@"\\.\root\wmi"); + using (var instances = mclass.GetInstances()) + { + foreach (ManagementObject instance in instances) + { + object[] args = { 1, brightness }; + instance.InvokeMethod("WmiSetBrightness", args); + } + } + } + } + catch + { + } + } + + public static short GetBrightness() + { + try + { + using (var mclass = new ManagementClass("WmiMonitorBrightness")) + { + mclass.Scope = new ManagementScope(@"\\.\root\wmi"); + using (var instances = mclass.GetInstances()) + { + foreach (ManagementObject instance in instances) + return (byte)instance.GetPropertyValue("CurrentBrightness"); + } + } + + return 0; + } + catch + { + } + + return -1; + } + + private class MMDeviceNotificationClient : IMMNotificationClient + { + public void OnDefaultDeviceChanged(DataFlow flow, Role role, string defaultDeviceId) + { + SetDefaultAudioEndPoint(); + } + + public void OnDeviceAdded(string deviceId) + { + } + + public void OnDeviceRemoved(string deviceId) + { + } + + public void OnDeviceStateChanged(string deviceId, DeviceState newState) + { + } + + public void OnPropertyValueChanged(string deviceId, PropertyKey key) + { + } + } + + #region imports + + public enum DMDO + { + DEFAULT = 0, + D90 = 1, + D180 = 2, + D270 = 3 + } + + public const int CDS_UPDATEREGISTRY = 0x01; + public const int CDS_TEST = 0x02; + public const int DISP_CHANGE_SUCCESSFUL = 0; + public const int DISP_CHANGE_RESTART = 1; + public const int DISP_CHANGE_FAILED = -1; + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + public struct Display + { + public const int DM_DISPLAYFREQUENCY = 0x400000; + public const int DM_PELSWIDTH = 0x80000; + public const int DM_PELSHEIGHT = 0x100000; + private const int CCHDEVICENAME = 32; + private const int CCHFORMNAME = 32; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)] + public string dmDeviceName; + + public short dmSpecVersion; + public short dmDriverVersion; + public short dmSize; + public short dmDriverExtra; + public int dmFields; + + public int dmPositionX; + public int dmPositionY; + public DMDO dmDisplayOrientation; + public int dmDisplayFixedOutput; + + public short dmColor; + public short dmDuplex; + public short dmYResolution; + public short dmTTOption; + public short dmCollate; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)] + public string dmFormName; + + public short dmLogPixels; + public int dmBitsPerPel; + public int dmPelsWidth; + public int dmPelsHeight; + public int dmDisplayFlags; + public int dmDisplayFrequency; + public int dmICMMethod; + public int dmICMIntent; + public int dmMediaType; + public int dmDitherType; + public int dmReserved1; + public int dmReserved2; + public int dmPanningWidth; + public int dmPanningHeight; + + public override string ToString() + { + return $"{dmPelsWidth}x{dmPelsHeight}, {dmDisplayFrequency}, {dmBitsPerPel}"; + } + } + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern int ChangeDisplaySettings([In] ref Display lpDevMode, int dwFlags); + + [DllImport("user32.dll", CharSet = CharSet.Auto)] + private static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref Display lpDevMode); + + [Flags] + public enum DisplayDeviceStateFlags + { + /// <summary>The device is part of the desktop.</summary> + AttachedToDesktop = 0x1, + MultiDriver = 0x2, + + /// <summary>The device is part of the desktop.</summary> + PrimaryDevice = 0x4, + + /// <summary>Represents a pseudo device used to mirror application drawing for remoting or other purposes.</summary> + MirroringDriver = 0x8, + + /// <summary>The device is VGA compatible.</summary> + VGACompatible = 0x16, + + /// <summary>The device is removable; it cannot be the primary display.</summary> + Removable = 0x20, + + /// <summary>The device has more display modes than its output devices support.</summary> + ModesPruned = 0x8000000, + Remote = 0x4000000, + Disconnect = 0x2000000 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] + public struct DisplayDevice + { + [MarshalAs(UnmanagedType.U4)] public int cb; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string DeviceName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string DeviceString; + + [MarshalAs(UnmanagedType.U4)] public DisplayDeviceStateFlags StateFlags; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string DeviceID; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string DeviceKey; + } + + [DllImport("User32.dll")] + private static extern int EnumDisplayDevices(string lpDevice, int iDevNum, ref DisplayDevice lpDisplayDevice, + int dwFlags); + + #endregion + + #region events + + public static event RSRStateChangedEventHandler RSRStateChanged; + + public delegate void RSRStateChangedEventHandler(int RSRState, int RSRSharpness); + + public static event DisplaySettingsChangedEventHandler DisplaySettingsChanged; + + public delegate void DisplaySettingsChangedEventHandler(ScreenResolution resolution); + + public static event PrimaryScreenChangedEventHandler PrimaryScreenChanged; + + public delegate void PrimaryScreenChangedEventHandler(DesktopScreen screen); + + public static event DisplayOrientationChangedEventHandler DisplayOrientationChanged; + + public delegate void DisplayOrientationChangedEventHandler(ScreenRotation rotation); + + public static event VolumeNotificationEventHandler VolumeNotification; + + public delegate void VolumeNotificationEventHandler(float volume); + + public static event BrightnessNotificationEventHandler BrightnessNotification; + + public delegate void BrightnessNotificationEventHandler(int brightness); + + public static event InitializedEventHandler Initialized; + + public delegate void InitializedEventHandler(); + + #endregion +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } \ No newline at end of file diff --git a/HandheldCompanion/Managers/TaskManager.cs b/HandheldCompanion/Managers/TaskManager.cs index 876781e95..ac08e3df2 100644 --- a/HandheldCompanion/Managers/TaskManager.cs +++ b/HandheldCompanion/Managers/TaskManager.cs @@ -1,87 +1,90 @@ -using Microsoft.Win32.TaskScheduler; -using System; -using System.Security.Principal; - -namespace HandheldCompanion.Managers; - -public class TaskManager : Manager -{ - private readonly string TaskName; - private readonly string TaskExecutable; - - // TaskManager vars - private Task task; - private TaskDefinition taskDefinition; - private TaskService taskService; - - public TaskManager(string TaskName, string Executable) - { - this.TaskName = TaskName; - TaskExecutable = Executable; - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - } - - private void SettingsManager_SettingValueChanged(string name, object value) - { - switch (name) - { - case "RunAtStartup": - UpdateTask(Convert.ToBoolean(value)); - break; - } - } - - public override void Start() - { - taskService = new TaskService(); - task = taskService.FindTask(TaskName); - - try - { - if (task is not null) - taskService.RootFolder.DeleteTask(TaskName); - - taskDefinition = TaskService.Instance.NewTask(); - taskDefinition.Principal.RunLevel = TaskRunLevel.Highest; - taskDefinition.Principal.UserId = WindowsIdentity.GetCurrent().Name; - taskDefinition.Principal.LogonType = TaskLogonType.InteractiveToken; - taskDefinition.Settings.DisallowStartIfOnBatteries = false; - taskDefinition.Settings.StopIfGoingOnBatteries = false; - taskDefinition.Settings.ExecutionTimeLimit = TimeSpan.Zero; - taskDefinition.Settings.Enabled = false; - taskDefinition.Triggers.Add(new LogonTrigger() { UserId = WindowsIdentity.GetCurrent().Name }); - taskDefinition.Actions.Add(new ExecAction(TaskExecutable)); - - task = TaskService.Instance.RootFolder.RegisterTaskDefinition(TaskName, taskDefinition); - task.Enabled = SettingsManager.GetBoolean("RunAtStartup"); - } - catch - { - } - - base.Start(); - } - - public override void Stop() - { - if (!IsInitialized) - return; - - base.Stop(); - } - - public void UpdateTask(bool value) - { - if (task is null) - return; - - try - { - task.Enabled = value; - } - catch - { - } - } +using Microsoft.Win32.TaskScheduler; +using System; +<<<<<<< HEAD +using System.Security.Principal; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + +namespace HandheldCompanion.Managers; + +public class TaskManager : Manager +{ + private readonly string TaskName; + private readonly string TaskExecutable; + + // TaskManager vars + private Task task; + private TaskDefinition taskDefinition; + private TaskService taskService; + + public TaskManager(string TaskName, string Executable) + { + this.TaskName = TaskName; + TaskExecutable = Executable; + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + } + + private void SettingsManager_SettingValueChanged(string name, object value) + { + switch (name) + { + case "RunAtStartup": + UpdateTask(Convert.ToBoolean(value)); + break; + } + } + + public override void Start() + { + taskService = new TaskService(); + task = taskService.FindTask(TaskName); + + try + { + if (task is not null) + taskService.RootFolder.DeleteTask(TaskName); + + taskDefinition = TaskService.Instance.NewTask(); + taskDefinition.Principal.RunLevel = TaskRunLevel.Highest; + taskDefinition.Principal.UserId = WindowsIdentity.GetCurrent().Name; + taskDefinition.Principal.LogonType = TaskLogonType.InteractiveToken; + taskDefinition.Settings.DisallowStartIfOnBatteries = false; + taskDefinition.Settings.StopIfGoingOnBatteries = false; + taskDefinition.Settings.ExecutionTimeLimit = TimeSpan.Zero; + taskDefinition.Settings.Enabled = false; + taskDefinition.Triggers.Add(new LogonTrigger() { UserId = WindowsIdentity.GetCurrent().Name }); + taskDefinition.Actions.Add(new ExecAction(TaskExecutable)); + + task = TaskService.Instance.RootFolder.RegisterTaskDefinition(TaskName, taskDefinition); + task.Enabled = SettingsManager.GetBoolean("RunAtStartup"); + } + catch + { + } + + base.Start(); + } + + public override void Stop() + { + if (!IsInitialized) + return; + + base.Stop(); + } + + public void UpdateTask(bool value) + { + if (task is null) + return; + + try + { + task.Enabled = value; + } + catch + { + } + } } \ No newline at end of file diff --git a/HandheldCompanion/Managers/VirtualManager.cs b/HandheldCompanion/Managers/VirtualManager.cs index e0a49ee45..bdb25c70f 100644 --- a/HandheldCompanion/Managers/VirtualManager.cs +++ b/HandheldCompanion/Managers/VirtualManager.cs @@ -1,295 +1,359 @@ -using HandheldCompanion.Controllers; -using HandheldCompanion.Targets; -using HandheldCompanion.Utils; -using Nefarius.ViGEm.Client; -using System; -using System.Threading; - -namespace HandheldCompanion.Managers -{ - public static class VirtualManager - { - // controllers vars - public static ViGEmClient vClient; - public static ViGEmTarget vTarget; - - private static DSUServer DSUServer; - - // settings vars - public static HIDmode HIDmode = HIDmode.NoController; - public static HIDstatus HIDstatus = HIDstatus.Disconnected; - - public static ushort ProductId = 0x28E; // Xbox 360 - public static ushort VendorId = 0x45E; // Microsoft - - public static ushort FakeVendorId = 0x76B; // HC - - public static bool IsInitialized; - - public static event HIDChangedEventHandler HIDchanged; - public delegate void HIDChangedEventHandler(HIDmode HIDmode); - - - public static event ControllerSelectedEventHandler ControllerSelected; - public delegate void ControllerSelectedEventHandler(HIDmode mode); - - public static event InitializedEventHandler Initialized; - public delegate void InitializedEventHandler(); - - public static event VibrateEventHandler Vibrated; - public delegate void VibrateEventHandler(byte LargeMotor, byte SmallMotor); - - static VirtualManager() - { - // verifying ViGEm is installed - try - { - vClient = new ViGEmClient(); - } - catch (Exception) - { - LogManager.LogCritical("ViGEm is missing. Please get it from: {0}", "https://github.com/ViGEm/ViGEmBus/releases"); - throw new InvalidOperationException(); - } - - // initialize DSUClient - DSUServer = new DSUServer(); - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - SettingsManager.Initialized += SettingsManager_Initialized; - ProfileManager.Applied += ProfileManager_Applied; - } - - public static void Start() - { - // todo: improve me !! - while (!ControllerManager.IsInitialized) - Thread.Sleep(250); - - IsInitialized = true; - Initialized?.Invoke(); - - LogManager.LogInformation("{0} has started", "VirtualManager"); - } - - public static void Stop() - { - if (!IsInitialized) - return; - - ResetViGEm(); - DSUServer.Stop(); - - // unsubscrive events - ProfileManager.Applied -= ProfileManager_Applied; - - IsInitialized = false; - - LogManager.LogInformation("{0} has stopped", "VirtualManager"); - } - - public static void Resume() - { - // create new ViGEm client - if (vClient is null) - vClient = new ViGEmClient(); - - // set controller mode - SetControllerMode(HIDmode); - } - - public static void Suspend() - { - // reset vigem - ResetViGEm(); - } - - private static void SettingsManager_SettingValueChanged(string name, object value) - { - switch (name) - { - case "HIDmode": - SetControllerMode((HIDmode)Convert.ToInt32(value)); - break; - case "HIDstatus": - SetControllerStatus((HIDstatus)Convert.ToInt32(value)); - break; - case "DSUEnabled": - if (SettingsManager.IsInitialized) - SetDSUStatus(Convert.ToBoolean(value)); - break; - case "DSUport": - DSUServer.port = Convert.ToInt32(value); - if (SettingsManager.IsInitialized) - SetDSUStatus(SettingsManager.GetBoolean("DSUEnabled")); - break; - } - } - - private static void ProfileManager_Applied(Profile profile, UpdateSource source) - { - try - { - // SetControllerMode takes care of ignoring identical mode switching - if (HIDmode == profile.HID || profile.HID == HIDmode.NotSelected) - return; - - // todo: monitor ControllerManager and check if automatic controller management is running - - switch (profile.HID) - { - case HIDmode.Xbox360Controller: - case HIDmode.DualShock4Controller: - { - SetControllerMode(profile.HID); - break; - } - - default: // Default or not assigned - { - HIDmode defaultHIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); - SetControllerMode(defaultHIDmode); - break; - } - } - } - catch // TODO requires further testing - { - LogManager.LogError("********************** ProfileManager_Applied in VirtualManager **********************"); - } - } - - - private static void SettingsManager_Initialized() - { - SetDSUStatus(SettingsManager.GetBoolean("DSUEnabled")); - } - - private static void SetDSUStatus(bool started) - { - if (started) - DSUServer.Start(); - else - DSUServer.Stop(); - } - - public static void SetControllerMode(HIDmode mode) - { - // do not disconnect if similar to previous mode - if (HIDmode == mode && vTarget is not null) - return; - - // disconnect current virtual controller - if (vTarget is not null) - { - vTarget.Disconnect(); - vTarget.Dispose(); - vTarget = null; - } - - switch (mode) - { - default: - case HIDmode.NoController: - if (vTarget is not null) - { - vTarget.Disconnect(); - vTarget.Dispose(); - vTarget = null; - } - break; - case HIDmode.DualShock4Controller: - vTarget = new DualShock4Target(); - break; - case HIDmode.Xbox360Controller: - // Generate a new random ProductId to help the controller pick empty slot rather than getting its previous one - VendorId = (ushort)new Random().Next(ushort.MinValue, ushort.MaxValue); - ProductId = (ushort)new Random().Next(ushort.MinValue, ushort.MaxValue); - vTarget = new Xbox360Target(VendorId, ProductId); - break; - } - - ControllerSelected?.Invoke(mode); - - // failed to initialize controller - if (vTarget is null) - { - if (mode != HIDmode.NoController) - LogManager.LogError("Failed to initialise virtual controller with HIDmode: {0}", mode); - return; - } - - vTarget.Connected += OnTargetConnected; - vTarget.Disconnected += OnTargetDisconnected; - vTarget.Vibrated += OnTargetVibrated; - - // update status - SetControllerStatus(HIDstatus); - - // update current HIDmode - HIDmode = mode; - } - - public static void SetControllerStatus(HIDstatus status) - { - if (vTarget is null) - return; - - switch (status) - { - default: - case HIDstatus.Connected: - vTarget.Connect(); - break; - case HIDstatus.Disconnected: - vTarget.Disconnect(); - break; - } - - // update current HIDstatus - HIDstatus = status; - } - - private static void OnTargetConnected(ViGEmTarget target) - { - ToastManager.SendToast($"{target}", "is now connected", $"HIDmode{(uint)target.HID}"); - } - - private static void OnTargetDisconnected(ViGEmTarget target) - { - ToastManager.SendToast($"{target}", "is now disconnected", $"HIDmode{(uint)target.HID}"); - } - - private static void OnTargetVibrated(byte LargeMotor, byte SmallMotor) - { - Vibrated?.Invoke(LargeMotor, SmallMotor); - } - - public static void UpdateInputs(ControllerState controllerState) - { - // DS4Touch is used by both targets below, update first - DS4Touch.UpdateInputs(controllerState); - - vTarget?.UpdateInputs(controllerState); - DSUServer?.UpdateInputs(controllerState); - } - - private static void ResetViGEm() - { - // dispose virtual controller - if (vTarget is not null) - { - vTarget.Disconnect(); - vTarget.Dispose(); - vTarget = null; - } - - // dispose ViGEm drivers - if (vClient is not null) - { - vClient.Dispose(); - vClient = null; - } - } - } +using HandheldCompanion.Controllers; +using HandheldCompanion.Targets; +using HandheldCompanion.Utils; +using Nefarius.ViGEm.Client; +using System; +<<<<<<< HEAD +using System.Threading; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + +namespace HandheldCompanion.Managers +{ + public static class VirtualManager + { + // controllers vars + public static ViGEmClient vClient; + public static ViGEmTarget vTarget; + + private static DSUServer DSUServer; + + // settings vars +<<<<<<< HEAD + public static HIDmode HIDmode = HIDmode.NoController; + public static HIDstatus HIDstatus = HIDstatus.Disconnected; + + public static ushort ProductId = 0x28E; // Xbox 360 + public static ushort VendorId = 0x45E; // Microsoft + + public static ushort FakeVendorId = 0x76B; // HC + + public static bool IsInitialized; + + public static event HIDChangedEventHandler HIDchanged; + public delegate void HIDChangedEventHandler(HIDmode HIDmode); + + + public static event ControllerSelectedEventHandler ControllerSelected; + public delegate void ControllerSelectedEventHandler(HIDmode mode); +======= + private static HIDmode HIDmode = HIDmode.NoController; + private static HIDstatus HIDstatus = HIDstatus.Disconnected; + + public static bool IsInitialized; + + public static event ControllerSelectedEventHandler ControllerSelected; + public delegate void ControllerSelectedEventHandler(IController Controller); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + public static event InitializedEventHandler Initialized; + public delegate void InitializedEventHandler(); + + public static event VibrateEventHandler Vibrated; + public delegate void VibrateEventHandler(byte LargeMotor, byte SmallMotor); + + static VirtualManager() + { + // verifying ViGEm is installed + try + { + vClient = new ViGEmClient(); + } + catch (Exception) + { + LogManager.LogCritical("ViGEm is missing. Please get it from: {0}", "https://github.com/ViGEm/ViGEmBus/releases"); + throw new InvalidOperationException(); + } + + // initialize DSUClient + DSUServer = new DSUServer(); + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + SettingsManager.Initialized += SettingsManager_Initialized; +<<<<<<< HEAD + ProfileManager.Applied += ProfileManager_Applied; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public static void Start() + { +<<<<<<< HEAD + // todo: improve me !! + while (!ControllerManager.IsInitialized) + Thread.Sleep(250); + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + IsInitialized = true; + Initialized?.Invoke(); + + LogManager.LogInformation("{0} has started", "VirtualManager"); + } + + public static void Stop() + { + if (!IsInitialized) + return; + + ResetViGEm(); + DSUServer.Stop(); + +<<<<<<< HEAD + // unsubscrive events + ProfileManager.Applied -= ProfileManager_Applied; + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + IsInitialized = false; + + LogManager.LogInformation("{0} has stopped", "VirtualManager"); + } + + public static void Resume() + { +<<<<<<< HEAD + // create new ViGEm client + if (vClient is null) + vClient = new ViGEmClient(); +======= + // reset vigem + ResetViGEm(); + + // create new ViGEm client + vClient = new ViGEmClient(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // set controller mode + SetControllerMode(HIDmode); + } + +<<<<<<< HEAD + public static void Suspend() + { + // reset vigem +======= + public static void Pause() + { +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ResetViGEm(); + } + + private static void SettingsManager_SettingValueChanged(string name, object value) + { + switch (name) + { + case "HIDmode": + SetControllerMode((HIDmode)Convert.ToInt32(value)); + break; + case "HIDstatus": + SetControllerStatus((HIDstatus)Convert.ToInt32(value)); + break; + case "DSUEnabled": + if (SettingsManager.IsInitialized) + SetDSUStatus(Convert.ToBoolean(value)); + break; + case "DSUport": + DSUServer.port = Convert.ToInt32(value); + if (SettingsManager.IsInitialized) + SetDSUStatus(SettingsManager.GetBoolean("DSUEnabled")); + break; + } + } + +<<<<<<< HEAD + private static void ProfileManager_Applied(Profile profile, UpdateSource source) + { + try + { + // SetControllerMode takes care of ignoring identical mode switching + if (HIDmode == profile.HID || profile.HID == HIDmode.NotSelected) + return; + + // todo: monitor ControllerManager and check if automatic controller management is running + + switch (profile.HID) + { + case HIDmode.Xbox360Controller: + case HIDmode.DualShock4Controller: + { + SetControllerMode(profile.HID); + break; + } + + default: // Default or not assigned + { + HIDmode defaultHIDmode = (HIDmode)SettingsManager.GetInt("HIDmode", true); + SetControllerMode(defaultHIDmode); + break; + } + } + } + catch // TODO requires further testing + { + LogManager.LogError("********************** ProfileManager_Applied in VirtualManager **********************"); + } + } + + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + private static void SettingsManager_Initialized() + { + SetDSUStatus(SettingsManager.GetBoolean("DSUEnabled")); + } + + private static void SetDSUStatus(bool started) + { + if (started) + DSUServer.Start(); + else + DSUServer.Stop(); + } + +<<<<<<< HEAD + public static void SetControllerMode(HIDmode mode) +======= + private static void SetControllerMode(HIDmode mode) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + // do not disconnect if similar to previous mode + if (HIDmode == mode && vTarget is not null) + return; + + // disconnect current virtual controller + if (vTarget is not null) +<<<<<<< HEAD + { + vTarget.Disconnect(); + vTarget.Dispose(); + vTarget = null; + } +======= + vTarget.Disconnect(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + switch (mode) + { + default: + case HIDmode.NoController: + if (vTarget is not null) + { +<<<<<<< HEAD + vTarget.Disconnect(); +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + vTarget.Dispose(); + vTarget = null; + } + break; + case HIDmode.DualShock4Controller: + vTarget = new DualShock4Target(); + break; + case HIDmode.Xbox360Controller: +<<<<<<< HEAD + // Generate a new random ProductId to help the controller pick empty slot rather than getting its previous one + VendorId = (ushort)new Random().Next(ushort.MinValue, ushort.MaxValue); + ProductId = (ushort)new Random().Next(ushort.MinValue, ushort.MaxValue); + vTarget = new Xbox360Target(VendorId, ProductId); + break; + } + + ControllerSelected?.Invoke(mode); +======= + vTarget = new Xbox360Target(); + break; + } + + ControllerSelected?.Invoke(ControllerManager.GetEmulatedController()); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // failed to initialize controller + if (vTarget is null) + { + if (mode != HIDmode.NoController) + LogManager.LogError("Failed to initialise virtual controller with HIDmode: {0}", mode); + return; + } + + vTarget.Connected += OnTargetConnected; + vTarget.Disconnected += OnTargetDisconnected; + vTarget.Vibrated += OnTargetVibrated; + + // update status + SetControllerStatus(HIDstatus); + + // update current HIDmode + HIDmode = mode; + } + +<<<<<<< HEAD + public static void SetControllerStatus(HIDstatus status) +======= + private static void SetControllerStatus(HIDstatus status) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + if (vTarget is null) + return; + + switch (status) + { + default: + case HIDstatus.Connected: + vTarget.Connect(); + break; + case HIDstatus.Disconnected: + vTarget.Disconnect(); + break; + } + + // update current HIDstatus + HIDstatus = status; + } + + private static void OnTargetConnected(ViGEmTarget target) + { + ToastManager.SendToast($"{target}", "is now connected", $"HIDmode{(uint)target.HID}"); + } + + private static void OnTargetDisconnected(ViGEmTarget target) + { + ToastManager.SendToast($"{target}", "is now disconnected", $"HIDmode{(uint)target.HID}"); + } + + private static void OnTargetVibrated(byte LargeMotor, byte SmallMotor) + { + Vibrated?.Invoke(LargeMotor, SmallMotor); + } + + public static void UpdateInputs(ControllerState controllerState) + { + // DS4Touch is used by both targets below, update first + DS4Touch.UpdateInputs(controllerState); + + vTarget?.UpdateInputs(controllerState); + DSUServer?.UpdateInputs(controllerState); + } + + private static void ResetViGEm() + { + // dispose virtual controller + if (vTarget is not null) + { +<<<<<<< HEAD + vTarget.Disconnect(); +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + vTarget.Dispose(); + vTarget = null; + } + + // dispose ViGEm drivers + if (vClient is not null) + { + vClient.Dispose(); + vClient = null; + } + } + } } \ No newline at end of file diff --git a/HandheldCompanion/Misc/Layout.cs b/HandheldCompanion/Misc/Layout.cs index c9cd3b86f..50b185569 100644 --- a/HandheldCompanion/Misc/Layout.cs +++ b/HandheldCompanion/Misc/Layout.cs @@ -1,129 +1,133 @@ -using HandheldCompanion.Actions; -using HandheldCompanion.Controllers; -using HandheldCompanion.Inputs; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace HandheldCompanion; - -[Serializable] -public partial class Layout : ICloneable, IDisposable -{ - public SortedDictionary<ButtonFlags, List<IActions>> ButtonLayout { get; set; } = new(); - public SortedDictionary<AxisLayoutFlags, IActions> AxisLayout { get; set; } = new(); - public SortedDictionary<AxisLayoutFlags, IActions> GyroLayout { get; set; } = new(); - - public bool IsDefaultLayout { get; set; } - - // gyro related - - public Layout() - { - } - - public Layout(bool fill) : this() - { - // generic button mapping - foreach (ButtonFlags button in Enum.GetValues(typeof(ButtonFlags))) - { - if (!IController.TargetButtons.Contains(button)) - continue; - - ButtonLayout[button] = new List<IActions>() { new ButtonActions() { Button = button } }; - } - - // ButtonLayout[ButtonFlags.OEM1] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.Special } }; - ButtonLayout[ButtonFlags.LeftPadClickUp] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.DPadUp } }; - ButtonLayout[ButtonFlags.LeftPadClickDown] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.DPadDown } }; - ButtonLayout[ButtonFlags.LeftPadClickLeft] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.DPadLeft } }; - ButtonLayout[ButtonFlags.LeftPadClickRight] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.DPadRight } }; - - // generic axis mapping - foreach (AxisLayoutFlags axis in Enum.GetValues(typeof(AxisLayoutFlags))) - { - if (!IController.TargetAxis.Contains(axis)) - continue; - - switch (axis) - { - case AxisLayoutFlags.L2: - case AxisLayoutFlags.R2: - AxisLayout[axis] = new TriggerActions { Axis = axis }; - break; - default: - AxisLayout[axis] = new AxisActions { Axis = axis }; - break; - } - } - } - - public object Clone() - { - var jsonString = JsonConvert.SerializeObject(this, Formatting.Indented, - new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); - var deserialized = JsonConvert.DeserializeObject<Layout>(jsonString, - new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); - - deserialized.IsDefaultLayout = false; // Clone shouldn't be default layout in case it is true - - return deserialized; - } - - public void Dispose() - { - ButtonLayout.Clear(); - AxisLayout.Clear(); - GyroLayout.Clear(); - } - - public void UpdateLayout() - { - Updated?.Invoke(this); - } - - public void UpdateLayout(ButtonFlags button, List<IActions> actions) - { - ButtonLayout[button] = actions; - Updated?.Invoke(this); - } - - public void UpdateLayout(AxisLayoutFlags axis, IActions action) - { - switch(axis) - { - default: - AxisLayout[axis] = action; - break; - case AxisLayoutFlags.Gyroscope: - GyroLayout[axis] = action; - break; - } - Updated?.Invoke(this); - } - - public void RemoveLayout(ButtonFlags button) - { - ButtonLayout.Remove(button); - Updated?.Invoke(this); - } - - public void RemoveLayout(AxisLayoutFlags axis) - { - switch (axis) - { - default: - AxisLayout.Remove(axis); - break; - case AxisLayoutFlags.Gyroscope: - GyroLayout.Remove(axis); - break; - } - Updated?.Invoke(this); - } - - #region events - public event UpdatedEventHandler Updated; - public delegate void UpdatedEventHandler(Layout layout); - #endregion +using HandheldCompanion.Actions; +using HandheldCompanion.Controllers; +using HandheldCompanion.Inputs; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace HandheldCompanion; + +[Serializable] +public partial class Layout : ICloneable, IDisposable +{ + public SortedDictionary<ButtonFlags, List<IActions>> ButtonLayout { get; set; } = new(); + public SortedDictionary<AxisLayoutFlags, IActions> AxisLayout { get; set; } = new(); + public SortedDictionary<AxisLayoutFlags, IActions> GyroLayout { get; set; } = new(); + + public bool IsDefaultLayout { get; set; } + + // gyro related + + public Layout() + { + } + + public Layout(bool fill) : this() + { + // generic button mapping + foreach (ButtonFlags button in Enum.GetValues(typeof(ButtonFlags))) + { + if (!IController.TargetButtons.Contains(button)) + continue; + + ButtonLayout[button] = new List<IActions>() { new ButtonActions() { Button = button } }; + } + +<<<<<<< HEAD + // ButtonLayout[ButtonFlags.OEM1] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.Special } }; +======= + ButtonLayout[ButtonFlags.OEM1] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.Special } }; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + ButtonLayout[ButtonFlags.LeftPadClickUp] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.DPadUp } }; + ButtonLayout[ButtonFlags.LeftPadClickDown] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.DPadDown } }; + ButtonLayout[ButtonFlags.LeftPadClickLeft] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.DPadLeft } }; + ButtonLayout[ButtonFlags.LeftPadClickRight] = new List<IActions>() { new ButtonActions { Button = ButtonFlags.DPadRight } }; + + // generic axis mapping + foreach (AxisLayoutFlags axis in Enum.GetValues(typeof(AxisLayoutFlags))) + { + if (!IController.TargetAxis.Contains(axis)) + continue; + + switch (axis) + { + case AxisLayoutFlags.L2: + case AxisLayoutFlags.R2: + AxisLayout[axis] = new TriggerActions { Axis = axis }; + break; + default: + AxisLayout[axis] = new AxisActions { Axis = axis }; + break; + } + } + } + + public object Clone() + { + var jsonString = JsonConvert.SerializeObject(this, Formatting.Indented, + new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); + var deserialized = JsonConvert.DeserializeObject<Layout>(jsonString, + new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); + + deserialized.IsDefaultLayout = false; // Clone shouldn't be default layout in case it is true + + return deserialized; + } + + public void Dispose() + { + ButtonLayout.Clear(); + AxisLayout.Clear(); + GyroLayout.Clear(); + } + + public void UpdateLayout() + { + Updated?.Invoke(this); + } + + public void UpdateLayout(ButtonFlags button, List<IActions> actions) + { + ButtonLayout[button] = actions; + Updated?.Invoke(this); + } + + public void UpdateLayout(AxisLayoutFlags axis, IActions action) + { + switch(axis) + { + default: + AxisLayout[axis] = action; + break; + case AxisLayoutFlags.Gyroscope: + GyroLayout[axis] = action; + break; + } + Updated?.Invoke(this); + } + + public void RemoveLayout(ButtonFlags button) + { + ButtonLayout.Remove(button); + Updated?.Invoke(this); + } + + public void RemoveLayout(AxisLayoutFlags axis) + { + switch (axis) + { + default: + AxisLayout.Remove(axis); + break; + case AxisLayoutFlags.Gyroscope: + GyroLayout.Remove(axis); + break; + } + Updated?.Invoke(this); + } + + #region events + public event UpdatedEventHandler Updated; + public delegate void UpdatedEventHandler(Layout layout); + #endregion } \ No newline at end of file diff --git a/HandheldCompanion/Misc/MotherboardInfo.cs b/HandheldCompanion/Misc/MotherboardInfo.cs index 6d4539275..066a48189 100644 --- a/HandheldCompanion/Misc/MotherboardInfo.cs +++ b/HandheldCompanion/Misc/MotherboardInfo.cs @@ -1,465 +1,499 @@ -using HandheldCompanion.Views; -using System.Collections.Generic; -using System.Management; - -namespace HandheldCompanion; - -public static class MotherboardInfo -{ - private static readonly ManagementObjectSearcher baseboardSearcher = new("root\\CIMV2", "SELECT * FROM Win32_BaseBoard"); - private static ManagementObjectCollection baseboardCollection; - - private static readonly ManagementObjectSearcher motherboardSearcher = new("root\\CIMV2", "SELECT * FROM Win32_MotherboardDevice"); - private static ManagementObjectCollection motherboardCollection; - - private static readonly ManagementObjectSearcher processorSearcher = new("root\\CIMV2", "SELECT * FROM Win32_Processor"); - private static ManagementObjectCollection processorCollection; - - private static readonly ManagementObjectSearcher displaySearcher = new("root\\CIMV2", "SELECT * FROM Win32_DisplayConfiguration"); - private static ManagementObjectCollection displayCollection; - - public static void UpdateMotherboard() - { - // slow task, don't call me more than once - baseboardCollection = baseboardSearcher.Get(); - motherboardCollection = motherboardSearcher.Get(); - processorCollection = processorSearcher.Get(); - displayCollection = displaySearcher.Get(); - } - - public static string Availability - { - get - { - foreach (ManagementObject queryObj in motherboardCollection) - { - var query = queryObj["Availability"]; - if (query is not null) - if (int.TryParse(query.ToString(), out var value)) - return GetAvailability(value); - } - - return string.Empty; - } - } - - public static List<string> DisplayDescription - { - get - { - List<string> strings = new List<string>(); - foreach (ManagementObject queryObj in displayCollection) - { - var query = queryObj["Description"]; - if (query is not null) - strings.Add(query.ToString().ToUpper()); - } - - return strings; - } - } - - public static bool HostingBoard - { - get - { - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["HostingBoard"]; - if (query is not null) - if (query.ToString() == "True") - return true; - } - - return false; - } - } - - public static string InstallDate - { - get - { - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["InstallDate"]; - if (query is not null) - return ConvertToDateTime(query.ToString()); - } - - return string.Empty; - } - } - - private static string _Manufacturer; - public static string Manufacturer - { - get - { - if (!string.IsNullOrEmpty(_Manufacturer)) - return _Manufacturer; - - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["Manufacturer"]; - if (query is not null) - { - _Manufacturer = query.ToString(); - break; - } - } - - return _Manufacturer; - } - } - - public static string Model - { - get - { - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["Model"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - private static int _NumberOfCores = 0; - public static int NumberOfCores - { - get - { - if (_NumberOfCores != 0) - return _NumberOfCores; - - foreach (ManagementObject queryObj in processorCollection) - { - var query = queryObj["NumberOfCores"]; - if (query is not null) - { - if (int.TryParse(query.ToString(), out var value)) - _NumberOfCores = value; - break; - } - } - - return _NumberOfCores; - } - } - - public static string PartNumber - { - get - { - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["PartNumber"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - public static string PNPDeviceID - { - get - { - foreach (ManagementObject queryObj in motherboardCollection) - { - var query = queryObj["PNPDeviceID"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - public static string PrimaryBusType - { - get - { - foreach (ManagementObject queryObj in motherboardCollection) - { - var query = queryObj["PrimaryBusType"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - public static string ProcessorID - { - get - { - foreach (ManagementObject queryObj in processorCollection) - { - var query = queryObj["processorID"]; - - if (query is not null) - return query.ToString().TrimEnd(); - } - - return string.Empty; - } - } - - public static string ProcessorName - { - get - { - foreach (ManagementObject queryObj in processorCollection) - { - var query = queryObj["Name"]; - - if (query is not null) - return query.ToString().TrimEnd(); - } - - return string.Empty; - } - } - - public static string ProcessorManufacturer - { - get - { - foreach (ManagementObject queryObj in processorCollection) - { - var query = queryObj["Manufacturer"]; - - if (query is not null) - return query.ToString().TrimEnd(); - } - - return string.Empty; - } - } - - private static uint _ProcessorMaxClockSpeed = 0; - public static uint ProcessorMaxClockSpeed - { - get - { - if (_ProcessorMaxClockSpeed != 0) - return _ProcessorMaxClockSpeed; - - foreach (ManagementObject queryObj in processorCollection) - { - var query = queryObj["MaxClockSpeed"]; - if (query is not null) - { - if (uint.TryParse(query.ToString(), out var value)) - _ProcessorMaxClockSpeed = value; - break; - } - } - - return _ProcessorMaxClockSpeed; - } - } - - private static uint _ProcessorMaxTurboSpeed = 0; - public static uint ProcessorMaxTurboSpeed - { - get - { - if (_ProcessorMaxTurboSpeed != 0) - return _ProcessorMaxTurboSpeed; - - _ProcessorMaxTurboSpeed = MainWindow.CurrentDevice.CpuClock; - - return _ProcessorMaxTurboSpeed; - } - } - - public static string Product - { - get - { - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["Product"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - public static bool Removable - { - get - { - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["Removable"]; - if (query is not null) - if (query.ToString() == "True") - return true; - } - - return false; - } - } - - public static bool Replaceable - { - get - { - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["Replaceable"]; - if (query is not null) - if (query.ToString() == "True") - return true; - } - - return false; - } - } - - public static string RevisionNumber - { - get - { - foreach (ManagementObject queryObj in motherboardCollection) - { - var query = queryObj["RevisionNumber"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - public static string SecondaryBusType - { - get - { - foreach (ManagementObject queryObj in motherboardCollection) - { - var query = queryObj["SecondaryBusType"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - public static string SerialNumber - { - get - { - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["SerialNumber"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - public static string Status - { - get - { - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["Status"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - public static string SystemName - { - get - { - foreach (ManagementObject queryObj in motherboardCollection) - { - var query = queryObj["SystemName"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - public static string Version - { - get - { - foreach (ManagementObject queryObj in baseboardCollection) - { - var query = queryObj["Version"]; - if (query is not null) - return query.ToString(); - } - - return string.Empty; - } - } - - private static string GetAvailability(int availability) - { - switch (availability) - { - case 1: return "Other"; - case 2: return "Unknown"; - case 3: return "Running or Full Power"; - case 4: return "Warning"; - case 5: return "In Test"; - case 6: return "Not Applicable"; - case 7: return "Power Off"; - case 8: return "Off Line"; - case 9: return "Off Duty"; - case 10: return "Degraded"; - case 11: return "Not Installed"; - case 12: return "Install Error"; - case 13: return "Power Save - Unknown"; - case 14: return "Power Save - Low Power Mode"; - case 15: return "Power Save - Standby"; - case 16: return "Power Cycle"; - case 17: return "Power Save - Warning"; - default: return "Unknown"; - } - } - - private static string ConvertToDateTime(string unconvertedTime) - { - var convertedTime = ""; - var year = int.Parse(unconvertedTime.Substring(0, 4)); - var month = int.Parse(unconvertedTime.Substring(4, 2)); - var date = int.Parse(unconvertedTime.Substring(6, 2)); - var hours = int.Parse(unconvertedTime.Substring(8, 2)); - var minutes = int.Parse(unconvertedTime.Substring(10, 2)); - var seconds = int.Parse(unconvertedTime.Substring(12, 2)); - var meridian = "AM"; - if (hours > 12) - { - hours -= 12; - meridian = "PM"; - } - - convertedTime = date + "/" + month + "/" + year + " " + - hours + ":" + minutes + ":" + seconds + " " + meridian; - return convertedTime; - } +<<<<<<< HEAD +using HandheldCompanion.Views; +using System.Collections.Generic; +======= +using System.Collections.Generic; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using System.Management; + +namespace HandheldCompanion; + +public static class MotherboardInfo +{ + private static readonly ManagementObjectSearcher baseboardSearcher = new("root\\CIMV2", "SELECT * FROM Win32_BaseBoard"); + private static ManagementObjectCollection baseboardCollection; + + private static readonly ManagementObjectSearcher motherboardSearcher = new("root\\CIMV2", "SELECT * FROM Win32_MotherboardDevice"); + private static ManagementObjectCollection motherboardCollection; + + private static readonly ManagementObjectSearcher processorSearcher = new("root\\CIMV2", "SELECT * FROM Win32_Processor"); + private static ManagementObjectCollection processorCollection; + + private static readonly ManagementObjectSearcher displaySearcher = new("root\\CIMV2", "SELECT * FROM Win32_DisplayConfiguration"); + private static ManagementObjectCollection displayCollection; + + public static void UpdateMotherboard() + { + // slow task, don't call me more than once + baseboardCollection = baseboardSearcher.Get(); + motherboardCollection = motherboardSearcher.Get(); + processorCollection = processorSearcher.Get(); + displayCollection = displaySearcher.Get(); + } + + private static readonly ManagementObjectSearcher processerSearcher = + new("root\\CIMV2", "SELECT * FROM Win32_Processor"); + + private static readonly ManagementObjectSearcher displaySearcher = + new("root\\CIMV2", "SELECT * FROM Win32_DisplayConfiguration"); + + public static string Availability + { + get + { + foreach (ManagementObject queryObj in motherboardCollection) + { + var query = queryObj["Availability"]; + if (query is not null) + if (int.TryParse(query.ToString(), out var value)) + return GetAvailability(value); + } + + return string.Empty; + } + } + + public static List<string> DisplayDescription + { + get + { + List<string> strings = new List<string>(); +<<<<<<< HEAD + foreach (ManagementObject queryObj in displayCollection) +======= + foreach (ManagementObject queryObj in displaySearcher.Get()) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + var query = queryObj["Description"]; + if (query is not null) + strings.Add(query.ToString().ToUpper()); + } + + return strings; + } + } + + public static bool HostingBoard + { + get + { + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["HostingBoard"]; + if (query is not null) + if (query.ToString() == "True") + return true; + } + + return false; + } + } + + public static string InstallDate + { + get + { + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["InstallDate"]; + if (query is not null) + return ConvertToDateTime(query.ToString()); + } + + return string.Empty; + } + } + + private static string _Manufacturer; + public static string Manufacturer + { + get + { + if (!string.IsNullOrEmpty(_Manufacturer)) + return _Manufacturer; + + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["Manufacturer"]; + if (query is not null) + { + _Manufacturer = query.ToString(); + break; + } + } + + return _Manufacturer; + } + } + + public static string Model + { + get + { + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["Model"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + + private static int _NumberOfCores = 0; + public static int NumberOfCores + { + get + { + if (_NumberOfCores != 0) + return _NumberOfCores; + +<<<<<<< HEAD + foreach (ManagementObject queryObj in processorCollection) + { + var query = queryObj["NumberOfCores"]; + if (query is not null) + { + if (int.TryParse(query.ToString(), out var value)) + _NumberOfCores = value; + break; + } +======= + foreach (ManagementObject queryObj in processerSearcher.Get()) + { + var query = queryObj["NumberOfCores"]; + if (query is not null) + if (int.TryParse(query.ToString(), out var value)) + _NumberOfCores = value; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + return _NumberOfCores; + } + } + + public static string PartNumber + { + get + { + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["PartNumber"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + + public static string PNPDeviceID + { + get + { + foreach (ManagementObject queryObj in motherboardCollection) + { + var query = queryObj["PNPDeviceID"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + + public static string PrimaryBusType + { + get + { + foreach (ManagementObject queryObj in motherboardCollection) + { + var query = queryObj["PrimaryBusType"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + +<<<<<<< HEAD + public static string ProcessorID + { + get + { + foreach (ManagementObject queryObj in processorCollection) + { + var query = queryObj["processorID"]; + + if (query is not null) + return query.ToString().TrimEnd(); + } + + return string.Empty; + } + } + + public static string ProcessorName + { + get + { + foreach (ManagementObject queryObj in processorCollection) +======= + public static string Processor + { + get + { + foreach (ManagementObject queryObj in processerSearcher.Get()) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + var query = queryObj["Name"]; + + if (query is not null) + return query.ToString().TrimEnd(); + } + + return string.Empty; + } + } + +<<<<<<< HEAD + public static string ProcessorManufacturer + { + get + { + foreach (ManagementObject queryObj in processorCollection) + { + var query = queryObj["Manufacturer"]; + + if (query is not null) + return query.ToString().TrimEnd(); + } + + return string.Empty; + } + } + + private static uint _ProcessorMaxClockSpeed = 0; + public static uint ProcessorMaxClockSpeed + { + get + { + if (_ProcessorMaxClockSpeed != 0) + return _ProcessorMaxClockSpeed; + + foreach (ManagementObject queryObj in processorCollection) + { + var query = queryObj["MaxClockSpeed"]; + if (query is not null) + { + if (uint.TryParse(query.ToString(), out var value)) + _ProcessorMaxClockSpeed = value; + break; + } + } + + return _ProcessorMaxClockSpeed; + } + } + + private static uint _ProcessorMaxTurboSpeed = 0; + public static uint ProcessorMaxTurboSpeed + { + get + { + if (_ProcessorMaxTurboSpeed != 0) + return _ProcessorMaxTurboSpeed; + + _ProcessorMaxTurboSpeed = MainWindow.CurrentDevice.CpuClock; + + return _ProcessorMaxTurboSpeed; + } + } + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public static string Product + { + get + { + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["Product"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + + public static bool Removable + { + get + { + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["Removable"]; + if (query is not null) + if (query.ToString() == "True") + return true; + } + + return false; + } + } + + public static bool Replaceable + { + get + { + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["Replaceable"]; + if (query is not null) + if (query.ToString() == "True") + return true; + } + + return false; + } + } + + public static string RevisionNumber + { + get + { + foreach (ManagementObject queryObj in motherboardCollection) + { + var query = queryObj["RevisionNumber"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + + public static string SecondaryBusType + { + get + { + foreach (ManagementObject queryObj in motherboardCollection) + { + var query = queryObj["SecondaryBusType"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + + public static string SerialNumber + { + get + { + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["SerialNumber"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + + public static string Status + { + get + { + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["Status"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + + public static string SystemName + { + get + { + foreach (ManagementObject queryObj in motherboardCollection) + { + var query = queryObj["SystemName"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + + public static string Version + { + get + { + foreach (ManagementObject queryObj in baseboardCollection) + { + var query = queryObj["Version"]; + if (query is not null) + return query.ToString(); + } + + return string.Empty; + } + } + + private static string GetAvailability(int availability) + { + switch (availability) + { + case 1: return "Other"; + case 2: return "Unknown"; + case 3: return "Running or Full Power"; + case 4: return "Warning"; + case 5: return "In Test"; + case 6: return "Not Applicable"; + case 7: return "Power Off"; + case 8: return "Off Line"; + case 9: return "Off Duty"; + case 10: return "Degraded"; + case 11: return "Not Installed"; + case 12: return "Install Error"; + case 13: return "Power Save - Unknown"; + case 14: return "Power Save - Low Power Mode"; + case 15: return "Power Save - Standby"; + case 16: return "Power Cycle"; + case 17: return "Power Save - Warning"; + default: return "Unknown"; + } + } + + private static string ConvertToDateTime(string unconvertedTime) + { + var convertedTime = ""; + var year = int.Parse(unconvertedTime.Substring(0, 4)); + var month = int.Parse(unconvertedTime.Substring(4, 2)); + var date = int.Parse(unconvertedTime.Substring(6, 2)); + var hours = int.Parse(unconvertedTime.Substring(8, 2)); + var minutes = int.Parse(unconvertedTime.Substring(10, 2)); + var seconds = int.Parse(unconvertedTime.Substring(12, 2)); + var meridian = "AM"; + if (hours > 12) + { + hours -= 12; + meridian = "PM"; + } + + convertedTime = date + "/" + month + "/" + year + " " + + hours + ":" + minutes + ":" + seconds + " " + meridian; + return convertedTime; + } } \ No newline at end of file diff --git a/HandheldCompanion/Misc/PnPDetails.cs b/HandheldCompanion/Misc/PnPDetails.cs index 89f1e7998..4ac725047 100644 --- a/HandheldCompanion/Misc/PnPDetails.cs +++ b/HandheldCompanion/Misc/PnPDetails.cs @@ -1,208 +1,266 @@ -using Nefarius.Utilities.DeviceManagement.Extensions; -using Nefarius.Utilities.DeviceManagement.PnP; -using System.Runtime.InteropServices; - -namespace HandheldCompanion; - -[StructLayout(LayoutKind.Sequential)] -public class PnPDetails -{ - public string deviceInstanceId; - public string baseContainerDeviceInstanceId; - - public bool isGaming; - public bool isHooked; - - public bool isVirtual; - public bool isPhysical => !isVirtual; - - public string devicePath; - public string baseContainerDevicePath; - - public string Name; - public string SymLink; - public string Enumerator; - - public ushort ProductID; - public ushort VendorID; - - // XInput - public bool isXInput; - public byte XInputUserIndex = byte.MaxValue; - public int XInputDeviceIdx; - - public string GetProductID() - { - return "0x" + ProductID.ToString("X4"); - } - - public string GetVendorID() - { - return "0x" + VendorID.ToString("X4"); - } - - public short GetMI() - { - string low = SymLink.ToLower(); - int index = low.IndexOf("mi_"); - if (index == -1) - return -1; - string mi = low.Substring(index + 3, 2); - - if (short.TryParse(mi, out short number)) - return number; - - return -1; - } - - public string GetEnumerator() - { - return Enumerator; - } - - public UsbPnPDevice GetUsbPnPDevice() - { - PnPDevice device = GetBasePnPDevice(); - if (device is null) - return null; - - // is this a USB device - switch (Enumerator) - { - default: - case "BTHENUM": - return null; - case "USB": - break; - } - - return device.ToUsbPnPDevice(); - } - - public PnPDevice GetPnPDevice() - { - try - { - return PnPDevice.GetDeviceByInstanceId(deviceInstanceId); - } - catch { } - - return null; - } - - public PnPDevice GetBasePnPDevice() - { - try - { - return PnPDevice.GetDeviceByInstanceId(baseContainerDeviceInstanceId); - } - catch { } - - return null; - } - - public bool CyclePort() - { - UsbPnPDevice device = GetUsbPnPDevice(); - - try - { - if (device is not null) - { - device.CyclePort(); - return true; - } - } - catch { } - - return false; - } - - public bool InstallNullDrivers(bool basedevice = true) - { - PnPDevice device; - - switch (basedevice) - { - case true: - device = GetBasePnPDevice(); - break; - case false: - device = GetPnPDevice(); - break; - } - - try - { - if (device is not null) - { - device.InstallNullDriver(); - return true; - } - } - catch { } - - return false; - } - - public bool InstallCustomDriver(string driverName, bool basedevice = true) - { - PnPDevice device; - - switch(basedevice) - { - case true: - device = GetBasePnPDevice(); - break; - case false: - device = GetPnPDevice(); - break; - } - - try - { - if (device is not null) - { - device.InstallCustomDriver(driverName); - return true; - } - } - catch { } - - return false; - } - - public bool Uninstall(bool basedevice = true, bool parent = false) - { - PnPDevice device; - - switch (basedevice) - { - case true: - device = GetBasePnPDevice(); - - if (parent) - { - var parentId = device.GetProperty<string>(DevicePropertyKey.Device_Parent); - device = PnPDevice.GetDeviceByInstanceId(parentId); - } - - break; - case false: - device = GetPnPDevice(); - break; - } - - try - { - if (device is not null) - { - device.Uninstall(); - return true; - } - } - catch { } - - return false; - } +<<<<<<< HEAD +using Nefarius.Utilities.DeviceManagement.Extensions; +using Nefarius.Utilities.DeviceManagement.PnP; +======= +using HandheldCompanion.Managers.Hid; +using Nefarius.Utilities.DeviceManagement.Extensions; +using Nefarius.Utilities.DeviceManagement.PnP; +using System; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using System.Runtime.InteropServices; + +namespace HandheldCompanion; + +[StructLayout(LayoutKind.Sequential)] +<<<<<<< HEAD +public class PnPDetails +{ + public string deviceInstanceId; + public string baseContainerDeviceInstanceId; + +======= +public class PnPDetails : IDisposable +{ + public DateTimeOffset arrivalDate; + + public Attributes attributes; + public string baseContainerDeviceInstanceId; + public Capabilities capabilities; + + public string deviceInstanceId; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public bool isGaming; + public bool isHooked; + + public bool isVirtual; +<<<<<<< HEAD + public bool isPhysical => !isVirtual; + + public string devicePath; + public string baseContainerDevicePath; + + public string Name; + public string SymLink; + public string Enumerator; + + public ushort ProductID; + public ushort VendorID; +======= + public string Name; + public string Path; + public string SymLink; + + // dirty + public int DeviceIdx; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // XInput + public bool isXInput; + public byte XInputUserIndex = byte.MaxValue; +<<<<<<< HEAD + public int XInputDeviceIdx; + + public string GetProductID() + { + return "0x" + ProductID.ToString("X4"); +======= + + public void Dispose() + { + GC.SuppressFinalize(this); + } + + public string GetProductID() + { + return "0x" + attributes.ProductID.ToString("X4"); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public string GetVendorID() + { +<<<<<<< HEAD + return "0x" + VendorID.ToString("X4"); +======= + return "0x" + attributes.VendorID.ToString("X4"); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public short GetMI() + { + string low = SymLink.ToLower(); + int index = low.IndexOf("mi_"); + if (index == -1) + return -1; + string mi = low.Substring(index + 3, 2); + + if (short.TryParse(mi, out short number)) + return number; + + return -1; + } + + public string GetEnumerator() + { +<<<<<<< HEAD + return Enumerator; +======= + PnPDevice device = GetBasePnPDevice(); + if (device is not null) + return device.GetProperty<string>(DevicePropertyKey.Device_EnumeratorName); + + return string.Empty; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public UsbPnPDevice GetUsbPnPDevice() + { + PnPDevice device = GetBasePnPDevice(); + if (device is null) + return null; + + // is this a USB device +<<<<<<< HEAD + switch (Enumerator) +======= + string enumerator = GetEnumerator(); + + switch (enumerator) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + default: + case "BTHENUM": + return null; + case "USB": + break; + } + + return device.ToUsbPnPDevice(); + } + + public PnPDevice GetPnPDevice() + { + try + { + return PnPDevice.GetDeviceByInstanceId(deviceInstanceId); + } + catch { } + + return null; + } + + public PnPDevice GetBasePnPDevice() + { + try + { + return PnPDevice.GetDeviceByInstanceId(baseContainerDeviceInstanceId); + } + catch { } + + return null; + } + + public bool CyclePort() + { + UsbPnPDevice device = GetUsbPnPDevice(); + + try + { + if (device is not null) + { + device.CyclePort(); + return true; + } + } + catch { } + + return false; + } + + public bool InstallNullDrivers(bool basedevice = true) + { + PnPDevice device; + + switch (basedevice) + { + case true: + device = GetBasePnPDevice(); + break; + case false: + device = GetPnPDevice(); + break; + } + + try + { + if (device is not null) + { + device.InstallNullDriver(); + return true; + } + } + catch { } + + return false; + } + + public bool InstallCustomDriver(string driverName, bool basedevice = true) + { + PnPDevice device; + + switch(basedevice) + { + case true: + device = GetBasePnPDevice(); + break; + case false: + device = GetPnPDevice(); + break; + } + + try + { + if (device is not null) + { + device.InstallCustomDriver(driverName); + return true; + } + } + catch { } + + return false; + } + + public bool Uninstall(bool basedevice = true, bool parent = false) + { + PnPDevice device; + + switch (basedevice) + { + case true: + device = GetBasePnPDevice(); + + if (parent) + { + var parentId = device.GetProperty<string>(DevicePropertyKey.Device_Parent); + device = PnPDevice.GetDeviceByInstanceId(parentId); + } + + break; + case false: + device = GetPnPDevice(); + break; + } + + try + { + if (device is not null) + { + device.Uninstall(); + return true; + } + } + catch { } + + return false; + } } \ No newline at end of file diff --git a/HandheldCompanion/Misc/PnPUtil.cs b/HandheldCompanion/Misc/PnPUtil.cs index d4427d0df..0160ae33e 100644 --- a/HandheldCompanion/Misc/PnPUtil.cs +++ b/HandheldCompanion/Misc/PnPUtil.cs @@ -1,138 +1,168 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.Text.RegularExpressions; - -namespace HandheldCompanion -{ - // https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/pnputil-return-values - public class PnPUtilResult - { - public int ExitCode; - public string StandardOutput; - - public PnPUtilResult(int exitCode, string output) - { - ExitCode = exitCode; - StandardOutput = output; - } - } - - public static class PnPUtil - { - private const int ERROR_SUCCESS = 0; - private const int ERROR_NO_MORE_ITEMS = 259; - private const int ERROR_SUCCESS_REBOOT_REQUIRED = 3010; - private const int ERROR_SUCCESS_REBOOT_INITIATED = 1641; - - public static bool RestartDevice(string InstanceId) - { - var pnpResult = GetPnPUtilResult($"/restart-device \"{InstanceId}\""); - return ValidateChangeDeviceStatusResult(InstanceId, pnpResult); - } - - public static bool EnableDevice(string InstanceId) - { - var pnpResult = GetPnPUtilResult($"/enable-device \"{InstanceId}\""); - return ValidateChangeDeviceStatusResult(InstanceId, pnpResult); - } - - public static bool DisableDevice(string InstanceId) - { - var pnpResult = GetPnPUtilResult($"/disable-device \"{InstanceId}\""); - return ValidateChangeDeviceStatusResult(InstanceId, pnpResult); - } - - public static bool EnableDevices(string Class) - { - var pnpResult = GetPnPUtilResult($"/enable-device /class \"{Class}\""); - return pnpResult.ExitCode == ERROR_SUCCESS; - } - - public static List<string> GetDevices(string className, string status = "/connected") - { - // A list of string to store the Instance ID values - List<string> instanceIDs = new List<string>(); - - // A regular expression to match the Instance ID pattern - Regex regex = new Regex(@"Instance ID:\s+(.*)"); - - // Loop through each line of the input string - string input = GetPnPUtilOutput($"/enum-devices {status} /class {className}"); - foreach (string line in input.Split('\r')) - { - // Try to match the line with the regular expression - Match match = regex.Match(line); - - // If there is a match, add the Instance ID value to the list - if (match.Success) - { - instanceIDs.Add(match.Groups[1].Value); - } - } - - // Print the list of Instance ID values - Debug.WriteLine("The Instance ID values are:"); - foreach (string id in instanceIDs) - { - Debug.WriteLine(id); - } - - return instanceIDs; - } - - public static Process StartPnPUtil(string arguments) - { - Process process = new(); - - process.StartInfo.FileName = "pnputil.exe"; - process.StartInfo.Arguments = arguments; - - process.StartInfo.UseShellExecute = false; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.RedirectStandardError = true; - process.StartInfo.CreateNoWindow = true; - - process.Start(); - - return process; - } - - private static string GetPnPUtilOutput(string arguments) - { - Process process = StartPnPUtil(arguments); - var output = process.StandardOutput.ReadToEnd(); - - return output; - } - - private static PnPUtilResult GetPnPUtilResult(string arguments) - { - Process process = StartPnPUtil(arguments); - var output = process.StandardOutput.ReadToEnd(); - var exitCode = process.ExitCode; - - return new PnPUtilResult(exitCode, output); - } - - // this function validates the results for /enable-device, /disable-device and /restart-device. - private static bool ValidateChangeDeviceStatusResult(string instanceId, PnPUtilResult pnpResult) - { - string[] output = pnpResult.StandardOutput.Split("\r\n"); - var exitCode = pnpResult.ExitCode; - - switch (exitCode) - { - case ERROR_SUCCESS: - if (output[2].Contains(instanceId)) - // we assume the operation was successful if the instance id was found - return true; - break; - default: - // operation was not successful or requires a reboot. - return false; - } - - return true; - } - } -} +<<<<<<< HEAD +using System.Collections.Generic; +using System.Diagnostics; +using System.Text.RegularExpressions; +======= +using System.Diagnostics; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + +namespace HandheldCompanion +{ + // https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/pnputil-return-values + public class PnPUtilResult + { + public int ExitCode; + public string StandardOutput; + + public PnPUtilResult(int exitCode, string output) + { + ExitCode = exitCode; + StandardOutput = output; + } + } + + public static class PnPUtil + { + private const int ERROR_SUCCESS = 0; + private const int ERROR_NO_MORE_ITEMS = 259; + private const int ERROR_SUCCESS_REBOOT_REQUIRED = 3010; + private const int ERROR_SUCCESS_REBOOT_INITIATED = 1641; + + public static bool RestartDevice(string InstanceId) + { +<<<<<<< HEAD + var pnpResult = GetPnPUtilResult($"/restart-device \"{InstanceId}\""); + return ValidateChangeDeviceStatusResult(InstanceId, pnpResult); + } + + public static bool EnableDevice(string InstanceId) + { + var pnpResult = GetPnPUtilResult($"/enable-device \"{InstanceId}\""); +======= + var pnpResult = StartPnPUtil($"/restart-device \"{InstanceId}\""); + return ValidateChangeDeviceStatusResult(InstanceId, pnpResult); + } + public static bool EnableDevice(string InstanceId) + { + var pnpResult = StartPnPUtil($"/enable-device \"{InstanceId}\""); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + return ValidateChangeDeviceStatusResult(InstanceId, pnpResult); + } + + public static bool DisableDevice(string InstanceId) + { +<<<<<<< HEAD + var pnpResult = GetPnPUtilResult($"/disable-device \"{InstanceId}\""); + return ValidateChangeDeviceStatusResult(InstanceId, pnpResult); + } + + public static bool EnableDevices(string Class) + { + var pnpResult = GetPnPUtilResult($"/enable-device /class \"{Class}\""); + return pnpResult.ExitCode == ERROR_SUCCESS; + } + + public static List<string> GetDevices(string className, string status = "/connected") + { + // A list of string to store the Instance ID values + List<string> instanceIDs = new List<string>(); + + // A regular expression to match the Instance ID pattern + Regex regex = new Regex(@"Instance ID:\s+(.*)"); + + // Loop through each line of the input string + string input = GetPnPUtilOutput($"/enum-devices {status} /class {className}"); + foreach (string line in input.Split('\r')) + { + // Try to match the line with the regular expression + Match match = regex.Match(line); + + // If there is a match, add the Instance ID value to the list + if (match.Success) + { + instanceIDs.Add(match.Groups[1].Value); + } + } + + // Print the list of Instance ID values + Debug.WriteLine("The Instance ID values are:"); + foreach (string id in instanceIDs) + { + Debug.WriteLine(id); + } + + return instanceIDs; + } + + public static Process StartPnPUtil(string arguments) + { + Process process = new(); +======= + var pnpResult = StartPnPUtil($"/disable-device \"{InstanceId}\""); + return ValidateChangeDeviceStatusResult(InstanceId, pnpResult); + } + + private static PnPUtilResult StartPnPUtil(string arguments) + { + using Process process = new(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + process.StartInfo.FileName = "pnputil.exe"; + process.StartInfo.Arguments = arguments; + + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.RedirectStandardError = true; + process.StartInfo.CreateNoWindow = true; + + process.Start(); + +<<<<<<< HEAD + return process; + } + + private static string GetPnPUtilOutput(string arguments) + { + Process process = StartPnPUtil(arguments); + var output = process.StandardOutput.ReadToEnd(); + + return output; + } + + private static PnPUtilResult GetPnPUtilResult(string arguments) + { + Process process = StartPnPUtil(arguments); + var output = process.StandardOutput.ReadToEnd(); +======= + var output = process.StandardOutput.ReadToEnd(); + + process.WaitForExit(); + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + var exitCode = process.ExitCode; + + return new PnPUtilResult(exitCode, output); + } + + // this function validates the results for /enable-device, /disable-device and /restart-device. + private static bool ValidateChangeDeviceStatusResult(string instanceId, PnPUtilResult pnpResult) + { + string[] output = pnpResult.StandardOutput.Split("\r\n"); + var exitCode = pnpResult.ExitCode; + + switch (exitCode) + { + case ERROR_SUCCESS: + if (output[2].Contains(instanceId)) + // we assume the operation was successful if the instance id was found + return true; + break; + default: + // operation was not successful or requires a reboot. + return false; + } + + return true; + } + } +} diff --git a/HandheldCompanion/Misc/PowerProfile.cs b/HandheldCompanion/Misc/PowerProfile.cs index e0735d7e7..5e22349e8 100644 --- a/HandheldCompanion/Misc/PowerProfile.cs +++ b/HandheldCompanion/Misc/PowerProfile.cs @@ -1,264 +1,519 @@ -using HandheldCompanion.Managers; -using Inkore.UI.WPF.Modern.Controls; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text.RegularExpressions; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using Page = System.Windows.Controls.Page; - -namespace HandheldCompanion.Misc -{ - [Serializable] - public class PowerProfile - { - private string _Name; - public string Name - { - get - { - return _Name; - } - set - { - _Name = value; - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - foreach (UIElement uIElement in uIElements.Values) - uIElement.textBlock1.Text = value; - }); - } - } - - private string _Description; - public string Description - { - get - { - return _Description; - } - set - { - _Description = value; - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - foreach (UIElement uIElement in uIElements.Values) - uIElement.textBlock2.Text = value; - }); - } - } - - public string FileName { get; set; } - public bool Default { get; set; } - - public Version Version { get; set; } = new(); - public Guid Guid { get; set; } = Guid.NewGuid(); - - public bool TDPOverrideEnabled { get; set; } - public double[] TDPOverrideValues { get; set; } - - public bool CPUOverrideEnabled { get; set; } - public double CPUOverrideValue { get; set; } - - public bool GPUOverrideEnabled { get; set; } - public double GPUOverrideValue { get; set; } - - public bool AutoTDPEnabled { get; set; } - public float AutoTDPRequestedFPS { get; set; } = 30.0f; - - public bool EPPOverrideEnabled { get; set; } - public uint EPPOverrideValue { get; set; } = 50; - - public bool CPUCoreEnabled { get; set; } - public int CPUCoreCount { get; set; } = Environment.ProcessorCount; - - public bool CPUBoostEnabled { get; set; } - - public FanProfile FanProfile { get; set; } = new(); - - public int OEMPowerMode { get; set; } = 0xFF; - public Guid OSPowerMode { get; set; } = PowerMode.BetterPerformance; - - private Dictionary<Page, UIElement> uIElements = new(); - - public PowerProfile() - { } - - public PowerProfile(string name, string description) - { - Name = name; - Description = description; - - // Remove any invalid characters from the input - string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars())); - string output = Regex.Replace(name, "[" + invalidChars + "]", string.Empty); - output = output.Trim(); - - FileName = output; - } - - public string GetFileName() - { - return $"{FileName}.json"; - } - - public bool IsDefault() - { - return Default; - } - - public void DrawUI(Page page) - { - if (uIElements.ContainsKey(page)) - return; - - // Add to dictionnary - UIElement uIElement = new(this, page); - uIElement.textBlock1.Text = Name; - uIElement.textBlock2.Text = Description; - - uIElements[page] = uIElement; - } - - private struct UIElement - { - // UI Elements, move me! - public Button button; - public Grid grid; - public DockPanel dockPanel; - public RadioButtons radioButtons; - public RadioButton radioButton; - public SimpleStackPanel simpleStackPanel; - public TextBlock textBlock1 = new(); - public TextBlock textBlock2 = new(); - - public UIElement(PowerProfile powerProfile, Page page) - { - // Create a button - button = new Button(); - button.Height = 66; - button.Margin = new Thickness(-16); - button.Padding = new Thickness(50, 12, 12, 12); - button.HorizontalAlignment = HorizontalAlignment.Stretch; - button.HorizontalContentAlignment = HorizontalAlignment.Stretch; - button.Background = Brushes.Transparent; - button.BorderBrush = Brushes.Transparent; - - // Stored current profile, might be useful - button.Tag = powerProfile; - - // Create a grid - grid = new Grid(); - - // Define the Columns - var colDef1 = new ColumnDefinition - { - Width = new GridLength(10, GridUnitType.Star), - }; - grid.ColumnDefinitions.Add(colDef1); - - var colDef2 = new ColumnDefinition - { - MinWidth = 40 - }; - grid.ColumnDefinitions.Add(colDef2); - - // Create a dock panel - dockPanel = new DockPanel(); - dockPanel.HorizontalAlignment = HorizontalAlignment.Left; - - // Create a radio buttons - radioButtons = new RadioButtons(); - radioButtons.HorizontalAlignment = HorizontalAlignment.Center; - - // Create a radio button - radioButton = new RadioButton(); - radioButton.GroupName = $"PowerProfile{page.Name}"; - - // Create a simple stack panel - simpleStackPanel = new(); - simpleStackPanel.Margin = new Thickness(0, -10, 0, 0); - - // Create a text block for the controller layout - textBlock1.Style = (Style)Application.Current.Resources["BodyTextBlockStyle"]; - - // Create a text block for the controller layout description - textBlock2.TextWrapping = TextWrapping.NoWrap; - textBlock2.Style = (Style)Application.Current.Resources["CaptionTextBlockStyle"]; - textBlock2.SetResourceReference(Control.ForegroundProperty, "SystemControlForegroundBaseMediumBrush"); - - // Add the text blocks to the simple stack panel - simpleStackPanel.Children.Add(textBlock1); - simpleStackPanel.Children.Add(textBlock2); - - // Add the simple stack panel to the radio button control - radioButton.Content = simpleStackPanel; - radioButton.Checked += RadioButton_Checked; - radioButton.Unchecked += RadioButton_Unchecked; - - // Add the radio button to the radio buttons control - radioButtons.Items.Add(radioButton); - - // Add the radio buttons control to the dock panel - dockPanel.Children.Add(radioButtons); - - // Create a font icon - FontIcon fontIcon = new FontIcon(); - fontIcon.Margin = new Thickness(0, 0, 7, 0); - fontIcon.HorizontalAlignment = HorizontalAlignment.Right; - fontIcon.FontSize = 12; - fontIcon.Glyph = "\uE974"; - fontIcon.FontFamily = new("Segoe Fluent Icons"); - Grid.SetColumn(fontIcon, 1); - - // Add the dock panel and the font icon to the grid - grid.Children.Add(dockPanel); - grid.Children.Add(fontIcon); - - // Add the grid to the button - button.Content = grid; - } - - private void RadioButton_Checked(object sender, RoutedEventArgs e) - { - // do something - } - - private void RadioButton_Unchecked(object sender, RoutedEventArgs e) - { - // do something - } - } - - public Button GetButton(Page page) - { - return uIElements[page].button; - } - - public RadioButton GetRadioButton(Page page) - { - return uIElements[page].radioButton; - } - - public void Check(Page page) - { - uIElements[page].radioButton.IsChecked = true; - } - - public void Uncheck(Page page) - { - uIElements[page].radioButton.IsChecked = false; - } - - public override string ToString() - { - return Name; - } - } -} +<<<<<<< HEAD +using HandheldCompanion.Managers; +using Inkore.UI.WPF.Modern.Controls; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using Page = System.Windows.Controls.Page; + +namespace HandheldCompanion.Misc +{ + [Serializable] + public class PowerProfile + { + private string _Name; + public string Name + { + get + { + return _Name; + } + set + { + _Name = value; + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + foreach (UIElement uIElement in uIElements.Values) + uIElement.textBlock1.Text = value; + }); + } + } + + private string _Description; + public string Description + { + get + { + return _Description; + } + set + { + _Description = value; + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + foreach (UIElement uIElement in uIElements.Values) + uIElement.textBlock2.Text = value; + }); + } + } + + public string FileName { get; set; } + public bool Default { get; set; } + + public Version Version { get; set; } = new(); + public Guid Guid { get; set; } = Guid.NewGuid(); + + public bool TDPOverrideEnabled { get; set; } + public double[] TDPOverrideValues { get; set; } + + public bool CPUOverrideEnabled { get; set; } + public double CPUOverrideValue { get; set; } + + public bool GPUOverrideEnabled { get; set; } + public double GPUOverrideValue { get; set; } + + public bool AutoTDPEnabled { get; set; } + public float AutoTDPRequestedFPS { get; set; } = 30.0f; + + public bool EPPOverrideEnabled { get; set; } + public uint EPPOverrideValue { get; set; } = 50; + + public bool CPUCoreEnabled { get; set; } + public int CPUCoreCount { get; set; } = Environment.ProcessorCount; + + public bool CPUBoostEnabled { get; set; } + + public FanProfile FanProfile { get; set; } = new(); + + public int OEMPowerMode { get; set; } = 0xFF; + public Guid OSPowerMode { get; set; } = PowerMode.BetterPerformance; + + private Dictionary<Page, UIElement> uIElements = new(); + + public PowerProfile() + { } + + public PowerProfile(string name, string description) + { + Name = name; + Description = description; + + // Remove any invalid characters from the input + string invalidChars = Regex.Escape(new string(Path.GetInvalidFileNameChars())); + string output = Regex.Replace(name, "[" + invalidChars + "]", string.Empty); + output = output.Trim(); + + FileName = output; + } + + public string GetFileName() + { + return $"{FileName}.json"; + } + + public bool IsDefault() + { + return Default; + } + + public void DrawUI(Page page) + { + if (uIElements.ContainsKey(page)) + return; + + // Add to dictionnary + UIElement uIElement = new(this, page); + uIElement.textBlock1.Text = Name; + uIElement.textBlock2.Text = Description; + + uIElements[page] = uIElement; + } + + private struct UIElement + { + // UI Elements, move me! + public Button button; + public Grid grid; + public DockPanel dockPanel; + public RadioButtons radioButtons; + public RadioButton radioButton; + public SimpleStackPanel simpleStackPanel; + public TextBlock textBlock1 = new(); + public TextBlock textBlock2 = new(); + + public UIElement(PowerProfile powerProfile, Page page) + { + // Create a button + button = new Button(); + button.Height = 66; + button.Margin = new Thickness(-16); + button.Padding = new Thickness(50, 12, 12, 12); + button.HorizontalAlignment = HorizontalAlignment.Stretch; + button.HorizontalContentAlignment = HorizontalAlignment.Stretch; + button.Background = Brushes.Transparent; + button.BorderBrush = Brushes.Transparent; + + // Stored current profile, might be useful + button.Tag = powerProfile; + + // Create a grid + grid = new Grid(); + + // Define the Columns + var colDef1 = new ColumnDefinition + { + Width = new GridLength(10, GridUnitType.Star), + }; + grid.ColumnDefinitions.Add(colDef1); + + var colDef2 = new ColumnDefinition + { + MinWidth = 40 + }; + grid.ColumnDefinitions.Add(colDef2); + + // Create a dock panel + dockPanel = new DockPanel(); + dockPanel.HorizontalAlignment = HorizontalAlignment.Left; + + // Create a radio buttons + radioButtons = new RadioButtons(); + radioButtons.HorizontalAlignment = HorizontalAlignment.Center; + + // Create a radio button + radioButton = new RadioButton(); + radioButton.GroupName = $"PowerProfile{page.Name}"; + + // Create a simple stack panel + simpleStackPanel = new(); + simpleStackPanel.Margin = new Thickness(0, -10, 0, 0); + + // Create a text block for the controller layout + textBlock1.Style = (Style)Application.Current.Resources["BodyTextBlockStyle"]; + + // Create a text block for the controller layout description + textBlock2.TextWrapping = TextWrapping.NoWrap; + textBlock2.Style = (Style)Application.Current.Resources["CaptionTextBlockStyle"]; + textBlock2.SetResourceReference(Control.ForegroundProperty, "SystemControlForegroundBaseMediumBrush"); + + // Add the text blocks to the simple stack panel + simpleStackPanel.Children.Add(textBlock1); + simpleStackPanel.Children.Add(textBlock2); + + // Add the simple stack panel to the radio button control + radioButton.Content = simpleStackPanel; + radioButton.Checked += RadioButton_Checked; + radioButton.Unchecked += RadioButton_Unchecked; + + // Add the radio button to the radio buttons control + radioButtons.Items.Add(radioButton); + + // Add the radio buttons control to the dock panel + dockPanel.Children.Add(radioButtons); + + // Create a font icon + FontIcon fontIcon = new FontIcon(); + fontIcon.Margin = new Thickness(0, 0, 7, 0); + fontIcon.HorizontalAlignment = HorizontalAlignment.Right; + fontIcon.FontSize = 12; + fontIcon.Glyph = "\uE974"; + fontIcon.FontFamily = new("Segoe Fluent Icons"); + Grid.SetColumn(fontIcon, 1); + + // Add the dock panel and the font icon to the grid + grid.Children.Add(dockPanel); + grid.Children.Add(fontIcon); + + // Add the grid to the button + button.Content = grid; + } + + private void RadioButton_Checked(object sender, RoutedEventArgs e) + { + // do something + } + + private void RadioButton_Unchecked(object sender, RoutedEventArgs e) + { + // do something + } + } + + public Button GetButton(Page page) + { + return uIElements[page].button; + } + + public RadioButton GetRadioButton(Page page) + { + return uIElements[page].radioButton; + } + + public void Check(Page page) + { + uIElements[page].radioButton.IsChecked = true; + } + + public void Uncheck(Page page) + { + uIElements[page].radioButton.IsChecked = false; + } + + public override string ToString() + { + return Name; + } + } +} +======= +using System; +using System.Runtime.InteropServices; + +namespace HandheldCompanion.Misc; + +public enum PowerIndexType +{ + AC, + DC +} + +// For a reference on additional subgroup and setting GUIDs, run the command powercfg.exe /qh +// This will list all hidden settings with both the names and GUIDs, descriptions, current values, and allowed values. + +public static class PowerSubGroup +{ + public static Guid SUB_PROCESSOR = new("54533251-82be-4824-96c1-47b60b740d00"); +} + +public static class PowerSetting +{ + public static Guid PERFBOOSTMODE = new("be337238-0d82-4146-a960-4f3749d470c7"); // Processor performance boost mode + + public static Guid + PROCFREQMAX = + new("75b0ae3f-bce0-45a7-8c89-c9611c25e100"); // Maximum processor frequency in MHz, 0 for no limit (default) + + public static Guid + CPMINCORES = + new("0cc5b647-c1df-4637-891a-dec35c318583"); // Processor performance core parking min cores, expressed as a percent from 0 - 100 + + public static Guid + CPMAXCORES = + new("ea062031-0e34-4ff1-9b6d-eb1059334028"); // Processor performance core parking max cores, expressed as a percent from 0 - 100 + + public static Guid + PERFEPP = new( + "36687f9e-e3a5-4dbf-b1dc-15eb381c6863"); // Processor energy performance preference policy, expressed as a percent from 0 - 100 + + public static Guid + PERFEPP1 = new( + "36687f9e-e3a5-4dbf-b1dc-15eb381c6864"); // Processor energy performance preference policy for Processor Power Efficiency Class 1, expressed as a percent from 0 - 100 +} + +public enum PerfBoostMode +{ + Disabled = 0, + Enabled = 1, + Aggressive = 2, + EfficientEnabled = 3, + EfficientAggressive = 4, + AggressiveAtGuaranteed = 5, + EfficientAggressiveAtGuaranteed = 6 +} + +public static class PowerScheme +{ + // Wrapper for the actual PowerGetActiveScheme. Converts GUID to the built-in type on output and handles the LocalFree call. + /// <summary> + /// Retrieves the active power scheme and returns a GUID that identifies the scheme. + /// </summary> + /// <param name="UserRootPowerKey">This parameter is reserved for future use and must be set to zero.</param> + /// <param name="ActivePolicyGuid">A pointer that receives a GUID structure.</param> + /// <returns>Returns zero if the call was successful, and a nonzero value if the call failed.</returns> + private static uint PowerGetActiveScheme(nint UserRootPowerKey, out Guid ActivePolicyGuid) + { + var activePolicyGuidPtr = nint.Zero; + ActivePolicyGuid = Guid.Empty; + + var result = PowerGetActiveScheme(UserRootPowerKey, out activePolicyGuidPtr); + + if (result == 0 && activePolicyGuidPtr != nint.Zero) + { + ActivePolicyGuid = (Guid)Marshal.PtrToStructure(activePolicyGuidPtr, typeof(Guid)); + LocalFree(activePolicyGuidPtr); + } + + return result; + } + + public static bool GetActiveScheme(out Guid ActivePolicyGuid) + { + return PowerGetActiveScheme(nint.Zero, out ActivePolicyGuid) == 0; + } + + public static bool SetActiveScheme(Guid SchemeGuid) + { + return PowerSetActiveScheme(nint.Zero, SchemeGuid) == 0; + } + + public static bool GetValue(PowerIndexType powerType, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, + Guid PowerSettingGuid, out uint value) + { + switch (powerType) + { + case PowerIndexType.AC: + return PowerReadACValueIndex(nint.Zero, SchemeGuid, SubGroupOfPowerSettingsGuid, PowerSettingGuid, + out value) == 0; + case PowerIndexType.DC: + return PowerReadDCValueIndex(nint.Zero, SchemeGuid, SubGroupOfPowerSettingsGuid, PowerSettingGuid, + out value) == 0; + } + + value = 0; + return false; + } + + public static bool SetValue(PowerIndexType powerType, Guid SchemeGuid, Guid SubGroupOfPowerSettingsGuid, + Guid PowerSettingGuid, uint value) + { + switch (powerType) + { + case PowerIndexType.AC: + return PowerWriteACValueIndex(nint.Zero, SchemeGuid, SubGroupOfPowerSettingsGuid, PowerSettingGuid, + value) == 0; + case PowerIndexType.DC: + return PowerWriteDCValueIndex(nint.Zero, SchemeGuid, SubGroupOfPowerSettingsGuid, PowerSettingGuid, + value) == 0; + } + + return false; + } + + public static bool SetAttribute(Guid SubGroupOfPowerSettingsGuid, Guid PowerSettingGuid, bool hide) + { + return PowerWriteSettingAttributes(SubGroupOfPowerSettingsGuid, PowerSettingGuid, (uint)(hide ? 1 : 0)) == 0; + } + + public static uint[] ReadPowerCfg(Guid SubGroup, Guid Settings) + { + var results = new uint[2]; + + if (GetActiveScheme(out var currentScheme)) + { + // read AC/DC values + GetValue(PowerIndexType.AC, currentScheme, SubGroup, Settings, + out results[(int)PowerIndexType.AC]); + GetValue(PowerIndexType.DC, currentScheme, SubGroup, Settings, + out results[(int)PowerIndexType.DC]); + } + + return results; + } + + public static void WritePowerCfg(Guid SubGroup, Guid Settings, uint ACValue, uint DCValue) + { + if (GetActiveScheme(out var currentScheme)) + { + // unhide attribute + SetAttribute(SubGroup, Settings, false); + + // set value(s) + SetValue(PowerIndexType.AC, currentScheme, SubGroup, Settings, ACValue); + SetValue(PowerIndexType.DC, currentScheme, SubGroup, Settings, DCValue); + + // activate scheme + SetActiveScheme(currentScheme); + } + } + + #region imports + + /// <summary> + /// Retrieves the active power scheme and returns a GUID that identifies the scheme. + /// </summary> + /// <param name="UserRootPowerKey">This parameter is reserved for future use and must be set to zero.</param> + /// <param name="ActivePolicyGuid"> + /// A pointer that receives a pointer to a GUID structure. Use the LocalFree function to + /// free this memory. + /// </param> + /// <returns>Returns zero if the call was successful, and a nonzero value if the call failed.</returns> + [DllImport("powrprof.dll", EntryPoint = "PowerGetActiveScheme")] + private static extern uint PowerGetActiveScheme(nint UserRootPowerKey, out nint ActivePolicyGuid); + + /// <summary> + /// Sets the active power scheme for the current user. + /// </summary> + /// <param name="UserRootPowerKey">This parameter is reserved for future use and must be set to zero.</param> + /// <param name="SchemeGuid">The identifier of the power scheme.</param> + /// <returns>Returns zero if the call was successful, and a nonzero value if the call failed.</returns> + [DllImport("powrprof.dll", EntryPoint = "PowerSetActiveScheme")] + private static extern uint PowerSetActiveScheme(nint UserRootPowerKey, in Guid SchemeGuid); + + /// <summary> + /// Retrieves the AC value index of the specified power setting. + /// </summary> + /// <param name="RootPowerKey">This parameter is reserved for future use and must be set to zero.</param> + /// <param name="SchemeGuid">The identifier of the power scheme.</param> + /// <param name="SubGroupOfPowerSettingsGuid">The subgroup of power settings.</param> + /// <param name="PowerSettingGuid">The identifier of the power setting.</param> + /// <param name="AcValueIndex">A pointer to a variable that receives the AC value index.</param> + /// <returns>Returns zero if the call was successful, and a nonzero value if the call failed.</returns> + [DllImport("powrprof.dll", EntryPoint = "PowerReadACValueIndex")] + private static extern uint PowerReadACValueIndex(nint RootPowerKey, in Guid SchemeGuid, + in Guid SubGroupOfPowerSettingsGuid, in Guid PowerSettingGuid, out uint AcValueIndex); + + /// <summary> + /// Retrieves the DC value index of the specified power setting. + /// </summary> + /// <param name="RootPowerKey">This parameter is reserved for future use and must be set to zero.</param> + /// <param name="SchemeGuid">The identifier of the power scheme.</param> + /// <param name="SubGroupOfPowerSettingsGuid">The subgroup of power settings.</param> + /// <param name="PowerSettingGuid">The identifier of the power setting.</param> + /// <param name="DcValueIndex">A pointer to a variable that receives the DC value index.</param> + /// <returns>Returns zero if the call was successful, and a nonzero value if the call failed.</returns> + [DllImport("powrprof.dll", EntryPoint = "PowerReadDCValueIndex")] + private static extern uint PowerReadDCValueIndex(nint RootPowerKey, in Guid SchemeGuid, + in Guid SubGroupOfPowerSettingsGuid, in Guid PowerSettingGuid, out uint DcValueIndex); + + /// <summary> + /// Sets the AC value index of the specified power setting. + /// </summary> + /// <param name="RootPowerKey">This parameter is reserved for future use and must be set to zero.</param> + /// <param name="SchemeGuid">The identifier of the power scheme.</param> + /// <param name="SubGroupOfPowerSettingsGuid">The subgroup of power settings.</param> + /// <param name="PowerSettingGuid">The identifier of the power setting.</param> + /// <param name="AcValueIndex">The AC value index.</param> + /// <returns>Returns zero if the call was successful, and a nonzero value if the call failed.</returns> + [DllImport("powrprof.dll", EntryPoint = "PowerWriteACValueIndex")] + private static extern uint PowerWriteACValueIndex(nint RootPowerKey, in Guid SchemeGuid, + in Guid SubGroupOfPowerSettingsGuid, in Guid PowerSettingGuid, uint AcValueIndex); + + /// <summary> + /// Sets the DC value index of the specified power setting. + /// </summary> + /// <param name="RootPowerKey">This parameter is reserved for future use and must be set to zero.</param> + /// <param name="SchemeGuid">The identifier of the power scheme.</param> + /// <param name="SubGroupOfPowerSettingsGuid">The subgroup of power settings.</param> + /// <param name="PowerSettingGuid">The identifier of the power setting.</param> + /// <param name="DcValueIndex">The DC value index.</param> + /// <returns>Returns zero if the call was successful, and a nonzero value if the call failed.</returns> + [DllImport("powrprof.dll", EntryPoint = "PowerWriteDCValueIndex")] + private static extern uint PowerWriteDCValueIndex(nint RootPowerKey, in Guid SchemeGuid, + in Guid SubGroupOfPowerSettingsGuid, in Guid PowerSettingGuid, uint DcValueIndex); + + /// <summary> + /// Frees the specified local memory object and invalidates its handle. + /// </summary> + /// <param name="hMem">A handle to the local memory object.</param> + /// <returns> + /// If the function succeeds, the return value is zero, and if the function fails, the return value is equal to a + /// handle to the local memory object. + /// </returns> + [DllImport("kernel32.dll", EntryPoint = "LocalFree")] + private static extern nint LocalFree(nint hMem); + + [DllImport("powrprof.dll", EntryPoint = "PowerWriteSettingAttributes")] + private static extern uint PowerWriteSettingAttributes(in Guid SubGroupOfPowerSettingsGuid, + in Guid PowerSettingGuid, uint Attributes); + + #endregion +} +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d diff --git a/HandheldCompanion/Misc/Profile.cs b/HandheldCompanion/Misc/Profile.cs index 03a3cabb2..e498828bf 100644 --- a/HandheldCompanion/Misc/Profile.cs +++ b/HandheldCompanion/Misc/Profile.cs @@ -1,166 +1,239 @@ -using HandheldCompanion.Inputs; -using HandheldCompanion.Utils; -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using static HandheldCompanion.Utils.XInputPlusUtils; - -namespace HandheldCompanion; - -[Flags] -public enum ProfileErrorCode -{ - None = 0, - MissingExecutable = 1, - MissingPath = 2, - MissingPermission = 3, - Default = 4, - Running = 5 -} - -[Flags] -public enum UpdateSource -{ - Background = 0, - ProfilesPage = 1, - QuickProfilesPage = 2, - Creation = 4, - Serializer = 5 -} - -[Serializable] -public partial class Profile : ICloneable, IComparable -{ - [JsonIgnore] public const int SensivityArraySize = 49; // x + 1 (hidden) - - public ProfileErrorCode ErrorCode = ProfileErrorCode.None; - - public string Name { get; set; } = string.Empty; - public string Path { get; set; } = string.Empty; - - public Guid Guid { get; set; } = Guid.NewGuid(); - public string Executable { get; set; } = string.Empty; - - public bool Enabled { get; set; } - - public bool Default { get; set; } - public Version Version { get; set; } = new(); - - public string LayoutTitle { get; set; } = string.Empty; - public bool LayoutEnabled { get; set; } = false; - public Layout Layout { get; set; } = new(); - - public bool Whitelisted { get; set; } // if true, can see through the HidHide cloak - - public XInputPlusMethod XInputPlus { get; set; } // if true, deploy xinput1_3.dll - - public float GyrometerMultiplier { get; set; } = 1.0f; // gyroscope multiplicator (remove me) - public float AccelerometerMultiplier { get; set; } = 1.0f; // accelerometer multiplicator (remove me) - - public int SteeringAxis { get; set; } = 0; // 0 = Roll, 1 = Yaw - - public bool MotionInvertHorizontal { get; set; } // if true, invert horizontal axis - public bool MotionInvertVertical { get; set; } // if false, invert vertical axis - public float MotionSensivityX { get; set; } = 1.0f; - public float MotionSensivityY { get; set; } = 1.0f; - public SortedDictionary<double, double> MotionSensivityArray { get; set; } = new(); - - // steering - public float SteeringMaxAngle { get; set; } = 30.0f; - public float SteeringPower { get; set; } = 1.0f; - public float SteeringDeadzone { get; set; } = 0.0f; - - // Aiming down sights - public float AimingSightsMultiplier { get; set; } = 1.0f; - public ButtonState AimingSightsTrigger { get; set; } = new(); - - // flickstick - public bool FlickstickEnabled { get; set; } - public float FlickstickDuration { get; set; } = 0.1f; - public float FlickstickSensivity { get; set; } = 3.0f; - - // power - public Guid PowerProfile { get; set; } = new(); - - public bool FramerateEnabled { get; set; } - public int FramerateValue { get; set; } = 0; - - public bool RSREnabled { get; set; } - public int RSRSharpness { get; set; } = 20; - - public bool CPUCoreEnabled { get; set; } - public int CPUCoreCount { get; set; } = Environment.ProcessorCount; - - // emulated controller type, default is default - public HIDmode HID { get; set; } = HIDmode.NotSelected; - - public Profile() - { - // initialize aiming array - if (MotionSensivityArray.Count == 0) - for (var i = 0; i < SensivityArraySize; i++) - { - var value = i / (double)(SensivityArraySize - 1); - MotionSensivityArray[value] = 0.5f; - } - } - - public Profile(string path) : this() - { - if (!string.IsNullOrEmpty(path)) - { - - var AppProperties = ProcessUtils.GetAppProperties(path); - - var ProductName = AppProperties.TryGetValue("FileDescription", out var property) ? property : AppProperties["ItemFolderNameDisplay"]; - // string Version = AppProperties.ContainsKey("FileVersion") ? AppProperties["FileVersion"] : "1.0.0.0"; - // string Company = AppProperties.ContainsKey("Company") ? AppProperties["Company"] : AppProperties.ContainsKey("Copyright") ? AppProperties["Copyright"] : "Unknown"; - - Executable = AppProperties["FileName"]; - Name = ProductName; - Path = path; - } - - // enable the below variables when profile is created - Enabled = true; - } - - public object Clone() - { - var jsonString = JsonConvert.SerializeObject(this, Formatting.Indented, - new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); - return JsonConvert.DeserializeObject<Profile>(jsonString, - new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); - } - - public int CompareTo(object obj) - { - var profile = (Profile)obj; - return profile.Name.CompareTo(Name); - } - - public float GetSensitivityX() - { - return MotionSensivityX * 1000.0f; - } - - public float GetSensitivityY() - { - return MotionSensivityY * 1000.0f; - } - - public string GetFileName() - { - var name = Name; - - if (!Default) - name = System.IO.Path.GetFileNameWithoutExtension(Executable); - - return $"{name}.json"; - } - - public override string ToString() - { - return Name; - } +using HandheldCompanion.Inputs; +<<<<<<< HEAD +======= +using HandheldCompanion.Properties; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using HandheldCompanion.Utils; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using static HandheldCompanion.Utils.XInputPlusUtils; + +namespace HandheldCompanion; + +[Flags] +public enum ProfileErrorCode +{ + None = 0, + MissingExecutable = 1, + MissingPath = 2, + MissingPermission = 3, + Default = 4, + Running = 5 +} + +[Flags] +<<<<<<< HEAD +public enum UpdateSource +======= +public enum ProfileUpdateSource +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +{ + Background = 0, + ProfilesPage = 1, + QuickProfilesPage = 2, + Creation = 4, + Serializer = 5 +} + +[Serializable] +public partial class Profile : ICloneable, IComparable +{ + [JsonIgnore] public const int SensivityArraySize = 49; // x + 1 (hidden) + +<<<<<<< HEAD + public ProfileErrorCode ErrorCode = ProfileErrorCode.None; + +======= + // todo: move me out of here ! + public static readonly SortedDictionary<MotionInput, string> InputDescription = new() + { + { MotionInput.JoystickCamera, Resources.JoystickCameraDesc }, + { MotionInput.JoystickSteering, Resources.JoystickSteeringDesc }, + { MotionInput.PlayerSpace, Resources.PlayerSpaceDesc }, + { MotionInput.AutoRollYawSwap, Resources.AutoRollYawSwapDesc } + }; + + public ProfileErrorCode ErrorCode = ProfileErrorCode.None; + + public Profile() + { + // initialize aiming array + if (MotionSensivityArray.Count == 0) + for (var i = 0; i < SensivityArraySize; i++) + { + var value = i / (double)(SensivityArraySize - 1); + MotionSensivityArray[value] = 0.5f; + } + } + + public Profile(string path) : this() + { + if (!string.IsNullOrEmpty(path)) + { + + var AppProperties = ProcessUtils.GetAppProperties(path); + + var ProductName = AppProperties.TryGetValue("FileDescription", out var property) ? property : AppProperties["ItemFolderNameDisplay"]; + // string Version = AppProperties.ContainsKey("FileVersion") ? AppProperties["FileVersion"] : "1.0.0.0"; + // string Company = AppProperties.ContainsKey("Company") ? AppProperties["Company"] : AppProperties.ContainsKey("Copyright") ? AppProperties["Copyright"] : "Unknown"; + + Executable = AppProperties["FileName"]; + Name = ProductName; + Path = path; + } + + // enable the below variables when profile is created + Enabled = true; + } + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public string Name { get; set; } = string.Empty; + public string Path { get; set; } = string.Empty; + + public Guid Guid { get; set; } = Guid.NewGuid(); + public string Executable { get; set; } = string.Empty; + + public bool Enabled { get; set; } + + public bool Default { get; set; } + public Version Version { get; set; } = new(); + + public string LayoutTitle { get; set; } = string.Empty; + public bool LayoutEnabled { get; set; } = false; + public Layout Layout { get; set; } = new(); + + public bool Whitelisted { get; set; } // if true, can see through the HidHide cloak + + public XInputPlusMethod XInputPlus { get; set; } // if true, deploy xinput1_3.dll + + public float GyrometerMultiplier { get; set; } = 1.0f; // gyroscope multiplicator (remove me) + public float AccelerometerMultiplier { get; set; } = 1.0f; // accelerometer multiplicator (remove me) + + public int SteeringAxis { get; set; } = 0; // 0 = Roll, 1 = Yaw + + public bool MotionInvertHorizontal { get; set; } // if true, invert horizontal axis + public bool MotionInvertVertical { get; set; } // if false, invert vertical axis + public float MotionSensivityX { get; set; } = 1.0f; + public float MotionSensivityY { get; set; } = 1.0f; + public SortedDictionary<double, double> MotionSensivityArray { get; set; } = new(); + + // steering + public float SteeringMaxAngle { get; set; } = 30.0f; + public float SteeringPower { get; set; } = 1.0f; + public float SteeringDeadzone { get; set; } = 0.0f; + + // Aiming down sights + public float AimingSightsMultiplier { get; set; } = 1.0f; + public ButtonState AimingSightsTrigger { get; set; } = new(); + + // flickstick + public bool FlickstickEnabled { get; set; } + public float FlickstickDuration { get; set; } = 0.1f; + public float FlickstickSensivity { get; set; } = 3.0f; + + // power +<<<<<<< HEAD + public Guid PowerProfile { get; set; } = new(); +======= + public bool TDPOverrideEnabled { get; set; } + public double[] TDPOverrideValues { get; set; } + + public bool GPUOverrideEnabled { get; set; } + public double GPUOverrideValue { get; set; } + + public bool AutoTDPEnabled { get; set; } + public float AutoTDPRequestedFPS { get; set; } = 30.0f; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + public bool FramerateEnabled { get; set; } + public int FramerateValue { get; set; } = 0; + +<<<<<<< HEAD +======= + public bool EPPOverrideEnabled { get; set; } + public uint EPPOverrideValue { get; set; } = 50; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public bool RSREnabled { get; set; } + public int RSRSharpness { get; set; } = 20; + + public bool CPUCoreEnabled { get; set; } + public int CPUCoreCount { get; set; } = Environment.ProcessorCount; + +<<<<<<< HEAD + // emulated controller type, default is default + public HIDmode HID { get; set; } = HIDmode.NotSelected; + + public Profile() + { + // initialize aiming array + if (MotionSensivityArray.Count == 0) + for (var i = 0; i < SensivityArraySize; i++) + { + var value = i / (double)(SensivityArraySize - 1); + MotionSensivityArray[value] = 0.5f; + } + } + + public Profile(string path) : this() + { + if (!string.IsNullOrEmpty(path)) + { + + var AppProperties = ProcessUtils.GetAppProperties(path); + + var ProductName = AppProperties.TryGetValue("FileDescription", out var property) ? property : AppProperties["ItemFolderNameDisplay"]; + // string Version = AppProperties.ContainsKey("FileVersion") ? AppProperties["FileVersion"] : "1.0.0.0"; + // string Company = AppProperties.ContainsKey("Company") ? AppProperties["Company"] : AppProperties.ContainsKey("Copyright") ? AppProperties["Copyright"] : "Unknown"; + + Executable = AppProperties["FileName"]; + Name = ProductName; + Path = path; + } + + // enable the below variables when profile is created + Enabled = true; + } + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public object Clone() + { + var jsonString = JsonConvert.SerializeObject(this, Formatting.Indented, + new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); + return JsonConvert.DeserializeObject<Profile>(jsonString, + new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); + } + + public int CompareTo(object obj) + { + var profile = (Profile)obj; + return profile.Name.CompareTo(Name); + } + + public float GetSensitivityX() + { + return MotionSensivityX * 1000.0f; + } + + public float GetSensitivityY() + { + return MotionSensivityY * 1000.0f; + } + + public string GetFileName() + { + var name = Name; + + if (!Default) + name = System.IO.Path.GetFileNameWithoutExtension(Executable); + + return $"{name}.json"; + } + + public override string ToString() + { + return Name; + } } \ No newline at end of file diff --git a/HandheldCompanion/Platforms/HWiNFO.cs b/HandheldCompanion/Platforms/HWiNFO.cs index c5e2633d5..92802e6d9 100644 --- a/HandheldCompanion/Platforms/HWiNFO.cs +++ b/HandheldCompanion/Platforms/HWiNFO.cs @@ -1,684 +1,695 @@ -using HandheldCompanion.Managers; -using HandheldCompanion.Processors; -using HandheldCompanion.Utils; -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.IO; -using System.IO.MemoryMappedFiles; -using System.Runtime.InteropServices; -using System.Threading; -using System.Timers; -using System.Windows; -using Timer = System.Timers.Timer; - -namespace HandheldCompanion.Platforms; - -public class HWiNFO : IPlatform -{ - public enum SensorElementType - { - CPUTemperature, - CPUFrequency, - CPUPower, - CPUUsage, - - GPUTemperature, - GPUFrequency, - GPUPower, - GPUUsage, - GPUMemoryUsage, - - PL1, - PL2, - - BatteryChargeLevel, - BatteryRemainingCapacity, - BatteryRemainingTime, - - PhysicalMemoryUsage, - VirtualMemoryUsage - } - - private const string HWiNFO_SHARED_MEM_FILE_NAME = "Global\\HWiNFO_SENS_SM2"; - private const int HWiNFO_SENSORS_STRING_LEN = 128; - private const int HWiNFO_UNIT_STRING_LEN = 16; - private const int MemoryInterval = 1000; - - private readonly Timer MemoryTimer; - - private SharedMemory HWiNFOMemory; - - private ConcurrentDictionary<uint, Sensor> HWiNFOSensors; - private MemoryMappedViewAccessor MemoryAccessor; - - private MemoryMappedFile MemoryMapped; - - public ConcurrentDictionary<SensorElementType, SensorElement> MonitoredSensors = new(); - - private long prevPoll_time = -1; - - public HWiNFO() - { - PlatformType = PlatformType.HWiNFO; - ExpectedVersion = new Version(7, 42, 5030); - Url = "https://www.hwinfo.com/files/hwi_742.exe"; - - Name = "HWiNFO64"; - ExecutableName = RunningName = "HWiNFO64.exe"; - - // check if platform is installed - InstallPath = RegistryUtils.GetString(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\HWiNFO64_is1", - "InstallLocation"); - if (Path.Exists(InstallPath)) - { - // update paths - SettingsPath = Path.Combine(InstallPath, "HWiNFO64.ini"); - ExecutablePath = Path.Combine(InstallPath, ExecutableName); - - // check executable - if (File.Exists(ExecutablePath)) - { - // check executable version - var versionInfo = FileVersionInfo.GetVersionInfo(ExecutablePath); - var CurrentVersion = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, - versionInfo.ProductBuildPart); - - if (CurrentVersion < ExpectedVersion) - { - LogManager.LogWarning("HWiNFO is outdated. Please get it from: {0}", Url); - return; - } - - IsInstalled = true; - } - } - - if (!IsInstalled) - { - LogManager.LogWarning("HWiNFO is missing. Please get it from: {0}", Url); - return; - } - - // those are used for computes - MonitoredSensors[SensorElementType.PL1] = new SensorElement(); - MonitoredSensors[SensorElementType.PL2] = new SensorElement(); - MonitoredSensors[SensorElementType.CPUFrequency] = new SensorElement(); - MonitoredSensors[SensorElementType.GPUFrequency] = new SensorElement(); - - // file watcher - if (File.Exists(SettingsPath)) - { - systemWatcher = new(Path.GetDirectoryName(SettingsPath)); - systemWatcher.Filter = "*.ini"; - systemWatcher.EnableRaisingEvents = true; - systemWatcher.Changed += SystemWatcher_Changed; - } - - // our main watchdog to (re)apply requested settings - PlatformWatchdog = new Timer(3000) { Enabled = false }; - PlatformWatchdog.Elapsed += Watchdog_Elapsed; - - // secondary watchdog to (re)populate sensors - MemoryTimer = new Timer(MemoryInterval) { Enabled = false }; - MemoryTimer.Elapsed += (sender, e) => PopulateSensors(); - } - - private void SystemWatcher_Changed(object sender, FileSystemEventArgs e) - { - bool SensorsSM = GetProperty("SensorsSM"); - SystemWatcher_Changed("SensorsSM", SensorsSM); - } - - public override bool Start() - { - // not authorized - // var hasSensorsSM = GetProperty("SensorsSM"); - - // start HWiNFO if not running or Shared Memory is disabled - if (!IsRunning) // || !hasSensorsSM) - { - // StopProcess(); - StartProcess(); - } - else - { - // hook into current process - Process.Exited += Process_Exited; - } - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - - return base.Start(); - } - - public override bool Stop(bool kill = false) - { - if (MemoryTimer is not null) - MemoryTimer.Stop(); - - SettingsManager.SettingValueChanged -= SettingsManager_SettingValueChanged; - - return base.Stop(kill); - } - - private void SettingsManager_SettingValueChanged(string name, object value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (name) - { - case "OnScreenDisplayRefreshRate": - SetProperty("SensorInterval", Convert.ToInt32(value)); - break; - } - }); - } - - private void Watchdog_Elapsed(object? sender, ElapsedEventArgs e) - { - if (Monitor.TryEnter(updateLock)) - { - try - { - // check shared memory - MemoryMappedFile.OpenExisting(HWiNFO_SHARED_MEM_FILE_NAME, MemoryMappedFileRights.Read); - } - catch - { - // not authorized - // shared memory is disabled, halt process - /* - if (prevPoll_time != -1) - StopProcess(); - */ - - // raise event - // todo: implement a new hint - SetStatus(PlatformStatus.Stalled); - Monitor.Exit(updateLock); - return; - } - - // we couldn't poll HWiNFO, halt process - if (HWiNFOMemory.poll_time == prevPoll_time) - { - // not authorized - // StopProcess(); - - // raise event - // todo: implement a new hint - SetStatus(PlatformStatus.Stalled); - Monitor.Exit(updateLock); - return; - } - - // update poll time - if (HWiNFOMemory.poll_time != 0) - prevPoll_time = HWiNFOMemory.poll_time; - - // reset tentative counter - Tentative = 0; - - // connect to shared memory - if (MemoryMapped is null) - MemoryMapped = MemoryMappedFile.OpenExisting(HWiNFO_SHARED_MEM_FILE_NAME, MemoryMappedFileRights.Read); - - // get accessor - if (MemoryAccessor is null) - MemoryAccessor = - MemoryMapped.CreateViewAccessor(0L, Marshal.SizeOf(typeof(SharedMemory)), MemoryMappedFileAccess.Read); - MemoryAccessor.Read(0L, out HWiNFOMemory); - - // we listed sensors already - if (HWiNFOSensors is null) - { - // (re)set sensors array - HWiNFOSensors = new ConcurrentDictionary<uint, Sensor>(); - - // populate sensors array - GetSensors(); - } - - MemoryTimer.Start(); - - Monitor.Exit(updateLock); - } - } - - public void GetSensors() - { - try - { - for (uint index = 0; index < HWiNFOMemory.dwNumSensorElements; ++index) - using (var viewStream = MemoryMapped.CreateViewStream( - HWiNFOMemory.dwOffsetOfSensorSection + index * HWiNFOMemory.dwSizeOfSensorElement, - HWiNFOMemory.dwSizeOfSensorElement, MemoryMappedFileAccess.Read)) - { - var buffer = new byte[(int)HWiNFOMemory.dwSizeOfSensorElement]; - viewStream.Read(buffer, 0, (int)HWiNFOMemory.dwSizeOfSensorElement); - var gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); - var structure = - (SensorStructure)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(SensorStructure)); - gcHandle.Free(); - var sensor = new Sensor - { - NameOrig = structure.szSensorNameOrig, - NameUser = structure.szSensorNameUser, - Elements = new ConcurrentDictionary<uint, SensorElement>() - }; - HWiNFOSensors[index] = sensor; - } - } - catch - { - // do something - } - } - - public void PopulateSensors() - { - if (MemoryMapped is null) - return; - - try - { - for (uint index = 0; index < HWiNFOMemory.dwNumReadingElements; ++index) - using (var viewStream = MemoryMapped.CreateViewStream( - HWiNFOMemory.dwOffsetOfReadingSection + index * HWiNFOMemory.dwSizeOfReadingElement, - HWiNFOMemory.dwSizeOfReadingElement, MemoryMappedFileAccess.Read)) - { - var buffer = new byte[(int)HWiNFOMemory.dwSizeOfReadingElement]; - viewStream.Read(buffer, 0, (int)HWiNFOMemory.dwSizeOfReadingElement); - var gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); - - var element = - (SensorElement)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(SensorElement)); - gcHandle.Free(); - - if (HWiNFOSensors.TryGetValue(element.dwSensorIndex, out var sensor)) - sensor.Elements[element.dwSensorID] = element; - else - continue; - - switch (element.tReading) - { - case SENSOR_READING_TYPE.SENSOR_TYPE_TEMP: - { - switch (element.szLabelOrig) - { - case "CPU Package": - case "CPU (Tctl/Tdie)": - MonitoredSensors[SensorElementType.CPUTemperature] = element; - break; - - case "CPU GT Cores (Graphics)": - case "GPU Temperature": - MonitoredSensors[SensorElementType.GPUTemperature] = element; - break; - } - } - break; - - case SENSOR_READING_TYPE.SENSOR_TYPE_POWER: - { - switch (element.szLabelOrig) - { - case "CPU Package Power": - case "CPU PPT": - MonitoredSensors[SensorElementType.CPUPower] = element; - break; - - case "PL1 Power Limit": - // case "PL1 Power Limit (Static)": - case "PL1 Power Limit (Dynamic)": - { - var reading = (int)Math.Ceiling(element.Value); - if (reading != MonitoredSensors[SensorElementType.PL1].Value) - PowerLimitChanged?.Invoke(PowerType.Slow, reading); - - element.Value = reading; - MonitoredSensors[SensorElementType.PL1] = element; - } - break; - case "PL2 Power Limit": - // case "PL2 Power Limit (Static)": - case "PL2 Power Limit (Dynamic)": - { - var reading = (int)Math.Ceiling(element.Value); - if (reading != MonitoredSensors[SensorElementType.PL2].Value) - PowerLimitChanged?.Invoke(PowerType.Fast, reading); - - element.Value = reading; - MonitoredSensors[SensorElementType.PL2] = element; - } - break; - - case "GPU ASIC Power": - case "GT Cores Power": - case "GPU SoC Power (VDDCR_SOC)": - case "GPU PPT": - MonitoredSensors[SensorElementType.GPUPower] = element; - break; - } - } - break; - - case SENSOR_READING_TYPE.SENSOR_TYPE_USAGE: - { - switch (element.szLabelOrig) - { - case "GPU Utilization": - case "GPU D3D Usage": - MonitoredSensors[SensorElementType.GPUUsage] = element; - break; - - case "Total CPU Usage": - MonitoredSensors[SensorElementType.CPUUsage] = element; - break; - - case "CPU PPT SLOW Limit": - { - var reading = (int)Math.Floor(MonitoredSensors[SensorElementType.CPUPower].Value / - element.Value * 100.0d); - if (reading != MonitoredSensors[SensorElementType.PL1].Value) - PowerLimitChanged?.Invoke(PowerType.Slow, reading); - - element.Value = reading; - MonitoredSensors[SensorElementType.PL1] = element; - } - break; - case "CPU PPT FAST Limit": - { - var reading = (int)Math.Floor(MonitoredSensors[SensorElementType.CPUPower].Value / - element.Value * 100.0d); - if (reading != MonitoredSensors[SensorElementType.PL2].Value) - PowerLimitChanged?.Invoke(PowerType.Fast, reading); - - element.Value = reading; - MonitoredSensors[SensorElementType.PL2] = element; - } - break; - } - } - break; - - case SENSOR_READING_TYPE.SENSOR_TYPE_CLOCK: - { - switch (element.szLabelOrig) - { - case "GPU Clock": - case "GPU SoC Clock": // keep me ? - { - var reading = element.Value; - if (reading != MonitoredSensors[SensorElementType.GPUFrequency].Value) - GPUFrequencyChanged?.Invoke(reading); - - MonitoredSensors[SensorElementType.GPUFrequency] = element; - } - break; - - case "Core 0 Clock": - case "Core 1 Clock": - case "Core 2 Clock": - case "Core 3 Clock": - case "Core 4 Clock": - case "Core 5 Clock": - case "Core 6 Clock": - case "Core 7 Clock": - case "Core 8 Clock": - case "Core 9 Clock": - case "Core 10 Clock": - case "Core 11 Clock": - case "Core 12 Clock": - case "Core 13 Clock": - case "Core 14 Clock": - case "Core 15 Clock": - case "Core 16 Clock": - case "Core 17 Clock": - case "Core 18 Clock": // improve me (lol) - { - // we'll keep the highest known frequency right now - if (element.Value > MonitoredSensors[SensorElementType.CPUFrequency].Value) - MonitoredSensors[SensorElementType.CPUFrequency] = element; - } - break; - } - } - break; - - case SENSOR_READING_TYPE.SENSOR_TYPE_VOLT: - { - } - break; - - case SENSOR_READING_TYPE.SENSOR_TYPE_OTHER: - { - } - break; - } - - // move me ! - switch (element.szLabelOrig) - { - case "Remaining Capacity": - MonitoredSensors[SensorElementType.BatteryRemainingCapacity] = element; - break; - case "Charge Level": - MonitoredSensors[SensorElementType.BatteryChargeLevel] = element; - break; - case "Estimated Remaining Time": - MonitoredSensors[SensorElementType.BatteryRemainingTime] = element; - break; - - case "Physical Memory Used": - MonitoredSensors[SensorElementType.PhysicalMemoryUsage] = element; - break; - case "Virtual Memory Committed": - MonitoredSensors[SensorElementType.VirtualMemoryUsage] = element; - break; - - case "GPU D3D Memory Dynamic": - case "GPU Memory Usage": - MonitoredSensors[SensorElementType.GPUMemoryUsage] = element; - break; - } - // Debug.WriteLine("{0}:\t\t{1} {2}\t{3}", sensor.szLabelOrig, sensor.Value, sensor.szUnit, sensor.tReading); - } - } - catch - { - // do something - } - } - - public bool SetProperty(string propertyName, object value) - { - try - { - IniFile settings = new(SettingsPath); - settings.Write(propertyName, Convert.ToString(value), "Settings"); - - return true; - } - catch - { - return false; - } - } - - public bool GetProperty(string propertyName) - { - try - { - IniFile settings = new(SettingsPath); - string value = settings.Read(propertyName, "Settings"); - - if (string.IsNullOrEmpty(value)) - return false; - - return Convert.ToBoolean(Convert.ToInt16(value)); - } - catch - { - return false; - } - } - - public override bool StartProcess() - { - if (!IsInstalled) - return false; - - if (IsRunning) - KillProcess(); - - // (re)set elements - DisposeMemory(); - - // Quiet startup - SetProperty("OpenSystemSummary", 0); - SetProperty("OpenSensors", 1); - SetProperty("MinimalizeMainWnd", 1); - SetProperty("MinimalizeSensors", 1); - SetProperty("MinimalizeSensorsClose", 1); - - // not authorized - // SetProperty("SensorsSM", 1); // Shared Memory Support [12-HOUR LIMIT] - - SetProperty("ShowWelcomeAndProgress", 0); - SetProperty("SensorsOnly", 1); - SetProperty("AutoUpdateBetaDisable", 1); - SetProperty("AutoUpdate", 0); - - // stop watchdog - PlatformWatchdog.Stop(); - - return base.StartProcess(); - } - - public override bool StopProcess() - { - if (IsStarting) - return false; - - KillProcess(); - - return true; - } - - private void DisposeMemory() - { - if (MemoryMapped is not null) - { - MemoryMapped.Dispose(); - MemoryMapped = null; - } - - if (MemoryAccessor is not null) - { - MemoryAccessor.Dispose(); - MemoryAccessor = null; - } - - if (HWiNFOSensors is not null) - HWiNFOSensors = null; - - prevPoll_time = -1; - } - - public override void Dispose() - { - DisposeMemory(); - base.Dispose(); - } - - #region struct - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct SharedMemory - { - public uint dwSignature; - public uint dwVersion; - public uint dwRevision; - public long poll_time; - public uint dwOffsetOfSensorSection; - public uint dwSizeOfSensorElement; - public uint dwNumSensorElements; - public uint dwOffsetOfReadingSection; - public uint dwSizeOfReadingElement; - public uint dwNumReadingElements; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct SensorStructure - { - public uint dwSensorID; - public uint dwSensorInst; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] - public string szSensorNameOrig; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] - public string szSensorNameUser; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct SensorElement - { - public SENSOR_READING_TYPE tReading; - public uint dwSensorIndex; - public uint dwSensorID; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] - public string szLabelOrig; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] - public string szLabelUser; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_UNIT_STRING_LEN)] - public string szUnit; - - public double Value; - public double FanValueMin; - public double FanValueMax; - public double ValueAvg; - } - - public enum SENSOR_READING_TYPE - { - SENSOR_TYPE_NONE, - SENSOR_TYPE_TEMP, - SENSOR_TYPE_VOLT, - SENSOR_TYPE_FAN, - SENSOR_TYPE_CURRENT, - SENSOR_TYPE_POWER, - SENSOR_TYPE_CLOCK, - SENSOR_TYPE_USAGE, - SENSOR_TYPE_OTHER - } - - public class Sensor - { - public ConcurrentDictionary<uint, SensorElement> Elements; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] - public string NameOrig; - - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] - public string NameUser; - } - - #endregion - - #region events - - public event LimitChangedHandler PowerLimitChanged; - public delegate void LimitChangedHandler(PowerType type, int limit); - - public event GPUFrequencyChangedHandler GPUFrequencyChanged; - public delegate void GPUFrequencyChangedHandler(double value); - - #endregion -} \ No newline at end of file +using HandheldCompanion.Managers; +using HandheldCompanion.Processors; +using HandheldCompanion.Utils; +using System; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.IO.MemoryMappedFiles; +using System.Runtime.InteropServices; +using System.Threading; +using System.Timers; +using System.Windows; +<<<<<<< HEAD +using Timer = System.Timers.Timer; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + +namespace HandheldCompanion.Platforms; + +public class HWiNFO : IPlatform +{ + public enum SensorElementType + { + CPUTemperature, + CPUFrequency, + CPUPower, + CPUUsage, + + GPUTemperature, + GPUFrequency, + GPUPower, + GPUUsage, + GPUMemoryUsage, + + PL1, + PL2, + + BatteryChargeLevel, + BatteryRemainingCapacity, + BatteryRemainingTime, + + PhysicalMemoryUsage, + VirtualMemoryUsage + } + + private const string HWiNFO_SHARED_MEM_FILE_NAME = "Global\\HWiNFO_SENS_SM2"; + private const int HWiNFO_SENSORS_STRING_LEN = 128; + private const int HWiNFO_UNIT_STRING_LEN = 16; + private const int MemoryInterval = 1000; + + private readonly Timer MemoryTimer; + + private SharedMemory HWiNFOMemory; + + private ConcurrentDictionary<uint, Sensor> HWiNFOSensors; + private MemoryMappedViewAccessor MemoryAccessor; + + private MemoryMappedFile MemoryMapped; + + public ConcurrentDictionary<SensorElementType, SensorElement> MonitoredSensors = new(); + + private long prevPoll_time = -1; + + public HWiNFO() + { + PlatformType = PlatformType.HWiNFO; + ExpectedVersion = new Version(7, 42, 5030); + Url = "https://www.hwinfo.com/files/hwi_742.exe"; + + Name = "HWiNFO64"; + ExecutableName = RunningName = "HWiNFO64.exe"; + + // check if platform is installed + InstallPath = RegistryUtils.GetString(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\HWiNFO64_is1", + "InstallLocation"); + if (Path.Exists(InstallPath)) + { + // update paths + SettingsPath = Path.Combine(InstallPath, "HWiNFO64.ini"); + ExecutablePath = Path.Combine(InstallPath, ExecutableName); + + // check executable + if (File.Exists(ExecutablePath)) + { + // check executable version + var versionInfo = FileVersionInfo.GetVersionInfo(ExecutablePath); + var CurrentVersion = new Version(versionInfo.ProductMajorPart, versionInfo.ProductMinorPart, + versionInfo.ProductBuildPart); + + if (CurrentVersion < ExpectedVersion) + { + LogManager.LogWarning("HWiNFO is outdated. Please get it from: {0}", Url); + return; + } + + IsInstalled = true; + } + } + + if (!IsInstalled) + { + LogManager.LogWarning("HWiNFO is missing. Please get it from: {0}", Url); + return; + } + + // those are used for computes + MonitoredSensors[SensorElementType.PL1] = new SensorElement(); + MonitoredSensors[SensorElementType.PL2] = new SensorElement(); + MonitoredSensors[SensorElementType.CPUFrequency] = new SensorElement(); + MonitoredSensors[SensorElementType.GPUFrequency] = new SensorElement(); + + // file watcher + if (File.Exists(SettingsPath)) + { + systemWatcher = new(Path.GetDirectoryName(SettingsPath)); + systemWatcher.Filter = "*.ini"; + systemWatcher.EnableRaisingEvents = true; + systemWatcher.Changed += SystemWatcher_Changed; + } + + // our main watchdog to (re)apply requested settings + PlatformWatchdog = new Timer(3000) { Enabled = false }; + PlatformWatchdog.Elapsed += Watchdog_Elapsed; + + // secondary watchdog to (re)populate sensors + MemoryTimer = new Timer(MemoryInterval) { Enabled = false }; + MemoryTimer.Elapsed += (sender, e) => PopulateSensors(); + } + + private void SystemWatcher_Changed(object sender, FileSystemEventArgs e) + { + bool SensorsSM = GetProperty("SensorsSM"); + SystemWatcher_Changed("SensorsSM", SensorsSM); + } + + public override bool Start() + { + // not authorized + // var hasSensorsSM = GetProperty("SensorsSM"); + + // start HWiNFO if not running or Shared Memory is disabled +<<<<<<< HEAD + if (!IsRunning) // || !hasSensorsSM) +======= + var hasSensorsSM = GetProperty("SensorsSM"); + if (!IsRunning || !hasSensorsSM) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + // StopProcess(); + StartProcess(); + } + else + { + // hook into current process + Process.Exited += Process_Exited; + } + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + + return base.Start(); + } + + public override bool Stop(bool kill = false) + { + if (MemoryTimer is not null) + MemoryTimer.Stop(); + + SettingsManager.SettingValueChanged -= SettingsManager_SettingValueChanged; + + return base.Stop(kill); + } + + private void SettingsManager_SettingValueChanged(string name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { + case "OnScreenDisplayRefreshRate": + SetProperty("SensorInterval", Convert.ToInt32(value)); + break; + } + }); + } + + private void Watchdog_Elapsed(object? sender, ElapsedEventArgs e) + { + if (Monitor.TryEnter(updateLock)) + { + try + { + // check shared memory + MemoryMappedFile.OpenExisting(HWiNFO_SHARED_MEM_FILE_NAME, MemoryMappedFileRights.Read); + } + catch + { + // not authorized + // shared memory is disabled, halt process + /* + if (prevPoll_time != -1) + StopProcess(); + */ + + // raise event + // todo: implement a new hint + SetStatus(PlatformStatus.Stalled); + Monitor.Exit(updateLock); + return; + } + + // we couldn't poll HWiNFO, halt process + if (HWiNFOMemory.poll_time == prevPoll_time) + { + // not authorized + // StopProcess(); + + // raise event + // todo: implement a new hint + SetStatus(PlatformStatus.Stalled); + Monitor.Exit(updateLock); + return; + } + + // update poll time + if (HWiNFOMemory.poll_time != 0) + prevPoll_time = HWiNFOMemory.poll_time; + + // reset tentative counter + Tentative = 0; + + // connect to shared memory + if (MemoryMapped is null) + MemoryMapped = MemoryMappedFile.OpenExisting(HWiNFO_SHARED_MEM_FILE_NAME, MemoryMappedFileRights.Read); + + // get accessor + if (MemoryAccessor is null) + MemoryAccessor = + MemoryMapped.CreateViewAccessor(0L, Marshal.SizeOf(typeof(SharedMemory)), MemoryMappedFileAccess.Read); + MemoryAccessor.Read(0L, out HWiNFOMemory); + + // we listed sensors already + if (HWiNFOSensors is null) + { + // (re)set sensors array + HWiNFOSensors = new ConcurrentDictionary<uint, Sensor>(); + + // populate sensors array + GetSensors(); + } + + MemoryTimer.Start(); + + Monitor.Exit(updateLock); + } + } + + public void GetSensors() + { + try + { + for (uint index = 0; index < HWiNFOMemory.dwNumSensorElements; ++index) + using (var viewStream = MemoryMapped.CreateViewStream( + HWiNFOMemory.dwOffsetOfSensorSection + index * HWiNFOMemory.dwSizeOfSensorElement, + HWiNFOMemory.dwSizeOfSensorElement, MemoryMappedFileAccess.Read)) + { + var buffer = new byte[(int)HWiNFOMemory.dwSizeOfSensorElement]; + viewStream.Read(buffer, 0, (int)HWiNFOMemory.dwSizeOfSensorElement); + var gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + var structure = + (SensorStructure)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(SensorStructure)); + gcHandle.Free(); + var sensor = new Sensor + { + NameOrig = structure.szSensorNameOrig, + NameUser = structure.szSensorNameUser, + Elements = new ConcurrentDictionary<uint, SensorElement>() + }; + HWiNFOSensors[index] = sensor; + } + } + catch + { + // do something + } + } + + public void PopulateSensors() + { + if (MemoryMapped is null) + return; + + try + { + for (uint index = 0; index < HWiNFOMemory.dwNumReadingElements; ++index) + using (var viewStream = MemoryMapped.CreateViewStream( + HWiNFOMemory.dwOffsetOfReadingSection + index * HWiNFOMemory.dwSizeOfReadingElement, + HWiNFOMemory.dwSizeOfReadingElement, MemoryMappedFileAccess.Read)) + { + var buffer = new byte[(int)HWiNFOMemory.dwSizeOfReadingElement]; + viewStream.Read(buffer, 0, (int)HWiNFOMemory.dwSizeOfReadingElement); + var gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + + var element = + (SensorElement)Marshal.PtrToStructure(gcHandle.AddrOfPinnedObject(), typeof(SensorElement)); + gcHandle.Free(); + + if (HWiNFOSensors.TryGetValue(element.dwSensorIndex, out var sensor)) + sensor.Elements[element.dwSensorID] = element; + else + continue; + + switch (element.tReading) + { + case SENSOR_READING_TYPE.SENSOR_TYPE_TEMP: + { + switch (element.szLabelOrig) + { + case "CPU Package": + case "CPU (Tctl/Tdie)": + MonitoredSensors[SensorElementType.CPUTemperature] = element; + break; + + case "CPU GT Cores (Graphics)": + case "GPU Temperature": + MonitoredSensors[SensorElementType.GPUTemperature] = element; + break; + } + } + break; + + case SENSOR_READING_TYPE.SENSOR_TYPE_POWER: + { + switch (element.szLabelOrig) + { + case "CPU Package Power": + case "CPU PPT": + MonitoredSensors[SensorElementType.CPUPower] = element; + break; + + case "PL1 Power Limit": + // case "PL1 Power Limit (Static)": + case "PL1 Power Limit (Dynamic)": + { + var reading = (int)Math.Ceiling(element.Value); + if (reading != MonitoredSensors[SensorElementType.PL1].Value) + PowerLimitChanged?.Invoke(PowerType.Slow, reading); + + element.Value = reading; + MonitoredSensors[SensorElementType.PL1] = element; + } + break; + case "PL2 Power Limit": + // case "PL2 Power Limit (Static)": + case "PL2 Power Limit (Dynamic)": + { + var reading = (int)Math.Ceiling(element.Value); + if (reading != MonitoredSensors[SensorElementType.PL2].Value) + PowerLimitChanged?.Invoke(PowerType.Fast, reading); + + element.Value = reading; + MonitoredSensors[SensorElementType.PL2] = element; + } + break; + + case "GPU ASIC Power": + case "GT Cores Power": + case "GPU SoC Power (VDDCR_SOC)": + case "GPU PPT": + MonitoredSensors[SensorElementType.GPUPower] = element; + break; + } + } + break; + + case SENSOR_READING_TYPE.SENSOR_TYPE_USAGE: + { + switch (element.szLabelOrig) + { + case "GPU Utilization": + case "GPU D3D Usage": + MonitoredSensors[SensorElementType.GPUUsage] = element; + break; + + case "Total CPU Usage": + MonitoredSensors[SensorElementType.CPUUsage] = element; + break; + + case "CPU PPT SLOW Limit": + { + var reading = (int)Math.Floor(MonitoredSensors[SensorElementType.CPUPower].Value / + element.Value * 100.0d); + if (reading != MonitoredSensors[SensorElementType.PL1].Value) + PowerLimitChanged?.Invoke(PowerType.Slow, reading); + + element.Value = reading; + MonitoredSensors[SensorElementType.PL1] = element; + } + break; + case "CPU PPT FAST Limit": + { + var reading = (int)Math.Floor(MonitoredSensors[SensorElementType.CPUPower].Value / + element.Value * 100.0d); + if (reading != MonitoredSensors[SensorElementType.PL2].Value) + PowerLimitChanged?.Invoke(PowerType.Fast, reading); + + element.Value = reading; + MonitoredSensors[SensorElementType.PL2] = element; + } + break; + } + } + break; + + case SENSOR_READING_TYPE.SENSOR_TYPE_CLOCK: + { + switch (element.szLabelOrig) + { + case "GPU Clock": + case "GPU SoC Clock": // keep me ? + { + var reading = element.Value; + if (reading != MonitoredSensors[SensorElementType.GPUFrequency].Value) + GPUFrequencyChanged?.Invoke(reading); + + MonitoredSensors[SensorElementType.GPUFrequency] = element; + } + break; + + case "Core 0 Clock": + case "Core 1 Clock": + case "Core 2 Clock": + case "Core 3 Clock": + case "Core 4 Clock": + case "Core 5 Clock": + case "Core 6 Clock": + case "Core 7 Clock": + case "Core 8 Clock": + case "Core 9 Clock": + case "Core 10 Clock": + case "Core 11 Clock": + case "Core 12 Clock": + case "Core 13 Clock": + case "Core 14 Clock": + case "Core 15 Clock": + case "Core 16 Clock": + case "Core 17 Clock": + case "Core 18 Clock": // improve me (lol) + { + // we'll keep the highest known frequency right now + if (element.Value > MonitoredSensors[SensorElementType.CPUFrequency].Value) + MonitoredSensors[SensorElementType.CPUFrequency] = element; + } + break; + } + } + break; + + case SENSOR_READING_TYPE.SENSOR_TYPE_VOLT: + { + } + break; + + case SENSOR_READING_TYPE.SENSOR_TYPE_OTHER: + { + } + break; + } + + // move me ! + switch (element.szLabelOrig) + { + case "Remaining Capacity": + MonitoredSensors[SensorElementType.BatteryRemainingCapacity] = element; + break; + case "Charge Level": + MonitoredSensors[SensorElementType.BatteryChargeLevel] = element; + break; + case "Estimated Remaining Time": + MonitoredSensors[SensorElementType.BatteryRemainingTime] = element; + break; + + case "Physical Memory Used": + MonitoredSensors[SensorElementType.PhysicalMemoryUsage] = element; + break; + case "Virtual Memory Committed": + MonitoredSensors[SensorElementType.VirtualMemoryUsage] = element; + break; + + case "GPU D3D Memory Dynamic": + case "GPU Memory Usage": + MonitoredSensors[SensorElementType.GPUMemoryUsage] = element; + break; + } + // Debug.WriteLine("{0}:\t\t{1} {2}\t{3}", sensor.szLabelOrig, sensor.Value, sensor.szUnit, sensor.tReading); + } + } + catch + { + // do something + } + } + + public bool SetProperty(string propertyName, object value) + { + try + { + IniFile settings = new(SettingsPath); + settings.Write(propertyName, Convert.ToString(value), "Settings"); + + return true; + } + catch + { + return false; + } + } + + public bool GetProperty(string propertyName) + { + try + { + IniFile settings = new(SettingsPath); + string value = settings.Read(propertyName, "Settings"); + + if (string.IsNullOrEmpty(value)) + return false; + + return Convert.ToBoolean(Convert.ToInt16(value)); + } + catch + { + return false; + } + } + + public override bool StartProcess() + { + if (!IsInstalled) + return false; + + if (IsRunning) + KillProcess(); + + // (re)set elements + DisposeMemory(); + + // Quiet startup + SetProperty("OpenSystemSummary", 0); + SetProperty("OpenSensors", 1); + SetProperty("MinimalizeMainWnd", 1); + SetProperty("MinimalizeSensors", 1); + SetProperty("MinimalizeSensorsClose", 1); +<<<<<<< HEAD + + // not authorized + // SetProperty("SensorsSM", 1); // Shared Memory Support [12-HOUR LIMIT] + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + SetProperty("ShowWelcomeAndProgress", 0); + SetProperty("SensorsOnly", 1); + SetProperty("AutoUpdateBetaDisable", 1); + SetProperty("AutoUpdate", 0); + + // stop watchdog + PlatformWatchdog.Stop(); + + return base.StartProcess(); + } + + public override bool StopProcess() + { + if (IsStarting) + return false; + + KillProcess(); + + return true; + } + + private void DisposeMemory() + { + if (MemoryMapped is not null) + { + MemoryMapped.Dispose(); + MemoryMapped = null; + } + + if (MemoryAccessor is not null) + { + MemoryAccessor.Dispose(); + MemoryAccessor = null; + } + + if (HWiNFOSensors is not null) + HWiNFOSensors = null; + + prevPoll_time = -1; + } + + public override void Dispose() + { + DisposeMemory(); + base.Dispose(); + } + + #region struct + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SharedMemory + { + public uint dwSignature; + public uint dwVersion; + public uint dwRevision; + public long poll_time; + public uint dwOffsetOfSensorSection; + public uint dwSizeOfSensorElement; + public uint dwNumSensorElements; + public uint dwOffsetOfReadingSection; + public uint dwSizeOfReadingElement; + public uint dwNumReadingElements; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SensorStructure + { + public uint dwSensorID; + public uint dwSensorInst; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] + public string szSensorNameOrig; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] + public string szSensorNameUser; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SensorElement + { + public SENSOR_READING_TYPE tReading; + public uint dwSensorIndex; + public uint dwSensorID; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] + public string szLabelOrig; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] + public string szLabelUser; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_UNIT_STRING_LEN)] + public string szUnit; + + public double Value; + public double FanValueMin; + public double FanValueMax; + public double ValueAvg; + } + + public enum SENSOR_READING_TYPE + { + SENSOR_TYPE_NONE, + SENSOR_TYPE_TEMP, + SENSOR_TYPE_VOLT, + SENSOR_TYPE_FAN, + SENSOR_TYPE_CURRENT, + SENSOR_TYPE_POWER, + SENSOR_TYPE_CLOCK, + SENSOR_TYPE_USAGE, + SENSOR_TYPE_OTHER + } + + public class Sensor + { + public ConcurrentDictionary<uint, SensorElement> Elements; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] + public string NameOrig; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = HWiNFO_SENSORS_STRING_LEN)] + public string NameUser; + } + + #endregion + + #region events + + public event LimitChangedHandler PowerLimitChanged; + public delegate void LimitChangedHandler(PowerType type, int limit); + + public event GPUFrequencyChangedHandler GPUFrequencyChanged; + public delegate void GPUFrequencyChangedHandler(double value); + + #endregion +} diff --git a/HandheldCompanion/Processors/Processor.cs b/HandheldCompanion/Processors/Processor.cs index 011e1bb8b..0e60648f3 100644 --- a/HandheldCompanion/Processors/Processor.cs +++ b/HandheldCompanion/Processors/Processor.cs @@ -1,165 +1,169 @@ -using HandheldCompanion.Managers; -using System.Collections.Generic; -using System.Timers; - -namespace HandheldCompanion.Processors; - -public enum PowerType -{ - // long - Slow = 0, - Stapm = 1, - Fast = 2, - MsrSlow = 3, - MsrFast = 4 -} - -public class Processor -{ - private static Processor processor; - private static string Manufacturer; - - protected readonly Timer updateTimer = new() { Interval = 3000, AutoReset = true }; - - public bool CanChangeTDP, CanChangeGPU; - protected object IsBusy = new(); - public bool IsInitialized; - - protected Dictionary<PowerType, int> m_Limits = new(); - - protected Dictionary<string, float> m_Misc = new(); - protected Dictionary<PowerType, int> m_PrevLimits = new(); - protected Dictionary<string, float> m_PrevMisc = new(); - protected Dictionary<PowerType, float> m_PrevValues = new(); - - protected Dictionary<PowerType, float> m_Values = new(); - - protected string Name, ProcessorID; - - public Processor() - { - Name = MotherboardInfo.ProcessorName; - ProcessorID = MotherboardInfo.ProcessorID; - Manufacturer = MotherboardInfo.ProcessorManufacturer; - - // write default miscs - m_Misc["gfx_clk"] = m_PrevMisc["gfx_clk"] = 0; - } - - public static Processor GetCurrent() - { - if (processor is not null) - return processor; - - switch (Manufacturer) - { - case "GenuineIntel": - processor = new IntelProcessor(); - break; - case "AuthenticAMD": - processor = new AMDProcessor(); - break; - } - - return processor; - } - - public virtual void Initialize() - { - StatusChanged?.Invoke(CanChangeTDP, CanChangeGPU); - Initialized?.Invoke(); - - // deprecated, we're using HWiNFO to provide values and limits - /* - if (CanChangeTDP) - updateTimer.Start(); - */ - } - - public virtual void Stop() - { - // deprecated, we're using HWiNFO to provide values and limits - /* - if (CanChangeTDP) - updateTimer.Stop(); - */ - } - - public virtual void SetTDPLimit(PowerType type, double limit, int result = 0) - { - LogManager.LogDebug("User requested {0} TDP limit: {1}, error code: {2}", type, (uint)limit, result); - } - - public virtual void SetGPUClock(double clock, int result = 0) - { - /* - * #define ADJ_ERR_FAM_UNSUPPORTED -1 - * #define ADJ_ERR_SMU_TIMEOUT -2 - * #define ADJ_ERR_SMU_UNSUPPORTED -3 - * #define ADJ_ERR_SMU_REJECTED -4 - * #define ADJ_ERR_MEMORY_ACCESS -5 - */ - - LogManager.LogInformation("User requested GPU clock: {0}, error code: {1}", clock, result); - } - - protected virtual void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e) - { - // search for limit changes - foreach (var pair in m_Limits) - { - if (m_PrevLimits[pair.Key] == pair.Value) - continue; - - LimitChanged?.Invoke(pair.Key, pair.Value); - - m_PrevLimits[pair.Key] = pair.Value; - } - - // search for value changes - foreach (var pair in m_Values) - { - if (m_PrevValues[pair.Key] == pair.Value) - continue; - - ValueChanged?.Invoke(pair.Key, pair.Value); - - m_PrevValues[pair.Key] = pair.Value; - } - - // search for misc changes - foreach (var pair in m_Misc) - { - if (m_PrevMisc[pair.Key] == pair.Value) - continue; - - MiscChanged?.Invoke(pair.Key, pair.Value); - - m_PrevMisc[pair.Key] = pair.Value; - } - } - - #region events - - public event LimitChangedHandler LimitChanged; - - public delegate void LimitChangedHandler(PowerType type, int limit); - - public event ValueChangedHandler ValueChanged; - - public delegate void ValueChangedHandler(PowerType type, float value); - - public event GfxChangedHandler MiscChanged; - - public delegate void GfxChangedHandler(string misc, float value); - - public event StatusChangedHandler StatusChanged; - - public delegate void StatusChangedHandler(bool CanChangeTDP, bool CanChangeGPU); - - public event InitializedEventHandler Initialized; - - public delegate void InitializedEventHandler(); - - #endregion +using HandheldCompanion.Managers; +using System.Collections.Generic; +<<<<<<< HEAD +======= +using System.Management; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using System.Timers; + +namespace HandheldCompanion.Processors; + +public enum PowerType +{ + // long + Slow = 0, + Stapm = 1, + Fast = 2, + MsrSlow = 3, + MsrFast = 4 +} + +public class Processor +{ + private static Processor processor; + private static string Manufacturer; + + protected readonly Timer updateTimer = new() { Interval = 3000, AutoReset = true }; + + public bool CanChangeTDP, CanChangeGPU; + protected object IsBusy = new(); + public bool IsInitialized; + + protected Dictionary<PowerType, int> m_Limits = new(); + + protected Dictionary<string, float> m_Misc = new(); + protected Dictionary<PowerType, int> m_PrevLimits = new(); + protected Dictionary<string, float> m_PrevMisc = new(); + protected Dictionary<PowerType, float> m_PrevValues = new(); + + protected Dictionary<PowerType, float> m_Values = new(); + + protected string Name, ProcessorID; + + public Processor() + { + Name = MotherboardInfo.ProcessorName; + ProcessorID = MotherboardInfo.ProcessorID; + Manufacturer = MotherboardInfo.ProcessorManufacturer; + + // write default miscs + m_Misc["gfx_clk"] = m_PrevMisc["gfx_clk"] = 0; + } + + public static Processor GetCurrent() + { + if (processor is not null) + return processor; + + switch (Manufacturer) + { + case "GenuineIntel": + processor = new IntelProcessor(); + break; + case "AuthenticAMD": + processor = new AMDProcessor(); + break; + } + + return processor; + } + + public virtual void Initialize() + { + StatusChanged?.Invoke(CanChangeTDP, CanChangeGPU); + Initialized?.Invoke(); + + // deprecated, we're using HWiNFO to provide values and limits + /* + if (CanChangeTDP) + updateTimer.Start(); + */ + } + + public virtual void Stop() + { + // deprecated, we're using HWiNFO to provide values and limits + /* + if (CanChangeTDP) + updateTimer.Stop(); + */ + } + + public virtual void SetTDPLimit(PowerType type, double limit, int result = 0) + { + LogManager.LogDebug("User requested {0} TDP limit: {1}, error code: {2}", type, (uint)limit, result); + } + + public virtual void SetGPUClock(double clock, int result = 0) + { + /* + * #define ADJ_ERR_FAM_UNSUPPORTED -1 + * #define ADJ_ERR_SMU_TIMEOUT -2 + * #define ADJ_ERR_SMU_UNSUPPORTED -3 + * #define ADJ_ERR_SMU_REJECTED -4 + * #define ADJ_ERR_MEMORY_ACCESS -5 + */ + + LogManager.LogInformation("User requested GPU clock: {0}, error code: {1}", clock, result); + } + + protected virtual void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e) + { + // search for limit changes + foreach (var pair in m_Limits) + { + if (m_PrevLimits[pair.Key] == pair.Value) + continue; + + LimitChanged?.Invoke(pair.Key, pair.Value); + + m_PrevLimits[pair.Key] = pair.Value; + } + + // search for value changes + foreach (var pair in m_Values) + { + if (m_PrevValues[pair.Key] == pair.Value) + continue; + + ValueChanged?.Invoke(pair.Key, pair.Value); + + m_PrevValues[pair.Key] = pair.Value; + } + + // search for misc changes + foreach (var pair in m_Misc) + { + if (m_PrevMisc[pair.Key] == pair.Value) + continue; + + MiscChanged?.Invoke(pair.Key, pair.Value); + + m_PrevMisc[pair.Key] = pair.Value; + } + } + + #region events + + public event LimitChangedHandler LimitChanged; + + public delegate void LimitChangedHandler(PowerType type, int limit); + + public event ValueChangedHandler ValueChanged; + + public delegate void ValueChangedHandler(PowerType type, float value); + + public event GfxChangedHandler MiscChanged; + + public delegate void GfxChangedHandler(string misc, float value); + + public event StatusChangedHandler StatusChanged; + + public delegate void StatusChangedHandler(bool CanChangeTDP, bool CanChangeGPU); + + public event InitializedEventHandler Initialized; + + public delegate void InitializedEventHandler(); + + #endregion } \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.Designer.cs b/HandheldCompanion/Properties/Resources.Designer.cs index b467041ed..366c78a26 100644 --- a/HandheldCompanion/Properties/Resources.Designer.cs +++ b/HandheldCompanion/Properties/Resources.Designer.cs @@ -267,6 +267,15 @@ public static string Administrator { } } + /// <summary> + /// Looks up a localized string similar to This input will operate as a simple joystick. Ideal for laptop and clamshell type handhelds, automatic yaw roll swap based on how device is being held (90 or 180 degree open).. + /// </summary> + public static string AutoRollYawSwapDesc { + get { + return ResourceManager.GetString("AutoRollYawSwapDesc", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to A,B,X,Y. /// </summary> @@ -737,11 +746,73 @@ public static string ControllerPage_SteamControllerMuteDesc { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to Try again. /// </summary> public static string ControllerPage_TryAgain { get { return ResourceManager.GetString("ControllerPage_TryAgain", resourceCulture); +======= + /// Looks up a localized string similar to Steam Controller Settings. + /// </summary> + public static string ControllerPage_SteamControllerSettings { + get { + return ResourceManager.GetString("ControllerPage_SteamControllerSettings", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to You should restart Steam, so that Handheld Companion can automatically adjust Steam Desktop Layout and prevent double input.. + /// </summary> + public static string ControllerPage_SteamNeptuneDesktopAction { + get { + return ResourceManager.GetString("ControllerPage_SteamNeptuneDesktopAction", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to It appears that Steam is already running and Steam Desktop Layout is applied. This may result in double input.. + /// </summary> + public static string ControllerPage_SteamNeptuneDesktopDesc { + get { + return ResourceManager.GetString("ControllerPage_SteamNeptuneDesktopDesc", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Steam Desktop Layout is applied. + /// </summary> + public static string ControllerPage_SteamNeptuneDesktopWarning { + get { + return ResourceManager.GetString("ControllerPage_SteamNeptuneDesktopWarning", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to You might want to uninstall Steam Xbox Controller Enhanced Features Driver. + /// </summary> + public static string ControllerPage_SteamXboxDriversAction { + get { + return ResourceManager.GetString("ControllerPage_SteamXboxDriversAction", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to It appears you have installed Steam Xbox Controller Enhanced Features Driver which are breaking HidHide capacities to hide/unhide your physical controller. + /// </summary> + public static string ControllerPage_SteamXboxDriversDesc { + get { + return ResourceManager.GetString("ControllerPage_SteamXboxDriversDesc", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Steam Xbox Controller Enhanced Features Driver is installed. + /// </summary> + public static string ControllerPage_SteamXboxDriversWarning { + get { + return ResourceManager.GetString("ControllerPage_SteamXboxDriversWarning", resourceCulture); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } @@ -800,6 +871,7 @@ public static string ControllerPage_VibrationStrengthExpl { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to Ambilight Vertical Blackbar detection. /// </summary> public static string DevicePage_AmbilightVerticalBlackBarDetection { @@ -814,6 +886,13 @@ public static string DevicePage_AmbilightVerticalBlackBarDetection { public static string DevicePage_AmbilightVerticalBlackBarDetectionDesc { get { return ResourceManager.GetString("DevicePage_AmbilightVerticalBlackBarDetectionDesc", resourceCulture); +======= + /// Looks up a localized string similar to Please switch your controller off and then on again by pressing and holding the Guide button to finalize pairing.. + /// </summary> + public static string ControllerPage_XInputControllerWarning { + get { + return ResourceManager.GetString("ControllerPage_XInputControllerWarning", resourceCulture); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } @@ -827,6 +906,7 @@ public static string DevicePage_Device { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to Effects. /// </summary> public static string DevicePage_DynamicLighting { @@ -980,6 +1060,8 @@ public static string DevicePage_DynamicLightingDesc { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to Power options. /// </summary> public static string DevicePage_PowerOptions { @@ -989,6 +1071,7 @@ public static string DevicePage_PowerOptions { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to Your device must be restarted in order for the changes to take effect. Would you like to restart now?. /// </summary> public static string Dialog_ForceRestartDesc { @@ -1025,6 +1108,8 @@ public static string Dialog_Yes { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to DIRECTIONAL PAD. /// </summary> public static string DPadPage_DPad { @@ -2286,6 +2371,7 @@ public static string Enum_KeyFlags_Z { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to M2. /// </summary> public static string Enum_LegionController_ButtonFlags_B5 { @@ -2376,6 +2462,8 @@ public static string Enum_LegionGo_ButtonFlags_OEM2 { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to Auto Roll Yaw Swap. /// </summary> public static string Enum_MotionInput_AutoRollYawSwap { @@ -2385,6 +2473,7 @@ public static string Enum_MotionInput_AutoRollYawSwap { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to This input will operate as a simple joystick. Ideal for laptop and clamshell type handhelds, automatic yaw roll swap based on how device is being held (90 or 180 degree open).. /// </summary> public static string Enum_MotionInput_AutoRollYawSwap_Desc { @@ -2394,6 +2483,8 @@ public static string Enum_MotionInput_AutoRollYawSwap_Desc { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to Joystick Camera. /// </summary> public static string Enum_MotionInput_JoystickCamera { @@ -2403,6 +2494,7 @@ public static string Enum_MotionInput_JoystickCamera { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to This input will operate as a simple joystick. This is intended for traditional joystick applications. /// </summary> public static string Enum_MotionInput_JoystickCamera_Desc { @@ -2412,6 +2504,8 @@ public static string Enum_MotionInput_JoystickCamera_Desc { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to Joystick Steering. /// </summary> public static string Enum_MotionInput_JoystickSteering { @@ -2421,6 +2515,7 @@ public static string Enum_MotionInput_JoystickSteering { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to This input will operate as a joystick optimized for controlling a steering wheel or a racing game. /// </summary> public static string Enum_MotionInput_JoystickSteering_Desc { @@ -2430,6 +2525,8 @@ public static string Enum_MotionInput_JoystickSteering_Desc { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to Player Space. /// </summary> public static string Enum_MotionInput_PlayerSpace { @@ -2439,6 +2536,7 @@ public static string Enum_MotionInput_PlayerSpace { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to This input will operate as a joystick optimized for controlling a first or third person camera. /// </summary> public static string Enum_MotionInput_PlayerSpace_Desc { @@ -2448,6 +2546,8 @@ public static string Enum_MotionInput_PlayerSpace_Desc { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to LeftStick. /// </summary> public static string Enum_MotionOuput_LeftStick { @@ -2799,7 +2899,11 @@ public static string Enum_ROGAlly_ButtonFlags_OEM2 { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to M1. +======= + /// Looks up a localized string similar to M1 / M2. +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// </summary> public static string Enum_ROGAlly_ButtonFlags_OEM3 { get { @@ -2808,6 +2912,7 @@ public static string Enum_ROGAlly_ButtonFlags_OEM3 { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to M2. /// </summary> public static string Enum_ROGAlly_ButtonFlags_OEM4 { @@ -2817,6 +2922,8 @@ public static string Enum_ROGAlly_ButtonFlags_OEM4 { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to Automatic. /// </summary> public static string Enum_ServiceStartMode_Automatic { @@ -3249,6 +3356,7 @@ public static string Enum_XInputController_ButtonFlags_Start { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to Core isolation features are turned on. /// </summary> public static string Hint_CoreIsolationCheck { @@ -3447,6 +3555,8 @@ public static string Hint_SteamXboxDriversReadme { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized resource of type System.Drawing.Bitmap. /// </summary> public static System.Drawing.Bitmap icon { @@ -3951,6 +4061,15 @@ public static string InputsHotkey_suspendResumeTaskDesc { } } + /// <summary> + /// Looks up a localized string similar to This input will operate as a simple joystick. This is intended for traditional joystick applications. + /// </summary> + public static string JoystickCameraDesc { + get { + return ResourceManager.GetString("JoystickCameraDesc", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to LEFT JOYSTICK. /// </summary> @@ -3987,6 +4106,15 @@ public static string JoystickPage_Joystick_Right_Buttons { } } + /// <summary> + /// Looks up a localized string similar to This input will operate as a joystick optimized for controlling a steering wheel or a racing game. + /// </summary> + public static string JoystickSteeringDesc { + get { + return ResourceManager.GetString("JoystickSteeringDesc", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to Apply template. /// </summary> @@ -4177,6 +4305,7 @@ public static string MainWindow_Back { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to Yes. /// </summary> public static string MainWindow_ControllerManagementClosePrimary { @@ -4213,6 +4342,8 @@ public static string MainWindow_ControllerManagementCloseTitle { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to Exit. /// </summary> public static string MainWindow_Exit { @@ -4285,6 +4416,7 @@ public static string MainWindow_Navigate { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to Notifications. /// </summary> public static string MainWindow_navNotifications { @@ -4294,6 +4426,8 @@ public static string MainWindow_navNotifications { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to Overlay. /// </summary> public static string MainWindow_navOverlay { @@ -4357,29 +4491,62 @@ public static string MainWindow_Settings { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to Toggle. /// </summary> public static string MainWindow_Toggle { get { return ResourceManager.GetString("MainWindow_Toggle", resourceCulture); +======= + /// Looks up a localized string similar to Yes. + /// </summary> + public static string MainWindow_VirtualControllerForceOrderClosePrimary { + get { + return ResourceManager.GetString("MainWindow_VirtualControllerForceOrderClosePrimary", resourceCulture); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to No notifications yet. /// </summary> public static string NotificationsPage_NothingToSee { get { return ResourceManager.GetString("NotificationsPage_NothingToSee", resourceCulture); +======= + /// Looks up a localized string similar to No. + /// </summary> + public static string MainWindow_VirtualControllerForceOrderCloseSecondary { + get { + return ResourceManager.GetString("MainWindow_VirtualControllerForceOrderCloseSecondary", resourceCulture); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to You have no notifications right now.. /// </summary> public static string NotificationsPage_NothingToSeeDesc { get { return ResourceManager.GetString("NotificationsPage_NothingToSeeDesc", resourceCulture); +======= + /// Looks up a localized string similar to Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?. + /// </summary> + public static string MainWindow_VirtualControllerForceOrderCloseText { + get { + return ResourceManager.GetString("MainWindow_VirtualControllerForceOrderCloseText", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Warning. + /// </summary> + public static string MainWindow_VirtualControllerForceOrderCloseTitle { + get { + return ResourceManager.GetString("MainWindow_VirtualControllerForceOrderCloseTitle", resourceCulture); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } @@ -4906,6 +5073,7 @@ public static string OverlayPage_ZDOPlusController { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to Performance. /// </summary> public static string PerformancePage_Device { @@ -5001,6 +5169,13 @@ public static string PowerProfileTurboDescription { public static string PowerProfileTurboName { get { return ResourceManager.GetString("PowerProfileTurboName", resourceCulture); +======= + /// Looks up a localized string similar to This input will operate as a joystick optimized for controlling a first or third person camera. + /// </summary> + public static string PlayerSpaceDesc { + get { + return ResourceManager.GetString("PlayerSpaceDesc", resourceCulture); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } @@ -6015,6 +6190,7 @@ public static string ProfilesPage_Wrapper_Redirection { } /// <summary> +<<<<<<< HEAD /// Looks up a localized string similar to . /// </summary> public static string ProfilesPage_WrapperDesc { @@ -6024,6 +6200,8 @@ public static string ProfilesPage_WrapperDesc { } /// <summary> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d /// Looks up a localized string similar to Yaw. /// </summary> public static string ProfilesPage_Yaw { @@ -7040,6 +7218,60 @@ public static string SettingsPage_EnableDesktopProfileOnStartDesc { } } + /// <summary> + /// Looks up a localized string similar to Improve virtual controller detection. + /// </summary> + public static string SettingsPage_ForceVirtualControllerOrder { + get { + return ResourceManager.GetString("SettingsPage_ForceVirtualControllerOrder", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Forces the virtual controller to be detected as first controller during Windows start. Enable this if your app/game doesn't detect your inputs (requires device reboot).. + /// </summary> + public static string SettingsPage_ForceVirtualControllerOrderDesc { + get { + return ResourceManager.GetString("SettingsPage_ForceVirtualControllerOrderDesc", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Yes. + /// </summary> + public static string SettingsPage_ForceVirtualControllerOrderPrimary { + get { + return ResourceManager.GetString("SettingsPage_ForceVirtualControllerOrderPrimary", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to No. + /// </summary> + public static string SettingsPage_ForceVirtualControllerOrderSecondary { + get { + return ResourceManager.GetString("SettingsPage_ForceVirtualControllerOrderSecondary", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Your device must be restarted in order for the changes to take effect. Would you like to restart now?. + /// </summary> + public static string SettingsPage_ForceVirtualControllerOrderText { + get { + return ResourceManager.GetString("SettingsPage_ForceVirtualControllerOrderText", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Restart required. + /// </summary> + public static string SettingsPage_ForceVirtualControllerOrderTitle { + get { + return ResourceManager.GetString("SettingsPage_ForceVirtualControllerOrderTitle", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to General options. /// </summary> @@ -7544,6 +7776,42 @@ public static string SettingsPage_UpToDate { } } + /// <summary> + /// Looks up a localized string similar to Yes. + /// </summary> + public static string SettingsPage_VirtualControllerForceOrderDependencyPrimary { + get { + return ResourceManager.GetString("SettingsPage_VirtualControllerForceOrderDependencyPrimary", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to No. + /// </summary> + public static string SettingsPage_VirtualControllerForceOrderDependencySecondary { + get { + return ResourceManager.GetString("SettingsPage_VirtualControllerForceOrderDependencySecondary", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Disabling this setting will also disable "Improve virtual controller detection". Do you want to continue?. + /// </summary> + public static string SettingsPage_VirtualControllerForceOrderDependencyText { + get { + return ResourceManager.GetString("SettingsPage_VirtualControllerForceOrderDependencyText", resourceCulture); + } + } + + /// <summary> + /// Looks up a localized string similar to Warning. + /// </summary> + public static string SettingsPage_VirtualControllerForceOrderDependencyTitle { + get { + return ResourceManager.GetString("SettingsPage_VirtualControllerForceOrderDependencyTitle", resourceCulture); + } + } + /// <summary> /// Looks up a localized string similar to Controller is now cloaked and inputs forwarded to virtual controller. /// </summary> diff --git a/HandheldCompanion/Properties/Resources.de-DE.resx b/HandheldCompanion/Properties/Resources.de-DE.resx index 02d09f096..a7e2f6cb4 100644 --- a/HandheldCompanion/Properties/Resources.de-DE.resx +++ b/HandheldCompanion/Properties/Resources.de-DE.resx @@ -1,3 +1,4 @@ +<<<<<<< HEAD <?xml version="1.0" encoding="utf-8"?> <root> <!-- @@ -2358,4 +2359,2333 @@ Wenn die Bewegungseingabe aktiviert ist, verwenden Sie die ausgewählte(n) Taste <data name="IController_ControllerIndex" xml:space="preserve"> <value>Gamepad: {0}</value> </data> +======= +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="AboutPage_About" xml:space="preserve"> + <value>Über</value> + </data> + <data name="AboutPage_AboutDescription" xml:space="preserve"> + <value>Eine Kombination aus einem Windows-Dienst und einer für das Touch-Overlay optimierten GUI, um Ihr Handheld-Gaming-Computer-Erlebnis zu verbessern. Zu den Funktionen gehören: Bewegungssteuerung (Gyrometer), virtuelle Controller-Simulation, Quick-Tools-Overlay, virtuelle Touchpads, 3D-Controller-Modell, anwendungsbasiertes Profileinstellungssystem. Handheld Companion basiert auf dem ViGEmBus-Treiber und den ViGEmClient-Bibliotheken sowie dem HidHide-Filtertreiber für den Kernel-Modus. Die Algorithmen zur Bewegungssteuerung basieren auf der Arbeit von Jibbsmart und den verfügbaren Informationen im GyroWiki. </value> + </data> + <data name="AboutPage_Author" xml:space="preserve"> + <value>Autor</value> + </data> + <data name="AboutPage_Contributors" xml:space="preserve"> + <value>Mitwirkende</value> + </data> + <data name="AboutPage_Description" xml:space="preserve"> + <value>Beschreibung</value> + </data> + <data name="AboutPage_Donate" xml:space="preserve"> + <value>Spenden</value> + </data> + <data name="AboutPage_Gyrometer" xml:space="preserve"> + <value>Gyrometer</value> + </data> + <data name="AboutPage_Inclinometer" xml:space="preserve"> + <value>Neigungssensor</value> + </data> + <data name="AboutPage_NotApplicable" xml:space="preserve"> + <value>N/A</value> + </data> + <data name="AboutPage_RelatedLinks" xml:space="preserve"> + <value>Weitere Links</value> + </data> + <data name="AboutPage_SensorExternal" xml:space="preserve"> + <value>Extern</value> + </data> + <data name="AboutPage_SensorInternal" xml:space="preserve"> + <value>Intern</value> + </data> + <data name="AboutPage_SensorName" xml:space="preserve"> + <value>Sensor-Name</value> + </data> + <data name="AboutPage_SensorSpecification" xml:space="preserve"> + <value>Sensor-Spezifikation</value> + </data> + <data name="AboutPage_Service" xml:space="preserve"> + <value>Service</value> + </data> + <data name="AboutPage_SourceCode" xml:space="preserve"> + <value>Quellcode</value> + </data> + <data name="AboutPage_Version" xml:space="preserve"> + <value>Version</value> + </data> + <data name="AboutPage_Wiki" xml:space="preserve"> + <value>Wiki</value> + </data> + <data name="Administrator" xml:space="preserve"> + <value>Administrator</value> + </data> + <data name="ControllerPage_CloakDevice" xml:space="preserve"> + <value>Verbundene Controller verstecken</value> + </data> + <data name="ControllerPage_CloakDeviceDesc" xml:space="preserve"> + <value>Änderung der Sichtbarkeit des physischen Controllers für andere Anwendungen</value> + </data> + <data name="ControllerPage_Connect" xml:space="preserve"> + <value>Verbinden</value> + </data> + <data name="ControllerPage_Controller" xml:space="preserve"> + <value>Controller</value> + </data> + <data name="ControllerPage_DeviceCloaking" xml:space="preserve"> + <value>Controller verstecken</value> + </data> + <data name="ControllerPage_DeviceSettings" xml:space="preserve"> + <value>Controller-Einstellungen</value> + </data> + <data name="ControllerPage_Disconnect" xml:space="preserve"> + <value>Getrennt</value> + </data> + <data name="ControllerPage_InputDevices" xml:space="preserve"> + <value>Eingabegeräte</value> + </data> + <data name="ControllerPage_UncloakOnClose" xml:space="preserve"> + <value>Controller beim Schließen anzeigen</value> + </data> + <data name="ControllerPage_UncloakOnCloseDesc" xml:space="preserve"> + <value>Wiederherstellung der Sichtbarkeit aller physischen Steuerungen für andere Anwendungen bei Beendigung des Dienstes</value> + </data> + <data name="ControllerPage_VibrationStrength" xml:space="preserve"> + <value>Vibrations-Intensität</value> + </data> + <data name="ControllerPage_VibrationStrengthExpl" xml:space="preserve"> + <value>Vibrationsstärke des Controllers ändern</value> + </data> + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\icon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="InputsHotkey_decreaseTDP" xml:space="preserve"> + <value>Thermische Leistungsbregrenzung (TDP) verringern</value> + </data> + <data name="InputsHotkey_decreaseTDPDesc" xml:space="preserve"> + <value>TDP um einen Watt senken</value> + </data> + <data name="InputsHotkey_fallbackInput" xml:space="preserve"> + <value>Drücken, um die Hotkey-Eingabe zu definieren</value> + </data> + <data name="InputsHotkey_fallbackOutput" xml:space="preserve"> + <value>Drücken, um die Hotkey-Ausgabe zu definieren</value> + </data> + <data name="InputsHotkey_increaseTDP" xml:space="preserve"> + <value>Thermische Leistungsgrenze (TDP) erhöhen</value> + </data> + <data name="InputsHotkey_increaseTDPDesc" xml:space="preserve"> + <value>Erhöhen Sie den TDP des Systems oder des aktuell verwendeten Profils um ein Watt</value> + </data> + <data name="InputsHotkey_overlayGamepad" xml:space="preserve"> + <value>3D-Controller anzeigen</value> + </data> + <data name="InputsHotkey_overlayGamepadDesc" xml:space="preserve"> + <value>Ändern des 3D-Hotkeys durch Drücken eines Hotkeys oder einer speziellen Taste</value> + </data> + <data name="InputsHotkey_overlayTrackpads" xml:space="preserve"> + <value>Virtuelle Trackpads anzeigen</value> + </data> + <data name="InputsHotkey_overlayTrackpadsDesc" xml:space="preserve"> + <value>Hotkey durch Drücken eines Buttons oder einer speziellen Taste ändern</value> + </data> + <data name="InputsHotkey_quickTools" xml:space="preserve"> + <value>Fenster für QuickTools aufrufen</value> + </data> + <data name="InputsHotkey_quickToolsDesc" xml:space="preserve"> + <value>Hotkey durch Drücken einer Schaltfläche oder einer speziellen Taste ändern</value> + </data> + <data name="InputsHotkey_shortcutCustom" xml:space="preserve"> + <value>Benutzerdefinierte Verknüpfung</value> + </data> + <data name="InputsHotkey_shortcutCustomDesc" xml:space="preserve"> + <value>Hotkey durch Drücken einer Schaltfläche oder einer speziellen Taste ändern</value> + </data> + <data name="InputsHotkey_shortcutDesktop" xml:space="preserve"> + <value>Desktop anzeigen und ausblenden</value> + </data> + <data name="InputsHotkey_shortcutDesktopDesc" xml:space="preserve"> + <value>Drücke diese Tasten: Windows + D</value> + </data> + <data name="InputsHotkey_shortcutESC" xml:space="preserve"> + <value>Escape</value> + </data> + <data name="InputsHotkey_shortcutESCDesc" xml:space="preserve"> + <value>Drücke diese Tasten: Escape</value> + </data> + <data name="InputsHotkey_shortcutExpand" xml:space="preserve"> + <value>Zwischen Fenster und Vollbild wechseln</value> + </data> + <data name="InputsHotkey_shortcutExpandDesc" xml:space="preserve"> + <value>Drücke diese Tasten: Alt + Enter</value> + </data> + <data name="InputsHotkey_shortcutGuide" xml:space="preserve"> + <value>XBOX- oder PS-Taste</value> + </data> + <data name="InputsHotkey_shortcutGuideDesc" xml:space="preserve"> + <value>XBOX- oder PS-Taste simulieren</value> + </data> + <data name="InputsHotkey_shortcutKeyboard" xml:space="preserve"> + <value>Bildschirmtastatur anzeigen</value> + </data> + <data name="InputsHotkey_shortcutKeyboardDesc" xml:space="preserve"> + <value>Hotkey durch Drücken einer Schaltfläche oder einer speziellen Taste ändern</value> + </data> + <data name="InputsHotkey_shortcutKillApp" xml:space="preserve"> + <value>Beenden der Anwendung erzwingen</value> + </data> + <data name="InputsHotkey_shortcutKillAppDesc" xml:space="preserve"> + <value>Hotkey durch Drücken einer Schaltfläche oder einer speziellen Taste ändern</value> + </data> + <data name="InputsHotkey_shortcutMainwindow" xml:space="preserve"> + <value>Hauptfenster anzeigen und ausblenden</value> + </data> + <data name="InputsHotkey_shortcutMainwindowDesc" xml:space="preserve"> + <value>Hotkey durch Drücken einer Schaltfläche oder einer speziellen Taste ändern</value> + </data> + <data name="InputsHotkey_shortcutTaskManager" xml:space="preserve"> + <value>Task-Manager öffnen</value> + </data> + <data name="InputsHotkey_shortcutTaskManagerDesc" xml:space="preserve"> + <value>Drücke diese Tasten: Strg + Shift + Esc</value> + </data> + <data name="InputsHotkey_shortcutTaskview" xml:space="preserve"> + <value>Aufgabenansicht öffnen</value> + </data> + <data name="InputsHotkey_shortcutTaskviewDesc" xml:space="preserve"> + <value>Drücke diese Tasten: Windows + Tab</value> + </data> + <data name="InputsHotkey_suspendResumeTask" xml:space="preserve"> + <value>Suspendierung schalten</value> + </data> + <data name="InputsHotkey_suspendResumeTaskDesc" xml:space="preserve"> + <value>Unterbrechen oder Fortsetzen der Vordergrundanwendung</value> + </data> + <data name="MainWindow_HandheldCompanion" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="MainWindow_navAbout" xml:space="preserve"> + <value>Über</value> + </data> + <data name="MainWindow_navController" xml:space="preserve"> + <value>Controller</value> + </data> + <data name="MainWindow_navOverlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="MainWindow_navProfiles" xml:space="preserve"> + <value>Profile</value> + </data> + <data name="MainWindow_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="MainWindow_Settings" xml:space="preserve"> + <value>Einstellungen</value> + </data> + <data name="OverlayPage_8BitDoLite2Controller" xml:space="preserve"> + <value>8BitDo Lite 2</value> + </data> + <data name="OverlayPage_Alignment" xml:space="preserve"> + <value>Ausrichtung</value> + </data> + <data name="OverlayPage_AlignmentDesc" xml:space="preserve"> + <value>Ausrichtung des 3D-Controller-Overlays ändern</value> + </data> + <data name="OverlayPage_AlignmentTrackpadDesc" xml:space="preserve"> + <value>Trackpad-Overlay-Ausrichtung ändern</value> + </data> + <data name="OverlayPage_AlwaysOnTop" xml:space="preserve"> + <value>Immer ganz oben</value> + </data> + <data name="OverlayPage_AlwaysOnTopDesc" xml:space="preserve"> + <value>Wenn umgeschaltet, bleibt das 3D-Controller-Overlay über anderen Fenstern.</value> + </data> + <data name="OverlayPage_BackButton" xml:space="preserve"> + <value>Zurück</value> + </data> + <data name="OverlayPage_CameraAngle" xml:space="preserve"> + <value>Kamera-Ausrichtung</value> + </data> + <data name="OverlayPage_CameraAngleDesc" xml:space="preserve"> + <value>Ändern Sie das Verhalten des 3D-Controller-Overlay-Modells für die Ausrichtung der Kamera</value> + </data> + <data name="OverlayPage_CameraAnglePitch" xml:space="preserve"> + <value>Statische Kameraneigung</value> + </data> + <data name="OverlayPage_CameraAnglePitchDesc" xml:space="preserve"> + <value>Ändern Sie den Winkel, in Grad</value> + </data> + <data name="OverlayPage_Color" xml:space="preserve"> + <value>Hintergrundfarbe</value> + </data> + <data name="OverlayPage_ColorDesc" xml:space="preserve"> + <value>Hintergrundfarbe des 3D-Controller-Overlays ändern</value> + </data> + <data name="OverlayPage_ControllerOptions" xml:space="preserve"> + <value>Controller-Optionen</value> + </data> + <data name="OverlayPage_DualSenseController" xml:space="preserve"> + <value>PlayStation DualSense</value> + </data> + <data name="OverlayPage_EmulatedController" xml:space="preserve"> + <value>Emulierter Controller</value> + </data> + <data name="OverlayPage_FaceCamera" xml:space="preserve"> + <value>Kamera-Ausrichtung</value> + </data> + <data name="OverlayPage_FaceCameraDesc" xml:space="preserve"> + <value>3D-Modell dreht sich langsam in Richtung Kamera als Standardposition</value> + </data> + <data name="OverlayPage_Listening" xml:space="preserve"> + <value>Warten...</value> + </data> + <data name="OverlayPage_MachenikeHG510Controller" xml:space="preserve"> + <value>MACHENIKE HG510</value> + </data> + <data name="OverlayPage_MainTrigger" xml:space="preserve"> + <value>Hauptschalter</value> + </data> + <data name="OverlayPage_MainTriggerDesc" xml:space="preserve"> + <value>3D-Controller-Overlay-Hauptschalter ändern</value> + </data> + <data name="OverlayPage_MotionActivated" xml:space="preserve"> + <value>Bewegung</value> + </data> + <data name="OverlayPage_MotionActivatedDesc" xml:space="preserve"> + <value>Das Modell bewegt sich entsprechend den Bewegungen des Benutzers auf der Grundlage von Sensorinformationen</value> + </data> + <data name="OverlayPage_N64Controller" xml:space="preserve"> + <value>N64</value> + </data> + <data name="OverlayPage_OEMController" xml:space="preserve"> + <value>OEM-Controller</value> + </data> + <data name="OverlayPage_Opacity" xml:space="preserve"> + <value>Deckkraft</value> + </data> + <data name="OverlayPage_OpacityControllerDesc" xml:space="preserve"> + <value>Deckkraft der 3D-Controller-Überlagerung ändern</value> + </data> + <data name="OverlayPage_OpacityTrackpadDesc" xml:space="preserve"> + <value>Trackpad-Overlay-Transparent ändern</value> + </data> + <data name="OverlayPage_Overlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="OverlayPage_OverlayModel" xml:space="preserve"> + <value>Overlay-Modell</value> + </data> + <data name="OverlayPage_OverlayModelDesc" xml:space="preserve"> + <value>3D-Overlay-Modell ändern</value> + </data> + <data name="OverlayPage_OverlayPreview" xml:space="preserve"> + <value>Overlay-Vorschau</value> + </data> + <data name="OverlayPage_RenderSettings" xml:space="preserve"> + <value>Render-Einstellungen</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasing" xml:space="preserve"> + <value>Anti-Aliasing</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasingDesc" xml:space="preserve"> + <value>Ändern des Rendering-Anti-Aliasing-Status</value> + </data> + <data name="OverlayPage_RenderSettingsDesc" xml:space="preserve"> + <value>Ändern Sie die Rendereinstellungen Ihres stationären 3D-Overlay-Controller-Modells</value> + </data> + <data name="OverlayPage_RenderSettingsFramerate" xml:space="preserve"> + <value>Aktualisierungsrate</value> + </data> + <data name="OverlayPage_RenderSettingsFramerateDesc" xml:space="preserve"> + <value>Ändern Sie die Render-Update-Rate, in Update pro Sekunde</value> + </data> + <data name="OverlayPage_Size" xml:space="preserve"> + <value>Größe</value> + </data> + <data name="OverlayPage_SizeDesc" xml:space="preserve"> + <value>Größe des 3D-Controller-Overlays ändern</value> + </data> + <data name="OverlayPage_SizeOverlayDesc" xml:space="preserve"> + <value>Trackpad-Overlay-Größe ändern</value> + </data> + <data name="OverlayPage_StartButton" xml:space="preserve"> + <value>Start</value> + </data> + <data name="OverlayPage_ToyController" xml:space="preserve"> + <value>Fisher-Price Controller</value> + </data> + <data name="OverlayPage_TrackpadsOptions" xml:space="preserve"> + <value>Optionen für Trackpads</value> + </data> + <data name="OverlayPage_XboxOneController" xml:space="preserve"> + <value>Xbox One</value> + </data> + <data name="OverlayPage_ZDOPlusController" xml:space="preserve"> + <value>ZD O+</value> + </data> + <data name="Overlay_Overlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="ProcessEx_processResume" xml:space="preserve"> + <value>Fortsetzen</value> + </data> + <data name="ProcessEx_processSuspend" xml:space="preserve"> + <value>Pausieren</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplier" xml:space="preserve"> + <value>Beschleunigungssensor-Multiplikator</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplierDesc" xml:space="preserve"> + <value>Ändern Sie den vom System gemeldeten Beschleunigungssensorwert</value> + </data> + <data name="ProfilesPage_AdditionalSettings" xml:space="preserve"> + <value>Zusätzliche Einstellungen</value> + </data> + <data name="ProfilesPage_AntiDeadzone" xml:space="preserve"> + <value>Anti-Deadzone</value> + </data> + <data name="ProfilesPage_AntiDeadzoneDesc" xml:space="preserve"> + <value>Ändern Sie die Anti-Deadzone im Spiel, in Prozent</value> + </data> + <data name="ProfilesPage_AntiDeadzoneUnitPercentage" xml:space="preserve"> + <value> %</value> + </data> + <data name="ProfilesPage_AreYouSureDelete1" xml:space="preserve"> + <value>Sind Sie sicher, dass Sie dies löschen wollen?</value> + </data> + <data name="ProfilesPage_AreYouSureDelete2" xml:space="preserve"> + <value>Dieses Element wird sofort gelöscht. Sie können diese Aktion nicht rückgängig machen.</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite1" xml:space="preserve"> + <value>Sind Sie sicher, dass Sie das Profil überschreiben wollen?</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite2" xml:space="preserve"> + <value>{0} wird überschrieben. Sie können diese Aktion nicht rückgängig machen.</value> + </data> + <data name="ProfilesPage_BoostPower" xml:space="preserve"> + <value>Schub-Begrenzung</value> + </data> + <data name="ProfilesPage_BoostPowerDesc" xml:space="preserve"> + <value>Ändere TDP-Boost-Begrenzung</value> + </data> + <data name="ProfilesPage_Cancel" xml:space="preserve"> + <value>Abbrechen</value> + </data> + <data name="ProfilesPage_CreateNewProfile" xml:space="preserve"> + <value>Neues Profil erstellen</value> + </data> + <data name="ProfilesPage_Delete" xml:space="preserve"> + <value>Löschen</value> + </data> + <data name="ProfilesPage_DeleteProfile" xml:space="preserve"> + <value>Profil löschen</value> + </data> + <data name="ProfilesPage_EnableProfile" xml:space="preserve"> + <value>Profil aktivieren</value> + </data> + <data name="ProfilesPage_EnableProfileDesc" xml:space="preserve"> + <value>Das Profil wird automatisch angewendet, wenn die zugehörige Anwendung erkannt wird.</value> + </data> + <data name="ProfilesPage_GlobalSettings" xml:space="preserve"> + <value>Globale Einstellungen</value> + </data> + <data name="ProfilesPage_GlobalSettingsDesc" xml:space="preserve"> + <value>Ändern Sie die globalen Profileinstellungen</value> + </data> + <data name="ProfilesPage_GyrometerMultiplier" xml:space="preserve"> + <value>Gyrometer-Multiplikator</value> + </data> + <data name="ProfilesPage_GyrometerMultiplierDesc" xml:space="preserve"> + <value>Ändern Sie den vom System gemeldeten Gyrometerwert</value> + </data> + <data name="ProfilesPage_GyroSteeringAxis" xml:space="preserve"> + <value>Gyro-Lenkachse</value> + </data> + <data name="ProfilesPage_GyroSteeringAxisDesc" xml:space="preserve"> + <value>Zur Steuerung der horizontalen Bewegung des Controllers können Sie entweder die Gier- oder die Rollachse verwenden</value> + </data> + <data name="ProfilesPage_InvertHorizontalAxis" xml:space="preserve"> + <value>Horizontale Achse invertieren</value> + </data> + <data name="ProfilesPage_InvertVerticalAxis" xml:space="preserve"> + <value>Vertikale Achse invertieren</value> + </data> + <data name="ProfilesPage_MotionControlSettings" xml:space="preserve"> + <value>Einstellungen der Bewegungssteuerung</value> + </data> + <data name="ProfilesPage_MotionControlSettingsDesc" xml:space="preserve"> + <value>Ändern Sie die globalen Bewegungssteuerungseinstellungen</value> + </data> + <data name="ProfilesPage_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="ProfilesPage_PowerSettings" xml:space="preserve"> + <value>Energie-Einstellungen</value> + </data> + <data name="ProfilesPage_PowerSettingsDesc" xml:space="preserve"> + <value>Ändern Sie die Energieeinstellungen</value> + </data> + <data name="ProfilesPage_ProfileDetails" xml:space="preserve"> + <value>Profil-Details</value> + </data> + <data name="ProfilesPage_ProfileName" xml:space="preserve"> + <value>Profilname</value> + </data> + <data name="ProfilesPage_ProfilePath" xml:space="preserve"> + <value>Profilpfad</value> + </data> + <data name="ProfilesPage_Profiles" xml:space="preserve"> + <value>Profile</value> + </data> + <data name="ProfilesPage_ProfileSelection" xml:space="preserve"> + <value>Profilauswahl</value> + </data> + <data name="ProfilesPage_ProfileSelectionDesc" xml:space="preserve"> + <value>Wählen Sie das Profil, das Sie bearbeiten möchten</value> + </data> + <data name="ProfilesPage_ProfileSettings" xml:space="preserve"> + <value>Profileinstellungen</value> + </data> + <data name="ProfilesPage_ProfileUpdated1" xml:space="preserve"> + <value>Profil aktualisieren</value> + </data> + <data name="ProfilesPage_ProfileUpdated2" xml:space="preserve"> + <value>wurden aktualisiert.</value> + </data> + <data name="ProfilesPage_Roll" xml:space="preserve"> + <value>Roll</value> + </data> + <data name="ProfilesPage_SensitivityX" xml:space="preserve"> + <value>X</value> + </data> + <data name="ProfilesPage_SensitivityY" xml:space="preserve"> + <value>Y</value> + </data> + <data name="ProfilesPage_StyleofInput" xml:space="preserve"> + <value>Eingabeart</value> + </data> + <data name="ProfilesPage_StyleofInputTooltip" xml:space="preserve"> + <value>Die physikalischen Controllereingaben können so programmiert werden, dass sie sich wie verschiedene Gerätetypen verhalten</value> + </data> + <data name="ProfilesPage_StyleofOutput" xml:space="preserve"> + <value>Ausgabegerät</value> + </data> + <data name="ProfilesPage_StyleofOutputTooltip" xml:space="preserve"> + <value>Wählen Sie das Gerät, das die Bewegungsbefehle empfangen soll</value> + </data> + <data name="ProfilesPage_SustainedPower" xml:space="preserve"> + <value>Anhaltende Leistungsgrenze</value> + </data> + <data name="ProfilesPage_SustainedPowerDesc" xml:space="preserve"> + <value>Änderung der Dauerwärmeleistungsgrenze</value> + </data> + <data name="ProfilesPage_TDPOverride" xml:space="preserve"> + <value>Thermische Leistung (TDP) überschreiben</value> + </data> + <data name="ProfilesPage_TDPOverrideDesc" xml:space="preserve"> + <value>Überschreiben Sie das thermal power limit des Prozessors</value> + </data> + <data name="ProfilesPage_UMCEnable" xml:space="preserve"> + <value>Aktivieren der universellen Bewegungssteuerung</value> + </data> + <data name="ProfilesPage_UMCMotionOff" xml:space="preserve"> + <value>Deaktiviert, einschalten mit Taste(n)</value> + </data> + <data name="ProfilesPage_UMCMotionOn" xml:space="preserve"> + <value>Aktiviert, ausschalten mit Taste(n)</value> + </data> + <data name="ProfilesPage_UMCMotionOnOff" xml:space="preserve"> + <value>Bewegungsaktivierung</value> + </data> + <data name="ProfilesPage_UMCMotionOnOffDesc" xml:space="preserve"> + <value>Wenn die Bewegungseingabe deaktiviert ist, verwenden Sie die ausgewählte(n) Taste(n), um die Bewegung zu aktivieren, +Wenn die Bewegungseingabe aktiviert ist, verwenden Sie die ausgewählte(n) Taste(n), um die Bewegung zu deaktivieren.</value> + </data> + <data name="ProfilesPage_UMCSelectionRightLeftDesc" xml:space="preserve"> + <value>Wählen Sie das Gerät, das die Bewegungsbefehle empfangen soll</value> + </data> + <data name="ProfilesPage_UMCSettings" xml:space="preserve"> + <value>Universelle Einstellungen für die Bewegungssteuerung</value> + </data> + <data name="ProfilesPage_UMCSettingsDesc" xml:space="preserve"> + <value>Übersetzen von Gerätebewegungen in Controllereingaben</value> + </data> + <data name="ProfilesPage_UpdateProfile" xml:space="preserve"> + <value>Profil aktualisieren</value> + </data> + <data name="ProfilesPage_Whitelist" xml:space="preserve"> + <value>Der Anwendung den Zugriff auf den physischen Controller des Geräts ermöglichen</value> + </data> + <data name="ProfilesPage_Wrapper" xml:space="preserve"> + <value>Erweiterte Kompatibilität (XInputPlus)</value> + </data> + <data name="ProfilesPage_Yaw" xml:space="preserve"> + <value>Gier</value> + </data> + <data name="ProfilesPage_Yes" xml:space="preserve"> + <value>Ja</value> + </data> + <data name="QuickPerformancePage_GPUControl" xml:space="preserve"> + <value>Manuelle GPU-Takteinstellungen</value> + </data> + <data name="QuickPerformancePage_GPUControlDesc" xml:space="preserve"> + <value>Setzt die GPU auf einen festen Takt</value> + </data> + <data name="QuickPerformancePage_GPUUnit" xml:space="preserve"> + <value> MHz</value> + </data> + <data name="QuickPerformancePage_PowerMode" xml:space="preserve"> + <value>Leistungsmodus</value> + </data> + <data name="QuickPerformancePage_PowerModeBalanced" xml:space="preserve"> + <value>Ausgeglichen</value> + </data> + <data name="QuickPerformancePage_PowerModeDesc" xml:space="preserve"> + <value>Optimieren Sie Ihr Gerät auf der Grundlage von Stromverbrauch und Leistung</value> + </data> + <data name="QuickPerformancePage_PowerModeEfficiency" xml:space="preserve"> + <value>Effizienz</value> + </data> + <data name="QuickPerformancePage_PowerModePerformance" xml:space="preserve"> + <value>Leistung</value> + </data> + <data name="QuickPerformancePage_TDPBoost" xml:space="preserve"> + <value>Schub</value> + </data> + <data name="QuickPerformancePage_TDPLimit" xml:space="preserve"> + <value>Thermische Leistungsgrenze (TDP)</value> + </data> + <data name="QuickPerformancePage_TDPLimitDesc" xml:space="preserve"> + <value>Begrenze Prozessorleistung für weniger Gesamtleistung</value> + </data> + <data name="QuickPerformancePage_TDPOverWrittenWarning" xml:space="preserve"> + <value>Thermal Power Limit wird von einem Profil überschrieben</value> + </data> + <data name="QuickPerformancePage_TDPSustained" xml:space="preserve"> + <value>Anhaltend</value> + </data> + <data name="QuickPerformancePage_TDPUnitWatt" xml:space="preserve"> + <value> W</value> + </data> + <data name="QuickProfilesPage_Create" xml:space="preserve"> + <value>Profil erstellen</value> + </data> + <data name="ServiceDescription" xml:space="preserve"> + <value>Bietet Gyroskop- und Beschleunigungssensor-Unterstützung für Windows-Handheld-Gamingcomputer über einen virtuellen Controller. Wenn der Dienst aktiviert ist, wird der eingebettete Controller für Anwendungen außerhalb der Whitelist getarnt. Wenn der Dienst deaktiviert ist, wird der eingebettete Controller enttarnt und der virtuelle Controller deaktiviert.</value> + </data> + <data name="ServiceName" xml:space="preserve"> + <value>Controller-Service</value> + </data> + <data name="SettingsPage_AppLanguage" xml:space="preserve"> + <value>Sprache</value> + </data> + <data name="SettingsPage_AppLanguageDesc" xml:space="preserve"> + <value>Die Anwendungssprache</value> + </data> + <data name="SettingsPage_AppLanguageWarning" xml:space="preserve"> + <value>Neustart erforderlich</value> + </data> + <data name="SettingsPage_AppLanguageWarningDesc" xml:space="preserve"> + <value>Damit die Änderungen wirksam werden, starten Sie bitte die Anwendung neu</value> + </data> + <data name="SettingsPage_AppTheme" xml:space="preserve"> + <value>Anwendungs-Theme</value> + </data> + <data name="SettingsPage_AppThemeDesc" xml:space="preserve"> + <value>Das Anwednungs-Theme, light oder dark mode</value> + </data> + <data name="SettingsPage_AutoStartApp" xml:space="preserve"> + <value>Anwendung mit Windows starten</value> + </data> + <data name="SettingsPage_AutoStartAppDesc" xml:space="preserve"> + <value>Die Anwendung wird automatisch gestartet, wenn ich mich bei Windows anmelde</value> + </data> + <data name="SettingsPage_Backdrop" xml:space="preserve"> + <value>Acrylhintergrund verwenden</value> + </data> + <data name="SettingsPage_BackdropAcrylic" xml:space="preserve"> + <value>Acryl</value> + </data> + <data name="SettingsPage_BackdropDesc" xml:space="preserve"> + <value>Für die Anwendung wird ein Acrylhintergrund verwendet</value> + </data> + <data name="SettingsPage_BackdropMica" xml:space="preserve"> + <value>Glimmer</value> + </data> + <data name="SettingsPage_BackdropNone" xml:space="preserve"> + <value>Keine</value> + </data> + <data name="SettingsPage_BackdropTabbed" xml:space="preserve"> + <value>Tabbed</value> + </data> + <data name="SettingsPage_CheckForUpdates" xml:space="preserve"> + <value>Nach Updates suchen</value> + </data> + <data name="SettingsPage_CloseMinimizes" xml:space="preserve"> + <value>Minimiert schließen</value> + </data> + <data name="SettingsPage_CloseMinimizesDesc" xml:space="preserve"> + <value>Die Anwendung wird minimiert und nicht geschlossen</value> + </data> + <data name="SettingsPage_Download" xml:space="preserve"> + <value>Download</value> + </data> + <data name="SettingsPage_DownloadingPercentage" xml:space="preserve"> + <value>Downloaden -</value> + </data> + <data name="SettingsPage_EcoQoS" xml:space="preserve"> + <value>EcoQoS</value> + </data> + <data name="SettingsPage_EcoQoSDesc" xml:space="preserve"> + <value>Inaktive oder im Hintergrund ablaufende Prozesse und Anwendungen drosseln, um die Energieeffizienz zu verbessern</value> + </data> + <data name="SettingsPage_GeneralOptions" xml:space="preserve"> + <value>Allgemeine Optionen</value> + </data> + <data name="SettingsPage_InstallNow" xml:space="preserve"> + <value>Jetzt installieren</value> + </data> + <data name="SettingsPage_LastChecked" xml:space="preserve"> + <value>Zuletzt geprüft: </value> + </data> + <data name="SettingsPage_NotificationOptions" xml:space="preserve"> + <value>Optionen für Benachrichtigungen</value> + </data> + <data name="SettingsPage_OpenAppBackground" xml:space="preserve"> + <value>Anwendung im Hintergrund öffnen</value> + </data> + <data name="SettingsPage_OpenAppBackgroundDesc" xml:space="preserve"> + <value>Die Anwendung wird minimiert gestartet und erscheint in der Taskleiste</value> + </data> + <data name="SettingsPage_SensorExternal" xml:space="preserve"> + <value>Extern</value> + </data> + <data name="SettingsPage_SensorInternal" xml:space="preserve"> + <value>Intern</value> + </data> + <data name="SettingsPage_SensorOptions" xml:space="preserve"> + <value>Sensor-Optionen</value> + </data> + <data name="SettingsPage_SensorPlacementDirection" xml:space="preserve"> + <value>Externer Sensor-Ausrichtung</value> + </data> + <data name="SettingsPage_SensorPlacementDirectionDesc" xml:space="preserve"> + <value>Wählen Sie aus, auf welcher Seite des Geräts der Sensor montiert wurde</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDown" xml:space="preserve"> + <value>Externer Sensor kopfüber</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDownDesc" xml:space="preserve"> + <value>Der Sensor wurde verkehrt herum montiert, möglich mit USB-C-Konvertern</value> + </data> + <data name="SettingsPage_SensorSelection" xml:space="preserve"> + <value>Sensorauswahl</value> + </data> + <data name="SettingsPage_SensorSelectionDesc" xml:space="preserve"> + <value>Wählen Sie den gewünschten Sensor für die Bewegungseingabe aus</value> + </data> + <data name="SettingsPage_Settings" xml:space="preserve"> + <value>Einstellungen</value> + </data> + <data name="SettingsPage_StartupType" xml:space="preserve"> + <value>Starttyp</value> + </data> + <data name="SettingsPage_StartupTypeDesc" xml:space="preserve"> + <value>Wird vom Dienstmanager verwendet, um den Starttyp des Dienstes zu definieren</value> + </data> + <data name="SettingsPage_TDPMax" xml:space="preserve"> + <value>Maximale Leistung</value> + </data> + <data name="SettingsPage_TDPMaxDesc" xml:space="preserve"> + <value>Die maximale Leistung in Watt, die dem Prozessor zugeführt wird</value> + </data> + <data name="SettingsPage_TDPMin" xml:space="preserve"> + <value>Minimale Leistung</value> + </data> + <data name="SettingsPage_TDPMinDesc" xml:space="preserve"> + <value>Die Mindestleistung in Watt, die dem Prozessor zugeführt wird</value> + </data> + <data name="SettingsPage_TDPRangeOverride" xml:space="preserve"> + <value>Configurable Power (cTDP) konfigurieren</value> + </data> + <data name="SettingsPage_TDPRangeOverrideDesc" xml:space="preserve"> + <value>Ermöglicht die Änderung der minimalen und maximalen Leistungswerte (TDP) über die CPU-Spezifikationen hinaus</value> + </data> + <data name="SettingsPage_ThemeDark" xml:space="preserve"> + <value>Dunkel</value> + </data> + <data name="SettingsPage_ThemeLight" xml:space="preserve"> + <value>Hell</value> + </data> + <data name="SettingsPage_ToastNotification" xml:space="preserve"> + <value>Toast-Benachrichtigung</value> + </data> + <data name="SettingsPage_ToastNotificationDesc" xml:space="preserve"> + <value>Benachrichtigungen von der Anwendung im Info-Center abrufen</value> + </data> + <data name="SettingsPage_UpdateAvailable" xml:space="preserve"> + <value>Update verfügbar</value> + </data> + <data name="SettingsPage_UpdateCheck" xml:space="preserve"> + <value>Suche nach Updates...</value> + </data> + <data name="SettingsPage_UpdateFailedDownload" xml:space="preserve"> + <value>Wir konnten das Update nicht downloaden.</value> + </data> + <data name="SettingsPage_UpdateFailedGithub" xml:space="preserve"> + <value>Wir konnten GitHub nicht erreichen</value> + </data> + <data name="SettingsPage_UpdateFailedInstall" xml:space="preserve"> + <value>Wir konnten die Update-Datei nicht finden</value> + </data> + <data name="SettingsPage_UpdateWarning" xml:space="preserve"> + <value>Ups. Es gab ein Problem.</value> + </data> + <data name="SettingsPage_UpToDate" xml:space="preserve"> + <value>Sie sind auf dem neusten Stand</value> + </data> + <data name="ToastNewControllerEx" xml:space="preserve"> + <value>Dein Controller ist nun versteckt. Eingaben werden an den Virtuellen Controller weitergeleitet</value> + </data> + <data name="User" xml:space="preserve"> + <value>Nutzer</value> + </data> + <data name="WarningElevated" xml:space="preserve"> + <value>Führen Sie dieses Tool als Administrator aus, um diese Einstellungen freizugeben</value> + </data> + <data name="xinput1_x64" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\xinput1_x64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="xinput1_x86" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\xinput1_x86.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="XInputPlus" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\XInputPlus.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value> + </data> + <data name="ControllerPage_SteamControllerMute" xml:space="preserve"> + <value>Virtuellen Controller stummschalten</value> + </data> + <data name="ControllerPage_SteamControllerMuteDesc" xml:space="preserve"> + <value>Viertuellen Controller in Steam-Applikationen stummschalten</value> + </data> + <data name="ProfilesPage_ControllerSettings" xml:space="preserve"> + <value>Controller-Einstellungen</value> + </data> + <data name="ProfilesPage_ControllerSettingsDesc" xml:space="preserve"> + <value>Ändern Sie die Einstellungen der virtuellen Steuerung</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzone" xml:space="preserve"> + <value>Bewegungs-Anti-Deadzone</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzoneDesc" xml:space="preserve"> + <value>Kompensierung der Deadzone im Spiel, verbesserte Registrierung kleiner Bewegungen</value> + </data> + <data name="chord_neptune" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\controller_base\chord_neptune.vdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="empty_neptune" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\controller_base\empty_neptune.vdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityDesc" xml:space="preserve"> + <value>Verbessern Sie die Zirkularität des Joysticks</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityLeft" xml:space="preserve"> + <value>Zirkularität linker Stick</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityRight" xml:space="preserve"> + <value>Zirkularität rechter Stick</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeft" xml:space="preserve"> + <value>Joystick Deadzone % links</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeftDesc" xml:space="preserve"> + <value>Einstellen der inneren und äußeren Deadzone (Prozent) des linken Joysticks</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRight" xml:space="preserve"> + <value>Joystick Deadzone % rechts</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRightDesc" xml:space="preserve"> + <value>Einstellen der inneren und äußeren Deadzone (Prozent) des rechten Joysticks</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeft" xml:space="preserve"> + <value>Linke Analog-Deadzone %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeftDesc" xml:space="preserve"> + <value>Einstellen der inneren und äußeren Deadzone (Prozent) des linken Joysticks</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRight" xml:space="preserve"> + <value>Rechter Trigger Deadzone %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRightDesc" xml:space="preserve"> + <value>Einstellen der inneren und äußeren Deadzone (Prozent) des rechten Joysticks</value> + </data> + <data name="SettingsMode0_AdditionalSettings" xml:space="preserve"> + <value>Zusätzliche Einstellungen</value> + </data> + <data name="SettingsMode0_AimingDownSights" xml:space="preserve"> + <value>Multiplikator für Bewegungen beim Zielen +</value> + </data> + <data name="SettingsMode0_AimingDownSightsActivation" xml:space="preserve"> + <value>Aktivierungstaste</value> + </data> + <data name="SettingsMode0_AimingDownSightsDesc" xml:space="preserve"> + <value>Ein zusätzlicher Multiplikator für die Bewegungsempfindlichkeit beim Anvisieren der Visierung oder des Zielfernrohrs durch die Verwendung der konfigurierten Aktivierungstaste</value> + </data> + <data name="SettingsMode0_AimingDownSightsMultiplier" xml:space="preserve"> + <value>Multiplikatorwert</value> + </data> + <data name="SettingsMode0_CameraOptions" xml:space="preserve"> + <value>Kamera-Optionen</value> + </data> + <data name="SettingsMode0_CustomResponseCurve" xml:space="preserve"> + <value>Benutzerdefinierte Reaktionskurve</value> + </data> + <data name="SettingsMode0_CustomResponseCurveGameOutput" xml:space="preserve"> + <value>Ausgabe an das Spiel gesendet</value> + </data> + <data name="SettingsMode0_CustomResponseIntensity" xml:space="preserve"> + <value>Bewegungsintensität</value> + </data> + <data name="SettingsMode0_CustomResponsePresetAgressive" xml:space="preserve"> + <value>Aggressiv</value> + </data> + <data name="SettingsMode0_CustomResponsePresetDefault" xml:space="preserve"> + <value>Standard</value> + </data> + <data name="SettingsMode0_CustomResponsePresetOptions" xml:space="preserve"> + <value>Voreinstellungs-Optionen</value> + </data> + <data name="SettingsMode0_CustomResponsePresetPrecise" xml:space="preserve"> + <value>Präzise</value> + </data> + <data name="SettingsMode0_FlickDuration" xml:space="preserve"> + <value>Streifendauer</value> + </data> + <data name="SettingsMode0_FlickDurationDesc" xml:space="preserve"> + <value>Ändern Sie die Streifendauer, kalibrieren Sie auf eine 180-Grad-Drehung, in Millisekunden</value> + </data> + <data name="SettingsMode0_FlickStick" xml:space="preserve"> + <value>Flick-Stick (Experimental)</value> + </data> + <data name="SettingsMode0_FlickStickDesc" xml:space="preserve"> + <value>Richten Sie die Kamera in Richtung des (rechten) Joysticks, drehen Sie die Kamera rein in der horizontalen Ebene durch Drehen</value> + </data> + <data name="SettingsMode0_FlickStickEnable" xml:space="preserve"> + <value>Aktiviere Flick-Stick</value> + </data> + <data name="SettingsMode0_Sensitivity" xml:space="preserve"> + <value>Empfindlichkeit</value> + </data> + <data name="SettingsMode0_SensitivityDesc" xml:space="preserve"> + <value>Ändern Sie die Bewegungsempfindlichkeit der horizontalen und vertikalen Achse</value> + </data> + <data name="SettingsMode0_SensitivityX" xml:space="preserve"> + <value>Empfindlichkeit X</value> + </data> + <data name="SettingsMode0_SensitivityXDesc" xml:space="preserve"> + <value>Ändern Sie die Bewegungsempfindlichkeit der horizontalen Achse</value> + </data> + <data name="SettingsMode0_SensitivityY" xml:space="preserve"> + <value>Empfindlichkeit Y</value> + </data> + <data name="SettingsMode0_SensitivityYDesc" xml:space="preserve"> + <value>Ändern Sie die Bewegungsempfindlichkeit der vertikalen Achse</value> + </data> + <data name="SettingsMode0_StickSensitivtity" xml:space="preserve"> + <value>Stick-Empfindlichkeit</value> + </data> + <data name="SettingsMode0_StickSensitivtityDesc" xml:space="preserve"> + <value>Drehrate ändern</value> + </data> + <data name="SettingsMode1_AdditionalSettings" xml:space="preserve"> + <value>Zusätzliche Einstellungen</value> + </data> + <data name="SettingsMode1_Deadzone" xml:space="preserve"> + <value>Deadzone</value> + </data> + <data name="SettingsMode1_DeadzoneDesc" xml:space="preserve"> + <value>Ändern Sie die Deadzone in Grad. Verbessert das Geradeauslaufen</value> + </data> + <data name="SettingsMode1_JoystickGameInput" xml:space="preserve"> + <value>Joystick-Spiel Eingabe</value> + </data> + <data name="SettingsMode1_JoystickSteering" xml:space="preserve"> + <value>Joystick-Lenkung</value> + </data> + <data name="SettingsMode1_JoystickSteeringOptions" xml:space="preserve"> + <value>Joystick-Lenkung-Optionen</value> + </data> + <data name="SettingsMode1_JoystickSteeringPreview" xml:space="preserve"> + <value>Joystick-Lenkung-Vorschau</value> + </data> + <data name="SettingsMode1_MaxSteeringAngle" xml:space="preserve"> + <value>Maximaler Lenkwinkel</value> + </data> + <data name="SettingsMode1_MaxSteeringAngleDesc" xml:space="preserve"> + <value>Den maximalen Lenkwinkelwert in Grad ändern</value> + </data> + <data name="SettingsMode1_SteeringLinearity" xml:space="preserve"> + <value>Linearität der Lenkung</value> + </data> + <data name="SettingsMode1_SteeringLinearityDesc" xml:space="preserve"> + <value>Zuordnung zwischen Eingabe und Lenkung. Niedrigere Werte bieten mehr Genauigkeit in der Nähe des vollen Lenkausschlags, aber weniger Genauigkeit in der Nähe der Mittellage. Höhere Werte bieten mehr Genauigkeit in der Nähe der Mittellage, aber weniger Genauigkeit beim vollen Lenkausschlag. Ein Wert von 1,0 entspricht einer linearen Zuordnung.</value> + </data> + <data name="AboutPage_Manufacturer" xml:space="preserve"> + <value>Hersteller</value> + </data> + <data name="AboutPage_Partner" xml:space="preserve"> + <value>Partner</value> + </data> + <data name="AboutPage_ProductName" xml:space="preserve"> + <value>Produktname</value> + </data> + <data name="ButtonsPage_ABXY" xml:space="preserve"> + <value>A,B,X,Y</value> + </data> + <data name="ButtonsPage_Back_Grips" xml:space="preserve"> + <value>BACK GRIPS</value> + </data> + <data name="ButtonsPage_Bumpers" xml:space="preserve"> + <value>Schultertasten</value> + </data> + <data name="ButtonsPage_Menu" xml:space="preserve"> + <value>MENÜ</value> + </data> + <data name="ButtonsPage_OEM" xml:space="preserve"> + <value>OEM</value> + </data> + <data name="ControllerPage_DesktopLayout" xml:space="preserve"> + <value>Desktop-Layout</value> + </data> + <data name="ControllerPage_DesktopLayoutDefine" xml:space="preserve"> + <value>Desktop-Layout festlegen</value> + </data> + <data name="ControllerPage_DesktopLayoutDefineController" xml:space="preserve"> + <value>Controller-Layout im Desktop festlegen</value> + </data> + <data name="ControllerPage_DesktopLayoutEdit" xml:space="preserve"> + <value>Bearbeiten</value> + </data> + <data name="ControllerPage_DesktopLayoutEnable" xml:space="preserve"> + <value>Desktop-Layout aktivieren</value> + </data> + <data name="ControllerPage_NonGameControllerLayouts" xml:space="preserve"> + <value>Non-Spiel Controller-Layouts</value> + </data> + <data name="ControllerPage_NoPhysicalControllerAction" xml:space="preserve"> + <value>Klicken Sie auf Verbinden neben Ihrem angeschlossenen Controller.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDesc" xml:space="preserve"> + <value>Sie haben keinen physischen Controller angeschlossen. Es werden keine Eingaben an HC oder seinen Dienst gesendet.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedAction" xml:space="preserve"> + <value>Bitte stellen Sie sicher, dass Sie ein kompatibles XInput- oder DInput-Gerät angeschlossen haben.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedWarning" xml:space="preserve"> + <value>Kein physischer Controller gefunden</value> + </data> + <data name="ControllerPage_NoPhysicalControllerWarning" xml:space="preserve"> + <value>Kein physischer Controller gefunden</value> + </data> + <data name="ControllerPage_NoVirtualControllerAction" xml:space="preserve"> + <value>Starten Sie den Begleitdienst oder stellen Sie sicher, dass der Status Ihrer virtuellen Steuerung auf "Verbunden" eingestellt ist.</value> + </data> + <data name="ControllerPage_NoVirtualControllerDesc" xml:space="preserve"> + <value>Ihr physischer Controller ist versteckt, aber sie haben keinen Virtuellen Controller. Keine Eingaben werden an das Spiel geschickt.</value> + </data> + <data name="ControllerPage_NoVirtualControllerWarning" xml:space="preserve"> + <value>Kein virtueller Controller gefunden</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenAction" xml:space="preserve"> + <value>Heben Sie die Stummschaltung Ihres virtuellen Controllers auf oder zeigen Sie Ihren physischen Controller an.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenDesc" xml:space="preserve"> + <value>Ihr physischer Controller ist versteckt, aber ihr Virtueller Controller ist stumm.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenWarning" xml:space="preserve"> + <value>Physischer Controller ist versteckt</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenAction" xml:space="preserve"> + <value>Verstecken Sie Ihren physischen Controller oder schalten Sie Ihren virtuellen Controller stumm.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenDesc" xml:space="preserve"> + <value>Ihr physischer Controller ist nicht versteckt, jedoch haben sie ihren virtuellen Controller stummgeschaltet. Es kann Doppelte Tasteneingaben geben.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenWarning" xml:space="preserve"> + <value>Physischer Controller ist nicht versteckt</value> + </data> + <data name="ControllerPage_SteamControllerHDRumble" xml:space="preserve"> + <value>HD-Rumble</value> + </data> + <data name="ControllerPage_SteamControllerHDRumbleDesc" xml:space="preserve"> + <value>Verwendung einer HD-Rumble-Engine, auf Kosten einer höheren CPU-Auslastung</value> + </data> + <data name="ControllerPage_SteamControllerSettings" xml:space="preserve"> + <value>Steam Controller Einstellungen</value> + </data> + <data name="ControllerPage_VibrateDevice" xml:space="preserve"> + <value>Vibrieren, wenn Controller verbunden ist</value> + </data> + <data name="ControllerPage_VibrateDeviceDesc" xml:space="preserve"> + <value>Physischen Kontroller vibrieren, wenn verbunden</value> + </data> + <data name="DPadPage_DPad" xml:space="preserve"> + <value>STEUERKREUZ</value> + </data> + <data name="InputsHotkey_decreaseBrightness" xml:space="preserve"> + <value>Helligkeit verringern</value> + </data> + <data name="InputsHotkey_decreaseBrightnessDesc" xml:space="preserve"> + <value>Systemhelligkeit um 5% verringern</value> + </data> + <data name="InputsHotkey_decreaseVolume" xml:space="preserve"> + <value>Lautstärke verringern</value> + </data> + <data name="InputsHotkey_decreaseVolumeDesc" xml:space="preserve"> + <value>Systemlautstärke um 5% verringern</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabledDesc" xml:space="preserve"> + <value>Desktop-Controllerlayout schalten</value> + </data> + <data name="InputsHotkey_increaseBrightness" xml:space="preserve"> + <value>Helligkeit erhöhen</value> + </data> + <data name="InputsHotkey_increaseBrightnessDesc" xml:space="preserve"> + <value>Erhöhe die Bildschirmhelligkeit um 5%</value> + </data> + <data name="InputsHotkey_increaseVolume" xml:space="preserve"> + <value>Lautstärke erhöhen</value> + </data> + <data name="InputsHotkey_increaseVolumeDesc" xml:space="preserve"> + <value>Erhöhe die Lautstärke um 5%</value> + </data> + <data name="InputsHotkey_OnScreenDisplay" xml:space="preserve"> + <value>Bildschirmanzeige</value> + </data> + <data name="InputsHotkey_OnScreenDisplayDesc" xml:space="preserve"> + <value>Display-Unterstützung aktivieren</value> + </data> + <data name="InputsHotkey_QuietModeToggled" xml:space="preserve"> + <value>Lüftersteuerung</value> + </data> + <data name="InputsHotkey_QuietModeToggledDesc" xml:space="preserve"> + <value>Einstellen des Lüfterbetriebszyklus auf einen benutzerdefinierten Wert</value> + </data> + <data name="InputsHotkey_shortcutControlCenterDesc" xml:space="preserve"> + <value>Info-Center anzeigen und ausblenden</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabled" xml:space="preserve"> + <value>Desktop-Layout</value> + </data> + <data name="InputsHotkey_shortcutControlCenter" xml:space="preserve"> + <value>Info-Center anzeigen</value> + </data> + <data name="InputsHotkey_shortcutPrintScreen" xml:space="preserve"> + <value>Snipping-Tool öffnen</value> + </data> + <data name="InputsHotkey_shortcutPrintScreenDesc" xml:space="preserve"> + <value>Press this key: Windows + Shift + S</value> + </data> + <data name="JoystickPage_Joystick_Left" xml:space="preserve"> + <value>LINKER JOYSTICK</value> + </data> + <data name="JoystickPage_Joystick_Left_Buttons" xml:space="preserve"> + <value>LINKE JOYSTICK BUTTONS</value> + </data> + <data name="JoystickPage_Joystick_Right" xml:space="preserve"> + <value>RECHTER JOYSTICK</value> + </data> + <data name="JoystickPage_Joystick_Right_Buttons" xml:space="preserve"> + <value>RECHTE JOYSTICK BUTTONS</value> + </data> + <data name="LayoutPage_ApplyTemplate" xml:space="preserve"> + <value>Vorlage anwenden</value> + </data> + <data name="LayoutPage_Buttons" xml:space="preserve"> + <value>Tasten</value> + </data> + <data name="LayoutPage_Cancel" xml:space="preserve"> + <value>Abbrechen</value> + </data> + <data name="LayoutPage_Community" xml:space="preserve"> + <value>COMMUNITY</value> + </data> + <data name="LayoutPage_Confirm" xml:space="preserve"> + <value>Bestätigen</value> + </data> + <data name="LayoutPage_Dpad" xml:space="preserve"> + <value>Steuerkreuz</value> + </data> + <data name="LayoutPage_ExportCurrentController" xml:space="preserve"> + <value>Für aktuellen Controller exportieren</value> + </data> + <data name="LayoutPage_ExportLayout" xml:space="preserve"> + <value>Layout exportieren</value> + </data> + <data name="LayoutPage_Gyro" xml:space="preserve"> + <value>Gyro</value> + </data> + <data name="LayoutPage_Joysticks" xml:space="preserve"> + <value>Joysticks</value> + </data> + <data name="LayoutPage_LayoutAuthor" xml:space="preserve"> + <value>Layout-Autor</value> + </data> + <data name="LayoutPage_LayoutDesc" xml:space="preserve"> + <value>Layout-Beschreibung</value> + </data> + <data name="LayoutPage_LayoutTitle" xml:space="preserve"> + <value>Layout-Titel</value> + </data> + <data name="LayoutPage_SaveGameInfoLayout" xml:space="preserve"> + <value>Spielinformationen mit Layout speichern</value> + </data> + <data name="LayoutPage_TemplatePicker" xml:space="preserve"> + <value>Layout-Vorlagen auswählen</value> + </data> + <data name="LayoutPage_Templates" xml:space="preserve"> + <value>VORLAGEN</value> + </data> + <data name="LayoutPage_Trackpads" xml:space="preserve"> + <value>Trackpads</value> + </data> + <data name="LayoutPage_Triggers" xml:space="preserve"> + <value>Trigger</value> + </data> + <data name="MainWindow_navHotkeys" xml:space="preserve"> + <value>Hotkeys</value> + </data> + <data name="OverlayPage_Millisecond" xml:space="preserve"> + <value>ms</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel" xml:space="preserve"> + <value>Overlay-Anzeigenlevel</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Disabled" xml:space="preserve"> + <value>Deaktiviert</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Extended" xml:space="preserve"> + <value>Erweitert</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Full" xml:space="preserve"> + <value>Komplett</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Minimal" xml:space="preserve"> + <value>Minimal</value> + </data> + <data name="OverlayPage_OverlayDisplayLevelDesc" xml:space="preserve"> + <value>Verändere die Menge der Statistikwerten</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRate" xml:space="preserve"> + <value>Aktualisierungsrate</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRateDesc" xml:space="preserve"> + <value>Verändere die Update-Rate der Statistikwerte</value> + </data> + <data name="ProfilesPage_AutoTDP" xml:space="preserve"> + <value>Automatische TDP</value> + </data> + <data name="ProfilesPage_AutoTDPDesc" xml:space="preserve"> + <value>Automatische Anpassung des TDP auf der Grundlage der gemessenen FPS und des gewünschten FPS-Ziels</value> + </data> + <data name="ProfilesPage_AutoTDPFPS" xml:space="preserve"> + <value>Framerate-Ziel</value> + </data> + <data name="ProfilesPage_AutoTDPFPSDesc" xml:space="preserve"> + <value>Gewünschtes FPS-Ziel für Auto-TDP-Controller</value> + </data> + <data name="ProfilesPage_ControllerLayout" xml:space="preserve"> + <value>Controller-Layout</value> + </data> + <data name="ProfilesPage_ControllerLayoutDesc" xml:space="preserve"> + <value>Ändern Sie das virtuelle Steuerungslayout</value> + </data> + <data name="ProfilesPage_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="ProfilesPage_EPP" xml:space="preserve"> + <value>Bevorzugte Energieleistung (EPP)</value> + </data> + <data name="ProfilesPage_EPPBalance" xml:space="preserve"> + <value>CPU/GPU-Leistungsbalance</value> + </data> + <data name="ProfilesPage_EPPDesc" xml:space="preserve"> + <value>Legt die Stromverteilungsrichtlinien zwischen CPU und GPU fest</value> + </data> + <data name="ProfilesPage_FramerateLimit" xml:space="preserve"> + <value>Framerate-Limit</value> + </data> + <data name="ProfilesPage_FramerateLimitDesc" xml:space="preserve"> + <value>Begrenzt FPS für 3D-Programme</value> + </data> + <data name="ProfilesPage_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="ProfilesPage_GPUMhz" xml:space="preserve"> + <value>Maximale GPU-Taktfrequenz</value> + </data> + <data name="ProfilesPage_GPUMhzDesc" xml:space="preserve"> + <value>Maximale Taktgeschwindigkeit des GPU in MHz</value> + </data> + <data name="ProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Leistungsgrenze-Ziel</value> + </data> + <data name="Properties.Resources.ControllerPage_Disconnect" xml:space="preserve"> + <value>Getrennt</value> + </data> + <data name="QuickPerformancePage_AutoTDPUnitFPS" xml:space="preserve"> + <value>FPS</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStart" xml:space="preserve"> + <value>Desktop-Profil beim Start aktivieren</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStartDesc" xml:space="preserve"> + <value>Das Desktop-Profil wird beim Start der Anwendung automatisch aktiviert.</value> + </data> + <data name="SettingsPage_NativeDisplayOrientation" xml:space="preserve"> + <value>Native Display-Orientierung</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDesc" xml:space="preserve"> + <value>Einige Funktionen hängen davon ab, dass Sie die ursprüngliche Ausrichtung des Bildschirms kennen, damit sie richtig funktionieren. Wenn dies nicht richtig erkannt wurde, stellen Sie die Ausrichtung Ihres Bildschirms so ein, dass sie mit der Ausrichtung Ihres Controllers übereinstimmt, und klicken Sie dann auf Erkennen</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDetect" xml:space="preserve"> + <value>Erkennen</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationNotSet" xml:space="preserve"> + <value>Nicht belegt</value> + </data> + <data name="SettingsPage_ThemeDefault" xml:space="preserve"> + <value>Systemeinstellung benutzen</value> + </data> + <data name="TrackPadsPage_Trackpad_Left" xml:space="preserve"> + <value>LINKER TRACKPAD</value> + </data> + <data name="TrackPadsPage_Trackpad_Left_Buttons" xml:space="preserve"> + <value>LINKE TRACKPAD-TASTEN</value> + </data> + <data name="TrackPadsPage_Trackpad_Right" xml:space="preserve"> + <value>RECHTER TRACKPAD</value> + </data> + <data name="TrackPadsPage_Trackpad_Right_Buttons" xml:space="preserve"> + <value>RECHTE TRACKPAD-TASTEN</value> + </data> + <data name="TriggersPage_Trigger_Left" xml:space="preserve"> + <value>LINKE ANALOGTASTE</value> + </data> + <data name="TriggersPage_Trigger_Left_Button" xml:space="preserve"> + <value>LINKE ANALOGTASTE</value> + </data> + <data name="TriggersPage_Trigger_Right" xml:space="preserve"> + <value>RECHTER TRIGGER</value> + </data> + <data name="TriggersPage_Trigger_Right_Button" xml:space="preserve"> + <value>RECHTE TRIGGER-TASTEN</value> + </data> + <data name="QuickProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Leistungsgrenze-Ziel</value> + </data> + <data name="SettingsPage_QuickToolsOptions" xml:space="preserve"> + <value>QuickTools-Optionen</value> + </data> + <data name="QuickPerformancePage_FanOverridePercentage" xml:space="preserve"> + <value>%</value> + </data> + <data name="QuickPerformancePage_CPUBoostModeDesc" xml:space="preserve"> + <value>Aktuellen CPU-Boost-Modus einstellen</value> + </data> + <data name="QuickPerformancePage_CPUBoostMode" xml:space="preserve"> + <value>CPU-Boost-Modus</value> + </data> + <data name="QuickPerformancePage_FanOverrideDesc" xml:space="preserve"> + <value>Einstellen des Lüfterbetriebszyklus auf einen benutzerdefinierten Wert</value> + </data> + <data name="QuickPerformancePage_FanOverride" xml:space="preserve"> + <value>Lüfter-Steuerung</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRateDesc" xml:space="preserve"> + <value>Hauptdisplay-Auflösung und Bildwiederholrate anpassen</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRate" xml:space="preserve"> + <value>Display-Auflösung und Bildwiederholrate</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopRight" xml:space="preserve"> + <value>Oben Rechts</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopLeft" xml:space="preserve"> + <value>Oben links</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocation" xml:space="preserve"> + <value>Fensterplatzierung</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomRight" xml:space="preserve"> + <value>Unten Rechts</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomLeft" xml:space="preserve"> + <value>Unten Links</value> + </data> + <data name="DevicePage_PowerOptions" xml:space="preserve"> + <value>Energie-Optionen</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationDesc" xml:space="preserve"> + <value>Standort des QuickTools-Fenster festlegen</value> + </data> + <data name="SettingsPage_ThirdPartyApps" xml:space="preserve"> + <value>Drittanbieter-Programme</value> + </data> + <data name="SettingsPage_SensorNone" xml:space="preserve"> + <value>Keine</value> + </data> + <data name="SettingsPage_QuickToolsBackdrop" xml:space="preserve"> + <value>QuickTools-Hintergrund, Keine, Glimmer, Tabbed, Akryl</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServerDesc" xml:space="preserve"> + <value>Automatically turn RTSS off when companion is closed</value> + </data> + <data name="SettingsPage_HwInfoDesc" xml:space="preserve"> + <value>Automatically turn HWiNFO off when companion is closed</value> + </data> + <data name="SettingsPage_HwInfo" xml:space="preserve"> + <value>HWiNFO</value> + </data> + <data name="QuickProfilesPage_CurrentProfileDefault" xml:space="preserve"> + <value>Standard</value> + </data> + <data name="QuickProfilesPage_CurrentProfile" xml:space="preserve"> + <value>Aktuelles Profil:</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServer" xml:space="preserve"> + <value>RivaTuner Statistics Server</value> + </data> + <data name="SettingsPage_HideWhenLoseFocusDesc" xml:space="preserve"> + <value>Automatically hide quick tools when the user clicks outside of window</value> + </data> + <data name="SettingsPage_HideWhenLoseFocus" xml:space="preserve"> + <value>Bei Fokusverlust ausblenden</value> + </data> + <data name="AutoRollYawSwapDesc" xml:space="preserve"> + <value>Dieser Eingang funktioniert wie ein einfacher Joystick. Ideal für Laptops und Clamshell-Handhelds, automatischer Yaw-Roll-Wechsel je nachdem, wie das Gerät gehalten wird (90 oder 180 Grad geöffnet).</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.A" xml:space="preserve"> + <value>Taste A</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.AlwaysOn" xml:space="preserve"> + <value>Immer an</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.B" xml:space="preserve"> + <value>Button B</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Back" xml:space="preserve"> + <value>Zurück</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadDown" xml:space="preserve"> + <value>Unten</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadLeft" xml:space="preserve"> + <value>Links</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadRight" xml:space="preserve"> + <value>Rechts</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadUp" xml:space="preserve"> + <value>Hoch</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftShoulder" xml:space="preserve"> + <value>Linke Schultertaste</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftThumb" xml:space="preserve"> + <value>Linker Stick</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftTrigger" xml:space="preserve"> + <value>Linke Analogtaste</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightShoulder" xml:space="preserve"> + <value>Rechte Schultertaste</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightThumb" xml:space="preserve"> + <value>Rechter Stick</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightTrigger" xml:space="preserve"> + <value>Rechte Triggertaste</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Start" xml:space="preserve"> + <value>Start</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.X" xml:space="preserve"> + <value>X-Taste</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Y" xml:space="preserve"> + <value>Y-Taste</value> + </data> + <data name="Enum.HIDmode.DualShock4Controller" xml:space="preserve"> + <value>Emulierter DualShock 4 Controller</value> + </data> + <data name="Enum.HIDmode.NoController" xml:space="preserve"> + <value>Kein emulierter Controller</value> + </data> + <data name="Enum.HIDmode.Xbox360Controller" xml:space="preserve"> + <value>Emulierter XBOX 360 Controller</value> + </data> + <data name="Enum.HIDstatus.Connected" xml:space="preserve"> + <value>Verbunden</value> + </data> + <data name="Enum.HIDstatus.Disconnected" xml:space="preserve"> + <value>Getrennt</value> + </data> + <data name="Enum.Input.AutoRollYawSwap" xml:space="preserve"> + <value>Auto-Roll-Gier-Swap</value> + </data> + <data name="Enum.Input.JoystickCamera" xml:space="preserve"> + <value>Joystick-Kamera</value> + </data> + <data name="Enum.Input.JoystickSteering" xml:space="preserve"> + <value>Joystick-Lenkung</value> + </data> + <data name="Enum.Input.PlayerSpace" xml:space="preserve"> + <value>Player space</value> + </data> + <data name="Enum.InputsHotkeyType.HC" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.InputsHotkeyType.Overlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="Enum.InputsHotkeyType.Quicktools" xml:space="preserve"> + <value>QuickTools</value> + </data> + <data name="Enum.InputsHotkeyType.Windows" xml:space="preserve"> + <value>Windows</value> + </data> + <data name="Enum.Output.LeftStick" xml:space="preserve"> + <value>Linker Joystick</value> + </data> + <data name="Enum.Output.RightStick" xml:space="preserve"> + <value>Rechter Joystick</value> + </data> + <data name="Enum.ProfileErrorCode.IsRunning" xml:space="preserve"> + <value>Hoppla. Es scheint, dass dieses Profil ausgeführt wird. Einige Optionen, die eine ausführbare Datei erfordern, sind möglicherweise deaktiviert.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingExecutable" xml:space="preserve"> + <value>Hoppla. Es scheint, dass dieses Profil keine ausführbare Datei hat. Wie ist das überhaupt möglich?</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPath" xml:space="preserve"> + <value>Hoppla, Es scheint, dass dieses Profil keinen Pfad zu der Anwendung enthält. Einige Optionen, die eine ausführbare Datei erfordern, sind möglicherweise deaktiviert.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPermission" xml:space="preserve"> + <value>Hoppla. Es scheint, dass Sie nicht über die erforderliche Berechtigungsstufe verfügen, um den Inhalt dieser Anwendung zu ändern. Vergewissern Sie sich, dass Sie das Programm im Administratormodus gestartet haben.</value> + </data> + <data name="Enum.ProfileErrorCode.None" xml:space="preserve"> + <value>Hier gibt es nichts zu sehen.</value> + </data> + <data name="Enum.QualityOfServiceLevel.Default" xml:space="preserve"> + <value>Standard</value> + </data> + <data name="Enum.QualityOfServiceLevel.Eco" xml:space="preserve"> + <value>Eco</value> + </data> + <data name="Enum.QualityOfServiceLevel.High" xml:space="preserve"> + <value>Hoch</value> + </data> + <data name="Enum.ServiceStartMode.Automatic" xml:space="preserve"> + <value>Automatisch</value> + </data> + <data name="Enum.ServiceStartMode.Disabled" xml:space="preserve"> + <value>Deaktiviert</value> + </data> + <data name="Enum.ServiceStartMode.Manual" xml:space="preserve"> + <value>Manuell</value> + </data> + <data name="JoystickCameraDesc" xml:space="preserve"> + <value>Dieser Eingang funktioniert wie ein einfacher Joystick. Er ist für traditionelle Joystick-Anwendungen gedacht</value> + </data> + <data name="JoystickSteeringDesc" xml:space="preserve"> + <value>Dieser Eingang funktioniert wie ein Joystick, der für die Steuerung eines Lenkrads oder eines Rennspiels optimiert ist.</value> + </data> + <data name="PlayerSpaceDesc" xml:space="preserve"> + <value>Diese Eingabe funktioniert wie ein Joystick, der für die Steuerung einer First- oder Third-Person-Kamera optimiert ist</value> + </data> + <data name="Enum.InputsHotkeyType.Handheld" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM3" xml:space="preserve"> + <value>KB</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM1" xml:space="preserve"> + <value>Win</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM2" xml:space="preserve"> + <value>Esc</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.ProfileErrorCode.Default" xml:space="preserve"> + <value>Dies ist Ihr Standard-Controller-Profil. Dieses Profil wird für alle Ihre Anwendungen verwendet, die kein spezifisches Profil haben. Einige Optionen, die eine ausführbare Datei erfordern, sind möglicherweise deaktiviert.</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM3" xml:space="preserve"> + <value>M1 / M2</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B1" xml:space="preserve"> + <value>Kreuz</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B2" xml:space="preserve"> + <value>Kreis</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B3" xml:space="preserve"> + <value>Viereck</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B4" xml:space="preserve"> + <value>Dreieck</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Back" xml:space="preserve"> + <value>Teilen</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Start" xml:space="preserve"> + <value>Optionen</value> + </data> + <data name="Enum.InputsHotkeyType.Custom" xml:space="preserve"> + <value>Benutzerdefiniert</value> + </data> + <data name="Enum.InputsHotkeyType.Device" xml:space="preserve"> + <value>Gerät</value> + </data> + <data name="Enum.MotionInput.AutoRollYawSwap" xml:space="preserve"> + <value>Auto-Roll-Gier-Swap</value> + </data> + <data name="Enum.MotionInput.JoystickCamera" xml:space="preserve"> + <value>Joystick Camera</value> + </data> + <data name="Enum.MotionInput.JoystickSteering" xml:space="preserve"> + <value>Joystick Steering</value> + </data> + <data name="Enum.MotionInput.PlayerSpace" xml:space="preserve"> + <value>Player Space</value> + </data> + <data name="Enum.MotionOutput.LeftStick" xml:space="preserve"> + <value>Linker Stick</value> + </data> + <data name="Enum.MotionOutput.RightStick" xml:space="preserve"> + <value>Rechter Stick</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Back" xml:space="preserve"> + <value>Anzeigen</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Special" xml:space="preserve"> + <value>STEAM</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Start" xml:space="preserve"> + <value>Menü</value> + </data> + <data name="Enum.ProfileErrorCode.Running" xml:space="preserve"> + <value>Hoppla, It seems this profile excutable is running. Some options requiring an executable might be disabled.</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM1" xml:space="preserve"> + <value>Command Center</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM2" xml:space="preserve"> + <value>Armory Crate</value> + </data> + <data name="Enum.SteamDeck.ButtonFlags.OEM1" xml:space="preserve"> + <value>Optionen</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftThumb" xml:space="preserve"> + <value>Linker Stick</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightThumb" xml:space="preserve"> + <value>Rechter Stick</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B5" xml:space="preserve"> + <value>B5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B6" xml:space="preserve"> + <value>B6</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B7" xml:space="preserve"> + <value>B7</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B8" xml:space="preserve"> + <value>B8</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Back" xml:space="preserve"> + <value>Anzeigen</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadDown" xml:space="preserve"> + <value>Unten</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadLeft" xml:space="preserve"> + <value>Links</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadRight" xml:space="preserve"> + <value>PPad Rechts</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadUp" xml:space="preserve"> + <value>Hoch</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L1" xml:space="preserve"> + <value>LB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L3" xml:space="preserve"> + <value>L3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L4" xml:space="preserve"> + <value>L4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L5" xml:space="preserve"> + <value>L5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumb" xml:space="preserve"> + <value>Linker Stick</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbDown" xml:space="preserve"> + <value>Linker Stick unten</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbLeft" xml:space="preserve"> + <value>Linker Stick links</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbRight" xml:space="preserve"> + <value>Linker Stick rechts</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbUp" xml:space="preserve"> + <value>Linker Stick oben</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM1" xml:space="preserve"> + <value>OEM1</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM2" xml:space="preserve"> + <value>OEM2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM3" xml:space="preserve"> + <value>OEM3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R1" xml:space="preserve"> + <value>RB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R3" xml:space="preserve"> + <value>R3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R4" xml:space="preserve"> + <value>R4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R5" xml:space="preserve"> + <value>R5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumb" xml:space="preserve"> + <value>Rechter Stick</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbDown" xml:space="preserve"> + <value>Rechter Stick unten</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbLeft" xml:space="preserve"> + <value>Rechter Stick links</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbRight" xml:space="preserve"> + <value>Rechter Stick rechts</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbUp" xml:space="preserve"> + <value>Rechter Stick oben</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Special" xml:space="preserve"> + <value>XBOX-Taste</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Start" xml:space="preserve"> + <value>Menü</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B1" xml:space="preserve"> + <value>Cross</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B2" xml:space="preserve"> + <value>Circle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B3" xml:space="preserve"> + <value>Square</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B4" xml:space="preserve"> + <value>Triangle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Start" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.KeyFlags.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.KeyFlags.Alt" xml:space="preserve"> + <value>Alt</value> + </data> + <data name="Enum.KeyFlags.Apostrophe" xml:space="preserve"> + <value>Apostrophe</value> + </data> + <data name="Enum.KeyFlags.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.KeyFlags.Backslash" xml:space="preserve"> + <value>Backslash</value> + </data> + <data name="Enum.KeyFlags.Backspace" xml:space="preserve"> + <value>Backspace</value> + </data> + <data name="Enum.KeyFlags.C" xml:space="preserve"> + <value>C</value> + </data> + <data name="Enum.KeyFlags.Comma" xml:space="preserve"> + <value>Comma</value> + </data> + <data name="Enum.KeyFlags.Control" xml:space="preserve"> + <value>Control</value> + </data> + <data name="Enum.KeyFlags.D" xml:space="preserve"> + <value>D</value> + </data> + <data name="Enum.KeyFlags.Delete" xml:space="preserve"> + <value>Delete</value> + </data> + <data name="Enum.KeyFlags.E" xml:space="preserve"> + <value>E</value> + </data> + <data name="Enum.KeyFlags.End" xml:space="preserve"> + <value>End</value> + </data> + <data name="Enum.KeyFlags.Enter" xml:space="preserve"> + <value>Enter</value> + </data> + <data name="Enum.KeyFlags.Equal" xml:space="preserve"> + <value>Equal</value> + </data> + <data name="Enum.KeyFlags.Escape" xml:space="preserve"> + <value>Escape</value> + </data> + <data name="Enum.KeyFlags.F" xml:space="preserve"> + <value>F</value> + </data> + <data name="Enum.KeyFlags.F1" xml:space="preserve"> + <value>F1</value> + </data> + <data name="Enum.KeyFlags.F10" xml:space="preserve"> + <value>F10</value> + </data> + <data name="Enum.KeyFlags.F11" xml:space="preserve"> + <value>F11</value> + </data> + <data name="Enum.KeyFlags.F12" xml:space="preserve"> + <value>F12</value> + </data> + <data name="Enum.KeyFlags.F2" xml:space="preserve"> + <value>F2</value> + </data> + <data name="Enum.KeyFlags.F3" xml:space="preserve"> + <value>F3</value> + </data> + <data name="Enum.KeyFlags.F4" xml:space="preserve"> + <value>F4</value> + </data> + <data name="Enum.KeyFlags.F5" xml:space="preserve"> + <value>F5</value> + </data> + <data name="Enum.KeyFlags.F6" xml:space="preserve"> + <value>F6</value> + </data> + <data name="Enum.KeyFlags.F7" xml:space="preserve"> + <value>F7</value> + </data> + <data name="Enum.KeyFlags.F8" xml:space="preserve"> + <value>F8</value> + </data> + <data name="Enum.KeyFlags.F9" xml:space="preserve"> + <value>F9</value> + </data> + <data name="Enum.KeyFlags.G" xml:space="preserve"> + <value>G</value> + </data> + <data name="Enum.KeyFlags.Grave" xml:space="preserve"> + <value>Grave</value> + </data> + <data name="Enum.KeyFlags.H" xml:space="preserve"> + <value>H</value> + </data> + <data name="Enum.KeyFlags.Home" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.KeyFlags.I" xml:space="preserve"> + <value>I</value> + </data> + <data name="Enum.KeyFlags.Insert" xml:space="preserve"> + <value>Insert</value> + </data> + <data name="Enum.KeyFlags.J" xml:space="preserve"> + <value>J</value> + </data> + <data name="Enum.KeyFlags.K" xml:space="preserve"> + <value>K</value> + </data> + <data name="Enum.KeyFlags.L" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.KeyFlags.M" xml:space="preserve"> + <value>M</value> + </data> + <data name="Enum.KeyFlags.Minus" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.KeyFlags.N" xml:space="preserve"> + <value>N</value> + </data> + <data name="Enum.KeyFlags.O" xml:space="preserve"> + <value>O</value> + </data> + <data name="Enum.KeyFlags.P" xml:space="preserve"> + <value>P</value> + </data> + <data name="Enum.KeyFlags.Pause" xml:space="preserve"> + <value>Pause</value> + </data> + <data name="Enum.KeyFlags.Period" xml:space="preserve"> + <value>Period</value> + </data> + <data name="Enum.KeyFlags.Q" xml:space="preserve"> + <value>Q</value> + </data> + <data name="Enum.KeyFlags.R" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.KeyFlags.Semicolon" xml:space="preserve"> + <value>Semicolon</value> + </data> + <data name="Enum.KeyFlags.Shift" xml:space="preserve"> + <value>Shift</value> + </data> + <data name="Enum.KeyFlags.Slash" xml:space="preserve"> + <value>Slash</value> + </data> + <data name="Enum.KeyFlags.Space" xml:space="preserve"> + <value>Space</value> + </data> + <data name="Enum.KeyFlags.T" xml:space="preserve"> + <value>T</value> + </data> + <data name="Enum.KeyFlags.Tab" xml:space="preserve"> + <value>Tab</value> + </data> + <data name="Enum.KeyFlags.U" xml:space="preserve"> + <value>U</value> + </data> + <data name="Enum.KeyFlags.V" xml:space="preserve"> + <value>V</value> + </data> + <data name="Enum.KeyFlags.W" xml:space="preserve"> + <value>W</value> + </data> + <data name="Enum.KeyFlags.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.KeyFlags.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.KeyFlags.Z" xml:space="preserve"> + <value>Z</value> + </data> + <data name="Enum.MotionOuput.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.MotionOuput.MoveCursor" xml:space="preserve"> + <value>MoveCursor</value> + </data> + <data name="Enum.MotionOuput.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="Enum.MotionOuput.ScrollWheel" xml:space="preserve"> + <value>ScrollWheel</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>ZL</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>ZR</value> + </data> + <data name="Enum.ProController.ButtonFlags.B1" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.ProController.ButtonFlags.B2" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.ProController.ButtonFlags.B3" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.ProController.ButtonFlags.B4" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.ProController.ButtonFlags.Back" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.ProController.ButtonFlags.L1" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.ProController.ButtonFlags.R1" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special2" xml:space="preserve"> + <value>Capture</value> + </data> + <data name="Enum.ProController.ButtonFlags.Start" xml:space="preserve"> + <value>Plus</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderClosePrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseText" xml:space="preserve"> + <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="ProfilesPage_Wrapper_Disabled" xml:space="preserve"> + <value>Disabled</value> + </data> + <data name="ProfilesPage_Wrapper_Injection" xml:space="preserve"> + <value>Injection (recommended)</value> + </data> + <data name="ProfilesPage_Wrapper_Redirection" xml:space="preserve"> + <value>Redirection</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrder" xml:space="preserve"> + <value>Improve virtual controller detection</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderDesc" xml:space="preserve"> + <value>Forces the virtual controller to be detected as first controller during Windows start. Enable this if your app/game doesn't detect your inputs (requires device reboot).</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderText" xml:space="preserve"> + <value>Your device must be restarted in order for the changes to take effect. Would you like to restart now?</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderTitle" xml:space="preserve"> + <value>Restart required</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencySecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyText" xml:space="preserve"> + <value>Disabling this setting will also disable "Improve virtual controller detection". Do you want to continue?</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="Enum.KeyFlags.S" xml:space="preserve"> + <value>S</value> + </data> + <data name="Controller_Connect" xml:space="preserve"> + <value>Connect</value> + </data> + <data name="Controller_Disconnect" xml:space="preserve"> + <value>Disconnect</value> + </data> + <data name="Controller_Hide" xml:space="preserve"> + <value>Hide</value> + </data> + <data name="Controller_Unhide" xml:space="preserve"> + <value>Unhide</value> + </data> + <data name="Controller_Virtual" xml:space="preserve"> + <value>Virtual </value> + </data> + <data name="AboutPage_Accelerometer" xml:space="preserve"> + <value>Accelerometer</value> + </data> + <data name="MainWindow_Back" xml:space="preserve"> + <value>Back</value> + </data> + <data name="MainWindow_Exit" xml:space="preserve"> + <value>Exit</value> + </data> + <data name="LayoutPage_ShowCurrentControllerTemplates" xml:space="preserve"> + <value>Show current controller templates only</value> + </data> + <data name="MainWindow_MainWindow" xml:space="preserve"> + <value>Main Window</value> + </data> + <data name="MainWindow_Navigate" xml:space="preserve"> + <value>Navigate</value> + </data> + <data name="MainWindow_QuickTools" xml:space="preserve"> + <value>Quick Tools</value> + </data> + <data name="MainWindow_Select" xml:space="preserve"> + <value>Select</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_External" xml:space="preserve"> + <value>External</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate1" xml:space="preserve"> + <value>Are you sure you want to apply this template? All layout settings will be overridden.</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate2" xml:space="preserve"> + <value>You can't undo this action. Previously applied template: {0}</value> + </data> + <data name="QuickProfilesPage_Waiting" xml:space="preserve"> + <value>Waiting for foreground process...</value> + </data> + <data name="LayoutPage_SetAsDefault" xml:space="preserve"> + <value>Make this the default layout</value> + </data> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </root> \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.es-ES.resx b/HandheldCompanion/Properties/Resources.es-ES.resx index 863066413..1c0205bf7 100644 --- a/HandheldCompanion/Properties/Resources.es-ES.resx +++ b/HandheldCompanion/Properties/Resources.es-ES.resx @@ -1,3 +1,4 @@ +<<<<<<< HEAD <?xml version="1.0" encoding="utf-8"?> <root> <!-- @@ -2356,4 +2357,2331 @@ <data name="IController_ControllerIndex" xml:space="preserve"> <value>Gamepad: {0}</value> </data> +======= +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="AboutPage_About" xml:space="preserve"> + <value>Acerca de</value> + </data> + <data name="AboutPage_AboutDescription" xml:space="preserve"> + <value>Una combinación de un servicio de Windows y una interfaz gráfica de usuario optimizada para aumentar la experiencia de su computadora portátil para juegos. Las características incluyen: control de movimiento, también conocido como control giroscópico, simulación de controlador virtual, superposición de herramientas rápidas, paneles táctiles virtuales, modelo de controlador 3D, sistema de configuración de perfil basado en la aplicación. Handheld Companion se basa en el controlador ViGEmBus y las bibliotecas ViGEmClient, así como en el controlador de filtro en modo kernel HidHide. Los algoritmos de control de movimiento se basan en el trabajo de Jibbsmart y en la información disponible en GyroWiki.</value> + </data> + <data name="AboutPage_Accelerometer" xml:space="preserve"> + <value>Acelerometro</value> + </data> + <data name="AboutPage_Author" xml:space="preserve"> + <value>Autor</value> + </data> + <data name="AboutPage_Contributors" xml:space="preserve"> + <value>Contribuyentes</value> + </data> + <data name="AboutPage_Description" xml:space="preserve"> + <value>Descripcion</value> + </data> + <data name="AboutPage_Donate" xml:space="preserve"> + <value>Donar</value> + </data> + <data name="AboutPage_Gyrometer" xml:space="preserve"> + <value>Inclinómetro</value> + </data> + <data name="AboutPage_Inclinometer" xml:space="preserve"> + <value>Clinómetro</value> + </data> + <data name="AboutPage_NotApplicable" xml:space="preserve"> + <value>N/A</value> + </data> + <data name="AboutPage_RelatedLinks" xml:space="preserve"> + <value>Enlaces Relacionados</value> + </data> + <data name="AboutPage_SensorExternal" xml:space="preserve"> + <value>External</value> + </data> + <data name="AboutPage_SensorInternal" xml:space="preserve"> + <value>Internal</value> + </data> + <data name="AboutPage_SensorName" xml:space="preserve"> + <value>Nombre del Sensor</value> + </data> + <data name="AboutPage_SensorSpecification" xml:space="preserve"> + <value>Especificaciones del Sensor</value> + </data> + <data name="AboutPage_Service" xml:space="preserve"> + <value>Servicio</value> + </data> + <data name="AboutPage_SourceCode" xml:space="preserve"> + <value>Codigo Fuente</value> + </data> + <data name="AboutPage_Version" xml:space="preserve"> + <value>Version</value> + </data> + <data name="AboutPage_Wiki" xml:space="preserve"> + <value>Wiki</value> + </data> + <data name="Administrator" xml:space="preserve"> + <value>Administrador</value> + </data> + <data name="ControllerPage_CloakDevice" xml:space="preserve"> + <value>Ocultar mando al conectar</value> + </data> + <data name="ControllerPage_CloakDeviceDesc" xml:space="preserve"> + <value>Ocultar el mando físico cuando está conectado</value> + </data> + <data name="ControllerPage_Connect" xml:space="preserve"> + <value>Conectar</value> + </data> + <data name="ControllerPage_Controller" xml:space="preserve"> + <value>Mando</value> + </data> + <data name="ControllerPage_DeviceCloaking" xml:space="preserve"> + <value>Encubrimiento del controlador</value> + </data> + <data name="ControllerPage_DeviceSettings" xml:space="preserve"> + <value>Opciones del mando</value> + </data> + <data name="ControllerPage_Disconnect" xml:space="preserve"> + <value>Desconectar</value> + </data> + <data name="ControllerPage_InputDevices" xml:space="preserve"> + <value>Dispositivos de entrada</value> + </data> + <data name="ControllerPage_UncloakOnClose" xml:space="preserve"> + <value>Esconder mando o cerrar</value> + </data> + <data name="ControllerPage_UncloakOnCloseDesc" xml:space="preserve"> + <value>Restaurar la visibilidad de todos los mandos físicos cuando se sale de la aplicación</value> + </data> + <data name="ControllerPage_VibrationStrength" xml:space="preserve"> + <value>Intensidad de vibración</value> + </data> + <data name="ControllerPage_VibrationStrengthExpl" xml:space="preserve"> + <value>Cambiar intensidad de vibración del mando</value> + </data> + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\icon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="InputsHotkey_decreaseTDP" xml:space="preserve"> + <value>Reducir el límite de potencia térmica (TDP)</value> + </data> + <data name="InputsHotkey_decreaseTDPDesc" xml:space="preserve"> + <value>Disminuya el TDP del sistema o del perfil actualmente aplicado en 1W</value> + </data> + <data name="InputsHotkey_fallbackInput" xml:space="preserve"> + <value>Presione para definir el gatillo</value> + </data> + <data name="InputsHotkey_fallbackOutput" xml:space="preserve"> + <value>Presione para definir la salida del teclado</value> + </data> + <data name="InputsHotkey_increaseTDP" xml:space="preserve"> + <value>Aumentar el límite de potencia térmica (TDP)</value> + </data> + <data name="InputsHotkey_increaseTDPDesc" xml:space="preserve"> + <value>Aumente el TDP del sistema o del perfil actualmente aplicado en 1W</value> + </data> + <data name="InputsHotkey_overlayGamepad" xml:space="preserve"> + <value>Controlador de pantalla 3D</value> + </data> + <data name="InputsHotkey_overlayGamepadDesc" xml:space="preserve"> + <value>Cambie la tecla de acceso rápido 3D presionando un botón o una tecla especial</value> + </data> + <data name="InputsHotkey_overlayTrackpads" xml:space="preserve"> + <value>Mostrar trackpads virtuales</value> + </data> + <data name="InputsHotkey_overlayTrackpadsDesc" xml:space="preserve"> + <value>Cambie la tecla de acceso rápido presionando un botón o una tecla especial</value> + </data> + <data name="InputsHotkey_quickTools" xml:space="preserve"> + <value>Abrir ventana de herramientas rápidas</value> + </data> + <data name="InputsHotkey_quickToolsDesc" xml:space="preserve"> + <value>Cambie la tecla de acceso rápido presionando un botón o una tecla especial</value> + </data> + <data name="InputsHotkey_shortcutCustom" xml:space="preserve"> + <value>Acceso directo personalizado</value> + </data> + <data name="InputsHotkey_shortcutCustomDesc" xml:space="preserve"> + <value>Cambie la tecla de acceso rápido presionando un botón o una tecla especial</value> + </data> + <data name="InputsHotkey_shortcutDesktop" xml:space="preserve"> + <value>Mostrar y ocultar el escritorio</value> + </data> + <data name="InputsHotkey_shortcutDesktopDesc" xml:space="preserve"> + <value>Presiona esta tecla: Windows + D</value> + </data> + <data name="InputsHotkey_shortcutESC" xml:space="preserve"> + <value>Esc</value> + </data> + <data name="InputsHotkey_shortcutESCDesc" xml:space="preserve"> + <value>Presiona la tecla: Esc</value> + </data> + <data name="InputsHotkey_shortcutExpand" xml:space="preserve"> + <value>Cambia entre ventana y pantalla completa</value> + </data> + <data name="InputsHotkey_shortcutExpandDesc" xml:space="preserve"> + <value>Presiona la tecla: Enter</value> + </data> + <data name="InputsHotkey_shortcutGuide" xml:space="preserve"> + <value>Boton Guia o Boton PS</value> + </data> + <data name="InputsHotkey_shortcutGuideDesc" xml:space="preserve"> + <value>Simular boton Xbox Guia o boton Sony PS</value> + </data> + <data name="InputsHotkey_shortcutKeyboard" xml:space="preserve"> + <value>Mostrar teclado</value> + </data> + <data name="InputsHotkey_shortcutKeyboardDesc" xml:space="preserve"> + <value>Cambie la tecla de acceso rápido presionando un botón o una tecla especial</value> + </data> + <data name="InputsHotkey_shortcutKillApp" xml:space="preserve"> + <value>Forzar el cierre de la aplicación</value> + </data> + <data name="InputsHotkey_shortcutKillAppDesc" xml:space="preserve"> + <value>Cambie la tecla de acceso rápido presionando un botón o una tecla especial</value> + </data> + <data name="InputsHotkey_shortcutMainwindow" xml:space="preserve"> + <value>Mostrar y ocultar la menú principal</value> + </data> + <data name="InputsHotkey_shortcutMainwindowDesc" xml:space="preserve"> + <value>Cambie la tecla de acceso rápido presionando un botón o una tecla especial</value> + </data> + <data name="InputsHotkey_shortcutTaskManager" xml:space="preserve"> + <value>Abrir administrador de tareas</value> + </data> + <data name="InputsHotkey_shortcutTaskManagerDesc" xml:space="preserve"> + <value>Presiona las teclas: Ctrl + Shift + Esc</value> + </data> + <data name="InputsHotkey_shortcutTaskview" xml:space="preserve"> + <value>Abrir vista de tareas</value> + </data> + <data name="InputsHotkey_shortcutTaskviewDesc" xml:space="preserve"> + <value>Pulsa las teclas: Windows + Tab</value> + </data> + <data name="InputsHotkey_suspendResumeTask" xml:space="preserve"> + <value>Toggle Suspender</value> + </data> + <data name="InputsHotkey_suspendResumeTaskDesc" xml:space="preserve"> + <value>Suspender o reanudar la aplicación en primer plano</value> + </data> + <data name="MainWindow_HandheldCompanion" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="MainWindow_navAbout" xml:space="preserve"> + <value>Acerca de</value> + </data> + <data name="MainWindow_navController" xml:space="preserve"> + <value>Mando</value> + </data> + <data name="MainWindow_navOverlay" xml:space="preserve"> + <value>Superposicion</value> + </data> + <data name="MainWindow_navProfiles" xml:space="preserve"> + <value>Perfiles</value> + </data> + <data name="MainWindow_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="MainWindow_Settings" xml:space="preserve"> + <value>Opciones</value> + </data> + <data name="OverlayPage_8BitDoLite2Controller" xml:space="preserve"> + <value>8BitDo Lite 2</value> + </data> + <data name="OverlayPage_Alignment" xml:space="preserve"> + <value>Alineación</value> + </data> + <data name="OverlayPage_AlignmentDesc" xml:space="preserve"> + <value>Cambiar la alineación de la superposición del controlador 3D</value> + </data> + <data name="OverlayPage_AlignmentTrackpadDesc" xml:space="preserve"> + <value>Cambiar la alineación de la superposición de los trackpads</value> + </data> + <data name="OverlayPage_AlwaysOnTop" xml:space="preserve"> + <value>Siempre arriba</value> + </data> + <data name="OverlayPage_AlwaysOnTopDesc" xml:space="preserve"> + <value>Cuando se activa, la superposición del controlador 3D se sobrepondrá encima de otras ventanas</value> + </data> + <data name="OverlayPage_BackButton" xml:space="preserve"> + <value>Atras</value> + </data> + <data name="OverlayPage_CameraAngle" xml:space="preserve"> + <value>Reconocimiento facial</value> + </data> + <data name="OverlayPage_CameraAngleDesc" xml:space="preserve"> + <value>Cambie el comportamiento del modelo de superposición del controlador 3D para mirar hacia la cámara</value> + </data> + <data name="OverlayPage_CameraAnglePitch" xml:space="preserve"> + <value>Inmovilizar angulo de la camara</value> + </data> + <data name="OverlayPage_CameraAnglePitchDesc" xml:space="preserve"> + <value>Cambiar el ángulo, en grados</value> + </data> + <data name="OverlayPage_Color" xml:space="preserve"> + <value>Color de fondo</value> + </data> + <data name="OverlayPage_ColorDesc" xml:space="preserve"> + <value>Cambiar el color de fondo de la superposición del controlador 3D</value> + </data> + <data name="OverlayPage_ControllerOptions" xml:space="preserve"> + <value>Opciones del Mando</value> + </data> + <data name="OverlayPage_DualSenseController" xml:space="preserve"> + <value>PlayStation DualSense</value> + </data> + <data name="OverlayPage_EmulatedController" xml:space="preserve"> + <value>Emulacion mando</value> + </data> + <data name="OverlayPage_FaceCamera" xml:space="preserve"> + <value>Camara facial</value> + </data> + <data name="OverlayPage_FaceCameraDesc" xml:space="preserve"> + <value>El modelo 3D gira lentamente para mirar a la cámara como posición predeterminada</value> + </data> + <data name="OverlayPage_Listening" xml:space="preserve"> + <value>Escuchando...</value> + </data> + <data name="OverlayPage_MachenikeHG510Controller" xml:space="preserve"> + <value>MACHENIKE HG510</value> + </data> + <data name="OverlayPage_MainTrigger" xml:space="preserve"> + <value>Gatillo Principal</value> + </data> + <data name="OverlayPage_MainTriggerDesc" xml:space="preserve"> + <value>Cambiar el gatillo principal de superposición del mando 3D</value> + </data> + <data name="OverlayPage_MotionActivated" xml:space="preserve"> + <value>Movimiento</value> + </data> + <data name="OverlayPage_MotionActivatedDesc" xml:space="preserve"> + <value>El modelo se moverá de acuerdo con los movimientos del usuario según la información del sensor</value> + </data> + <data name="OverlayPage_N64Controller" xml:space="preserve"> + <value>N64</value> + </data> + <data name="OverlayPage_OEMController" xml:space="preserve"> + <value>Controlador OEM</value> + </data> + <data name="OverlayPage_Opacity" xml:space="preserve"> + <value>Opacidad</value> + </data> + <data name="OverlayPage_OpacityControllerDesc" xml:space="preserve"> + <value>Cambiar la opacidad de superposición del mando 3D</value> + </data> + <data name="OverlayPage_OpacityTrackpadDesc" xml:space="preserve"> + <value>Cambiar la opacidad de la superposición de los trackpads</value> + </data> + <data name="OverlayPage_Overlay" xml:space="preserve"> + <value>Sobreposicion</value> + </data> + <data name="OverlayPage_OverlayModel" xml:space="preserve"> + <value>Modelo de sobreposicion</value> + </data> + <data name="OverlayPage_OverlayModelDesc" xml:space="preserve"> + <value>Cambiar el modelo de sobreposicion del mando 3D</value> + </data> + <data name="OverlayPage_OverlayPreview" xml:space="preserve"> + <value>Vista previa sobreposicion</value> + </data> + <data name="OverlayPage_RenderSettings" xml:space="preserve"> + <value>Configuración de procesamiento</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasing" xml:space="preserve"> + <value>Anti-Aliasing</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasingDesc" xml:space="preserve"> + <value>Cambiar el estado del suavizado de renderizado</value> + </data> + <data name="OverlayPage_RenderSettingsDesc" xml:space="preserve"> + <value>Cambie la configuración de procesamiento de su modelo de mando de superposición 3D</value> + </data> + <data name="OverlayPage_RenderSettingsFramerate" xml:space="preserve"> + <value>Frecuencia de actualización</value> + </data> + <data name="OverlayPage_RenderSettingsFramerateDesc" xml:space="preserve"> + <value>Cambiar la tasa de actualización de procesamiento, en actualización por segundo</value> + </data> + <data name="OverlayPage_Size" xml:space="preserve"> + <value>Tamaño</value> + </data> + <data name="OverlayPage_SizeDesc" xml:space="preserve"> + <value>Cambiar el tamaño de superposición del mando 3D</value> + </data> + <data name="OverlayPage_SizeOverlayDesc" xml:space="preserve"> + <value>Cambiar el tamaño de superposición de los trackpads</value> + </data> + <data name="OverlayPage_StartButton" xml:space="preserve"> + <value>Start</value> + </data> + <data name="OverlayPage_ToyController" xml:space="preserve"> + <value>Controlador de Fisher-Price</value> + </data> + <data name="OverlayPage_TrackpadsOptions" xml:space="preserve"> + <value>Opciones de Trackpads</value> + </data> + <data name="OverlayPage_XboxOneController" xml:space="preserve"> + <value>Xbox One</value> + </data> + <data name="OverlayPage_ZDOPlusController" xml:space="preserve"> + <value>ZD O+</value> + </data> + <data name="Overlay_Overlay" xml:space="preserve"> + <value>Superponer</value> + </data> + <data name="ProcessEx_processResume" xml:space="preserve"> + <value>Reanudar</value> + </data> + <data name="ProcessEx_processSuspend" xml:space="preserve"> + <value>Suspender</value> + </data> + <data name="SettingsMode0_AdditionalSettings" xml:space="preserve"> + <value>Opciones adicionales</value> + </data> + <data name="SettingsMode0_AimingDownSights" xml:space="preserve"> + <value>Multiplicador de movimiento apuntando hacia abajo</value> + </data> + <data name="SettingsMode0_AimingDownSightsActivation" xml:space="preserve"> + <value>Boton de activación</value> + </data> + <data name="SettingsMode0_AimingDownSightsDesc" xml:space="preserve"> + <value>Un multiplicador de sensibilidad de movimiento adicional al apuntar hacia abajo o el alcance mediante el uso del botón de activación configurado</value> + </data> + <data name="SettingsMode0_AimingDownSightsMultiplier" xml:space="preserve"> + <value>Valor multiplicador</value> + </data> + <data name="SettingsMode0_CameraOptions" xml:space="preserve"> + <value>Opciones de camara</value> + </data> + <data name="SettingsMode0_CustomResponseCurve" xml:space="preserve"> + <value>Curva de respuesta personalizada</value> + </data> + <data name="SettingsMode0_CustomResponseCurveGameOutput" xml:space="preserve"> + <value>Salida enviada al juego</value> + </data> + <data name="SettingsMode0_CustomResponseIntensity" xml:space="preserve"> + <value>Intensidad de movimiento</value> + </data> + <data name="SettingsMode0_CustomResponsePresetAgressive" xml:space="preserve"> + <value>Agresivo</value> + </data> + <data name="SettingsMode0_CustomResponsePresetDefault" xml:space="preserve"> + <value>Por defecto</value> + </data> + <data name="SettingsMode0_CustomResponsePresetOptions" xml:space="preserve"> + <value>Opciones preestablecidas</value> + </data> + <data name="SettingsMode0_CustomResponsePresetPrecise" xml:space="preserve"> + <value>Precisa</value> + </data> + <data name="SettingsMode0_FlickDuration" xml:space="preserve"> + <value>Duración del movimiento</value> + </data> + <data name="SettingsMode0_FlickDurationDesc" xml:space="preserve"> + <value>Cambie la duración del movimiento rápido, calibre a un giro de 180 grados, en milisegundos</value> + </data> + <data name="SettingsMode0_FlickStick" xml:space="preserve"> + <value>Flick stick (experimental)</value> + </data> + <data name="SettingsMode0_FlickStickDesc" xml:space="preserve"> + <value>Apunte la cámara en la dirección del movimiento del joystick (derecho), gire la cámara puramente en el plano horizontal girando</value> + </data> + <data name="SettingsMode0_FlickStickEnable" xml:space="preserve"> + <value>Habilitar dispositivo móvil</value> + </data> + <data name="SettingsMode0_Sensitivity" xml:space="preserve"> + <value>Sensibilidad</value> + </data> + <data name="SettingsMode0_SensitivityDesc" xml:space="preserve"> + <value>Cambiar la sensibilidad de movimiento del eje horizontal y vertical</value> + </data> + <data name="SettingsMode0_SensitivityX" xml:space="preserve"> + <value>Sensibilidad X</value> + </data> + <data name="SettingsMode0_SensitivityXDesc" xml:space="preserve"> + <value>Cambiar la sensibilidad de movimiento del eje horizontal</value> + </data> + <data name="SettingsMode0_SensitivityY" xml:space="preserve"> + <value>Sensibilidad Y</value> + </data> + <data name="SettingsMode0_SensitivityYDesc" xml:space="preserve"> + <value>Cambiar la sensibilidad de movimiento del eje vertical</value> + </data> + <data name="SettingsMode0_StickSensitivtity" xml:space="preserve"> + <value>Sensibilidad del stick</value> + </data> + <data name="SettingsMode0_StickSensitivtityDesc" xml:space="preserve"> + <value>Cambiar la sensibilidad de movimiento del eje vertical</value> + </data> + <data name="SettingsMode1_AdditionalSettings" xml:space="preserve"> + <value>Opciones adicionales</value> + </data> + <data name="SettingsMode1_Deadzone" xml:space="preserve"> + <value>Zona muerta</value> + </data> + <data name="SettingsMode1_DeadzoneDesc" xml:space="preserve"> + <value>Cambia la zona muerta de la dirección, en grados. Mejora la dirección recta</value> + </data> + <data name="SettingsMode1_JoystickGameInput" xml:space="preserve"> + <value>Joystick game Input</value> + </data> + <data name="SettingsMode1_JoystickSteering" xml:space="preserve"> + <value>Dirección con joystick</value> + </data> + <data name="SettingsMode1_JoystickSteeringOptions" xml:space="preserve"> + <value>Opciones de dirección con joystick</value> + </data> + <data name="SettingsMode1_JoystickSteeringPreview" xml:space="preserve"> + <value>Vista previa de la dirección con joystick</value> + </data> + <data name="SettingsMode1_MaxSteeringAngle" xml:space="preserve"> + <value>Ángulo de dirección máximo</value> + </data> + <data name="SettingsMode1_MaxSteeringAngleDesc" xml:space="preserve"> + <value>Cambiar el valor máximo del ángulo de dirección, en grados</value> + </data> + <data name="SettingsMode1_SteeringLinearity" xml:space="preserve"> + <value>Linealidad de dirección</value> + </data> + <data name="SettingsMode1_SteeringLinearityDesc" xml:space="preserve"> + <value>Mapeo entre entrada y dirección. Los valores más bajos proporcionan más precisión cerca del enganche completo pero menos precisión cerca del centro. Los valores más altos proporcionan más precisión cerca del centro pero menos precisión cerca del enganche completo. 1.0 es un mapeo lineal</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplier" xml:space="preserve"> + <value>Multiplicador de acelerómetro</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplierDesc" xml:space="preserve"> + <value>Cambiar el valor del acelerómetro informado por el sistema</value> + </data> + <data name="ProfilesPage_AdditionalSettings" xml:space="preserve"> + <value>Opciones adicionales</value> + </data> + <data name="ProfilesPage_AntiDeadzone" xml:space="preserve"> + <value>Anti-zona muerta</value> + </data> + <data name="ProfilesPage_AntiDeadzoneDesc" xml:space="preserve"> + <value>Cambiar la anti-zona muerta del juego, en porcentaje</value> + </data> + <data name="ProfilesPage_AntiDeadzoneUnitPercentage" xml:space="preserve"> + <value>%</value> + </data> + <data name="ProfilesPage_AreYouSureDelete1" xml:space="preserve"> + <value>Estás seguro de que quieres eliminar?</value> + </data> + <data name="ProfilesPage_AreYouSureDelete2" xml:space="preserve"> + <value>Este artículo será eliminado inmediatamente. No puede deshacer esta acción.</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite1" xml:space="preserve"> + <value>Estás seguro de que quieres sobrescribir este perfil?</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite2" xml:space="preserve"> + <value>{0} se sobrescribirá. No puede deshacer esta acción.</value> + </data> + <data name="ProfilesPage_BoostPower" xml:space="preserve"> + <value>Aumento del limite de potencia</value> + </data> + <data name="ProfilesPage_BoostPowerDesc" xml:space="preserve"> + <value>Cambiar el límite de potencia térmica de impulso</value> + </data> + <data name="ProfilesPage_Cancel" xml:space="preserve"> + <value>Cancelar</value> + </data> + <data name="ProfilesPage_CreateNewProfile" xml:space="preserve"> + <value>Crear un nuevo perfil</value> + </data> + <data name="ProfilesPage_Delete" xml:space="preserve"> + <value>Eliminar</value> + </data> + <data name="ProfilesPage_DeleteProfile" xml:space="preserve"> + <value>Eliminar perfil</value> + </data> + <data name="ProfilesPage_EnableProfile" xml:space="preserve"> + <value>Perfil de usuario por juego</value> + </data> + <data name="ProfilesPage_EnableProfileDesc" xml:space="preserve"> + <value>El perfil se aplicará automáticamente cuando se detecte la aplicación asociada</value> + </data> + <data name="ProfilesPage_GlobalSettings" xml:space="preserve"> + <value>Opciones globales</value> + </data> + <data name="ProfilesPage_GlobalSettingsDesc" xml:space="preserve"> + <value>Cambiar la configuración de perfiles globales</value> + </data> + <data name="ProfilesPage_GyrometerMultiplier" xml:space="preserve"> + <value>Multiplicador de girómetro</value> + </data> + <data name="ProfilesPage_GyrometerMultiplierDesc" xml:space="preserve"> + <value>Cambiar el valor del girómetro informado por el sistema</value> + </data> + <data name="ProfilesPage_GyroSteeringAxis" xml:space="preserve"> + <value>Eje de dirección del giroscopio</value> + </data> + <data name="ProfilesPage_GyroSteeringAxisDesc" xml:space="preserve"> + <value>Para controlar el movimiento horizontal del controlador,puede usar el eje de yaw o balanceo</value> + </data> + <data name="ProfilesPage_InvertHorizontalAxis" xml:space="preserve"> + <value>Invertir el eje horizontal</value> + </data> + <data name="ProfilesPage_InvertVerticalAxis" xml:space="preserve"> + <value>Invertir el eje vertical</value> + </data> + <data name="ProfilesPage_MotionControlSettings" xml:space="preserve"> + <value>Ajustes de control de movimiento</value> + </data> + <data name="ProfilesPage_MotionControlSettingsDesc" xml:space="preserve"> + <value>Cambiar la configuración de control de movimiento global</value> + </data> + <data name="ProfilesPage_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="ProfilesPage_PowerSettings" xml:space="preserve"> + <value>configuración de energía</value> + </data> + <data name="ProfilesPage_PowerSettingsDesc" xml:space="preserve"> + <value>Cambiar la configuración de energía</value> + </data> + <data name="ProfilesPage_ProfileDetails" xml:space="preserve"> + <value>Detalles del perfil</value> + </data> + <data name="ProfilesPage_ProfileName" xml:space="preserve"> + <value>Nombre del perfil</value> + </data> + <data name="ProfilesPage_ProfilePath" xml:space="preserve"> + <value>Ruta del perfil</value> + </data> + <data name="ProfilesPage_Profiles" xml:space="preserve"> + <value>Perfiles</value> + </data> + <data name="ProfilesPage_ProfileSelection" xml:space="preserve"> + <value>Seleccion de perfil</value> + </data> + <data name="ProfilesPage_ProfileSelectionDesc" xml:space="preserve"> + <value>Selecciona el perfil que quieres modificar</value> + </data> + <data name="ProfilesPage_ProfileSettings" xml:space="preserve"> + <value>Detalles del perfil</value> + </data> + <data name="ProfilesPage_ProfileUpdated1" xml:space="preserve"> + <value>Perfil actualizado</value> + </data> + <data name="ProfilesPage_ProfileUpdated2" xml:space="preserve"> + <value>Fue actualizado</value> + </data> + <data name="ProfilesPage_Roll" xml:space="preserve"> + <value>Balanceo</value> + </data> + <data name="ProfilesPage_SensitivityX" xml:space="preserve"> + <value>X</value> + </data> + <data name="ProfilesPage_SensitivityY" xml:space="preserve"> + <value>Y</value> + </data> + <data name="ProfilesPage_StyleofInput" xml:space="preserve"> + <value>Estilo de entrada</value> + </data> + <data name="ProfilesPage_StyleofInputTooltip" xml:space="preserve"> + <value>Las entradas físicas del controlador se pueden programar para actuar como diferentes tipos de dispositivos</value> + </data> + <data name="ProfilesPage_StyleofOutput" xml:space="preserve"> + <value>Dispositivo de salida</value> + </data> + <data name="ProfilesPage_StyleofOutputTooltip" xml:space="preserve"> + <value>Seleccione el dispositivo que recibirá los comandos de movimiento</value> + </data> + <data name="ProfilesPage_SustainedPower" xml:space="preserve"> + <value>Límite de potencia sostenida</value> + </data> + <data name="ProfilesPage_SustainedPowerDesc" xml:space="preserve"> + <value>Cambiar límite de potencia térmica sostenida</value> + </data> + <data name="ProfilesPage_TDPOverride" xml:space="preserve"> + <value>Límite de potencia térmica (TDP)</value> + </data> + <data name="ProfilesPage_TDPOverrideDesc" xml:space="preserve"> + <value>Limita la potencia del procesador para reducir la potencia total</value> + </data> + <data name="ProfilesPage_UMCEnable" xml:space="preserve"> + <value>Habilitar controlador de movimiento universal</value> + </data> + <data name="ProfilesPage_UMCMotionOff" xml:space="preserve"> + <value>Deshabilitado, encender con botón(es)</value> + </data> + <data name="ProfilesPage_UMCMotionOn" xml:space="preserve"> + <value>Habilitado, apaga con botón(es)</value> + </data> + <data name="ProfilesPage_UMCMotionOnOff" xml:space="preserve"> + <value>Activación de movimiento</value> + </data> + <data name="ProfilesPage_UMCMotionOnOffDesc" xml:space="preserve"> + <value>Compensa la zona muerta del juego, mejora el registro de pequeños movimientos</value> + </data> + <data name="ProfilesPage_UMCSelectionRightLeftDesc" xml:space="preserve"> + <value>Seleccione el dispositivo que recibirá los comandos de movimiento</value> + </data> + <data name="ProfilesPage_UMCSettings" xml:space="preserve"> + <value>Ajustes de control de movimiento universal</value> + </data> + <data name="ProfilesPage_UMCSettingsDesc" xml:space="preserve"> + <value>Traducir los movimientos del dispositivo en entradas del controlador</value> + </data> + <data name="ProfilesPage_UpdateProfile" xml:space="preserve"> + <value>Actualización del perfil</value> + </data> + <data name="ProfilesPage_Whitelist" xml:space="preserve"> + <value>Permitir que la aplicación acceda al controlador físico del dispositivo</value> + </data> + <data name="ProfilesPage_Wrapper" xml:space="preserve"> + <value>Compatibilidad extendida (XInputPlus)</value> + </data> + <data name="ProfilesPage_Yaw" xml:space="preserve"> + <value>Yaw</value> + </data> + <data name="ProfilesPage_Yes" xml:space="preserve"> + <value>Si</value> + </data> + <data name="Properties.Resources.ControllerPage_Disconnect" xml:space="preserve"> + <value>Desconectar</value> + </data> + <data name="QuickPerformancePage_GPUControl" xml:space="preserve"> + <value>Control manual del reloj de la GPU</value> + </data> + <data name="QuickPerformancePage_GPUControlDesc" xml:space="preserve"> + <value>Establece la GPU en un reloj fijo</value> + </data> + <data name="QuickPerformancePage_GPUUnit" xml:space="preserve"> + <value>MHz</value> + </data> + <data name="QuickPerformancePage_PowerMode" xml:space="preserve"> + <value>Modo de energia</value> + </data> + <data name="QuickPerformancePage_PowerModeBalanced" xml:space="preserve"> + <value>Equilibrado</value> + </data> + <data name="QuickPerformancePage_PowerModeDesc" xml:space="preserve"> + <value>Optimice su dispositivo según el uso de energía y el rendimiento</value> + </data> + <data name="QuickPerformancePage_PowerModeEfficiency" xml:space="preserve"> + <value>Eficiencia</value> + </data> + <data name="QuickPerformancePage_PowerModePerformance" xml:space="preserve"> + <value>Rendimiento</value> + </data> + <data name="QuickPerformancePage_TDPBoost" xml:space="preserve"> + <value>Aumentar</value> + </data> + <data name="QuickPerformancePage_TDPLimit" xml:space="preserve"> + <value>Límite de potencia térmica (TDP)</value> + </data> + <data name="QuickPerformancePage_TDPLimitDesc" xml:space="preserve"> + <value>Limita la potencia del procesador para reducir la potencia total</value> + </data> + <data name="QuickPerformancePage_TDPOverWrittenWarning" xml:space="preserve"> + <value>El límite de potencia térmica se sobrescribe con un perfil</value> + </data> + <data name="QuickPerformancePage_TDPSustained" xml:space="preserve"> + <value>Sostenido</value> + </data> + <data name="QuickPerformancePage_TDPUnitWatt" xml:space="preserve"> + <value> W</value> + </data> + <data name="QuickProfilesPage_Create" xml:space="preserve"> + <value>Crear perfil</value> + </data> + <data name="QuickProfilesPage_Waiting" xml:space="preserve"> + <value>Esperando el proceso de primer plano...</value> + </data> + <data name="ServiceDescription" xml:space="preserve"> + <value>Brinda soporte de giroscopio y acelerómetro a las computadoras de juegos portátiles de Windows a través de un controlador virtual. Si el servicio está habilitado, el controlador incorporado se ocultará a las aplicaciones fuera de la lista blanca. Si el servicio está deshabilitado, el controlador integrado se desenmascarará y el controlador virtual se deshabilitará.</value> + </data> + <data name="ServiceName" xml:space="preserve"> + <value>Servicio del controlador</value> + </data> + <data name="SettingsPage_AppLanguage" xml:space="preserve"> + <value>Idioma</value> + </data> + <data name="SettingsPage_AppLanguageDesc" xml:space="preserve"> + <value>Idioma de la aplicacion</value> + </data> + <data name="SettingsPage_AppLanguageWarning" xml:space="preserve"> + <value>Reinicio necesario</value> + </data> + <data name="SettingsPage_AppLanguageWarningDesc" xml:space="preserve"> + <value>Para que los cambios surtan efecto, por favor reinicie la aplicación</value> + </data> + <data name="SettingsPage_AppTheme" xml:space="preserve"> + <value>Tema de la aplicacion</value> + </data> + <data name="SettingsPage_AppThemeDesc" xml:space="preserve"> + <value>Tema de aplicacion, oscuro o claro</value> + </data> + <data name="SettingsPage_AutoStartApp" xml:space="preserve"> + <value>Auto-inicio de la aplicacion</value> + </data> + <data name="SettingsPage_AutoStartAppDesc" xml:space="preserve"> + <value>La aplicacion iniciara al iniciar windows</value> + </data> + <data name="SettingsPage_Backdrop" xml:space="preserve"> + <value>Aplicacion de fondo</value> + </data> + <data name="SettingsPage_BackdropAcrylic" xml:space="preserve"> + <value>Acrilico</value> + </data> + <data name="SettingsPage_BackdropDesc" xml:space="preserve"> + <value>El fondo de la aplicación, ninguno, mica, tabulado, acrílico</value> + </data> + <data name="SettingsPage_BackdropMica" xml:space="preserve"> + <value>Mica</value> + </data> + <data name="SettingsPage_BackdropNone" xml:space="preserve"> + <value>Ninguno</value> + </data> + <data name="SettingsPage_BackdropTabbed" xml:space="preserve"> + <value>Pestañas</value> + </data> + <data name="SettingsPage_CheckForUpdates" xml:space="preserve"> + <value>Buscar actualizaciones</value> + </data> + <data name="SettingsPage_CloseMinimizes" xml:space="preserve"> + <value>Cerrar, minimiza el programa</value> + </data> + <data name="SettingsPage_CloseMinimizesDesc" xml:space="preserve"> + <value>La aplicación se minimizará en lugar de cerrarse</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStart" xml:space="preserve"> + <value>Habilitar perfil de escritorio al inicio</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStartDesc" xml:space="preserve"> + <value>El perfil de escritorio se habilitará automáticamente al iniciar la aplicación</value> + </data> + <data name="SettingsPage_Download" xml:space="preserve"> + <value>Descargar</value> + </data> + <data name="SettingsPage_DownloadingPercentage" xml:space="preserve"> + <value>Descargando</value> + </data> + <data name="SettingsPage_EcoQoS" xml:space="preserve"> + <value>Eficiencia inteligente</value> + </data> + <data name="SettingsPage_EcoQoSDesc" xml:space="preserve"> + <value>El modo de eficiencia inteligente reduce la prioridad de los procesos en segundo plano y mejora la eficiencia energética</value> + </data> + <data name="SettingsPage_GeneralOptions" xml:space="preserve"> + <value>Opciones generales</value> + </data> + <data name="SettingsPage_InstallNow" xml:space="preserve"> + <value>Instalar ahora</value> + </data> + <data name="SettingsPage_LastChecked" xml:space="preserve"> + <value>Ultima comprobación: </value> + </data> + <data name="SettingsPage_NotificationOptions" xml:space="preserve"> + <value>Opciones de notificación</value> + </data> + <data name="SettingsPage_OpenAppBackground" xml:space="preserve"> + <value>Abrir aplicacion en segundo plano</value> + </data> + <data name="SettingsPage_OpenAppBackgroundDesc" xml:space="preserve"> + <value>La aplicación inicialmente comenzará minimizada y aparecerá en la barra de tareas</value> + </data> + <data name="SettingsPage_SensorExternal" xml:space="preserve"> + <value>Externo</value> + </data> + <data name="SettingsPage_SensorInternal" xml:space="preserve"> + <value>Interno</value> + </data> + <data name="SettingsPage_SensorOptions" xml:space="preserve"> + <value>Opciones del sensor</value> + </data> + <data name="SettingsPage_SensorPlacementDirection" xml:space="preserve"> + <value>Dirección de colocación del sensor externo</value> + </data> + <data name="SettingsPage_SensorPlacementDirectionDesc" xml:space="preserve"> + <value>Seleccione en qué lado del dispositivo se ha montado el sensor</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDown" xml:space="preserve"> + <value>Sensor externo al revés</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDownDesc" xml:space="preserve"> + <value>El sensor se ha montado al revés, posible con convertidor USB-C</value> + </data> + <data name="SettingsPage_SensorSelection" xml:space="preserve"> + <value>Seleccion de sensores</value> + </data> + <data name="SettingsPage_SensorSelectionDesc" xml:space="preserve"> + <value>Seleccione el sensor deseado utilizado para la entrada de movimiento</value> + </data> + <data name="SettingsPage_Settings" xml:space="preserve"> + <value>Opciones</value> + </data> + <data name="SettingsPage_StartupType" xml:space="preserve"> + <value>Tipo de inicio</value> + </data> + <data name="SettingsPage_StartupTypeDesc" xml:space="preserve"> + <value>Utilizado por el administrador de servicios para definir el tipo de inicio del servicio</value> + </data> + <data name="SettingsPage_TDPMax" xml:space="preserve"> + <value>Maximo poder</value> + </data> + <data name="SettingsPage_TDPMaxDesc" xml:space="preserve"> + <value>La potencia máxima en vatios suministrada al procesador</value> + </data> + <data name="SettingsPage_TDPMin" xml:space="preserve"> + <value>Poder minimo</value> + </data> + <data name="SettingsPage_TDPMinDesc" xml:space="preserve"> + <value>La potencia mínima en vatios suministrada al procesador</value> + </data> + <data name="SettingsPage_TDPRangeOverride" xml:space="preserve"> + <value>Anulación de potencia configurable (cTDP)</value> + </data> + <data name="SettingsPage_TDPRangeOverrideDesc" xml:space="preserve"> + <value>Permite modificar los valores mínimo y máximo de potencia (TDP) más allá de las especificaciones de la CPU</value> + </data> + <data name="SettingsPage_ThemeDark" xml:space="preserve"> + <value>Oscuro</value> + </data> + <data name="SettingsPage_ThemeLight" xml:space="preserve"> + <value>Claro</value> + </data> + <data name="SettingsPage_ToastNotification" xml:space="preserve"> + <value>Notificacion flotante</value> + </data> + <data name="SettingsPage_ToastNotificationDesc" xml:space="preserve"> + <value>Recibe notificaciones de la aplicación en el centro de acción de Windows</value> + </data> + <data name="SettingsPage_UpdateAvailable" xml:space="preserve"> + <value>Actualizaciones disponibles</value> + </data> + <data name="SettingsPage_UpdateCheck" xml:space="preserve"> + <value>Comprobando actualizaciones...</value> + </data> + <data name="SettingsPage_UpdateFailedDownload" xml:space="preserve"> + <value>No pudimos descargar el archivo de actualización.</value> + </data> + <data name="SettingsPage_UpdateFailedGithub" xml:space="preserve"> + <value>No se pudo acceder a Github</value> + </data> + <data name="SettingsPage_UpdateFailedInstall" xml:space="preserve"> + <value>No pudimos localizar el archivo de actualización.</value> + </data> + <data name="SettingsPage_UpdateWarning" xml:space="preserve"> + <value>Ups. Hubo un problema</value> + </data> + <data name="SettingsPage_UpToDate" xml:space="preserve"> + <value>Esta todo actualizado</value> + </data> + <data name="ToastNewControllerEx" xml:space="preserve"> + <value>El controlador ahora está oculto y las entradas se reenvían al controlador virtual</value> + </data> + <data name="User" xml:space="preserve"> + <value>Usuario</value> + </data> + <data name="WarningElevated" xml:space="preserve"> + <value>Ejecute esta herramienta como administrador para desbloquear esta configuración</value> + </data> + <data name="xinput1_x64" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\xinput1_x64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="xinput1_x86" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\xinput1_x86.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="XInputPlus" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\XInputPlus.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value> + </data> + <data name="ControllerPage_SteamControllerMute" xml:space="preserve"> + <value>Silenciar controlador virtual</value> + </data> + <data name="ControllerPage_SteamControllerMuteDesc" xml:space="preserve"> + <value>Silenciar el controlador virtual en aplicaciones relacionadas con Steam</value> + </data> + <data name="ProfilesPage_ControllerSettings" xml:space="preserve"> + <value>Opciones del mando</value> + </data> + <data name="ProfilesPage_ControllerSettingsDesc" xml:space="preserve"> + <value>Cambiar la configuración del mando virtual</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzone" xml:space="preserve"> + <value>Movimiento anti-zona muerta</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzoneDesc" xml:space="preserve"> + <value>Compensa la zona muerta del juego, mejora el registro de pequeños movimientos</value> + </data> + <data name="chord_neptune" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\controller_base\chord_neptune.vdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="empty_neptune" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\controller_base\empty_neptune.vdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityDesc" xml:space="preserve"> + <value>Mejorar la circularidad de el stick.</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityLeft" xml:space="preserve"> + <value>Circularidad stick izquierdo</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityRight" xml:space="preserve"> + <value>Circularidad stick derecho</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeft" xml:space="preserve"> + <value>Joystick zona muerta % izquierdo</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeftDesc" xml:space="preserve"> + <value>Ajuste el porcentaje de zona muerta interior y exterior del joystick izquierdo</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRight" xml:space="preserve"> + <value>Joystick zona muerta % derecho</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRightDesc" xml:space="preserve"> + <value>Ajuste el porcentaje de zona muerta interior y exterior del joystick derecho</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeft" xml:space="preserve"> + <value>Gatillo izquierdo deadzone %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeftDesc" xml:space="preserve"> + <value>Ajuste el porcentaje de zona muerta interior y exterior del gatillo izquierdo</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRight" xml:space="preserve"> + <value>Gatillo derecho deadzone %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRightDesc" xml:space="preserve"> + <value>Ajuste el porcentaje de zona muerta interior y exterior del gatillo derecho</value> + </data> + <data name="ControllerPage_VibrateDevice" xml:space="preserve"> + <value>Controlador de vibración al conectar</value> + </data> + <data name="ControllerPage_VibrateDeviceDesc" xml:space="preserve"> + <value>Vibración del mando cuando esta conectado</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabled" xml:space="preserve"> + <value>Diseño de escritorio</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabledDesc" xml:space="preserve"> + <value>Cambiar el diseño del controlador de escritorio</value> + </data> + <data name="InputsHotkey_shortcutControlCenter" xml:space="preserve"> + <value>Mostrar Centro de acción</value> + </data> + <data name="InputsHotkey_shortcutControlCenterDesc" xml:space="preserve"> + <value>Mostrar y ocultar Centro de acción de Windows</value> + </data> + <data name="ProfilesPage_ControllerLayout" xml:space="preserve"> + <value>Disposicion del mando</value> + </data> + <data name="InputsHotkey_QuietModeToggled" xml:space="preserve"> + <value>Anulación del ventilador</value> + </data> + <data name="InputsHotkey_QuietModeToggledDesc" xml:space="preserve"> + <value>Establezca el ciclo de trabajo del ventilador en un valor definido por el usuario</value> + </data> + <data name="InputsHotkey_OnScreenDisplay" xml:space="preserve"> + <value>Visualización en pantalla</value> + </data> + <data name="InputsHotkey_OnScreenDisplayDesc" xml:space="preserve"> + <value>Habilitar soporte de visualización en pantalla</value> + </data> + <data name="InputsHotkey_decreaseBrightness" xml:space="preserve"> + <value>Reducir el brillo</value> + </data> + <data name="InputsHotkey_decreaseBrightnessDesc" xml:space="preserve"> + <value>Reducir el brillo de la pantalla actual en un 5%</value> + </data> + <data name="InputsHotkey_decreaseVolume" xml:space="preserve"> + <value>Reducir volumen</value> + </data> + <data name="InputsHotkey_decreaseVolumeDesc" xml:space="preserve"> + <value>Reducir el volumen del sistema en un 5%</value> + </data> + <data name="InputsHotkey_increaseBrightness" xml:space="preserve"> + <value>Aumentar brillo</value> + </data> + <data name="InputsHotkey_increaseBrightnessDesc" xml:space="preserve"> + <value>Aumentar el brillo de la pantalla actual en un 5%</value> + </data> + <data name="InputsHotkey_increaseVolume" xml:space="preserve"> + <value>Aumentar volumen</value> + </data> + <data name="InputsHotkey_increaseVolumeDesc" xml:space="preserve"> + <value>Aumentar volumen actual en un 5%</value> + </data> + <data name="ProfilesPage_AutoTDP" xml:space="preserve"> + <value>TDP Automático</value> + </data> + <data name="ProfilesPage_AutoTDPDesc" xml:space="preserve"> + <value>Ajuste automático de TDP basado en FPS medido y objetivo de FPS solicitado</value> + </data> + <data name="ProfilesPage_AutoTDPFPS" xml:space="preserve"> + <value>FPS objetivo</value> + </data> + <data name="ProfilesPage_AutoTDPFPSDesc" xml:space="preserve"> + <value>Valor objetivo de FPS deseado para el controlador TDP automático</value> + </data> + <data name="ProfilesPage_GPUMhz" xml:space="preserve"> + <value>GPU Clock maxima frecuencia</value> + </data> + <data name="ProfilesPage_GPUMhzDesc" xml:space="preserve"> + <value>Velocidad máxima de reloj de la GPU en Mhz</value> + </data> + <data name="QuickPerformancePage_AutoTDPUnitFPS" xml:space="preserve"> + <value>FPS</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel" xml:space="preserve"> + <value>Nivel de visualización de sobreposicion</value> + </data> + <data name="OverlayPage_OverlayDisplayLevelDesc" xml:space="preserve"> + <value>Cambiar el nivel de visualización de la información en pantalla</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRate" xml:space="preserve"> + <value>Frecuencia de actualización</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRateDesc" xml:space="preserve"> + <value>Cambiar la frecuencia de actualización de visualización en pantalla</value> + </data> + <data name="ProfilesPage_FramerateLimit" xml:space="preserve"> + <value>Limite de FPS</value> + </data> + <data name="ProfilesPage_FramerateLimitDesc" xml:space="preserve"> + <value>Limite de FPS para aplicaciones 3D</value> + </data> + <data name="InputsHotkey_shortcutPrintScreen" xml:space="preserve"> + <value>Abra la herramienta Recortes</value> + </data> + <data name="InputsHotkey_shortcutPrintScreenDesc" xml:space="preserve"> + <value>Presiona las teclas: Windows + Shift</value> + </data> + <data name="MainWindow_navHotkeys" xml:space="preserve"> + <value>Teclas acceso rapido</value> + </data> + <data name="SettingsPage_ThemeDefault" xml:space="preserve"> + <value>Usar la configuración del sistema</value> + </data> + <data name="SettingsPage_ThirdPartyApps" xml:space="preserve"> + <value>Aplicaciones de terceros</value> + </data> + <data name="LayoutPage_ExportLayout" xml:space="preserve"> + <value>Exportar disposicion</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedWarning" xml:space="preserve"> + <value>No se detectó ningún controlador físico</value> + </data> + <data name="LayoutPage_ExportCurrentController" xml:space="preserve"> + <value>Exportar para el controlador actual</value> + </data> + <data name="ControllerPage_NoVirtualControllerAction" xml:space="preserve"> + <value>Es posible que desee iniciar el servicio complementario o asegurarse de que el estado de su controlador virtual esté configurado en: Conectado</value> + </data> + <data name="LayoutPage_Dpad" xml:space="preserve"> + <value>Dpad</value> + </data> + <data name="ControllerPage_NoVirtualControllerDesc" xml:space="preserve"> + <value>Su controlador físico está oculto, pero no tiene ningún controlador virtual disponible. No se enviarán entradas a los juegos.</value> + </data> + <data name="ControllerPage_NoVirtualControllerWarning" xml:space="preserve"> + <value>No se detectó ningún controlador virtual</value> + </data> + <data name="LayoutPage_Confirm" xml:space="preserve"> + <value>Confirmar</value> + </data> + <data name="LayoutPage_Community" xml:space="preserve"> + <value>Comunidad</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenAction" xml:space="preserve"> + <value>Es posible que desee activar el silencio de su controlador virtual o mostrar su controlador físico.</value> + </data> + <data name="LayoutPage_Cancel" xml:space="preserve"> + <value>Cancelar</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenDesc" xml:space="preserve"> + <value>Su controlador físico está oculto, pero ha silenciado su controlador virtual.</value> + </data> + <data name="LayoutPage_Buttons" xml:space="preserve"> + <value>Botones</value> + </data> + <data name="LayoutPage_ApplyTemplate" xml:space="preserve"> + <value>Aplicar plantilla</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenWarning" xml:space="preserve"> + <value>El controlador físico está oculto.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenAction" xml:space="preserve"> + <value>Es posible que desee ocultar su controlador físico o silenciar su controlador virtual.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenDesc" xml:space="preserve"> + <value>Su controlador físico no está oculto, pero tiene un controlador virtual activado. Es posible que encuentres entradas dobles en los juegos.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenWarning" xml:space="preserve"> + <value>El controlador físico no está oculto.</value> + </data> + <data name="ControllerPage_SteamControllerSettings" xml:space="preserve"> + <value>Opciones Steam Deck</value> + </data> + <data name="LayoutPage_Gyro" xml:space="preserve"> + <value>Girómetro</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedAction" xml:space="preserve"> + <value>Asegúrese de haber conectado un dispositivo XInput o DInput compatible.</value> + </data> + <data name="LayoutPage_LayoutAuthor" xml:space="preserve"> + <value>Autor de el diseño</value> + </data> + <data name="AboutPage_Partner" xml:space="preserve"> + <value>Socio</value> + </data> + <data name="AboutPage_Manufacturer" xml:space="preserve"> + <value>Fabricante</value> + </data> + <data name="AboutPage_ProductName" xml:space="preserve"> + <value>Nombre del producto</value> + </data> + <data name="LayoutPage_Trackpads" xml:space="preserve"> + <value>Trackpads</value> + </data> + <data name="ControllerPage_NoPhysicalControllerWarning" xml:space="preserve"> + <value>No hay ningún controlador físico conectado</value> + </data> + <data name="ControllerPage_DesktopLayout" xml:space="preserve"> + <value>Diseño de escritorio</value> + </data> + <data name="ControllerPage_DesktopLayoutDefine" xml:space="preserve"> + <value>Definir el diseño de escritorio</value> + </data> + <data name="ControllerPage_DesktopLayoutDefineController" xml:space="preserve"> + <value>Definir el diseño del controlador cuando esté en modo de escritorio</value> + </data> + <data name="ControllerPage_DesktopLayoutEdit" xml:space="preserve"> + <value>Editar</value> + </data> + <data name="LayoutPage_Templates" xml:space="preserve"> + <value>Plantillas</value> + </data> + <data name="ControllerPage_DesktopLayoutEnable" xml:space="preserve"> + <value>Habilitar diseño de escritorio</value> + </data> + <data name="LayoutPage_TemplatePicker" xml:space="preserve"> + <value>Selector de plantilla de diseño</value> + </data> + <data name="ControllerPage_NonGameControllerLayouts" xml:space="preserve"> + <value>Diseños de controladores que no son de juegos</value> + </data> + <data name="LayoutPage_ShowCurrentControllerTemplates" xml:space="preserve"> + <value>Mostrar solo las plantillas de controlador actuales</value> + </data> + <data name="LayoutPage_SaveGameInfoLayout" xml:space="preserve"> + <value>Guardar información del juego con el diseño.</value> + </data> + <data name="LayoutPage_LayoutTitle" xml:space="preserve"> + <value>Título del diseño</value> + </data> + <data name="ControllerPage_NoPhysicalControllerAction" xml:space="preserve"> + <value>Es posible que desees hacer clic en Conectar junto a tu controlador conectado.</value> + </data> + <data name="LayoutPage_LayoutDesc" xml:space="preserve"> + <value>Descripción del diseño</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDesc" xml:space="preserve"> + <value>No tienes ningún controlador físico conectado. No se enviarán insumos a HC ni a su servicio.</value> + </data> + <data name="LayoutPage_Joysticks" xml:space="preserve"> + <value>Joysticks</value> + </data> + <data name="ControllerPage_SteamControllerHDRumble" xml:space="preserve"> + <value>Vibración HD</value> + </data> + <data name="JoystickPage_Joystick_Left" xml:space="preserve"> + <value>JOYSTICK IZQUIERDO</value> + </data> + <data name="JoystickPage_Joystick_Left_Buttons" xml:space="preserve"> + <value>BOTONES JOYSTICK IZQUIERDO</value> + </data> + <data name="SettingsPage_HideWhenLoseFocusDesc" xml:space="preserve"> + <value>Ocultar automáticamente herramientas rápidas cuando el usuario hace click fuera de la ventana</value> + </data> + <data name="SettingsPage_HideWhenLoseFocus" xml:space="preserve"> + <value>Ocultar cuando pierde el foco</value> + </data> + <data name="QuickProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Objetivo de límite de potencia</value> + </data> + <data name="QuickProfilesPage_CurrentProfileDefault" xml:space="preserve"> + <value>Por defecto</value> + </data> + <data name="JoystickPage_Joystick_Right" xml:space="preserve"> + <value>JOYSTICK DERECHO</value> + </data> + <data name="DPadPage_DPad" xml:space="preserve"> + <value>PAD DIRECCIONAL</value> + </data> + <data name="QuickProfilesPage_CurrentProfile" xml:space="preserve"> + <value>Perfil Actual: </value> + </data> + <data name="QuickPerformancePage_FanOverrideDesc" xml:space="preserve"> + <value>Establezca el ciclo de trabajo del ventilador en un valor definido por el usuario</value> + </data> + <data name="QuickPerformancePage_FanOverride" xml:space="preserve"> + <value>Anulación del ventilador</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRate" xml:space="preserve"> + <value>Resolución de pantalla y frecuencia de actualización</value> + </data> + <data name="QuickPerformancePage_CPUBoostModeDesc" xml:space="preserve"> + <value>Establecer modo de refuerzo de CPU actual</value> + </data> + <data name="QuickPerformancePage_CPUBoostMode" xml:space="preserve"> + <value>Modo de refuerzo de CPU</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationNotSet" xml:space="preserve"> + <value>No establecido</value> + </data> + <data name="QuickPerformancePage_FanOverridePercentage" xml:space="preserve"> + <value>%</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDetect" xml:space="preserve"> + <value>Detectar</value> + </data> + <data name="ButtonsPage_OEM" xml:space="preserve"> + <value>OEM</value> + </data> + <data name="ButtonsPage_Bumpers" xml:space="preserve"> + <value>BUMPERS</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServerDesc" xml:space="preserve"> + <value>Desactiva automáticamente RTSS cuando HC está cerrado</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServer" xml:space="preserve"> + <value>RivaTuner Statistics Server</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopRight" xml:space="preserve"> + <value>Parte superior derecha</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopLeft" xml:space="preserve"> + <value>Parte superior izquierda</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationDesc" xml:space="preserve"> + <value>Definir la ubicación de la ventana de herramientas rápidas</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomRight" xml:space="preserve"> + <value>Parte inferior derecha</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomLeft" xml:space="preserve"> + <value>Parte inferior izquierda</value> + </data> + <data name="ButtonsPage_Menu" xml:space="preserve"> + <value>MENU</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocation" xml:space="preserve"> + <value>Ubicación de la ventana</value> + </data> + <data name="SettingsPage_QuickToolsOptions" xml:space="preserve"> + <value>Opciones de herramientas rápidas</value> + </data> + <data name="SettingsPage_QuickToolsBackdrop" xml:space="preserve"> + <value>Fondo de herramientas rápidas, none, mica, tabbed, acrylic</value> + </data> + <data name="DevicePage_PowerOptions" xml:space="preserve"> + <value>Opciones de energía</value> + </data> + <data name="SettingsPage_HwInfoDesc" xml:space="preserve"> + <value>Desactiva automáticamente HWiNFO cuando HC está cerrado</value> + </data> + <data name="ButtonsPage_ABXY" xml:space="preserve"> + <value>A,B,X,Y</value> + </data> + <data name="SettingsPage_HwInfo" xml:space="preserve"> + <value>HWiNFO</value> + </data> + <data name="ButtonsPage_Back_Grips" xml:space="preserve"> + <value>CONTROLES TRASEROS</value> + </data> + <data name="SettingsPage_SensorNone" xml:space="preserve"> + <value>Ninguno</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDesc" xml:space="preserve"> + <value>Algunas funciones dependen de conocer la orientación de la pantalla nativa para funcionar correctamente. Si esto no se detectó correctamente, configure la orientación de su pantalla en la orientación que coincida con su controlador, luego haga clic en Detectar</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRateDesc" xml:space="preserve"> + <value>Ajustar la resolución de la pantalla principal y la frecuencia de actualización</value> + </data> + <data name="ProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Objetivo de límite de potencia</value> + </data> + <data name="OverlayPage_Millisecond" xml:space="preserve"> + <value>ms</value> + </data> + <data name="TriggersPage_Trigger_Right_Button" xml:space="preserve"> + <value>BOTONES GATILLO DERECHO</value> + </data> + <data name="LayoutPage_Triggers" xml:space="preserve"> + <value>Gatillos</value> + </data> + <data name="SettingsPage_NativeDisplayOrientation" xml:space="preserve"> + <value>Orientación de pantalla nativa</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Disabled" xml:space="preserve"> + <value>Desactivado</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Extended" xml:space="preserve"> + <value>Extendido</value> + </data> + <data name="TriggersPage_Trigger_Left_Button" xml:space="preserve"> + <value>BOTONES GATILLO IZQUIERDO</value> + </data> + <data name="JoystickPage_Joystick_Right_Buttons" xml:space="preserve"> + <value>BOTONES JOYSTICK DERECHOS</value> + </data> + <data name="TrackPadsPage_Trackpad_Left" xml:space="preserve"> + <value>TRACKPAD IZUIERDO</value> + </data> + <data name="TrackPadsPage_Trackpad_Left_Buttons" xml:space="preserve"> + <value>BOTONES TRACKPAD IZQUIERDO</value> + </data> + <data name="TrackPadsPage_Trackpad_Right" xml:space="preserve"> + <value>TRACKPAD DERECHO</value> + </data> + <data name="ProfilesPage_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="TriggersPage_Trigger_Right" xml:space="preserve"> + <value>GATILLO DERECHO</value> + </data> + <data name="TrackPadsPage_Trackpad_Right_Buttons" xml:space="preserve"> + <value>BOTONES TRACKPAD DERECHO</value> + </data> + <data name="ProfilesPage_EPPDesc" xml:space="preserve"> + <value>Especifica la política de distribución de energía entre CPU y GPU.</value> + </data> + <data name="ProfilesPage_EPPBalance" xml:space="preserve"> + <value>Equilibrio de potencia CPU/GPU</value> + </data> + <data name="ControllerPage_SteamControllerHDRumbleDesc" xml:space="preserve"> + <value>Utilice un motor de vibración de alta definición, a costa de un mayor uso de CPU</value> + </data> + <data name="ProfilesPage_EPP" xml:space="preserve"> + <value>Preferencia de rendimiento energético (EPP)</value> + </data> + <data name="TriggersPage_Trigger_Left" xml:space="preserve"> + <value>GATILLO IZQUIERDO</value> + </data> + <data name="ProfilesPage_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Full" xml:space="preserve"> + <value>Completo</value> + </data> + <data name="ProfilesPage_ControllerLayoutDesc" xml:space="preserve"> + <value>Cambiar el diseño del controlador virtual</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Minimal" xml:space="preserve"> + <value>Mínimo</value> + </data> + <data name="AutoRollYawSwapDesc" xml:space="preserve"> + <value>Esta entrada funcionará como un simple joystick. Ideal para portátiles y dispositivos de mano tipo clamshell, cambio automático de balanceo de guiñada en función de cómo se sostenga el dispositivo (90 o 180 grados abiertos).</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B1" xml:space="preserve"> + <value>Equis</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B2" xml:space="preserve"> + <value>Circulo</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B3" xml:space="preserve"> + <value>Cuadrado</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B4" xml:space="preserve"> + <value>Triangulo</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Start" xml:space="preserve"> + <value>Opciones</value> + </data> + <data name="Enum.HIDmode.DualShock4Controller" xml:space="preserve"> + <value>Controlador DualShock 4 emulado</value> + </data> + <data name="Enum.HIDmode.NoController" xml:space="preserve"> + <value>Sin controlador emulado</value> + </data> + <data name="Enum.HIDmode.Xbox360Controller" xml:space="preserve"> + <value>Controlador XBOX 360 emulado</value> + </data> + <data name="Enum.HIDstatus.Connected" xml:space="preserve"> + <value>Conectado</value> + </data> + <data name="Enum.HIDstatus.Disconnected" xml:space="preserve"> + <value>Desconectado</value> + </data> + <data name="Enum.Input.AutoRollYawSwap" xml:space="preserve"> + <value>Auto Roll Yaw Swap</value> + </data> + <data name="Enum.Input.JoystickCamera" xml:space="preserve"> + <value>Cámara con joystick</value> + </data> + <data name="Enum.Input.JoystickSteering" xml:space="preserve"> + <value>Dirección con joystick</value> + </data> + <data name="Enum.Input.PlayerSpace" xml:space="preserve"> + <value>Espacio del jugador</value> + </data> + <data name="Enum.InputsHotkeyType.HC" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.InputsHotkeyType.Overlay" xml:space="preserve"> + <value>Sobreposicion</value> + </data> + <data name="Enum.InputsHotkeyType.Quicktools" xml:space="preserve"> + <value>Herramientas rápidas</value> + </data> + <data name="Enum.InputsHotkeyType.Windows" xml:space="preserve"> + <value>Windows</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Back" xml:space="preserve"> + <value>Vista</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="Enum.Output.LeftStick" xml:space="preserve"> + <value>Joystick izquierdo</value> + </data> + <data name="Enum.Output.RightStick" xml:space="preserve"> + <value>Joystick derecho</value> + </data> + <data name="Enum.ProfileErrorCode.Default" xml:space="preserve"> + <value>Este es su perfil de controlador predeterminado. Este perfil se aplicará a todas tus aplicaciones que no tengan un perfil específico. Algunas opciones que requieren un ejecutable pueden estar deshabilitadas.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingExecutable" xml:space="preserve"> + <value>Ups. Parece que este perfil no tiene un ejecutable. Como es esto posible?</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPath" xml:space="preserve"> + <value>Ups. Parece que este perfil no tiene una ruta a la aplicación. Algunas opciones que requieren un ejecutable pueden estar deshabilitadas.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPermission" xml:space="preserve"> + <value>Ups. Parece que no tienes el nivel de permiso necesario para modificar el contenido de esta aplicación. Asegúrese de haber iniciado este programa en modo administrador.</value> + </data> + <data name="Enum.ProfileErrorCode.None" xml:space="preserve"> + <value>Nada que ver aqui.</value> + </data> + <data name="Enum.ProfileErrorCode.Running" xml:space="preserve"> + <value>Ups. Parece que este perfil ejecutable se está ejecutando. Algunas opciones que requieren un ejecutable pueden estar deshabilitadas.</value> + </data> + <data name="Enum.QualityOfServiceLevel.Default" xml:space="preserve"> + <value>Por defecto</value> + </data> + <data name="Enum.QualityOfServiceLevel.Eco" xml:space="preserve"> + <value>Eco</value> + </data> + <data name="Enum.QualityOfServiceLevel.High" xml:space="preserve"> + <value>Alta</value> + </data> + <data name="Enum.ServiceStartMode.Automatic" xml:space="preserve"> + <value>Automático</value> + </data> + <data name="Enum.ServiceStartMode.Disabled" xml:space="preserve"> + <value>Desactivado</value> + </data> + <data name="Enum.ServiceStartMode.Manual" xml:space="preserve"> + <value>Manual</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Back" xml:space="preserve"> + <value>Vista</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L1" xml:space="preserve"> + <value>LB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R1" xml:space="preserve"> + <value>RB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Special" xml:space="preserve"> + <value>Guide</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="JoystickCameraDesc" xml:space="preserve"> + <value>Esta entrada funcionará como un simple joystick. Esto está diseñado para aplicaciones de joystick tradicionales.</value> + </data> + <data name="JoystickSteeringDesc" xml:space="preserve"> + <value>Esta entrada funcionará como un joystick optimizado para controlar un volante o un juego de carreras.</value> + </data> + <data name="PlayerSpaceDesc" xml:space="preserve"> + <value>Esta entrada funcionará como un joystick optimizado para controlar una cámara en primera o tercera persona</value> + </data> + <data name="Enum.InputsHotkeyType.Handheld" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.SteamDeck.ButtonFlags.OEM1" xml:space="preserve"> + <value>Opciones</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Special" xml:space="preserve"> + <value>STEAM</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM1" xml:space="preserve"> + <value>Command center</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM2" xml:space="preserve"> + <value>Armory crate</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM3" xml:space="preserve"> + <value>M1 / M2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbDown" xml:space="preserve"> + <value>Right Thumb Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbLeft" xml:space="preserve"> + <value>Right Thumb Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbRight" xml:space="preserve"> + <value>Right Thumb Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbUp" xml:space="preserve"> + <value>Right Thumb Up</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.AlwaysOn" xml:space="preserve"> + <value>Siempre encendido</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Back" xml:space="preserve"> + <value>Atrás</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumb" xml:space="preserve"> + <value>Right Thumb</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadDown" xml:space="preserve"> + <value>DPad Abajo</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadRight" xml:space="preserve"> + <value>DPad Derecho</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadUp" xml:space="preserve"> + <value>DPad Arriba</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftShoulder" xml:space="preserve"> + <value>Shoulder izquierdo</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftThumb" xml:space="preserve"> + <value>Thumb izquierdo</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftTrigger" xml:space="preserve"> + <value>Gatillo izquierdo</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightShoulder" xml:space="preserve"> + <value>Shoulder Derecho</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightThumb" xml:space="preserve"> + <value>Thumb Derecho</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightTrigger" xml:space="preserve"> + <value>Gatillo Derecho</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadLeft" xml:space="preserve"> + <value>DPad Izquierda</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R5" xml:space="preserve"> + <value>R5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R4" xml:space="preserve"> + <value>R4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R3" xml:space="preserve"> + <value>R3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B7" xml:space="preserve"> + <value>B7</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B8" xml:space="preserve"> + <value>B8</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadDown" xml:space="preserve"> + <value>DPad Abajo</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadLeft" xml:space="preserve"> + <value>DPad Izquierda</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadRight" xml:space="preserve"> + <value>PPad Derecha</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadUp" xml:space="preserve"> + <value>DPad Arriba</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L3" xml:space="preserve"> + <value>L3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L4" xml:space="preserve"> + <value>L4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L5" xml:space="preserve"> + <value>L5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumb" xml:space="preserve"> + <value>Thumb Izquierdo</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbDown" xml:space="preserve"> + <value>Left Thumb Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbLeft" xml:space="preserve"> + <value>Left Thumb Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbRight" xml:space="preserve"> + <value>Left Thumb Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbUp" xml:space="preserve"> + <value>Left Thumb Up</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM1" xml:space="preserve"> + <value>OEM1</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM2" xml:space="preserve"> + <value>OEM2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM3" xml:space="preserve"> + <value>OEM3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Start" xml:space="preserve"> + <value>Start</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.ProfileErrorCode.IsRunning" xml:space="preserve"> + <value>Ups. Parece que este perfil se está ejecutando. Algunas opciones que requieren un ejecutable pueden estar desactivadas.</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B6" xml:space="preserve"> + <value>B6</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B5" xml:space="preserve"> + <value>B5</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightThumb" xml:space="preserve"> + <value>Thumb Derecho</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM2" xml:space="preserve"> + <value>Esc</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM3" xml:space="preserve"> + <value>KB</value> + </data> + <data name="Enum.InputsHotkeyType.Custom" xml:space="preserve"> + <value>Personalizado</value> + </data> + <data name="Enum.InputsHotkeyType.Device" xml:space="preserve"> + <value>Dispositivo</value> + </data> + <data name="Enum.MotionInput.JoystickCamera" xml:space="preserve"> + <value>Joystick Cámara</value> + </data> + <data name="Enum.MotionInput.JoystickSteering" xml:space="preserve"> + <value>Joystick de dirección</value> + </data> + <data name="Enum.MotionInput.PlayerSpace" xml:space="preserve"> + <value>Espacio de jugador</value> + </data> + <data name="Enum.MotionOutput.LeftStick" xml:space="preserve"> + <value>Stick izquierdo</value> + </data> + <data name="Enum.MotionOutput.RightStick" xml:space="preserve"> + <value>Stick derecho</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftThumb" xml:space="preserve"> + <value>Thumb izquierdo</value> + </data> + <data name="Enum.MotionInput.AutoRollYawSwap" xml:space="preserve"> + <value>Auto Roll Yaw Swap</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM1" xml:space="preserve"> + <value>Win</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Deslizar con una sola pulsación</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Deslizamiento multitáctil</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Clic con un solo toque</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Toque único</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Clic multitáctil</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multitoque</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Deslizar con una sola pulsación</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Deslizamiento multitáctil</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B1" xml:space="preserve"> + <value>Equis</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B2" xml:space="preserve"> + <value>Circulo</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B3" xml:space="preserve"> + <value>Cuadrado</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B4" xml:space="preserve"> + <value>Triángulo</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Back" xml:space="preserve"> + <value>Compartir</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Clic con un solo toque</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Toque sencillo</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Click multitáctil</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multitoque</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Start" xml:space="preserve"> + <value>Opciones</value> + </data> + <data name="Enum.KeyFlags.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.KeyFlags.Alt" xml:space="preserve"> + <value>Alt</value> + </data> + <data name="Enum.KeyFlags.Apostrophe" xml:space="preserve"> + <value>Apóstrofe</value> + </data> + <data name="Enum.KeyFlags.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.KeyFlags.Backslash" xml:space="preserve"> + <value>Barra invertida</value> + </data> + <data name="Enum.KeyFlags.Backspace" xml:space="preserve"> + <value>Retroceso</value> + </data> + <data name="Enum.KeyFlags.C" xml:space="preserve"> + <value>C</value> + </data> + <data name="Enum.KeyFlags.Comma" xml:space="preserve"> + <value>Coma</value> + </data> + <data name="Enum.KeyFlags.Control" xml:space="preserve"> + <value>Control</value> + </data> + <data name="Enum.KeyFlags.D" xml:space="preserve"> + <value>D</value> + </data> + <data name="Enum.KeyFlags.Delete" xml:space="preserve"> + <value>Eliminar</value> + </data> + <data name="Enum.KeyFlags.E" xml:space="preserve"> + <value>E</value> + </data> + <data name="Enum.KeyFlags.End" xml:space="preserve"> + <value>Fin</value> + </data> + <data name="Enum.KeyFlags.Enter" xml:space="preserve"> + <value>Intro</value> + </data> + <data name="Enum.KeyFlags.Equal" xml:space="preserve"> + <value>Igual</value> + </data> + <data name="Enum.KeyFlags.Escape" xml:space="preserve"> + <value>Escape</value> + </data> + <data name="Enum.KeyFlags.F" xml:space="preserve"> + <value>F</value> + </data> + <data name="Enum.KeyFlags.F1" xml:space="preserve"> + <value>F1</value> + </data> + <data name="Enum.KeyFlags.F10" xml:space="preserve"> + <value>F10</value> + </data> + <data name="Enum.KeyFlags.F11" xml:space="preserve"> + <value>F11</value> + </data> + <data name="Enum.KeyFlags.F12" xml:space="preserve"> + <value>F12</value> + </data> + <data name="Enum.KeyFlags.F2" xml:space="preserve"> + <value>F2</value> + </data> + <data name="Enum.KeyFlags.F3" xml:space="preserve"> + <value>F3</value> + </data> + <data name="Enum.KeyFlags.F4" xml:space="preserve"> + <value>F4</value> + </data> + <data name="Enum.KeyFlags.F5" xml:space="preserve"> + <value>F5</value> + </data> + <data name="Enum.KeyFlags.F6" xml:space="preserve"> + <value>F6</value> + </data> + <data name="Enum.KeyFlags.F7" xml:space="preserve"> + <value>F7</value> + </data> + <data name="Enum.KeyFlags.F8" xml:space="preserve"> + <value>F8</value> + </data> + <data name="Enum.KeyFlags.F9" xml:space="preserve"> + <value>F9</value> + </data> + <data name="Enum.KeyFlags.G" xml:space="preserve"> + <value>G</value> + </data> + <data name="Enum.KeyFlags.Grave" xml:space="preserve"> + <value>Grave</value> + </data> + <data name="Enum.KeyFlags.H" xml:space="preserve"> + <value>H</value> + </data> + <data name="Enum.KeyFlags.Home" xml:space="preserve"> + <value>Inicio</value> + </data> + <data name="Enum.KeyFlags.I" xml:space="preserve"> + <value>I</value> + </data> + <data name="Enum.KeyFlags.Insert" xml:space="preserve"> + <value>Insertar</value> + </data> + <data name="Enum.KeyFlags.J" xml:space="preserve"> + <value>J</value> + </data> + <data name="Enum.KeyFlags.K" xml:space="preserve"> + <value>K</value> + </data> + <data name="Enum.KeyFlags.L" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.KeyFlags.M" xml:space="preserve"> + <value>M</value> + </data> + <data name="Enum.KeyFlags.Minus" xml:space="preserve"> + <value>Menos</value> + </data> + <data name="Enum.KeyFlags.N" xml:space="preserve"> + <value>N</value> + </data> + <data name="Enum.KeyFlags.O" xml:space="preserve"> + <value>O</value> + </data> + <data name="Enum.KeyFlags.P" xml:space="preserve"> + <value>P</value> + </data> + <data name="Enum.KeyFlags.Pause" xml:space="preserve"> + <value>Pause</value> + </data> + <data name="Enum.KeyFlags.Period" xml:space="preserve"> + <value>Período</value> + </data> + <data name="Enum.KeyFlags.Q" xml:space="preserve"> + <value>Q</value> + </data> + <data name="Enum.KeyFlags.R" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.KeyFlags.S" xml:space="preserve"> + <value>S</value> + </data> + <data name="Enum.KeyFlags.Semicolon" xml:space="preserve"> + <value>Punto y coma</value> + </data> + <data name="Enum.KeyFlags.Shift" xml:space="preserve"> + <value>Mayus</value> + </data> + <data name="Enum.KeyFlags.Slash" xml:space="preserve"> + <value>Barra</value> + </data> + <data name="Enum.KeyFlags.Space" xml:space="preserve"> + <value>Espacio</value> + </data> + <data name="Enum.KeyFlags.T" xml:space="preserve"> + <value>T</value> + </data> + <data name="Enum.KeyFlags.Tab" xml:space="preserve"> + <value>Tabulador</value> + </data> + <data name="Enum.KeyFlags.U" xml:space="preserve"> + <value>U</value> + </data> + <data name="Enum.KeyFlags.V" xml:space="preserve"> + <value>V</value> + </data> + <data name="Enum.KeyFlags.W" xml:space="preserve"> + <value>W</value> + </data> + <data name="Enum.KeyFlags.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.KeyFlags.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.KeyFlags.Z" xml:space="preserve"> + <value>Z</value> + </data> + <data name="Enum.MotionOuput.LeftStick" xml:space="preserve"> + <value>Stick izquierdo</value> + </data> + <data name="Enum.MotionOuput.MoveCursor" xml:space="preserve"> + <value>Mover el cursor</value> + </data> + <data name="Enum.MotionOuput.RightStick" xml:space="preserve"> + <value>Stick Derecho</value> + </data> + <data name="Enum.MotionOuput.ScrollWheel" xml:space="preserve"> + <value>Rueda Ratón</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>ZL</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>ZR</value> + </data> + <data name="Enum.ProController.ButtonFlags.B1" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.ProController.ButtonFlags.B2" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.ProController.ButtonFlags.B3" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.ProController.ButtonFlags.B4" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.ProController.ButtonFlags.Back" xml:space="preserve"> + <value>Menos</value> + </data> + <data name="Enum.ProController.ButtonFlags.L1" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.ProController.ButtonFlags.R1" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special" xml:space="preserve"> + <value>Inicio</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special2" xml:space="preserve"> + <value>Captura</value> + </data> + <data name="Enum.ProController.ButtonFlags.Start" xml:space="preserve"> + <value>Además</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftStick" xml:space="preserve"> + <value>Stick Izquierdo</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightStick" xml:space="preserve"> + <value>Stick Derecho</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderClosePrimary" xml:space="preserve"> + <value>Sí</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseText" xml:space="preserve"> + <value>Es posible que la mejora de la detección del controlador virtual no funcione si cierras Handheld Companion. ¿Está seguro?</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseTitle" xml:space="preserve"> + <value>Aviso</value> + </data> + <data name="ProfilesPage_Wrapper_Disabled" xml:space="preserve"> + <value>Desactivado</value> + </data> + <data name="ProfilesPage_Wrapper_Injection" xml:space="preserve"> + <value>Inyección (recomendado)</value> + </data> + <data name="ProfilesPage_Wrapper_Redirection" xml:space="preserve"> + <value>Redirección</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrder" xml:space="preserve"> + <value>Mejorar la detección de controladores virtuales</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderDesc" xml:space="preserve"> + <value>Obliga al controlador virtual a ser detectado como primer controlador durante el inicio de Windows. Actívalo si tu aplicación/juego no detecta tus entradas (requiere reiniciar el dispositivo).</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderPrimary" xml:space="preserve"> + <value>Sí</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderText" xml:space="preserve"> + <value>Es necesario reiniciar el dispositivo para que los cambios surtan efecto. ¿Quieres reiniciar ahora?</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderTitle" xml:space="preserve"> + <value>Reinicio requerido</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyPrimary" xml:space="preserve"> + <value>Sí</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencySecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyText" xml:space="preserve"> + <value>Si desactiva esta opción, también desactivará "Mejorar la detección de controladores virtuales". ¿Desea continuar?</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyTitle" xml:space="preserve"> + <value>Aviso</value> + </data> + <data name="Controller_Connect" xml:space="preserve"> + <value>Conectar</value> + </data> + <data name="Controller_Disconnect" xml:space="preserve"> + <value>Desconectar</value> + </data> + <data name="Controller_Unhide" xml:space="preserve"> + <value>Mostrar</value> + </data> + <data name="Controller_Virtual" xml:space="preserve"> + <value>Virtual </value> + </data> + <data name="Controller_Hide" xml:space="preserve"> + <value>Ocultar</value> + </data> + <data name="MainWindow_Back" xml:space="preserve"> + <value>Atras</value> + </data> + <data name="MainWindow_Exit" xml:space="preserve"> + <value>Salir</value> + </data> + <data name="MainWindow_MainWindow" xml:space="preserve"> + <value>Ventana principal</value> + </data> + <data name="MainWindow_Navigate" xml:space="preserve"> + <value>Navegar</value> + </data> + <data name="MainWindow_QuickTools" xml:space="preserve"> + <value>Herramientas rápidas</value> + </data> + <data name="MainWindow_Select" xml:space="preserve"> + <value>Selecciona</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_External" xml:space="preserve"> + <value>Externo</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate1" xml:space="preserve"> + <value>¿Está seguro de que desea aplicar esta plantilla? Se anularán todos los ajustes de diseño.</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate2" xml:space="preserve"> + <value>No se puede deshacer esta acción. Plantilla aplicada anteriormente: {0}</value> + </data> + <data name="LayoutPage_SetAsDefault" xml:space="preserve"> + <value>Make this the default layout</value> + </data> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </root> \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.fr-FR.resx b/HandheldCompanion/Properties/Resources.fr-FR.resx index 636f00073..84c705498 100644 --- a/HandheldCompanion/Properties/Resources.fr-FR.resx +++ b/HandheldCompanion/Properties/Resources.fr-FR.resx @@ -1,3 +1,4 @@ +<<<<<<< HEAD <?xml version="1.0" encoding="utf-8"?> <root> <!-- @@ -2347,4 +2348,2322 @@ with motion input enabled, use selected button(s) to disable motion.</value> <data name="IController_ControllerIndex" xml:space="preserve"> <value>Gamepad: {0}</value> </data> +======= +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="AboutPage_About" xml:space="preserve"> + <value>À propos</value> + </data> + <data name="AboutPage_AboutDescription" xml:space="preserve"> + <value>Handheld Companion fournit une prise en charge du gyroscope et de l'accéléromètre aux modèles AYA NEO 2020 et 2021 par le biais d'un contrôleur DualShock 4 virtuel. Si le service est activé, le contrôleur intégré sera masqué pour les applications en dehors de la liste blanche. Si le service est désactivé, le contrôleur intégré ne sera pas masqué et le contrôleur DualShock 4 virtuel sera désactivé. Handheld Companion s'appuie sur le pilote ViGEmBus et les bibliothèques ViGEmClient ainsi que sur le pilote de filtre en mode noyau HidHide. Les algorithmes de contrôle de mouvement sont basés sur le travail de Jibbsmart et les informations disponibles sur le GyroWiki.</value> + </data> + <data name="AboutPage_Accelerometer" xml:space="preserve"> + <value>Accéléromètre</value> + </data> + <data name="AboutPage_Author" xml:space="preserve"> + <value>Auteur</value> + </data> + <data name="AboutPage_Contributors" xml:space="preserve"> + <value>Contributeurs</value> + </data> + <data name="AboutPage_Description" xml:space="preserve"> + <value>Description</value> + </data> + <data name="AboutPage_Donate" xml:space="preserve"> + <value>Faire un don</value> + </data> + <data name="AboutPage_Gyrometer" xml:space="preserve"> + <value>Gyromètre</value> + </data> + <data name="AboutPage_Inclinometer" xml:space="preserve"> + <value>Inclinomètre</value> + </data> + <data name="AboutPage_NotApplicable" xml:space="preserve"> + <value>N/A</value> + </data> + <data name="AboutPage_RelatedLinks" xml:space="preserve"> + <value>Liens connexes</value> + </data> + <data name="AboutPage_SensorName" xml:space="preserve"> + <value>Nom du capteur</value> + </data> + <data name="AboutPage_SensorSpecification" xml:space="preserve"> + <value>Spécification du capteur</value> + </data> + <data name="AboutPage_Service" xml:space="preserve"> + <value>Service</value> + </data> + <data name="AboutPage_SourceCode" xml:space="preserve"> + <value>Code source</value> + </data> + <data name="AboutPage_Version" xml:space="preserve"> + <value>Version</value> + </data> + <data name="AboutPage_Wiki" xml:space="preserve"> + <value>Wiki</value> + </data> + <data name="Administrator" xml:space="preserve"> + <value>Administrateur</value> + </data> + <data name="ControllerPage_CloakDevice" xml:space="preserve"> + <value>Masquer le contrôleur physique</value> + </data> + <data name="ControllerPage_CloakDeviceDesc" xml:space="preserve"> + <value>Modifier la visibilité du contrôleur physique pour les autres applications</value> + </data> + <data name="ControllerPage_Connect" xml:space="preserve"> + <value>Connecter</value> + </data> + <data name="ControllerPage_Controller" xml:space="preserve"> + <value>Controller</value> + </data> + <data name="ControllerPage_DeviceCloaking" xml:space="preserve"> + <value>Contrôleur</value> + </data> + <data name="ControllerPage_Disconnect" xml:space="preserve"> + <value>Déconnecter</value> + </data> + <data name="ControllerPage_InputDevices" xml:space="preserve"> + <value>Dispositifs d'entrée</value> + </data> + <data name="ControllerPage_UncloakOnClose" xml:space="preserve"> + <value>Dévoiler à la fermeture</value> + </data> + <data name="ControllerPage_UncloakOnCloseDesc" xml:space="preserve"> + <value>Rétablissement de la visibilité des contrôleurs physique pour les autres applications à l'arrêt du service</value> + </data> + <data name="ControllerPage_VibrationStrength" xml:space="preserve"> + <value>Force de vibration</value> + </data> + <data name="ControllerPage_VibrationStrengthExpl" xml:space="preserve"> + <value>Modification de la force de vibration du contrôleur</value> + </data> + <data name="MainWindow_HandheldCompanion" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="MainWindow_navAbout" xml:space="preserve"> + <value>À propos</value> + </data> + <data name="MainWindow_navController" xml:space="preserve"> + <value>Contrôleur</value> + </data> + <data name="MainWindow_navOverlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="MainWindow_navProfiles" xml:space="preserve"> + <value>Profils</value> + </data> + <data name="MainWindow_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="MainWindow_Settings" xml:space="preserve"> + <value>Paramètres</value> + </data> + <data name="OverlayPage_Alignment" xml:space="preserve"> + <value>Alignement</value> + </data> + <data name="OverlayPage_AlignmentDesc" xml:space="preserve"> + <value>Modifier l'alignement du contrôleur</value> + </data> + <data name="OverlayPage_AlignmentTrackpadDesc" xml:space="preserve"> + <value>Modifier l'alignement des trackpads</value> + </data> + <data name="OverlayPage_BackButton" xml:space="preserve"> + <value>Retour</value> + </data> + <data name="OverlayPage_CameraAnglePitch" xml:space="preserve"> + <value>Stationary pitch</value> + </data> + <data name="OverlayPage_CameraAnglePitchDesc" xml:space="preserve"> + <value>Change the angle, in degree</value> + </data> + <data name="OverlayPage_ControllerOptions" xml:space="preserve"> + <value>Options du contrôleur</value> + </data> + <data name="OverlayPage_EmulatedController" xml:space="preserve"> + <value>Contrôleur émulé</value> + </data> + <data name="OverlayPage_FaceCamera" xml:space="preserve"> + <value>Face camera</value> + </data> + <data name="OverlayPage_FaceCameraDesc" xml:space="preserve"> + <value>3D Model slowly rotates to face camera as default position</value> + </data> + <data name="OverlayPage_MainTrigger" xml:space="preserve"> + <value>Déclencheur principal</value> + </data> + <data name="OverlayPage_MainTriggerDesc" xml:space="preserve"> + <value>Modifier le déclencheur principal</value> + </data> + <data name="OverlayPage_OEMController" xml:space="preserve"> + <value>Contrôleur OEM</value> + </data> + <data name="OverlayPage_Opacity" xml:space="preserve"> + <value>Opacité</value> + </data> + <data name="OverlayPage_OpacityTrackpadDesc" xml:space="preserve"> + <value>Modifier l'opacité des trackpads</value> + </data> + <data name="OverlayPage_Overlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="OverlayPage_OverlayModel" xml:space="preserve"> + <value>Modèle du contrôleur</value> + </data> + <data name="OverlayPage_OverlayModelDesc" xml:space="preserve"> + <value>Modifier le modèle du contrôleur</value> + </data> + <data name="OverlayPage_OverlayPreview" xml:space="preserve"> + <value>Aperçu de l'overlay</value> + </data> + <data name="OverlayPage_Size" xml:space="preserve"> + <value>Taille</value> + </data> + <data name="OverlayPage_SizeDesc" xml:space="preserve"> + <value>Modifier la taille du contrôleur</value> + </data> + <data name="OverlayPage_SizeOverlayDesc" xml:space="preserve"> + <value>Modifier la taille des trackpads</value> + </data> + <data name="OverlayPage_StartButton" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="OverlayPage_TrackpadsOptions" xml:space="preserve"> + <value>Options des trackpads</value> + </data> + <data name="OverlayPage_ZDOPlusController" xml:space="preserve"> + <value>ZD O+</value> + </data> + <data name="Overlay_Overlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="SettingsMode0_AdditionalSettings" xml:space="preserve"> + <value>Paramètres supplémentaires</value> + </data> + <data name="SettingsMode0_CameraOptions" xml:space="preserve"> + <value>Options de la caméra</value> + </data> + <data name="SettingsMode0_CustomResponseCurve" xml:space="preserve"> + <value>Courbe de réponse personnalisée</value> + </data> + <data name="SettingsMode0_CustomResponseCurveGameOutput" xml:space="preserve"> + <value>Commande envoyée au jeu</value> + </data> + <data name="SettingsMode0_CustomResponseIntensity" xml:space="preserve"> + <value>Intensité du mouvement</value> + </data> + <data name="SettingsMode0_CustomResponsePresetAgressive" xml:space="preserve"> + <value>Agressif</value> + </data> + <data name="SettingsMode0_CustomResponsePresetDefault" xml:space="preserve"> + <value>Défaut</value> + </data> + <data name="SettingsMode0_CustomResponsePresetOptions" xml:space="preserve"> + <value>Options de préréglage</value> + </data> + <data name="SettingsMode0_CustomResponsePresetPrecise" xml:space="preserve"> + <value>Précis</value> + </data> + <data name="SettingsMode0_FlickDuration" xml:space="preserve"> + <value>Durée du mouvement</value> + </data> + <data name="SettingsMode0_FlickDurationDesc" xml:space="preserve"> + <value>Modifier la durée du mouvement, calibrée à un virage de 180 degrés, en millisecondes</value> + </data> + <data name="SettingsMode0_FlickStick" xml:space="preserve"> + <value>Flick stick (expérimental)</value> + </data> + <data name="SettingsMode0_FlickStickDesc" xml:space="preserve"> + <value>Pointer la caméra dans la direction du mouvement du joystick droit, faites pivoter la caméra purement dans le plan horizontal en tournant le joystick</value> + </data> + <data name="SettingsMode0_FlickStickEnable" xml:space="preserve"> + <value>Activer flick stick</value> + </data> + <data name="SettingsMode0_Sensitivity" xml:space="preserve"> + <value>Sensibilité</value> + </data> + <data name="SettingsMode0_SensitivityDesc" xml:space="preserve"> + <value>Modifier la sensibilité au mouvement de l'axe horizontal et vertical</value> + </data> + <data name="SettingsMode0_StickSensitivtity" xml:space="preserve"> + <value>Sensibilité du joystick</value> + </data> + <data name="SettingsMode0_StickSensitivtityDesc" xml:space="preserve"> + <value>Modifier la vitesse de rotation</value> + </data> + <data name="SettingsMode1_AdditionalSettings" xml:space="preserve"> + <value>Paramètres supplémentaires</value> + </data> + <data name="SettingsMode1_Deadzone" xml:space="preserve"> + <value>Zone morte</value> + </data> + <data name="SettingsMode1_DeadzoneDesc" xml:space="preserve"> + <value>Modifier la zone morte de la direction, en degré. Améliore la rectitude de la direction</value> + </data> + <data name="SettingsMode1_JoystickGameInput" xml:space="preserve"> + <value>Entrée de jeu par joystick</value> + <comment>// not good</comment> + </data> + <data name="SettingsMode1_JoystickSteering" xml:space="preserve"> + <value>Direction par joystick</value> + </data> + <data name="SettingsMode1_JoystickSteeringOptions" xml:space="preserve"> + <value>Options de direction par joystick</value> + </data> + <data name="SettingsMode1_JoystickSteeringPreview" xml:space="preserve"> + <value>Aperçu de la direction par joystick</value> + </data> + <data name="SettingsMode1_MaxSteeringAngle" xml:space="preserve"> + <value>Angle de braquage maximal</value> + </data> + <data name="SettingsMode1_MaxSteeringAngleDesc" xml:space="preserve"> + <value>Modifier la valeur maximale de l'angle de braquage, en degrés</value> + </data> + <data name="SettingsMode1_SteeringLinearity" xml:space="preserve"> + <value>Linéarité de la direction</value> + </data> + <data name="SettingsMode1_SteeringLinearityDesc" xml:space="preserve"> + <value>Correspondance entre les commandes du joystick et la direction. Les valeurs les plus basses fournissent plus de précision près du braquage complet mais moins de précision près du centre. Des valeurs plus élevées fournissent plus de précision près du centre mais moins de précision près du braquage maximum</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplier" xml:space="preserve"> + <value>Multiplicateur de l'accéléromètre</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplierDesc" xml:space="preserve"> + <value>Modifier la valeur de l'accéléromètre rapportée par le système</value> + </data> + <data name="ProfilesPage_AdditionalSettings" xml:space="preserve"> + <value>Paramètres supplémentaires</value> + </data> + <data name="ProfilesPage_AntiDeadzone" xml:space="preserve"> + <value>Anti-zone morte</value> + </data> + <data name="ProfilesPage_AntiDeadzoneDesc" xml:space="preserve"> + <value>Modifier l'anti-zone morte, en pourcentage</value> + </data> + <data name="ProfilesPage_AreYouSureDelete1" xml:space="preserve"> + <value>Êtes-vous sûr de vouloir supprimer?</value> + </data> + <data name="ProfilesPage_AreYouSureDelete2" xml:space="preserve"> + <value>Cet élément sera supprimé immédiatement. Vous ne pouvez pas annuler cette action.</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite1" xml:space="preserve"> + <value>Etes-vous sûr de vouloir écraser?</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite2" xml:space="preserve"> + <value>Cet élément sera écrasé. Vous ne pouvez pas annuler cette action.</value> + </data> + <data name="ProfilesPage_Cancel" xml:space="preserve"> + <value>Annuler</value> + </data> + <data name="ProfilesPage_CreateNewProfile" xml:space="preserve"> + <value>Créer un nouveau profil</value> + </data> + <data name="ProfilesPage_Delete" xml:space="preserve"> + <value>Supprimer</value> + </data> + <data name="ProfilesPage_DeleteProfile" xml:space="preserve"> + <value>Supprimer le profil</value> + </data> + <data name="ProfilesPage_EnableProfile" xml:space="preserve"> + <value>Activer le profil</value> + </data> + <data name="ProfilesPage_EnableProfileDesc" xml:space="preserve"> + <value>Le profil sera automatiquement appliqué lorsque l'application associée sera détectée</value> + </data> + <data name="ProfilesPage_GlobalSettings" xml:space="preserve"> + <value>Paramètres globaux</value> + </data> + <data name="ProfilesPage_GlobalSettingsDesc" xml:space="preserve"> + <value>Modifier les paramètres des profils globaux</value> + </data> + <data name="ProfilesPage_GyrometerMultiplier" xml:space="preserve"> + <value>Multiplicateur du gyromètre</value> + </data> + <data name="ProfilesPage_GyrometerMultiplierDesc" xml:space="preserve"> + <value>Modifier la valeur du gyromètre rapportée par le système</value> + </data> + <data name="ProfilesPage_GyroSteeringAxis" xml:space="preserve"> + <value>Axe de direction du gyroscope</value> + </data> + <data name="ProfilesPage_GyroSteeringAxisDesc" xml:space="preserve"> + <value>Pour contrôler le mouvement horizontal du contrôleur, vous pouvez utiliser l'axe de lacet ou de roulis</value> + </data> + <data name="ProfilesPage_InvertHorizontalAxis" xml:space="preserve"> + <value>Inverser l'axe horizontal</value> + </data> + <data name="ProfilesPage_InvertVerticalAxis" xml:space="preserve"> + <value>Inverser l'axe vertical</value> + </data> + <data name="ProfilesPage_MotionControlSettings" xml:space="preserve"> + <value>Paramètres de contrôle du mouvement</value> + </data> + <data name="ProfilesPage_MotionControlSettingsDesc" xml:space="preserve"> + <value>Modifier les paramètres globaux de contrôle du mouvement</value> + </data> + <data name="ProfilesPage_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="ProfilesPage_ProfileDetails" xml:space="preserve"> + <value>Détails du profil</value> + </data> + <data name="ProfilesPage_ProfileName" xml:space="preserve"> + <value>Nom du profil</value> + </data> + <data name="ProfilesPage_ProfilePath" xml:space="preserve"> + <value>Chemin du profil</value> + </data> + <data name="ProfilesPage_Profiles" xml:space="preserve"> + <value>Profils</value> + </data> + <data name="ProfilesPage_ProfileSelection" xml:space="preserve"> + <value>Sélection du profil</value> + </data> + <data name="ProfilesPage_ProfileSelectionDesc" xml:space="preserve"> + <value>Sélectionner le profil que vous souhaitez modifier</value> + </data> + <data name="ProfilesPage_ProfileSettings" xml:space="preserve"> + <value>Paramètres du profil</value> + </data> + <data name="ProfilesPage_ProfileUpdated1" xml:space="preserve"> + <value>Profil mis à jour</value> + </data> + <data name="ProfilesPage_ProfileUpdated2" xml:space="preserve"> + <value>a été mis à jour.</value> + </data> + <data name="ProfilesPage_Roll" xml:space="preserve"> + <value>Roulis</value> + </data> + <data name="ProfilesPage_StyleofInput" xml:space="preserve"> + <value>Style d'entrée</value> + </data> + <data name="ProfilesPage_StyleofInputTooltip" xml:space="preserve"> + <value>Les entrées physiques du contrôleur peuvent être programmées pour agir comme différents types de dispositifs</value> + </data> + <data name="ProfilesPage_StyleofOutput" xml:space="preserve"> + <value>Dispositif de sortie</value> + </data> + <data name="ProfilesPage_StyleofOutputTooltip" xml:space="preserve"> + <value>Sélectionner le dispositif qui recevra les commandes de mouvement</value> + </data> + <data name="ProfilesPage_UMCEnable" xml:space="preserve"> + <value>Activer le contrôle universel de mouvements</value> + </data> + <data name="ProfilesPage_UMCMotionOff" xml:space="preserve"> + <value>Disabled, turn on with button(s)</value> + </data> + <data name="ProfilesPage_UMCMotionOn" xml:space="preserve"> + <value>Enabled, turn off with button(s)</value> + </data> + <data name="ProfilesPage_UMCMotionOnOff" xml:space="preserve"> + <value>Motion activation</value> + </data> + <data name="ProfilesPage_UMCMotionOnOffDesc" xml:space="preserve"> + <value>With motion input disabled, use selected button(s) to enable motion, +with motion input enabled, use selected button(s) to disable motion.</value> + </data> + <data name="ProfilesPage_UMCSelectionRightLeftDesc" xml:space="preserve"> + <value>Sélectionner le dispositif qui recevra les commandes de mouvement</value> + </data> + <data name="ProfilesPage_UMCSettings" xml:space="preserve"> + <value>Paramètres du contrôle universel de mouvements</value> + </data> + <data name="ProfilesPage_UMCSettingsDesc" xml:space="preserve"> + <value>Traduire les mouvements du dispositif en entrées du contrôleur</value> + </data> + <data name="ProfilesPage_UpdateProfile" xml:space="preserve"> + <value>Mettre à jour le profil</value> + </data> + <data name="ProfilesPage_Whitelist" xml:space="preserve"> + <value>Autoriser l'application à accéder au contrôleur physique du dispositif</value> + </data> + <data name="ProfilesPage_Wrapper" xml:space="preserve"> + <value>Compatibilitée améliorée (XInputPlus)</value> + </data> + <data name="ProfilesPage_Yaw" xml:space="preserve"> + <value>Lacet</value> + </data> + <data name="ProfilesPage_Yes" xml:space="preserve"> + <value>Oui</value> + </data> + <data name="ServiceDescription" xml:space="preserve"> + <value>Fournit une prise en charge du gyroscope et de l'accéléromètre aux ordinateurs de jeu portables Windows par le biais d'un contrôleur virtuel. Si le service est activé, le contrôleur intégré sera masqué pour les applications en dehors de la liste blanche. Si le service est désactivé, le contrôleur embarqué ne sera plus caché et le contrôleur virtuel sera désactivé.</value> + </data> + <data name="ServiceName" xml:space="preserve"> + <value>Controller Service</value> + </data> + <data name="SettingsPage_AppLanguage" xml:space="preserve"> + <value>Langue</value> + </data> + <data name="SettingsPage_AppLanguageDesc" xml:space="preserve"> + <value>La langue de l'application</value> + </data> + <data name="SettingsPage_AppLanguageWarning" xml:space="preserve"> + <value>Redémarrage nécessaire</value> + </data> + <data name="SettingsPage_AppLanguageWarningDesc" xml:space="preserve"> + <value>Pour que les modifications soient prises en compte, veuillez redémarrer l'application</value> + </data> + <data name="SettingsPage_AppTheme" xml:space="preserve"> + <value>Thème de l'application</value> + </data> + <data name="SettingsPage_AppThemeDesc" xml:space="preserve"> + <value>Le thème de l'application, mode clair ou foncé</value> + </data> + <data name="SettingsPage_AutoStartApp" xml:space="preserve"> + <value>Démarrage automatique de l'application</value> + </data> + <data name="SettingsPage_AutoStartAppDesc" xml:space="preserve"> + <value>L'application démarre automatiquement lorsque je me connecte à Windows</value> + </data> + <data name="SettingsPage_CheckForUpdates" xml:space="preserve"> + <value>Vérifier les mises à jour</value> + </data> + <data name="SettingsPage_CloseMinimizes" xml:space="preserve"> + <value>Fermer l'application la réduira</value> + </data> + <data name="SettingsPage_CloseMinimizesDesc" xml:space="preserve"> + <value>L'application sera réduite au lieu d'être fermée</value> + </data> + <data name="SettingsPage_DownloadingPercentage" xml:space="preserve"> + <value>Téléchargement - </value> + </data> + <data name="SettingsPage_GeneralOptions" xml:space="preserve"> + <value>Options générales</value> + </data> + <data name="SettingsPage_InstallNow" xml:space="preserve"> + <value>Installer maintenant</value> + </data> + <data name="SettingsPage_LastChecked" xml:space="preserve"> + <value>Dernière vérification: </value> + </data> + <data name="SettingsPage_NotificationOptions" xml:space="preserve"> + <value>Options de notification</value> + </data> + <data name="SettingsPage_OpenAppBackground" xml:space="preserve"> + <value>Ouvrir l'application en arrière-plan</value> + </data> + <data name="SettingsPage_OpenAppBackgroundDesc" xml:space="preserve"> + <value>L'application démarre initialement réduite et apparaît dans la barre des tâches</value> + </data> + <data name="SettingsPage_SensorOptions" xml:space="preserve"> + <value>Sensor options</value> + <comment>Translate</comment> + </data> + <data name="SettingsPage_SensorPlacementDirection" xml:space="preserve"> + <value>External sensor placement direction</value> + <comment>Translate</comment> + </data> + <data name="SettingsPage_SensorPlacementDirectionDesc" xml:space="preserve"> + <value>Select on which side of the device the senor has been mounted</value> + <comment>Translate</comment> + </data> + <data name="SettingsPage_SensorPlacementUpsideDown" xml:space="preserve"> + <value>External sensor upside down</value> + <comment>Translate</comment> + </data> + <data name="SettingsPage_SensorPlacementUpsideDownDesc" xml:space="preserve"> + <value>Sensor has been mounted upside down, possible with USB-C convertor</value> + <comment>Translate</comment> + </data> + <data name="SettingsPage_SensorSelection" xml:space="preserve"> + <value>Sensor selection</value> + <comment>Translate</comment> + </data> + <data name="SettingsPage_SensorSelectionDesc" xml:space="preserve"> + <value>Select the desired sensor used for motion input</value> + <comment>Translate</comment> + </data> + <data name="SettingsPage_Settings" xml:space="preserve"> + <value>Paramètres</value> + </data> + <data name="SettingsPage_StartupType" xml:space="preserve"> + <value>Type de démarrage</value> + </data> + <data name="SettingsPage_StartupTypeDesc" xml:space="preserve"> + <value>Utilisé par le gestionnaire du service pour définir le type de démarrage du service</value> + </data> + <data name="SettingsPage_ThemeDark" xml:space="preserve"> + <value>Foncé</value> + </data> + <data name="SettingsPage_ThemeLight" xml:space="preserve"> + <value>Clair</value> + </data> + <data name="SettingsPage_ToastNotification" xml:space="preserve"> + <value>Notification toast</value> + </data> + <data name="SettingsPage_ToastNotificationDesc" xml:space="preserve"> + <value>Obtenir des notifications de l'application dans le centre d'action Windows</value> + </data> + <data name="SettingsPage_UpdateAvailable" xml:space="preserve"> + <value>Mises à jour disponibles</value> + </data> + <data name="SettingsPage_UpdateCheck" xml:space="preserve"> + <value>Vérification des mises à jour...</value> + </data> + <data name="SettingsPage_UpToDate" xml:space="preserve"> + <value>Vous êtes à jour</value> + </data> + <data name="ToastNewControllerEx" xml:space="preserve"> + <value>Le contrôleur est maintenant masqué et les entrées sont transmises au contrôleur virtuel</value> + </data> + <data name="User" xml:space="preserve"> + <value>Utilisateur</value> + </data> + <data name="WarningElevated" xml:space="preserve"> + <value>Exécutez cet outil en tant qu'administrateur pour déverrouiller ces paramètres</value> + </data> + <data name="AboutPage_SensorExternal" xml:space="preserve"> + <value>External</value> + </data> + <data name="AboutPage_SensorInternal" xml:space="preserve"> + <value>Internal</value> + </data> + <data name="InputsHotkey_decreaseTDP" xml:space="preserve"> + <value>Decrease thermal power limit (TDP)</value> + </data> + <data name="InputsHotkey_decreaseTDPDesc" xml:space="preserve"> + <value>Decrease system or currently applied profile TDP by one watt</value> + </data> + <data name="InputsHotkey_fallbackInput" xml:space="preserve"> + <value>Press to define hotkey input</value> + </data> + <data name="InputsHotkey_fallbackOutput" xml:space="preserve"> + <value>Press to define keyboard output</value> + </data> + <data name="InputsHotkey_increaseTDP" xml:space="preserve"> + <value>Increase thermal power limit (TDP)</value> + </data> + <data name="InputsHotkey_increaseTDPDesc" xml:space="preserve"> + <value>Increase system or currently applied profile TDP by one watt</value> + </data> + <data name="InputsHotkey_overlayGamepad" xml:space="preserve"> + <value>Display 3D controller</value> + </data> + <data name="InputsHotkey_overlayGamepadDesc" xml:space="preserve"> + <value>Change 3D hotkey by pressing a button or a special key</value> + </data> + <data name="InputsHotkey_overlayTrackpads" xml:space="preserve"> + <value>Display virtual trackpads</value> + </data> + <data name="InputsHotkey_overlayTrackpadsDesc" xml:space="preserve"> + <value>Change hotkey by pressing a button or a special key</value> + </data> + <data name="InputsHotkey_quickTools" xml:space="preserve"> + <value>Summon quick tools window</value> + </data> + <data name="InputsHotkey_quickToolsDesc" xml:space="preserve"> + <value>Change hotkey by pressing a button or a special key</value> + </data> + <data name="InputsHotkey_shortcutCustom" xml:space="preserve"> + <value>Custom shortcut</value> + </data> + <data name="InputsHotkey_shortcutCustomDesc" xml:space="preserve"> + <value>Change hotkey by pressing a button or a special key</value> + </data> + <data name="InputsHotkey_shortcutDesktop" xml:space="preserve"> + <value>Display and hide the desktop</value> + </data> + <data name="InputsHotkey_shortcutDesktopDesc" xml:space="preserve"> + <value>Press this key: Windows + D</value> + </data> + <data name="InputsHotkey_shortcutESC" xml:space="preserve"> + <value>Exit</value> + </data> + <data name="InputsHotkey_shortcutESCDesc" xml:space="preserve"> + <value>Press this key: Escape</value> + </data> + <data name="InputsHotkey_shortcutExpand" xml:space="preserve"> + <value>Switches between window and full screen</value> + </data> + <data name="InputsHotkey_shortcutExpandDesc" xml:space="preserve"> + <value>Press this key: Alt + Enter</value> + </data> + <data name="InputsHotkey_shortcutGuide" xml:space="preserve"> + <value>Guide or PS button</value> + </data> + <data name="InputsHotkey_shortcutGuideDesc" xml:space="preserve"> + <value>Simulate a Xbox Guide or Sony PS button input</value> + </data> + <data name="InputsHotkey_shortcutKeyboard" xml:space="preserve"> + <value>Display touch keyboard</value> + </data> + <data name="InputsHotkey_shortcutKeyboardDesc" xml:space="preserve"> + <value>Change hotkey by pressing a button or a special key</value> + </data> + <data name="InputsHotkey_shortcutMainwindow" xml:space="preserve"> + <value>Display and hide main window</value> + </data> + <data name="InputsHotkey_shortcutMainwindowDesc" xml:space="preserve"> + <value>Change hotkey by pressing a button or a special key</value> + </data> + <data name="InputsHotkey_shortcutTaskManager" xml:space="preserve"> + <value>Open Task Manager</value> + </data> + <data name="InputsHotkey_shortcutTaskManagerDesc" xml:space="preserve"> + <value>Press this key: Ctrl + Shift + Esc</value> + </data> + <data name="InputsHotkey_shortcutTaskview" xml:space="preserve"> + <value>Open Task view</value> + </data> + <data name="InputsHotkey_shortcutTaskviewDesc" xml:space="preserve"> + <value>Press this key: Windows + Tab</value> + </data> + <data name="InputsHotkey_suspendResumeTask" xml:space="preserve"> + <value>Toggle Suspender</value> + </data> + <data name="InputsHotkey_suspendResumeTaskDesc" xml:space="preserve"> + <value>Suspend or resume the foreground application</value> + </data> + <data name="OverlayPage_8BitDoLite2Controller" xml:space="preserve"> + <value>8BitDo Lite 2</value> + </data> + <data name="OverlayPage_AlwaysOnTop" xml:space="preserve"> + <value>Always on top</value> + </data> + <data name="OverlayPage_AlwaysOnTopDesc" xml:space="preserve"> + <value>When toggled, 3D controller overlay will stay on top of other windows</value> + </data> + <data name="OverlayPage_CameraAngle" xml:space="preserve"> + <value>Face camera</value> + </data> + <data name="OverlayPage_CameraAngleDesc" xml:space="preserve"> + <value>Change 3D controller overlay model behaviour for facing the camera</value> + </data> + <data name="OverlayPage_Color" xml:space="preserve"> + <value>Background Color</value> + </data> + <data name="OverlayPage_ColorDesc" xml:space="preserve"> + <value>Change 3D controller overlay background color</value> + </data> + <data name="OverlayPage_Listening" xml:space="preserve"> + <value>Listening...</value> + </data> + <data name="OverlayPage_MachenikeHG510Controller" xml:space="preserve"> + <value>MACHENIKE HG510</value> + </data> + <data name="OverlayPage_N64Controller" xml:space="preserve"> + <value>N64</value> + </data> + <data name="OverlayPage_OpacityControllerDesc" xml:space="preserve"> + <value>Change 3D controller overlay opacity</value> + </data> + <data name="OverlayPage_RenderSettings" xml:space="preserve"> + <value>Render settings</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasing" xml:space="preserve"> + <value>Anti-Aliasing</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasingDesc" xml:space="preserve"> + <value>Change the render anti-aliasing status</value> + </data> + <data name="OverlayPage_RenderSettingsDesc" xml:space="preserve"> + <value>Change the render settings of your stationary 3D overlay controller model</value> + </data> + <data name="OverlayPage_RenderSettingsFramerate" xml:space="preserve"> + <value>Update rate</value> + </data> + <data name="OverlayPage_RenderSettingsFramerateDesc" xml:space="preserve"> + <value>Change the render update rate, in update per second</value> + </data> + <data name="OverlayPage_ToyController" xml:space="preserve"> + <value>Fisher-Price controller</value> + </data> + <data name="OverlayPage_XboxOneController" xml:space="preserve"> + <value>Xbox One</value> + </data> + <data name="ProcessEx_processResume" xml:space="preserve"> + <value>Resume</value> + </data> + <data name="ProcessEx_processSuspend" xml:space="preserve"> + <value>Suspend</value> + </data> + <data name="SettingsMode0_AimingDownSights" xml:space="preserve"> + <value>Aiming down sights motion multiplier</value> + </data> + <data name="SettingsMode0_AimingDownSightsActivation" xml:space="preserve"> + <value>Activation button</value> + </data> + <data name="SettingsMode0_AimingDownSightsDesc" xml:space="preserve"> + <value>An additional motion sensitivity multiplier when aiming down sights or scope through the use of the configured activation button</value> + </data> + <data name="SettingsMode0_AimingDownSightsMultiplier" xml:space="preserve"> + <value>Multiplier value</value> + </data> + <data name="ProfilesPage_BoostPower" xml:space="preserve"> + <value>Boost power limit</value> + </data> + <data name="ProfilesPage_BoostPowerDesc" xml:space="preserve"> + <value>Change boost thermal power limit</value> + </data> + <data name="ProfilesPage_PowerSettings" xml:space="preserve"> + <value>Power settings</value> + </data> + <data name="ProfilesPage_PowerSettingsDesc" xml:space="preserve"> + <value>Change the power settings</value> + </data> + <data name="ProfilesPage_SustainedPower" xml:space="preserve"> + <value>Sustained power limit</value> + </data> + <data name="ProfilesPage_SustainedPowerDesc" xml:space="preserve"> + <value>Change sustained thermal power limit</value> + </data> + <data name="ProfilesPage_TDPOverride" xml:space="preserve"> + <value>Thermal Power (TDP) Override</value> + </data> + <data name="ProfilesPage_TDPOverrideDesc" xml:space="preserve"> + <value>Override the processor thermal power limit</value> + </data> + <data name="QuickPerformancePage_GPUControl" xml:space="preserve"> + <value>Manual GPU Clock Control</value> + </data> + <data name="QuickPerformancePage_GPUControlDesc" xml:space="preserve"> + <value>Sets the GPU to a fixed clock</value> + </data> + <data name="QuickPerformancePage_GPUUnit" xml:space="preserve"> + <value> MHz</value> + </data> + <data name="QuickPerformancePage_PowerMode" xml:space="preserve"> + <value>Power mode</value> + </data> + <data name="QuickPerformancePage_PowerModeBalanced" xml:space="preserve"> + <value>Balanced</value> + </data> + <data name="QuickPerformancePage_PowerModeDesc" xml:space="preserve"> + <value>Optimize your device based on power use and performance</value> + </data> + <data name="QuickPerformancePage_PowerModeEfficiency" xml:space="preserve"> + <value>Efficiency</value> + </data> + <data name="QuickPerformancePage_PowerModePerformance" xml:space="preserve"> + <value>Performance</value> + </data> + <data name="QuickPerformancePage_TDPLimit" xml:space="preserve"> + <value>Thermal Power (TDP) Limit</value> + </data> + <data name="QuickPerformancePage_TDPLimitDesc" xml:space="preserve"> + <value>Limits processor power for less total power</value> + </data> + <data name="QuickPerformancePage_TDPOverWrittenWarning" xml:space="preserve"> + <value>Thermal Power Limit is overwritten by a profile</value> + </data> + <data name="QuickPerformancePage_TDPUnitWatt" xml:space="preserve"> + <value> W</value> + </data> + <data name="QuickProfilesPage_Create" xml:space="preserve"> + <value>Create profile</value> + </data> + <data name="QuickProfilesPage_Waiting" xml:space="preserve"> + <value>Waiting for foreground process...</value> + </data> + <data name="SettingsPage_Backdrop" xml:space="preserve"> + <value>Use Acrylic backdrop</value> + </data> + <data name="SettingsPage_BackdropAcrylic" xml:space="preserve"> + <value>Acrylic</value> + </data> + <data name="SettingsPage_BackdropDesc" xml:space="preserve"> + <value>The application will use acrylic backdrop</value> + </data> + <data name="SettingsPage_BackdropMica" xml:space="preserve"> + <value>Mica</value> + </data> + <data name="SettingsPage_BackdropNone" xml:space="preserve"> + <value>None</value> + </data> + <data name="SettingsPage_BackdropTabbed" xml:space="preserve"> + <value>Tabbed</value> + </data> + <data name="SettingsPage_Download" xml:space="preserve"> + <value>Download</value> + </data> + <data name="SettingsPage_EcoQoS" xml:space="preserve"> + <value>EcoQoS</value> + </data> + <data name="SettingsPage_EcoQoSDesc" xml:space="preserve"> + <value>Throttle inactive or background processes and applications to improve energy efficency</value> + </data> + <data name="SettingsPage_SensorExternal" xml:space="preserve"> + <value>External</value> + </data> + <data name="SettingsPage_SensorInternal" xml:space="preserve"> + <value>Internal</value> + </data> + <data name="SettingsPage_TDPMax" xml:space="preserve"> + <value>Maximum Power</value> + </data> + <data name="SettingsPage_TDPMaxDesc" xml:space="preserve"> + <value>The maximum power in watts supplied to the processor</value> + </data> + <data name="SettingsPage_TDPMin" xml:space="preserve"> + <value>Minimum Power</value> + </data> + <data name="SettingsPage_TDPMinDesc" xml:space="preserve"> + <value>The minimum power in watts supplied to the processor</value> + </data> + <data name="SettingsPage_TDPRangeOverride" xml:space="preserve"> + <value>Configurable Power (cTDP) override</value> + </data> + <data name="SettingsPage_TDPRangeOverrideDesc" xml:space="preserve"> + <value>Allows to modify the minimum and maximum power values (TDP) beyond the CPU specifications</value> + </data> + <data name="SettingsPage_UpdateFailedDownload" xml:space="preserve"> + <value>We couldn't download the update file.</value> + </data> + <data name="SettingsPage_UpdateFailedGithub" xml:space="preserve"> + <value>We couldn't reach github.</value> + </data> + <data name="SettingsPage_UpdateFailedInstall" xml:space="preserve"> + <value>We couldn't locate the update file.</value> + </data> + <data name="SettingsPage_UpdateWarning" xml:space="preserve"> + <value>Oups. There was an issue.</value> + </data> + <data name="ProfilesPage_AntiDeadzoneUnitPercentage" xml:space="preserve"> + <value> %</value> + </data> + <data name="InputsHotkey_shortcutKillApp" xml:space="preserve"> + <value>Force application shutdown</value> + </data> + <data name="InputsHotkey_shortcutKillAppDesc" xml:space="preserve"> + <value>Change hotkey by pressing a button or a special key</value> + </data> + <data name="OverlayPage_DualSenseController" xml:space="preserve"> + <value>PlayStation DualSense</value> + </data> + <data name="OverlayPage_MotionActivated" xml:space="preserve"> + <value>Motion</value> + </data> + <data name="OverlayPage_MotionActivatedDesc" xml:space="preserve"> + <value>Model will move in accordance with user movements based on sensor information</value> + </data> + <data name="SettingsMode0_SensitivityX" xml:space="preserve"> + <value>Sensitivity X</value> + </data> + <data name="SettingsMode0_SensitivityXDesc" xml:space="preserve"> + <value>Change the motion sensitivity of the horizontal axis</value> + </data> + <data name="SettingsMode0_SensitivityY" xml:space="preserve"> + <value>Sensitivity Y</value> + </data> + <data name="SettingsMode0_SensitivityYDesc" xml:space="preserve"> + <value>Change the motion sensitivity of the vertical axis</value> + </data> + <data name="ProfilesPage_SensitivityX" xml:space="preserve"> + <value>X</value> + </data> + <data name="ProfilesPage_SensitivityY" xml:space="preserve"> + <value>Y</value> + </data> + <data name="QuickPerformancePage_TDPBoost" xml:space="preserve"> + <value>Boost</value> + </data> + <data name="QuickPerformancePage_TDPSustained" xml:space="preserve"> + <value>Sustained</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrder" xml:space="preserve"> + <value>Améliorer la détection du contrôleur virtuel</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderDesc" xml:space="preserve"> + <value>Force le contrôleur virtuel à être en première position. Activez cette option si votre application/jeu ne détecte pas votre manette (redémarrage de l'appareil requis).</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderPrimary" xml:space="preserve"> + <value>Oui</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderSecondary" xml:space="preserve"> + <value>Non</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderText" xml:space="preserve"> + <value>Le redémarrage de votre appareil est requis pour que les changements soient pris en compte. Voulez-vous redémarrer maintenant ?</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderTitle" xml:space="preserve"> + <value>Redémarrage requis</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyPrimary" xml:space="preserve"> + <value>Oui</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencySecondary" xml:space="preserve"> + <value>Non</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyText" xml:space="preserve"> + <value>Si vous désactivez ce paramètre, le paramètre "Amélioration de la détection du contrôleur virtuel" sera aussi désactivé. Voulez-vous continuer?</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyTitle" xml:space="preserve"> + <value>Avertissement</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderClosePrimary" xml:space="preserve"> + <value>Oui</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseSecondary" xml:space="preserve"> + <value>Non</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseText" xml:space="preserve"> + <value>Améliorer la détection du controlleur ne fonctionnera pas si vous fermez Handheld Companion. Êtes-vous sûr?</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseTitle" xml:space="preserve"> + <value>Avertissement</value> + </data> + <data name="AboutPage_Manufacturer" xml:space="preserve"> + <value>Manufacturer</value> + </data> + <data name="AboutPage_Partner" xml:space="preserve"> + <value>Partner</value> + </data> + <data name="AboutPage_ProductName" xml:space="preserve"> + <value>Product name</value> + </data> + <data name="ButtonsPage_ABXY" xml:space="preserve"> + <value>A,B,X,Y</value> + </data> + <data name="ButtonsPage_Back_Grips" xml:space="preserve"> + <value>BACK GRIPS</value> + </data> + <data name="ButtonsPage_Bumpers" xml:space="preserve"> + <value>BUMPERS</value> + </data> + <data name="ButtonsPage_Menu" xml:space="preserve"> + <value>MENU</value> + </data> + <data name="ButtonsPage_OEM" xml:space="preserve"> + <value>OEM</value> + </data> + <data name="ControllerPage_DesktopLayout" xml:space="preserve"> + <value>Desktop layout</value> + </data> + <data name="ControllerPage_DesktopLayoutDefine" xml:space="preserve"> + <value>Define desktop layout</value> + </data> + <data name="ControllerPage_DesktopLayoutDefineController" xml:space="preserve"> + <value>Define controller layout when in desktop mode</value> + </data> + <data name="ControllerPage_DesktopLayoutEdit" xml:space="preserve"> + <value>Edit</value> + </data> + <data name="ControllerPage_DesktopLayoutEnable" xml:space="preserve"> + <value>Enable desktop layout</value> + </data> + <data name="ControllerPage_DeviceSettings" xml:space="preserve"> + <value>Controller settings</value> + </data> + <data name="ControllerPage_NonGameControllerLayouts" xml:space="preserve"> + <value>Non-game controller layouts</value> + </data> + <data name="ControllerPage_NoPhysicalControllerAction" xml:space="preserve"> + <value>You might want to click on Connect next to your plugged controller.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDesc" xml:space="preserve"> + <value>You have no physical controller connected. No inputs will be sent to HC or its service.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedAction" xml:space="preserve"> + <value>Please make sure you connected a compatible XInput or DInput device.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedWarning" xml:space="preserve"> + <value>No physical controller detected</value> + </data> + <data name="ControllerPage_NoPhysicalControllerWarning" xml:space="preserve"> + <value>No physical controller connected</value> + </data> + <data name="ControllerPage_NoVirtualControllerAction" xml:space="preserve"> + <value>You might want to start companion service or make sure your virtual controller status is set to: Connected</value> + </data> + <data name="ControllerPage_NoVirtualControllerDesc" xml:space="preserve"> + <value>Your physical controller is hidden, yet you have no virtual controller available. No inputs will be sent to games.</value> + </data> + <data name="ControllerPage_NoVirtualControllerWarning" xml:space="preserve"> + <value>No virtual controller detected</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenAction" xml:space="preserve"> + <value>You might want to unmute your virtual controller or unhide your physical controller.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenDesc" xml:space="preserve"> + <value>Your physical controller is hidden, yet you have muted your virtual controller.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenWarning" xml:space="preserve"> + <value>Physical controller is hidden</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenAction" xml:space="preserve"> + <value>You might want to hide your physical controller or mute your virtual controller.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenDesc" xml:space="preserve"> + <value>Your physical controller is not hidden, yet you have an unmuted virtual controller. You might encounter double inputs in games.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenWarning" xml:space="preserve"> + <value>Physical controller is not hidden</value> + </data> + <data name="ControllerPage_SteamControllerHDRumble" xml:space="preserve"> + <value>HD rumble</value> + </data> + <data name="ControllerPage_SteamControllerHDRumbleDesc" xml:space="preserve"> + <value>Use high-definition rumble engine, at the cost of higher CPU usage</value> + </data> + <data name="ControllerPage_SteamControllerMute" xml:space="preserve"> + <value>Mute virtual controller</value> + </data> + <data name="ControllerPage_SteamControllerMuteDesc" xml:space="preserve"> + <value>Mute virtual controller on steam related applications</value> + </data> + <data name="ControllerPage_SteamControllerSettings" xml:space="preserve"> + <value>Steam Deck Settings</value> + </data> + <data name="ControllerPage_VibrateDevice" xml:space="preserve"> + <value>Vibrate controller on connect</value> + </data> + <data name="ControllerPage_VibrateDeviceDesc" xml:space="preserve"> + <value>Vibrate physical controller when connected</value> + </data> + <data name="DPadPage_DPad" xml:space="preserve"> + <value>DIRECTIONAL PAD</value> + </data> + <data name="InputsHotkey_decreaseBrightness" xml:space="preserve"> + <value>Decrease brightness</value> + </data> + <data name="InputsHotkey_decreaseBrightnessDesc" xml:space="preserve"> + <value>Decrease the current display brightness by 5%</value> + </data> + <data name="InputsHotkey_decreaseVolume" xml:space="preserve"> + <value>Decrease volume</value> + </data> + <data name="InputsHotkey_decreaseVolumeDesc" xml:space="preserve"> + <value>Decrease the system volume by 5%</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabledDesc" xml:space="preserve"> + <value>Toggle Desktop controller layout</value> + </data> + <data name="InputsHotkey_increaseBrightness" xml:space="preserve"> + <value>Increase brightness</value> + </data> + <data name="InputsHotkey_increaseBrightnessDesc" xml:space="preserve"> + <value>Increase the current display brightness by 5%</value> + </data> + <data name="InputsHotkey_increaseVolume" xml:space="preserve"> + <value>Increase volume</value> + </data> + <data name="InputsHotkey_increaseVolumeDesc" xml:space="preserve"> + <value>Increase the system volume by 5%</value> + </data> + <data name="InputsHotkey_OnScreenDisplay" xml:space="preserve"> + <value>On-screen display</value> + </data> + <data name="InputsHotkey_OnScreenDisplayDesc" xml:space="preserve"> + <value>Enable on-screen display support</value> + </data> + <data name="InputsHotkey_QuietModeToggled" xml:space="preserve"> + <value>Fan override</value> + </data> + <data name="InputsHotkey_QuietModeToggledDesc" xml:space="preserve"> + <value>Set the fan duty cycle to user-defined value</value> + </data> + <data name="InputsHotkey_shortcutControlCenterDesc" xml:space="preserve"> + <value>Display and hide Windows Action center</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabled" xml:space="preserve"> + <value>Desktop layout</value> + </data> + <data name="InputsHotkey_shortcutControlCenter" xml:space="preserve"> + <value>Display Action center</value> + </data> + <data name="InputsHotkey_shortcutPrintScreen" xml:space="preserve"> + <value>Open Snipping tool</value> + </data> + <data name="InputsHotkey_shortcutPrintScreenDesc" xml:space="preserve"> + <value>Press this key: Windows + Shift + S</value> + </data> + <data name="JoystickPage_Joystick_Left" xml:space="preserve"> + <value>LEFT JOYSTICK</value> + </data> + <data name="JoystickPage_Joystick_Left_Buttons" xml:space="preserve"> + <value>LEFT JOYSTICK BUTTONS</value> + </data> + <data name="JoystickPage_Joystick_Right" xml:space="preserve"> + <value>RIGHT JOYSTICK</value> + </data> + <data name="JoystickPage_Joystick_Right_Buttons" xml:space="preserve"> + <value>RIGHT JOYSTICK BUTTONS</value> + </data> + <data name="LayoutPage_ApplyTemplate" xml:space="preserve"> + <value>Apply template</value> + </data> + <data name="LayoutPage_Buttons" xml:space="preserve"> + <value>Buttons</value> + </data> + <data name="LayoutPage_Cancel" xml:space="preserve"> + <value>Cancel</value> + </data> + <data name="LayoutPage_Community" xml:space="preserve"> + <value>COMMUNITY</value> + </data> + <data name="LayoutPage_Confirm" xml:space="preserve"> + <value>Confirm</value> + </data> + <data name="LayoutPage_Dpad" xml:space="preserve"> + <value>Dpad</value> + </data> + <data name="LayoutPage_ExportCurrentController" xml:space="preserve"> + <value>Export for current controller</value> + </data> + <data name="LayoutPage_ExportLayout" xml:space="preserve"> + <value>Export layout</value> + </data> + <data name="LayoutPage_Gyro" xml:space="preserve"> + <value>Gyro</value> + </data> + <data name="LayoutPage_Joysticks" xml:space="preserve"> + <value>Joysticks</value> + </data> + <data name="LayoutPage_LayoutAuthor" xml:space="preserve"> + <value>Layout author</value> + </data> + <data name="LayoutPage_LayoutDesc" xml:space="preserve"> + <value>Layout description</value> + </data> + <data name="LayoutPage_LayoutTitle" xml:space="preserve"> + <value>Layout title</value> + </data> + <data name="LayoutPage_SaveGameInfoLayout" xml:space="preserve"> + <value>Save game information with the layout</value> + </data> + <data name="LayoutPage_ShowCurrentControllerTemplates" xml:space="preserve"> + <value>Show current controller templates only</value> + </data> + <data name="LayoutPage_TemplatePicker" xml:space="preserve"> + <value>Layout template picker</value> + </data> + <data name="LayoutPage_Templates" xml:space="preserve"> + <value>TEMPLATES</value> + </data> + <data name="LayoutPage_Trackpads" xml:space="preserve"> + <value>Trackpads</value> + </data> + <data name="LayoutPage_Triggers" xml:space="preserve"> + <value>Triggers</value> + </data> + <data name="MainWindow_navHotkeys" xml:space="preserve"> + <value>Hotkeys</value> + </data> + <data name="OverlayPage_Millisecond" xml:space="preserve"> + <value>ms</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel" xml:space="preserve"> + <value>Overlay display level</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Disabled" xml:space="preserve"> + <value>Disabled</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Extended" xml:space="preserve"> + <value>Extended</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Full" xml:space="preserve"> + <value>Full</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Minimal" xml:space="preserve"> + <value>Minimal</value> + </data> + <data name="OverlayPage_OverlayDisplayLevelDesc" xml:space="preserve"> + <value>Change the on-screen display level of information</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRate" xml:space="preserve"> + <value>Update rate</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRateDesc" xml:space="preserve"> + <value>Change the on-screen display update rate</value> + </data> + <data name="ProfilesPage_AutoTDP" xml:space="preserve"> + <value>Automatic TDP</value> + </data> + <data name="ProfilesPage_AutoTDPDesc" xml:space="preserve"> + <value>Automatic adjustment of TDP based on measured FPS and requested FPS target</value> + </data> + <data name="ProfilesPage_AutoTDPFPS" xml:space="preserve"> + <value>Framerate target</value> + </data> + <data name="ProfilesPage_AutoTDPFPSDesc" xml:space="preserve"> + <value>Desired FPS target value for automatic TDP controller</value> + </data> + <data name="ProfilesPage_ControllerLayout" xml:space="preserve"> + <value>Controller layout</value> + </data> + <data name="ProfilesPage_ControllerLayoutDesc" xml:space="preserve"> + <value>Change the virtual controller layout</value> + </data> + <data name="ProfilesPage_ControllerSettings" xml:space="preserve"> + <value>Controller settings</value> + </data> + <data name="ProfilesPage_ControllerSettingsDesc" xml:space="preserve"> + <value>Change the virtual controller settings</value> + </data> + <data name="ProfilesPage_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="ProfilesPage_EPP" xml:space="preserve"> + <value>Energy performance preference (EPP)</value> + </data> + <data name="ProfilesPage_EPPBalance" xml:space="preserve"> + <value>CPU/GPU power balance</value> + </data> + <data name="ProfilesPage_EPPDesc" xml:space="preserve"> + <value>Specifies power distribution policy between CPU and GPU</value> + </data> + <data name="ProfilesPage_FramerateLimit" xml:space="preserve"> + <value>Framerate limit</value> + </data> + <data name="ProfilesPage_FramerateLimitDesc" xml:space="preserve"> + <value>Limits framerate for 3D applications</value> + </data> + <data name="ProfilesPage_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="ProfilesPage_GPUMhz" xml:space="preserve"> + <value>GPU Clock max frequency</value> + </data> + <data name="ProfilesPage_GPUMhzDesc" xml:space="preserve"> + <value>Maximum clock speed of the GPU in Mhz</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeft" xml:space="preserve"> + <value>Joystick deadzone % left</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeftDesc" xml:space="preserve"> + <value>Adjust the inner and outer deadzone percentage of the left joystick</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRight" xml:space="preserve"> + <value>Joystick deadzone % right</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRightDesc" xml:space="preserve"> + <value>Adjust the inner and outer deadzone percentage of the right joystick</value> + </data> + <data name="ProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Power limit target</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityDesc" xml:space="preserve"> + <value>Improve the stick circularity</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityLeft" xml:space="preserve"> + <value>Circularity stick left</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityRight" xml:space="preserve"> + <value>Circularity stick right</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeft" xml:space="preserve"> + <value>Left trigger deadzone %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeftDesc" xml:space="preserve"> + <value>Adjust the inner and outer deadzone percentage of the left trigger</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRight" xml:space="preserve"> + <value>Right trigger deadzone %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRightDesc" xml:space="preserve"> + <value>Adjust the inner and outer deadzone percentage of the right trigger</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzone" xml:space="preserve"> + <value>Motion anti-deadzone</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzoneDesc" xml:space="preserve"> + <value>Compensate for in game deadzone, improve registration of small movements</value> + </data> + <data name="Properties.Resources.ControllerPage_Disconnect" xml:space="preserve"> + <value>Disconnect</value> + </data> + <data name="QuickPerformancePage_AutoTDPUnitFPS" xml:space="preserve"> + <value>FPS</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStart" xml:space="preserve"> + <value>Enable desktop profile on start</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStartDesc" xml:space="preserve"> + <value>The desktop profile will be automatically enabled on application start</value> + </data> + <data name="SettingsPage_NativeDisplayOrientation" xml:space="preserve"> + <value>Native display orientation</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDesc" xml:space="preserve"> + <value>Some features depend on knowing the native display orientation to work properly. If this was not detected properly, set your display's orientation to the orientation that matches your controller, then click Detect</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDetect" xml:space="preserve"> + <value>Detect</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationNotSet" xml:space="preserve"> + <value>Not set</value> + </data> + <data name="SettingsPage_ThemeDefault" xml:space="preserve"> + <value>Use system setting</value> + </data> + <data name="TrackPadsPage_Trackpad_Left" xml:space="preserve"> + <value>LEFT TRACKPAD</value> + </data> + <data name="TrackPadsPage_Trackpad_Left_Buttons" xml:space="preserve"> + <value>LEFT TRACKPAD BUTTONS</value> + </data> + <data name="TrackPadsPage_Trackpad_Right" xml:space="preserve"> + <value>RIGHT TRACKPAD</value> + </data> + <data name="TrackPadsPage_Trackpad_Right_Buttons" xml:space="preserve"> + <value>RIGHT TRACKPAD BUTTONS</value> + </data> + <data name="TriggersPage_Trigger_Left" xml:space="preserve"> + <value>LEFT TRIGGER</value> + </data> + <data name="TriggersPage_Trigger_Left_Button" xml:space="preserve"> + <value>LEFT TRIGGER BUTTONS</value> + </data> + <data name="TriggersPage_Trigger_Right" xml:space="preserve"> + <value>RIGHT TRIGGER</value> + </data> + <data name="TriggersPage_Trigger_Right_Button" xml:space="preserve"> + <value>RIGHT TRIGGER BUTTONS</value> + </data> + <data name="QuickProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Power limit target</value> + </data> + <data name="SettingsPage_QuickToolsOptions" xml:space="preserve"> + <value>Quicktools options</value> + </data> + <data name="QuickPerformancePage_FanOverridePercentage" xml:space="preserve"> + <value>%</value> + </data> + <data name="QuickPerformancePage_CPUBoostModeDesc" xml:space="preserve"> + <value>Set current CPU boost mode</value> + </data> + <data name="QuickPerformancePage_CPUBoostMode" xml:space="preserve"> + <value>CPU boost mode</value> + </data> + <data name="QuickPerformancePage_FanOverrideDesc" xml:space="preserve"> + <value>Set the fan duty cycle to user-defined value</value> + </data> + <data name="QuickPerformancePage_FanOverride" xml:space="preserve"> + <value>Fan override</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRateDesc" xml:space="preserve"> + <value>Adjust main display resolution and refresh rate</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRate" xml:space="preserve"> + <value>Display resolution and refresh rate</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopRight" xml:space="preserve"> + <value>Top right</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopLeft" xml:space="preserve"> + <value>Top left</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocation" xml:space="preserve"> + <value>Window location</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomRight" xml:space="preserve"> + <value>Bottom right</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomLeft" xml:space="preserve"> + <value>Bottom left</value> + </data> + <data name="DevicePage_PowerOptions" xml:space="preserve"> + <value>Power options</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationDesc" xml:space="preserve"> + <value>Define quicktools window location</value> + </data> + <data name="SettingsPage_ThirdPartyApps" xml:space="preserve"> + <value>Third-party applications</value> + </data> + <data name="SettingsPage_SensorNone" xml:space="preserve"> + <value>None</value> + </data> + <data name="SettingsPage_QuickToolsBackdrop" xml:space="preserve"> + <value>Quicktools backdrop, none, mica, tabbed, acrylic</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServerDesc" xml:space="preserve"> + <value>Automatically turn RTSS off when companion is closed</value> + </data> + <data name="SettingsPage_HwInfoDesc" xml:space="preserve"> + <value>Automatically turn HWiNFO off when companion is closed</value> + </data> + <data name="SettingsPage_HwInfo" xml:space="preserve"> + <value>HWiNFO</value> + </data> + <data name="QuickProfilesPage_CurrentProfileDefault" xml:space="preserve"> + <value>Default</value> + </data> + <data name="QuickProfilesPage_CurrentProfile" xml:space="preserve"> + <value>Current profile:</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServer" xml:space="preserve"> + <value>RivaTuner Statistics Server</value> + </data> + <data name="SettingsPage_HideWhenLoseFocusDesc" xml:space="preserve"> + <value>Automatically hide quick tools when the user clicks outside of window</value> + </data> + <data name="SettingsPage_HideWhenLoseFocus" xml:space="preserve"> + <value>Hide when loses focus</value> + </data> + <data name="ProfilesPage_Wrapper_Disabled" xml:space="preserve"> + <value>Désactivé</value> + </data> + <data name="ProfilesPage_Wrapper_Injection" xml:space="preserve"> + <value>Injection (recommandé)</value> + </data> + <data name="ProfilesPage_Wrapper_Redirection" xml:space="preserve"> + <value>Redirection</value> + </data> + <data name="AutoRollYawSwapDesc" xml:space="preserve"> + <value>This input will operate as a simple joystick. Ideal for laptop and clamshell type handhelds, automatic yaw roll swap based on how device is being held (90 or 180 degree open).</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.A" xml:space="preserve"> + <value>Bouton A</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.AlwaysOn" xml:space="preserve"> + <value>Toujours activé</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.B" xml:space="preserve"> + <value>Bouton B</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Back" xml:space="preserve"> + <value>Retour</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadDown" xml:space="preserve"> + <value>DPAD - Bas</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadLeft" xml:space="preserve"> + <value>DPAD - Gauche</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadRight" xml:space="preserve"> + <value>DPAD - Droit</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadUp" xml:space="preserve"> + <value>DPAD - Bas</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftShoulder" xml:space="preserve"> + <value>Gâchette haute gauche</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftThumb" xml:space="preserve"> + <value>Clic stick gauche</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftTrigger" xml:space="preserve"> + <value>Gâchette gauche</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightShoulder" xml:space="preserve"> + <value>Gâchette haute droite</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightThumb" xml:space="preserve"> + <value>Clic stick droit</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightTrigger" xml:space="preserve"> + <value>Gâchette droite</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.X" xml:space="preserve"> + <value>Bouton X</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Y" xml:space="preserve"> + <value>Bouton Y</value> + </data> + <data name="Enum.HIDmode.DualShock4Controller" xml:space="preserve"> + <value>Contrôleur DualShock 4 émulé</value> + </data> + <data name="Enum.HIDmode.NoController" xml:space="preserve"> + <value>Aucun contrôleur émulé</value> + </data> + <data name="Enum.HIDmode.Xbox360Controller" xml:space="preserve"> + <value>Contrôleur XBOX 360 émulé</value> + </data> + <data name="Enum.HIDstatus.Connected" xml:space="preserve"> + <value>Connecté</value> + </data> + <data name="Enum.HIDstatus.Disconnected" xml:space="preserve"> + <value>Déconnecté</value> + </data> + <data name="Enum.Input.AutoRollYawSwap" xml:space="preserve"> + <value>Auto Roll Yaw Swap</value> + </data> + <data name="Enum.Input.JoystickCamera" xml:space="preserve"> + <value>Caméra joystick</value> + </data> + <data name="Enum.Input.JoystickSteering" xml:space="preserve"> + <value>Conduite par joystick</value> + </data> + <data name="Enum.Input.PlayerSpace" xml:space="preserve"> + <value>Player space</value> + <comment>A traduire...</comment> + </data> + <data name="Enum.Output.LeftStick" xml:space="preserve"> + <value>Joystick gauche</value> + </data> + <data name="Enum.Output.RightStick" xml:space="preserve"> + <value>Joystick droit</value> + </data> + <data name="Enum.ProfileErrorCode.Default" xml:space="preserve"> + <value>Il s'agit de votre profil de contrôleur par défaut. Ce profil sera appliqué pour toutes vos applications qui n'ont pas de profil spécifique. Certaines options nécessitant un exécutable peuvent être désactivées.</value> + </data> + <data name="Enum.ProfileErrorCode.IsRunning" xml:space="preserve"> + <value>Oups. Il semble que ce profil soit en cours d'exécution. Certaines options nécessitant un exécutable pourraient être désactivées.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingExecutable" xml:space="preserve"> + <value>Oups. Il semble que ce profil n'ait pas d'exécutable. Comment cela est-il possible ?</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPath" xml:space="preserve"> + <value>Oups. Il semble que ce profil ne dispose pas d'un chemin vers l'application. Certaines options nécessitant un exécutable pourraient être désactivées.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPermission" xml:space="preserve"> + <value>Oups. Il semble que vous n'ayez pas le niveau de permission nécessaire pour modifier le contenu de cette application. Assurez-vous que vous avez lancé ce programme en mode administrateur.</value> + </data> + <data name="Enum.ProfileErrorCode.None" xml:space="preserve"> + <value>Rien à voir ici.</value> + </data> + <data name="Enum.ServiceStartMode.Automatic" xml:space="preserve"> + <value>Automatique</value> + </data> + <data name="Enum.ServiceStartMode.Disabled" xml:space="preserve"> + <value>Désactivé</value> + </data> + <data name="Enum.ServiceStartMode.Manual" xml:space="preserve"> + <value>Manuel</value> + </data> + <data name="JoystickCameraDesc" xml:space="preserve"> + <value>Cette entrée fonctionnera comme un simple joystick. Elle est destinée aux applications traditionnelles de joystick</value> + </data> + <data name="JoystickSteeringDesc" xml:space="preserve"> + <value>Cette entrée fonctionnera comme un joystick optimisé pour le contrôle d'un volant ou d'un jeu de course</value> + </data> + <data name="PlayerSpaceDesc" xml:space="preserve"> + <value>Cette entrée fonctionnera comme un joystick optimisé pour le contrôle d'une caméra à la première ou à la troisième personne</value> + </data> + <data name="Enum.InputsHotkeyType.Handheld" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.InputsHotkeyType.Overlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="Enum.InputsHotkeyType.Quicktools" xml:space="preserve"> + <value>Quick tools</value> + </data> + <data name="Enum.InputsHotkeyType.Windows" xml:space="preserve"> + <value>Windows</value> + </data> + <data name="Enum.QualityOfServiceLevel.Default" xml:space="preserve"> + <value>Default</value> + </data> + <data name="Enum.QualityOfServiceLevel.Eco" xml:space="preserve"> + <value>Eco</value> + </data> + <data name="Enum.QualityOfServiceLevel.High" xml:space="preserve"> + <value>High</value> + </data> + <data name="Enum.InputsHotkeyType.HC" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM3" xml:space="preserve"> + <value>KB</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM1" xml:space="preserve"> + <value>Win</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM2" xml:space="preserve"> + <value>Esc</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM3" xml:space="preserve"> + <value>M1 / M2</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B1" xml:space="preserve"> + <value>Cross</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B2" xml:space="preserve"> + <value>Circle</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B3" xml:space="preserve"> + <value>Square</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B4" xml:space="preserve"> + <value>Triangle</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Start" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.InputsHotkeyType.Custom" xml:space="preserve"> + <value>Custom</value> + </data> + <data name="Enum.InputsHotkeyType.Device" xml:space="preserve"> + <value>Device</value> + </data> + <data name="Enum.MotionInput.AutoRollYawSwap" xml:space="preserve"> + <value>Auto Roll Yaw Swap</value> + </data> + <data name="Enum.MotionInput.JoystickCamera" xml:space="preserve"> + <value>Joystick Camera</value> + </data> + <data name="Enum.MotionInput.JoystickSteering" xml:space="preserve"> + <value>Joystick Steering</value> + </data> + <data name="Enum.MotionInput.PlayerSpace" xml:space="preserve"> + <value>Player Space</value> + </data> + <data name="Enum.MotionOutput.LeftStick" xml:space="preserve"> + <value>Left Stick</value> + </data> + <data name="Enum.MotionOutput.RightStick" xml:space="preserve"> + <value>Right Stick</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Back" xml:space="preserve"> + <value>View</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Special" xml:space="preserve"> + <value>STEAM</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="Enum.ProfileErrorCode.Running" xml:space="preserve"> + <value>Oops. It seems this profile excutable is running. Some options requiring an executable might be disabled.</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM1" xml:space="preserve"> + <value>Command center</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM2" xml:space="preserve"> + <value>Armory crate</value> + </data> + <data name="Enum.SteamDeck.ButtonFlags.OEM1" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightThumb" xml:space="preserve"> + <value>Right Thumb</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B5" xml:space="preserve"> + <value>B5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B6" xml:space="preserve"> + <value>B6</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B7" xml:space="preserve"> + <value>B7</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B8" xml:space="preserve"> + <value>B8</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Back" xml:space="preserve"> + <value>View</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadDown" xml:space="preserve"> + <value>DPad Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadLeft" xml:space="preserve"> + <value>DPad Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadRight" xml:space="preserve"> + <value>PPad Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadUp" xml:space="preserve"> + <value>DPad Up</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L1" xml:space="preserve"> + <value>LB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L3" xml:space="preserve"> + <value>L3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L4" xml:space="preserve"> + <value>L4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L5" xml:space="preserve"> + <value>L5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumb" xml:space="preserve"> + <value>Left Thumb</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbDown" xml:space="preserve"> + <value>Left Thumb Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbLeft" xml:space="preserve"> + <value>Left Thumb Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbRight" xml:space="preserve"> + <value>Left Thumb Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbUp" xml:space="preserve"> + <value>Left Thumb Up</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM1" xml:space="preserve"> + <value>OEM1</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM2" xml:space="preserve"> + <value>OEM2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM3" xml:space="preserve"> + <value>OEM3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R1" xml:space="preserve"> + <value>RB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R3" xml:space="preserve"> + <value>R3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R4" xml:space="preserve"> + <value>R4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R5" xml:space="preserve"> + <value>R5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumb" xml:space="preserve"> + <value>Right Thumb</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbDown" xml:space="preserve"> + <value>Right Thumb Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbLeft" xml:space="preserve"> + <value>Right Thumb Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbRight" xml:space="preserve"> + <value>Right Thumb Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbUp" xml:space="preserve"> + <value>Right Thumb Up</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Special" xml:space="preserve"> + <value>Guide</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftThumb" xml:space="preserve"> + <value>Left Thumb</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B1" xml:space="preserve"> + <value>Cross</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B2" xml:space="preserve"> + <value>Circle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B3" xml:space="preserve"> + <value>Square</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B4" xml:space="preserve"> + <value>Triangle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Start" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.KeyFlags.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.KeyFlags.Alt" xml:space="preserve"> + <value>Alt</value> + </data> + <data name="Enum.KeyFlags.Apostrophe" xml:space="preserve"> + <value>Apostrophe</value> + </data> + <data name="Enum.KeyFlags.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.KeyFlags.Backslash" xml:space="preserve"> + <value>Backslash</value> + </data> + <data name="Enum.KeyFlags.Backspace" xml:space="preserve"> + <value>Backspace</value> + </data> + <data name="Enum.KeyFlags.C" xml:space="preserve"> + <value>C</value> + </data> + <data name="Enum.KeyFlags.Comma" xml:space="preserve"> + <value>Comma</value> + </data> + <data name="Enum.KeyFlags.Control" xml:space="preserve"> + <value>Control</value> + </data> + <data name="Enum.KeyFlags.D" xml:space="preserve"> + <value>D</value> + </data> + <data name="Enum.KeyFlags.Delete" xml:space="preserve"> + <value>Delete</value> + </data> + <data name="Enum.KeyFlags.E" xml:space="preserve"> + <value>E</value> + </data> + <data name="Enum.KeyFlags.End" xml:space="preserve"> + <value>End</value> + </data> + <data name="Enum.KeyFlags.Enter" xml:space="preserve"> + <value>Enter</value> + </data> + <data name="Enum.KeyFlags.Equal" xml:space="preserve"> + <value>Equal</value> + </data> + <data name="Enum.KeyFlags.Escape" xml:space="preserve"> + <value>Escape</value> + </data> + <data name="Enum.KeyFlags.F" xml:space="preserve"> + <value>F</value> + </data> + <data name="Enum.KeyFlags.F1" xml:space="preserve"> + <value>F1</value> + </data> + <data name="Enum.KeyFlags.F10" xml:space="preserve"> + <value>F10</value> + </data> + <data name="Enum.KeyFlags.F11" xml:space="preserve"> + <value>F11</value> + </data> + <data name="Enum.KeyFlags.F12" xml:space="preserve"> + <value>F12</value> + </data> + <data name="Enum.KeyFlags.F2" xml:space="preserve"> + <value>F2</value> + </data> + <data name="Enum.KeyFlags.F3" xml:space="preserve"> + <value>F3</value> + </data> + <data name="Enum.KeyFlags.F4" xml:space="preserve"> + <value>F4</value> + </data> + <data name="Enum.KeyFlags.F5" xml:space="preserve"> + <value>F5</value> + </data> + <data name="Enum.KeyFlags.F6" xml:space="preserve"> + <value>F6</value> + </data> + <data name="Enum.KeyFlags.F7" xml:space="preserve"> + <value>F7</value> + </data> + <data name="Enum.KeyFlags.F8" xml:space="preserve"> + <value>F8</value> + </data> + <data name="Enum.KeyFlags.F9" xml:space="preserve"> + <value>F9</value> + </data> + <data name="Enum.KeyFlags.G" xml:space="preserve"> + <value>G</value> + </data> + <data name="Enum.KeyFlags.Grave" xml:space="preserve"> + <value>Grave</value> + </data> + <data name="Enum.KeyFlags.H" xml:space="preserve"> + <value>H</value> + </data> + <data name="Enum.KeyFlags.Home" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.KeyFlags.I" xml:space="preserve"> + <value>I</value> + </data> + <data name="Enum.KeyFlags.Insert" xml:space="preserve"> + <value>Insert</value> + </data> + <data name="Enum.KeyFlags.J" xml:space="preserve"> + <value>J</value> + </data> + <data name="Enum.KeyFlags.K" xml:space="preserve"> + <value>K</value> + </data> + <data name="Enum.KeyFlags.L" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.KeyFlags.M" xml:space="preserve"> + <value>M</value> + </data> + <data name="Enum.KeyFlags.Minus" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.KeyFlags.N" xml:space="preserve"> + <value>N</value> + </data> + <data name="Enum.KeyFlags.O" xml:space="preserve"> + <value>O</value> + </data> + <data name="Enum.KeyFlags.P" xml:space="preserve"> + <value>P</value> + </data> + <data name="Enum.KeyFlags.Pause" xml:space="preserve"> + <value>Pause</value> + </data> + <data name="Enum.KeyFlags.Period" xml:space="preserve"> + <value>Period</value> + </data> + <data name="Enum.KeyFlags.Q" xml:space="preserve"> + <value>Q</value> + </data> + <data name="Enum.KeyFlags.R" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.KeyFlags.S" xml:space="preserve"> + <value>S</value> + </data> + <data name="Enum.KeyFlags.Semicolon" xml:space="preserve"> + <value>Semicolon</value> + </data> + <data name="Enum.KeyFlags.Shift" xml:space="preserve"> + <value>Shift</value> + </data> + <data name="Enum.KeyFlags.Slash" xml:space="preserve"> + <value>Slash</value> + </data> + <data name="Enum.KeyFlags.Space" xml:space="preserve"> + <value>Space</value> + </data> + <data name="Enum.KeyFlags.T" xml:space="preserve"> + <value>T</value> + </data> + <data name="Enum.KeyFlags.Tab" xml:space="preserve"> + <value>Tab</value> + </data> + <data name="Enum.KeyFlags.U" xml:space="preserve"> + <value>U</value> + </data> + <data name="Enum.KeyFlags.V" xml:space="preserve"> + <value>V</value> + </data> + <data name="Enum.KeyFlags.W" xml:space="preserve"> + <value>W</value> + </data> + <data name="Enum.KeyFlags.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.KeyFlags.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.KeyFlags.Z" xml:space="preserve"> + <value>Z</value> + </data> + <data name="Enum.MotionOuput.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.MotionOuput.MoveCursor" xml:space="preserve"> + <value>MoveCursor</value> + </data> + <data name="Enum.MotionOuput.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="Enum.MotionOuput.ScrollWheel" xml:space="preserve"> + <value>ScrollWheel</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>ZL</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>ZR</value> + </data> + <data name="Enum.ProController.ButtonFlags.B1" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.ProController.ButtonFlags.B2" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.ProController.ButtonFlags.B3" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.ProController.ButtonFlags.B4" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.ProController.ButtonFlags.Back" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.ProController.ButtonFlags.L1" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.ProController.ButtonFlags.R1" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special2" xml:space="preserve"> + <value>Capture</value> + </data> + <data name="Enum.ProController.ButtonFlags.Start" xml:space="preserve"> + <value>Plus</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="Controller_Connect" xml:space="preserve"> + <value>Connect</value> + </data> + <data name="Controller_Disconnect" xml:space="preserve"> + <value>Disconnect</value> + </data> + <data name="Controller_Hide" xml:space="preserve"> + <value>Hide</value> + </data> + <data name="Controller_Unhide" xml:space="preserve"> + <value>Unhide</value> + </data> + <data name="Controller_Virtual" xml:space="preserve"> + <value>Virtual </value> + </data> + <data name="MainWindow_Back" xml:space="preserve"> + <value>Back</value> + </data> + <data name="MainWindow_Exit" xml:space="preserve"> + <value>Exit</value> + </data> + <data name="MainWindow_MainWindow" xml:space="preserve"> + <value>Main Window</value> + </data> + <data name="MainWindow_Navigate" xml:space="preserve"> + <value>Navigate</value> + </data> + <data name="MainWindow_QuickTools" xml:space="preserve"> + <value>Quick Tools</value> + </data> + <data name="MainWindow_Select" xml:space="preserve"> + <value>Select</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_External" xml:space="preserve"> + <value>External</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate1" xml:space="preserve"> + <value>Are you sure you want to apply this template? All layout settings will be overridden.</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate2" xml:space="preserve"> + <value>You can't undo this action. Previously applied template: {0}</value> + </data> + <data name="LayoutPage_SetAsDefault" xml:space="preserve"> + <value>Make this the default layout</value> + </data> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </root> \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.it-IT.resx b/HandheldCompanion/Properties/Resources.it-IT.resx index 207980777..c4d737ac5 100644 --- a/HandheldCompanion/Properties/Resources.it-IT.resx +++ b/HandheldCompanion/Properties/Resources.it-IT.resx @@ -1,3 +1,4 @@ +<<<<<<< HEAD <?xml version="1.0" encoding="utf-8"?> <root> <!-- @@ -2356,4 +2357,2331 @@ <data name="IController_ControllerIndex" xml:space="preserve"> <value>Gamepad: {0}</value> </data> +======= +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="AboutPage_About" xml:space="preserve"> + <value>Informazioni</value> + </data> + <data name="AboutPage_AboutDescription" xml:space="preserve"> + <value>Una combinazione di un servizio Windows e un'interfaccia touch ottimizzata per migliorare la tua esperienza con il computer portatile da gioco. Le funzionalità includono: controllo del movimento, simulazione del controller virtuale, sovrapposizione di strumenti rapidi, touchpad virtuali, modello di controller 3D, sistema di impostazioni del profilo basato sull'applicazione. Handheld Companion si basa sul driver ViGEmBus e sulle librerie ViGEmClient, nonché sul driver del filtro di livello kernel HidHide. Gli algoritmi di controllo del movimento si basano sul lavoro di Jibbsmart e sulle informazioni disponibili su GyroWiki.</value> + </data> + <data name="AboutPage_Accelerometer" xml:space="preserve"> + <value>Accelerometro</value> + </data> + <data name="AboutPage_Author" xml:space="preserve"> + <value>Autore</value> + </data> + <data name="AboutPage_Contributors" xml:space="preserve"> + <value>Contributori</value> + </data> + <data name="AboutPage_Description" xml:space="preserve"> + <value>Descrizione</value> + </data> + <data name="AboutPage_Donate" xml:space="preserve"> + <value>Donazioni</value> + </data> + <data name="AboutPage_Gyrometer" xml:space="preserve"> + <value>Giroscopio</value> + </data> + <data name="AboutPage_Inclinometer" xml:space="preserve"> + <value>Inclinometro</value> + </data> + <data name="AboutPage_NotApplicable" xml:space="preserve"> + <value>N/D</value> + </data> + <data name="AboutPage_RelatedLinks" xml:space="preserve"> + <value>Link correlati</value> + </data> + <data name="AboutPage_SensorExternal" xml:space="preserve"> + <value>Esterno</value> + </data> + <data name="AboutPage_SensorInternal" xml:space="preserve"> + <value>Interno</value> + </data> + <data name="AboutPage_SensorName" xml:space="preserve"> + <value>Nome del sensore</value> + </data> + <data name="AboutPage_SensorSpecification" xml:space="preserve"> + <value>Specifiche del sensore</value> + </data> + <data name="AboutPage_Service" xml:space="preserve"> + <value>Servizio</value> + </data> + <data name="AboutPage_SourceCode" xml:space="preserve"> + <value>Codice sorgente</value> + </data> + <data name="AboutPage_Version" xml:space="preserve"> + <value>Versione</value> + </data> + <data name="AboutPage_Wiki" xml:space="preserve"> + <value>Wiki</value> + </data> + <data name="Administrator" xml:space="preserve"> + <value>Amministratore</value> + </data> + <data name="ControllerPage_CloakDevice" xml:space="preserve"> + <value>Nascondi controller al collegamento</value> + </data> + <data name="ControllerPage_CloakDeviceDesc" xml:space="preserve"> + <value>Nascondi il controller fisico quando è collegato</value> + </data> + <data name="ControllerPage_Connect" xml:space="preserve"> + <value>Collega</value> + </data> + <data name="ControllerPage_Controller" xml:space="preserve"> + <value>Controller</value> + </data> + <data name="ControllerPage_DeviceCloaking" xml:space="preserve"> + <value>Nascondi controller</value> + </data> + <data name="ControllerPage_DeviceSettings" xml:space="preserve"> + <value>Impostazioni controller</value> + </data> + <data name="ControllerPage_Disconnect" xml:space="preserve"> + <value>Disconnetti</value> + </data> + <data name="ControllerPage_InputDevices" xml:space="preserve"> + <value>Dispositivi di input</value> + </data> + <data name="ControllerPage_UncloakOnClose" xml:space="preserve"> + <value>Ripristina visibilità controller alla chiusura</value> + </data> + <data name="ControllerPage_UncloakOnCloseDesc" xml:space="preserve"> + <value>Ripristina la visibilità di tutti i controller fisici quando l'applicazione viene chiusa</value> + </data> + <data name="ControllerPage_VibrationStrength" xml:space="preserve"> + <value>Intensità vibrazione</value> + </data> + <data name="ControllerPage_VibrationStrengthExpl" xml:space="preserve"> + <value>Cambia l'intensità della vibrazione del controller</value> + </data> + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\icon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="InputsHotkey_decreaseTDP" xml:space="preserve"> + <value>Riduci limite di potenza termica (TDP)</value> + </data> + <data name="InputsHotkey_decreaseTDPDesc" xml:space="preserve"> + <value>Riduci il TDP del sistema o del profilo correntemente applicato di un watt</value> + </data> + <data name="InputsHotkey_fallbackInput" xml:space="preserve"> + <value>Premi per definire il trigger</value> + </data> + <data name="InputsHotkey_fallbackOutput" xml:space="preserve"> + <value>Premi per definire l'output della tastiera</value> + </data> + <data name="InputsHotkey_increaseTDP" xml:space="preserve"> + <value>Aumenta limite di potenza termica (TDP)</value> + </data> + <data name="InputsHotkey_increaseTDPDesc" xml:space="preserve"> + <value>Aumenta il TDP del sistema o del profilo correntemente applicato di un watt</value> + </data> + <data name="InputsHotkey_overlayGamepad" xml:space="preserve"> + <value>Mostra controller 3D</value> + </data> + <data name="InputsHotkey_overlayGamepadDesc" xml:space="preserve"> + <value>Cambia l'hotkey del controller 3D premendo un pulsante o un tasto speciale</value> + </data> + <data name="InputsHotkey_overlayTrackpads" xml:space="preserve"> + <value>Mostra trackpad virtuali</value> + </data> + <data name="InputsHotkey_overlayTrackpadsDesc" xml:space="preserve"> + <value>Cambia l'hotkey premendo un pulsante o un tasto speciale</value> + </data> + <data name="InputsHotkey_quickTools" xml:space="preserve"> + <value>Richiama finestra strumenti rapidi</value> + </data> + <data name="InputsHotkey_quickToolsDesc" xml:space="preserve"> + <value>Cambia l'hotkey premendo un pulsante o un tasto speciale</value> + </data> + <data name="InputsHotkey_shortcutCustom" xml:space="preserve"> + <value>Scorciatoia personalizzata</value> + </data> + <data name="InputsHotkey_shortcutCustomDesc" xml:space="preserve"> + <value>Cambia l'hotkey premendo un pulsante o un tasto speciale</value> + </data> + <data name="InputsHotkey_shortcutDesktop" xml:space="preserve"> + <value>Mostra e nascondi il desktop</value> + </data> + <data name="InputsHotkey_shortcutDesktopDesc" xml:space="preserve"> + <value>Premi questa combinazione di tasti: Windows + D</value> + </data> + <data name="InputsHotkey_shortcutESC" xml:space="preserve"> + <value>Tasto Escape</value> + </data> + <data name="InputsHotkey_shortcutESCDesc" xml:space="preserve"> + <value>Premi questo tasto: Escape</value> + </data> + <data name="InputsHotkey_shortcutExpand" xml:space="preserve"> + <value>Passa da finestra a schermo intero</value> + </data> + <data name="InputsHotkey_shortcutExpandDesc" xml:space="preserve"> + <value>Premi questa combinazione di tasti: Alt + Enter</value> + </data> + <data name="InputsHotkey_shortcutGuide" xml:space="preserve"> + <value>Pulsante Guide o PS</value> + </data> + <data name="InputsHotkey_shortcutGuideDesc" xml:space="preserve"> + <value>Simula l'input di un pulsante Guide di Xbox o PS di Sony</value> + </data> + <data name="InputsHotkey_shortcutKeyboard" xml:space="preserve"> + <value>Mostra tastiera touch</value> + </data> + <data name="InputsHotkey_shortcutKeyboardDesc" xml:space="preserve"> + <value>Cambia l'hotkey premendo un pulsante o un tasto speciale</value> + </data> + <data name="InputsHotkey_shortcutKillApp" xml:space="preserve"> + <value>Forza la chiusura dell'applicazione</value> + </data> + <data name="InputsHotkey_shortcutKillAppDesc" xml:space="preserve"> + <value>Cambia l'hotkey premendo un pulsante o un tasto speciale</value> + </data> + <data name="InputsHotkey_shortcutMainwindow" xml:space="preserve"> + <value>Mostra e nascondi finestra principale</value> + </data> + <data name="InputsHotkey_shortcutMainwindowDesc" xml:space="preserve"> + <value>Cambia l'hotkey premendo un pulsante o un tasto speciale</value> + </data> + <data name="InputsHotkey_shortcutTaskManager" xml:space="preserve"> + <value>Apri Task Manager</value> + </data> + <data name="InputsHotkey_shortcutTaskManagerDesc" xml:space="preserve"> + <value>Premi questa combinazione di tasti: Ctrl + Shift + Esc</value> + </data> + <data name="InputsHotkey_shortcutTaskview" xml:space="preserve"> + <value>Apri visualizzazione attività</value> + </data> + <data name="InputsHotkey_shortcutTaskviewDesc" xml:space="preserve"> + <value>Premi questa combinazione di tasti: Windows + Tab</value> + </data> + <data name="InputsHotkey_suspendResumeTask" xml:space="preserve"> + <value>Attiva/disattiva sospensione</value> + </data> + <data name="InputsHotkey_suspendResumeTaskDesc" xml:space="preserve"> + <value>Sospendi o riprendi l'applicazione in primo piano</value> + </data> + <data name="MainWindow_HandheldCompanion" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="MainWindow_navAbout" xml:space="preserve"> + <value>Informazioni</value> + </data> + <data name="MainWindow_navController" xml:space="preserve"> + <value>Controller</value> + </data> + <data name="MainWindow_navOverlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="MainWindow_navProfiles" xml:space="preserve"> + <value>Profili</value> + </data> + <data name="MainWindow_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="MainWindow_Settings" xml:space="preserve"> + <value>Impostazioni</value> + </data> + <data name="OverlayPage_8BitDoLite2Controller" xml:space="preserve"> + <value>8BitDo Lite 2</value> + </data> + <data name="OverlayPage_Alignment" xml:space="preserve"> + <value>Allineamento</value> + </data> + <data name="OverlayPage_AlignmentDesc" xml:space="preserve"> + <value>Cambia l'allineamento dell'overlay del controller 3D</value> + </data> + <data name="OverlayPage_AlignmentTrackpadDesc" xml:space="preserve"> + <value>Cambia l'allineamento dell'overlay dei trackpad</value> + </data> + <data name="OverlayPage_AlwaysOnTop" xml:space="preserve"> + <value>Sempre in primo piano</value> + </data> + <data name="OverlayPage_AlwaysOnTopDesc" xml:space="preserve"> + <value>Quando attivato, l'overlay del controller 3D rimarrà sempre in primo piano rispetto alle altre finestre</value> + </data> + <data name="OverlayPage_BackButton" xml:space="preserve"> + <value>Indietro</value> + </data> + <data name="OverlayPage_CameraAngle" xml:space="preserve"> + <value>Inquadra la fotocamera</value> + </data> + <data name="OverlayPage_CameraAngleDesc" xml:space="preserve"> + <value>Cambia il comportamento del modello dell'overlay del controller 3D per inquadrare la fotocamera</value> + </data> + <data name="OverlayPage_CameraAnglePitch" xml:space="preserve"> + <value>Pitch fisso</value> + </data> + <data name="OverlayPage_CameraAnglePitchDesc" xml:space="preserve"> + <value>Cambia l'angolo, in gradi</value> + </data> + <data name="OverlayPage_Color" xml:space="preserve"> + <value>Colore sfondo</value> + </data> + <data name="OverlayPage_ColorDesc" xml:space="preserve"> + <value>Cambia il colore di sfondo dell'overlay del controller 3D</value> + </data> + <data name="OverlayPage_ControllerOptions" xml:space="preserve"> + <value>Opzioni del controller</value> + </data> + <data name="OverlayPage_DualSenseController" xml:space="preserve"> + <value>PlayStation DualSense</value> + </data> + <data name="OverlayPage_EmulatedController" xml:space="preserve"> + <value>Controller emulato</value> + </data> + <data name="OverlayPage_FaceCamera" xml:space="preserve"> + <value>Inquadra la fotocamera</value> + </data> + <data name="OverlayPage_FaceCameraDesc" xml:space="preserve"> + <value>Il modello 3D ruota lentamente per inquadrare la fotocamera come posizione predefinita</value> + </data> + <data name="OverlayPage_Listening" xml:space="preserve"> + <value>In ascolto...</value> + </data> + <data name="OverlayPage_MachenikeHG510Controller" xml:space="preserve"> + <value>MACHENIKE HG510</value> + </data> + <data name="OverlayPage_MainTrigger" xml:space="preserve"> + <value>Pulsante principale</value> + </data> + <data name="OverlayPage_MainTriggerDesc" xml:space="preserve"> + <value>Cambia il pulsante principale dell'overlay del controller 3D</value> + </data> + <data name="OverlayPage_MotionActivated" xml:space="preserve"> + <value>Movimento</value> + </data> + <data name="OverlayPage_MotionActivatedDesc" xml:space="preserve"> + <value>Il modello si muoverà in base ai movimenti dell'utente basati sulle informazioni dei sensori</value> + </data> + <data name="OverlayPage_N64Controller" xml:space="preserve"> + <value>N64</value> + </data> + <data name="OverlayPage_OEMController" xml:space="preserve"> + <value>Controller OEM</value> + </data> + <data name="OverlayPage_Opacity" xml:space="preserve"> + <value>Opacità</value> + </data> + <data name="OverlayPage_OpacityControllerDesc" xml:space="preserve"> + <value>Cambia l'opacità dell'overlay del controller 3D</value> + </data> + <data name="OverlayPage_OpacityTrackpadDesc" xml:space="preserve"> + <value>Cambia l'opacità dell'overlay dei trackpad</value> + </data> + <data name="OverlayPage_Overlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="OverlayPage_OverlayModel" xml:space="preserve"> + <value>Modello overlay</value> + </data> + <data name="OverlayPage_OverlayModelDesc" xml:space="preserve"> + <value>Cambia il modello dell'overlay del controller 3D</value> + </data> + <data name="OverlayPage_OverlayPreview" xml:space="preserve"> + <value>Anteprima overlay</value> + </data> + <data name="OverlayPage_RenderSettings" xml:space="preserve"> + <value>Impostazioni di rendering</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasing" xml:space="preserve"> + <value>Anti-Aliasing</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasingDesc" xml:space="preserve"> + <value>Cambia lo stato dell'anti-aliasing del rendering</value> + </data> + <data name="OverlayPage_RenderSettingsDesc" xml:space="preserve"> + <value>Cambia le impostazioni di rendering del modello del tuo controller 3D overlay stazionario</value> + </data> + <data name="OverlayPage_RenderSettingsFramerate" xml:space="preserve"> + <value>Frequenza di aggiornamento</value> + </data> + <data name="OverlayPage_RenderSettingsFramerateDesc" xml:space="preserve"> + <value>Cambia la frequenza di aggiornamento del rendering, in aggiornamenti al secondo</value> + </data> + <data name="OverlayPage_Size" xml:space="preserve"> + <value>Dimensioni</value> + </data> + <data name="OverlayPage_SizeDesc" xml:space="preserve"> + <value>Cambia le dimensioni dell'overlay del controller 3D</value> + </data> + <data name="OverlayPage_SizeOverlayDesc" xml:space="preserve"> + <value>Cambia le dimensioni dell'overlay dei trackpad</value> + </data> + <data name="OverlayPage_StartButton" xml:space="preserve"> + <value>Avvia</value> + </data> + <data name="OverlayPage_ToyController" xml:space="preserve"> + <value>Controller Fisher-Price</value> + </data> + <data name="OverlayPage_TrackpadsOptions" xml:space="preserve"> + <value>Opzioni dei trackpad</value> + </data> + <data name="OverlayPage_XboxOneController" xml:space="preserve"> + <value>Xbox One</value> + </data> + <data name="OverlayPage_ZDOPlusController" xml:space="preserve"> + <value>ZD O+</value> + </data> + <data name="Overlay_Overlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="ProcessEx_processResume" xml:space="preserve"> + <value>Riprendi</value> + </data> + <data name="ProcessEx_processSuspend" xml:space="preserve"> + <value>Sospendi</value> + </data> + <data name="SettingsMode0_AdditionalSettings" xml:space="preserve"> + <value>Impostazioni aggiuntive</value> + </data> + <data name="SettingsMode0_AimingDownSights" xml:space="preserve"> + <value>Moltiplicatore movimento mira</value> + </data> + <data name="SettingsMode0_AimingDownSightsActivation" xml:space="preserve"> + <value>Pulsante di attivazione</value> + </data> + <data name="SettingsMode0_AimingDownSightsDesc" xml:space="preserve"> + <value>Un moltiplicatore di sensibilità del movimento aggiuntivo quando si mira o si utilizza un mirino attraverso l'uso del pulsante di attivazione configurato</value> + </data> + <data name="SettingsMode0_AimingDownSightsMultiplier" xml:space="preserve"> + <value>Valore moltiplicatore</value> + </data> + <data name="SettingsMode0_CameraOptions" xml:space="preserve"> + <value>Opzioni fotocamera</value> + </data> + <data name="SettingsMode0_CustomResponseCurve" xml:space="preserve"> + <value>Curva di risposta personalizzata</value> + </data> + <data name="SettingsMode0_CustomResponseCurveGameOutput" xml:space="preserve"> + <value>Uscita inviata al gioco</value> + </data> + <data name="SettingsMode0_CustomResponseIntensity" xml:space="preserve"> + <value>Intensità del movimento</value> + </data> + <data name="SettingsMode0_CustomResponsePresetAgressive" xml:space="preserve"> + <value>Aggressivo</value> + </data> + <data name="SettingsMode0_CustomResponsePresetDefault" xml:space="preserve"> + <value>Predefinito</value> + </data> + <data name="SettingsMode0_CustomResponsePresetOptions" xml:space="preserve"> + <value>Opzioni predefinite</value> + </data> + <data name="SettingsMode0_CustomResponsePresetPrecise" xml:space="preserve"> + <value>Preciso</value> + </data> + <data name="SettingsMode0_FlickDuration" xml:space="preserve"> + <value>Durata flick</value> + </data> + <data name="SettingsMode0_FlickDurationDesc" xml:space="preserve"> + <value>Cambia la durata del flick, calibra per una rotazione di 180 gradi, in millisecondi</value> + </data> + <data name="SettingsMode0_FlickStick" xml:space="preserve"> + <value>Flick stick (sperimentale)</value> + </data> + <data name="SettingsMode0_FlickStickDesc" xml:space="preserve"> + <value>Punta la fotocamera nella direzione del flick del joystick (destro), ruota la fotocamera esclusivamente nel piano orizzontale ruotando</value> + </data> + <data name="SettingsMode0_FlickStickEnable" xml:space="preserve"> + <value>Abilita flick stick</value> + </data> + <data name="SettingsMode0_Sensitivity" xml:space="preserve"> + <value>Sensibilità</value> + </data> + <data name="SettingsMode0_SensitivityDesc" xml:space="preserve"> + <value>Cambia la sensibilità del movimento dell'asse orizzontale e verticale</value> + </data> + <data name="SettingsMode0_SensitivityX" xml:space="preserve"> + <value>Sensibilità X</value> + </data> + <data name="SettingsMode0_SensitivityXDesc" xml:space="preserve"> + <value>Cambia la sensibilità del movimento dell'asse orizzontale</value> + </data> + <data name="SettingsMode0_SensitivityY" xml:space="preserve"> + <value>Sensibilità Y</value> + </data> + <data name="SettingsMode0_SensitivityYDesc" xml:space="preserve"> + <value>Cambia la sensibilità del movimento dell'asse verticale</value> + </data> + <data name="SettingsMode0_StickSensitivtity" xml:space="preserve"> + <value>Sensibilità del joystick</value> + </data> + <data name="SettingsMode0_StickSensitivtityDesc" xml:space="preserve"> + <value>Cambia la velocità di rotazione</value> + </data> + <data name="SettingsMode1_AdditionalSettings" xml:space="preserve"> + <value>Impostazioni aggiuntive</value> + </data> + <data name="SettingsMode1_Deadzone" xml:space="preserve"> + <value>Zona morta</value> + </data> + <data name="SettingsMode1_DeadzoneDesc" xml:space="preserve"> + <value>Cambia la zona morta dello sterzo, in gradi. Migliora la rettilineità dello sterzo</value> + </data> + <data name="SettingsMode1_JoystickGameInput" xml:space="preserve"> + <value>Input joystick gioco</value> + </data> + <data name="SettingsMode1_JoystickSteering" xml:space="preserve"> + <value>Sterzo joystick</value> + </data> + <data name="SettingsMode1_JoystickSteeringOptions" xml:space="preserve"> + <value>Opzioni sterzo joystick</value> + </data> + <data name="SettingsMode1_JoystickSteeringPreview" xml:space="preserve"> + <value>Anteprima sterzo joystick</value> + </data> + <data name="SettingsMode1_MaxSteeringAngle" xml:space="preserve"> + <value>Angolo massimo di sterzo</value> + </data> + <data name="SettingsMode1_MaxSteeringAngleDesc" xml:space="preserve"> + <value>Cambia il valore massimo dell'angolo di sterzo, in gradi</value> + </data> + <data name="SettingsMode1_SteeringLinearity" xml:space="preserve"> + <value>Linearità dello sterzo</value> + </data> + <data name="SettingsMode1_SteeringLinearityDesc" xml:space="preserve"> + <value>Mappatura tra input e sterzo. Valori più bassi forniscono maggiore precisione vicino alla massima rotazione ma minore precisione vicino al centro. Valori più alti forniscono maggiore precisione vicino al centro ma minore precisione vicino alla massima rotazione. 1.0 è una mappatura lineare</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplier" xml:space="preserve"> + <value>Moltiplicatore accelerometro</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplierDesc" xml:space="preserve"> + <value>Cambia il valore dell'accelerometro riportato dal sistema</value> + </data> + <data name="ProfilesPage_AdditionalSettings" xml:space="preserve"> + <value>Impostazioni aggiuntive</value> + </data> + <data name="ProfilesPage_AntiDeadzone" xml:space="preserve"> + <value>Anti-zona morta</value> + </data> + <data name="ProfilesPage_AntiDeadzoneDesc" xml:space="preserve"> + <value>Cambia l'anti-zona morta nel gioco, in percentuale</value> + </data> + <data name="ProfilesPage_AntiDeadzoneUnitPercentage" xml:space="preserve"> + <value> %</value> + </data> + <data name="ProfilesPage_AreYouSureDelete1" xml:space="preserve"> + <value>Sei sicuro di voler eliminare?</value> + </data> + <data name="ProfilesPage_AreYouSureDelete2" xml:space="preserve"> + <value>Questo elemento verrà eliminato immediatamente. Non è possibile annullare questa azione.</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite1" xml:space="preserve"> + <value>Sei sicuro di voler sovrascrivere questo profilo?</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite2" xml:space="preserve"> + <value>{0} sarà sovrascritto. Non è possibile annullare questa azione.</value> + </data> + <data name="ProfilesPage_BoostPower" xml:space="preserve"> + <value>Limite potenza boost</value> + </data> + <data name="ProfilesPage_BoostPowerDesc" xml:space="preserve"> + <value>Cambia il limite di potenza termica del boost</value> + </data> + <data name="ProfilesPage_Cancel" xml:space="preserve"> + <value>Annulla</value> + </data> + <data name="ProfilesPage_CreateNewProfile" xml:space="preserve"> + <value>Crea un nuovo profilo</value> + </data> + <data name="ProfilesPage_Delete" xml:space="preserve"> + <value>Elimina</value> + </data> + <data name="ProfilesPage_DeleteProfile" xml:space="preserve"> + <value>Elimina profilo</value> + </data> + <data name="ProfilesPage_EnableProfile" xml:space="preserve"> + <value>Profilo utente per gioco</value> + </data> + <data name="ProfilesPage_EnableProfileDesc" xml:space="preserve"> + <value>Il profilo verrà applicato automaticamente quando verrà rilevata l'applicazione associata</value> + </data> + <data name="ProfilesPage_GlobalSettings" xml:space="preserve"> + <value>Impostazioni globali</value> + </data> + <data name="ProfilesPage_GlobalSettingsDesc" xml:space="preserve"> + <value>Cambia le impostazioni globali dei profili</value> + </data> + <data name="ProfilesPage_GyrometerMultiplier" xml:space="preserve"> + <value>Moltiplicatore giroscopio</value> + </data> + <data name="ProfilesPage_GyrometerMultiplierDesc" xml:space="preserve"> + <value>Cambia il valore del giroscopio riportato dal sistema</value> + </data> + <data name="ProfilesPage_GyroSteeringAxis" xml:space="preserve"> + <value>Asse sterzo giroscopio</value> + </data> + <data name="ProfilesPage_GyroSteeringAxisDesc" xml:space="preserve"> + <value>Per il controllo del movimento orizzontale del controller, puoi utilizzare l'asse di imbardata o rollio</value> + </data> + <data name="ProfilesPage_InvertHorizontalAxis" xml:space="preserve"> + <value>Inverti asse orizzontale</value> + </data> + <data name="ProfilesPage_InvertVerticalAxis" xml:space="preserve"> + <value>Inverti asse verticale</value> + </data> + <data name="ProfilesPage_MotionControlSettings" xml:space="preserve"> + <value>Impostazioni controllo del movimento</value> + </data> + <data name="ProfilesPage_MotionControlSettingsDesc" xml:space="preserve"> + <value>Cambia le impostazioni globali del controllo del movimento</value> + </data> + <data name="ProfilesPage_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="ProfilesPage_PowerSettings" xml:space="preserve"> + <value>Impostazioni alimentazione</value> + </data> + <data name="ProfilesPage_PowerSettingsDesc" xml:space="preserve"> + <value>Cambia le impostazioni di alimentazione</value> + </data> + <data name="ProfilesPage_ProfileDetails" xml:space="preserve"> + <value>Dettagli profilo</value> + </data> + <data name="ProfilesPage_ProfileName" xml:space="preserve"> + <value>Nome profilo</value> + </data> + <data name="ProfilesPage_ProfilePath" xml:space="preserve"> + <value>Percorso profilo</value> + </data> + <data name="ProfilesPage_Profiles" xml:space="preserve"> + <value>Profili</value> + </data> + <data name="ProfilesPage_ProfileSelection" xml:space="preserve"> + <value>Selezione profilo</value> + </data> + <data name="ProfilesPage_ProfileSelectionDesc" xml:space="preserve"> + <value>Seleziona il profilo che desideri modificare</value> + </data> + <data name="ProfilesPage_ProfileSettings" xml:space="preserve"> + <value>Impostazioni profilo</value> + </data> + <data name="ProfilesPage_ProfileUpdated1" xml:space="preserve"> + <value>Profilo aggiornato</value> + </data> + <data name="ProfilesPage_ProfileUpdated2" xml:space="preserve"> + <value>è stato aggiornato.</value> + </data> + <data name="ProfilesPage_Roll" xml:space="preserve"> + <value>Rollio</value> + </data> + <data name="ProfilesPage_SensitivityX" xml:space="preserve"> + <value>X</value> + </data> + <data name="ProfilesPage_SensitivityY" xml:space="preserve"> + <value>Y</value> + </data> + <data name="ProfilesPage_StyleofInput" xml:space="preserve"> + <value>Stile di input</value> + </data> + <data name="ProfilesPage_StyleofInputTooltip" xml:space="preserve"> + <value>I comandi fisici del controller possono essere programmati per agire come diversi tipi di dispositivi</value> + </data> + <data name="ProfilesPage_StyleofOutput" xml:space="preserve"> + <value>Dispositivo di output</value> + </data> + <data name="ProfilesPage_StyleofOutputTooltip" xml:space="preserve"> + <value>Seleziona il dispositivo che riceverà i comandi di movimento</value> + </data> + <data name="ProfilesPage_SustainedPower" xml:space="preserve"> + <value>Limite di potenza sostenuta</value> + </data> + <data name="ProfilesPage_SustainedPowerDesc" xml:space="preserve"> + <value>Cambia il limite di potenza termica sostenuta</value> + </data> + <data name="ProfilesPage_TDPOverride" xml:space="preserve"> + <value>Limite di potenza termica (TDP)</value> + </data> + <data name="ProfilesPage_TDPOverrideDesc" xml:space="preserve"> + <value>Limita la potenza del processore per una potenza totale inferiore</value> + </data> + <data name="ProfilesPage_UMCEnable" xml:space="preserve"> + <value>Abilita il controller di movimento universale</value> + </data> + <data name="ProfilesPage_UMCMotionOff" xml:space="preserve"> + <value>Disattivato, attiva con il pulsante(i)</value> + </data> + <data name="ProfilesPage_UMCMotionOn" xml:space="preserve"> + <value>Attivato, disattiva con il pulsante(i)</value> + </data> + <data name="ProfilesPage_UMCMotionOnOff" xml:space="preserve"> + <value>Attivazione movimento</value> + </data> + <data name="ProfilesPage_UMCMotionOnOffDesc" xml:space="preserve"> + <value>Con l'input di movimento disabilitato, utilizza il/i pulsante/i selezionato/i per abilitare il movimento, con l'input di movimento abilitato, utilizza il/i pulsante/i selezionato/i per disabilitare il movimento.</value> + </data> + <data name="ProfilesPage_UMCSelectionRightLeftDesc" xml:space="preserve"> + <value>Seleziona il dispositivo che riceverà i comandi di movimento</value> + </data> + <data name="ProfilesPage_UMCSettings" xml:space="preserve"> + <value>Impostazioni del controllo di movimento universale</value> + </data> + <data name="ProfilesPage_UMCSettingsDesc" xml:space="preserve"> + <value>Traduci i movimenti del dispositivo in input del controller</value> + </data> + <data name="ProfilesPage_UpdateProfile" xml:space="preserve"> + <value>Aggiorna profilo</value> + </data> + <data name="ProfilesPage_Whitelist" xml:space="preserve"> + <value>Consenti all'applicazione di accedere al controller fisico del dispositivo</value> + </data> + <data name="ProfilesPage_Wrapper" xml:space="preserve"> + <value>Compatibilità estesa (XInputPlus)</value> + </data> + <data name="ProfilesPage_Yaw" xml:space="preserve"> + <value>Giro</value> + </data> + <data name="ProfilesPage_Yes" xml:space="preserve"> + <value>Sì</value> + </data> + <data name="Properties.Resources.ControllerPage_Disconnect" xml:space="preserve"> + <value>Disconnetti</value> + </data> + <data name="QuickPerformancePage_GPUControl" xml:space="preserve"> + <value>Controllo manuale dell'orologio della GPU</value> + </data> + <data name="QuickPerformancePage_GPUControlDesc" xml:space="preserve"> + <value>Imposta la GPU su un orologio fisso</value> + </data> + <data name="QuickPerformancePage_GPUUnit" xml:space="preserve"> + <value> MHz</value> + </data> + <data name="QuickPerformancePage_PowerMode" xml:space="preserve"> + <value>Modalità di alimentazione</value> + </data> + <data name="QuickPerformancePage_PowerModeBalanced" xml:space="preserve"> + <value>Bilanciata</value> + </data> + <data name="QuickPerformancePage_PowerModeDesc" xml:space="preserve"> + <value>Ottimizza il dispositivo in base all'utilizzo dell'alimentazione e alle prestazioni</value> + </data> + <data name="QuickPerformancePage_PowerModeEfficiency" xml:space="preserve"> + <value>Efficienza</value> + </data> + <data name="QuickPerformancePage_PowerModePerformance" xml:space="preserve"> + <value>Prestazioni</value> + </data> + <data name="QuickPerformancePage_TDPBoost" xml:space="preserve"> + <value>Boost</value> + </data> + <data name="QuickPerformancePage_TDPLimit" xml:space="preserve"> + <value>Limite di potenza termica (TDP)</value> + </data> + <data name="QuickPerformancePage_TDPLimitDesc" xml:space="preserve"> + <value>Limita la potenza del processore per una potenza totale inferiore</value> + </data> + <data name="QuickPerformancePage_TDPOverWrittenWarning" xml:space="preserve"> + <value>Il limite di potenza termica è sovrascritto da un profilo</value> + </data> + <data name="QuickPerformancePage_TDPSustained" xml:space="preserve"> + <value>Sostenuta</value> + </data> + <data name="QuickPerformancePage_TDPUnitWatt" xml:space="preserve"> + <value> W</value> + </data> + <data name="QuickProfilesPage_Create" xml:space="preserve"> + <value>Crea profilo</value> + </data> + <data name="QuickProfilesPage_Waiting" xml:space="preserve"> + <value>In attesa del processo in primo piano...</value> + </data> + <data name="ServiceDescription" xml:space="preserve"> + <value>Fornisce supporto per giroscopio e accelerometro ai computer portatili per il gioco con Windows tramite un controller virtuale. Se il servizio è abilitato, il controller integrato sarà nascosto alle applicazioni al di fuori della whitelist. Se il servizio è disabilitato, il controller integrato sarà visibile e il controller virtuale sarà disabilitato.</value> + </data> + <data name="ServiceName" xml:space="preserve"> + <value>Controller Service</value> + </data> + <data name="SettingsPage_AppLanguage" xml:space="preserve"> + <value>Lingua</value> + </data> + <data name="SettingsPage_AppLanguageDesc" xml:space="preserve"> + <value>La lingua dell'applicazione</value> + </data> + <data name="SettingsPage_AppLanguageWarning" xml:space="preserve"> + <value>Riavvio necessario</value> + </data> + <data name="SettingsPage_AppLanguageWarningDesc" xml:space="preserve"> + <value>Per rendere effettive le modifiche, riavviare l'applicazione</value> + </data> + <data name="SettingsPage_AppTheme" xml:space="preserve"> + <value>Tema dell'applicazione</value> + </data> + <data name="SettingsPage_AppThemeDesc" xml:space="preserve"> + <value>Il tema dell'applicazione, modalità chiara o scura</value> + </data> + <data name="SettingsPage_AutoStartApp" xml:space="preserve"> + <value>Avvia automaticamente l'applicazione</value> + </data> + <data name="SettingsPage_AutoStartAppDesc" xml:space="preserve"> + <value>L'applicazione si avvierà automaticamente quando accedo a Windows</value> + </data> + <data name="SettingsPage_Backdrop" xml:space="preserve"> + <value>Sfondo dell'applicazione</value> + </data> + <data name="SettingsPage_BackdropAcrylic" xml:space="preserve"> + <value>Acrylic</value> + </data> + <data name="SettingsPage_BackdropDesc" xml:space="preserve"> + <value>Lo sfondo dell'applicazione, nessuno, mica, a schede, acrilico</value> + </data> + <data name="SettingsPage_BackdropMica" xml:space="preserve"> + <value>Mica</value> + </data> + <data name="SettingsPage_BackdropNone" xml:space="preserve"> + <value>Nessuno</value> + </data> + <data name="SettingsPage_BackdropTabbed" xml:space="preserve"> + <value>A schede</value> + </data> + <data name="SettingsPage_CheckForUpdates" xml:space="preserve"> + <value>Verifica aggiornamenti</value> + </data> + <data name="SettingsPage_CloseMinimizes" xml:space="preserve"> + <value>Chiudi minimizza</value> + </data> + <data name="SettingsPage_CloseMinimizesDesc" xml:space="preserve"> + <value>L'applicazione verrà ridotta a icona invece di essere chiusa</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStart" xml:space="preserve"> + <value>Abilita il profilo desktop all'avvio</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStartDesc" xml:space="preserve"> + <value>Il profilo desktop verrà abilitato automaticamente all'avvio dell'applicazione</value> + </data> + <data name="SettingsPage_Download" xml:space="preserve"> + <value>Scarica</value> + </data> + <data name="SettingsPage_DownloadingPercentage" xml:space="preserve"> + <value>Download in corso - </value> + </data> + <data name="SettingsPage_EcoQoS" xml:space="preserve"> + <value>Efficienza intelligente</value> + </data> + <data name="SettingsPage_EcoQoSDesc" xml:space="preserve"> + <value>La modalità di efficienza intelligente riduce la priorità dei processi in background e migliora l'efficienza energetica</value> + </data> + <data name="SettingsPage_GeneralOptions" xml:space="preserve"> + <value>Opzioni generali</value> + </data> + <data name="SettingsPage_InstallNow" xml:space="preserve"> + <value>Installa ora</value> + </data> + <data name="SettingsPage_LastChecked" xml:space="preserve"> + <value>Ultimo controllo: </value> + </data> + <data name="SettingsPage_NotificationOptions" xml:space="preserve"> + <value>Opzioni di notifica</value> + </data> + <data name="SettingsPage_OpenAppBackground" xml:space="preserve"> + <value>Apri l'applicazione in background</value> + </data> + <data name="SettingsPage_OpenAppBackgroundDesc" xml:space="preserve"> + <value>L'applicazione si avvierà inizialmente minimizzata e apparirà nella barra delle applicazioni</value> + </data> + <data name="SettingsPage_SensorExternal" xml:space="preserve"> + <value>Esterno</value> + </data> + <data name="SettingsPage_SensorInternal" xml:space="preserve"> + <value>Interno</value> + </data> + <data name="SettingsPage_SensorOptions" xml:space="preserve"> + <value>Opzioni del sensore</value> + </data> + <data name="SettingsPage_SensorPlacementDirection" xml:space="preserve"> + <value>Direzione di posizionamento del sensore esterno</value> + </data> + <data name="SettingsPage_SensorPlacementDirectionDesc" xml:space="preserve"> + <value>Seleziona su quale lato del dispositivo è stato montato il sensore</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDown" xml:space="preserve"> + <value>Sensore esterno capovolto</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDownDesc" xml:space="preserve"> + <value>Il sensore è stato montato capovolto, possibile con convertitore USB-C</value> + </data> + <data name="SettingsPage_SensorSelection" xml:space="preserve"> + <value>Selezione del sensore</value> + </data> + <data name="SettingsPage_SensorSelectionDesc" xml:space="preserve"> + <value>Seleziona il sensore desiderato utilizzato per l'input di movimento</value> + </data> + <data name="SettingsPage_Settings" xml:space="preserve"> + <value>Impostazioni</value> + </data> + <data name="SettingsPage_StartupType" xml:space="preserve"> + <value>Tipo di avvio</value> + </data> + <data name="SettingsPage_StartupTypeDesc" xml:space="preserve"> + <value>Utilizzato dal gestore dei servizi per definire il tipo di avvio del servizio</value> + </data> + <data name="SettingsPage_TDPMax" xml:space="preserve"> + <value>Potenza massima</value> + </data> + <data name="SettingsPage_TDPMaxDesc" xml:space="preserve"> + <value>La potenza massima in watt fornita al processore</value> + </data> + <data name="SettingsPage_TDPMin" xml:space="preserve"> + <value>Potenza minima</value> + </data> + <data name="SettingsPage_TDPMinDesc" xml:space="preserve"> + <value>La potenza minima in watt fornita al processore</value> + </data> + <data name="SettingsPage_TDPRangeOverride" xml:space="preserve"> + <value>Sovrascrittura dell'intervallo di potenza configurabile (cTDP)</value> + </data> + <data name="SettingsPage_TDPRangeOverrideDesc" xml:space="preserve"> + <value>Consente di modificare i valori di potenza minima e massima (TDP) al di là delle specifiche della CPU</value> + </data> + <data name="SettingsPage_ThemeDark" xml:space="preserve"> + <value>Scuro</value> + </data> + <data name="SettingsPage_ThemeLight" xml:space="preserve"> + <value>Chiaro</value> + </data> + <data name="SettingsPage_ToastNotification" xml:space="preserve"> + <value>Notifica a comparsa</value> + </data> + <data name="SettingsPage_ToastNotificationDesc" xml:space="preserve"> + <value>Ottieni notifiche dall'applicazione nel centro notifiche di Windows</value> + </data> + <data name="SettingsPage_UpdateAvailable" xml:space="preserve"> + <value>Aggiornamenti disponibili</value> + </data> + <data name="SettingsPage_UpdateCheck" xml:space="preserve"> + <value>Controllo degli aggiornamenti in corso...</value> + </data> + <data name="SettingsPage_UpdateFailedDownload" xml:space="preserve"> + <value>Impossibile scaricare il file di aggiornamento.</value> + </data> + <data name="SettingsPage_UpdateFailedGithub" xml:space="preserve"> + <value>Impossibile raggiungere GitHub.</value> + </data> + <data name="SettingsPage_UpdateFailedInstall" xml:space="preserve"> + <value>Impossibile individuare il file di aggiornamento.</value> + </data> + <data name="SettingsPage_UpdateWarning" xml:space="preserve"> + <value>Oops. Si è verificato un problema.</value> + </data> + <data name="SettingsPage_UpToDate" xml:space="preserve"> + <value>Sei aggiornato</value> + </data> + <data name="ToastNewControllerEx" xml:space="preserve"> + <value>Il controller è ora nascosto e gli input vengono inoltrati al controller virtuale</value> + </data> + <data name="User" xml:space="preserve"> + <value>Utente</value> + </data> + <data name="WarningElevated" xml:space="preserve"> + <value>Esegui questo strumento come amministratore per sbloccare queste impostazioni</value> + </data> + <data name="xinput1_x64" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\xinput1_x64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="xinput1_x86" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\xinput1_x86.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="XInputPlus" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\XInputPlus.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value> + </data> + <data name="ControllerPage_SteamControllerMute" xml:space="preserve"> + <value>Silenzia il controller virtuale</value> + </data> + <data name="ControllerPage_SteamControllerMuteDesc" xml:space="preserve"> + <value>Silenzia il controller virtuale nelle applicazioni correlate a Steam</value> + </data> + <data name="ProfilesPage_ControllerSettings" xml:space="preserve"> + <value>Impostazioni del controller</value> + </data> + <data name="ProfilesPage_ControllerSettingsDesc" xml:space="preserve"> + <value>Modifica le impostazioni del controller virtuale</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzone" xml:space="preserve"> + <value>Antimuorto di movimento</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzoneDesc" xml:space="preserve"> + <value>Compensa la zona morta in gioco, migliora la registrazione dei piccoli movimenti</value> + </data> + <data name="chord_neptune" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\controller_base\chord_neptune.vdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="empty_neptune" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\controller_base\empty_neptune.vdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityDesc" xml:space="preserve"> + <value>Migliora la circolarità dello stick</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityLeft" xml:space="preserve"> + <value>Circolarità stick sinistro</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityRight" xml:space="preserve"> + <value>Circolarità stick destro</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeft" xml:space="preserve"> + <value>Zona morta joystick % sinistro</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeftDesc" xml:space="preserve"> + <value>Regola la percentuale della zona morta interna ed esterna del joystick sinistro</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRight" xml:space="preserve"> + <value>Zona morta joystick % destro</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRightDesc" xml:space="preserve"> + <value>Regola la percentuale della zona morta interna ed esterna del joystick destro</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeft" xml:space="preserve"> + <value>Zona morta del trigger sinistro %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeftDesc" xml:space="preserve"> + <value>Regola la percentuale della zona morta interna ed esterna del trigger sinistro</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRight" xml:space="preserve"> + <value>Zona morta del trigger destro %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRightDesc" xml:space="preserve"> + <value>Regola la percentuale della zona morta interna ed esterna del trigger destro</value> + </data> + <data name="ControllerPage_VibrateDevice" xml:space="preserve"> + <value>Vibra il controller al collegamento</value> + </data> + <data name="ControllerPage_VibrateDeviceDesc" xml:space="preserve"> + <value>Fai vibrare il controller fisico quando è collegato</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabled" xml:space="preserve"> + <value>Layout desktop</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabledDesc" xml:space="preserve"> + <value>Passa al layout del controller desktop</value> + </data> + <data name="InputsHotkey_shortcutControlCenter" xml:space="preserve"> + <value>Mostra Centro notifiche</value> + </data> + <data name="InputsHotkey_shortcutControlCenterDesc" xml:space="preserve"> + <value>Mostra e nasconde il Centro notifiche di Windows</value> + </data> + <data name="ProfilesPage_ControllerLayout" xml:space="preserve"> + <value>Layout del controller</value> + </data> + <data name="InputsHotkey_QuietModeToggled" xml:space="preserve"> + <value>Sovrascrittura ventola</value> + </data> + <data name="InputsHotkey_QuietModeToggledDesc" xml:space="preserve"> + <value>Imposta il ciclo di dovere della ventola su un valore definito dall'utente</value> + </data> + <data name="InputsHotkey_OnScreenDisplay" xml:space="preserve"> + <value>Visualizzazione a schermo</value> + </data> + <data name="InputsHotkey_OnScreenDisplayDesc" xml:space="preserve"> + <value>Abilita il supporto per la visualizzazione a schermo</value> + </data> + <data name="InputsHotkey_decreaseBrightness" xml:space="preserve"> + <value>Riduci luminosità</value> + </data> + <data name="InputsHotkey_decreaseBrightnessDesc" xml:space="preserve"> + <value>Riduci la luminosità corrente del display del 5%</value> + </data> + <data name="InputsHotkey_decreaseVolume" xml:space="preserve"> + <value>Riduci volume</value> + </data> + <data name="InputsHotkey_decreaseVolumeDesc" xml:space="preserve"> + <value>Riduci il volume del sistema del 5%</value> + </data> + <data name="InputsHotkey_increaseBrightness" xml:space="preserve"> + <value>Aumenta luminosità</value> + </data> + <data name="InputsHotkey_increaseVolume" xml:space="preserve"> + <value>Aumenta volume</value> + </data> + <data name="InputsHotkey_increaseVolumeDesc" xml:space="preserve"> + <value>Aumenta il volume del sistema del 5%</value> + </data> + <data name="ProfilesPage_AutoTDP" xml:space="preserve"> + <value>TDP automatico</value> + </data> + <data name="ProfilesPage_AutoTDPDesc" xml:space="preserve"> + <value>Regolazione automatica del TDP in base al framerate misurato e al framerate target richiesto</value> + </data> + <data name="ProfilesPage_AutoTDPFPS" xml:space="preserve"> + <value>Obiettivo framerate</value> + </data> + <data name="ProfilesPage_AutoTDPFPSDesc" xml:space="preserve"> + <value>Valore obiettivo del framerate desiderato per il controller TDP automatico</value> + </data> + <data name="ProfilesPage_GPUMhz" xml:space="preserve"> + <value>Frequenza massima clock GPU</value> + </data> + <data name="ProfilesPage_GPUMhzDesc" xml:space="preserve"> + <value>Frequenza massima del clock della GPU in Mhz</value> + </data> + <data name="QuickPerformancePage_AutoTDPUnitFPS" xml:space="preserve"> + <value>FPS</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel" xml:space="preserve"> + <value>Livello di visualizzazione sovrapposizione</value> + </data> + <data name="OverlayPage_OverlayDisplayLevelDesc" xml:space="preserve"> + <value>Cambia il livello di visualizzazione delle informazioni sulla sovrapposizione a schermo</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRate" xml:space="preserve"> + <value>Frequenza di aggiornamento</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRateDesc" xml:space="preserve"> + <value>Cambia la frequenza di aggiornamento della sovrapposizione a schermo</value> + </data> + <data name="ProfilesPage_FramerateLimit" xml:space="preserve"> + <value>Limite framerate</value> + </data> + <data name="ProfilesPage_FramerateLimitDesc" xml:space="preserve"> + <value>Limita il framerate per le applicazioni 3D</value> + </data> + <data name="InputsHotkey_shortcutPrintScreen" xml:space="preserve"> + <value>Apri strumento di cattura</value> + </data> + <data name="InputsHotkey_shortcutPrintScreenDesc" xml:space="preserve"> + <value>Premi questi tasti: Windows + Shift + S</value> + </data> + <data name="MainWindow_navHotkeys" xml:space="preserve"> + <value>Tasti di scelta rapida</value> + </data> + <data name="SettingsPage_ThemeDefault" xml:space="preserve"> + <value>Usa impostazione di sistema</value> + </data> + <data name="ButtonsPage_ABXY" xml:space="preserve"> + <value>A,B,X,Y</value> + </data> + <data name="ButtonsPage_Back_Grips" xml:space="preserve"> + <value>GRIP POSTERIORI</value> + </data> + <data name="ButtonsPage_Bumpers" xml:space="preserve"> + <value>BUMPERS</value> + </data> + <data name="ButtonsPage_Menu" xml:space="preserve"> + <value>MENU</value> + </data> + <data name="ButtonsPage_OEM" xml:space="preserve"> + <value>OEM</value> + </data> + <data name="DPadPage_DPad" xml:space="preserve"> + <value>PAD DIREZIONALE</value> + </data> + <data name="JoystickPage_Joystick_Left" xml:space="preserve"> + <value>JOYSTICK SINISTRO</value> + </data> + <data name="JoystickPage_Joystick_Left_Buttons" xml:space="preserve"> + <value>PULSANTI JOYSTICK SINISTRO</value> + </data> + <data name="JoystickPage_Joystick_Right" xml:space="preserve"> + <value>JOYSTICK DESTRO</value> + </data> + <data name="JoystickPage_Joystick_Right_Buttons" xml:space="preserve"> + <value>PULSANTI JOYSTICK DESTRO</value> + </data> + <data name="TrackPadsPage_Trackpad_Left" xml:space="preserve"> + <value>TRACKPAD SINISTRO</value> + </data> + <data name="TrackPadsPage_Trackpad_Left_Buttons" xml:space="preserve"> + <value>PULSANTI TRACKPAD SINISTRO</value> + </data> + <data name="TrackPadsPage_Trackpad_Right" xml:space="preserve"> + <value>TRACKPAD DESTRO</value> + </data> + <data name="TrackPadsPage_Trackpad_Right_Buttons" xml:space="preserve"> + <value>PULSANTI TRACKPAD DESTRO</value> + </data> + <data name="TriggersPage_Trigger_Left" xml:space="preserve"> + <value>TRIGGER SINISTRO</value> + </data> + <data name="TriggersPage_Trigger_Left_Button" xml:space="preserve"> + <value>PULSANTI TRIGGER SINISTRO</value> + </data> + <data name="TriggersPage_Trigger_Right" xml:space="preserve"> + <value>TRIGGER DESTRO</value> + </data> + <data name="TriggersPage_Trigger_Right_Button" xml:space="preserve"> + <value>PULSANTI TRIGGER DESTRO</value> + </data> + <data name="AboutPage_Partner" xml:space="preserve"> + <value>Partner</value> + </data> + <data name="AboutPage_Manufacturer" xml:space="preserve"> + <value>Produttore</value> + </data> + <data name="AboutPage_ProductName" xml:space="preserve"> + <value>Nome prodotto</value> + </data> + <data name="ControllerPage_NoPhysicalControllerWarning" xml:space="preserve"> + <value>Nessun controller fisico connesso</value> + </data> + <data name="ControllerPage_DesktopLayout" xml:space="preserve"> + <value>Layout desktop</value> + </data> + <data name="ControllerPage_DesktopLayoutDefine" xml:space="preserve"> + <value>Definisci layout desktop</value> + </data> + <data name="ControllerPage_DesktopLayoutDefineController" xml:space="preserve"> + <value>Definisci il layout del controller quando sei in modalità desktop</value> + </data> + <data name="ControllerPage_DesktopLayoutEdit" xml:space="preserve"> + <value>Modifica</value> + </data> + <data name="ControllerPage_DesktopLayoutEnable" xml:space="preserve"> + <value>Abilita layout desktop</value> + </data> + <data name="ControllerPage_NonGameControllerLayouts" xml:space="preserve"> + <value>Layout di controller non da gioco</value> + </data> + <data name="ControllerPage_NoPhysicalControllerAction" xml:space="preserve"> + <value>Potresti voler fare clic su Connetti accanto al tuo controller collegato.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDesc" xml:space="preserve"> + <value>Non hai nessun controller fisico collegato. Nessun input verrà inviato a HC o al suo servizio.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedAction" xml:space="preserve"> + <value>Assicurati di aver collegato un dispositivo XInput o DInput compatibile.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedWarning" xml:space="preserve"> + <value>Nessun controller fisico rilevato</value> + </data> + <data name="ControllerPage_NoVirtualControllerAction" xml:space="preserve"> + <value>Potresti voler avviare il servizio companion o assicurarti che lo stato del tuo controller virtuale sia impostato su: Connesso</value> + </data> + <data name="ControllerPage_NoVirtualControllerDesc" xml:space="preserve"> + <value>Il tuo controller fisico è nascosto, ma non hai a disposizione un controller virtuale. Nessun input verrà inviato ai giochi.</value> + </data> + <data name="ControllerPage_NoVirtualControllerWarning" xml:space="preserve"> + <value>Nessun controller virtuale rilevato</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenAction" xml:space="preserve"> + <value>Potresti voler attivare l'audio del tuo controller virtuale o rendere visibile il tuo controller fisico.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenDesc" xml:space="preserve"> + <value>Il tuo controller fisico è nascosto, ma hai attivato l'audio del tuo controller virtuale.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenWarning" xml:space="preserve"> + <value>Controller fisico nascosto</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenAction" xml:space="preserve"> + <value>Potresti voler nascondere il tuo controller fisico o disattivare l'audio del tuo controller virtuale.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenDesc" xml:space="preserve"> + <value>Il tuo controller fisico non è nascosto, ma hai un controller virtuale non silenziato. Potresti riscontrare input doppi nei giochi.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenWarning" xml:space="preserve"> + <value>Controller fisico non nascosto</value> + </data> + <data name="ControllerPage_SteamControllerHDRumble" xml:space="preserve"> + <value>HD rumble</value> + </data> + <data name="ControllerPage_SteamControllerHDRumbleDesc" xml:space="preserve"> + <value>Utilizza il motore di vibrazione ad alta definizione, a scapito di un utilizzo maggiore della CPU</value> + </data> + <data name="ControllerPage_SteamControllerSettings" xml:space="preserve"> + <value>Impostazioni Steam Deck</value> + </data> + <data name="LayoutPage_ApplyTemplate" xml:space="preserve"> + <value>Applica modello</value> + </data> + <data name="LayoutPage_Buttons" xml:space="preserve"> + <value>Pulsanti</value> + </data> + <data name="LayoutPage_Cancel" xml:space="preserve"> + <value>Annulla</value> + </data> + <data name="LayoutPage_Community" xml:space="preserve"> + <value>COMUNITÀ</value> + </data> + <data name="LayoutPage_Confirm" xml:space="preserve"> + <value>Conferma</value> + </data> + <data name="LayoutPage_Dpad" xml:space="preserve"> + <value>Pad direzionale</value> + </data> + <data name="LayoutPage_ExportCurrentController" xml:space="preserve"> + <value>Esporta per il controller attuale</value> + </data> + <data name="LayoutPage_ExportLayout" xml:space="preserve"> + <value>Esporta layout</value> + </data> + <data name="LayoutPage_Gyro" xml:space="preserve"> + <value>Giroscopio</value> + </data> + <data name="LayoutPage_Joysticks" xml:space="preserve"> + <value>Joystick</value> + </data> + <data name="LayoutPage_LayoutAuthor" xml:space="preserve"> + <value>Autore layout</value> + </data> + <data name="LayoutPage_LayoutDesc" xml:space="preserve"> + <value>Descrizione layout</value> + </data> + <data name="LayoutPage_LayoutTitle" xml:space="preserve"> + <value>Titolo layout</value> + </data> + <data name="LayoutPage_SaveGameInfoLayout" xml:space="preserve"> + <value>Salva informazioni di gioco con il layout</value> + </data> + <data name="LayoutPage_ShowCurrentControllerTemplates" xml:space="preserve"> + <value>Mostra solo i modelli del controller attuale</value> + </data> + <data name="LayoutPage_TemplatePicker" xml:space="preserve"> + <value>Selezionatore di modelli di layout</value> + </data> + <data name="LayoutPage_Templates" xml:space="preserve"> + <value>MODELLI</value> + </data> + <data name="LayoutPage_Trackpads" xml:space="preserve"> + <value>Trackpad</value> + </data> + <data name="LayoutPage_Triggers" xml:space="preserve"> + <value>Trigger</value> + </data> + <data name="OverlayPage_Millisecond" xml:space="preserve"> + <value>ms</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Disabled" xml:space="preserve"> + <value>Disabilitato</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Extended" xml:space="preserve"> + <value>Esteso</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Full" xml:space="preserve"> + <value>Completo</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Minimal" xml:space="preserve"> + <value>Minimale</value> + </data> + <data name="ProfilesPage_ControllerLayoutDesc" xml:space="preserve"> + <value>Cambia il layout del controller virtuale</value> + </data> + <data name="ProfilesPage_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="ProfilesPage_EPP" xml:space="preserve"> + <value>Preferenza di prestazioni energetiche (EPP)</value> + </data> + <data name="ProfilesPage_EPPBalance" xml:space="preserve"> + <value>Bilanciamento della potenza CPU/GPU</value> + </data> + <data name="ProfilesPage_EPPDesc" xml:space="preserve"> + <value>Specifica la politica di distribuzione della potenza tra CPU e GPU</value> + </data> + <data name="ProfilesPage_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="ProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Target del limite di potenza</value> + </data> + <data name="SettingsPage_NativeDisplayOrientation" xml:space="preserve"> + <value>Orientamento display nativo</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDesc" xml:space="preserve"> + <value>Alcune funzionalità dipendono dalla corretta rilevazione dell'orientamento del display per funzionare correttamente. Se ciò non è stato rilevato correttamente, impostare l'orientamento del display sulla stessa orientamento del tuo controller, quindi fare clic su Rileva</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDetect" xml:space="preserve"> + <value>Rileva</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationNotSet" xml:space="preserve"> + <value>Non impostato</value> + </data> + <data name="InputsHotkey_increaseBrightnessDesc" xml:space="preserve"> + <value>Increase the current display brightness by 5%</value> + </data> + <data name="QuickProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Power limit target</value> + </data> + <data name="SettingsPage_QuickToolsOptions" xml:space="preserve"> + <value>Quicktools options</value> + </data> + <data name="QuickPerformancePage_FanOverridePercentage" xml:space="preserve"> + <value>%</value> + </data> + <data name="QuickPerformancePage_CPUBoostModeDesc" xml:space="preserve"> + <value>Set current CPU boost mode</value> + </data> + <data name="QuickPerformancePage_CPUBoostMode" xml:space="preserve"> + <value>CPU boost mode</value> + </data> + <data name="QuickPerformancePage_FanOverrideDesc" xml:space="preserve"> + <value>Set the fan duty cycle to user-defined value</value> + </data> + <data name="QuickPerformancePage_FanOverride" xml:space="preserve"> + <value>Fan override</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRateDesc" xml:space="preserve"> + <value>Adjust main display resolution and refresh rate</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRate" xml:space="preserve"> + <value>Display resolution and refresh rate</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopRight" xml:space="preserve"> + <value>Top right</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopLeft" xml:space="preserve"> + <value>Top left</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocation" xml:space="preserve"> + <value>Window location</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomRight" xml:space="preserve"> + <value>Bottom right</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomLeft" xml:space="preserve"> + <value>Bottom left</value> + </data> + <data name="DevicePage_PowerOptions" xml:space="preserve"> + <value>Power options</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationDesc" xml:space="preserve"> + <value>Define quicktools window location</value> + </data> + <data name="SettingsPage_ThirdPartyApps" xml:space="preserve"> + <value>Third-party applications</value> + </data> + <data name="SettingsPage_SensorNone" xml:space="preserve"> + <value>None</value> + </data> + <data name="SettingsPage_QuickToolsBackdrop" xml:space="preserve"> + <value>Quicktools backdrop, none, mica, tabbed, acrylic</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServerDesc" xml:space="preserve"> + <value>Automatically turn RTSS off when companion is closed</value> + </data> + <data name="SettingsPage_HwInfoDesc" xml:space="preserve"> + <value>Automatically turn HWiNFO off when companion is closed</value> + </data> + <data name="SettingsPage_HwInfo" xml:space="preserve"> + <value>HWiNFO</value> + </data> + <data name="QuickProfilesPage_CurrentProfileDefault" xml:space="preserve"> + <value>Default</value> + </data> + <data name="QuickProfilesPage_CurrentProfile" xml:space="preserve"> + <value>Current profile:</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServer" xml:space="preserve"> + <value>RivaTuner Statistics Server</value> + </data> + <data name="SettingsPage_HideWhenLoseFocusDesc" xml:space="preserve"> + <value>Automatically hide quick tools when the user clicks outside of window</value> + </data> + <data name="SettingsPage_HideWhenLoseFocus" xml:space="preserve"> + <value>Hide when loses focus</value> + </data> + <data name="AutoRollYawSwapDesc" xml:space="preserve"> + <value>Questo input funzionerà come un semplice joystick. Ideale per laptop e dispositivi portatili di tipo clamshell, lo scambio automatico di rollio di beccheggio in base al modo in cui il dispositivo viene tenuto (aperto a 90 o 180 gradi).</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B1" xml:space="preserve"> + <value>Croce</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B2" xml:space="preserve"> + <value>Cerchio</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B3" xml:space="preserve"> + <value>Quadrato</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B4" xml:space="preserve"> + <value>Triangolo</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Back" xml:space="preserve"> + <value>Condividi</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Start" xml:space="preserve"> + <value>Opzioni</value> + </data> + <data name="Enum.HIDmode.DualShock4Controller" xml:space="preserve"> + <value>Controller DualShock 4 emulato</value> + </data> + <data name="Enum.HIDmode.NoController" xml:space="preserve"> + <value>Nessun controller emulato</value> + </data> + <data name="Enum.HIDmode.Xbox360Controller" xml:space="preserve"> + <value>Controller XBOX 360 emulato</value> + </data> + <data name="Enum.HIDstatus.Connected" xml:space="preserve"> + <value>Connesso</value> + </data> + <data name="Enum.HIDstatus.Disconnected" xml:space="preserve"> + <value>Disconnesso</value> + </data> + <data name="Enum.Input.AutoRollYawSwap" xml:space="preserve"> + <value>Scambio automatico beccheggio-rollio</value> + </data> + <data name="Enum.Input.JoystickCamera" xml:space="preserve"> + <value>Joystick telecamera</value> + </data> + <data name="Enum.Input.JoystickSteering" xml:space="preserve"> + <value>Joystick sterzo</value> + </data> + <data name="Enum.Input.PlayerSpace" xml:space="preserve"> + <value>Spazio del giocatore</value> + </data> + <data name="Enum.InputsHotkeyType.HC" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.InputsHotkeyType.Overlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="Enum.InputsHotkeyType.Quicktools" xml:space="preserve"> + <value>Strumenti rapidi</value> + </data> + <data name="Enum.InputsHotkeyType.Windows" xml:space="preserve"> + <value>Windows</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Back" xml:space="preserve"> + <value>Vista</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="Enum.Output.LeftStick" xml:space="preserve"> + <value>Joystick sinistro</value> + </data> + <data name="Enum.Output.RightStick" xml:space="preserve"> + <value>Joystick destro</value> + </data> + <data name="Enum.ProfileErrorCode.Default" xml:space="preserve"> + <value>Questo è il tuo profilo controller predefinito. Questo profilo verrà applicato a tutte le tue applicazioni che non hanno un profilo specifico. Alcune opzioni che richiedono un'eseguibile potrebbero essere disabilitate.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingExecutable" xml:space="preserve"> + <value>Oops. Sembra che questo profilo non abbia un'eseguibile. Come è possibile?</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPath" xml:space="preserve"> + <value>Oops. Sembra che questo profilo non abbia un percorso per l'applicazione. Alcune opzioni che richiedono un'eseguibile potrebbero essere disabilitate.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPermission" xml:space="preserve"> + <value>Oops. Sembra che tu non abbia il livello di autorizzazione necessario per modificare il contenuto di questa applicazione. Assicurati di aver avviato questo programma in modalità amministratore.</value> + </data> + <data name="Enum.ProfileErrorCode.None" xml:space="preserve"> + <value>Niente da vedere qui.</value> + </data> + <data name="Enum.ProfileErrorCode.Running" xml:space="preserve"> + <value>Oops. Sembra che l'eseguibile di questo profilo sia in esecuzione. Alcune opzioni che richiedono un'eseguibile potrebbero essere disabilitate.</value> + </data> + <data name="Enum.QualityOfServiceLevel.Default" xml:space="preserve"> + <value>Predefinito</value> + </data> + <data name="Enum.QualityOfServiceLevel.Eco" xml:space="preserve"> + <value>Eco</value> + </data> + <data name="Enum.QualityOfServiceLevel.High" xml:space="preserve"> + <value>Alta</value> + </data> + <data name="Enum.ServiceStartMode.Automatic" xml:space="preserve"> + <value>Automatico</value> + </data> + <data name="Enum.ServiceStartMode.Disabled" xml:space="preserve"> + <value>Disabilitato</value> + </data> + <data name="Enum.ServiceStartMode.Manual" xml:space="preserve"> + <value>Manuale</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Back" xml:space="preserve"> + <value>Vista</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L1" xml:space="preserve"> + <value>LB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R1" xml:space="preserve"> + <value>RB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Special" xml:space="preserve"> + <value>Guida</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="JoystickCameraDesc" xml:space="preserve"> + <value>Questo input funzionerà come un semplice joystick. È destinato alle applicazioni joystick tradizionali</value> + </data> + <data name="JoystickSteeringDesc" xml:space="preserve"> + <value>Questo input funzionerà come un joystick ottimizzato per il controllo di un volante o un gioco di guida</value> + </data> + <data name="PlayerSpaceDesc" xml:space="preserve"> + <value>Questo input funzionerà come un joystick ottimizzato per il controllo di una telecamera in prima o terza persona</value> + </data> + <data name="Enum.InputsHotkeyType.Handheld" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.SteamDeck.ButtonFlags.OEM1" xml:space="preserve"> + <value>Opzioni</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Special" xml:space="preserve"> + <value>STEAM</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM1" xml:space="preserve"> + <value>Centro comandi</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM2" xml:space="preserve"> + <value>Cassetta dell'armatura</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM3" xml:space="preserve"> + <value>M1 / M2</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM1" xml:space="preserve"> + <value>Win</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM2" xml:space="preserve"> + <value>Esc</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM3" xml:space="preserve"> + <value>Tastiera</value> + </data> + <data name="Enum.InputsHotkeyType.Custom" xml:space="preserve"> + <value>Personalizzato</value> + </data> + <data name="Enum.InputsHotkeyType.Device" xml:space="preserve"> + <value>Dispositivo</value> + </data> + <data name="Enum.MotionInput.AutoRollYawSwap" xml:space="preserve"> + <value>Scambio automatico beccheggio-rollio</value> + </data> + <data name="Enum.MotionInput.JoystickCamera" xml:space="preserve"> + <value>Joystick telecamera</value> + </data> + <data name="Enum.MotionInput.JoystickSteering" xml:space="preserve"> + <value>Joystick sterzo</value> + </data> + <data name="Enum.MotionInput.PlayerSpace" xml:space="preserve"> + <value>Spazio del giocatore</value> + </data> + <data name="Enum.MotionOutput.LeftStick" xml:space="preserve"> + <value>Joystick sinistro</value> + </data> + <data name="Enum.MotionOutput.RightStick" xml:space="preserve"> + <value>Joystick destro</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftThumb" xml:space="preserve"> + <value>Levetta sinistra</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightThumb" xml:space="preserve"> + <value>Levetta destra</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B5" xml:space="preserve"> + <value>B5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B6" xml:space="preserve"> + <value>B6</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B7" xml:space="preserve"> + <value>B7</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B8" xml:space="preserve"> + <value>B8</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadDown" xml:space="preserve"> + <value>Pulsante D-Pad giù</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadLeft" xml:space="preserve"> + <value>Pulsante D-Pad sinistra</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadRight" xml:space="preserve"> + <value>Pulsante D-Pad destra</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadUp" xml:space="preserve"> + <value>Pulsante D-Pad su</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L3" xml:space="preserve"> + <value>L3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L4" xml:space="preserve"> + <value>L4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L5" xml:space="preserve"> + <value>L5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumb" xml:space="preserve"> + <value>Levetta sinistra</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbDown" xml:space="preserve"> + <value>Levetta sinistra giù</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbLeft" xml:space="preserve"> + <value>Levetta sinistra sinistra</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbRight" xml:space="preserve"> + <value>Levetta sinistra destra</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbUp" xml:space="preserve"> + <value>Levetta sinistra su</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM1" xml:space="preserve"> + <value>OEM1</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM2" xml:space="preserve"> + <value>OEM2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM3" xml:space="preserve"> + <value>OEM3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R3" xml:space="preserve"> + <value>R3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R4" xml:space="preserve"> + <value>R4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R5" xml:space="preserve"> + <value>R5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumb" xml:space="preserve"> + <value>Levetta destra</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbDown" xml:space="preserve"> + <value>Levetta destra giù</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbLeft" xml:space="preserve"> + <value>Levetta destra sinistra</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbRight" xml:space="preserve"> + <value>Levetta destra destra</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbUp" xml:space="preserve"> + <value>Levetta destra su</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.AlwaysOn" xml:space="preserve"> + <value>Always On</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Back" xml:space="preserve"> + <value>Indietro</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadDown" xml:space="preserve"> + <value>Pulsante D-Pad giù</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadLeft" xml:space="preserve"> + <value>Pulsante D-Pad sinistra</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadRight" xml:space="preserve"> + <value>Pulsante D-Pad destra</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadUp" xml:space="preserve"> + <value>Pulsante D-Pad su</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftShoulder" xml:space="preserve"> + <value>Spalla sinistra</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftThumb" xml:space="preserve"> + <value>Levetta sinistra</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftTrigger" xml:space="preserve"> + <value>Grilletto sinistro</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightShoulder" xml:space="preserve"> + <value>Spalla destra</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightThumb" xml:space="preserve"> + <value>Levetta destra</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightTrigger" xml:space="preserve"> + <value>Grilletto destro</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Start" xml:space="preserve"> + <value>Avvio</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.ProfileErrorCode.IsRunning" xml:space="preserve"> + <value>Oops. Sembra che questo profilo sia già in esecuzione. Alcune opzioni che richiedono un'eseguibile potrebbero essere disabilitate.</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B1" xml:space="preserve"> + <value>Cross</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B2" xml:space="preserve"> + <value>Circle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B3" xml:space="preserve"> + <value>Square</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B4" xml:space="preserve"> + <value>Triangle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Start" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.KeyFlags.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.KeyFlags.Alt" xml:space="preserve"> + <value>Alt</value> + </data> + <data name="Enum.KeyFlags.Apostrophe" xml:space="preserve"> + <value>Apostrophe</value> + </data> + <data name="Enum.KeyFlags.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.KeyFlags.Backslash" xml:space="preserve"> + <value>Backslash</value> + </data> + <data name="Enum.KeyFlags.Backspace" xml:space="preserve"> + <value>Backspace</value> + </data> + <data name="Enum.KeyFlags.C" xml:space="preserve"> + <value>C</value> + </data> + <data name="Enum.KeyFlags.Comma" xml:space="preserve"> + <value>Comma</value> + </data> + <data name="Enum.KeyFlags.Control" xml:space="preserve"> + <value>Control</value> + </data> + <data name="Enum.KeyFlags.D" xml:space="preserve"> + <value>D</value> + </data> + <data name="Enum.KeyFlags.Delete" xml:space="preserve"> + <value>Delete</value> + </data> + <data name="Enum.KeyFlags.E" xml:space="preserve"> + <value>E</value> + </data> + <data name="Enum.KeyFlags.End" xml:space="preserve"> + <value>End</value> + </data> + <data name="Enum.KeyFlags.Enter" xml:space="preserve"> + <value>Enter</value> + </data> + <data name="Enum.KeyFlags.Equal" xml:space="preserve"> + <value>Equal</value> + </data> + <data name="Enum.KeyFlags.Escape" xml:space="preserve"> + <value>Escape</value> + </data> + <data name="Enum.KeyFlags.F" xml:space="preserve"> + <value>F</value> + </data> + <data name="Enum.KeyFlags.F1" xml:space="preserve"> + <value>F1</value> + </data> + <data name="Enum.KeyFlags.F10" xml:space="preserve"> + <value>F10</value> + </data> + <data name="Enum.KeyFlags.F11" xml:space="preserve"> + <value>F11</value> + </data> + <data name="Enum.KeyFlags.F12" xml:space="preserve"> + <value>F12</value> + </data> + <data name="Enum.KeyFlags.F2" xml:space="preserve"> + <value>F2</value> + </data> + <data name="Enum.KeyFlags.F3" xml:space="preserve"> + <value>F3</value> + </data> + <data name="Enum.KeyFlags.F4" xml:space="preserve"> + <value>F4</value> + </data> + <data name="Enum.KeyFlags.F5" xml:space="preserve"> + <value>F5</value> + </data> + <data name="Enum.KeyFlags.F6" xml:space="preserve"> + <value>F6</value> + </data> + <data name="Enum.KeyFlags.F7" xml:space="preserve"> + <value>F7</value> + </data> + <data name="Enum.KeyFlags.F8" xml:space="preserve"> + <value>F8</value> + </data> + <data name="Enum.KeyFlags.F9" xml:space="preserve"> + <value>F9</value> + </data> + <data name="Enum.KeyFlags.G" xml:space="preserve"> + <value>G</value> + </data> + <data name="Enum.KeyFlags.Grave" xml:space="preserve"> + <value>Grave</value> + </data> + <data name="Enum.KeyFlags.H" xml:space="preserve"> + <value>H</value> + </data> + <data name="Enum.KeyFlags.Home" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.KeyFlags.I" xml:space="preserve"> + <value>I</value> + </data> + <data name="Enum.KeyFlags.Insert" xml:space="preserve"> + <value>Insert</value> + </data> + <data name="Enum.KeyFlags.J" xml:space="preserve"> + <value>J</value> + </data> + <data name="Enum.KeyFlags.K" xml:space="preserve"> + <value>K</value> + </data> + <data name="Enum.KeyFlags.L" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.KeyFlags.Minus" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.KeyFlags.N" xml:space="preserve"> + <value>N</value> + </data> + <data name="Enum.KeyFlags.O" xml:space="preserve"> + <value>O</value> + </data> + <data name="Enum.KeyFlags.P" xml:space="preserve"> + <value>P</value> + </data> + <data name="Enum.KeyFlags.Pause" xml:space="preserve"> + <value>Pause</value> + </data> + <data name="Enum.KeyFlags.Period" xml:space="preserve"> + <value>Period</value> + </data> + <data name="Enum.KeyFlags.Q" xml:space="preserve"> + <value>Q</value> + </data> + <data name="Enum.KeyFlags.R" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.KeyFlags.S" xml:space="preserve"> + <value>S</value> + </data> + <data name="Enum.KeyFlags.Semicolon" xml:space="preserve"> + <value>Semicolon</value> + </data> + <data name="Enum.KeyFlags.Shift" xml:space="preserve"> + <value>Shift</value> + </data> + <data name="Enum.KeyFlags.Slash" xml:space="preserve"> + <value>Slash</value> + </data> + <data name="Enum.KeyFlags.Space" xml:space="preserve"> + <value>Space</value> + </data> + <data name="Enum.KeyFlags.T" xml:space="preserve"> + <value>T</value> + </data> + <data name="Enum.KeyFlags.Tab" xml:space="preserve"> + <value>Tab</value> + </data> + <data name="Enum.KeyFlags.U" xml:space="preserve"> + <value>U</value> + </data> + <data name="Enum.KeyFlags.V" xml:space="preserve"> + <value>V</value> + </data> + <data name="Enum.KeyFlags.W" xml:space="preserve"> + <value>W</value> + </data> + <data name="Enum.KeyFlags.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.KeyFlags.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.KeyFlags.Z" xml:space="preserve"> + <value>Z</value> + </data> + <data name="Enum.MotionOuput.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.MotionOuput.MoveCursor" xml:space="preserve"> + <value>MoveCursor</value> + </data> + <data name="Enum.MotionOuput.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="Enum.MotionOuput.ScrollWheel" xml:space="preserve"> + <value>ScrollWheel</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>ZL</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>ZR</value> + </data> + <data name="Enum.ProController.ButtonFlags.B1" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.ProController.ButtonFlags.B2" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.ProController.ButtonFlags.B3" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.ProController.ButtonFlags.B4" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.ProController.ButtonFlags.Back" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.ProController.ButtonFlags.L1" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.ProController.ButtonFlags.R1" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special2" xml:space="preserve"> + <value>Capture</value> + </data> + <data name="Enum.ProController.ButtonFlags.Start" xml:space="preserve"> + <value>Plus</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderClosePrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseText" xml:space="preserve"> + <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="ProfilesPage_Wrapper_Disabled" xml:space="preserve"> + <value>Disabled</value> + </data> + <data name="ProfilesPage_Wrapper_Injection" xml:space="preserve"> + <value>Injection (recommended)</value> + </data> + <data name="ProfilesPage_Wrapper_Redirection" xml:space="preserve"> + <value>Redirection</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrder" xml:space="preserve"> + <value>Improve virtual controller detection</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderDesc" xml:space="preserve"> + <value>Forces the virtual controller to be detected as first controller during Windows start. Enable this if your app/game doesn't detect your inputs (requires device reboot).</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderText" xml:space="preserve"> + <value>Your device must be restarted in order for the changes to take effect. Would you like to restart now?</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderTitle" xml:space="preserve"> + <value>Restart required</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencySecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyText" xml:space="preserve"> + <value>Disabling this setting will also disable "Improve virtual controller detection". Do you want to continue?</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="Enum.KeyFlags.M" xml:space="preserve"> + <value>M</value> + </data> + <data name="Controller_Connect" xml:space="preserve"> + <value>Connect</value> + </data> + <data name="Controller_Disconnect" xml:space="preserve"> + <value>Disconnect</value> + </data> + <data name="Controller_Hide" xml:space="preserve"> + <value>Hide</value> + </data> + <data name="Controller_Unhide" xml:space="preserve"> + <value>Unhide</value> + </data> + <data name="Controller_Virtual" xml:space="preserve"> + <value>Virtual </value> + </data> + <data name="MainWindow_Back" xml:space="preserve"> + <value>Back</value> + </data> + <data name="MainWindow_Exit" xml:space="preserve"> + <value>Exit</value> + </data> + <data name="MainWindow_MainWindow" xml:space="preserve"> + <value>Main Window</value> + </data> + <data name="MainWindow_Navigate" xml:space="preserve"> + <value>Navigate</value> + </data> + <data name="MainWindow_QuickTools" xml:space="preserve"> + <value>Quick Tools</value> + </data> + <data name="MainWindow_Select" xml:space="preserve"> + <value>Select</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_External" xml:space="preserve"> + <value>External</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate1" xml:space="preserve"> + <value>Are you sure you want to apply this template? All layout settings will be overridden.</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate2" xml:space="preserve"> + <value>You can't undo this action. Previously applied template: {0}</value> + </data> + <data name="LayoutPage_SetAsDefault" xml:space="preserve"> + <value>Make this the default layout</value> + </data> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </root> \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.ja-JP.resx b/HandheldCompanion/Properties/Resources.ja-JP.resx index ec5c5ed54..ab2bf2a79 100644 --- a/HandheldCompanion/Properties/Resources.ja-JP.resx +++ b/HandheldCompanion/Properties/Resources.ja-JP.resx @@ -1,3 +1,4 @@ +<<<<<<< HEAD <?xml version="1.0" encoding="utf-8"?> <root> <!-- @@ -2356,4 +2357,2331 @@ <data name="IController_ControllerIndex" xml:space="preserve"> <value>Gamepad: {0}</value> </data> +======= +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="AboutPage_About" xml:space="preserve"> + <value>概要</value> + </data> + <data name="AboutPage_AboutDescription" xml:space="preserve"> + <value>>Windows サービスとタッチ インターフェイスに最適化された GUI の組み合わせにより、ハンドヘルド ゲーム コンピューターのエクスペリエンスが向上します。機能には、ジャイロ コントロール、仮想コントローラ シミュレーション、クイック ツール オーバーレイ、仮想タッチパッド、3D コントローラ モデル、アプリケーションごとのベースのプロファイル設定システムが含まれます。Handheld Companion は、ViGEmBus ドライバーと ViGEmClient ライブラリ、および HidHide カーネル モード フィルター ドライバーに依存しています。モーション コントロール アルゴリズムは、Jibbsmart による研究と GyroWiki で入手可能な情報に基づいています。</value> + </data> + <data name="AboutPage_Accelerometer" xml:space="preserve"> + <value>加速度計</value> + </data> + <data name="AboutPage_Author" xml:space="preserve"> + <value>作成者</value> + </data> + <data name="AboutPage_Contributors" xml:space="preserve"> + <value>貢献者</value> + </data> + <data name="AboutPage_Description" xml:space="preserve"> + <value>説明</value> + </data> + <data name="AboutPage_Donate" xml:space="preserve"> + <value>寄付する</value> + </data> + <data name="AboutPage_Gyrometer" xml:space="preserve"> + <value>ジャイロ</value> + </data> + <data name="AboutPage_Inclinometer" xml:space="preserve"> + <value>傾き</value> + </data> + <data name="AboutPage_NotApplicable" xml:space="preserve"> + <value>該当なし</value> + </data> + <data name="AboutPage_RelatedLinks" xml:space="preserve"> + <value>関連リンク</value> + </data> + <data name="AboutPage_SensorExternal" xml:space="preserve"> + <value>External</value> + </data> + <data name="AboutPage_SensorInternal" xml:space="preserve"> + <value>Internal</value> + </data> + <data name="AboutPage_SensorName" xml:space="preserve"> + <value>センサー名</value> + </data> + <data name="AboutPage_SensorSpecification" xml:space="preserve"> + <value>センサー仕様</value> + </data> + <data name="AboutPage_Service" xml:space="preserve"> + <value>サービス</value> + </data> + <data name="AboutPage_SourceCode" xml:space="preserve"> + <value>ソースコード</value> + </data> + <data name="AboutPage_Version" xml:space="preserve"> + <value>バージョン</value> + </data> + <data name="AboutPage_Wiki" xml:space="preserve"> + <value>Wiki</value> + </data> + <data name="Administrator" xml:space="preserve"> + <value>管理者</value> + </data> + <data name="ControllerPage_CloakDevice" xml:space="preserve"> + <value>接続時にコントローラーを非表示にする</value> + </data> + <data name="ControllerPage_CloakDeviceDesc" xml:space="preserve"> + <value>接続時に物理コントローラーを非表示にする</value> + </data> + <data name="ControllerPage_Connect" xml:space="preserve"> + <value>接続</value> + </data> + <data name="ControllerPage_Controller" xml:space="preserve"> + <value>コントローラ</value> + </data> + <data name="ControllerPage_DeviceCloaking" xml:space="preserve"> + <value>コントローラを隠す</value> + </data> + <data name="ControllerPage_DeviceSettings" xml:space="preserve"> + <value>コントローラー設定</value> + </data> + <data name="ControllerPage_Disconnect" xml:space="preserve"> + <value>切断</value> + </data> + <data name="ControllerPage_InputDevices" xml:space="preserve"> + <value>入力デバイス</value> + </data> + <data name="ControllerPage_UncloakOnClose" xml:space="preserve"> + <value>>閉じるときにコントローラを再表示する</value> + </data> + <data name="ControllerPage_UncloakOnCloseDesc" xml:space="preserve"> + <value>アプリケーションの終了時にすべての物理コントローラーを復元</value> + </data> + <data name="ControllerPage_VibrationStrength" xml:space="preserve"> + <value>振動の強さ</value> + </data> + <data name="ControllerPage_VibrationStrengthExpl" xml:space="preserve"> + <value>コントローラーの振動の強さを変更</value> + </data> + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\icon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="InputsHotkey_decreaseTDP" xml:space="preserve"> + <value>TDPを下げる</value> + </data> + <data name="InputsHotkey_decreaseTDPDesc" xml:space="preserve"> + <value>システムまたは現在適用されているプロファイルの TDP を 1 ワット減少</value> + </data> + <data name="InputsHotkey_fallbackInput" xml:space="preserve"> + <value>押してホットキー入力を設定</value> + </data> + <data name="InputsHotkey_fallbackOutput" xml:space="preserve"> + <value>押してキーボード出力を設定</value> + </data> + <data name="InputsHotkey_increaseTDP" xml:space="preserve"> + <value>TDPを上げる</value> + </data> + <data name="InputsHotkey_increaseTDPDesc" xml:space="preserve"> + <value>システムまたは現在適用されているプロファイルの TDP を 1 ワット増加</value> + </data> + <data name="InputsHotkey_overlayGamepad" xml:space="preserve"> + <value>3D コントローラーを表示</value> + </data> + <data name="InputsHotkey_overlayGamepadDesc" xml:space="preserve"> + <value>ボタンまたは特殊キーを押して 3D ホットキーを変更</value> + </data> + <data name="InputsHotkey_overlayTrackpads" xml:space="preserve"> + <value>仮想トラックパッドを表示する</value> + </data> + <data name="InputsHotkey_overlayTrackpadsDesc" xml:space="preserve"> + <value>ボタンまたは特殊キーを押してホットキーを変更</value> + </data> + <data name="InputsHotkey_quickTools" xml:space="preserve"> + <value>クイック ツール ウィンドウの呼び出し</value> + </data> + <data name="InputsHotkey_quickToolsDesc" xml:space="preserve"> + <value>カスタムショートカット</value> + </data> + <data name="InputsHotkey_shortcutCustom" xml:space="preserve"> + <value>カスタムショートカット</value> + </data> + <data name="InputsHotkey_shortcutCustomDesc" xml:space="preserve"> + <value>ボタンまたは特殊キーを押してホットキーを変更</value> + </data> + <data name="InputsHotkey_shortcutDesktop" xml:space="preserve"> + <value>デスクトップの表示と非表示を切り替える</value> + </data> + <data name="InputsHotkey_shortcutDesktopDesc" xml:space="preserve"> + <value>次のキーを押します: Windows + D</value> + </data> + <data name="InputsHotkey_shortcutESC" xml:space="preserve"> + <value>Escape</value> + </data> + <data name="InputsHotkey_shortcutESCDesc" xml:space="preserve"> + <value>このキーを押します: Escape</value> + </data> + <data name="InputsHotkey_shortcutExpand" xml:space="preserve"> + <value>ウィンドウと全画面を切り替える</value> + </data> + <data name="InputsHotkey_shortcutExpandDesc" xml:space="preserve"> + <value>次のキーを押します: Alt + Enter</value> + </data> + <data name="InputsHotkey_shortcutGuide" xml:space="preserve"> + <value>ガイドまたは PS ボタン</value> + </data> + <data name="InputsHotkey_shortcutGuideDesc" xml:space="preserve"> + <value>Xbox ガイドまたは Sony PS ボタン入力をシミュレートする</value> + </data> + <data name="InputsHotkey_shortcutKeyboard" xml:space="preserve"> + <value>タッチ キーボードを表示する</value> + </data> + <data name="InputsHotkey_shortcutKeyboardDesc" xml:space="preserve"> + <value>ボタンまたは特殊キーを押してホットキーを変更</value> + </data> + <data name="InputsHotkey_shortcutKillApp" xml:space="preserve"> + <value>アプリケーションを強制的にシャットダウン</value> + </data> + <data name="InputsHotkey_shortcutKillAppDesc" xml:space="preserve"> + <value>ボタンまたは特殊キーを押してホットキーを変更</value> + </data> + <data name="InputsHotkey_shortcutMainwindow" xml:space="preserve"> + <value>メインウィンドウの表示と非表示を切り替える</value> + </data> + <data name="InputsHotkey_shortcutMainwindowDesc" xml:space="preserve"> + <value>ボタンまたは特殊キーを押してホットキーを変更</value> + </data> + <data name="InputsHotkey_shortcutTaskManager" xml:space="preserve"> + <value>タスク マネージャーを開く</value> + </data> + <data name="InputsHotkey_shortcutTaskManagerDesc" xml:space="preserve"> + <value>次のキーを押します: Ctrl + Shift + Esc</value> + </data> + <data name="InputsHotkey_shortcutTaskview" xml:space="preserve"> + <value>タスクビューを開く</value> + </data> + <data name="InputsHotkey_shortcutTaskviewDesc" xml:space="preserve"> + <value>次のキーを押します: Windows + Tab</value> + </data> + <data name="InputsHotkey_suspendResumeTask" xml:space="preserve"> + <value>再開と停止の切り替え</value> + </data> + <data name="InputsHotkey_suspendResumeTaskDesc" xml:space="preserve"> + <value>フォアグラウンド アプリケーションを一時停止または再開</value> + </data> + <data name="MainWindow_HandheldCompanion" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="MainWindow_navAbout" xml:space="preserve"> + <value>概要</value> + </data> + <data name="MainWindow_navController" xml:space="preserve"> + <value>コントローラー</value> + </data> + <data name="MainWindow_navOverlay" xml:space="preserve"> + <value>オーバーレイ</value> + </data> + <data name="MainWindow_navProfiles" xml:space="preserve"> + <value>プロファイル</value> + </data> + <data name="MainWindow_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="MainWindow_Settings" xml:space="preserve"> + <value>設定</value> + </data> + <data name="OverlayPage_8BitDoLite2Controller" xml:space="preserve"> + <value>8BitDo Lite 2</value> + </data> + <data name="OverlayPage_Alignment" xml:space="preserve"> + <value>配置</value> + </data> + <data name="OverlayPage_AlignmentDesc" xml:space="preserve"> + <value>3D コントローラー オーバーレイの配置を変更</value> + </data> + <data name="OverlayPage_AlignmentTrackpadDesc" xml:space="preserve"> + <value>トラックパッド オーバーレイの配置を変更する</value> + </data> + <data name="OverlayPage_AlwaysOnTop" xml:space="preserve"> + <value>常に先頭に表示</value> + </data> + <data name="OverlayPage_AlwaysOnTopDesc" xml:space="preserve"> + <value>切り替えると、3D コントローラーのオーバーレイが他のウィンドウの上に表示</value> + </data> + <data name="OverlayPage_BackButton" xml:space="preserve"> + <value>戻る</value> + </data> + <data name="OverlayPage_CameraAngle" xml:space="preserve"> + <value>フェイスカメラ</value> + </data> + <data name="OverlayPage_CameraAngleDesc" xml:space="preserve"> + <value>カメラを向いた場合の 3D コントローラー オーバーレイ モデルの動作を変更</value> + </data> + <data name="OverlayPage_CameraAnglePitch" xml:space="preserve"> + <value>ピッチ</value> + </data> + <data name="OverlayPage_CameraAnglePitchDesc" xml:space="preserve"> + <value>角度を度単位で変更</value> + </data> + <data name="OverlayPage_Color" xml:space="preserve"> + <value>背景色</value> + </data> + <data name="OverlayPage_ColorDesc" xml:space="preserve"> + <value>3D コントローラーのオーバーレイの背景色を変更</value> + </data> + <data name="OverlayPage_ControllerOptions" xml:space="preserve"> + <value>コントローラ オプション</value> + </data> + <data name="OverlayPage_DualSenseController" xml:space="preserve"> + <value>プレイステーション DualSense</value> + </data> + <data name="OverlayPage_EmulatedController" xml:space="preserve"> + <value>エミュレートされたコントローラー</value> + </data> + <data name="OverlayPage_FaceCamera" xml:space="preserve"> + <value>フェイスカメラ</value> + </data> + <data name="OverlayPage_FaceCameraDesc" xml:space="preserve"> + <value>3D モデルはデフォルトの位置としてカメラに向かうようにゆっくり回転します</value> + </data> + <data name="OverlayPage_Listening" xml:space="preserve"> + <value>Listening...</value> + </data> + <data name="OverlayPage_MachenikeHG510Controller" xml:space="preserve"> + <value>MACHENIKE HG510</value> + </data> + <data name="OverlayPage_MainTrigger" xml:space="preserve"> + <value>メイントリガー</value> + </data> + <data name="OverlayPage_MainTriggerDesc" xml:space="preserve"> + <value>3D コントローラー オーバーレイのメイン トリガーを変更</value> + </data> + <data name="OverlayPage_MotionActivated" xml:space="preserve"> + <value>モーション</value> + </data> + <data name="OverlayPage_MotionActivatedDesc" xml:space="preserve"> + <value>センサー情報をもとにユーザーの動きに合わせてモデルが動きます</value> + </data> + <data name="OverlayPage_N64Controller" xml:space="preserve"> + <value>N64</value> + </data> + <data name="OverlayPage_OEMController" xml:space="preserve"> + <value>OEM コントローラー</value> + </data> + <data name="OverlayPage_Opacity" xml:space="preserve"> + <value>不透明度</value> + </data> + <data name="OverlayPage_OpacityControllerDesc" xml:space="preserve"> + <value>3D コントローラーのオーバーレイの不透明度を変更</value> + </data> + <data name="OverlayPage_OpacityTrackpadDesc" xml:space="preserve"> + <value>トラックパッドのオーバーレイの不透明度を変更</value> + </data> + <data name="OverlayPage_Overlay" xml:space="preserve"> + <value>オーバーレイ</value> + </data> + <data name="OverlayPage_OverlayModel" xml:space="preserve"> + <value>オーバーレイ モデル</value> + </data> + <data name="OverlayPage_OverlayModelDesc" xml:space="preserve"> + <value>3D コントローラーのオーバーレイ モデルを変更</value> + </data> + <data name="OverlayPage_OverlayPreview" xml:space="preserve"> + <value>オーバーレイ プレビュー</value> + </data> + <data name="OverlayPage_RenderSettings" xml:space="preserve"> + <value>レンダリング設定</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasing" xml:space="preserve"> + <value>アンチエイリアシング</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasingDesc" xml:space="preserve"> + <value>レンダリングのアンチエイリアスステータスを変更</value> + </data> + <data name="OverlayPage_RenderSettingsDesc" xml:space="preserve"> + <value>固定 3D オーバーレイ コントローラー モデルのレンダリング設定を変更</value> + </data> + <data name="OverlayPage_RenderSettingsFramerate" xml:space="preserve"> + <value>更新レート</value> + </data> + <data name="OverlayPage_RenderSettingsFramerateDesc" xml:space="preserve"> + <value>レンダリングの更新レートを 1秒あたりで変更</value> + </data> + <data name="OverlayPage_Size" xml:space="preserve"> + <value>サイズ</value> + </data> + <data name="OverlayPage_SizeDesc" xml:space="preserve"> + <value>3D コントローラーのオーバーレイ サイズを変更</value> + </data> + <data name="OverlayPage_SizeOverlayDesc" xml:space="preserve"> + <value>トラックパッドのオーバーレイ サイズを変更する</value> + </data> + <data name="OverlayPage_StartButton" xml:space="preserve"> + <value>Start</value> + </data> + <data name="OverlayPage_ToyController" xml:space="preserve"> + <value>Fisher-Price controller</value> + </data> + <data name="OverlayPage_TrackpadsOptions" xml:space="preserve"> + <value>トラックパッドのオプション</value> + </data> + <data name="OverlayPage_XboxOneController" xml:space="preserve"> + <value>Xbox One</value> + </data> + <data name="OverlayPage_ZDOPlusController" xml:space="preserve"> + <value>ZD O+</value> + </data> + <data name="Overlay_Overlay" xml:space="preserve"> + <value>オーバーレイ</value> + </data> + <data name="ProcessEx_processResume" xml:space="preserve"> + <value>再開</value> + </data> + <data name="ProcessEx_processSuspend" xml:space="preserve"> + <value>一時停止</value> + </data> + <data name="SettingsMode0_AdditionalSettings" xml:space="preserve"> + <value>追加設定</value> + </data> + <data name="SettingsMode0_AimingDownSights" xml:space="preserve"> + <value>照準を合わせるモーション数</value> + </data> + <data name="SettingsMode0_AimingDownSightsActivation" xml:space="preserve"> + <value>アクティブ化ボタン</value> + </data> + <data name="SettingsMode0_AimingDownSightsDesc" xml:space="preserve"> + <value>設定された起動ボタンを使用して照準器またはスコープを狙うときの追加のモーション感度数</value> + </data> + <data name="SettingsMode0_AimingDownSightsMultiplier" xml:space="preserve"> + <value>乗数値</value> + </data> + <data name="SettingsMode0_CameraOptions" xml:space="preserve"> + <value>カメラオプション</value> + </data> + <data name="SettingsMode0_CustomResponseCurve" xml:space="preserve"> + <value>カスタム応答曲線</value> + </data> + <data name="SettingsMode0_CustomResponseCurveGameOutput" xml:space="preserve"> + <value>ゲームに送信</value> + </data> + <data name="SettingsMode0_CustomResponseIntensity" xml:space="preserve"> + <value>動きの強さ</value> + </data> + <data name="SettingsMode0_CustomResponsePresetAgressive" xml:space="preserve"> + <value>アグレッシブ</value> + </data> + <data name="SettingsMode0_CustomResponsePresetDefault" xml:space="preserve"> + <value>デフォルト</value> + </data> + <data name="SettingsMode0_CustomResponsePresetOptions" xml:space="preserve"> + <value>プリセットオプション</value> + </data> + <data name="SettingsMode0_CustomResponsePresetPrecise" xml:space="preserve"> + <value>正確</value> + </data> + <data name="SettingsMode0_FlickDuration" xml:space="preserve"> + <value>フリック時間</value> + </data> + <data name="SettingsMode0_FlickDurationDesc" xml:space="preserve"> + <value>フリック時間を変更し、180 度回転するように調整</value> + </data> + <data name="SettingsMode0_FlickStick" xml:space="preserve"> + <value>フリックスティック (実験的)</value> + </data> + <data name="SettingsMode0_FlickStickDesc" xml:space="preserve"> + <value>ジョイスティックの(右)フリックの方向にカメラを向け、回転させることでカメラを純粋に水平面内で回転</value> + </data> + <data name="SettingsMode0_FlickStickEnable" xml:space="preserve"> + <value>フリックスティック有効</value> + </data> + <data name="SettingsMode0_Sensitivity" xml:space="preserve"> + <value>感度</value> + </data> + <data name="SettingsMode0_SensitivityDesc" xml:space="preserve"> + <value>水平軸と垂直軸のモーション感度を変更</value> + </data> + <data name="SettingsMode0_SensitivityX" xml:space="preserve"> + <value>感度 X</value> + </data> + <data name="SettingsMode0_SensitivityXDesc" xml:space="preserve"> + <value>横軸のモーション感度を変更</value> + </data> + <data name="SettingsMode0_SensitivityY" xml:space="preserve"> + <value>感度 Y</value> + </data> + <data name="SettingsMode0_SensitivityYDesc" xml:space="preserve"> + <value>垂直軸のモーション感度を変更</value> + </data> + <data name="SettingsMode0_StickSensitivtity" xml:space="preserve"> + <value>スティック感度</value> + </data> + <data name="SettingsMode0_StickSensitivtityDesc" xml:space="preserve"> + <value>回転速度を変更</value> + </data> + <data name="SettingsMode1_AdditionalSettings" xml:space="preserve"> + <value>追加設定</value> + </data> + <data name="SettingsMode1_Deadzone" xml:space="preserve"> + <value>デッドゾーン</value> + </data> + <data name="SettingsMode1_DeadzoneDesc" xml:space="preserve"> + <value>ステアリングのデッドゾーンを度単位で変更します。ステアリングの直進性が向上</value> + </data> + <data name="SettingsMode1_JoystickGameInput" xml:space="preserve"> + <value>ジョイスティック ゲーム入力</value> + </data> + <data name="SettingsMode1_JoystickSteering" xml:space="preserve"> + <value>ジョイスティックステアリング</value> + </data> + <data name="SettingsMode1_JoystickSteeringOptions" xml:space="preserve"> + <value>ジョイスティックステアリングオプション</value> + </data> + <data name="SettingsMode1_JoystickSteeringPreview" xml:space="preserve"> + <value>ジョイスティックステアリングのプレビュー</value> + </data> + <data name="SettingsMode1_MaxSteeringAngle" xml:space="preserve"> + <value>最大ステアリング角度</value> + </data> + <data name="SettingsMode1_MaxSteeringAngleDesc" xml:space="preserve"> + <value>ステアリング角度の最大値を度単位で変更</value> + </data> + <data name="SettingsMode1_SteeringLinearity" xml:space="preserve"> + <value>ステアリングリニアリティ</value> + </data> + <data name="SettingsMode1_SteeringLinearityDesc" xml:space="preserve"> + <value>入力とステアリングの間のマッピング。値を低くすると、フルロック付近の精度が高くなりますが、中心付近の精度は低くなります。値を大きくすると、中心付近の精度が高くなりますが、フルロック付近の精度は低くなります。1.0 は線形マッピングです</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplier" xml:space="preserve"> + <value>加速度計乗数</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplierDesc" xml:space="preserve"> + <value>システムが報告する加速度計の値を変更</value> + </data> + <data name="ProfilesPage_AdditionalSettings" xml:space="preserve"> + <value>追加設定</value> + </data> + <data name="ProfilesPage_AntiDeadzone" xml:space="preserve"> + <value>アンチデッドゾーン</value> + </data> + <data name="ProfilesPage_AntiDeadzoneDesc" xml:space="preserve"> + <value>ゲーム内のアンチデッドゾーンをパーセント単位で変更</value> + </data> + <data name="ProfilesPage_AntiDeadzoneUnitPercentage" xml:space="preserve"> + <value> %</value> + </data> + <data name="ProfilesPage_AreYouSureDelete1" xml:space="preserve"> + <value>削除してもよろしいですか?</value> + </data> + <data name="ProfilesPage_AreYouSureDelete2" xml:space="preserve"> + <value>このアイテムはすぐに削除されます。この操作を元に戻すことはできません</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite1" xml:space="preserve"> + <value>このプロファイルを上書きしてもよろしいですか?</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite2" xml:space="preserve"> + <value>{0} は上書きされます。この操作を元に戻すことはできません</value> + </data> + <data name="ProfilesPage_BoostPower" xml:space="preserve"> + <value>ブーストTDP</value> + </data> + <data name="ProfilesPage_BoostPowerDesc" xml:space="preserve"> + <value>ブーストTDPを変更</value> + </data> + <data name="ProfilesPage_Cancel" xml:space="preserve"> + <value>キャンセル</value> + </data> + <data name="ProfilesPage_CreateNewProfile" xml:space="preserve"> + <value>新しいプロファイルを作成</value> + </data> + <data name="ProfilesPage_Delete" xml:space="preserve"> + <value>削除</value> + </data> + <data name="ProfilesPage_DeleteProfile" xml:space="preserve"> + <value>プロファイルを削除</value> + </data> + <data name="ProfilesPage_EnableProfile" xml:space="preserve"> + <value>ゲームごとのユーザー プロファイル</value> + </data> + <data name="ProfilesPage_EnableProfileDesc" xml:space="preserve"> + <value>関連付けられたアプリケーションが検出されると、プロファイルは自動的に適用</value> + </data> + <data name="ProfilesPage_GlobalSettings" xml:space="preserve"> + <value>グローバル設定</value> + </data> + <data name="ProfilesPage_GlobalSettingsDesc" xml:space="preserve"> + <value>グローバル プロファイル設定を変更</value> + </data> + <data name="ProfilesPage_GyrometerMultiplier" xml:space="preserve"> + <value>ジャイロメーター乗数</value> + </data> + <data name="ProfilesPage_GyrometerMultiplierDesc" xml:space="preserve"> + <value>システムが報告するジャイロメーター値を変更</value> + </data> + <data name="ProfilesPage_GyroSteeringAxis" xml:space="preserve"> + <value>ジャイロステアリング</value> + </data> + <data name="ProfilesPage_GyroSteeringAxisDesc" xml:space="preserve"> + <value>コントローラーの水平方向の動きを制御するには、回転軸または横軸を使用できます</value> + </data> + <data name="ProfilesPage_InvertHorizontalAxis" xml:space="preserve"> + <value>横軸を反転</value> + </data> + <data name="ProfilesPage_InvertVerticalAxis" xml:space="preserve"> + <value>縦軸を反転</value> + </data> + <data name="ProfilesPage_MotionControlSettings" xml:space="preserve"> + <value>モーションコントロール設定</value> + </data> + <data name="ProfilesPage_MotionControlSettingsDesc" xml:space="preserve"> + <value>グローバル モーション コントロール設定を変更</value> + </data> + <data name="ProfilesPage_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="ProfilesPage_PowerSettings" xml:space="preserve"> + <value>電源設定</value> + </data> + <data name="ProfilesPage_PowerSettingsDesc" xml:space="preserve"> + <value>電源設定を変更</value> + </data> + <data name="ProfilesPage_ProfileDetails" xml:space="preserve"> + <value>プロファイルの詳細</value> + </data> + <data name="ProfilesPage_ProfileName" xml:space="preserve"> + <value>プロファイル名</value> + </data> + <data name="ProfilesPage_ProfilePath" xml:space="preserve"> + <value>プロファイル パス</value> + </data> + <data name="ProfilesPage_Profiles" xml:space="preserve"> + <value>プロファイル</value> + </data> + <data name="ProfilesPage_ProfileSelection" xml:space="preserve"> + <value>プロファイルの選択</value> + </data> + <data name="ProfilesPage_ProfileSelectionDesc" xml:space="preserve"> + <value>編集したいプロファイルを選択</value> + </data> + <data name="ProfilesPage_ProfileSettings" xml:space="preserve"> + <value>プロファイル設定</value> + </data> + <data name="ProfilesPage_ProfileUpdated1" xml:space="preserve"> + <value>プロファイルが更新されました</value> + </data> + <data name="ProfilesPage_ProfileUpdated2" xml:space="preserve"> + <value>更新されました</value> + </data> + <data name="ProfilesPage_Roll" xml:space="preserve"> + <value>ロール</value> + </data> + <data name="ProfilesPage_SensitivityX" xml:space="preserve"> + <value>X</value> + </data> + <data name="ProfilesPage_SensitivityY" xml:space="preserve"> + <value>Y</value> + </data> + <data name="ProfilesPage_StyleofInput" xml:space="preserve"> + <value>入力のスタイル</value> + </data> + <data name="ProfilesPage_StyleofInputTooltip" xml:space="preserve"> + <value>コントローラーの物理入力は、さまざまなタイプのデバイスのように動作するようにプログラムできます</value> + </data> + <data name="ProfilesPage_StyleofOutput" xml:space="preserve"> + <value>出力デバイス</value> + </data> + <data name="ProfilesPage_StyleofOutputTooltip" xml:space="preserve"> + <value>モーション コマンドを受信するデバイスを選択</value> + </data> + <data name="ProfilesPage_SustainedPower" xml:space="preserve"> + <value>TDP制限</value> + </data> + <data name="ProfilesPage_SustainedPowerDesc" xml:space="preserve"> + <value>TDPを変更</value> + </data> + <data name="ProfilesPage_TDPOverride" xml:space="preserve"> + <value>TDPを変更</value> + </data> + <data name="ProfilesPage_TDPOverrideDesc" xml:space="preserve"> + <value>プロセッサの電力を制限して総電力を削減</value> + </data> + <data name="ProfilesPage_UMCEnable" xml:space="preserve"> + <value>ユニバーサル モーション コントローラーを有効</value> + </data> + <data name="ProfilesPage_UMCMotionOff" xml:space="preserve"> + <value>無効、ボタンでオンにします</value> + </data> + <data name="ProfilesPage_UMCMotionOn" xml:space="preserve"> + <value>有効、ボタンでオフにします</value> + </data> + <data name="ProfilesPage_UMCMotionOnOff" xml:space="preserve"> + <value>モーションのアクティブ化</value> + </data> + <data name="ProfilesPage_UMCMotionOnOffDesc" xml:space="preserve"> + <value>モーション入力が無効な状態で、選択したボタンを使用してモーションを有効に +モーション入力が有効になっている場合、選択したボタンを使用してモーションを無効にします</value> + </data> + <data name="ProfilesPage_UMCSelectionRightLeftDesc" xml:space="preserve"> + <value>モーション コマンドを受信するデバイスを選択</value> + </data> + <data name="ProfilesPage_UMCSettings" xml:space="preserve"> + <value>ユニバーサル モーション コントロール設定</value> + </data> + <data name="ProfilesPage_UMCSettingsDesc" xml:space="preserve"> + <value>デバイスの動きをコントローラー入力に変換</value> + </data> + <data name="ProfilesPage_UpdateProfile" xml:space="preserve"> + <value>プロファイルを更新</value> + </data> + <data name="ProfilesPage_Whitelist" xml:space="preserve"> + <value>アプリケーションがデバイスの物理コントローラーにアクセスできるようにする</value> + </data> + <data name="ProfilesPage_Wrapper" xml:space="preserve"> + <value>拡張互換性 (XInputPlus)</value> + </data> + <data name="ProfilesPage_Yaw" xml:space="preserve"> + <value>回転</value> + </data> + <data name="ProfilesPage_Yes" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="QuickPerformancePage_GPUControl" xml:space="preserve"> + <value>手動 GPU クロック制御</value> + </data> + <data name="QuickPerformancePage_GPUControlDesc" xml:space="preserve"> + <value>GPU を固定クロックに設定</value> + </data> + <data name="QuickPerformancePage_GPUUnit" xml:space="preserve"> + <value> MHz</value> + </data> + <data name="QuickPerformancePage_PowerMode" xml:space="preserve"> + <value>電力モード</value> + </data> + <data name="QuickPerformancePage_PowerModeBalanced" xml:space="preserve"> + <value>バランス型</value> + </data> + <data name="QuickPerformancePage_PowerModeDesc" xml:space="preserve"> + <value>電力使用量とパフォーマンスに基づいてデバイスを最適化</value> + </data> + <data name="QuickPerformancePage_PowerModeEfficiency" xml:space="preserve"> + <value>効率</value> + </data> + <data name="QuickPerformancePage_PowerModePerformance" xml:space="preserve"> + <value>パフォーマンス</value> + </data> + <data name="QuickPerformancePage_TDPBoost" xml:space="preserve"> + <value>ブースト</value> + </data> + <data name="QuickPerformancePage_TDPLimit" xml:space="preserve"> + <value>TDP制限</value> + </data> + <data name="QuickPerformancePage_TDPLimitDesc" xml:space="preserve"> + <value>プロセッサの電力を制限して総電力を削減</value> + </data> + <data name="QuickPerformancePage_TDPOverWrittenWarning" xml:space="preserve"> + <value>TDP制限はプロファイルによって上書きされます</value> + </data> + <data name="QuickPerformancePage_TDPSustained" xml:space="preserve"> + <value>基本</value> + </data> + <data name="QuickPerformancePage_TDPUnitWatt" xml:space="preserve"> + <value> W</value> + </data> + <data name="QuickProfilesPage_Create" xml:space="preserve"> + <value>プロファイルを作成する</value> + </data> + <data name="QuickProfilesPage_Waiting" xml:space="preserve"> + <value>プロセスを待機しています...</value> + </data> + <data name="ServiceDescription" xml:space="preserve"> + <value>仮想コントローラーを介して Windows ハンドヘルド ゲーム コンピューターにジャイロスコープと加速度計のサポートを提供します。サービスが有効になっている場合、組み込みコントローラーはホワイトリスト外のアプリケーションに隠されます。サービスが無効になっている場合、組み込みコントローラーのクロークが解除され、仮想コントローラーが無効になります</value> + </data> + <data name="ServiceName" xml:space="preserve"> + <value>コントローラー サービス</value> + </data> + <data name="SettingsPage_AppLanguage" xml:space="preserve"> + <value>言語</value> + </data> + <data name="SettingsPage_AppLanguageDesc" xml:space="preserve"> + <value>アプリケーション言語</value> + </data> + <data name="SettingsPage_AppLanguageWarning" xml:space="preserve"> + <value>再起動が必要です</value> + </data> + <data name="SettingsPage_AppLanguageWarningDesc" xml:space="preserve"> + <value>変更を有効にするには、アプリケーションを再起動してください</value> + </data> + <data name="SettingsPage_AppTheme" xml:space="preserve"> + <value>テーマ</value> + </data> + <data name="SettingsPage_AppThemeDesc" xml:space="preserve"> + <value>ライト モードまたはダーク モード</value> + </data> + <data name="SettingsPage_AutoStartApp" xml:space="preserve"> + <value>アプリケーションの自動起動</value> + </data> + <data name="SettingsPage_AutoStartAppDesc" xml:space="preserve"> + <value>Windows にサインインすると、アプリケーションが自動的に起動します</value> + </data> + <data name="SettingsPage_Backdrop" xml:space="preserve"> + <value>アプリケーションの背景</value> + </data> + <data name="SettingsPage_BackdropAcrylic" xml:space="preserve"> + <value>アクリル</value> + </data> + <data name="SettingsPage_BackdropDesc" xml:space="preserve"> + <value>アプリケーションの背景、なし、マイカ、タブ付き、アクリル</value> + </data> + <data name="SettingsPage_BackdropMica" xml:space="preserve"> + <value>マイカ</value> + </data> + <data name="SettingsPage_BackdropNone" xml:space="preserve"> + <value>なし</value> + </data> + <data name="SettingsPage_BackdropTabbed" xml:space="preserve"> + <value>タブ</value> + </data> + <data name="SettingsPage_CheckForUpdates" xml:space="preserve"> + <value>更新を確認する</value> + </data> + <data name="SettingsPage_CloseMinimizes" xml:space="preserve"> + <value>閉じると最小化</value> + </data> + <data name="SettingsPage_CloseMinimizesDesc" xml:space="preserve"> + <value>アプリケーションを閉じずに最小化</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStart" xml:space="preserve"> + <value>起動時にデスクトップ プロファイルを有効にする</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStartDesc" xml:space="preserve"> + <value>デスクトップ プロファイルはアプリケーションの起動時に自動的に有効になります</value> + </data> + <data name="SettingsPage_Download" xml:space="preserve"> + <value>ダウンロード</value> + </data> + <data name="SettingsPage_DownloadingPercentage" xml:space="preserve"> + <value>ダウンロード中 - </value> + </data> + <data name="SettingsPage_EcoQoS" xml:space="preserve"> + <value>スマートな効率</value> + </data> + <data name="SettingsPage_EcoQoSDesc" xml:space="preserve"> + <value>スマート効率モードはバックグラウンドプロセスの優先順位を下げ、エネルギー効率を向上させます</value> + </data> + <data name="SettingsPage_GeneralOptions" xml:space="preserve"> + <value>一般オプション</value> + </data> + <data name="SettingsPage_InstallNow" xml:space="preserve"> + <value>今すぐインストール</value> + </data> + <data name="SettingsPage_LastChecked" xml:space="preserve"> + <value>最終チェック日: </value> + </data> + <data name="SettingsPage_NotificationOptions" xml:space="preserve"> + <value>通知オプション</value> + </data> + <data name="SettingsPage_OpenAppBackground" xml:space="preserve"> + <value>アプリケーションをバックグラウンドで開きます</value> + </data> + <data name="SettingsPage_OpenAppBackgroundDesc" xml:space="preserve"> + <value>アプリケーションは最初は最小化されて起動し、タスクバーに表示されます</value> + </data> + <data name="SettingsPage_SensorExternal" xml:space="preserve"> + <value>外部</value> + </data> + <data name="SettingsPage_SensorInternal" xml:space="preserve"> + <value>内部</value> + </data> + <data name="SettingsPage_SensorOptions" xml:space="preserve"> + <value>センサー オプション</value> + </data> + <data name="SettingsPage_SensorPlacementDirection" xml:space="preserve"> + <value>外部センサーの設置方向</value> + </data> + <data name="SettingsPage_SensorPlacementDirectionDesc" xml:space="preserve"> + <value>センサーがデバイスのどちら側に取り付けられているかを選択します</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDown" xml:space="preserve"> + <value>外部センサーの上下が逆</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDownDesc" xml:space="preserve"> + <value>センサーは逆さまに取り付けられており、USB-C コンバーターで可能</value> + </data> + <data name="SettingsPage_SensorSelection" xml:space="preserve"> + <value>センサーの選択</value> + </data> + <data name="SettingsPage_SensorSelectionDesc" xml:space="preserve"> + <value>モーション入力に使用する目的のセンサーを選択します</value> + </data> + <data name="SettingsPage_Settings" xml:space="preserve"> + <value>設定</value> + </data> + <data name="SettingsPage_StartupType" xml:space="preserve"> + <value>スタートアップの種類</value> + </data> + <data name="SettingsPage_StartupTypeDesc" xml:space="preserve"> + <value>サービスの起動タイプを定義するためにサービス マネージャーによって使用されます</value> + </data> + <data name="SettingsPage_TDPMax" xml:space="preserve"> + <value>最大電力</value> + </data> + <data name="SettingsPage_TDPMaxDesc" xml:space="preserve"> + <value>プロセッサに供給される最大電力 (ワット単位)</value> + </data> + <data name="SettingsPage_TDPMin" xml:space="preserve"> + <value>最小電力</value> + </data> + <data name="SettingsPage_TDPMinDesc" xml:space="preserve"> + <value>プロセッサに供給される最小電力 (ワット単位)</value> + </data> + <data name="SettingsPage_TDPRangeOverride" xml:space="preserve"> + <value>設定可能な電力 (cTDP) オーバーライド</value> + </data> + <data name="SettingsPage_TDPRangeOverrideDesc" xml:space="preserve"> + <value>CPU 仕様を超えて最小および最大電力値 (TDP) を変更できます</value> + </data> + <data name="SettingsPage_ThemeDark" xml:space="preserve"> + <value>Dark</value> + </data> + <data name="SettingsPage_ThemeLight" xml:space="preserve"> + <value>Light</value> + </data> + <data name="SettingsPage_ToastNotification" xml:space="preserve"> + <value>通知</value> + </data> + <data name="SettingsPage_ToastNotificationDesc" xml:space="preserve"> + <value>Windows アクション センターでアプリケーションから通知を取得</value> + </data> + <data name="SettingsPage_UpdateAvailable" xml:space="preserve"> + <value>利用可能なアップデート</value> + </data> + <data name="SettingsPage_UpdateCheck" xml:space="preserve"> + <value>更新をチェックしています...</value> + </data> + <data name="SettingsPage_UpdateFailedDownload" xml:space="preserve"> + <value>アップデート ファイルをダウンロードできませんでした</value> + </data> + <data name="SettingsPage_UpdateFailedGithub" xml:space="preserve"> + <value>github にアクセスできませんでした</value> + </data> + <data name="SettingsPage_UpdateFailedInstall" xml:space="preserve"> + <value>アップデート ファイルが見つかりませんでした</value> + </data> + <data name="SettingsPage_UpdateWarning" xml:space="preserve"> + <value>問題がありました</value> + </data> + <data name="SettingsPage_UpToDate" xml:space="preserve"> + <value>最新情報</value> + </data> + <data name="ToastNewControllerEx" xml:space="preserve"> + <value>コントローラが隠され、入力が仮想コントローラに転送されるようになりました</value> + </data> + <data name="User" xml:space="preserve"> + <value>User</value> + </data> + <data name="WarningElevated" xml:space="preserve"> + <value>これらの設定のロックを解除するには、管理者としてこのツールを実行してください</value> + </data> + <data name="xinput1_x64" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\xinput1_x64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="xinput1_x86" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\xinput1_x86.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="XInputPlus" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\XInputPlus.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value> + </data> + <data name="ControllerPage_SteamControllerMute" xml:space="preserve"> + <value>仮想コントローラーをミュートします</value> + </data> + <data name="ControllerPage_SteamControllerMuteDesc" xml:space="preserve"> + <value>Steam 関連アプリケーションの仮想コントローラーをミュートします</value> + </data> + <data name="ProfilesPage_ControllerSettings" xml:space="preserve"> + <value>コントローラー設定</value> + </data> + <data name="ProfilesPage_ControllerSettingsDesc" xml:space="preserve"> + <value>仮想コントローラーの設定を変更</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzone" xml:space="preserve"> + <value>モーションアンチデッドゾーン</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzoneDesc" xml:space="preserve"> + <value>ゲーム内のデッドゾーンを補正し、小さな動きの認識を改善</value> + </data> + <data name="chord_neptune" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\controller_base\chord_neptune.vdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="empty_neptune" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\controller_base\empty_neptune.vdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityDesc" xml:space="preserve"> + <value>スティックの真円度を改善</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityLeft" xml:space="preserve"> + <value>円形度を左に</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityRight" xml:space="preserve"> + <value>円形度を右に</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeft" xml:space="preserve"> + <value>左ジョイスティックのデッドゾーン % </value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeftDesc" xml:space="preserve"> + <value>左ジョイスティックの内側と外側のデッドゾーンのパーセンテージを調整</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRight" xml:space="preserve"> + <value>右ジョイスティックのデッドゾーン %</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRightDesc" xml:space="preserve"> + <value>右ジョイスティックの内側と外側のデッドゾーンの割合を調整</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeft" xml:space="preserve"> + <value>左トリガーデッドゾーン %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeftDesc" xml:space="preserve"> + <value>左トリガーの内側と外側のデッドゾーンの割合を調整</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRight" xml:space="preserve"> + <value>右トリガーデッドゾーン %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRightDesc" xml:space="preserve"> + <value>右トリガーの内側と外側のデッドゾーンの割合を調整</value> + </data> + <data name="ControllerPage_VibrateDevice" xml:space="preserve"> + <value>接続時にコントローラーを振動させる</value> + </data> + <data name="ControllerPage_VibrateDeviceDesc" xml:space="preserve"> + <value>接続時に物理コントローラーを振動させる</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabled" xml:space="preserve"> + <value>デスクトップレイアウト</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabledDesc" xml:space="preserve"> + <value>デスクトップ コントローラーのレイアウトを切り替え</value> + </data> + <data name="InputsHotkey_shortcutControlCenter" xml:space="preserve"> + <value>アクションセンターを表示</value> + </data> + <data name="InputsHotkey_shortcutControlCenterDesc" xml:space="preserve"> + <value>Windows アクション センターの表示と非表示を切り替え</value> + </data> + <data name="ProfilesPage_ControllerLayout" xml:space="preserve"> + <value>コントローラのレイアウト</value> + </data> + <data name="InputsHotkey_QuietModeToggled" xml:space="preserve"> + <value>ファンオーバーライド</value> + </data> + <data name="InputsHotkey_QuietModeToggledDesc" xml:space="preserve"> + <value>ファンのデューティ サイクルをユーザーの値に設定</value> + </data> + <data name="InputsHotkey_OnScreenDisplay" xml:space="preserve"> + <value>スクリーンに表示</value> + </data> + <data name="InputsHotkey_OnScreenDisplayDesc" xml:space="preserve"> + <value>スクリーン表示サポートを有効</value> + </data> + <data name="InputsHotkey_decreaseBrightness" xml:space="preserve"> + <value>明るさを下げる</value> + </data> + <data name="InputsHotkey_decreaseBrightnessDesc" xml:space="preserve"> + <value>現在のディスプレイの明るさを 5% 下げます</value> + </data> + <data name="InputsHotkey_decreaseVolume" xml:space="preserve"> + <value>音量を下げる</value> + </data> + <data name="InputsHotkey_decreaseVolumeDesc" xml:space="preserve"> + <value>システムの音量を 5% 下げます</value> + </data> + <data name="InputsHotkey_increaseBrightness" xml:space="preserve"> + <value>明るさを上げる</value> + </data> + <data name="InputsHotkey_increaseBrightnessDesc" xml:space="preserve"> + <value>現在のディスプレイの明るさを 5% 上げます</value> + </data> + <data name="InputsHotkey_increaseVolume" xml:space="preserve"> + <value>音量を上げる</value> + </data> + <data name="InputsHotkey_increaseVolumeDesc" xml:space="preserve"> + <value>システムの音量を 5% 上げます</value> + </data> + <data name="ProfilesPage_AutoTDP" xml:space="preserve"> + <value>AutoTDP</value> + </data> + <data name="ProfilesPage_AutoTDPDesc" xml:space="preserve"> + <value>測定された FPS と要求された FPS ターゲットに基づいて TDP を自動調整</value> + </data> + <data name="ProfilesPage_AutoTDPFPS" xml:space="preserve"> + <value>Framerateターゲット</value> + </data> + <data name="ProfilesPage_AutoTDPFPSDesc" xml:space="preserve"> + <value>AutoTDP コントローラーの必要な FPS ターゲット値</value> + </data> + <data name="ProfilesPage_GPUMhz" xml:space="preserve"> + <value>GPU クロックの最大周波数</value> + </data> + <data name="ProfilesPage_GPUMhzDesc" xml:space="preserve"> + <value>GPU の最大クロック速度 (Mhz)</value> + </data> + <data name="QuickPerformancePage_AutoTDPUnitFPS" xml:space="preserve"> + <value>FPS</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel" xml:space="preserve"> + <value>オーバーレイ表示レベル</value> + </data> + <data name="OverlayPage_OverlayDisplayLevelDesc" xml:space="preserve"> + <value>画面上の情報表示レベルを変更</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRate" xml:space="preserve"> + <value>更新レート</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRateDesc" xml:space="preserve"> + <value>オンスクリーン表示の更新レートを変更</value> + </data> + <data name="ProfilesPage_FramerateLimit" xml:space="preserve"> + <value>フレームレート制限</value> + </data> + <data name="ProfilesPage_FramerateLimitDesc" xml:space="preserve"> + <value>3D アプリケーションのフレームレートを制限</value> + </data> + <data name="InputsHotkey_shortcutPrintScreen" xml:space="preserve"> + <value>Open Snipping tool</value> + </data> + <data name="InputsHotkey_shortcutPrintScreenDesc" xml:space="preserve"> + <value>Press this key: Windows + Shift + S</value> + </data> + <data name="AboutPage_Manufacturer" xml:space="preserve"> + <value>Manufacturer</value> + </data> + <data name="AboutPage_Partner" xml:space="preserve"> + <value>Partner</value> + </data> + <data name="AboutPage_ProductName" xml:space="preserve"> + <value>Product name</value> + </data> + <data name="ButtonsPage_ABXY" xml:space="preserve"> + <value>A,B,X,Y</value> + </data> + <data name="ButtonsPage_Back_Grips" xml:space="preserve"> + <value>BACK GRIPS</value> + </data> + <data name="ButtonsPage_Bumpers" xml:space="preserve"> + <value>BUMPERS</value> + </data> + <data name="ButtonsPage_Menu" xml:space="preserve"> + <value>MENU</value> + </data> + <data name="ButtonsPage_OEM" xml:space="preserve"> + <value>OEM</value> + </data> + <data name="ControllerPage_DesktopLayout" xml:space="preserve"> + <value>Desktop layout</value> + </data> + <data name="ControllerPage_DesktopLayoutDefine" xml:space="preserve"> + <value>Define desktop layout</value> + </data> + <data name="ControllerPage_DesktopLayoutDefineController" xml:space="preserve"> + <value>Define controller layout when in desktop mode</value> + </data> + <data name="ControllerPage_DesktopLayoutEdit" xml:space="preserve"> + <value>Edit</value> + </data> + <data name="ControllerPage_DesktopLayoutEnable" xml:space="preserve"> + <value>Enable desktop layout</value> + </data> + <data name="ControllerPage_NonGameControllerLayouts" xml:space="preserve"> + <value>Non-game controller layouts</value> + </data> + <data name="ControllerPage_NoPhysicalControllerAction" xml:space="preserve"> + <value>You might want to click on Connect next to your plugged controller.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDesc" xml:space="preserve"> + <value>You have no physical controller connected. No inputs will be sent to HC or its service.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedAction" xml:space="preserve"> + <value>Please make sure you connected a compatible XInput or DInput device.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedWarning" xml:space="preserve"> + <value>No physical controller detected</value> + </data> + <data name="ControllerPage_NoPhysicalControllerWarning" xml:space="preserve"> + <value>No physical controller connected</value> + </data> + <data name="ControllerPage_NoVirtualControllerAction" xml:space="preserve"> + <value>You might want to start companion service or make sure your virtual controller status is set to: Connected</value> + </data> + <data name="ControllerPage_NoVirtualControllerDesc" xml:space="preserve"> + <value>Your physical controller is hidden, yet you have no virtual controller available. No inputs will be sent to games.</value> + </data> + <data name="ControllerPage_NoVirtualControllerWarning" xml:space="preserve"> + <value>No virtual controller detected</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenAction" xml:space="preserve"> + <value>You might want to unmute your virtual controller or unhide your physical controller.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenDesc" xml:space="preserve"> + <value>Your physical controller is hidden, yet you have muted your virtual controller.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenWarning" xml:space="preserve"> + <value>Physical controller is hidden</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenAction" xml:space="preserve"> + <value>You might want to hide your physical controller or mute your virtual controller.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenDesc" xml:space="preserve"> + <value>Your physical controller is not hidden, yet you have an unmuted virtual controller. You might encounter double inputs in games.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenWarning" xml:space="preserve"> + <value>Physical controller is not hidden</value> + </data> + <data name="ControllerPage_SteamControllerHDRumble" xml:space="preserve"> + <value>HD rumble</value> + </data> + <data name="ControllerPage_SteamControllerHDRumbleDesc" xml:space="preserve"> + <value>Use high-definition rumble engine, at the cost of higher CPU usage</value> + </data> + <data name="ControllerPage_SteamControllerSettings" xml:space="preserve"> + <value>Steam Deck Settings</value> + </data> + <data name="DPadPage_DPad" xml:space="preserve"> + <value>DIRECTIONAL PAD</value> + </data> + <data name="JoystickPage_Joystick_Left" xml:space="preserve"> + <value>LEFT JOYSTICK</value> + </data> + <data name="JoystickPage_Joystick_Left_Buttons" xml:space="preserve"> + <value>LEFT JOYSTICK BUTTONS</value> + </data> + <data name="JoystickPage_Joystick_Right" xml:space="preserve"> + <value>RIGHT JOYSTICK</value> + </data> + <data name="JoystickPage_Joystick_Right_Buttons" xml:space="preserve"> + <value>RIGHT JOYSTICK BUTTONS</value> + </data> + <data name="LayoutPage_ApplyTemplate" xml:space="preserve"> + <value>Apply template</value> + </data> + <data name="LayoutPage_Buttons" xml:space="preserve"> + <value>Buttons</value> + </data> + <data name="LayoutPage_Cancel" xml:space="preserve"> + <value>Cancel</value> + </data> + <data name="LayoutPage_Community" xml:space="preserve"> + <value>COMMUNITY</value> + </data> + <data name="LayoutPage_Confirm" xml:space="preserve"> + <value>Confirm</value> + </data> + <data name="LayoutPage_Dpad" xml:space="preserve"> + <value>Dpad</value> + </data> + <data name="LayoutPage_ExportCurrentController" xml:space="preserve"> + <value>Export for current controller</value> + </data> + <data name="LayoutPage_ExportLayout" xml:space="preserve"> + <value>Export layout</value> + </data> + <data name="LayoutPage_Gyro" xml:space="preserve"> + <value>Gyro</value> + </data> + <data name="LayoutPage_Joysticks" xml:space="preserve"> + <value>Joysticks</value> + </data> + <data name="LayoutPage_LayoutAuthor" xml:space="preserve"> + <value>Layout author</value> + </data> + <data name="LayoutPage_LayoutDesc" xml:space="preserve"> + <value>Layout description</value> + </data> + <data name="LayoutPage_LayoutTitle" xml:space="preserve"> + <value>Layout title</value> + </data> + <data name="LayoutPage_SaveGameInfoLayout" xml:space="preserve"> + <value>Save game information with the layout</value> + </data> + <data name="LayoutPage_ShowCurrentControllerTemplates" xml:space="preserve"> + <value>Show current controller templates only</value> + </data> + <data name="LayoutPage_TemplatePicker" xml:space="preserve"> + <value>Layout template picker</value> + </data> + <data name="LayoutPage_Templates" xml:space="preserve"> + <value>TEMPLATES</value> + </data> + <data name="LayoutPage_Trackpads" xml:space="preserve"> + <value>Trackpads</value> + </data> + <data name="LayoutPage_Triggers" xml:space="preserve"> + <value>Triggers</value> + </data> + <data name="MainWindow_navHotkeys" xml:space="preserve"> + <value>Hotkeys</value> + </data> + <data name="OverlayPage_Millisecond" xml:space="preserve"> + <value>ms</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Disabled" xml:space="preserve"> + <value>Disabled</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Extended" xml:space="preserve"> + <value>Extended</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Full" xml:space="preserve"> + <value>Full</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Minimal" xml:space="preserve"> + <value>Minimal</value> + </data> + <data name="ProfilesPage_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="ProfilesPage_EPP" xml:space="preserve"> + <value>Energy performance preference (EPP)</value> + </data> + <data name="ProfilesPage_EPPBalance" xml:space="preserve"> + <value>CPU/GPU power balance</value> + </data> + <data name="ProfilesPage_EPPDesc" xml:space="preserve"> + <value>Specifies power distribution policy between CPU and GPU</value> + </data> + <data name="ProfilesPage_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="ProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Power limit target</value> + </data> + <data name="Properties.Resources.ControllerPage_Disconnect" xml:space="preserve"> + <value>Disconnect</value> + </data> + <data name="SettingsPage_NativeDisplayOrientation" xml:space="preserve"> + <value>Native display orientation</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDesc" xml:space="preserve"> + <value>Some features depend on knowing the native display orientation to work properly. If this was not detected properly, set your display's orientation to the orientation that matches your controller, then click Detect</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDetect" xml:space="preserve"> + <value>Detect</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationNotSet" xml:space="preserve"> + <value>Not set</value> + </data> + <data name="SettingsPage_ThemeDefault" xml:space="preserve"> + <value>Use system setting</value> + </data> + <data name="TrackPadsPage_Trackpad_Left" xml:space="preserve"> + <value>LEFT TRACKPAD</value> + </data> + <data name="TrackPadsPage_Trackpad_Left_Buttons" xml:space="preserve"> + <value>LEFT TRACKPAD BUTTONS</value> + </data> + <data name="TrackPadsPage_Trackpad_Right" xml:space="preserve"> + <value>RIGHT TRACKPAD</value> + </data> + <data name="TrackPadsPage_Trackpad_Right_Buttons" xml:space="preserve"> + <value>RIGHT TRACKPAD BUTTONS</value> + </data> + <data name="TriggersPage_Trigger_Left" xml:space="preserve"> + <value>LEFT TRIGGER</value> + </data> + <data name="TriggersPage_Trigger_Left_Button" xml:space="preserve"> + <value>LEFT TRIGGER BUTTONS</value> + </data> + <data name="TriggersPage_Trigger_Right" xml:space="preserve"> + <value>RIGHT TRIGGER</value> + </data> + <data name="TriggersPage_Trigger_Right_Button" xml:space="preserve"> + <value>RIGHT TRIGGER BUTTONS</value> + </data> + <data name="QuickProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Power limit target</value> + </data> + <data name="SettingsPage_QuickToolsOptions" xml:space="preserve"> + <value>Quicktools options</value> + </data> + <data name="QuickPerformancePage_FanOverridePercentage" xml:space="preserve"> + <value>%</value> + </data> + <data name="QuickPerformancePage_CPUBoostModeDesc" xml:space="preserve"> + <value>Set current CPU boost mode</value> + </data> + <data name="QuickPerformancePage_CPUBoostMode" xml:space="preserve"> + <value>CPU boost mode</value> + </data> + <data name="QuickPerformancePage_FanOverrideDesc" xml:space="preserve"> + <value>Set the fan duty cycle to user-defined value</value> + </data> + <data name="QuickPerformancePage_FanOverride" xml:space="preserve"> + <value>Fan override</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRateDesc" xml:space="preserve"> + <value>Adjust main display resolution and refresh rate</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRate" xml:space="preserve"> + <value>Display resolution and refresh rate</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopRight" xml:space="preserve"> + <value>Top right</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopLeft" xml:space="preserve"> + <value>Top left</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocation" xml:space="preserve"> + <value>Window location</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomRight" xml:space="preserve"> + <value>Bottom right</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomLeft" xml:space="preserve"> + <value>Bottom left</value> + </data> + <data name="DevicePage_PowerOptions" xml:space="preserve"> + <value>Power options</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationDesc" xml:space="preserve"> + <value>Define quicktools window location</value> + </data> + <data name="SettingsPage_ThirdPartyApps" xml:space="preserve"> + <value>Third-party applications</value> + </data> + <data name="SettingsPage_SensorNone" xml:space="preserve"> + <value>None</value> + </data> + <data name="SettingsPage_QuickToolsBackdrop" xml:space="preserve"> + <value>Quicktools backdrop, none, mica, tabbed, acrylic</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServerDesc" xml:space="preserve"> + <value>Automatically turn RTSS off when companion is closed</value> + </data> + <data name="SettingsPage_HwInfoDesc" xml:space="preserve"> + <value>Automatically turn HWiNFO off when companion is closed</value> + </data> + <data name="SettingsPage_HwInfo" xml:space="preserve"> + <value>HWiNFO</value> + </data> + <data name="QuickProfilesPage_CurrentProfileDefault" xml:space="preserve"> + <value>Default</value> + </data> + <data name="QuickProfilesPage_CurrentProfile" xml:space="preserve"> + <value>Current profile:</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServer" xml:space="preserve"> + <value>RivaTuner Statistics Server</value> + </data> + <data name="SettingsPage_HideWhenLoseFocusDesc" xml:space="preserve"> + <value>Automatically hide quick tools when the user clicks outside of window</value> + </data> + <data name="SettingsPage_HideWhenLoseFocus" xml:space="preserve"> + <value>Hide when loses focus</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B1" xml:space="preserve"> + <value>Cross</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B2" xml:space="preserve"> + <value>Circle</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B3" xml:space="preserve"> + <value>Square</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B4" xml:space="preserve"> + <value>Triangle</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Start" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.HIDmode.DualShock4Controller" xml:space="preserve"> + <value>DualShock 4 コントローラー</value> + </data> + <data name="Enum.HIDmode.NoController" xml:space="preserve"> + <value>コントローラー無効</value> + </data> + <data name="Enum.HIDmode.Xbox360Controller" xml:space="preserve"> + <value>XBOX 360 コントローラー</value> + </data> + <data name="Enum.HIDstatus.Connected" xml:space="preserve"> + <value>接続済み</value> + </data> + <data name="Enum.HIDstatus.Disconnected" xml:space="preserve"> + <value>切断</value> + </data> + <data name="Enum.Input.AutoRollYawSwap" xml:space="preserve"> + <value>Auto Roll Yaw Swap</value> + </data> + <data name="Enum.Input.JoystickCamera" xml:space="preserve"> + <value>ジョイスティックカメラ</value> + </data> + <data name="Enum.Input.JoystickSteering" xml:space="preserve"> + <value>ジョイスティックステアリング</value> + </data> + <data name="Enum.Input.PlayerSpace" xml:space="preserve"> + <value>プレーヤースペース</value> + </data> + <data name="Enum.InputsHotkeyType.HC" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.InputsHotkeyType.Overlay" xml:space="preserve"> + <value>オーバーレイ</value> + </data> + <data name="Enum.InputsHotkeyType.Quicktools" xml:space="preserve"> + <value>クイック ツール</value> + </data> + <data name="Enum.InputsHotkeyType.Windows" xml:space="preserve"> + <value>Windows</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Back" xml:space="preserve"> + <value>View</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="Enum.Output.LeftStick" xml:space="preserve"> + <value>左ジョイスティック</value> + </data> + <data name="Enum.Output.RightStick" xml:space="preserve"> + <value>右ジョイスティック</value> + </data> + <data name="Enum.ProfileErrorCode.Default" xml:space="preserve"> + <value>デフォルトのコントローラー プロファイル</value> + </data> + <data name="Enum.ProfileErrorCode.MissingExecutable" xml:space="preserve"> + <value>実行可能ファイルがありません</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPath" xml:space="preserve"> + <value>アプリケーションへのパスがありません</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPermission" xml:space="preserve"> + <value>コンテンツを変更の権限がありません</value> + </data> + <data name="Enum.ProfileErrorCode.None" xml:space="preserve"> + <value>ここには何も表示されません</value> + </data> + <data name="Enum.ProfileErrorCode.Running" xml:space="preserve"> + <value>このプロファイルは実行されています</value> + </data> + <data name="Enum.QualityOfServiceLevel.Default" xml:space="preserve"> + <value>Default</value> + </data> + <data name="Enum.QualityOfServiceLevel.Eco" xml:space="preserve"> + <value>Eco</value> + </data> + <data name="Enum.QualityOfServiceLevel.High" xml:space="preserve"> + <value>High</value> + </data> + <data name="Enum.ServiceStartMode.Automatic" xml:space="preserve"> + <value>自動</value> + </data> + <data name="Enum.ServiceStartMode.Disabled" xml:space="preserve"> + <value>無効</value> + </data> + <data name="Enum.ServiceStartMode.Manual" xml:space="preserve"> + <value>マニュアル</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Back" xml:space="preserve"> + <value>View</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L1" xml:space="preserve"> + <value>LB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R1" xml:space="preserve"> + <value>RB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Special" xml:space="preserve"> + <value>Guide</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="JoystickCameraDesc" xml:space="preserve"> + <value>単純なジョイスティックとして動作します。これは従来のジョイスティック アプリケーションを対象としています</value> + </data> + <data name="JoystickSteeringDesc" xml:space="preserve"> + <value>ステアリング ホイールやレーシング ゲームの制御に最適化されたジョイスティックとして動作します</value> + </data> + <data name="PlayerSpaceDesc" xml:space="preserve"> + <value>一人称または三人称カメラの制御に最適化されたジョイスティックとして動作します</value> + </data> + <data name="Enum.InputsHotkeyType.Handheld" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.SteamDeck.ButtonFlags.OEM1" xml:space="preserve"> + <value>オプション</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Special" xml:space="preserve"> + <value>STEAM</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM3" xml:space="preserve"> + <value>KB</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM1" xml:space="preserve"> + <value>Win</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM2" xml:space="preserve"> + <value>Esc</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM3" xml:space="preserve"> + <value>M1 / M2</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.AlwaysOn" xml:space="preserve"> + <value>Always On</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Back" xml:space="preserve"> + <value>Back</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadDown" xml:space="preserve"> + <value>DPad Down</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadLeft" xml:space="preserve"> + <value>DPad Left</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadRight" xml:space="preserve"> + <value>DPad Right</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadUp" xml:space="preserve"> + <value>DPad Up</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftShoulder" xml:space="preserve"> + <value>Left Shoulder</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftThumb" xml:space="preserve"> + <value>Left Thumb</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftTrigger" xml:space="preserve"> + <value>Left Trigger</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightShoulder" xml:space="preserve"> + <value>Right Shoulder</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightThumb" xml:space="preserve"> + <value>Right Thumb</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightTrigger" xml:space="preserve"> + <value>Right Trigger</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Start" xml:space="preserve"> + <value>Start</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.InputsHotkeyType.Custom" xml:space="preserve"> + <value>Custom</value> + </data> + <data name="Enum.InputsHotkeyType.Device" xml:space="preserve"> + <value>Device</value> + </data> + <data name="Enum.MotionInput.AutoRollYawSwap" xml:space="preserve"> + <value>Auto Roll Yaw Swap</value> + </data> + <data name="Enum.MotionInput.JoystickCamera" xml:space="preserve"> + <value>Joystick Camera</value> + </data> + <data name="Enum.MotionInput.JoystickSteering" xml:space="preserve"> + <value>Joystick Steering</value> + </data> + <data name="Enum.MotionInput.PlayerSpace" xml:space="preserve"> + <value>Player Space</value> + </data> + <data name="Enum.MotionOutput.LeftStick" xml:space="preserve"> + <value>Left Stick</value> + </data> + <data name="Enum.MotionOutput.RightStick" xml:space="preserve"> + <value>Right Stick</value> + </data> + <data name="Enum.ProfileErrorCode.IsRunning" xml:space="preserve"> + <value>Oops. This profile seems to be running. Some options requiring an executable may be disabled.</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM1" xml:space="preserve"> + <value>Command center</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM2" xml:space="preserve"> + <value>Armory crate</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftThumb" xml:space="preserve"> + <value>Left Thumb</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightThumb" xml:space="preserve"> + <value>Right Thumb</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B5" xml:space="preserve"> + <value>B5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B6" xml:space="preserve"> + <value>B6</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B7" xml:space="preserve"> + <value>B7</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B8" xml:space="preserve"> + <value>B8</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadDown" xml:space="preserve"> + <value>DPad Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadLeft" xml:space="preserve"> + <value>DPad Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadRight" xml:space="preserve"> + <value>PPad Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadUp" xml:space="preserve"> + <value>DPad Up</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L3" xml:space="preserve"> + <value>L3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L4" xml:space="preserve"> + <value>L4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L5" xml:space="preserve"> + <value>L5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumb" xml:space="preserve"> + <value>Left Thumb</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbDown" xml:space="preserve"> + <value>Left Thumb Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbLeft" xml:space="preserve"> + <value>Left Thumb Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbRight" xml:space="preserve"> + <value>Left Thumb Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbUp" xml:space="preserve"> + <value>Left Thumb Up</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM1" xml:space="preserve"> + <value>OEM1</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM2" xml:space="preserve"> + <value>OEM2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM3" xml:space="preserve"> + <value>OEM3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R3" xml:space="preserve"> + <value>R3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R4" xml:space="preserve"> + <value>R4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R5" xml:space="preserve"> + <value>R5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumb" xml:space="preserve"> + <value>Right Thumb</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbDown" xml:space="preserve"> + <value>Right Thumb Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbLeft" xml:space="preserve"> + <value>Right Thumb Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbRight" xml:space="preserve"> + <value>Right Thumb Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbUp" xml:space="preserve"> + <value>Right Thumb Up</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B1" xml:space="preserve"> + <value>Cross</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B2" xml:space="preserve"> + <value>Circle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B3" xml:space="preserve"> + <value>Square</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B4" xml:space="preserve"> + <value>Triangle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Start" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.KeyFlags.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.KeyFlags.Alt" xml:space="preserve"> + <value>Alt</value> + </data> + <data name="Enum.KeyFlags.Apostrophe" xml:space="preserve"> + <value>Apostrophe</value> + </data> + <data name="Enum.KeyFlags.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.KeyFlags.Backslash" xml:space="preserve"> + <value>Backslash</value> + </data> + <data name="Enum.KeyFlags.Backspace" xml:space="preserve"> + <value>Backspace</value> + </data> + <data name="Enum.KeyFlags.C" xml:space="preserve"> + <value>C</value> + </data> + <data name="Enum.KeyFlags.Comma" xml:space="preserve"> + <value>Comma</value> + </data> + <data name="Enum.KeyFlags.Control" xml:space="preserve"> + <value>Control</value> + </data> + <data name="Enum.KeyFlags.D" xml:space="preserve"> + <value>D</value> + </data> + <data name="Enum.KeyFlags.Delete" xml:space="preserve"> + <value>Delete</value> + </data> + <data name="Enum.KeyFlags.E" xml:space="preserve"> + <value>E</value> + </data> + <data name="Enum.KeyFlags.End" xml:space="preserve"> + <value>End</value> + </data> + <data name="Enum.KeyFlags.Enter" xml:space="preserve"> + <value>Enter</value> + </data> + <data name="Enum.KeyFlags.Equal" xml:space="preserve"> + <value>Equal</value> + </data> + <data name="Enum.KeyFlags.Escape" xml:space="preserve"> + <value>Escape</value> + </data> + <data name="Enum.KeyFlags.F" xml:space="preserve"> + <value>F</value> + </data> + <data name="Enum.KeyFlags.F1" xml:space="preserve"> + <value>F1</value> + </data> + <data name="Enum.KeyFlags.F10" xml:space="preserve"> + <value>F10</value> + </data> + <data name="Enum.KeyFlags.F11" xml:space="preserve"> + <value>F11</value> + </data> + <data name="Enum.KeyFlags.F12" xml:space="preserve"> + <value>F12</value> + </data> + <data name="Enum.KeyFlags.F2" xml:space="preserve"> + <value>F2</value> + </data> + <data name="Enum.KeyFlags.F3" xml:space="preserve"> + <value>F3</value> + </data> + <data name="Enum.KeyFlags.F4" xml:space="preserve"> + <value>F4</value> + </data> + <data name="Enum.KeyFlags.F5" xml:space="preserve"> + <value>F5</value> + </data> + <data name="Enum.KeyFlags.F6" xml:space="preserve"> + <value>F6</value> + </data> + <data name="Enum.KeyFlags.F7" xml:space="preserve"> + <value>F7</value> + </data> + <data name="Enum.KeyFlags.F8" xml:space="preserve"> + <value>F8</value> + </data> + <data name="Enum.KeyFlags.F9" xml:space="preserve"> + <value>F9</value> + </data> + <data name="Enum.KeyFlags.G" xml:space="preserve"> + <value>G</value> + </data> + <data name="Enum.KeyFlags.Grave" xml:space="preserve"> + <value>Grave</value> + </data> + <data name="Enum.KeyFlags.H" xml:space="preserve"> + <value>H</value> + </data> + <data name="Enum.KeyFlags.Home" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.KeyFlags.I" xml:space="preserve"> + <value>I</value> + </data> + <data name="Enum.KeyFlags.Insert" xml:space="preserve"> + <value>Insert</value> + </data> + <data name="Enum.KeyFlags.J" xml:space="preserve"> + <value>J</value> + </data> + <data name="Enum.KeyFlags.K" xml:space="preserve"> + <value>K</value> + </data> + <data name="Enum.KeyFlags.L" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.KeyFlags.M" xml:space="preserve"> + <value>M</value> + </data> + <data name="Enum.KeyFlags.Minus" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.KeyFlags.N" xml:space="preserve"> + <value>N</value> + </data> + <data name="Enum.KeyFlags.O" xml:space="preserve"> + <value>O</value> + </data> + <data name="Enum.KeyFlags.P" xml:space="preserve"> + <value>P</value> + </data> + <data name="Enum.KeyFlags.Pause" xml:space="preserve"> + <value>Pause</value> + </data> + <data name="Enum.KeyFlags.Period" xml:space="preserve"> + <value>Period</value> + </data> + <data name="Enum.KeyFlags.Q" xml:space="preserve"> + <value>Q</value> + </data> + <data name="Enum.KeyFlags.R" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.KeyFlags.S" xml:space="preserve"> + <value>S</value> + </data> + <data name="Enum.KeyFlags.Semicolon" xml:space="preserve"> + <value>Semicolon</value> + </data> + <data name="Enum.KeyFlags.Shift" xml:space="preserve"> + <value>Shift</value> + </data> + <data name="Enum.KeyFlags.Slash" xml:space="preserve"> + <value>Slash</value> + </data> + <data name="Enum.KeyFlags.Space" xml:space="preserve"> + <value>Space</value> + </data> + <data name="Enum.KeyFlags.T" xml:space="preserve"> + <value>T</value> + </data> + <data name="Enum.KeyFlags.Tab" xml:space="preserve"> + <value>Tab</value> + </data> + <data name="Enum.KeyFlags.U" xml:space="preserve"> + <value>U</value> + </data> + <data name="Enum.KeyFlags.V" xml:space="preserve"> + <value>V</value> + </data> + <data name="Enum.KeyFlags.W" xml:space="preserve"> + <value>W</value> + </data> + <data name="Enum.KeyFlags.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.KeyFlags.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.KeyFlags.Z" xml:space="preserve"> + <value>Z</value> + </data> + <data name="Enum.MotionOuput.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.MotionOuput.MoveCursor" xml:space="preserve"> + <value>MoveCursor</value> + </data> + <data name="Enum.MotionOuput.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="Enum.MotionOuput.ScrollWheel" xml:space="preserve"> + <value>ScrollWheel</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>ZL</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>ZR</value> + </data> + <data name="Enum.ProController.ButtonFlags.B1" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.ProController.ButtonFlags.B2" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.ProController.ButtonFlags.B3" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.ProController.ButtonFlags.B4" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.ProController.ButtonFlags.Back" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.ProController.ButtonFlags.L1" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.ProController.ButtonFlags.R1" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special2" xml:space="preserve"> + <value>Capture</value> + </data> + <data name="Enum.ProController.ButtonFlags.Start" xml:space="preserve"> + <value>Plus</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderClosePrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseText" xml:space="preserve"> + <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="ProfilesPage_Wrapper_Disabled" xml:space="preserve"> + <value>Disabled</value> + </data> + <data name="ProfilesPage_Wrapper_Injection" xml:space="preserve"> + <value>Injection (recommended)</value> + </data> + <data name="ProfilesPage_Wrapper_Redirection" xml:space="preserve"> + <value>Redirection</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrder" xml:space="preserve"> + <value>Improve virtual controller detection</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderDesc" xml:space="preserve"> + <value>Forces the virtual controller to be detected as first controller during Windows start. Enable this if your app/game doesn't detect your inputs (requires device reboot).</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderText" xml:space="preserve"> + <value>Your device must be restarted in order for the changes to take effect. Would you like to restart now?</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderTitle" xml:space="preserve"> + <value>Restart required</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencySecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyText" xml:space="preserve"> + <value>Disabling this setting will also disable "Improve virtual controller detection". Do you want to continue?</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="ProfilesPage_ControllerLayoutDesc" xml:space="preserve"> + <value>Change the virtual controller layout</value> + </data> + <data name="AutoRollYawSwapDesc" xml:space="preserve"> + <value>This input will operate as a simple joystick. Ideal for laptop and clamshell type handhelds, automatic yaw roll swap based on how device is being held (90 or 180 degree open).</value> + </data> + <data name="Controller_Connect" xml:space="preserve"> + <value>Connect</value> + </data> + <data name="Controller_Disconnect" xml:space="preserve"> + <value>Disconnect</value> + </data> + <data name="Controller_Hide" xml:space="preserve"> + <value>Hide</value> + </data> + <data name="Controller_Unhide" xml:space="preserve"> + <value>Unhide</value> + </data> + <data name="Controller_Virtual" xml:space="preserve"> + <value>Virtual </value> + </data> + <data name="MainWindow_Back" xml:space="preserve"> + <value>Back</value> + </data> + <data name="MainWindow_Exit" xml:space="preserve"> + <value>Exit</value> + </data> + <data name="MainWindow_MainWindow" xml:space="preserve"> + <value>Main Window</value> + </data> + <data name="MainWindow_Navigate" xml:space="preserve"> + <value>Navigate</value> + </data> + <data name="MainWindow_QuickTools" xml:space="preserve"> + <value>Quick Tools</value> + </data> + <data name="MainWindow_Select" xml:space="preserve"> + <value>Select</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_External" xml:space="preserve"> + <value>External</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate1" xml:space="preserve"> + <value>Are you sure you want to apply this template? All layout settings will be overridden.</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate2" xml:space="preserve"> + <value>You can't undo this action. Previously applied template: {0}</value> + </data> + <data name="LayoutPage_SetAsDefault" xml:space="preserve"> + <value>Make this the default layout</value> + </data> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </root> \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.pt-BR.resx b/HandheldCompanion/Properties/Resources.pt-BR.resx index f8d92fb94..642ddde38 100644 --- a/HandheldCompanion/Properties/Resources.pt-BR.resx +++ b/HandheldCompanion/Properties/Resources.pt-BR.resx @@ -1,3 +1,4 @@ +<<<<<<< HEAD <?xml version="1.0" encoding="utf-8"?> <root> <!-- @@ -2357,4 +2358,2332 @@ com a entrada de movimento ligada, use o(s) botão(ões) selecionado(s) para des <data name="IController_ControllerIndex" xml:space="preserve"> <value>Gamepad: {0}</value> </data> +======= +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="AboutPage_About" xml:space="preserve"> + <value>Sobre</value> + </data> + <data name="AboutPage_AboutDescription" xml:space="preserve"> + <value>Uma combinação de serviço Windows e aplicação otimizada com interface de toque para melhorar a experiência de seu computador de jogo portátil de mão. Os recursos incluem: controle de movimento conhecido também como controle giroscópico, simulação de controlador virtual, sobreposição de acesso rápido para ferramentas, touchpads virtuais, modelo 3D de controlador, sistema de perfis de configurações baseado na aplicação ativa. Handheld Companion depende do driver do ViGEmBus e bibliotecas ViGEmClient como também o filtro de driver do HidHide em modo kernel. Algoritmos de controle de movimentos são baseados no trabalho de Jibbsmart e informação disponível em GyroWiki.</value> + </data> + <data name="AboutPage_Accelerometer" xml:space="preserve"> + <value>Acelerômetro</value> + </data> + <data name="AboutPage_Author" xml:space="preserve"> + <value>Autor</value> + </data> + <data name="AboutPage_Contributors" xml:space="preserve"> + <value>Colaboradores</value> + </data> + <data name="AboutPage_Description" xml:space="preserve"> + <value>Descrição</value> + </data> + <data name="AboutPage_Donate" xml:space="preserve"> + <value>Doar</value> + </data> + <data name="AboutPage_Gyrometer" xml:space="preserve"> + <value>Girometro</value> + </data> + <data name="AboutPage_Inclinometer" xml:space="preserve"> + <value>Inclinometro</value> + </data> + <data name="AboutPage_NotApplicable" xml:space="preserve"> + <value>N/A</value> + </data> + <data name="AboutPage_RelatedLinks" xml:space="preserve"> + <value>Links Relacionados</value> + </data> + <data name="AboutPage_SensorExternal" xml:space="preserve"> + <value>Externo</value> + </data> + <data name="AboutPage_SensorInternal" xml:space="preserve"> + <value>Interno</value> + </data> + <data name="AboutPage_SensorName" xml:space="preserve"> + <value>Nome do sensor</value> + </data> + <data name="AboutPage_SensorSpecification" xml:space="preserve"> + <value>Especificação do Sensor</value> + </data> + <data name="AboutPage_Service" xml:space="preserve"> + <value>Serviço</value> + </data> + <data name="AboutPage_SourceCode" xml:space="preserve"> + <value>Código Fonte</value> + </data> + <data name="AboutPage_Version" xml:space="preserve"> + <value>Versão</value> + </data> + <data name="AboutPage_Wiki" xml:space="preserve"> + <value>Wiki</value> + </data> + <data name="Administrator" xml:space="preserve"> + <value>Administrador</value> + </data> + <data name="ControllerPage_CloakDevice" xml:space="preserve"> + <value>Ocultar controle ao conectar</value> + </data> + <data name="ControllerPage_CloakDeviceDesc" xml:space="preserve"> + <value>Ocultar controle físico quando conectado</value> + </data> + <data name="ControllerPage_Connect" xml:space="preserve"> + <value>Conectar</value> + </data> + <data name="ControllerPage_Controller" xml:space="preserve"> + <value>Controle</value> + </data> + <data name="ControllerPage_DeviceCloaking" xml:space="preserve"> + <value>Ocultamento de controle</value> + </data> + <data name="ControllerPage_DeviceSettings" xml:space="preserve"> + <value>configurações de controle</value> + </data> + <data name="ControllerPage_Disconnect" xml:space="preserve"> + <value>Desconectar</value> + </data> + <data name="ControllerPage_InputDevices" xml:space="preserve"> + <value>Dispositivos de entrada</value> + </data> + <data name="ControllerPage_UncloakOnClose" xml:space="preserve"> + <value>Reexibir controle ao encerrar</value> + </data> + <data name="ControllerPage_UncloakOnCloseDesc" xml:space="preserve"> + <value>Restaurar visibilidade de todos os controles físicos quando a aplicação é encerrada</value> + </data> + <data name="ControllerPage_VibrationStrength" xml:space="preserve"> + <value>Força da vibração</value> + </data> + <data name="ControllerPage_VibrationStrengthExpl" xml:space="preserve"> + <value>Alterar a força da vibração do controle</value> + </data> + <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> + <data name="icon" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\icon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> + </data> + <data name="InputsHotkey_decreaseTDP" xml:space="preserve"> + <value>Diminuir o valor de TDP</value> + </data> + <data name="InputsHotkey_decreaseTDPDesc" xml:space="preserve"> + <value>Diminuir o TDP (do inglês, thermal design power ou potência térmica de projeto) do perfil aplicado ou do sistema em 1 (um) watt</value> + </data> + <data name="InputsHotkey_fallbackInput" xml:space="preserve"> + <value>Pressione para definir gatilho</value> + </data> + <data name="InputsHotkey_fallbackOutput" xml:space="preserve"> + <value>Pressione para definir saída de teclado</value> + </data> + <data name="InputsHotkey_increaseTDP" xml:space="preserve"> + <value>Aumentar o valor de TDP</value> + </data> + <data name="InputsHotkey_increaseTDPDesc" xml:space="preserve"> + <value>Aumentar o TDP (do inglês, thermal design power ou potência térmica de projeto) do perfil aplicado ou do sistema em 1 (um) watt</value> + </data> + <data name="InputsHotkey_overlayGamepad" xml:space="preserve"> + <value>Exibir controle 3D</value> + </data> + <data name="InputsHotkey_overlayGamepadDesc" xml:space="preserve"> + <value>Alterar a tecla de atalho 3D pressionando um botão ou uma tecla especial</value> + </data> + <data name="InputsHotkey_overlayTrackpads" xml:space="preserve"> + <value>Exibir trackpads virtuais</value> + </data> + <data name="InputsHotkey_overlayTrackpadsDesc" xml:space="preserve"> + <value>Alterar a tecla de atalho pressionando um botão ou uma tecla especial</value> + </data> + <data name="InputsHotkey_quickTools" xml:space="preserve"> + <value>Invocar a janela de ferramentas rápidas</value> + </data> + <data name="InputsHotkey_quickToolsDesc" xml:space="preserve"> + <value>Alterar a tecla de atalho pressionando um botão ou uma tecla especial</value> + </data> + <data name="InputsHotkey_shortcutCustom" xml:space="preserve"> + <value>Atalho personalizado</value> + </data> + <data name="InputsHotkey_shortcutCustomDesc" xml:space="preserve"> + <value>Alterar a tecla de atalho pressionando um botão ou uma tecla especial</value> + </data> + <data name="InputsHotkey_shortcutDesktop" xml:space="preserve"> + <value>Exibir e ocultar a área de trabalho</value> + </data> + <data name="InputsHotkey_shortcutDesktopDesc" xml:space="preserve"> + <value>Pressione esta combinação de teclas: Windows + D</value> + </data> + <data name="InputsHotkey_shortcutESC" xml:space="preserve"> + <value>ESC</value> + </data> + <data name="InputsHotkey_shortcutESCDesc" xml:space="preserve"> + <value>Pressione esta tecla: ESC</value> + </data> + <data name="InputsHotkey_shortcutExpand" xml:space="preserve"> + <value>Alterna entre modo janela e tela cheia</value> + </data> + <data name="InputsHotkey_shortcutExpandDesc" xml:space="preserve"> + <value>Pressione esta combinação de teclas: Alt + Enter</value> + </data> + <data name="InputsHotkey_shortcutGuide" xml:space="preserve"> + <value>Guia ou Botão PS</value> + </data> + <data name="InputsHotkey_shortcutGuideDesc" xml:space="preserve"> + <value>Simular a entrada de um botão Xbox Guia ou botão Sony PS</value> + </data> + <data name="InputsHotkey_shortcutKeyboard" xml:space="preserve"> + <value>Exibir o teclado em tela de toque</value> + </data> + <data name="InputsHotkey_shortcutKeyboardDesc" xml:space="preserve"> + <value>Alterar a tecla de atalho pressionando um botão ou uma tecla especial</value> + </data> + <data name="InputsHotkey_shortcutKillApp" xml:space="preserve"> + <value>Forçar encerramento da aplicação</value> + </data> + <data name="InputsHotkey_shortcutKillAppDesc" xml:space="preserve"> + <value>Alterar a tecla de atalho pressionando um botão ou uma tecla especial</value> + </data> + <data name="InputsHotkey_shortcutMainwindow" xml:space="preserve"> + <value>Exibir e ocultar a janela principal</value> + </data> + <data name="InputsHotkey_shortcutMainwindowDesc" xml:space="preserve"> + <value>Alterar a tecla de atalho pressionando um botão ou uma tecla especial</value> + </data> + <data name="InputsHotkey_shortcutTaskManager" xml:space="preserve"> + <value>Abrir o Gerenciador de Tarefas</value> + </data> + <data name="InputsHotkey_shortcutTaskManagerDesc" xml:space="preserve"> + <value>Pressione esta combinação de teclas: Ctrl + Shift + Esc</value> + </data> + <data name="InputsHotkey_shortcutTaskview" xml:space="preserve"> + <value>Abrir Visão de Tarefas</value> + </data> + <data name="InputsHotkey_shortcutTaskviewDesc" xml:space="preserve"> + <value>Pressionar esta combinação de teclas: Windows + Tab</value> + </data> + <data name="InputsHotkey_suspendResumeTask" xml:space="preserve"> + <value>Alternar Suspensão de tarefa</value> + </data> + <data name="InputsHotkey_suspendResumeTaskDesc" xml:space="preserve"> + <value>Suspender or resumir a aplicação em primeiro plano</value> + </data> + <data name="MainWindow_HandheldCompanion" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="MainWindow_navAbout" xml:space="preserve"> + <value>Sobre</value> + </data> + <data name="MainWindow_navController" xml:space="preserve"> + <value>Controle</value> + </data> + <data name="MainWindow_navOverlay" xml:space="preserve"> + <value>Sobreposição</value> + </data> + <data name="MainWindow_navProfiles" xml:space="preserve"> + <value>Perfis</value> + </data> + <data name="MainWindow_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="MainWindow_Settings" xml:space="preserve"> + <value>Configurações</value> + </data> + <data name="OverlayPage_8BitDoLite2Controller" xml:space="preserve"> + <value>8BitDo Lite 2</value> + </data> + <data name="OverlayPage_Alignment" xml:space="preserve"> + <value>Alinhamento</value> + </data> + <data name="OverlayPage_AlignmentDesc" xml:space="preserve"> + <value>Alterar o alinhamento da sobreposição de controle 3D</value> + </data> + <data name="OverlayPage_AlignmentTrackpadDesc" xml:space="preserve"> + <value>Alterar o alinhamento da sobreposição de trackpads</value> + </data> + <data name="OverlayPage_AlwaysOnTop" xml:space="preserve"> + <value>Sempre no topo</value> + </data> + <data name="OverlayPage_AlwaysOnTopDesc" xml:space="preserve"> + <value>Quando ativado, a sobreposição do controle 3D estará sempre no topo de outras janelas</value> + </data> + <data name="OverlayPage_BackButton" xml:space="preserve"> + <value>Back</value> + </data> + <data name="OverlayPage_CameraAngle" xml:space="preserve"> + <value>Frente para câmera</value> + </data> + <data name="OverlayPage_CameraAngleDesc" xml:space="preserve"> + <value>Alterar o comportamento do modelo de controle 3D em sobreposição para ficar de frente para a câmera</value> + </data> + <data name="OverlayPage_CameraAnglePitch" xml:space="preserve"> + <value>Eixo lateral (arfagem) estacionário</value> + </data> + <data name="OverlayPage_CameraAnglePitchDesc" xml:space="preserve"> + <value>Alterar o ângulo, por grau</value> + </data> + <data name="OverlayPage_Color" xml:space="preserve"> + <value>Cor de fundo</value> + </data> + <data name="OverlayPage_ColorDesc" xml:space="preserve"> + <value>Alterar a cor de fundo da sobreposição do controle 3D</value> + </data> + <data name="OverlayPage_ControllerOptions" xml:space="preserve"> + <value>Opções de controle</value> + </data> + <data name="OverlayPage_DualSenseController" xml:space="preserve"> + <value>PlayStation DualSense</value> + </data> + <data name="OverlayPage_EmulatedController" xml:space="preserve"> + <value>Controle emulado</value> + </data> + <data name="OverlayPage_FaceCamera" xml:space="preserve"> + <value>Frente para câmera</value> + </data> + <data name="OverlayPage_FaceCameraDesc" xml:space="preserve"> + <value>O modelo 3D vagarosamente rotaciona para encarar a câmera como posição padrão</value> + </data> + <data name="OverlayPage_Listening" xml:space="preserve"> + <value>Escutando...</value> + </data> + <data name="OverlayPage_MachenikeHG510Controller" xml:space="preserve"> + <value>MACHENIKE HG510</value> + </data> + <data name="OverlayPage_MainTrigger" xml:space="preserve"> + <value>Gatilho principal</value> + </data> + <data name="OverlayPage_MainTriggerDesc" xml:space="preserve"> + <value>Alterar o gatilho principal da sobreposição de controle 3D</value> + </data> + <data name="OverlayPage_MotionActivated" xml:space="preserve"> + <value>Movimento</value> + </data> + <data name="OverlayPage_MotionActivatedDesc" xml:space="preserve"> + <value>O modelo se movimentará de acordo com os movimentos do usuário baseado em informações do sensor</value> + </data> + <data name="OverlayPage_N64Controller" xml:space="preserve"> + <value>N64</value> + </data> + <data name="OverlayPage_OEMController" xml:space="preserve"> + <value>Controle OEM (Original do fabricante)</value> + </data> + <data name="OverlayPage_Opacity" xml:space="preserve"> + <value>Opacidade</value> + </data> + <data name="OverlayPage_OpacityControllerDesc" xml:space="preserve"> + <value>Alterar a opacidade da sobreposição de controle 3D</value> + </data> + <data name="OverlayPage_OpacityTrackpadDesc" xml:space="preserve"> + <value>Alterar a opacidade da sobreposição de trackpads</value> + </data> + <data name="OverlayPage_Overlay" xml:space="preserve"> + <value>Sobreposição</value> + </data> + <data name="OverlayPage_OverlayModel" xml:space="preserve"> + <value>Modelo de sobreposição</value> + </data> + <data name="OverlayPage_OverlayModelDesc" xml:space="preserve"> + <value>Alterar o modelo usado na sobreposição de controle 3D</value> + </data> + <data name="OverlayPage_OverlayPreview" xml:space="preserve"> + <value>Prévia de sobreposição</value> + </data> + <data name="OverlayPage_RenderSettings" xml:space="preserve"> + <value>Configurações de renderização</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasing" xml:space="preserve"> + <value>Antisserrilhamento</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasingDesc" xml:space="preserve"> + <value>Alterar a situação de antisserrilhamento (suavização de bordas) da sobreposição</value> + </data> + <data name="OverlayPage_RenderSettingsDesc" xml:space="preserve"> + <value>Alterar as configurações de renderização do seu modelo de controle 3D estacionário</value> + </data> + <data name="OverlayPage_RenderSettingsFramerate" xml:space="preserve"> + <value>Frequência de atualização</value> + </data> + <data name="OverlayPage_RenderSettingsFramerateDesc" xml:space="preserve"> + <value>Alterar a frequência de atualizações da renderização, em atualizações por segundo</value> + </data> + <data name="OverlayPage_Size" xml:space="preserve"> + <value>Tamanho</value> + </data> + <data name="OverlayPage_SizeDesc" xml:space="preserve"> + <value>Alterar o tamanho da sobreposição de controle 3D</value> + </data> + <data name="OverlayPage_SizeOverlayDesc" xml:space="preserve"> + <value>Alterar o tamanho da sobreposição dos trackpads</value> + </data> + <data name="OverlayPage_StartButton" xml:space="preserve"> + <value>Start</value> + </data> + <data name="OverlayPage_ToyController" xml:space="preserve"> + <value>Controle Fisher-Price</value> + </data> + <data name="OverlayPage_TrackpadsOptions" xml:space="preserve"> + <value>Opções dos trackpads</value> + </data> + <data name="OverlayPage_XboxOneController" xml:space="preserve"> + <value>Xbox One</value> + </data> + <data name="OverlayPage_ZDOPlusController" xml:space="preserve"> + <value>ZD O+</value> + </data> + <data name="Overlay_Overlay" xml:space="preserve"> + <value>Sobreposição</value> + </data> + <data name="ProcessEx_processResume" xml:space="preserve"> + <value>Resumir</value> + </data> + <data name="ProcessEx_processSuspend" xml:space="preserve"> + <value>Suspender</value> + </data> + <data name="SettingsMode0_AdditionalSettings" xml:space="preserve"> + <value>Configurações adicionais</value> + </data> + <data name="SettingsMode0_AimingDownSights" xml:space="preserve"> + <value>Multiplicador de movimento de mirar</value> + </data> + <data name="SettingsMode0_AimingDownSightsActivation" xml:space="preserve"> + <value>Botão de ativação</value> + </data> + <data name="SettingsMode0_AimingDownSightsDesc" xml:space="preserve"> + <value>Um multiplicador de sensibilidade adicional de movimento quando utilizando miras através do uso de um botão de ativação</value> + </data> + <data name="SettingsMode0_AimingDownSightsMultiplier" xml:space="preserve"> + <value>Valor de multiplicador</value> + </data> + <data name="SettingsMode0_CameraOptions" xml:space="preserve"> + <value>Opções de câmera</value> + </data> + <data name="SettingsMode0_CustomResponseCurve" xml:space="preserve"> + <value>Curva de resposta personalizada</value> + </data> + <data name="SettingsMode0_CustomResponseCurveGameOutput" xml:space="preserve"> + <value>Saída enviada para o jogo</value> + </data> + <data name="SettingsMode0_CustomResponseIntensity" xml:space="preserve"> + <value>Intensidade de movimentação</value> + </data> + <data name="SettingsMode0_CustomResponsePresetAgressive" xml:space="preserve"> + <value>Agressiva</value> + </data> + <data name="SettingsMode0_CustomResponsePresetDefault" xml:space="preserve"> + <value>Padrão</value> + </data> + <data name="SettingsMode0_CustomResponsePresetOptions" xml:space="preserve"> + <value>Opções predefinidas</value> + </data> + <data name="SettingsMode0_CustomResponsePresetPrecise" xml:space="preserve"> + <value>Precisa</value> + </data> + <data name="SettingsMode0_FlickDuration" xml:space="preserve"> + <value>Duração da virada</value> + </data> + <data name="SettingsMode0_FlickDurationDesc" xml:space="preserve"> + <value>Alterar a duração da virada, calibrar a virada em 180 graus, em milissegundos</value> + </data> + <data name="SettingsMode0_FlickStick" xml:space="preserve"> + <value>Analógio de virada (experimental)</value> + </data> + <data name="SettingsMode0_FlickStickDesc" xml:space="preserve"> + <value>Apontar a câmera na direção do analógico de virada (direito), rotacionar a câmera puramente no plano horizontal por rotação</value> + </data> + <data name="SettingsMode0_FlickStickEnable" xml:space="preserve"> + <value>Habilitar analógico de virada</value> + </data> + <data name="SettingsMode0_Sensitivity" xml:space="preserve"> + <value>Sensibilidade</value> + </data> + <data name="SettingsMode0_SensitivityDesc" xml:space="preserve"> + <value>Alterar a sensibilidade de movimento do eixo horizontal e vertical</value> + </data> + <data name="SettingsMode0_SensitivityX" xml:space="preserve"> + <value>Sensibilidade X</value> + </data> + <data name="SettingsMode0_SensitivityXDesc" xml:space="preserve"> + <value>Alterar a sensibilidade de movimento para o eixo horizontal</value> + </data> + <data name="SettingsMode0_SensitivityY" xml:space="preserve"> + <value>Sensibilidade Y</value> + </data> + <data name="SettingsMode0_SensitivityYDesc" xml:space="preserve"> + <value>Alterar a sensibilidade de movimento para o eixo vertical</value> + </data> + <data name="SettingsMode0_StickSensitivtity" xml:space="preserve"> + <value>Sensibilidade do analógico</value> + </data> + <data name="SettingsMode0_StickSensitivtityDesc" xml:space="preserve"> + <value>Alterar a taxa de rotação</value> + </data> + <data name="SettingsMode1_AdditionalSettings" xml:space="preserve"> + <value>Configurações adicionais</value> + </data> + <data name="SettingsMode1_Deadzone" xml:space="preserve"> + <value>Zona morta</value> + </data> + <data name="SettingsMode1_DeadzoneDesc" xml:space="preserve"> + <value>Altera a zona morta da direção, em graus. Melhora a direção reta</value> + </data> + <data name="SettingsMode1_JoystickGameInput" xml:space="preserve"> + <value>Entrada de jogo Joystick</value> + </data> + <data name="SettingsMode1_JoystickSteering" xml:space="preserve"> + <value>Joystick de direção</value> + </data> + <data name="SettingsMode1_JoystickSteeringOptions" xml:space="preserve"> + <value>Opções de joystick de direção</value> + </data> + <data name="SettingsMode1_JoystickSteeringPreview" xml:space="preserve"> + <value>Prévia de joystick de direção</value> + </data> + <data name="SettingsMode1_MaxSteeringAngle" xml:space="preserve"> + <value>Ângulo máximo de direção</value> + </data> + <data name="SettingsMode1_MaxSteeringAngleDesc" xml:space="preserve"> + <value>Alterar o valor de ângulo máximo de direção, em graus</value> + </data> + <data name="SettingsMode1_SteeringLinearity" xml:space="preserve"> + <value>Linearidade de direção</value> + </data> + <data name="SettingsMode1_SteeringLinearityDesc" xml:space="preserve"> + <value>Mapeamento entre entrada e direção. Valores mais baixos fornecem mais precisão próximos do travamento total do volante, mas menos precisão perto do centro. Valores altos fornecem mais precisão perto do centro, mas menos precisão perto do travamento total do volante. Valor 1.0 é um mapeamento linear</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplier" xml:space="preserve"> + <value>Multiplicador de acelerômetro</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplierDesc" xml:space="preserve"> + <value>Alterar o valor de acelerômetro relatado pelo sistema</value> + </data> + <data name="ProfilesPage_AdditionalSettings" xml:space="preserve"> + <value>Configurações adicionais</value> + </data> + <data name="ProfilesPage_AntiDeadzone" xml:space="preserve"> + <value>Anti-zona morta</value> + </data> + <data name="ProfilesPage_AntiDeadzoneDesc" xml:space="preserve"> + <value>Alterar a anti-zona morta de jogo, em percentual</value> + </data> + <data name="ProfilesPage_AntiDeadzoneUnitPercentage" xml:space="preserve"> + <value> %</value> + </data> + <data name="ProfilesPage_AreYouSureDelete1" xml:space="preserve"> + <value>Você tem certeza que deseja excluir?</value> + </data> + <data name="ProfilesPage_AreYouSureDelete2" xml:space="preserve"> + <value>Este item será excluído imediatamente. Você não pode desfazer esta ação.</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite1" xml:space="preserve"> + <value>Você tem certeza que deseja sobreescrever este perfil?</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite2" xml:space="preserve"> + <value>{0} será sobreescrito. Você não pode desfazer esta ação.</value> + </data> + <data name="ProfilesPage_BoostPower" xml:space="preserve"> + <value>Impulsionar a potência limite</value> + </data> + <data name="ProfilesPage_BoostPowerDesc" xml:space="preserve"> + <value>Alterar o impulso de limite de potência térmica</value> + </data> + <data name="ProfilesPage_Cancel" xml:space="preserve"> + <value>Cancelar</value> + </data> + <data name="ProfilesPage_CreateNewProfile" xml:space="preserve"> + <value>Criar um novo perfil</value> + </data> + <data name="ProfilesPage_Delete" xml:space="preserve"> + <value>Excluir</value> + </data> + <data name="ProfilesPage_DeleteProfile" xml:space="preserve"> + <value>Excluir perfil</value> + </data> + <data name="ProfilesPage_EnableProfile" xml:space="preserve"> + <value>Perfil de usuário por jogo</value> + </data> + <data name="ProfilesPage_EnableProfileDesc" xml:space="preserve"> + <value>O perfil será aplicado automaticamente quando a aplicação indicada é detectada</value> + </data> + <data name="ProfilesPage_GlobalSettings" xml:space="preserve"> + <value>Configurações globais</value> + </data> + <data name="ProfilesPage_GlobalSettingsDesc" xml:space="preserve"> + <value>Alterar as configurações do perfil global</value> + </data> + <data name="ProfilesPage_GyrometerMultiplier" xml:space="preserve"> + <value>Multiplicador do girometro</value> + </data> + <data name="ProfilesPage_GyrometerMultiplierDesc" xml:space="preserve"> + <value>Alterar o valor do girometro relatado pelo sistema</value> + </data> + <data name="ProfilesPage_GyroSteeringAxis" xml:space="preserve"> + <value>Eixo de direção pelo giroscópico</value> + </data> + <data name="ProfilesPage_GyroSteeringAxisDesc" xml:space="preserve"> + <value>Para controlar o movimento horizontal do controle, você pode usar tanto o eixo vertical (rolamento) ou o longitudinal (leme)</value> + </data> + <data name="ProfilesPage_InvertHorizontalAxis" xml:space="preserve"> + <value>Inverter o eixo horizontal</value> + </data> + <data name="ProfilesPage_InvertVerticalAxis" xml:space="preserve"> + <value>Inverter o eixo vertical</value> + </data> + <data name="ProfilesPage_MotionControlSettings" xml:space="preserve"> + <value>Configurações de controle de movimento</value> + </data> + <data name="ProfilesPage_MotionControlSettingsDesc" xml:space="preserve"> + <value>Alterar as configurações globais de controle de movimento</value> + </data> + <data name="ProfilesPage_OK" xml:space="preserve"> + <value>OK</value> + </data> + <data name="ProfilesPage_PowerSettings" xml:space="preserve"> + <value>Configurações de energia</value> + </data> + <data name="ProfilesPage_PowerSettingsDesc" xml:space="preserve"> + <value>Alterar as configurações de energia</value> + </data> + <data name="ProfilesPage_ProfileDetails" xml:space="preserve"> + <value>Detalhes do perfil</value> + </data> + <data name="ProfilesPage_ProfileName" xml:space="preserve"> + <value>Nome do perfil</value> + </data> + <data name="ProfilesPage_ProfilePath" xml:space="preserve"> + <value>Caminho do perfil</value> + </data> + <data name="ProfilesPage_Profiles" xml:space="preserve"> + <value>Perfis</value> + </data> + <data name="ProfilesPage_ProfileSelection" xml:space="preserve"> + <value>Seleção de perfil</value> + </data> + <data name="ProfilesPage_ProfileSelectionDesc" xml:space="preserve"> + <value>Selecionar o perfil que deseja editar</value> + </data> + <data name="ProfilesPage_ProfileSettings" xml:space="preserve"> + <value>Configurações de perfil</value> + </data> + <data name="ProfilesPage_ProfileUpdated1" xml:space="preserve"> + <value>Perfil atualizado</value> + </data> + <data name="ProfilesPage_ProfileUpdated2" xml:space="preserve"> + <value>foi atualizado.</value> + </data> + <data name="ProfilesPage_Roll" xml:space="preserve"> + <value>Rolamento</value> + </data> + <data name="ProfilesPage_SensitivityX" xml:space="preserve"> + <value>X</value> + </data> + <data name="ProfilesPage_SensitivityY" xml:space="preserve"> + <value>Y</value> + </data> + <data name="ProfilesPage_StyleofInput" xml:space="preserve"> + <value>Estilo de entrada</value> + </data> + <data name="ProfilesPage_StyleofInputTooltip" xml:space="preserve"> + <value>As entradas fisicas do controle podem ser programadas para agir como diferentes tipos de dispositivos</value> + </data> + <data name="ProfilesPage_StyleofOutput" xml:space="preserve"> + <value>Dispositivo de saída</value> + </data> + <data name="ProfilesPage_StyleofOutputTooltip" xml:space="preserve"> + <value>Selecionar o dispositivo que receberá os comandos de movimento</value> + </data> + <data name="ProfilesPage_SustainedPower" xml:space="preserve"> + <value>Limite de energia sustentado</value> + </data> + <data name="ProfilesPage_SustainedPowerDesc" xml:space="preserve"> + <value>Alterar o limite de energia térmico sustentado</value> + </data> + <data name="ProfilesPage_TDPOverride" xml:space="preserve"> + <value>Limite de TDP</value> + </data> + <data name="ProfilesPage_TDPOverrideDesc" xml:space="preserve"> + <value>Limita a energia do processador via ajuste de TDP (do inglês, Thermal Design Power ou potência térmica de projeto) para menos energia total</value> + </data> + <data name="ProfilesPage_UMCEnable" xml:space="preserve"> + <value>Ligar o controle de movimento universal</value> + </data> + <data name="ProfilesPage_UMCMotionOff" xml:space="preserve"> + <value>Desligado, ligar com botão(ões)</value> + </data> + <data name="ProfilesPage_UMCMotionOn" xml:space="preserve"> + <value>Ligado, desligar com botão(ões)</value> + </data> + <data name="ProfilesPage_UMCMotionOnOff" xml:space="preserve"> + <value>Ativação de movimento</value> + </data> + <data name="ProfilesPage_UMCMotionOnOffDesc" xml:space="preserve"> + <value>Com a entrada de movimento desligada, use o(s) botão(ões) selecionado(s) para ligar o movimento, +com a entrada de movimento ligada, use o(s) botão(ões) selecionado(s) para desligar o movimento.</value> + </data> + <data name="ProfilesPage_UMCSelectionRightLeftDesc" xml:space="preserve"> + <value>Selecionar o dispositivo que receberá os comandos de movimento</value> + </data> + <data name="ProfilesPage_UMCSettings" xml:space="preserve"> + <value>Configurações de controle de movimento universal</value> + </data> + <data name="ProfilesPage_UMCSettingsDesc" xml:space="preserve"> + <value>Traduz os movimentos dos dispositivos em entradas de controle</value> + </data> + <data name="ProfilesPage_UpdateProfile" xml:space="preserve"> + <value>Atualizar perfil</value> + </data> + <data name="ProfilesPage_Whitelist" xml:space="preserve"> + <value>Permitir acesso direto ao dispositivo de controle físico pela aplicação (ignorar controle virtual emulado)</value> + </data> + <data name="ProfilesPage_Wrapper" xml:space="preserve"> + <value>Compatiblidade expandida (XInputPlus)</value> + </data> + <data name="ProfilesPage_Yaw" xml:space="preserve"> + <value>Leme</value> + </data> + <data name="ProfilesPage_Yes" xml:space="preserve"> + <value>Sim</value> + </data> + <data name="Properties.Resources.ControllerPage_Disconnect" xml:space="preserve"> + <value>Desconectar</value> + </data> + <data name="QuickPerformancePage_GPUControl" xml:space="preserve"> + <value>Controle Manual da Frequência da GPU</value> + </data> + <data name="QuickPerformancePage_GPUControlDesc" xml:space="preserve"> + <value>Define a GPU para utilizar uma frequência fixa</value> + </data> + <data name="QuickPerformancePage_GPUUnit" xml:space="preserve"> + <value> MHz</value> + </data> + <data name="QuickPerformancePage_PowerMode" xml:space="preserve"> + <value>Modo de energia</value> + </data> + <data name="QuickPerformancePage_PowerModeBalanced" xml:space="preserve"> + <value>Equilibrado</value> + </data> + <data name="QuickPerformancePage_PowerModeDesc" xml:space="preserve"> + <value>Otimizar o seu dispositivo baseado com base no uso de energia e desempenho</value> + </data> + <data name="QuickPerformancePage_PowerModeEfficiency" xml:space="preserve"> + <value>Melhor eficiência energética</value> + </data> + <data name="QuickPerformancePage_PowerModePerformance" xml:space="preserve"> + <value>Melhor desempenho</value> + </data> + <data name="QuickPerformancePage_TDPBoost" xml:space="preserve"> + <value>Impulso</value> + </data> + <data name="QuickPerformancePage_TDPLimit" xml:space="preserve"> + <value>Limite de TDP</value> + </data> + <data name="QuickPerformancePage_TDPLimitDesc" xml:space="preserve"> + <value>Limita a energia do processador via ajuste de TDP (do inglês, Thermal Design Power ou potência térmica de projeto) para menos energia total</value> + </data> + <data name="QuickPerformancePage_TDPOverWrittenWarning" xml:space="preserve"> + <value>Limite de TDP é sobreescrito por um perfil</value> + </data> + <data name="QuickPerformancePage_TDPSustained" xml:space="preserve"> + <value>Sustentado</value> + </data> + <data name="QuickPerformancePage_TDPUnitWatt" xml:space="preserve"> + <value> W</value> + </data> + <data name="QuickProfilesPage_Create" xml:space="preserve"> + <value>Criar perfil</value> + </data> + <data name="QuickProfilesPage_Waiting" xml:space="preserve"> + <value>Aguardando por processo em primeiro plano...</value> + </data> + <data name="ServiceDescription" xml:space="preserve"> + <value>Fornece suporte de giroscópico e acelerômetro para computadores de jogo portáteis através de um controle virtual. Se o serviço está em execução, o controle embutido será ocultado das aplicações ausentes da lista de exceções. Se o serviço estiver desligado ou ausente, o controle embutido será revelado e o controle virtual desligado.</value> + </data> + <data name="ServiceName" xml:space="preserve"> + <value>Serviço de Controle</value> + </data> + <data name="SettingsPage_AppLanguage" xml:space="preserve"> + <value>Idioma</value> + </data> + <data name="SettingsPage_AppLanguageDesc" xml:space="preserve"> + <value>O idioma da aplicação</value> + </data> + <data name="SettingsPage_AppLanguageWarning" xml:space="preserve"> + <value>Reinício necessário</value> + </data> + <data name="SettingsPage_AppLanguageWarningDesc" xml:space="preserve"> + <value>Para que as alterações entrem em vigor, por favor reinicie a aplicação</value> + </data> + <data name="SettingsPage_AppTheme" xml:space="preserve"> + <value>Tema da aplicação</value> + </data> + <data name="SettingsPage_AppThemeDesc" xml:space="preserve"> + <value>O tema da aplicação, modo claro ou escuro</value> + </data> + <data name="SettingsPage_AutoStartApp" xml:space="preserve"> + <value>Iniciar a aplicação automaticamente</value> + </data> + <data name="SettingsPage_AutoStartAppDesc" xml:space="preserve"> + <value>A aplicação será iniciada automaticamente quando eu entrar no Windows</value> + </data> + <data name="SettingsPage_Backdrop" xml:space="preserve"> + <value>Tipo de fundo da aplicação</value> + </data> + <data name="SettingsPage_BackdropAcrylic" xml:space="preserve"> + <value>Acrílico</value> + </data> + <data name="SettingsPage_BackdropDesc" xml:space="preserve"> + <value>O fundo da aplicação, nenhum, mica, abas, acrílico</value> + </data> + <data name="SettingsPage_BackdropMica" xml:space="preserve"> + <value>Mica</value> + </data> + <data name="SettingsPage_BackdropNone" xml:space="preserve"> + <value>Nenhum</value> + </data> + <data name="SettingsPage_BackdropTabbed" xml:space="preserve"> + <value>Abas</value> + </data> + <data name="SettingsPage_CheckForUpdates" xml:space="preserve"> + <value>Verificar por atualizações</value> + </data> + <data name="SettingsPage_CloseMinimizes" xml:space="preserve"> + <value>Fechar minimiza</value> + </data> + <data name="SettingsPage_CloseMinimizesDesc" xml:space="preserve"> + <value>A aplicação será minimizada ao invés de encerrada ao clicar no botão fechar janela</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStart" xml:space="preserve"> + <value>Ligar o perfil de área de trabalho ao iniciar</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStartDesc" xml:space="preserve"> + <value>O perfil de área de trabalho será ligado automaticamente no início da aplicação</value> + </data> + <data name="SettingsPage_Download" xml:space="preserve"> + <value>Download</value> + </data> + <data name="SettingsPage_DownloadingPercentage" xml:space="preserve"> + <value>Baixando - </value> + </data> + <data name="SettingsPage_EcoQoS" xml:space="preserve"> + <value>Modo de eficiência</value> + </data> + <data name="SettingsPage_EcoQoSDesc" xml:space="preserve"> + <value>O modo de eficiência reduz a prioridade de processos de plano de fundo e melhora a eficiência energética</value> + </data> + <data name="SettingsPage_GeneralOptions" xml:space="preserve"> + <value>Opções gerais</value> + </data> + <data name="SettingsPage_InstallNow" xml:space="preserve"> + <value>Instalar agora</value> + </data> + <data name="SettingsPage_LastChecked" xml:space="preserve"> + <value>Última verificação: </value> + </data> + <data name="SettingsPage_NotificationOptions" xml:space="preserve"> + <value>Opções de notificação</value> + </data> + <data name="SettingsPage_OpenAppBackground" xml:space="preserve"> + <value>Abrir aplicação em plano de fundo</value> + </data> + <data name="SettingsPage_OpenAppBackgroundDesc" xml:space="preserve"> + <value>A aplicaç"ao será inicializada minimizada e exibida na barra de tarefas</value> + </data> + <data name="SettingsPage_SensorExternal" xml:space="preserve"> + <value>Externo</value> + </data> + <data name="SettingsPage_SensorInternal" xml:space="preserve"> + <value>Interno</value> + </data> + <data name="SettingsPage_SensorOptions" xml:space="preserve"> + <value>Opções de sensor</value> + </data> + <data name="SettingsPage_SensorPlacementDirection" xml:space="preserve"> + <value>Direção de colocação do sensor externo</value> + </data> + <data name="SettingsPage_SensorPlacementDirectionDesc" xml:space="preserve"> + <value>Escolhere em qual lado do dispositivo o sensor externo foi montado</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDown" xml:space="preserve"> + <value>Sensor externo de cabeça para baixo</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDownDesc" xml:space="preserve"> + <value>O sensor foi montado de cabeça para baixo, possivelmente com um conversor USB-C</value> + </data> + <data name="SettingsPage_SensorSelection" xml:space="preserve"> + <value>Seleção de sensor</value> + </data> + <data name="SettingsPage_SensorSelectionDesc" xml:space="preserve"> + <value>Selecionar o sensor desejado para entrada de movimento</value> + </data> + <data name="SettingsPage_Settings" xml:space="preserve"> + <value>Configurações</value> + </data> + <data name="SettingsPage_StartupType" xml:space="preserve"> + <value>Tipo de inicialização</value> + </data> + <data name="SettingsPage_StartupTypeDesc" xml:space="preserve"> + <value>Utilizado pelo gerenciador de serviço para definir o tipo de inicialização do serviço</value> + </data> + <data name="SettingsPage_TDPMax" xml:space="preserve"> + <value>Potência Máxima</value> + </data> + <data name="SettingsPage_TDPMaxDesc" xml:space="preserve"> + <value>O máximo de potência em watts fornecidos ao processador</value> + </data> + <data name="SettingsPage_TDPMin" xml:space="preserve"> + <value>Potência Mínima</value> + </data> + <data name="SettingsPage_TDPMinDesc" xml:space="preserve"> + <value>O mínimo de potência em watts fornecidos ao processador</value> + </data> + <data name="SettingsPage_TDPRangeOverride" xml:space="preserve"> + <value>Sobreposição dos limites de potência (cTDP)</value> + </data> + <data name="SettingsPage_TDPRangeOverrideDesc" xml:space="preserve"> + <value>Permite modificar o mínimo e máximo de valores de potência (TDP) além das especificações do CPU</value> + </data> + <data name="SettingsPage_ThemeDark" xml:space="preserve"> + <value>Escuro</value> + </data> + <data name="SettingsPage_ThemeLight" xml:space="preserve"> + <value>Claro</value> + </data> + <data name="SettingsPage_ToastNotification" xml:space="preserve"> + <value>Notificação toast</value> + </data> + <data name="SettingsPage_ToastNotificationDesc" xml:space="preserve"> + <value>Receber notificações da aplicação na Central de Ações do Windows</value> + </data> + <data name="SettingsPage_UpdateAvailable" xml:space="preserve"> + <value>Atualização disponível</value> + </data> + <data name="SettingsPage_UpdateCheck" xml:space="preserve"> + <value>Procurando atualizações...</value> + </data> + <data name="SettingsPage_UpdateFailedDownload" xml:space="preserve"> + <value>Não conseguimos baixar o arquivo de atualização.</value> + </data> + <data name="SettingsPage_UpdateFailedGithub" xml:space="preserve"> + <value>Não conseguimos alcançar a página do github.</value> + </data> + <data name="SettingsPage_UpdateFailedInstall" xml:space="preserve"> + <value>Não conseguimos localizar o arquivo de atualização.</value> + </data> + <data name="SettingsPage_UpdateWarning" xml:space="preserve"> + <value>Opa. Houve um problema com a atualização.</value> + </data> + <data name="SettingsPage_UpToDate" xml:space="preserve"> + <value>Você está com a versão mais recente</value> + </data> + <data name="ToastNewControllerEx" xml:space="preserve"> + <value>O controle físico está agora oculto e seus comandos estão sendo direcionados para o controle virtual</value> + </data> + <data name="User" xml:space="preserve"> + <value>Usuário</value> + </data> + <data name="WarningElevated" xml:space="preserve"> + <value>Execute esta ferramenta como um Administrador para destravar estas configurações</value> + </data> + <data name="xinput1_x64" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\xinput1_x64.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="xinput1_x86" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\xinput1_x86.dll;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="XInputPlus" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\XInputPlus.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-16</value> + </data> + <data name="ControllerPage_SteamControllerMute" xml:space="preserve"> + <value>Silenciar controle virtual</value> + </data> + <data name="ControllerPage_SteamControllerMuteDesc" xml:space="preserve"> + <value>Silenciar controle virutal em aplicações relacionadas ao Steam</value> + </data> + <data name="ProfilesPage_ControllerSettings" xml:space="preserve"> + <value>Configurações de controle</value> + </data> + <data name="ProfilesPage_ControllerSettingsDesc" xml:space="preserve"> + <value>Alterar as configurações de controle virtual</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzone" xml:space="preserve"> + <value>Anti-zona morta de movimento</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzoneDesc" xml:space="preserve"> + <value>Compensar por zona morta dentro do jogo, melhor o registro de movimentos pequenos</value> + </data> + <data name="chord_neptune" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\controller_base\chord_neptune.vdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="empty_neptune" type="System.Resources.ResXFileRef, System.Windows.Forms"> + <value>..\Resources\controller_base\empty_neptune.vdf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityDesc" xml:space="preserve"> + <value>Melhorar a circularidade do analógico</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityLeft" xml:space="preserve"> + <value>Circularidade do analógico esquerdo</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityRight" xml:space="preserve"> + <value>Circularidade do analógico direito</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeft" xml:space="preserve"> + <value>S</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeftDesc" xml:space="preserve"> + <value>Ajustar o percentual de zona morta interior e exterior do joystick esquerdo</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRight" xml:space="preserve"> + <value>% de zona morta do joystick direito</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRightDesc" xml:space="preserve"> + <value>Ajustar o percentual de zona morta interior e exterior do joystick direito</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeft" xml:space="preserve"> + <value>% de zona morta do gatilho esquerdo</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeftDesc" xml:space="preserve"> + <value>Ajustar o percentual de zona morta interior e exterior do gatilho esquerdo</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRight" xml:space="preserve"> + <value>% de zona morta do gatilho direito</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRightDesc" xml:space="preserve"> + <value>Ajustar o percentual de zona morta interior e exterior do gatilho direito</value> + </data> + <data name="ControllerPage_VibrateDevice" xml:space="preserve"> + <value>Vibrar controle ao conectar</value> + </data> + <data name="ControllerPage_VibrateDeviceDesc" xml:space="preserve"> + <value>Vibrar o controle físico quando conectado</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabled" xml:space="preserve"> + <value>Layout de área de trabalho</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabledDesc" xml:space="preserve"> + <value>Ligar e desligar o layout de controle para área de trabalho</value> + </data> + <data name="InputsHotkey_shortcutControlCenter" xml:space="preserve"> + <value>Exibir a Central de Ações</value> + </data> + <data name="InputsHotkey_shortcutControlCenterDesc" xml:space="preserve"> + <value>Exibe e oculta a Central de Ações do Windows</value> + </data> + <data name="ProfilesPage_ControllerLayout" xml:space="preserve"> + <value>Layout de controle</value> + </data> + <data name="InputsHotkey_QuietModeToggled" xml:space="preserve"> + <value>Cooler Fan/Ventoinha manual</value> + </data> + <data name="InputsHotkey_QuietModeToggledDesc" xml:space="preserve"> + <value>Defina o ciclo de funcionamento da ventoinha/cooler fan para o valor definido pelo usuário</value> + </data> + <data name="InputsHotkey_OnScreenDisplay" xml:space="preserve"> + <value>Exibição em tela</value> + </data> + <data name="InputsHotkey_OnScreenDisplayDesc" xml:space="preserve"> + <value>Ligar a exibição de suporte em tela</value> + </data> + <data name="InputsHotkey_decreaseBrightness" xml:space="preserve"> + <value>Diminuir brilho</value> + </data> + <data name="InputsHotkey_decreaseBrightnessDesc" xml:space="preserve"> + <value>Diminuir o brilho da tela ativa em 5%</value> + </data> + <data name="InputsHotkey_decreaseVolume" xml:space="preserve"> + <value>Diminuir volume</value> + </data> + <data name="InputsHotkey_decreaseVolumeDesc" xml:space="preserve"> + <value>Diminuir o volume de som do sistema em 5%</value> + </data> + <data name="InputsHotkey_increaseBrightness" xml:space="preserve"> + <value>Aumentar brilho</value> + </data> + <data name="InputsHotkey_increaseBrightnessDesc" xml:space="preserve"> + <value>Aumentar o brilho da tela ativa em 5%</value> + </data> + <data name="InputsHotkey_increaseVolume" xml:space="preserve"> + <value>Aumentar volume</value> + </data> + <data name="InputsHotkey_increaseVolumeDesc" xml:space="preserve"> + <value>Aumentar o volume de som do sistema em 5%</value> + </data> + <data name="ProfilesPage_AutoTDP" xml:space="preserve"> + <value>TDP Automático</value> + </data> + <data name="ProfilesPage_AutoTDPDesc" xml:space="preserve"> + <value>Ajuste automático do TDP baseado no FPS (Frames per second, quadros por segundo) medidos e o FPS alvo definido</value> + </data> + <data name="ProfilesPage_AutoTDPFPS" xml:space="preserve"> + <value>Taxa de quadros (FPS) alvo</value> + </data> + <data name="ProfilesPage_AutoTDPFPSDesc" xml:space="preserve"> + <value>O valor alvo de FPS (Frames per second, quadros por segundo) desejado para o controlador de TDP automático</value> + </data> + <data name="ProfilesPage_GPUMhz" xml:space="preserve"> + <value>Frequência máxima do clock da GPU</value> + </data> + <data name="ProfilesPage_GPUMhzDesc" xml:space="preserve"> + <value>Velocidade máxima do clock da GPU em Mhz</value> + </data> + <data name="QuickPerformancePage_AutoTDPUnitFPS" xml:space="preserve"> + <value>FPS</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel" xml:space="preserve"> + <value>Nível de exibição da sobreposição</value> + </data> + <data name="OverlayPage_OverlayDisplayLevelDesc" xml:space="preserve"> + <value>Alterar o nível de informação exibida na tela</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRate" xml:space="preserve"> + <value>Frequência de atualização</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRateDesc" xml:space="preserve"> + <value>Alterar a frequência de atualização das informações exibidas em tela</value> + </data> + <data name="ProfilesPage_FramerateLimit" xml:space="preserve"> + <value>Limite de quadros por segundo (FPS)</value> + </data> + <data name="ProfilesPage_FramerateLimitDesc" xml:space="preserve"> + <value>Limita a quantidade de quadros por segundo (FPS) para aplicações 3D</value> + </data> + <data name="InputsHotkey_shortcutPrintScreen" xml:space="preserve"> + <value>Abrir a Ferramenta de Captura</value> + </data> + <data name="InputsHotkey_shortcutPrintScreenDesc" xml:space="preserve"> + <value>Pressione esta combinação: Windows + Shift + S</value> + </data> + <data name="MainWindow_navHotkeys" xml:space="preserve"> + <value>Teclas de atalho</value> + </data> + <data name="SettingsPage_ThemeDefault" xml:space="preserve"> + <value>Usar padrão do sistema</value> + </data> + <data name="ButtonsPage_ABXY" xml:space="preserve"> + <value>A,B,X,Y</value> + </data> + <data name="ButtonsPage_Back_Grips" xml:space="preserve"> + <value>BOTÕES TRASEIROS</value> + </data> + <data name="ButtonsPage_Bumpers" xml:space="preserve"> + <value>BUMPERS</value> + </data> + <data name="ButtonsPage_Menu" xml:space="preserve"> + <value>MENU</value> + </data> + <data name="ButtonsPage_OEM" xml:space="preserve"> + <value>OEM</value> + </data> + <data name="DPadPage_DPad" xml:space="preserve"> + <value>PAD DIGITAL</value> + </data> + <data name="JoystickPage_Joystick_Left" xml:space="preserve"> + <value>JOYSTICK ESQUERDO</value> + </data> + <data name="JoystickPage_Joystick_Left_Buttons" xml:space="preserve"> + <value>BOTÕES JOYSTICK ESQUERDO</value> + </data> + <data name="JoystickPage_Joystick_Right" xml:space="preserve"> + <value>JOYSTICK DIREITO</value> + </data> + <data name="JoystickPage_Joystick_Right_Buttons" xml:space="preserve"> + <value>BOTÕES JOYSTICK DIREITO</value> + </data> + <data name="TrackPadsPage_Trackpad_Left" xml:space="preserve"> + <value>TRACKPAD ESQUERDO</value> + </data> + <data name="TrackPadsPage_Trackpad_Left_Buttons" xml:space="preserve"> + <value>BOTÕES TRACKPAD ESQUERDO</value> + </data> + <data name="TrackPadsPage_Trackpad_Right" xml:space="preserve"> + <value>TRACKPAD DIREITO</value> + </data> + <data name="TrackPadsPage_Trackpad_Right_Buttons" xml:space="preserve"> + <value>BOTÕES TRACKPAD DIREITO</value> + </data> + <data name="TriggersPage_Trigger_Left" xml:space="preserve"> + <value>GATILHO ESQUERDO</value> + </data> + <data name="TriggersPage_Trigger_Left_Button" xml:space="preserve"> + <value>BOTÕES GATILHO ESQUERDO</value> + </data> + <data name="TriggersPage_Trigger_Right" xml:space="preserve"> + <value>GATILHO DIREITO</value> + </data> + <data name="TriggersPage_Trigger_Right_Button" xml:space="preserve"> + <value>BOTÕES GATILHO DIREITO</value> + </data> + <data name="AboutPage_Manufacturer" xml:space="preserve"> + <value>Fabricante</value> + </data> + <data name="AboutPage_Partner" xml:space="preserve"> + <value>Parceiro</value> + </data> + <data name="AboutPage_ProductName" xml:space="preserve"> + <value>Nome do produto</value> + </data> + <data name="ControllerPage_DesktopLayout" xml:space="preserve"> + <value>Layout de Área de Trabalho</value> + </data> + <data name="ControllerPage_DesktopLayoutDefine" xml:space="preserve"> + <value>Definir layout de área de trabalho</value> + </data> + <data name="ControllerPage_DesktopLayoutDefineController" xml:space="preserve"> + <value>Definir layout do controle quando no modo de área de trabalho</value> + </data> + <data name="ControllerPage_DesktopLayoutEdit" xml:space="preserve"> + <value>Editar</value> + </data> + <data name="ControllerPage_DesktopLayoutEnable" xml:space="preserve"> + <value>Ligar layout de área de trabalho</value> + </data> + <data name="ControllerPage_NonGameControllerLayouts" xml:space="preserve"> + <value>Layouts de controle fora de jogos</value> + </data> + <data name="ControllerPage_NoPhysicalControllerAction" xml:space="preserve"> + <value>Talvez você queira clicar em Conectar ao lado do seu controle conectado.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDesc" xml:space="preserve"> + <value>Você não possui controle físico conectado. Nenhuma entrada será enviada para o HC ou seu serviço.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedAction" xml:space="preserve"> + <value>Por favor, certifique-se de conectar um dispositivo compatível com XInput ou DInput.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedWarning" xml:space="preserve"> + <value>Nenhum controle físico detectado</value> + </data> + <data name="ControllerPage_NoPhysicalControllerWarning" xml:space="preserve"> + <value>Nenhum controle físico conectado</value> + </data> + <data name="ControllerPage_NoVirtualControllerAction" xml:space="preserve"> + <value>Talvez você queira iniciar o serviço do Companion ou certificar-se que o a situação do seu controle virtual é: Conectado</value> + </data> + <data name="ControllerPage_NoVirtualControllerDesc" xml:space="preserve"> + <value>Seu controle físico está oculto, no entanto você não possui controle virtual disponível. Nenhuma entrada será enviada para os jogos.</value> + </data> + <data name="ControllerPage_NoVirtualControllerWarning" xml:space="preserve"> + <value>Nenhum controle virtual detectado</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenAction" xml:space="preserve"> + <value>Talvez você queira reativar seu controle virtual ou ocultar seu controle físico.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenDesc" xml:space="preserve"> + <value>Seu controle físico está oculto e ainda você também está com o controle virtual inativo.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenWarning" xml:space="preserve"> + <value>O controle físico está oculto</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenAction" xml:space="preserve"> + <value>Talvez você queira ocultar seu controle físico ou inativar seu controle virtual.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenDesc" xml:space="preserve"> + <value>Seu controle físico não está oculto, no entanto você tem um controle virtual ativo. Você pode encontrar entrada duplicada nos jogos.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenWarning" xml:space="preserve"> + <value>O controle físico não está oculto</value> + </data> + <data name="ControllerPage_SteamControllerHDRumble" xml:space="preserve"> + <value>HD rumble (tremida)</value> + </data> + <data name="ControllerPage_SteamControllerHDRumbleDesc" xml:space="preserve"> + <value>Usar o motor de tremida de alta definição, ao custo de maior uso de CPU</value> + </data> + <data name="ControllerPage_SteamControllerSettings" xml:space="preserve"> + <value>Configurações do Steam Deck</value> + </data> + <data name="LayoutPage_ApplyTemplate" xml:space="preserve"> + <value>Aplicar modelo</value> + </data> + <data name="LayoutPage_Buttons" xml:space="preserve"> + <value>Botões</value> + </data> + <data name="LayoutPage_Cancel" xml:space="preserve"> + <value>Cancelar</value> + </data> + <data name="LayoutPage_Community" xml:space="preserve"> + <value>COMUNIDADE</value> + </data> + <data name="LayoutPage_Confirm" xml:space="preserve"> + <value>Confirmar</value> + </data> + <data name="LayoutPage_Dpad" xml:space="preserve"> + <value>Dpad</value> + </data> + <data name="LayoutPage_ExportCurrentController" xml:space="preserve"> + <value>Exportar para controle atual</value> + </data> + <data name="LayoutPage_ExportLayout" xml:space="preserve"> + <value>Exportar layout</value> + </data> + <data name="LayoutPage_Gyro" xml:space="preserve"> + <value>Gyro</value> + </data> + <data name="LayoutPage_Joysticks" xml:space="preserve"> + <value>Joysticks</value> + </data> + <data name="LayoutPage_LayoutAuthor" xml:space="preserve"> + <value>Autor do layout</value> + </data> + <data name="LayoutPage_LayoutDesc" xml:space="preserve"> + <value>Descrição do layout</value> + </data> + <data name="LayoutPage_LayoutTitle" xml:space="preserve"> + <value>Título do layout</value> + </data> + <data name="LayoutPage_SaveGameInfoLayout" xml:space="preserve"> + <value>Salvar informações do jogo com o layout</value> + </data> + <data name="LayoutPage_ShowCurrentControllerTemplates" xml:space="preserve"> + <value>Exibir apenas os modelos do controle atual</value> + </data> + <data name="LayoutPage_TemplatePicker" xml:space="preserve"> + <value>Seletor de modelo de layout</value> + </data> + <data name="LayoutPage_Templates" xml:space="preserve"> + <value>MODELOS</value> + </data> + <data name="LayoutPage_Trackpads" xml:space="preserve"> + <value>Trackpads</value> + </data> + <data name="LayoutPage_Triggers" xml:space="preserve"> + <value>Gatilhos</value> + </data> + <data name="OverlayPage_Millisecond" xml:space="preserve"> + <value>ms</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Disabled" xml:space="preserve"> + <value>Desligado</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Extended" xml:space="preserve"> + <value>Estendido</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Full" xml:space="preserve"> + <value>Completo</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Minimal" xml:space="preserve"> + <value>Mínimo</value> + </data> + <data name="ProfilesPage_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="ProfilesPage_EPP" xml:space="preserve"> + <value>Preferência de desempenho energético (Energy performance preference - EPP)</value> + </data> + <data name="ProfilesPage_EPPBalance" xml:space="preserve"> + <value>Distribuição de energia CPU/GPU</value> + </data> + <data name="ProfilesPage_EPPDesc" xml:space="preserve"> + <value>Especifica a política de distribuição de energia entre CPU e GPU</value> + </data> + <data name="ProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Definição de limite de energia</value> + </data> + <data name="SettingsPage_NativeDisplayOrientation" xml:space="preserve"> + <value>Orientação da tela</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDesc" xml:space="preserve"> + <value>Alguns recursos dependem do conhecimento da orientação da tela para funcionar corretamente. Se isto não foi detectado apropriadamente, defina a orientação da sua tela correspondente ao seu controle, então clique Detectar</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDetect" xml:space="preserve"> + <value>Detectar</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationNotSet" xml:space="preserve"> + <value>Não definida</value> + </data> + <data name="QuickProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Definição de limite de energia</value> + </data> + <data name="SettingsPage_QuickToolsOptions" xml:space="preserve"> + <value>Opções de ferramentas rápidas</value> + </data> + <data name="QuickPerformancePage_FanOverridePercentage" xml:space="preserve"> + <value>%</value> + </data> + <data name="QuickPerformancePage_CPUBoostModeDesc" xml:space="preserve"> + <value>Definir o modo de impulso de CPU</value> + </data> + <data name="QuickPerformancePage_CPUBoostMode" xml:space="preserve"> + <value>Modo de impulso de CPU</value> + </data> + <data name="QuickPerformancePage_FanOverrideDesc" xml:space="preserve"> + <value>Definir o ciclo de funcionamento da ventoinha/cooler fan para o valor definido pelo usuário</value> + </data> + <data name="QuickPerformancePage_FanOverride" xml:space="preserve"> + <value>Cooler Fan/Ventoinha manual</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRateDesc" xml:space="preserve"> + <value>Ajustar a resolução da tela principal e taxa de atualização</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRate" xml:space="preserve"> + <value>Resolução de tela e taxa de atualização</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopRight" xml:space="preserve"> + <value>Superior direito</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopLeft" xml:space="preserve"> + <value>Superior esquerdo</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocation" xml:space="preserve"> + <value>Localização da janela</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomRight" xml:space="preserve"> + <value>Inferior direito</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomLeft" xml:space="preserve"> + <value>Inferior esquerdo</value> + </data> + <data name="DevicePage_PowerOptions" xml:space="preserve"> + <value>Opções de energia</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationDesc" xml:space="preserve"> + <value>Definir localização da janela de ferramentas rápidas</value> + </data> + <data name="SettingsPage_ThirdPartyApps" xml:space="preserve"> + <value>Aplicativos de terceiros</value> + </data> + <data name="SettingsPage_SensorNone" xml:space="preserve"> + <value>Nenhum</value> + </data> + <data name="SettingsPage_QuickToolsBackdrop" xml:space="preserve"> + <value>Fundo das ferramentas rápidas: Nenhum, Mica, Abas ou Acrílico</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServerDesc" xml:space="preserve"> + <value>Automaticamente desligar RTSS quando o Companion é encerrado</value> + </data> + <data name="SettingsPage_HwInfoDesc" xml:space="preserve"> + <value>Automaticamente desligar HWiNFO quando o Companion é encerrado</value> + </data> + <data name="SettingsPage_HwInfo" xml:space="preserve"> + <value>HWiNFO</value> + </data> + <data name="QuickProfilesPage_CurrentProfileDefault" xml:space="preserve"> + <value>Padrão</value> + </data> + <data name="QuickProfilesPage_CurrentProfile" xml:space="preserve"> + <value>Perfil atual:</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServer" xml:space="preserve"> + <value>RivaTuner Statistics Server</value> + </data> + <data name="SettingsPage_HideWhenLoseFocusDesc" xml:space="preserve"> + <value>Automaticamente esconder as ferramentas rápidas quando o usuário clica fora da janela</value> + </data> + <data name="SettingsPage_HideWhenLoseFocus" xml:space="preserve"> + <value>Ocultar quando perder foco</value> + </data> + <data name="AutoRollYawSwapDesc" xml:space="preserve"> + <value>Esta entrada irá operar como um joystick simples. Ideal para notebook ou portáteis no estilo concha, alternada automática de rolagem de guinada baseado em como o dispositivo está sendo segurado (aberto em 90 ou 180 graus).</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B1" xml:space="preserve"> + <value>Cruz</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B2" xml:space="preserve"> + <value>Círculo</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B3" xml:space="preserve"> + <value>Quadrado</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B4" xml:space="preserve"> + <value>Triângulo</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Start" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.HIDmode.DualShock4Controller" xml:space="preserve"> + <value>Controle DualShock 4 emulado</value> + </data> + <data name="Enum.HIDmode.NoController" xml:space="preserve"> + <value>Sem controle emulado</value> + </data> + <data name="Enum.HIDmode.Xbox360Controller" xml:space="preserve"> + <value>Controle XBOX 360 emulado</value> + </data> + <data name="Enum.HIDstatus.Connected" xml:space="preserve"> + <value>Conectado</value> + </data> + <data name="Enum.HIDstatus.Disconnected" xml:space="preserve"> + <value>Desconectado</value> + </data> + <data name="Enum.Input.AutoRollYawSwap" xml:space="preserve"> + <value>Alternar Rolagem de Guinada Auto</value> + </data> + <data name="Enum.Input.JoystickCamera" xml:space="preserve"> + <value>Câmera de Joystick</value> + </data> + <data name="Enum.Input.JoystickSteering" xml:space="preserve"> + <value>Joystick de direção (tipo volante)</value> + </data> + <data name="Enum.Input.PlayerSpace" xml:space="preserve"> + <value>Espaço do jogador</value> + </data> + <data name="Enum.InputsHotkeyType.HC" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.InputsHotkeyType.Overlay" xml:space="preserve"> + <value>Sobreposição</value> + </data> + <data name="Enum.InputsHotkeyType.Quicktools" xml:space="preserve"> + <value>Ferramentas rápidas</value> + </data> + <data name="Enum.InputsHotkeyType.Windows" xml:space="preserve"> + <value>Windows</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Back" xml:space="preserve"> + <value>View</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="Enum.Output.LeftStick" xml:space="preserve"> + <value>Analógico esquerdo</value> + </data> + <data name="Enum.Output.RightStick" xml:space="preserve"> + <value>Analógico direito</value> + </data> + <data name="Enum.ProfileErrorCode.Default" xml:space="preserve"> + <value>Este é seu perfil padrão de controle. Este perfil será aplicado a todas as suas aplicações que não possuem um perfil específico. Algumas opções que requerem a definição de um arquivo executável estarão desabilitadas.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingExecutable" xml:space="preserve"> + <value>Opa. Parece que esse perfil não possui um arquivo executável definido. Como isto será possível?</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPath" xml:space="preserve"> + <value>Opa. Parece que esse perfil não possui um caminho de diretório até a aplicação. Algumas opções que requerem a definição de um arquivo executável estarão desabilitadas.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPermission" xml:space="preserve"> + <value>Opa. Parece que você não possui o nível de permissão necessário para modificar o conteúdo desta aplicação. Certifique-se de iniciar este programa com privilégios administrativos.</value> + </data> + <data name="Enum.ProfileErrorCode.None" xml:space="preserve"> + <value>Nada para ver aqui.</value> + </data> + <data name="Enum.ProfileErrorCode.Running" xml:space="preserve"> + <value>Opa. Parece que o arquivo executável deste perfil está em execução. Algumas opções estarão indisponíveis até que a execução seja encerrada.</value> + </data> + <data name="Enum.QualityOfServiceLevel.Default" xml:space="preserve"> + <value>Padrão</value> + </data> + <data name="Enum.QualityOfServiceLevel.Eco" xml:space="preserve"> + <value>Eco</value> + </data> + <data name="Enum.QualityOfServiceLevel.High" xml:space="preserve"> + <value>Alta</value> + </data> + <data name="Enum.ServiceStartMode.Automatic" xml:space="preserve"> + <value>Automático</value> + </data> + <data name="Enum.ServiceStartMode.Disabled" xml:space="preserve"> + <value>Desabilitado</value> + </data> + <data name="Enum.ServiceStartMode.Manual" xml:space="preserve"> + <value>Manual</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Back" xml:space="preserve"> + <value>View</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L1" xml:space="preserve"> + <value>LB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R1" xml:space="preserve"> + <value>RB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Special" xml:space="preserve"> + <value>Guia</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="JoystickCameraDesc" xml:space="preserve"> + <value>Esta entrada irá operar como um joystick simples. Isto é voltado para aplicações que utilizam joystick tradicional</value> + </data> + <data name="JoystickSteeringDesc" xml:space="preserve"> + <value>Esta entrada irá operar como um joystick otimizado para controlar um volante ou um jogo de corrida</value> + </data> + <data name="PlayerSpaceDesc" xml:space="preserve"> + <value>Esta entrada irá operar como um joystick otimizado para controlar uma câmera em primeira ou terceira pessoa</value> + </data> + <data name="Enum.InputsHotkeyType.Handheld" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.SteamDeck.ButtonFlags.OEM1" xml:space="preserve"> + <value>Opções</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Special" xml:space="preserve"> + <value>STEAM</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM1" xml:space="preserve"> + <value>Centro de comando</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM2" xml:space="preserve"> + <value>Armory crate</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM3" xml:space="preserve"> + <value>M1 / M2</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM1" xml:space="preserve"> + <value>Win</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM2" xml:space="preserve"> + <value>Esc</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM3" xml:space="preserve"> + <value>KB</value> + </data> + <data name="Enum.InputsHotkeyType.Custom" xml:space="preserve"> + <value>Personalizado</value> + </data> + <data name="Enum.InputsHotkeyType.Device" xml:space="preserve"> + <value>Dispositivo</value> + </data> + <data name="Enum.MotionInput.AutoRollYawSwap" xml:space="preserve"> + <value>Troca automática Rolamento e Leme</value> + </data> + <data name="Enum.MotionInput.JoystickCamera" xml:space="preserve"> + <value>Câmera de Joystick</value> + </data> + <data name="Enum.MotionInput.JoystickSteering" xml:space="preserve"> + <value>Direção de Joystick</value> + </data> + <data name="Enum.MotionInput.PlayerSpace" xml:space="preserve"> + <value>Espaço do Jogador</value> + </data> + <data name="Enum.MotionOutput.LeftStick" xml:space="preserve"> + <value>Analógico Esquerdo</value> + </data> + <data name="Enum.MotionOutput.RightStick" xml:space="preserve"> + <value>Analógico Direito</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftThumb" xml:space="preserve"> + <value>Analógico Esquerdo</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightThumb" xml:space="preserve"> + <value>Analógico Direito</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B5" xml:space="preserve"> + <value>B5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B6" xml:space="preserve"> + <value>B6</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B7" xml:space="preserve"> + <value>B7</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B8" xml:space="preserve"> + <value>B8</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadDown" xml:space="preserve"> + <value>DPad Baixo</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadLeft" xml:space="preserve"> + <value>DPad Esquerda</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadRight" xml:space="preserve"> + <value>PPad Direita</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadUp" xml:space="preserve"> + <value>DPad Cima</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L3" xml:space="preserve"> + <value>L3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L4" xml:space="preserve"> + <value>L4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L5" xml:space="preserve"> + <value>L5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumb" xml:space="preserve"> + <value>Analógico Esquerdo</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbDown" xml:space="preserve"> + <value>Analógico Esquerdo Baixo</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbLeft" xml:space="preserve"> + <value>Analógico Esquerdo Esquerda</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbRight" xml:space="preserve"> + <value>Analógico Esquerdo Direita</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbUp" xml:space="preserve"> + <value>Analógico Esquerdo Cima</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM1" xml:space="preserve"> + <value>OEM1</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM2" xml:space="preserve"> + <value>OEM2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM3" xml:space="preserve"> + <value>OEM3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R3" xml:space="preserve"> + <value>R3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R4" xml:space="preserve"> + <value>R4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R5" xml:space="preserve"> + <value>R5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumb" xml:space="preserve"> + <value>Analógico Direito</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbDown" xml:space="preserve"> + <value>Analógico Direito Baixo</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbLeft" xml:space="preserve"> + <value>Analógico Direito Esquerda</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbRight" xml:space="preserve"> + <value>Analógico Direito Direita</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbUp" xml:space="preserve"> + <value>Analógico Direito Cima</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.AlwaysOn" xml:space="preserve"> + <value>Sempre Ligado</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Back" xml:space="preserve"> + <value>Back</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadDown" xml:space="preserve"> + <value>DPad Baixo</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadLeft" xml:space="preserve"> + <value>DPad Esquerda</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadRight" xml:space="preserve"> + <value>DPad Direita</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadUp" xml:space="preserve"> + <value>DPad Cima</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftShoulder" xml:space="preserve"> + <value>Ombro Esquerda</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftThumb" xml:space="preserve"> + <value>Polegar Esquerda</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftTrigger" xml:space="preserve"> + <value>Gatilho Esquerda</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightShoulder" xml:space="preserve"> + <value>Ombro Direita</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightThumb" xml:space="preserve"> + <value>Polegar Direita</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightTrigger" xml:space="preserve"> + <value>Gatilho Direita</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Start" xml:space="preserve"> + <value>Start</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.ProfileErrorCode.IsRunning" xml:space="preserve"> + <value>Opa. Este perfil parece estar em execução algumas opções necessitam que o executável esteja encerrado.</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B1" xml:space="preserve"> + <value>Cross</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B2" xml:space="preserve"> + <value>Circle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B3" xml:space="preserve"> + <value>Square</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B4" xml:space="preserve"> + <value>Triangle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Start" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.KeyFlags.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.KeyFlags.Alt" xml:space="preserve"> + <value>Alt</value> + </data> + <data name="Enum.KeyFlags.Apostrophe" xml:space="preserve"> + <value>Apostrophe</value> + </data> + <data name="Enum.KeyFlags.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.KeyFlags.Backslash" xml:space="preserve"> + <value>Backslash</value> + </data> + <data name="Enum.KeyFlags.Backspace" xml:space="preserve"> + <value>Backspace</value> + </data> + <data name="Enum.KeyFlags.C" xml:space="preserve"> + <value>C</value> + </data> + <data name="Enum.KeyFlags.Comma" xml:space="preserve"> + <value>Comma</value> + </data> + <data name="Enum.KeyFlags.Control" xml:space="preserve"> + <value>Control</value> + </data> + <data name="Enum.KeyFlags.D" xml:space="preserve"> + <value>D</value> + </data> + <data name="Enum.KeyFlags.Delete" xml:space="preserve"> + <value>Delete</value> + </data> + <data name="Enum.KeyFlags.E" xml:space="preserve"> + <value>E</value> + </data> + <data name="Enum.KeyFlags.End" xml:space="preserve"> + <value>End</value> + </data> + <data name="Enum.KeyFlags.Enter" xml:space="preserve"> + <value>Enter</value> + </data> + <data name="Enum.KeyFlags.Equal" xml:space="preserve"> + <value>Equal</value> + </data> + <data name="Enum.KeyFlags.Escape" xml:space="preserve"> + <value>Escape</value> + </data> + <data name="Enum.KeyFlags.F" xml:space="preserve"> + <value>F</value> + </data> + <data name="Enum.KeyFlags.F1" xml:space="preserve"> + <value>F1</value> + </data> + <data name="Enum.KeyFlags.F10" xml:space="preserve"> + <value>F10</value> + </data> + <data name="Enum.KeyFlags.F11" xml:space="preserve"> + <value>F11</value> + </data> + <data name="Enum.KeyFlags.F12" xml:space="preserve"> + <value>F12</value> + </data> + <data name="Enum.KeyFlags.F2" xml:space="preserve"> + <value>F2</value> + </data> + <data name="Enum.KeyFlags.F3" xml:space="preserve"> + <value>F3</value> + </data> + <data name="Enum.KeyFlags.F4" xml:space="preserve"> + <value>F4</value> + </data> + <data name="Enum.KeyFlags.F5" xml:space="preserve"> + <value>F5</value> + </data> + <data name="Enum.KeyFlags.F6" xml:space="preserve"> + <value>F6</value> + </data> + <data name="Enum.KeyFlags.F7" xml:space="preserve"> + <value>F7</value> + </data> + <data name="Enum.KeyFlags.F8" xml:space="preserve"> + <value>F8</value> + </data> + <data name="Enum.KeyFlags.F9" xml:space="preserve"> + <value>F9</value> + </data> + <data name="Enum.KeyFlags.G" xml:space="preserve"> + <value>G</value> + </data> + <data name="Enum.KeyFlags.Grave" xml:space="preserve"> + <value>Grave</value> + </data> + <data name="Enum.KeyFlags.H" xml:space="preserve"> + <value>H</value> + </data> + <data name="Enum.KeyFlags.Home" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.KeyFlags.I" xml:space="preserve"> + <value>I</value> + </data> + <data name="Enum.KeyFlags.Insert" xml:space="preserve"> + <value>Insert</value> + </data> + <data name="Enum.KeyFlags.J" xml:space="preserve"> + <value>J</value> + </data> + <data name="Enum.KeyFlags.K" xml:space="preserve"> + <value>K</value> + </data> + <data name="Enum.KeyFlags.L" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.KeyFlags.M" xml:space="preserve"> + <value>M</value> + </data> + <data name="Enum.KeyFlags.Minus" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.KeyFlags.N" xml:space="preserve"> + <value>N</value> + </data> + <data name="Enum.KeyFlags.O" xml:space="preserve"> + <value>O</value> + </data> + <data name="Enum.KeyFlags.P" xml:space="preserve"> + <value>P</value> + </data> + <data name="Enum.KeyFlags.Pause" xml:space="preserve"> + <value>Pause</value> + </data> + <data name="Enum.KeyFlags.Period" xml:space="preserve"> + <value>Period</value> + </data> + <data name="Enum.KeyFlags.Q" xml:space="preserve"> + <value>Q</value> + </data> + <data name="Enum.KeyFlags.R" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.KeyFlags.S" xml:space="preserve"> + <value>S</value> + </data> + <data name="Enum.KeyFlags.Semicolon" xml:space="preserve"> + <value>Semicolon</value> + </data> + <data name="Enum.KeyFlags.Shift" xml:space="preserve"> + <value>Shift</value> + </data> + <data name="Enum.KeyFlags.Slash" xml:space="preserve"> + <value>Slash</value> + </data> + <data name="Enum.KeyFlags.Space" xml:space="preserve"> + <value>Space</value> + </data> + <data name="Enum.KeyFlags.T" xml:space="preserve"> + <value>T</value> + </data> + <data name="Enum.KeyFlags.Tab" xml:space="preserve"> + <value>Tab</value> + </data> + <data name="Enum.KeyFlags.U" xml:space="preserve"> + <value>U</value> + </data> + <data name="Enum.KeyFlags.V" xml:space="preserve"> + <value>V</value> + </data> + <data name="Enum.KeyFlags.W" xml:space="preserve"> + <value>W</value> + </data> + <data name="Enum.KeyFlags.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.KeyFlags.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.KeyFlags.Z" xml:space="preserve"> + <value>Z</value> + </data> + <data name="Enum.MotionOuput.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.MotionOuput.MoveCursor" xml:space="preserve"> + <value>MoveCursor</value> + </data> + <data name="Enum.MotionOuput.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="Enum.MotionOuput.ScrollWheel" xml:space="preserve"> + <value>ScrollWheel</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>ZL</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>ZR</value> + </data> + <data name="Enum.ProController.ButtonFlags.B1" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.ProController.ButtonFlags.B2" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.ProController.ButtonFlags.B3" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.ProController.ButtonFlags.B4" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.ProController.ButtonFlags.Back" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.ProController.ButtonFlags.L1" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.ProController.ButtonFlags.R1" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special2" xml:space="preserve"> + <value>Capture</value> + </data> + <data name="Enum.ProController.ButtonFlags.Start" xml:space="preserve"> + <value>Plus</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderClosePrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseText" xml:space="preserve"> + <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="ProfilesPage_Wrapper_Disabled" xml:space="preserve"> + <value>Disabled</value> + </data> + <data name="ProfilesPage_Wrapper_Injection" xml:space="preserve"> + <value>Injection (recommended)</value> + </data> + <data name="ProfilesPage_Wrapper_Redirection" xml:space="preserve"> + <value>Redirection</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrder" xml:space="preserve"> + <value>Improve virtual controller detection</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderDesc" xml:space="preserve"> + <value>Forces the virtual controller to be detected as first controller during Windows start. Enable this if your app/game doesn't detect your inputs (requires device reboot).</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderText" xml:space="preserve"> + <value>Your device must be restarted in order for the changes to take effect. Would you like to restart now?</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderTitle" xml:space="preserve"> + <value>Restart required</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencySecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyText" xml:space="preserve"> + <value>Disabling this setting will also disable "Improve virtual controller detection". Do you want to continue?</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="ProfilesPage_ControllerLayoutDesc" xml:space="preserve"> + <value>Change the virtual controller layout</value> + </data> + <data name="Controller_Connect" xml:space="preserve"> + <value>Connect</value> + </data> + <data name="Controller_Disconnect" xml:space="preserve"> + <value>Disconnect</value> + </data> + <data name="Controller_Hide" xml:space="preserve"> + <value>Hide</value> + </data> + <data name="Controller_Unhide" xml:space="preserve"> + <value>Unhide</value> + </data> + <data name="Controller_Virtual" xml:space="preserve"> + <value>Virtual </value> + </data> + <data name="MainWindow_Back" xml:space="preserve"> + <value>Back</value> + </data> + <data name="MainWindow_Exit" xml:space="preserve"> + <value>Exit</value> + </data> + <data name="MainWindow_MainWindow" xml:space="preserve"> + <value>Main Window</value> + </data> + <data name="MainWindow_Navigate" xml:space="preserve"> + <value>Navigate</value> + </data> + <data name="MainWindow_QuickTools" xml:space="preserve"> + <value>Quick Tools</value> + </data> + <data name="MainWindow_Select" xml:space="preserve"> + <value>Select</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_External" xml:space="preserve"> + <value>External</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate1" xml:space="preserve"> + <value>Are you sure you want to apply this template? All layout settings will be overridden.</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate2" xml:space="preserve"> + <value>You can't undo this action. Previously applied template: {0}</value> + </data> + <data name="ProfilesPage_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="LayoutPage_SetAsDefault" xml:space="preserve"> + <value>Make this the default layout</value> + </data> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </root> \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.resx b/HandheldCompanion/Properties/Resources.resx index dd819e0e4..4d33fb4f7 100644 --- a/HandheldCompanion/Properties/Resources.resx +++ b/HandheldCompanion/Properties/Resources.resx @@ -763,7 +763,11 @@ <data name="ProfilesPage_UMCMotionOn" xml:space="preserve"> <value>Enabled, turn off with button(s)</value> </data> +<<<<<<< HEAD <data name="ProfilesPage_UMCMotionToggle" xml:space="preserve"> +======= + <data name="ProfilesPage_UMCMotionToggle" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>Toggle between enabled or disabled with button(s)</value> </data> <data name="ProfilesPage_UMCMotionOnOff" xml:space="preserve"> @@ -1263,6 +1267,7 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <data name="AboutPage_Partner" xml:space="preserve"> <value>Partner</value> </data> +<<<<<<< HEAD <data name="Dialog_Yes" xml:space="preserve"> <value>Yes</value> </data> @@ -1297,6 +1302,48 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> </data> <data name="MainWindow_ControllerManagementCloseTitle" xml:space="preserve"> +======= + <data name="SettingsPage_ForceVirtualControllerOrder" xml:space="preserve"> + <value>Improve virtual controller detection</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderDesc" xml:space="preserve"> + <value>Forces the virtual controller to be detected as first controller during Windows start. Enable this if your app/game doesn't detect your inputs (requires device reboot).</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderText" xml:space="preserve"> + <value>Your device must be restarted in order for the changes to take effect. Would you like to restart now?</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderTitle" xml:space="preserve"> + <value>Restart required</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencySecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyText" xml:space="preserve"> + <value>Disabling this setting will also disable "Improve virtual controller detection". Do you want to continue?</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderClosePrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseText" xml:space="preserve"> + <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseTitle" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>Warning</value> </data> <data name="AboutPage_Manufacturer" xml:space="preserve"> @@ -1371,8 +1418,13 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <data name="ControllerPage_SteamControllerHDRumbleDesc" xml:space="preserve"> <value>Use high-definition rumble engine, at the cost of higher CPU usage</value> </data> +<<<<<<< HEAD <data name="ControllerPage_DeviceSpecificSettings" xml:space="preserve"> <value>Controller settings</value> +======= + <data name="ControllerPage_SteamControllerSettings" xml:space="preserve"> + <value>Steam Controller Settings</value> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </data> <data name="LayoutPage_ApplyTemplate" xml:space="preserve"> <value>Apply template</value> @@ -1572,7 +1624,11 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <data name="ProfilesPage_Wrapper_Redirection" xml:space="preserve"> <value>Redirection</value> </data> +<<<<<<< HEAD <data name="Enum.MotionInput.AutoRollYawSwap.Desc" xml:space="preserve"> +======= + <data name="AutoRollYawSwapDesc" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>This input will operate as a simple joystick. Ideal for laptop and clamshell type handhelds, automatic yaw roll swap based on how device is being held (90 or 180 degree open).</value> </data> <data name="Enum.AYANEO2.ButtonFlags.OEM1" xml:space="preserve"> @@ -1770,6 +1826,7 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <data name="Enum.XInputController.ButtonFlags.Start" xml:space="preserve"> <value>Menu</value> </data> +<<<<<<< HEAD <data name="Enum.MotionInput.JoystickCamera.Desc" xml:space="preserve"> <value>This input will operate as a simple joystick. This is intended for traditional joystick applications</value> </data> @@ -1777,6 +1834,15 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <value>This input will operate as a joystick optimized for controlling a steering wheel or a racing game</value> </data> <data name="Enum.MotionInput.PlayerSpace.Desc" xml:space="preserve"> +======= + <data name="JoystickCameraDesc" xml:space="preserve"> + <value>This input will operate as a simple joystick. This is intended for traditional joystick applications</value> + </data> + <data name="JoystickSteeringDesc" xml:space="preserve"> + <value>This input will operate as a joystick optimized for controlling a steering wheel or a racing game</value> + </data> + <data name="PlayerSpaceDesc" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>This input will operate as a joystick optimized for controlling a first or third person camera</value> </data> <data name="Enum.InputsHotkeyType.Handheld" xml:space="preserve"> @@ -1795,7 +1861,11 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <value>Armory crate</value> </data> <data name="Enum.ROGAlly.ButtonFlags.OEM3" xml:space="preserve"> +<<<<<<< HEAD <value>M1</value> +======= + <value>M1 / M2</value> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </data> <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM1" xml:space="preserve"> <value>Win</value> @@ -2325,6 +2395,7 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <data name="MainWindow_Select" xml:space="preserve"> <value>Select</value> </data> +<<<<<<< HEAD <data name="Hint_SteamNeptuneReadme" xml:space="preserve"> <value>You should restart Steam, so that Handheld Companion can automatically adjust Steam Desktop Layout and prevent double input</value> </data> @@ -2341,6 +2412,24 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <value>Steam Xbox Controller Enhanced Features Driver is installed and is breaking HidHide capacities to hide/unhide your physical controller</value> </data> <data name="Hint_SteamXboxDrivers" xml:space="preserve"> +======= + <data name="ControllerPage_SteamNeptuneDesktopAction" xml:space="preserve"> + <value>You should restart Steam, so that Handheld Companion can automatically adjust Steam Desktop Layout and prevent double input.</value> + </data> + <data name="ControllerPage_SteamNeptuneDesktopDesc" xml:space="preserve"> + <value>It appears that Steam is already running and Steam Desktop Layout is applied. This may result in double input.</value> + </data> + <data name="ControllerPage_SteamNeptuneDesktopWarning" xml:space="preserve"> + <value>Steam Desktop Layout is applied</value> + </data> + <data name="ControllerPage_SteamXboxDriversAction" xml:space="preserve"> + <value>You might want to uninstall Steam Xbox Controller Enhanced Features Driver</value> + </data> + <data name="ControllerPage_SteamXboxDriversDesc" xml:space="preserve"> + <value>It appears you have installed Steam Xbox Controller Enhanced Features Driver which are breaking HidHide capacities to hide/unhide your physical controller</value> + </data> + <data name="ControllerPage_SteamXboxDriversWarning" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>Steam Xbox Controller Enhanced Features Driver is installed</value> </data> <data name="DevicePage_Device" xml:space="preserve"> @@ -2349,6 +2438,7 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <data name="MainWindow_navDevice" xml:space="preserve"> <value>Device</value> </data> +<<<<<<< HEAD <data name="XInputController_Warning_BTH" xml:space="preserve"> <value>Please switch your controller off and then on again by pressing and holding the Guide button to finalize pairing.</value> </data> @@ -2667,4 +2757,12 @@ with motion input toggle, press selected button(s) to switch from enabled to dis <data name="Hint_CoreIsolationCheckReadme" xml:space="preserve"> <value>You might want to turn off core isolation and restart your system to enable TDP manipulations</value> </data> +======= + <data name="ControllerPage_XInputControllerWarning" xml:space="preserve"> + <value>Please switch your controller off and then on again by pressing and holding the Guide button to finalize pairing.</value> + </data> + <data name="LayoutPage_SetAsDefault" xml:space="preserve"> + <value>Make this the default layout</value> + </data> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </root> \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.ru-RU.resx b/HandheldCompanion/Properties/Resources.ru-RU.resx index 370f7e872..5b3c56d08 100644 --- a/HandheldCompanion/Properties/Resources.ru-RU.resx +++ b/HandheldCompanion/Properties/Resources.ru-RU.resx @@ -1,3 +1,4 @@ +<<<<<<< HEAD <?xml version="1.0" encoding="utf-8"?> <root> <!-- @@ -2338,4 +2339,2313 @@ <data name="IController_ControllerIndex" xml:space="preserve"> <value>Gamepad: {0}</value> </data> +======= +<?xml version="1.0" encoding="utf-8"?> +<root> + <!-- + Microsoft ResX Schema + + Version 2.0 + + The primary goals of this format is to allow a simple XML format + that is mostly human readable. The generation and parsing of the + various data types are done through the TypeConverter classes + associated with the data types. + + Example: + + ... ado.net/XML headers & schema ... + <resheader name="resmimetype">text/microsoft-resx</resheader> + <resheader name="version">2.0</resheader> + <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> + <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> + <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data> + <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> + <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> + <value>[base64 mime encoded serialized .NET Framework object]</value> + </data> + <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> + <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value> + <comment>This is a comment</comment> + </data> + + There are any number of "resheader" rows that contain simple + name/value pairs. + + Each data row contains a name, and value. The row also contains a + type or mimetype. Type corresponds to a .NET class that support + text/value conversion through the TypeConverter architecture. + Classes that don't support this are serialized and stored with the + mimetype set. + + The mimetype is used for serialized objects, and tells the + ResXResourceReader how to depersist the object. This is currently not + extensible. For a given mimetype the value must be set accordingly: + + Note - application/x-microsoft.net.object.binary.base64 is the format + that the ResXResourceWriter will generate, however the reader can + read any of the formats listed below. + + mimetype: application/x-microsoft.net.object.binary.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.soap.base64 + value : The object must be serialized with + : System.Runtime.Serialization.Formatters.Soap.SoapFormatter + : and then encoded with base64 encoding. + + mimetype: application/x-microsoft.net.object.bytearray.base64 + value : The object must be serialized into a byte array + : using a System.ComponentModel.TypeConverter + : and then encoded with base64 encoding. + --> + <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> + <xsd:import namespace="http://www.w3.org/XML/1998/namespace" /> + <xsd:element name="root" msdata:IsDataSet="true"> + <xsd:complexType> + <xsd:choice maxOccurs="unbounded"> + <xsd:element name="metadata"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" /> + </xsd:sequence> + <xsd:attribute name="name" use="required" type="xsd:string" /> + <xsd:attribute name="type" type="xsd:string" /> + <xsd:attribute name="mimetype" type="xsd:string" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="assembly"> + <xsd:complexType> + <xsd:attribute name="alias" type="xsd:string" /> + <xsd:attribute name="name" type="xsd:string" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="data"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" /> + <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> + <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> + <xsd:attribute ref="xml:space" /> + </xsd:complexType> + </xsd:element> + <xsd:element name="resheader"> + <xsd:complexType> + <xsd:sequence> + <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> + </xsd:sequence> + <xsd:attribute name="name" type="xsd:string" use="required" /> + </xsd:complexType> + </xsd:element> + </xsd:choice> + </xsd:complexType> + </xsd:element> + </xsd:schema> + <resheader name="resmimetype"> + <value>text/microsoft-resx</value> + </resheader> + <resheader name="version"> + <value>2.0</value> + </resheader> + <resheader name="reader"> + <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <resheader name="writer"> + <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> + </resheader> + <data name="AboutPage_About" xml:space="preserve"> + <value>О программе</value> + </data> + <data name="AboutPage_AboutDescription" xml:space="preserve"> + <value>Набор служб Windows и оптимизированного под сенсорный дисплей интерфейса для расширения возможностей вашего портативного игрового устройства. Включает в себя следующие фукнции: управление движением(гироском), эмуляцию виртуального контроллера, оверлей быстрого доступа, виртуальные сенсорные панели, визуализация контрллера, систему настроек профиля для каждого приложения. Handheld Companion использует драйвер ViGEmBus и библиотеки ViGEmClient, а также драйвер фильтра режима ядра HidHide. Алгоритмы управления движением основаны на работе Jibbsmart и доступной информации на GyroWiki.</value> + </data> + <data name="AboutPage_Accelerometer" xml:space="preserve"> + <value>Акселерометр</value> + </data> + <data name="AboutPage_Author" xml:space="preserve"> + <value>Автор</value> + </data> + <data name="AboutPage_Contributors" xml:space="preserve"> + <value>Разработчики</value> + </data> + <data name="AboutPage_Description" xml:space="preserve"> + <value>Описание</value> + </data> + <data name="AboutPage_Donate" xml:space="preserve"> + <value>Помочь проекту</value> + </data> + <data name="AboutPage_Gyrometer" xml:space="preserve"> + <value>Гироскоп</value> + </data> + <data name="AboutPage_Inclinometer" xml:space="preserve"> + <value>Датчик наклона</value> + </data> + <data name="AboutPage_NotApplicable" xml:space="preserve"> + <value>Н/Д</value> + </data> + <data name="AboutPage_Partner" xml:space="preserve"> + <value>При поддержке</value> + </data> + <data name="AboutPage_RelatedLinks" xml:space="preserve"> + <value>Ссылки</value> + </data> + <data name="AboutPage_SensorExternal" xml:space="preserve"> + <value>Встроенные</value> + </data> + <data name="AboutPage_SensorInternal" xml:space="preserve"> + <value>Внешние</value> + </data> + <data name="AboutPage_SensorName" xml:space="preserve"> + <value>Название датчика</value> + </data> + <data name="AboutPage_SensorSpecification" xml:space="preserve"> + <value>Спецификации датчиков</value> + </data> + <data name="AboutPage_Service" xml:space="preserve"> + <value>Служба</value> + </data> + <data name="AboutPage_SourceCode" xml:space="preserve"> + <value>Исходный код</value> + </data> + <data name="AboutPage_Version" xml:space="preserve"> + <value>Версия</value> + </data> + <data name="AboutPage_Wiki" xml:space="preserve"> + <value>Вики</value> + </data> + <data name="Administrator" xml:space="preserve"> + <value>Администратор</value> + </data> + <data name="ButtonsPage_ABXY" xml:space="preserve"> + <value>A,B,X,Y</value> + </data> + <data name="ButtonsPage_Back_Grips" xml:space="preserve"> + <value>ЗАДНИЕ КНОПКИ</value> + </data> + <data name="ButtonsPage_Bumpers" xml:space="preserve"> + <value>БАМПЕРЫ</value> + </data> + <data name="ButtonsPage_Menu" xml:space="preserve"> + <value>MENU</value> + </data> + <data name="ButtonsPage_OEM" xml:space="preserve"> + <value>OEM</value> + </data> + <data name="ControllerPage_CloakDevice" xml:space="preserve"> + <value>Скрыть контроллер при подключении</value> + </data> + <data name="ControllerPage_CloakDeviceDesc" xml:space="preserve"> + <value>Скрыть физический контроллер при подключении</value> + </data> + <data name="ControllerPage_Connect" xml:space="preserve"> + <value>Подключить</value> + </data> + <data name="ControllerPage_Controller" xml:space="preserve"> + <value>Контроллер</value> + </data> + <data name="ControllerPage_DeviceCloaking" xml:space="preserve"> + <value>Маскировка контроллера</value> + </data> + <data name="ControllerPage_DeviceSettings" xml:space="preserve"> + <value>Настойки контроллера</value> + </data> + <data name="ControllerPage_Disconnect" xml:space="preserve"> + <value>Отключить</value> + </data> + <data name="ControllerPage_InputDevices" xml:space="preserve"> + <value>Устройства ввода</value> + </data> + <data name="ControllerPage_UncloakOnClose" xml:space="preserve"> + <value>Показать контроллер при закрытии</value> + </data> + <data name="ControllerPage_UncloakOnCloseDesc" xml:space="preserve"> + <value>Восстановить видимость всех физических контроллеров при выходе из приложения</value> + </data> + <data name="ControllerPage_VibrateDevice" xml:space="preserve"> + <value>Вибрация при подключении</value> + </data> + <data name="ControllerPage_VibrateDeviceDesc" xml:space="preserve"> + <value>Вибрировать при подключении контроллера</value> + </data> + <data name="ControllerPage_VibrationStrength" xml:space="preserve"> + <value>Сила вибрации</value> + </data> + <data name="ControllerPage_VibrationStrengthExpl" xml:space="preserve"> + <value>Изменить силу вибрации контрллера</value> + </data> + <data name="DPadPage_DPad" xml:space="preserve"> + <value>КРЕСТОВИНА</value> + </data> + <data name="InputsHotkey_decreaseBrightness" xml:space="preserve"> + <value>Уменьшить яркость</value> + </data> + <data name="InputsHotkey_decreaseBrightnessDesc" xml:space="preserve"> + <value>Уменьшить текущую яркость экрана на 5%</value> + </data> + <data name="InputsHotkey_decreaseTDP" xml:space="preserve"> + <value>Уменьшить лимит энергомотребления (TDP)</value> + </data> + <data name="InputsHotkey_decreaseTDPDesc" xml:space="preserve"> + <value>Уменьшить TDP системы или текущего профиля на 1 ватт</value> + </data> + <data name="InputsHotkey_decreaseVolume" xml:space="preserve"> + <value>Уменьшить громкость</value> + </data> + <data name="InputsHotkey_decreaseVolumeDesc" xml:space="preserve"> + <value>Уменьшить громкость на 5%</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabled" xml:space="preserve"> + <value>Раскладка рабочего стола</value> + </data> + <data name="InputsHotkey_DesktopLayoutEnabledDesc" xml:space="preserve"> + <value>Переключить раскладку рабочего стола</value> + </data> + <data name="InputsHotkey_fallbackInput" xml:space="preserve"> + <value>Нажмите, чтобы задать кнопку</value> + </data> + <data name="InputsHotkey_fallbackOutput" xml:space="preserve"> + <value>Нажмите, чтобы задать кнопку клавиатуры</value> + </data> + <data name="InputsHotkey_increaseBrightness" xml:space="preserve"> + <value>Увеличить яркость</value> + </data> + <data name="InputsHotkey_increaseBrightnessDesc" xml:space="preserve"> + <value>Увеличить якрость экрана на 5%</value> + </data> + <data name="InputsHotkey_increaseTDP" xml:space="preserve"> + <value>Увеличить лимит энергопотребления (TDP)</value> + </data> + <data name="InputsHotkey_increaseTDPDesc" xml:space="preserve"> + <value>Увеличить TDP системы или текущего профиля на 1 ватт</value> + </data> + <data name="InputsHotkey_increaseVolume" xml:space="preserve"> + <value>Увеличить громкость</value> + </data> + <data name="InputsHotkey_increaseVolumeDesc" xml:space="preserve"> + <value>Увеличить громкость на 5%</value> + </data> + <data name="InputsHotkey_OnScreenDisplay" xml:space="preserve"> + <value>Оверлей</value> + </data> + <data name="InputsHotkey_OnScreenDisplayDesc" xml:space="preserve"> + <value>Включить поддержку оверлея</value> + </data> + <data name="InputsHotkey_overlayGamepad" xml:space="preserve"> + <value>Отобразить оверлей контроллера</value> + </data> + <data name="InputsHotkey_overlayGamepadDesc" xml:space="preserve"> + <value>Change 3D hotkey by pressing a button or a special key</value> + </data> + <data name="InputsHotkey_overlayTrackpads" xml:space="preserve"> + <value>Отобразить виртуальные трекпады</value> + </data> + <data name="InputsHotkey_overlayTrackpadsDesc" xml:space="preserve"> + <value>Измените горячую клавишу нажатием кнопки или специальной клавиши</value> + </data> + <data name="InputsHotkey_quickTools" xml:space="preserve"> + <value>Открыть окно быстрых настроек</value> + </data> + <data name="InputsHotkey_quickToolsDesc" xml:space="preserve"> + <value>Измените горячую клавишу нажатием кнопки или специальной клавиши</value> + </data> + <data name="InputsHotkey_QuietModeToggled" xml:space="preserve"> + <value>Настройки вентилятора</value> + </data> + <data name="InputsHotkey_QuietModeToggledDesc" xml:space="preserve"> + <value>Установите рабочий цикл вентилятора на заданное пользователем значение</value> + </data> + <data name="InputsHotkey_shortcutControlCenter" xml:space="preserve"> + <value>Показать центр уведомлений</value> + </data> + <data name="InputsHotkey_shortcutControlCenterDesc" xml:space="preserve"> + <value>Отображение и скрытие Центра уведомлений Windows</value> + </data> + <data name="InputsHotkey_shortcutCustom" xml:space="preserve"> + <value>Назначить хоткей</value> + </data> + <data name="InputsHotkey_shortcutCustomDesc" xml:space="preserve"> + <value>Измените горячую клавишу нажатием кнопки или специальной клавиши</value> + </data> + <data name="InputsHotkey_shortcutDesktop" xml:space="preserve"> + <value>Показать или скрыть рабочий стол</value> + </data> + <data name="InputsHotkey_shortcutDesktopDesc" xml:space="preserve"> + <value>Путем нажатия: Windows + D</value> + </data> + <data name="InputsHotkey_shortcutESC" xml:space="preserve"> + <value>Escape</value> + </data> + <data name="InputsHotkey_shortcutESCDesc" xml:space="preserve"> + <value>Нажать кнопку: Escape</value> + </data> + <data name="InputsHotkey_shortcutExpand" xml:space="preserve"> + <value>Переключение между оконным и полноэкранным режимом</value> + </data> + <data name="InputsHotkey_shortcutExpandDesc" xml:space="preserve"> + <value>Нажать сочетание: Alt + Enter</value> + </data> + <data name="InputsHotkey_shortcutGuide" xml:space="preserve"> + <value>Guide или PS кнопка</value> + </data> + <data name="InputsHotkey_shortcutGuideDesc" xml:space="preserve"> + <value>Эмулировать Xbox Guide или Sony PS кнопки</value> + </data> + <data name="InputsHotkey_shortcutKeyboard" xml:space="preserve"> + <value>Показать экранную клавиатуру</value> + </data> + <data name="InputsHotkey_shortcutKeyboardDesc" xml:space="preserve"> + <value>Измените горячую клавишу нажатием кнопки или специальной клавиши</value> + </data> + <data name="InputsHotkey_shortcutKillApp" xml:space="preserve"> + <value>Принудитильное закрытие приложение</value> + </data> + <data name="InputsHotkey_shortcutKillAppDesc" xml:space="preserve"> + <value>Измените горячую клавишу нажатием кнопки или специальной клавиши</value> + </data> + <data name="InputsHotkey_shortcutMainwindow" xml:space="preserve"> + <value>Переключить окно</value> + </data> + <data name="InputsHotkey_shortcutMainwindowDesc" xml:space="preserve"> + <value>Измените горячую клавишу нажатием кнопки или специальной клавиши</value> + </data> + <data name="InputsHotkey_shortcutPrintScreen" xml:space="preserve"> + <value>Открыть инструмент «Ножницы»</value> + </data> + <data name="InputsHotkey_shortcutPrintScreenDesc" xml:space="preserve"> + <value>Нажать сочетание: Windows + Shift + S</value> + </data> + <data name="InputsHotkey_shortcutTaskManager" xml:space="preserve"> + <value>Открыть диспетчер задач</value> + </data> + <data name="InputsHotkey_shortcutTaskManagerDesc" xml:space="preserve"> + <value>Нажать сочетание: Ctrl + Shift + Esc</value> + </data> + <data name="InputsHotkey_shortcutTaskview" xml:space="preserve"> + <value>Открыть режим представление задач</value> + </data> + <data name="InputsHotkey_shortcutTaskviewDesc" xml:space="preserve"> + <value>Нажать сочетание: Windows + Tab</value> + </data> + <data name="InputsHotkey_suspendResumeTask" xml:space="preserve"> + <value>Переключатель паузы</value> + </data> + <data name="InputsHotkey_suspendResumeTaskDesc" xml:space="preserve"> + <value>Приостановить или возобновить работу текущего приложения</value> + </data> + <data name="JoystickPage_Joystick_Left" xml:space="preserve"> + <value>ЛЕВЫЙ СТИК</value> + </data> + <data name="JoystickPage_Joystick_Left_Buttons" xml:space="preserve"> + <value>КНОПКИ ЛЕВОГО СТИКА</value> + </data> + <data name="JoystickPage_Joystick_Right" xml:space="preserve"> + <value>ПРАВЫЙ СТИК</value> + </data> + <data name="JoystickPage_Joystick_Right_Buttons" xml:space="preserve"> + <value>КНОПКИ ПРАВОГО СТИКА</value> + </data> + <data name="MainWindow_HandheldCompanion" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="MainWindow_navAbout" xml:space="preserve"> + <value>О программе</value> + </data> + <data name="MainWindow_navController" xml:space="preserve"> + <value>Контроллер</value> + </data> + <data name="MainWindow_navHotkeys" xml:space="preserve"> + <value>Хоткеи</value> + </data> + <data name="MainWindow_navOverlay" xml:space="preserve"> + <value>Оверлей</value> + </data> + <data name="MainWindow_navProfiles" xml:space="preserve"> + <value>Профили</value> + </data> + <data name="MainWindow_OK" xml:space="preserve"> + <value>ОК</value> + </data> + <data name="MainWindow_Settings" xml:space="preserve"> + <value>Настройки</value> + </data> + <data name="OverlayPage_8BitDoLite2Controller" xml:space="preserve"> + <value>8BitDo Lite 2</value> + </data> + <data name="OverlayPage_Alignment" xml:space="preserve"> + <value>Выравнивание</value> + </data> + <data name="OverlayPage_AlignmentDesc" xml:space="preserve"> + <value>Изменить расположение оверлея контроллера</value> + </data> + <data name="OverlayPage_AlignmentTrackpadDesc" xml:space="preserve"> + <value>Изменение расположение оверлея трекпадов</value> + </data> + <data name="OverlayPage_AlwaysOnTop" xml:space="preserve"> + <value>Всегда поверх все окон</value> + </data> + <data name="OverlayPage_AlwaysOnTopDesc" xml:space="preserve"> + <value>Когда включено, оверлей контроллера отображается поверх все окон</value> + </data> + <data name="OverlayPage_BackButton" xml:space="preserve"> + <value>Назад</value> + </data> + <data name="OverlayPage_CameraAngle" xml:space="preserve"> + <value>Face camera</value> + </data> + <data name="OverlayPage_CameraAngleDesc" xml:space="preserve"> + <value>Change 3D controller overlay model behaviour for facing the camera</value> + </data> + <data name="OverlayPage_CameraAnglePitch" xml:space="preserve"> + <value>Stationary pitch</value> + </data> + <data name="OverlayPage_CameraAnglePitchDesc" xml:space="preserve"> + <value>Change the angle, in degree</value> + </data> + <data name="OverlayPage_Color" xml:space="preserve"> + <value>Цвет фона</value> + </data> + <data name="OverlayPage_ColorDesc" xml:space="preserve"> + <value>Изменить цвет фона наложения оверлея контроллера</value> + </data> + <data name="OverlayPage_ControllerOptions" xml:space="preserve"> + <value>Оверлей контроллера</value> + </data> + <data name="OverlayPage_DualSenseController" xml:space="preserve"> + <value>Playstation DualSense</value> + </data> + <data name="OverlayPage_EmulatedController" xml:space="preserve"> + <value>Эмулируемый контроллер</value> + </data> + <data name="OverlayPage_FaceCamera" xml:space="preserve"> + <value>Face camera</value> + </data> + <data name="OverlayPage_FaceCameraDesc" xml:space="preserve"> + <value>3D Model slowly rotates to face camera as default position</value> + </data> + <data name="OverlayPage_Listening" xml:space="preserve"> + <value>Ожидаю...</value> + </data> + <data name="OverlayPage_MachenikeHG510Controller" xml:space="preserve"> + <value>MACHENIKE HG510</value> + </data> + <data name="OverlayPage_MainTrigger" xml:space="preserve"> + <value>Main trigger</value> + </data> + <data name="OverlayPage_MainTriggerDesc" xml:space="preserve"> + <value>Change 3D controller overlay main trigger</value> + </data> + <data name="OverlayPage_MotionActivated" xml:space="preserve"> + <value>Движение</value> + </data> + <data name="OverlayPage_MotionActivatedDesc" xml:space="preserve"> + <value>Модель будет двигаться в соответствии с движениями пользователя на основе информации датчиков</value> + </data> + <data name="OverlayPage_N64Controller" xml:space="preserve"> + <value>N64</value> + </data> + <data name="OverlayPage_OEMController" xml:space="preserve"> + <value>OEM контроллер</value> + </data> + <data name="OverlayPage_Opacity" xml:space="preserve"> + <value>Прозрачность</value> + </data> + <data name="OverlayPage_OpacityControllerDesc" xml:space="preserve"> + <value>Изменить непрозрачность оверлея контроллера</value> + </data> + <data name="OverlayPage_OpacityTrackpadDesc" xml:space="preserve"> + <value>Изменить непрозрачность оверлея трекпада</value> + </data> + <data name="OverlayPage_Overlay" xml:space="preserve"> + <value>Оверлей</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel" xml:space="preserve"> + <value>Уровень отображаемых оверлеем данных</value> + </data> + <data name="OverlayPage_OverlayDisplayLevelDesc" xml:space="preserve"> + <value>Изменение уровня отображаемой информации на экране</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRate" xml:space="preserve"> + <value>Скорость обновления</value> + </data> + <data name="OverlayPage_OverlayDisplayUpdateRateDesc" xml:space="preserve"> + <value>Изменить скорость обновления данных</value> + </data> + <data name="OverlayPage_OverlayModel" xml:space="preserve"> + <value>Вид контроллера</value> + </data> + <data name="OverlayPage_OverlayModelDesc" xml:space="preserve"> + <value>Изменить вид оверлея контроллера</value> + </data> + <data name="OverlayPage_OverlayPreview" xml:space="preserve"> + <value>Предпросмотр оверлея</value> + </data> + <data name="OverlayPage_RenderSettings" xml:space="preserve"> + <value>Настройки рендеринга</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasing" xml:space="preserve"> + <value>Сглаживание</value> + </data> + <data name="OverlayPage_RenderSettingsAntialiasingDesc" xml:space="preserve"> + <value>Изменить уровень сглаживания рендеринга</value> + </data> + <data name="OverlayPage_RenderSettingsDesc" xml:space="preserve"> + <value>Измените настройки рендеринга оверлея контроллера.</value> + </data> + <data name="OverlayPage_RenderSettingsFramerate" xml:space="preserve"> + <value>Скорость обновления</value> + </data> + <data name="OverlayPage_RenderSettingsFramerateDesc" xml:space="preserve"> + <value>Изменить скорость обновления рендеринга, в обновлениях в секунду</value> + </data> + <data name="OverlayPage_Size" xml:space="preserve"> + <value>Размер</value> + </data> + <data name="OverlayPage_SizeDesc" xml:space="preserve"> + <value>Изменит размер отображаемой модели контроллера</value> + </data> + <data name="OverlayPage_SizeOverlayDesc" xml:space="preserve"> + <value>Изменить размер оверлея трекпадов</value> + </data> + <data name="OverlayPage_StartButton" xml:space="preserve"> + <value>Старт</value> + </data> + <data name="OverlayPage_ToyController" xml:space="preserve"> + <value>Fisher-Price controller</value> + </data> + <data name="OverlayPage_TrackpadsOptions" xml:space="preserve"> + <value>Настройки трекпадов</value> + </data> + <data name="OverlayPage_XboxOneController" xml:space="preserve"> + <value>Xbox One</value> + </data> + <data name="OverlayPage_ZDOPlusController" xml:space="preserve"> + <value>ZD O+</value> + </data> + <data name="Overlay_Overlay" xml:space="preserve"> + <value>Оверлей</value> + </data> + <data name="ProcessEx_processResume" xml:space="preserve"> + <value>Продолжить</value> + </data> + <data name="ProcessEx_processSuspend" xml:space="preserve"> + <value>Приостановить</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplier" xml:space="preserve"> + <value>Множитель акселерометра</value> + </data> + <data name="ProfilesPage_AccelerometerMultiplierDesc" xml:space="preserve"> + <value>Изменяет значение получаемых от акселерометра данных</value> + </data> + <data name="ProfilesPage_AdditionalSettings" xml:space="preserve"> + <value>Дополнительные настройки</value> + </data> + <data name="ProfilesPage_AntiDeadzone" xml:space="preserve"> + <value>Уменьшение мертвых зон</value> + </data> + <data name="ProfilesPage_AntiDeadzoneDesc" xml:space="preserve"> + <value>Изменить внутриигровую мёртвую зону, в процентах</value> + </data> + <data name="ProfilesPage_AntiDeadzoneUnitPercentage" xml:space="preserve"> + <value> %</value> + </data> + <data name="ProfilesPage_AreYouSureDelete1" xml:space="preserve"> + <value>Вы уверены что хотите удалить?</value> + </data> + <data name="ProfilesPage_AreYouSureDelete2" xml:space="preserve"> + <value>Этот элемент будет немедленно удален. Это действие нельзя отменить.</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite1" xml:space="preserve"> + <value>Вы уверены что хотите переписать профиль?</value> + </data> + <data name="ProfilesPage_AreYouSureOverwrite2" xml:space="preserve"> + <value>{0} будет перезаписан. Это действие нельзя отменить.</value> + </data> + <data name="ProfilesPage_AutoTDP" xml:space="preserve"> + <value>Автоматический TDP</value> + </data> + <data name="ProfilesPage_AutoTDPDesc" xml:space="preserve"> + <value>Автоматическая подстойка TDP на основе текущего FPS и целевого FPS</value> + </data> + <data name="ProfilesPage_AutoTDPFPS" xml:space="preserve"> + <value>Целевая частота кадров</value> + </data> + <data name="ProfilesPage_AutoTDPFPSDesc" xml:space="preserve"> + <value>Желаемая частота кадров для автоматического контроля TDP</value> + </data> + <data name="ProfilesPage_BoostPower" xml:space="preserve"> + <value>Лимит энергопотребления турбо</value> + </data> + <data name="ProfilesPage_BoostPowerDesc" xml:space="preserve"> + <value>Изменить лимит энергопотребления для турбо режима</value> + </data> + <data name="ProfilesPage_Cancel" xml:space="preserve"> + <value>Отмена</value> + </data> + <data name="ProfilesPage_ControllerLayout" xml:space="preserve"> + <value>Раскладка контроллера</value> + </data> + <data name="ProfilesPage_ControllerSettings" xml:space="preserve"> + <value>Настройки контроллера</value> + </data> + <data name="ProfilesPage_ControllerSettingsDesc" xml:space="preserve"> + <value>Изменить настройки виртуального контроллера</value> + </data> + <data name="ProfilesPage_CreateNewProfile" xml:space="preserve"> + <value>Создать новый профиль</value> + </data> + <data name="ProfilesPage_Delete" xml:space="preserve"> + <value>Удалить</value> + </data> + <data name="ProfilesPage_DeleteProfile" xml:space="preserve"> + <value>Удалить профиль</value> + </data> + <data name="ProfilesPage_EnableProfile" xml:space="preserve"> + <value>Пользовательские профили для игры</value> + </data> + <data name="ProfilesPage_EnableProfileDesc" xml:space="preserve"> + <value>Профиль будет применен автоматически при обнаружении указанного приложения</value> + </data> + <data name="ProfilesPage_FramerateLimit" xml:space="preserve"> + <value>Лимит частоты кадров</value> + </data> + <data name="ProfilesPage_FramerateLimitDesc" xml:space="preserve"> + <value>Ограничивает частоту кадров для 3D приложений</value> + </data> + <data name="ProfilesPage_GlobalSettings" xml:space="preserve"> + <value>Глобальные настройки</value> + </data> + <data name="ProfilesPage_GlobalSettingsDesc" xml:space="preserve"> + <value>Изменить глобальные настройки профилей</value> + </data> + <data name="ProfilesPage_GPUMhz" xml:space="preserve"> + <value>Максимальная частота GPU</value> + </data> + <data name="ProfilesPage_GPUMhzDesc" xml:space="preserve"> + <value>Максимальная частота GPU В МГц</value> + </data> + <data name="ProfilesPage_GyrometerMultiplier" xml:space="preserve"> + <value>Множитель гироскопа</value> + </data> + <data name="ProfilesPage_GyrometerMultiplierDesc" xml:space="preserve"> + <value>Изменяет значение получаемых от гироскопа данных</value> + </data> + <data name="ProfilesPage_GyroSteeringAxis" xml:space="preserve"> + <value>Ось гироскопа</value> + </data> + <data name="ProfilesPage_GyroSteeringAxisDesc" xml:space="preserve"> + <value>Для управления горизонтальным перемещением контроллера вы можете использовать либо вертикальную ось, либо горизонтпльную.</value> + </data> + <data name="ProfilesPage_InvertHorizontalAxis" xml:space="preserve"> + <value>Инвертировать горизонтальную ось</value> + </data> + <data name="ProfilesPage_InvertVerticalAxis" xml:space="preserve"> + <value>Инвертировать вертикальную ось</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeft" xml:space="preserve"> + <value>% Остаточной мертвый зоны левого стика</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneLeftDesc" xml:space="preserve"> + <value>Регулировка процента внутренней и внешней мертвой зоны левого стика</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRight" xml:space="preserve"> + <value>% Остаточной мертвый зоны правого стика</value> + </data> + <data name="ProfilesPage_JoystickDeadZoneRightDesc" xml:space="preserve"> + <value>Регулировка процента внутренней и внешней мертвой зоны правого стика</value> + </data> + <data name="ProfilesPage_MotionControlSettings" xml:space="preserve"> + <value>Настройки управления движением</value> + </data> + <data name="ProfilesPage_MotionControlSettingsDesc" xml:space="preserve"> + <value>Изменить глобальные настройки управления движением</value> + </data> + <data name="ProfilesPage_OK" xml:space="preserve"> + <value>ОК</value> + </data> + <data name="ProfilesPage_PowerSettings" xml:space="preserve"> + <value>Настройки электропитания</value> + </data> + <data name="ProfilesPage_PowerSettingsDesc" xml:space="preserve"> + <value>Изменить параметры питания</value> + </data> + <data name="ProfilesPage_ProfileDetails" xml:space="preserve"> + <value>Информация о профиле</value> + </data> + <data name="ProfilesPage_ProfileName" xml:space="preserve"> + <value>Имя профиля</value> + </data> + <data name="ProfilesPage_ProfilePath" xml:space="preserve"> + <value>Путь профиля</value> + </data> + <data name="ProfilesPage_Profiles" xml:space="preserve"> + <value>Профили</value> + </data> + <data name="ProfilesPage_ProfileSelection" xml:space="preserve"> + <value>Выбор профиля</value> + </data> + <data name="ProfilesPage_ProfileSelectionDesc" xml:space="preserve"> + <value>Выберите профиль, который хотите изменить</value> + </data> + <data name="ProfilesPage_ProfileSettings" xml:space="preserve"> + <value>Настройки профиля</value> + </data> + <data name="ProfilesPage_ProfileUpdated1" xml:space="preserve"> + <value>Профиль обновлен</value> + </data> + <data name="ProfilesPage_ProfileUpdated2" xml:space="preserve"> + <value>обновлен.</value> + </data> + <data name="ProfilesPage_Roll" xml:space="preserve"> + <value>Горизонтальная</value> + </data> + <data name="ProfilesPage_SensitivityX" xml:space="preserve"> + <value>X</value> + </data> + <data name="ProfilesPage_SensitivityY" xml:space="preserve"> + <value>Y</value> + </data> + <data name="ProfilesPage_StyleofInput" xml:space="preserve"> + <value>Тип ввода</value> + </data> + <data name="ProfilesPage_StyleofInputTooltip" xml:space="preserve"> + <value>Физические входы контроллера можно запрограммировать, чтобы они действовали как различные типы устройств.</value> + </data> + <data name="ProfilesPage_StyleofOutput" xml:space="preserve"> + <value>Устройство вывода</value> + </data> + <data name="ProfilesPage_StyleofOutputTooltip" xml:space="preserve"> + <value>Выберите устройство, которое будет получать команды движения</value> + </data> + <data name="ProfilesPage_SustainedPower" xml:space="preserve"> + <value>Постоянный лимит энергопотребления</value> + </data> + <data name="ProfilesPage_SustainedPowerDesc" xml:space="preserve"> + <value>Изменяет установенный лимит энергопотребления</value> + </data> + <data name="ProfilesPage_TDPOverride" xml:space="preserve"> + <value>Лимит энергопотребления(TDP)</value> + </data> + <data name="ProfilesPage_TDPOverrideDesc" xml:space="preserve"> + <value>Ограничивает мощность процессора для уменьшения общей мощности</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityDesc" xml:space="preserve"> + <value>Improve the stick circularity</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityLeft" xml:space="preserve"> + <value>Circularity stick left</value> + </data> + <data name="ProfilesPage_ThumbImproveCircularityRight" xml:space="preserve"> + <value>Circularity stick right</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeft" xml:space="preserve"> + <value>Мертвая зона левого триггера %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneLeftDesc" xml:space="preserve"> + <value>Настройка внутренней и внешней мертвой зоны левого триггера</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRight" xml:space="preserve"> + <value>Мертвая зона правого триггера %</value> + </data> + <data name="ProfilesPage_TriggerDeadZoneRightDesc" xml:space="preserve"> + <value>Настройка внутренней и внешней мертвой зоны правого триггера</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzone" xml:space="preserve"> + <value>Защита от мертвой зоны</value> + </data> + <data name="ProfilesPage_UMCAntiDeadzoneDesc" xml:space="preserve"> + <value>Компенсирует внутриигровую мертвую зону, улучшает регистрацию небольших движений</value> + </data> + <data name="ProfilesPage_UMCEnable" xml:space="preserve"> + <value>Включить универсальный контроллер движения</value> + </data> + <data name="ProfilesPage_UMCMotionOff" xml:space="preserve"> + <value>Выключено, включение по кнопке</value> + </data> + <data name="ProfilesPage_UMCMotionOn" xml:space="preserve"> + <value>Включено, отключение по кнопке</value> + </data> + <data name="ProfilesPage_UMCMotionOnOff" xml:space="preserve"> + <value>Активация управления движением</value> + </data> + <data name="ProfilesPage_UMCMotionOnOffDesc" xml:space="preserve"> + <value>При отключенном управлении движением используйте выбранные кнопки для включения, +при включенном используйте выбранные кнопки для отключения.</value> + </data> + <data name="ProfilesPage_UMCSelectionRightLeftDesc" xml:space="preserve"> + <value>Выберите устройство, которое будет получать команды управления движением</value> + </data> + <data name="ProfilesPage_UMCSettings" xml:space="preserve"> + <value>Универсальные настройки управления движением</value> + </data> + <data name="ProfilesPage_UMCSettingsDesc" xml:space="preserve"> + <value>Преобразовывать движения устройтва в ввод с контроллера</value> + </data> + <data name="ProfilesPage_UpdateProfile" xml:space="preserve"> + <value>Обновить профил</value> + </data> + <data name="ProfilesPage_Whitelist" xml:space="preserve"> + <value>Разрешить приложению доступ к физическому контроллеру устройства</value> + </data> + <data name="ProfilesPage_Wrapper" xml:space="preserve"> + <value>Расширенная совместимость (XInputPlus)</value> + </data> + <data name="ProfilesPage_Yaw" xml:space="preserve"> + <value>Вертикальная</value> + </data> + <data name="ProfilesPage_Yes" xml:space="preserve"> + <value>Да</value> + </data> + <data name="Properties.Resources.ControllerPage_Disconnect" xml:space="preserve"> + <value>Отключить</value> + </data> + <data name="QuickPerformancePage_AutoTDPUnitFPS" xml:space="preserve"> + <value>FPS</value> + </data> + <data name="QuickPerformancePage_GPUControl" xml:space="preserve"> + <value>Ручной контроль частоты GPU</value> + </data> + <data name="QuickPerformancePage_GPUControlDesc" xml:space="preserve"> + <value>Устанавливает фиксированную частоту GPU</value> + </data> + <data name="QuickPerformancePage_GPUUnit" xml:space="preserve"> + <value> ГГц</value> + </data> + <data name="QuickPerformancePage_PowerMode" xml:space="preserve"> + <value>Режим энергопотребления</value> + </data> + <data name="QuickPerformancePage_PowerModeBalanced" xml:space="preserve"> + <value>Баланс</value> + </data> + <data name="QuickPerformancePage_PowerModeDesc" xml:space="preserve"> + <value>Оптимизизация на основе энергопотребления и производительности</value> + </data> + <data name="QuickPerformancePage_PowerModeEfficiency" xml:space="preserve"> + <value>Экономия</value> + </data> + <data name="QuickPerformancePage_PowerModePerformance" xml:space="preserve"> + <value>Производительный</value> + </data> + <data name="QuickPerformancePage_TDPBoost" xml:space="preserve"> + <value>Турбо</value> + </data> + <data name="QuickPerformancePage_TDPLimit" xml:space="preserve"> + <value>Лимит энергопотребления (TDP)</value> + </data> + <data name="QuickPerformancePage_TDPLimitDesc" xml:space="preserve"> + <value>Ограничивает мощность процессора для уменьшения общего энергомотребления</value> + </data> + <data name="QuickPerformancePage_TDPOverWrittenWarning" xml:space="preserve"> + <value>TDP переназначен профилем</value> + </data> + <data name="QuickPerformancePage_TDPSustained" xml:space="preserve"> + <value>Постоянный</value> + </data> + <data name="QuickPerformancePage_TDPUnitWatt" xml:space="preserve"> + <value> Вт</value> + </data> + <data name="QuickProfilesPage_Create" xml:space="preserve"> + <value>Созданить профиль</value> + </data> + <data name="QuickProfilesPage_Waiting" xml:space="preserve"> + <value>Ожидание завершения фоновых процессов...</value> + </data> + <data name="ServiceDescription" xml:space="preserve"> + <value>Обеспечивает поддержку гироскопа и акселерометра для Windows через эмуляцию контроллера. Если служба включена, встроенный контроллер будет скрыт от приложений, не входящих в белый список. Если служба отключена, встроенный контроллер будет включен, а виртуальный контроллер отключен.</value> + </data> + <data name="ServiceName" xml:space="preserve"> + <value>Служба игрового контроллера</value> + </data> + <data name="SettingsMode0_AdditionalSettings" xml:space="preserve"> + <value>Дополнительные настройки</value> + </data> + <data name="SettingsMode0_AimingDownSights" xml:space="preserve"> + <value>Aiming down sights motion multiplier</value> + </data> + <data name="SettingsMode0_AimingDownSightsActivation" xml:space="preserve"> + <value>Кнопка включения</value> + </data> + <data name="SettingsMode0_AimingDownSightsDesc" xml:space="preserve"> + <value>An additional motion sensitivity multiplier when aiming down sights or scope through the use of the configured activation button</value> + </data> + <data name="SettingsMode0_AimingDownSightsMultiplier" xml:space="preserve"> + <value>Значение множителя</value> + </data> + <data name="SettingsMode0_CameraOptions" xml:space="preserve"> + <value>Camera options</value> + </data> + <data name="SettingsMode0_CustomResponseCurve" xml:space="preserve"> + <value>Custom response curve</value> + </data> + <data name="SettingsMode0_CustomResponseCurveGameOutput" xml:space="preserve"> + <value>Output sent to game</value> + </data> + <data name="SettingsMode0_CustomResponseIntensity" xml:space="preserve"> + <value>Movement intensity</value> + </data> + <data name="SettingsMode0_CustomResponsePresetAgressive" xml:space="preserve"> + <value>Agressive</value> + </data> + <data name="SettingsMode0_CustomResponsePresetDefault" xml:space="preserve"> + <value>Default</value> + </data> + <data name="SettingsMode0_CustomResponsePresetOptions" xml:space="preserve"> + <value>Preset options</value> + </data> + <data name="SettingsMode0_CustomResponsePresetPrecise" xml:space="preserve"> + <value>Precise</value> + </data> + <data name="SettingsMode0_FlickDuration" xml:space="preserve"> + <value>Flick duration</value> + </data> + <data name="SettingsMode0_FlickDurationDesc" xml:space="preserve"> + <value>Change the flick duration, calibrate to 180 degree turn, in milliseconds</value> + </data> + <data name="SettingsMode0_FlickStick" xml:space="preserve"> + <value>Flick stick (experimental)</value> + </data> + <data name="SettingsMode0_FlickStickDesc" xml:space="preserve"> + <value>Point camera in direction of (right) joystick flick, rotate camera purely in horizontal plane by rotating</value> + </data> + <data name="SettingsMode0_FlickStickEnable" xml:space="preserve"> + <value>Enable flick stick</value> + </data> + <data name="SettingsMode0_Sensitivity" xml:space="preserve"> + <value>Sensitivity</value> + </data> + <data name="SettingsMode0_SensitivityDesc" xml:space="preserve"> + <value>Change the motion sensitivity of the horizontal and vertical axis</value> + </data> + <data name="SettingsMode0_SensitivityX" xml:space="preserve"> + <value>Sensitivity X</value> + </data> + <data name="SettingsMode0_SensitivityXDesc" xml:space="preserve"> + <value>Change the motion sensitivity of the horizontal axis</value> + </data> + <data name="SettingsMode0_SensitivityY" xml:space="preserve"> + <value>Sensitivity Y</value> + </data> + <data name="SettingsMode0_SensitivityYDesc" xml:space="preserve"> + <value>Change the motion sensitivity of the vertical axis</value> + </data> + <data name="SettingsMode0_StickSensitivtity" xml:space="preserve"> + <value>Чувствительность стика</value> + </data> + <data name="SettingsMode0_StickSensitivtityDesc" xml:space="preserve"> + <value>Change the rotation rate</value> + </data> + <data name="SettingsMode1_AdditionalSettings" xml:space="preserve"> + <value>Дополнительные настройки</value> + </data> + <data name="SettingsMode1_Deadzone" xml:space="preserve"> + <value>Мертвая зона</value> + </data> + <data name="SettingsMode1_DeadzoneDesc" xml:space="preserve"> + <value>Change the steering deadzone, in degree. Improves steering straight</value> + </data> + <data name="SettingsMode1_JoystickGameInput" xml:space="preserve"> + <value>Joystick game Input</value> + </data> + <data name="SettingsMode1_JoystickSteering" xml:space="preserve"> + <value>Joystick steering</value> + </data> + <data name="SettingsMode1_JoystickSteeringOptions" xml:space="preserve"> + <value>Joystick steering options</value> + </data> + <data name="SettingsMode1_JoystickSteeringPreview" xml:space="preserve"> + <value>Joystick steering preview</value> + </data> + <data name="SettingsMode1_MaxSteeringAngle" xml:space="preserve"> + <value>Max steering angle</value> + </data> + <data name="SettingsMode1_MaxSteeringAngleDesc" xml:space="preserve"> + <value>Change the maximum steering angle value, in degree</value> + </data> + <data name="SettingsMode1_SteeringLinearity" xml:space="preserve"> + <value>Steering linearity</value> + </data> + <data name="SettingsMode1_SteeringLinearityDesc" xml:space="preserve"> + <value>Mapping between input and steering. Lower values provide more accuracy near full lock but less accuracy near the center. Higher values provide more accuracy near the center but less accuracy near full lock. 1.0 is a linear mapping</value> + </data> + <data name="SettingsPage_AppLanguage" xml:space="preserve"> + <value>Язык</value> + </data> + <data name="SettingsPage_AppLanguageDesc" xml:space="preserve"> + <value>Язык приложения</value> + </data> + <data name="SettingsPage_AppLanguageWarning" xml:space="preserve"> + <value>Требуется перезапуск</value> + </data> + <data name="SettingsPage_AppLanguageWarningDesc" xml:space="preserve"> + <value>Чтобы изменения вступили в силу, перезапустите приложение</value> + </data> + <data name="SettingsPage_AppTheme" xml:space="preserve"> + <value>Тема</value> + </data> + <data name="SettingsPage_AppThemeDesc" xml:space="preserve"> + <value>Тема приложения, светлая или темная</value> + </data> + <data name="SettingsPage_AutoStartApp" xml:space="preserve"> + <value>Автозапуск</value> + </data> + <data name="SettingsPage_AutoStartAppDesc" xml:space="preserve"> + <value>Приложение будет запускаться автоматически при входе в Windows.</value> + </data> + <data name="SettingsPage_Backdrop" xml:space="preserve"> + <value>Фон</value> + </data> + <data name="SettingsPage_BackdropAcrylic" xml:space="preserve"> + <value>Акрил</value> + </data> + <data name="SettingsPage_BackdropDesc" xml:space="preserve"> + <value>Фон приложения, пусто, матовый, tabbed, акрил</value> + </data> + <data name="SettingsPage_BackdropMica" xml:space="preserve"> + <value>Матовый</value> + </data> + <data name="SettingsPage_BackdropNone" xml:space="preserve"> + <value>Пусто</value> + </data> + <data name="SettingsPage_BackdropTabbed" xml:space="preserve"> + <value>Tabbed</value> + </data> + <data name="SettingsPage_CheckForUpdates" xml:space="preserve"> + <value>Проверить обновления</value> + </data> + <data name="SettingsPage_CloseMinimizes" xml:space="preserve"> + <value>Сворачивать при закрытии</value> + </data> + <data name="SettingsPage_CloseMinimizesDesc" xml:space="preserve"> + <value>Приложение будет свернуто в трей вместо закрытия</value> + </data> + <data name="SettingsPage_Download" xml:space="preserve"> + <value>Загрузка</value> + </data> + <data name="SettingsPage_DownloadingPercentage" xml:space="preserve"> + <value>Загрузка - </value> + </data> + <data name="SettingsPage_EcoQoS" xml:space="preserve"> + <value>Умное энергопотребление</value> + </data> + <data name="SettingsPage_EcoQoSDesc" xml:space="preserve"> + <value>Режим умного энергопотребления снижает приоритет фоновых процессов и повышает энергоэффективность</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStart" xml:space="preserve"> + <value>Включить профиль рабочего стола при запуске</value> + </data> + <data name="SettingsPage_EnableDesktopProfileOnStartDesc" xml:space="preserve"> + <value>Профиль рабочего стола будет автоматически включен при запуске приложения.</value> + </data> + <data name="SettingsPage_GeneralOptions" xml:space="preserve"> + <value>Общие настройки</value> + </data> + <data name="SettingsPage_InstallNow" xml:space="preserve"> + <value>Установить сейчас</value> + </data> + <data name="SettingsPage_LastChecked" xml:space="preserve"> + <value>Последняя проверка: </value> + </data> + <data name="SettingsPage_NotificationOptions" xml:space="preserve"> + <value>Настройки уведомлений</value> + </data> + <data name="SettingsPage_OpenAppBackground" xml:space="preserve"> + <value>Открыть приложение в фоновом режиме</value> + </data> + <data name="SettingsPage_OpenAppBackgroundDesc" xml:space="preserve"> + <value>По-умолчанию приложение запустится в свернутом виде и появится на панели задач.</value> + </data> + <data name="SettingsPage_SensorExternal" xml:space="preserve"> + <value>Встроенны</value> + </data> + <data name="SettingsPage_SensorInternal" xml:space="preserve"> + <value>Внешний</value> + </data> + <data name="SettingsPage_SensorOptions" xml:space="preserve"> + <value>Настройки датчиков</value> + </data> + <data name="SettingsPage_SensorPlacementDirection" xml:space="preserve"> + <value>Направление размещения внешнего датчика</value> + </data> + <data name="SettingsPage_SensorPlacementDirectionDesc" xml:space="preserve"> + <value>Выберите, на какой стороне устройства был установлен датчик</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDown" xml:space="preserve"> + <value>Внешний датчик перевернут</value> + </data> + <data name="SettingsPage_SensorPlacementUpsideDownDesc" xml:space="preserve"> + <value>Датчик был установлен вверх ногами, возможно через USB-C переходник.</value> + </data> + <data name="SettingsPage_SensorSelection" xml:space="preserve"> + <value>Выбор датчика</value> + </data> + <data name="SettingsPage_SensorSelectionDesc" xml:space="preserve"> + <value>Выберите нужный датчик, используемый для управление движением</value> + </data> + <data name="SettingsPage_Settings" xml:space="preserve"> + <value>Настройки</value> + </data> + <data name="SettingsPage_StartupType" xml:space="preserve"> + <value>Тип запуска</value> + </data> + <data name="SettingsPage_StartupTypeDesc" xml:space="preserve"> + <value>Используется диспетчером служб для определения типа запуска службы.</value> + </data> + <data name="SettingsPage_TDPMax" xml:space="preserve"> + <value>Максимальное энергопотребление</value> + </data> + <data name="SettingsPage_TDPMaxDesc" xml:space="preserve"> + <value>Максимальная мощность в ваттах для процессора</value> + </data> + <data name="SettingsPage_TDPMin" xml:space="preserve"> + <value>Минимальное энергопотребление</value> + </data> + <data name="SettingsPage_TDPMinDesc" xml:space="preserve"> + <value>Минимальная мощность в ваттах для процессора</value> + </data> + <data name="SettingsPage_TDPRangeOverride" xml:space="preserve"> + <value>Переопределение настраиваемой мощности (cTDP)</value> + </data> + <data name="SettingsPage_TDPRangeOverrideDesc" xml:space="preserve"> + <value>Позволяет изменять минимальные и максимальные значения мощности (TDP) вне зависимости от спецификаций ЦП.</value> + </data> + <data name="SettingsPage_ThemeDark" xml:space="preserve"> + <value>Темная</value> + </data> + <data name="SettingsPage_ThemeDefault" xml:space="preserve"> + <value>Использовать системное значение</value> + </data> + <data name="SettingsPage_ThemeLight" xml:space="preserve"> + <value>Светлая</value> + </data> + <data name="SettingsPage_ToastNotification" xml:space="preserve"> + <value>Всплывающие уведомления</value> + </data> + <data name="SettingsPage_ToastNotificationDesc" xml:space="preserve"> + <value>Получать уведомления от приложения в центре уведомлений Windows</value> + </data> + <data name="SettingsPage_UpdateAvailable" xml:space="preserve"> + <value>Доступны обновления</value> + </data> + <data name="SettingsPage_UpdateCheck" xml:space="preserve"> + <value>Проверка обновлений...</value> + </data> + <data name="SettingsPage_UpdateFailedDownload" xml:space="preserve"> + <value>Не удалось скачать файл обновления.</value> + </data> + <data name="SettingsPage_UpdateFailedGithub" xml:space="preserve"> + <value>Не удалось подключиться к github.</value> + </data> + <data name="SettingsPage_UpdateFailedInstall" xml:space="preserve"> + <value>Не удалось найти файл обновления.</value> + </data> + <data name="SettingsPage_UpdateWarning" xml:space="preserve"> + <value>Упс. Произошла ошибка</value> + </data> + <data name="SettingsPage_UpToDate" xml:space="preserve"> + <value>У вас последняя версия</value> + </data> + <data name="ToastNewControllerEx" xml:space="preserve"> + <value>Контроллер теперь скрыт, а входные данные перенаправляются на виртуальный контроллер</value> + </data> + <data name="TrackPadsPage_Trackpad_Left" xml:space="preserve"> + <value>ЛЕВЫЙ ТРЕКПАД</value> + </data> + <data name="TrackPadsPage_Trackpad_Left_Buttons" xml:space="preserve"> + <value>ФУНКЦИИ ЛЕВЫЙ ТРЕКПАД</value> + </data> + <data name="TrackPadsPage_Trackpad_Right" xml:space="preserve"> + <value>ПРАВЫЙ ТРЕКПАД</value> + </data> + <data name="TrackPadsPage_Trackpad_Right_Buttons" xml:space="preserve"> + <value>ФУНКЦИИ ПРАВЫЙТРЕКПАД</value> + </data> + <data name="TriggersPage_Trigger_Left" xml:space="preserve"> + <value>ЛЕВЫЙ ТРИГГЕР</value> + </data> + <data name="TriggersPage_Trigger_Left_Button" xml:space="preserve"> + <value>ФУНКЦИИ ЛЕВЫЙ ТРИГГЕР</value> + </data> + <data name="TriggersPage_Trigger_Right" xml:space="preserve"> + <value>ПРАВЫЙ ТРИГГЕР</value> + </data> + <data name="TriggersPage_Trigger_Right_Button" xml:space="preserve"> + <value>ФУНКЦИИ ПРАВЫЙ ТРИГГЕР</value> + </data> + <data name="User" xml:space="preserve"> + <value>Пользователь</value> + </data> + <data name="WarningElevated" xml:space="preserve"> + <value>Запустите от имени администратора чтобы разблокировать эти настройки</value> + </data> + <data name="Controller_Connect" xml:space="preserve"> + <value>Connect</value> + </data> + <data name="Controller_Disconnect" xml:space="preserve"> + <value>Disconnect</value> + </data> + <data name="Controller_Hide" xml:space="preserve"> + <value>Hide</value> + </data> + <data name="Controller_Unhide" xml:space="preserve"> + <value>Unhide</value> + </data> + <data name="Controller_Virtual" xml:space="preserve"> + <value>Virtual </value> + </data> + <data name="AboutPage_Manufacturer" xml:space="preserve"> + <value>Manufacturer</value> + </data> + <data name="AboutPage_ProductName" xml:space="preserve"> + <value>Product name</value> + </data> + <data name="AutoRollYawSwapDesc" xml:space="preserve"> + <value>This input will operate as a simple joystick. Ideal for laptop and clamshell type handhelds, automatic yaw roll swap based on how device is being held (90 or 180 degree open).</value> + </data> + <data name="ControllerPage_DesktopLayout" xml:space="preserve"> + <value>Desktop layout</value> + </data> + <data name="ControllerPage_DesktopLayoutDefine" xml:space="preserve"> + <value>Define desktop layout</value> + </data> + <data name="ControllerPage_DesktopLayoutDefineController" xml:space="preserve"> + <value>Define controller layout when in desktop mode</value> + </data> + <data name="ControllerPage_DesktopLayoutEdit" xml:space="preserve"> + <value>Edit</value> + </data> + <data name="ControllerPage_DesktopLayoutEnable" xml:space="preserve"> + <value>Enable desktop layout</value> + </data> + <data name="ControllerPage_NonGameControllerLayouts" xml:space="preserve"> + <value>Non-game controller layouts</value> + </data> + <data name="ControllerPage_NoPhysicalControllerAction" xml:space="preserve"> + <value>You might want to click on Connect next to your plugged controller.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDesc" xml:space="preserve"> + <value>You have no physical controller connected. No inputs will be sent to HC or its service.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedAction" xml:space="preserve"> + <value>Please make sure you connected a compatible XInput or DInput device.</value> + </data> + <data name="ControllerPage_NoPhysicalControllerDetectedWarning" xml:space="preserve"> + <value>No physical controller detected</value> + </data> + <data name="ControllerPage_NoPhysicalControllerWarning" xml:space="preserve"> + <value>No physical controller connected</value> + </data> + <data name="ControllerPage_NoVirtualControllerAction" xml:space="preserve"> + <value>You might want to start companion service or make sure your virtual controller status is set to: Connected</value> + </data> + <data name="ControllerPage_NoVirtualControllerDesc" xml:space="preserve"> + <value>Your physical controller is hidden, yet you have no virtual controller available. No inputs will be sent to games.</value> + </data> + <data name="ControllerPage_NoVirtualControllerWarning" xml:space="preserve"> + <value>No virtual controller detected</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenAction" xml:space="preserve"> + <value>You might want to unmute your virtual controller or unhide your physical controller.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenDesc" xml:space="preserve"> + <value>Your physical controller is hidden, yet you have muted your virtual controller.</value> + </data> + <data name="ControllerPage_PhysicalControllerHiddenWarning" xml:space="preserve"> + <value>Physical controller is hidden</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenAction" xml:space="preserve"> + <value>You might want to hide your physical controller or mute your virtual controller.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenDesc" xml:space="preserve"> + <value>Your physical controller is not hidden, yet you have an unmuted virtual controller. You might encounter double inputs in games.</value> + </data> + <data name="ControllerPage_PhysicalControllerNotHiddenWarning" xml:space="preserve"> + <value>Physical controller is not hidden</value> + </data> + <data name="ControllerPage_SteamControllerHDRumble" xml:space="preserve"> + <value>HD rumble</value> + </data> + <data name="ControllerPage_SteamControllerHDRumbleDesc" xml:space="preserve"> + <value>Use high-definition rumble engine, at the cost of higher CPU usage</value> + </data> + <data name="ControllerPage_SteamControllerMute" xml:space="preserve"> + <value>Приостановить виртуальные контроллер</value> + </data> + <data name="ControllerPage_SteamControllerMuteDesc" xml:space="preserve"> + <value>Временно отключает виртуальный контрллер в Steam приложених</value> + </data> + <data name="ControllerPage_SteamControllerSettings" xml:space="preserve"> + <value>Steam Controller Settings</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEO2.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM1" xml:space="preserve"> + <value>Win</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM2" xml:space="preserve"> + <value>Esc</value> + </data> + <data name="Enum.AYANEO2021Pro.ButtonFlags.OEM3" xml:space="preserve"> + <value>KB</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRLite.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM2" xml:space="preserve"> + <value>≈</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM3" xml:space="preserve"> + <value>RC</value> + </data> + <data name="Enum.AYANEOAIRPro.ButtonFlags.OEM4" xml:space="preserve"> + <value>LC</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DS4Controller.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B1" xml:space="preserve"> + <value>Cross</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B2" xml:space="preserve"> + <value>Circle</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B3" xml:space="preserve"> + <value>Square</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.B4" xml:space="preserve"> + <value>Triangle</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DS4Controller.ButtonFlags.Start" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.LeftPad" xml:space="preserve"> + <value>Single-touch swipe</value> + </data> + <data name="Enum.DualSenseController.AxisLayoutFlags.RightPad" xml:space="preserve"> + <value>Multi-touch swipe</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B1" xml:space="preserve"> + <value>Cross</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B2" xml:space="preserve"> + <value>Circle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B3" xml:space="preserve"> + <value>Square</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.B4" xml:space="preserve"> + <value>Triangle</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Back" xml:space="preserve"> + <value>Share</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadClick" xml:space="preserve"> + <value>Single-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.LeftPadTouch" xml:space="preserve"> + <value>Single-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadClick" xml:space="preserve"> + <value>Multi-touch click</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.RightPadTouch" xml:space="preserve"> + <value>Multi-touch tap</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Special" xml:space="preserve"> + <value>Sony</value> + </data> + <data name="Enum.DualSenseController.ButtonFlags.Start" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.AlwaysOn" xml:space="preserve"> + <value>Always On</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Back" xml:space="preserve"> + <value>Back</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadDown" xml:space="preserve"> + <value>DPad Down</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadLeft" xml:space="preserve"> + <value>DPad Left</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadRight" xml:space="preserve"> + <value>DPad Right</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.DPadUp" xml:space="preserve"> + <value>DPad Up</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftShoulder" xml:space="preserve"> + <value>Left Shoulder</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftThumb" xml:space="preserve"> + <value>Left Thumb</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.LeftTrigger" xml:space="preserve"> + <value>Left Trigger</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightShoulder" xml:space="preserve"> + <value>Right Shoulder</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightThumb" xml:space="preserve"> + <value>Right Thumb</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.RightTrigger" xml:space="preserve"> + <value>Right Trigger</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Start" xml:space="preserve"> + <value>Start</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.GamepadButtonFlagsExt.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.HIDmode.DualShock4Controller" xml:space="preserve"> + <value>Emulated DualShock 4 controller</value> + </data> + <data name="Enum.HIDmode.NoController" xml:space="preserve"> + <value>No emulated controller</value> + </data> + <data name="Enum.HIDmode.Xbox360Controller" xml:space="preserve"> + <value>Emulated XBOX 360 controller</value> + </data> + <data name="Enum.HIDstatus.Connected" xml:space="preserve"> + <value>Connected</value> + </data> + <data name="Enum.HIDstatus.Disconnected" xml:space="preserve"> + <value>Disconnected</value> + </data> + <data name="Enum.Input.AutoRollYawSwap" xml:space="preserve"> + <value>Auto Roll Yaw Swap</value> + </data> + <data name="Enum.Input.JoystickCamera" xml:space="preserve"> + <value>Joystick camera</value> + </data> + <data name="Enum.Input.JoystickSteering" xml:space="preserve"> + <value>Joystick steering</value> + </data> + <data name="Enum.Input.PlayerSpace" xml:space="preserve"> + <value>Player space</value> + </data> + <data name="Enum.InputsHotkeyType.Custom" xml:space="preserve"> + <value>Custom</value> + </data> + <data name="Enum.InputsHotkeyType.Device" xml:space="preserve"> + <value>Device</value> + </data> + <data name="Enum.InputsHotkeyType.Handheld" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.InputsHotkeyType.HC" xml:space="preserve"> + <value>Handheld Companion</value> + </data> + <data name="Enum.InputsHotkeyType.Overlay" xml:space="preserve"> + <value>Overlay</value> + </data> + <data name="Enum.InputsHotkeyType.Quicktools" xml:space="preserve"> + <value>Quick tools</value> + </data> + <data name="Enum.InputsHotkeyType.Windows" xml:space="preserve"> + <value>Windows</value> + </data> + <data name="Enum.KeyFlags.A" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.KeyFlags.Alt" xml:space="preserve"> + <value>Alt</value> + </data> + <data name="Enum.KeyFlags.Apostrophe" xml:space="preserve"> + <value>Apostrophe</value> + </data> + <data name="Enum.KeyFlags.B" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.KeyFlags.Backslash" xml:space="preserve"> + <value>Backslash</value> + </data> + <data name="Enum.KeyFlags.Backspace" xml:space="preserve"> + <value>Backspace</value> + </data> + <data name="Enum.KeyFlags.C" xml:space="preserve"> + <value>C</value> + </data> + <data name="Enum.KeyFlags.Comma" xml:space="preserve"> + <value>Comma</value> + </data> + <data name="Enum.KeyFlags.Control" xml:space="preserve"> + <value>Control</value> + </data> + <data name="Enum.KeyFlags.D" xml:space="preserve"> + <value>D</value> + </data> + <data name="Enum.KeyFlags.Delete" xml:space="preserve"> + <value>Delete</value> + </data> + <data name="Enum.KeyFlags.E" xml:space="preserve"> + <value>E</value> + </data> + <data name="Enum.KeyFlags.End" xml:space="preserve"> + <value>End</value> + </data> + <data name="Enum.KeyFlags.Enter" xml:space="preserve"> + <value>Enter</value> + </data> + <data name="Enum.KeyFlags.Equal" xml:space="preserve"> + <value>Equal</value> + </data> + <data name="Enum.KeyFlags.Escape" xml:space="preserve"> + <value>Escape</value> + </data> + <data name="Enum.KeyFlags.F" xml:space="preserve"> + <value>F</value> + </data> + <data name="Enum.KeyFlags.F1" xml:space="preserve"> + <value>F1</value> + </data> + <data name="Enum.KeyFlags.F10" xml:space="preserve"> + <value>F10</value> + </data> + <data name="Enum.KeyFlags.F11" xml:space="preserve"> + <value>F11</value> + </data> + <data name="Enum.KeyFlags.F12" xml:space="preserve"> + <value>F12</value> + </data> + <data name="Enum.KeyFlags.F2" xml:space="preserve"> + <value>F2</value> + </data> + <data name="Enum.KeyFlags.F3" xml:space="preserve"> + <value>F3</value> + </data> + <data name="Enum.KeyFlags.F4" xml:space="preserve"> + <value>F4</value> + </data> + <data name="Enum.KeyFlags.F5" xml:space="preserve"> + <value>F5</value> + </data> + <data name="Enum.KeyFlags.F6" xml:space="preserve"> + <value>F6</value> + </data> + <data name="Enum.KeyFlags.F7" xml:space="preserve"> + <value>F7</value> + </data> + <data name="Enum.KeyFlags.F8" xml:space="preserve"> + <value>F8</value> + </data> + <data name="Enum.KeyFlags.F9" xml:space="preserve"> + <value>F9</value> + </data> + <data name="Enum.KeyFlags.G" xml:space="preserve"> + <value>G</value> + </data> + <data name="Enum.KeyFlags.Grave" xml:space="preserve"> + <value>Grave</value> + </data> + <data name="Enum.KeyFlags.H" xml:space="preserve"> + <value>H</value> + </data> + <data name="Enum.KeyFlags.Home" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.KeyFlags.I" xml:space="preserve"> + <value>I</value> + </data> + <data name="Enum.KeyFlags.Insert" xml:space="preserve"> + <value>Insert</value> + </data> + <data name="Enum.KeyFlags.J" xml:space="preserve"> + <value>J</value> + </data> + <data name="Enum.KeyFlags.K" xml:space="preserve"> + <value>K</value> + </data> + <data name="Enum.KeyFlags.L" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.KeyFlags.M" xml:space="preserve"> + <value>M</value> + </data> + <data name="Enum.KeyFlags.Minus" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.KeyFlags.N" xml:space="preserve"> + <value>N</value> + </data> + <data name="Enum.KeyFlags.O" xml:space="preserve"> + <value>O</value> + </data> + <data name="Enum.KeyFlags.P" xml:space="preserve"> + <value>P</value> + </data> + <data name="Enum.KeyFlags.Pause" xml:space="preserve"> + <value>Pause</value> + </data> + <data name="Enum.KeyFlags.Period" xml:space="preserve"> + <value>Period</value> + </data> + <data name="Enum.KeyFlags.Q" xml:space="preserve"> + <value>Q</value> + </data> + <data name="Enum.KeyFlags.R" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.KeyFlags.S" xml:space="preserve"> + <value>S</value> + </data> + <data name="Enum.KeyFlags.Semicolon" xml:space="preserve"> + <value>Semicolon</value> + </data> + <data name="Enum.KeyFlags.Shift" xml:space="preserve"> + <value>Shift</value> + </data> + <data name="Enum.KeyFlags.Slash" xml:space="preserve"> + <value>Slash</value> + </data> + <data name="Enum.KeyFlags.Space" xml:space="preserve"> + <value>Space</value> + </data> + <data name="Enum.KeyFlags.T" xml:space="preserve"> + <value>T</value> + </data> + <data name="Enum.KeyFlags.Tab" xml:space="preserve"> + <value>Tab</value> + </data> + <data name="Enum.KeyFlags.U" xml:space="preserve"> + <value>U</value> + </data> + <data name="Enum.KeyFlags.V" xml:space="preserve"> + <value>V</value> + </data> + <data name="Enum.KeyFlags.W" xml:space="preserve"> + <value>W</value> + </data> + <data name="Enum.KeyFlags.X" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.KeyFlags.Y" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.KeyFlags.Z" xml:space="preserve"> + <value>Z</value> + </data> + <data name="Enum.MotionInput.AutoRollYawSwap" xml:space="preserve"> + <value>Auto Roll Yaw Swap</value> + </data> + <data name="Enum.MotionInput.JoystickCamera" xml:space="preserve"> + <value>Joystick Camera</value> + </data> + <data name="Enum.MotionInput.JoystickSteering" xml:space="preserve"> + <value>Joystick Steering</value> + </data> + <data name="Enum.MotionInput.PlayerSpace" xml:space="preserve"> + <value>Player Space</value> + </data> + <data name="Enum.MotionOuput.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.MotionOuput.MoveCursor" xml:space="preserve"> + <value>MoveCursor</value> + </data> + <data name="Enum.MotionOuput.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="Enum.MotionOuput.ScrollWheel" xml:space="preserve"> + <value>ScrollWheel</value> + </data> + <data name="Enum.MotionOutput.LeftStick" xml:space="preserve"> + <value>Left Stick</value> + </data> + <data name="Enum.MotionOutput.RightStick" xml:space="preserve"> + <value>Right Stick</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Back" xml:space="preserve"> + <value>View</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Special" xml:space="preserve"> + <value>STEAM</value> + </data> + <data name="Enum.NeptuneController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="Enum.Output.LeftStick" xml:space="preserve"> + <value>Left joystick</value> + </data> + <data name="Enum.Output.RightStick" xml:space="preserve"> + <value>Right joystick</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>ZL</value> + </data> + <data name="Enum.ProController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>ZR</value> + </data> + <data name="Enum.ProController.ButtonFlags.B1" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.ProController.ButtonFlags.B2" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.ProController.ButtonFlags.B3" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.ProController.ButtonFlags.B4" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.ProController.ButtonFlags.Back" xml:space="preserve"> + <value>Minus</value> + </data> + <data name="Enum.ProController.ButtonFlags.L1" xml:space="preserve"> + <value>L</value> + </data> + <data name="Enum.ProController.ButtonFlags.R1" xml:space="preserve"> + <value>R</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special" xml:space="preserve"> + <value>Home</value> + </data> + <data name="Enum.ProController.ButtonFlags.Special2" xml:space="preserve"> + <value>Capture</value> + </data> + <data name="Enum.ProController.ButtonFlags.Start" xml:space="preserve"> + <value>Plus</value> + </data> + <data name="Enum.ProfileErrorCode.Default" xml:space="preserve"> + <value>This is your default controller profile. This profile will be applied for all your applications that do not have a specific profile. Some options requiring an executable might be disabled.</value> + </data> + <data name="Enum.ProfileErrorCode.IsRunning" xml:space="preserve"> + <value>Oops. This profile seems to be running. Some options requiring an executable may be disabled.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingExecutable" xml:space="preserve"> + <value>Oops. It seems this profile does not have an executable. How is this even possible?</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPath" xml:space="preserve"> + <value>Oops. It seems this profile does not have a path to the application. Some options requiring an executable might be disabled.</value> + </data> + <data name="Enum.ProfileErrorCode.MissingPermission" xml:space="preserve"> + <value>Oops. It seems you do not have the necessary permission level to modify the content of this application. Make sure you have started this program in administrator mode.</value> + </data> + <data name="Enum.ProfileErrorCode.None" xml:space="preserve"> + <value>Nothing to see here.</value> + </data> + <data name="Enum.ProfileErrorCode.Running" xml:space="preserve"> + <value>Oops. It seems this profile excutable is running. Some options requiring an executable might be disabled.</value> + </data> + <data name="Enum.QualityOfServiceLevel.Default" xml:space="preserve"> + <value>Default</value> + </data> + <data name="Enum.QualityOfServiceLevel.Eco" xml:space="preserve"> + <value>Eco</value> + </data> + <data name="Enum.QualityOfServiceLevel.High" xml:space="preserve"> + <value>High</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM1" xml:space="preserve"> + <value>Command center</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM2" xml:space="preserve"> + <value>Armory crate</value> + </data> + <data name="Enum.ROGAlly.ButtonFlags.OEM3" xml:space="preserve"> + <value>M1 / M2</value> + </data> + <data name="Enum.ServiceStartMode.Automatic" xml:space="preserve"> + <value>Automatic</value> + </data> + <data name="Enum.ServiceStartMode.Disabled" xml:space="preserve"> + <value>Disabled</value> + </data> + <data name="Enum.ServiceStartMode.Manual" xml:space="preserve"> + <value>Manual</value> + </data> + <data name="Enum.SteamDeck.ButtonFlags.OEM1" xml:space="preserve"> + <value>Options</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftStick" xml:space="preserve"> + <value>LeftStick</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.LeftThumb" xml:space="preserve"> + <value>Left Thumb</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightThumb" xml:space="preserve"> + <value>Right Thumb</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B1" xml:space="preserve"> + <value>A</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B2" xml:space="preserve"> + <value>B</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B3" xml:space="preserve"> + <value>X</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B4" xml:space="preserve"> + <value>Y</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B5" xml:space="preserve"> + <value>B5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B6" xml:space="preserve"> + <value>B6</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B7" xml:space="preserve"> + <value>B7</value> + </data> + <data name="Enum.XInputController.ButtonFlags.B8" xml:space="preserve"> + <value>B8</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Back" xml:space="preserve"> + <value>View</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadDown" xml:space="preserve"> + <value>DPad Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadLeft" xml:space="preserve"> + <value>DPad Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadRight" xml:space="preserve"> + <value>PPad Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.DPadUp" xml:space="preserve"> + <value>DPad Up</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L1" xml:space="preserve"> + <value>LB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L2" xml:space="preserve"> + <value>L2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L3" xml:space="preserve"> + <value>L3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L4" xml:space="preserve"> + <value>L4</value> + </data> + <data name="Enum.AYANEOAIR.ButtonFlags.OEM1" xml:space="preserve"> + <value>AYA</value> + </data> + <data name="Enum.XInputController.AxisLayoutFlags.RightStick" xml:space="preserve"> + <value>RightStick</value> + </data> + <data name="Enum.XInputController.ButtonFlags.L5" xml:space="preserve"> + <value>L5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumb" xml:space="preserve"> + <value>Left Thumb</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbDown" xml:space="preserve"> + <value>Left Thumb Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbLeft" xml:space="preserve"> + <value>Left Thumb Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbRight" xml:space="preserve"> + <value>Left Thumb Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.LeftThumbUp" xml:space="preserve"> + <value>Left Thumb Up</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM1" xml:space="preserve"> + <value>OEM1</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM2" xml:space="preserve"> + <value>OEM2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.OEM3" xml:space="preserve"> + <value>OEM3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R1" xml:space="preserve"> + <value>RB</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R2" xml:space="preserve"> + <value>R2</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R3" xml:space="preserve"> + <value>R3</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R4" xml:space="preserve"> + <value>R4</value> + </data> + <data name="Enum.XInputController.ButtonFlags.R5" xml:space="preserve"> + <value>R5</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumb" xml:space="preserve"> + <value>Right Thumb</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbDown" xml:space="preserve"> + <value>Right Thumb Down</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbLeft" xml:space="preserve"> + <value>Right Thumb Left</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbRight" xml:space="preserve"> + <value>Right Thumb Right</value> + </data> + <data name="Enum.XInputController.ButtonFlags.RightThumbUp" xml:space="preserve"> + <value>Right Thumb Up</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Special" xml:space="preserve"> + <value>Guide</value> + </data> + <data name="Enum.XInputController.ButtonFlags.Start" xml:space="preserve"> + <value>Menu</value> + </data> + <data name="JoystickSteeringDesc" xml:space="preserve"> + <value>This input will operate as a joystick optimized for controlling a steering wheel or a racing game</value> + </data> + <data name="LayoutPage_ApplyTemplate" xml:space="preserve"> + <value>Apply template</value> + </data> + <data name="LayoutPage_Buttons" xml:space="preserve"> + <value>Buttons</value> + </data> + <data name="LayoutPage_Cancel" xml:space="preserve"> + <value>Cancel</value> + </data> + <data name="LayoutPage_Community" xml:space="preserve"> + <value>COMMUNITY</value> + </data> + <data name="LayoutPage_Confirm" xml:space="preserve"> + <value>Confirm</value> + </data> + <data name="LayoutPage_Dpad" xml:space="preserve"> + <value>Dpad</value> + </data> + <data name="LayoutPage_ExportCurrentController" xml:space="preserve"> + <value>Export for current controller</value> + </data> + <data name="LayoutPage_ExportLayout" xml:space="preserve"> + <value>Export layout</value> + </data> + <data name="LayoutPage_Gyro" xml:space="preserve"> + <value>Gyro</value> + </data> + <data name="LayoutPage_Joysticks" xml:space="preserve"> + <value>Joysticks</value> + </data> + <data name="LayoutPage_LayoutAuthor" xml:space="preserve"> + <value>Layout author</value> + </data> + <data name="LayoutPage_LayoutDesc" xml:space="preserve"> + <value>Layout description</value> + </data> + <data name="LayoutPage_LayoutTitle" xml:space="preserve"> + <value>Layout title</value> + </data> + <data name="LayoutPage_SaveGameInfoLayout" xml:space="preserve"> + <value>Save game information with the layout</value> + </data> + <data name="LayoutPage_ShowCurrentControllerTemplates" xml:space="preserve"> + <value>Show current controller templates only</value> + </data> + <data name="LayoutPage_TemplatePicker" xml:space="preserve"> + <value>Layout template picker</value> + </data> + <data name="LayoutPage_Templates" xml:space="preserve"> + <value>TEMPLATES</value> + </data> + <data name="LayoutPage_Trackpads" xml:space="preserve"> + <value>Trackpads</value> + </data> + <data name="LayoutPage_Triggers" xml:space="preserve"> + <value>Triggers</value> + </data> + <data name="JoystickCameraDesc" xml:space="preserve"> + <value>This input will operate as a simple joystick. This is intended for traditional joystick applications</value> + </data> + <data name="MainWindow_Back" xml:space="preserve"> + <value>Back</value> + </data> + <data name="MainWindow_Exit" xml:space="preserve"> + <value>Exit</value> + </data> + <data name="MainWindow_MainWindow" xml:space="preserve"> + <value>Main Window</value> + </data> + <data name="MainWindow_Navigate" xml:space="preserve"> + <value>Navigate</value> + </data> + <data name="MainWindow_QuickTools" xml:space="preserve"> + <value>Quick Tools</value> + </data> + <data name="MainWindow_Select" xml:space="preserve"> + <value>Select</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderClosePrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseText" xml:space="preserve"> + <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="OverlayPage_Millisecond" xml:space="preserve"> + <value>ms</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Disabled" xml:space="preserve"> + <value>Disabled</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Extended" xml:space="preserve"> + <value>Extended</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_External" xml:space="preserve"> + <value>External</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Full" xml:space="preserve"> + <value>Full</value> + </data> + <data name="OverlayPage_OverlayDisplayLevel_Minimal" xml:space="preserve"> + <value>Minimal</value> + </data> + <data name="PlayerSpaceDesc" xml:space="preserve"> + <value>This input will operate as a joystick optimized for controlling a first or third person camera</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate1" xml:space="preserve"> + <value>Are you sure you want to apply this template? All layout settings will be overridden.</value> + </data> + <data name="ProfilesPage_AreYouSureApplyTemplate2" xml:space="preserve"> + <value>You can't undo this action. Previously applied template: {0}</value> + </data> + <data name="ProfilesPage_ControllerLayoutDesc" xml:space="preserve"> + <value>Change the virtual controller layout</value> + </data> + <data name="ProfilesPage_CPU" xml:space="preserve"> + <value>CPU</value> + </data> + <data name="ProfilesPage_EPP" xml:space="preserve"> + <value>Energy performance preference (EPP)</value> + </data> + <data name="ProfilesPage_EPPBalance" xml:space="preserve"> + <value>CPU/GPU power balance</value> + </data> + <data name="ProfilesPage_EPPDesc" xml:space="preserve"> + <value>Specifies power distribution policy between CPU and GPU</value> + </data> + <data name="ProfilesPage_GPU" xml:space="preserve"> + <value>GPU</value> + </data> + <data name="ProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Power limit target</value> + </data> + <data name="ProfilesPage_Wrapper_Disabled" xml:space="preserve"> + <value>Disabled</value> + </data> + <data name="ProfilesPage_Wrapper_Injection" xml:space="preserve"> + <value>Injection (recommended)</value> + </data> + <data name="ProfilesPage_Wrapper_Redirection" xml:space="preserve"> + <value>Redirection</value> + </data> + <data name="QuickPerformancePage_CPUBoostMode" xml:space="preserve"> + <value>CPU boost mode</value> + </data> + <data name="QuickPerformancePage_CPUBoostModeDesc" xml:space="preserve"> + <value>Set current CPU boost mode</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRate" xml:space="preserve"> + <value>Display resolution and refresh rate</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRateDesc" xml:space="preserve"> + <value>Adjust main display resolution and refresh rate</value> + </data> + <data name="QuickPerformancePage_FanOverride" xml:space="preserve"> + <value>Fan override</value> + </data> + <data name="QuickPerformancePage_FanOverrideDesc" xml:space="preserve"> + <value>Set the fan duty cycle to user-defined value</value> + </data> + <data name="QuickPerformancePage_FanOverridePercentage" xml:space="preserve"> + <value>%</value> + </data> + <data name="QuickProfilesPage_CurrentProfile" xml:space="preserve"> + <value>Current profile:</value> + </data> + <data name="QuickProfilesPage_CurrentProfileDefault" xml:space="preserve"> + <value>Default</value> + </data> + <data name="QuickProfilesPage_PowerLimitTarget" xml:space="preserve"> + <value>Power limit target</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrder" xml:space="preserve"> + <value>Improve virtual controller detection</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderDesc" xml:space="preserve"> + <value>Forces the virtual controller to be detected as first controller during Windows start. Enable this if your app/game doesn't detect your inputs (requires device reboot).</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderText" xml:space="preserve"> + <value>Your device must be restarted in order for the changes to take effect. Would you like to restart now?</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderTitle" xml:space="preserve"> + <value>Restart required</value> + </data> + <data name="SettingsPage_HideWhenLoseFocus" xml:space="preserve"> + <value>Hide when loses focus</value> + </data> + <data name="SettingsPage_HideWhenLoseFocusDesc" xml:space="preserve"> + <value>Automatically hide quick tools when the user clicks outside of window</value> + </data> + <data name="SettingsPage_HwInfo" xml:space="preserve"> + <value>HWiNFO</value> + </data> + <data name="SettingsPage_HwInfoDesc" xml:space="preserve"> + <value>Automatically turn HWiNFO off when companion is closed</value> + </data> + <data name="SettingsPage_NativeDisplayOrientation" xml:space="preserve"> + <value>Native display orientation</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDesc" xml:space="preserve"> + <value>Some features depend on knowing the native display orientation to work properly. If this was not detected properly, set your display's orientation to the orientation that matches your controller, then click Detect</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationDetect" xml:space="preserve"> + <value>Detect</value> + </data> + <data name="SettingsPage_NativeDisplayOrientationNotSet" xml:space="preserve"> + <value>Not set</value> + </data> + <data name="DevicePage_PowerOptions" xml:space="preserve"> + <value>Power options</value> + </data> + <data name="SettingsPage_QuickToolsBackdrop" xml:space="preserve"> + <value>Quicktools backdrop, none, mica, tabbed, acrylic</value> + </data> + <data name="SettingsPage_QuickToolsOptions" xml:space="preserve"> + <value>Quicktools options</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocation" xml:space="preserve"> + <value>Window location</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomLeft" xml:space="preserve"> + <value>Bottom left</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationBottomRight" xml:space="preserve"> + <value>Bottom right</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationDesc" xml:space="preserve"> + <value>Define quicktools window location</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopLeft" xml:space="preserve"> + <value>Top left</value> + </data> + <data name="SettingsPage_QuickToolsWindowLocationTopRight" xml:space="preserve"> + <value>Top right</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServer" xml:space="preserve"> + <value>RivaTuner Statistics Server</value> + </data> + <data name="SettingsPage_RivaTunerStatisticsServerDesc" xml:space="preserve"> + <value>Automatically turn RTSS off when companion is closed</value> + </data> + <data name="SettingsPage_SensorNone" xml:space="preserve"> + <value>None</value> + </data> + <data name="SettingsPage_ThirdPartyApps" xml:space="preserve"> + <value>Third-party applications</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencySecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyText" xml:space="preserve"> + <value>Disabling this setting will also disable "Improve virtual controller detection". Do you want to continue?</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="LayoutPage_SetAsDefault" xml:space="preserve"> + <value>Make this the default layout</value> + </data> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </root> \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.zh-CN.resx b/HandheldCompanion/Properties/Resources.zh-CN.resx index d387da295..128b26951 100644 --- a/HandheldCompanion/Properties/Resources.zh-CN.resx +++ b/HandheldCompanion/Properties/Resources.zh-CN.resx @@ -1117,8 +1117,13 @@ <data name="ControllerPage_SteamControllerMuteDesc" xml:space="preserve"> <value>对Steam相关程序禁用虚拟手柄</value> </data> +<<<<<<< HEAD <data name="ControllerPage_DeviceSpecificSettings" xml:space="preserve"> <value>控制器设置</value> +======= + <data name="ControllerPage_SteamControllerSettings" xml:space="preserve"> + <value>Steam Deck 配置</value> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </data> <data name="ControllerPage_VibrateDevice" xml:space="preserve"> <value>手柄连接时震动提示</value> @@ -1507,7 +1512,11 @@ <data name="SettingsPage_HideWhenLoseFocus" xml:space="preserve"> <value>自动最小化</value> </data> +<<<<<<< HEAD <data name="Enum.MotionInput.AutoRollYawSwap.Desc" xml:space="preserve"> +======= + <data name="AutoRollYawSwapDesc" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>该模式适合笔记本或翻盖式掌机,可以根据该设备如何被握持(屏幕以90度角或者180度角打开)判断重力方向,来自动切换陀螺仪方向。</value> </data> <data name="Enum.GamepadButtonFlagsExt.A" xml:space="preserve"> @@ -1642,6 +1651,7 @@ <data name="Enum.ServiceStartMode.Manual" xml:space="preserve"> <value>手动</value> </data> +<<<<<<< HEAD <data name="Enum.MotionInput.JoystickCamera.Desc" xml:space="preserve"> <value>这个模式将反转X轴,适用于将陀螺仪模拟鼠标。</value> </data> @@ -1649,6 +1659,15 @@ <value>这个模式适用于一些赛车游戏,陀螺仪输入将模拟方向盘。</value> </data> <data name="Enum.MotionInput.PlayerSpace.Desc" xml:space="preserve"> +======= + <data name="JoystickCameraDesc" xml:space="preserve"> + <value>这个模式将反转X轴,适用于将陀螺仪模拟鼠标。</value> + </data> + <data name="JoystickSteeringDesc" xml:space="preserve"> + <value>这个模式适用于一些赛车游戏,陀螺仪输入将模拟方向盘。</value> + </data> + <data name="PlayerSpaceDesc" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>这个模式适用于第一人称或第三人称视角的游戏,体感输入将模拟玩家视角,</value> </data> <data name="Enum.InputsHotkeyType.HC" xml:space="preserve"> @@ -1712,7 +1731,11 @@ <value>LC</value> </data> <data name="Enum.ROGAlly.ButtonFlags.OEM3" xml:space="preserve"> +<<<<<<< HEAD <value>M1</value> +======= + <value>M1 / M2</value> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </data> <data name="Enum.DS4Controller.ButtonFlags.B1" xml:space="preserve"> <value>叉</value> @@ -2209,6 +2232,7 @@ <data name="Enum.XInputController.AxisLayoutFlags.RightStick" xml:space="preserve"> <value>RightStick</value> </data> +<<<<<<< HEAD <data name="MainWindow_ControllerManagementClosePrimary" xml:space="preserve"> <value>Yes</value> </data> @@ -2219,6 +2243,18 @@ <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> </data> <data name="MainWindow_ControllerManagementCloseTitle" xml:space="preserve"> +======= + <data name="MainWindow_VirtualControllerForceOrderClosePrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseText" xml:space="preserve"> + <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseTitle" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>Warning</value> </data> <data name="ProfilesPage_Wrapper_Disabled" xml:space="preserve"> @@ -2242,6 +2278,7 @@ <data name="SettingsPage_ForceVirtualControllerOrderSecondary" xml:space="preserve"> <value>No</value> </data> +<<<<<<< HEAD <data name="Dialog_ForceRestartDesc" xml:space="preserve"> <value>Your device must be restarted in order for the changes to take effect. Would you like to restart now?</value> </data> @@ -2258,6 +2295,24 @@ <value>Disabling this setting will also disable "Improve virtual controller detection". Do you want to continue?</value> </data> <data name="SettingsPage_ControllerManagementDependencyTitle" xml:space="preserve"> +======= + <data name="SettingsPage_ForceVirtualControllerOrderText" xml:space="preserve"> + <value>Your device must be restarted in order for the changes to take effect. Would you like to restart now?</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderTitle" xml:space="preserve"> + <value>Restart required</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencySecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyText" xml:space="preserve"> + <value>Disabling this setting will also disable "Improve virtual controller detection". Do you want to continue?</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyTitle" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>Warning</value> </data> <data name="Controller_Connect" xml:space="preserve"> @@ -2302,6 +2357,7 @@ <data name="ProfilesPage_AreYouSureApplyTemplate2" xml:space="preserve"> <value>You can't undo this action. Previously applied template: {0}</value> </data> +<<<<<<< HEAD <data name="Enum.LegionController.ButtonFlags.B5" xml:space="preserve"> <value>M2</value> </data> @@ -2337,5 +2393,9 @@ </data> <data name="IController_ControllerIndex" xml:space="preserve"> <value>Gamepad: {0}</value> +======= + <data name="LayoutPage_SetAsDefault" xml:space="preserve"> + <value>Make this the default layout</value> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </data> </root> \ No newline at end of file diff --git a/HandheldCompanion/Properties/Resources.zh-Hant.resx b/HandheldCompanion/Properties/Resources.zh-Hant.resx index 38cde275d..12e998db8 100644 --- a/HandheldCompanion/Properties/Resources.zh-Hant.resx +++ b/HandheldCompanion/Properties/Resources.zh-Hant.resx @@ -1117,8 +1117,13 @@ <data name="ControllerPage_SteamControllerMuteDesc" xml:space="preserve"> <value>對Steam相關程序禁用虛擬手柄</value> </data> +<<<<<<< HEAD <data name="ControllerPage_DeviceSpecificSettings" xml:space="preserve"> <value>控制器设置</value> +======= + <data name="ControllerPage_SteamControllerSettings" xml:space="preserve"> + <value>Steam Deck 配置</value> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </data> <data name="ControllerPage_VibrateDevice" xml:space="preserve"> <value>手柄連接時震動提示</value> @@ -1447,10 +1452,17 @@ <data name="QuickPerformancePage_FanOverride" xml:space="preserve"> <value>風扇轉速</value> </data> +<<<<<<< HEAD <data name="QuickSettingsPage_DisplayResolutionDesc" xml:space="preserve"> <value>調整顯示器的解析度與刷新率</value> </data> <data name="QuickSettingsPage_DisplayResolution" xml:space="preserve"> +======= + <data name="QuickPerformancePage_DisplayResolutionRefreshRateDesc" xml:space="preserve"> + <value>調整顯示器的解析度與刷新率</value> + </data> + <data name="QuickPerformancePage_DisplayResolutionRefreshRate" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>顯示器解析度與刷新率</value> </data> <data name="SettingsPage_QuickToolsWindowLocationTopRight" xml:space="preserve"> @@ -1639,6 +1651,7 @@ <data name="Enum.ServiceStartMode.Manual" xml:space="preserve"> <value>手動</value> </data> +<<<<<<< HEAD <data name="Enum.MotionInput.JoystickCamera.Desc" xml:space="preserve"> <value>這個模式將反轉X軸,適用於將陀螺儀模擬滑鼠。</value> </data> @@ -1646,6 +1659,15 @@ <value>這個模式適用於一些賽車遊戲,陀螺儀輸入將模擬方向盤。</value> </data> <data name="Enum.MotionInput.PlayerSpace.Desc" xml:space="preserve"> +======= + <data name="JoystickCameraDesc" xml:space="preserve"> + <value>這個模式將反轉X軸,適用於將陀螺儀模擬滑鼠。</value> + </data> + <data name="JoystickSteeringDesc" xml:space="preserve"> + <value>這個模式適用於一些賽車遊戲,陀螺儀輸入將模擬方向盤。</value> + </data> + <data name="PlayerSpaceDesc" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>這個模式適用於第一人稱或第三人稱視角的遊戲,體感輸入將模擬玩家視角,</value> </data> <data name="Enum.InputsHotkeyType.HC" xml:space="preserve"> @@ -1709,7 +1731,11 @@ <value>LC</value> </data> <data name="Enum.ROGAlly.ButtonFlags.OEM3" xml:space="preserve"> +<<<<<<< HEAD <value>M1</value> +======= + <value>M1 / M2</value> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </data> <data name="Enum.DS4Controller.ButtonFlags.B1" xml:space="preserve"> <value>叉</value> @@ -2206,6 +2232,7 @@ <data name="Enum.XInputController.AxisLayoutFlags.RightStick" xml:space="preserve"> <value>RightStick</value> </data> +<<<<<<< HEAD <data name="MainWindow_ControllerManagementClosePrimary" xml:space="preserve"> <value>Yes</value> </data> @@ -2216,6 +2243,18 @@ <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> </data> <data name="MainWindow_ControllerManagementCloseTitle" xml:space="preserve"> +======= + <data name="MainWindow_VirtualControllerForceOrderClosePrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseSecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseText" xml:space="preserve"> + <value>Improve virtual controller detection might not work if you close Handheld Companion. Are you sure ?</value> + </data> + <data name="MainWindow_VirtualControllerForceOrderCloseTitle" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>Warning</value> </data> <data name="ProfilesPage_Wrapper_Disabled" xml:space="preserve"> @@ -2239,6 +2278,7 @@ <data name="SettingsPage_ForceVirtualControllerOrderSecondary" xml:space="preserve"> <value>No</value> </data> +<<<<<<< HEAD <data name="Dialog_ForceRestartDesc" xml:space="preserve"> <value>Your device must be restarted in order for the changes to take effect. Would you like to restart now?</value> </data> @@ -2258,6 +2298,27 @@ <value>Warning</value> </data> <data name="Enum.MotionInput.AutoRollYawSwap.Desc" xml:space="preserve"> +======= + <data name="SettingsPage_ForceVirtualControllerOrderText" xml:space="preserve"> + <value>Your device must be restarted in order for the changes to take effect. Would you like to restart now?</value> + </data> + <data name="SettingsPage_ForceVirtualControllerOrderTitle" xml:space="preserve"> + <value>Restart required</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyPrimary" xml:space="preserve"> + <value>Yes</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencySecondary" xml:space="preserve"> + <value>No</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyText" xml:space="preserve"> + <value>Disabling this setting will also disable "Improve virtual controller detection". Do you want to continue?</value> + </data> + <data name="SettingsPage_VirtualControllerForceOrderDependencyTitle" xml:space="preserve"> + <value>Warning</value> + </data> + <data name="AutoRollYawSwapDesc" xml:space="preserve"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <value>This input will operate as a simple joystick. Ideal for laptop and clamshell type handhelds, automatic yaw roll swap based on how device is being held (90 or 180 degree open).</value> </data> <data name="Controller_Connect" xml:space="preserve"> @@ -2302,6 +2363,7 @@ <data name="ProfilesPage_AreYouSureApplyTemplate2" xml:space="preserve"> <value>You can't undo this action. Previously applied template: {0}</value> </data> +<<<<<<< HEAD <data name="Enum.LegionController.ButtonFlags.B5" xml:space="preserve"> <value>M2</value> </data> @@ -2337,5 +2399,9 @@ </data> <data name="IController_ControllerIndex" xml:space="preserve"> <value>Gamepad: {0}</value> +======= + <data name="LayoutPage_SetAsDefault" xml:space="preserve"> + <value>Make this the default layout</value> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </data> </root> \ No newline at end of file diff --git a/HandheldCompanion/Properties/Settings.Designer.cs b/HandheldCompanion/Properties/Settings.Designer.cs index 54fcf05d5..84082fed4 100644 --- a/HandheldCompanion/Properties/Settings.Designer.cs +++ b/HandheldCompanion/Properties/Settings.Designer.cs @@ -11,11 +11,19 @@ using System.Configuration; namespace HandheldCompanion.Properties { +<<<<<<< HEAD [SettingsProvider(typeof(CustomSettingsProvider))] internal sealed partial class Settings : ApplicationSettingsBase { +======= + + + [SettingsProvider(typeof(CustomSettingsProvider))] + internal sealed partial class Settings : ApplicationSettingsBase + { +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); public static Settings Default { @@ -542,7 +550,11 @@ public bool OverlayControllerMotion { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] +<<<<<<< HEAD [global::System.Configuration.DefaultSettingValueAttribute("False")] +======= + [global::System.Configuration.DefaultSettingValueAttribute("True")] +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d public bool SteamControllerMute { get { return ((bool)(this["SteamControllerMute"])); @@ -770,6 +782,7 @@ public bool QuickToolsAutoHide { [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] +<<<<<<< HEAD [global::System.Configuration.DefaultSettingValueAttribute("True")] public bool ControllerManagement { get { @@ -777,17 +790,35 @@ public bool ControllerManagement { } set { this["ControllerManagement"] = value; +======= + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool VirtualControllerForceOrder { + get { + return ((bool)(this["VirtualControllerForceOrder"])); + } + set { + this["VirtualControllerForceOrder"] = value; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] +<<<<<<< HEAD public global::System.Collections.Specialized.StringCollection SuspendedControllers { get { return ((global::System.Collections.Specialized.StringCollection)(this["SuspendedControllers"])); } set { this["SuspendedControllers"] = value; +======= + public global::System.Collections.Specialized.StringCollection PhysicalControllerInstanceIds { + get { + return ((global::System.Collections.Specialized.StringCollection)(this["PhysicalControllerInstanceIds"])); + } + set { + this["PhysicalControllerInstanceIds"] = value; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } @@ -862,6 +893,7 @@ public string DSUip { this["DSUip"] = value; } } +<<<<<<< HEAD [global::System.Configuration.UserScopedSettingAttribute()] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] @@ -981,5 +1013,7 @@ public bool LegionControllerPassthrough { this["LegionControllerPassthrough"] = value; } } +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } diff --git a/HandheldCompanion/Properties/Settings.settings b/HandheldCompanion/Properties/Settings.settings index 74c119a81..8eaca638d 100644 --- a/HandheldCompanion/Properties/Settings.settings +++ b/HandheldCompanion/Properties/Settings.settings @@ -132,7 +132,11 @@ <Value Profile="(Default)">True</Value> </Setting> <Setting Name="SteamControllerMute" Type="System.Boolean" Scope="User"> +<<<<<<< HEAD <Value Profile="(Default)">False</Value> +======= + <Value Profile="(Default)">True</Value> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </Setting> <Setting Name="HIDcloakonconnect" Type="System.Boolean" Scope="User"> <Value Profile="(Default)">True</Value> @@ -186,6 +190,7 @@ <Value Profile="(Default)">1</Value> </Setting> <Setting Name="QuickToolsAutoHide" Type="System.Boolean" Scope="User"> +<<<<<<< HEAD <Value Profile="(Default)">False</Value> </Setting> <Setting Name="ControllerManagement" Type="System.Boolean" Scope="User"> @@ -240,7 +245,33 @@ <Value Profile="(Default)" /> </Setting> <Setting Name="LegionControllerPassthrough" Type="System.Boolean" Scope="User"> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + <Value Profile="(Default)">False</Value> + </Setting> + <Setting Name="VirtualControllerForceOrder" Type="System.Boolean" Scope="User"> + <Value Profile="(Default)">False</Value> + </Setting> + <Setting Name="PhysicalControllerInstanceIds" Type="System.Collections.Specialized.StringCollection" Scope="User"> + <Value Profile="(Default)" /> + </Setting> + <Setting Name="HidModeDoNotShowAgain" Type="System.Boolean" Scope="User"> <Value Profile="(Default)">False</Value> </Setting> + <Setting Name="HIDmode" Type="System.Int32" Scope="User"> + <Value Profile="(Default)">0</Value> + </Setting> + <Setting Name="HIDstatus" Type="System.Int32" Scope="User"> + <Value Profile="(Default)">1</Value> + </Setting> + <Setting Name="DSUEnabled" Type="System.Boolean" Scope="User"> + <Value Profile="(Default)">True</Value> + </Setting> + <Setting Name="DSUport" Type="System.Int32" Scope="User"> + <Value Profile="(Default)">26760</Value> + </Setting> + <Setting Name="DSUip" Type="System.String" Scope="User"> + <Value Profile="(Default)">127.0.0.1</Value> + </Setting> </Settings> </SettingsFile> \ No newline at end of file diff --git a/HandheldCompanion/Sensors/IMUAccelerometer.cs b/HandheldCompanion/Sensors/IMUAccelerometer.cs index 987335207..0e1cf3770 100644 --- a/HandheldCompanion/Sensors/IMUAccelerometer.cs +++ b/HandheldCompanion/Sensors/IMUAccelerometer.cs @@ -1,166 +1,176 @@ -using HandheldCompanion.Managers; -using HandheldCompanion.Utils; -using HandheldCompanion.Views; -using System.Numerics; -using Windows.Devices.Sensors; -using static HandheldCompanion.Utils.DeviceUtils; - -namespace HandheldCompanion.Sensors; - -public class IMUAccelerometer : IMUSensor -{ - public new static SensorSpec sensorSpec = new() - { - minIn = -2.0f, - maxIn = 2.0f, - minOut = short.MinValue, - maxOut = short.MaxValue - }; - - public IMUAccelerometer(SensorFamily sensorFamily, int updateInterval) - { - this.sensorFamily = sensorFamily; - this.updateInterval = updateInterval; - - UpdateSensor(); - } - - public void UpdateSensor() - { - switch (sensorFamily) - { - case SensorFamily.Windows: - sensor = Accelerometer.GetDefault(); - break; - case SensorFamily.SerialUSBIMU: - sensor = SerialUSBIMU.GetDefault(); - break; - case SensorFamily.Controller: - sensor = new object(); - break; - } - - if (sensor is null) - { - LogManager.LogWarning("{0} not initialised as a {1}", ToString(), sensorFamily.ToString()); - return; - } - - switch (sensorFamily) - { - case SensorFamily.Windows: - ((Accelerometer)sensor).ReportInterval = (uint)updateInterval; - filter.SetFilterAttrs(MainWindow.CurrentDevice.oneEuroSettings.minCutoff, MainWindow.CurrentDevice.oneEuroSettings.beta); - - LogManager.LogInformation("{0} initialised as a {1}. Report interval set to {2}ms", ToString(), - sensorFamily.ToString(), updateInterval); - break; - case SensorFamily.SerialUSBIMU: - filter.SetFilterAttrs(((SerialUSBIMU)sensor).GetFilterCutoff(), ((SerialUSBIMU)sensor).GetFilterBeta()); - - LogManager.LogInformation("{0} initialised as a {1}. Baud rate set to {2}", ToString(), - sensorFamily.ToString(), ((SerialUSBIMU)sensor).GetInterval()); - break; - case SensorFamily.Controller: - LogManager.LogInformation("{0} initialised as a {1}", ToString(), sensorFamily.ToString()); - break; - } - - StartListening(); - } - - public override void StartListening() - { - switch (sensorFamily) - { - case SensorFamily.Windows: - ((Accelerometer)sensor).ReadingChanged += ReadingChanged; - break; - case SensorFamily.SerialUSBIMU: - ((SerialUSBIMU)sensor).ReadingChanged += ReadingChanged; - break; - } - } - - public override void StopListening() - { - if (sensor is null) - return; - - switch (sensorFamily) - { - case SensorFamily.Windows: - ((Accelerometer)sensor).ReadingChanged -= ReadingChanged; - break; - case SensorFamily.SerialUSBIMU: - ((SerialUSBIMU)sensor).ReadingChanged -= ReadingChanged; - break; - } - - sensor = null; - - base.StopListening(); - } - - private void ReadingChanged(float GyroAccelX, float GyroAccelY, float GyroAccelZ) - { - switch (sensorFamily) - { - case SensorFamily.Controller: - { - reading.X = GyroAccelX; - reading.Y = GyroAccelY; - reading.Z = GyroAccelZ; - - base.ReadingChanged(); - } - break; - } - } - - private void ReadingChanged(Vector3 AccelerationG, Vector3 AngularVelocityDeg) - { - reading.X = (float)filter.axis1Filter.Filter(AccelerationG.X, MotionManager.DeltaSeconds); - reading.Y = (float)filter.axis2Filter.Filter(AccelerationG.Y, MotionManager.DeltaSeconds); - reading.Z = (float)filter.axis3Filter.Filter(AccelerationG.Z, MotionManager.DeltaSeconds); - - base.ReadingChanged(); - } - - private void ReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args) - { - if (sensor is null) - return; - - foreach (var axis in reading_axis.Keys) - switch (MainWindow.CurrentDevice.AccelerometerAxisSwap[axis]) - { - default: - case 'X': - reading_axis[axis] = args.Reading.AccelerationX; - break; - case 'Y': - reading_axis[axis] = args.Reading.AccelerationY; - break; - case 'Z': - reading_axis[axis] = args.Reading.AccelerationZ; - break; - } - - reading.X = (float)reading_axis['X'] * MainWindow.CurrentDevice.AccelerometerAxis.X; - reading.Y = (float)reading_axis['Y'] * MainWindow.CurrentDevice.AccelerometerAxis.Y; - reading.Z = (float)reading_axis['Z'] * MainWindow.CurrentDevice.AccelerometerAxis.Z; - - base.ReadingChanged(); - } - - private void Shaken(Accelerometer sender, AccelerometerShakenEventArgs args) - { - // throw new NotImplementedException(); - } - - public new Vector3 GetCurrentReading(bool center = false, bool ratio = false) - { - return this.reading; - } +using HandheldCompanion.Managers; +using HandheldCompanion.Utils; +using HandheldCompanion.Views; +using System.Numerics; +using Windows.Devices.Sensors; +using static HandheldCompanion.Utils.DeviceUtils; + +namespace HandheldCompanion.Sensors; + +public class IMUAccelerometer : IMUSensor +{ + public new static SensorSpec sensorSpec = new() + { + minIn = -2.0f, + maxIn = 2.0f, + minOut = short.MinValue, + maxOut = short.MaxValue + }; + + public IMUAccelerometer(SensorFamily sensorFamily, int updateInterval) + { + this.sensorFamily = sensorFamily; + this.updateInterval = updateInterval; + + UpdateSensor(); + } + + public void UpdateSensor() + { + switch (sensorFamily) + { + case SensorFamily.Windows: + sensor = Accelerometer.GetDefault(); + break; + case SensorFamily.SerialUSBIMU: + sensor = SerialUSBIMU.GetDefault(); + break; + case SensorFamily.Controller: + sensor = new object(); + break; + } + + if (sensor is null) + { + LogManager.LogWarning("{0} not initialised as a {1}", ToString(), sensorFamily.ToString()); + return; + } + + switch (sensorFamily) + { + case SensorFamily.Windows: + ((Accelerometer)sensor).ReportInterval = (uint)updateInterval; + filter.SetFilterAttrs(MainWindow.CurrentDevice.oneEuroSettings.minCutoff, MainWindow.CurrentDevice.oneEuroSettings.beta); + + LogManager.LogInformation("{0} initialised as a {1}. Report interval set to {2}ms", ToString(), + sensorFamily.ToString(), updateInterval); + break; + case SensorFamily.SerialUSBIMU: + filter.SetFilterAttrs(((SerialUSBIMU)sensor).GetFilterCutoff(), ((SerialUSBIMU)sensor).GetFilterBeta()); + + LogManager.LogInformation("{0} initialised as a {1}. Baud rate set to {2}", ToString(), + sensorFamily.ToString(), ((SerialUSBIMU)sensor).GetInterval()); + break; + case SensorFamily.Controller: + LogManager.LogInformation("{0} initialised as a {1}", ToString(), sensorFamily.ToString()); + break; + } + + StartListening(); + } + + public override void StartListening() + { + switch (sensorFamily) + { + case SensorFamily.Windows: + ((Accelerometer)sensor).ReadingChanged += ReadingChanged; + break; + case SensorFamily.SerialUSBIMU: + ((SerialUSBIMU)sensor).ReadingChanged += ReadingChanged; + break; + } + } + + public override void StopListening() + { + if (sensor is null) + return; + + switch (sensorFamily) + { + case SensorFamily.Windows: + ((Accelerometer)sensor).ReadingChanged -= ReadingChanged; + break; + case SensorFamily.SerialUSBIMU: + ((SerialUSBIMU)sensor).ReadingChanged -= ReadingChanged; + break; + } + + sensor = null; + + base.StopListening(); + } + + private void ReadingChanged(float GyroAccelX, float GyroAccelY, float GyroAccelZ) + { + switch (sensorFamily) + { + case SensorFamily.Controller: + { + reading.X = GyroAccelX; + reading.Y = GyroAccelY; + reading.Z = GyroAccelZ; + + base.ReadingChanged(); + } + break; + } + } + + private void ReadingChanged(Vector3 AccelerationG, Vector3 AngularVelocityDeg) + { + reading.X = (float)filter.axis1Filter.Filter(AccelerationG.X, MotionManager.DeltaSeconds); + reading.Y = (float)filter.axis2Filter.Filter(AccelerationG.Y, MotionManager.DeltaSeconds); + reading.Z = (float)filter.axis3Filter.Filter(AccelerationG.Z, MotionManager.DeltaSeconds); + + base.ReadingChanged(); + } + + private void ReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args) + { + if (sensor is null) + return; + + foreach (var axis in reading_axis.Keys) +<<<<<<< HEAD + switch (MainWindow.CurrentDevice.AccelerometerAxisSwap[axis]) +======= + switch (MainWindow.CurrentDevice.AccelerationAxisSwap[axis]) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + default: + case 'X': + reading_axis[axis] = args.Reading.AccelerationX; + break; + case 'Y': + reading_axis[axis] = args.Reading.AccelerationY; + break; + case 'Z': + reading_axis[axis] = args.Reading.AccelerationZ; + break; + } + +<<<<<<< HEAD + reading.X = (float)reading_axis['X'] * MainWindow.CurrentDevice.AccelerometerAxis.X; + reading.Y = (float)reading_axis['Y'] * MainWindow.CurrentDevice.AccelerometerAxis.Y; + reading.Z = (float)reading_axis['Z'] * MainWindow.CurrentDevice.AccelerometerAxis.Z; +======= + reading.X = (float)reading_axis['X'] * MainWindow.CurrentDevice.AccelerationAxis.X; + reading.Y = (float)reading_axis['Y'] * MainWindow.CurrentDevice.AccelerationAxis.Y; + reading.Z = (float)reading_axis['Z'] * MainWindow.CurrentDevice.AccelerationAxis.Z; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + base.ReadingChanged(); + } + + private void Shaken(Accelerometer sender, AccelerometerShakenEventArgs args) + { + // throw new NotImplementedException(); + } + + public new Vector3 GetCurrentReading(bool center = false, bool ratio = false) + { + return this.reading; + } } \ No newline at end of file diff --git a/HandheldCompanion/Sensors/IMUGyrometer.cs b/HandheldCompanion/Sensors/IMUGyrometer.cs index 9246f640b..21a905110 100644 --- a/HandheldCompanion/Sensors/IMUGyrometer.cs +++ b/HandheldCompanion/Sensors/IMUGyrometer.cs @@ -1,160 +1,170 @@ -using HandheldCompanion.Managers; -using HandheldCompanion.Utils; -using HandheldCompanion.Views; -using System.Numerics; -using Windows.Devices.Sensors; -using static HandheldCompanion.Utils.DeviceUtils; - -namespace HandheldCompanion.Sensors; - -public class IMUGyrometer : IMUSensor -{ - public new static SensorSpec sensorSpec = new() - { - minIn = -128.0f, - maxIn = 128.0f, - minOut = -2048.0f, - maxOut = 2048.0f - }; - - public IMUGyrometer(SensorFamily sensorFamily, int updateInterval) - { - this.sensorFamily = sensorFamily; - this.updateInterval = updateInterval; - - UpdateSensor(); - } - - public void UpdateSensor() - { - switch (sensorFamily) - { - case SensorFamily.Windows: - sensor = Gyrometer.GetDefault(); - break; - case SensorFamily.SerialUSBIMU: - sensor = SerialUSBIMU.GetDefault(); - break; - case SensorFamily.Controller: - sensor = new object(); - break; - } - - if (sensor is null) - { - LogManager.LogWarning("{0} not initialised as a {1}", ToString(), sensorFamily.ToString()); - return; - } - - switch (sensorFamily) - { - case SensorFamily.Windows: - { - ((Gyrometer)sensor).ReportInterval = (uint)updateInterval; - - LogManager.LogInformation("{0} initialised as a {1}. Report interval set to {2}ms", ToString(), - sensorFamily.ToString(), updateInterval); - } - break; - case SensorFamily.SerialUSBIMU: - LogManager.LogInformation("{0} initialised as a {1}. Baud rate set to {2}", ToString(), - sensorFamily.ToString(), ((SerialUSBIMU)sensor).GetInterval()); - break; - case SensorFamily.Controller: - LogManager.LogInformation("{0} initialised as a {1}", ToString(), sensorFamily.ToString()); - break; - } - - StartListening(); - } - - public override void StartListening() - { - switch (sensorFamily) - { - case SensorFamily.Windows: - ((Gyrometer)sensor).ReadingChanged += ReadingChanged; - break; - case SensorFamily.SerialUSBIMU: - ((SerialUSBIMU)sensor).ReadingChanged += ReadingChanged; - break; - } - } - - public override void StopListening() - { - if (sensor is null) - return; - - switch (sensorFamily) - { - case SensorFamily.Windows: - ((Gyrometer)sensor).ReadingChanged -= ReadingChanged; - break; - case SensorFamily.SerialUSBIMU: - ((SerialUSBIMU)sensor).ReadingChanged -= ReadingChanged; - break; - } - - sensor = null; - - base.StopListening(); - } - - private void ReadingChanged(float GyroRoll, float GyroPitch, float GyroYaw) - { - switch (sensorFamily) - { - case SensorFamily.Controller: - { - reading.X = GyroRoll; - reading.Y = GyroPitch; - reading.Z = GyroYaw; - - base.ReadingChanged(); - } - break; - } - } - - private void ReadingChanged(Vector3 AccelerationG, Vector3 AngularVelocityDeg) - { - reading.X = AngularVelocityDeg.X; - reading.Y = AngularVelocityDeg.Y; - reading.Z = AngularVelocityDeg.Z; - - base.ReadingChanged(); - } - - private void ReadingChanged(Gyrometer sender, GyrometerReadingChangedEventArgs args) - { - if (sensor is null) - return; - - foreach (var axis in reading_axis.Keys) - switch (MainWindow.CurrentDevice.GyrometerAxisSwap[axis]) - { - default: - case 'X': - reading_axis[axis] = args.Reading.AngularVelocityX; - break; - case 'Y': - reading_axis[axis] = args.Reading.AngularVelocityY; - break; - case 'Z': - reading_axis[axis] = args.Reading.AngularVelocityZ; - break; - } - - reading.X = (float)reading_axis['X'] * MainWindow.CurrentDevice.GyrometerAxis.X; - reading.Y = (float)reading_axis['Y'] * MainWindow.CurrentDevice.GyrometerAxis.Y; - reading.Z = (float)reading_axis['Z'] * MainWindow.CurrentDevice.GyrometerAxis.Z; - - base.ReadingChanged(); - } - - public new Vector3 GetCurrentReading() - { - return this.reading; - } +using HandheldCompanion.Managers; +using HandheldCompanion.Utils; +using HandheldCompanion.Views; +using System.Numerics; +using Windows.Devices.Sensors; +using static HandheldCompanion.Utils.DeviceUtils; + +namespace HandheldCompanion.Sensors; + +public class IMUGyrometer : IMUSensor +{ + public new static SensorSpec sensorSpec = new() + { + minIn = -128.0f, + maxIn = 128.0f, + minOut = -2048.0f, + maxOut = 2048.0f + }; + + public IMUGyrometer(SensorFamily sensorFamily, int updateInterval) + { + this.sensorFamily = sensorFamily; + this.updateInterval = updateInterval; + + UpdateSensor(); + } + + public void UpdateSensor() + { + switch (sensorFamily) + { + case SensorFamily.Windows: + sensor = Gyrometer.GetDefault(); + break; + case SensorFamily.SerialUSBIMU: + sensor = SerialUSBIMU.GetDefault(); + break; + case SensorFamily.Controller: + sensor = new object(); + break; + } + + if (sensor is null) + { + LogManager.LogWarning("{0} not initialised as a {1}", ToString(), sensorFamily.ToString()); + return; + } + + switch (sensorFamily) + { + case SensorFamily.Windows: + { + ((Gyrometer)sensor).ReportInterval = (uint)updateInterval; + + LogManager.LogInformation("{0} initialised as a {1}. Report interval set to {2}ms", ToString(), + sensorFamily.ToString(), updateInterval); + } + break; + case SensorFamily.SerialUSBIMU: + LogManager.LogInformation("{0} initialised as a {1}. Baud rate set to {2}", ToString(), + sensorFamily.ToString(), ((SerialUSBIMU)sensor).GetInterval()); + break; + case SensorFamily.Controller: + LogManager.LogInformation("{0} initialised as a {1}", ToString(), sensorFamily.ToString()); + break; + } + + StartListening(); + } + + public override void StartListening() + { + switch (sensorFamily) + { + case SensorFamily.Windows: + ((Gyrometer)sensor).ReadingChanged += ReadingChanged; + break; + case SensorFamily.SerialUSBIMU: + ((SerialUSBIMU)sensor).ReadingChanged += ReadingChanged; + break; + } + } + + public override void StopListening() + { + if (sensor is null) + return; + + switch (sensorFamily) + { + case SensorFamily.Windows: + ((Gyrometer)sensor).ReadingChanged -= ReadingChanged; + break; + case SensorFamily.SerialUSBIMU: + ((SerialUSBIMU)sensor).ReadingChanged -= ReadingChanged; + break; + } + + sensor = null; + + base.StopListening(); + } + + private void ReadingChanged(float GyroRoll, float GyroPitch, float GyroYaw) + { + switch (sensorFamily) + { + case SensorFamily.Controller: + { + reading.X = GyroRoll; + reading.Y = GyroPitch; + reading.Z = GyroYaw; + + base.ReadingChanged(); + } + break; + } + } + + private void ReadingChanged(Vector3 AccelerationG, Vector3 AngularVelocityDeg) + { + reading.X = AngularVelocityDeg.X; + reading.Y = AngularVelocityDeg.Y; + reading.Z = AngularVelocityDeg.Z; + + base.ReadingChanged(); + } + + private void ReadingChanged(Gyrometer sender, GyrometerReadingChangedEventArgs args) + { + if (sensor is null) + return; + + foreach (var axis in reading_axis.Keys) +<<<<<<< HEAD + switch (MainWindow.CurrentDevice.GyrometerAxisSwap[axis]) +======= + switch (MainWindow.CurrentDevice.AngularVelocityAxisSwap[axis]) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + default: + case 'X': + reading_axis[axis] = args.Reading.AngularVelocityX; + break; + case 'Y': + reading_axis[axis] = args.Reading.AngularVelocityY; + break; + case 'Z': + reading_axis[axis] = args.Reading.AngularVelocityZ; + break; + } + +<<<<<<< HEAD + reading.X = (float)reading_axis['X'] * MainWindow.CurrentDevice.GyrometerAxis.X; + reading.Y = (float)reading_axis['Y'] * MainWindow.CurrentDevice.GyrometerAxis.Y; + reading.Z = (float)reading_axis['Z'] * MainWindow.CurrentDevice.GyrometerAxis.Z; +======= + reading.X = (float)reading_axis['X'] * MainWindow.CurrentDevice.AngularVelocityAxis.X; + reading.Y = (float)reading_axis['Y'] * MainWindow.CurrentDevice.AngularVelocityAxis.Y; + reading.Z = (float)reading_axis['Z'] * MainWindow.CurrentDevice.AngularVelocityAxis.Z; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + base.ReadingChanged(); + } + + public new Vector3 GetCurrentReading() + { + return this.reading; + } } \ No newline at end of file diff --git a/HandheldCompanion/Targets/Xbox360Target.cs b/HandheldCompanion/Targets/Xbox360Target.cs index f1a092938..3920d1eb8 100644 --- a/HandheldCompanion/Targets/Xbox360Target.cs +++ b/HandheldCompanion/Targets/Xbox360Target.cs @@ -1,122 +1,130 @@ -using HandheldCompanion.Inputs; -using HandheldCompanion.Managers; -using HandheldCompanion.Utils; -using Nefarius.ViGEm.Client.Exceptions; -using Nefarius.ViGEm.Client.Targets; -using Nefarius.ViGEm.Client.Targets.Xbox360; -using System; - -namespace HandheldCompanion.Targets -{ - internal partial class Xbox360Target : ViGEmTarget - { - private new IXbox360Controller virtualController; - - public Xbox360Target(ushort vendorId, ushort productId) : base() - { - // initialize controller - HID = HIDmode.Xbox360Controller; - - virtualController = VirtualManager.vClient.CreateXbox360Controller(vendorId, productId); - virtualController.AutoSubmitReport = false; - virtualController.FeedbackReceived += FeedbackReceived; - - LogManager.LogInformation("{0} initialized, {1}", ToString(), virtualController); - } - - public override void Connect() - { - if (IsConnected) - return; - - try - { - virtualController.Connect(); - TimerManager.Tick += UpdateReport; - - base.Connect(); - } - catch (Exception ex) - { - virtualController.Disconnect(); - LogManager.LogWarning("Failed to connect {0}. {1}", this.ToString(), ex.Message); - } - } - - public override void Disconnect() - { - if (!IsConnected) - return; - - try - { - virtualController.Disconnect(); - TimerManager.Tick -= UpdateReport; - - base.Disconnect(); - } - catch { } - } - - public void FeedbackReceived(object sender, Xbox360FeedbackReceivedEventArgs e) - { - SendVibrate(e.LargeMotor, e.SmallMotor); - } - - public override unsafe void UpdateReport(long ticks) - { - if (!IsConnected) - return; - - virtualController.SetAxisValue(Xbox360Axis.LeftThumbX, Inputs.AxisState[AxisFlags.LeftStickX]); - virtualController.SetAxisValue(Xbox360Axis.LeftThumbY, Inputs.AxisState[AxisFlags.LeftStickY]); - virtualController.SetAxisValue(Xbox360Axis.RightThumbX, Inputs.AxisState[AxisFlags.RightStickX]); - virtualController.SetAxisValue(Xbox360Axis.RightThumbY, Inputs.AxisState[AxisFlags.RightStickY]); - - virtualController.SetSliderValue(Xbox360Slider.LeftTrigger, (byte)Inputs.AxisState[AxisFlags.L2]); - virtualController.SetSliderValue(Xbox360Slider.RightTrigger, (byte)Inputs.AxisState[AxisFlags.R2]); - - virtualController.SetButtonState(Xbox360Button.A, Inputs.ButtonState[ButtonFlags.B1]); - virtualController.SetButtonState(Xbox360Button.B, Inputs.ButtonState[ButtonFlags.B2]); - virtualController.SetButtonState(Xbox360Button.X, Inputs.ButtonState[ButtonFlags.B3]); - virtualController.SetButtonState(Xbox360Button.Y, Inputs.ButtonState[ButtonFlags.B4]); - - virtualController.SetButtonState(Xbox360Button.Up, Inputs.ButtonState[ButtonFlags.DPadUp]); - virtualController.SetButtonState(Xbox360Button.Down, Inputs.ButtonState[ButtonFlags.DPadDown]); - virtualController.SetButtonState(Xbox360Button.Left, Inputs.ButtonState[ButtonFlags.DPadLeft]); - virtualController.SetButtonState(Xbox360Button.Right, Inputs.ButtonState[ButtonFlags.DPadRight]); - - virtualController.SetButtonState(Xbox360Button.Back, Inputs.ButtonState[ButtonFlags.Back]); - virtualController.SetButtonState(Xbox360Button.Start, Inputs.ButtonState[ButtonFlags.Start]); - - virtualController.SetButtonState(Xbox360Button.LeftShoulder, Inputs.ButtonState[ButtonFlags.L1]); - virtualController.SetButtonState(Xbox360Button.RightShoulder, Inputs.ButtonState[ButtonFlags.R1]); - - virtualController.SetButtonState(Xbox360Button.LeftThumb, Inputs.ButtonState[ButtonFlags.LeftStickClick]); - virtualController.SetButtonState(Xbox360Button.RightThumb, Inputs.ButtonState[ButtonFlags.RightStickClick]); - - virtualController.SetButtonState(Xbox360Button.Guide, Inputs.ButtonState[ButtonFlags.Special]); - - try - { - virtualController.SubmitReport(); - } - catch (VigemBusNotFoundException ex) - { - LogManager.LogCritical(ex.Message); - } - catch (VigemInvalidTargetException ex) - { - LogManager.LogCritical(ex.Message); - } - } - - public override void Dispose() - { - if (virtualController is not null) - virtualController.Disconnect(); - - base.Dispose(); - } - } +using HandheldCompanion.Inputs; +using HandheldCompanion.Managers; +using HandheldCompanion.Utils; +using Nefarius.ViGEm.Client.Exceptions; +using Nefarius.ViGEm.Client.Targets; +using Nefarius.ViGEm.Client.Targets.Xbox360; +using System; + +namespace HandheldCompanion.Targets +{ + internal partial class Xbox360Target : ViGEmTarget + { + private new IXbox360Controller virtualController; + +<<<<<<< HEAD + public Xbox360Target(ushort vendorId, ushort productId) : base() +======= + public Xbox360Target() : base() +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + // initialize controller + HID = HIDmode.Xbox360Controller; + +<<<<<<< HEAD + virtualController = VirtualManager.vClient.CreateXbox360Controller(vendorId, productId); +======= + virtualController = VirtualManager.vClient.CreateXbox360Controller(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + virtualController.AutoSubmitReport = false; + virtualController.FeedbackReceived += FeedbackReceived; + + LogManager.LogInformation("{0} initialized, {1}", ToString(), virtualController); + } + + public override void Connect() + { + if (IsConnected) + return; + + try + { + virtualController.Connect(); + TimerManager.Tick += UpdateReport; + + base.Connect(); + } + catch (Exception ex) + { + virtualController.Disconnect(); + LogManager.LogWarning("Failed to connect {0}. {1}", this.ToString(), ex.Message); + } + } + + public override void Disconnect() + { + if (!IsConnected) + return; + + try + { + virtualController.Disconnect(); + TimerManager.Tick -= UpdateReport; + + base.Disconnect(); + } + catch { } + } + + public void FeedbackReceived(object sender, Xbox360FeedbackReceivedEventArgs e) + { + SendVibrate(e.LargeMotor, e.SmallMotor); + } + + public override unsafe void UpdateReport(long ticks) + { + if (!IsConnected) + return; + + virtualController.SetAxisValue(Xbox360Axis.LeftThumbX, Inputs.AxisState[AxisFlags.LeftStickX]); + virtualController.SetAxisValue(Xbox360Axis.LeftThumbY, Inputs.AxisState[AxisFlags.LeftStickY]); + virtualController.SetAxisValue(Xbox360Axis.RightThumbX, Inputs.AxisState[AxisFlags.RightStickX]); + virtualController.SetAxisValue(Xbox360Axis.RightThumbY, Inputs.AxisState[AxisFlags.RightStickY]); + + virtualController.SetSliderValue(Xbox360Slider.LeftTrigger, (byte)Inputs.AxisState[AxisFlags.L2]); + virtualController.SetSliderValue(Xbox360Slider.RightTrigger, (byte)Inputs.AxisState[AxisFlags.R2]); + + virtualController.SetButtonState(Xbox360Button.A, Inputs.ButtonState[ButtonFlags.B1]); + virtualController.SetButtonState(Xbox360Button.B, Inputs.ButtonState[ButtonFlags.B2]); + virtualController.SetButtonState(Xbox360Button.X, Inputs.ButtonState[ButtonFlags.B3]); + virtualController.SetButtonState(Xbox360Button.Y, Inputs.ButtonState[ButtonFlags.B4]); + + virtualController.SetButtonState(Xbox360Button.Up, Inputs.ButtonState[ButtonFlags.DPadUp]); + virtualController.SetButtonState(Xbox360Button.Down, Inputs.ButtonState[ButtonFlags.DPadDown]); + virtualController.SetButtonState(Xbox360Button.Left, Inputs.ButtonState[ButtonFlags.DPadLeft]); + virtualController.SetButtonState(Xbox360Button.Right, Inputs.ButtonState[ButtonFlags.DPadRight]); + + virtualController.SetButtonState(Xbox360Button.Back, Inputs.ButtonState[ButtonFlags.Back]); + virtualController.SetButtonState(Xbox360Button.Start, Inputs.ButtonState[ButtonFlags.Start]); + + virtualController.SetButtonState(Xbox360Button.LeftShoulder, Inputs.ButtonState[ButtonFlags.L1]); + virtualController.SetButtonState(Xbox360Button.RightShoulder, Inputs.ButtonState[ButtonFlags.R1]); + + virtualController.SetButtonState(Xbox360Button.LeftThumb, Inputs.ButtonState[ButtonFlags.LeftStickClick]); + virtualController.SetButtonState(Xbox360Button.RightThumb, Inputs.ButtonState[ButtonFlags.RightStickClick]); + + virtualController.SetButtonState(Xbox360Button.Guide, Inputs.ButtonState[ButtonFlags.Special]); + + try + { + virtualController.SubmitReport(); + } + catch (VigemBusNotFoundException ex) + { + LogManager.LogCritical(ex.Message); + } + catch (VigemInvalidTargetException ex) + { + LogManager.LogCritical(ex.Message); + } + } + + public override void Dispose() + { + if (virtualController is not null) + virtualController.Disconnect(); + + base.Dispose(); + } + } } \ No newline at end of file diff --git a/HandheldCompanion/Utils/RegistryUtils.cs b/HandheldCompanion/Utils/RegistryUtils.cs index 2734457ba..4346a3f7c 100644 --- a/HandheldCompanion/Utils/RegistryUtils.cs +++ b/HandheldCompanion/Utils/RegistryUtils.cs @@ -1,87 +1,136 @@ -using Microsoft.Win32; -using System; - -namespace HandheldCompanion.Utils; - -public static class RegistryUtils -{ - private const string HKLM = @"HKEY_LOCAL_MACHINE"; - - private static object GetValue(string key, string valueName) - { - var keyName = HKLM + "\\" + key; - return Registry.GetValue(keyName, valueName, null); - } - - public static void SetValue(string key, string valueName, object value) - { - var keyName = HKLM + "\\" + key; - Registry.SetValue(keyName, valueName, value); - } - - public static bool KeyExists(string key, string valueName) - { - var keyName = HKLM + "\\" + key; - return Registry.GetValue(keyName, valueName, null) != null; - } - - public static string GetString(string key, string valueName) - { - return Convert.ToString(GetValue(key, valueName)); - } - - public static int GetInt(string key, string valueName) - { - try - { - return Convert.ToInt32(GetValue(key, valueName)); - } - catch { } - - return 0; - } - - public static bool GetBoolean(string key, string valueName) - { - try - { - return Convert.ToBoolean(GetValue(key, valueName)); - } - catch { } - - return false; - } - - public static bool SearchForKeyValue(string key, string valueName, string value) - { - // Open the root registry key for reading - RegistryKey root = Registry.LocalMachine.OpenSubKey(key); - - // Check if the root key exists - if (root != null) - { - // Get the names of the subkeys under the root key - string[] subkeys = root.GetSubKeyNames(); - - // Loop through each subkey - foreach (string subkey in subkeys) - { - // Open the subkey for reading - RegistryKey subKey = root.OpenSubKey(subkey); - - // Check if the subkey exists and has a value named DeviceDesc - if (subKey != null && subKey.GetValue(valueName) != null) - { - // Get the value of DeviceDesc as a string - string subKeyDesc = subKey.GetValue(valueName).ToString(); - - // Check if the value contains the target strings - if (subKeyDesc.Contains(value, StringComparison.InvariantCultureIgnoreCase)) - return true; - } - } - } - - return false; - } +using Microsoft.Win32; +using System; + +namespace HandheldCompanion.Utils; + +public static class RegistryUtils +{ + private const string HKLM = @"HKEY_LOCAL_MACHINE"; + +<<<<<<< HEAD + private static object GetValue(string key, string valueName) + { + var keyName = HKLM + "\\" + key; + return Registry.GetValue(keyName, valueName, null); + } + + public static void SetValue(string key, string valueName, object value) + { + var keyName = HKLM + "\\" + key; + Registry.SetValue(keyName, valueName, value); + } + + public static bool KeyExists(string key, string valueName) + { + var keyName = HKLM + "\\" + key; + return Registry.GetValue(keyName, valueName, null) != null; + } + + public static string GetString(string key, string valueName) + { + return Convert.ToString(GetValue(key, valueName)); + } + + public static int GetInt(string key, string valueName) + { + try + { + return Convert.ToInt32(GetValue(key, valueName)); + } + catch { } +======= + private static object GetValue(string key, string value) + { + var keyName = HKLM + "\\" + key; + return Registry.GetValue(keyName, value, null); + } + + public static string GetString(string key, string value) + { + return Convert.ToString(GetValue(key, value)); + } + + public static int GetInt(string key, string value) + { + try + { + return Convert.ToInt32(GetValue(key, value)); + } + catch + { + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + return 0; + } + +<<<<<<< HEAD + public static bool GetBoolean(string key, string valueName) + { + try + { + return Convert.ToBoolean(GetValue(key, valueName)); + } + catch { } +======= + public static bool GetBoolean(string key, string value) + { + try + { + return Convert.ToBoolean(GetValue(key, value)); + } + catch + { + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + return false; + } + +<<<<<<< HEAD + public static bool SearchForKeyValue(string key, string valueName, string value) +======= + public static bool SearchForKeyValue(string key, string value, string content) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + // Open the root registry key for reading + RegistryKey root = Registry.LocalMachine.OpenSubKey(key); + + // Check if the root key exists + if (root != null) + { + // Get the names of the subkeys under the root key + string[] subkeys = root.GetSubKeyNames(); + + // Loop through each subkey + foreach (string subkey in subkeys) + { + // Open the subkey for reading + RegistryKey subKey = root.OpenSubKey(subkey); + + // Check if the subkey exists and has a value named DeviceDesc +<<<<<<< HEAD + if (subKey != null && subKey.GetValue(valueName) != null) + { + // Get the value of DeviceDesc as a string + string subKeyDesc = subKey.GetValue(valueName).ToString(); + + // Check if the value contains the target strings + if (subKeyDesc.Contains(value, StringComparison.InvariantCultureIgnoreCase)) +======= + if (subKey != null && subKey.GetValue(value) != null) + { + // Get the value of DeviceDesc as a string + string subKeyDesc = subKey.GetValue(value).ToString(); + + // Check if the value contains the target strings + if (subKeyDesc.Contains(content, StringComparison.InvariantCultureIgnoreCase)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + return true; + } + } + } + + return false; + } } \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/AboutPage.xaml b/HandheldCompanion/Views/Pages/AboutPage.xaml index 40bb5f4ae..529c29776 100644 --- a/HandheldCompanion/Views/Pages/AboutPage.xaml +++ b/HandheldCompanion/Views/Pages/AboutPage.xaml @@ -1,284 +1,287 @@ -<Page - x:Class="HandheldCompanion.Views.Pages.AboutPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Name="About" - Title="{x:Static resx:Resources.AboutPage_About}" - d:Background="White" - d:DesignHeight="1000" - d:DesignWidth="1000" - KeepAlive="True" - mc:Ignorable="d"> - - <Grid Name="MainGrid" Margin="20"> - <ui:SimpleStackPanel Spacing="6"> - - <!-- Warning --> - <Border - Name="WarningBorder" - Padding="15,12,12,12" - d:Visibility="Visible" - ui:ThemeManager.RequestedTheme="{Binding Source={x:Static ui:ThemeManager.Current}, Path=ActualApplicationTheme, Converter={StaticResource InverseAppThemeConverter}}" - Background="{DynamicResource SystemControlPageBackgroundAltHighBrush}" - CornerRadius="{DynamicResource ControlCornerRadius}" - Visibility="Collapsed"> - - <DockPanel> - <Grid> - <ui:FontIcon - ui:ThemeManager.RequestedTheme="{Binding Source={x:Static ui:ThemeManager.Current}, Path=ActualApplicationTheme, Converter={StaticResource InverseAppThemeConverter}}" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Glyph="" /> - <ui:FontIcon FontFamily="{DynamicResource SymbolThemeFontFamily}" Glyph="" /> - </Grid> - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock - Name="WarningContent" - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Style="{StaticResource BodyTextBlockStyle}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - </Border> - - <!-- Device details --> - <Border - Name="HandheldGrid" - Padding="20,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}" - Visibility="Collapsed"> - - <Grid> - <ui:SimpleStackPanel Orientation="Horizontal" Spacing="12"> - <Image - Name="ImageDevice" - Width="120" - Height="50"> - <Image.Source> - <BitmapImage UriSource="/Resources/device_generic.png" /> - </Image.Source> - </Image> - <ui:SimpleStackPanel VerticalAlignment="Center" Orientation="Vertical"> - <TextBlock - Name="LabelManufacturer" - FontSize="20" - Style="{StaticResource BaseTextBlockStyle}" - Text="{x:Static resx:Resources.AboutPage_Manufacturer}" /> - <TextBlock - Name="LabelProductName" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Text="{x:Static resx:Resources.AboutPage_ProductName}" /> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - </Border> - - <!-- Sensor specifications --> - <Expander - HorizontalAlignment="Stretch" - Expanded="Expander_Expanded" - IsExpanded="True"> - <Expander.Header> - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.AboutPage_SensorSpecification}" /> - </ui:SimpleStackPanel> - </DockPanel> - </Expander.Header> - - <Expander.Content> - <Grid FlowDirection="LeftToRight"> - <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="120" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <ui:SimpleStackPanel Spacing="12"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.AboutPage_SensorInternal}" /> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.AboutPage_SensorExternal}" /> - </ui:SimpleStackPanel> - - <ui:SimpleStackPanel Grid.Column="1" Spacing="12"> - <TextBlock - Name="SensorInternal" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.AboutPage_NotApplicable}" /> - <TextBlock - Name="SensorExternal" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.AboutPage_NotApplicable}" /> - </ui:SimpleStackPanel> - </Grid> - </ui:SimpleStackPanel> - </Grid> - </Expander.Content> - </Expander> - - <!-- Service --> - <Expander - HorizontalAlignment="Stretch" - Expanded="Expander_Expanded" - IsExpanded="True"> - <Expander.Header> - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.AboutPage_Service}" /> - </ui:SimpleStackPanel> - </DockPanel> - </Expander.Header> - - <Expander.Content> - <Grid FlowDirection="LeftToRight"> - <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="120" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <Grid.RowDefinitions> - <RowDefinition /> - <RowDefinition /> - <RowDefinition /> - <RowDefinition /> - <RowDefinition /> - </Grid.RowDefinitions> - - <!-- About --> - <TextBlock - Grid.Row="0" - Grid.Column="0" - Margin="0,0,0,5" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.AboutPage_Version}" /> - <TextBlock - Name="VersionValue" - Grid.Row="0" - Grid.Column="1" - Margin="0,0,0,5" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.AboutPage_NotApplicable}" /> - - <!-- Author --> - <TextBlock - Grid.Row="1" - Grid.Column="0" - Margin="0,0,0,5" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.AboutPage_Author}" /> - <TextBlock - Grid.Row="1" - Grid.Column="1" - Margin="0,0,0,5" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="BenjaminLSR" /> - - <!-- Contributors --> - <TextBlock - Grid.Row="2" - Grid.Column="0" - Margin="0,0,0,5" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.AboutPage_Contributors}" /> - <TextBlock - Grid.Row="2" - Grid.Column="1" - Margin="0,0,0,5" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="Nefarius, CasperH2O, B-Core, Frank东, Toomy, Staubgeborener, Havner, Shadowflare, Trippy, Bagboi" /> - - <!-- Description --> - <TextBlock - Grid.Row="3" - Grid.Column="0" - Margin="0,0,0,5" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.AboutPage_Description}" /> - <TextBlock - Grid.Row="3" - Grid.Column="1" - Margin="0,0,0,5" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.AboutPage_AboutDescription}" /> - - <!-- Partner --> - <TextBlock - Grid.Row="4" - Grid.Column="0" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.AboutPage_Partner}" /> - <TextBlock - Grid.Row="4" - Grid.Column="1" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource BodyTextBlockStyle}"> - <Hyperlink NavigateUri="https://droix.net/?ref=dxhc" RequestNavigate="Hyperlink_RequestNavigate"> - DroiX - </Hyperlink></TextBlock> - </Grid> - - <!-- Separator --> - <Separator - Margin="-46,0,-16,0" - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" /> - - <ui:SimpleStackPanel Orientation="Horizontal" Spacing="12"> - <TextBlock Text="{x:Static resx:Resources.AboutPage_RelatedLinks}" /> - <TextBlock> - <Hyperlink NavigateUri="https://github.com/Valkirie/HandheldCompanion" RequestNavigate="Hyperlink_RequestNavigate"> - SourceCode - </Hyperlink> - </TextBlock> - <TextBlock> - <Hyperlink NavigateUri="https://github.com/Valkirie/HandheldCompanion/wiki" RequestNavigate="Hyperlink_RequestNavigate"> - Wiki - </Hyperlink> - </TextBlock> - <TextBlock> - <Hyperlink NavigateUri="https://www.paypal.com/paypalme/BenjaminLSR" RequestNavigate="Hyperlink_RequestNavigate"> - Donate - </Hyperlink> - </TextBlock> - <TextBlock> - <Hyperlink NavigateUri="https://www.patreon.com/handheldcompanion" RequestNavigate="Hyperlink_RequestNavigate"> - Patreon - </Hyperlink> - </TextBlock> - </ui:SimpleStackPanel> - - </ui:SimpleStackPanel> - </Grid> - </Expander.Content> - </Expander> - - </ui:SimpleStackPanel> - </Grid> +<Page + x:Class="HandheldCompanion.Views.Pages.AboutPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" +<<<<<<< HEAD + Name="About" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Title="{x:Static resx:Resources.AboutPage_About}" + d:Background="White" + d:DesignHeight="1000" + d:DesignWidth="1000" + KeepAlive="True" + mc:Ignorable="d"> + + <Grid Name="MainGrid" Margin="20"> + <ui:SimpleStackPanel Spacing="6"> + + <!-- Warning --> + <Border + Name="WarningBorder" + Padding="15,12,12,12" + d:Visibility="Visible" + ui:ThemeManager.RequestedTheme="{Binding Source={x:Static ui:ThemeManager.Current}, Path=ActualApplicationTheme, Converter={StaticResource InverseAppThemeConverter}}" + Background="{DynamicResource SystemControlPageBackgroundAltHighBrush}" + CornerRadius="{DynamicResource ControlCornerRadius}" + Visibility="Collapsed"> + + <DockPanel> + <Grid> + <ui:FontIcon + ui:ThemeManager.RequestedTheme="{Binding Source={x:Static ui:ThemeManager.Current}, Path=ActualApplicationTheme, Converter={StaticResource InverseAppThemeConverter}}" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Glyph="" /> + <ui:FontIcon FontFamily="{DynamicResource SymbolThemeFontFamily}" Glyph="" /> + </Grid> + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock + Name="WarningContent" + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Style="{StaticResource BodyTextBlockStyle}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Border> + + <!-- Device details --> + <Border + Name="HandheldGrid" + Padding="20,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}" + Visibility="Collapsed"> + + <Grid> + <ui:SimpleStackPanel Orientation="Horizontal" Spacing="12"> + <Image + Name="ImageDevice" + Width="120" + Height="50"> + <Image.Source> + <BitmapImage UriSource="/Resources/device_generic.png" /> + </Image.Source> + </Image> + <ui:SimpleStackPanel VerticalAlignment="Center" Orientation="Vertical"> + <TextBlock + Name="LabelManufacturer" + FontSize="20" + Style="{StaticResource BaseTextBlockStyle}" + Text="{x:Static resx:Resources.AboutPage_Manufacturer}" /> + <TextBlock + Name="LabelProductName" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Text="{x:Static resx:Resources.AboutPage_ProductName}" /> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + </Border> + + <!-- Sensor specifications --> + <Expander + HorizontalAlignment="Stretch" + Expanded="Expander_Expanded" + IsExpanded="True"> + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.AboutPage_SensorSpecification}" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + + <Expander.Content> + <Grid FlowDirection="LeftToRight"> + <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="120" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <ui:SimpleStackPanel Spacing="12"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.AboutPage_SensorInternal}" /> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.AboutPage_SensorExternal}" /> + </ui:SimpleStackPanel> + + <ui:SimpleStackPanel Grid.Column="1" Spacing="12"> + <TextBlock + Name="SensorInternal" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.AboutPage_NotApplicable}" /> + <TextBlock + Name="SensorExternal" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.AboutPage_NotApplicable}" /> + </ui:SimpleStackPanel> + </Grid> + </ui:SimpleStackPanel> + </Grid> + </Expander.Content> + </Expander> + + <!-- Service --> + <Expander + HorizontalAlignment="Stretch" + Expanded="Expander_Expanded" + IsExpanded="True"> + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.AboutPage_Service}" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + + <Expander.Content> + <Grid FlowDirection="LeftToRight"> + <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="120" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <Grid.RowDefinitions> + <RowDefinition /> + <RowDefinition /> + <RowDefinition /> + <RowDefinition /> + <RowDefinition /> + </Grid.RowDefinitions> + + <!-- About --> + <TextBlock + Grid.Row="0" + Grid.Column="0" + Margin="0,0,0,5" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.AboutPage_Version}" /> + <TextBlock + Name="VersionValue" + Grid.Row="0" + Grid.Column="1" + Margin="0,0,0,5" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.AboutPage_NotApplicable}" /> + + <!-- Author --> + <TextBlock + Grid.Row="1" + Grid.Column="0" + Margin="0,0,0,5" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.AboutPage_Author}" /> + <TextBlock + Grid.Row="1" + Grid.Column="1" + Margin="0,0,0,5" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="BenjaminLSR" /> + + <!-- Contributors --> + <TextBlock + Grid.Row="2" + Grid.Column="0" + Margin="0,0,0,5" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.AboutPage_Contributors}" /> + <TextBlock + Grid.Row="2" + Grid.Column="1" + Margin="0,0,0,5" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="Nefarius, CasperH2O, B-Core, Frank东, Toomy, Staubgeborener, Havner, Shadowflare, Trippy, Bagboi" /> + + <!-- Description --> + <TextBlock + Grid.Row="3" + Grid.Column="0" + Margin="0,0,0,5" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.AboutPage_Description}" /> + <TextBlock + Grid.Row="3" + Grid.Column="1" + Margin="0,0,0,5" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.AboutPage_AboutDescription}" /> + + <!-- Partner --> + <TextBlock + Grid.Row="4" + Grid.Column="0" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.AboutPage_Partner}" /> + <TextBlock + Grid.Row="4" + Grid.Column="1" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource BodyTextBlockStyle}"> + <Hyperlink NavigateUri="https://droix.net/?ref=dxhc" RequestNavigate="Hyperlink_RequestNavigate"> + DroiX + </Hyperlink></TextBlock> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + + <ui:SimpleStackPanel Orientation="Horizontal" Spacing="12"> + <TextBlock Text="{x:Static resx:Resources.AboutPage_RelatedLinks}" /> + <TextBlock> + <Hyperlink NavigateUri="https://github.com/Valkirie/HandheldCompanion" RequestNavigate="Hyperlink_RequestNavigate"> + SourceCode + </Hyperlink> + </TextBlock> + <TextBlock> + <Hyperlink NavigateUri="https://github.com/Valkirie/HandheldCompanion/wiki" RequestNavigate="Hyperlink_RequestNavigate"> + Wiki + </Hyperlink> + </TextBlock> + <TextBlock> + <Hyperlink NavigateUri="https://www.paypal.com/paypalme/BenjaminLSR" RequestNavigate="Hyperlink_RequestNavigate"> + Donate + </Hyperlink> + </TextBlock> + <TextBlock> + <Hyperlink NavigateUri="https://www.patreon.com/handheldcompanion" RequestNavigate="Hyperlink_RequestNavigate"> + Patreon + </Hyperlink> + </TextBlock> + </ui:SimpleStackPanel> + + </ui:SimpleStackPanel> + </Grid> + </Expander.Content> + </Expander> + + </ui:SimpleStackPanel> + </Grid> </Page> \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/ControllerPage.xaml b/HandheldCompanion/Views/Pages/ControllerPage.xaml index db14b2a3f..da73f42e4 100644 --- a/HandheldCompanion/Views/Pages/ControllerPage.xaml +++ b/HandheldCompanion/Views/Pages/ControllerPage.xaml @@ -1,728 +1,826 @@ -<Page - x:Class="HandheldCompanion.Views.Pages.ControllerPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:converters="clr-namespace:HandheldCompanion.Converters" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:hints="clr-namespace:HandheldCompanion.Controls.Hints" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Name="Controller" - Title="{x:Static resx:Resources.ControllerPage_Controller}" - d:Background="White" - d:DesignHeight="1800" - d:DesignWidth="1000" - Loaded="Page_Loaded" - mc:Ignorable="d"> - - <Page.Resources> - <converters:InverseAppThemeConverter x:Key="InverseAppThemeConverter" /> - </Page.Resources> - - <Grid Name="MainGrid" Margin="20"> - <Border> - <ui:SimpleStackPanel Spacing="24"> - - <!-- Controller --> - <Border - Height="300" - Padding="15,12,12,12" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid Name="ControllerGrid"> - <Grid.Background> - <ImageBrush ImageSource="/Resources/controller_2_0.png" Stretch="Uniform" /> - </Grid.Background> - - <ui:SimpleStackPanel - HorizontalAlignment="Center" - VerticalAlignment="Center" - Spacing="12"> - - <ui:ProgressRing - Name="ControllerLoading" - Width="125" - Height="125" - Visibility="Hidden" /> - - <ui:SimpleStackPanel - Name="UserIndexPanel" - HorizontalAlignment="Left" - Orientation="Horizontal" - Spacing="2"> - <Border - Width="12" - Height="12" - Background="{DynamicResource SystemControlForegroundBaseLowBrush}" - CornerRadius="2" /> - <Border - Width="12" - Height="12" - Background="{DynamicResource SystemControlForegroundBaseLowBrush}" - CornerRadius="2" /> - <Border - Width="12" - Height="12" - Background="{DynamicResource SystemControlForegroundBaseLowBrush}" - CornerRadius="2" /> - <Border - Width="12" - Height="12" - Background="{DynamicResource SystemControlForegroundBaseLowBrush}" - CornerRadius="2" /> - <Border - Width="12" - Height="12" - Background="{DynamicResource SystemControlForegroundBaseLowBrush}" - CornerRadius="2" /> - <Border - Width="12" - Height="12" - Background="{DynamicResource SystemControlForegroundBaseLowBrush}" - CornerRadius="2" /> - <Border - Width="12" - Height="12" - Background="{DynamicResource SystemControlForegroundBaseLowBrush}" - CornerRadius="2" /> - <Border - Width="12" - Height="12" - Background="{DynamicResource SystemControlForegroundBaseLowBrush}" - CornerRadius="2" /> - <Border - Width="12" - Height="12" - Background="{DynamicResource SystemControlForegroundBaseLowBrush}" - CornerRadius="2" /> - <Border - Width="12" - Height="12" - Background="{DynamicResource SystemControlForegroundBaseLowBrush}" - CornerRadius="2" /> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - - <ui:SimpleStackPanel - HorizontalAlignment="Right" - VerticalAlignment="Bottom" - Orientation="Horizontal" - Spacing="6"> - <ComboBox - Name="cB_ServiceSwitch" - Width="Auto" - SelectionChanged="cB_ServiceSwitch_SelectionChanged" /> - <ComboBox - Name="cB_HidMode" - Width="Auto" - SelectionChanged="cB_HidMode_SelectionChanged" /> - </ui:SimpleStackPanel> - </Grid> - </Border> - - <!-- Hints --> - <ui:SimpleStackPanel Name="Hints" Spacing="6"> - - <!-- Has physical controller but none are connected --> - <Expander - Name="HintsNoPhysicalConnected" - Padding="15,12,12,12" - HorizontalAlignment="Stretch" - d:Visibility="Visible" - Visibility="Collapsed"> - - <Expander.Header> - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="Segoe Fluent Icons" - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerWarning}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - </Expander.Header> - <TextBlock Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerAction}" TextWrapping="Wrap" /> - </Expander> - - <!-- Emulated controller managed by profile --> - <Expander - Name="HintsHIDManagedByProfile" - Padding="15,12,12,12" - HorizontalAlignment="Stretch" - d:Visibility="Visible" - Visibility="Collapsed"> - - <Expander.Header> - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="Segoe Fluent Icons" - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_HIDManagedByProfileWarning}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_HIDManagedByProfileDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - </Expander.Header> - <TextBlock Text="{x:Static resx:Resources.ControllerPage_HIDManagedByProfileAction}" TextWrapping="Wrap" /> - </Expander> - - - <!-- Has hidden physical controller but no virtual controller --> - <Expander - Name="HintsNoVirtual" - Padding="15,12,12,12" - HorizontalAlignment="Stretch" - d:Visibility="Visible" - Visibility="Collapsed"> - - <Expander.Header> - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="Segoe Fluent Icons" - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_NoVirtualControllerWarning}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_NoVirtualControllerDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - </Expander.Header> - <TextBlock Text="{x:Static resx:Resources.ControllerPage_NoVirtualControllerAction}" TextWrapping="Wrap" /> - </Expander> - - <!-- Has hidden Neptune controller but virtual controller is muted --> - <Expander - Name="HintsNeptuneHidden" - Padding="15,12,12,12" - HorizontalAlignment="Stretch" - d:Visibility="Visible" - Visibility="Collapsed"> - - <Expander.Header> - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="Segoe Fluent Icons" - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerHiddenWarning}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerHiddenDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - </Expander.Header> - <TextBlock Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerHiddenAction}" TextWrapping="Wrap" /> - </Expander> - - <!-- Physical controller is not hidden and virtual controller detected --> - <Expander - Name="HintsNotMuted" - Padding="15,12,12,12" - HorizontalAlignment="Stretch" - d:Visibility="Visible" - Visibility="Collapsed"> - - <Expander.Header> - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="Segoe Fluent Icons" - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerNotHiddenWarning}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerNotHiddenDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - </Expander.Header> - <TextBlock Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerNotHiddenAction}" TextWrapping="Wrap" /> - </Expander> - </ui:SimpleStackPanel> - - <!-- Input devices --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_InputDevices}" /> - - <ui:SimpleStackPanel - Name="InputDevices" - Spacing="6" - Visibility="Collapsed" /> - - <!-- WARNING: No physical controller detected --> - <Border - Name="WarningNoPhysical" - Padding="15,12,12,12" - d:Visibility="Visible" - ui:ThemeManager.RequestedTheme="{Binding Source={x:Static ui:ThemeManager.Current}, Path=ActualApplicationTheme, Converter={StaticResource InverseAppThemeConverter}}" - Background="{DynamicResource SystemControlPageBackgroundAltHighBrush}" - CornerRadius="{DynamicResource ControlCornerRadius}" - Visibility="Visible"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerDetectedWarning}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerDetectedAction}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - </Grid> - </Border> - </ui:SimpleStackPanel> - - <!-- Virtual controller settings --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_DeviceSettings}" /> - - <!-- Vibration Strength --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_VibrationStrength}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_VibrationStrengthExpl}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <DockPanel - Grid.Column="1" - Margin="12,0,0,0" - ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N0, ElementName=SliderStrength, Mode=OneWay}" - TextAlignment="Center" /> - <TextBlock VerticalAlignment="Center" Text="%" /> - <Slider - x:Name="SliderStrength" - Margin="6,0,0,0" - VerticalAlignment="Center" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="10" - Maximum="100" - Minimum="0" - SmallChange="5" - Style="{DynamicResource SliderStyle1}" - TickFrequency="5" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="SliderStrength_ValueChanged" - Value="100" /> - </DockPanel> - </Grid> - </Border> - - <!-- Vibrate on connect --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_VibrateDevice}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_VibrateDeviceDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_Vibrate" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_Vibrate_Toggled" /> - </Grid> - </Border> - - <!-- Controller management --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_ControllerManagement}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_ControllerManagementDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_ControllerManagement" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_ControllerManagement_Toggled" /> - </Grid> - </Border> - </ui:SimpleStackPanel> - - <!-- Non-game controller layouts --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_NonGameControllerLayouts}" /> - - <Expander - Name="GlobalSettings" - HorizontalAlignment="Stretch" - Expanded="Expander_Expanded" - IsExpanded="{Binding ElementName=Toggle_DesktopLayout, Path=IsOn, UpdateSourceTrigger=Explicit}"> - <Expander.Header> - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_DesktopLayout}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_DesktopLayoutDefineController}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - </Expander.Header> - - <Expander.Content> - <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_DesktopLayoutEnable}" /> - - <ui:ToggleSwitch - Name="Toggle_DesktopLayout" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_DesktopLayout_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - Margin="-46,0,-16,0" - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" /> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_DesktopLayoutDefine}" /> - - <Button - Name="Button_Layout" - Grid.Column="1" - Width="120" - Height="40" - HorizontalAlignment="Right" - Click="Button_Layout_Click" - Content="{x:Static resx:Resources.ControllerPage_DesktopLayoutEdit}" /> - </Grid> - </ui:SimpleStackPanel> - </Expander.Content> - </Expander> - </ui:SimpleStackPanel> - - <!-- Controller cloaking --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_DeviceCloaking}" /> - - <!-- Clock on connect --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_CloakDevice}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_CloakDeviceDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_Cloaked" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_Cloaked_Toggled" /> - </Grid> - </Border> - - <!-- Uncloak on close --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_UncloakOnClose}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_UncloakOnCloseDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_Uncloak" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_Uncloak_Toggled" /> - </Grid> - </Border> - </ui:SimpleStackPanel> - - <!-- Steam Deck settings --> - <ui:SimpleStackPanel - Name="DeviceSpecificPanel" - d:Visibility="Visible" - Spacing="6" - Visibility="Collapsed"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_DeviceSpecificSettings}" /> - - <!-- Mute virtual controller (Steam Deck) --> - <Border - Name="MuteVirtualController" - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_SteamControllerMute}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_SteamControllerMuteDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_SCMuteController" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_SCMuteController_Toggled" /> - </Grid> - </Border> - - <!-- Touchpad passthrough (Legion Go) --> - <Border - Name="TouchpadPassthrough" - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_LegionGoPassthrough}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ControllerPage_LegionGoPassthroughDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_TouchpadPassthrough" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_TouchpadPassthrough_Toggled" /> - </Grid> - </Border> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Border> - </Grid> +<Page + x:Class="HandheldCompanion.Views.Pages.ControllerPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:converters="clr-namespace:HandheldCompanion.Converters" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:hints="clr-namespace:HandheldCompanion.Controls.Hints" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" +<<<<<<< HEAD + Name="Controller" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Title="{x:Static resx:Resources.ControllerPage_Controller}" + d:Background="White" + d:DesignHeight="1800" + d:DesignWidth="1000" + Loaded="Page_Loaded" + mc:Ignorable="d"> + + <Page.Resources> + <converters:InverseAppThemeConverter x:Key="InverseAppThemeConverter" /> + </Page.Resources> + + <Grid Name="MainGrid" Margin="20"> + <Border> + <ui:SimpleStackPanel Spacing="24"> + + <!-- Controller --> + <Border + Height="300" + Padding="15,12,12,12" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid Name="ControllerGrid"> + <Grid.Background> + <ImageBrush ImageSource="/Resources/controller_2_0.png" Stretch="Uniform" /> + </Grid.Background> + +<<<<<<< HEAD + <ui:SimpleStackPanel + HorizontalAlignment="Center" + VerticalAlignment="Center" + Spacing="12"> + + <ui:ProgressRing + Name="ControllerLoading" + Width="125" + Height="125" + Visibility="Hidden" /> + + <ui:SimpleStackPanel + Name="UserIndexPanel" + HorizontalAlignment="Left" + Orientation="Horizontal" + Spacing="2"> + <Border + Width="12" + Height="12" + Background="{DynamicResource SystemControlForegroundBaseLowBrush}" + CornerRadius="2" /> + <Border + Width="12" + Height="12" + Background="{DynamicResource SystemControlForegroundBaseLowBrush}" + CornerRadius="2" /> + <Border + Width="12" + Height="12" + Background="{DynamicResource SystemControlForegroundBaseLowBrush}" + CornerRadius="2" /> + <Border + Width="12" + Height="12" + Background="{DynamicResource SystemControlForegroundBaseLowBrush}" + CornerRadius="2" /> + <Border + Width="12" + Height="12" + Background="{DynamicResource SystemControlForegroundBaseLowBrush}" + CornerRadius="2" /> + <Border + Width="12" + Height="12" + Background="{DynamicResource SystemControlForegroundBaseLowBrush}" + CornerRadius="2" /> + <Border + Width="12" + Height="12" + Background="{DynamicResource SystemControlForegroundBaseLowBrush}" + CornerRadius="2" /> + <Border + Width="12" + Height="12" + Background="{DynamicResource SystemControlForegroundBaseLowBrush}" + CornerRadius="2" /> + <Border + Width="12" + Height="12" + Background="{DynamicResource SystemControlForegroundBaseLowBrush}" + CornerRadius="2" /> + <Border + Width="12" + Height="12" + Background="{DynamicResource SystemControlForegroundBaseLowBrush}" + CornerRadius="2" /> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + <ui:SimpleStackPanel + HorizontalAlignment="Right" + VerticalAlignment="Bottom" + Orientation="Horizontal" + Spacing="6"> + <ComboBox + Name="cB_ServiceSwitch" + Width="Auto" + SelectionChanged="cB_ServiceSwitch_SelectionChanged" /> + <ComboBox + Name="cB_HidMode" + Width="Auto" + SelectionChanged="cB_HidMode_SelectionChanged" /> + </ui:SimpleStackPanel> + </Grid> + </Border> + + <!-- Hints --> + <ui:SimpleStackPanel Name="Hints" Spacing="6"> + + <!-- Has physical controller but none are connected --> + <Expander + Name="HintsNoPhysicalConnected" + Padding="15,12,12,12" + HorizontalAlignment="Stretch" + d:Visibility="Visible" + Visibility="Collapsed"> + + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="Segoe Fluent Icons" + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerWarning}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerDesc}" + TextWrapping="Wrap" /> +<<<<<<< HEAD + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + <TextBlock Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerAction}" TextWrapping="Wrap" /> + </Expander> + + <!-- Emulated controller managed by profile --> + <Expander + Name="HintsHIDManagedByProfile" + Padding="15,12,12,12" + HorizontalAlignment="Stretch" + d:Visibility="Visible" + Visibility="Collapsed"> + + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="Segoe Fluent Icons" + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_HIDManagedByProfileWarning}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_HIDManagedByProfileDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + <TextBlock Text="{x:Static resx:Resources.ControllerPage_HIDManagedByProfileAction}" TextWrapping="Wrap" /> + </Expander> + +======= + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + <TextBlock Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerAction}" TextWrapping="Wrap" /> + </Expander> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + <!-- Has hidden physical controller but no virtual controller --> + <Expander + Name="HintsNoVirtual" + Padding="15,12,12,12" + HorizontalAlignment="Stretch" + d:Visibility="Visible" + Visibility="Collapsed"> + + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="Segoe Fluent Icons" + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_NoVirtualControllerWarning}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_NoVirtualControllerDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + <TextBlock Text="{x:Static resx:Resources.ControllerPage_NoVirtualControllerAction}" TextWrapping="Wrap" /> + </Expander> + + <!-- Has hidden Neptune controller but virtual controller is muted --> + <Expander + Name="HintsNeptuneHidden" + Padding="15,12,12,12" + HorizontalAlignment="Stretch" + d:Visibility="Visible" + Visibility="Collapsed"> + + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="Segoe Fluent Icons" + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerHiddenWarning}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerHiddenDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + <TextBlock Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerHiddenAction}" TextWrapping="Wrap" /> + </Expander> + + <!-- Physical controller is not hidden and virtual controller detected --> + <Expander + Name="HintsNotMuted" + Padding="15,12,12,12" + HorizontalAlignment="Stretch" + d:Visibility="Visible" + Visibility="Collapsed"> + + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="Segoe Fluent Icons" + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerNotHiddenWarning}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerNotHiddenDesc}" + TextWrapping="Wrap" /> +<<<<<<< HEAD + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + <TextBlock Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerNotHiddenAction}" TextWrapping="Wrap" /> +======= + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + <TextBlock Text="{x:Static resx:Resources.ControllerPage_PhysicalControllerNotHiddenAction}" TextWrapping="Wrap" /> + </Expander> + + <!-- Steam Xbox Controller Enhanced Features Driver --> + <Expander + Name="HintsSteamXboxDrivers" + Padding="15,12,12,12" + HorizontalAlignment="Stretch" + d:Visibility="Visible" + Visibility="Collapsed"> + + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_SteamXboxDriversWarning}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_SteamXboxDriversDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + <TextBlock Text="{x:Static resx:Resources.ControllerPage_SteamXboxDriversAction}" TextWrapping="Wrap" /> + </Expander> + + <!-- Steam Desktop Layout --> + <Expander + Name="HintsSteamNeptuneDeskop" + Padding="15,12,12,12" + HorizontalAlignment="Stretch" + d:Visibility="Visible" + Visibility="Collapsed"> + + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_SteamNeptuneDesktopWarning}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_SteamNeptuneDesktopDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + <TextBlock Text="{x:Static resx:Resources.ControllerPage_SteamNeptuneDesktopAction}" TextWrapping="Wrap" /> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + </Expander> + </ui:SimpleStackPanel> + + <!-- Input devices --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_InputDevices}" /> + + <ui:SimpleStackPanel + Name="InputDevices" + Spacing="6" + Visibility="Collapsed" /> + + <!-- WARNING: No physical controller detected --> + <Border + Name="WarningNoPhysical" + Padding="15,12,12,12" + d:Visibility="Visible" + ui:ThemeManager.RequestedTheme="{Binding Source={x:Static ui:ThemeManager.Current}, Path=ActualApplicationTheme, Converter={StaticResource InverseAppThemeConverter}}" + Background="{DynamicResource SystemControlPageBackgroundAltHighBrush}" + CornerRadius="{DynamicResource ControlCornerRadius}" + Visibility="Visible"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerDetectedWarning}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_NoPhysicalControllerDetectedAction}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Grid> + </Border> + </ui:SimpleStackPanel> + + <!-- Virtual controller settings --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_DeviceSettings}" /> + + <!-- Vibration Strength --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_VibrationStrength}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_VibrationStrengthExpl}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <DockPanel + Grid.Column="1" + Margin="12,0,0,0" + ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=SliderStrength, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock VerticalAlignment="Center" Text="%" /> + <Slider + x:Name="SliderStrength" + Margin="6,0,0,0" + VerticalAlignment="Center" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="10" + Maximum="100" + Minimum="0" + SmallChange="5" + Style="{DynamicResource SliderStyle1}" + TickFrequency="5" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="SliderStrength_ValueChanged" + Value="100" /> + </DockPanel> + </Grid> + </Border> + + <!-- Vibrate on connect --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_VibrateDevice}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_VibrateDeviceDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_Vibrate" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_Vibrate_Toggled" /> + </Grid> + </Border> + + <!-- Controller management --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_ControllerManagement}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_ControllerManagementDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_ControllerManagement" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_ControllerManagement_Toggled" /> + </Grid> + </Border> + </ui:SimpleStackPanel> + + <!-- Non-game controller layouts --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_NonGameControllerLayouts}" /> + + <Expander + Name="GlobalSettings" + HorizontalAlignment="Stretch" + Expanded="Expander_Expanded" + IsExpanded="{Binding ElementName=Toggle_DesktopLayout, Path=IsOn, UpdateSourceTrigger=Explicit}"> + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_DesktopLayout}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_DesktopLayoutDefineController}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + + <Expander.Content> + <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_DesktopLayoutEnable}" /> + + <ui:ToggleSwitch + Name="Toggle_DesktopLayout" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_DesktopLayout_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_DesktopLayoutDefine}" /> + + <Button + Name="Button_Layout" + Grid.Column="1" + Width="120" + Height="40" + HorizontalAlignment="Right" + Click="Button_Layout_Click" + Content="{x:Static resx:Resources.ControllerPage_DesktopLayoutEdit}" /> + </Grid> + </ui:SimpleStackPanel> + </Expander.Content> + </Expander> + </ui:SimpleStackPanel> + + <!-- Controller cloaking --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_DeviceCloaking}" /> + + <!-- Clock on connect --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_CloakDevice}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_CloakDeviceDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_Cloaked" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_Cloaked_Toggled" /> + </Grid> + </Border> + + <!-- Uncloak on close --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_UncloakOnClose}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_UncloakOnCloseDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_Uncloak" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_Uncloak_Toggled" /> + </Grid> + </Border> + </ui:SimpleStackPanel> + + <!-- Steam Deck settings --> + <ui:SimpleStackPanel +<<<<<<< HEAD + Name="DeviceSpecificPanel" + d:Visibility="Visible" + Spacing="6" + Visibility="Collapsed"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_DeviceSpecificSettings}" /> + + <!-- Mute virtual controller (Steam Deck) --> + <Border + Name="MuteVirtualController" +======= + Name="SteamControllerPanel" + d:Visibility="Visible" + Spacing="6" + Visibility="Collapsed"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_SteamControllerSettings}" /> + + <!-- Mute virtual controller --> + <Border +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> +<<<<<<< HEAD + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> +======= + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_SteamControllerMute}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_SteamControllerMuteDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_SCMuteController" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_SCMuteController_Toggled" /> +<<<<<<< HEAD + </Grid> + </Border> + + <!-- Touchpad passthrough (Legion Go) --> + <Border + Name="TouchpadPassthrough" + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ControllerPage_LegionGoPassthrough}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ControllerPage_LegionGoPassthroughDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_TouchpadPassthrough" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_TouchpadPassthrough_Toggled" /> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + </Grid> + </Border> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Border> + </Grid> </Page> \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/ControllerPage.xaml.cs b/HandheldCompanion/Views/Pages/ControllerPage.xaml.cs index 317377b70..299ef2e12 100644 --- a/HandheldCompanion/Views/Pages/ControllerPage.xaml.cs +++ b/HandheldCompanion/Views/Pages/ControllerPage.xaml.cs @@ -1,522 +1,638 @@ -using HandheldCompanion.Controllers; -using HandheldCompanion.Controls; -using HandheldCompanion.Managers; -using HandheldCompanion.Misc; -using HandheldCompanion.Utils; -using Inkore.UI.WPF.Modern.Controls; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using Page = System.Windows.Controls.Page; - -namespace HandheldCompanion.Views.Pages; - -/// <summary> -/// Interaction logic for Devices.xaml -/// </summary> -public partial class ControllerPage : Page -{ - // controllers vars - private HIDmode controllerMode = HIDmode.NoController; - private HIDstatus controllerStatus = HIDstatus.Disconnected; - - public ControllerPage() - { - InitializeComponent(); - - // initialize components - foreach (var mode in ((HIDmode[])Enum.GetValues(typeof(HIDmode))).Where(a => a != HIDmode.NoController && a != HIDmode.NotSelected)) - cB_HidMode.Items.Add(EnumUtils.GetDescriptionFromEnumValue(mode)); - - foreach (var status in (HIDstatus[])Enum.GetValues(typeof(HIDstatus))) - cB_ServiceSwitch.Items.Add(EnumUtils.GetDescriptionFromEnumValue(status)); - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - - ControllerManager.ControllerPlugged += ControllerPlugged; - ControllerManager.ControllerUnplugged += ControllerUnplugged; - ControllerManager.ControllerSelected += ControllerManager_ControllerSelected; - ControllerManager.Working += ControllerManager_Working; - ProfileManager.Applied += ProfileManager_Applied; - - VirtualManager.ControllerSelected += VirtualManager_ControllerSelected; - } - - public ControllerPage(string Tag) : this() - { - this.Tag = Tag; - } - - private void ProfileManager_Applied(Profile profile, UpdateSource source) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // disable emulated controller combobox if profile is not default or set to default controller - if (!profile.Default && (HIDmode)profile.HID != HIDmode.NotSelected) - { - cB_HidMode.IsEnabled = false; - HintsHIDManagedByProfile.Visibility = Visibility.Visible; - } - else - { - cB_HidMode.IsEnabled = true; - HintsHIDManagedByProfile.Visibility = Visibility.Collapsed; - } - }); - } - private void SettingsManager_SettingValueChanged(string name, object value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (name) - { - case "HIDcloakonconnect": - Toggle_Cloaked.IsOn = Convert.ToBoolean(value); - break; - case "HIDuncloakonclose": - Toggle_Uncloak.IsOn = Convert.ToBoolean(value); - break; - case "HIDvibrateonconnect": - Toggle_Vibrate.IsOn = Convert.ToBoolean(value); - break; - case "ControllerManagement": - Toggle_ControllerManagement.IsOn = Convert.ToBoolean(value); - break; - case "VibrationStrength": - SliderStrength.Value = Convert.ToDouble(value); - break; - case "DesktopLayoutEnabled": - Toggle_DesktopLayout.IsOn = Convert.ToBoolean(value); - break; - case "SteamControllerMute": - Toggle_SCMuteController.IsOn = Convert.ToBoolean(value); - ControllerRefresh(); - break; - case "LegionControllerPassthrough": - Toggle_TouchpadPassthrough.IsOn = Convert.ToBoolean(value); - break; - case "HIDmode": - cB_HidMode.SelectedIndex = Convert.ToInt32(value); - break; - case "HIDstatus": - cB_ServiceSwitch.SelectedIndex = Convert.ToInt32(value); - UpdateControllerImage(); - break; - } - }); - } - - private void Page_Loaded(object sender, RoutedEventArgs e) - { - } - - public void Page_Closed() - { - } - - private void ControllerUnplugged(IController Controller, bool IsPowerCycling) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // Search for an existing controller, remove it - foreach (IController ctrl in InputDevices.Children) - { - if (ctrl.GetContainerInstancePath() == Controller.GetContainerInstancePath()) - { - if (!IsPowerCycling) - { - InputDevices.Children.Remove(ctrl); - ControllerRefresh(); - break; - } - } - } - }); - - if (Controller.IsVirtual()) - Controller.UserIndexChanged -= Controller_UserIndexChanged; - } - - private void ControllerPlugged(IController Controller, bool IsPowerCycling) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // Search for an existing controller, remove it - foreach (IController ctrl in InputDevices.Children) - if (ctrl.GetContainerInstancePath() == Controller.GetContainerInstancePath()) - return; - - // Add new controller to list if no existing controller was found - InputDevices.Children.Add(Controller); - - // todo: move me - var ui_button_hook = Controller.GetButtonHook(); - ui_button_hook.Click += (sender, e) => ControllerHookClicked(Controller); - - var ui_button_hide = Controller.GetButtonHide(); - ui_button_hide.Click += (sender, e) => ControllerHideClicked(Controller); - - ControllerRefresh(); - }); - - if (Controller.IsVirtual()) - Controller.UserIndexChanged += Controller_UserIndexChanged; - } - - private void ControllerManager_ControllerSelected(IController Controller) - { - // UI thread (async) - ControllerRefresh(); - } - - private void SetVirtualControllerVisualIndex(int value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - foreach (FrameworkElement frameworkElement in UserIndexPanel.Children) - { - if (frameworkElement is not Border) - continue; - - Border border = (Border)frameworkElement; - int idx = UserIndexPanel.Children.IndexOf(border); - - if (idx == value) - border.SetResourceReference(BackgroundProperty, "AccentAAFillColorDefaultBrush"); - else - border.SetResourceReference(BackgroundProperty, "SystemControlForegroundBaseLowBrush"); - } - }); - } - - private int workingIdx = 0; - private Thread workingThread; - private bool workingThreadRunning; - - private void workingThreadLoop(object? obj) - { - int maxIdx = 8; - int direction = 1; // 1 for increasing, -1 for decreasing - - // UI thread (async) - Application.Current.Dispatcher.Invoke(() => - { - maxIdx = UserIndexPanel.Children.Count; - }); - - while (workingThreadRunning) - { - workingIdx += direction; // increment or decrement the index - if (workingIdx == maxIdx - 1 || workingIdx == 0) // if the index reaches the limit or zero - { - direction = -direction; // reverse the direction - } - - SetVirtualControllerVisualIndex(workingIdx); - - Thread.Sleep(100); - } - } - - private void ControllerManager_Working(int status) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(async () => - { - // status: 0:wip, 1:sucess, 2:failed - switch (status) - { - case 0: - ControllerLoading.Visibility = Visibility.Visible; - InputDevices.IsEnabled = false; - ControllerGrid.IsEnabled = false; - - if (!workingThreadRunning) - { - workingThreadRunning = true; - - workingThread = new Thread(workingThreadLoop); - workingThread.IsBackground = true; - workingThread.Start(); - } - break; - case 1: - case 2: - ControllerLoading.Visibility = Visibility.Hidden; - InputDevices.IsEnabled = true; - ControllerGrid.IsEnabled = true; - - if (workingThreadRunning) - { - workingThreadRunning = false; - workingThread.Join(); - } - break; - } - - while (workingThread is not null && workingThread.IsAlive) - await Task.Delay(1000); - - ControllerRefresh(); - - // failed - if (status == 2) - { - // todo: translate me - var result = Dialog.ShowAsync( - Properties.Resources.SettingsPage_UpdateWarning, - $"We've failed to reorder your controllers. For maximum compatibility, we encourage you to restart HandheldCompanion", - ContentDialogButton.Close, - Properties.Resources.ControllerPage_TryAgain, - Properties.Resources.ControllerPage_Close); - - await result; // sync call - - switch (result.Result) - { - default: - case ContentDialogResult.Primary: - Toggle_ControllerManagement.IsOn = false; - break; - case ContentDialogResult.None: - Toggle_ControllerManagement.IsOn = true; - break; - } - } - }); - } - - private void Controller_UserIndexChanged(byte UserIndex) - { - SetVirtualControllerVisualIndex(UserIndex); - } - - private void VirtualManager_ControllerSelected(HIDmode mode) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - cB_HidMode.SelectedIndex = (int)mode; - }); - } - - private void ControllerHookClicked(IController Controller) - { - // todo: move me - var path = Controller.GetContainerInstancePath(); - ControllerManager.SetTargetController(path, false); - - ControllerRefresh(); - } - - private void ControllerHideClicked(IController Controller) - { - // todo: move me - if (Controller.IsHidden()) - Controller.Unhide(); - else - Controller.Hide(); - - ControllerRefresh(); - } - - private void ControllerRefresh() - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // we're busy - if (ControllerLoading.Visibility is Visibility.Visible) - return; - - bool hasPhysical = ControllerManager.HasPhysicalController(); - bool hasVirtual = ControllerManager.HasVirtualController(); - bool hasTarget = ControllerManager.GetTargetController() != null; - - // check: do we have any plugged physical controller - InputDevices.Visibility = hasPhysical ? Visibility.Visible : Visibility.Collapsed; - WarningNoPhysical.Visibility = !hasPhysical ? Visibility.Visible : Visibility.Collapsed; - - IController targetController = ControllerManager.GetTargetController(); - IController virtualController = ControllerManager.GetVirtualControllers().LastOrDefault(); - int idx = virtualController is null ? 255 : virtualController.GetUserIndex(); - SetVirtualControllerVisualIndex(idx); - - bool isPlugged = hasTarget; - bool isHidden = hasTarget && targetController.IsHidden(); - bool isSteam = hasTarget && (targetController is NeptuneController || targetController is GordonController); - bool isMuted = SettingsManager.GetBoolean("SteamControllerMute"); - - DeviceSpecificPanel.Visibility = targetController is SteamController || targetController is LegionController ? Visibility.Visible : Visibility.Collapsed; - MuteVirtualController.Visibility = targetController is SteamController ? Visibility.Visible : Visibility.Collapsed; - TouchpadPassthrough.Visibility = targetController is LegionController ? Visibility.Visible : Visibility.Collapsed; - - // hint: Has physical controller, but is not connected - HintsNoPhysicalConnected.Visibility = - hasPhysical && !isPlugged ? Visibility.Visible : Visibility.Collapsed; - - // hint: Has physical controller (not Neptune) hidden, but no virtual controller - bool hiddenbutnovirtual = isHidden && !hasVirtual; - HintsNoVirtual.Visibility = hiddenbutnovirtual ? Visibility.Visible : Visibility.Collapsed; - - // hint: Has physical controller (Neptune) hidden, but virtual controller is muted - bool neptunehidden = isHidden && isSteam && isMuted; - HintsNeptuneHidden.Visibility = neptunehidden ? Visibility.Visible : Visibility.Collapsed; - - // hint: Has physical controller not hidden, and virtual controller - bool notmuted = !isHidden && hasVirtual && (!isSteam || (isSteam && !isMuted)); - HintsNotMuted.Visibility = notmuted ? Visibility.Visible : Visibility.Collapsed; - }); - } - - private void UpdateControllerImage() - { - BitmapImage controllerImage; - if (controllerMode == HIDmode.NoController || controllerStatus == HIDstatus.Disconnected) - controllerImage = new BitmapImage(new Uri($"pack://application:,,,/Resources/controller_2_0.png")); - else - controllerImage = new BitmapImage(new Uri($"pack://application:,,,/Resources/controller_{Convert.ToInt32(controllerMode)}_{Convert.ToInt32(controllerStatus)}.png")); - - // update UI icon to match HIDmode - ImageBrush uniformToFillBrush = new ImageBrush() - { - Stretch = Stretch.Uniform, - ImageSource = controllerImage, - }; - uniformToFillBrush.Freeze(); - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - ControllerGrid.Background = uniformToFillBrush; - }); - } - - private async void cB_HidMode_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (cB_HidMode.SelectedIndex == -1) - return; - - controllerMode = (HIDmode)cB_HidMode.SelectedIndex; - UpdateControllerImage(); - - // only change HIDmode setting if current profile is default or set to default controller - var currentProfile = ProfileManager.GetCurrent(); - if (currentProfile.Default || (HIDmode)currentProfile.HID == HIDmode.NotSelected) - { - SettingsManager.SetProperty("HIDmode", cB_HidMode.SelectedIndex); - } - } - - private void cB_ServiceSwitch_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (cB_HidMode.SelectedIndex == -1) - return; - - controllerStatus = (HIDstatus)cB_ServiceSwitch.SelectedIndex; - UpdateControllerImage(); - - SettingsManager.SetProperty("HIDstatus", cB_ServiceSwitch.SelectedIndex); - } - - private void Toggle_Cloaked_Toggled(object sender, RoutedEventArgs e) - { - if (!IsLoaded) - return; - - SettingsManager.SetProperty("HIDcloakonconnect", Toggle_Cloaked.IsOn); - } - - private void Toggle_Uncloak_Toggled(object sender, RoutedEventArgs e) - { - if (!IsLoaded) - return; - - SettingsManager.SetProperty("HIDuncloakonclose", Toggle_Uncloak.IsOn); - } - - private void SliderStrength_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - var value = SliderStrength.Value; - if (double.IsNaN(value)) - return; - - SliderStrength.Value = value; - - if (!IsLoaded) - return; - - SettingsManager.SetProperty("VibrationStrength", value); - } - - private void Toggle_SCMuteController_Toggled(object sender, RoutedEventArgs e) - { - if (!IsLoaded) - return; - - SettingsManager.SetProperty("SteamControllerMute", Toggle_SCMuteController.IsOn); - } - - private void Toggle_Vibrate_Toggled(object sender, RoutedEventArgs e) - { - if (!IsLoaded) - return; - - SettingsManager.SetProperty("HIDvibrateonconnect", Toggle_Vibrate.IsOn); - } - - private void Toggle_ControllerManagement_Toggled(object sender, RoutedEventArgs e) - { - if (!IsLoaded) - return; - - SettingsManager.SetProperty("ControllerManagement", Toggle_ControllerManagement.IsOn); - } - - private void Button_Layout_Click(object sender, RoutedEventArgs e) - { - // prepare layout editor, desktopLayout gets saved automatically - LayoutTemplate desktopTemplate = new(LayoutManager.GetDesktop()) - { - Name = LayoutTemplate.DesktopLayout.Name, - Description = LayoutTemplate.DesktopLayout.Description, - Author = Environment.UserName, - Executable = string.Empty, - Product = string.Empty // UI might've set something here, nullify - }; - MainWindow.layoutPage.UpdateLayoutTemplate(desktopTemplate); - MainWindow.NavView_Navigate(MainWindow.layoutPage); - } - - private void Toggle_DesktopLayout_Toggled(object sender, RoutedEventArgs e) - { - if (!IsLoaded) - return; - - // temporary settings - SettingsManager.SetProperty("DesktopLayoutEnabled", Toggle_DesktopLayout.IsOn, false, true); - } - - private void Expander_Expanded(object sender, RoutedEventArgs e) - { - ((Expander)sender).BringIntoView(); - } - - private void Toggle_TouchpadPassthrough_Toggled(object sender, RoutedEventArgs e) - { - if (!IsLoaded) - return; - - SettingsManager.SetProperty("LegionControllerPassthrough", Toggle_TouchpadPassthrough.IsOn); - } +using HandheldCompanion.Controllers; +using HandheldCompanion.Controls; +using HandheldCompanion.Managers; +<<<<<<< HEAD +using HandheldCompanion.Misc; +using HandheldCompanion.Utils; +using Inkore.UI.WPF.Modern.Controls; +======= +using HandheldCompanion.Platforms; +using HandheldCompanion.Utils; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using System; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using Page = System.Windows.Controls.Page; + +namespace HandheldCompanion.Views.Pages; + +/// <summary> +/// Interaction logic for Devices.xaml +/// </summary> +public partial class ControllerPage : Page +{ +<<<<<<< HEAD +======= + public delegate void HIDchangedEventHandler(HIDmode HID); + public event HIDchangedEventHandler HIDchanged; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + // controllers vars + private HIDmode controllerMode = HIDmode.NoController; + private HIDstatus controllerStatus = HIDstatus.Disconnected; + + public ControllerPage() + { + InitializeComponent(); + + // initialize components + foreach (var mode in ((HIDmode[])Enum.GetValues(typeof(HIDmode))).Where(a => a != HIDmode.NoController && a != HIDmode.NotSelected)) + cB_HidMode.Items.Add(EnumUtils.GetDescriptionFromEnumValue(mode)); + + foreach (var status in (HIDstatus[])Enum.GetValues(typeof(HIDstatus))) + cB_ServiceSwitch.Items.Add(EnumUtils.GetDescriptionFromEnumValue(status)); + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + + ControllerManager.ControllerPlugged += ControllerPlugged; + ControllerManager.ControllerUnplugged += ControllerUnplugged; + ControllerManager.ControllerSelected += ControllerManager_ControllerSelected; +<<<<<<< HEAD + ControllerManager.Working += ControllerManager_Working; + ProfileManager.Applied += ProfileManager_Applied; + + VirtualManager.ControllerSelected += VirtualManager_ControllerSelected; +======= + + PlatformManager.Initialized += PlatformManager_Initialized; + PlatformManager.Steam.Updated += Steam_Updated; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + public ControllerPage(string Tag) : this() + { + this.Tag = Tag; + } + +<<<<<<< HEAD + private void ProfileManager_Applied(Profile profile, UpdateSource source) +======= + private void PlatformManager_Initialized() + { + HintsSteamXboxDrivers.Visibility = PlatformManager.Steam.HasXboxDriversInstalled() ? Visibility.Visible : Visibility.Collapsed; + Steam_Updated(PlatformManager.Steam.IsRunning ? PlatformStatus.Started : PlatformStatus.Stopped); + } + + private void Steam_Updated(PlatformStatus status) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { +<<<<<<< HEAD + // disable emulated controller combobox if profile is not default or set to default controller + if (!profile.Default && (HIDmode)profile.HID != HIDmode.NotSelected) + { + cB_HidMode.IsEnabled = false; + HintsHIDManagedByProfile.Visibility = Visibility.Visible; + } + else + { + cB_HidMode.IsEnabled = true; + HintsHIDManagedByProfile.Visibility = Visibility.Collapsed; + } + }); + } +======= + switch (status) + { + case PlatformStatus.Stopping: + case PlatformStatus.Stopped: + HintsSteamNeptuneDeskop.Visibility = Visibility.Collapsed; + break; + case PlatformStatus.Started: + HintsSteamNeptuneDeskop.Visibility = PlatformManager.Steam.HasDesktopProfileApplied() ? Visibility.Visible : Visibility.Collapsed; + break; + } + }); + } + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + private void SettingsManager_SettingValueChanged(string name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { + case "HIDcloakonconnect": + Toggle_Cloaked.IsOn = Convert.ToBoolean(value); + break; + case "HIDuncloakonclose": + Toggle_Uncloak.IsOn = Convert.ToBoolean(value); + break; + case "HIDvibrateonconnect": + Toggle_Vibrate.IsOn = Convert.ToBoolean(value); + break; +<<<<<<< HEAD + case "ControllerManagement": + Toggle_ControllerManagement.IsOn = Convert.ToBoolean(value); + break; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + case "VibrationStrength": + SliderStrength.Value = Convert.ToDouble(value); + break; + case "DesktopLayoutEnabled": + Toggle_DesktopLayout.IsOn = Convert.ToBoolean(value); + break; + case "SteamControllerMute": + Toggle_SCMuteController.IsOn = Convert.ToBoolean(value); + ControllerRefresh(); + break; +<<<<<<< HEAD + case "LegionControllerPassthrough": + Toggle_TouchpadPassthrough.IsOn = Convert.ToBoolean(value); + break; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + case "HIDmode": + cB_HidMode.SelectedIndex = Convert.ToInt32(value); + break; + case "HIDstatus": + cB_ServiceSwitch.SelectedIndex = Convert.ToInt32(value); + UpdateControllerImage(); + break; + } + }); + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + } + + public void Page_Closed() + { + } + + private void ControllerUnplugged(IController Controller, bool IsPowerCycling) + { +<<<<<<< HEAD +======= + LogManager.LogDebug("Controller unplugged: {0}", Controller.ToString()); + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // Search for an existing controller, remove it + foreach (IController ctrl in InputDevices.Children) + { + if (ctrl.GetContainerInstancePath() == Controller.GetContainerInstancePath()) + { + if (!IsPowerCycling) + { + InputDevices.Children.Remove(ctrl); + ControllerRefresh(); + break; + } + } + } + }); + + if (Controller.IsVirtual()) + Controller.UserIndexChanged -= Controller_UserIndexChanged; + } + +<<<<<<< HEAD + private void ControllerPlugged(IController Controller, bool IsPowerCycling) +======= + private void ControllerPlugged(IController Controller, bool isHCVirtualController, bool IsPowerCycling) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // Search for an existing controller, remove it + foreach (IController ctrl in InputDevices.Children) +<<<<<<< HEAD + if (ctrl.GetContainerInstancePath() == Controller.GetContainerInstancePath()) + return; +======= + { + if (ctrl.GetContainerInstancePath() == Controller.GetContainerInstancePath()) + { + InputDevices.Children.Remove(ctrl); + break; + } + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // Add new controller to list if no existing controller was found + InputDevices.Children.Add(Controller); + + // todo: move me + var ui_button_hook = Controller.GetButtonHook(); + ui_button_hook.Click += (sender, e) => ControllerHookClicked(Controller); + + var ui_button_hide = Controller.GetButtonHide(); + ui_button_hide.Click += (sender, e) => ControllerHideClicked(Controller); + + ControllerRefresh(); + }); + +<<<<<<< HEAD + if (Controller.IsVirtual()) + Controller.UserIndexChanged += Controller_UserIndexChanged; +======= + // we assume this is HC virtual controller + if (Controller.IsVirtual() && isHCVirtualController) + { + if (SettingsManager.GetBoolean("VirtualControllerForceOrder")) + { + // enable physical controller(s) after virtual controller to ensure first order + foreach (var physicalControllerInstanceId in SettingsManager.GetStringCollection("PhysicalControllerInstanceIds")) + { + PnPUtil.EnableDevice(physicalControllerInstanceId); + } + } + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void ControllerManager_ControllerSelected(IController Controller) + { + // UI thread (async) +<<<<<<< HEAD + ControllerRefresh(); +======= + Application.Current.Dispatcher.BeginInvoke(() => + { + SteamControllerPanel.Visibility = Controller is SteamController ? Visibility.Visible : Visibility.Collapsed; + }); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void SetVirtualControllerVisualIndex(int value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + foreach (FrameworkElement frameworkElement in UserIndexPanel.Children) + { + if (frameworkElement is not Border) + continue; + + Border border = (Border)frameworkElement; + int idx = UserIndexPanel.Children.IndexOf(border); + + if (idx == value) + border.SetResourceReference(BackgroundProperty, "AccentAAFillColorDefaultBrush"); + else + border.SetResourceReference(BackgroundProperty, "SystemControlForegroundBaseLowBrush"); + } + }); + } + + private int workingIdx = 0; + private Thread workingThread; + private bool workingThreadRunning; + + private void workingThreadLoop(object? obj) + { + int maxIdx = 8; + int direction = 1; // 1 for increasing, -1 for decreasing + + // UI thread (async) + Application.Current.Dispatcher.Invoke(() => + { + maxIdx = UserIndexPanel.Children.Count; + }); + + while (workingThreadRunning) + { + workingIdx += direction; // increment or decrement the index + if (workingIdx == maxIdx - 1 || workingIdx == 0) // if the index reaches the limit or zero + { + direction = -direction; // reverse the direction + } + + SetVirtualControllerVisualIndex(workingIdx); + + Thread.Sleep(100); + } + } + + private void ControllerManager_Working(int status) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(async () => + { + // status: 0:wip, 1:sucess, 2:failed + switch (status) + { + case 0: + ControllerLoading.Visibility = Visibility.Visible; + InputDevices.IsEnabled = false; + ControllerGrid.IsEnabled = false; + + if (!workingThreadRunning) + { + workingThreadRunning = true; + + workingThread = new Thread(workingThreadLoop); + workingThread.IsBackground = true; + workingThread.Start(); + } + break; + case 1: + case 2: + ControllerLoading.Visibility = Visibility.Hidden; + InputDevices.IsEnabled = true; + ControllerGrid.IsEnabled = true; + + if (workingThreadRunning) + { + workingThreadRunning = false; + workingThread.Join(); + } + break; + } + + while (workingThread is not null && workingThread.IsAlive) + await Task.Delay(1000); + + ControllerRefresh(); + + // failed + if (status == 2) + { + // todo: translate me + var result = Dialog.ShowAsync( + Properties.Resources.SettingsPage_UpdateWarning, + $"We've failed to reorder your controllers. For maximum compatibility, we encourage you to restart HandheldCompanion", + ContentDialogButton.Close, + Properties.Resources.ControllerPage_TryAgain, + Properties.Resources.ControllerPage_Close); + + await result; // sync call + + switch (result.Result) + { + default: + case ContentDialogResult.Primary: + Toggle_ControllerManagement.IsOn = false; + break; + case ContentDialogResult.None: + Toggle_ControllerManagement.IsOn = true; + break; + } + } + }); + } + + private void Controller_UserIndexChanged(byte UserIndex) + { + SetVirtualControllerVisualIndex(UserIndex); + } + + private void VirtualManager_ControllerSelected(HIDmode mode) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + cB_HidMode.SelectedIndex = (int)mode; + }); + } + + private void ControllerHookClicked(IController Controller) + { + // todo: move me + var path = Controller.GetContainerInstancePath(); + ControllerManager.SetTargetController(path, false); + + ControllerRefresh(); + } + + private void ControllerHideClicked(IController Controller) + { + // todo: move me + if (Controller.IsHidden()) + Controller.Unhide(); + else + Controller.Hide(); + + if (!ControllerManager.PowerCyclers.ContainsKey(Controller.GetContainerInstancePath())) + ControllerRefresh(); + } + + private void ControllerRefresh() + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { +<<<<<<< HEAD + // we're busy + if (ControllerLoading.Visibility is Visibility.Visible) + return; + + bool hasPhysical = ControllerManager.HasPhysicalController(); + bool hasVirtual = ControllerManager.HasVirtualController(); + bool hasTarget = ControllerManager.GetTargetController() != null; +======= + var hasPhysical = ControllerManager.HasPhysicalController(); + var hasVirtual = ControllerManager.HasVirtualController(); + var hasTarget = ControllerManager.GetTargetController() != null; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // check: do we have any plugged physical controller + InputDevices.Visibility = hasPhysical ? Visibility.Visible : Visibility.Collapsed; + WarningNoPhysical.Visibility = !hasPhysical ? Visibility.Visible : Visibility.Collapsed; + +<<<<<<< HEAD + IController targetController = ControllerManager.GetTargetController(); + IController virtualController = ControllerManager.GetVirtualControllers().LastOrDefault(); + int idx = virtualController is null ? 255 : virtualController.GetUserIndex(); + SetVirtualControllerVisualIndex(idx); + + bool isPlugged = hasTarget; + bool isHidden = hasTarget && targetController.IsHidden(); + bool isSteam = hasTarget && (targetController is NeptuneController || targetController is GordonController); + bool isMuted = SettingsManager.GetBoolean("SteamControllerMute"); + + DeviceSpecificPanel.Visibility = targetController is SteamController || targetController is LegionController ? Visibility.Visible : Visibility.Collapsed; + MuteVirtualController.Visibility = targetController is SteamController ? Visibility.Visible : Visibility.Collapsed; + TouchpadPassthrough.Visibility = targetController is LegionController ? Visibility.Visible : Visibility.Collapsed; +======= + var target = ControllerManager.GetTargetController(); + var isPlugged = hasTarget && target.IsPlugged(); + var isHidden = hasTarget && target.IsHidden(); + var isSteam = hasTarget && (target.GetType() == typeof(NeptuneController) || target.GetType() == typeof(GordonController)); + var isMuted = SettingsManager.GetBoolean("SteamControllerMute"); + var isForceOrder = SettingsManager.GetBoolean("VirtualControllerForceOrder"); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // hint: Has physical controller, but is not connected + HintsNoPhysicalConnected.Visibility = + hasPhysical && !isPlugged ? Visibility.Visible : Visibility.Collapsed; + + // hint: Has physical controller (not Neptune) hidden, but no virtual controller + bool hiddenbutnovirtual = isHidden && !hasVirtual; + HintsNoVirtual.Visibility = hiddenbutnovirtual ? Visibility.Visible : Visibility.Collapsed; + + // hint: Has physical controller (Neptune) hidden, but virtual controller is muted +<<<<<<< HEAD + bool neptunehidden = isHidden && isSteam && isMuted; +======= + var neptunehidden = isHidden && isSteam && isMuted; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + HintsNeptuneHidden.Visibility = neptunehidden ? Visibility.Visible : Visibility.Collapsed; + + // hint: Has physical controller not hidden, and virtual controller + bool notmuted = !isHidden && hasVirtual && (!isSteam || (isSteam && !isMuted)); + HintsNotMuted.Visibility = notmuted ? Visibility.Visible : Visibility.Collapsed; + }); + } + + private void UpdateControllerImage() + { + BitmapImage controllerImage; + if (controllerMode == HIDmode.NoController || controllerStatus == HIDstatus.Disconnected) + controllerImage = new BitmapImage(new Uri($"pack://application:,,,/Resources/controller_2_0.png")); + else + controllerImage = new BitmapImage(new Uri($"pack://application:,,,/Resources/controller_{Convert.ToInt32(controllerMode)}_{Convert.ToInt32(controllerStatus)}.png")); + + // update UI icon to match HIDmode + ImageBrush uniformToFillBrush = new ImageBrush() + { + Stretch = Stretch.Uniform, + ImageSource = controllerImage, + }; + uniformToFillBrush.Freeze(); + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + ControllerGrid.Background = uniformToFillBrush; + }); + } + + private async void cB_HidMode_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (cB_HidMode.SelectedIndex == -1) + return; + + controllerMode = (HIDmode)cB_HidMode.SelectedIndex; + UpdateControllerImage(); + +<<<<<<< HEAD + // only change HIDmode setting if current profile is default or set to default controller + var currentProfile = ProfileManager.GetCurrent(); + if (currentProfile.Default || (HIDmode)currentProfile.HID == HIDmode.NotSelected) + { + SettingsManager.SetProperty("HIDmode", cB_HidMode.SelectedIndex); + } +======= + // raise event + HIDchanged?.Invoke(controllerMode); + + SettingsManager.SetProperty("HIDmode", cB_HidMode.SelectedIndex); + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void cB_ServiceSwitch_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (cB_HidMode.SelectedIndex == -1) + return; + + controllerStatus = (HIDstatus)cB_ServiceSwitch.SelectedIndex; + UpdateControllerImage(); + + SettingsManager.SetProperty("HIDstatus", cB_ServiceSwitch.SelectedIndex); + } + + private void Toggle_Cloaked_Toggled(object sender, RoutedEventArgs e) + { + if (!IsLoaded) + return; + + SettingsManager.SetProperty("HIDcloakonconnect", Toggle_Cloaked.IsOn); + } + + private void Toggle_Uncloak_Toggled(object sender, RoutedEventArgs e) + { + if (!IsLoaded) + return; + + SettingsManager.SetProperty("HIDuncloakonclose", Toggle_Uncloak.IsOn); + } + + private void SliderStrength_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + var value = SliderStrength.Value; + if (double.IsNaN(value)) + return; + + SliderStrength.Value = value; + + if (!IsLoaded) + return; + + SettingsManager.SetProperty("VibrationStrength", value); + } + + private void Toggle_SCMuteController_Toggled(object sender, RoutedEventArgs e) + { + if (!IsLoaded) + return; + + SettingsManager.SetProperty("SteamControllerMute", Toggle_SCMuteController.IsOn); + } + + private void Toggle_Vibrate_Toggled(object sender, RoutedEventArgs e) + { + if (!IsLoaded) + return; + + SettingsManager.SetProperty("HIDvibrateonconnect", Toggle_Vibrate.IsOn); + } + + private void Toggle_ControllerManagement_Toggled(object sender, RoutedEventArgs e) + { + if (!IsLoaded) + return; + + SettingsManager.SetProperty("ControllerManagement", Toggle_ControllerManagement.IsOn); + } + + private void Button_Layout_Click(object sender, RoutedEventArgs e) + { + // prepare layout editor, desktopLayout gets saved automatically + LayoutTemplate desktopTemplate = new(LayoutManager.GetDesktop()) + { + Name = LayoutTemplate.DesktopLayout.Name, + Description = LayoutTemplate.DesktopLayout.Description, + Author = Environment.UserName, + Executable = string.Empty, + Product = string.Empty // UI might've set something here, nullify + }; + MainWindow.layoutPage.UpdateLayoutTemplate(desktopTemplate); + MainWindow.NavView_Navigate(MainWindow.layoutPage); + } + + private void Toggle_DesktopLayout_Toggled(object sender, RoutedEventArgs e) + { + if (!IsLoaded) + return; + + // temporary settings + SettingsManager.SetProperty("DesktopLayoutEnabled", Toggle_DesktopLayout.IsOn, false, true); + } + + private void Expander_Expanded(object sender, RoutedEventArgs e) + { + ((Expander)sender).BringIntoView(); + } + + private void Toggle_TouchpadPassthrough_Toggled(object sender, RoutedEventArgs e) + { + if (!IsLoaded) + return; + + SettingsManager.SetProperty("LegionControllerPassthrough", Toggle_TouchpadPassthrough.IsOn); + } } \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/DevicePage.xaml b/HandheldCompanion/Views/Pages/DevicePage.xaml index c415f307d..973b0883a 100644 --- a/HandheldCompanion/Views/Pages/DevicePage.xaml +++ b/HandheldCompanion/Views/Pages/DevicePage.xaml @@ -1,548 +1,566 @@ -<Page - x:Class="HandheldCompanion.Views.Pages.DevicePage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Name="Device" - Title="{x:Static resx:Resources.DevicePage_Device}" - d:Background="White" - d:DesignHeight="1500" - d:DesignWidth="1000" - KeepAlive="True" - Loaded="Page_Loaded" - mc:Ignorable="d"> - - <Grid Name="MainGrid" Margin="20"> - <ui:SimpleStackPanel Spacing="24"> - <!-- Power options --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.DevicePage_PowerOptions}" /> - - <!-- Configurable TDP --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_TDPRangeOverride}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_TDPRangeOverrideDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_cTDP" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_cTDP_Toggled" /> - </Grid> - </Border> - - <!-- Configurable TDP down --> - <Border - Padding="15,12,12,12" - d:Visibility="Visible" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}" - Visibility="{Binding ElementName=Toggle_cTDP, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_TDPMin}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_TDPMinDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:NumberBox - Name="NumberBox_TDPMin" - Grid.Column="1" - VerticalAlignment="Center" - Minimum="3" - SpinButtonPlacementMode="Inline" - ValueChanged="NumberBox_TDPMin_ValueChanged" /> - </Grid> - </Border> - - <!-- Configurable TDP up --> - <Border - Padding="15,12,12,12" - d:Visibility="Visible" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}" - Visibility="{Binding ElementName=Toggle_cTDP, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_TDPMax}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_TDPMaxDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:NumberBox - Name="NumberBox_TDPMax" - Grid.Column="1" - VerticalAlignment="Center" - Maximum="64" - SpinButtonPlacementMode="Inline" - ValueChanged="NumberBox_TDPMax_ValueChanged" /> - </Grid> - </Border> - </ui:SimpleStackPanel> - - <!-- Dynamic lighting settings --> - <ui:SimpleStackPanel Name="DynamicLightingPanel" Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="Dynamic lighting settings" /> - - <!-- Use Dynamic lighting --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Use dynamic lighting on my device" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="UseDynamicLightingToggle" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="UseDynamicLightingToggle_Toggled" /> - </Grid> - </Border> - - <ui:SimpleStackPanel - Name="LEDPanel" - IsEnabled="{Binding ElementName=UseDynamicLightingToggle, Path=IsOn}" - Spacing="6"> - <!-- Static LED Brightness --> - <Border - Name="LEDBrightness" - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.DevicePage_DynamicLighting_Brightness}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.DevicePage_DynamicLighting_BrightnessDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <DockPanel - Grid.Column="1" - Margin="12,0,0,0" - ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N0, ElementName=SliderLEDBrightness, Mode=OneWay}" - TextAlignment="Center" /> - <TextBlock VerticalAlignment="Center" Text="%" /> - <Slider - x:Name="SliderLEDBrightness" - Margin="6,0,0,0" - VerticalAlignment="Center" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="10" - Maximum="100" - Minimum="0" - SmallChange="5" - Style="{DynamicResource SliderStyle1}" - TickFrequency="5" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="SliderLEDBrightness_ValueChanged" - Value="100" /> - </DockPanel> - </Grid> - </Border> - - <!-- Dynamic lighting effects --> - <Expander - Name="DynamicLightingEffects" - HorizontalAlignment="Stretch" - Expanded="Expander_Expanded"> - - <Expander.Header> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.DevicePage_DynamicLighting}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.DevicePage_DynamicLightingDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="LEDSettingsLevel" - Grid.Column="1" - Width="140" - Margin="12,0,0,0" - HorizontalAlignment="Right" - VerticalAlignment="Center" - SelectionChanged="LEDSettingsLevel_SelectionChanged"> - - <ComboBoxItem - Name="LEDSolidColor" - Content="{x:Static resx:Resources.DevicePage_DynamicLighting_SolidColor}" - IsEnabled="False" - Tag="0" /> - <ComboBoxItem - Name="LEDBreathing" - Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Breathing}" - IsEnabled="False" - Tag="1" /> - <ComboBoxItem - Name="LEDRainbow" - Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Rainbow}" - IsEnabled="False" - Tag="2" /> - <ComboBoxItem - Name="LEDWave" - Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Wave}" - IsEnabled="False" - Tag="4" /> - <ComboBoxItem - Name="LEDWheel" - Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Wheel}" - IsEnabled="False" - Tag="8" /> - <ComboBoxItem - Name="LEDGradient" - Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Gradient}" - IsEnabled="False" - Tag="16" /> - <ComboBoxItem - Name="LEDAmbilight" - Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Ambilight}" - IsEnabled="False" - Tag="32" /> - </ComboBox> - - </Grid> - </Expander.Header> - - <Expander.Content> - <ui:SimpleStackPanel Margin="30,-6,0,-6" Spacing="6"> - - <!-- Effect speed --> - <ui:SimpleStackPanel - d:Visibility="Visible" - Spacing="6" - Visibility="{Binding ElementName=LEDSettingsLevel, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=1|2|3|4|5|6}"> - - <Grid Height="40"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.DevicePage_DynamicLighting_EffectSpeed}" /> - - <DockPanel - Grid.Column="1" - Margin="12,0,0,0" - ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N0, ElementName=SliderLEDSpeed, Mode=OneWay}" - TextAlignment="Center" /> - <TextBlock VerticalAlignment="Center" Text="%" /> - <Slider - x:Name="SliderLEDSpeed" - Margin="6,0,0,0" - VerticalAlignment="Center" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="10" - Maximum="100" - Minimum="10" - SmallChange="5" - Style="{DynamicResource SliderStyle1}" - TickFrequency="5" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="SliderLEDSpeed_ValueChanged" - Value="100" /> - </DockPanel> - </Grid> - - <!-- Separator --> - <Separator - Margin="-46,0,-16,0" - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" /> - </ui:SimpleStackPanel> - - <!-- Direction --> - <!-- Visibility="{Binding ElementName=LEDSettingsLevel, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=2|3|4|5}" --> - <ui:SimpleStackPanel - d:Visibility="Visible" - Spacing="6" - Visibility="Collapsed"> - - <Grid Height="40"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.DevicePage_DynamicLighting_EffectDirection}" /> - - <DockPanel Grid.Column="1" Margin="12,0,0,0"> - <ComboBox - Name="LEDDirection" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - SelectedIndex="0" - SelectionChanged="LEDDirection_SelectionChanged" /> - </DockPanel> - </Grid> - - <!-- Separator --> - <Separator - Margin="-46,0,-16,0" - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" /> - </ui:SimpleStackPanel> - - <!-- Main color --> - <ui:SimpleStackPanel - d:Visibility="Visible" - Spacing="6" - Visibility="{Binding ElementName=LEDSettingsLevel, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=0|1|3|5}"> - - <!-- Match my Windows accent color --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.DevicePage_DynamicLighting_MatchAccentColor}" /> - - <ui:ToggleSwitch - Name="MatchAccentColor" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="MatchAccentColor_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - Margin="-46,0,-16,0" - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" /> - - <!-- Main color picker --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.DevicePage_DynamicLighting_MainColorSelection}" /> - - <colorpicker:PortableColorPicker - Name="MainColorPicker" - Grid.Column="1" - Height="40" - Margin="12,0,0,0" - ColorChanged="MainColorPicker_ColorChanged" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{StaticResource DefaultColorPickerStyle}" /> - </Grid> - - <!-- Separator --> - <Separator - Margin="-46,0,-16,0" - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" /> - </ui:SimpleStackPanel> - - <!-- Second color --> - <!-- Visibility="{Binding ElementName=LEDSettingsLevel, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=3|5}" --> - <ui:SimpleStackPanel - d:Visibility="Visible" - Spacing="6" - Visibility="Collapsed"> - - <!-- Second color --> - <Grid Height="40"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.DevicePage_DynamicLighting_SecondColorSelection}" /> - - <colorpicker:PortableColorPicker - Name="SecondColorPicker" - Grid.Column="1" - Height="40" - Margin="12,0,0,0" - ColorChanged="SecondColorPicker_ColorChanged" - ScrollViewer.PanningMode="HorizontalOnly" - Style="{StaticResource DefaultColorPickerStyle}" /> - </Grid> - - <!-- Separator --> - <Separator - Margin="-46,0,-16,0" - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" /> - </ui:SimpleStackPanel> - - <!-- Vertical blackbar detection --> - <ui:SimpleStackPanel - d:Visibility="Visible" - Spacing="6" - Visibility="{Binding ElementName=LEDSettingsLevel, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=6}"> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.DevicePage_AmbilightVerticalBlackBarDetectionDesc}" - TextWrapping="Wrap" /> - - <ui:ToggleSwitch - Name="Toggle_AmbilightVerticalBlackBarDetection" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_AmbilightVerticalBlackBarDetection_Toggled" /> - </Grid> - </ui:SimpleStackPanel> - - </ui:SimpleStackPanel> - </Expander.Content> - </Expander> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> +<Page + x:Class="HandheldCompanion.Views.Pages.DevicePage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" +<<<<<<< HEAD + xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + Name="Device" + Title="{x:Static resx:Resources.DevicePage_Device}" + d:Background="White" + d:DesignHeight="1500" +======= + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + Title="{x:Static resx:Resources.DevicePage_Device}" + d:Background="White" + d:DesignHeight="500" +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + d:DesignWidth="1000" + KeepAlive="True" + Loaded="Page_Loaded" + mc:Ignorable="d"> + + <Grid Name="MainGrid" Margin="20"> + <ui:SimpleStackPanel Spacing="24"> + <!-- Power options --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.DevicePage_PowerOptions}" /> + + <!-- Configurable TDP --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_TDPRangeOverride}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_TDPRangeOverrideDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_cTDP" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_cTDP_Toggled" /> + </Grid> + </Border> + + <!-- Configurable TDP down --> + <Border + Padding="15,12,12,12" +<<<<<<< HEAD + d:Visibility="Visible" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}" + Visibility="{Binding ElementName=Toggle_cTDP, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_TDPMin}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_TDPMinDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:NumberBox + Name="NumberBox_TDPMin" + Grid.Column="1" + VerticalAlignment="Center" + Minimum="3" + SpinButtonPlacementMode="Inline" + ValueChanged="NumberBox_TDPMin_ValueChanged" /> + </Grid> + </Border> + + <!-- Configurable TDP up --> + <Border + Padding="15,12,12,12" +<<<<<<< HEAD + d:Visibility="Visible" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}" + Visibility="{Binding ElementName=Toggle_cTDP, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_TDPMax}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_TDPMaxDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:NumberBox + Name="NumberBox_TDPMax" + Grid.Column="1" + VerticalAlignment="Center" + Maximum="64" + SpinButtonPlacementMode="Inline" + ValueChanged="NumberBox_TDPMax_ValueChanged" /> + </Grid> + </Border> + </ui:SimpleStackPanel> +<<<<<<< HEAD + + <!-- Dynamic lighting settings --> + <ui:SimpleStackPanel Name="DynamicLightingPanel" Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="Dynamic lighting settings" /> + + <!-- Use Dynamic lighting --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Use dynamic lighting on my device" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="UseDynamicLightingToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="UseDynamicLightingToggle_Toggled" /> + </Grid> + </Border> + + <ui:SimpleStackPanel + Name="LEDPanel" + IsEnabled="{Binding ElementName=UseDynamicLightingToggle, Path=IsOn}" + Spacing="6"> + <!-- Static LED Brightness --> + <Border + Name="LEDBrightness" + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.DevicePage_DynamicLighting_Brightness}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.DevicePage_DynamicLighting_BrightnessDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <DockPanel + Grid.Column="1" + Margin="12,0,0,0" + ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=SliderLEDBrightness, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock VerticalAlignment="Center" Text="%" /> + <Slider + x:Name="SliderLEDBrightness" + Margin="6,0,0,0" + VerticalAlignment="Center" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="10" + Maximum="100" + Minimum="0" + SmallChange="5" + Style="{DynamicResource SliderStyle1}" + TickFrequency="5" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="SliderLEDBrightness_ValueChanged" + Value="100" /> + </DockPanel> + </Grid> + </Border> + + <!-- Dynamic lighting effects --> + <Expander + Name="DynamicLightingEffects" + HorizontalAlignment="Stretch" + Expanded="Expander_Expanded"> + + <Expander.Header> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.DevicePage_DynamicLighting}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.DevicePage_DynamicLightingDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="LEDSettingsLevel" + Grid.Column="1" + Width="140" + Margin="12,0,0,0" + HorizontalAlignment="Right" + VerticalAlignment="Center" + SelectionChanged="LEDSettingsLevel_SelectionChanged"> + + <ComboBoxItem + Name="LEDSolidColor" + Content="{x:Static resx:Resources.DevicePage_DynamicLighting_SolidColor}" + IsEnabled="False" + Tag="0" /> + <ComboBoxItem + Name="LEDBreathing" + Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Breathing}" + IsEnabled="False" + Tag="1" /> + <ComboBoxItem + Name="LEDRainbow" + Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Rainbow}" + IsEnabled="False" + Tag="2" /> + <ComboBoxItem + Name="LEDWave" + Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Wave}" + IsEnabled="False" + Tag="4" /> + <ComboBoxItem + Name="LEDWheel" + Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Wheel}" + IsEnabled="False" + Tag="8" /> + <ComboBoxItem + Name="LEDGradient" + Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Gradient}" + IsEnabled="False" + Tag="16" /> + <ComboBoxItem + Name="LEDAmbilight" + Content="{x:Static resx:Resources.DevicePage_DynamicLighting_Ambilight}" + IsEnabled="False" + Tag="32" /> + </ComboBox> + + </Grid> + </Expander.Header> + + <Expander.Content> + <ui:SimpleStackPanel Margin="30,-6,0,-6" Spacing="6"> + + <!-- Effect speed --> + <ui:SimpleStackPanel + d:Visibility="Visible" + Spacing="6" + Visibility="{Binding ElementName=LEDSettingsLevel, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=1|2|3|4|5|6}"> + + <Grid Height="40"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.DevicePage_DynamicLighting_EffectSpeed}" /> + + <DockPanel + Grid.Column="1" + Margin="12,0,0,0" + ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=SliderLEDSpeed, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock VerticalAlignment="Center" Text="%" /> + <Slider + x:Name="SliderLEDSpeed" + Margin="6,0,0,0" + VerticalAlignment="Center" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="10" + Maximum="100" + Minimum="10" + SmallChange="5" + Style="{DynamicResource SliderStyle1}" + TickFrequency="5" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="SliderLEDSpeed_ValueChanged" + Value="100" /> + </DockPanel> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + </ui:SimpleStackPanel> + + <!-- Direction --> + <!-- Visibility="{Binding ElementName=LEDSettingsLevel, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=2|3|4|5}" --> + <ui:SimpleStackPanel + d:Visibility="Visible" + Spacing="6" + Visibility="Collapsed"> + + <Grid Height="40"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.DevicePage_DynamicLighting_EffectDirection}" /> + + <DockPanel Grid.Column="1" Margin="12,0,0,0"> + <ComboBox + Name="LEDDirection" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + SelectedIndex="0" + SelectionChanged="LEDDirection_SelectionChanged" /> + </DockPanel> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + </ui:SimpleStackPanel> + + <!-- Main color --> + <ui:SimpleStackPanel + d:Visibility="Visible" + Spacing="6" + Visibility="{Binding ElementName=LEDSettingsLevel, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=0|1|3|5}"> + + <!-- Match my Windows accent color --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.DevicePage_DynamicLighting_MatchAccentColor}" /> + + <ui:ToggleSwitch + Name="MatchAccentColor" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="MatchAccentColor_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + + <!-- Main color picker --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.DevicePage_DynamicLighting_MainColorSelection}" /> + + <colorpicker:PortableColorPicker + Name="MainColorPicker" + Grid.Column="1" + Height="40" + Margin="12,0,0,0" + ColorChanged="MainColorPicker_ColorChanged" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{StaticResource DefaultColorPickerStyle}" /> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + </ui:SimpleStackPanel> + + <!-- Second color --> + <!-- Visibility="{Binding ElementName=LEDSettingsLevel, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=3|5}" --> + <ui:SimpleStackPanel + d:Visibility="Visible" + Spacing="6" + Visibility="Collapsed"> + + <!-- Second color --> + <Grid Height="40"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.DevicePage_DynamicLighting_SecondColorSelection}" /> + + <colorpicker:PortableColorPicker + Name="SecondColorPicker" + Grid.Column="1" + Height="40" + Margin="12,0,0,0" + ColorChanged="SecondColorPicker_ColorChanged" + ScrollViewer.PanningMode="HorizontalOnly" + Style="{StaticResource DefaultColorPickerStyle}" /> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + </ui:SimpleStackPanel> + + <!-- Vertical blackbar detection --> + <ui:SimpleStackPanel + d:Visibility="Visible" + Spacing="6" + Visibility="{Binding ElementName=LEDSettingsLevel, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=6}"> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.DevicePage_AmbilightVerticalBlackBarDetectionDesc}" + TextWrapping="Wrap" /> + + <ui:ToggleSwitch + Name="Toggle_AmbilightVerticalBlackBarDetection" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_AmbilightVerticalBlackBarDetection_Toggled" /> + </Grid> + </ui:SimpleStackPanel> + + </ui:SimpleStackPanel> + </Expander.Content> + </Expander> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + </ui:SimpleStackPanel> + </Grid> </Page> \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/DevicePage.xaml.cs b/HandheldCompanion/Views/Pages/DevicePage.xaml.cs index 93ab45ece..5d1f2c010 100644 --- a/HandheldCompanion/Views/Pages/DevicePage.xaml.cs +++ b/HandheldCompanion/Views/Pages/DevicePage.xaml.cs @@ -1,3 +1,4 @@ +<<<<<<< HEAD using ColorPicker; using ColorPicker.Models; using HandheldCompanion.Devices; @@ -313,3 +314,130 @@ private void LEDDirection_SelectionChanged(object sender, SelectionChangedEventA } } } +======= +using ColorPicker.Models; +using ColorPicker; +using HandheldCompanion.Devices; +using HandheldCompanion.Managers; +using HandheldCompanion.Misc; +using Inkore.UI.WPF.Modern.Controls; +using System; +using System.Windows; +using Page = System.Windows.Controls.Page; +using System.Windows.Media; + +namespace HandheldCompanion.Views.Pages +{ + /// <summary> + /// Interaction logic for DevicePage.xaml + /// </summary> + public partial class DevicePage : Page + { + public DevicePage() + { + InitializeComponent(); + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + } + + public DevicePage(string? Tag) : this() + { + this.Tag = Tag; + } + + private void Page_Loaded(object? sender, RoutedEventArgs? e) + { + } + + public void Page_Closed() + { + } + + private void SettingsManager_SettingValueChanged(string? name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { + case "ConfigurableTDPOverride": + Toggle_cTDP.IsOn = Convert.ToBoolean(value); + break; + case "ConfigurableTDPOverrideDown": + NumberBox_TDPMin.Value = Convert.ToDouble(value); + break; + case "ConfigurableTDPOverrideUp": + NumberBox_TDPMax.Value = Convert.ToDouble(value); + break; + } + }); + } + + private async void Toggle_cTDP_Toggled(object? sender, RoutedEventArgs? e) + { + if (!IsLoaded) + return; + + if (Toggle_cTDP.IsOn) + { + // todo: localize me ! + var result = Dialog.ShowAsync( + "Warning", + "Altering minimum and maximum CPU power values might cause instabilities. Product warranties may not apply if the processor is operated beyond its specifications. Use at your own risk.", + ContentDialogButton.Primary, "Cancel", Properties.Resources.ProfilesPage_OK); + + await result; // sync call + + switch (result.Result) + { + case ContentDialogResult.Primary: + break; + default: + case ContentDialogResult.None: + // restore previous state + Toggle_cTDP.IsOn = false; + return; + } + } + + SettingsManager.SetProperty("ConfigurableTDPOverride", Toggle_cTDP.IsOn); + SettingsManager.SetProperty("ConfigurableTDPOverrideUp", NumberBox_TDPMax.Value); + SettingsManager.SetProperty("ConfigurableTDPOverrideDown", NumberBox_TDPMin.Value); + } + + private void NumberBox_TDPMax_ValueChanged(NumberBox? sender, NumberBoxValueChangedEventArgs? args) + { + var value = NumberBox_TDPMax.Value; + if (double.IsNaN(value)) + return; + + NumberBox_TDPMin.Maximum = value; + + if (!IsLoaded) + return; + + // update current device cTDP + MainWindow.CurrentDevice.cTDP[1] = value; + + SettingsManager.SetProperty("ConfigurableTDPOverrideUp", value); + } + + private void NumberBox_TDPMin_ValueChanged(NumberBox? sender, NumberBoxValueChangedEventArgs? args) + { + var value = NumberBox_TDPMin.Value; + if (double.IsNaN(value)) + return; + + NumberBox_TDPMax.Minimum = value; + + if (!IsLoaded) + return; + + // update current device cTDP + MainWindow.CurrentDevice.cTDP[0] = value; + + SettingsManager.SetProperty("ConfigurableTDPOverrideDown", value); + } + } +} +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d diff --git a/HandheldCompanion/Views/Pages/HotkeysPage.xaml b/HandheldCompanion/Views/Pages/HotkeysPage.xaml index b57dd66d3..d98067014 100644 --- a/HandheldCompanion/Views/Pages/HotkeysPage.xaml +++ b/HandheldCompanion/Views/Pages/HotkeysPage.xaml @@ -1,20 +1,23 @@ -<Page - x:Class="HandheldCompanion.Views.Pages.HotkeysPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Name="Hotkeys" - Title="Hotkeys" - d:Background="White" - d:DesignHeight="1500" - d:DesignWidth="1000" - KeepAlive="True" - Loaded="Page_Loaded" - mc:Ignorable="d"> - - <Grid Name="MainGrid" Margin="20"> - <ui:SimpleStackPanel Name="HotkeysPanel" Spacing="24" /> - </Grid> +<Page + x:Class="HandheldCompanion.Views.Pages.HotkeysPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" +<<<<<<< HEAD + Name="Hotkeys" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Title="Hotkeys" + d:Background="White" + d:DesignHeight="1500" + d:DesignWidth="1000" + KeepAlive="True" + Loaded="Page_Loaded" + mc:Ignorable="d"> + + <Grid Name="MainGrid" Margin="20"> + <ui:SimpleStackPanel Name="HotkeysPanel" Spacing="24" /> + </Grid> </Page> \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/Layout/ButtonsPage.xaml.cs b/HandheldCompanion/Views/Pages/Layout/ButtonsPage.xaml.cs index 7448ff32c..2172c7acf 100644 --- a/HandheldCompanion/Views/Pages/Layout/ButtonsPage.xaml.cs +++ b/HandheldCompanion/Views/Pages/Layout/ButtonsPage.xaml.cs @@ -1,126 +1,130 @@ -using HandheldCompanion.Controllers; -using HandheldCompanion.Controls; -using HandheldCompanion.Inputs; -using Inkore.UI.WPF.Modern.Controls; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows; - -namespace HandheldCompanion.Views.Pages -{ - /// <summary> - /// Interaction logic for ButtonsPage.xaml - /// </summary> - public partial class ButtonsPage : ILayoutPage - { - public static List<ButtonFlags> ABXY = new() { ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4, ButtonFlags.B5, ButtonFlags.B6, ButtonFlags.B7, ButtonFlags.B8 }; - public static List<ButtonFlags> BUMPERS = new() { ButtonFlags.L1, ButtonFlags.R1 }; - public static List<ButtonFlags> MENU = new() { ButtonFlags.Back, ButtonFlags.Start, ButtonFlags.Special, ButtonFlags.Special2 }; - public static List<ButtonFlags> BACKGRIPS = new() { ButtonFlags.L4, ButtonFlags.L5, ButtonFlags.R4, ButtonFlags.R5 }; - public static List<ButtonFlags> OEM = new() { ButtonFlags.OEM1, ButtonFlags.OEM2, ButtonFlags.OEM3, ButtonFlags.OEM4, ButtonFlags.OEM5, ButtonFlags.OEM6, ButtonFlags.OEM7, ButtonFlags.OEM8, ButtonFlags.OEM9, ButtonFlags.OEM10 }; - - public ButtonsPage() - { - InitializeComponent(); - - // draw UI - foreach (ButtonFlags button in ABXY) - { - ButtonStack panel = new(button); - ButtonsStackPanel.Children.Add(panel); - - ButtonStacks.Add(button, panel); - } - - foreach (ButtonFlags button in BUMPERS) - { - ButtonStack panel = new(button); - BumpersStackPanel.Children.Add(panel); - - ButtonStacks.Add(button, panel); - } - - foreach (ButtonFlags button in MENU) - { - ButtonStack panel = new(button); - MenuStackPanel.Children.Add(panel); - - ButtonStacks.Add(button, panel); - } - - foreach (ButtonFlags button in BACKGRIPS) - { - ButtonStack panel = new(button); - BackgripsStackPanel.Children.Add(panel); - - ButtonStacks.Add(button, panel); - } - - foreach (ButtonFlags button in OEM) - { - if (!MainWindow.CurrentDevice.OEMButtons.Contains(button)) - continue; - - ButtonStack panel = new(button); - OEMStackPanel.Children.Add(panel); - - ButtonStacks.Add(button, panel); - } - - // manage layout pages visibility - gridOEM.Visibility = MainWindow.CurrentDevice.OEMButtons.Count() > 0 ? Visibility.Visible : Visibility.Collapsed; - } - - public override void UpdateController(IController controller) - { - // this will collapse all OEM buttons - base.UpdateController(controller); - - // controller based - foreach (var pair in ButtonStacks) - { - ButtonFlags button = pair.Key; - ButtonStack buttonStack = pair.Value; - - // TODO: simplify or even completely remove OEMs from the mapper - // specific buttons are handled elsewhere - if (OEM.Contains(button)) - { - buttonStack.Visibility = Visibility.Visible; - - // update icon - FontIcon newIcon = MainWindow.CurrentDevice.GetFontIcon(button); - string newLabel = MainWindow.CurrentDevice.GetButtonName(button); - buttonStack.UpdateIcon(newIcon, newLabel); - } - } - - // manage layout pages visibility - bool abxy = CheckController(controller, ABXY); - bool bumpers = CheckController(controller, BUMPERS); - bool menu = CheckController(controller, MENU); - bool backgrips = CheckController(controller, BACKGRIPS); - - gridButtons.Visibility = abxy ? Visibility.Visible : Visibility.Collapsed; - gridBumpers.Visibility = bumpers ? Visibility.Visible : Visibility.Collapsed; - gridMenu.Visibility = menu ? Visibility.Visible : Visibility.Collapsed; - gridBackgrips.Visibility = backgrips ? Visibility.Visible : Visibility.Collapsed; - - enabled = abxy || bumpers || menu || backgrips; - } - - public ButtonsPage(string Tag) : this() - { - this.Tag = Tag; - } - - private void Page_Loaded(object sender, RoutedEventArgs e) - { - } - - public void Page_Closed() - { - } - } +using HandheldCompanion.Controllers; +using HandheldCompanion.Controls; +using HandheldCompanion.Inputs; +using Inkore.UI.WPF.Modern.Controls; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; + +namespace HandheldCompanion.Views.Pages +{ + /// <summary> + /// Interaction logic for ButtonsPage.xaml + /// </summary> + public partial class ButtonsPage : ILayoutPage + { + public static List<ButtonFlags> ABXY = new() { ButtonFlags.B1, ButtonFlags.B2, ButtonFlags.B3, ButtonFlags.B4, ButtonFlags.B5, ButtonFlags.B6, ButtonFlags.B7, ButtonFlags.B8 }; + public static List<ButtonFlags> BUMPERS = new() { ButtonFlags.L1, ButtonFlags.R1 }; + public static List<ButtonFlags> MENU = new() { ButtonFlags.Back, ButtonFlags.Start, ButtonFlags.Special, ButtonFlags.Special2 }; + public static List<ButtonFlags> BACKGRIPS = new() { ButtonFlags.L4, ButtonFlags.L5, ButtonFlags.R4, ButtonFlags.R5 }; + public static List<ButtonFlags> OEM = new() { ButtonFlags.OEM1, ButtonFlags.OEM2, ButtonFlags.OEM3, ButtonFlags.OEM4, ButtonFlags.OEM5, ButtonFlags.OEM6, ButtonFlags.OEM7, ButtonFlags.OEM8, ButtonFlags.OEM9, ButtonFlags.OEM10 }; + + public ButtonsPage() + { + InitializeComponent(); + + // draw UI + foreach (ButtonFlags button in ABXY) + { + ButtonStack panel = new(button); + ButtonsStackPanel.Children.Add(panel); + + ButtonStacks.Add(button, panel); + } + + foreach (ButtonFlags button in BUMPERS) + { + ButtonStack panel = new(button); + BumpersStackPanel.Children.Add(panel); + + ButtonStacks.Add(button, panel); + } + + foreach (ButtonFlags button in MENU) + { + ButtonStack panel = new(button); + MenuStackPanel.Children.Add(panel); + + ButtonStacks.Add(button, panel); + } + + foreach (ButtonFlags button in BACKGRIPS) + { + ButtonStack panel = new(button); + BackgripsStackPanel.Children.Add(panel); + + ButtonStacks.Add(button, panel); + } + + foreach (ButtonFlags button in OEM) + { + if (!MainWindow.CurrentDevice.OEMButtons.Contains(button)) + continue; + + ButtonStack panel = new(button); + OEMStackPanel.Children.Add(panel); + + ButtonStacks.Add(button, panel); + } + + // manage layout pages visibility + gridOEM.Visibility = MainWindow.CurrentDevice.OEMButtons.Count() > 0 ? Visibility.Visible : Visibility.Collapsed; + } + + public override void UpdateController(IController controller) + { + // this will collapse all OEM buttons + base.UpdateController(controller); + + // controller based + foreach (var pair in ButtonStacks) + { + ButtonFlags button = pair.Key; + ButtonStack buttonStack = pair.Value; + + // TODO: simplify or even completely remove OEMs from the mapper + // specific buttons are handled elsewhere + if (OEM.Contains(button)) + { + buttonStack.Visibility = Visibility.Visible; + + // update icon +<<<<<<< HEAD + FontIcon newIcon = MainWindow.CurrentDevice.GetFontIcon(button); +======= + FontIcon newIcon = controller.GetFontIcon(button); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + string newLabel = MainWindow.CurrentDevice.GetButtonName(button); + buttonStack.UpdateIcon(newIcon, newLabel); + } + } + + // manage layout pages visibility + bool abxy = CheckController(controller, ABXY); + bool bumpers = CheckController(controller, BUMPERS); + bool menu = CheckController(controller, MENU); + bool backgrips = CheckController(controller, BACKGRIPS); + + gridButtons.Visibility = abxy ? Visibility.Visible : Visibility.Collapsed; + gridBumpers.Visibility = bumpers ? Visibility.Visible : Visibility.Collapsed; + gridMenu.Visibility = menu ? Visibility.Visible : Visibility.Collapsed; + gridBackgrips.Visibility = backgrips ? Visibility.Visible : Visibility.Collapsed; + + enabled = abxy || bumpers || menu || backgrips; + } + + public ButtonsPage(string Tag) : this() + { + this.Tag = Tag; + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + } + + public void Page_Closed() + { + } + } } \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/LayoutPage.xaml b/HandheldCompanion/Views/Pages/LayoutPage.xaml index e635e4c6e..b72d9bb2f 100644 --- a/HandheldCompanion/Views/Pages/LayoutPage.xaml +++ b/HandheldCompanion/Views/Pages/LayoutPage.xaml @@ -1,229 +1,232 @@ -<Page - x:Class="HandheldCompanion.Views.Pages.LayoutPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Name="Layout" - Title="{x:Static resx:Resources.ProfilesPage_ControllerLayout}" - d:Background="White" - d:DesignHeight="1000" - d:DesignWidth="1000" - Loaded="Page_Loaded" - mc:Ignorable="d"> - - <ui:NavigationView - Name="navView" - Margin="20" - IsBackButtonVisible="Collapsed" - IsPaneToggleButtonVisible="False" - IsSettingsVisible="False" - ItemInvoked="navView_ItemInvoked" - Loaded="navView_Loaded" - OpenPaneLength="150" - PaneDisplayMode="Top" - SelectionFollowsFocus="Enabled" - ShoulderNavigationEnabled="WhenSelectionFollowsFocus"> - - <ui:NavigationView.MenuItems> - <ui:NavigationViewItem - Name="navButtons" - Content="{x:Static resx:Resources.LayoutPage_Buttons}" - Tag="ButtonsPage"> - <ui:NavigationViewItem.Icon> - <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> - </ui:NavigationViewItem.Icon> - </ui:NavigationViewItem> - - <ui:NavigationViewItem - Name="navDpad" - Content="{x:Static resx:Resources.LayoutPage_Dpad}" - Tag="DpadPage"> - <ui:NavigationViewItem.Icon> - <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> - </ui:NavigationViewItem.Icon> - </ui:NavigationViewItem> - - <ui:NavigationViewItem - Name="navTriggers" - Content="{x:Static resx:Resources.LayoutPage_Triggers}" - Tag="TriggersPage"> - <ui:NavigationViewItem.Icon> - <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> - </ui:NavigationViewItem.Icon> - </ui:NavigationViewItem> - - <ui:NavigationViewItem - Name="navJoysticks" - Content="{x:Static resx:Resources.LayoutPage_Joysticks}" - Tag="JoysticksPage"> - <ui:NavigationViewItem.Icon> - <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> - </ui:NavigationViewItem.Icon> - </ui:NavigationViewItem> - - <ui:NavigationViewItem - Name="navTrackpads" - Content="{x:Static resx:Resources.LayoutPage_Trackpads}" - Tag="TrackpadsPage"> - <ui:NavigationViewItem.Icon> - <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> - </ui:NavigationViewItem.Icon> - </ui:NavigationViewItem> - - <ui:NavigationViewItem - Name="navGyro" - Content="{x:Static resx:Resources.LayoutPage_Gyro}" - IsEnabled="False" - Tag="GyroPage"> - <ui:NavigationViewItem.Icon> - <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> - </ui:NavigationViewItem.Icon> - </ui:NavigationViewItem> - </ui:NavigationView.MenuItems> - - <!-- Padding is set to NavView OpenPaneLength --> - <Grid Name="MainGrid" Margin="20"> - <ui:SimpleStackPanel Spacing="10"> - <!-- Profile picker --> - <ui:SimpleStackPanel - Name="LayoutPickerPanel" - d:Visibility="Visible" - Spacing="12"> - - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.LayoutPage_TemplatePicker}" /> - <ComboBox - Name="cB_Layouts" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - HorizontalContentAlignment="Left" - SelectionChanged="cB_Layouts_SelectionChanged" - SizeChanged="cB_Layouts_SizeChanged"> - - <!-- TEMPLATES --> - <ComboBoxItem - Name="cB_LayoutsSplitterTemplates" - Content="{x:Static resx:Resources.LayoutPage_Templates}" - IsEnabled="false" /> - <!-- COMMUNITY --> - <ComboBoxItem - Name="cB_LayoutsSplitterCommunity" - Content="{x:Static resx:Resources.LayoutPage_Community}" - IsEnabled="false" /> - </ComboBox> - - <ui:SimpleStackPanel - Orientation="Vertical" - Spacing="6"> - - <Grid> - <CheckBox - Name="CheckBoxDeviceLayouts" - Checked="CheckBoxDeviceLayouts_Checked" - Content="{x:Static resx:Resources.LayoutPage_ShowCurrentControllerTemplates}" - IsChecked="True" - Unchecked="CheckBoxDeviceLayouts_Checked" /> - - <ui:SimpleStackPanel - HorizontalAlignment="Right" - Orientation="Horizontal" - Spacing="6"> - - <!-- Apply template --> - <Button - Name="ButtonApplyLayout" - Click="ButtonApplyLayout_Click" - Content="{x:Static resx:Resources.LayoutPage_ApplyTemplate}" - IsEnabled="False" - Style="{DynamicResource AccentButtonStyle}" /> - - <!-- Export layout --> - <Button Name="ButtonLayoutSettings" Content="Export layout"> - <ui:FlyoutService.Flyout> - <ui:Flyout - x:Name="LayoutFlyout" - AreOpenCloseAnimationsEnabled="True" - Opening="Flyout_Opening" - Placement="Left"> - <ui:SimpleStackPanel Width="400" Spacing="24"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.LayoutPage_ExportLayout}" /> - - <ui:SimpleStackPanel Spacing="6"> - <TextBox - Name="LayoutTitle" - HorizontalAlignment="Stretch" - ui:ControlHelper.Header="{x:Static resx:Resources.LayoutPage_LayoutTitle}" /> - <TextBox - Name="LayoutDescription" - HorizontalAlignment="Stretch" - ui:ControlHelper.Header="{x:Static resx:Resources.LayoutPage_LayoutDesc}" /> - <TextBox - Name="LayoutAuthor" - HorizontalAlignment="Stretch" - ui:ControlHelper.Header="{x:Static resx:Resources.LayoutPage_LayoutAuthor}" /> - <CheckBox x:Name="ExportForCurrent" Content="{x:Static resx:Resources.LayoutPage_ExportCurrentController}" /> - <CheckBox - x:Name="SaveGameInfo" - Click="SaveGameInfo_Toggled" - Content="{x:Static resx:Resources.LayoutPage_SaveGameInfoLayout}" - IsChecked="True" /> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" /> - <ColumnDefinition Width="0.5*" /> - <ColumnDefinition Width="5*" /> - </Grid.ColumnDefinitions> - - <Button - Name="LayoutExportButton" - Grid.Column="0" - HorizontalAlignment="Stretch" - Click="LayoutExportButton_Click" - Content="{x:Static resx:Resources.LayoutPage_Confirm}" /> - <Button - Name="LayoutCancelButton" - Grid.Column="2" - HorizontalAlignment="Stretch" - Click="LayoutCancelButton_Click" - Content="{x:Static resx:Resources.LayoutPage_Cancel}" /> - </Grid> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </ui:Flyout> - </ui:FlyoutService.Flyout> - </Button> - </ui:SimpleStackPanel> - </Grid> - - <CheckBox - Name="CheckBoxDefaultLayout" - Checked="CheckBoxDefaultLayout_Checked" - Content="{x:Static resx:Resources.LayoutPage_SetAsDefault}" - Unchecked="CheckBoxDefaultLayout_Checked" /> - </ui:SimpleStackPanel> - - <!-- Separator --> - <Separator - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" - Opacity="0.25" /> - - </ui:SimpleStackPanel> - - <ui:Frame - Name="ContentFrame" - Width="{Binding ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type FrameworkElement}}}" - HorizontalAlignment="Left" /> - </ui:SimpleStackPanel> - </Grid> - </ui:NavigationView> +<Page + x:Class="HandheldCompanion.Views.Pages.LayoutPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" +<<<<<<< HEAD + Name="Layout" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Title="{x:Static resx:Resources.ProfilesPage_ControllerLayout}" + d:Background="White" + d:DesignHeight="1000" + d:DesignWidth="1000" + Loaded="Page_Loaded" + mc:Ignorable="d"> + + <ui:NavigationView + Name="navView" + Margin="20" + IsBackButtonVisible="Collapsed" + IsPaneToggleButtonVisible="False" + IsSettingsVisible="False" + ItemInvoked="navView_ItemInvoked" + Loaded="navView_Loaded" + OpenPaneLength="150" + PaneDisplayMode="Top" + SelectionFollowsFocus="Enabled" + ShoulderNavigationEnabled="WhenSelectionFollowsFocus"> + + <ui:NavigationView.MenuItems> + <ui:NavigationViewItem + Name="navButtons" + Content="{x:Static resx:Resources.LayoutPage_Buttons}" + Tag="ButtonsPage"> + <ui:NavigationViewItem.Icon> + <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + </ui:NavigationViewItem.Icon> + </ui:NavigationViewItem> + + <ui:NavigationViewItem + Name="navDpad" + Content="{x:Static resx:Resources.LayoutPage_Dpad}" + Tag="DpadPage"> + <ui:NavigationViewItem.Icon> + <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + </ui:NavigationViewItem.Icon> + </ui:NavigationViewItem> + + <ui:NavigationViewItem + Name="navTriggers" + Content="{x:Static resx:Resources.LayoutPage_Triggers}" + Tag="TriggersPage"> + <ui:NavigationViewItem.Icon> + <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + </ui:NavigationViewItem.Icon> + </ui:NavigationViewItem> + + <ui:NavigationViewItem + Name="navJoysticks" + Content="{x:Static resx:Resources.LayoutPage_Joysticks}" + Tag="JoysticksPage"> + <ui:NavigationViewItem.Icon> + <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + </ui:NavigationViewItem.Icon> + </ui:NavigationViewItem> + + <ui:NavigationViewItem + Name="navTrackpads" + Content="{x:Static resx:Resources.LayoutPage_Trackpads}" + Tag="TrackpadsPage"> + <ui:NavigationViewItem.Icon> + <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + </ui:NavigationViewItem.Icon> + </ui:NavigationViewItem> + + <ui:NavigationViewItem + Name="navGyro" + Content="{x:Static resx:Resources.LayoutPage_Gyro}" + IsEnabled="False" + Tag="GyroPage"> + <ui:NavigationViewItem.Icon> + <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + </ui:NavigationViewItem.Icon> + </ui:NavigationViewItem> + </ui:NavigationView.MenuItems> + + <!-- Padding is set to NavView OpenPaneLength --> + <Grid Name="MainGrid" Margin="20"> + <ui:SimpleStackPanel Spacing="10"> + <!-- Profile picker --> + <ui:SimpleStackPanel + Name="LayoutPickerPanel" + d:Visibility="Visible" + Spacing="12"> + + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.LayoutPage_TemplatePicker}" /> + <ComboBox + Name="cB_Layouts" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + HorizontalContentAlignment="Left" + SelectionChanged="cB_Layouts_SelectionChanged" + SizeChanged="cB_Layouts_SizeChanged"> + + <!-- TEMPLATES --> + <ComboBoxItem + Name="cB_LayoutsSplitterTemplates" + Content="{x:Static resx:Resources.LayoutPage_Templates}" + IsEnabled="false" /> + <!-- COMMUNITY --> + <ComboBoxItem + Name="cB_LayoutsSplitterCommunity" + Content="{x:Static resx:Resources.LayoutPage_Community}" + IsEnabled="false" /> + </ComboBox> + + <ui:SimpleStackPanel + Orientation="Vertical" + Spacing="6"> + + <Grid> + <CheckBox + Name="CheckBoxDeviceLayouts" + Checked="CheckBoxDeviceLayouts_Checked" + Content="{x:Static resx:Resources.LayoutPage_ShowCurrentControllerTemplates}" + IsChecked="True" + Unchecked="CheckBoxDeviceLayouts_Checked" /> + + <ui:SimpleStackPanel + HorizontalAlignment="Right" + Orientation="Horizontal" + Spacing="6"> + + <!-- Apply template --> + <Button + Name="ButtonApplyLayout" + Click="ButtonApplyLayout_Click" + Content="{x:Static resx:Resources.LayoutPage_ApplyTemplate}" + IsEnabled="False" + Style="{DynamicResource AccentButtonStyle}" /> + + <!-- Export layout --> + <Button Name="ButtonLayoutSettings" Content="Export layout"> + <ui:FlyoutService.Flyout> + <ui:Flyout + x:Name="LayoutFlyout" + AreOpenCloseAnimationsEnabled="True" + Opening="Flyout_Opening" + Placement="Left"> + <ui:SimpleStackPanel Width="400" Spacing="24"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.LayoutPage_ExportLayout}" /> + + <ui:SimpleStackPanel Spacing="6"> + <TextBox + Name="LayoutTitle" + HorizontalAlignment="Stretch" + ui:ControlHelper.Header="{x:Static resx:Resources.LayoutPage_LayoutTitle}" /> + <TextBox + Name="LayoutDescription" + HorizontalAlignment="Stretch" + ui:ControlHelper.Header="{x:Static resx:Resources.LayoutPage_LayoutDesc}" /> + <TextBox + Name="LayoutAuthor" + HorizontalAlignment="Stretch" + ui:ControlHelper.Header="{x:Static resx:Resources.LayoutPage_LayoutAuthor}" /> + <CheckBox x:Name="ExportForCurrent" Content="{x:Static resx:Resources.LayoutPage_ExportCurrentController}" /> + <CheckBox + x:Name="SaveGameInfo" + Click="SaveGameInfo_Toggled" + Content="{x:Static resx:Resources.LayoutPage_SaveGameInfoLayout}" + IsChecked="True" /> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" /> + <ColumnDefinition Width="0.5*" /> + <ColumnDefinition Width="5*" /> + </Grid.ColumnDefinitions> + + <Button + Name="LayoutExportButton" + Grid.Column="0" + HorizontalAlignment="Stretch" + Click="LayoutExportButton_Click" + Content="{x:Static resx:Resources.LayoutPage_Confirm}" /> + <Button + Name="LayoutCancelButton" + Grid.Column="2" + HorizontalAlignment="Stretch" + Click="LayoutCancelButton_Click" + Content="{x:Static resx:Resources.LayoutPage_Cancel}" /> + </Grid> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </ui:Flyout> + </ui:FlyoutService.Flyout> + </Button> + </ui:SimpleStackPanel> + </Grid> + + <CheckBox + Name="CheckBoxDefaultLayout" + Checked="CheckBoxDefaultLayout_Checked" + Content="{x:Static resx:Resources.LayoutPage_SetAsDefault}" + Unchecked="CheckBoxDefaultLayout_Checked" /> + </ui:SimpleStackPanel> + + <!-- Separator --> + <Separator + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" + Opacity="0.25" /> + + </ui:SimpleStackPanel> + + <ui:Frame + Name="ContentFrame" + Width="{Binding ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type FrameworkElement}}}" + HorizontalAlignment="Left" /> + </ui:SimpleStackPanel> + </Grid> + </ui:NavigationView> </Page> \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/LayoutPage.xaml.cs b/HandheldCompanion/Views/Pages/LayoutPage.xaml.cs index 9e21645c1..c41ad84d3 100644 --- a/HandheldCompanion/Views/Pages/LayoutPage.xaml.cs +++ b/HandheldCompanion/Views/Pages/LayoutPage.xaml.cs @@ -1,561 +1,573 @@ -using HandheldCompanion.Actions; -using HandheldCompanion.Controllers; -using HandheldCompanion.Controls; -using HandheldCompanion.Inputs; -using HandheldCompanion.Managers; -using HandheldCompanion.Misc; -using HandheldCompanion.Utils; -using Inkore.UI.WPF.Modern.Controls; -using Nefarius.Utilities.DeviceManagement.PnP; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Navigation; -using Page = System.Windows.Controls.Page; - -namespace HandheldCompanion.Views.Pages; - -/// <summary> -/// Interaction logic for ControllerSettings.xaml -/// </summary> -public partial class LayoutPage : Page -{ - private LayoutTemplate currentTemplate = new(); - protected LockObject updateLock = new(); - - // page vars - private Dictionary<string, (ILayoutPage, NavigationViewItem)> pages; - private readonly ButtonsPage buttonsPage = new(); - private readonly DpadPage dpadPage = new(); - private readonly GyroPage gyroPage = new(); - private readonly JoysticksPage joysticksPage = new(); - private readonly TrackpadsPage trackpadsPage = new(); - private readonly TriggersPage triggersPage = new(); - - private NavigationView parentNavView; - private string preNavItemTag; - - public LayoutPage() - { - InitializeComponent(); - } - - public LayoutPage(string Tag, NavigationView parent) : this() - { - this.Tag = Tag; - this.parentNavView = parent; - - // create controller related pages - this.pages = new() - { - // buttons - { "ButtonsPage", ( buttonsPage, navButtons ) }, - { "DpadPage", ( dpadPage, navDpad ) }, - - // triger - { "TriggersPage", ( triggersPage, navTriggers ) }, - - // axis - { "JoysticksPage", ( joysticksPage, navJoysticks ) }, - { "TrackpadsPage", ( trackpadsPage, navTrackpads ) }, - - // gyro - { "GyroPage", ( gyroPage, navGyro ) }, - }; - - foreach (ButtonStack buttonStack in buttonsPage.ButtonStacks.Values.Union(dpadPage.ButtonStacks.Values).Union(triggersPage.ButtonStacks.Values).Union(joysticksPage.ButtonStacks.Values).Union(trackpadsPage.ButtonStacks.Values)) - { - buttonStack.Updated += (sender, actions) => ButtonMapping_Updated((ButtonFlags)sender, actions); - buttonStack.Deleted += (sender) => ButtonMapping_Deleted((ButtonFlags)sender); - } - - foreach (TriggerMapping axisMapping in triggersPage.TriggerMappings.Values) - { - axisMapping.Updated += (sender, action) => AxisMapping_Updated((AxisLayoutFlags)sender, action); - axisMapping.Deleted += (sender) => AxisMapping_Deleted((AxisLayoutFlags)sender); - } - - foreach (AxisMapping axisMapping in joysticksPage.AxisMappings.Values.Union(trackpadsPage.AxisMappings.Values)) - { - axisMapping.Updated += (sender, action) => AxisMapping_Updated((AxisLayoutFlags)sender, action); - axisMapping.Deleted += (sender) => AxisMapping_Deleted((AxisLayoutFlags)sender); - } - - foreach (GyroMapping gyroMapping in gyroPage.GyroMappings.Values) - { - gyroMapping.Updated += (sender, action) => AxisMapping_Updated((AxisLayoutFlags)sender, action); - gyroMapping.Deleted += (sender) => AxisMapping_Deleted((AxisLayoutFlags)sender); - } - - LayoutManager.Updated += LayoutManager_Updated; - LayoutManager.Initialized += LayoutManager_Initialized; - ControllerManager.ControllerSelected += ControllerManager_ControllerSelected; - VirtualManager.ControllerSelected += VirtualManager_ControllerSelected; - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - DeviceManager.UsbDeviceArrived += DeviceManager_UsbDeviceUpdated; - DeviceManager.UsbDeviceRemoved += DeviceManager_UsbDeviceUpdated; - ProfileManager.Updated += ProfileManager_Updated; - } - - private void ProfileManager_Updated(Profile profile, UpdateSource source, bool isCurrent) - { - if (!MainWindow.CurrentPageName.Equals("LayoutPage")) - return; - - // update layout page if layout was updated elsewhere - // good enough - switch(source) - { - case UpdateSource.QuickProfilesPage: - { - if (currentTemplate.Executable.Equals(profile.Executable)) - MainWindow.layoutPage.UpdateLayout(profile.Layout); - } - break; - } - } - - private void ControllerManager_ControllerSelected(IController controller) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - RefreshLayoutList(); - - // cascade update to (sub)pages - foreach (var page in pages.Values) - { - page.Item1.UpdateController(controller); - page.Item2.IsEnabled = page.Item1.IsEnabled(); - } - }); - } - - private void DeviceManager_UsbDeviceUpdated(PnPDevice device, DeviceEventArgs obj) - { - IController controller = ControllerManager.GetTargetController(); - - // lazy - if (controller is not null) - ControllerManager_ControllerSelected(controller); - } - - // todo: fix me when migrated to NO-SERVICE - private void VirtualManager_ControllerSelected(HIDmode HID) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // cascade update to (sub)pages - foreach (var page in pages.Values) - page.Item1.UpdateSelections(); - }); - } - - private void LayoutManager_Initialized() - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - RefreshLayoutList(); - }); - } - - private void LayoutManager_Updated(LayoutTemplate layoutTemplate) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // Get template separator index - var idx = -1; - - // search if we already have this template listed - foreach (var item in cB_Layouts.Items) - { - if (item is not ComboBoxItem) - continue; - - // get template - var parent = (ComboBoxItem)item; - if (parent.Content is not LayoutTemplate) - continue; - - var template = (LayoutTemplate)parent.Content; - if (template.Guid.Equals(layoutTemplate.Guid)) - { - idx = cB_Layouts.Items.IndexOf(parent); - break; - } - } - - if (idx != -1) - { - // found it - cB_Layouts.Items[idx] = new ComboBoxItem { Content = layoutTemplate }; - } - else - { - // new entry - if (layoutTemplate.IsInternal) - idx = cB_Layouts.Items.IndexOf(cB_LayoutsSplitterTemplates) + 1; - else - idx = cB_Layouts.Items.IndexOf(cB_LayoutsSplitterCommunity) + 1; - - cB_Layouts.Items.Insert(idx, new ComboBoxItem { Content = layoutTemplate }); - } - - cB_Layouts.Items.Refresh(); - cB_Layouts.InvalidateVisual(); - }); - } - - private void SettingsManager_SettingValueChanged(string? name, object value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (name) - { - case "LayoutFilterOnDevice": - CheckBoxDeviceLayouts.IsChecked = Convert.ToBoolean(value); - RefreshLayoutList(); - break; - } - }); - } - - private void RefreshLayoutList() - { - // Get filter settings - var FilterOnDevice = SettingsManager.GetBoolean("LayoutFilterOnDevice"); - - // Get current controller - var controller = ControllerManager.GetTargetController(); - - foreach (var layoutTemplate in LayoutManager.Templates) - { - // get parent - if (layoutTemplate.Parent is not ComboBoxItem parent) - continue; - - if (layoutTemplate.ControllerType is not null && FilterOnDevice) - if (layoutTemplate.ControllerType != controller?.GetType()) - { - parent.Visibility = Visibility.Collapsed; - continue; - } - - parent.Visibility = Visibility.Visible; - } - } - - private void ButtonMapping_Deleted(ButtonFlags button) - { - if (updateLock) - return; - - currentTemplate.Layout.RemoveLayout(button); - } - - private void ButtonMapping_Updated(ButtonFlags button, List<IActions> actions) - { - if (updateLock) - return; - - currentTemplate.Layout.UpdateLayout(button, actions); - } - - private void AxisMapping_Deleted(AxisLayoutFlags axis) - { - if (updateLock) - return; - - currentTemplate.Layout.RemoveLayout(axis); - } - - private void AxisMapping_Updated(AxisLayoutFlags axis, IActions action) - { - if (updateLock) - return; - - currentTemplate.Layout.UpdateLayout(axis, action); - } - - private void Page_Loaded(object sender, RoutedEventArgs e) - { - } - - public void Page_Closed() - { - } - - private void Expander_Expanded(object sender, RoutedEventArgs e) - { - ((Expander)sender).BringIntoView(); - } - - public void UpdateLayout(Layout layout) - { - currentTemplate.Layout = layout; - - UpdatePages(); - } - - public void UpdateLayoutTemplate(LayoutTemplate layoutTemplate) - { - // TODO: Not entirely sure what is going on here, but the old templates were still sending - // events. Shouldn't they be destroyed? Either there is a bug or I don't understand something - // in C# (probably the latter). Either way this handles/fixes/workarounds the issue. - if (layoutTemplate.Layout != currentTemplate.Layout) - currentTemplate = layoutTemplate; - - UpdatePages(); - } - - private void UpdatePages() - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // This is a very important lock, it blocks backward events to the layout when - // this is actually the backend that triggered the update. Notifications on higher - // levels (pages and mappings) could potentially be blocked for optimization. - using (new ScopedLock(updateLock)) - { - // cascade update to (sub)pages - foreach (var page in pages.Values) - page.Item1.Update(currentTemplate.Layout); - - // clear layout selection - cB_Layouts.SelectedValue = null; - - CheckBoxDefaultLayout.IsChecked = currentTemplate.Layout.IsDefaultLayout; - CheckBoxDefaultLayout.IsEnabled = currentTemplate.Layout != LayoutManager.GetDesktop(); - } - }); - } - - private void cB_Layouts_SizeChanged(object sender, SizeChangedEventArgs e) - { - var comboBox = (ComboBox)sender; - foreach (var item in comboBox.Items) - { - if (item is not ComboBoxItem) - continue; - - var layoutTemplate = (ComboBoxItem)item; - layoutTemplate.Width = comboBox.ActualWidth; - layoutTemplate.InvalidateVisual(); - } - } - - private async void ButtonApplyLayout_Click(object sender, RoutedEventArgs e) - { - if (cB_Layouts.SelectedItem is null) - return; - - if (cB_Layouts.SelectedItem is not ComboBoxItem) - return; - - // get parent - var parent = cB_Layouts.SelectedItem as ComboBoxItem; - - if (parent.Content is not LayoutTemplate) - return; - - // get template - var layoutTemplate = (LayoutTemplate)parent.Content; - - var result = Dialog.ShowAsync( - string.Format(Properties.Resources.ProfilesPage_AreYouSureApplyTemplate1, currentTemplate.Name), - string.Format(Properties.Resources.ProfilesPage_AreYouSureApplyTemplate2, currentTemplate.Name), - ContentDialogButton.Primary, - $"{Properties.Resources.ProfilesPage_Cancel}", - $"{Properties.Resources.ProfilesPage_Yes}"); - - await result; // sync call - - switch (result.Result) - { - case ContentDialogResult.Primary: - { - // do not overwrite currentTemplate and currentTemplate.Layout as a whole - // because they both have important Update notifitications set - var newLayout = layoutTemplate.Layout.Clone() as Layout; - currentTemplate.Layout.AxisLayout = newLayout.AxisLayout; - currentTemplate.Layout.ButtonLayout = newLayout.ButtonLayout; - currentTemplate.Layout.GyroLayout = newLayout.GyroLayout; - - currentTemplate.Name = layoutTemplate.Name; - currentTemplate.Description = layoutTemplate.Description; - currentTemplate.Guid = layoutTemplate.Guid; // not needed - - // the whole layout has been updated without notification, trigger one - currentTemplate.Layout.UpdateLayout(); - - UpdatePages(); - } - break; - } - } - - private void cB_Layouts_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - ButtonApplyLayout.IsEnabled = cB_Layouts.SelectedIndex != -1; - } - - private void CheckBoxDeviceLayouts_Checked(object sender, RoutedEventArgs e) - { - if (!IsLoaded) - return; - - SettingsManager.SetProperty("LayoutFilterOnDevice", CheckBoxDeviceLayouts.IsChecked); - } - - private void LayoutExportButton_Click(object sender, RoutedEventArgs e) - { - LayoutTemplate newLayout = new() - { - Layout = currentTemplate.Layout, - Name = LayoutTitle.Text, - Description = LayoutDescription.Text, - Author = LayoutAuthor.Text, - Executable = SaveGameInfo.IsChecked == true ? currentTemplate.Executable : "", - Product = SaveGameInfo.IsChecked == true ? currentTemplate.Product : "", - IsInternal = false - }; - - if (newLayout.Name == string.Empty) - { - LayoutFlyout.Hide(); - _ = Dialog.ShowAsync("Layout template name cannot be empty", - "Layout was not exported.", - ContentDialogButton.Primary, null, $"{Properties.Resources.ProfilesPage_OK}"); - return; - } - - if (ExportForCurrent.IsChecked == true) - newLayout.ControllerType = ControllerManager.GetTargetController()?.GetType(); - - LayoutManager.SerializeLayoutTemplate(newLayout); - - LayoutFlyout.Hide(); - _ = Dialog.ShowAsync("Layout template exported", - $"{newLayout.Name} was exported.", - ContentDialogButton.Primary, null, $"{Properties.Resources.ProfilesPage_OK}"); - } - - private void Flyout_Opening(object sender, object e) - { - if (currentTemplate.Executable == string.Empty && currentTemplate.Product == string.Empty) - SaveGameInfo.IsChecked = SaveGameInfo.IsEnabled = false; - else - SaveGameInfo.IsChecked = SaveGameInfo.IsEnabled = true; - - var separator = currentTemplate.Name.Length > 0 && currentTemplate.Product.Length > 0 ? " - " : ""; - - LayoutTitle.Text = $"{currentTemplate.Name}{separator}{currentTemplate.Product}"; - LayoutDescription.Text = currentTemplate.Description; - LayoutAuthor.Text = currentTemplate.Author; - } - - private void SaveGameInfo_Toggled(object sender, RoutedEventArgs e) - { - if (SaveGameInfo.IsChecked == true) - { - var separator = currentTemplate.Name.Length > 0 && currentTemplate.Product.Length > 0 ? " - " : ""; - LayoutTitle.Text = $"{currentTemplate.Name}{separator}{currentTemplate.Product}"; - } - else - { - LayoutTitle.Text = $"{currentTemplate.Name}"; - } - } - - private void LayoutCancelButton_Click(object sender, RoutedEventArgs e) - { - LayoutFlyout.Hide(); - } - - private void navView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) - { - if (args.InvokedItemContainer is null) - return; - - NavigationViewItem navItem = (NavigationViewItem)args.InvokedItemContainer; - preNavItemTag = (string)navItem.Tag; - NavView_Navigate(preNavItemTag); - } - - public void NavView_Navigate(string navItemTag) - { - var item = pages.FirstOrDefault(p => p.Key.Equals(navItemTag)); - Page _page = item.Value.Item1; - - // Get the page type before navigation so you can prevent duplicate - // entries in the backstack. - var preNavPageType = ContentFrame.CurrentSourcePageType; - - // Only navigate if the selected page isn't currently loaded. - if (!(_page is null) && !Equals(preNavPageType, _page)) NavView_Navigate(_page); - } - - public void NavView_Navigate(Page _page) - { - ContentFrame.Navigate(_page); - } - - private void navView_Loaded(object sender, RoutedEventArgs e) - { - // Add handler for ContentFrame navigation. - ContentFrame.Navigated += On_Navigated; - - // NavView doesn't load any page by default, so load home page. - navView.SelectedItem = navView.MenuItems[0]; - - // If navigation occurs on SelectionChanged, this isn't needed. - // Because we use ItemInvoked to navigate, we need to call Navigate - // here to load the home page. - preNavItemTag = "ButtonsPage"; - NavView_Navigate(preNavItemTag); - } - - private void On_Navigated(object sender, NavigationEventArgs e) - { - navView.IsBackEnabled = ContentFrame.CanGoBack; - - if (ContentFrame.SourcePageType is not null) - { - var preNavPageType = ContentFrame.CurrentSourcePageType; - var preNavPageName = preNavPageType.Name; - - var NavViewItem = navView.MenuItems - .OfType<NavigationViewItem>().FirstOrDefault(n => n.Tag.Equals(preNavPageName)); - - if (!(NavViewItem is null)) - navView.SelectedItem = NavViewItem; - - string header = currentTemplate.Product.Length > 0 ? - "Profile: " + currentTemplate.Product : "Layout: Desktop"; - parentNavView.Header = new TextBlock() { Text = header }; - } - } - - private void CheckBoxDefaultLayout_Checked(object sender, RoutedEventArgs e) - { - var isDefaultLayout = (bool)CheckBoxDefaultLayout.IsChecked; - var prevDefaultLayoutProfile = ProfileManager.GetProfileWithDefaultLayout(); - - currentTemplate.Layout.IsDefaultLayout = isDefaultLayout; - currentTemplate.Layout.UpdateLayout(); - - // If option is enabled and a different default layout profile exists, we want to set option false on prev profile. - if (isDefaultLayout && prevDefaultLayoutProfile != null && prevDefaultLayoutProfile.Layout != currentTemplate.Layout) - { - prevDefaultLayoutProfile.Layout.IsDefaultLayout = false; - ProfileManager.UpdateOrCreateProfile(prevDefaultLayoutProfile); - } - } +using HandheldCompanion.Actions; +using HandheldCompanion.Controllers; +using HandheldCompanion.Controls; +using HandheldCompanion.Inputs; +using HandheldCompanion.Managers; +using HandheldCompanion.Misc; +using HandheldCompanion.Utils; +using Inkore.UI.WPF.Modern.Controls; +using Nefarius.Utilities.DeviceManagement.PnP; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Navigation; +using Page = System.Windows.Controls.Page; + +namespace HandheldCompanion.Views.Pages; + +/// <summary> +/// Interaction logic for ControllerSettings.xaml +/// </summary> +public partial class LayoutPage : Page +{ + private LayoutTemplate currentTemplate = new(); + protected LockObject updateLock = new(); + + // page vars + private Dictionary<string, (ILayoutPage, NavigationViewItem)> pages; + private readonly ButtonsPage buttonsPage = new(); + private readonly DpadPage dpadPage = new(); + private readonly GyroPage gyroPage = new(); + private readonly JoysticksPage joysticksPage = new(); + private readonly TrackpadsPage trackpadsPage = new(); + private readonly TriggersPage triggersPage = new(); + + private NavigationView parentNavView; + private string preNavItemTag; + + public LayoutPage() + { + InitializeComponent(); + } + + public LayoutPage(string Tag, NavigationView parent) : this() + { + this.Tag = Tag; + this.parentNavView = parent; + + // create controller related pages + this.pages = new() + { + // buttons + { "ButtonsPage", ( buttonsPage, navButtons ) }, + { "DpadPage", ( dpadPage, navDpad ) }, + + // triger + { "TriggersPage", ( triggersPage, navTriggers ) }, + + // axis + { "JoysticksPage", ( joysticksPage, navJoysticks ) }, + { "TrackpadsPage", ( trackpadsPage, navTrackpads ) }, + + // gyro + { "GyroPage", ( gyroPage, navGyro ) }, + }; + + foreach (ButtonStack buttonStack in buttonsPage.ButtonStacks.Values.Union(dpadPage.ButtonStacks.Values).Union(triggersPage.ButtonStacks.Values).Union(joysticksPage.ButtonStacks.Values).Union(trackpadsPage.ButtonStacks.Values)) + { + buttonStack.Updated += (sender, actions) => ButtonMapping_Updated((ButtonFlags)sender, actions); + buttonStack.Deleted += (sender) => ButtonMapping_Deleted((ButtonFlags)sender); + } + + foreach (TriggerMapping axisMapping in triggersPage.TriggerMappings.Values) + { + axisMapping.Updated += (sender, action) => AxisMapping_Updated((AxisLayoutFlags)sender, action); + axisMapping.Deleted += (sender) => AxisMapping_Deleted((AxisLayoutFlags)sender); + } + + foreach (AxisMapping axisMapping in joysticksPage.AxisMappings.Values.Union(trackpadsPage.AxisMappings.Values)) + { + axisMapping.Updated += (sender, action) => AxisMapping_Updated((AxisLayoutFlags)sender, action); + axisMapping.Deleted += (sender) => AxisMapping_Deleted((AxisLayoutFlags)sender); + } + + foreach (GyroMapping gyroMapping in gyroPage.GyroMappings.Values) + { + gyroMapping.Updated += (sender, action) => AxisMapping_Updated((AxisLayoutFlags)sender, action); + gyroMapping.Deleted += (sender) => AxisMapping_Deleted((AxisLayoutFlags)sender); + } + + LayoutManager.Updated += LayoutManager_Updated; + LayoutManager.Initialized += LayoutManager_Initialized; + ControllerManager.ControllerSelected += ControllerManager_ControllerSelected; +<<<<<<< HEAD + VirtualManager.ControllerSelected += VirtualManager_ControllerSelected; +======= + MainWindow.controllerPage.HIDchanged += VirtualManager_ControllerSelected; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + DeviceManager.UsbDeviceArrived += DeviceManager_UsbDeviceUpdated; + DeviceManager.UsbDeviceRemoved += DeviceManager_UsbDeviceUpdated; + ProfileManager.Updated += ProfileManager_Updated; + } + +<<<<<<< HEAD + private void ProfileManager_Updated(Profile profile, UpdateSource source, bool isCurrent) +======= + private void ProfileManager_Updated(Profile profile, ProfileUpdateSource source, bool isCurrent) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + if (!MainWindow.CurrentPageName.Equals("LayoutPage")) + return; + + // update layout page if layout was updated elsewhere + // good enough + switch(source) + { +<<<<<<< HEAD + case UpdateSource.QuickProfilesPage: +======= + case ProfileUpdateSource.QuickProfilesPage: +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + { + if (currentTemplate.Executable.Equals(profile.Executable)) + MainWindow.layoutPage.UpdateLayout(profile.Layout); + } + break; + } + } + + private void ControllerManager_ControllerSelected(IController controller) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + RefreshLayoutList(); + + // cascade update to (sub)pages + foreach (var page in pages.Values) + { + page.Item1.UpdateController(controller); + page.Item2.IsEnabled = page.Item1.IsEnabled(); + } + }); + } + + private void DeviceManager_UsbDeviceUpdated(PnPDevice device, DeviceEventArgs obj) + { + IController controller = ControllerManager.GetTargetController(); + + // lazy + if (controller is not null) + ControllerManager_ControllerSelected(controller); + } + + // todo: fix me when migrated to NO-SERVICE + private void VirtualManager_ControllerSelected(HIDmode HID) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // cascade update to (sub)pages + foreach (var page in pages.Values) + page.Item1.UpdateSelections(); + }); + } + + private void LayoutManager_Initialized() + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + RefreshLayoutList(); + }); + } + + private void LayoutManager_Updated(LayoutTemplate layoutTemplate) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // Get template separator index + var idx = -1; + + // search if we already have this template listed + foreach (var item in cB_Layouts.Items) + { + if (item is not ComboBoxItem) + continue; + + // get template + var parent = (ComboBoxItem)item; + if (parent.Content is not LayoutTemplate) + continue; + + var template = (LayoutTemplate)parent.Content; + if (template.Guid.Equals(layoutTemplate.Guid)) + { + idx = cB_Layouts.Items.IndexOf(parent); + break; + } + } + + if (idx != -1) + { + // found it + cB_Layouts.Items[idx] = new ComboBoxItem { Content = layoutTemplate }; + } + else + { + // new entry + if (layoutTemplate.IsInternal) + idx = cB_Layouts.Items.IndexOf(cB_LayoutsSplitterTemplates) + 1; + else + idx = cB_Layouts.Items.IndexOf(cB_LayoutsSplitterCommunity) + 1; + + cB_Layouts.Items.Insert(idx, new ComboBoxItem { Content = layoutTemplate }); + } + + cB_Layouts.Items.Refresh(); + cB_Layouts.InvalidateVisual(); + }); + } + + private void SettingsManager_SettingValueChanged(string? name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { + case "LayoutFilterOnDevice": + CheckBoxDeviceLayouts.IsChecked = Convert.ToBoolean(value); + RefreshLayoutList(); + break; + } + }); + } + + private void RefreshLayoutList() + { + // Get filter settings + var FilterOnDevice = SettingsManager.GetBoolean("LayoutFilterOnDevice"); + + // Get current controller + var controller = ControllerManager.GetTargetController(); + + foreach (var layoutTemplate in LayoutManager.Templates) + { + // get parent + if (layoutTemplate.Parent is not ComboBoxItem parent) + continue; + + if (layoutTemplate.ControllerType is not null && FilterOnDevice) + if (layoutTemplate.ControllerType != controller?.GetType()) + { + parent.Visibility = Visibility.Collapsed; + continue; + } + + parent.Visibility = Visibility.Visible; + } + } + + private void ButtonMapping_Deleted(ButtonFlags button) + { + if (updateLock) + return; + + currentTemplate.Layout.RemoveLayout(button); + } + + private void ButtonMapping_Updated(ButtonFlags button, List<IActions> actions) + { + if (updateLock) + return; + + currentTemplate.Layout.UpdateLayout(button, actions); + } + + private void AxisMapping_Deleted(AxisLayoutFlags axis) + { + if (updateLock) + return; + + currentTemplate.Layout.RemoveLayout(axis); + } + + private void AxisMapping_Updated(AxisLayoutFlags axis, IActions action) + { + if (updateLock) + return; + + currentTemplate.Layout.UpdateLayout(axis, action); + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + } + + public void Page_Closed() + { + } + + private void Expander_Expanded(object sender, RoutedEventArgs e) + { + ((Expander)sender).BringIntoView(); + } + + public void UpdateLayout(Layout layout) + { + currentTemplate.Layout = layout; + + UpdatePages(); + } + + public void UpdateLayoutTemplate(LayoutTemplate layoutTemplate) + { + // TODO: Not entirely sure what is going on here, but the old templates were still sending + // events. Shouldn't they be destroyed? Either there is a bug or I don't understand something + // in C# (probably the latter). Either way this handles/fixes/workarounds the issue. + if (layoutTemplate.Layout != currentTemplate.Layout) + currentTemplate = layoutTemplate; + + UpdatePages(); + } + + private void UpdatePages() + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // This is a very important lock, it blocks backward events to the layout when + // this is actually the backend that triggered the update. Notifications on higher + // levels (pages and mappings) could potentially be blocked for optimization. + using (new ScopedLock(updateLock)) + { + // cascade update to (sub)pages + foreach (var page in pages.Values) + page.Item1.Update(currentTemplate.Layout); + + // clear layout selection + cB_Layouts.SelectedValue = null; + + CheckBoxDefaultLayout.IsChecked = currentTemplate.Layout.IsDefaultLayout; + CheckBoxDefaultLayout.IsEnabled = currentTemplate.Layout != LayoutManager.GetDesktop(); + } + }); + } + + private void cB_Layouts_SizeChanged(object sender, SizeChangedEventArgs e) + { + var comboBox = (ComboBox)sender; + foreach (var item in comboBox.Items) + { + if (item is not ComboBoxItem) + continue; + + var layoutTemplate = (ComboBoxItem)item; + layoutTemplate.Width = comboBox.ActualWidth; + layoutTemplate.InvalidateVisual(); + } + } + + private async void ButtonApplyLayout_Click(object sender, RoutedEventArgs e) + { + if (cB_Layouts.SelectedItem is null) + return; + + if (cB_Layouts.SelectedItem is not ComboBoxItem) + return; + + // get parent + var parent = cB_Layouts.SelectedItem as ComboBoxItem; + + if (parent.Content is not LayoutTemplate) + return; + + // get template + var layoutTemplate = (LayoutTemplate)parent.Content; + + var result = Dialog.ShowAsync( + string.Format(Properties.Resources.ProfilesPage_AreYouSureApplyTemplate1, currentTemplate.Name), + string.Format(Properties.Resources.ProfilesPage_AreYouSureApplyTemplate2, currentTemplate.Name), + ContentDialogButton.Primary, + $"{Properties.Resources.ProfilesPage_Cancel}", + $"{Properties.Resources.ProfilesPage_Yes}"); + + await result; // sync call + + switch (result.Result) + { + case ContentDialogResult.Primary: + { + // do not overwrite currentTemplate and currentTemplate.Layout as a whole + // because they both have important Update notifitications set + var newLayout = layoutTemplate.Layout.Clone() as Layout; + currentTemplate.Layout.AxisLayout = newLayout.AxisLayout; + currentTemplate.Layout.ButtonLayout = newLayout.ButtonLayout; + currentTemplate.Layout.GyroLayout = newLayout.GyroLayout; + + currentTemplate.Name = layoutTemplate.Name; + currentTemplate.Description = layoutTemplate.Description; + currentTemplate.Guid = layoutTemplate.Guid; // not needed + + // the whole layout has been updated without notification, trigger one + currentTemplate.Layout.UpdateLayout(); + + UpdatePages(); + } + break; + } + } + + private void cB_Layouts_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + ButtonApplyLayout.IsEnabled = cB_Layouts.SelectedIndex != -1; + } + + private void CheckBoxDeviceLayouts_Checked(object sender, RoutedEventArgs e) + { + if (!IsLoaded) + return; + + SettingsManager.SetProperty("LayoutFilterOnDevice", CheckBoxDeviceLayouts.IsChecked); + } + + private void LayoutExportButton_Click(object sender, RoutedEventArgs e) + { + LayoutTemplate newLayout = new() + { + Layout = currentTemplate.Layout, + Name = LayoutTitle.Text, + Description = LayoutDescription.Text, + Author = LayoutAuthor.Text, + Executable = SaveGameInfo.IsChecked == true ? currentTemplate.Executable : "", + Product = SaveGameInfo.IsChecked == true ? currentTemplate.Product : "", + IsInternal = false + }; + + if (newLayout.Name == string.Empty) + { + LayoutFlyout.Hide(); + _ = Dialog.ShowAsync("Layout template name cannot be empty", + "Layout was not exported.", + ContentDialogButton.Primary, null, $"{Properties.Resources.ProfilesPage_OK}"); + return; + } + + if (ExportForCurrent.IsChecked == true) + newLayout.ControllerType = ControllerManager.GetTargetController()?.GetType(); + + LayoutManager.SerializeLayoutTemplate(newLayout); + + LayoutFlyout.Hide(); + _ = Dialog.ShowAsync("Layout template exported", + $"{newLayout.Name} was exported.", + ContentDialogButton.Primary, null, $"{Properties.Resources.ProfilesPage_OK}"); + } + + private void Flyout_Opening(object sender, object e) + { + if (currentTemplate.Executable == string.Empty && currentTemplate.Product == string.Empty) + SaveGameInfo.IsChecked = SaveGameInfo.IsEnabled = false; + else + SaveGameInfo.IsChecked = SaveGameInfo.IsEnabled = true; + + var separator = currentTemplate.Name.Length > 0 && currentTemplate.Product.Length > 0 ? " - " : ""; + + LayoutTitle.Text = $"{currentTemplate.Name}{separator}{currentTemplate.Product}"; + LayoutDescription.Text = currentTemplate.Description; + LayoutAuthor.Text = currentTemplate.Author; + } + + private void SaveGameInfo_Toggled(object sender, RoutedEventArgs e) + { + if (SaveGameInfo.IsChecked == true) + { + var separator = currentTemplate.Name.Length > 0 && currentTemplate.Product.Length > 0 ? " - " : ""; + LayoutTitle.Text = $"{currentTemplate.Name}{separator}{currentTemplate.Product}"; + } + else + { + LayoutTitle.Text = $"{currentTemplate.Name}"; + } + } + + private void LayoutCancelButton_Click(object sender, RoutedEventArgs e) + { + LayoutFlyout.Hide(); + } + + private void navView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) + { + if (args.InvokedItemContainer is null) + return; + + NavigationViewItem navItem = (NavigationViewItem)args.InvokedItemContainer; + preNavItemTag = (string)navItem.Tag; + NavView_Navigate(preNavItemTag); + } + + public void NavView_Navigate(string navItemTag) + { + var item = pages.FirstOrDefault(p => p.Key.Equals(navItemTag)); + Page _page = item.Value.Item1; + + // Get the page type before navigation so you can prevent duplicate + // entries in the backstack. + var preNavPageType = ContentFrame.CurrentSourcePageType; + + // Only navigate if the selected page isn't currently loaded. + if (!(_page is null) && !Equals(preNavPageType, _page)) NavView_Navigate(_page); + } + + public void NavView_Navigate(Page _page) + { + ContentFrame.Navigate(_page); + } + + private void navView_Loaded(object sender, RoutedEventArgs e) + { + // Add handler for ContentFrame navigation. + ContentFrame.Navigated += On_Navigated; + + // NavView doesn't load any page by default, so load home page. + navView.SelectedItem = navView.MenuItems[0]; + + // If navigation occurs on SelectionChanged, this isn't needed. + // Because we use ItemInvoked to navigate, we need to call Navigate + // here to load the home page. + preNavItemTag = "ButtonsPage"; + NavView_Navigate(preNavItemTag); + } + + private void On_Navigated(object sender, NavigationEventArgs e) + { + navView.IsBackEnabled = ContentFrame.CanGoBack; + + if (ContentFrame.SourcePageType is not null) + { + var preNavPageType = ContentFrame.CurrentSourcePageType; + var preNavPageName = preNavPageType.Name; + + var NavViewItem = navView.MenuItems + .OfType<NavigationViewItem>().FirstOrDefault(n => n.Tag.Equals(preNavPageName)); + + if (!(NavViewItem is null)) + navView.SelectedItem = NavViewItem; + + string header = currentTemplate.Product.Length > 0 ? + "Profile: " + currentTemplate.Product : "Layout: Desktop"; + parentNavView.Header = new TextBlock() { Text = header }; + } + } + + private void CheckBoxDefaultLayout_Checked(object sender, RoutedEventArgs e) + { + var isDefaultLayout = (bool)CheckBoxDefaultLayout.IsChecked; + var prevDefaultLayoutProfile = ProfileManager.GetProfileWithDefaultLayout(); + + currentTemplate.Layout.IsDefaultLayout = isDefaultLayout; + currentTemplate.Layout.UpdateLayout(); + + // If option is enabled and a different default layout profile exists, we want to set option false on prev profile. + if (isDefaultLayout && prevDefaultLayoutProfile != null && prevDefaultLayoutProfile.Layout != currentTemplate.Layout) + { + prevDefaultLayoutProfile.Layout.IsDefaultLayout = false; + ProfileManager.UpdateOrCreateProfile(prevDefaultLayoutProfile); + } + } } \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/OverlayPage.xaml b/HandheldCompanion/Views/Pages/OverlayPage.xaml index 74be973ce..042ea4c40 100644 --- a/HandheldCompanion/Views/Pages/OverlayPage.xaml +++ b/HandheldCompanion/Views/Pages/OverlayPage.xaml @@ -1,865 +1,868 @@ -<Page - x:Class="HandheldCompanion.Views.Pages.OverlayPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Name="Overlay" - Title="{x:Static resx:Resources.OverlayPage_Overlay}" - d:Background="White" - d:DesignHeight="1800" - d:DesignWidth="1000" - KeepAlive="True" - Loaded="Page_Loaded" - mc:Ignorable="d"> - - <Grid Name="MainGrid" Margin="20"> - <ui:SimpleStackPanel Spacing="24"> - - <!-- OSD options --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="On-screen display options" /> - - <!-- On-screen display level --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevelDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="OnScreenDisplayLevel" - Grid.Column="1" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - SelectionChanged="OnScreenDisplayLevel_SelectionChanged"> - - <Label Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Disabled}" /> - <Label Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Minimal}" /> - <Label Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Extended}" /> - <Label Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Full}" /> - <Label Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_External}" /> - </ComboBox> - </Grid> - </Border> - - <!-- On-screen display update rate --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_OverlayDisplayUpdateRate}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_OverlayDisplayUpdateRateDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - x:Name="HelperOnScreenUpdateRate" - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, ElementName=SliderOnScreenUpdateRate, Mode=OneWay}" - TextAlignment="Center" /> - <TextBlock VerticalAlignment="Center" Text="{x:Static resx:Resources.OverlayPage_Millisecond}" /> - <Slider - x:Name="SliderOnScreenUpdateRate" - Margin="6,0,0,0" - VerticalAlignment="Center" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="200" - Maximum="1000" - Minimum="100" - SmallChange="100" - Style="{DynamicResource SliderStyle1}" - TickFrequency="100" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="SliderOnScreenUpdateRate_ValueChanged" /> - </DockPanel> - </Grid> - </Border> - - </ui:SimpleStackPanel> - - <!-- Controller options --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_ControllerOptions}" /> - - <!-- Controller model --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_OverlayModel}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_OverlayModelDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="OverlayModel" - Grid.Column="1" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - SelectionChanged="OverlayModel_SelectionChanged"> - <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_OEMController}" /> - <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_EmulatedController}" /> - <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_XboxOneController}" /> - <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_ZDOPlusController}" /> - <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_8BitDoLite2Controller}" /> - <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_MachenikeHG510Controller}" /> - <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_ToyController}" /> - <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_N64Controller}" /> - <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_DualSenseController}" /> - </ComboBox> - </Grid> - </Border> - - <!-- Controller Alignment --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Alignment}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_AlignmentDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <Grid Grid.Column="1" HorizontalAlignment="Left"> - <ui:SimpleStackPanel - Name="OverlayControllerAlignment" - HorizontalAlignment="Right" - Spacing="6"> - <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> - <Button - Width="50" - Height="50" - Click="ControllerAlignment_Click" - Tag="0" /> - <Button - Width="50" - Height="50" - Click="ControllerAlignment_Click" - Tag="1" /> - <Button - Width="50" - Height="50" - Click="ControllerAlignment_Click" - Tag="2" /> - </ui:SimpleStackPanel> - <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> - <Button - Width="50" - Height="50" - Click="ControllerAlignment_Click" - Tag="3" /> - <Button - Width="50" - Height="50" - Click="ControllerAlignment_Click" - Tag="4" /> - <Button - Width="50" - Height="50" - Click="ControllerAlignment_Click" - Tag="5" /> - </ui:SimpleStackPanel> - <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> - <Button - Width="50" - Height="50" - Click="ControllerAlignment_Click" - Tag="6" /> - <Button - Width="50" - Height="50" - Click="ControllerAlignment_Click" - Tag="7" /> - <Button - Width="50" - Height="50" - Click="ControllerAlignment_Click" - Tag="8" /> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - </Grid> - </Border> - - <!-- Size --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Size}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_SizeDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N0, ElementName=SliderControllerSize, Mode=OneWay}" - TextAlignment="Center" /> - <Slider - x:Name="SliderControllerSize" - Margin="6,0,0,0" - VerticalAlignment="Center" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="100" - Maximum="800" - Minimum="200" - SmallChange="50" - Style="{DynamicResource SliderStyle1}" - TickFrequency="50" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="SliderControllerSize_ValueChanged" /> - </DockPanel> - </Grid> - </Border> - - <!-- Opacity --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Opacity}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_OpacityControllerDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N1, ElementName=SliderControllerOpacity, Mode=OneWay}" - TextAlignment="Center" /> - <Slider - x:Name="SliderControllerOpacity" - Margin="6,0,0,0" - VerticalAlignment="Center" - AutoToolTipPrecision="1" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="0.2" - Maximum="1" - Minimum="0.1" - SmallChange="0.1" - Style="{DynamicResource SliderStyle1}" - TickFrequency="0.1" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N1, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="SliderControllerOpacity_ValueChanged" /> - </DockPanel> - </Grid> - </Border> - - <!-- Background color --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Color}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_ColorDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> - <colorpicker:PortableColorPicker - Name="ColorPicker" - ColorChanged="StandardColorPicker_ColorChanged" - Style="{StaticResource DefaultColorPickerStyle}" /> - </DockPanel> - </Grid> - </Border> - - <!-- AlwaysOnTop --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="9*" MinWidth="200" /> - <ColumnDefinition MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_AlwaysOnTop}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_AlwaysOnTopDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_AlwaysOnTop" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_AlwaysOnTop_Toggled" /> - </Grid> - </Border> - - <!-- Motion Activated --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="9*" MinWidth="200" /> - <ColumnDefinition MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_MotionActivated}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_MotionActivatedDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_MotionActivated" - Grid.Column="1" - HorizontalAlignment="Right" - IsOn="True" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_MotionActivated_Toggled" /> - </Grid> - </Border> - - <!-- Model settings --> - <Expander HorizontalAlignment="Stretch" Expanded="Expander_Expanded"> - <Expander.Header> - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_RenderSettings}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_RenderSettingsDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - </Expander.Header> - - <Expander.Content> - <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> - - <!-- Framerate --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <StackPanel VerticalAlignment="Center" Orientation="Vertical"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_RenderSettingsFramerate}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_RenderSettingsFramerateDesc}" - TextWrapping="Wrap" /> - </StackPanel> - - <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N0, ElementName=Slider_Framerate, Mode=OneWay}" - TextAlignment="Center" /> - <Slider - x:Name="Slider_Framerate" - Margin="6,0,0,0" - VerticalAlignment="Center" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="10" - Maximum="60" - Minimum="10" - SmallChange="5" - Style="{DynamicResource SliderStyle1}" - TickFrequency="5" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Slider_Framerate_ValueChanged" /> - </DockPanel> - </Grid> - - <!-- Separator --> - <Separator - Margin="-46,0,-16,0" - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" /> - - <!-- Anti-Aliasing --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="9*" MinWidth="200" /> - <ColumnDefinition MinWidth="200" /> - </Grid.ColumnDefinitions> - - <StackPanel VerticalAlignment="Center" Orientation="Vertical"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_RenderSettingsAntialiasing}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_RenderSettingsAntialiasingDesc}" - TextWrapping="Wrap" /> - </StackPanel> - - <ui:ToggleSwitch - Name="Toggle_RenderAA" - Grid.Column="1" - HorizontalAlignment="Right" - IsOn="False" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_RenderAA_Toggled" /> - </Grid> - </ui:SimpleStackPanel> - </Expander.Content> - </Expander> - - <!-- Resting angles --> - <Expander HorizontalAlignment="Stretch" Expanded="Expander_Expanded"> - <Expander.Header> - <DockPanel Margin="0,12,12,12"> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_CameraAngle}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_CameraAngleDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - </Expander.Header> - - <Expander.Content> - <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> - - <!-- Face camera --> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="9*" MinWidth="200" /> - <ColumnDefinition MinWidth="200" /> - </Grid.ColumnDefinitions> - - <StackPanel VerticalAlignment="Center" Orientation="Vertical"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_FaceCamera}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_FaceCameraDesc}" - TextWrapping="Wrap" /> - </StackPanel> - - <ui:ToggleSwitch - Name="Toggle_FaceCamera" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_FaceCamera_Toggled" /> - </Grid> - - <!-- Separator --> - <Separator - Margin="-46,0,-16,0" - BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" - BorderThickness="0,1,0,0" /> - - - <!-- Resting pitch --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <StackPanel VerticalAlignment="Center" Orientation="Vertical"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_CameraAnglePitch}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_CameraAnglePitchDesc}" - TextWrapping="Wrap" /> - </StackPanel> - - <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N1, ElementName=Slider_RestingPitch, Mode=OneWay}" - TextAlignment="Center" /> - <Slider - x:Name="Slider_RestingPitch" - Margin="6,0,0,0" - VerticalAlignment="Center" - AutoToolTipPrecision="1" - IsEnabled="False" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="10" - Maximum="90" - Minimum="-90" - SmallChange="5" - Style="{DynamicResource SliderStyle1}" - TickFrequency="5" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N1, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="Slider_RestingPitch_ValueChanged" /> - </DockPanel> - </Grid> - - </ui:SimpleStackPanel> - </Expander.Content> - </Expander> - </ui:SimpleStackPanel> - - <!-- Trackpads options --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_TrackpadsOptions}" /> - - <!-- Controller Alignment --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="1*" MinWidth="70" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Alignment}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_AlignmentTrackpadDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <Grid Grid.Column="1"> - <ui:SimpleStackPanel - Name="OverlayTrackpadsAlignment" - HorizontalAlignment="Right" - Spacing="6"> - <Button - Width="50" - Height="50" - Click="TrackpadsAlignment_Click" - Tag="0" /> - <Button - Width="50" - Height="50" - Click="TrackpadsAlignment_Click" - Tag="1" /> - <Button - Width="50" - Height="50" - Click="TrackpadsAlignment_Click" - Tag="2" /> - </ui:SimpleStackPanel> - </Grid> - </Grid> - </Border> - - <!-- Size --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Size}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_SizeOverlayDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N0, ElementName=SliderTrackpadsSize, Mode=OneWay}" - TextAlignment="Center" /> - <Slider - x:Name="SliderTrackpadsSize" - Margin="6,0,0,0" - VerticalAlignment="Center" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="100" - Maximum="800" - Minimum="100" - SmallChange="50" - Style="{DynamicResource SliderStyle1}" - TickFrequency="50" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="SliderTrackpadsSize_ValueChanged" /> - </DockPanel> - </Grid> - </Border> - - <!-- Opacity --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Opacity}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.OverlayPage_OpacityTrackpadDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N2, ElementName=SliderTrackpadsOpacity, Mode=OneWay}" - TextAlignment="Center" /> - <Slider - x:Name="SliderTrackpadsOpacity" - Margin="6,0,0,0" - VerticalAlignment="Center" - AutoToolTipPrecision="1" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="0.1" - Maximum="0.25" - Minimum="0.01" - SmallChange="0.01" - Style="{DynamicResource SliderStyle1}" - TickFrequency="0.01" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N2, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="SliderTrackpadsOpacity_ValueChanged" /> - </DockPanel> - </Grid> - </Border> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> +<Page + x:Class="HandheldCompanion.Views.Pages.OverlayPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:colorpicker="clr-namespace:ColorPicker;assembly=ColorPicker" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" +<<<<<<< HEAD + Name="Overlay" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Title="{x:Static resx:Resources.OverlayPage_Overlay}" + d:Background="White" + d:DesignHeight="1800" + d:DesignWidth="1000" + KeepAlive="True" + Loaded="Page_Loaded" + mc:Ignorable="d"> + + <Grid Name="MainGrid" Margin="20"> + <ui:SimpleStackPanel Spacing="24"> + + <!-- OSD options --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="On-screen display options" /> + + <!-- On-screen display level --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevelDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="OnScreenDisplayLevel" + Grid.Column="1" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + SelectionChanged="OnScreenDisplayLevel_SelectionChanged"> + + <Label Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Disabled}" /> + <Label Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Minimal}" /> + <Label Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Extended}" /> + <Label Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Full}" /> + <Label Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_External}" /> + </ComboBox> + </Grid> + </Border> + + <!-- On-screen display update rate --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_OverlayDisplayUpdateRate}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_OverlayDisplayUpdateRateDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + x:Name="HelperOnScreenUpdateRate" + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, ElementName=SliderOnScreenUpdateRate, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock VerticalAlignment="Center" Text="{x:Static resx:Resources.OverlayPage_Millisecond}" /> + <Slider + x:Name="SliderOnScreenUpdateRate" + Margin="6,0,0,0" + VerticalAlignment="Center" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="200" + Maximum="1000" + Minimum="100" + SmallChange="100" + Style="{DynamicResource SliderStyle1}" + TickFrequency="100" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="SliderOnScreenUpdateRate_ValueChanged" /> + </DockPanel> + </Grid> + </Border> + + </ui:SimpleStackPanel> + + <!-- Controller options --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_ControllerOptions}" /> + + <!-- Controller model --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_OverlayModel}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_OverlayModelDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="OverlayModel" + Grid.Column="1" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + SelectionChanged="OverlayModel_SelectionChanged"> + <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_OEMController}" /> + <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_EmulatedController}" /> + <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_XboxOneController}" /> + <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_ZDOPlusController}" /> + <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_8BitDoLite2Controller}" /> + <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_MachenikeHG510Controller}" /> + <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_ToyController}" /> + <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_N64Controller}" /> + <ComboBoxItem Content="{x:Static resx:Resources.OverlayPage_DualSenseController}" /> + </ComboBox> + </Grid> + </Border> + + <!-- Controller Alignment --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Alignment}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_AlignmentDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <Grid Grid.Column="1" HorizontalAlignment="Left"> + <ui:SimpleStackPanel + Name="OverlayControllerAlignment" + HorizontalAlignment="Right" + Spacing="6"> + <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> + <Button + Width="50" + Height="50" + Click="ControllerAlignment_Click" + Tag="0" /> + <Button + Width="50" + Height="50" + Click="ControllerAlignment_Click" + Tag="1" /> + <Button + Width="50" + Height="50" + Click="ControllerAlignment_Click" + Tag="2" /> + </ui:SimpleStackPanel> + <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> + <Button + Width="50" + Height="50" + Click="ControllerAlignment_Click" + Tag="3" /> + <Button + Width="50" + Height="50" + Click="ControllerAlignment_Click" + Tag="4" /> + <Button + Width="50" + Height="50" + Click="ControllerAlignment_Click" + Tag="5" /> + </ui:SimpleStackPanel> + <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> + <Button + Width="50" + Height="50" + Click="ControllerAlignment_Click" + Tag="6" /> + <Button + Width="50" + Height="50" + Click="ControllerAlignment_Click" + Tag="7" /> + <Button + Width="50" + Height="50" + Click="ControllerAlignment_Click" + Tag="8" /> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + </Grid> + </Border> + + <!-- Size --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Size}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_SizeDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=SliderControllerSize, Mode=OneWay}" + TextAlignment="Center" /> + <Slider + x:Name="SliderControllerSize" + Margin="6,0,0,0" + VerticalAlignment="Center" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="100" + Maximum="800" + Minimum="200" + SmallChange="50" + Style="{DynamicResource SliderStyle1}" + TickFrequency="50" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="SliderControllerSize_ValueChanged" /> + </DockPanel> + </Grid> + </Border> + + <!-- Opacity --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Opacity}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_OpacityControllerDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N1, ElementName=SliderControllerOpacity, Mode=OneWay}" + TextAlignment="Center" /> + <Slider + x:Name="SliderControllerOpacity" + Margin="6,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="1" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="0.2" + Maximum="1" + Minimum="0.1" + SmallChange="0.1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="0.1" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N1, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="SliderControllerOpacity_ValueChanged" /> + </DockPanel> + </Grid> + </Border> + + <!-- Background color --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Color}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_ColorDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> + <colorpicker:PortableColorPicker + Name="ColorPicker" + ColorChanged="StandardColorPicker_ColorChanged" + Style="{StaticResource DefaultColorPickerStyle}" /> + </DockPanel> + </Grid> + </Border> + + <!-- AlwaysOnTop --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_AlwaysOnTop}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_AlwaysOnTopDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_AlwaysOnTop" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_AlwaysOnTop_Toggled" /> + </Grid> + </Border> + + <!-- Motion Activated --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_MotionActivated}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_MotionActivatedDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_MotionActivated" + Grid.Column="1" + HorizontalAlignment="Right" + IsOn="True" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_MotionActivated_Toggled" /> + </Grid> + </Border> + + <!-- Model settings --> + <Expander HorizontalAlignment="Stretch" Expanded="Expander_Expanded"> + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_RenderSettings}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_RenderSettingsDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + + <Expander.Content> + <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> + + <!-- Framerate --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel VerticalAlignment="Center" Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_RenderSettingsFramerate}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_RenderSettingsFramerateDesc}" + TextWrapping="Wrap" /> + </StackPanel> + + <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=Slider_Framerate, Mode=OneWay}" + TextAlignment="Center" /> + <Slider + x:Name="Slider_Framerate" + Margin="6,0,0,0" + VerticalAlignment="Center" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="10" + Maximum="60" + Minimum="10" + SmallChange="5" + Style="{DynamicResource SliderStyle1}" + TickFrequency="5" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="Slider_Framerate_ValueChanged" /> + </DockPanel> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + + <!-- Anti-Aliasing --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel VerticalAlignment="Center" Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_RenderSettingsAntialiasing}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_RenderSettingsAntialiasingDesc}" + TextWrapping="Wrap" /> + </StackPanel> + + <ui:ToggleSwitch + Name="Toggle_RenderAA" + Grid.Column="1" + HorizontalAlignment="Right" + IsOn="False" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_RenderAA_Toggled" /> + </Grid> + </ui:SimpleStackPanel> + </Expander.Content> + </Expander> + + <!-- Resting angles --> + <Expander HorizontalAlignment="Stretch" Expanded="Expander_Expanded"> + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_CameraAngle}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_CameraAngleDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + + <Expander.Content> + <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> + + <!-- Face camera --> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel VerticalAlignment="Center" Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_FaceCamera}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_FaceCameraDesc}" + TextWrapping="Wrap" /> + </StackPanel> + + <ui:ToggleSwitch + Name="Toggle_FaceCamera" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_FaceCamera_Toggled" /> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + + + <!-- Resting pitch --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel VerticalAlignment="Center" Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_CameraAnglePitch}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_CameraAnglePitchDesc}" + TextWrapping="Wrap" /> + </StackPanel> + + <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N1, ElementName=Slider_RestingPitch, Mode=OneWay}" + TextAlignment="Center" /> + <Slider + x:Name="Slider_RestingPitch" + Margin="6,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="1" + IsEnabled="False" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="10" + Maximum="90" + Minimum="-90" + SmallChange="5" + Style="{DynamicResource SliderStyle1}" + TickFrequency="5" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N1, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="Slider_RestingPitch_ValueChanged" /> + </DockPanel> + </Grid> + + </ui:SimpleStackPanel> + </Expander.Content> + </Expander> + </ui:SimpleStackPanel> + + <!-- Trackpads options --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_TrackpadsOptions}" /> + + <!-- Controller Alignment --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="1*" MinWidth="70" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Alignment}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_AlignmentTrackpadDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <Grid Grid.Column="1"> + <ui:SimpleStackPanel + Name="OverlayTrackpadsAlignment" + HorizontalAlignment="Right" + Spacing="6"> + <Button + Width="50" + Height="50" + Click="TrackpadsAlignment_Click" + Tag="0" /> + <Button + Width="50" + Height="50" + Click="TrackpadsAlignment_Click" + Tag="1" /> + <Button + Width="50" + Height="50" + Click="TrackpadsAlignment_Click" + Tag="2" /> + </ui:SimpleStackPanel> + </Grid> + </Grid> + </Border> + + <!-- Size --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Size}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_SizeOverlayDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=SliderTrackpadsSize, Mode=OneWay}" + TextAlignment="Center" /> + <Slider + x:Name="SliderTrackpadsSize" + Margin="6,0,0,0" + VerticalAlignment="Center" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="100" + Maximum="800" + Minimum="100" + SmallChange="50" + Style="{DynamicResource SliderStyle1}" + TickFrequency="50" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="SliderTrackpadsSize_ValueChanged" /> + </DockPanel> + </Grid> + </Border> + + <!-- Opacity --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.OverlayPage_Opacity}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.OverlayPage_OpacityTrackpadDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <DockPanel Grid.Column="1" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N2, ElementName=SliderTrackpadsOpacity, Mode=OneWay}" + TextAlignment="Center" /> + <Slider + x:Name="SliderTrackpadsOpacity" + Margin="6,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="1" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="0.1" + Maximum="0.25" + Minimum="0.01" + SmallChange="0.01" + Style="{DynamicResource SliderStyle1}" + TickFrequency="0.01" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N2, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="SliderTrackpadsOpacity_ValueChanged" /> + </DockPanel> + </Grid> + </Border> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> </Page> \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/Profiles/SettingsMode0.xaml.cs b/HandheldCompanion/Views/Pages/Profiles/SettingsMode0.xaml.cs index 185c207d1..ad55ed2a6 100644 --- a/HandheldCompanion/Views/Pages/Profiles/SettingsMode0.xaml.cs +++ b/HandheldCompanion/Views/Pages/Profiles/SettingsMode0.xaml.cs @@ -1,303 +1,347 @@ -using HandheldCompanion.Inputs; -using HandheldCompanion.Managers; -using HandheldCompanion.Sensors; -using HandheldCompanion.Utils; -using System; -using System.Numerics; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Input; -using System.Windows.Media; - -namespace HandheldCompanion.Views.Pages.Profiles; - -/// <summary> -/// Interaction logic for SettingsMode0.xaml -/// </summary> -public partial class SettingsMode0 : Page -{ - private Hotkey ProfilesPageHotkey; - private LockObject updateLock = new(); - - public SettingsMode0() - { - InitializeComponent(); - } - - public SettingsMode0(string Tag) : this() - { - this.Tag = Tag; - - MotionManager.SettingsMode0Update += MotionManager_SettingsMode0Update; - - HotkeysManager.HotkeyCreated += TriggerCreated; - InputsManager.TriggerUpdated += TriggerUpdated; - } - - public void SetProfile() - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - using (new ScopedLock(updateLock)) - { - SliderSensitivityX.Value = ProfilesPage.selectedProfile.MotionSensivityX; - SliderSensitivityY.Value = ProfilesPage.selectedProfile.MotionSensivityY; - tb_ProfileAimingDownSightsMultiplier.Value = ProfilesPage.selectedProfile.AimingSightsMultiplier; - Toggle_FlickStick.IsOn = ProfilesPage.selectedProfile.FlickstickEnabled; - tb_ProfileFlickDuration.Value = ProfilesPage.selectedProfile.FlickstickDuration * 1000; - tb_ProfileStickSensitivity.Value = ProfilesPage.selectedProfile.FlickstickSensivity; - - // todo: improve me ? - ProfilesPageHotkey.inputsChord.State = ProfilesPage.selectedProfile.AimingSightsTrigger.Clone() as ButtonState; - ProfilesPageHotkey.DrawInput(); - - // temp - StackCurve.Children.Clear(); - foreach (var elem in ProfilesPage.selectedProfile.MotionSensivityArray) - { - // skip first item ? - if (elem.Key == 0) - continue; - - var height = elem.Value * StackCurve.Height; - var thumb = new Thumb - { - Tag = elem.Key, - Width = 8, - MaxHeight = StackCurve.Height, - Height = height, - VerticalAlignment = VerticalAlignment.Bottom, - Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentLowBrush"], - BorderThickness = new Thickness(0), - BorderBrush = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentHighBrush"], - IsEnabled = false // prevent the control from being clickable - }; - - StackCurve.Children.Add(thumb); - } - } - }); - } - - private void Page_Loaded(object sender, RoutedEventArgs e) - { - } - - public void Page_Closed() - { - } - - private void MotionManager_SettingsMode0Update(Vector3 gyrometer) - { - Highlight_Thumb(Math.Max(Math.Max(Math.Abs(gyrometer.Z), Math.Abs(gyrometer.X)), Math.Abs(gyrometer.Y))); - } - - private void SliderSensitivityX_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (ProfilesPage.selectedProfile is null) - return; - - if (updateLock) - return; - - ProfilesPage.selectedProfile.MotionSensivityX = (float)SliderSensitivityX.Value; - ProfilesPage.UpdateProfile(); - } - - private void SliderSensitivityY_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (ProfilesPage.selectedProfile is null) - return; - - if (updateLock) - return; - - ProfilesPage.selectedProfile.MotionSensivityY = (float)SliderSensitivityY.Value; - ProfilesPage.UpdateProfile(); - } - - private void Highlight_Thumb(float value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - double dist_x = value / IMUGyrometer.sensorSpec.maxIn; - - foreach (Control control in StackCurve.Children) - { - var x = (double)control.Tag; - - if (dist_x > x) - control.BorderThickness = new Thickness(0, 0, 0, 20); - else - control.BorderThickness = new Thickness(0); - } - }); - } - - private void StackCurve_MouseDown(object sender, MouseButtonEventArgs e) - { - StackCurve_MouseMove(sender, e); - } - - private void StackCurve_MouseMove(object sender, MouseEventArgs e) - { - if (ProfilesPage.selectedProfile is null) - return; - - Control Thumb = null; - - foreach (Control control in StackCurve.Children) - { - var position = e.GetPosition(control); - var dist_x = Math.Abs(position.X); - - control.Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentLowBrush"]; - - if (dist_x <= control.Width) - Thumb = control; - } - - if (Thumb is null) - return; - - Thumb.Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentHighBrush"]; - - if (e.LeftButton == MouseButtonState.Pressed) - { - var x = (double)Thumb.Tag; - Thumb.Height = StackCurve.ActualHeight - e.GetPosition(StackCurve).Y; - ProfilesPage.selectedProfile.MotionSensivityArray[x] = Thumb.Height / StackCurve.Height; - ProfilesPage.UpdateProfile(); - } - } - - private void Button_Click(object sender, RoutedEventArgs e) - { - // default preset - foreach (Control Thumb in StackCurve.Children) - { - var x = (double)Thumb.Tag; - Thumb.Height = StackCurve.Height / 2.0f; - ProfilesPage.selectedProfile.MotionSensivityArray[x] = Thumb.Height / StackCurve.Height; - ProfilesPage.UpdateProfile(); - } - } - - private void Button_Click_1(object sender, RoutedEventArgs e) - { - // agressive preset - var tempx = 24f / Profile.SensivityArraySize; - foreach (Control Thumb in StackCurve.Children) - { - var x = (double)Thumb.Tag; - var value = (float)(-Math.Sqrt(x * tempx) + 0.85f); - - Thumb.Height = StackCurve.Height * value; - ProfilesPage.selectedProfile.MotionSensivityArray[x] = Thumb.Height / StackCurve.Height; - ProfilesPage.UpdateProfile(); - } - } - - private void Button_Click_2(object sender, RoutedEventArgs e) - { - // precise preset - var tempx = 12f / Profile.SensivityArraySize; - foreach (Control Thumb in StackCurve.Children) - { - var x = (double)Thumb.Tag; - var value = (float)(Math.Sqrt(x * tempx) + 0.25f - tempx * x); - - Thumb.Height = StackCurve.Height * value; - ProfilesPage.selectedProfile.MotionSensivityArray[x] = Thumb.Height / StackCurve.Height; - ProfilesPage.UpdateProfile(); - } - } - - private void Expander_Expanded(object sender, RoutedEventArgs e) - { - ((Expander)sender).BringIntoView(); - } - - private void SliderAimingDownSightsMultiplier_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (ProfilesPage.selectedProfile is null) - return; - - if (updateLock) - return; - - ProfilesPage.selectedProfile.AimingSightsMultiplier = (float)tb_ProfileAimingDownSightsMultiplier.Value; - ProfilesPage.UpdateProfile(); - } - - private void Toggle_FlickStick_Toggled(object sender, RoutedEventArgs e) - { - if (ProfilesPage.selectedProfile is null) - return; - - if (updateLock) - return; - - ProfilesPage.selectedProfile.FlickstickEnabled = Toggle_FlickStick.IsOn; - ProfilesPage.UpdateProfile(); - } - - private void SliderFlickDuration_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (ProfilesPage.selectedProfile is null) - return; - - if (updateLock) - return; - - ProfilesPage.selectedProfile.FlickstickDuration = (float)tb_ProfileFlickDuration.Value / 1000; - ProfilesPage.UpdateProfile(); - } - - private void SliderStickSensivity_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (ProfilesPage.selectedProfile is null) - return; - - if (updateLock) - return; - - ProfilesPage.selectedProfile.FlickstickSensivity = (float)tb_ProfileStickSensitivity.Value; - ProfilesPage.UpdateProfile(); - } - - private void TriggerCreated(Hotkey hotkey) - { - switch (hotkey.inputsHotkey.Listener) - { - case "shortcutProfilesSettingsMode0": - { - // pull hotkey - ProfilesPageHotkey = hotkey; - - // add to UI - var hotkeyBorder = ProfilesPageHotkey.GetControl(); - if (hotkeyBorder is null || hotkeyBorder.Parent is not null) - return; - - if (UMC_Activator.Children.Count == 0) - UMC_Activator.Children.Add(hotkeyBorder); - } - break; - } - } - - private void TriggerUpdated(string listener, InputsChord inputs, InputsManager.ListenerType type) - { - switch (listener) - { - case "shortcutProfilesSettingsMode0": - ProfilesPage.selectedProfile.AimingSightsTrigger = inputs.State.Clone() as ButtonState; - ProfilesPage.UpdateProfile(); - break; - } - } +using HandheldCompanion.Inputs; +using HandheldCompanion.Managers; +using HandheldCompanion.Sensors; +using HandheldCompanion.Utils; +using System; +using System.Numerics; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Media; + +namespace HandheldCompanion.Views.Pages.Profiles; + +/// <summary> +/// Interaction logic for SettingsMode0.xaml +/// </summary> +public partial class SettingsMode0 : Page +{ + private Hotkey ProfilesPageHotkey; + private LockObject updateLock = new(); + + public SettingsMode0() + { + InitializeComponent(); + } + + public SettingsMode0(string Tag) : this() + { + this.Tag = Tag; + + MotionManager.SettingsMode0Update += MotionManager_SettingsMode0Update; + + HotkeysManager.HotkeyCreated += TriggerCreated; + InputsManager.TriggerUpdated += TriggerUpdated; + } + + public void SetProfile() + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + using (new ScopedLock(updateLock)) + { + SliderSensitivityX.Value = ProfilesPage.selectedProfile.MotionSensivityX; + SliderSensitivityY.Value = ProfilesPage.selectedProfile.MotionSensivityY; + tb_ProfileAimingDownSightsMultiplier.Value = ProfilesPage.selectedProfile.AimingSightsMultiplier; + Toggle_FlickStick.IsOn = ProfilesPage.selectedProfile.FlickstickEnabled; + tb_ProfileFlickDuration.Value = ProfilesPage.selectedProfile.FlickstickDuration * 1000; + tb_ProfileStickSensitivity.Value = ProfilesPage.selectedProfile.FlickstickSensivity; + + // todo: improve me ? + ProfilesPageHotkey.inputsChord.State = ProfilesPage.selectedProfile.AimingSightsTrigger.Clone() as ButtonState; + ProfilesPageHotkey.DrawInput(); + + // temp + StackCurve.Children.Clear(); + foreach (var elem in ProfilesPage.selectedProfile.MotionSensivityArray) + { + // skip first item ? + if (elem.Key == 0) + continue; + + var height = elem.Value * StackCurve.Height; + var thumb = new Thumb + { + Tag = elem.Key, + Width = 8, + MaxHeight = StackCurve.Height, + Height = height, + VerticalAlignment = VerticalAlignment.Bottom, + Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentLowBrush"], + BorderThickness = new Thickness(0), + BorderBrush = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentHighBrush"], + IsEnabled = false // prevent the control from being clickable + }; + + StackCurve.Children.Add(thumb); + } + } + }); + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + } + + public void Page_Closed() + { + } + + private void MotionManager_SettingsMode0Update(Vector3 gyrometer) + { + Highlight_Thumb(Math.Max(Math.Max(Math.Abs(gyrometer.Z), Math.Abs(gyrometer.X)), Math.Abs(gyrometer.Y))); + } + + private void SliderSensitivityX_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (ProfilesPage.selectedProfile is null) + return; + + if (updateLock) + return; + + ProfilesPage.selectedProfile.MotionSensivityX = (float)SliderSensitivityX.Value; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void SliderSensitivityY_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (ProfilesPage.selectedProfile is null) + return; + + if (updateLock) + return; + + ProfilesPage.selectedProfile.MotionSensivityY = (float)SliderSensitivityY.Value; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void Highlight_Thumb(float value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + double dist_x = value / IMUGyrometer.sensorSpec.maxIn; + + foreach (Control control in StackCurve.Children) + { + var x = (double)control.Tag; + + if (dist_x > x) + control.BorderThickness = new Thickness(0, 0, 0, 20); + else + control.BorderThickness = new Thickness(0); + } + }); + } + + private void StackCurve_MouseDown(object sender, MouseButtonEventArgs e) + { + StackCurve_MouseMove(sender, e); + } + + private void StackCurve_MouseMove(object sender, MouseEventArgs e) + { + if (ProfilesPage.selectedProfile is null) + return; + + Control Thumb = null; + + foreach (Control control in StackCurve.Children) + { + var position = e.GetPosition(control); + var dist_x = Math.Abs(position.X); + + control.Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentLowBrush"]; + + if (dist_x <= control.Width) + Thumb = control; + } + + if (Thumb is null) + return; + + Thumb.Background = (Brush)Application.Current.Resources["SystemControlHighlightAltListAccentHighBrush"]; + + if (e.LeftButton == MouseButtonState.Pressed) + { + var x = (double)Thumb.Tag; + Thumb.Height = StackCurve.ActualHeight - e.GetPosition(StackCurve).Y; + ProfilesPage.selectedProfile.MotionSensivityArray[x] = Thumb.Height / StackCurve.Height; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + } + + private void Button_Click(object sender, RoutedEventArgs e) + { + // default preset + foreach (Control Thumb in StackCurve.Children) + { + var x = (double)Thumb.Tag; + Thumb.Height = StackCurve.Height / 2.0f; + ProfilesPage.selectedProfile.MotionSensivityArray[x] = Thumb.Height / StackCurve.Height; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + } + + private void Button_Click_1(object sender, RoutedEventArgs e) + { + // agressive preset + var tempx = 24f / Profile.SensivityArraySize; + foreach (Control Thumb in StackCurve.Children) + { + var x = (double)Thumb.Tag; + var value = (float)(-Math.Sqrt(x * tempx) + 0.85f); + + Thumb.Height = StackCurve.Height * value; + ProfilesPage.selectedProfile.MotionSensivityArray[x] = Thumb.Height / StackCurve.Height; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + } + + private void Button_Click_2(object sender, RoutedEventArgs e) + { + // precise preset + var tempx = 12f / Profile.SensivityArraySize; + foreach (Control Thumb in StackCurve.Children) + { + var x = (double)Thumb.Tag; + var value = (float)(Math.Sqrt(x * tempx) + 0.25f - tempx * x); + + Thumb.Height = StackCurve.Height * value; + ProfilesPage.selectedProfile.MotionSensivityArray[x] = Thumb.Height / StackCurve.Height; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + } + + private void Expander_Expanded(object sender, RoutedEventArgs e) + { + ((Expander)sender).BringIntoView(); + } + + private void SliderAimingDownSightsMultiplier_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (ProfilesPage.selectedProfile is null) + return; + + if (updateLock) + return; + + ProfilesPage.selectedProfile.AimingSightsMultiplier = (float)tb_ProfileAimingDownSightsMultiplier.Value; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void Toggle_FlickStick_Toggled(object sender, RoutedEventArgs e) + { + if (ProfilesPage.selectedProfile is null) + return; + + if (updateLock) + return; + + ProfilesPage.selectedProfile.FlickstickEnabled = Toggle_FlickStick.IsOn; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void SliderFlickDuration_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (ProfilesPage.selectedProfile is null) + return; + + if (updateLock) + return; + + ProfilesPage.selectedProfile.FlickstickDuration = (float)tb_ProfileFlickDuration.Value / 1000; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void SliderStickSensivity_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (ProfilesPage.selectedProfile is null) + return; + + if (updateLock) + return; + + ProfilesPage.selectedProfile.FlickstickSensivity = (float)tb_ProfileStickSensitivity.Value; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void TriggerCreated(Hotkey hotkey) + { + switch (hotkey.inputsHotkey.Listener) + { + case "shortcutProfilesSettingsMode0": + { + // pull hotkey + ProfilesPageHotkey = hotkey; + + // add to UI + var hotkeyBorder = ProfilesPageHotkey.GetControl(); + if (hotkeyBorder is null || hotkeyBorder.Parent is not null) + return; + + if (UMC_Activator.Children.Count == 0) + UMC_Activator.Children.Add(hotkeyBorder); + } + break; + } + } + + private void TriggerUpdated(string listener, InputsChord inputs, InputsManager.ListenerType type) + { + switch (listener) + { + case "shortcutProfilesSettingsMode0": + ProfilesPage.selectedProfile.AimingSightsTrigger = inputs.State.Clone() as ButtonState; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + break; + } + } } \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/Profiles/SettingsMode1.xaml.cs b/HandheldCompanion/Views/Pages/Profiles/SettingsMode1.xaml.cs index 0aaf1f7c5..087329aea 100644 --- a/HandheldCompanion/Views/Pages/Profiles/SettingsMode1.xaml.cs +++ b/HandheldCompanion/Views/Pages/Profiles/SettingsMode1.xaml.cs @@ -1,125 +1,137 @@ -using HandheldCompanion.Managers; -using HandheldCompanion.Utils; -using LiveCharts; -using LiveCharts.Defaults; -using System; -using System.Numerics; -using System.Windows; -using System.Windows.Controls; - -namespace HandheldCompanion.Views.Pages.Profiles; - -/// <summary> -/// Interaction logic for SettingsMode1.xaml -/// </summary> -public partial class SettingsMode1 : Page -{ - private LockObject updateLock = new(); - - private readonly int SteeringArraySize = 30; - private readonly ChartValues<ObservablePoint> SteeringLinearityPoints; - - public SettingsMode1() - { - InitializeComponent(); - } - - public SettingsMode1(string Tag) : this() - { - this.Tag = Tag; - - lvCartesianChart.DataTooltip = null; - - MotionManager.SettingsMode1Update += MotionManager_SettingsMode1Update; - - SteeringLinearityPoints = new ChartValues<ObservablePoint>(); - for (var i = 0; i < SteeringArraySize; i++) - { - var value = i / (double)(SteeringArraySize - 1); - SteeringLinearityPoints.Add(new ObservablePoint { X = value, Y = value }); - } - - lvLineSeriesDefault.Values = new ChartValues<double> { 0, 1 }; - } - - public void SetProfile() - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - using (new ScopedLock(updateLock)) - { - SliderDeadzoneAngle.Value = ProfilesPage.selectedProfile.SteeringDeadzone; - SliderPower.Value = ProfilesPage.selectedProfile.SteeringPower; - SliderSteeringAngle.Value = ProfilesPage.selectedProfile.SteeringMaxAngle; - - lvLineSeriesValues.Values = GeneratePoints(ProfilesPage.selectedProfile.SteeringPower); - } - }); - } - - private void Page_Loaded(object sender, RoutedEventArgs e) - { - } - - public void Page_Closed() - { - } - - private void MotionManager_SettingsMode1Update(Vector2 deviceAngle) - { - Rotate_Needle(-deviceAngle.Y); - } - - private void Rotate_Needle(float y) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => { lvAngularGauge.Value = y; }); - } - - private void SliderSteeringAngle_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (ProfilesPage.selectedProfile is null) - return; - - if (updateLock) - return; - - ProfilesPage.selectedProfile.SteeringMaxAngle = (float)SliderSteeringAngle.Value; - ProfilesPage.UpdateProfile(); - } - - private void SliderPower_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (ProfilesPage.selectedProfile is null) - return; - - if (updateLock) - return; - - lvLineSeriesValues.Values = GeneratePoints(SliderPower.Value); - - ProfilesPage.selectedProfile.SteeringPower = (float)SliderPower.Value; - ProfilesPage.UpdateProfile(); - } - - private void SliderDeadzoneAngle_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (ProfilesPage.selectedProfile is null) - return; - - if (updateLock) - return; - - ProfilesPage.selectedProfile.SteeringDeadzone = (float)SliderDeadzoneAngle.Value; - ProfilesPage.UpdateProfile(); - } - - private ChartValues<ObservablePoint> GeneratePoints(double Power) - { - for (var i = 0; i < SteeringArraySize; i++) - SteeringLinearityPoints[i].Y = (float)Math.Pow(SteeringLinearityPoints[i].X, Power); - - return SteeringLinearityPoints; - } +using HandheldCompanion.Managers; +using HandheldCompanion.Utils; +using LiveCharts; +using LiveCharts.Defaults; +using System; +using System.Numerics; +using System.Windows; +using System.Windows.Controls; + +namespace HandheldCompanion.Views.Pages.Profiles; + +/// <summary> +/// Interaction logic for SettingsMode1.xaml +/// </summary> +public partial class SettingsMode1 : Page +{ + private LockObject updateLock = new(); + + private readonly int SteeringArraySize = 30; + private readonly ChartValues<ObservablePoint> SteeringLinearityPoints; + + public SettingsMode1() + { + InitializeComponent(); + } + + public SettingsMode1(string Tag) : this() + { + this.Tag = Tag; + + lvCartesianChart.DataTooltip = null; + + MotionManager.SettingsMode1Update += MotionManager_SettingsMode1Update; + + SteeringLinearityPoints = new ChartValues<ObservablePoint>(); + for (var i = 0; i < SteeringArraySize; i++) + { + var value = i / (double)(SteeringArraySize - 1); + SteeringLinearityPoints.Add(new ObservablePoint { X = value, Y = value }); + } + + lvLineSeriesDefault.Values = new ChartValues<double> { 0, 1 }; + } + + public void SetProfile() + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + using (new ScopedLock(updateLock)) + { + SliderDeadzoneAngle.Value = ProfilesPage.selectedProfile.SteeringDeadzone; + SliderPower.Value = ProfilesPage.selectedProfile.SteeringPower; + SliderSteeringAngle.Value = ProfilesPage.selectedProfile.SteeringMaxAngle; + + lvLineSeriesValues.Values = GeneratePoints(ProfilesPage.selectedProfile.SteeringPower); + } + }); + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + } + + public void Page_Closed() + { + } + + private void MotionManager_SettingsMode1Update(Vector2 deviceAngle) + { + Rotate_Needle(-deviceAngle.Y); + } + + private void Rotate_Needle(float y) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => { lvAngularGauge.Value = y; }); + } + + private void SliderSteeringAngle_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (ProfilesPage.selectedProfile is null) + return; + + if (updateLock) + return; + + ProfilesPage.selectedProfile.SteeringMaxAngle = (float)SliderSteeringAngle.Value; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void SliderPower_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (ProfilesPage.selectedProfile is null) + return; + + if (updateLock) + return; + + lvLineSeriesValues.Values = GeneratePoints(SliderPower.Value); + + ProfilesPage.selectedProfile.SteeringPower = (float)SliderPower.Value; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void SliderDeadzoneAngle_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (ProfilesPage.selectedProfile is null) + return; + + if (updateLock) + return; + + ProfilesPage.selectedProfile.SteeringDeadzone = (float)SliderDeadzoneAngle.Value; +<<<<<<< HEAD + ProfilesPage.UpdateProfile(); +======= + ProfilesPage.RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private ChartValues<ObservablePoint> GeneratePoints(double Power) + { + for (var i = 0; i < SteeringArraySize; i++) + SteeringLinearityPoints[i].Y = (float)Math.Pow(SteeringLinearityPoints[i].X, Power); + + return SteeringLinearityPoints; + } } \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/ProfilesPage.xaml b/HandheldCompanion/Views/Pages/ProfilesPage.xaml index 21d34a3b0..a50f6d85d 100644 --- a/HandheldCompanion/Views/Pages/ProfilesPage.xaml +++ b/HandheldCompanion/Views/Pages/ProfilesPage.xaml @@ -6,7 +6,10 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:HandheldCompanion.Properties" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" +<<<<<<< HEAD Name="Profiles" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d Title="{x:Static resx:Resources.ProfilesPage_Profiles}" d:Background="White" d:DesignHeight="2000" @@ -275,6 +278,7 @@ <Grid> <Grid.ColumnDefinitions> +<<<<<<< HEAD <ColumnDefinition Width="5*" MinWidth="200" /> <ColumnDefinition Width="5*" MinWidth="200" /> </Grid.ColumnDefinitions> @@ -283,6 +287,24 @@ VerticalAlignment="Center" Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" Text="{x:Static resx:Resources.ProfilesPage_Wrapper}" /> +======= + <ColumnDefinition Width="1*" MinWidth="250" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Glyph="" /> + <TextBlock + Margin="12,0,0,0" + VerticalAlignment="Center" + Foreground="{DynamicResource SystemControlForegroundBaseHighBrush}" + Text="{x:Static resx:Resources.ProfilesPage_Wrapper}" /> + </DockPanel> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <DockPanel Grid.Column="1" Margin="12,0,0,0"> <ComboBox @@ -392,7 +414,10 @@ Style="{DynamicResource SliderStyle1}" TickFrequency="5" TickPlacement="BottomRight" +<<<<<<< HEAD ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d ValueChanged="RSRSlider_ValueChanged" /> </DockPanel> </StackPanel> @@ -491,6 +516,396 @@ </StackPanel> </Grid> +<<<<<<< HEAD +======= + </ui:SimpleStackPanel> + </Expander.Content> + </Expander> + + <!-- Power settings --> + <Expander + Name="PowerSettings" + HorizontalAlignment="Stretch" + Expanded="Expander_Expanded"> + <Expander.Header> + <DockPanel Margin="0,12,12,12"> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_PowerSettings}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_PowerSettingsDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Expander.Header> + + <Expander.Content> + <ui:SimpleStackPanel Margin="30,0,0,0" Spacing="12"> + + <!-- Maximum CPU Count --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Maximum CPU Count" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="Controls CPU unparked core count limit" + TextWrapping="Wrap" /> + </StackPanel> + + <ui:ToggleSwitch + Name="CPUCoreToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="CPUCoreToggle_Toggled" /> + </Grid> + + <!-- Content --> + <Grid IsEnabled="{Binding ElementName=CPUCoreToggle, Path=IsOn}"> + + <StackPanel> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="Cores" /> + + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=CPUCoreSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="C" /> + <Slider + x:Name="CPUCoreSlider" + Margin="6,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Minimum="2" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + TickPlacement="BottomRight" + ValueChanged="CPUCoreSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + + <!-- Thermal Power (TDP) limit --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_TDPOverride}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_TDPOverrideDesc}" + TextWrapping="Wrap" /> + </StackPanel> + + <ui:ToggleSwitch + Name="TDPToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="TDPToggle_Toggled" /> + </Grid> + + <!-- Content --> + <Grid IsEnabled="{Binding ElementName=TDPToggle, Path=IsOn}"> + + <StackPanel> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_PowerLimitTarget}" /> + + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=TDPSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_TDPUnitWatt}" /> + <Slider + x:Name="TDPSlider" + Margin="6,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="5" + Maximum="30" + Minimum="5" + SmallChange="1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + TickPlacement="BottomRight" + ValueChanged="TDPSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + + <!-- Auto TDP --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_AutoTDP}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_AutoTDPDesc}" + TextWrapping="Wrap" /> + </StackPanel> + + <ui:ToggleSwitch + Name="AutoTDPToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="AutoTDPToggle_Toggled" /> + </Grid> + + <!-- Auto TDP FPS Slider --> + <Grid IsEnabled="{Binding ElementName=AutoTDPToggle, Path=IsOn}"> + + <StackPanel> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_AutoTDPFPS}" /> + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=AutoTDPSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_AutoTDPUnitFPS}" /> + <Slider + x:Name="AutoTDPSlider" + Margin="6,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="10" + Maximum="120" + Minimum="20" + SmallChange="1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + TickPlacement="BottomRight" + ValueChanged="AutoTDPSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + + <!-- EPP --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_EPP}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_EPPDesc}" + TextWrapping="Wrap" /> + </StackPanel> + + <ui:ToggleSwitch + Name="EPPToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="EPPToggle_Toggled" /> + </Grid> + + <!-- EPP Slider --> + <Grid IsEnabled="{Binding ElementName=EPPToggle, Path=IsOn}"> + + <StackPanel> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_EPPBalance}" /> + + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=EPPSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="%" /> + + <ui:SimpleStackPanel + Margin="0,0,0,0" + VerticalAlignment="Center" + ScrollViewer.PanningMode="HorizontalOnly"> + <Slider + x:Name="EPPSlider" + Margin="6,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="10" + Maximum="100" + Minimum="0" + SmallChange="1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="10" + TickPlacement="BottomRight" + ValueChanged="EPPSlider_ValueChanged" /> + + <Grid Name="EPPGrid"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + + <TextBlock + Grid.Column="1" + HorizontalAlignment="Left" + Text="{x:Static resx:Resources.ProfilesPage_CPU}" /> + <TextBlock + Grid.Column="2" + HorizontalAlignment="Right" + Text="{x:Static resx:Resources.ProfilesPage_GPU}" /> + </Grid> + </ui:SimpleStackPanel> + </DockPanel> + </StackPanel> + </Grid> + + <!-- Separator --> + <Separator + Margin="-46,0,-16,0" + BorderBrush="{DynamicResource SystemControlBackgroundChromeMediumBrush}" + BorderThickness="0,1,0,0" /> + + <!-- GPU --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_GPUControl}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.QuickPerformancePage_GPUControlDesc}" + TextWrapping="Wrap" /> + </StackPanel> + + <ui:ToggleSwitch + Name="GPUToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="GPUToggle_Toggled" /> + </Grid> + + <!-- GPU Mhz Slider --> + <Grid IsEnabled="{Binding ElementName=GPUToggle, Path=IsOn}"> + + <StackPanel> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_GPUMhz}" /> + + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=GPUSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_GPUUnit}" /> + <Slider + x:Name="GPUSlider" + Margin="6,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="500" + SmallChange="100" + Style="{DynamicResource SliderStyle1}" + TickFrequency="100" + TickPlacement="BottomRight" + ValueChanged="GPUSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </Grid> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </ui:SimpleStackPanel> </Expander.Content> </Expander> diff --git a/HandheldCompanion/Views/Pages/ProfilesPage.xaml.cs b/HandheldCompanion/Views/Pages/ProfilesPage.xaml.cs index 1d2b1b130..8717c3c89 100644 --- a/HandheldCompanion/Views/Pages/ProfilesPage.xaml.cs +++ b/HandheldCompanion/Views/Pages/ProfilesPage.xaml.cs @@ -1,919 +1,1269 @@ -using HandheldCompanion.Actions; -using HandheldCompanion.Controls; -using HandheldCompanion.Inputs; -using HandheldCompanion.Managers; -using HandheldCompanion.Managers.Desktop; -using HandheldCompanion.Misc; -using HandheldCompanion.Utils; -using HandheldCompanion.Views.Pages.Profiles; -using Inkore.UI.WPF.Modern.Controls; -using Microsoft.Win32; -using System; -using System.ComponentModel; -using System.IO; -using System.Timers; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; -using System.Xml; -using static HandheldCompanion.Utils.XInputPlusUtils; -using Page = System.Windows.Controls.Page; - -namespace HandheldCompanion.Views.Pages; - -/// <summary> -/// Interaction logic for Profiles.xaml -/// </summary> -public partial class ProfilesPage : Page -{ - // when set on start cannot be null anymore - public static Profile selectedProfile; - - private readonly SettingsMode0 page0 = new("SettingsMode0"); - private readonly SettingsMode1 page1 = new("SettingsMode1"); - - private LockObject updateLock = new(); - - private const int UpdateInterval = 500; - private static Timer UpdateTimer; - - public ProfilesPage() - { - InitializeComponent(); - - UpdateTimer = new Timer(UpdateInterval); - UpdateTimer.AutoReset = false; - UpdateTimer.Elapsed += (sender, e) => SubmitProfile(); - } - - public ProfilesPage(string Tag) : this() - { - this.Tag = Tag; - - ProfileManager.Deleted += ProfileDeleted; - ProfileManager.Updated += ProfileUpdated; - ProfileManager.Applied += ProfileApplied; - - PowerProfileManager.Updated += PowerProfileManager_Updated; - PowerProfileManager.Deleted += PowerProfileManager_Deleted; - - ProfileManager.Initialized += ProfileManagerLoaded; - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - - SystemManager.DisplaySettingsChanged += SystemManager_DisplaySettingsChanged; - SystemManager.RSRStateChanged += SystemManager_RSRStateChanged; - - // auto-sort - cB_Profiles.Items.SortDescriptions.Add(new SortDescription(string.Empty, ListSortDirection.Descending)); - } - - private void SystemManager_RSRStateChanged(int RSRState, int RSRSharpness) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (RSRState) - { - case -1: - RSRToggle.IsEnabled = false; - break; - case 0: - RSRToggle.IsEnabled = true; - RSRToggle.IsOn = false; - RSRSlider.Value = RSRSharpness; - break; - case 1: - RSRToggle.IsEnabled = true; - RSRToggle.IsOn = true; - RSRSlider.Value = RSRSharpness; - break; - } - }); - } - - public void SettingsManager_SettingValueChanged(string name, object value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (name) - { - } - }); - } - - private void SystemManager_DisplaySettingsChanged(ScreenResolution resolution) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - var screenFrequency = SystemManager.GetDesktopScreen().GetFrequency(); - - FramerateQuarter.Content = Convert.ToString(screenFrequency.GetValue(Frequency.Quarter)); - FramerateThird.Content = Convert.ToString(screenFrequency.GetValue(Frequency.Third)); - FramerateHalf.Content = Convert.ToString(screenFrequency.GetValue(Frequency.Half)); - FramerateFull.Content = Convert.ToString(screenFrequency.GetValue(Frequency.Full)); - }); - } - - private void Page_Loaded(object sender, RoutedEventArgs e) - { - } - - public void Page_Closed() - { - } - - private async void b_CreateProfile_Click(object sender, RoutedEventArgs e) - { - // if an update is pending, execute it and stop timer - if (UpdateTimer.Enabled) - UpdateTimer.Stop(); - - var openFileDialog = new OpenFileDialog() - { - Filter = "Executable|*.exe|UWP manifest|AppxManifest.xml", - }; - - if (openFileDialog.ShowDialog() == true) - try - { - string path = openFileDialog.FileName; - string folder = Path.GetDirectoryName(path); - - string file = openFileDialog.SafeFileName; - string ext = Path.GetExtension(file); - - switch (ext) - { - default: - case ".exe": - break; - case ".xml": - try - { - XmlDocument doc = new XmlDocument(); - string UWPpath = string.Empty; - string UWPfile = string.Empty; - - // check if MicrosoftGame.config exists - string configPath = Path.Combine(folder, "MicrosoftGame.config"); - if (File.Exists(configPath)) - { - doc.Load(configPath); - - XmlNodeList ExecutableList = doc.GetElementsByTagName("ExecutableList"); - foreach (XmlNode node in ExecutableList) - foreach (XmlNode child in node.ChildNodes) - if (child.Name.Equals("Executable")) - if (child.Attributes is not null) - foreach (XmlAttribute attribute in child.Attributes) - switch (attribute.Name) - { - case "Name": - UWPpath = Path.Combine(folder, attribute.InnerText); - UWPfile = Path.GetFileName(path); - break; - } - } - - // either there was no config file, either we couldn't find an executable within it - if (!File.Exists(UWPpath)) - { - doc.Load(path); - - XmlNodeList Applications = doc.GetElementsByTagName("Applications"); - foreach (XmlNode node in Applications) - foreach (XmlNode child in node.ChildNodes) - if (child.Name.Equals("Application")) - if (child.Attributes is not null) - foreach (XmlAttribute attribute in child.Attributes) - switch (attribute.Name) - { - case "Executable": - UWPpath = Path.Combine(folder, attribute.InnerText); - UWPfile = Path.GetFileName(path); - break; - } - } - - // we're good to go - if (File.Exists(UWPpath)) - { - path = UWPpath; - file = UWPfile; - } - } - catch (Exception ex) - { - LogManager.LogError(ex.Message, true); - } - - break; - } - - Profile profile = new Profile(path); - Layout toCloneLayout = ProfileManager.GetProfileWithDefaultLayout()?.Layout ?? LayoutTemplate.DefaultLayout.Layout; - profile.Layout = toCloneLayout.Clone() as Layout; - profile.LayoutTitle = LayoutTemplate.DefaultLayout.Name; - - var exists = false; - - // check on path rather than profile - if (ProfileManager.Contains(path)) - { - var result = Dialog.ShowAsync( - string.Format(Properties.Resources.ProfilesPage_AreYouSureOverwrite1, profile.Name), - string.Format(Properties.Resources.ProfilesPage_AreYouSureOverwrite2, profile.Name), - ContentDialogButton.Primary, - $"{Properties.Resources.ProfilesPage_Cancel}", - $"{Properties.Resources.ProfilesPage_Yes}"); - - await result; // sync call - - switch (result.Result) - { - case ContentDialogResult.Primary: - exists = false; - break; - default: - exists = true; - break; - } - } - - if (!exists) - ProfileManager.UpdateOrCreateProfile(profile, UpdateSource.Creation); - } - catch (Exception ex) - { - LogManager.LogError(ex.Message); - } - } - - private void b_AdditionalSettings_Click(object sender, RoutedEventArgs e) - { - if (selectedProfile is null) - return; - - if (!selectedProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) - return; - - // TODO: MOVE ME TO LAYOUT ! - switch (((GyroActions)currentAction).MotionInput) - { - default: - case MotionInput.JoystickCamera: - case MotionInput.PlayerSpace: - page0.SetProfile(); - MainWindow.NavView_Navigate(page0); - break; - case MotionInput.JoystickSteering: - page1.SetProfile(); - MainWindow.NavView_Navigate(page1); - break; - } - } - - private void PowerProfileManager_Deleted(PowerProfile powerProfile) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - int idx = -1; - foreach (var item in ProfileStack.Children) - { - if (item is not Button) - continue; - - // get power profile - var parent = (Button)item; - if (parent.Tag is not PowerProfile) - continue; - - PowerProfile pr = (PowerProfile)parent.Tag; - - bool isCurrent = pr.Guid == powerProfile.Guid; - if (isCurrent) - { - idx = ProfileStack.Children.IndexOf(parent); - break; - } - } - - if (idx != -1) - { - // remove profile - ProfileStack.Children.RemoveAt(idx); - - // remove separator - if (idx >= ProfileStack.Children.Count) - idx = ProfileStack.Children.Count - 1; - ProfileStack.Children.RemoveAt(idx); - } - }); - } - - private void PowerProfileManager_Updated(PowerProfile powerProfile, UpdateSource source) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - int idx = -1; - foreach (var item in ProfileStack.Children) - { - if (item is not Button) - continue; - - // get power profile - var parent = (Button)item; - if (parent.Tag is not PowerProfile) - continue; - - PowerProfile pr = (PowerProfile)parent.Tag; - - bool isCurrent = pr.Guid == powerProfile.Guid; - if (isCurrent) - { - idx = ProfileStack.Children.IndexOf(parent); - break; - } - } - - if (idx != -1) - { - // found it - return; - } - else - { - // draw UI elements - powerProfile.DrawUI(this); - - idx = ProfileStack.Children.Count; - if (idx != 0) - { - // Create a separator - Separator separator = new Separator(); - separator.Margin = new Thickness(-16, 0, -16, 0); - separator.BorderBrush = (Brush)FindResource("SystemControlBackgroundChromeMediumBrush"); - separator.BorderThickness = new Thickness(0, 1, 0, 0); - ProfileStack.Children.Add(separator); - } - - Button button = powerProfile.GetButton(this); - button.Click += (sender, e) => PowerProfile_Clicked(powerProfile); - - RadioButton radioButton = powerProfile.GetRadioButton(this); - radioButton.Checked += (sender, e) => PowerProfile_Selected(powerProfile); - - // add new entry - ProfileStack.Children.Add(button); - } - }); - } - - private void PowerProfile_Clicked(PowerProfile powerProfile) - { - RadioButton radioButton = powerProfile.GetRadioButton(this); - if (radioButton.IsMouseOver) - return; - - MainWindow.performancePage.SelectionChanged(powerProfile.Guid); - MainWindow.GetCurrent().NavView_Navigate("PerformancePage"); - } - - private void PowerProfile_Selected(PowerProfile powerProfile) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // update UI - SelectedPowerProfileName.Text = powerProfile.Name; - - // update profile - selectedProfile.PowerProfile = powerProfile.Guid; - UpdateProfile(); - }); - } - - private void cB_Profiles_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (cB_Profiles.SelectedItem is null) - return; - - // if an update is pending, cut it short, it will disturb profile selection though - if (UpdateTimer.Enabled) - { - UpdateTimer.Stop(); - SubmitProfile(); - } - - selectedProfile = (Profile)cB_Profiles.SelectedItem; - - UpdateUI(); - } - - private void UpdateMotionControlsVisibility() - { - bool MotionMapped = false; - if (selectedProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions action)) - if (action is not null && action.ActionType != ActionType.Disabled) - MotionMapped = true; - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - MotionControlAdditional.IsEnabled = MotionMapped ? true : false; - }); - } - - private void UpdateUI() - { - if (selectedProfile is null) - return; - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - using (new ScopedLock(updateLock)) - { - // disable delete button if is default profile or running - b_DeleteProfile.IsEnabled = !selectedProfile.ErrorCode.HasFlag(ProfileErrorCode.Default & ProfileErrorCode.Running); - // prevent user from renaming default profile - tB_ProfileName.IsEnabled = !selectedProfile.Default; - // prevent user from disabling default profile - Toggle_EnableProfile.IsEnabled = !selectedProfile.Default; - // prevent user from disabling default profile layout - Toggle_ControllerLayout.IsEnabled = !selectedProfile.Default; - // prevent user from using Wrapper on default profile - cB_Wrapper.IsEnabled = !selectedProfile.Default; - - // Profile info - tB_ProfileName.Text = selectedProfile.Name; - tB_ProfilePath.Text = selectedProfile.Path; - Toggle_EnableProfile.IsOn = selectedProfile.Enabled; - - // Global settings - cB_Whitelist.IsChecked = selectedProfile.Whitelisted; - cB_Wrapper.SelectedIndex = (int)selectedProfile.XInputPlus; - - // Emulated controller assigned to the profile - cB_EmulatedController.IsEnabled = !selectedProfile.Default; // if default profile, disable combobox - cB_EmulatedController.SelectedIndex = new Func<int>(() => - { - if (selectedProfile.Default) // Default profile always shows default, but keeps track of default controller internally - return 0; - else if (selectedProfile.HID == HIDmode.Xbox360Controller) - return 1; - else if (selectedProfile.HID == HIDmode.DualShock4Controller) - return 2; - else - return 0; // Current or not assigned - })(); - - // Motion control settings - tb_ProfileGyroValue.Value = selectedProfile.GyrometerMultiplier; - tb_ProfileAcceleroValue.Value = selectedProfile.AccelerometerMultiplier; - - cB_GyroSteering.SelectedIndex = selectedProfile.SteeringAxis; - cB_InvertHorizontal.IsChecked = selectedProfile.MotionInvertHorizontal; - cB_InvertVertical.IsChecked = selectedProfile.MotionInvertVertical; - - UpdateMotionControlsVisibility(); - - // RSR - RSRToggle.IsOn = selectedProfile.RSREnabled; - RSRSlider.Value = selectedProfile.RSRSharpness; - - // Framerate limit - FramerateToggle.IsOn = selectedProfile.FramerateEnabled; - FramerateSlider.Value = selectedProfile.FramerateValue; - - // Layout settings - Toggle_ControllerLayout.IsOn = selectedProfile.LayoutEnabled; - - // power profile - PowerProfile powerProfile = PowerProfileManager.GetProfile(selectedProfile.PowerProfile); - powerProfile.Check(this); - - // display warnings - WarningContent.Text = EnumUtils.GetDescriptionFromEnumValue(selectedProfile.ErrorCode); - - switch (selectedProfile.ErrorCode) - { - default: - case ProfileErrorCode.None: - WarningBorder.Visibility = Visibility.Collapsed; - cB_Whitelist.IsEnabled = true; - - // wrapper - cB_Wrapper_Injection.IsEnabled = true; - cB_Wrapper_Redirection.IsEnabled = true; - break; - - case ProfileErrorCode.Running: // application is running - case ProfileErrorCode.MissingExecutable: // profile has no executable - case ProfileErrorCode.MissingPath: // profile has no path - case ProfileErrorCode.Default: // profile is default - WarningBorder.Visibility = Visibility.Visible; - cB_Whitelist.IsEnabled = false; - cB_Wrapper.IsEnabled = false; - - // wrapper - cB_Wrapper_Injection.IsEnabled = false; - cB_Wrapper_Redirection.IsEnabled = false; - break; - - case ProfileErrorCode.MissingPermission: - WarningBorder.Visibility = Visibility.Visible; - cB_Whitelist.IsEnabled = true; - - // wrapper - cB_Wrapper_Injection.IsEnabled = true; - cB_Wrapper_Redirection.IsEnabled = false; - break; - } - } - }); - } - - private async void b_DeleteProfile_Click(object sender, RoutedEventArgs e) - { - if (selectedProfile is null) - return; - - var result = Dialog.ShowAsync( - $"{Properties.Resources.ProfilesPage_AreYouSureDelete1} \"{selectedProfile.Name}\"?", - $"{Properties.Resources.ProfilesPage_AreYouSureDelete2}", - ContentDialogButton.Primary, - $"{Properties.Resources.ProfilesPage_Cancel}", - $"{Properties.Resources.ProfilesPage_Delete}"); - await result; // sync call - - switch (result.Result) - { - case ContentDialogResult.Primary: - ProfileManager.DeleteProfile(selectedProfile); - cB_Profiles.SelectedIndex = 0; - break; - } - } - - private void cB_Whitelist_Checked(object sender, RoutedEventArgs e) - { - // wait until lock is released - if (updateLock) - return; - - selectedProfile.Whitelisted = (bool)cB_Whitelist.IsChecked; - UpdateProfile(); - } - - private void cB_Wrapper_SelectionChanged(object sender, RoutedEventArgs e) - { - if (cB_Wrapper.SelectedIndex == -1) - return; - - if (selectedProfile is null) - return; - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.XInputPlus = (XInputPlusMethod)cB_Wrapper.SelectedIndex; - - switch (selectedProfile.XInputPlus) - { - case XInputPlusMethod.Injection: - cB_Whitelist.IsChecked = true; - break; - case XInputPlusMethod.Redirection: - cB_Whitelist.IsChecked = false; - break; - } - - UpdateProfile(); - } - - private void Expander_Expanded(object sender, RoutedEventArgs e) - { - ((Expander)sender).BringIntoView(); - } - - private void Toggle_EnableProfile_Toggled(object sender, RoutedEventArgs e) - { - // wait until lock is released - if (updateLock) - return; - - selectedProfile.Enabled = Toggle_EnableProfile.IsOn; - UpdateProfile(); - } - - private void FramerateToggle_Toggled(object sender, RoutedEventArgs e) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - if (FramerateToggle.IsOn) - { - FramerateSlider_ValueChanged(null, null); - } - else - { - foreach (Control control in FramerateModeGrid.Children) - { - if (control is not Label) - continue; - - control.SetResourceReference(Control.ForegroundProperty, "SystemControlForegroundBaseMediumBrush"); - } - } - }); - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.FramerateEnabled = FramerateToggle.IsOn; - UpdateProfile(); - } - - private void FramerateSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - var value = (int)FramerateSlider.Value; - - foreach (Control control in FramerateModeGrid.Children) - { - if (control is not Label) - continue; - - control.SetResourceReference(Control.ForegroundProperty, "SystemControlForegroundBaseMediumBrush"); - } - - Label label = (Label)FramerateModeGrid.Children[value]; - label.SetResourceReference(Control.ForegroundProperty, "AccentButtonBackground"); - }); - - if (!FramerateSlider.IsInitialized) - return; - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.FramerateValue = (int)FramerateSlider.Value; - UpdateProfile(); - } - - private void ControllerSettingsButton_Click(object sender, RoutedEventArgs e) - { - // prepare layout editor - LayoutTemplate layoutTemplate = new(selectedProfile.Layout) - { - Name = selectedProfile.LayoutTitle, - Description = "Your modified layout for this executable.", - Author = Environment.UserName, - Executable = selectedProfile.Executable, - Product = selectedProfile.Name, - }; - layoutTemplate.Updated += Template_Updated; - - MainWindow.layoutPage.UpdateLayoutTemplate(layoutTemplate); - MainWindow.NavView_Navigate(MainWindow.layoutPage); - } - - private void Template_Updated(LayoutTemplate layoutTemplate) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - selectedProfile.LayoutTitle = layoutTemplate.Name; - }); - - selectedProfile.Layout = layoutTemplate.Layout; - UpdateMotionControlsVisibility(); - - UpdateProfile(); - } - - #region UI - - private void ProfileApplied(Profile profile, UpdateSource source) - { - if (profile.Default) - return; - - ProfileUpdated(profile, source, true); - } - - public void ProfileUpdated(Profile profile, UpdateSource source, bool isCurrent) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - var idx = -1; - foreach (Profile pr in cB_Profiles.Items) - { - var isCurrent = pr.Path.Equals(profile.Path, StringComparison.InvariantCultureIgnoreCase); - if (isCurrent) - { - idx = cB_Profiles.Items.IndexOf(pr); - break; - } - } - - if (idx != -1) - cB_Profiles.Items[idx] = profile; - else - cB_Profiles.Items.Add(profile); - - cB_Profiles.Items.Refresh(); - - cB_Profiles.SelectedItem = profile; - }); - } - - public void ProfileDeleted(Profile profile) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - var idx = -1; - foreach (Profile pr in cB_Profiles.Items) - { - var isCurrent = pr.Path.Equals(profile.Path, StringComparison.InvariantCultureIgnoreCase); - if (isCurrent) - { - idx = cB_Profiles.Items.IndexOf(pr); - break; - } - } - - cB_Profiles.Items.RemoveAt(idx); - }); - } - - private void ProfileManagerLoaded() - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => { cB_Profiles.SelectedItem = ProfileManager.GetDefault(); }); - } - - #endregion - - private void tB_ProfileName_TextChanged(object sender, TextChangedEventArgs e) - { - // wait until lock is released - if (updateLock) - return; - - selectedProfile.Name = tB_ProfileName.Text; - UpdateProfile(); - } - - private void tb_ProfileGyroValue_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (!tb_ProfileGyroValue.IsInitialized) - return; - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.GyrometerMultiplier = (float)tb_ProfileGyroValue.Value; - UpdateProfile(); - } - - private void tb_ProfileAcceleroValue_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (!tb_ProfileAcceleroValue.IsInitialized) - return; - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.AccelerometerMultiplier = (float)tb_ProfileAcceleroValue.Value; - UpdateProfile(); - } - - private void cB_GyroSteering_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (cB_GyroSteering.SelectedIndex == -1) - return; - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.SteeringAxis = cB_GyroSteering.SelectedIndex; - UpdateProfile(); - } - - private void cB_InvertHorizontal_Checked(object sender, RoutedEventArgs e) - { - // wait until lock is released - if (updateLock) - return; - - selectedProfile.MotionInvertHorizontal = (bool)cB_InvertHorizontal.IsChecked; - UpdateProfile(); - } - - private void cB_InvertVertical_Checked(object sender, RoutedEventArgs e) - { - // wait until lock is released - if (updateLock) - return; - - selectedProfile.MotionInvertVertical = (bool)cB_InvertVertical.IsChecked; - UpdateProfile(); - } - - private void Toggle_ControllerLayout_Toggled(object sender, RoutedEventArgs e) - { - // wait until lock is released - if (updateLock) - return; - - // Layout settings - switch (selectedProfile.Default) - { - case true: - selectedProfile.LayoutEnabled = true; - break; - case false: - selectedProfile.LayoutEnabled = Toggle_ControllerLayout.IsOn; - break; - } - UpdateProfile(); - } - - public static void UpdateProfile() - { - if (UpdateTimer is not null) - { - UpdateTimer.Stop(); - UpdateTimer.Start(); - } - } - - public void SubmitProfile(UpdateSource source = UpdateSource.ProfilesPage) - { - if (selectedProfile is null) - return; - - ProfileManager.UpdateOrCreateProfile(selectedProfile, source); - } - - private void RSRToggle_Toggled(object sender, RoutedEventArgs e) - { - // wait until lock is released - if (updateLock) - return; - - selectedProfile.RSREnabled = RSRToggle.IsOn; - UpdateProfile(); - } - - private void RSRSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (!RSRSlider.IsInitialized) - return; - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.RSRSharpness = (int)RSRSlider.Value; - UpdateProfile(); - } - - private void cB_EmulatedController_Changed(object sender, SelectionChangedEventArgs e) - { - // wait until lock is released - if (updateLock) - return; - - if (selectedProfile is null) - return; - - ComboBoxItem selectedEmulatedController = (ComboBoxItem)cB_EmulatedController.SelectedItem; - - if (selectedEmulatedController is null || selectedEmulatedController.Tag is null) - return; - - HIDmode HIDmode = (HIDmode)int.Parse(selectedEmulatedController.Tag.ToString()); - selectedProfile.HID = HIDmode; - - UpdateProfile(); - } - +using HandheldCompanion.Actions; +using HandheldCompanion.Controls; +using HandheldCompanion.Inputs; +using HandheldCompanion.Managers; +using HandheldCompanion.Managers.Desktop; +using HandheldCompanion.Misc; +<<<<<<< HEAD +======= +using HandheldCompanion.Processors; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using HandheldCompanion.Utils; +using HandheldCompanion.Views.Pages.Profiles; +using Inkore.UI.WPF.Modern.Controls; +using Microsoft.Win32; +using System; +using System.ComponentModel; +using System.IO; +<<<<<<< HEAD +======= +using System.Linq; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using System.Timers; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using System.Xml; +using static HandheldCompanion.Utils.XInputPlusUtils; +using Page = System.Windows.Controls.Page; + +namespace HandheldCompanion.Views.Pages; + +/// <summary> +/// Interaction logic for Profiles.xaml +/// </summary> +public partial class ProfilesPage : Page +{ + // when set on start cannot be null anymore + public static Profile selectedProfile; + + private readonly SettingsMode0 page0 = new("SettingsMode0"); + private readonly SettingsMode1 page1 = new("SettingsMode1"); + + private LockObject updateLock = new(); + + private const int UpdateInterval = 500; + private static Timer UpdateTimer; + + public ProfilesPage() + { + InitializeComponent(); + + UpdateTimer = new Timer(UpdateInterval); + UpdateTimer.AutoReset = false; + UpdateTimer.Elapsed += (sender, e) => SubmitProfile(); + } + + public ProfilesPage(string Tag) : this() + { + this.Tag = Tag; + + ProfileManager.Deleted += ProfileDeleted; + ProfileManager.Updated += ProfileUpdated; + ProfileManager.Applied += ProfileApplied; + + PowerProfileManager.Updated += PowerProfileManager_Updated; + PowerProfileManager.Deleted += PowerProfileManager_Deleted; + + ProfileManager.Initialized += ProfileManagerLoaded; + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + + SystemManager.DisplaySettingsChanged += SystemManager_DisplaySettingsChanged; + SystemManager.RSRStateChanged += SystemManager_RSRStateChanged; + +<<<<<<< HEAD + // auto-sort + cB_Profiles.Items.SortDescriptions.Add(new SortDescription(string.Empty, ListSortDirection.Descending)); +======= + MainWindow.performanceManager.ProcessorStatusChanged += PerformanceManager_StatusChanged; + MainWindow.performanceManager.EPPChanged += PerformanceManager_EPPChanged; + + // auto-sort + cB_Profiles.Items.SortDescriptions.Add(new SortDescription("", ListSortDirection.Descending)); + + // device settings + GPUSlider.Minimum = MainWindow.CurrentDevice.GfxClock[0]; + GPUSlider.Maximum = MainWindow.CurrentDevice.GfxClock[1]; + + // motherboard settings + CPUCoreSlider.Maximum = MotherboardInfo.NumberOfCores; + } + + private void SystemManager_RSRStateChanged(int RSRState, int RSRSharpness) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (RSRState) + { + case -1: + RSRToggle.IsEnabled = false; + break; + case 0: + RSRToggle.IsEnabled = true; + RSRToggle.IsOn = false; + RSRSlider.Value = RSRSharpness; + break; + case 1: + RSRToggle.IsEnabled = true; + RSRToggle.IsOn = true; + RSRSlider.Value = RSRSharpness; + break; + } + }); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void SystemManager_RSRStateChanged(int RSRState, int RSRSharpness) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (RSRState) + { + case -1: + RSRToggle.IsEnabled = false; + break; + case 0: + RSRToggle.IsEnabled = true; + RSRToggle.IsOn = false; + RSRSlider.Value = RSRSharpness; + break; + case 1: + RSRToggle.IsEnabled = true; + RSRToggle.IsOn = true; + RSRSlider.Value = RSRSharpness; + break; + } + }); + } + + public void SettingsManager_SettingValueChanged(string name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { +<<<<<<< HEAD +======= + case "ConfigurableTDPOverrideDown": + { + using (new ScopedLock(updateLock)) + { + TDPSlider.Minimum = (double)value; + } + } + break; + case "ConfigurableTDPOverrideUp": + { + using (new ScopedLock(updateLock)) + { + TDPSlider.Maximum = (double)value; + } + } + break; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + }); + } + + private void SystemManager_DisplaySettingsChanged(ScreenResolution resolution) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + var screenFrequency = SystemManager.GetDesktopScreen().GetFrequency(); + + FramerateQuarter.Content = Convert.ToString(screenFrequency.GetValue(Frequency.Quarter)); + FramerateThird.Content = Convert.ToString(screenFrequency.GetValue(Frequency.Third)); + FramerateHalf.Content = Convert.ToString(screenFrequency.GetValue(Frequency.Half)); + FramerateFull.Content = Convert.ToString(screenFrequency.GetValue(Frequency.Full)); + }); + } + + private void Page_Loaded(object sender, RoutedEventArgs e) + { + } + + public void Page_Closed() + { + } + + private async void b_CreateProfile_Click(object sender, RoutedEventArgs e) + { + // if an update is pending, execute it and stop timer + if (UpdateTimer.Enabled) + UpdateTimer.Stop(); + + var openFileDialog = new OpenFileDialog() + { + Filter = "Executable|*.exe|UWP manifest|AppxManifest.xml", + }; + + if (openFileDialog.ShowDialog() == true) + try + { + string path = openFileDialog.FileName; + string folder = Path.GetDirectoryName(path); + + string file = openFileDialog.SafeFileName; + string ext = Path.GetExtension(file); + + switch (ext) + { + default: + case ".exe": + break; + case ".xml": + try + { + XmlDocument doc = new XmlDocument(); + string UWPpath = string.Empty; + string UWPfile = string.Empty; + + // check if MicrosoftGame.config exists + string configPath = Path.Combine(folder, "MicrosoftGame.config"); + if (File.Exists(configPath)) + { + doc.Load(configPath); + + XmlNodeList ExecutableList = doc.GetElementsByTagName("ExecutableList"); + foreach (XmlNode node in ExecutableList) + foreach (XmlNode child in node.ChildNodes) + if (child.Name.Equals("Executable")) + if (child.Attributes is not null) + foreach (XmlAttribute attribute in child.Attributes) + switch (attribute.Name) + { + case "Name": + UWPpath = Path.Combine(folder, attribute.InnerText); + UWPfile = Path.GetFileName(path); + break; + } + } + + // either there was no config file, either we couldn't find an executable within it + if (!File.Exists(UWPpath)) + { + doc.Load(path); + + XmlNodeList Applications = doc.GetElementsByTagName("Applications"); + foreach (XmlNode node in Applications) + foreach (XmlNode child in node.ChildNodes) + if (child.Name.Equals("Application")) + if (child.Attributes is not null) + foreach (XmlAttribute attribute in child.Attributes) + switch (attribute.Name) + { + case "Executable": + UWPpath = Path.Combine(folder, attribute.InnerText); + UWPfile = Path.GetFileName(path); + break; + } + } + + // we're good to go + if (File.Exists(UWPpath)) + { + path = UWPpath; + file = UWPfile; + } + } + catch (Exception ex) + { + LogManager.LogError(ex.Message, true); + } + + break; + } + +<<<<<<< HEAD + Profile profile = new Profile(path); + Layout toCloneLayout = ProfileManager.GetProfileWithDefaultLayout()?.Layout ?? LayoutTemplate.DefaultLayout.Layout; +======= + var profile = new Profile(path); + var toCloneLayout = ProfileManager.GetProfileWithDefaultLayout()?.Layout ?? LayoutTemplate.DefaultLayout.Layout; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + profile.Layout = toCloneLayout.Clone() as Layout; + profile.LayoutTitle = LayoutTemplate.DefaultLayout.Name; + + var exists = false; + + // check on path rather than profile + if (ProfileManager.Contains(path)) + { + var result = Dialog.ShowAsync( + string.Format(Properties.Resources.ProfilesPage_AreYouSureOverwrite1, profile.Name), + string.Format(Properties.Resources.ProfilesPage_AreYouSureOverwrite2, profile.Name), + ContentDialogButton.Primary, + $"{Properties.Resources.ProfilesPage_Cancel}", + $"{Properties.Resources.ProfilesPage_Yes}"); + + await result; // sync call + + switch (result.Result) + { + case ContentDialogResult.Primary: + exists = false; + break; + default: + exists = true; + break; + } + } + + if (!exists) + ProfileManager.UpdateOrCreateProfile(profile, UpdateSource.Creation); + } + catch (Exception ex) + { + LogManager.LogError(ex.Message); + } + } + + private void b_AdditionalSettings_Click(object sender, RoutedEventArgs e) + { + if (selectedProfile is null) + return; + + if (!selectedProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) + return; + + // TODO: MOVE ME TO LAYOUT ! + switch (((GyroActions)currentAction).MotionInput) + { + default: + case MotionInput.JoystickCamera: + case MotionInput.PlayerSpace: + page0.SetProfile(); + MainWindow.NavView_Navigate(page0); + break; + case MotionInput.JoystickSteering: + page1.SetProfile(); + MainWindow.NavView_Navigate(page1); + break; + } + } + + private void PowerProfileManager_Deleted(PowerProfile powerProfile) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + int idx = -1; + foreach (var item in ProfileStack.Children) + { + if (item is not Button) + continue; + + // get power profile + var parent = (Button)item; + if (parent.Tag is not PowerProfile) + continue; + + PowerProfile pr = (PowerProfile)parent.Tag; + + bool isCurrent = pr.Guid == powerProfile.Guid; + if (isCurrent) + { + idx = ProfileStack.Children.IndexOf(parent); + break; + } + } + + if (idx != -1) + { + // remove profile + ProfileStack.Children.RemoveAt(idx); + + // remove separator + if (idx >= ProfileStack.Children.Count) + idx = ProfileStack.Children.Count - 1; + ProfileStack.Children.RemoveAt(idx); + } + }); + } + + private void PowerProfileManager_Updated(PowerProfile powerProfile, UpdateSource source) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + int idx = -1; + foreach (var item in ProfileStack.Children) + { + if (item is not Button) + continue; + + // get power profile + var parent = (Button)item; + if (parent.Tag is not PowerProfile) + continue; + + PowerProfile pr = (PowerProfile)parent.Tag; + + bool isCurrent = pr.Guid == powerProfile.Guid; + if (isCurrent) + { + idx = ProfileStack.Children.IndexOf(parent); + break; + } + } + + if (idx != -1) + { + // found it + return; + } + else + { + // draw UI elements + powerProfile.DrawUI(this); + + idx = ProfileStack.Children.Count; + if (idx != 0) + { + // Create a separator + Separator separator = new Separator(); + separator.Margin = new Thickness(-16, 0, -16, 0); + separator.BorderBrush = (Brush)FindResource("SystemControlBackgroundChromeMediumBrush"); + separator.BorderThickness = new Thickness(0, 1, 0, 0); + ProfileStack.Children.Add(separator); + } + + Button button = powerProfile.GetButton(this); + button.Click += (sender, e) => PowerProfile_Clicked(powerProfile); + + RadioButton radioButton = powerProfile.GetRadioButton(this); + radioButton.Checked += (sender, e) => PowerProfile_Selected(powerProfile); + + // add new entry + ProfileStack.Children.Add(button); + } + }); + } + + private void PowerProfile_Clicked(PowerProfile powerProfile) + { + RadioButton radioButton = powerProfile.GetRadioButton(this); + if (radioButton.IsMouseOver) + return; + + MainWindow.performancePage.SelectionChanged(powerProfile.Guid); + MainWindow.GetCurrent().NavView_Navigate("PerformancePage"); + } + + private void PowerProfile_Selected(PowerProfile powerProfile) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // update UI + SelectedPowerProfileName.Text = powerProfile.Name; + + // update profile + selectedProfile.PowerProfile = powerProfile.Guid; + UpdateProfile(); + }); + } + + private void cB_Profiles_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (cB_Profiles.SelectedItem is null) + return; + +<<<<<<< HEAD + // if an update is pending, cut it short, it will disturb profile selection though +======= + // if an update is pending, cut it short, it will distirb profile selection though +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + if (UpdateTimer.Enabled) + { + UpdateTimer.Stop(); + SubmitProfile(); + } + + selectedProfile = (Profile)cB_Profiles.SelectedItem; + + UpdateUI(); + } + + private void UpdateMotionControlsVisibility() +<<<<<<< HEAD + { + bool MotionMapped = false; + if (selectedProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions action)) + if (action is not null && action.ActionType != ActionType.Disabled) + MotionMapped = true; + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + MotionControlAdditional.IsEnabled = MotionMapped ? true : false; + }); + } + + private void UpdateUI() + { +======= + { + bool MotionMapped = false; + if (selectedProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions action)) + if (action is not null && action.ActionType != ActionType.Disabled) + MotionMapped = true; + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + MotionControlAdditional.IsEnabled = MotionMapped ? true : false; + }); + } + + private void DrawProfile() + { +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + if (selectedProfile is null) + return; + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + using (new ScopedLock(updateLock)) + { + // disable delete button if is default profile or running + b_DeleteProfile.IsEnabled = !selectedProfile.ErrorCode.HasFlag(ProfileErrorCode.Default & ProfileErrorCode.Running); + // prevent user from renaming default profile + tB_ProfileName.IsEnabled = !selectedProfile.Default; + // prevent user from disabling default profile + Toggle_EnableProfile.IsEnabled = !selectedProfile.Default; + // prevent user from disabling default profile layout + Toggle_ControllerLayout.IsEnabled = !selectedProfile.Default; + // prevent user from using Wrapper on default profile + cB_Wrapper.IsEnabled = !selectedProfile.Default; + + // Profile info + tB_ProfileName.Text = selectedProfile.Name; + tB_ProfilePath.Text = selectedProfile.Path; + Toggle_EnableProfile.IsOn = selectedProfile.Enabled; + + // Global settings + cB_Whitelist.IsChecked = selectedProfile.Whitelisted; + cB_Wrapper.SelectedIndex = (int)selectedProfile.XInputPlus; + +<<<<<<< HEAD + // Emulated controller assigned to the profile + cB_EmulatedController.IsEnabled = !selectedProfile.Default; // if default profile, disable combobox + cB_EmulatedController.SelectedIndex = new Func<int>(() => + { + if (selectedProfile.Default) // Default profile always shows default, but keeps track of default controller internally + return 0; + else if (selectedProfile.HID == HIDmode.Xbox360Controller) + return 1; + else if (selectedProfile.HID == HIDmode.DualShock4Controller) + return 2; + else + return 0; // Current or not assigned + })(); + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + // Motion control settings + tb_ProfileGyroValue.Value = selectedProfile.GyrometerMultiplier; + tb_ProfileAcceleroValue.Value = selectedProfile.AccelerometerMultiplier; + + cB_GyroSteering.SelectedIndex = selectedProfile.SteeringAxis; + cB_InvertHorizontal.IsChecked = selectedProfile.MotionInvertHorizontal; + cB_InvertVertical.IsChecked = selectedProfile.MotionInvertVertical; + + UpdateMotionControlsVisibility(); + +<<<<<<< HEAD +======= + // Sustained TDP settings (slow, stapm, long) + TDPToggle.IsOn = selectedProfile.TDPOverrideEnabled; + var TDP = selectedProfile.TDPOverrideValues is not null + ? selectedProfile.TDPOverrideValues + : MainWindow.CurrentDevice.nTDP; + TDPSlider.Value = TDP[(int)PowerType.Slow]; + + // define slider(s) min and max values based on device specifications + TDPSlider.Minimum = SettingsManager.GetInt("ConfigurableTDPOverrideDown"); + TDPSlider.Maximum = SettingsManager.GetInt("ConfigurableTDPOverrideUp"); + + // Automatic TDP + AutoTDPToggle.IsOn = selectedProfile.AutoTDPEnabled; + AutoTDPSlider.Value = (int)selectedProfile.AutoTDPRequestedFPS; + + // EPP + EPPToggle.IsOn = selectedProfile.EPPOverrideEnabled; + EPPSlider.Value = selectedProfile.EPPOverrideValue; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + // RSR + RSRToggle.IsOn = selectedProfile.RSREnabled; + RSRSlider.Value = selectedProfile.RSRSharpness; + +<<<<<<< HEAD +======= + // CPU Core Count + CPUCoreToggle.IsOn = selectedProfile.CPUCoreEnabled; + CPUCoreSlider.Value = selectedProfile.CPUCoreCount; + + // GPU Clock control + GPUToggle.IsOn = selectedProfile.GPUOverrideEnabled; + GPUSlider.Value = selectedProfile.GPUOverrideValue != 0 ? selectedProfile.GPUOverrideValue : 255 * 50; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + // Framerate limit + FramerateToggle.IsOn = selectedProfile.FramerateEnabled; + FramerateSlider.Value = selectedProfile.FramerateValue; + + // Layout settings + Toggle_ControllerLayout.IsOn = selectedProfile.LayoutEnabled; + +<<<<<<< HEAD + // power profile + PowerProfile powerProfile = PowerProfileManager.GetProfile(selectedProfile.PowerProfile); + powerProfile.Check(this); +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // display warnings + WarningContent.Text = EnumUtils.GetDescriptionFromEnumValue(selectedProfile.ErrorCode); + + switch (selectedProfile.ErrorCode) + { + default: + case ProfileErrorCode.None: + WarningBorder.Visibility = Visibility.Collapsed; + cB_Whitelist.IsEnabled = true; + + // wrapper + cB_Wrapper_Injection.IsEnabled = true; + cB_Wrapper_Redirection.IsEnabled = true; + break; + + case ProfileErrorCode.Running: // application is running + case ProfileErrorCode.MissingExecutable: // profile has no executable + case ProfileErrorCode.MissingPath: // profile has no path + case ProfileErrorCode.Default: // profile is default + WarningBorder.Visibility = Visibility.Visible; + cB_Whitelist.IsEnabled = false; + cB_Wrapper.IsEnabled = false; + + // wrapper + cB_Wrapper_Injection.IsEnabled = false; + cB_Wrapper_Redirection.IsEnabled = false; + break; + + case ProfileErrorCode.MissingPermission: + WarningBorder.Visibility = Visibility.Visible; + cB_Whitelist.IsEnabled = true; + + // wrapper + cB_Wrapper_Injection.IsEnabled = true; + cB_Wrapper_Redirection.IsEnabled = false; + break; + } + } + }); + } + + private async void b_DeleteProfile_Click(object sender, RoutedEventArgs e) + { + if (selectedProfile is null) + return; + + var result = Dialog.ShowAsync( + $"{Properties.Resources.ProfilesPage_AreYouSureDelete1} \"{selectedProfile.Name}\"?", + $"{Properties.Resources.ProfilesPage_AreYouSureDelete2}", + ContentDialogButton.Primary, + $"{Properties.Resources.ProfilesPage_Cancel}", + $"{Properties.Resources.ProfilesPage_Delete}"); + await result; // sync call + + switch (result.Result) + { + case ContentDialogResult.Primary: + ProfileManager.DeleteProfile(selectedProfile); + cB_Profiles.SelectedIndex = 0; + break; + } + } + + private void cB_Whitelist_Checked(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.Whitelisted = (bool)cB_Whitelist.IsChecked; +<<<<<<< HEAD + UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void cB_Wrapper_SelectionChanged(object sender, RoutedEventArgs e) + { + if (cB_Wrapper.SelectedIndex == -1) + return; + + if (selectedProfile is null) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.XInputPlus = (XInputPlusMethod)cB_Wrapper.SelectedIndex; + + switch (selectedProfile.XInputPlus) + { + case XInputPlusMethod.Injection: + cB_Whitelist.IsChecked = true; + break; + case XInputPlusMethod.Redirection: + cB_Whitelist.IsChecked = false; + break; + } + +<<<<<<< HEAD + UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void Expander_Expanded(object sender, RoutedEventArgs e) + { + ((Expander)sender).BringIntoView(); + } + + private void Toggle_EnableProfile_Toggled(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.Enabled = Toggle_EnableProfile.IsOn; +<<<<<<< HEAD + UpdateProfile(); +======= + RequestUpdate(); + } + + private void TDPToggle_Toggled(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.TDPOverrideEnabled = TDPToggle.IsOn; + RequestUpdate(); + } + + private void TDPSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (!TDPSlider.IsInitialized) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.TDPOverrideValues = new double[3] + { + (int)TDPSlider.Value, + (int)TDPSlider.Value, + (int)TDPSlider.Value + }; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void FramerateToggle_Toggled(object sender, RoutedEventArgs e) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + if (FramerateToggle.IsOn) + { + FramerateSlider_ValueChanged(null, null); + } + else + { + foreach (Control control in FramerateModeGrid.Children) + { +<<<<<<< HEAD + if (control is not Label) +======= + if (control.GetType() != typeof(Label)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + continue; + + control.SetResourceReference(Control.ForegroundProperty, "SystemControlForegroundBaseMediumBrush"); + } + } + }); + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.FramerateEnabled = FramerateToggle.IsOn; +<<<<<<< HEAD + UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void FramerateSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + var value = (int)FramerateSlider.Value; + + foreach (Control control in FramerateModeGrid.Children) + { +<<<<<<< HEAD + if (control is not Label) +======= + if (control.GetType() != typeof(Label)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + continue; + + control.SetResourceReference(Control.ForegroundProperty, "SystemControlForegroundBaseMediumBrush"); + } + +<<<<<<< HEAD + Label label = (Label)FramerateModeGrid.Children[value]; + label.SetResourceReference(Control.ForegroundProperty, "AccentButtonBackground"); + }); + + if (!FramerateSlider.IsInitialized) +======= + Label Label = (Label)FramerateModeGrid.Children[value]; + Label.SetResourceReference(Control.ForegroundProperty, "AccentButtonBackground"); + }); + + if (!FramerateSlider.IsInitialized) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.FramerateValue = (int)FramerateSlider.Value; + RequestUpdate(); + } + + private void AutoTDPToggle_Toggled(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.AutoTDPEnabled = AutoTDPToggle.IsOn; + RequestUpdate(); + } + + private void AutoTDPSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (!AutoTDPSlider.IsInitialized) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + return; + + // wait until lock is released + if (updateLock) + return; + +<<<<<<< HEAD + selectedProfile.FramerateValue = (int)FramerateSlider.Value; + UpdateProfile(); +======= + selectedProfile.AutoTDPRequestedFPS = (int)AutoTDPSlider.Value; + RequestUpdate(); + } + + private void GPUToggle_Toggled(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.GPUOverrideEnabled = GPUToggle.IsOn; + RequestUpdate(); + } + + private void GPUSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (!GPUSlider.IsInitialized) + return; + + if (selectedProfile is null) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.GPUOverrideValue = (int)GPUSlider.Value; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void ControllerSettingsButton_Click(object sender, RoutedEventArgs e) + { + // prepare layout editor + LayoutTemplate layoutTemplate = new(selectedProfile.Layout) + { + Name = selectedProfile.LayoutTitle, + Description = "Your modified layout for this executable.", + Author = Environment.UserName, + Executable = selectedProfile.Executable, + Product = selectedProfile.Name, + }; + layoutTemplate.Updated += Template_Updated; + + MainWindow.layoutPage.UpdateLayoutTemplate(layoutTemplate); + MainWindow.NavView_Navigate(MainWindow.layoutPage); + } + + private void Template_Updated(LayoutTemplate layoutTemplate) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + selectedProfile.LayoutTitle = layoutTemplate.Name; + }); +<<<<<<< HEAD + + selectedProfile.Layout = layoutTemplate.Layout; + UpdateMotionControlsVisibility(); + + UpdateProfile(); +======= + + selectedProfile.Layout = layoutTemplate.Layout; + UpdateMotionControlsVisibility(); + + RequestUpdate(); + } + + private void EPPToggle_Toggled(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.EPPOverrideEnabled = EPPToggle.IsOn; + RequestUpdate(); + } + + private void EPPSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (!EPPSlider.IsInitialized) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.EPPOverrideValue = (uint)EPPSlider.Value; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + #region UI + + private void ProfileApplied(Profile profile, UpdateSource source) + { + if (profile.Default) + return; + + ProfileUpdated(profile, source, true); + } + + public void ProfileUpdated(Profile profile, UpdateSource source, bool isCurrent) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + var idx = -1; + foreach (Profile pr in cB_Profiles.Items) + { + var isCurrent = pr.Path.Equals(profile.Path, StringComparison.InvariantCultureIgnoreCase); + if (isCurrent) + { + idx = cB_Profiles.Items.IndexOf(pr); + break; + } + } + + if (idx != -1) + cB_Profiles.Items[idx] = profile; + else + cB_Profiles.Items.Add(profile); + + cB_Profiles.Items.Refresh(); + + cB_Profiles.SelectedItem = profile; + }); + } + + public void ProfileDeleted(Profile profile) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + var idx = -1; + foreach (Profile pr in cB_Profiles.Items) + { + var isCurrent = pr.Path.Equals(profile.Path, StringComparison.InvariantCultureIgnoreCase); + if (isCurrent) + { + idx = cB_Profiles.Items.IndexOf(pr); + break; + } + } + + cB_Profiles.Items.RemoveAt(idx); + }); + } + + private void ProfileManagerLoaded() + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => { cB_Profiles.SelectedItem = ProfileManager.GetDefault(); }); + } + + #endregion + + private void tB_ProfileName_TextChanged(object sender, TextChangedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.Name = tB_ProfileName.Text; +<<<<<<< HEAD + UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void tb_ProfileGyroValue_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (!tb_ProfileGyroValue.IsInitialized) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.GyrometerMultiplier = (float)tb_ProfileGyroValue.Value; +<<<<<<< HEAD + UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void tb_ProfileAcceleroValue_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (!tb_ProfileAcceleroValue.IsInitialized) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.AccelerometerMultiplier = (float)tb_ProfileAcceleroValue.Value; +<<<<<<< HEAD + UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void cB_GyroSteering_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (cB_GyroSteering.SelectedIndex == -1) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.SteeringAxis = cB_GyroSteering.SelectedIndex; +<<<<<<< HEAD + UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void cB_InvertHorizontal_Checked(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.MotionInvertHorizontal = (bool)cB_InvertHorizontal.IsChecked; +<<<<<<< HEAD + UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void cB_InvertVertical_Checked(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.MotionInvertVertical = (bool)cB_InvertVertical.IsChecked; +<<<<<<< HEAD + UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void Toggle_ControllerLayout_Toggled(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + // Layout settings + switch (selectedProfile.Default) + { + case true: + selectedProfile.LayoutEnabled = true; + break; + case false: + selectedProfile.LayoutEnabled = Toggle_ControllerLayout.IsOn; + break; + } + UpdateProfile(); + } + + public static void UpdateProfile() + { + if (UpdateTimer is not null) + { + UpdateTimer.Stop(); + UpdateTimer.Start(); + } + } + + public void SubmitProfile(UpdateSource source = UpdateSource.ProfilesPage) + { + if (selectedProfile is null) + return; + + ProfileManager.UpdateOrCreateProfile(selectedProfile, source); +<<<<<<< HEAD +======= + } + + private void RSRToggle_Toggled(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.RSREnabled = RSRToggle.IsOn; + RequestUpdate(); + } + + private void RSRSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (!RSRSlider.IsInitialized) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.RSRSharpness = (int)RSRSlider.Value; + RequestUpdate(); + } + + private void CPUCoreToggle_Toggled(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.CPUCoreEnabled = CPUCoreToggle.IsOn; + RequestUpdate(); + } + + private void CPUCoreSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (!CPUCoreSlider.IsInitialized) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.CPUCoreCount = (int)CPUCoreSlider.Value; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void RSRToggle_Toggled(object sender, RoutedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + selectedProfile.RSREnabled = RSRToggle.IsOn; + UpdateProfile(); + } + + private void RSRSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (!RSRSlider.IsInitialized) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.RSRSharpness = (int)RSRSlider.Value; + UpdateProfile(); + } + + private void cB_EmulatedController_Changed(object sender, SelectionChangedEventArgs e) + { + // wait until lock is released + if (updateLock) + return; + + if (selectedProfile is null) + return; + + ComboBoxItem selectedEmulatedController = (ComboBoxItem)cB_EmulatedController.SelectedItem; + + if (selectedEmulatedController is null || selectedEmulatedController.Tag is null) + return; + + HIDmode HIDmode = (HIDmode)int.Parse(selectedEmulatedController.Tag.ToString()); + selectedProfile.HID = HIDmode; + + UpdateProfile(); + } + } \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/SettingsPage.xaml b/HandheldCompanion/Views/Pages/SettingsPage.xaml index b89085817..24eaac487 100644 --- a/HandheldCompanion/Views/Pages/SettingsPage.xaml +++ b/HandheldCompanion/Views/Pages/SettingsPage.xaml @@ -1,878 +1,919 @@ -<Page - x:Class="HandheldCompanion.Views.Pages.SettingsPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Name="Settings" - Title="{x:Static resx:Resources.SettingsPage_Settings}" - d:Background="White" - d:DesignHeight="2400" - d:DesignWidth="1000" - KeepAlive="True" - Loaded="Page_Loaded" - mc:Ignorable="d"> - - <Grid Name="MainGrid" Margin="20"> - <ui:SimpleStackPanel Spacing="24"> - - <!-- Update manager --> - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="100" MinWidth="100" /> - <ColumnDefinition /> - <ColumnDefinition /> - </Grid.ColumnDefinitions> - - <Grid Grid.Column="0"> - <ui:FontIcon - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - FontSize="80" - Foreground="{DynamicResource SystemControlBackgroundBaseHighBrush}" - Glyph="" /> - <Grid Name="GridUpdateSymbol" Visibility="Collapsed"> - <ui:FontIcon - Margin="50,50,0,0" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - FontSize="30" - Foreground="{DynamicResource SystemAccentColorLight2Brush}" - Glyph="" /> - <ui:FontIcon - Margin="50,50,0,0" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - FontSize="20" - Foreground="White" - Glyph="" /> - </Grid> - </Grid> - - <ui:SimpleStackPanel - Grid.Column="1" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - Orientation="Vertical" - Spacing="6"> - <TextBlock - Name="LabelUpdate" - FontSize="20" - Foreground="{DynamicResource SystemControlBackgroundBaseHighBrush}" - Style="{StaticResource BaseTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_UpToDate}" /> - <TextBlock - Name="LabelUpdateDate" - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Text="{x:Static resx:Resources.SettingsPage_LastChecked}" /> - <ui:ProgressBar - Name="ProgressBarUpdate" - Margin="0,0,10,0" - IsIndeterminate="True" - Visibility="Collapsed" /> - </ui:SimpleStackPanel> - - <Button - Name="B_CheckUpdate" - Grid.Column="2" - HorizontalAlignment="Right" - VerticalAlignment="Center" - Click="B_CheckUpdate_Click" - Content="{x:Static resx:Resources.SettingsPage_CheckForUpdates}" - Style="{DynamicResource AccentButtonStyle}" /> - </Grid> - - <!-- Changelog --> - <TextBox - Name="CurrentChangelog" - IsReadOnly="True" - ScrollViewer.HorizontalScrollBarVisibility="Auto" - Visibility="Collapsed" /> - - <!-- Update(s) --> - <ui:SimpleStackPanel Name="CurrentUpdates" Spacing="6" /> - - <!-- General options --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_GeneralOptions}" /> - - <!-- Auto-start application --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_AutoStartApp}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_AutoStartAppDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_AutoStart" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_AutoStart_Toggled" /> - </Grid> - </Border> - - <!-- Open application in background --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_OpenAppBackground}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_OpenAppBackgroundDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_Background" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_Background_Toggled" /> - </Grid> - </Border> - - <!-- Close minimizes --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_CloseMinimizes}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_CloseMinimizesDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_CloseMinimizes" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_CloseMinimizes_Toggled" /> - </Grid> - </Border> - - <!-- Enable desktop profile on start --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_EnableDesktopProfileOnStart}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_EnableDesktopProfileOnStartDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_DesktopProfileOnStart" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_DesktopProfileOnStart_Toggled" /> - </Grid> - </Border> - - <!-- Native display orientation --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_NativeDisplayOrientation}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_NativeDisplayOrientationDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <DockPanel - Grid.Column="1" - Margin="12,0,0,0" - HorizontalAlignment="Right" - ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - x:Name="Text_NativeDisplayOrientation" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_NativeDisplayOrientationNotSet}" /> - <Button - Name="Button_DetectNativeDisplayOrientation" - Width="120" - Height="40" - Margin="12,0,0,0" - HorizontalAlignment="Right" - Click="Button_DetectNativeDisplayOrientation_Click" - Content="{x:Static resx:Resources.SettingsPage_NativeDisplayOrientationDetect}" /> - </DockPanel> - </Grid> - </Border> - - <!-- Application theme --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_AppTheme}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_AppThemeDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="cB_Theme" - Grid.Column="1" - Margin="12,0,0,0" - HorizontalAlignment="Stretch" - SelectionChanged="cB_Theme_SelectionChanged"> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_ThemeDefault}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_ThemeLight}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_ThemeDark}" /> - </ComboBox> - </Grid> - </Border> - - <!-- Application backdrop --> - <Border - Padding="15,12,12,12" - d:Visibility="Visible" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_Backdrop}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_BackdropDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="cB_Backdrop" - Grid.Column="1" - Margin="12,0,0,0" - HorizontalAlignment="Stretch" - SelectionChanged="cB_Backdrop_SelectionChanged"> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropNone}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropMica}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropTabbed}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropAcrylic}" /> - </ComboBox> - </Grid> - </Border> - - <!-- Application language --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_AppLanguage}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_AppLanguageDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="cB_Language" - Grid.Column="1" - Margin="12,0,0,0" - HorizontalAlignment="Stretch" - SelectionChanged="cB_Language_SelectionChanged" /> - </Grid> - </Border> - </ui:SimpleStackPanel> - - <!-- Quicktools options --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_QuickToolsOptions}" /> - - <!-- Quicktools position --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocation}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocationDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="cB_QuicktoolsPosition" - Grid.Column="1" - Margin="12,0,0,0" - HorizontalAlignment="Stretch" - SelectionChanged="cB_QuicktoolsPosition_SelectionChanged"> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocationTopLeft}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocationTopRight}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocationBottomLeft}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocationBottomRight}" /> - </ComboBox> - </Grid> - </Border> - - <!-- Application backdrop --> - <Border - Padding="15,12,12,12" - d:Visibility="Visible" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_Backdrop}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_QuickToolsBackdrop}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="cB_QuickToolsBackdrop" - Grid.Column="1" - Margin="12,0,0,0" - HorizontalAlignment="Stretch" - SelectionChanged="cB_QuickToolsBackdrop_SelectionChanged"> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropNone}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropMica}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropTabbed}" /> - <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropAcrylic}" /> - </ComboBox> - </Grid> - </Border> - - <!-- Quicktools autohide --> - <!-- Disabled until we figure out a way to make it work without having to actually click on the window --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}" - Visibility="Collapsed"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_HideWhenLoseFocus}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_HideWhenLoseFocusDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_QuicktoolsAutoHide" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_QuicktoolsAutoHide_Toggled" /> - </Grid> - </Border> - </ui:SimpleStackPanel> - - <!-- Notification options --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_NotificationOptions}" /> - - <!-- Toast notification --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_ToastNotification}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_ToastNotificationDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_Notification" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_Notification_Toggled" /> - </Grid> - </Border> - </ui:SimpleStackPanel> - - <!-- Sensor options --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_SensorOptions}" /> - - <!-- Sensor Selection --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_SensorSelection}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_SensorSelectionDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="cB_SensorSelection" - Grid.Column="1" - Margin="12,0,0,0" - HorizontalAlignment="Stretch" - SelectionChanged="cB_SensorSelection_SelectionChanged"> - - <ComboBoxItem - Name="SensorNone" - Content="{x:Static resx:Resources.SettingsPage_SensorNone}" - IsEnabled="True" /> - <ComboBoxItem - Name="SensorInternal" - Content="{x:Static resx:Resources.SettingsPage_SensorInternal}" - IsEnabled="False" /> - <ComboBoxItem - Name="SensorExternal" - Content="{x:Static resx:Resources.SettingsPage_SensorExternal}" - IsEnabled="False" /> - <ComboBoxItem - Name="SensorController" - Content="{x:Static resx:Resources.MainWindow_navController}" - IsEnabled="False" /> - </ComboBox> - </Grid> - </Border> - - <!-- Sensor Placement Direction --> - <Border - Name="SensorPlacementVisualisation" - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_SensorPlacementDirection}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_SensorPlacementDirectionDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <Grid Grid.Column="1"> - <ui:SimpleStackPanel - Name="Grid_SensorPlacementVisualisation" - HorizontalAlignment="Right" - IsEnabled="False" - Spacing="6"> - <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> - <Button - Width="50" - Height="50" - Tag="99" - Visibility="Hidden" /> - <Button - Width="50" - Height="50" - Click="SensorPlacement_Click" - Tag="0" /> - <Button - Width="50" - Height="50" - Tag="99" - Visibility="Hidden" /> - </ui:SimpleStackPanel> - <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> - <Button - Width="50" - Height="50" - Click="SensorPlacement_Click" - Tag="1" /> - <Button - Width="50" - Height="50" - Tag="99" - Visibility="Hidden" /> - <Button - Width="50" - Height="50" - Click="SensorPlacement_Click" - Tag="2" /> - </ui:SimpleStackPanel> - <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> - <Button - Width="50" - Height="50" - Tag="99" - Visibility="Hidden" /> - <Button - Width="50" - Height="50" - Click="SensorPlacement_Click" - Tag="3" /> - <Button - Width="50" - Height="50" - Tag="99" - Visibility="Hidden" /> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> - </Grid> - </Grid> - </Border> - - <!-- Sensor Placement Mirror --> - <Border - Name="SensorPlacementUpsideDown" - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_SensorPlacementUpsideDown}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_SensorPlacementUpsideDownDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_SensorPlacementUpsideDown" - Grid.Column="1" - HorizontalAlignment="Right" - IsEnabled="False" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_SensorPlacementUpsideDown_Toggled" /> - </Grid> - </Border> - </ui:SimpleStackPanel> - - <!-- Third-party applications --> - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_ThirdPartyApps}" /> - - <!-- RTSS --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_RivaTunerStatisticsServer}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_RivaTunerStatisticsServerDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_RTSS" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_RTSS_Toggled" /> - </Grid> - </Border> - - <!-- HWiNFO --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_HwInfo}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.SettingsPage_HwInfoDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="Toggle_HWiNFO" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="Toggle_HWiNFO_Toggled" /> - </Grid> - </Border> - </ui:SimpleStackPanel> - - </ui:SimpleStackPanel> - </Grid> +<Page + x:Class="HandheldCompanion.Views.Pages.SettingsPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" +<<<<<<< HEAD + Name="Settings" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Title="{x:Static resx:Resources.SettingsPage_Settings}" + d:Background="White" + d:DesignHeight="2400" + d:DesignWidth="1000" + KeepAlive="True" + Loaded="Page_Loaded" + mc:Ignorable="d"> + + <Grid Name="MainGrid" Margin="20"> + <ui:SimpleStackPanel Spacing="24"> + + <!-- Update manager --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="100" MinWidth="100" /> + <ColumnDefinition /> + <ColumnDefinition /> + </Grid.ColumnDefinitions> + + <Grid Grid.Column="0"> + <ui:FontIcon + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + FontSize="80" + Foreground="{DynamicResource SystemControlBackgroundBaseHighBrush}" + Glyph="" /> + <Grid Name="GridUpdateSymbol" Visibility="Collapsed"> + <ui:FontIcon + Margin="50,50,0,0" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + FontSize="30" + Foreground="{DynamicResource SystemAccentColorLight2Brush}" + Glyph="" /> + <ui:FontIcon + Margin="50,50,0,0" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + FontSize="20" + Foreground="White" + Glyph="" /> + </Grid> + </Grid> + + <ui:SimpleStackPanel + Grid.Column="1" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + Orientation="Vertical" + Spacing="6"> + <TextBlock + Name="LabelUpdate" + FontSize="20" + Foreground="{DynamicResource SystemControlBackgroundBaseHighBrush}" + Style="{StaticResource BaseTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_UpToDate}" /> + <TextBlock + Name="LabelUpdateDate" + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Text="{x:Static resx:Resources.SettingsPage_LastChecked}" /> + <ui:ProgressBar + Name="ProgressBarUpdate" + Margin="0,0,10,0" + IsIndeterminate="True" + Visibility="Collapsed" /> + </ui:SimpleStackPanel> + + <Button + Name="B_CheckUpdate" + Grid.Column="2" + HorizontalAlignment="Right" + VerticalAlignment="Center" + Click="B_CheckUpdate_Click" + Content="{x:Static resx:Resources.SettingsPage_CheckForUpdates}" + Style="{DynamicResource AccentButtonStyle}" /> + </Grid> + + <!-- Changelog --> + <TextBox + Name="CurrentChangelog" + IsReadOnly="True" + ScrollViewer.HorizontalScrollBarVisibility="Auto" + Visibility="Collapsed" /> + + <!-- Update(s) --> + <ui:SimpleStackPanel Name="CurrentUpdates" Spacing="6" /> + + <!-- General options --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_GeneralOptions}" /> + + <!-- Auto-start application --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_AutoStartApp}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_AutoStartAppDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_AutoStart" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_AutoStart_Toggled" /> + </Grid> + </Border> + + <!-- Open application in background --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_OpenAppBackground}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_OpenAppBackgroundDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_Background" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_Background_Toggled" /> + </Grid> + </Border> + + <!-- Close minimizes --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_CloseMinimizes}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_CloseMinimizesDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_CloseMinimizes" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_CloseMinimizes_Toggled" /> + </Grid> + </Border> + + <!-- Enable desktop profile on start --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_EnableDesktopProfileOnStart}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_EnableDesktopProfileOnStartDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_DesktopProfileOnStart" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_DesktopProfileOnStart_Toggled" /> + </Grid> + </Border> + + <!-- Force virtual controller order --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="9*" MinWidth="200" /> + <ColumnDefinition MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_ForceVirtualControllerOrder}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_ForceVirtualControllerOrderDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_ForceVirtualControllerOrder" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_ForceVirtualControllerOrder_Toggled" /> + </Grid> + </Border> + + <!-- Native display orientation --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_NativeDisplayOrientation}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_NativeDisplayOrientationDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <DockPanel + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Right" + ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + x:Name="Text_NativeDisplayOrientation" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_NativeDisplayOrientationNotSet}" /> + <Button + Name="Button_DetectNativeDisplayOrientation" + Width="120" + Height="40" + Margin="12,0,0,0" + HorizontalAlignment="Right" + Click="Button_DetectNativeDisplayOrientation_Click" + Content="{x:Static resx:Resources.SettingsPage_NativeDisplayOrientationDetect}" /> + </DockPanel> + </Grid> + </Border> + + <!-- Application theme --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_AppTheme}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_AppThemeDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="cB_Theme" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Stretch" + SelectionChanged="cB_Theme_SelectionChanged"> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_ThemeDefault}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_ThemeLight}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_ThemeDark}" /> + </ComboBox> + </Grid> + </Border> + + <!-- Application backdrop --> + <Border + Padding="15,12,12,12" + d:Visibility="Visible" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_Backdrop}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_BackdropDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="cB_Backdrop" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Stretch" + SelectionChanged="cB_Backdrop_SelectionChanged"> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropNone}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropMica}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropTabbed}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropAcrylic}" /> + </ComboBox> + </Grid> + </Border> + + <!-- Application language --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_AppLanguage}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_AppLanguageDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="cB_Language" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Stretch" + SelectionChanged="cB_Language_SelectionChanged" /> + </Grid> + </Border> + </ui:SimpleStackPanel> + + <!-- Quicktools options --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_QuickToolsOptions}" /> + + <!-- Quicktools position --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocation}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocationDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="cB_QuicktoolsPosition" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Stretch" + SelectionChanged="cB_QuicktoolsPosition_SelectionChanged"> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocationTopLeft}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocationTopRight}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocationBottomLeft}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_QuickToolsWindowLocationBottomRight}" /> + </ComboBox> + </Grid> + </Border> + + <!-- Application backdrop --> + <Border + Padding="15,12,12,12" + d:Visibility="Visible" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_Backdrop}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_QuickToolsBackdrop}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="cB_QuickToolsBackdrop" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Stretch" + SelectionChanged="cB_QuickToolsBackdrop_SelectionChanged"> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropNone}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropMica}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropTabbed}" /> + <ComboBoxItem Content="{x:Static resx:Resources.SettingsPage_BackdropAcrylic}" /> + </ComboBox> + </Grid> + </Border> + + <!-- Quicktools autohide --> + <!-- Disabled until we figure out a way to make it work without having to actually click on the window --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}" + Visibility="Collapsed"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_HideWhenLoseFocus}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_HideWhenLoseFocusDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_QuicktoolsAutoHide" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_QuicktoolsAutoHide_Toggled" /> + </Grid> + </Border> + </ui:SimpleStackPanel> + + <!-- Notification options --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_NotificationOptions}" /> + + <!-- Toast notification --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_ToastNotification}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_ToastNotificationDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_Notification" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_Notification_Toggled" /> + </Grid> + </Border> + </ui:SimpleStackPanel> + + <!-- Sensor options --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_SensorOptions}" /> + + <!-- Sensor Selection --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_SensorSelection}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_SensorSelectionDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="cB_SensorSelection" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Stretch" + SelectionChanged="cB_SensorSelection_SelectionChanged"> + + <ComboBoxItem + Name="SensorNone" + Content="{x:Static resx:Resources.SettingsPage_SensorNone}" + IsEnabled="True" /> + <ComboBoxItem + Name="SensorInternal" + Content="{x:Static resx:Resources.SettingsPage_SensorInternal}" + IsEnabled="False" /> + <ComboBoxItem + Name="SensorExternal" + Content="{x:Static resx:Resources.SettingsPage_SensorExternal}" + IsEnabled="False" /> + <ComboBoxItem + Name="SensorController" + Content="{x:Static resx:Resources.MainWindow_navController}" + IsEnabled="False" /> + </ComboBox> + </Grid> + </Border> + + <!-- Sensor Placement Direction --> + <Border + Name="SensorPlacementVisualisation" + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_SensorPlacementDirection}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_SensorPlacementDirectionDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <Grid Grid.Column="1"> + <ui:SimpleStackPanel + Name="Grid_SensorPlacementVisualisation" + HorizontalAlignment="Right" + IsEnabled="False" + Spacing="6"> + <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> + <Button + Width="50" + Height="50" + Tag="99" + Visibility="Hidden" /> + <Button + Width="50" + Height="50" + Click="SensorPlacement_Click" + Tag="0" /> + <Button + Width="50" + Height="50" + Tag="99" + Visibility="Hidden" /> + </ui:SimpleStackPanel> + <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> + <Button + Width="50" + Height="50" + Click="SensorPlacement_Click" + Tag="1" /> + <Button + Width="50" + Height="50" + Tag="99" + Visibility="Hidden" /> + <Button + Width="50" + Height="50" + Click="SensorPlacement_Click" + Tag="2" /> + </ui:SimpleStackPanel> + <ui:SimpleStackPanel Orientation="Horizontal" Spacing="6"> + <Button + Width="50" + Height="50" + Tag="99" + Visibility="Hidden" /> + <Button + Width="50" + Height="50" + Click="SensorPlacement_Click" + Tag="3" /> + <Button + Width="50" + Height="50" + Tag="99" + Visibility="Hidden" /> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Grid> + </Grid> + </Border> + + <!-- Sensor Placement Mirror --> + <Border + Name="SensorPlacementUpsideDown" + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_SensorPlacementUpsideDown}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_SensorPlacementUpsideDownDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_SensorPlacementUpsideDown" + Grid.Column="1" + HorizontalAlignment="Right" + IsEnabled="False" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_SensorPlacementUpsideDown_Toggled" /> + </Grid> + </Border> + </ui:SimpleStackPanel> + + <!-- Third-party applications --> + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_ThirdPartyApps}" /> + + <!-- RTSS --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_RivaTunerStatisticsServer}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_RivaTunerStatisticsServerDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_RTSS" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_RTSS_Toggled" /> + </Grid> + </Border> + + <!-- HWiNFO --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.SettingsPage_HwInfo}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.SettingsPage_HwInfoDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="Toggle_HWiNFO" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_HWiNFO_Toggled" /> + </Grid> + </Border> + </ui:SimpleStackPanel> + + </ui:SimpleStackPanel> + </Grid> </Page> \ No newline at end of file diff --git a/HandheldCompanion/Views/Pages/SettingsPage.xaml.cs b/HandheldCompanion/Views/Pages/SettingsPage.xaml.cs index 435b84cca..621f9cb68 100644 --- a/HandheldCompanion/Views/Pages/SettingsPage.xaml.cs +++ b/HandheldCompanion/Views/Pages/SettingsPage.xaml.cs @@ -10,6 +10,11 @@ using Nefarius.Utilities.DeviceManagement.PnP; using System; using System.Collections.Generic; +<<<<<<< HEAD +======= +using System.Collections.Specialized; +using System.Diagnostics; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d using System.Globalization; using System.Threading; using System.Windows; @@ -183,6 +188,9 @@ private void SettingsManager_SettingValueChanged(string? name, object value) case "DesktopProfileOnStart": Toggle_DesktopProfileOnStart.IsOn = Convert.ToBoolean(value); break; + case "VirtualControllerForceOrder": + Toggle_ForceVirtualControllerOrder.IsOn = Convert.ToBoolean(value); + break; case "NativeDisplayOrientation": var nativeOrientation = (ScreenRotation.Rotations)Convert.ToInt32(value); @@ -264,6 +272,26 @@ private async void Toggle_AutoStart_Toggled(object? sender, RoutedEventArgs? e) if (!IsLoaded) return; + if (!Toggle_AutoStart.IsOn && SettingsManager.GetBoolean("VirtualControllerForceOrder")) + { + var result = Dialog.ShowAsync(Properties.Resources.SettingsPage_VirtualControllerForceOrderDependencyTitle, + Properties.Resources.SettingsPage_VirtualControllerForceOrderDependencyText, + ContentDialogButton.Primary, null, + Properties.Resources.SettingsPage_VirtualControllerForceOrderDependencyPrimary, + Properties.Resources.SettingsPage_VirtualControllerForceOrderDependencySecondary); + + await result; + + switch (result.Result) + { + case ContentDialogResult.Primary: + SettingsManager.SetProperty("VirtualControllerForceOrder", false); + break; + Toggle_AutoStart.IsOn = true; + break; + } + } + SettingsManager.SetProperty("RunAtStartup", Toggle_AutoStart.IsOn); } @@ -291,6 +319,58 @@ private void Toggle_DesktopProfileOnStart_Toggled(object? sender, RoutedEventArg SettingsManager.SetProperty("DesktopProfileOnStart", Toggle_DesktopProfileOnStart.IsOn); } + private async void Toggle_ForceVirtualControllerOrder_Toggled(object sender, RoutedEventArgs e) + { + var ForceVirtualControllerOrder = SettingsManager.GetBoolean("VirtualControllerForceOrder"); + + if (Toggle_ForceVirtualControllerOrder.IsOn && !ForceVirtualControllerOrder) + { + var physicalControllerInstanceIds = new StringCollection(); + var physicalControllers = ControllerManager.GetPhysicalControllers(); + + foreach (var physicalController in physicalControllers) + { + physicalControllerInstanceIds.Add(physicalController.Details.baseContainerDeviceInstanceId); + } + + SettingsManager.SetProperty("PhysicalControllerInstanceIds", physicalControllerInstanceIds); + + var result = Dialog.ShowAsync($"{Properties.Resources.SettingsPage_ForceVirtualControllerOrderTitle}", + $"{Properties.Resources.SettingsPage_ForceVirtualControllerOrderText}", + ContentDialogButton.Primary, null, + $"{Properties.Resources.SettingsPage_ForceVirtualControllerOrderPrimary}", + $"{Properties.Resources.SettingsPage_ForceVirtualControllerOrderSecondary}"); + + await result; + + switch (result.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; + } + } + + // RunAtStartup is required for this feature + if (Toggle_ForceVirtualControllerOrder.IsOn) + { + SettingsManager.SetProperty("RunAtStartup", true); + } + + + SettingsManager.SetProperty("VirtualControllerForceOrder", Toggle_ForceVirtualControllerOrder.IsOn); + } + private void Button_DetectNativeDisplayOrientation_Click(object sender, RoutedEventArgs? e) { if (!IsLoaded) diff --git a/HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml b/HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml index c71cc5762..f4ea6a374 100644 --- a/HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml +++ b/HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml @@ -1,544 +1,691 @@ -<Page - x:Class="HandheldCompanion.Views.QuickPages.QuickPerformancePage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Name="QuickPerformance" - Title="{x:Static resx:Resources.QuickPerformancePage_Title}" - Margin="15,0,0,0" - d:Background="White" - d:DesignHeight="1500" - d:DesignWidth="640" - KeepAlive="True" - mc:Ignorable="d"> - - <ui:SimpleStackPanel Margin="0,6,0,6"> - - <ui:SimpleStackPanel Spacing="6"> - <TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="Power settings" /> - - <!-- Maximum CPU Count --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <ui:SimpleStackPanel Name="StackProfileCPUCore" Spacing="6"> - <Grid> - <ui:SimpleStackPanel VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Maximum CPU Count" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="Controls CPU unparked core count limit" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - - <ui:ToggleSwitch - Name="CPUCoreToggle" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="CPUCoreToggle_Toggled" /> - </Grid> - - <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=CPUCoreToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> - - <StackPanel Visibility="{Binding ElementName=CPUCoreToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="Cores" /> - <DockPanel Margin="8,0,0,0" ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N0, ElementName=CPUCoreSlider, Mode=OneWay}" - TextAlignment="Center" /> - <TextBlock - Width="30" - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="C" /> - <Slider - x:Name="CPUCoreSlider" - Margin="8,0,0,0" - VerticalAlignment="Center" - AutoToolTipPrecision="0" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - Minimum="2" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="CPUCoreSlider_ValueChanged" /> - </DockPanel> - </StackPanel> - </ui:SimpleStackPanel> - </Border> - - <!-- Thermal Power (TDP) Limit --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <ui:SimpleStackPanel - Name="StackProfileTDP" - IsEnabled="False" - Spacing="6"> - - <Grid> - <ui:SimpleStackPanel VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_TDPOverride}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ProfilesPage_TDPOverrideDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - - <ui:ToggleSwitch - Name="TDPToggle" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="TDPToggle_Toggled" /> - </Grid> - - <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=TDPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> - - <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=TDPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="{x:Static resx:Resources.QuickProfilesPage_PowerLimitTarget}" /> - <DockPanel Margin="8,0,0,0" ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N0, ElementName=TDPSlider, Mode=OneWay}" - TextAlignment="Center" /> - <TextBlock - Width="30" - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="{x:Static resx:Resources.QuickPerformancePage_TDPUnitWatt}" /> - <Slider - x:Name="TDPSlider" - Margin="8,0,0,0" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - AutoToolTipPrecision="0" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="5" - SmallChange="1" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="TDPSlider_ValueChanged" /> - </DockPanel> - </StackPanel> - </ui:SimpleStackPanel> - </Border> - - <!-- Auto TDP --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <ui:SimpleStackPanel - Name="StackProfileAutoTDP" - IsEnabled="False" - Spacing="6"> - - <Grid> - <ui:SimpleStackPanel VerticalAlignment="Center"> - <TextBlock Text="{x:Static resx:Resources.ProfilesPage_AutoTDP}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ProfilesPage_AutoTDPDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - - <ui:ToggleSwitch - Name="AutoTDPToggle" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="AutoTDPToggle_Toggled" /> - </Grid> - - <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=AutoTDPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> - - <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=AutoTDPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> - - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="{x:Static resx:Resources.ProfilesPage_AutoTDPFPS}" /> - - <DockPanel Margin="8,0,0,0" ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, StringFormat=N0, ElementName=AutoTDPRequestedFPSSlider, Mode=OneWay}" - TextAlignment="Center" /> - <TextBlock - Width="30" - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="{x:Static resx:Resources.QuickPerformancePage_AutoTDPUnitFPS}" /> - <Slider - x:Name="AutoTDPRequestedFPSSlider" - Margin="8,0,0,0" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - AutoToolTipPrecision="0" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="5" - Maximum="120" - Minimum="20" - SmallChange="1" - Style="{DynamicResource SliderStyle1}" - TickFrequency="1" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="AutoTDPRequestedFPSSlider_ValueChanged" - Value="30" /> - </DockPanel> - </StackPanel> - </ui:SimpleStackPanel> - </Border> - - <!-- Processor energy performance preference policy --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <ui:SimpleStackPanel Name="StackProfileEPP" Spacing="6"> - - <Grid> - <ui:SimpleStackPanel VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_EPP}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.ProfilesPage_EPPDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - - <ui:ToggleSwitch - Name="EPPToggle" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="EPPToggle_Toggled" /> - </Grid> - - <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=EPPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> - - <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=EPPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="{x:Static resx:Resources.ProfilesPage_EPPBalance}" /> - <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> - - <ui:SimpleStackPanel - Margin="0,0,0,0" - VerticalAlignment="Center" - ScrollViewer.PanningMode="HorizontalOnly"> - <Slider - x:Name="EPPSlider" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - AutoToolTipPrecision="0" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="10" - Maximum="100" - Minimum="0" - SmallChange="1" - Style="{DynamicResource SliderStyle1}" - TickFrequency="10" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="EPPSlider_ValueChanged" /> - - <Grid Name="EPPGrid"> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="*" /> - <ColumnDefinition Width="*" /> - <ColumnDefinition Width="*" /> - <ColumnDefinition Width="*" /> - </Grid.ColumnDefinitions> - - <TextBlock - Grid.Column="1" - HorizontalAlignment="Left" - Text="{x:Static resx:Resources.ProfilesPage_CPU}" /> - <TextBlock - Grid.Column="2" - HorizontalAlignment="Right" - Text="{x:Static resx:Resources.ProfilesPage_GPU}" /> - </Grid> - </ui:SimpleStackPanel> - </DockPanel> - </StackPanel> - </ui:SimpleStackPanel> - </Border> - - <!-- Manual CPU Clock Control --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <ui:SimpleStackPanel Name="StackProfileCPUClock" Spacing="6"> - - <Grid> - <ui:SimpleStackPanel VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_CPUControl}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.QuickPerformancePage_CPUControlDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - - <ui:ToggleSwitch - Name="CPUToggle" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="CPUToggle_Toggled" /> - </Grid> - - <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=CPUToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> - - <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=CPUToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="{x:Static resx:Resources.ProfilesPage_CPUMhz}" /> - <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, ElementName=CPUSlider, Mode=OneWay}" - TextAlignment="Center" /> - <TextBlock - Width="30" - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="{x:Static resx:Resources.QuickPerformancePage_CPUUnit}" /> - <Slider - x:Name="CPUSlider" - Margin="8,0,0,0" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - AutoToolTipPrecision="0" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="500" - SmallChange="100" - Style="{DynamicResource SliderStyle1}" - TickFrequency="100" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="CPUSlider_ValueChanged" /> - </DockPanel> - </StackPanel> - </ui:SimpleStackPanel> - </Border> - - <!-- Manual GPU Clock Control --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <ui:SimpleStackPanel - Name="StackProfileGPUClock" - IsEnabled="False" - Spacing="6"> - - <Grid> - <ui:SimpleStackPanel VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_GPUControl}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.QuickPerformancePage_GPUControlDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - - <ui:ToggleSwitch - Name="GPUToggle" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="GPUToggle_Toggled" /> - </Grid> - - <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=GPUToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> - - <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=GPUToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> - <TextBlock - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="{x:Static resx:Resources.ProfilesPage_GPUMhz}" /> - <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> - <TextBlock - Width="35" - VerticalAlignment="Center" - Text="{Binding Value, ElementName=GPUSlider, Mode=OneWay}" - TextAlignment="Center" /> - <TextBlock - Width="30" - HorizontalAlignment="Left" - VerticalAlignment="Center" - Text="{x:Static resx:Resources.QuickPerformancePage_GPUUnit}" /> - <Slider - x:Name="GPUSlider" - Margin="8,0,0,0" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - AutoToolTipPrecision="0" - IsMoveToPointEnabled="True" - IsSnapToTickEnabled="True" - LargeChange="500" - SmallChange="100" - Style="{DynamicResource SliderStyle1}" - TickFrequency="100" - TickPlacement="BottomRight" - ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" - ValueChanged="GPUSlider_ValueChanged" /> - </DockPanel> - </StackPanel> - </ui:SimpleStackPanel> - </Border> - - <!-- CPU Boost --> - <Border - Name="StackProfileCPUBoost" - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - <Grid> - <ui:SimpleStackPanel VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_CPUBoostMode}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.QuickPerformancePage_CPUBoostModeDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - - <ui:ToggleSwitch - Name="CPUBoostToggle" - Grid.Column="1" - HorizontalAlignment="Right" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="CPUBoostToggle_Toggled" /> - </Grid> - </Border> - - <!-- Power mode --> - <Border - Name="StackProfilePowerMode" - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <ui:SimpleStackPanel VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_PowerMode}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.QuickPerformancePage_PowerModeDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - - <DockPanel Grid.Column="1" Margin="12,0,0,0"> - <ComboBox - Name="PowerMode" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - SelectedIndex="0" - SelectionChanged="PowerMode_SelectionChanged"> - <ComboBoxItem Name="PowerModeEfficiency" Content="{x:Static resx:Resources.QuickPerformancePage_PowerModeEfficiency}" /> - <ComboBoxItem Name="PowerModeBalanced" Content="{x:Static resx:Resources.QuickPerformancePage_PowerModeBalanced}" /> - <ComboBoxItem Name="PowerModePerformance" Content="{x:Static resx:Resources.QuickPerformancePage_PowerModePerformance}" /> - </ComboBox> - </DockPanel> - </Grid> - </Border> - - <!-- Fan mode --> - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <StackPanel Orientation="Vertical"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Fan mode" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="Change the power profile fan mode" - TextWrapping="Wrap" /> - </StackPanel> - - <DockPanel Grid.Column="1" Margin="12,0,0,0"> - <ComboBox - Name="FanMode" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - SelectedIndex="0" - SelectionChanged="FanMode_SelectionChanged"> - <ComboBoxItem Name="FanModeHardware" Content="Hardware" /> - <ComboBoxItem Name="FanModeSoftware" Content="Software" /> - </ComboBox> - </DockPanel> - </Grid> - </Border> - - <Border Padding="0,12,0,12"> - <Grid> - <Button - Name="Button_PowerSettings_Delete" - HorizontalAlignment="Stretch" - VerticalAlignment="Center" - Click="Button_PowerSettings_Delete_Click" - Style="{DynamicResource AccentButtonStyle}"> - <TextBlock Text="{x:Static resx:Resources.ProfilesPage_DeletePowerProfileButton}" /> - </Button> - </Grid> - </Border> - </ui:SimpleStackPanel> - </ui:SimpleStackPanel> +<Page + x:Class="HandheldCompanion.Views.QuickPages.QuickPerformancePage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" +<<<<<<< HEAD + Name="QuickPerformance" + Title="{x:Static resx:Resources.QuickPerformancePage_Title}" +======= + Title="Performance" +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Margin="15,0,0,0" + d:Background="White" + d:DesignHeight="1500" + d:DesignWidth="640" + KeepAlive="True" + mc:Ignorable="d"> + +<<<<<<< HEAD + <ui:SimpleStackPanel Margin="0,6,0,6"> + + <ui:SimpleStackPanel Spacing="6"> + <TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="Power settings" /> +======= + <ui:SimpleStackPanel Margin="15,6,15,6" Spacing="10"> + <ui:SimpleStackPanel Spacing="6"> + <!-- Header --> + <Border CornerRadius="{DynamicResource ControlCornerRadius}"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + <!-- Maximum CPU Count --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <ui:SimpleStackPanel Name="StackProfileCPUCore" Spacing="6"> + <Grid> + <ui:SimpleStackPanel VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Maximum CPU Count" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="Controls CPU unparked core count limit" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + +<<<<<<< HEAD + <ui:ToggleSwitch + Name="CPUCoreToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="CPUCoreToggle_Toggled" /> + </Grid> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=CPUCoreToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> + + <StackPanel Visibility="{Binding ElementName=CPUCoreToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> +======= + <!-- Content --> + <Border CornerRadius="{DynamicResource ControlCornerRadius}"> + <ComboBox + Name="ComboBoxOverlayDisplayLevel" + HorizontalAlignment="Stretch" + IsEnabled="False" + IsReadOnly="True" + SelectionChanged="ComboBoxOverlayDisplayLevel_SelectionChanged"> + + <ComboBoxItem Name="OverlayDisplayLevelDisabled" Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Disabled}" /> + <ComboBoxItem Name="OverlayDisplayLevelMinimal" Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Minimal}" /> + <ComboBoxItem + Name="OverlayDisplayLevelExtended" + Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Extended}" + IsEnabled="False" /> + <ComboBoxItem + Name="OverlayDisplayLevelFull" + Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_Full}" + IsEnabled="False" /> + <ComboBoxItem Name="OverlayDisplayLevelExternal" Content="{x:Static resx:Resources.OverlayPage_OverlayDisplayLevel_External}" /> + </ComboBox> + </Border> + </ui:SimpleStackPanel> + + <!-- Refresh rate --> + <ui:SimpleStackPanel Spacing="6"> + <!-- Header --> + <Border CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="Segoe UI Symbol" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_DisplayResolutionRefreshRate}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.QuickPerformancePage_DisplayResolutionRefreshRateDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + </Grid> + </Border> + + <!-- Content --> + <Border CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="7*" /> + <ColumnDefinition Width="3*" /> + </Grid.ColumnDefinitions> + + <ComboBox + Name="ComboBoxResolution" + Grid.Column="0" + HorizontalAlignment="Stretch" + IsReadOnly="True" + SelectionChanged="ComboBoxResolution_SelectionChanged" /> + <ComboBox + Name="ComboBoxFrequency" + Grid.Column="1" + Margin="6,0,0,0" + HorizontalAlignment="Stretch" + IsReadOnly="True" + SelectionChanged="ComboBoxFrequency_SelectionChanged" /> + </Grid> + </Border> + </ui:SimpleStackPanel> + + <!-- Quiet mode --> + <Border CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_FanOverride}" /> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="Cores" /> + <DockPanel Margin="8,0,0,0" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=CPUCoreSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="C" /> + <Slider + x:Name="CPUCoreSlider" + Margin="8,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Minimum="2" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="CPUCoreSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + </Border> + + <!-- Thermal Power (TDP) Limit --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <ui:SimpleStackPanel + Name="StackProfileTDP" + IsEnabled="False" + Spacing="6"> + + <Grid> + <ui:SimpleStackPanel VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_TDPOverride}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_TDPOverrideDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + +<<<<<<< HEAD + <ui:ToggleSwitch + Name="TDPToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="TDPToggle_Toggled" /> + </Grid> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=TDPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> + + <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=TDPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickProfilesPage_PowerLimitTarget}" /> + <DockPanel Margin="8,0,0,0" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=TDPSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_TDPUnitWatt}" /> + <Slider + x:Name="TDPSlider" + Margin="8,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="5" + SmallChange="1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="TDPSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + </Border> +======= + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, ElementName=QuietModeSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_FanOverridePercentage}" /> + <Slider + x:Name="QuietModeSlider" + Margin="8,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Maximum="100" + Minimum="10" + Style="{DynamicResource SliderStyle1}" + TickFrequency="2" + TickPlacement="BottomRight" + ValueChanged="QuietModeSlider_ValueChanged" /> + </DockPanel> + </Border> + + <!-- Quiet mode --> + <Border CornerRadius="{DynamicResource ControlCornerRadius}"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + <!-- Auto TDP --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <ui:SimpleStackPanel + Name="StackProfileAutoTDP" + IsEnabled="False" + Spacing="6"> + + <Grid> + <ui:SimpleStackPanel VerticalAlignment="Center"> + <TextBlock Text="{x:Static resx:Resources.ProfilesPage_AutoTDP}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_AutoTDPDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + + <ui:ToggleSwitch + Name="AutoTDPToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="AutoTDPToggle_Toggled" /> + </Grid> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=AutoTDPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> + + <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=AutoTDPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_AutoTDPFPS}" /> + + <DockPanel Margin="8,0,0,0" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=AutoTDPRequestedFPSSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_AutoTDPUnitFPS}" /> + <Slider + x:Name="AutoTDPRequestedFPSSlider" + Margin="8,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="5" + Maximum="120" + Minimum="20" + SmallChange="1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="AutoTDPRequestedFPSSlider_ValueChanged" + Value="30" /> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + </Border> + + <!-- Processor energy performance preference policy --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <ui:SimpleStackPanel Name="StackProfileEPP" Spacing="6"> + + <Grid> + <ui:SimpleStackPanel VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_EPP}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_EPPDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + + <ui:ToggleSwitch + Name="EPPToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="EPPToggle_Toggled" /> + </Grid> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=EPPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> + + <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=EPPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_EPPBalance}" /> + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + + <ui:SimpleStackPanel + Margin="0,0,0,0" + VerticalAlignment="Center" + ScrollViewer.PanningMode="HorizontalOnly"> + <Slider + x:Name="EPPSlider" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="10" + Maximum="100" + Minimum="0" + SmallChange="1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="10" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="EPPSlider_ValueChanged" /> + + <Grid Name="EPPGrid"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + + <TextBlock + Grid.Column="1" + HorizontalAlignment="Left" + Text="{x:Static resx:Resources.ProfilesPage_CPU}" /> + <TextBlock + Grid.Column="2" + HorizontalAlignment="Right" + Text="{x:Static resx:Resources.ProfilesPage_GPU}" /> + </Grid> + </ui:SimpleStackPanel> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + </Border> + + <!-- Manual CPU Clock Control --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <ui:SimpleStackPanel Name="StackProfileCPUClock" Spacing="6"> + + <Grid> + <ui:SimpleStackPanel VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_CPUControl}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.QuickPerformancePage_CPUControlDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + + <ui:ToggleSwitch + Name="CPUToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="CPUToggle_Toggled" /> + </Grid> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=CPUToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> + + <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=CPUToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_CPUMhz}" /> + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, ElementName=CPUSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_CPUUnit}" /> + <Slider + x:Name="CPUSlider" + Margin="8,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="500" + SmallChange="100" + Style="{DynamicResource SliderStyle1}" + TickFrequency="100" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="CPUSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + </Border> + + <!-- Manual GPU Clock Control --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <ui:SimpleStackPanel + Name="StackProfileGPUClock" + IsEnabled="False" + Spacing="6"> + + <Grid> + <ui:SimpleStackPanel VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_GPUControl}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.QuickPerformancePage_GPUControlDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + + <ui:ToggleSwitch + Name="GPUToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="GPUToggle_Toggled" /> + </Grid> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" Visibility="{Binding ElementName=GPUToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}" /> + + <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=GPUToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_GPUMhz}" /> + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, ElementName=GPUSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_GPUUnit}" /> + <Slider + x:Name="GPUSlider" + Margin="8,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="500" + SmallChange="100" + Style="{DynamicResource SliderStyle1}" + TickFrequency="100" + TickPlacement="BottomRight" + ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" + ValueChanged="GPUSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + </Border> + + <!-- CPU Boost --> + <Border + Name="StackProfileCPUBoost" + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + <Grid> + <ui:SimpleStackPanel VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_CPUBoostMode}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.QuickPerformancePage_CPUBoostModeDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + + <ui:ToggleSwitch + Name="CPUBoostToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="CPUBoostToggle_Toggled" /> + </Grid> + </Border> + +<<<<<<< HEAD + <!-- Power mode --> + <Border + Name="StackProfilePowerMode" + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> +======= + <!-- Power mode --> + <ui:SimpleStackPanel Spacing="6"> + <!-- Header --> + <Border CornerRadius="{DynamicResource ControlCornerRadius}"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <ui:SimpleStackPanel VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_PowerMode}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.QuickPerformancePage_PowerModeDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + + <DockPanel Grid.Column="1" Margin="12,0,0,0"> + <ComboBox + Name="PowerMode" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + SelectedIndex="0" + SelectionChanged="PowerMode_SelectionChanged"> + <ComboBoxItem Name="PowerModeEfficiency" Content="{x:Static resx:Resources.QuickPerformancePage_PowerModeEfficiency}" /> + <ComboBoxItem Name="PowerModeBalanced" Content="{x:Static resx:Resources.QuickPerformancePage_PowerModeBalanced}" /> + <ComboBoxItem Name="PowerModePerformance" Content="{x:Static resx:Resources.QuickPerformancePage_PowerModePerformance}" /> + </ComboBox> + </DockPanel> + </Grid> + </Border> + +<<<<<<< HEAD + <!-- Fan mode --> + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> +======= + <!-- Content --> + <Border CornerRadius="{DynamicResource ControlCornerRadius}"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <StackPanel Orientation="Vertical"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Fan mode" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="Change the power profile fan mode" + TextWrapping="Wrap" /> + </StackPanel> + + <DockPanel Grid.Column="1" Margin="12,0,0,0"> + <ComboBox + Name="FanMode" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + SelectedIndex="0" + SelectionChanged="FanMode_SelectionChanged"> + <ComboBoxItem Name="FanModeHardware" Content="Hardware" /> + <ComboBoxItem Name="FanModeSoftware" Content="Software" /> + </ComboBox> + </DockPanel> + </Grid> + </Border> + + <Border Padding="0,12,0,12"> + <Grid> + <Button + Name="Button_PowerSettings_Delete" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + Click="Button_PowerSettings_Delete_Click" + Style="{DynamicResource AccentButtonStyle}"> + <TextBlock Text="{x:Static resx:Resources.ProfilesPage_DeletePowerProfileButton}" /> + </Button> + </Grid> + </Border> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> </Page> \ No newline at end of file diff --git a/HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml.cs b/HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml.cs index 7397049bd..a7ca3fb1f 100644 --- a/HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml.cs +++ b/HandheldCompanion/Views/QuickPages/QuickPerformancePage.xaml.cs @@ -1,541 +1,557 @@ -using HandheldCompanion.Devices; -using HandheldCompanion.Managers; -using HandheldCompanion.Misc; -using HandheldCompanion.Platforms; -using HandheldCompanion.Processors; -using HandheldCompanion.Utils; -using Inkore.UI.WPF.Modern.Controls; -using System; -using System.Timers; -using System.Windows; -using System.Windows.Controls; -using Page = System.Windows.Controls.Page; - -namespace HandheldCompanion.Views.QuickPages; - -/// <summary> -/// Interaction logic for QuickPerformancePage.xaml -/// </summary> -public partial class QuickPerformancePage : Page -{ - private const int UpdateInterval = 500; - private readonly Timer UpdateTimer; - private PowerProfile selectedProfile; - - private LockObject updateLock = new(); - - public QuickPerformancePage(string Tag) : this() - { - this.Tag = Tag; - } - - public QuickPerformancePage() - { - InitializeComponent(); - - /* - MainWindow.performanceManager.PowerModeChanged += PerformanceManager_PowerModeChanged; - MainWindow.performanceManager.PerfBoostModeChanged += PerformanceManager_PerfBoostModeChanged; - MainWindow.performanceManager.EPPChanged += PerformanceManager_EPPChanged; - */ - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - - PlatformManager.RTSS.Updated += RTSS_Updated; - - MainWindow.performanceManager.ProcessorStatusChanged += PerformanceManager_StatusChanged; - MainWindow.performanceManager.EPPChanged += PerformanceManager_EPPChanged; - MainWindow.performanceManager.Initialized += PerformanceManager_Initialized; - - HotkeysManager.CommandExecuted += HotkeysManager_CommandExecuted; - - PowerProfileManager.Updated += PowerProfileManager_Updated; - PowerProfileManager.Deleted += PowerProfileManager_Deleted; - - // device settings - GPUSlider.Minimum = MainWindow.CurrentDevice.GfxClock[0]; - GPUSlider.Maximum = MainWindow.CurrentDevice.GfxClock[1]; - - CPUSlider.Minimum = MotherboardInfo.ProcessorMaxTurboSpeed / 4.0d; - CPUSlider.Maximum = MotherboardInfo.ProcessorMaxTurboSpeed; - - // motherboard settings - CPUCoreSlider.Maximum = MotherboardInfo.NumberOfCores; - - FanModeSoftware.IsEnabled = MainWindow.CurrentDevice.Capabilities.HasFlag(DeviceCapabilities.FanControl); - - UpdateTimer = new Timer(UpdateInterval); - UpdateTimer.AutoReset = false; - UpdateTimer.Elapsed += (sender, e) => SubmitProfile(); - - // force call - RTSS_Updated(PlatformManager.RTSS.Status); - } - - private void HotkeysManager_CommandExecuted(string listener) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (listener) - { - case "increaseTDP": - { - if (selectedProfile is null || !selectedProfile.TDPOverrideEnabled) - return; - - TDPSlider.Value++; - } - break; - case "decreaseTDP": - { - if (selectedProfile is null || !selectedProfile.TDPOverrideEnabled) - return; - - TDPSlider.Value--; - } - break; - } - }); - } - - private void PowerProfileManager_Deleted(PowerProfile profile) - { - // current power profile deleted, return to previous page - bool isCurrent = selectedProfile?.Guid == profile.Guid; - if (isCurrent) - MainWindow.overlayquickTools.ContentFrame.GoBack(); - } - - private void PowerProfileManager_Updated(PowerProfile profile, UpdateSource source) - { - if (selectedProfile is null) - return; - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // current power profile updated, update UI - bool isCurrent = selectedProfile.Guid == profile.Guid; - if (isCurrent) - UpdateUI(); - }); - } - - private void RTSS_Updated(PlatformStatus status) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (status) - { - case PlatformStatus.Ready: - var Processor = MainWindow.performanceManager.GetProcessor(); - StackProfileAutoTDP.IsEnabled = true && Processor is not null ? Processor.CanChangeTDP : false; - break; - case PlatformStatus.Stalled: - // StackProfileFramerate.IsEnabled = false; - // StackProfileAutoTDP.IsEnabled = false; - break; - } - }); - } - - public void UpdateProfile() - { - if (UpdateTimer is not null) - { - UpdateTimer.Stop(); - UpdateTimer.Start(); - } - } - - public void SubmitProfile(UpdateSource source = UpdateSource.ProfilesPage) - { - if (selectedProfile is null) - return; - - PowerProfileManager.UpdateOrCreateProfile(selectedProfile, source); - } - - private void PerformanceManager_StatusChanged(bool CanChangeTDP, bool CanChangeGPU) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - StackProfileTDP.IsEnabled = CanChangeTDP; - StackProfileAutoTDP.IsEnabled = CanChangeTDP && PlatformManager.RTSS.IsInstalled; - - StackProfileGPUClock.IsEnabled = CanChangeGPU; - }); - } - - private void PerformanceManager_EPPChanged(uint EPP) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - EPPSlider.Value = EPP; - }); - } - - private void PerformanceManager_Initialized() - { - Processor processor = MainWindow.performanceManager.GetProcessor(); - if (processor is null) - return; - - PerformanceManager_StatusChanged(processor.CanChangeTDP, processor.CanChangeGPU); - } - - /* - private void PerformanceManager_PowerModeChanged(int idx) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => { PowerModeSlider.Value = idx; }); - } - - private void PerformanceManager_PerfBoostModeChanged(bool value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => { CPUBoostToggle.IsOn = value; }); - } - */ - - private void SettingsManager_SettingValueChanged(string name, object value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (name) - { - case "ConfigurableTDPOverrideDown": - { - using (new ScopedLock(updateLock)) - { - TDPSlider.Minimum = (double)value; - } - } - break; - case "ConfigurableTDPOverrideUp": - { - using (new ScopedLock(updateLock)) - { - TDPSlider.Maximum = (double)value; - } - } - break; - } - }); - } - - private void PowerMode_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (PowerMode.SelectedIndex == -1) - return; - - if (selectedProfile is null) - return; - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.OSPowerMode = PerformanceManager.PowerModes[PowerMode.SelectedIndex]; - UpdateProfile(); - } - - private void CPUBoostToggle_Toggled(object sender, RoutedEventArgs e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.CPUBoostEnabled = CPUBoostToggle.IsOn; - UpdateProfile(); - } - - public void SelectionChanged(Guid guid) - { - // if an update is pending, cut it short, it will disturb profile selection though - // keep me ? - if (UpdateTimer.Enabled) - { - UpdateTimer.Stop(); - SubmitProfile(); - } - - selectedProfile = PowerProfileManager.GetProfile(guid); - UpdateUI(); - } - - private void UpdateUI() - { - if (selectedProfile is null) - return; - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - using (new ScopedLock(updateLock)) - { - switch (selectedProfile.Default) - { - case true: - // we shouldn't allow users to mess with default profile fan mode - FanMode.IsEnabled = false; - break; - case false: - FanMode.IsEnabled = true; - break; - } - - // we shouldn't allow users to modify some of default profile settings - Button_PowerSettings_Delete.IsEnabled = !selectedProfile.Default; - - // page name - this.Title = selectedProfile.Name; - - // TDP - TDPToggle.IsOn = selectedProfile.TDPOverrideEnabled; - var TDP = selectedProfile.TDPOverrideValues is not null - ? selectedProfile.TDPOverrideValues - : MainWindow.CurrentDevice.nTDP; - TDPSlider.Value = TDP[(int)PowerType.Slow]; - - // CPU Clock control - CPUToggle.IsOn = selectedProfile.CPUOverrideEnabled; - CPUSlider.Value = selectedProfile.CPUOverrideValue != 0 ? selectedProfile.CPUOverrideValue : MotherboardInfo.ProcessorMaxTurboSpeed; - - // GPU Clock control - GPUToggle.IsOn = selectedProfile.GPUOverrideEnabled; - GPUSlider.Value = selectedProfile.GPUOverrideValue != 0 ? selectedProfile.GPUOverrideValue : 255 * 50; - - // AutoTDP - AutoTDPToggle.IsOn = selectedProfile.AutoTDPEnabled; - AutoTDPRequestedFPSSlider.Value = selectedProfile.AutoTDPRequestedFPS; - - // EPP - EPPToggle.IsOn = selectedProfile.EPPOverrideEnabled; - EPPSlider.Value = selectedProfile.EPPOverrideValue; - - // CPU Core Count - CPUCoreToggle.IsOn = selectedProfile.CPUCoreEnabled; - CPUCoreSlider.Value = selectedProfile.CPUCoreCount; - - // CPU Boost - CPUBoostToggle.IsOn = selectedProfile.CPUBoostEnabled; - - // Power Mode - PowerMode.SelectedIndex = Array.IndexOf(PerformanceManager.PowerModes, selectedProfile.OSPowerMode); - - // Fan control - FanMode.SelectedIndex = (int)selectedProfile.FanProfile.fanMode; - } - }); - } - - private void TDPToggle_Toggled(object sender, RoutedEventArgs e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.TDPOverrideEnabled = TDPToggle.IsOn; - selectedProfile.TDPOverrideValues = new double[3] - { - TDPSlider.Value, - TDPSlider.Value, - TDPSlider.Value - }; - - UpdateProfile(); - } - - private void TDPSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.TDPOverrideValues = new double[3] - { - TDPSlider.Value, - TDPSlider.Value, - TDPSlider.Value - }; - - UpdateProfile(); - } - - private void AutoTDPToggle_Toggled(object sender, RoutedEventArgs e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.AutoTDPEnabled = AutoTDPToggle.IsOn; - AutoTDPRequestedFPSSlider.Value = selectedProfile.AutoTDPRequestedFPS; - - UpdateProfile(); - } - - private void AutoTDPRequestedFPSSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.AutoTDPRequestedFPS = (int)AutoTDPRequestedFPSSlider.Value; - UpdateProfile(); - } - - private void CPUToggle_Toggled(object sender, RoutedEventArgs e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.CPUOverrideEnabled = CPUToggle.IsOn; - selectedProfile.CPUOverrideValue = (int)CPUSlider.Value; - UpdateProfile(); - } - - private void CPUSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.CPUOverrideValue = (int)CPUSlider.Value; - UpdateProfile(); - } - - private void GPUToggle_Toggled(object sender, RoutedEventArgs e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.GPUOverrideEnabled = GPUToggle.IsOn; - selectedProfile.GPUOverrideValue = (int)GPUSlider.Value; - UpdateProfile(); - } - - private void GPUSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.GPUOverrideValue = (int)GPUSlider.Value; - UpdateProfile(); - } - - private void EPPToggle_Toggled(object sender, RoutedEventArgs e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.EPPOverrideEnabled = EPPToggle.IsOn; - selectedProfile.EPPOverrideValue = (uint)EPPSlider.Value; - UpdateProfile(); - } - - private void EPPSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (selectedProfile is null) - return; - - if (updateLock) - return; - - selectedProfile.EPPOverrideValue = (uint)EPPSlider.Value; - UpdateProfile(); - } - - private void CPUCoreToggle_Toggled(object sender, RoutedEventArgs e) - { - if (selectedProfile is null) - return; - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.CPUCoreEnabled = CPUCoreToggle.IsOn; - selectedProfile.CPUCoreCount = (int)CPUCoreSlider.Value; - UpdateProfile(); - } - - private void CPUCoreSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) - { - if (selectedProfile is null) - return; - - if (!CPUCoreSlider.IsInitialized) - return; - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.CPUCoreCount = (int)CPUCoreSlider.Value; - UpdateProfile(); - } - - private void FanMode_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (FanMode.SelectedIndex == -1) - return; - - if (selectedProfile is null) - return; - - // wait until lock is released - if (updateLock) - return; - - selectedProfile.FanProfile.fanMode = (FanMode)FanMode.SelectedIndex; - UpdateProfile(); - } - - private async void Button_PowerSettings_Delete_Click(object sender, RoutedEventArgs e) - { - var result = Dialog.ShowAsync( - $"{Properties.Resources.ProfilesPage_AreYouSureDelete1} \"{selectedProfile.Name}\"?", - $"{Properties.Resources.ProfilesPage_AreYouSureDelete2}", - ContentDialogButton.Primary, - $"{Properties.Resources.ProfilesPage_Cancel}", - $"{Properties.Resources.ProfilesPage_Delete}"); - await result; // sync call - - switch (result.Result) - { - case ContentDialogResult.Primary: - PowerProfileManager.DeleteProfile(selectedProfile); - break; - } - } +using HandheldCompanion.Devices; +using HandheldCompanion.Managers; +<<<<<<< HEAD +using HandheldCompanion.Misc; +using HandheldCompanion.Platforms; +using HandheldCompanion.Processors; +using HandheldCompanion.Utils; +using Inkore.UI.WPF.Modern.Controls; +using System; +using System.Timers; +======= +using HandheldCompanion.Managers.Desktop; +using HandheldCompanion.Misc; +using HandheldCompanion.Platforms; +using Inkore.UI.WPF.Modern.Controls; +using System; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using System.Windows; +using System.Windows.Controls; +using Page = System.Windows.Controls.Page; + +namespace HandheldCompanion.Views.QuickPages; + +/// <summary> +/// Interaction logic for QuickPerformancePage.xaml +/// </summary> +public partial class QuickPerformancePage : Page +{ + private const int UpdateInterval = 500; + private readonly Timer UpdateTimer; + private PowerProfile selectedProfile; + + private LockObject updateLock = new(); + + public QuickPerformancePage(string Tag) : this() + { + this.Tag = Tag; + } + + public QuickPerformancePage() + { + InitializeComponent(); + + /* + MainWindow.performanceManager.PowerModeChanged += PerformanceManager_PowerModeChanged; + MainWindow.performanceManager.PerfBoostModeChanged += PerformanceManager_PerfBoostModeChanged; + MainWindow.performanceManager.EPPChanged += PerformanceManager_EPPChanged; + */ + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + + PlatformManager.RTSS.Updated += RTSS_Updated; + + MainWindow.performanceManager.ProcessorStatusChanged += PerformanceManager_StatusChanged; + MainWindow.performanceManager.EPPChanged += PerformanceManager_EPPChanged; + MainWindow.performanceManager.Initialized += PerformanceManager_Initialized; + + HotkeysManager.CommandExecuted += HotkeysManager_CommandExecuted; + + PowerProfileManager.Updated += PowerProfileManager_Updated; + PowerProfileManager.Deleted += PowerProfileManager_Deleted; + + // device settings + GPUSlider.Minimum = MainWindow.CurrentDevice.GfxClock[0]; + GPUSlider.Maximum = MainWindow.CurrentDevice.GfxClock[1]; + + CPUSlider.Minimum = MotherboardInfo.ProcessorMaxTurboSpeed / 4.0d; + CPUSlider.Maximum = MotherboardInfo.ProcessorMaxTurboSpeed; + + // motherboard settings + CPUCoreSlider.Maximum = MotherboardInfo.NumberOfCores; + + FanModeSoftware.IsEnabled = MainWindow.CurrentDevice.Capabilities.HasFlag(DeviceCapabilities.FanControl); + + UpdateTimer = new Timer(UpdateInterval); + UpdateTimer.AutoReset = false; + UpdateTimer.Elapsed += (sender, e) => SubmitProfile(); + + // force call + RTSS_Updated(PlatformManager.RTSS.Status); +<<<<<<< HEAD +======= + HWiNFO_Updated(PlatformManager.HWiNFO.Status); + + // todo: move me ? + SettingsManager.SetProperty("QuietModeEnabled", + MainWindow.CurrentDevice.Capabilities.HasFlag(DeviceCapabilities.FanControl)); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void HotkeysManager_CommandExecuted(string listener) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (listener) + { + case "increaseTDP": + { + if (selectedProfile is null || !selectedProfile.TDPOverrideEnabled) + return; + + TDPSlider.Value++; + } + break; + case "decreaseTDP": + { + if (selectedProfile is null || !selectedProfile.TDPOverrideEnabled) + return; + + TDPSlider.Value--; + } + break; + } + }); + } + + private void PowerProfileManager_Deleted(PowerProfile profile) + { + // current power profile deleted, return to previous page + bool isCurrent = selectedProfile?.Guid == profile.Guid; + if (isCurrent) + MainWindow.overlayquickTools.ContentFrame.GoBack(); + } + + private void PowerProfileManager_Updated(PowerProfile profile, UpdateSource source) + { + if (selectedProfile is null) + return; + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // current power profile updated, update UI + bool isCurrent = selectedProfile.Guid == profile.Guid; + if (isCurrent) + UpdateUI(); + }); + } + + private void RTSS_Updated(PlatformStatus status) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (status) + { + case PlatformStatus.Ready: + var Processor = MainWindow.performanceManager.GetProcessor(); + StackProfileAutoTDP.IsEnabled = true && Processor is not null ? Processor.CanChangeTDP : false; + break; + case PlatformStatus.Stalled: + // StackProfileFramerate.IsEnabled = false; + // StackProfileAutoTDP.IsEnabled = false; + break; + } + }); + } + + public void UpdateProfile() + { + if (UpdateTimer is not null) + { + UpdateTimer.Stop(); + UpdateTimer.Start(); + } + } + + public void SubmitProfile(UpdateSource source = UpdateSource.ProfilesPage) + { + if (selectedProfile is null) + return; + + PowerProfileManager.UpdateOrCreateProfile(selectedProfile, source); + } + + private void PerformanceManager_StatusChanged(bool CanChangeTDP, bool CanChangeGPU) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + StackProfileTDP.IsEnabled = CanChangeTDP; + StackProfileAutoTDP.IsEnabled = CanChangeTDP && PlatformManager.RTSS.IsInstalled; + + StackProfileGPUClock.IsEnabled = CanChangeGPU; + }); + } + + private void PerformanceManager_EPPChanged(uint EPP) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + EPPSlider.Value = EPP; + }); + } + + private void PerformanceManager_Initialized() + { + Processor processor = MainWindow.performanceManager.GetProcessor(); + if (processor is null) + return; + + PerformanceManager_StatusChanged(processor.CanChangeTDP, processor.CanChangeGPU); + } + + /* + private void PerformanceManager_PowerModeChanged(int idx) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => { PowerModeSlider.Value = idx; }); + } + + private void PerformanceManager_PerfBoostModeChanged(bool value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => { CPUBoostToggle.IsOn = value; }); + } + */ + + private void SettingsManager_SettingValueChanged(string name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { + case "ConfigurableTDPOverrideDown": + { + using (new ScopedLock(updateLock)) + { + TDPSlider.Minimum = (double)value; + } + } + break; + case "ConfigurableTDPOverrideUp": + { + using (new ScopedLock(updateLock)) + { + TDPSlider.Maximum = (double)value; + } + } + break; + } + }); + } + + private void PowerMode_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (PowerMode.SelectedIndex == -1) + return; + + if (selectedProfile is null) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.OSPowerMode = PerformanceManager.PowerModes[PowerMode.SelectedIndex]; + UpdateProfile(); + } + + private void CPUBoostToggle_Toggled(object sender, RoutedEventArgs e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.CPUBoostEnabled = CPUBoostToggle.IsOn; + UpdateProfile(); + } + + public void SelectionChanged(Guid guid) + { + // if an update is pending, cut it short, it will disturb profile selection though + // keep me ? + if (UpdateTimer.Enabled) + { + UpdateTimer.Stop(); + SubmitProfile(); + } + + selectedProfile = PowerProfileManager.GetProfile(guid); + UpdateUI(); + } + + private void UpdateUI() + { + if (selectedProfile is null) + return; + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + using (new ScopedLock(updateLock)) + { + switch (selectedProfile.Default) + { + case true: + // we shouldn't allow users to mess with default profile fan mode + FanMode.IsEnabled = false; + break; + case false: + FanMode.IsEnabled = true; + break; + } + + // we shouldn't allow users to modify some of default profile settings + Button_PowerSettings_Delete.IsEnabled = !selectedProfile.Default; + + // page name + this.Title = selectedProfile.Name; + + // TDP + TDPToggle.IsOn = selectedProfile.TDPOverrideEnabled; + var TDP = selectedProfile.TDPOverrideValues is not null + ? selectedProfile.TDPOverrideValues + : MainWindow.CurrentDevice.nTDP; + TDPSlider.Value = TDP[(int)PowerType.Slow]; + + // CPU Clock control + CPUToggle.IsOn = selectedProfile.CPUOverrideEnabled; + CPUSlider.Value = selectedProfile.CPUOverrideValue != 0 ? selectedProfile.CPUOverrideValue : MotherboardInfo.ProcessorMaxTurboSpeed; + + // GPU Clock control + GPUToggle.IsOn = selectedProfile.GPUOverrideEnabled; + GPUSlider.Value = selectedProfile.GPUOverrideValue != 0 ? selectedProfile.GPUOverrideValue : 255 * 50; + + // AutoTDP + AutoTDPToggle.IsOn = selectedProfile.AutoTDPEnabled; + AutoTDPRequestedFPSSlider.Value = selectedProfile.AutoTDPRequestedFPS; + + // EPP + EPPToggle.IsOn = selectedProfile.EPPOverrideEnabled; + EPPSlider.Value = selectedProfile.EPPOverrideValue; + + // CPU Core Count + CPUCoreToggle.IsOn = selectedProfile.CPUCoreEnabled; + CPUCoreSlider.Value = selectedProfile.CPUCoreCount; + + // CPU Boost + CPUBoostToggle.IsOn = selectedProfile.CPUBoostEnabled; + + // Power Mode + PowerMode.SelectedIndex = Array.IndexOf(PerformanceManager.PowerModes, selectedProfile.OSPowerMode); + + // Fan control + FanMode.SelectedIndex = (int)selectedProfile.FanProfile.fanMode; + } + }); + } + + private void TDPToggle_Toggled(object sender, RoutedEventArgs e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.TDPOverrideEnabled = TDPToggle.IsOn; + selectedProfile.TDPOverrideValues = new double[3] + { + TDPSlider.Value, + TDPSlider.Value, + TDPSlider.Value + }; + + UpdateProfile(); + } + + private void TDPSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.TDPOverrideValues = new double[3] + { + TDPSlider.Value, + TDPSlider.Value, + TDPSlider.Value + }; + + UpdateProfile(); + } + + private void AutoTDPToggle_Toggled(object sender, RoutedEventArgs e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.AutoTDPEnabled = AutoTDPToggle.IsOn; + AutoTDPRequestedFPSSlider.Value = selectedProfile.AutoTDPRequestedFPS; + + UpdateProfile(); + } + + private void AutoTDPRequestedFPSSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.AutoTDPRequestedFPS = (int)AutoTDPRequestedFPSSlider.Value; + UpdateProfile(); + } + + private void CPUToggle_Toggled(object sender, RoutedEventArgs e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.CPUOverrideEnabled = CPUToggle.IsOn; + selectedProfile.CPUOverrideValue = (int)CPUSlider.Value; + UpdateProfile(); + } + + private void CPUSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.CPUOverrideValue = (int)CPUSlider.Value; + UpdateProfile(); + } + + private void GPUToggle_Toggled(object sender, RoutedEventArgs e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.GPUOverrideEnabled = GPUToggle.IsOn; + selectedProfile.GPUOverrideValue = (int)GPUSlider.Value; + UpdateProfile(); + } + + private void GPUSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.GPUOverrideValue = (int)GPUSlider.Value; + UpdateProfile(); + } + + private void EPPToggle_Toggled(object sender, RoutedEventArgs e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.EPPOverrideEnabled = EPPToggle.IsOn; + selectedProfile.EPPOverrideValue = (uint)EPPSlider.Value; + UpdateProfile(); + } + + private void EPPSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (selectedProfile is null) + return; + + if (updateLock) + return; + + selectedProfile.EPPOverrideValue = (uint)EPPSlider.Value; + UpdateProfile(); + } + + private void CPUCoreToggle_Toggled(object sender, RoutedEventArgs e) + { + if (selectedProfile is null) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.CPUCoreEnabled = CPUCoreToggle.IsOn; + selectedProfile.CPUCoreCount = (int)CPUCoreSlider.Value; + UpdateProfile(); + } + + private void CPUCoreSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (selectedProfile is null) + return; + + if (!CPUCoreSlider.IsInitialized) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.CPUCoreCount = (int)CPUCoreSlider.Value; + UpdateProfile(); + } + + private void FanMode_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (FanMode.SelectedIndex == -1) + return; + + if (selectedProfile is null) + return; + + // wait until lock is released + if (updateLock) + return; + + selectedProfile.FanProfile.fanMode = (FanMode)FanMode.SelectedIndex; + UpdateProfile(); + } + + private async void Button_PowerSettings_Delete_Click(object sender, RoutedEventArgs e) + { + var result = Dialog.ShowAsync( + $"{Properties.Resources.ProfilesPage_AreYouSureDelete1} \"{selectedProfile.Name}\"?", + $"{Properties.Resources.ProfilesPage_AreYouSureDelete2}", + ContentDialogButton.Primary, + $"{Properties.Resources.ProfilesPage_Cancel}", + $"{Properties.Resources.ProfilesPage_Delete}"); + await result; // sync call + + switch (result.Result) + { + case ContentDialogResult.Primary: + PowerProfileManager.DeleteProfile(selectedProfile); + break; + } + } } \ No newline at end of file diff --git a/HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml b/HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml index 96e380cd8..defa9d3c9 100644 --- a/HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml +++ b/HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml @@ -6,8 +6,12 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:resx="clr-namespace:HandheldCompanion.Properties" xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" +<<<<<<< HEAD Name="QuickProfiles" Title="{x:Static resx:Resources.QuickProfilesPage_Title}" +======= + Title="Profiles" +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d Margin="15,0,0,0" d:Background="White" d:DesignHeight="1500" @@ -15,7 +19,11 @@ KeepAlive="True" mc:Ignorable="d"> +<<<<<<< HEAD <ui:SimpleStackPanel Margin="0,6,0,6" Spacing="10"> +======= + <ui:SimpleStackPanel Margin="15,6,15,6" Spacing="10"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <Grid> <!-- Header --> @@ -47,10 +55,14 @@ <Grid Name="StackProfileToggle"> <!-- Header --> +<<<<<<< HEAD <Border Padding="15,12,12,12" Background="{DynamicResource ExpanderHeaderBackground}" CornerRadius="{DynamicResource ControlCornerRadius}"> +======= + <Border CornerRadius="{DynamicResource ControlCornerRadius}"> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <Grid> <Grid.ColumnDefinitions> @@ -100,6 +112,7 @@ </Grid> <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> +<<<<<<< HEAD <ui:SimpleStackPanel Name="GridProfile" Spacing="6"> <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="Performance profile" /> @@ -186,10 +199,19 @@ TextWrapping="Wrap" /> </ui:SimpleStackPanel> </DockPanel> +======= + + <ui:SimpleStackPanel Name="GridProfile" Spacing="10"> + + <Expander HorizontalAlignment="Stretch" IsExpanded="True"> + <Expander.Header> + <TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="Power settings" /> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d </Expander.Header> <ui:SimpleStackPanel Spacing="6"> +<<<<<<< HEAD <!-- Controller layout --> <Grid> <Grid.ColumnDefinitions> @@ -251,6 +273,426 @@ <!-- Style of input --> <Grid> +======= + <!-- Maximum CPU Count --> + <ui:SimpleStackPanel Name="StackProfileCPUCore" Spacing="6"> + <Grid> + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Maximum CPU Count" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="Controls CPU unparked core count limit" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="CPUCoreToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="CPUCoreToggle_Toggled" /> + </Grid> + + <StackPanel Visibility="{Binding ElementName=CPUCoreToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="Cores" /> + <DockPanel Margin="8,0,0,0" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=CPUCoreSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="C" /> + <Slider + x:Name="CPUCoreSlider" + Margin="8,0,0,0" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + Minimum="2" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + TickPlacement="BottomRight" + ValueChanged="CPUCoreSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> + + <!-- Thermal Power (TDP) Limit --> + <ui:SimpleStackPanel + Name="StackProfileTDP" + IsEnabled="False" + Spacing="6"> + + <Grid> + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_TDPOverride}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_TDPOverrideDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="TDPToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="TDPToggle_Toggled" /> + </Grid> + + <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=TDPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickProfilesPage_PowerLimitTarget}" /> + <DockPanel Margin="8,0,0,0" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=TDPSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_TDPUnitWatt}" /> + <Slider + x:Name="TDPSlider" + Margin="8,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="5" + SmallChange="1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + TickPlacement="BottomRight" + ValueChanged="TDPSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> + + <!-- Auto TDP --> + <ui:SimpleStackPanel + Name="StackProfileAutoTDP" + IsEnabled="False" + Spacing="6"> + + <Grid> + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Text="{x:Static resx:Resources.ProfilesPage_AutoTDP}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_AutoTDPDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="AutoTDPToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="AutoTDPToggle_Toggled" /> + </Grid> + <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=AutoTDPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_AutoTDPFPS}" /> + + <DockPanel Margin="8,0,0,0" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=AutoTDPRequestedFPSSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_AutoTDPUnitFPS}" /> + <Slider + x:Name="AutoTDPRequestedFPSSlider" + Margin="8,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="5" + Maximum="120" + Minimum="20" + SmallChange="1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="1" + TickPlacement="BottomRight" + ValueChanged="AutoTDPRequestedFPSSlider_ValueChanged" + Value="30" /> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> + + <!-- Processor energy performance preference policy --> + <ui:SimpleStackPanel Name="StackProfileEPP" Spacing="6"> + + <Grid> + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_EPP}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_EPPDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="EPPToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="EPPToggle_Toggled" /> + </Grid> + + <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=EPPToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_EPPBalance}" /> + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + + <ui:SimpleStackPanel + Margin="0,0,0,0" + VerticalAlignment="Center" + ScrollViewer.PanningMode="HorizontalOnly"> + <Slider + x:Name="EPPSlider" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="10" + Maximum="100" + Minimum="0" + SmallChange="1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="10" + TickPlacement="BottomRight" + ValueChanged="EPPSlider_ValueChanged" /> + + <Grid Name="EPPGrid"> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + <ColumnDefinition Width="*" /> + </Grid.ColumnDefinitions> + + <TextBlock + Grid.Column="1" + HorizontalAlignment="Left" + Text="{x:Static resx:Resources.ProfilesPage_CPU}" /> + <TextBlock + Grid.Column="2" + HorizontalAlignment="Right" + Text="{x:Static resx:Resources.ProfilesPage_GPU}" /> + </Grid> + </ui:SimpleStackPanel> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> + + <!-- Manual GPU Clock Control --> + <ui:SimpleStackPanel + Name="StackProfileGPU" + IsEnabled="False" + Spacing="6"> + + <Grid> + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickPerformancePage_GPUControl}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.QuickPerformancePage_GPUControlDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="GPUToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="GPUToggle_Toggled" /> + </Grid> + + <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=GPUToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.ProfilesPage_GPUMhz}" /> + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, ElementName=GPUSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{x:Static resx:Resources.QuickPerformancePage_GPUUnit}" /> + <Slider + x:Name="GPUSlider" + Margin="8,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="500" + SmallChange="100" + Style="{DynamicResource SliderStyle1}" + TickFrequency="100" + TickPlacement="BottomRight" + ValueChanged="GPUSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Expander> + + <Expander HorizontalAlignment="Stretch" IsExpanded="False"> + <Expander.Header> + <TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_MotionControlSettings}" /> + </Expander.Header> + + <ui:SimpleStackPanel Spacing="6"> + + <!-- Controller layout --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="1*" /> + <ColumnDefinition Width="1*" /> + </Grid.ColumnDefinitions> + + <StackPanel VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_ControllerLayout}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="Enable controller layout" + TextWrapping="Wrap" /> + </StackPanel> + + <ui:ToggleSwitch + Name="Toggle_ControllerLayout" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Right" + VerticalAlignment="Center" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="Toggle_ControllerLayout_Toggled" /> + </Grid> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> + + <!-- Output device --> + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="1*" /> + <ColumnDefinition Width="1*" /> + </Grid.ColumnDefinitions> + + <StackPanel VerticalAlignment="Center"> + <TextBlock + Style="{StaticResource BodyTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_StyleofOutput}" + ToolTip="{x:Static resx:Resources.ProfilesPage_StyleofOutputTooltip}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_UMCSelectionRightLeftDesc}" + TextWrapping="Wrap" /> + </StackPanel> + + <ComboBox + Name="cB_Output" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + SelectionChanged="cB_Output_SelectionChanged" /> + </Grid> + + <ui:SimpleStackPanel Spacing="6" Visibility="{Binding ElementName=cB_Output, Path=SelectedIndex, Converter={StaticResource IndexToVisibilityConverter}, ConverterParameter=1|2|3|4|5}"> + <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> + + <!-- Style of input --> + <Grid> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <Grid.ColumnDefinitions> <ColumnDefinition Width="1*" /> <ColumnDefinition Width="1*" /> @@ -406,7 +848,10 @@ Style="{DynamicResource SliderStyle1}" TickFrequency="0.1" TickPlacement="BottomRight" +<<<<<<< HEAD ToolTip="{Binding Value, StringFormat=N1, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d ValueChanged="Slider_GyroWeight_ValueChanged" /> </DockPanel> </Grid> @@ -457,11 +902,15 @@ Style="{DynamicResource SliderStyle1}" TickFrequency="0.1" TickPlacement="BottomRight" +<<<<<<< HEAD ToolTip="{Binding Value, StringFormat=N1, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d ValueChanged="SliderSensitivityX_ValueChanged" Value="1" /> </DockPanel> </StackPanel> +<<<<<<< HEAD <StackPanel> <TextBlock Text="Vertical sensitivity" /> @@ -617,6 +1066,162 @@ Toggled="FramerateToggle_Toggled" /> </Grid> +======= + + <StackPanel> + <TextBlock Text="Vertical sensitivity" /> + <DockPanel ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N1, ElementName=SliderSensitivityY, Mode=OneWay}" + TextAlignment="Center" /> + <Slider + x:Name="SliderSensitivityY" + Margin="8,0,0,0" + HorizontalAlignment="Stretch" + VerticalAlignment="Center" + AutoToolTipPrecision="1" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="0.1" + Maximum="3.0" + Minimum="0.1" + SmallChange="0.1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="0.1" + TickPlacement="BottomRight" + ValueChanged="SliderSensitivityY_ValueChanged" + Value="1" /> + </DockPanel> + </StackPanel> + </StackPanel> + </Grid> + </ui:SimpleStackPanel> + </ui:SimpleStackPanel> + </Expander> + + <Expander HorizontalAlignment="Stretch" IsExpanded="False"> + <Expander.Header> + <TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="Graphics settings" /> + </Expander.Header> + + <ui:SimpleStackPanel Spacing="6"> + + <!-- RSR --> + <ui:SimpleStackPanel + Name="StackProfileRSR" + IsEnabled="False" + Spacing="6"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="1*" /> + <ColumnDefinition Width="1*" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="Radeon Super Resolution" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="In-driver-based upscaling feature for faster framerates" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="RSRToggle" + Grid.Column="1" + Margin="12,0,0,0" + HorizontalAlignment="Right" + VerticalAlignment="Center" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="RSRToggle_Toggled" /> + </Grid> + + <StackPanel d:Visibility="Visible" Visibility="{Binding ElementName=RSRToggle, Path=IsOn, Converter={StaticResource BooleanToVisibilityConverter}}"> + <TextBlock + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="Sharpen effect" /> + <DockPanel Margin="8,0,0,0" ScrollViewer.PanningMode="HorizontalOnly"> + <TextBlock + Width="35" + VerticalAlignment="Center" + Text="{Binding Value, StringFormat=N0, ElementName=RSRSlider, Mode=OneWay}" + TextAlignment="Center" /> + <TextBlock + Width="30" + HorizontalAlignment="Left" + VerticalAlignment="Center" + Text="%" /> + <Slider + x:Name="RSRSlider" + VerticalAlignment="Center" + AutoToolTipPrecision="0" + IsMoveToPointEnabled="True" + IsSnapToTickEnabled="True" + LargeChange="5" + Maximum="100" + Minimum="0" + SmallChange="1" + Style="{DynamicResource SliderStyle1}" + TickFrequency="5" + TickPlacement="BottomRight" + ValueChanged="RSRSlider_ValueChanged" /> + </DockPanel> + </StackPanel> + </ui:SimpleStackPanel> + + <Separator Background="{DynamicResource ExpanderHeaderBackground}" /> + + <!-- Framerate Limit --> + <ui:SimpleStackPanel + Name="StackProfileFramerate" + IsEnabled="False" + Spacing="6"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition /> + <ColumnDefinition Width="0.2*" MinWidth="80" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.ProfilesPage_FramerateLimit}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.ProfilesPage_FramerateLimitDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="FramerateToggle" + Grid.Column="1" + HorizontalAlignment="Right" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="FramerateToggle_Toggled" /> + </Grid> + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <DockPanel Margin="0,0,0,0" d:Visibility="Visible" @@ -640,7 +1245,10 @@ Style="{DynamicResource SliderStyle1}" TickFrequency="1" TickPlacement="BottomRight" +<<<<<<< HEAD ToolTip="{Binding Value, StringFormat=N0, RelativeSource={RelativeSource Self}, Mode=OneWay}" +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d ValueChanged="FramerateSlider_ValueChanged" /> <Grid Name="FramerateModeGrid"> diff --git a/HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml.cs b/HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml.cs index d894d48c7..fc8901dc8 100644 --- a/HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml.cs +++ b/HandheldCompanion/Views/QuickPages/QuickProfilesPage.xaml.cs @@ -3,8 +3,13 @@ using HandheldCompanion.Inputs; using HandheldCompanion.Managers; using HandheldCompanion.Managers.Desktop; +<<<<<<< HEAD using HandheldCompanion.Misc; using HandheldCompanion.Platforms; +======= +using HandheldCompanion.Platforms; +using HandheldCompanion.Processors; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d using HandheldCompanion.Utils; using Inkore.UI.WPF.Modern.Controls; using System; @@ -12,7 +17,10 @@ using System.Timers; using System.Windows; using System.Windows.Controls; +<<<<<<< HEAD using System.Windows.Media; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d using Page = System.Windows.Controls.Page; using Separator = System.Windows.Controls.Separator; @@ -46,8 +54,13 @@ public QuickProfilesPage() ProfileManager.Applied += ProfileApplied; +<<<<<<< HEAD PowerProfileManager.Updated += PowerProfileManager_Updated; PowerProfileManager.Deleted += PowerProfileManager_Deleted; +======= + SystemManager.DisplaySettingsChanged += SystemManager_DisplaySettingsChanged; + SystemManager.RSRStateChanged += SystemManager_RSRStateChanged; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d SystemManager.DisplaySettingsChanged += SystemManager_DisplaySettingsChanged; SystemManager.RSRStateChanged += SystemManager_RSRStateChanged; @@ -107,6 +120,57 @@ public QuickProfilesPage() cB_Output.Items.Add(comboBoxItem); } + foreach (var mode in (MotionOuput[])Enum.GetValues(typeof(MotionOuput))) + { + // create panel + ComboBoxItem comboBoxItem = new ComboBoxItem() + { + HorizontalAlignment = HorizontalAlignment.Stretch, + HorizontalContentAlignment = HorizontalAlignment.Left, + }; + + SimpleStackPanel simpleStackPanel = new SimpleStackPanel + { + Spacing = 6, + Orientation = Orientation.Horizontal, + VerticalAlignment = VerticalAlignment.Center + }; + + // create icon + var icon = new FontIcon(); + + switch (mode) + { + case MotionOuput.Disabled: + icon.Glyph = "\uE8D8"; + break; + case MotionOuput.RightStick: + icon.Glyph = "\uF109"; + break; + case MotionOuput.LeftStick: + icon.Glyph = "\uF108"; + break; + case MotionOuput.MoveCursor: + icon.Glyph = "\uE962"; + break; + case MotionOuput.ScrollWheel: + icon.Glyph = "\uEC8F"; + break; + } + + if (!string.IsNullOrEmpty(icon.Glyph)) + simpleStackPanel.Children.Add(icon); + + // create textblock + var description = EnumUtils.GetDescriptionFromEnumValue(mode); + var text = new TextBlock { Text = description }; + + simpleStackPanel.Children.Add(text); + + comboBoxItem.Content = simpleStackPanel; + cB_Output.Items.Add(comboBoxItem); + } + foreach (var mode in (MotionInput[])Enum.GetValues(typeof(MotionInput))) { // create panel @@ -151,11 +215,26 @@ public QuickProfilesPage() var text = new TextBlock { Text = description }; simpleStackPanel.Children.Add(text); +<<<<<<< HEAD comboBoxItem.Content = simpleStackPanel; cB_Input.Items.Add(comboBoxItem); } +======= + + comboBoxItem.Content = simpleStackPanel; + cB_Input.Items.Add(comboBoxItem); + } + + // device settings + GPUSlider.Minimum = MainWindow.CurrentDevice.GfxClock[0]; + GPUSlider.Maximum = MainWindow.CurrentDevice.GfxClock[1]; + + // motherboard settings + CPUCoreSlider.Maximum = MotherboardInfo.NumberOfCores; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d UpdateTimer = new Timer(UpdateInterval); UpdateTimer.AutoReset = false; UpdateTimer.Elapsed += (sender, e) => SubmitProfile(); @@ -239,6 +318,7 @@ private void PowerProfileManager_Deleted(PowerProfile powerProfile) int idx = -1; foreach (var item in ProfileStack.Children) { +<<<<<<< HEAD if (item is not Button) continue; @@ -253,6 +333,23 @@ private void PowerProfileManager_Deleted(PowerProfile powerProfile) if (isCurrent) { idx = ProfileStack.Children.IndexOf(parent); +======= + case "increaseTDP": + { + if (currentProfile is null || !currentProfile.TDPOverrideEnabled) + return; + + TDPSlider.Value++; + } + break; + case "decreaseTDP": + { + if (currentProfile is null || !currentProfile.TDPOverrideEnabled) + return; + + TDPSlider.Value--; + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d break; } } @@ -278,6 +375,7 @@ private void PowerProfileManager_Updated(PowerProfile powerProfile, UpdateSource int idx = -1; foreach (var item in ProfileStack.Children) { +<<<<<<< HEAD if (item is not Button) continue; @@ -325,6 +423,24 @@ private void PowerProfileManager_Updated(PowerProfile powerProfile, UpdateSource // add new entry ProfileStack.Children.Add(button); +======= + case "ConfigurableTDPOverrideDown": + { + using (new ScopedLock(updateLock)) + { + TDPSlider.Minimum = (double)value; + } + } + break; + case "ConfigurableTDPOverrideUp": + { + using (new ScopedLock(updateLock)) + { + TDPSlider.Maximum = (double)value; + } + } + break; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } }); } @@ -373,7 +489,11 @@ private void ProfileApplied(Profile profile, UpdateSource source) } // update profile +<<<<<<< HEAD selectedProfile = profile; +======= + currentProfile = profile; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d // UI thread (async) Application.Current.Dispatcher.BeginInvoke(() => @@ -381,6 +501,7 @@ private void ProfileApplied(Profile profile, UpdateSource source) using (new ScopedLock(updateLock)) { // update profile name +<<<<<<< HEAD CurrentProfileName.Text = selectedProfile.Name; Toggle_ControllerLayout.IsEnabled = selectedProfile.Default ? false : true; Toggle_ControllerLayout.IsOn = selectedProfile.LayoutEnabled; @@ -432,6 +553,78 @@ private void ProfileApplied(Profile profile, UpdateSource source) // RSR RSRToggle.IsOn = selectedProfile.RSREnabled; RSRSlider.Value = selectedProfile.RSRSharpness; +======= + CurrentProfileName.Text = currentProfile.Name; + Toggle_ControllerLayout.IsEnabled = currentProfile.Default ? false : true; + Toggle_ControllerLayout.IsOn = currentProfile.LayoutEnabled; + + // gyro layout + if (!currentProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) + { + // no gyro layout available, mark as disabled + cB_Output.SelectedIndex = (int)MotionOuput.Disabled; + } + else + { + // IActions + GridAntiDeadzone.Visibility = currentAction is AxisActions ? Visibility.Visible : Visibility.Collapsed; + GridGyroWeight.Visibility = currentAction is AxisActions ? Visibility.Visible : Visibility.Collapsed; + + if (currentAction is AxisActions) + { + cB_Output.SelectedIndex = (int)((AxisActions)currentAction).Axis; + SliderUMCAntiDeadzone.Value = ((AxisActions)currentAction).AxisAntiDeadZone; + Slider_GyroWeight.Value = ((AxisActions)currentAction).gyroWeight; + } + else if (currentAction is MouseActions) + { + cB_Output.SelectedIndex = (int)((MouseActions)currentAction).MouseType - 1; + } + + // GyroActions + cB_Input.SelectedIndex = (int)((GyroActions)currentAction).MotionInput; + cB_UMC_MotionDefaultOffOn.SelectedIndex = (int)((GyroActions)currentAction).MotionMode; + + // todo: move me to layout ? + SliderSensitivityX.Value = currentProfile.MotionSensivityX; + SliderSensitivityY.Value = currentProfile.MotionSensivityY; + + // todo: improve me ? + GyroHotkey.inputsChord.State = ((GyroActions)currentAction).MotionTrigger.Clone() as ButtonState; + GyroHotkey.DrawInput(); + } + + // TDP + TDPToggle.IsOn = currentProfile.TDPOverrideEnabled; + var TDP = currentProfile.TDPOverrideValues is not null + ? currentProfile.TDPOverrideValues + : MainWindow.CurrentDevice.nTDP; + TDPSlider.Value = TDP[(int)PowerType.Slow]; + + // GPU + GPUToggle.IsOn = currentProfile.GPUOverrideEnabled; + GPUSlider.Value = currentProfile.GPUOverrideValue != 0 ? currentProfile.GPUOverrideValue : 255 * 50; + + // Framerate + FramerateToggle.IsOn = currentProfile.FramerateEnabled; + FramerateSlider.Value = currentProfile.FramerateValue; + + // AutoTDP + AutoTDPToggle.IsOn = currentProfile.AutoTDPEnabled; + AutoTDPRequestedFPSSlider.Value = currentProfile.AutoTDPRequestedFPS; + + // EPP + EPPToggle.IsOn = currentProfile.EPPOverrideEnabled; + EPPSlider.Value = currentProfile.EPPOverrideValue; + + // RSR + RSRToggle.IsOn = currentProfile.RSREnabled; + RSRSlider.Value = currentProfile.RSRSharpness; + + // CPU Core Count + CPUCoreToggle.IsOn = currentProfile.CPUCoreEnabled; + CPUCoreSlider.Value = currentProfile.CPUCoreCount; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } }); } @@ -496,7 +689,11 @@ private void ProfileToggle_Toggled(object sender, RoutedEventArgs e) else { realProfile.Enabled = ProfileToggle.IsOn; +<<<<<<< HEAD ProfileManager.UpdateOrCreateProfile(realProfile, UpdateSource.Creation); +======= + ProfileManager.UpdateOrCreateProfile(realProfile, ProfileUpdateSource.Creation); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } @@ -506,15 +703,26 @@ private void CreateProfile() return; // create profile +<<<<<<< HEAD selectedProfile = new Profile(currentProcess.Path); selectedProfile.Layout = (ProfileManager.GetProfileWithDefaultLayout()?.Layout ?? LayoutTemplate.DefaultLayout.Layout).Clone() as Layout; selectedProfile.LayoutTitle = LayoutTemplate.DesktopLayout.Name; +======= + currentProfile = new Profile(currentProcess.Path); + currentProfile.Layout = (ProfileManager.GetProfileWithDefaultLayout()?.Layout ?? LayoutTemplate.DefaultLayout.Layout).Clone() as Layout; + currentProfile.LayoutTitle = LayoutTemplate.DesktopLayout.Name; + currentProfile.TDPOverrideValues = MainWindow.CurrentDevice.nTDP; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d // if an update is pending, execute it and stop timer if (UpdateTimer.Enabled) UpdateTimer.Stop(); +<<<<<<< HEAD SubmitProfile(UpdateSource.Creation); +======= + SubmitProfile(ProfileUpdateSource.Creation); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void cB_Input_SelectionChanged(object sender, SelectionChangedEventArgs e) @@ -522,20 +730,35 @@ private void cB_Input_SelectionChanged(object sender, SelectionChangedEventArgs if (cB_Input.SelectedIndex == -1) return; +<<<<<<< HEAD MotionInput input = (MotionInput)cB_Input.SelectedIndex; Text_InputHint.Text = EnumUtils.GetDescriptionFromEnumValue(input, string.Empty, "Desc"); if (selectedProfile is null) +======= + var input = (MotionInput)cB_Input.SelectedIndex; + Text_InputHint.Text = Profile.InputDescription[input]; + + if (currentProfile is null) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d return; if (updateLock) return; +<<<<<<< HEAD if (!selectedProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) return; ((GyroActions)currentAction).MotionInput = (MotionInput)cB_Input.SelectedIndex; UpdateProfile(); +======= + if (!currentProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) + return; + + ((GyroActions)currentAction).MotionInput = (MotionInput)cB_Input.SelectedIndex; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void cB_Output_SelectionChanged(object sender, SelectionChangedEventArgs e) @@ -544,6 +767,7 @@ private void cB_Output_SelectionChanged(object sender, SelectionChangedEventArgs return; if (updateLock) +<<<<<<< HEAD return; // try get current actions, if exists @@ -590,12 +814,102 @@ private void cB_Output_SelectionChanged(object sender, SelectionChangedEventArgs } break; } +======= + return; + + IActions newActions = null; + + MotionOuput motionOuput = (MotionOuput)cB_Output.SelectedIndex; + switch (motionOuput) + { + case MotionOuput.Disabled: + currentProfile.Layout.RemoveLayout(AxisLayoutFlags.Gyroscope); + break; + case MotionOuput.LeftStick: + case MotionOuput.RightStick: + newActions = new AxisActions() + { + Axis = GyroActions.DefaultAxisLayoutFlags, + AxisAntiDeadZone = GyroActions.DefaultAxisAntiDeadZone, + MotionTrigger = GyroHotkey.inputsChord.State.Clone() as ButtonState + }; + break; + case MotionOuput.MoveCursor: + case MotionOuput.ScrollWheel: + newActions = new MouseActions() + { + MouseType = GyroActions.DefaultMouseActionsType, + Sensivity = GyroActions.DefaultSensivity, + Deadzone = GyroActions.DefaultDeadzone, + MotionTrigger = GyroHotkey.inputsChord.State.Clone() as ButtonState + }; + break; + } + + // proper layout update + if (newActions is not null) + currentProfile.Layout.UpdateLayout(AxisLayoutFlags.Gyroscope, newActions); + + RequestUpdate(); + } + + private void TDPToggle_Toggled(object sender, RoutedEventArgs e) + { + if (currentProfile is null) + return; + + if (updateLock) + return; + + currentProfile.TDPOverrideEnabled = TDPToggle.IsOn; + RequestUpdate(); + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d // proper layout update if (gyroActions is not null) selectedProfile.Layout.UpdateLayout(AxisLayoutFlags.Gyroscope, gyroActions); +<<<<<<< HEAD SubmitProfile(UpdateSource.Creation); +======= + if (updateLock) + return; + + currentProfile.TDPOverrideValues = new double[3] + { + (int)TDPSlider.Value, + (int)TDPSlider.Value, + (int)TDPSlider.Value + }; + RequestUpdate(); + } + + private void AutoTDPToggle_Toggled(object sender, RoutedEventArgs e) + { + if (currentProfile is null) + return; + + if (updateLock) + return; + + currentProfile.AutoTDPEnabled = AutoTDPToggle.IsOn; + AutoTDPRequestedFPSSlider.Value = currentProfile.AutoTDPRequestedFPS; + + RequestUpdate(); + } + + private void AutoTDPRequestedFPSSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (currentProfile is null) + return; + + if (updateLock) + return; + + currentProfile.AutoTDPRequestedFPS = (int)AutoTDPRequestedFPSSlider.Value; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void SliderUMCAntiDeadzone_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) @@ -606,30 +920,50 @@ private void SliderUMCAntiDeadzone_ValueChanged(object sender, RoutedPropertyCha if (updateLock) return; +<<<<<<< HEAD if (!selectedProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) +======= + if (!currentProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d return; if (currentAction is AxisActions) ((AxisActions)currentAction).AxisAntiDeadZone = (int)SliderUMCAntiDeadzone.Value; +<<<<<<< HEAD UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void Slider_GyroWeight_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { +<<<<<<< HEAD if (selectedProfile is null) +======= + if (currentProfile is null) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d return; if (updateLock) return; +<<<<<<< HEAD if (!selectedProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) +======= + if (!currentProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d return; if (currentAction is AxisActions) ((AxisActions)currentAction).gyroWeight = (float)Slider_GyroWeight.Value; +<<<<<<< HEAD UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void SliderSensitivityX_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) @@ -640,8 +974,13 @@ private void SliderSensitivityX_ValueChanged(object sender, RoutedPropertyChange if (updateLock) return; +<<<<<<< HEAD selectedProfile.MotionSensivityX = (float)SliderSensitivityX.Value; UpdateProfile(); +======= + currentProfile.MotionSensivityX = (float)SliderSensitivityX.Value; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void SliderSensitivityY_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) @@ -652,8 +991,13 @@ private void SliderSensitivityY_ValueChanged(object sender, RoutedPropertyChange if (updateLock) return; +<<<<<<< HEAD selectedProfile.MotionSensivityY = (float)SliderSensitivityY.Value; UpdateProfile(); +======= + currentProfile.MotionSensivityY = (float)SliderSensitivityY.Value; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void TriggerCreated(Hotkey hotkey) @@ -683,13 +1027,20 @@ private void TriggerUpdated(string listener, InputsChord inputs, InputsManager.L if (!selectedProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) return; + if (!currentProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) + return; + // no Monitor on threaded calls ? switch (listener) { case "shortcutProfilesPage@": case "shortcutProfilesPage@@": ((GyroActions)currentAction).MotionTrigger = inputs.State.Clone() as ButtonState; +<<<<<<< HEAD UpdateProfile(); +======= + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d break; } } @@ -700,6 +1051,7 @@ private void cB_UMC_MotionDefaultOffOn_SelectionChanged(object sender, RoutedEve return; if (updateLock) +<<<<<<< HEAD return; if (!selectedProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) @@ -707,6 +1059,39 @@ private void cB_UMC_MotionDefaultOffOn_SelectionChanged(object sender, RoutedEve ((GyroActions)currentAction).MotionMode = (MotionMode)cB_UMC_MotionDefaultOffOn.SelectedIndex; UpdateProfile(); +======= + return; + + if (!currentProfile.Layout.GyroLayout.TryGetValue(AxisLayoutFlags.Gyroscope, out IActions currentAction)) + return; + + ((GyroActions)currentAction).MotionMode = (MotionMode)cB_UMC_MotionDefaultOffOn.SelectedIndex; + RequestUpdate(); + } + + private void GPUToggle_Toggled(object sender, RoutedEventArgs e) + { + if (currentProfile is null) + return; + + if (updateLock) + return; + + currentProfile.GPUOverrideEnabled = GPUToggle.IsOn; + RequestUpdate(); + } + + private void GPUSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (currentProfile is null) + return; + + if (updateLock) + return; + + currentProfile.GPUOverrideValue = (int)GPUSlider.Value; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void FramerateToggle_Toggled(object sender, RoutedEventArgs e) @@ -722,7 +1107,11 @@ private void FramerateToggle_Toggled(object sender, RoutedEventArgs e) { foreach (Control control in FramerateModeGrid.Children) { +<<<<<<< HEAD if (control is not Label) +======= + if (control.GetType() != typeof(Label)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d continue; control.SetResourceReference(Control.ForegroundProperty, "SystemControlForegroundBaseMediumBrush"); @@ -730,14 +1119,23 @@ private void FramerateToggle_Toggled(object sender, RoutedEventArgs e) } }); +<<<<<<< HEAD if (selectedProfile is null) +======= + if (currentProfile is null) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d return; if (updateLock) return; +<<<<<<< HEAD selectedProfile.FramerateEnabled = FramerateToggle.IsOn; UpdateProfile(); +======= + currentProfile.FramerateEnabled = FramerateToggle.IsOn; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void FramerateSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) @@ -749,14 +1147,23 @@ private void FramerateSlider_ValueChanged(object sender, RoutedPropertyChangedEv foreach (Control control in FramerateModeGrid.Children) { +<<<<<<< HEAD if (control is not Label) +======= + if (control.GetType() != typeof(Label)) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d continue; control.SetResourceReference(Control.ForegroundProperty, "SystemControlForegroundBaseMediumBrush"); } +<<<<<<< HEAD Label label = (Label)FramerateModeGrid.Children[value]; label.SetResourceReference(Control.ForegroundProperty, "AccentButtonBackground"); +======= + Label Label = (Label)FramerateModeGrid.Children[value]; + Label.SetResourceReference(Control.ForegroundProperty, "AccentButtonBackground"); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d }); if (selectedProfile is null) @@ -765,8 +1172,13 @@ private void FramerateSlider_ValueChanged(object sender, RoutedPropertyChangedEv if (updateLock) return; +<<<<<<< HEAD selectedProfile.FramerateValue = (int)FramerateSlider.Value; UpdateProfile(); +======= + currentProfile.FramerateValue = (int)FramerateSlider.Value; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void RSRToggle_Toggled(object sender, RoutedEventArgs e) @@ -774,12 +1186,20 @@ private void RSRToggle_Toggled(object sender, RoutedEventArgs e) if (selectedProfile is null) return; +<<<<<<< HEAD // wait until lock is released if (updateLock) return; selectedProfile.RSREnabled = RSRToggle.IsOn; UpdateProfile(); +======= + if (updateLock) + return; + + currentProfile.EPPOverrideEnabled = EPPToggle.IsOn; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } private void RSRSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) @@ -787,6 +1207,34 @@ private void RSRSlider_ValueChanged(object sender, RoutedPropertyChangedEventArg if (selectedProfile is null) return; +<<<<<<< HEAD +======= + if (updateLock) + return; + + currentProfile.EPPOverrideValue = (uint)EPPSlider.Value; + RequestUpdate(); + } + + private void RSRToggle_Toggled(object sender, RoutedEventArgs e) + { + if (currentProfile is null) + return; + + // wait until lock is released + if (updateLock) + return; + + currentProfile.RSREnabled = RSRToggle.IsOn; + RequestUpdate(); + } + + private void RSRSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (currentProfile is null) + return; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d if (!RSRSlider.IsInitialized) return; @@ -794,6 +1242,7 @@ private void RSRSlider_ValueChanged(object sender, RoutedPropertyChangedEventArg if (updateLock) return; +<<<<<<< HEAD selectedProfile.RSRSharpness = (int)RSRSlider.Value; UpdateProfile(); } @@ -801,12 +1250,22 @@ private void RSRSlider_ValueChanged(object sender, RoutedPropertyChangedEventArg private void Toggle_ControllerLayout_Toggled(object sender, RoutedEventArgs e) { if (selectedProfile is null) +======= + currentProfile.RSRSharpness = (int)RSRSlider.Value; + RequestUpdate(); + } + + private void CPUCoreToggle_Toggled(object sender, RoutedEventArgs e) + { + if (currentProfile is null) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d return; // wait until lock is released if (updateLock) return; +<<<<<<< HEAD selectedProfile.LayoutEnabled = Toggle_ControllerLayout.IsOn; UpdateProfile(); } @@ -819,5 +1278,38 @@ private void Button_PowerSettings_Create_Click(object sender, RoutedEventArgs e) PowerProfile powerProfile = new PowerProfile(Name, Properties.Resources.PowerProfileManualDescription); PowerProfileManager.UpdateOrCreateProfile(powerProfile, UpdateSource.Creation); +======= + currentProfile.CPUCoreEnabled = CPUCoreToggle.IsOn; + RequestUpdate(); + } + + private void CPUCoreSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) + { + if (currentProfile is null) + return; + + if (!CPUCoreSlider.IsInitialized) + return; + + // wait until lock is released + if (updateLock) + return; + + currentProfile.CPUCoreCount = (int)CPUCoreSlider.Value; + RequestUpdate(); + } + + private void Toggle_ControllerLayout_Toggled(object sender, RoutedEventArgs e) + { + if (currentProfile is null) + return; + + // wait until lock is released + if (updateLock) + return; + + currentProfile.LayoutEnabled = Toggle_ControllerLayout.IsOn; + RequestUpdate(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d } } \ No newline at end of file diff --git a/HandheldCompanion/Views/QuickPages/QuickSettingsPage.xaml b/HandheldCompanion/Views/QuickPages/QuickSettingsPage.xaml index 6e952c89d..d2386ec16 100644 --- a/HandheldCompanion/Views/QuickPages/QuickSettingsPage.xaml +++ b/HandheldCompanion/Views/QuickPages/QuickSettingsPage.xaml @@ -1,193 +1,198 @@ -<Page - x:Class="HandheldCompanion.Views.QuickPages.QuickSettingsPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Name="QuickSettings" - Title="{x:Static resx:Resources.QuickSettingsPage_Title}" - Margin="15,0,0,0" - d:Background="White" - d:DesignHeight="1200" - d:DesignWidth="640" - KeepAlive="True" - mc:Ignorable="d"> - - <Grid> - <Grid.RowDefinitions> - <RowDefinition Height="7*" /> - <RowDefinition Height="3*" /> - </Grid.RowDefinitions> - - <!-- Shortcuts --> - <UniformGrid - Name="QuickHotkeys" - VerticalAlignment="Top" - Columns="4"> - <UniformGrid.Resources> - <Style TargetType="{x:Type ui:SimpleStackPanel}"> - <Setter Property="Margin" Value="6" /> - </Style> - </UniformGrid.Resources> - </UniformGrid> - - <!-- Toggles --> - <ui:SimpleStackPanel - Grid.Row="1" - Margin="0,6,0,6" - VerticalAlignment="Bottom" - Spacing="6"> - <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="Device settings" /> - - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="Segoe Fluent Icons" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickSettingsPage_DisplayResolution}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.QuickSettingsPage_DisplayResolutionDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="ComboBoxResolution" - Grid.Column="1" - Margin="6,0,0,0" - HorizontalAlignment="Stretch" - IsReadOnly="True" - SelectionChanged="ComboBoxResolution_SelectionChanged" /> - </Grid> - </Border> - - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition Width="5*" MinWidth="200" /> - <ColumnDefinition Width="5*" MinWidth="200" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="Segoe UI Symbol" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickSettingsPage_DisplayFrequency}" /> - <TextBlock - Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" - Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Static resx:Resources.QuickSettingsPage_DisplayFrequencyDesc}" - TextWrapping="Wrap" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ComboBox - Name="ComboBoxFrequency" - Grid.Column="1" - Margin="6,0,0,0" - HorizontalAlignment="Stretch" - IsReadOnly="True" - SelectionChanged="ComboBoxFrequency_SelectionChanged" /> - </Grid> - </Border> - - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition /> - <ColumnDefinition Width="0.2*" MinWidth="80" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Wifi" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="WifiToggle" - Grid.Column="1" - HorizontalAlignment="Right" - IsEnabled="False" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="WIFIToggle_Toggled" /> - </Grid> - </Border> - - <Border - Padding="15,12,12,12" - Background="{DynamicResource ExpanderHeaderBackground}" - CornerRadius="{DynamicResource ControlCornerRadius}"> - - <Grid> - <Grid.ColumnDefinitions> - <ColumnDefinition /> - <ColumnDefinition Width="0.2*" MinWidth="80" /> - </Grid.ColumnDefinitions> - - <DockPanel> - <ui:FontIcon - Height="40" - HorizontalAlignment="Center" - FontFamily="{DynamicResource SymbolThemeFontFamily}" - Glyph="" /> - - <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> - <TextBlock - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" - Text="Bluetooth" /> - </ui:SimpleStackPanel> - </DockPanel> - - <ui:ToggleSwitch - Name="BluetoothToggle" - Grid.Column="1" - HorizontalAlignment="Right" - IsEnabled="False" - Style="{DynamicResource InvertedToggleSwitchStyle}" - Toggled="BluetoothToggle_Toggled" /> - </Grid> - </Border> - </ui:SimpleStackPanel> - </Grid> +<Page + x:Class="HandheldCompanion.Views.QuickPages.QuickSettingsPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" +<<<<<<< HEAD + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + Name="QuickSettings" + Title="{x:Static resx:Resources.QuickSettingsPage_Title}" +======= + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + Title="Settings" +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Margin="15,0,0,0" + d:Background="White" + d:DesignHeight="1200" + d:DesignWidth="640" + KeepAlive="True" + mc:Ignorable="d"> + + <Grid> + <Grid.RowDefinitions> + <RowDefinition Height="7*" /> + <RowDefinition Height="3*" /> + </Grid.RowDefinitions> + + <!-- Shortcuts --> + <UniformGrid + Name="QuickHotkeys" + VerticalAlignment="Top" + Columns="4"> + <UniformGrid.Resources> + <Style TargetType="{x:Type ui:SimpleStackPanel}"> + <Setter Property="Margin" Value="6" /> + </Style> + </UniformGrid.Resources> + </UniformGrid> + + <!-- Toggles --> + <ui:SimpleStackPanel + Grid.Row="1" + Margin="0,6,0,6" + VerticalAlignment="Bottom" + Spacing="6"> + <TextBlock Style="{StaticResource BaseTextBlockStyle}" Text="Device settings" /> + + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="Segoe Fluent Icons" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickSettingsPage_DisplayResolution}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.QuickSettingsPage_DisplayResolutionDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="ComboBoxResolution" + Grid.Column="1" + Margin="6,0,0,0" + HorizontalAlignment="Stretch" + IsReadOnly="True" + SelectionChanged="ComboBoxResolution_SelectionChanged" /> + </Grid> + </Border> + + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition Width="5*" MinWidth="200" /> + <ColumnDefinition Width="5*" MinWidth="200" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="Segoe UI Symbol" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Static resx:Resources.QuickSettingsPage_DisplayFrequency}" /> + <TextBlock + Foreground="{DynamicResource SystemControlForegroundBaseMediumBrush}" + Style="{StaticResource CaptionTextBlockStyle}" + Text="{x:Static resx:Resources.QuickSettingsPage_DisplayFrequencyDesc}" + TextWrapping="Wrap" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ComboBox + Name="ComboBoxFrequency" + Grid.Column="1" + Margin="6,0,0,0" + HorizontalAlignment="Stretch" + IsReadOnly="True" + SelectionChanged="ComboBoxFrequency_SelectionChanged" /> + </Grid> + </Border> + + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition /> + <ColumnDefinition Width="0.2*" MinWidth="80" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Wifi" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="WifiToggle" + Grid.Column="1" + HorizontalAlignment="Right" + IsEnabled="False" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="WIFIToggle_Toggled" /> + </Grid> + </Border> + + <Border + Padding="15,12,12,12" + Background="{DynamicResource ExpanderHeaderBackground}" + CornerRadius="{DynamicResource ControlCornerRadius}"> + + <Grid> + <Grid.ColumnDefinitions> + <ColumnDefinition /> + <ColumnDefinition Width="0.2*" MinWidth="80" /> + </Grid.ColumnDefinitions> + + <DockPanel> + <ui:FontIcon + Height="40" + HorizontalAlignment="Center" + FontFamily="{DynamicResource SymbolThemeFontFamily}" + Glyph="" /> + + <ui:SimpleStackPanel Margin="12,0,0,0" VerticalAlignment="Center"> + <TextBlock + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" + Text="Bluetooth" /> + </ui:SimpleStackPanel> + </DockPanel> + + <ui:ToggleSwitch + Name="BluetoothToggle" + Grid.Column="1" + HorizontalAlignment="Right" + IsEnabled="False" + Style="{DynamicResource InvertedToggleSwitchStyle}" + Toggled="BluetoothToggle_Toggled" /> + </Grid> + </Border> + </ui:SimpleStackPanel> + </Grid> </Page> \ No newline at end of file diff --git a/HandheldCompanion/Views/QuickPages/QuickSettingsPage.xaml.cs b/HandheldCompanion/Views/QuickPages/QuickSettingsPage.xaml.cs index 83fa53afa..3d781b118 100644 --- a/HandheldCompanion/Views/QuickPages/QuickSettingsPage.xaml.cs +++ b/HandheldCompanion/Views/QuickPages/QuickSettingsPage.xaml.cs @@ -1,154 +1,176 @@ -using HandheldCompanion.Managers; -using HandheldCompanion.Managers.Desktop; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Timers; -using System.Windows; -using System.Windows.Controls; -using Windows.Devices.Bluetooth; -using Windows.Devices.Radios; - -namespace HandheldCompanion.Views.QuickPages; - -/// <summary> -/// Interaction logic for QuickSettingsPage.xaml -/// </summary> -public partial class QuickSettingsPage : Page -{ - private IReadOnlyList<Radio> radios; - private Timer radioTimer; - - public QuickSettingsPage(string Tag) : this() - { - this.Tag = Tag; - - HotkeysManager.HotkeyCreated += HotkeysManager_HotkeyCreated; - HotkeysManager.HotkeyUpdated += HotkeysManager_HotkeyUpdated; - - SystemManager.PrimaryScreenChanged += DesktopManager_PrimaryScreenChanged; - SystemManager.DisplaySettingsChanged += DesktopManager_DisplaySettingsChanged; - - radioTimer = new(1000); - radioTimer.Elapsed += RadioTimer_Elapsed; - radioTimer.Start(); - } - - private void RadioTimer_Elapsed(object? sender, ElapsedEventArgs e) - { - new Task(async () => - { - // Get the Bluetooth adapter - BluetoothAdapter adapter = await BluetoothAdapter.GetDefaultAsync(); - - // Get the Bluetooth radio - radios = await Radio.GetRadiosAsync(); - - // UI thread (async) - _ = Application.Current.Dispatcher.BeginInvoke(() => - { - // WIFI - WifiToggle.IsEnabled = radios.Where(radio => radio.Kind == RadioKind.WiFi).Any(); - WifiToggle.IsOn = radios.Where(radio => radio.Kind == RadioKind.WiFi && radio.State == RadioState.On).Any(); - - // Bluetooth - BluetoothToggle.IsEnabled = radios.Where(radio => radio.Kind == RadioKind.Bluetooth).Any(); - BluetoothToggle.IsOn = radios.Where(radio => radio.Kind == RadioKind.Bluetooth && radio.State == RadioState.On).Any(); - }); - }).Start(); - } - - public QuickSettingsPage() - { - InitializeComponent(); - } - - private void HotkeysManager_HotkeyUpdated(Hotkey hotkey) - { - UpdatePins(); - } - - private void HotkeysManager_HotkeyCreated(Hotkey hotkey) - { - UpdatePins(); - } - - private void UpdatePins() - { - // todo, implement quick hotkey order - QuickHotkeys.Children.Clear(); - - foreach (var hotkey in HotkeysManager.Hotkeys.Values.Where(item => item.IsPinned)) - QuickHotkeys.Children.Add(hotkey.GetPin()); - } - - private void DesktopManager_PrimaryScreenChanged(DesktopScreen screen) - { - ComboBoxResolution.Items.Clear(); - foreach (var resolution in screen.resolutions) - ComboBoxResolution.Items.Add(resolution); - } - - private void DesktopManager_DisplaySettingsChanged(ScreenResolution resolution) - { - ComboBoxResolution.SelectedItem = resolution; - ComboBoxFrequency.SelectedItem = SystemManager.GetDesktopScreen().GetFrequency(); - } - - private void ComboBoxResolution_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (ComboBoxResolution.SelectedItem is null) - return; - - var resolution = (ScreenResolution)ComboBoxResolution.SelectedItem; - - ComboBoxFrequency.Items.Clear(); - foreach (var frequency in resolution.Frequencies.Values) - ComboBoxFrequency.Items.Add(frequency); - - ComboBoxFrequency.SelectedItem = SystemManager.GetDesktopScreen().GetFrequency(); - - SetResolution(); - } - - private void ComboBoxFrequency_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (ComboBoxFrequency.SelectedItem is null) - return; - - SetResolution(); - } - - private void SetResolution() - { - if (ComboBoxResolution.SelectedItem is null) - return; - - if (ComboBoxFrequency.SelectedItem is null) - return; - - var resolution = (ScreenResolution)ComboBoxResolution.SelectedItem; - var frequency = (ScreenFrequency)ComboBoxFrequency.SelectedItem; - - // update current screen resolution - SystemManager.SetResolution(resolution.Width, resolution.Height, (int)frequency.GetValue(Frequency.Full), resolution.BitsPerPel); - } - - private void WIFIToggle_Toggled(object sender, RoutedEventArgs e) - { - foreach(Radio radio in radios.Where(r => r.Kind == RadioKind.WiFi)) - _ = radio.SetStateAsync(WifiToggle.IsOn ? RadioState.On : RadioState.Off); - } - - private void BluetoothToggle_Toggled(object sender, RoutedEventArgs e) - { - foreach (Radio radio in radios.Where(r => r.Kind == RadioKind.Bluetooth)) - _ = radio.SetStateAsync(BluetoothToggle.IsOn ? RadioState.On : RadioState.Off); - } - - internal void Close() - { - radioTimer.Stop(); - } +using HandheldCompanion.Managers; +<<<<<<< HEAD +using HandheldCompanion.Managers.Desktop; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Timers; +using System.Windows; +using System.Windows.Controls; +using Windows.Devices.Bluetooth; +using Windows.Devices.Radios; +======= +using System; +using System.Linq; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + +namespace HandheldCompanion.Views.QuickPages; + +/// <summary> +/// Interaction logic for QuickSettingsPage.xaml +/// </summary> +public partial class QuickSettingsPage : Page +{ + private IReadOnlyList<Radio> radios; + private Timer radioTimer; + + public QuickSettingsPage(string Tag) : this() + { + this.Tag = Tag; + + HotkeysManager.HotkeyCreated += HotkeysManager_HotkeyCreated; + HotkeysManager.HotkeyUpdated += HotkeysManager_HotkeyUpdated; + + SystemManager.PrimaryScreenChanged += DesktopManager_PrimaryScreenChanged; + SystemManager.DisplaySettingsChanged += DesktopManager_DisplaySettingsChanged; + + radioTimer = new(1000); + radioTimer.Elapsed += RadioTimer_Elapsed; + radioTimer.Start(); + } + + private void RadioTimer_Elapsed(object? sender, ElapsedEventArgs e) + { + new Task(async () => + { + // Get the Bluetooth adapter + BluetoothAdapter adapter = await BluetoothAdapter.GetDefaultAsync(); + + // Get the Bluetooth radio + radios = await Radio.GetRadiosAsync(); + + // UI thread (async) + _ = Application.Current.Dispatcher.BeginInvoke(() => + { + // WIFI + WifiToggle.IsEnabled = radios.Where(radio => radio.Kind == RadioKind.WiFi).Any(); + WifiToggle.IsOn = radios.Where(radio => radio.Kind == RadioKind.WiFi && radio.State == RadioState.On).Any(); + + // Bluetooth + BluetoothToggle.IsEnabled = radios.Where(radio => radio.Kind == RadioKind.Bluetooth).Any(); + BluetoothToggle.IsOn = radios.Where(radio => radio.Kind == RadioKind.Bluetooth && radio.State == RadioState.On).Any(); + }); + }).Start(); + } + + public QuickSettingsPage() + { + InitializeComponent(); + } + + private void HotkeysManager_HotkeyUpdated(Hotkey hotkey) + { + UpdatePins(); + } + + private void HotkeysManager_HotkeyCreated(Hotkey hotkey) + { + UpdatePins(); + } + + private void UpdatePins() + { + // todo, implement quick hotkey order + QuickHotkeys.Children.Clear(); + + foreach (var hotkey in HotkeysManager.Hotkeys.Values.Where(item => item.IsPinned)) + QuickHotkeys.Children.Add(hotkey.GetPin()); + } + + private void DesktopManager_PrimaryScreenChanged(DesktopScreen screen) + { + ComboBoxResolution.Items.Clear(); + foreach (var resolution in screen.resolutions) + ComboBoxResolution.Items.Add(resolution); + } + + private void DesktopManager_DisplaySettingsChanged(ScreenResolution resolution) + { +<<<<<<< HEAD + ComboBoxResolution.SelectedItem = resolution; + ComboBoxFrequency.SelectedItem = SystemManager.GetDesktopScreen().GetFrequency(); +======= + if (Monitor.TryEnter(volumeLock)) + { + // UI thread + Application.Current.Dispatcher.Invoke(() => + { + // todo: update volume icon on update + SliderVolume.Value = Math.Round(volume); + }); + + Monitor.Exit(volumeLock); + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void ComboBoxResolution_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (ComboBoxResolution.SelectedItem is null) + return; + + var resolution = (ScreenResolution)ComboBoxResolution.SelectedItem; + + ComboBoxFrequency.Items.Clear(); + foreach (var frequency in resolution.Frequencies.Values) + ComboBoxFrequency.Items.Add(frequency); + + ComboBoxFrequency.SelectedItem = SystemManager.GetDesktopScreen().GetFrequency(); + + SetResolution(); + } + + private void ComboBoxFrequency_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (ComboBoxFrequency.SelectedItem is null) + return; + + SetResolution(); + } + + private void SetResolution() + { + if (ComboBoxResolution.SelectedItem is null) + return; + + if (ComboBoxFrequency.SelectedItem is null) + return; + + var resolution = (ScreenResolution)ComboBoxResolution.SelectedItem; + var frequency = (ScreenFrequency)ComboBoxFrequency.SelectedItem; + + // update current screen resolution + SystemManager.SetResolution(resolution.Width, resolution.Height, (int)frequency.GetValue(Frequency.Full), resolution.BitsPerPel); + } + + private void WIFIToggle_Toggled(object sender, RoutedEventArgs e) + { + foreach(Radio radio in radios.Where(r => r.Kind == RadioKind.WiFi)) + _ = radio.SetStateAsync(WifiToggle.IsOn ? RadioState.On : RadioState.Off); + } + + private void BluetoothToggle_Toggled(object sender, RoutedEventArgs e) + { + foreach (Radio radio in radios.Where(r => r.Kind == RadioKind.Bluetooth)) + _ = radio.SetStateAsync(BluetoothToggle.IsOn ? RadioState.On : RadioState.Off); + } + + internal void Close() + { + radioTimer.Stop(); + } } \ No newline at end of file diff --git a/HandheldCompanion/Views/QuickPages/QuickSuspenderPage.xaml b/HandheldCompanion/Views/QuickPages/QuickSuspenderPage.xaml index d5c8a2a90..fbe89dea8 100644 --- a/HandheldCompanion/Views/QuickPages/QuickSuspenderPage.xaml +++ b/HandheldCompanion/Views/QuickPages/QuickSuspenderPage.xaml @@ -1,19 +1,24 @@ -<Page - x:Class="HandheldCompanion.Views.QuickPages.QuickSuspenderPage" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:resx="clr-namespace:HandheldCompanion.Properties" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Name="QuickSuspender" - Title="{x:Static resx:Resources.QuickSuspenderPage_Title}" - Margin="15,0,0,0" - d:Background="White" - d:DesignHeight="1200" - d:DesignWidth="640" - KeepAlive="True" - mc:Ignorable="d"> - - <ui:SimpleStackPanel Name="CurrentProcesses" Spacing="6" /> +<Page + x:Class="HandheldCompanion.Views.QuickPages.QuickSuspenderPage" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" +<<<<<<< HEAD + xmlns:resx="clr-namespace:HandheldCompanion.Properties" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + Name="QuickSuspender" + Title="{x:Static resx:Resources.QuickSuspenderPage_Title}" +======= + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + Title="Suspender" +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + Margin="15,0,0,0" + d:Background="White" + d:DesignHeight="1200" + d:DesignWidth="640" + KeepAlive="True" + mc:Ignorable="d"> + + <ui:SimpleStackPanel Name="CurrentProcesses" Spacing="6" /> </Page> \ No newline at end of file diff --git a/HandheldCompanion/Views/SplashScreen.xaml.cs b/HandheldCompanion/Views/SplashScreen.xaml.cs index ef315ae20..a15166bf1 100644 --- a/HandheldCompanion/Views/SplashScreen.xaml.cs +++ b/HandheldCompanion/Views/SplashScreen.xaml.cs @@ -1,15 +1,19 @@ -using HandheldCompanion.Views.Classes; - -namespace HandheldCompanion.Views -{ - /// <summary> - /// Interaction logic for SplashScreen.xaml - /// </summary> - public partial class SplashScreen : OverlayWindow - { - public SplashScreen() - { - InitializeComponent(); - } - } -} +using HandheldCompanion.Views.Classes; +<<<<<<< HEAD +======= +using System.Windows; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + +namespace HandheldCompanion.Views +{ + /// <summary> + /// Interaction logic for SplashScreen.xaml + /// </summary> + public partial class SplashScreen : OverlayWindow + { + public SplashScreen() + { + InitializeComponent(); + } + } +} diff --git a/HandheldCompanion/Views/Windows/MainWindow.xaml b/HandheldCompanion/Views/Windows/MainWindow.xaml index 049593825..0b811cf3f 100644 --- a/HandheldCompanion/Views/Windows/MainWindow.xaml +++ b/HandheldCompanion/Views/Windows/MainWindow.xaml @@ -47,6 +47,18 @@ Tag="ControllerPage"> <ui:NavigationViewItem.Icon> <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + <!-- <Image Source="/Resources/icons/manette-48.png" Margin="-10"/> --> + </ui:NavigationViewItem.Icon> + </ui:NavigationViewItem> + + <!-- Device icon --> + <ui:NavigationViewItem + Name="navDevice" + Content="{x:Static resx:Resources.MainWindow_navDevice}" + Tag="DevicePage"> + <ui:NavigationViewItem.Icon> + <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + <!-- <Image Source="/Resources/icons/device-48.png" Margin="-10"/> --> </ui:NavigationViewItem.Icon> </ui:NavigationViewItem> @@ -77,6 +89,7 @@ Tag="ProfilesPage"> <ui:NavigationViewItem.Icon> <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + <!-- <Image Source="/Resources/icons/games-folder-48.png" Margin="-10"/> --> </ui:NavigationViewItem.Icon> </ui:NavigationViewItem> @@ -87,6 +100,7 @@ Tag="OverlayPage"> <ui:NavigationViewItem.Icon> <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + <!-- <Image Source="/Resources/icons/calques-48.png" Margin="-10"/> --> </ui:NavigationViewItem.Icon> </ui:NavigationViewItem> @@ -97,6 +111,7 @@ Tag="HotkeysPage"> <ui:NavigationViewItem.Icon> <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + <!-- <Image Source="/Resources/icons/ctrl-48.png" Margin="-10"/> --> </ui:NavigationViewItem.Icon> </ui:NavigationViewItem> @@ -107,6 +122,7 @@ Tag="AboutPage"> <ui:NavigationViewItem.Icon> <ui:FontIcon FontFamily="{StaticResource SymbolThemeFontFamily}" Glyph="" /> + <!-- <Image Source="/Resources/icons/info-48.png" Margin="-10"/> --> </ui:NavigationViewItem.Icon> </ui:NavigationViewItem> </ui:NavigationView.MenuItems> @@ -143,9 +159,14 @@ Tag="SettingsPage"> <ui:NavigationViewItem.Icon> <ui:SymbolIcon Symbol="Setting" /> + <!-- <Image Source="/Resources/icons/settings-48.png" Margin="-10"/> --> </ui:NavigationViewItem.Icon> </ui:NavigationViewItem> +<<<<<<< HEAD </ui:NavigationView.FooterMenuItems> +======= + </ui:NavigationView.MenuItems> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d <ui:NavigationView.Content> <Grid> diff --git a/HandheldCompanion/Views/Windows/MainWindow.xaml.cs b/HandheldCompanion/Views/Windows/MainWindow.xaml.cs index c2311f30e..48c197a7c 100644 --- a/HandheldCompanion/Views/Windows/MainWindow.xaml.cs +++ b/HandheldCompanion/Views/Windows/MainWindow.xaml.cs @@ -1,852 +1,949 @@ -using HandheldCompanion.Controllers; -using HandheldCompanion.Devices; -using HandheldCompanion.Inputs; -using HandheldCompanion.Managers; -using HandheldCompanion.Utils; -using HandheldCompanion.Views.Classes; -using HandheldCompanion.Views.Pages; -using HandheldCompanion.Views.Windows; -using Inkore.UI.WPF.Modern.Controls; -using Nefarius.Utilities.DeviceManagement.PnP; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Forms; -using System.Windows.Input; -using System.Windows.Interop; -using System.Windows.Navigation; -using Windows.UI.ViewManagement; -using static HandheldCompanion.Managers.InputsHotkey; -using Application = System.Windows.Application; -using Control = System.Windows.Controls.Control; -using Page = System.Windows.Controls.Page; -using RadioButton = System.Windows.Controls.RadioButton; - -namespace HandheldCompanion.Views; - -/// <summary> -/// Interaction logic for MainWindow.xaml -/// </summary> -public partial class MainWindow : GamepadWindow -{ - // devices vars - public static IDevice CurrentDevice; - - // page vars - private static readonly Dictionary<string, Page> _pages = new(); - - public static ControllerPage controllerPage; - public static DevicePage devicePage; - public static PerformancePage performancePage; - public static ProfilesPage profilesPage; - public static SettingsPage settingsPage; - public static AboutPage aboutPage; - public static OverlayPage overlayPage; - public static HotkeysPage hotkeysPage; - public static LayoutPage layoutPage; - public static NotificationsPage notificationsPage; - - // overlay(s) vars - public static OverlayModel overlayModel; - public static OverlayTrackpad overlayTrackpad; - public static OverlayQuickTools overlayquickTools; - - // manager(s) vars - private static readonly List<Manager> _managers = new(); - public static TaskManager taskManager; - public static PerformanceManager performanceManager; - public static UpdateManager updateManager; - - public static string CurrentExe, CurrentPath; - - private static MainWindow CurrentWindow; - public static FileVersionInfo fileVersionInfo; - - public static string InstallPath = string.Empty; - public static string SettingsPath = string.Empty; - public static string CurrentPageName = string.Empty; - - private bool appClosing; - private bool IsReady; - private readonly NotifyIcon notifyIcon; - private bool NotifyInTaskbar; - private string preNavItemTag; - - private WindowState prevWindowState; - private SplashScreen splashScreen; - - public static UISettings uiSettings; - - private const int WM_QUERYENDSESSION = 0x0011; - - public MainWindow(FileVersionInfo _fileVersionInfo, Assembly CurrentAssembly) - { - InitializeComponent(); - - fileVersionInfo = _fileVersionInfo; - CurrentWindow = this; - - // used by system manager, controller manager - uiSettings = new UISettings(); - - // used by gamepad navigation - Tag = "MainWindow"; - - // get process - var process = Process.GetCurrentProcess(); - - // fix touch support - var tablets = Tablet.TabletDevices; - - // get first start - bool FirstStart = SettingsManager.GetBoolean("FirstStart"); - - // define current directory - InstallPath = AppDomain.CurrentDomain.BaseDirectory; - SettingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), - "HandheldCompanion"); - - // initialiaze path - if (!Directory.Exists(SettingsPath)) - Directory.CreateDirectory(SettingsPath); - - Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); - - // initialize XInputWrapper - XInputPlus.ExtractXInputPlusLibraries(); - - // initialize notifyIcon - notifyIcon = new NotifyIcon - { - Text = Title, - Icon = System.Drawing.Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location), - Visible = false, - ContextMenuStrip = new ContextMenuStrip() - }; - - notifyIcon.DoubleClick += (sender, e) => { SwapWindowState(); }; - - AddNotifyIconItem(Properties.Resources.MainWindow_MainWindow); - AddNotifyIconItem(Properties.Resources.MainWindow_QuickTools); - - AddNotifyIconSeparator(); - - AddNotifyIconItem(Properties.Resources.MainWindow_Exit); - - // paths - CurrentExe = process.MainModule.FileName; - CurrentPath = AppDomain.CurrentDomain.BaseDirectory; - - // initialize HidHide - HidHide.RegisterApplication(CurrentExe); - - // initialize title - Title += $" ({fileVersionInfo.FileVersion})"; - - // initialize device - CurrentDevice = IDevice.GetDefault(); - CurrentDevice.PullSensors(); - - // workaround for Bosch BMI320/BMI323 (as of 06/20/2023) - // todo: check if still needed with Bosch G-sensor Driver V1.0.1.7 - // https://dlcdnets.asus.com/pub/ASUS/IOTHMD/Image/Driver/Chipset/34644/BoschG-sensor_ROG_Bosch_Z_V1.0.1.7_34644.exe?model=ROG%20Ally%20(2023) - - string currentDeviceType = CurrentDevice.GetType().Name; - switch (currentDeviceType) - { - case "AYANEOAIRPlus": - case "ROGAlly": - { - LogManager.LogInformation("Restarting: {0}", CurrentDevice.InternalSensorName); - - if (CurrentDevice.RestartSensor()) - { - // give the device some breathing space once restarted - Thread.Sleep(500); - - LogManager.LogInformation("Successfully restarted: {0}", CurrentDevice.InternalSensorName); - } - else - LogManager.LogError("Failed to restart: {0}", CurrentDevice.InternalSensorName); - } - break; - - case "SteamDeck": - { - // prevent Steam Deck controller from being hidden by default - if (FirstStart) - SettingsManager.SetProperty("HIDcloakonconnect", false); - } - break; - } - - // initialize splash screen on first start only - if (FirstStart) - { - splashScreen = new SplashScreen(); - splashScreen.Show(); - - SettingsManager.SetProperty("FirstStart", false); - } - - // load manager(s) - // todo: make me static - loadManagers(); - - // load window(s) - loadWindows(); - - // load page(s) - loadPages(); - - // start static managers in sequence - // managers that has to be stopped/started when session status changes shouldn't be put here - - ToastManager.Start(); - ToastManager.IsEnabled = SettingsManager.GetBoolean("ToastEnable"); - - PowerProfileManager.Start(); - ProfileManager.Start(); - - ControllerManager.ControllerSelected += ControllerManager_ControllerSelected; - ControllerManager.Start(); - HotkeysManager.Start(); - - DeviceManager.UsbDeviceArrived += GenericDeviceUpdated; - DeviceManager.UsbDeviceRemoved += GenericDeviceUpdated; - DeviceManager.Start(); - - OSDManager.Start(); - LayoutManager.Start(); - - // todo: improve overall threading logic - new Thread(() => - { - PlatformManager.Start(); - ProcessManager.Start(); - }).Start(); - - PowerManager.SystemStatusChanged += OnSystemStatusChanged; - PowerManager.Start(); - - DynamicLightingManager.Start(); - - SystemManager.Start(); - VirtualManager.Start(); - - InputsManager.TriggerRaised += InputsManager_TriggerRaised; - InputsManager.Start(); - SensorsManager.Start(); - TimerManager.Start(); - - VirtualManager.ControllerSelected += VirtualManager_ControllerSelected; - - // start managers asynchroneously - foreach (var manager in _managers) - new Thread(manager.Start).Start(); - - // start setting last - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - SettingsManager.Start(); - - // update Position and Size - Height = (int)Math.Max(MinHeight, SettingsManager.GetDouble("MainWindowHeight")); - Width = (int)Math.Max(MinWidth, SettingsManager.GetDouble("MainWindowWidth")); - Left = Math.Min(SystemParameters.PrimaryScreenWidth - MinWidth, SettingsManager.GetDouble("MainWindowLeft")); - Top = Math.Min(SystemParameters.PrimaryScreenHeight - MinHeight, SettingsManager.GetDouble("MainWindowTop")); - navView.IsPaneOpen = SettingsManager.GetBoolean("MainWindowIsPaneOpen"); - } - - private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) - { - // windows shutting down event - if (msg == WM_QUERYENDSESSION) - { - // do something - } - - return IntPtr.Zero; - } - - private void ControllerManager_ControllerSelected(IController Controller) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - GamepadUISelectIcon.Glyph = Controller.GetGlyph(ButtonFlags.B1); - GamepadUISelectIcon.Foreground = Controller.GetGlyphColor(ButtonFlags.B1); - - GamepadUIBackIcon.Glyph = Controller.GetGlyph(ButtonFlags.B2); - GamepadUIBackIcon.Foreground = Controller.GetGlyphColor(ButtonFlags.B2); - - GamepadUIToggleIcon.Glyph = Controller.GetGlyph(ButtonFlags.B4); - GamepadUIToggleIcon.Foreground = Controller.GetGlyphColor(ButtonFlags.B4); - }); - } - - private void GamepadFocusManagerOnFocused(Control control) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - // todo : localize me - string controlType = control.GetType().Name; - switch (controlType) - { - default: - { - GamepadUISelect.Visibility = Visibility.Visible; - GamepadUIBack.Visibility = Visibility.Visible; - GamepadUIToggle.Visibility = Visibility.Collapsed; - - GamepadUISelectDesc.Text = Properties.Resources.MainWindow_Select; - GamepadUIBackDesc.Text = Properties.Resources.MainWindow_Back; - } - break; - - case "Button": - { - GamepadUISelect.Visibility = Visibility.Visible; - GamepadUIBack.Visibility = Visibility.Visible; - - GamepadUISelectDesc.Text = Properties.Resources.MainWindow_Select; - GamepadUIBackDesc.Text = Properties.Resources.MainWindow_Back; - - // To get the first RadioButton in the list, if any - RadioButton firstRadioButton = WPFUtils.FindChildren(control).FirstOrDefault(c => c is RadioButton) as RadioButton; - if (firstRadioButton is not null) - { - GamepadUIToggle.Visibility = Visibility.Visible; - GamepadUIToggleDesc.Text = Properties.Resources.MainWindow_Toggle; - } - } - break; - - case "Slider": - { - GamepadUISelect.Visibility = Visibility.Collapsed; - GamepadUIBack.Visibility = Visibility.Visible; - GamepadUIToggle.Visibility = Visibility.Collapsed; - } - break; - - case "NavigationViewItem": - { - GamepadUISelect.Visibility = Visibility.Visible; - GamepadUIBack.Visibility = Visibility.Collapsed; - GamepadUIToggle.Visibility = Visibility.Collapsed; - - GamepadUISelectDesc.Text = Properties.Resources.MainWindow_Navigate; - } - break; - } - }); - } - - private void AddNotifyIconItem(string name, object tag = null) - { - tag ??= string.Concat(name.Where(c => !char.IsWhiteSpace(c))); - - var menuItemMainWindow = new ToolStripMenuItem(name); - menuItemMainWindow.Tag = tag; - menuItemMainWindow.Click += MenuItem_Click; - notifyIcon.ContextMenuStrip.Items.Add(menuItemMainWindow); - } - - private void AddNotifyIconSeparator() - { - var separator = new ToolStripSeparator(); - notifyIcon.ContextMenuStrip.Items.Add(separator); - } - - private void SettingsManager_SettingValueChanged(string name, object value) - { - switch (name) - { - case "ToastEnable": - ToastManager.IsEnabled = Convert.ToBoolean(value); - break; - case "DesktopProfileOnStart": - if (SettingsManager.IsInitialized) - break; - - var DesktopLayout = Convert.ToBoolean(value); - SettingsManager.SetProperty("DesktopLayoutEnabled", DesktopLayout, false, true); - break; - } - } - - public void SwapWindowState() - { - // UI thread - Application.Current.Dispatcher.Invoke(() => - { - switch (WindowState) - { - case WindowState.Normal: - case WindowState.Maximized: - WindowState = WindowState.Minimized; - break; - case WindowState.Minimized: - WindowState = prevWindowState; - break; - } - }); - } - - public static MainWindow GetCurrent() - { - return CurrentWindow; - } - - private void loadPages() - { - // initialize pages - controllerPage = new ControllerPage("controller"); - controllerPage.Loaded += ControllerPage_Loaded; - - devicePage = new DevicePage("device"); - performancePage = new PerformancePage("performance"); - profilesPage = new ProfilesPage("profiles"); - settingsPage = new SettingsPage("settings"); - aboutPage = new AboutPage("about"); - overlayPage = new OverlayPage("overlay"); - hotkeysPage = new HotkeysPage("hotkeys"); - layoutPage = new LayoutPage("layout", navView); - notificationsPage = new NotificationsPage("notifications"); - notificationsPage.StatusChanged += NotificationsPage_LayoutUpdated; - - // store pages - _pages.Add("ControllerPage", controllerPage); - _pages.Add("DevicePage", devicePage); - _pages.Add("PerformancePage", performancePage); - _pages.Add("ProfilesPage", profilesPage); - _pages.Add("AboutPage", aboutPage); - _pages.Add("OverlayPage", overlayPage); - _pages.Add("SettingsPage", settingsPage); - _pages.Add("HotkeysPage", hotkeysPage); - _pages.Add("LayoutPage", layoutPage); - _pages.Add("NotificationsPage", notificationsPage); - } - - private void loadWindows() - { - // initialize overlay - overlayModel = new OverlayModel(); - overlayTrackpad = new OverlayTrackpad(); - overlayquickTools = new OverlayQuickTools(); - } - - private void loadManagers() - { - // initialize managers - taskManager = new TaskManager("HandheldCompanion", CurrentExe); - performanceManager = new PerformanceManager(); - updateManager = new UpdateManager(); - - // store managers - _managers.Add(taskManager); - _managers.Add(performanceManager); - _managers.Add(updateManager); - } - - private void GenericDeviceUpdated(PnPDevice device, DeviceEventArgs obj) - { - // todo: improve me - CurrentDevice.PullSensors(); - - aboutPage.UpdateDevice(device); - settingsPage.UpdateDevice(device); - } - - private void InputsManager_TriggerRaised(string listener, InputsChord input, InputsHotkeyType type, bool IsKeyDown, - bool IsKeyUp) - { - switch (listener) - { - case "quickTools": - overlayquickTools.ToggleVisibility(); - break; - case "overlayGamepad": - overlayModel.ToggleVisibility(); - break; - case "overlayTrackpads": - overlayTrackpad.ToggleVisibility(); - break; - case "shortcutMainwindow": - SwapWindowState(); - break; - } - } - - private void MenuItem_Click(object? sender, EventArgs e) - { - switch (((ToolStripMenuItem)sender).Tag) - { - case "MainWindow": - SwapWindowState(); - break; - case "QuickTools": - overlayquickTools.ToggleVisibility(); - break; - case "Exit": - appClosing = true; - Close(); - break; - } - } - - private void Window_Loaded(object sender, RoutedEventArgs e) - { - // load gamepad navigation maanger - gamepadFocusManager = new(this, ContentFrame); - - HwndSource source = PresentationSource.FromVisual(this) as HwndSource; - source.AddHook(WndProc); // Hook into the window's message loop - } - - private void ControllerPage_Loaded(object sender, RoutedEventArgs e) - { - if (IsReady) - return; - - // hide splashscreen - if (splashScreen is not null) - splashScreen.Close(); - - // home page has loaded, display main window - WindowState = SettingsManager.GetBoolean("StartMinimized") - ? WindowState.Minimized - : (WindowState)SettingsManager.GetInt("MainWindowState"); - prevWindowState = (WindowState)SettingsManager.GetInt("MainWindowPrevState"); - - IsReady = true; - } - - private void NotificationsPage_LayoutUpdated(int status) - { - bool hasNotification = Convert.ToBoolean(status); - - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - HasNotifications.Visibility = hasNotification ? Visibility.Visible : Visibility.Collapsed; - }); - } - - private void VirtualManager_ControllerSelected(HIDmode HIDmode) - { - Application.Current.Dispatcher.BeginInvoke(() => - { - overlayModel.UpdateHIDMode(HIDmode); - }); - CurrentDevice.SetKeyPressDelay(HIDmode); - } - - public void UpdateSettings(Dictionary<string, string> args) - { - foreach (var pair in args) - { - var name = pair.Key; - var property = pair.Value; - - switch (name) - { - case "DSUEnabled": - break; - case "DSUip": - break; - case "DSUport": - break; - } - } - } - - // no code from the cases inside this function will be called on program start - private async void OnSystemStatusChanged(PowerManager.SystemStatus status, PowerManager.SystemStatus prevStatus) - { - if (status == prevStatus) - return; - - switch (status) - { - case PowerManager.SystemStatus.SystemReady: - { - // resume from sleep - if (prevStatus == PowerManager.SystemStatus.SystemPending) - { - // use device-specific delay - await Task.Delay(CurrentDevice.ResumeDelay); - - // restore inputs manager - InputsManager.Start(); - - // start timer manager - TimerManager.Start(); - - // resume the virtual controller last - VirtualManager.Resume(); - - // restart IMU - SensorsManager.Resume(true); - } - - // open device, when ready - new Thread(() => - { - // wait for all HIDs to be ready - while (!CurrentDevice.IsReady()) - Thread.Sleep(500); - - // open current device (threaded to avoid device to hang) - CurrentDevice.Open(); - }).Start(); - } - break; - - case PowerManager.SystemStatus.SystemPending: - // sleep - { - // stop the virtual controller - VirtualManager.Suspend(); - - // stop timer manager - TimerManager.Stop(); - - // stop sensors - SensorsManager.Stop(); - - // pause inputs manager - InputsManager.Stop(); - - // close current device - CurrentDevice.Close(); - } - break; - } - } - - #region UI - - private void navView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) - { - if (args.InvokedItemContainer is not null) - { - var navItem = (NavigationViewItem)args.InvokedItemContainer; - var navItemTag = (string)navItem.Tag; - - switch (navItemTag) - { - default: - preNavItemTag = navItemTag; - break; - } - - NavView_Navigate(preNavItemTag); - } - } - - public void NavView_Navigate(string navItemTag) - { - var item = _pages.FirstOrDefault(p => p.Key.Equals(navItemTag)); - var _page = item.Value; - - // Get the page type before navigation so you can prevent duplicate - // entries in the backstack. - var preNavPageType = ContentFrame.CurrentSourcePageType; - - // Only navigate if the selected page isn't currently loaded. - if (!(_page is null) && !Equals(preNavPageType, _page)) NavView_Navigate(_page); - } - - public static void NavView_Navigate(Page _page) - { - CurrentWindow.ContentFrame.Navigate(_page); - CurrentWindow.scrollViewer.ScrollToTop(); - } - - private void navView_BackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args) - { - TryGoBack(); - } - - private void Window_Closed(object sender, EventArgs e) - { - CurrentDevice.Close(); - - notifyIcon.Visible = false; - notifyIcon.Dispose(); - - overlayModel.Close(); - overlayTrackpad.Close(); - overlayquickTools.Close(true); - - // TODO: Make static - taskManager.Stop(); - performanceManager.Stop(); - - VirtualManager.Stop(); - SystemManager.Stop(); - MotionManager.Stop(); - SensorsManager.Stop(); - - ControllerManager.Stop(); - InputsManager.Stop(); - DeviceManager.Stop(); - PlatformManager.Stop(); - OSDManager.Stop(); - PowerProfileManager.Stop(); - ProfileManager.Stop(); - LayoutManager.Stop(); - PowerManager.Stop(); - ProcessManager.Stop(); - ToastManager.Stop(); - - // closing page(s) - controllerPage.Page_Closed(); - profilesPage.Page_Closed(); - settingsPage.Page_Closed(); - overlayPage.Page_Closed(); - hotkeysPage.Page_Closed(); - layoutPage.Page_Closed(); - notificationsPage.Page_Closed(); - - // force kill application - Environment.Exit(0); - } - - private async void Window_Closing(object sender, CancelEventArgs e) - { - // position and size settings - switch (WindowState) - { - case WindowState.Normal: - SettingsManager.SetProperty("MainWindowLeft", Left); - SettingsManager.SetProperty("MainWindowTop", Top); - SettingsManager.SetProperty("MainWindowWidth", ActualWidth); - SettingsManager.SetProperty("MainWindowHeight", ActualHeight); - break; - case WindowState.Maximized: - SettingsManager.SetProperty("MainWindowLeft", 0); - SettingsManager.SetProperty("MainWindowTop", 0); - SettingsManager.SetProperty("MainWindowWidth", SystemParameters.MaximizedPrimaryScreenWidth); - SettingsManager.SetProperty("MainWindowHeight", SystemParameters.MaximizedPrimaryScreenHeight); - - break; - } - - SettingsManager.SetProperty("MainWindowState", (int)WindowState); - SettingsManager.SetProperty("MainWindowPrevState", (int)prevWindowState); - - SettingsManager.SetProperty("MainWindowIsPaneOpen", navView.IsPaneOpen); - - if (SettingsManager.GetBoolean("CloseMinimises") && !appClosing) - { - e.Cancel = true; - WindowState = WindowState.Minimized; - return; - } - } - - private void Window_StateChanged(object sender, EventArgs e) - { - switch (WindowState) - { - case WindowState.Minimized: - notifyIcon.Visible = true; - ShowInTaskbar = false; - - if (!NotifyInTaskbar) - { - ToastManager.SendToast(Title, "is running in the background"); - NotifyInTaskbar = true; - } - - break; - case WindowState.Normal: - case WindowState.Maximized: - notifyIcon.Visible = false; - ShowInTaskbar = true; - - Activate(); - Topmost = true; // important - Topmost = false; // important - Focus(); - - prevWindowState = WindowState; - break; - } - } - - private void navView_Loaded(object sender, RoutedEventArgs e) - { - // Add handler for ContentFrame navigation. - ContentFrame.Navigated += On_Navigated; - - // NavView doesn't load any page by default, so load home page. - navView.SelectedItem = navView.MenuItems[0]; - - // If navigation occurs on SelectionChanged, this isn't needed. - // Because we use ItemInvoked to navigate, we need to call Navigate - // here to load the home page. - preNavItemTag = "ControllerPage"; - NavView_Navigate(preNavItemTag); - } - - private void GamepadWindow_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) - { - if (!e.NewFocus.GetType().IsSubclassOf(typeof(Control))) - return; - - GamepadFocusManagerOnFocused((Control)e.NewFocus); - } - - private void GamepadWindow_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) - { - // do something - } - - private bool TryGoBack() - { - if (!ContentFrame.CanGoBack) - return false; - - // Don't go back if the nav pane is overlayed. - if (navView.IsPaneOpen && - (navView.DisplayMode == NavigationViewDisplayMode.Compact || - navView.DisplayMode == NavigationViewDisplayMode.Minimal)) - return false; - - ContentFrame.GoBack(); - return true; - } - - private void On_Navigated(object sender, NavigationEventArgs e) - { - navView.IsBackEnabled = ContentFrame.CanGoBack; - - if (ContentFrame.SourcePageType is not null) - { - CurrentPageName = ContentFrame.CurrentSourcePageType.Name; - - var NavViewItem = navView.MenuItems - .OfType<NavigationViewItem>() - .Where(n => n.Tag.Equals(CurrentPageName)).FirstOrDefault(); - - if (!(NavViewItem is null)) - navView.SelectedItem = NavViewItem; - - navView.Header = new TextBlock() { Text = (string)((Page)e.Content).Title }; - } - } - - #endregion -} +using HandheldCompanion.Controllers; +using HandheldCompanion.Devices; +using HandheldCompanion.Inputs; +using HandheldCompanion.Managers; +<<<<<<< HEAD +======= +using HandheldCompanion.Misc; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using HandheldCompanion.Utils; +using HandheldCompanion.Views.Classes; +using HandheldCompanion.Views.Pages; +using HandheldCompanion.Views.Windows; +using Inkore.UI.WPF.Modern.Controls; +using Nefarius.Utilities.DeviceManagement.PnP; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Forms; +using System.Windows.Input; +using System.Windows.Interop; +using System.Windows.Navigation; +<<<<<<< HEAD +using Windows.UI.ViewManagement; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d +using static HandheldCompanion.Managers.InputsHotkey; +using Application = System.Windows.Application; +using Control = System.Windows.Controls.Control; +using Page = System.Windows.Controls.Page; +<<<<<<< HEAD +using RadioButton = System.Windows.Controls.RadioButton; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + +namespace HandheldCompanion.Views; + +/// <summary> +/// Interaction logic for MainWindow.xaml +/// </summary> +public partial class MainWindow : GamepadWindow +{ + // devices vars + public static IDevice CurrentDevice; + + // page vars + private static readonly Dictionary<string, Page> _pages = new(); + + public static ControllerPage controllerPage; + public static DevicePage devicePage; +<<<<<<< HEAD + public static PerformancePage performancePage; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + public static ProfilesPage profilesPage; + public static SettingsPage settingsPage; + public static AboutPage aboutPage; + public static OverlayPage overlayPage; + public static HotkeysPage hotkeysPage; + public static LayoutPage layoutPage; + public static NotificationsPage notificationsPage; + + // overlay(s) vars + public static OverlayModel overlayModel; + public static OverlayTrackpad overlayTrackpad; + public static OverlayQuickTools overlayquickTools; + + // manager(s) vars + private static readonly List<Manager> _managers = new(); + public static TaskManager taskManager; + public static PerformanceManager performanceManager; + public static UpdateManager updateManager; + + public static string CurrentExe, CurrentPath; + + private static MainWindow CurrentWindow; + public static FileVersionInfo fileVersionInfo; + + public static string InstallPath = string.Empty; + public static string SettingsPath = string.Empty; + public static string CurrentPageName = string.Empty; + + private bool appClosing; + private bool IsReady; + private readonly NotifyIcon notifyIcon; + private bool NotifyInTaskbar; + private string preNavItemTag; + + private WindowState prevWindowState; + private SplashScreen splashScreen; + +<<<<<<< HEAD + public static UISettings uiSettings; + +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + private const int WM_QUERYENDSESSION = 0x0011; + + public MainWindow(FileVersionInfo _fileVersionInfo, Assembly CurrentAssembly) + { + InitializeComponent(); + + fileVersionInfo = _fileVersionInfo; + CurrentWindow = this; + + // used by system manager, controller manager + uiSettings = new UISettings(); + + // used by gamepad navigation + Tag = "MainWindow"; + + // get process + var process = Process.GetCurrentProcess(); + + // fix touch support + var tablets = Tablet.TabletDevices; + + // get first start + bool FirstStart = SettingsManager.GetBoolean("FirstStart"); + + // define current directory + InstallPath = AppDomain.CurrentDomain.BaseDirectory; + SettingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), + "HandheldCompanion"); + + // initialiaze path + if (!Directory.Exists(SettingsPath)) + Directory.CreateDirectory(SettingsPath); + + Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); + + // initialize XInputWrapper + XInputPlus.ExtractXInputPlusLibraries(); + + // initialize notifyIcon + notifyIcon = new NotifyIcon + { + Text = Title, + Icon = System.Drawing.Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location), + Visible = false, + ContextMenuStrip = new ContextMenuStrip() + }; + + notifyIcon.DoubleClick += (sender, e) => { SwapWindowState(); }; + + AddNotifyIconItem(Properties.Resources.MainWindow_MainWindow); + AddNotifyIconItem(Properties.Resources.MainWindow_QuickTools); + + AddNotifyIconSeparator(); + + AddNotifyIconItem(Properties.Resources.MainWindow_Exit); + + // paths + CurrentExe = process.MainModule.FileName; + CurrentPath = AppDomain.CurrentDomain.BaseDirectory; + + // initialize HidHide + HidHide.RegisterApplication(CurrentExe); + + // initialize title + Title += $" ({fileVersionInfo.FileVersion})"; + + // initialize device + CurrentDevice = IDevice.GetDefault(); + CurrentDevice.PullSensors(); + + // workaround for Bosch BMI320/BMI323 (as of 06/20/2023) + // todo: check if still needed with Bosch G-sensor Driver V1.0.1.7 + // https://dlcdnets.asus.com/pub/ASUS/IOTHMD/Image/Driver/Chipset/34644/BoschG-sensor_ROG_Bosch_Z_V1.0.1.7_34644.exe?model=ROG%20Ally%20(2023) + + string currentDeviceType = CurrentDevice.GetType().Name; + switch (currentDeviceType) + { + case "AYANEOAIRPlus": + case "ROGAlly": + { + LogManager.LogInformation("Restarting: {0}", CurrentDevice.InternalSensorName); + + if (CurrentDevice.RestartSensor()) + { + // give the device some breathing space once restarted + Thread.Sleep(500); + + LogManager.LogInformation("Successfully restarted: {0}", CurrentDevice.InternalSensorName); + } + else + LogManager.LogError("Failed to restart: {0}", CurrentDevice.InternalSensorName); + } + break; + + case "SteamDeck": + { + // prevent Steam Deck controller from being hidden by default + if (FirstStart) + SettingsManager.SetProperty("HIDcloakonconnect", false); + } + break; + } + + // initialize splash screen on first start only + if (FirstStart) + { + splashScreen = new SplashScreen(); + splashScreen.Show(); + + SettingsManager.SetProperty("FirstStart", false); + } + + // load manager(s) + // todo: make me static + loadManagers(); + + // load window(s) + loadWindows(); + + // load page(s) + loadPages(); + + // start static managers in sequence + // managers that has to be stopped/started when session status changes shouldn't be put here + + ToastManager.Start(); + ToastManager.IsEnabled = SettingsManager.GetBoolean("ToastEnable"); + + PowerProfileManager.Start(); + ProfileManager.Start(); + + ControllerManager.ControllerSelected += ControllerManager_ControllerSelected; + ControllerManager.Start(); + HotkeysManager.Start(); + + DeviceManager.UsbDeviceArrived += GenericDeviceUpdated; + DeviceManager.UsbDeviceRemoved += GenericDeviceUpdated; + DeviceManager.Start(); + + OSDManager.Start(); + LayoutManager.Start(); +<<<<<<< HEAD + + // todo: improve overall threading logic + new Thread(() => + { + PlatformManager.Start(); + ProcessManager.Start(); + }).Start(); +======= + ProcessManager.Start(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + PowerManager.SystemStatusChanged += OnSystemStatusChanged; + PowerManager.Start(); + + DynamicLightingManager.Start(); + + SystemManager.Start(); + VirtualManager.Start(); + + InputsManager.TriggerRaised += InputsManager_TriggerRaised; + InputsManager.Start(); + SensorsManager.Start(); + TimerManager.Start(); +<<<<<<< HEAD + + VirtualManager.ControllerSelected += VirtualManager_ControllerSelected; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // start managers asynchroneously + foreach (var manager in _managers) + new Thread(manager.Start).Start(); + + // start setting last + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + SettingsManager.Start(); + + // update Position and Size + Height = (int)Math.Max(MinHeight, SettingsManager.GetDouble("MainWindowHeight")); + Width = (int)Math.Max(MinWidth, SettingsManager.GetDouble("MainWindowWidth")); + Left = Math.Min(SystemParameters.PrimaryScreenWidth - MinWidth, SettingsManager.GetDouble("MainWindowLeft")); + Top = Math.Min(SystemParameters.PrimaryScreenHeight - MinHeight, SettingsManager.GetDouble("MainWindowTop")); + navView.IsPaneOpen = SettingsManager.GetBoolean("MainWindowIsPaneOpen"); + } + + private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) + { + // windows shutting down event + if (msg == WM_QUERYENDSESSION) + { +<<<<<<< HEAD + // do something +======= + if (SettingsManager.GetBoolean("VirtualControllerForceOrder")) + { + // disable physical controllers when shutting down to ensure we can give the first order to virtual controller on next boot + foreach (var physicalControllerInstanceId in SettingsManager.GetStringCollection("PhysicalControllerInstanceIds")) + { + PnPUtil.DisableDevice(physicalControllerInstanceId); + } + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + return IntPtr.Zero; + } + + private void ControllerManager_ControllerSelected(IController Controller) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + GamepadUISelectIcon.Glyph = Controller.GetGlyph(ButtonFlags.B1); + GamepadUISelectIcon.Foreground = Controller.GetGlyphColor(ButtonFlags.B1); + + GamepadUIBackIcon.Glyph = Controller.GetGlyph(ButtonFlags.B2); + GamepadUIBackIcon.Foreground = Controller.GetGlyphColor(ButtonFlags.B2); + + GamepadUIToggleIcon.Glyph = Controller.GetGlyph(ButtonFlags.B4); + GamepadUIToggleIcon.Foreground = Controller.GetGlyphColor(ButtonFlags.B4); + }); + } + + private void GamepadFocusManagerOnFocused(Control control) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + // todo : localize me + string controlType = control.GetType().Name; + switch (controlType) + { + default: + { + GamepadUISelect.Visibility = Visibility.Visible; +<<<<<<< HEAD + GamepadUIBack.Visibility = Visibility.Visible; + GamepadUIToggle.Visibility = Visibility.Collapsed; + + GamepadUISelectDesc.Text = Properties.Resources.MainWindow_Select; + GamepadUIBackDesc.Text = Properties.Resources.MainWindow_Back; + } + break; + + case "Button": + { + GamepadUISelect.Visibility = Visibility.Visible; + GamepadUIBack.Visibility = Visibility.Visible; + + GamepadUISelectDesc.Text = Properties.Resources.MainWindow_Select; + GamepadUIBackDesc.Text = Properties.Resources.MainWindow_Back; + + // To get the first RadioButton in the list, if any + RadioButton firstRadioButton = WPFUtils.FindChildren(control).FirstOrDefault(c => c is RadioButton) as RadioButton; + if (firstRadioButton is not null) + { + GamepadUIToggle.Visibility = Visibility.Visible; + GamepadUIToggleDesc.Text = Properties.Resources.MainWindow_Toggle; + } +======= + GamepadUISelectDesc.Text = Properties.Resources.MainWindow_Select; + + GamepadUIBack.Visibility = Visibility.Visible; + GamepadUIBackDesc.Text = Properties.Resources.MainWindow_Back; +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + break; + + case "Slider": + { + GamepadUISelect.Visibility = Visibility.Collapsed; + GamepadUIBack.Visibility = Visibility.Visible; + GamepadUIToggle.Visibility = Visibility.Collapsed; + } + break; + + case "NavigationViewItem": + { + GamepadUISelect.Visibility = Visibility.Visible; +<<<<<<< HEAD +======= + GamepadUISelectDesc.Text = Properties.Resources.MainWindow_Navigate; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + GamepadUIBack.Visibility = Visibility.Collapsed; + GamepadUIToggle.Visibility = Visibility.Collapsed; + + GamepadUISelectDesc.Text = Properties.Resources.MainWindow_Navigate; + } + break; + } + }); + } + + private void AddNotifyIconItem(string name, object tag = null) + { + tag ??= string.Concat(name.Where(c => !char.IsWhiteSpace(c))); + + var menuItemMainWindow = new ToolStripMenuItem(name); + menuItemMainWindow.Tag = tag; + menuItemMainWindow.Click += MenuItem_Click; + notifyIcon.ContextMenuStrip.Items.Add(menuItemMainWindow); + } + + private void AddNotifyIconSeparator() + { + var separator = new ToolStripSeparator(); + notifyIcon.ContextMenuStrip.Items.Add(separator); + } + + private void SettingsManager_SettingValueChanged(string name, object value) + { + switch (name) + { + case "ToastEnable": + ToastManager.IsEnabled = Convert.ToBoolean(value); + break; + case "DesktopProfileOnStart": + if (SettingsManager.IsInitialized) + break; + + var DesktopLayout = Convert.ToBoolean(value); + SettingsManager.SetProperty("DesktopLayoutEnabled", DesktopLayout, false, true); + break; + } + } + + public void SwapWindowState() + { + // UI thread + Application.Current.Dispatcher.Invoke(() => + { + switch (WindowState) + { + case WindowState.Normal: + case WindowState.Maximized: + WindowState = WindowState.Minimized; + break; + case WindowState.Minimized: + WindowState = prevWindowState; + break; + } + }); + } + + public static MainWindow GetCurrent() + { + return CurrentWindow; + } + + private void loadPages() + { + // initialize pages + controllerPage = new ControllerPage("controller"); + controllerPage.Loaded += ControllerPage_Loaded; + + devicePage = new DevicePage("device"); +<<<<<<< HEAD + performancePage = new PerformancePage("performance"); +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + profilesPage = new ProfilesPage("profiles"); + settingsPage = new SettingsPage("settings"); + aboutPage = new AboutPage("about"); + overlayPage = new OverlayPage("overlay"); + hotkeysPage = new HotkeysPage("hotkeys"); + layoutPage = new LayoutPage("layout", navView); +<<<<<<< HEAD + notificationsPage = new NotificationsPage("notifications"); + notificationsPage.StatusChanged += NotificationsPage_LayoutUpdated; +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // store pages + _pages.Add("ControllerPage", controllerPage); + _pages.Add("DevicePage", devicePage); +<<<<<<< HEAD + _pages.Add("PerformancePage", performancePage); +======= +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + _pages.Add("ProfilesPage", profilesPage); + _pages.Add("AboutPage", aboutPage); + _pages.Add("OverlayPage", overlayPage); + _pages.Add("SettingsPage", settingsPage); + _pages.Add("HotkeysPage", hotkeysPage); + _pages.Add("LayoutPage", layoutPage); + _pages.Add("NotificationsPage", notificationsPage); + } + + private void loadWindows() + { + // initialize overlay + overlayModel = new OverlayModel(); + overlayTrackpad = new OverlayTrackpad(); + overlayquickTools = new OverlayQuickTools(); + } + + private void loadManagers() + { + // initialize managers + taskManager = new TaskManager("HandheldCompanion", CurrentExe); + performanceManager = new PerformanceManager(); + updateManager = new UpdateManager(); + + // store managers + _managers.Add(taskManager); + _managers.Add(performanceManager); + _managers.Add(updateManager); + } + + private void GenericDeviceUpdated(PnPDevice device, DeviceEventArgs obj) + { + // todo: improve me + CurrentDevice.PullSensors(); + + aboutPage.UpdateDevice(device); + settingsPage.UpdateDevice(device); + } + + private void InputsManager_TriggerRaised(string listener, InputsChord input, InputsHotkeyType type, bool IsKeyDown, + bool IsKeyUp) + { + switch (listener) + { + case "quickTools": + overlayquickTools.ToggleVisibility(); + break; + case "overlayGamepad": + overlayModel.ToggleVisibility(); + break; + case "overlayTrackpads": + overlayTrackpad.ToggleVisibility(); + break; + case "shortcutMainwindow": + SwapWindowState(); + break; + } + } + + private void MenuItem_Click(object? sender, EventArgs e) + { + switch (((ToolStripMenuItem)sender).Tag) + { + case "MainWindow": + SwapWindowState(); + break; + case "QuickTools": + overlayquickTools.ToggleVisibility(); + break; + case "Exit": + if (SettingsManager.GetBoolean("VirtualControllerForceOrder")) + SwapWindowState(); + + appClosing = true; + Close(); + break; + } + } + + private void Window_Loaded(object sender, RoutedEventArgs e) + { + // load gamepad navigation maanger + gamepadFocusManager = new(this, ContentFrame); + + HwndSource source = PresentationSource.FromVisual(this) as HwndSource; + source.AddHook(WndProc); // Hook into the window's message loop + } + + private void ControllerPage_Loaded(object sender, RoutedEventArgs e) + { + if (IsReady) + return; + + // hide splashscreen + if (splashScreen is not null) + splashScreen.Close(); + + // home page has loaded, display main window + WindowState = SettingsManager.GetBoolean("StartMinimized") + ? WindowState.Minimized + : (WindowState)SettingsManager.GetInt("MainWindowState"); + prevWindowState = (WindowState)SettingsManager.GetInt("MainWindowPrevState"); + + IsReady = true; + } + + private void NotificationsPage_LayoutUpdated(int status) + { + bool hasNotification = Convert.ToBoolean(status); + + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + HasNotifications.Visibility = hasNotification ? Visibility.Visible : Visibility.Collapsed; + }); + } + + private void VirtualManager_ControllerSelected(HIDmode HIDmode) + { + Application.Current.Dispatcher.BeginInvoke(() => + { + overlayModel.UpdateHIDMode(HIDmode); + }); + CurrentDevice.SetKeyPressDelay(HIDmode); + } + + public void UpdateSettings(Dictionary<string, string> args) + { + foreach (var pair in args) + { + var name = pair.Key; + var property = pair.Value; + + switch (name) + { + case "DSUEnabled": + break; + case "DSUip": + break; + case "DSUport": + break; + } + } + } + + // no code from the cases inside this function will be called on program start + private async void OnSystemStatusChanged(PowerManager.SystemStatus status, PowerManager.SystemStatus prevStatus) + { + if (status == prevStatus) + return; + + switch (status) + { + case PowerManager.SystemStatus.SystemReady: + { + // resume from sleep + if (prevStatus == PowerManager.SystemStatus.SystemPending) + { + // use device-specific delay + await Task.Delay(CurrentDevice.ResumeDelay); + + // restore inputs manager + InputsManager.Start(); + + // start timer manager + TimerManager.Start(); + + // resume the virtual controller last + VirtualManager.Resume(); + + // restart IMU + SensorsManager.Resume(true); + } + + // open device, when ready + new Thread(() => + { + // wait for all HIDs to be ready + while (!CurrentDevice.IsReady()) + Thread.Sleep(500); + + // open current device (threaded to avoid device to hang) + CurrentDevice.Open(); + }).Start(); + } + break; + + case PowerManager.SystemStatus.SystemPending: + // sleep + { + // stop the virtual controller +<<<<<<< HEAD + VirtualManager.Suspend(); +======= + VirtualManager.Pause(); +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + + // stop timer manager + TimerManager.Stop(); + + // stop sensors + SensorsManager.Stop(); + + // pause inputs manager + InputsManager.Stop(); + + // close current device + CurrentDevice.Close(); + } + break; + } + } + + #region UI + + private void navView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args) + { + if (args.InvokedItemContainer is not null) + { + var navItem = (NavigationViewItem)args.InvokedItemContainer; + var navItemTag = (string)navItem.Tag; + + switch (navItemTag) + { + default: + preNavItemTag = navItemTag; + break; + } + + NavView_Navigate(preNavItemTag); + } + } + + public void NavView_Navigate(string navItemTag) + { + var item = _pages.FirstOrDefault(p => p.Key.Equals(navItemTag)); + var _page = item.Value; + + // Get the page type before navigation so you can prevent duplicate + // entries in the backstack. + var preNavPageType = ContentFrame.CurrentSourcePageType; + + // Only navigate if the selected page isn't currently loaded. + if (!(_page is null) && !Equals(preNavPageType, _page)) NavView_Navigate(_page); + } + + public static void NavView_Navigate(Page _page) + { + CurrentWindow.ContentFrame.Navigate(_page); + CurrentWindow.scrollViewer.ScrollToTop(); + } + + private void navView_BackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args) + { + TryGoBack(); + } + + private void Window_Closed(object sender, EventArgs e) + { + CurrentDevice.Close(); + + notifyIcon.Visible = false; + notifyIcon.Dispose(); + + overlayModel.Close(); + overlayTrackpad.Close(); + overlayquickTools.Close(true); + + // TODO: Make static + taskManager.Stop(); + performanceManager.Stop(); + + VirtualManager.Stop(); + SystemManager.Stop(); + MotionManager.Stop(); + SensorsManager.Stop(); + + ControllerManager.Stop(); + InputsManager.Stop(); + DeviceManager.Stop(); + PlatformManager.Stop(); + OSDManager.Stop(); + PowerProfileManager.Stop(); + ProfileManager.Stop(); + LayoutManager.Stop(); + PowerManager.Stop(); + ProcessManager.Stop(); + ToastManager.Stop(); + + // closing page(s) + controllerPage.Page_Closed(); + profilesPage.Page_Closed(); + settingsPage.Page_Closed(); + overlayPage.Page_Closed(); + hotkeysPage.Page_Closed(); + layoutPage.Page_Closed(); + notificationsPage.Page_Closed(); + + // force kill application + Environment.Exit(0); + } + +<<<<<<< HEAD +======= + bool CloseOverride = false; + +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + private async void Window_Closing(object sender, CancelEventArgs e) + { + // position and size settings + switch (WindowState) + { + case WindowState.Normal: + SettingsManager.SetProperty("MainWindowLeft", Left); + SettingsManager.SetProperty("MainWindowTop", Top); + SettingsManager.SetProperty("MainWindowWidth", ActualWidth); + SettingsManager.SetProperty("MainWindowHeight", ActualHeight); + break; + case WindowState.Maximized: + SettingsManager.SetProperty("MainWindowLeft", 0); + SettingsManager.SetProperty("MainWindowTop", 0); + SettingsManager.SetProperty("MainWindowWidth", SystemParameters.MaximizedPrimaryScreenWidth); + SettingsManager.SetProperty("MainWindowHeight", SystemParameters.MaximizedPrimaryScreenHeight); + + break; + } + + SettingsManager.SetProperty("MainWindowState", (int)WindowState); + SettingsManager.SetProperty("MainWindowPrevState", (int)prevWindowState); + + SettingsManager.SetProperty("MainWindowIsPaneOpen", navView.IsPaneOpen); + + if (SettingsManager.GetBoolean("CloseMinimises") && !appClosing) + { + e.Cancel = true; + WindowState = WindowState.Minimized; + return; + } +<<<<<<< HEAD +======= + + if (SettingsManager.GetBoolean("VirtualControllerForceOrder") && !CloseOverride) + { + // we have to cancel closing the window to be able to prompt the user + e.Cancel = true; + + // warn user when attempting to close HC while using Improve virtual controller detection + var result = Dialog.ShowAsync( + Properties.Resources.MainWindow_VirtualControllerForceOrderCloseTitle, + Properties.Resources.MainWindow_VirtualControllerForceOrderCloseText, + ContentDialogButton.Primary, null, + Properties.Resources.MainWindow_VirtualControllerForceOrderClosePrimary, + Properties.Resources.MainWindow_VirtualControllerForceOrderCloseSecondary); + + await result; + + switch (result.Result) + { + case ContentDialogResult.Primary: + CloseOverride = true; + Close(); + break; + case ContentDialogResult.Secondary: + appClosing = false; + return; + } + } +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + } + + private void Window_StateChanged(object sender, EventArgs e) + { + switch (WindowState) + { + case WindowState.Minimized: + notifyIcon.Visible = true; + ShowInTaskbar = false; + + if (!NotifyInTaskbar) + { + ToastManager.SendToast(Title, "is running in the background"); + NotifyInTaskbar = true; + } + + break; + case WindowState.Normal: + case WindowState.Maximized: + notifyIcon.Visible = false; + ShowInTaskbar = true; + + Activate(); + Topmost = true; // important + Topmost = false; // important + Focus(); + + prevWindowState = WindowState; + break; + } + } + + private void navView_Loaded(object sender, RoutedEventArgs e) + { + // Add handler for ContentFrame navigation. + ContentFrame.Navigated += On_Navigated; + + // NavView doesn't load any page by default, so load home page. + navView.SelectedItem = navView.MenuItems[0]; + + // If navigation occurs on SelectionChanged, this isn't needed. + // Because we use ItemInvoked to navigate, we need to call Navigate + // here to load the home page. + preNavItemTag = "ControllerPage"; + NavView_Navigate(preNavItemTag); + } + + private void GamepadWindow_PreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + if (!e.NewFocus.GetType().IsSubclassOf(typeof(Control))) + return; + + GamepadFocusManagerOnFocused((Control)e.NewFocus); + } + + private void GamepadWindow_PreviewLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) + { + // do something + } + + private bool TryGoBack() + { + if (!ContentFrame.CanGoBack) + return false; + + // Don't go back if the nav pane is overlayed. + if (navView.IsPaneOpen && + (navView.DisplayMode == NavigationViewDisplayMode.Compact || + navView.DisplayMode == NavigationViewDisplayMode.Minimal)) + return false; + + ContentFrame.GoBack(); + return true; + } + + private void On_Navigated(object sender, NavigationEventArgs e) + { + navView.IsBackEnabled = ContentFrame.CanGoBack; + + if (ContentFrame.SourcePageType is not null) + { + CurrentPageName = ContentFrame.CurrentSourcePageType.Name; + + var NavViewItem = navView.MenuItems + .OfType<NavigationViewItem>() + .Where(n => n.Tag.Equals(CurrentPageName)).FirstOrDefault(); + + if (!(NavViewItem is null)) + navView.SelectedItem = NavViewItem; + + navView.Header = new TextBlock() { Text = (string)((Page)e.Content).Title }; + } + } + + #endregion +} diff --git a/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml b/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml index 6696782a8..27feab2e7 100644 --- a/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml +++ b/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml @@ -1,82 +1,98 @@ -<common:GamepadWindow - x:Class="HandheldCompanion.Views.Windows.OverlayQuickTools" - xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" - xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:common="clr-namespace:HandheldCompanion.Views.Classes" - xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" - Title="QuickTools" - Width="640" - Height="720" - MinWidth="640" - MinHeight="450" - MaxWidth="640" - Margin="12" - ui:ThemeManager.IsThemeAware="True" - ui:TitleBar.ExtendViewIntoTitleBar="True" - ui:WindowHelper.SystemBackdropType="Mica" - ui:WindowHelper.UseAcrylicBackdrop="True" - ui:WindowHelper.UseAeroBackdrop="True" - ui:WindowHelper.UseModernWindowStyle="True" - Closing="Window_Closing" - Loaded="Window_Loaded" - ResizeMode="NoResize" - ShowActivated="False" - ShowInTaskbar="False" - Topmost="False" - WindowStyle="ToolWindow" - mc:Ignorable="d"> - - <Grid> - <ui:NavigationView - Name="navView" - Margin="0,1,0,0" - BackRequested="navView_BackRequested" - IsPaneToggleButtonVisible="False" - IsSettingsVisible="False" - ItemInvoked="navView_ItemInvoked" - Loaded="navView_Loaded" - PaneDisplayMode="Top" - SelectionFollowsFocus="Enabled"> - - <ui:NavigationView.Header> - <TextBlock x:Name="navHeader" Margin="-34,-30,0,0" /> - </ui:NavigationView.Header> - - <ui:NavigationView.FooterMenuItems> - <ui:NavigationViewItem IsEnabled="False"> - <DockPanel Height="40"> - <ui:FontIcon - Name="BatteryIndicatorIcon" - VerticalAlignment="Center" - FontSize="30" /> - <TextBlock - Name="BatteryIndicatorPercentage" - Margin="2,0,0,0" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" /> - <TextBlock - Name="BatteryIndicatorLifeRemaining" - Margin="4,0,0,0" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" /> - <TextBlock - Name="Time" - Margin="12,0,0,0" - VerticalAlignment="Center" - Style="{StaticResource BodyTextBlockStyle}" /> - </DockPanel> - </ui:NavigationViewItem> - <ui:NavigationViewItem IsEnabled="False" /> - </ui:NavigationView.FooterMenuItems> - - <common:TouchScrollViewer - x:Name="scrollViewer" - Margin="0,-20,0,12" - PanningMode="VerticalOnly"> - <ui:Frame Name="ContentFrame" /> - </common:TouchScrollViewer> - </ui:NavigationView> - </Grid> +<common:GamepadWindow + x:Class="HandheldCompanion.Views.Windows.OverlayQuickTools" + xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:common="clr-namespace:HandheldCompanion.Views.Classes" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ui="http://schemas.inkore.net/lib/ui/wpf/modern" + Title="QuickTools" + Width="640" + Height="720" + MinWidth="640" + MinHeight="450" + MaxWidth="640" + Margin="12" + ui:ThemeManager.IsThemeAware="True" + ui:TitleBar.ExtendViewIntoTitleBar="True" + ui:WindowHelper.SystemBackdropType="Mica" + ui:WindowHelper.UseAcrylicBackdrop="True" + ui:WindowHelper.UseAeroBackdrop="True" + ui:WindowHelper.UseModernWindowStyle="True" + Closing="Window_Closing" + Loaded="Window_Loaded" + ResizeMode="NoResize" + ShowActivated="False" + ShowInTaskbar="False" + Topmost="False" + WindowStyle="ToolWindow" + mc:Ignorable="d"> + + <Grid> + <ui:NavigationView + Name="navView" + Margin="0,1,0,0" + BackRequested="navView_BackRequested" + IsPaneToggleButtonVisible="False" + IsSettingsVisible="False" + ItemInvoked="navView_ItemInvoked" + Loaded="navView_Loaded" + PaneDisplayMode="Top" + SelectionFollowsFocus="Enabled"> + + <ui:NavigationView.Header> + <TextBlock x:Name="navHeader" Margin="-34,-30,0,0" /> + </ui:NavigationView.Header> + + <ui:NavigationView.FooterMenuItems> + <ui:NavigationViewItem IsEnabled="False"> + <DockPanel Height="40"> + <ui:FontIcon + Name="BatteryIndicatorIcon" + VerticalAlignment="Center" + FontSize="30" /> + <TextBlock + Name="BatteryIndicatorPercentage" + Margin="2,0,0,0" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" /> + <TextBlock + Name="BatteryIndicatorLifeRemaining" + Margin="4,0,0,0" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" /> + <TextBlock + Name="Time" + Margin="12,0,0,0" + VerticalAlignment="Center" + Style="{StaticResource BodyTextBlockStyle}" /> + </DockPanel> + </ui:NavigationViewItem> +<<<<<<< HEAD + <ui:NavigationViewItem IsEnabled="False" /> +======= + <ui:NavigationViewItem IsEnabled="False"> + <DockPanel> + <ui:FontIcon + VerticalAlignment="Center" + FontSize="21" + Glyph="{x:Static ui:SegoeIcons.Recent}" /> + <TextBlock + Name="Time" + Margin="4,0,0,0" + VerticalAlignment="Center" + Style="{StaticResource CaptionTextBlockStyle}" /> + </DockPanel> + </ui:NavigationViewItem> +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + </ui:NavigationView.FooterMenuItems> + + <common:TouchScrollViewer + x:Name="scrollViewer" + Margin="0,-20,0,12" + PanningMode="VerticalOnly"> + <ui:Frame Name="ContentFrame" /> + </common:TouchScrollViewer> + </ui:NavigationView> + </Grid> </common:GamepadWindow> \ No newline at end of file diff --git a/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml.cs b/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml.cs index ebcfd52f5..1eb15be9c 100644 --- a/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml.cs +++ b/HandheldCompanion/Views/Windows/OverlayQuickTools.xaml.cs @@ -487,5 +487,11 @@ private void UpdateTime(object? sender, EventArgs e) Time.Text = DateTime.Now.ToString(timeFormat); } + private void UpdateTime(object? sender, EventArgs e) + { + var timeFormat = CultureInfo.InstalledUICulture.DateTimeFormat.ShortTimePattern; + Time.Text = DateTime.Now.ToString(timeFormat); + } + #endregion } \ No newline at end of file diff --git a/HandheldCompanion/Views/Windows/OverlayTrackpad.xaml.cs b/HandheldCompanion/Views/Windows/OverlayTrackpad.xaml.cs index 31d107178..84e47ab01 100644 --- a/HandheldCompanion/Views/Windows/OverlayTrackpad.xaml.cs +++ b/HandheldCompanion/Views/Windows/OverlayTrackpad.xaml.cs @@ -1,240 +1,248 @@ -using HandheldCompanion.Managers; -using HandheldCompanion.Views.Classes; -using System; -using System.ComponentModel; -using System.Windows; -using System.Windows.Forms; -using System.Windows.Input; -using static HandheldCompanion.DS4Touch; -using Application = System.Windows.Application; -using HorizontalAlignment = System.Windows.HorizontalAlignment; - -namespace HandheldCompanion.Views.Windows; - -/// <summary> -/// Interaction logic for Overlay.xaml -/// </summary> -public partial class OverlayTrackpad : OverlayWindow -{ - private readonly double dpiInput; - - private readonly TouchInput leftInput; - private readonly TouchInput rightInput; - - private double TrackpadOpacity = 0.25; - private readonly double TrackpadOpacityTouched = 0.10; // extra opacity when touched - - public OverlayTrackpad() - { - InitializeComponent(); - this._hotkeyId = 2; - - SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; - - // touch vars - dpiInput = GetWindowsScaling(); - leftInput = new TouchInput(); - rightInput = new TouchInput(); - } - - private void SettingsManager_SettingValueChanged(string name, object value) - { - // UI thread (async) - Application.Current.Dispatcher.BeginInvoke(() => - { - switch (name) - { - case "OverlayTrackpadsSize": - { - var size = Convert.ToInt32(value); - LeftTrackpad.Width = size; - RightTrackpad.Width = size; - Height = size; - HorizontalAlignment = HorizontalAlignment.Stretch; - } - break; - case "OverlayTrackpadsAlignment": - { - var trackpadsAlignment = Convert.ToInt32(value); - switch (trackpadsAlignment) - { - case 0: - VerticalAlignment = VerticalAlignment.Top; - break; - case 1: - VerticalAlignment = VerticalAlignment.Center; - break; - case 2: - VerticalAlignment = VerticalAlignment.Bottom; - break; - } - } - break; - case "OverlayTrackpadsOpacity": - { - TrackpadOpacity = Convert.ToDouble(value); - LeftTrackpad.Opacity = TrackpadOpacity; - RightTrackpad.Opacity = TrackpadOpacity; - } - break; - } - }); - } - - private void Window_Closing(object sender, CancelEventArgs e) - { - // do something - } - - public double GetWindowsScaling() - { - return Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth; - } - - private void Trackpad_TouchInput(TouchEventArgs e, CursorAction action, CursorButton button) - { - var args = e.TouchDevice; - TouchPoint point; - int flags; - - switch (button) - { - default: - case CursorButton.TouchLeft: - { - point = args.GetTouchPoint(LeftTrackpad); - flags = leftInput.Flags; - - leftInput.Timestamp = e.Timestamp; - } - break; - case CursorButton.TouchRight: - { - point = args.GetTouchPoint(RightTrackpad); - flags = rightInput.Flags; - - rightInput.Timestamp = e.Timestamp; - } - break; - } - - var normalizedX = point.Position.X / LeftTrackpad.ActualWidth * dpiInput / 2.0d; - var normalizedY = point.Position.Y / LeftTrackpad.ActualWidth * dpiInput; - - normalizedX += button == CursorButton.TouchRight ? 0.5d : 0.0d; - - switch (action) - { - case CursorAction.CursorUp: - DS4Touch.OnMouseUp(normalizedX, normalizedY, button, flags); - break; - case CursorAction.CursorDown: - DS4Touch.OnMouseDown(normalizedX, normalizedY, button, flags); - break; - case CursorAction.CursorMove: - DS4Touch.OnMouseMove(normalizedX, normalizedY, button, flags); - break; - } - } - - private void Trackpad_PreviewTouchMove(object sender, TouchEventArgs e) - { - var name = ((FrameworkElement)sender).Name; - - switch (name) - { - case "LeftTrackpad": - { - Trackpad_TouchInput(e, CursorAction.CursorMove, CursorButton.TouchLeft); - } - break; - case "RightTrackpad": - { - Trackpad_TouchInput(e, CursorAction.CursorMove, CursorButton.TouchRight); - } - break; - } - - e.Handled = true; - } - - private void Trackpad_PreviewTouchDown(object sender, TouchEventArgs e) - { - var name = ((FrameworkElement)sender).Name; - - switch (name) - { - case "LeftTrackpad": - { - var elapsed = e.Timestamp - leftInput.Timestamp; - if (elapsed < SystemInformation.DoubleClickTime) - leftInput.Flags = 30; - - Trackpad_TouchInput(e, CursorAction.CursorDown, CursorButton.TouchLeft); - - LeftTrackpad.Opacity = TrackpadOpacity + TrackpadOpacityTouched; - - // send vibration (todo: make it a setting) - ControllerManager.GetTargetController()?.Rumble(); // (1, 25, 0, 60); - } - break; - case "RightTrackpad": - { - var elapsed = e.Timestamp - rightInput.Timestamp; - if (elapsed < SystemInformation.DoubleClickTime) - rightInput.Flags = 30; - - Trackpad_TouchInput(e, CursorAction.CursorDown, CursorButton.TouchRight); - - RightTrackpad.Opacity = TrackpadOpacity + TrackpadOpacityTouched; - - // send vibration (todo: make it a setting) - ControllerManager.GetTargetController()?.Rumble(); // (1, 25, 0, 60); - } - break; - } - - e.Handled = true; - } - - private void Trackpad_PreviewTouchUp(object sender, TouchEventArgs e) - { - var name = ((FrameworkElement)sender).Name; - - switch (name) - { - case "LeftTrackpad": - { - leftInput.Flags = 0; - Trackpad_TouchInput(e, CursorAction.CursorUp, CursorButton.TouchLeft); - LeftTrackpad.Opacity = TrackpadOpacity - TrackpadOpacityTouched; - } - break; - case "RightTrackpad": - { - rightInput.Flags = 0; - Trackpad_TouchInput(e, CursorAction.CursorUp, CursorButton.TouchRight); - RightTrackpad.Opacity = TrackpadOpacity - TrackpadOpacityTouched; - } - break; - } - - e.Handled = true; - } - - private class TouchInput - { - public short Flags; - public int Timestamp; - } - - private void LeftTrackpadClick_PreviewTouchDown(object sender, TouchEventArgs e) - { - DS4Touch.OutputClickButton = true; - } - - private void RightTrackpadClick_PreviewTouchDown(object sender, TouchEventArgs e) - { - DS4Touch.OutputClickButton = true; - } +using HandheldCompanion.Managers; +using HandheldCompanion.Views.Classes; +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Forms; +using System.Windows.Input; +using static HandheldCompanion.DS4Touch; +using Application = System.Windows.Application; +using HorizontalAlignment = System.Windows.HorizontalAlignment; + +namespace HandheldCompanion.Views.Windows; + +/// <summary> +/// Interaction logic for Overlay.xaml +/// </summary> +public partial class OverlayTrackpad : OverlayWindow +{ + private readonly double dpiInput; + + private readonly TouchInput leftInput; + private readonly TouchInput rightInput; + + private double TrackpadOpacity = 0.25; + private readonly double TrackpadOpacityTouched = 0.10; // extra opacity when touched + + public OverlayTrackpad() + { + InitializeComponent(); + this._hotkeyId = 2; + + SettingsManager.SettingValueChanged += SettingsManager_SettingValueChanged; + + // touch vars + dpiInput = GetWindowsScaling(); + leftInput = new TouchInput(); + rightInput = new TouchInput(); + } + + private void SettingsManager_SettingValueChanged(string name, object value) + { + // UI thread (async) + Application.Current.Dispatcher.BeginInvoke(() => + { + switch (name) + { + case "OverlayTrackpadsSize": + { + var size = Convert.ToInt32(value); + LeftTrackpad.Width = size; + RightTrackpad.Width = size; + Height = size; + HorizontalAlignment = HorizontalAlignment.Stretch; + } + break; + case "OverlayTrackpadsAlignment": + { + var trackpadsAlignment = Convert.ToInt32(value); + switch (trackpadsAlignment) + { + case 0: + VerticalAlignment = VerticalAlignment.Top; + break; + case 1: + VerticalAlignment = VerticalAlignment.Center; + break; + case 2: + VerticalAlignment = VerticalAlignment.Bottom; + break; + } + } + break; + case "OverlayTrackpadsOpacity": + { + TrackpadOpacity = Convert.ToDouble(value); + LeftTrackpad.Opacity = TrackpadOpacity; + RightTrackpad.Opacity = TrackpadOpacity; + } + break; + } + }); + } + + private void Window_Closing(object sender, CancelEventArgs e) + { + // do something + } + + public double GetWindowsScaling() + { + return Screen.PrimaryScreen.Bounds.Width / SystemParameters.PrimaryScreenWidth; + } + + private void Trackpad_TouchInput(TouchEventArgs e, CursorAction action, CursorButton button) + { + var args = e.TouchDevice; + TouchPoint point; + int flags; + + switch (button) + { + default: + case CursorButton.TouchLeft: + { + point = args.GetTouchPoint(LeftTrackpad); + flags = leftInput.Flags; + + leftInput.Timestamp = e.Timestamp; + } + break; + case CursorButton.TouchRight: + { + point = args.GetTouchPoint(RightTrackpad); + flags = rightInput.Flags; + + rightInput.Timestamp = e.Timestamp; + } + break; + } + + var normalizedX = point.Position.X / LeftTrackpad.ActualWidth * dpiInput / 2.0d; + var normalizedY = point.Position.Y / LeftTrackpad.ActualWidth * dpiInput; + + normalizedX += button == CursorButton.TouchRight ? 0.5d : 0.0d; + + switch (action) + { + case CursorAction.CursorUp: + DS4Touch.OnMouseUp(normalizedX, normalizedY, button, flags); + break; + case CursorAction.CursorDown: + DS4Touch.OnMouseDown(normalizedX, normalizedY, button, flags); + break; + case CursorAction.CursorMove: + DS4Touch.OnMouseMove(normalizedX, normalizedY, button, flags); + break; + } + } + + private void Trackpad_PreviewTouchMove(object sender, TouchEventArgs e) + { + var name = ((FrameworkElement)sender).Name; + + switch (name) + { + case "LeftTrackpad": + { + Trackpad_TouchInput(e, CursorAction.CursorMove, CursorButton.TouchLeft); + } + break; + case "RightTrackpad": + { + Trackpad_TouchInput(e, CursorAction.CursorMove, CursorButton.TouchRight); + } + break; + } + + e.Handled = true; + } + + private void Trackpad_PreviewTouchDown(object sender, TouchEventArgs e) + { + var name = ((FrameworkElement)sender).Name; + + switch (name) + { + case "LeftTrackpad": + { + var elapsed = e.Timestamp - leftInput.Timestamp; +<<<<<<< HEAD + if (elapsed < SystemInformation.DoubleClickTime) +======= + if (elapsed < 200) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + leftInput.Flags = 30; + + Trackpad_TouchInput(e, CursorAction.CursorDown, CursorButton.TouchLeft); + + LeftTrackpad.Opacity = TrackpadOpacity + TrackpadOpacityTouched; + + // send vibration (todo: make it a setting) + ControllerManager.GetTargetController()?.Rumble(); // (1, 25, 0, 60); + } + break; + case "RightTrackpad": + { + var elapsed = e.Timestamp - rightInput.Timestamp; +<<<<<<< HEAD + if (elapsed < SystemInformation.DoubleClickTime) +======= + if (elapsed < 200) +>>>>>>> f8fea3c25fb5fd254f5020d43305b7356ec9770d + rightInput.Flags = 30; + + Trackpad_TouchInput(e, CursorAction.CursorDown, CursorButton.TouchRight); + + RightTrackpad.Opacity = TrackpadOpacity + TrackpadOpacityTouched; + + // send vibration (todo: make it a setting) + ControllerManager.GetTargetController()?.Rumble(); // (1, 25, 0, 60); + } + break; + } + + e.Handled = true; + } + + private void Trackpad_PreviewTouchUp(object sender, TouchEventArgs e) + { + var name = ((FrameworkElement)sender).Name; + + switch (name) + { + case "LeftTrackpad": + { + leftInput.Flags = 0; + Trackpad_TouchInput(e, CursorAction.CursorUp, CursorButton.TouchLeft); + LeftTrackpad.Opacity = TrackpadOpacity - TrackpadOpacityTouched; + } + break; + case "RightTrackpad": + { + rightInput.Flags = 0; + Trackpad_TouchInput(e, CursorAction.CursorUp, CursorButton.TouchRight); + RightTrackpad.Opacity = TrackpadOpacity - TrackpadOpacityTouched; + } + break; + } + + e.Handled = true; + } + + private class TouchInput + { + public short Flags; + public int Timestamp; + } + + private void LeftTrackpadClick_PreviewTouchDown(object sender, TouchEventArgs e) + { + DS4Touch.OutputClickButton = true; + } + + private void RightTrackpadClick_PreviewTouchDown(object sender, TouchEventArgs e) + { + DS4Touch.OutputClickButton = true; + } } \ No newline at end of file diff --git a/README.md b/README.md index dd18db469..63f25e088 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,12 @@ The software is built for Windows 10/Windows 11 (x86 and amd64). - AYA Neo Next and its different versions - AYA Neo Air and it's different versions - AYA Neo 2, Geek, 2S Geek 1S +- Ayn Loki (all models) - ONEXPLAYER MINI and its different versions (Intel, AMD, Gundam) - GPD WIN Max 2 (Intel and AMD) - GPD Win 2, 3 and 4 - Steam Deck +- Lenovo Legion Go (work in progress, may not work as expected) ## Supported Sensors - Bosch BMI160 (and similar) diff --git a/redist/windowsdesktop-runtime-7.0.10-win-x64.exe b/redist/windowsdesktop-runtime-7.0.10-win-x64.exe new file mode 100644 index 000000000..83aa3bf0f Binary files /dev/null and b/redist/windowsdesktop-runtime-7.0.10-win-x64.exe differ