Compare commits

..

21 Commits

Author SHA1 Message Date
TSRBerry
3a4eeb77fe headless: Change window icon size to 48x48 (#4247) 2023-01-09 18:02:41 +00:00
TSRBerry
51b3953cfc [Headless] Add missing arguments & Fix typos (#4193)
* headless: Fix typos in command line options

* Remove nullable from command line options

Add EnableMacroHLE option
Add HideCursorOnIdle option

* headless: Adjust enable-ptc help text

* headless: Use switch statement instead of if-else chain

* headless: Improve formatting for long constructors

* headless: Remove discards from SDL_ShowCursor()

* headless: Add window icon

* Fix hiding cursor on idle

At least on Wayland, SDL2 doesn't produce any mouse motion events.

* Add new command line args: BaseDataDir and UserProfile

* headless: Read icon from embedded resource

* headless: Skip SetWindowIcon() on Windows if dll isn't present

* headless: Fix division by zero

* headless: Fix command line options not working correctly

* headless: Fix crash when viewing command line options

* headless: Load window icon bmp from memory

* Add comment to the workaround for SDL_LoadBMP_RW

* headless: Enable logging to file by default

* headless: Add 3 options for --hide-cursor

Replaces --disable-hide-cursor-on-idle
2023-01-09 04:55:37 +01:00
Ac_K
610eecc1c1 ava: Fixes regressions from refactoring (#4237)
* ava: Fix regressions from #4178

* Remove duplicated code

* real fix for right click menu

Co-Authored-By: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com>

* Remove ContentDialogOverlay

Co-authored-by: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com>
2023-01-09 03:37:20 +00:00
merry
492056abf6 Ava: Make Avalonia use our logging system (#4231)
* Ava: Make Avalonia use our logging system

* LoggerAdapter: Address review comments

* Update Ryujinx.Common/Logging/LogClass.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-09 04:32:20 +01:00
Isaac Marovitz
ee6e682ab4 Fix selection bar (#4236) 2023-01-09 03:36:11 +01:00
gdkchan
6f60e102a2 HIPC: Fix reply possibly also receiving one request (#4232) 2023-01-08 15:34:49 -03:00
Isaac Marovitz
eeb2af9953 Ava GUI: MainWindow Refactor (#4178)
* Fix redundancies

* Add back elses

* `MainWindow` Refactor

* Switch commands to `ReflectionBinding`

Not required in Ava 11

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/AppHost.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/Main/MainMenuBarView.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/Views/Main/MainViewControls.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Resolve issues

* Remove Ava 11 Fix

* Update Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Update Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Fix whitespace + other suggestions

* Move Vsync colours to `Styles.xaml`

* Remove catch all

* Use `switch` instead of `if`

* Update locale keys

* Use block-scoped namespaces

* Fix improper Ava api usage then

* Static PTC

* Fix `GridItemSelectorSize` with `ShowNames`

* Update for new About Window

* Add back search fix

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-01-08 18:46:25 +01:00
Ac_K
550747eac6 Horizon: Impl Prepo, Fixes bugs, Clean things (#4220)
* Horizon: Impl Prepo, Fixes bugs, Clean things

* remove ToArray()

* resultCode > status

* Remove old services

* Addresses gdkchan's comments and more cleanup

* Addresses Gdkchan's feedback 2

* Reorganize services, make sure service are loaded before guest

Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com>

* Create interfaces for lm and sm

Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com>
2023-01-08 12:13:39 +00:00
merry
3ffceab1fb MainWindow: Vertically center SearchBox TextPresenter (#4223) 2023-01-07 16:01:53 +00:00
Mary-nyan
b9f2a96595 ava: Fix regression caused by #4013 (#4222)
Avalonia seems to not like when the artifact doesns't match the root namespace...

Address that by moving the binary to "Ryujinx" like we do on macOS build.
2023-01-07 12:24:21 +01:00
RMED24
cbaa845f5d Include a start.sh file with correct launch options (#4013)
* Include reference to start.sh to be bundled

* Add start.sh

* Fix silly mistake I made on windows-x64

* ... I cannot read properly

* Make same changes for avalonia csproj

* Remove notice from start.sh

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx/Ryujinx.csproj

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Ava/Ryujinx.Ava.csproj

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update distribution/linux/start.sh

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update distribution/linux/start.sh

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/Ryujinx.Ava.csproj

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.csproj

* Update Ryujinx.Ava.csproj

* Rename start.sh to Ryujinx.sh

* Update Ryujinx.csproj

* Update Ryujinx.Ava.csproj

* Update Ryujinx.Ava.csproj

* Update Ryujinx.Ava/Ryujinx.Ava.csproj

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Add `GDK_BACKEND` variable

* Update Ryujinx.Ava.csproj

* Update Program.cs

* Update Program.cs

* Update Ryujinx.sh

* Update Program.cs

* linux: Register mime types on launch

* Add DOTNET_EnableAlternateStackCheck=1 to desktop file

* linux: Add exclusion for RegisterMimeTypes for flathub builds

* Update logo path

* Cleanup Ryujinx.sh

* Fix typo in ReleaseInformation

* gha: Fix permissions for linux release binaries

* ava: Rename output assembly to Ryujinx

* Update mime database after installing new types

Wait until logging is available before registering mime types

* Copy mime types to output directory

Co-authored-by: Mary-nyan <thog@protonmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-01-07 09:06:13 +01:00
Isaac Marovitz
9e2681f2d7 Ava GUI: AboutWindow Refactor (#4196)
* Start `AboutWindow` refactor

* Redesign

* Update logos + ViewModel

* Fix GTK

* Cleanup usings

* Fix mismatched font size

* Update LocaleKeys

* Block scoped namespace

* Fix appearence on German

* Update Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Move version number up

* Move see all contributors button left

* Add a couple `\n`

* Tooltips

* Layout fix

* Update Ryujinx.Ava/UI/ViewModels/AboutWindowViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-07 01:29:18 +01:00
TSRBerry
81fae0d1a6 [hipc] Fix 'Unexpected result code Success returned' in Reply() (#4215)
* horizon: Add AbortOnFailureUnless()

* hipc: Replace AbortUnless() with AbortOnFailureUnless() in Reply()
2023-01-07 00:57:21 +01:00
Isaac Marovitz
38519f3b9a Ava GUI: SettingsWindow Refactor (#4177)
* Fix redundancies

* Add back elses

* Settings Refactor

* Fix Disposal functions

* Use `ReflectionBinding` instead of redundant funcs

* Ac_K suggestions

* Update Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update locale keys

* Update Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Ava/UI/Windows/SettingsWindow.axaml.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Use block-scoped namespaces

* Fix typo

* Make `TimeZone` internal again

Co-authored-by: Ac_K <Acoustik666@gmail.com>
Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2023-01-07 00:35:21 +01:00
Mary-nyan
7f27aabbd0 chore: Update Ryujinx.SDL2-CS to 2.26.1 (#4199) 2023-01-06 17:52:25 +01:00
Ac_K
e876c43ce9 Misc; Remove duplicated entries and clean locale csproj (#4209) 2023-01-05 04:30:55 +01:00
Mary
8639245533 hle: Add safety measure around overflow in ScheduleFutureInvocation
Fix crash on Linux since 08831eecf7.
2023-01-05 01:55:27 +01:00
Ac_K
d6b86a6629 Readd Ryujinx.Ui.LocaleGenerator removed in #4188 (again) 2023-01-05 00:23:17 +01:00
Ac_K
8f2b7b5b8e Readd Ryujinx.Ui.LocaleGenerator removed in #4188 2023-01-05 00:20:47 +01:00
gdkchan
fc4b7cba2c Make PPTC state non-static (#4157)
* Make PPTC state non-static

* DiskCacheLoadState can be null
2023-01-05 00:01:44 +01:00
gdkchan
08831eecf7 IPC refactor part 3+4: New server HIPC message processor (#4188)
* IPC refactor part 3 + 4: New server HIPC message processor with source generator based serialization

* Make types match on calls to AlignUp/AlignDown

* Formatting

* Address some PR feedback

* Move BitfieldExtensions to Ryujinx.Common.Utilities and consolidate implementations

* Rename Reader/Writer to SpanReader/SpanWriter and move to Ryujinx.Common.Memory

* Implement EventType

* Address more PR feedback

* Log request processing errors since they are not normal

* Rename waitable to multiwait and add missing lock

* PR feedback

* Ac_K PR feedback
2023-01-04 23:15:45 +01:00
339 changed files with 15503 additions and 4996 deletions

View File

@@ -69,6 +69,12 @@ jobs:
- name: Publish Ryujinx.Ava - name: Publish Ryujinx.Ava
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER Ryujinx.Ava --self-contained true
if: github.event_name == 'pull_request' if: github.event_name == 'pull_request'
- name: Rename Avalonia (Windows)
run: mv ./publish_ava/Ryujinx.Ava.exe ./publish_ava/Ryujinx.exe
if: runner.os == 'Windows' && github.event_name == 'pull_request'
- name: Rename Avalonia (Unix)
run: mv ./publish_ava/Ryujinx.Ava ./publish_ava/Ryujinx
if: runner.os != 'Windows' && github.event_name == 'pull_request'
- name: Upload Ryujinx artifact - name: Upload Ryujinx artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:

View File

@@ -38,19 +38,21 @@ jobs:
shell: bash shell: bash
- name: Configure for release - name: Configure for release
run: | run: |
sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformations.cs sed -r --in-place 's/\%\%RYUJINX_BUILD_VERSION\%\%/${{ steps.version_info.outputs.build_version }}/g;' Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformations.cs sed -r --in-place 's/\%\%RYUJINX_BUILD_GIT_HASH\%\%/${{ steps.version_info.outputs.git_short_hash }}/g;' Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformations.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_NAME\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_NAME }}/g;' Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformations.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_OWNER\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_OWNER }}/g;' Ryujinx.Common/ReleaseInformation.cs
sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformations.cs sed -r --in-place 's/\%\%RYUJINX_TARGET_RELEASE_CHANNEL_REPO\%\%/${{ env.RYUJINX_TARGET_RELEASE_CHANNEL_REPO }}/g;' Ryujinx.Common/ReleaseInformation.cs
shell: bash shell: bash
- name: Create output dir - name: Create output dir
run: "mkdir release_output" run: "mkdir release_output"
- name: Publish Windows - name: Publish Windows
run: | run: |
dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true dotnet publish -c Release -r win10-x64 -o ./publish_windows/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true dotnet publish -c Release -r win10-x64 -o ./publish_windows_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true
dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true dotnet publish -c Release -r win10-x64 -o ./publish_windows_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true
mv ./publish_windows_ava/publish/Ryujinx.Ava.exe ./publish_windows_ava/publish/Ryujinx.exe
- name: Packing Windows builds - name: Packing Windows builds
run: | run: |
pushd publish_windows pushd publish_windows
@@ -71,19 +73,28 @@ jobs:
dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true dotnet publish -c Release -r linux-x64 -o ./publish_linux/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx --self-contained true
dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true dotnet publish -c Release -r linux-x64 -o ./publish_linux_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Headless.SDL2 --self-contained true
dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true dotnet publish -c Release -r linux-x64 -o ./publish_linux_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded Ryujinx.Ava --self-contained true
mv ./publish_linux_ava/publish/Ryujinx.Ava ./publish_linux_ava/publish/Ryujinx
- name: Packing Linux builds - name: Packing Linux builds
run: | run: |
pushd publish_linux pushd publish_linux
tar -czvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish tar --exclude "publish/Ryujinx" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
gzip -9 < ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
rm ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
popd popd
pushd publish_linux_sdl2_headless pushd publish_linux_sdl2_headless
tar -czvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish tar --exclude "publish/Ryujinx.Headless.SDL2" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Headless.SDL2" "publish/Ryujinx.Headless.SDL2"
gzip -9 < ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
rm ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
popd popd
pushd publish_linux_ava pushd publish_linux_ava
tar -czvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz publish tar --exclude "publish/Ryujinx" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
gzip -9 < ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar > ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar.gz
rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
popd popd
shell: bash shell: bash

View File

@@ -6,7 +6,6 @@ using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory; using ARMeilleure.Memory;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation.PTC;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
@@ -44,14 +43,13 @@ namespace ARMeilleure.Translation
public IMemoryManager Memory { get; } public IMemoryManager Memory { get; }
public bool HasPtc { get; }
public EntryTable<uint> CountTable { get; } public EntryTable<uint> CountTable { get; }
public AddressTable<ulong> FunctionTable { get; } public AddressTable<ulong> FunctionTable { get; }
public TranslatorStubs Stubs { get; } public TranslatorStubs Stubs { get; }
public ulong EntryAddress { get; } public ulong EntryAddress { get; }
public bool HighCq { get; } public bool HighCq { get; }
public bool HasPtc { get; }
public Aarch32Mode Mode { get; } public Aarch32Mode Mode { get; }
private int _ifThenBlockStateIndex = 0; private int _ifThenBlockStateIndex = 0;
@@ -66,15 +64,16 @@ namespace ARMeilleure.Translation
TranslatorStubs stubs, TranslatorStubs stubs,
ulong entryAddress, ulong entryAddress,
bool highCq, bool highCq,
bool hasPtc,
Aarch32Mode mode) Aarch32Mode mode)
{ {
HasPtc = Ptc.State != PtcState.Disabled;
Memory = memory; Memory = memory;
CountTable = countTable; CountTable = countTable;
FunctionTable = funcTable; FunctionTable = funcTable;
Stubs = stubs; Stubs = stubs;
EntryAddress = entryAddress; EntryAddress = entryAddress;
HighCq = highCq; HighCq = highCq;
HasPtc = hasPtc;
Mode = mode; Mode = mode;
_labels = new Dictionary<ulong, Operand>(); _labels = new Dictionary<ulong, Operand>();

View File

@@ -0,0 +1,10 @@
using System;
namespace ARMeilleure.Translation.PTC
{
public interface IPtcLoadState
{
event Action<PtcLoadingState, int, int> PtcStateChanged;
void Continue();
}
}

View File

@@ -22,7 +22,7 @@ using static ARMeilleure.Translation.PTC.PtcFormatter;
namespace ARMeilleure.Translation.PTC namespace ARMeilleure.Translation.PTC
{ {
public static class Ptc class Ptc : IPtcLoadState
{ {
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
@@ -35,45 +35,49 @@ namespace ARMeilleure.Translation.PTC
private const string TitleIdTextDefault = "0000000000000000"; private const string TitleIdTextDefault = "0000000000000000";
private const string DisplayVersionDefault = "0"; private const string DisplayVersionDefault = "0";
internal static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1); public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
internal static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2); public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
internal static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3); public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
private const byte FillingByte = 0x00; private const byte FillingByte = 0x00;
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
public PtcProfiler Profiler { get; }
// Carriers. // Carriers.
private static MemoryStream _infosStream; private MemoryStream _infosStream;
private static List<byte[]> _codesList; private List<byte[]> _codesList;
private static MemoryStream _relocsStream; private MemoryStream _relocsStream;
private static MemoryStream _unwindInfosStream; private MemoryStream _unwindInfosStream;
private static readonly ulong _outerHeaderMagic; private readonly ulong _outerHeaderMagic;
private static readonly ulong _innerHeaderMagic; private readonly ulong _innerHeaderMagic;
private static readonly ManualResetEvent _waitEvent; private readonly ManualResetEvent _waitEvent;
private static readonly object _lock; private readonly object _lock;
private static bool _disposed; private bool _disposed;
internal static string TitleIdText { get; private set; } public string TitleIdText { get; private set; }
internal static string DisplayVersion { get; private set; } public string DisplayVersion { get; private set; }
private static MemoryManagerMode _memoryMode; private MemoryManagerType _memoryMode;
internal static string CachePathActual { get; private set; } public string CachePathActual { get; private set; }
internal static string CachePathBackup { get; private set; } public string CachePathBackup { get; private set; }
internal static PtcState State { get; private set; } public PtcState State { get; private set; }
// Progress reporting helpers. // Progress reporting helpers.
private static volatile int _translateCount; private volatile int _translateCount;
private static volatile int _translateTotalCount; private volatile int _translateTotalCount;
public static event Action<PtcLoadingState, int, int> PtcStateChanged; public event Action<PtcLoadingState, int, int> PtcStateChanged;
static Ptc() public Ptc()
{ {
Profiler = new PtcProfiler(this);
InitializeCarriers(); InitializeCarriers();
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
@@ -94,12 +98,12 @@ namespace ARMeilleure.Translation.PTC
Disable(); Disable();
} }
public static void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerMode memoryMode) public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode)
{ {
Wait(); Wait();
PtcProfiler.Wait(); Profiler.Wait();
PtcProfiler.ClearEntries(); Profiler.ClearEntries();
Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled})."); Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
@@ -137,12 +141,12 @@ namespace ARMeilleure.Translation.PTC
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion); CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
PreLoad(); PreLoad();
PtcProfiler.PreLoad(); Profiler.PreLoad();
Enable(); Enable();
} }
private static void InitializeCarriers() private void InitializeCarriers()
{ {
_infosStream = new MemoryStream(); _infosStream = new MemoryStream();
_codesList = new List<byte[]>(); _codesList = new List<byte[]>();
@@ -150,7 +154,7 @@ namespace ARMeilleure.Translation.PTC
_unwindInfosStream = new MemoryStream(); _unwindInfosStream = new MemoryStream();
} }
private static void DisposeCarriers() private void DisposeCarriers()
{ {
_infosStream.Dispose(); _infosStream.Dispose();
_codesList.Clear(); _codesList.Clear();
@@ -158,12 +162,12 @@ namespace ARMeilleure.Translation.PTC
_unwindInfosStream.Dispose(); _unwindInfosStream.Dispose();
} }
private static bool AreCarriersEmpty() private bool AreCarriersEmpty()
{ {
return _infosStream.Length == 0L && _codesList.Count == 0 && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L; return _infosStream.Length == 0L && _codesList.Count == 0 && _relocsStream.Length == 0L && _unwindInfosStream.Length == 0L;
} }
private static void ResetCarriersIfNeeded() private void ResetCarriersIfNeeded()
{ {
if (AreCarriersEmpty()) if (AreCarriersEmpty())
{ {
@@ -175,7 +179,7 @@ namespace ARMeilleure.Translation.PTC
InitializeCarriers(); InitializeCarriers();
} }
private static void PreLoad() private void PreLoad()
{ {
string fileNameActual = string.Concat(CachePathActual, ".cache"); string fileNameActual = string.Concat(CachePathActual, ".cache");
string fileNameBackup = string.Concat(CachePathBackup, ".cache"); string fileNameBackup = string.Concat(CachePathBackup, ".cache");
@@ -199,7 +203,7 @@ namespace ARMeilleure.Translation.PTC
} }
} }
private static unsafe bool Load(string fileName, bool isBackup) private unsafe bool Load(string fileName, bool isBackup)
{ {
using (FileStream compressedStream = new(fileName, FileMode.Open)) using (FileStream compressedStream = new(fileName, FileMode.Open))
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true)) using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
@@ -376,12 +380,12 @@ namespace ARMeilleure.Translation.PTC
return true; return true;
} }
private static void InvalidateCompressedStream(FileStream compressedStream) private void InvalidateCompressedStream(FileStream compressedStream)
{ {
compressedStream.SetLength(0L); compressedStream.SetLength(0L);
} }
private static void PreSave() private void PreSave()
{ {
_waitEvent.Reset(); _waitEvent.Reset();
@@ -409,7 +413,7 @@ namespace ARMeilleure.Translation.PTC
_waitEvent.Set(); _waitEvent.Set();
} }
private static unsafe void Save(string fileName) private unsafe void Save(string fileName)
{ {
int translatedFuncsCount; int translatedFuncsCount;
@@ -517,7 +521,7 @@ namespace ARMeilleure.Translation.PTC
} }
} }
internal static void LoadTranslations(Translator translator) public void LoadTranslations(Translator translator)
{ {
if (AreCarriersEmpty()) if (AreCarriersEmpty())
{ {
@@ -550,7 +554,7 @@ namespace ARMeilleure.Translation.PTC
bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize); bool isEntryChanged = infoEntry.Hash != ComputeHash(translator.Memory, infoEntry.Address, infoEntry.GuestSize);
if (isEntryChanged || (!infoEntry.HighCq && PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq)) if (isEntryChanged || (!infoEntry.HighCq && Profiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) && value.HighCq))
{ {
infoEntry.Stubbed = true; infoEntry.Stubbed = true;
infoEntry.CodeLength = 0; infoEntry.CodeLength = 0;
@@ -601,38 +605,38 @@ namespace ARMeilleure.Translation.PTC
Logger.Info?.Print(LogClass.Ptc, $"{translator.Functions.Count} translated functions loaded"); Logger.Info?.Print(LogClass.Ptc, $"{translator.Functions.Count} translated functions loaded");
} }
private static int GetEntriesCount() private int GetEntriesCount()
{ {
return _codesList.Count; return _codesList.Count;
} }
[Conditional("DEBUG")] [Conditional("DEBUG")]
private static void SkipCode(int index, int codeLength) private void SkipCode(int index, int codeLength)
{ {
Debug.Assert(_codesList[index].Length == 0); Debug.Assert(_codesList[index].Length == 0);
Debug.Assert(codeLength == 0); Debug.Assert(codeLength == 0);
} }
private static void SkipReloc(int relocEntriesCount) private void SkipReloc(int relocEntriesCount)
{ {
_relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current); _relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current);
} }
private static void SkipUnwindInfo(BinaryReader unwindInfosReader) private void SkipUnwindInfo(BinaryReader unwindInfosReader)
{ {
int pushEntriesLength = unwindInfosReader.ReadInt32(); int pushEntriesLength = unwindInfosReader.ReadInt32();
_unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current); _unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current);
} }
private static byte[] ReadCode(int index, int codeLength) private byte[] ReadCode(int index, int codeLength)
{ {
Debug.Assert(_codesList[index].Length == codeLength); Debug.Assert(_codesList[index].Length == codeLength);
return _codesList[index]; return _codesList[index];
} }
private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount) private RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount)
{ {
RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount]; RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount];
@@ -648,7 +652,7 @@ namespace ARMeilleure.Translation.PTC
return relocEntries; return relocEntries;
} }
private static void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter) private void PatchCode(Translator translator, Span<byte> code, RelocEntry[] relocEntries, out Counter<uint> callCounter)
{ {
callCounter = null; callCounter = null;
@@ -702,7 +706,7 @@ namespace ARMeilleure.Translation.PTC
} }
} }
private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader) private UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader)
{ {
int pushEntriesLength = unwindInfosReader.ReadInt32(); int pushEntriesLength = unwindInfosReader.ReadInt32();
@@ -723,7 +727,7 @@ namespace ARMeilleure.Translation.PTC
return new UnwindInfo(pushEntries, prologueSize); return new UnwindInfo(pushEntries, prologueSize);
} }
private static TranslatedFunction FastTranslate( private TranslatedFunction FastTranslate(
byte[] code, byte[] code,
Counter<uint> callCounter, Counter<uint> callCounter,
ulong guestSize, ulong guestSize,
@@ -736,19 +740,19 @@ namespace ARMeilleure.Translation.PTC
return new TranslatedFunction(gFunc, callCounter, guestSize, highCq); return new TranslatedFunction(gFunc, callCounter, guestSize, highCq);
} }
private static void UpdateInfo(InfoEntry infoEntry) private void UpdateInfo(InfoEntry infoEntry)
{ {
_infosStream.Seek(-Unsafe.SizeOf<InfoEntry>(), SeekOrigin.Current); _infosStream.Seek(-Unsafe.SizeOf<InfoEntry>(), SeekOrigin.Current);
SerializeStructure(_infosStream, infoEntry); SerializeStructure(_infosStream, infoEntry);
} }
private static void StubCode(int index) private void StubCode(int index)
{ {
_codesList[index] = Array.Empty<byte>(); _codesList[index] = Array.Empty<byte>();
} }
private static void StubReloc(int relocEntriesCount) private void StubReloc(int relocEntriesCount)
{ {
for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++) for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++)
{ {
@@ -756,7 +760,7 @@ namespace ARMeilleure.Translation.PTC
} }
} }
private static void StubUnwindInfo(BinaryReader unwindInfosReader) private void StubUnwindInfo(BinaryReader unwindInfosReader)
{ {
int pushEntriesLength = unwindInfosReader.ReadInt32(); int pushEntriesLength = unwindInfosReader.ReadInt32();
@@ -766,9 +770,9 @@ namespace ARMeilleure.Translation.PTC
} }
} }
internal static void MakeAndSaveTranslations(Translator translator) public void MakeAndSaveTranslations(Translator translator)
{ {
var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(translator.Functions); var profiledFuncsToTranslate = Profiler.GetProfiledFuncsToTranslate(translator.Functions);
_translateCount = 0; _translateCount = 0;
_translateTotalCount = profiledFuncsToTranslate.Count; _translateTotalCount = profiledFuncsToTranslate.Count;
@@ -811,7 +815,7 @@ namespace ARMeilleure.Translation.PTC
{ {
ulong address = item.address; ulong address = item.address;
Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address)); Debug.Assert(Profiler.IsAddressInStaticCodeRange(address));
TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq); TranslatedFunction func = translator.Translate(address, item.funcProfile.Mode, item.funcProfile.HighCq);
@@ -861,7 +865,7 @@ namespace ARMeilleure.Translation.PTC
preSaveThread.Start(); preSaveThread.Start();
} }
private static void ReportProgress(object state) private void ReportProgress(object state)
{ {
const int refreshRate = 50; // ms. const int refreshRate = 50; // ms.
@@ -882,12 +886,12 @@ namespace ARMeilleure.Translation.PTC
while (!endEvent.WaitOne(refreshRate)); while (!endEvent.WaitOne(refreshRate));
} }
internal static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize) public static Hash128 ComputeHash(IMemoryManager memory, ulong address, ulong guestSize)
{ {
return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize)))); return XXHash128.ComputeHash(memory.GetSpan(address, checked((int)(guestSize))));
} }
internal static void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc) public void WriteCompiledFunction(ulong address, ulong guestSize, Hash128 hash, bool highCq, CompiledFunction compiledFunc)
{ {
lock (_lock) lock (_lock)
{ {
@@ -936,12 +940,12 @@ namespace ARMeilleure.Translation.PTC
} }
} }
private static void WriteCode(ReadOnlySpan<byte> code) private void WriteCode(ReadOnlySpan<byte> code)
{ {
_codesList.Add(code.ToArray()); _codesList.Add(code.ToArray());
} }
internal static bool GetEndianness() public static bool GetEndianness()
{ {
return BitConverter.IsLittleEndian; return BitConverter.IsLittleEndian;
} }
@@ -955,7 +959,7 @@ namespace ARMeilleure.Translation.PTC
(uint)HardwareCapabilities.FeatureInfo7Ecx); (uint)HardwareCapabilities.FeatureInfo7Ecx);
} }
private static byte GetMemoryManagerMode() private byte GetMemoryManagerMode()
{ {
return (byte)_memoryMode; return (byte)_memoryMode;
} }
@@ -1050,12 +1054,12 @@ namespace ARMeilleure.Translation.PTC
public int RelocEntriesCount; public int RelocEntriesCount;
} }
private static void Enable() private void Enable()
{ {
State = PtcState.Enabled; State = PtcState.Enabled;
} }
public static void Continue() public void Continue()
{ {
if (State == PtcState.Enabled) if (State == PtcState.Enabled)
{ {
@@ -1063,7 +1067,7 @@ namespace ARMeilleure.Translation.PTC
} }
} }
public static void Close() public void Close()
{ {
if (State == PtcState.Enabled || if (State == PtcState.Enabled ||
State == PtcState.Continuing) State == PtcState.Continuing)
@@ -1072,17 +1076,17 @@ namespace ARMeilleure.Translation.PTC
} }
} }
internal static void Disable() public void Disable()
{ {
State = PtcState.Disabled; State = PtcState.Disabled;
} }
private static void Wait() private void Wait()
{ {
_waitEvent.WaitOne(); _waitEvent.WaitOne();
} }
public static void Dispose() public void Dispose()
{ {
if (!_disposed) if (!_disposed)
{ {

View File

@@ -16,7 +16,7 @@ using static ARMeilleure.Translation.PTC.PtcFormatter;
namespace ARMeilleure.Translation.PTC namespace ARMeilleure.Translation.PTC
{ {
public static class PtcProfiler class PtcProfiler
{ {
private const string OuterHeaderMagicString = "Pohd\0\0\0\0"; private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
@@ -26,27 +26,31 @@ namespace ARMeilleure.Translation.PTC
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest; private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
private static readonly System.Timers.Timer _timer; private readonly Ptc _ptc;
private static readonly ulong _outerHeaderMagic; private readonly System.Timers.Timer _timer;
private static readonly ManualResetEvent _waitEvent; private readonly ulong _outerHeaderMagic;
private static readonly object _lock; private readonly ManualResetEvent _waitEvent;
private static bool _disposed; private readonly object _lock;
private static Hash128 _lastHash; private bool _disposed;
internal static Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; } private Hash128 _lastHash;
internal static bool Enabled { get; private set; } public Dictionary<ulong, FuncProfile> ProfiledFuncs { get; private set; }
public static ulong StaticCodeStart { internal get; set; } public bool Enabled { get; private set; }
public static ulong StaticCodeSize { internal get; set; }
static PtcProfiler() public ulong StaticCodeStart { get; set; }
public ulong StaticCodeSize { get; set; }
public PtcProfiler(Ptc ptc)
{ {
_ptc = ptc;
_timer = new System.Timers.Timer((double)SaveInterval * 1000d); _timer = new System.Timers.Timer((double)SaveInterval * 1000d);
_timer.Elapsed += PreSave; _timer.Elapsed += PreSave;
@@ -63,7 +67,7 @@ namespace ARMeilleure.Translation.PTC
Enabled = false; Enabled = false;
} }
internal static void AddEntry(ulong address, ExecutionMode mode, bool highCq) public void AddEntry(ulong address, ExecutionMode mode, bool highCq)
{ {
if (IsAddressInStaticCodeRange(address)) if (IsAddressInStaticCodeRange(address))
{ {
@@ -76,7 +80,7 @@ namespace ARMeilleure.Translation.PTC
} }
} }
internal static void UpdateEntry(ulong address, ExecutionMode mode, bool highCq) public void UpdateEntry(ulong address, ExecutionMode mode, bool highCq)
{ {
if (IsAddressInStaticCodeRange(address)) if (IsAddressInStaticCodeRange(address))
{ {
@@ -91,12 +95,12 @@ namespace ARMeilleure.Translation.PTC
} }
} }
internal static bool IsAddressInStaticCodeRange(ulong address) public bool IsAddressInStaticCodeRange(ulong address)
{ {
return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize; return address >= StaticCodeStart && address < StaticCodeStart + StaticCodeSize;
} }
internal static ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs) public ConcurrentQueue<(ulong address, FuncProfile funcProfile)> GetProfiledFuncsToTranslate(TranslatorCache<TranslatedFunction> funcs)
{ {
var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>(); var profiledFuncsToTranslate = new ConcurrentQueue<(ulong address, FuncProfile funcProfile)>();
@@ -111,18 +115,18 @@ namespace ARMeilleure.Translation.PTC
return profiledFuncsToTranslate; return profiledFuncsToTranslate;
} }
internal static void ClearEntries() public void ClearEntries()
{ {
ProfiledFuncs.Clear(); ProfiledFuncs.Clear();
ProfiledFuncs.TrimExcess(); ProfiledFuncs.TrimExcess();
} }
internal static void PreLoad() public void PreLoad()
{ {
_lastHash = default; _lastHash = default;
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info"); string fileNameActual = string.Concat(_ptc.CachePathActual, ".info");
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info"); string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info");
FileInfo fileInfoActual = new FileInfo(fileNameActual); FileInfo fileInfoActual = new FileInfo(fileNameActual);
FileInfo fileInfoBackup = new FileInfo(fileNameBackup); FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
@@ -143,7 +147,7 @@ namespace ARMeilleure.Translation.PTC
} }
} }
private static bool Load(string fileName, bool isBackup) private bool Load(string fileName, bool isBackup)
{ {
using (FileStream compressedStream = new(fileName, FileMode.Open)) using (FileStream compressedStream = new(fileName, FileMode.Open))
using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true)) using (DeflateStream deflateStream = new(compressedStream, CompressionMode.Decompress, true))
@@ -228,22 +232,22 @@ namespace ARMeilleure.Translation.PTC
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream)); return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream));
} }
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream) private ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
{ {
return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position); return new(memoryStream.GetBuffer(), (int)memoryStream.Position, (int)memoryStream.Length - (int)memoryStream.Position);
} }
private static void InvalidateCompressedStream(FileStream compressedStream) private void InvalidateCompressedStream(FileStream compressedStream)
{ {
compressedStream.SetLength(0L); compressedStream.SetLength(0L);
} }
private static void PreSave(object source, System.Timers.ElapsedEventArgs e) private void PreSave(object source, System.Timers.ElapsedEventArgs e)
{ {
_waitEvent.Reset(); _waitEvent.Reset();
string fileNameActual = string.Concat(Ptc.CachePathActual, ".info"); string fileNameActual = string.Concat(_ptc.CachePathActual, ".info");
string fileNameBackup = string.Concat(Ptc.CachePathBackup, ".info"); string fileNameBackup = string.Concat(_ptc.CachePathBackup, ".info");
FileInfo fileInfoActual = new FileInfo(fileNameActual); FileInfo fileInfoActual = new FileInfo(fileNameActual);
@@ -257,7 +261,7 @@ namespace ARMeilleure.Translation.PTC
_waitEvent.Set(); _waitEvent.Set();
} }
private static void Save(string fileName) private void Save(string fileName)
{ {
int profiledFuncsCount; int profiledFuncsCount;
@@ -329,7 +333,7 @@ namespace ARMeilleure.Translation.PTC
} }
} }
private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs) private void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
{ {
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure)); SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure));
} }
@@ -361,7 +365,7 @@ namespace ARMeilleure.Translation.PTC
} }
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)] [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 5*/)]
internal struct FuncProfile public struct FuncProfile
{ {
public ExecutionMode Mode; public ExecutionMode Mode;
public bool HighCq; public bool HighCq;
@@ -373,10 +377,10 @@ namespace ARMeilleure.Translation.PTC
} }
} }
internal static void Start() public void Start()
{ {
if (Ptc.State == PtcState.Enabled || if (_ptc.State == PtcState.Enabled ||
Ptc.State == PtcState.Continuing) _ptc.State == PtcState.Continuing)
{ {
Enabled = true; Enabled = true;
@@ -384,7 +388,7 @@ namespace ARMeilleure.Translation.PTC
} }
} }
public static void Stop() public void Stop()
{ {
Enabled = false; Enabled = false;
@@ -394,12 +398,12 @@ namespace ARMeilleure.Translation.PTC
} }
} }
internal static void Wait() public void Wait()
{ {
_waitEvent.WaitOne(); _waitEvent.WaitOne();
} }
public static void Dispose() public void Dispose()
{ {
if (!_disposed) if (!_disposed)
{ {

View File

@@ -44,6 +44,8 @@ namespace ARMeilleure.Translation
private readonly IJitMemoryAllocator _allocator; private readonly IJitMemoryAllocator _allocator;
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs; private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
private readonly Ptc _ptc;
internal TranslatorCache<TranslatedFunction> Functions { get; } internal TranslatorCache<TranslatedFunction> Functions { get; }
internal AddressTable<ulong> FunctionTable { get; } internal AddressTable<ulong> FunctionTable { get; }
internal EntryTable<uint> CountTable { get; } internal EntryTable<uint> CountTable { get; }
@@ -63,6 +65,8 @@ namespace ARMeilleure.Translation
_oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>(); _oldFuncs = new ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>>();
_ptc = new Ptc();
Queue = new TranslatorQueue(); Queue = new TranslatorQueue();
JitCache.Initialize(allocator); JitCache.Initialize(allocator);
@@ -80,22 +84,37 @@ namespace ARMeilleure.Translation
} }
} }
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
{
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type);
return _ptc;
}
public void PrepareCodeRange(ulong address, ulong size)
{
if (_ptc.Profiler.StaticCodeSize == 0)
{
_ptc.Profiler.StaticCodeStart = address;
_ptc.Profiler.StaticCodeSize = size;
}
}
public void Execute(State.ExecutionContext context, ulong address) public void Execute(State.ExecutionContext context, ulong address)
{ {
if (Interlocked.Increment(ref _threadCount) == 1) if (Interlocked.Increment(ref _threadCount) == 1)
{ {
IsReadyForTranslation.WaitOne(); IsReadyForTranslation.WaitOne();
if (Ptc.State == PtcState.Enabled) if (_ptc.State == PtcState.Enabled)
{ {
Debug.Assert(Functions.Count == 0); Debug.Assert(Functions.Count == 0);
Ptc.LoadTranslations(this); _ptc.LoadTranslations(this);
Ptc.MakeAndSaveTranslations(this); _ptc.MakeAndSaveTranslations(this);
} }
PtcProfiler.Start(); _ptc.Profiler.Start();
Ptc.Disable(); _ptc.Disable();
// Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht // Simple heuristic, should be user configurable in future. (1 for 4 core/ht or less, 2 for 6 core + ht
// etc). All threads are normal priority except from the last, which just fills as much of the last core // etc). All threads are normal priority except from the last, which just fills as much of the last core
@@ -148,6 +167,12 @@ namespace ARMeilleure.Translation
Stubs.Dispose(); Stubs.Dispose();
FunctionTable.Dispose(); FunctionTable.Dispose();
CountTable.Dispose(); CountTable.Dispose();
_ptc.Close();
_ptc.Profiler.Stop();
_ptc.Dispose();
_ptc.Profiler.Dispose();
} }
} }
@@ -189,9 +214,9 @@ namespace ARMeilleure.Translation
func = oldFunc; func = oldFunc;
} }
if (PtcProfiler.Enabled) if (_ptc.Profiler.Enabled)
{ {
PtcProfiler.AddEntry(address, mode, highCq: false); _ptc.Profiler.AddEntry(address, mode, highCq: false);
} }
RegisterFunction(address, func); RegisterFunction(address, func);
@@ -217,6 +242,7 @@ namespace ARMeilleure.Translation
Stubs, Stubs,
address, address,
highCq, highCq,
_ptc.State != PtcState.Disabled,
mode: Aarch32Mode.User); mode: Aarch32Mode.User);
Logger.StartPass(PassName.Decoding); Logger.StartPass(PassName.Decoding);
@@ -262,7 +288,7 @@ namespace ARMeilleure.Translation
{ {
Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize); Hash128 hash = Ptc.ComputeHash(Memory, address, funcSize);
Ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc); _ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
} }
GuestFunction func = compiledFunc.Map<GuestFunction>(); GuestFunction func = compiledFunc.Map<GuestFunction>();
@@ -284,9 +310,9 @@ namespace ARMeilleure.Translation
return func; return func;
}); });
if (PtcProfiler.Enabled) if (_ptc.Profiler.Enabled)
{ {
PtcProfiler.UpdateEntry(request.Address, request.Mode, highCq: true); _ptc.Profiler.UpdateEntry(request.Address, request.Mode, highCq: true);
} }
RegisterFunction(request.Address, func); RegisterFunction(request.Address, func);

View File

@@ -34,7 +34,7 @@
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" /> <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" /> <PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" /> <PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.24.2-build21" /> <PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.1-build23" />
<PackageVersion Include="shaderc.net" Version="0.1.0" /> <PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.1" /> <PackageVersion Include="SharpZipLib" Version="1.4.1" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" /> <PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
@@ -50,7 +50,5 @@
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" /> <PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" /> <PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" /> <PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,5 +1,6 @@
using ARMeilleure.Translation; using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Threading; using Avalonia.Threading;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
@@ -14,6 +15,7 @@ using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
@@ -43,7 +45,7 @@ using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Image = SixLabors.ImageSharp.Image;
using InputManager = Ryujinx.Input.HLE.InputManager; using InputManager = Ryujinx.Input.HLE.InputManager;
using Key = Ryujinx.Input.Key; using Key = Ryujinx.Input.Key;
using MouseButton = Ryujinx.Input.MouseButton; using MouseButton = Ryujinx.Input.MouseButton;
@@ -68,7 +70,7 @@ namespace Ryujinx.Ava
private readonly AccountManager _accountManager; private readonly AccountManager _accountManager;
private readonly UserChannelPersistence _userChannelPersistence; private readonly UserChannelPersistence _userChannelPersistence;
private readonly InputManager _inputManager; private readonly InputManager _inputManager;
private readonly MainWindow _parent; private readonly MainWindowViewModel _viewModel;
private readonly IKeyboard _keyboardInterface; private readonly IKeyboard _keyboardInterface;
private readonly GraphicsDebugLevel _glLogLevel; private readonly GraphicsDebugLevel _glLogLevel;
@@ -111,6 +113,7 @@ namespace Ryujinx.Ava
public bool ScreenshotRequested { get; set; } public bool ScreenshotRequested { get; set; }
private object _lockObject = new(); private object _lockObject = new();
private TopLevel _topLevel;
public AppHost( public AppHost(
RendererHost renderer, RendererHost renderer,
@@ -120,9 +123,10 @@ namespace Ryujinx.Ava
ContentManager contentManager, ContentManager contentManager,
AccountManager accountManager, AccountManager accountManager,
UserChannelPersistence userChannelPersistence, UserChannelPersistence userChannelPersistence,
MainWindow parent) MainWindowViewModel viewmodel,
TopLevel topLevel)
{ {
_parent = parent; _viewModel = viewmodel;
_inputManager = inputManager; _inputManager = inputManager;
_accountManager = accountManager; _accountManager = accountManager;
_userChannelPersistence = userChannelPersistence; _userChannelPersistence = userChannelPersistence;
@@ -130,7 +134,8 @@ namespace Ryujinx.Ava
_hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle;
_lastCursorMoveTime = Stopwatch.GetTimestamp(); _lastCursorMoveTime = Stopwatch.GetTimestamp();
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
_inputManager.SetMouseDriver(new AvaloniaMouseDriver(_parent, renderer)); _topLevel = topLevel;
_inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer));
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
NpadManager = _inputManager.CreateNpadManager(); NpadManager = _inputManager.CreateNpadManager();
@@ -145,15 +150,15 @@ namespace Ryujinx.Ava
if (ApplicationPath.StartsWith("@SystemContent")) if (ApplicationPath.StartsWith("@SystemContent"))
{ {
ApplicationPath = _parent.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); ApplicationPath = _viewModel.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
_isFirmwareTitle = true; _isFirmwareTitle = true;
} }
ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorState_Changed; ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorState_Changed;
_parent.PointerLeave += Parent_PointerLeft; _topLevel.PointerLeave += TopLevel_PointerLeave;
_parent.PointerMoved += Parent_PointerMoved; _topLevel.PointerMoved += TopLevel_PointerMoved;
ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
@@ -163,25 +168,27 @@ namespace Ryujinx.Ava
_gpuCancellationTokenSource = new CancellationTokenSource(); _gpuCancellationTokenSource = new CancellationTokenSource();
} }
private void Parent_PointerMoved(object sender, PointerEventArgs e) private void TopLevel_PointerMoved(object sender, PointerEventArgs e)
{ {
_lastCursorMoveTime = Stopwatch.GetTimestamp(); if (sender is Control visual)
var p = e.GetCurrentPoint(_parent).Position; {
var r = _parent.InputHitTest(p); _lastCursorMoveTime = Stopwatch.GetTimestamp();
_isMouseInRenderer = r == Renderer; var point = e.GetCurrentPoint(visual).Position;
_isMouseInRenderer = Equals(visual.InputHitTest(point), Renderer);
}
} }
private void Parent_PointerLeft(object sender, PointerEventArgs e) private void TopLevel_PointerLeave(object sender, PointerEventArgs e)
{ {
_isMouseInRenderer = false; _isMouseInRenderer = false;
_parent.Cursor = Cursor.Default; _viewModel.Cursor = Cursor.Default;
} }
private void SetRendererWindowSize(Size size) private void SetRendererWindowSize(Size size)
{ {
if (_renderer != null) if (_renderer != null)
{ {
double scale = _parent.PlatformImpl.RenderScaling; double scale = _topLevel.PlatformImpl.RenderScaling;
_renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale)); _renderer.Window?.SetSize((int)(size.Width * scale), (int)(size.Height * scale));
} }
} }
@@ -257,7 +264,7 @@ namespace Ryujinx.Ava
NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); NpadManager.Initialize(Device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
TouchScreenManager.Initialize(Device); TouchScreenManager.Initialize(Device);
_parent.ViewModel.IsGameRunning = true; _viewModel.IsGameRunning = true;
string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName)
? string.Empty ? string.Empty
@@ -277,10 +284,10 @@ namespace Ryujinx.Ava
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
_parent.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; _viewModel.Title = $"Ryujinx {Program.Version}{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
}); });
_parent.ViewModel.HandleShaderProgress(Device); _viewModel.SetUIProgressHandlers(Device);
Renderer.SizeChanged += Window_SizeChanged; Renderer.SizeChanged += Window_SizeChanged;
@@ -288,7 +295,7 @@ namespace Ryujinx.Ava
_renderingThread.Start(); _renderingThread.Start();
_parent.ViewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value; _viewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value;
MainLoop(); MainLoop();
@@ -322,7 +329,7 @@ namespace Ryujinx.Ava
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
var value = e.NewValue; var value = e.NewValue;
_parent.ViewModel.Volume = e.NewValue; _viewModel.Volume = e.NewValue;
}); });
} }
@@ -357,8 +364,6 @@ namespace Ryujinx.Ava
DisplaySleep.Restore(); DisplaySleep.Restore();
Ptc.Close();
PtcProfiler.Stop();
NpadManager.Dispose(); NpadManager.Dispose();
TouchScreenManager.Dispose(); TouchScreenManager.Dispose();
Device.Dispose(); Device.Dispose();
@@ -372,7 +377,7 @@ namespace Ryujinx.Ava
{ {
if (Device.Application != null) if (Device.Application != null)
{ {
_parent.UpdateGameMetadata(Device.Application.TitleIdText); _viewModel.UpdateGameMetadata(Device.Application.TitleIdText);
} }
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
@@ -380,6 +385,9 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState; ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState; ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState;
_topLevel.PointerLeave -= TopLevel_PointerLeave;
_topLevel.PointerMoved -= TopLevel_PointerMoved;
_gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Cancel();
_gpuCancellationTokenSource.Dispose(); _gpuCancellationTokenSource.Dispose();
@@ -413,7 +421,7 @@ namespace Ryujinx.Ava
} }
else else
{ {
_parent.Cursor = Cursor.Default; _viewModel.Cursor = Cursor.Default;
} }
}); });
} }
@@ -425,57 +433,65 @@ namespace Ryujinx.Ava
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError)) if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion)) if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError))
{ {
if (userError == UserError.NoFirmware)
{ {
UserResult result = await ContentDialogHelper.CreateConfirmationDialog( if (SetupValidator.CanFixStartApplication(ContentManager, ApplicationPath, userError, out firmwareVersion))
LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage], firmwareVersion.VersionString),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
"");
if (result != UserResult.Yes)
{ {
await UserErrorDialog.ShowUserErrorDialog(userError, _parent); if (userError == UserError.NoFirmware)
{
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
LocaleManager.Instance[LocaleKeys.DialogFirmwareNoFirmwareInstalledMessage],
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedMessage],
firmwareVersion.VersionString),
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
"");
if (result != UserResult.Yes)
{
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
Device.Dispose();
return false;
}
}
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
{
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
Device.Dispose();
return false;
}
// Tell the user that we installed a firmware for them.
if (userError == UserError.NoFirmware)
{
firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
_viewModel.RefreshFirmwareStatus();
await ContentDialogHelper.CreateInfoDialog(
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage],
firmwareVersion.VersionString),
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage],
firmwareVersion.VersionString),
LocaleManager.Instance[LocaleKeys.InputDialogOk],
"",
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
}
}
else
{
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow));
Device.Dispose(); Device.Dispose();
return false; return false;
} }
} }
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
{
await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
Device.Dispose();
return false;
}
// Tell the user that we installed a firmware for them.
if (userError == UserError.NoFirmware)
{
firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
_parent.RefreshFirmwareStatus();
await ContentDialogHelper.CreateInfoDialog(
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstalledMessage], firmwareVersion.VersionString),
string.Format(LocaleManager.Instance[LocaleKeys.DialogFirmwareInstallEmbeddedSuccessMessage], firmwareVersion.VersionString),
LocaleManager.Instance[LocaleKeys.InputDialogOk],
"",
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
}
}
else
{
await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
Device.Dispose();
return false;
} }
} }
@@ -570,7 +586,7 @@ namespace Ryujinx.Ava
DiscordIntegrationModule.SwitchToPlayingState(Device.Application.TitleIdText, Device.Application.TitleName); DiscordIntegrationModule.SwitchToPlayingState(Device.Application.TitleIdText, Device.Application.TitleName);
_parent.ApplicationLibrary.LoadAndSaveMetaData(Device.Application.TitleIdText, appMetadata => _viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Application.TitleIdText, appMetadata =>
{ {
appMetadata.LastPlayed = DateTime.UtcNow.ToString(); appMetadata.LastPlayed = DateTime.UtcNow.ToString();
}); });
@@ -581,13 +597,13 @@ namespace Ryujinx.Ava
internal void Resume() internal void Resume()
{ {
Device?.System.TogglePauseEmulation(false); Device?.System.TogglePauseEmulation(false);
_parent.ViewModel.IsPaused = false; _viewModel.IsPaused = false;
} }
internal void Pause() internal void Pause()
{ {
Device?.System.TogglePauseEmulation(true); Device?.System.TogglePauseEmulation(true);
_parent.ViewModel.IsPaused = true; _viewModel.IsPaused = true;
} }
private void InitializeSwitchInstance() private void InitializeSwitchInstance()
@@ -635,7 +651,7 @@ namespace Ryujinx.Ava
Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration."); Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl; ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl;
MainWindow.SaveConfig(); MainWindowViewModel.SaveConfig();
deviceDriver = new OpenALHardwareDeviceDriver(); deviceDriver = new OpenALHardwareDeviceDriver();
} }
@@ -648,7 +664,7 @@ namespace Ryujinx.Ava
Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration."); Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo; ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo;
MainWindow.SaveConfig(); MainWindowViewModel.SaveConfig();
deviceDriver = new SoundIoHardwareDeviceDriver(); deviceDriver = new SoundIoHardwareDeviceDriver();
} }
@@ -674,7 +690,7 @@ namespace Ryujinx.Ava
Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration."); Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2; ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2;
MainWindow.SaveConfig(); MainWindowViewModel.SaveConfig();
deviceDriver = new SDL2HardwareDeviceDriver(); deviceDriver = new SDL2HardwareDeviceDriver();
} }
@@ -687,7 +703,7 @@ namespace Ryujinx.Ava
Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration."); Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl; ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl;
MainWindow.SaveConfig(); MainWindowViewModel.SaveConfig();
deviceDriver = new OpenALHardwareDeviceDriver(); deviceDriver = new OpenALHardwareDeviceDriver();
} }
@@ -713,7 +729,7 @@ namespace Ryujinx.Ava
Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration."); Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2; ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2;
MainWindow.SaveConfig(); MainWindowViewModel.SaveConfig();
deviceDriver = new SDL2HardwareDeviceDriver(); deviceDriver = new SDL2HardwareDeviceDriver();
} }
@@ -726,7 +742,7 @@ namespace Ryujinx.Ava
Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration."); Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo; ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo;
MainWindow.SaveConfig(); MainWindowViewModel.SaveConfig();
deviceDriver = new SoundIoHardwareDeviceDriver(); deviceDriver = new SoundIoHardwareDeviceDriver();
} }
@@ -743,14 +759,14 @@ namespace Ryujinx.Ava
IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None;
HLE.HLEConfiguration configuration = new HLE.HLEConfiguration(VirtualFileSystem, HLE.HLEConfiguration configuration = new HLE.HLEConfiguration(VirtualFileSystem,
_parent.LibHacHorizonManager, _viewModel.LibHacHorizonManager,
ContentManager, ContentManager,
_accountManager, _accountManager,
_userChannelPersistence, _userChannelPersistence,
renderer, renderer,
deviceDriver, deviceDriver,
memoryConfiguration, memoryConfiguration,
_parent.UiHandler, _viewModel.UiHandler,
(SystemLanguage)ConfigurationState.Instance.System.Language.Value, (SystemLanguage)ConfigurationState.Instance.System.Language.Value,
(RegionCode)ConfigurationState.Instance.System.Region.Value, (RegionCode)ConfigurationState.Instance.System.Region.Value,
ConfigurationState.Instance.Graphics.EnableVsync, ConfigurationState.Instance.Graphics.EnableVsync,
@@ -791,14 +807,14 @@ namespace Ryujinx.Ava
{ {
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
if (_parent.ViewModel.StartGamesInFullscreen) if (_viewModel.StartGamesInFullscreen)
{ {
_parent.WindowState = WindowState.FullScreen; _viewModel.WindowState = WindowState.FullScreen;
} }
if (_parent.WindowState == WindowState.FullScreen) if (_viewModel.WindowState == WindowState.FullScreen)
{ {
_parent.ViewModel.ShowMenuAndStatusBar = false; _viewModel.ShowMenuAndStatusBar = false;
} }
}); });
@@ -822,7 +838,7 @@ namespace Ryujinx.Ava
Width = (int)Renderer.Bounds.Width; Width = (int)Renderer.Bounds.Width;
Height = (int)Renderer.Bounds.Height; Height = (int)Renderer.Bounds.Height;
_renderer.Window.SetSize((int)(Width * _parent.PlatformImpl.RenderScaling), (int)(Height * _parent.PlatformImpl.RenderScaling)); _renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling));
_chrono.Start(); _chrono.Start();
@@ -850,7 +866,7 @@ namespace Ryujinx.Ava
if (!_renderingStarted) if (!_renderingStarted)
{ {
_renderingStarted = true; _renderingStarted = true;
_parent.SwitchToGameControl(); _viewModel.SwitchToRenderer(false);
} }
Device.PresentFrame(() => Renderer?.SwapBuffers()); Device.PresentFrame(() => Renderer?.SwapBuffers());
@@ -917,7 +933,7 @@ namespace Ryujinx.Ava
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
_parent.Cursor = _isMouseInRenderer ? InvisibleCursor : Cursor.Default; _viewModel.Cursor = _isMouseInRenderer ? InvisibleCursor : Cursor.Default;
}); });
} }
else else
@@ -928,7 +944,7 @@ namespace Ryujinx.Ava
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
_parent.Cursor = cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency ? InvisibleCursor : Cursor.Default; _viewModel.Cursor = cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency ? InvisibleCursor : Cursor.Default;
}); });
} }
} }
@@ -941,22 +957,22 @@ namespace Ryujinx.Ava
return false; return false;
} }
if (_parent.IsActive) if (_viewModel.IsActive)
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
HandleScreenState(); HandleScreenState();
if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _parent.WindowState != WindowState.FullScreen) if (_keyboardInterface.GetKeyboardStateSnapshot().IsPressed(Key.Delete) && _viewModel.WindowState != WindowState.FullScreen)
{ {
Ptc.Continue(); Device.Application.DiskCacheLoadState?.Cancel();
} }
}); });
} }
NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
if (_parent.IsActive) if (_viewModel.IsActive)
{ {
KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); KeyboardHotkeyState currentHotkeyState = GetHotkeyState();
@@ -972,10 +988,10 @@ namespace Ryujinx.Ava
ScreenshotRequested = true; ScreenshotRequested = true;
break; break;
case KeyboardHotkeyState.ShowUi: case KeyboardHotkeyState.ShowUi:
_parent.ViewModel.ShowMenuAndStatusBar = true; _viewModel.ShowMenuAndStatusBar = true;
break; break;
case KeyboardHotkeyState.Pause: case KeyboardHotkeyState.Pause:
if (_parent.ViewModel.IsPaused) if (_viewModel.IsPaused)
{ {
Resume(); Resume();
} }
@@ -994,7 +1010,7 @@ namespace Ryujinx.Ava
Device.SetVolume(0); Device.SetVolume(0);
} }
_parent.ViewModel.Volume = Device.GetVolume(); _viewModel.Volume = Device.GetVolume();
break; break;
case KeyboardHotkeyState.ResScaleUp: case KeyboardHotkeyState.ResScaleUp:
GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1; GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1;
@@ -1007,13 +1023,13 @@ namespace Ryujinx.Ava
_newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2);
Device.SetVolume(_newVolume); Device.SetVolume(_newVolume);
_parent.ViewModel.Volume = Device.GetVolume(); _viewModel.Volume = Device.GetVolume();
break; break;
case KeyboardHotkeyState.VolumeDown: case KeyboardHotkeyState.VolumeDown:
_newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2); _newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2);
Device.SetVolume(_newVolume); Device.SetVolume(_newVolume);
_parent.ViewModel.Volume = Device.GetVolume(); _viewModel.Volume = Device.GetVolume();
break; break;
case KeyboardHotkeyState.None: case KeyboardHotkeyState.None:
(_keyboardInterface as AvaloniaKeyboard).Clear(); (_keyboardInterface as AvaloniaKeyboard).Clear();
@@ -1033,7 +1049,7 @@ namespace Ryujinx.Ava
// Touchscreen // Touchscreen
bool hasTouch = false; bool hasTouch = false;
if (_parent.IsActive && !ConfigurationState.Instance.Hid.EnableMouse) if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse)
{ {
hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
} }

View File

@@ -269,6 +269,8 @@
<Color x:Key="DataGridSelectionColor">#FF00FABB</Color> <Color x:Key="DataGridSelectionColor">#FF00FABB</Color>
<Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color> <Color x:Key="ThemeContentBackgroundColor">#FF2D2D2D</Color>
<Color x:Key="ThemeControlBorderColor">#FF505050</Color> <Color x:Key="ThemeControlBorderColor">#FF505050</Color>
<Color x:Key="VsyncEnabled">#FF2EEAC9</Color>
<Color x:Key="VsyncDisabled">#FFFF4554</Color>
<x:Double x:Key="ScrollBarThickness">15</x:Double> <x:Double x:Key="ScrollBarThickness">15</x:Double>
<x:Double x:Key="FontSizeSmall">8</x:Double> <x:Double x:Key="FontSizeSmall">8</x:Double>
<x:Double x:Key="FontSizeNormal">10</x:Double> <x:Double x:Key="FontSizeNormal">10</x:Double>

View File

@@ -0,0 +1,111 @@
using Avalonia.Utilities;
using System;
using System.Text;
namespace Ryujinx.Ava.UI.Helper
{
using AvaLogger = Avalonia.Logging.Logger;
using AvaLogLevel = Avalonia.Logging.LogEventLevel;
using RyuLogger = Ryujinx.Common.Logging.Logger;
using RyuLogClass = Ryujinx.Common.Logging.LogClass;
internal class LoggerAdapter : Avalonia.Logging.ILogSink
{
public static void Register()
{
AvaLogger.Sink = new LoggerAdapter();
}
private static RyuLogger.Log? GetLog(AvaLogLevel level)
{
return level switch
{
AvaLogLevel.Verbose => RyuLogger.Trace,
AvaLogLevel.Debug => RyuLogger.Debug,
AvaLogLevel.Information => RyuLogger.Info,
AvaLogLevel.Warning => RyuLogger.Warning,
AvaLogLevel.Error => RyuLogger.Error,
AvaLogLevel.Fatal => RyuLogger.Notice,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null)
};
}
public bool IsEnabled(AvaLogLevel level, string area)
{
return GetLog(level) != null;
}
public void Log(AvaLogLevel level, string area, object source, string messageTemplate)
{
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, null));
}
public void Log<T0>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0)
{
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, new object[] { propertyValue0 }));
}
public void Log<T0, T1>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1)
{
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, new object[] { propertyValue0, propertyValue1 }));
}
public void Log<T0, T1, T2>(AvaLogLevel level, string area, object source, string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2)
{
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, new object[] { propertyValue0, propertyValue1, propertyValue2 }));
}
public void Log(AvaLogLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
{
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(area, messageTemplate, source, propertyValues));
}
private static string Format(string area, string template, object source, object[] v)
{
var result = new StringBuilder();
var r = new CharacterReader(template.AsSpan());
var i = 0;
result.Append('[');
result.Append(area);
result.Append("] ");
while (!r.End)
{
var c = r.Take();
if (c != '{')
{
result.Append(c);
}
else
{
if (r.Peek != '{')
{
result.Append('\'');
result.Append(i < v.Length ? v[i++] : null);
result.Append('\'');
r.TakeUntil('}');
r.Take();
}
else
{
result.Append('{');
r.Take();
}
}
}
if (source != null)
{
result.Append(" (");
result.Append(source.GetType().Name);
result.Append(" #");
result.Append(source.GetHashCode());
result.Append(')');
}
return result.ToString();
}
}
}

View File

@@ -11,10 +11,10 @@ namespace Ryujinx.Ava.Input
{ {
internal class AvaloniaMouseDriver : IGamepadDriver internal class AvaloniaMouseDriver : IGamepadDriver
{ {
private Control _widget; private Control _widget;
private bool _isDisposed; private bool _isDisposed;
private Size _size; private Size _size;
private readonly Window _window; private readonly TopLevel _window;
public bool[] PressedButtons { get; } public bool[] PressedButtons { get; }
public Vector2 CurrentPosition { get; private set; } public Vector2 CurrentPosition { get; private set; }
@@ -23,7 +23,7 @@ namespace Ryujinx.Ava.Input
public string DriverName => "AvaloniaMouseDriver"; public string DriverName => "AvaloniaMouseDriver";
public ReadOnlySpan<string> GamepadsIds => new[] { "0" }; public ReadOnlySpan<string> GamepadsIds => new[] { "0" };
public AvaloniaMouseDriver(Window window, Control parent) public AvaloniaMouseDriver(TopLevel window, Control parent)
{ {
_widget = parent; _widget = parent;
_window = window; _window = window;

View File

@@ -56,7 +56,7 @@ namespace Ryujinx.Modules
} }
Running = true; Running = true;
mainWindow.CanUpdate = false; mainWindow.ViewModel.CanUpdate = false;
// Detect current platform // Detect current platform
if (OperatingSystem.IsMacOS()) if (OperatingSystem.IsMacOS())
@@ -95,7 +95,7 @@ namespace Ryujinx.Modules
{ {
using (HttpClient jsonClient = ConstructHttpClient()) using (HttpClient jsonClient = ConstructHttpClient())
{ {
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformations.ReleaseChannelOwner}/{ReleaseInformations.ReleaseChannelRepo}/releases/latest"; string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL);
JObject jsonRoot = JObject.Parse(fetchedJson); JObject jsonRoot = JObject.Parse(fetchedJson);
@@ -182,7 +182,7 @@ namespace Ryujinx.Modules
} }
Running = false; Running = false;
mainWindow.CanUpdate = true; mainWindow.ViewModel.CanUpdate = true;
return; return;
} }
@@ -625,7 +625,7 @@ namespace Ryujinx.Modules
return false; return false;
} }
if (Program.Version.Contains("dirty") || !ReleaseInformations.IsValid()) if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid())
{ {
if (showWarnings) if (showWarnings)
{ {
@@ -640,7 +640,7 @@ namespace Ryujinx.Modules
#else #else
if (showWarnings) if (showWarnings)
{ {
if (ReleaseInformations.IsFlatHubBuild()) if (ReleaseInformation.IsFlatHubBuild())
{ {
ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]); ContentDialogHelper.CreateWarningDialog(LocaleManager.Instance[LocaleKeys.UpdaterDisabledWarningTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterFlatpakNotSupportedMessage]);
} }
@@ -711,4 +711,4 @@ namespace Ryujinx.Modules
} }
} }
} }
} }

View File

@@ -1,6 +1,6 @@
using ARMeilleure.Translation.PTC;
using Avalonia; using Avalonia;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.UI.Helper;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
@@ -14,8 +14,10 @@ using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava namespace Ryujinx.Ava
@@ -33,9 +35,51 @@ namespace Ryujinx.Ava
private const uint MB_ICONWARNING = 0x30; private const uint MB_ICONWARNING = 0x30;
[SupportedOSPlatform("linux")]
static void RegisterMimeTypes()
{
if (ReleaseInformation.IsFlatHubBuild())
{
return;
}
string mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime");
if (!File.Exists(Path.Combine(mimeDbPath, "packages", "Ryujinx.xml")))
{
string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml");
using Process mimeProcess = new();
mimeProcess.StartInfo.FileName = "xdg-mime";
mimeProcess.StartInfo.Arguments = $"install --novendor --mode user {mimeTypesFile}";
mimeProcess.Start();
mimeProcess.WaitForExit();
if (mimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Unable to install mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}");
return;
}
using Process updateMimeProcess = new();
updateMimeProcess.StartInfo.FileName = "update-mime-database";
updateMimeProcess.StartInfo.Arguments = mimeDbPath;
updateMimeProcess.Start();
updateMimeProcess.WaitForExit();
if (updateMimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}");
}
}
}
public static void Main(string[] args) public static void Main(string[] args)
{ {
Version = ReleaseInformations.GetVersion(); Version = ReleaseInformation.GetVersion();
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
{ {
@@ -46,6 +90,8 @@ namespace Ryujinx.Ava
Initialize(args); Initialize(args);
LoggerAdapter.Register();
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
} }
@@ -67,8 +113,7 @@ namespace Ryujinx.Ava
AllowEglInitialization = false, AllowEglInitialization = false,
CompositionBackdropCornerRadius = 8.0f, CompositionBackdropCornerRadius = 8.0f,
}) })
.UseSkia() .UseSkia();
.LogToTrace();
} }
private static void Initialize(string[] args) private static void Initialize(string[] args)
@@ -94,6 +139,12 @@ namespace Ryujinx.Ava
// Initialize the logger system. // Initialize the logger system.
LoggerModule.Initialize(); LoggerModule.Initialize();
// Register mime types on linux.
if (OperatingSystem.IsLinux())
{
RegisterMimeTypes();
}
// Initialize Discord integration. // Initialize Discord integration.
DiscordIntegrationModule.Initialize(); DiscordIntegrationModule.Initialize();
@@ -197,9 +248,6 @@ namespace Ryujinx.Ava
private static void ProcessUnhandledException(Exception ex, bool isTerminating) private static void ProcessUnhandledException(Exception ex, bool isTerminating)
{ {
Ptc.Close();
PtcProfiler.Stop();
string message = $"Unhandled exception caught: {ex}"; string message = $"Unhandled exception caught: {ex}";
Logger.Error?.PrintMsg(LogClass.Application, message); Logger.Error?.PrintMsg(LogClass.Application, message);
@@ -219,10 +267,7 @@ namespace Ryujinx.Ava
{ {
DiscordIntegrationModule.Exit(); DiscordIntegrationModule.Exit();
Ptc.Dispose();
PtcProfiler.Dispose();
Logger.Shutdown(); Logger.Shutdown();
} }
} }
} }

View File

@@ -76,6 +76,16 @@
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
<Content Include="..\distribution\linux\Ryujinx.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="..\distribution\linux\mime\Ryujinx.xml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>mime\Ryujinx.xml</TargetPath>
</Content>
</ItemGroup>
<ItemGroup> <ItemGroup>
<AvaloniaResource Include="Ui\**\*.xaml"> <AvaloniaResource Include="Ui\**\*.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>

View File

@@ -144,9 +144,9 @@ namespace Ryujinx.Ava.UI.Applet
public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value) public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value)
{ {
device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value); device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value);
if (_parent.AppHost != null) if (_parent.ViewModel.AppHost != null)
{ {
_parent.AppHost.Stop(); _parent.ViewModel.AppHost.Stop();
} }
} }

View File

@@ -136,7 +136,7 @@ namespace Ryujinx.Ava.UI.Applet
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
_hiddenTextBox.Clear(); _hiddenTextBox.Clear();
_parent.RendererControl.Focus(); _parent.ViewModel.RendererControl.Focus();
_parent = null; _parent = null;
}); });

View File

@@ -13,8 +13,8 @@ namespace Ryujinx.Ava.UI.Applet
DefaultBackgroundColor = BrushToThemeColor(parent.Background); DefaultBackgroundColor = BrushToThemeColor(parent.Background);
DefaultForegroundColor = BrushToThemeColor(parent.Foreground); DefaultForegroundColor = BrushToThemeColor(parent.Foreground);
DefaultBorderColor = BrushToThemeColor(parent.BorderBrush); DefaultBorderColor = BrushToThemeColor(parent.BorderBrush);
SelectionBackgroundColor = BrushToThemeColor(parent.SearchBox.SelectionBrush); SelectionBackgroundColor = BrushToThemeColor(parent.ViewControls.SearchBox.SelectionBrush);
SelectionForegroundColor = BrushToThemeColor(parent.SearchBox.SelectionForegroundBrush); SelectionForegroundColor = BrushToThemeColor(parent.ViewControls.SearchBox.SelectionForegroundBrush);
} }
public string FontFamily { get; } public string FontFamily { get; }

View File

@@ -139,6 +139,9 @@
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter"> <Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" /> <Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" />
</Style> </Style>
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.GridItemSelectorSize}" />
</Style>
</ListBox.Styles> </ListBox.Styles>
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>

View File

@@ -38,9 +38,9 @@ namespace Ryujinx.Ava.UI.Controls
{ {
if (sender is ListBox listBox) if (sender is ListBox listBox)
{ {
var selected = listBox.SelectedItem as ApplicationData; _selectedApplication = listBox.SelectedItem as ApplicationData;
_selectedApplication = selected; (DataContext as MainWindowViewModel).GridSelectedApplication = _selectedApplication;
} }
} }

View File

@@ -137,12 +137,12 @@
<Style Selector="ListBoxItem:selected /template/ ContentPresenter"> <Style Selector="ListBoxItem:selected /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" /> <Setter Property="Background" Value="{DynamicResource AppListBackgroundColor}" />
</Style> </Style>
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
<Setter Property="MinHeight" Value="100" />
</Style>
<Style Selector="ListBoxItem:pointerover /template/ ContentPresenter"> <Style Selector="ListBoxItem:pointerover /template/ ContentPresenter">
<Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" /> <Setter Property="Background" Value="{DynamicResource AppListHoverBackgroundColor}" />
</Style> </Style>
<Style Selector="ListBoxItem:selected /template/ Border#SelectionIndicator">
<Setter Property="MinHeight" Value="{Binding $parent[UserControl].DataContext.ListItemSelectorSize}" />
</Style>
</ListBox.Styles> </ListBox.Styles>
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>

View File

@@ -38,9 +38,9 @@ namespace Ryujinx.Ava.UI.Controls
{ {
if (sender is ListBox listBox) if (sender is ListBox listBox)
{ {
var selected = listBox.SelectedItem as ApplicationData; _selectedApplication = listBox.SelectedItem as ApplicationData;
_selectedApplication = selected; (DataContext as MainWindowViewModel).ListSelectedApplication = _selectedApplication;
} }
} }

View File

@@ -19,7 +19,56 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
private static bool _isChoiceDialogOpen; private static bool _isChoiceDialogOpen;
private async static Task<UserResult> ShowContentDialog( public async static Task<UserResult> ShowContentDialog(
string title,
object content,
string primaryButton,
string secondaryButton,
string closeButton,
UserResult primaryButtonResult = UserResult.Ok,
ManualResetEvent deferResetEvent = null,
Func<Window, Task> doWhileDeferred = null,
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
{
UserResult result = UserResult.None;
ContentDialog contentDialog = new()
{
Title = title,
PrimaryButtonText = primaryButton,
SecondaryButtonText = secondaryButton,
CloseButtonText = closeButton,
Content = content
};
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
{
result = primaryButtonResult;
});
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{
result = UserResult.No;
contentDialog.PrimaryButtonClick -= deferCloseAction;
});
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
{
result = UserResult.Cancel;
contentDialog.PrimaryButtonClick -= deferCloseAction;
});
if (deferResetEvent != null)
{
contentDialog.PrimaryButtonClick += deferCloseAction;
}
await ShowAsync(contentDialog);
return result;
}
private async static Task<UserResult> ShowTextDialog(
string title, string title,
string primaryText, string primaryText,
string secondaryText, string secondaryText,
@@ -32,119 +81,9 @@ namespace Ryujinx.Ava.UI.Helpers
Func<Window, Task> doWhileDeferred = null, Func<Window, Task> doWhileDeferred = null,
TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null) TypedEventHandler<ContentDialog, ContentDialogButtonClickEventArgs> deferCloseAction = null)
{ {
UserResult result = UserResult.None; Grid content = CreateTextDialogContent(primaryText, secondaryText, iconSymbol);
bool useOverlay = false; return await ShowContentDialog(title, content, primaryButton, secondaryButton, closeButton, primaryButtonResult, deferResetEvent, doWhileDeferred, deferCloseAction);
Window mainWindow = null;
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
{
foreach (var item in al.Windows)
{
if (item.IsActive && item is MainWindow window && window.ViewModel.IsGameRunning)
{
mainWindow = window;
useOverlay = true;
break;
}
}
}
ContentDialog contentDialog = null;
ContentDialogOverlayWindow overlay = null;
if (useOverlay)
{
overlay = new ContentDialogOverlayWindow()
{
Height = mainWindow.Bounds.Height,
Width = mainWindow.Bounds.Width,
Position = mainWindow.PointToScreen(new Point())
};
mainWindow.PositionChanged += OverlayOnPositionChanged;
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
{
overlay.Position = mainWindow.PointToScreen(new Point());
}
contentDialog = overlay.ContentDialog;
bool opened = false;
overlay.Opened += OverlayOnActivated;
async void OverlayOnActivated(object sender, EventArgs e)
{
if (opened)
{
return;
}
opened = true;
overlay.Position = mainWindow.PointToScreen(new Point());
await ShowDialog();
}
await overlay.ShowDialog(mainWindow);
}
else
{
contentDialog = new ContentDialog();
await ShowDialog();
}
async Task ShowDialog()
{
contentDialog.Title = title;
contentDialog.PrimaryButtonText = primaryButton;
contentDialog.SecondaryButtonText = secondaryButton;
contentDialog.CloseButtonText = closeButton;
contentDialog.Content = CreateDialogTextContent(primaryText, secondaryText, iconSymbol);
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
{
result = primaryButtonResult;
});
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{
result = UserResult.No;
contentDialog.PrimaryButtonClick -= deferCloseAction;
});
contentDialog.CloseButtonCommand = MiniCommand.Create(() =>
{
result = UserResult.Cancel;
contentDialog.PrimaryButtonClick -= deferCloseAction;
});
if (deferResetEvent != null)
{
contentDialog.PrimaryButtonClick += deferCloseAction;
}
if (useOverlay)
{
await contentDialog.ShowAsync(overlay, ContentDialogPlacement.Popup);
overlay!.Close();
}
else
{
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
}
}
if (useOverlay)
{
overlay.Content = null;
overlay.Close();
}
return result;
} }
public async static Task<UserResult> ShowDeferredContentDialog( public async static Task<UserResult> ShowDeferredContentDialog(
@@ -162,7 +101,7 @@ namespace Ryujinx.Ava.UI.Helpers
bool startedDeferring = false; bool startedDeferring = false;
UserResult result = UserResult.None; UserResult result = UserResult.None;
return await ShowContentDialog( return await ShowTextDialog(
title, title,
primaryText, primaryText,
secondaryText, secondaryText,
@@ -192,8 +131,7 @@ namespace Ryujinx.Ava.UI.Helpers
sender.PrimaryButtonClick -= DeferClose; sender.PrimaryButtonClick -= DeferClose;
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed _ = Task.Run(() =>
Task.Run(() =>
{ {
deferResetEvent.WaitOne(); deferResetEvent.WaitOne();
@@ -202,7 +140,6 @@ namespace Ryujinx.Ava.UI.Helpers
deferral.Complete(); deferral.Complete();
}); });
}); });
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
if (doWhileDeferred != null) if (doWhileDeferred != null)
{ {
@@ -213,34 +150,42 @@ namespace Ryujinx.Ava.UI.Helpers
} }
} }
private static Grid CreateDialogTextContent(string primaryText, string secondaryText, int symbol) private static Grid CreateTextDialogContent(string primaryText, string secondaryText, int symbol)
{ {
Grid content = new Grid(); Grid content = new()
content.RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() }; {
content.ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() }; RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() },
ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() },
content.MinHeight = 80; MinHeight = 80
};
SymbolIcon icon = new()
{
Symbol = (Symbol)symbol,
Margin = new Thickness(10),
FontSize = 40,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
};
SymbolIcon icon = new SymbolIcon { Symbol = (Symbol)symbol, Margin = new Thickness(10) };
icon.FontSize = 40;
icon.VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center;
Grid.SetColumn(icon, 0); Grid.SetColumn(icon, 0);
Grid.SetRowSpan(icon, 2); Grid.SetRowSpan(icon, 2);
Grid.SetRow(icon, 0); Grid.SetRow(icon, 0);
TextBlock primaryLabel = new TextBlock() TextBlock primaryLabel = new()
{ {
Text = primaryText, Text = primaryText,
Margin = new Thickness(5), Margin = new Thickness(5),
TextWrapping = TextWrapping.Wrap, TextWrapping = TextWrapping.Wrap,
MaxWidth = 450 MaxWidth = 450
}; };
TextBlock secondaryLabel = new TextBlock()
TextBlock secondaryLabel = new()
{ {
Text = secondaryText, Text = secondaryText,
Margin = new Thickness(5), Margin = new Thickness(5),
TextWrapping = TextWrapping.Wrap, TextWrapping = TextWrapping.Wrap,
MaxWidth = 450 MaxWidth = 450
}; };
Grid.SetColumn(primaryLabel, 1); Grid.SetColumn(primaryLabel, 1);
@@ -262,7 +207,7 @@ namespace Ryujinx.Ava.UI.Helpers
string closeButton, string closeButton,
string title) string title)
{ {
return await ShowContentDialog( return await ShowTextDialog(
title, title,
primary, primary,
secondaryText, secondaryText,
@@ -280,7 +225,7 @@ namespace Ryujinx.Ava.UI.Helpers
string title, string title,
UserResult primaryButtonResult = UserResult.Yes) UserResult primaryButtonResult = UserResult.Yes)
{ {
return await ShowContentDialog( return await ShowTextDialog(
string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle] : title, string.IsNullOrWhiteSpace(title) ? LocaleManager.Instance[LocaleKeys.DialogConfirmationTitle] : title,
primaryText, primaryText,
secondaryText, secondaryText,
@@ -298,7 +243,7 @@ namespace Ryujinx.Ava.UI.Helpers
internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText) internal static async Task CreateUpdaterInfoDialog(string primary, string secondaryText)
{ {
await ShowContentDialog( await ShowTextDialog(
LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle], LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle],
primary, primary,
secondaryText, secondaryText,
@@ -310,7 +255,7 @@ namespace Ryujinx.Ava.UI.Helpers
internal static async Task CreateWarningDialog(string primary, string secondaryText) internal static async Task CreateWarningDialog(string primary, string secondaryText)
{ {
await ShowContentDialog( await ShowTextDialog(
LocaleManager.Instance[LocaleKeys.DialogWarningTitle], LocaleManager.Instance[LocaleKeys.DialogWarningTitle],
primary, primary,
secondaryText, secondaryText,
@@ -324,7 +269,7 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
Logger.Error?.Print(LogClass.Application, errorMessage); Logger.Error?.Print(LogClass.Application, errorMessage);
await ShowContentDialog( await ShowTextDialog(
LocaleManager.Instance[LocaleKeys.DialogErrorTitle], LocaleManager.Instance[LocaleKeys.DialogErrorTitle],
LocaleManager.Instance[LocaleKeys.DialogErrorMessage], LocaleManager.Instance[LocaleKeys.DialogErrorMessage],
errorMessage, errorMessage,
@@ -343,16 +288,15 @@ namespace Ryujinx.Ava.UI.Helpers
_isChoiceDialogOpen = true; _isChoiceDialogOpen = true;
UserResult response = UserResult response = await ShowTextDialog(
await ShowContentDialog( title,
title, primary,
primary, secondaryText,
secondaryText, LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogYes], "",
"", LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.InputDialogNo], (int)Symbol.Help,
(int)Symbol.Help, UserResult.Yes);
UserResult.Yes);
_isChoiceDialogOpen = false; _isChoiceDialogOpen = false;
@@ -396,5 +340,98 @@ namespace Ryujinx.Ava.UI.Helpers
return string.Empty; return string.Empty;
} }
public static async Task<ContentDialogResult> ShowAsync(ContentDialog contentDialog)
{
ContentDialogResult result;
ContentDialogOverlayWindow contentDialogOverlayWindow = null;
Window parent = GetMainWindow();
if (parent.IsActive && parent is MainWindow window && window.ViewModel.IsGameRunning)
{
contentDialogOverlayWindow = new()
{
Height = parent.Bounds.Height,
Width = parent.Bounds.Width,
Position = parent.PointToScreen(new Point()),
ShowInTaskbar = false
};
parent.PositionChanged += OverlayOnPositionChanged;
void OverlayOnPositionChanged(object sender, PixelPointEventArgs e)
{
contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
}
contentDialogOverlayWindow.ContentDialog = contentDialog;
bool opened = false;
contentDialogOverlayWindow.Opened += OverlayOnActivated;
async void OverlayOnActivated(object sender, EventArgs e)
{
if (opened)
{
return;
}
opened = true;
contentDialogOverlayWindow.Position = parent.PointToScreen(new Point());
result = await ShowDialog();
}
result = await contentDialogOverlayWindow.ShowDialog<ContentDialogResult>(parent);
}
else
{
result = await ShowDialog();
}
async Task<ContentDialogResult> ShowDialog()
{
if (contentDialogOverlayWindow is not null)
{
result = await contentDialog.ShowAsync(contentDialogOverlayWindow);
contentDialogOverlayWindow!.Close();
}
else
{
result = await contentDialog.ShowAsync();
}
return result;
}
if (contentDialogOverlayWindow is not null)
{
contentDialogOverlayWindow.Content = null;
contentDialogOverlayWindow.Close();
}
return result;
}
private static Window GetMainWindow()
{
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
{
foreach (Window item in al.Windows)
{
if (item.IsActive && item is MainWindow window)
{
return window;
}
}
}
return null;
}
} }
} }

View File

@@ -0,0 +1,136 @@
using Avalonia;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Configuration;
using System;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.ViewModels
{
public class AboutWindowViewModel : BaseModel
{
private Bitmap _githubLogo;
private Bitmap _discordLogo;
private Bitmap _patreonLogo;
private Bitmap _twitterLogo;
private string _version;
private string _supporters;
public Bitmap GithubLogo
{
get => _githubLogo;
set
{
_githubLogo = value;
OnPropertyChanged();
}
}
public Bitmap DiscordLogo
{
get => _discordLogo;
set
{
_discordLogo = value;
OnPropertyChanged();
}
}
public Bitmap PatreonLogo
{
get => _patreonLogo;
set
{
_patreonLogo = value;
OnPropertyChanged();
}
}
public Bitmap TwitterLogo
{
get => _twitterLogo;
set
{
_twitterLogo = value;
OnPropertyChanged();
}
}
public string Supporters
{
get => _supporters;
set
{
_supporters = value;
OnPropertyChanged();
}
}
public string Version
{
get => _version;
set
{
_version = value;
OnPropertyChanged();
}
}
public string Developers
{
get => string.Format(LocaleManager.Instance[LocaleKeys.AboutPageDeveloperListMore], "gdkchan, Ac_K, marysaka, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, GoffyDude, TSRBerry, IsaacMarovitz");
}
public AboutWindowViewModel()
{
Version = Program.Version;
var assets = AvaloniaLocator.Current.GetService<Avalonia.Platform.IAssetLoader>();
if (ConfigurationState.Instance.Ui.BaseStyle.Value == "Light")
{
GithubLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.Ui.Common")));
DiscordLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.Ui.Common")));
PatreonLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.Ui.Common")));
TwitterLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.Ui.Common")));
}
else
{
GithubLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.Ui.Common")));
DiscordLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.Ui.Common")));
PatreonLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.Ui.Common")));
TwitterLogo = new Bitmap(assets.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.Ui.Common")));
}
Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson);
}
private async Task DownloadPatronsJson()
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
Supporters = LocaleManager.Instance[LocaleKeys.ConnectionError];
return;
}
HttpClient httpClient = new();
try
{
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString)) + "\n\n";
}
catch
{
Supporters = LocaleManager.Instance[LocaleKeys.ApiError];
}
}
}
}

View File

@@ -244,9 +244,9 @@ namespace Ryujinx.Ava.UI.ViewModels
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
if (_mainWindow.AppHost != null) if (_mainWindow.ViewModel.AppHost != null)
{ {
_mainWindow.AppHost.NpadManager.BlockInputUpdates(); _mainWindow.ViewModel.AppHost.NpadManager.BlockInputUpdates();
} }
_isLoaded = false; _isLoaded = false;
@@ -862,7 +862,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
_mainWindow.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); _mainWindow.ViewModel.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
// Atomically replace and signal input change. // Atomically replace and signal input change.
// NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event. // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
@@ -891,7 +891,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
_mainWindow.AppHost?.NpadManager.UnblockInputUpdates(); _mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
SelectedGamepad?.Dispose(); SelectedGamepad?.Dispose();

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,3 @@
using Avalonia;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading; using Avalonia.Threading;
@@ -8,8 +7,6 @@ using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Audio.Backends.SDL2; using Ryujinx.Audio.Backends.SDL2;
using Ryujinx.Audio.Backends.SoundIo; using Ryujinx.Audio.Backends.SoundIo;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
@@ -19,7 +16,6 @@ using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.Input;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Common.Configuration.System;
using System; using System;
@@ -30,11 +26,10 @@ using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
internal class SettingsViewModel : BaseModel public class SettingsViewModel : BaseModel
{ {
private readonly VirtualFileSystem _virtualFileSystem; private readonly VirtualFileSystem _virtualFileSystem;
private readonly ContentManager _contentManager; private readonly ContentManager _contentManager;
private readonly StyleableWindow _owner;
private TimeZoneContentManager _timeZoneContentManager; private TimeZoneContentManager _timeZoneContentManager;
private readonly List<string> _validTzRegions; private readonly List<string> _validTzRegions;
@@ -44,10 +39,14 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _graphicsBackendMultithreadingIndex; private int _graphicsBackendMultithreadingIndex;
private float _volume; private float _volume;
private bool _isVulkanAvailable = true; private bool _isVulkanAvailable = true;
private bool _directoryChanged = false; private bool _directoryChanged;
private List<string> _gpuIds = new List<string>(); private List<string> _gpuIds = new();
private KeyboardHotkeys _keyboardHotkeys; private KeyboardHotkeys _keyboardHotkeys;
private int _graphicsBackendIndex; private int _graphicsBackendIndex;
private string _customThemePath;
public event Action CloseWindow;
public event Action SaveSettingsEvent;
public int ResolutionScale public int ResolutionScale
{ {
@@ -67,19 +66,16 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
_graphicsBackendMultithreadingIndex = value; _graphicsBackendMultithreadingIndex = value;
if (_owner != null) if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
{ {
if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value) Dispatcher.UIThread.Post(async () =>
{ {
Dispatcher.UIThread.Post(async () => await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage],
{ "",
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningMessage], "",
"", LocaleManager.Instance[LocaleKeys.InputDialogOk],
"", LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]);
LocaleManager.Instance[LocaleKeys.InputDialogOk], });
LocaleManager.Instance[LocaleKeys.DialogSettingsBackendThreadingWarningTitle]);
});
}
} }
OnPropertyChanged(); OnPropertyChanged();
@@ -120,12 +116,12 @@ namespace Ryujinx.Ava.UI.ViewModels
OnPropertyChanged(); OnPropertyChanged();
} }
} }
public bool IsMacOS public bool IsMacOS
{ {
get => OperatingSystem.IsMacOS(); get => OperatingSystem.IsMacOS();
} }
public bool EnableDiscordIntegration { get; set; } public bool EnableDiscordIntegration { get; set; }
public bool CheckUpdatesOnStart { get; set; } public bool CheckUpdatesOnStart { get; set; }
public bool ShowConfirmExit { get; set; } public bool ShowConfirmExit { get; set; }
@@ -160,7 +156,20 @@ namespace Ryujinx.Ava.UI.ViewModels
public string TimeZone { get; set; } public string TimeZone { get; set; }
public string ShaderDumpPath { get; set; } public string ShaderDumpPath { get; set; }
public string CustomThemePath { get; set; }
public string CustomThemePath
{
get
{
return _customThemePath;
}
set
{
_customThemePath = value;
OnPropertyChanged();
}
}
public int Language { get; set; } public int Language { get; set; }
public int Region { get; set; } public int Region { get; set; }
@@ -191,7 +200,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
_volume = value; _volume = value;
ConfigurationState.Instance.System.AudioVolume.Value = (float)(_volume / 100); ConfigurationState.Instance.System.AudioVolume.Value = _volume / 100;
OnPropertyChanged(); OnPropertyChanged();
} }
@@ -199,7 +208,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public DateTimeOffset DateOffset { get; set; } public DateTimeOffset DateOffset { get; set; }
public TimeSpan TimeOffset { get; set; } public TimeSpan TimeOffset { get; set; }
public AvaloniaList<TimeZone> TimeZones { get; set; } private AvaloniaList<TimeZone> TimeZones { get; set; }
public AvaloniaList<string> GameDirectories { get; set; } public AvaloniaList<string> GameDirectories { get; set; }
public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; } public ObservableCollection<ComboBoxItem> AvailableGpus { get; set; }
@@ -214,17 +223,13 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public IGamepadDriver AvaloniaKeyboardDriver { get; } public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this()
public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager, StyleableWindow owner) : this()
{ {
_virtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
_contentManager = contentManager; _contentManager = contentManager;
_owner = owner;
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
LoadTimeZones(); LoadTimeZones();
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
} }
} }
@@ -251,10 +256,10 @@ namespace Ryujinx.Ava.UI.ViewModels
IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported; IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
} }
private unsafe void LoadAvailableGpus() private void LoadAvailableGpus()
{ {
_gpuIds = new List<string>(); _gpuIds = new List<string>();
List<string> names = new List<string>(); List<string> names = new();
var devices = VulkanRenderer.GetPhysicalDevices(); var devices = VulkanRenderer.GetPhysicalDevices();
if (devices.Length == 0) if (devices.Length == 0)
@@ -272,7 +277,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
AvailableGpus.Clear(); AvailableGpus.Clear();
AvailableGpus.AddRange(names.Select(x => new ComboBoxItem() { Content = x })); AvailableGpus.AddRange(names.Select(x => new ComboBoxItem { Content = x }));
} }
public void LoadTimeZones() public void LoadTimeZones()
@@ -302,25 +307,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public async void BrowseTheme()
{
var dialog = new OpenFileDialog()
{
Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle],
AllowMultiple = false
};
dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] });
var file = await dialog.ShowAsync(_owner);
if (file != null && file.Length > 0)
{
CustomThemePath = file[0];
OnPropertyChanged(nameof(CustomThemePath));
}
}
public void LoadCurrentConfiguration() public void LoadCurrentConfiguration()
{ {
ConfigurationState config = ConfigurationState.Instance; ConfigurationState config = ConfigurationState.Instance;
@@ -477,16 +463,8 @@ namespace Ryujinx.Ava.UI.ViewModels
config.ToFileFormat().SaveConfig(Program.ConfigurationPath); config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
MainWindow.UpdateGraphicsConfig(); MainWindow.UpdateGraphicsConfig();
if (_owner is SettingsWindow owner) SaveSettingsEvent?.Invoke();
{
owner.ControllerSettings?.SaveCurrentProfile();
}
if (_owner.Owner is MainWindow window && _directoryChanged)
{
window.ViewModel.LoadApplications();
}
_directoryChanged = false; _directoryChanged = false;
} }
@@ -504,13 +482,13 @@ namespace Ryujinx.Ava.UI.ViewModels
public void OkButton() public void OkButton()
{ {
SaveSettings(); SaveSettings();
_owner.Close(); CloseWindow?.Invoke();
} }
public void CancelButton() public void CancelButton()
{ {
RevertIfNotSaved(); RevertIfNotSaved();
_owner.Close(); CloseWindow?.Invoke();
} }
} }
} }

View File

@@ -0,0 +1,216 @@
<UserControl
xmlns="https://github.com/avaloniaui"
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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
mc:Ignorable="d"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
x:DataType="viewModels:MainWindowViewModel"
x:Class="Ryujinx.Ava.UI.Views.Main.MainMenuBarView"
x:CompileBindings="True">
<Design.DataContext>
<viewModels:MainWindowViewModel />
</Design.DataContext>
<DockPanel HorizontalAlignment="Stretch">
<Menu
Name="Menu"
Height="35"
Margin="0"
HorizontalAlignment="Left">
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel Margin="0" HorizontalAlignment="Stretch" />
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarFile}">
<MenuItem
Command="{ReflectionBinding OpenFile}"
Header="{locale:Locale MenuBarFileOpenFromFile}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" />
<MenuItem
Command="{ReflectionBinding OpenFolder}"
Header="{locale:Locale MenuBarFileOpenUnpacked}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" />
<MenuItem Header="{locale:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}">
<MenuItem
Click="OpenMiiApplet"
Header="Mii Edit Applet"
ToolTip.Tip="{locale:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
</MenuItem>
<Separator />
<MenuItem
Command="{ReflectionBinding OpenRyujinxFolder}"
Header="{locale:Locale MenuBarFileOpenEmuFolder}"
ToolTip.Tip="{locale:Locale OpenRyujinxFolderTooltip}" />
<MenuItem
Command="{ReflectionBinding OpenLogsFolder}"
Header="{locale:Locale MenuBarFileOpenLogsFolder}"
ToolTip.Tip="{locale:Locale OpenRyujinxLogsTooltip}" />
<Separator />
<MenuItem
Click="CloseWindow"
Header="{locale:Locale MenuBarFileExit}"
ToolTip.Tip="{locale:Locale ExitTooltip}" />
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarOptions}">
<MenuItem
Command="{ReflectionBinding ToggleFullscreen}"
Header="{locale:Locale MenuBarOptionsToggleFullscreen}"
InputGesture="F11" />
<MenuItem>
<MenuItem.Icon>
<CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}"
MinWidth="250">
<TextBlock Text="{locale:Locale MenuBarOptionsStartGamesInFullscreen}"/>
</CheckBox>
</MenuItem.Icon>
</MenuItem>
<MenuItem IsVisible="{Binding ShowConsoleVisible}">
<MenuItem.Icon>
<CheckBox IsChecked="{Binding ShowConsole, Mode=TwoWay}"
MinWidth="250">
<TextBlock Text="{locale:Locale MenuBarOptionsShowConsole}"/>
</CheckBox>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="{locale:Locale MenuBarOptionsChangeLanguage}">
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="de_DE"
Header="Deutsch" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="en_US"
Header="English (US)" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="es_ES"
Header="Español (ES)" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="fr_FR"
Header="Français" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="it_IT"
Header="Italiano" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="pt_BR"
Header="Português (BR)" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="tr_TR"
Header="Türkçe" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="el_GR"
Header="Ελληνικά" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="pl_PL"
Header="Polski" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ru_RU"
Header="Русский" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="zh_CN"
Header="简体中文" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="zh_TW"
Header="繁體中文" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ja_JP"
Header="日本語" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ko_KR"
Header="한국어" />
</MenuItem>
<Separator />
<MenuItem
Click="OpenSettings"
Header="{locale:Locale MenuBarOptionsSettings}"
ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" />
<MenuItem
Command="{ReflectionBinding ManageProfiles}"
Header="{locale:Locale MenuBarOptionsManageUserProfiles}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" />
</MenuItem>
<MenuItem
Name="ActionsMenuItem"
VerticalAlignment="Center"
Header="{locale:Locale MenuBarActions}"
IsEnabled="{Binding IsGameRunning}">
<MenuItem
Click="PauseEmulation_Click"
Header="{locale:Locale MenuBarOptionsPauseEmulation}"
InputGesture="{Binding PauseKey}"
IsEnabled="{Binding !IsPaused}"
IsVisible="{Binding !IsPaused}" />
<MenuItem
Click="ResumeEmulation_Click"
Header="{locale:Locale MenuBarOptionsResumeEmulation}"
InputGesture="{Binding PauseKey}"
IsEnabled="{Binding IsPaused}"
IsVisible="{Binding IsPaused}" />
<MenuItem
Click="StopEmulation_Click"
Header="{locale:Locale MenuBarOptionsStopEmulation}"
InputGesture="Escape"
IsEnabled="{Binding IsGameRunning}"
ToolTip.Tip="{locale:Locale StopEmulationTooltip}" />
<MenuItem Command="{ReflectionBinding SimulateWakeUpMessage}" Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" />
<Separator />
<MenuItem
Name="ScanAmiiboMenuItem"
AttachedToVisualTree="ScanAmiiboMenuItem_AttachedToVisualTree"
Click="OpenAmiiboWindow"
Header="{locale:Locale MenuBarActionsScanAmiibo}"
IsEnabled="{Binding IsAmiiboRequested}" />
<MenuItem
Command="{ReflectionBinding TakeScreenshot}"
Header="{locale:Locale MenuBarFileToolsTakeScreenshot}"
InputGesture="{Binding ScreenshotKey}"
IsEnabled="{Binding IsGameRunning}" />
<MenuItem
Command="{ReflectionBinding HideUi}"
Header="{locale:Locale MenuBarFileToolsHideUi}"
InputGesture="{Binding ShowUiKey}"
IsEnabled="{Binding IsGameRunning}" />
<MenuItem
Click="OpenCheatManagerForCurrentApp"
Header="{locale:Locale GameListContextMenuManageCheat}"
IsEnabled="{Binding IsGameRunning}" />
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarTools}">
<MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}" IsEnabled="{Binding EnableNonGameRunningControls}">
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
</MenuItem>
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
<MenuItem
Name="UpdateMenuItem"
IsEnabled="{Binding CanUpdate}"
Click="CheckForUpdates"
Header="{locale:Locale MenuBarHelpCheckForUpdates}"
ToolTip.Tip="{locale:Locale CheckUpdatesTooltip}" />
<Separator />
<MenuItem
Click="OpenAboutWindow"
Header="{locale:Locale MenuBarHelpAbout}"
ToolTip.Tip="{locale:Locale OpenAboutTooltip}" />
</MenuItem>
</Menu>
</DockPanel>
</UserControl>

View File

@@ -0,0 +1,143 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using System.Threading.Tasks;
using LibHac.FsSystem;
using LibHac.Ncm;
using Ryujinx.HLE.HOS;
using Ryujinx.Modules;
namespace Ryujinx.Ava.UI.Views.Main
{
public partial class MainMenuBarView : UserControl
{
public MainWindow Window { get; private set; }
public MainWindowViewModel ViewModel { get; private set; }
public MainMenuBarView()
{
InitializeComponent();
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if (this.VisualRoot is MainWindow window)
{
Window = window;
}
ViewModel = Window.ViewModel;
DataContext = ViewModel;
}
private async void StopEmulation_Click(object sender, RoutedEventArgs e)
{
await Window.ViewModel.AppHost?.ShowExitPrompt();
}
private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() =>
{
Window.ViewModel.AppHost?.Pause();
});
}
private async void ResumeEmulation_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() =>
{
Window.ViewModel.AppHost?.Resume();
});
}
public async void OpenSettings(object sender, RoutedEventArgs e)
{
Window.SettingsWindow = new(Window.VirtualFileSystem, Window.ContentManager);
await Window.SettingsWindow.ShowDialog(Window);
ViewModel.LoadConfigurableHotKeys();
}
public void OpenMiiApplet(object sender, RoutedEventArgs e)
{
string contentPath = ViewModel.ContentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
if (!string.IsNullOrEmpty(contentPath))
{
ViewModel.LoadApplication(contentPath, false, "Mii Applet");
}
}
public async void OpenAmiiboWindow(object sender, RoutedEventArgs e)
{
if (!ViewModel.IsAmiiboRequested)
{
return;
}
if (ViewModel.AppHost.Device.System.SearchingForAmiibo(out int deviceId))
{
string titleId = ViewModel.AppHost.Device.Application.TitleIdText.ToUpper();
AmiiboWindow window = new(ViewModel.ShowAll, ViewModel.LastScannedAmiiboId, titleId);
await window.ShowDialog(Window);
if (window.IsScanned)
{
ViewModel.ShowAll = window.ViewModel.ShowAllAmiibo;
ViewModel.LastScannedAmiiboId = window.ScannedAmiibo.GetId();
ViewModel.AppHost.Device.System.ScanAmiibo(deviceId, ViewModel.LastScannedAmiiboId, window.ViewModel.UseRandomUuid);
}
}
}
public async void OpenCheatManagerForCurrentApp(object sender, RoutedEventArgs e)
{
if (!ViewModel.IsGameRunning)
{
return;
}
ApplicationLoader application = ViewModel.AppHost.Device.Application;
if (application != null)
{
await new CheatWindow(Window.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(Window);
ViewModel.AppHost.Device.EnableCheats();
}
}
private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{
if (sender is MenuItem)
{
ViewModel.IsAmiiboRequested = Window.ViewModel.AppHost.Device.System.SearchingForAmiibo(out _);
}
}
public async void CheckForUpdates(object sender, RoutedEventArgs e)
{
if (Updater.CanUpdate(true, Window))
{
await Updater.BeginParse(Window, true);
}
}
public async void OpenAboutWindow(object sender, RoutedEventArgs e)
{
await AboutWindow.Show();
}
public void CloseWindow(object sender, RoutedEventArgs e)
{
Window.Close();
}
}
}

View File

@@ -0,0 +1,232 @@
<UserControl
xmlns="https://github.com/avaloniaui"
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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Views.Main.MainStatusBarView"
x:CompileBindings="True"
x:DataType="viewModels:MainWindowViewModel">
<Design.DataContext>
<viewModels:MainWindowViewModel />
</Design.DataContext>
<Grid
Name="StatusBar"
Margin="0"
MinHeight="22"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Background="{DynamicResource ThemeContentBackgroundColor}"
DockPanel.Dock="Bottom"
IsVisible="{Binding ShowMenuAndStatusBar}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Column="0"
Margin="5"
VerticalAlignment="Center"
IsVisible="{Binding EnableNonGameRunningControls}">
<Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button
Width="25"
Height="25"
MinWidth="0"
Margin="0,0,5,0"
VerticalAlignment="Center"
Background="Transparent"
Command="{ReflectionBinding LoadApplications}">
<ui:SymbolIcon
Width="50"
Height="100"
Symbol="Refresh" />
</Button>
<TextBlock
Name="LoadStatus"
Grid.Column="1"
Margin="0,0,5,0"
VerticalAlignment="Center"
IsVisible="{Binding EnableNonGameRunningControls}"
Text="{locale:Locale StatusBarGamesLoaded}" />
<ProgressBar
Name="LoadProgressBar"
Grid.Column="2"
Height="6"
VerticalAlignment="Center"
Foreground="{DynamicResource HighlightColor}"
IsVisible="{Binding StatusBarVisible}"
Maximum="{Binding StatusBarProgressMaximum}"
Value="{Binding StatusBarProgressValue}" />
</Grid>
</StackPanel>
<StackPanel
Grid.Column="1"
Margin="0,2"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding IsGameRunning}"
MaxHeight="18"
Orientation="Horizontal">
<TextBlock
Name="VsyncStatus"
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="{Binding VsyncColor}"
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="VsyncStatus_PointerReleased"
Text="VSync"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Name="DockedStatus"
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="DockedStatus_PointerReleased"
Text="{Binding DockedStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Name="AspectRatioStatus"
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="AspectRatioStatus_PointerReleased"
Text="{Binding AspectRatioStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<ui:ToggleSplitButton
Name="VolumeStatus"
Padding="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Background="{DynamicResource ThemeContentBackgroundColor}"
BorderThickness="0"
Content="{Binding VolumeStatusText}"
IsChecked="{Binding VolumeMuted}"
IsVisible="{Binding !ShowLoadProgress}">
<ui:ToggleSplitButton.Flyout>
<Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
<Grid Margin="0">
<Slider
MaxHeight="40"
Width="150"
Margin="0"
Padding="0"
IsSnapToTickEnabled="True"
LargeChange="0.05"
Maximum="1"
Minimum="0"
SmallChange="0.01"
TickFrequency="0.05"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Value="{Binding Volume}" />
</Grid>
</Flyout>
</ui:ToggleSplitButton.Flyout>
</ui:ToggleSplitButton>
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding GameStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding FifoStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding BackendText}"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding GpuNameText}"
TextAlignment="Left" />
</StackPanel>
<StackPanel
Grid.Column="3"
Margin="0,0,5,0"
VerticalAlignment="Center"
IsVisible="{Binding ShowFirmwareStatus}"
Orientation="Horizontal">
<TextBlock
Name="FirmwareStatus"
Margin="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{locale:Locale StatusBarSystemVersion}" />
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,52 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Ui.Common.Configuration;
using System;
namespace Ryujinx.Ava.UI.Views.Main
{
public partial class MainStatusBarView : UserControl
{
public MainWindow Window;
public MainStatusBarView()
{
InitializeComponent();
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if (VisualRoot is MainWindow window)
{
Window = window;
}
DataContext = Window.ViewModel;
}
private void VsyncStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
{
Window.ViewModel.AppHost.Device.EnableDeviceVsync = !Window.ViewModel.AppHost.Device.EnableDeviceVsync;
Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {Window.ViewModel.AppHost.Device.EnableDeviceVsync}");
}
private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
{
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
}
private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
{
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
}
}
}

View File

@@ -0,0 +1,176 @@
<UserControl
xmlns="https://github.com/avaloniaui"
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="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Ryujinx.Ava.UI.Views.Main.MainViewControls"
x:CompileBindings="True"
x:DataType="viewModels:MainWindowViewModel">
<Design.DataContext>
<viewModels:MainWindowViewModel />
</Design.DataContext>
<DockPanel
Margin="0,0,0,5"
HorizontalAlignment="Stretch">
<Button
Width="40"
MinWidth="40"
Margin="5,2,0,2"
VerticalAlignment="Stretch"
Command="{ReflectionBinding SetListMode}"
IsEnabled="{Binding IsGrid}">
<ui:FontIcon
Margin="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
Glyph="{helpers:GlyphValueConverter List}" />
</Button>
<Button
Width="40"
MinWidth="40"
Margin="5,2,5,2"
VerticalAlignment="Stretch"
Command="{ReflectionBinding SetGridMode}"
IsEnabled="{Binding IsList}">
<ui:FontIcon
Margin="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
Glyph="{helpers:GlyphValueConverter Grid}" />
</Button>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
Text="{locale:Locale IconSize}"
ToolTip.Tip="{locale:Locale IconSizeTooltip}" />
<Slider
Width="150"
Height="35"
Margin="5,-10,5,0"
VerticalAlignment="Center"
IsSnapToTickEnabled="True"
Maximum="4"
Minimum="1"
TickFrequency="1"
ToolTip.Tip="{locale:Locale IconSizeTooltip}"
Value="{Binding GridSizeScale}" />
<CheckBox
Margin="0"
VerticalAlignment="Center"
IsChecked="{Binding ShowNames, Mode=TwoWay}"
IsVisible="{Binding IsGrid}">
<TextBlock Margin="5,3,0,0" Text="{locale:Locale CommonShowNames}" />
</CheckBox>
<TextBox
Name="SearchBox"
MinWidth="200"
Margin="5,0,5,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
DockPanel.Dock="Right"
KeyUp="SearchBox_OnKeyUp"
Text="{Binding SearchText}"
Watermark="{locale:Locale MenuSearch}" />
<ui:DropDownButton
Width="150"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{Binding SortName}"
DockPanel.Dock="Right">
<ui:DropDownButton.Flyout>
<Flyout Placement="Bottom">
<StackPanel
Margin="0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<StackPanel>
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale CommonFavorite}"
GroupName="Sort"
IsChecked="{Binding IsSortedByFavorite, Mode=OneTime}"
Tag="Favorite" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderApplication}"
GroupName="Sort"
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
Tag="Title" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderDeveloper}"
GroupName="Sort"
IsChecked="{Binding IsSortedByDeveloper, Mode=OneTime}"
Tag="Developer" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderTimePlayed}"
GroupName="Sort"
IsChecked="{Binding IsSortedByTimePlayed, Mode=OneTime}"
Tag="TotalTimePlayed" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderLastPlayed}"
GroupName="Sort"
IsChecked="{Binding IsSortedByLastPlayed, Mode=OneTime}"
Tag="LastPlayed" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderFileExtension}"
GroupName="Sort"
IsChecked="{Binding IsSortedByType, Mode=OneTime}"
Tag="FileType" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderFileSize}"
GroupName="Sort"
IsChecked="{Binding IsSortedBySize, Mode=OneTime}"
Tag="FileSize" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderPath}"
GroupName="Sort"
IsChecked="{Binding IsSortedByPath, Mode=OneTime}"
Tag="Path" />
</StackPanel>
<Border
Width="60"
Height="2"
Margin="5"
HorizontalAlignment="Stretch"
BorderBrush="White"
BorderThickness="0,1,0,0">
<Separator Height="0" HorizontalAlignment="Stretch" />
</Border>
<RadioButton
Checked="Order_Checked"
Content="{locale:Locale OrderAscending}"
GroupName="Order"
IsChecked="{Binding IsAscending, Mode=OneTime}"
Tag="Ascending" />
<RadioButton
Checked="Order_Checked"
Content="{locale:Locale OrderDescending}"
GroupName="Order"
IsChecked="{Binding !IsAscending, Mode=OneTime}"
Tag="Descending" />
</StackPanel>
</Flyout>
</ui:DropDownButton.Flyout>
</ui:DropDownButton>
<TextBlock
Margin="10,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
DockPanel.Dock="Right"
Text="{locale:Locale CommonSort}" />
</DockPanel>
</UserControl>

View File

@@ -0,0 +1,54 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using System;
namespace Ryujinx.Ava.UI.Views.Main
{
public partial class MainViewControls : UserControl
{
public MainWindowViewModel ViewModel;
public MainViewControls()
{
InitializeComponent();
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if (VisualRoot is MainWindow window)
{
ViewModel = window.ViewModel;
}
DataContext = ViewModel;
}
public void Sort_Checked(object sender, RoutedEventArgs args)
{
if (sender is RadioButton button)
{
ViewModel.Sort(Enum.Parse<ApplicationSort>(button.Tag.ToString()));
}
}
public void Order_Checked(object sender, RoutedEventArgs args)
{
if (sender is RadioButton button)
{
ViewModel.Sort(button.Tag.ToString() != "Descending");
}
}
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
{
ViewModel.SearchText = SearchBox.Text;
}
}
}

View File

@@ -0,0 +1,81 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsAudioView"
xmlns="https://github.com/avaloniaui"
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="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<ScrollViewer
Name="AudioPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabAudio}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemAudioBackend}"
ToolTip.Tip="{locale:Locale AudioBackendTooltip}"
Width="250" />
<ComboBox SelectedIndex="{Binding AudioBackend}"
Width="350"
HorizontalContentAlignment="Left">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendDummy}" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsOpenAlEnabled}">
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendOpenAL}" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsSoundIoEnabled}">
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSoundIO}" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsSDL2Enabled}">
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSDL2}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemAudioVolume}"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Width="250" />
<ui:NumberBox Value="{Binding Volume}"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Width="350"
SmallChange="1"
LargeChange="10"
SimpleNumberFormat="F0"
SpinButtonPlacementMode="Inline"
Minimum="0"
Maximum="100" />
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<Slider Value="{Binding Volume}"
Margin="250,0,0,0"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Minimum="0"
Maximum="100"
SmallChange="5"
TickFrequency="5"
IsSnapToTickEnabled="True"
LargeChange="10"
Width="350" />
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsAudioView : UserControl
{
public SettingsAudioView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,72 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsCPUView"
xmlns="https://github.com/avaloniaui"
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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<ScrollViewer
Name="CpuPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuCache}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<CheckBox IsChecked="{Binding EnablePptc}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnablePptc}"
ToolTip.Tip="{locale:Locale PptcToggleTooltip}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuMemory}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemMemoryManagerMode}"
ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
Width="250" />
<ComboBox SelectedIndex="{Binding MemoryMode}"
ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<ComboBoxItem
ToolTip.Tip="{locale:Locale MemoryManagerSoftwareTooltip}">
<TextBlock
Text="{locale:Locale SettingsTabSystemMemoryManagerModeSoftware}" />
</ComboBoxItem>
<ComboBoxItem
ToolTip.Tip="{locale:Locale MemoryManagerHostTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemMemoryManagerModeHost}" />
</ComboBoxItem>
<ComboBoxItem
ToolTip.Tip="{locale:Locale MemoryManagerUnsafeTooltip}">
<TextBlock
Text="{locale:Locale SettingsTabSystemMemoryManagerModeHostUnchecked}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsCPUView : UserControl
{
public SettingsCPUView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,218 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsGraphicsView"
xmlns="https://github.com/avaloniaui"
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="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<ScrollViewer
Name="GraphicsPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsAPI}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
Text="{locale:Locale SettingsTabGraphicsBackend}"
Width="250" />
<ComboBox Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
SelectedIndex="{Binding GraphicsBackendIndex}">
<ComboBoxItem IsVisible="{Binding IsVulkanAvailable}">
<TextBlock Text="Vulkan" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsOpenGLAvailable}">
<TextBlock Text="OpenGL" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" IsVisible="{Binding IsVulkanSelected}">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
Text="{locale:Locale SettingsTabGraphicsPreferredGpu}"
Width="250" />
<ComboBox Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
SelectedIndex="{Binding PreferredGpuIndex}"
Items="{Binding AvailableGpus}"/>
</StackPanel>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsFeatures}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableShaderCache}"
ToolTip.Tip="{locale:Locale ShaderCacheToggleTooltip}">
<TextBlock Text="{locale:Locale SettingsTabGraphicsEnableShaderCache}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableTextureRecompression}"
ToolTip.Tip="{locale:Locale SettingsEnableTextureRecompressionTooltip}">
<TextBlock Text="{locale:Locale SettingsEnableTextureRecompression}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableMacroHLE}"
ToolTip.Tip="{locale:Locale SettingsEnableMacroHLETooltip}">
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
</CheckBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}"
Text="{locale:Locale SettingsTabGraphicsResolutionScale}"
Width="250" />
<ComboBox SelectedIndex="{Binding ResolutionScale}"
Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleCustom}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleNative}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale2x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale3x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale4x}" />
</ComboBoxItem>
</ComboBox>
<ui:NumberBox
Margin="10,0,0,0"
ToolTip.Tip="{locale:Locale ResolutionScaleEntryTooltip}"
MinWidth="150"
SmallChange="0.1"
LargeChange="1"
SimpleNumberFormat="F2"
SpinButtonPlacementMode="Inline"
IsVisible="{Binding IsCustomResolutionScaleActive}"
Maximum="100"
Minimum="0.1"
Value="{Binding CustomResolutionScale}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale AnisotropyTooltip}"
Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering}"
Width="250" />
<ComboBox SelectedIndex="{Binding MaxAnisotropy}"
Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale AnisotropyTooltip}">
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabGraphicsAnisotropicFilteringAuto}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering2x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering4x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering8x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering16x}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale AspectRatioTooltip}"
Text="{locale:Locale SettingsTabGraphicsAspectRatio}"
Width="250" />
<ComboBox SelectedIndex="{Binding AspectRatio}"
Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale AspectRatioTooltip}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio4x3}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x9}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x10}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio21x9}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio32x9}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatioStretch}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale GraphicsBackendThreadingTooltip}"
Text="{locale:Locale SettingsTabGraphicsBackendMultithreading}"
Width="250" />
<ComboBox Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale GalThreadingTooltip}"
SelectedIndex="{Binding GraphicsBackendMultithreadingIndex}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale CommonAuto}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale CommonOff}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale CommonOn}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsDeveloperOptions}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}"
Text="{locale:Locale SettingsTabGraphicsShaderDumpPath}"
Width="250" />
<TextBox Text="{Binding ShaderDumpPath}"
Width="350"
ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}" />
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsGraphicsView : UserControl
{
public SettingsGraphicsView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,104 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView"
xmlns="https://github.com/avaloniaui"
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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel"
Focusable="True">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<UserControl.Resources>
<helpers:KeyValueConverter x:Key="Key" />
</UserControl.Resources>
<ScrollViewer
Name="HotkeysPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel Margin="10" Orientation="Vertical" Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ToggleVsync, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.Screenshot, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ShowUi, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.Pause, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ToggleMute, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ResScaleUp, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ResScaleDown, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.VolumeUp, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.VolumeDown, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,81 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Input;
using Ryujinx.Input.Assigner;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsHotkeysView : UserControl
{
private ButtonKeyAssigner _currentAssigner;
private IGamepadDriver AvaloniaKeyboardDriver;
public SettingsHotkeysView()
{
InitializeComponent();
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
}
private void MouseClick(object sender, PointerPressedEventArgs e)
{
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
_currentAssigner?.Cancel(shouldUnbind);
PointerPressed -= MouseClick;
}
private void Button_Checked(object sender, RoutedEventArgs e)
{
if (sender is ToggleButton button)
{
if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
{
return;
}
if (_currentAssigner == null && button.IsChecked != null && (bool)button.IsChecked)
{
_currentAssigner = new ButtonKeyAssigner(button);
FocusManager.Instance?.Focus(this, NavigationMethod.Pointer);
PointerPressed += MouseClick;
var keyboard = (IKeyboard)AvaloniaKeyboardDriver.GetGamepad(AvaloniaKeyboardDriver.GamepadsIds[0]);
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
_currentAssigner.GetInputAndAssign(assigner);
}
else
{
if (_currentAssigner != null)
{
ToggleButton oldButton = _currentAssigner.ToggledButton;
_currentAssigner.Cancel();
_currentAssigner = null;
button.IsChecked = false;
}
}
}
}
private void Button_Unchecked(object sender, RoutedEventArgs e)
{
_currentAssigner?.Cancel();
_currentAssigner = null;
}
public void Dispose()
{
_currentAssigner?.Cancel();
_currentAssigner = null;
}
}
}

View File

@@ -0,0 +1,46 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsInputView"
xmlns="https://github.com/avaloniaui"
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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<ScrollViewer
Name="InputPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel Margin="4" Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<CheckBox Margin="5,0"
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
IsChecked="{Binding EnableDockedMode}">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
</CheckBox>
<CheckBox Margin="5,0"
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
IsChecked="{Binding EnableKeyboard}">
<TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
</CheckBox>
<CheckBox Margin="5,0"
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
IsChecked="{Binding EnableMouse}">
<TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
</CheckBox>
</StackPanel>
<window:ControllerSettingsWindow Name="ControllerSettings" Margin="0" MinHeight="600" />
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,17 @@
using Avalonia.Controls;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsInputView : UserControl
{
public SettingsInputView()
{
InitializeComponent();
}
public void Dispose()
{
ControllerSettings.Dispose();
}
}
}

View File

@@ -0,0 +1,118 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsLoggingView"
xmlns="https://github.com/avaloniaui"
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="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<ScrollViewer
Name="LoggingPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingLogging}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableFileLog}"
ToolTip.Tip="{locale:Locale FileLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableLoggingToFile}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableStub}"
ToolTip.Tip="{locale:Locale StubLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableStubLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableInfo}"
ToolTip.Tip="{locale:Locale InfoLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableInfoLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableWarn}"
ToolTip.Tip="{locale:Locale WarnLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableWarningLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableError}"
ToolTip.Tip="{locale:Locale ErrorLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableErrorLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableTrace}"
ToolTip.Tip="{locale:Locale TraceLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableTraceLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableGuest}"
ToolTip.Tip="{locale:Locale GuestLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableGuestLogs}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<StackPanel Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableDebug}"
ToolTip.Tip="{locale:Locale DebugLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableDebugLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableFsAccessLog}"
ToolTip.Tip="{locale:Locale FileAccessLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableFsAccessLogs}" />
</CheckBox>
<StackPanel Margin="0,10,0,0" Orientation="Horizontal" VerticalAlignment="Stretch">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale FSAccessLogModeTooltip}"
Text="{locale:Locale SettingsTabLoggingFsGlobalAccessLogMode}"
Width="285" />
<ui:NumberBox
Maximum="3"
Minimum="0"
Width="150"
SpinButtonPlacementMode="Inline"
SmallChange="1"
LargeChange="1"
Value="{Binding FsGlobalAccessLogMode}" />
</StackPanel>
<StackPanel Margin="0,10,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevel}"
ToolTip.Tip="{locale:Locale OpenGlLogLevel}"
Width="285" />
<ComboBox SelectedIndex="{Binding OpenglDebugLevel}"
Width="150"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale OpenGlLogLevel}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelNone}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelError}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelPerformance}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelAll}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsLoggingView : UserControl
{
public SettingsLoggingView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,35 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsNetworkView"
xmlns="https://github.com/avaloniaui"
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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<ScrollViewer
Name="NetworkPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabNetworkConnection}" />
<CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
</CheckBox>
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsNetworkView : UserControl
{
public SettingsNetworkView()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,195 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsSystemView"
xmlns="https://github.com/avaloniaui"
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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<ScrollViewer
Name="SystemPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemRegion}"
Width="250" />
<ComboBox SelectedIndex="{Binding Region}"
ToolTip.Tip="{locale:Locale RegionTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionUSA}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionEurope}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionAustralia}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionChina}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionKorea}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionTaiwan}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemLanguage}"
ToolTip.Tip="{locale:Locale LanguageTooltip}"
Width="250" />
<ComboBox SelectedIndex="{Binding Language}"
ToolTip.Tip="{locale:Locale LanguageTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageFrench}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageGerman}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageItalian}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageSpanish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageKorean}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageDutch}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguagePortuguese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageRussian}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTaiwanese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemTimeZone}"
ToolTip.Tip="{locale:Locale TimezoneTooltip}"
Width="250" />
<AutoCompleteBox
Name="TimeZoneBox"
Width="350"
MaxDropDownHeight="500"
FilterMode="Contains"
Items="{Binding TimeZones}"
SelectionChanged="TimeZoneBox_OnSelectionChanged"
Text="{Binding Path=TimeZone, Mode=OneWay}"
TextChanged="TimeZoneBox_OnTextChanged"
ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemTime}"
ToolTip.Tip="{locale:Locale TimeTooltip}"
Width="250"/>
<DatePicker VerticalAlignment="Center" SelectedDate="{Binding DateOffset}"
ToolTip.Tip="{locale:Locale TimeTooltip}"
Width="350" />
</StackPanel>
<StackPanel Margin="250,0,0,10" Orientation="Horizontal">
<TimePicker
VerticalAlignment="Center"
ClockIdentifier="24HourClock"
SelectedTime="{Binding TimeOffset}"
Width="350"
ToolTip.Tip="{locale:Locale TimeTooltip}" />
</StackPanel>
<CheckBox IsChecked="{Binding EnableVsync}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}"
ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}"
ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<StackPanel Orientation="Horizontal">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" />
<TextBlock Text="{locale:Locale SettingsTabSystemHacksNote}" />
</StackPanel>
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<CheckBox IsChecked="{Binding ExpandDramSize}"
ToolTip.Tip="{locale:Locale DRamTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" />
</CheckBox>
<CheckBox IsChecked="{Binding IgnoreMissingServices}"
ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" />
</CheckBox>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,52 @@
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Ryujinx.Ava.UI.ViewModels;
using System;
using System.Linq;
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsSystemView : UserControl
{
public SettingsViewModel ViewModel;
public SettingsSystemView()
{
InitializeComponent();
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim());
MultiBinding tzMultiBinding = new() { Converter = converter };
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
tzMultiBinding.Bindings.Add(new Binding("Location"));
tzMultiBinding.Bindings.Add(new Binding("Abbreviation"));
TimeZoneBox.ValueMemberBinding = tzMultiBinding;
}
private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems != null && e.AddedItems.Count > 0)
{
if (e.AddedItems[0] is TimeZone timeZone)
{
e.Handled = true;
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
}
}
}
private void TimeZoneBox_OnTextChanged(object sender, EventArgs e)
{
if (sender is AutoCompleteBox box && box.SelectedItem is TimeZone timeZone)
{
{
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
}
}
}
}
}

View File

@@ -0,0 +1,156 @@
<UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsUIView"
xmlns="https://github.com/avaloniaui"
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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel">
<Design.DataContext>
<viewModels:SettingsViewModel />
</Design.DataContext>
<ScrollViewer
Name="UiPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGeneral}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableDiscordIntegration}">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ToggleDiscordTooltip}"
Text="{locale:Locale SettingsTabGeneralEnableDiscordRichPresence}" />
</CheckBox>
<CheckBox IsChecked="{Binding CheckUpdatesOnStart}">
<TextBlock Text="{locale:Locale SettingsTabGeneralCheckUpdatesOnLaunch}" />
</CheckBox>
<CheckBox IsChecked="{Binding ShowConfirmExit}">
<TextBlock Text="{locale:Locale SettingsTabGeneralShowConfirmExitDialog}" />
</CheckBox>
<CheckBox IsChecked="{Binding HideCursorOnIdle}">
<TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorOnIdle}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGameDirectories}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<ListBox
Name="GameList"
MinHeight="230"
Items="{Binding GameDirectories}">
<ListBox.Styles>
<Style Selector="ListBoxItem">
<Setter Property="Padding" Value="10" />
<Setter Property="Background" Value="{DynamicResource ListBoxBackground}" />
</Style>
</ListBox.Styles>
</ListBox>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox
Name="PathBox"
Margin="0"
ToolTip.Tip="{locale:Locale AddGameDirBoxTooltip}"
VerticalAlignment="Stretch" />
<Button
Name="AddButton"
Grid.Column="1"
MinWidth="90"
Margin="10,0,0,0"
ToolTip.Tip="{locale:Locale AddGameDirTooltip}"
Click="AddButton_OnClick">
<TextBlock HorizontalAlignment="Center"
Text="{locale:Locale SettingsTabGeneralAdd}" />
</Button>
<Button
Name="RemoveButton"
Grid.Column="2"
MinWidth="90"
Margin="10,0,0,0"
ToolTip.Tip="{locale:Locale RemoveGameDirTooltip}"
Click="RemoveButton_OnClick">
<TextBlock HorizontalAlignment="Center"
Text="{locale:Locale SettingsTabGeneralRemove}" />
</Button>
</Grid>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralTheme}" />
<Grid Margin="10,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<CheckBox
IsChecked="{Binding EnableCustomTheme}"
ToolTip.Tip="{locale:Locale CustomThemeCheckTooltip}">
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeEnableCustomTheme}" />
</CheckBox>
<TextBlock
Grid.Column="0"
Grid.Row="1"
VerticalAlignment="Center"
Margin="0,10,0,0"
Text="{locale:Locale SettingsTabGeneralThemeCustomTheme}"
ToolTip.Tip="{locale:Locale CustomThemePathTooltip}" />
<TextBox
Grid.Row="1"
Grid.Column="1"
Margin="0,10,0,0"
Text="{Binding CustomThemePath}" />
<Button
Grid.Row="1"
Grid.Column="2"
Margin="10,10,0,0"
Click="BrowseTheme"
ToolTip.Tip="{locale:Locale CustomThemeBrowseTooltip}"
Content="{locale:Locale ButtonBrowse}" />
<TextBlock
Grid.Column="0"
Grid.Row="2"
VerticalAlignment="Center"
Margin="0,10,0,0"
Text="{locale:Locale SettingsTabGeneralThemeBaseStyle}" />
<ComboBox
Grid.Column="1"
Grid.Row="2"
VerticalAlignment="Center"
Margin="0,10,0,0"
MinWidth="100"
SelectedIndex="{Binding BaseStyleIndex}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleLight}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleDark}" />
</ComboBoxItem>
</ComboBox>
</Grid>
</StackPanel>
</Border>
</ScrollViewer>
</UserControl>

View File

@@ -0,0 +1,82 @@
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.ViewModels;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Ryujinx.Ava.UI.Views.Settings
{
public partial class SettingsUIView : UserControl
{
public SettingsViewModel ViewModel;
public SettingsUIView()
{
InitializeComponent();
}
private async void AddButton_OnClick(object sender, RoutedEventArgs e)
{
string path = PathBox.Text;
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.GameDirectories.Contains(path))
{
ViewModel.GameDirectories.Add(path);
ViewModel.DirectoryChanged = true;
}
else
{
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
path = await new OpenFolderDialog().ShowAsync(desktop.MainWindow);
if (!string.IsNullOrWhiteSpace(path))
{
ViewModel.GameDirectories.Add(path);
ViewModel.DirectoryChanged = true;
}
}
}
}
private void RemoveButton_OnClick(object sender, RoutedEventArgs e)
{
int oldIndex = GameList.SelectedIndex;
foreach (string path in new List<string>(GameList.SelectedItems.Cast<string>()))
{
ViewModel.GameDirectories.Remove(path);
ViewModel.DirectoryChanged = true;
}
if (GameList.ItemCount > 0)
{
GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0;
}
}
public async void BrowseTheme(object sender, RoutedEventArgs e)
{
var dialog = new OpenFileDialog()
{
Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle],
AllowMultiple = false
};
dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] });
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var file = await dialog.ShowAsync(desktop.MainWindow);
if (file != null && file.Length > 0)
{
ViewModel.CustomThemePath = file[0];
}
}
}
}
}

View File

@@ -1,282 +1,247 @@
<window:StyleableWindow <UserControl
x:Class="Ryujinx.Ava.UI.Windows.AboutWindow" x:Class="Ryujinx.Ava.UI.Windows.AboutWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
Width="850" xmlns:viewModel="clr-namespace:Ryujinx.Ava.UI.ViewModels"
Height="550" Width="550"
MinWidth="500" Height="260"
MinHeight="550" Margin="0,-12,0,0"
d:DesignHeight="350" d:DesignHeight="260"
d:DesignWidth="400" d:DesignWidth="550"
CanResize="False" x:CompileBindings="True"
SizeToContent="Width" x:DataType="viewModel:AboutWindowViewModel"
WindowStartupLocation="CenterOwner" Focusable="True"
mc:Ignorable="d" mc:Ignorable="d">
Focusable="True"> <Design.DataContext>
<Grid <viewModel:AboutWindowViewModel />
Margin="15" </Design.DataContext>
HorizontalAlignment="Stretch" <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
VerticalAlignment="Stretch"> <Grid.ColumnDefinitions>
<Grid.RowDefinitions> <ColumnDefinition Width="Auto" />
<RowDefinition Height="Auto" /> <ColumnDefinition Width="Auto" />
<RowDefinition Height="*" /> <ColumnDefinition Width="*" />
</Grid.RowDefinitions> </Grid.ColumnDefinitions>
<Grid.ColumnDefinitions> <Grid
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid
Grid.Row="1"
Grid.Column="0" Grid.Column="0"
Margin="20"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition /> <RowDefinition Height="Auto" />
<RowDefinition /> </Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <StackPanel
</Grid.RowDefinitions> Grid.Row="0"
<Grid Grid.Row="0"> HorizontalAlignment="Stretch"
<Grid.ColumnDefinitions> VerticalAlignment="Stretch"
<ColumnDefinition /> Spacing="10">
<ColumnDefinition /> <Grid>
</Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<Grid.RowDefinitions> <ColumnDefinition Width="Auto" />
<RowDefinition Height="Auto" /> <ColumnDefinition Width="*" />
<RowDefinition Height="Auto" /> <ColumnDefinition Width="Auto" />
<RowDefinition Height="Auto" /> </Grid.ColumnDefinitions>
</Grid.RowDefinitions> <Image
<Image Grid.Column="0"
Grid.Row="0" Height="80"
Grid.RowSpan="3" Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
Grid.Column="0" <flex:FlexPanel
Height="110" Grid.Column="2"
MinWidth="50" HorizontalAlignment="Stretch"
Margin="5,10,20,10" VerticalAlignment="Stretch"
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" /> Direction="Column"
<TextBlock JustifyContent="SpaceAround"
Grid.Row="0" RowSpacing="2">
Grid.Column="1" <TextBlock
Margin="0,20,0,0" FontSize="28"
FontSize="35" FontWeight="Bold"
Text="Ryujinx" Text="Ryujinx"
TextAlignment="Center" /> TextAlignment="Left" />
<TextBlock <TextBlock Text="(REE-YOU-JINX)" TextAlignment="Left" />
Grid.Row="1" </flex:FlexPanel>
Grid.Column="1" </Grid>
Margin="0,0,0,0" <TextBlock
FontSize="16"
Text="(REE-YOU-JINX)"
TextAlignment="Center" />
<Button
Grid.Row="2"
Grid.Column="1"
Margin="0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="10"
LineHeight="12"
Text="{Binding Version}"
TextAlignment="Center" />
</StackPanel>
<StackPanel
Grid.Row="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Spacing="10">
<TextBlock
Width="200"
HorizontalAlignment="Center"
FontSize="10"
LineHeight="12"
Text="{locale:Locale AboutDisclaimerMessage}"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBlock
Name="AmiiboLabel"
Width="200"
HorizontalAlignment="Center"
FontSize="10"
LineHeight="12"
PointerPressed="AmiiboLabel_OnPointerPressed"
Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
TextAlignment="Center"
TextWrapping="Wrap" />
<StackPanel
HorizontalAlignment="Center"
Orientation="Horizontal"
Spacing="10">
<Button
MinWidth="30"
MinHeight="30"
MaxWidth="30"
MaxHeight="30"
Padding="8"
Background="Transparent"
Click="Button_OnClick"
CornerRadius="15"
Tag="https://www.patreon.com/ryujinx"
ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
<Image Source="{Binding PatreonLogo}" />
</Button>
<Button
MinWidth="30"
MinHeight="30"
MaxWidth="30"
MaxHeight="30"
Padding="8"
Background="Transparent"
Click="Button_OnClick"
CornerRadius="15"
Tag="https://github.com/Ryujinx/Ryujinx"
ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
<Image Source="{Binding GithubLogo}" />
</Button>
<Button
MinWidth="30"
MinHeight="30"
MaxWidth="30"
MaxHeight="30"
Padding="8"
Background="Transparent"
Click="Button_OnClick"
CornerRadius="15"
Tag="https://discordapp.com/invite/N2FmfVc"
ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
<Image Source="{Binding DiscordLogo}" />
</Button>
<Button
MinWidth="30"
MinHeight="30"
MaxWidth="30"
MaxHeight="30"
Padding="8"
Background="Transparent"
Click="Button_OnClick"
CornerRadius="15"
Tag="https://twitter.com/RyujinxEmu"
ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
<Image Source="{Binding TwitterLogo}" />
</Button>
<Button
MinWidth="30"
MinHeight="30"
MaxWidth="30"
MaxHeight="30"
Padding="8"
Background="Transparent"
Click="Button_OnClick"
CornerRadius="15"
Tag="https://www.ryujinx.org"
ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}">
<ui:SymbolIcon Foreground="{DynamicResource ThemeForegroundColor}" Symbol="Link" />
</Button>
</StackPanel>
</StackPanel>
</Grid>
<Border
Grid.Column="1"
Width="1"
Margin="20,0"
VerticalAlignment="Stretch"
BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1,0,0,0" />
<Grid
Grid.Column="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel
Grid.Row="0"
Margin="0,10,0,0"
Spacing="2">
<TextBlock
FontSize="15"
FontWeight="Bold"
Text="{locale:Locale AboutRyujinxAboutTitle}" />
<TextBlock
FontSize="10"
Text="{locale:Locale AboutRyujinxAboutContent}"
TextWrapping="Wrap" />
</StackPanel>
<StackPanel
Grid.Row="1"
Margin="0,10,0,0"
Spacing="2">
<TextBlock
FontSize="15"
FontWeight="Bold"
Text="{locale:Locale AboutRyujinxMaintainersTitle}" />
<TextBlock
FontSize="10"
Text="{Binding Developers}"
TextWrapping="Wrap" />
<Button
Padding="5"
HorizontalAlignment="Left"
Background="Transparent" Background="Transparent"
Click="Button_OnClick" Click="Button_OnClick"
Tag="https://www.ryujinx.org/"> Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a">
<TextBlock <TextBlock
Text="www.ryujinx.org" FontSize="10"
TextAlignment="Center" Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
TextDecorations="Underline" TextAlignment="Right"
ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}" /> ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
</Button> </Button>
</Grid> </StackPanel>
<TextBlock <StackPanel
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Version}"
TextAlignment="Center" />
<TextBlock
Grid.Row="2"
Margin="20"
HorizontalAlignment="Center"
MaxLines="2"
Text="{locale:Locale AboutDisclaimerMessage}"
TextAlignment="Center" />
<TextBlock
Name="AmiiboLabel"
Grid.Row="3"
Margin="20"
HorizontalAlignment="Center"
MaxLines="2"
PointerPressed="AmiiboLabel_OnPointerPressed"
Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
TextAlignment="Center" />
<StackPanel
Grid.Row="4"
HorizontalAlignment="Center"
Orientation="Horizontal"
Spacing="10">
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
<Button
Height="65"
Background="Transparent"
Click="Button_OnClick"
Tag="https://www.patreon.com/ryujinx">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Patreon.png?assembly=Ryujinx.Ui.Common" />
<TextBlock
Grid.Row="1"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Text="Patreon" />
</Grid>
</Button>
</StackPanel>
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
<Button
Height="65"
Background="Transparent"
Click="Button_OnClick"
Tag="https://github.com/Ryujinx/Ryujinx">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_GitHub.png?assembly=Ryujinx.Ui.Common" />
<TextBlock
Grid.Row="1"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Text="GitHub" />
</Grid>
</Button>
</StackPanel>
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
<Button
Height="65"
Background="Transparent"
Click="Button_OnClick"
Tag="https://discordapp.com/invite/N2FmfVc">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Discord.png?assembly=Ryujinx.Ui.Common" />
<TextBlock
Grid.Row="1"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Text="Discord" />
</Grid>
</Button>
</StackPanel>
<StackPanel Orientation="Vertical" ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
<Button
Height="65"
Background="Transparent"
Click="Button_OnClick"
Tag="https://twitter.com/RyujinxEmu">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image Source="resm:Ryujinx.Ui.Common.Resources.Logo_Twitter.png?assembly=Ryujinx.Ui.Common" />
<TextBlock
Grid.Row="1"
Margin="0,5,0,0"
HorizontalAlignment="Center"
Text="Twitter" />
</Grid>
</Button>
</StackPanel>
</StackPanel>
</Grid>
<Border
Grid.Row="1"
Grid.Column="1"
Width="2"
Margin="5"
VerticalAlignment="Stretch"
BorderBrush="White"
BorderThickness="1,0,0,0">
<Separator Width="0" />
</Border>
<Grid
Grid.Row="1"
Grid.Column="2"
Margin="20"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock
FontWeight="Bold"
Text="{locale:Locale AboutRyujinxAboutTitle}"
TextDecorations="Underline" />
<TextBlock
Grid.Row="1"
Margin="20,5,5,5"
LineHeight="20"
Text="{locale:Locale AboutRyujinxAboutContent}" />
<TextBlock
Grid.Row="2" Grid.Row="2"
Margin="0,10,0,0" Margin="0,10,0,0"
FontWeight="Bold" Spacing="2">
Text="{locale:Locale AboutRyujinxMaintainersTitle}" <TextBlock
TextDecorations="Underline" /> FontSize="15"
<TextBlock FontWeight="Bold"
Grid.Row="3" Text="{locale:Locale AboutRyujinxSupprtersTitle}" />
Margin="20,5,5,5" <ScrollViewer
LineHeight="20" Height="70"
Text="{Binding Developers}" /> HorizontalScrollBarVisibility="Disabled"
<Button VerticalScrollBarVisibility="Visible">
Grid.Row="4" <TextBlock
HorizontalAlignment="Right" Name="SupportersTextBlock"
Background="Transparent" VerticalAlignment="Top"
Click="Button_OnClick" FontSize="10"
Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a"> Text="{Binding Supporters}"
<TextBlock TextWrapping="Wrap" />
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}" </ScrollViewer>
TextAlignment="Right" </StackPanel>
TextDecorations="Underline" </Grid>
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" /> </Grid>
</Button> </UserControl>
<TextBlock
Grid.Row="5"
Margin="0,0,0,0"
FontWeight="Bold"
Text="{locale:Locale AboutRyujinxSupprtersTitle}"
TextDecorations="Underline" />
<Border
Grid.Row="6"
Width="460"
Height="200"
Margin="20,5"
Padding="5"
VerticalAlignment="Stretch"
BorderBrush="White"
BorderThickness="1">
<TextBlock
Name="SupportersTextBlock"
VerticalAlignment="Top"
Text="{Binding Supporters}"
TextWrapping="Wrap" />
</Border>
</Grid>
</Grid>
</window:StyleableWindow>

View File

@@ -1,38 +1,47 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Threading; using Avalonia.Styling;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Utilities; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Threading.Tasks; using System.Threading.Tasks;
using Button = Avalonia.Controls.Button;
namespace Ryujinx.Ava.UI.Windows namespace Ryujinx.Ava.UI.Windows
{ {
public partial class AboutWindow : StyleableWindow public partial class AboutWindow : UserControl
{ {
public AboutWindow() public AboutWindow()
{ {
if (Program.PreviewerDetached) DataContext = new AboutWindowViewModel();
{
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.MenuBarHelpAbout];
}
Version = Program.Version;
DataContext = this;
InitializeComponent(); InitializeComponent();
_ = DownloadPatronsJson();
} }
public string Supporters { get; set; } public static async Task Show()
public string Version { get; set; } {
ContentDialog contentDialog = new()
{
PrimaryButtonText = "",
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
Content = new AboutWindow()
};
public string Developers => string.Format(LocaleManager.Instance[LocaleKeys.AboutPageDeveloperListMore], "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD«"); Style closeButton = new(x => x.Name("CloseButton"));
closeButton.Setters.Add(new Setter(WidthProperty, 80d));
Style closeButtonParent = new(x => x.Name("CommandSpace"));
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, Avalonia.Layout.HorizontalAlignment.Right));
contentDialog.Styles.Add(closeButton);
contentDialog.Styles.Add(closeButtonParent);
await ContentDialogHelper.ShowAsync(contentDialog);
}
private void Button_OnClick(object sender, RoutedEventArgs e) private void Button_OnClick(object sender, RoutedEventArgs e)
{ {
@@ -42,31 +51,6 @@ namespace Ryujinx.Ava.UI.Windows
} }
} }
private async Task DownloadPatronsJson()
{
if (!NetworkInterface.GetIsNetworkAvailable())
{
Supporters = LocaleManager.Instance[LocaleKeys.ConnectionError];
return;
}
HttpClient httpClient = new();
try
{
string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/");
Supporters = string.Join(", ", JsonHelper.Deserialize<string[]>(patreonJsonString));
}
catch
{
Supporters = LocaleManager.Instance[LocaleKeys.ApiError];
}
await Dispatcher.UIThread.InvokeAsync(() => SupportersTextBlock.Text = Supporters);
}
private void AmiiboLabel_OnPointerPressed(object sender, PointerPressedEventArgs e) private void AmiiboLabel_OnPointerPressed(object sender, PointerPressedEventArgs e)
{ {
if (sender is TextBlock) if (sender is TextBlock)

View File

@@ -2,15 +2,16 @@
x:Class="Ryujinx.Ava.UI.Windows.MainWindow" x:Class="Ryujinx.Ava.UI.Windows.MainWindow"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows" xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
Title="Ryujinx" xmlns:controls="clr-namespace:Ryujinx.Ava.UI.Controls"
xmlns:main="clr-namespace:Ryujinx.Ava.UI.Views.Main"
Cursor="{Binding Cursor}"
Title="{Binding Title}"
WindowState="{Binding WindowState}"
Width="1280" Width="1280"
Height="777" Height="777"
MinWidth="1092" MinWidth="1092"
@@ -66,206 +67,8 @@
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
IsVisible="{Binding ShowMenuAndStatusBar}" IsVisible="{Binding ShowMenuAndStatusBar}"
Orientation="Vertical"> Orientation="Vertical">
<DockPanel HorizontalAlignment="Stretch"> <main:MainMenuBarView
<Menu Name="MenuBarView" />
Name="Menu"
Height="35"
Margin="0"
HorizontalAlignment="Left">
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel Margin="0" HorizontalAlignment="Stretch" />
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarFile}">
<MenuItem
Command="{ReflectionBinding OpenFile}"
Header="{locale:Locale MenuBarFileOpenFromFile}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale LoadApplicationFileTooltip}" />
<MenuItem
Command="{ReflectionBinding OpenFolder}"
Header="{locale:Locale MenuBarFileOpenUnpacked}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale LoadApplicationFolderTooltip}" />
<MenuItem Header="{locale:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}">
<MenuItem
Command="{ReflectionBinding OpenMiiApplet}"
Header="Mii Edit Applet"
ToolTip.Tip="{locale:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
</MenuItem>
<Separator />
<MenuItem
Command="{ReflectionBinding OpenRyujinxFolder}"
Header="{locale:Locale MenuBarFileOpenEmuFolder}"
ToolTip.Tip="{locale:Locale OpenRyujinxFolderTooltip}" />
<MenuItem
Command="{ReflectionBinding OpenLogsFolder}"
Header="{locale:Locale MenuBarFileOpenLogsFolder}"
ToolTip.Tip="{locale:Locale OpenRyujinxLogsTooltip}" />
<Separator />
<MenuItem
Command="{ReflectionBinding CloseWindow}"
Header="{locale:Locale MenuBarFileExit}"
ToolTip.Tip="{locale:Locale ExitTooltip}" />
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarOptions}">
<MenuItem
Command="{ReflectionBinding ToggleFullscreen}"
Header="{locale:Locale MenuBarOptionsToggleFullscreen}"
InputGesture="F11" />
<MenuItem>
<MenuItem.Icon>
<CheckBox IsChecked="{Binding StartGamesInFullscreen, Mode=TwoWay}"
MinWidth="250">
<TextBlock Text="{locale:Locale MenuBarOptionsStartGamesInFullscreen}"/>
</CheckBox>
</MenuItem.Icon>
</MenuItem>
<MenuItem IsVisible="{Binding ShowConsoleVisible}">
<MenuItem.Icon>
<CheckBox IsChecked="{Binding ShowConsole, Mode=TwoWay}"
MinWidth="250">
<TextBlock Text="{locale:Locale MenuBarOptionsShowConsole}"/>
</CheckBox>
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="{locale:Locale MenuBarOptionsChangeLanguage}">
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="de_DE"
Header="Deutsch" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="en_US"
Header="English (US)" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="es_ES"
Header="Español (ES)" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="fr_FR"
Header="Français" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="it_IT"
Header="Italiano" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="pt_BR"
Header="Português (BR)" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="tr_TR"
Header="Türkçe" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="el_GR"
Header="Ελληνικά" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="pl_PL"
Header="Polski" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ru_RU"
Header="Русский" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="zh_CN"
Header="简体中文" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="zh_TW"
Header="繁體中文" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ja_JP"
Header="日本語" />
<MenuItem
Command="{ReflectionBinding ChangeLanguage}"
CommandParameter="ko_KR"
Header="한국어" />
</MenuItem>
<Separator />
<MenuItem
Command="{ReflectionBinding OpenSettings}"
Header="{locale:Locale MenuBarOptionsSettings}"
ToolTip.Tip="{locale:Locale OpenSettingsTooltip}" />
<MenuItem
Command="{ReflectionBinding ManageProfiles}"
Header="{locale:Locale MenuBarOptionsManageUserProfiles}"
IsEnabled="{Binding EnableNonGameRunningControls}"
ToolTip.Tip="{locale:Locale OpenProfileManagerTooltip}" />
</MenuItem>
<MenuItem
Name="ActionsMenuItem"
VerticalAlignment="Center"
Header="{locale:Locale MenuBarActions}"
IsEnabled="{Binding IsGameRunning}">
<MenuItem
Click="PauseEmulation_Click"
Header="{locale:Locale MenuBarOptionsPauseEmulation}"
InputGesture="{Binding PauseKey}"
IsEnabled="{Binding !IsPaused}"
IsVisible="{Binding !IsPaused}" />
<MenuItem
Click="ResumeEmulation_Click"
Header="{locale:Locale MenuBarOptionsResumeEmulation}"
InputGesture="{Binding PauseKey}"
IsEnabled="{Binding IsPaused}"
IsVisible="{Binding IsPaused}" />
<MenuItem
Click="StopEmulation_Click"
Header="{locale:Locale MenuBarOptionsStopEmulation}"
InputGesture="Escape"
IsEnabled="{Binding IsGameRunning}"
ToolTip.Tip="{locale:Locale StopEmulationTooltip}" />
<MenuItem Command="{ReflectionBinding SimulateWakeUpMessage}" Header="{locale:Locale MenuBarOptionsSimulateWakeUpMessage}" />
<Separator />
<MenuItem
Name="ScanAmiiboMenuItem"
AttachedToVisualTree="ScanAmiiboMenuItem_AttachedToVisualTree"
Command="{ReflectionBinding OpenAmiiboWindow}"
Header="{locale:Locale MenuBarActionsScanAmiibo}"
IsEnabled="{Binding IsAmiiboRequested}" />
<MenuItem
Command="{ReflectionBinding TakeScreenshot}"
Header="{locale:Locale MenuBarFileToolsTakeScreenshot}"
InputGesture="{Binding ScreenshotKey}"
IsEnabled="{Binding IsGameRunning}" />
<MenuItem
Command="{ReflectionBinding HideUi}"
Header="{locale:Locale MenuBarFileToolsHideUi}"
InputGesture="{Binding ShowUiKey}"
IsEnabled="{Binding IsGameRunning}" />
<MenuItem
Command="{ReflectionBinding OpenCheatManagerForCurrentApp}"
Header="{locale:Locale GameListContextMenuManageCheat}"
IsEnabled="{Binding IsGameRunning}" />
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarTools}">
<MenuItem Header="{locale:Locale MenuBarToolsInstallFirmware}" IsEnabled="{Binding EnableNonGameRunningControls}">
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
</MenuItem>
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
<MenuItem
Name="UpdateMenuItem"
Command="{ReflectionBinding CheckForUpdates}"
Header="{locale:Locale MenuBarHelpCheckForUpdates}"
ToolTip.Tip="{locale:Locale CheckUpdatesTooltip}" />
<Separator />
<MenuItem
Command="{ReflectionBinding OpenAboutWindow}"
Header="{locale:Locale MenuBarHelpAbout}"
ToolTip.Tip="{locale:Locale OpenAboutTooltip}" />
</MenuItem>
</Menu>
</DockPanel>
</StackPanel> </StackPanel>
<ContentControl <ContentControl
Name="MainContent" Name="MainContent"
@@ -277,170 +80,14 @@
BorderThickness="0,0,0,0" BorderThickness="0,0,0,0"
DockPanel.Dock="Top" DockPanel.Dock="Top"
IsVisible="{Binding ShowContent}"> IsVisible="{Binding ShowContent}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Name="GameLibrary">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<DockPanel <main:MainViewControls
Grid.Row="0" Name="ViewControls"
Margin="0,0,0,5" Grid.Row="0"/>
HorizontalAlignment="Stretch">
<Button
Width="40"
MinWidth="40"
Margin="5,2,0,2"
VerticalAlignment="Stretch"
Command="{ReflectionBinding SetListMode}"
IsEnabled="{Binding IsGrid}">
<ui:FontIcon
Margin="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
Glyph="{helpers:GlyphValueConverter List}" />
</Button>
<Button
Width="40"
MinWidth="40"
Margin="5,2,5,2"
VerticalAlignment="Stretch"
Command="{ReflectionBinding SetGridMode}"
IsEnabled="{Binding IsList}">
<ui:FontIcon
Margin="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
FontFamily="avares://FluentAvalonia/Fonts#Symbols"
Glyph="{helpers:GlyphValueConverter Grid}" />
</Button>
<TextBlock
Margin="10,0"
VerticalAlignment="Center"
Text="{locale:Locale IconSize}"
ToolTip.Tip="{locale:Locale IconSizeTooltip}" />
<Slider
Width="150"
Height="35"
Margin="5,-10,5,0"
VerticalAlignment="Center"
IsSnapToTickEnabled="True"
Maximum="4"
Minimum="1"
TickFrequency="1"
ToolTip.Tip="{locale:Locale IconSizeTooltip}"
Value="{Binding GridSizeScale}" />
<CheckBox
Margin="0"
VerticalAlignment="Center"
IsChecked="{Binding ShowNames, Mode=TwoWay}"
IsVisible="{Binding IsGrid}">
<TextBlock Margin="5,3,0,0" Text="{locale:Locale CommonShowNames}" />
</CheckBox>
<TextBox
Name="SearchBox"
MinWidth="200"
Margin="5,0,5,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
DockPanel.Dock="Right"
KeyUp="SearchBox_OnKeyUp"
Text="{Binding SearchText}"
Watermark="{locale:Locale MenuSearch}" />
<ui:DropDownButton
Width="150"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Content="{Binding SortName}"
DockPanel.Dock="Right">
<ui:DropDownButton.Flyout>
<Flyout Placement="Bottom">
<StackPanel
Margin="0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<StackPanel>
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale CommonFavorite}"
GroupName="Sort"
IsChecked="{Binding IsSortedByFavorite, Mode=OneTime}"
Tag="Favorite" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderApplication}"
GroupName="Sort"
IsChecked="{Binding IsSortedByTitle, Mode=OneTime}"
Tag="Title" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderDeveloper}"
GroupName="Sort"
IsChecked="{Binding IsSortedByDeveloper, Mode=OneTime}"
Tag="Developer" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderTimePlayed}"
GroupName="Sort"
IsChecked="{Binding IsSortedByTimePlayed, Mode=OneTime}"
Tag="TotalTimePlayed" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderLastPlayed}"
GroupName="Sort"
IsChecked="{Binding IsSortedByLastPlayed, Mode=OneTime}"
Tag="LastPlayed" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderFileExtension}"
GroupName="Sort"
IsChecked="{Binding IsSortedByType, Mode=OneTime}"
Tag="FileType" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderFileSize}"
GroupName="Sort"
IsChecked="{Binding IsSortedBySize, Mode=OneTime}"
Tag="FileSize" />
<RadioButton
Checked="Sort_Checked"
Content="{locale:Locale GameListHeaderPath}"
GroupName="Sort"
IsChecked="{Binding IsSortedByPath, Mode=OneTime}"
Tag="Path" />
</StackPanel>
<Border
Width="60"
Height="2"
Margin="5"
HorizontalAlignment="Stretch"
BorderBrush="White"
BorderThickness="0,1,0,0">
<Separator Height="0" HorizontalAlignment="Stretch" />
</Border>
<RadioButton
Checked="Order_Checked"
Content="{locale:Locale OrderAscending}"
GroupName="Order"
IsChecked="{Binding IsAscending, Mode=OneTime}"
Tag="Ascending" />
<RadioButton
Checked="Order_Checked"
Content="{locale:Locale OrderDescending}"
GroupName="Order"
IsChecked="{Binding !IsAscending, Mode=OneTime}"
Tag="Descending" />
</StackPanel>
</Flyout>
</ui:DropDownButton.Flyout>
</ui:DropDownButton>
<TextBlock
Margin="10,0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
DockPanel.Dock="Right"
Text="{locale:Locale CommonSort}" />
</DockPanel>
<controls:GameListView <controls:GameListView
x:Name="GameList" x:Name="GameList"
Grid.Row="1" Grid.Row="1"
@@ -512,7 +159,8 @@
IsVisible="{Binding ShowLoadProgress}" IsVisible="{Binding ShowLoadProgress}"
Text="{Binding LoadHeading}" Text="{Binding LoadHeading}"
TextAlignment="Left" TextAlignment="Left"
TextWrapping="Wrap" /> TextWrapping="Wrap"
MaxWidth="500" />
<Border <Border
Grid.Row="1" Grid.Row="1"
Margin="10" Margin="10"
@@ -529,7 +177,6 @@
Margin="0" Margin="0"
Padding="0" Padding="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Background="{Binding ProgressBarBackgroundColor}"
ClipToBounds="True" ClipToBounds="True"
CornerRadius="5" CornerRadius="5"
Foreground="{Binding ProgressBarForegroundColor}" Foreground="{Binding ProgressBarForegroundColor}"
@@ -545,226 +192,14 @@
FontSize="18" FontSize="18"
IsVisible="{Binding ShowLoadProgress}" IsVisible="{Binding ShowLoadProgress}"
Text="{Binding CacheLoadStatus}" Text="{Binding CacheLoadStatus}"
TextAlignment="Left" /> TextAlignment="Left"
MaxWidth="500" />
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>
<Grid <main:MainStatusBarView
Name="StatusBar" Name="StatusBarView"
Grid.Row="2" Grid.Row="2" />
Margin="0"
MinHeight="22"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Background="{DynamicResource ThemeContentBackgroundColor}"
DockPanel.Dock="Bottom"
IsVisible="{Binding ShowMenuAndStatusBar}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Column="0"
Margin="5"
VerticalAlignment="Center"
IsVisible="{Binding EnableNonGameRunningControls}">
<Grid Margin="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button
Width="25"
Height="25"
MinWidth="0"
Margin="0,0,5,0"
VerticalAlignment="Center"
Background="Transparent"
Command="{ReflectionBinding LoadApplications}">
<ui:SymbolIcon
Width="50"
Height="100"
Symbol="Refresh" />
</Button>
<TextBlock
Name="LoadStatus"
Grid.Column="1"
Margin="0,0,5,0"
VerticalAlignment="Center"
IsVisible="{Binding EnableNonGameRunningControls}"
Text="{locale:Locale StatusBarGamesLoaded}" />
<ProgressBar
Name="LoadProgressBar"
Grid.Column="2"
Height="6"
VerticalAlignment="Center"
Foreground="{DynamicResource HighlightColor}"
IsVisible="{Binding EnableNonGameRunningControls}"
Maximum="{Binding StatusBarProgressMaximum}"
Value="{Binding StatusBarProgressValue}" />
</Grid>
</StackPanel>
<StackPanel
Grid.Column="1"
Margin="0,2"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding IsGameRunning}"
Orientation="Horizontal">
<TextBlock
Name="VsyncStatus"
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Foreground="{Binding VsyncColor}"
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="VsyncStatus_PointerReleased"
Text="VSync"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Name="DockedStatus"
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="DockedStatus_PointerReleased"
Text="{Binding DockedStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Name="AspectRatioStatus"
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
PointerReleased="AspectRatioStatus_PointerReleased"
Text="{Binding AspectRatioStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<ui:ToggleSplitButton
Name="VolumeStatus"
Padding="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Background="{DynamicResource ThemeContentBackgroundColor}"
BorderThickness="0"
Content="{Binding VolumeStatusText}"
IsChecked="{Binding VolumeMuted}"
IsVisible="{Binding !ShowLoadProgress}">
<ui:ToggleSplitButton.Flyout>
<Flyout Placement="Bottom" ShowMode="TransientWithDismissOnPointerMoveAway">
<Grid Margin="0">
<Slider
MaxHeight="40"
Width="150"
Margin="0"
Padding="0"
IsSnapToTickEnabled="True"
LargeChange="0.05"
Maximum="1"
Minimum="0"
SmallChange="0.01"
TickFrequency="0.05"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Value="{Binding Volume}" />
</Grid>
</Flyout>
</ui:ToggleSplitButton.Flyout>
</ui:ToggleSplitButton>
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding GameStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding FifoStatusText}"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding BackendText}"
TextAlignment="Left" />
<Border
Width="2"
Height="12"
Margin="0"
BorderBrush="Gray"
BorderThickness="1"
IsVisible="{Binding !ShowLoadProgress}" />
<TextBlock
Margin="5,0,5,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
IsVisible="{Binding !ShowLoadProgress}"
Text="{Binding GpuNameText}"
TextAlignment="Left" />
</StackPanel>
<StackPanel
Grid.Column="3"
Margin="0,0,5,0"
VerticalAlignment="Center"
IsVisible="{Binding ShowFirmwareStatus}"
Orientation="Horizontal">
<TextBlock
Name="FirmwareStatus"
Margin="0"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Text="{locale:Locale StatusBarSystemVersion}" />
</StackPanel>
</Grid>
</Grid> </Grid>
</Grid> </Grid>
</window:StyleableWindow> </window:StyleableWindow>

View File

@@ -1,19 +1,14 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common; using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input; using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Applet; using Ryujinx.Ava.UI.Applet;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
@@ -25,11 +20,9 @@ using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common; using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using SixLabors.ImageSharp.PixelFormats;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using InputManager = Ryujinx.Input.HLE.InputManager; using InputManager = Ryujinx.Input.HLE.InputManager;
@@ -38,19 +31,14 @@ namespace Ryujinx.Ava.UI.Windows
public partial class MainWindow : StyleableWindow public partial class MainWindow : StyleableWindow
{ {
internal static MainWindowViewModel MainWindowViewModel { get; private set; } internal static MainWindowViewModel MainWindowViewModel { get; private set; }
private bool _canUpdate;
private bool _isClosing;
private bool _isLoading;
private Control _mainViewContent; private bool _isLoading;
private UserChannelPersistence _userChannelPersistence; private UserChannelPersistence _userChannelPersistence;
private static bool _deferLoad; private static bool _deferLoad;
private static string _launchPath; private static string _launchPath;
private static bool _startFullscreen; private static bool _startFullscreen;
private string _currentEmulatedGamePath;
internal readonly AvaHostUiHandler UiHandler; internal readonly AvaHostUiHandler UiHandler;
private AutoResetEvent _rendererWaitEvent;
public VirtualFileSystem VirtualFileSystem { get; private set; } public VirtualFileSystem VirtualFileSystem { get; private set; }
public ContentManager ContentManager { get; private set; } public ContentManager ContentManager { get; private set; }
@@ -58,30 +46,17 @@ namespace Ryujinx.Ava.UI.Windows
public LibHacHorizonManager LibHacHorizonManager { get; private set; } public LibHacHorizonManager LibHacHorizonManager { get; private set; }
internal AppHost AppHost { get; private set; }
public InputManager InputManager { get; private set; } public InputManager InputManager { get; private set; }
internal RendererHost RendererControl { get; private set; }
internal MainWindowViewModel ViewModel { get; private set; } internal MainWindowViewModel ViewModel { get; private set; }
public SettingsWindow SettingsWindow { get; set; } public SettingsWindow SettingsWindow { get; set; }
public bool CanUpdate
{
get => _canUpdate;
set
{
_canUpdate = value;
Dispatcher.UIThread.InvokeAsync(() => UpdateMenuItem.IsEnabled = _canUpdate);
}
}
public static bool ShowKeyErrorOnLoad { get; set; } public static bool ShowKeyErrorOnLoad { get; set; }
public ApplicationLibrary ApplicationLibrary { get; set; } public ApplicationLibrary ApplicationLibrary { get; set; }
public MainWindow() public MainWindow()
{ {
ViewModel = new MainWindowViewModel(this); ViewModel = new MainWindowViewModel();
MainWindowViewModel = ViewModel; MainWindowViewModel = ViewModel;
@@ -92,10 +67,10 @@ namespace Ryujinx.Ava.UI.Windows
UiHandler = new AvaHostUiHandler(this); UiHandler = new AvaHostUiHandler(this);
Title = $"Ryujinx {Program.Version}"; ViewModel.Title = $"Ryujinx {Program.Version}";
// NOTE: Height of MenuBar and StatusBar is not usable here, since it would still be 0 at this point. // NOTE: Height of MenuBar and StatusBar is not usable here, since it would still be 0 at this point.
double barHeight = MenuBar.MinHeight + StatusBar.MinHeight; double barHeight = MenuBar.MinHeight + StatusBarView.StatusBar.MinHeight;
Height = ((Height - barHeight) / Program.WindowScaleFactor) + barHeight; Height = ((Height - barHeight) / Program.WindowScaleFactor) + barHeight;
Width /= Program.WindowScaleFactor; Width /= Program.WindowScaleFactor;
@@ -103,14 +78,37 @@ namespace Ryujinx.Ava.UI.Windows
{ {
Initialize(); Initialize();
ViewModel.Initialize();
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver()); InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
ViewModel.Initialize(
ContentManager,
ApplicationLibrary,
VirtualFileSystem,
AccountManager,
InputManager,
_userChannelPersistence,
LibHacHorizonManager,
UiHandler,
ShowLoading,
SwitchToGameControl,
SetMainContent,
this);
ViewModel.RefreshFirmwareStatus();
LoadGameList(); LoadGameList();
this.GetObservable(IsActiveProperty).Subscribe(IsActiveChanged);
} }
_rendererWaitEvent = new AutoResetEvent(false); ApplicationLibrary.ApplicationCountUpdated += ApplicationLibrary_ApplicationCountUpdated;
ApplicationLibrary.ApplicationAdded += ApplicationLibrary_ApplicationAdded;
ViewModel.ReloadGameList += ReloadGameList;
}
private void IsActiveChanged(bool obj)
{
ViewModel.IsActive = obj;
} }
public void LoadGameList() public void LoadGameList()
@@ -122,45 +120,51 @@ namespace Ryujinx.Ava.UI.Windows
_isLoading = true; _isLoading = true;
ViewModel.LoadApplications(); LoadApplications();
_isLoading = false; _isLoading = false;
} }
private void Update_StatusBar(object sender, StatusUpdatedEventArgs args)
{
if (ViewModel.ShowMenuAndStatusBar && !ViewModel.ShowLoadProgress)
{
Dispatcher.UIThread.InvokeAsync(() =>
{
if (args.VSyncEnabled)
{
ViewModel.VsyncColor = new SolidColorBrush(Color.Parse("#ff2eeac9"));
}
else
{
ViewModel.VsyncColor = new SolidColorBrush(Color.Parse("#ffff4554"));
}
ViewModel.DockedStatusText = args.DockedMode;
ViewModel.AspectRatioStatusText = args.AspectRatio;
ViewModel.GameStatusText = args.GameStatus;
ViewModel.VolumeStatusText = args.VolumeStatus;
ViewModel.FifoStatusText = args.FifoStatus;
ViewModel.GpuNameText = args.GpuName;
ViewModel.BackendText = args.GpuBackend;
ViewModel.ShowStatusSeparator = true;
});
}
}
protected override void HandleScalingChanged(double scale) protected override void HandleScalingChanged(double scale)
{ {
Program.DesktopScaleFactor = scale; Program.DesktopScaleFactor = scale;
base.HandleScalingChanged(scale); base.HandleScalingChanged(scale);
} }
public void AddApplication(ApplicationData applicationData)
{
Dispatcher.UIThread.InvokeAsync(() =>
{
ViewModel.Applications.Add(applicationData);
});
}
private void ApplicationLibrary_ApplicationAdded(object sender, ApplicationAddedEventArgs e)
{
AddApplication(e.AppData);
}
private void ApplicationLibrary_ApplicationCountUpdated(object sender, ApplicationCountUpdatedEventArgs e)
{
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, e.NumAppsLoaded, e.NumAppsFound);
Dispatcher.UIThread.Post(() =>
{
ViewModel.StatusBarProgressValue = e.NumAppsLoaded;
ViewModel.StatusBarProgressMaximum = e.NumAppsFound;
if (e.NumAppsFound == 0)
{
StatusBarView.LoadProgressBar.IsVisible = false;
}
if (e.NumAppsLoaded == e.NumAppsFound)
{
StatusBarView.LoadProgressBar.IsVisible = false;
}
});
}
public void Application_Opened(object sender, ApplicationOpenedEventArgs args) public void Application_Opened(object sender, ApplicationOpenedEventArgs args)
{ {
if (args.Application != null) if (args.Application != null)
@@ -169,50 +173,12 @@ namespace Ryujinx.Ava.UI.Windows
string path = new FileInfo(args.Application.Path).FullName; string path = new FileInfo(args.Application.Path).FullName;
LoadApplication(path); ViewModel.LoadApplication(path);
} }
args.Handled = true; args.Handled = true;
} }
public async Task PerformanceCheck()
{
if (ConfigurationState.Instance.Logger.EnableTrace.Value)
{
string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledMessage];
string secondaryMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckLoggingEnabledConfirmMessage];
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(mainMessage, secondaryMessage,
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result != UserResult.Yes)
{
ConfigurationState.Instance.Logger.EnableTrace.Value = false;
SaveConfig();
}
}
if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value))
{
string mainMessage = LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledMessage];
string secondaryMessage =
LocaleManager.Instance[LocaleKeys.DialogPerformanceCheckShaderDumpEnabledConfirmMessage];
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(mainMessage, secondaryMessage,
LocaleManager.Instance[LocaleKeys.InputDialogYes], LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result != UserResult.Yes)
{
ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = "";
SaveConfig();
}
}
}
internal static void DeferLoadApplication(string launchPathArg, bool startFullscreenArg) internal static void DeferLoadApplication(string launchPathArg, bool startFullscreenArg)
{ {
_deferLoad = true; _deferLoad = true;
@@ -220,109 +186,6 @@ namespace Ryujinx.Ava.UI.Windows
_startFullscreen = startFullscreenArg; _startFullscreen = startFullscreenArg;
} }
#pragma warning disable CS1998
public async void LoadApplication(string path, bool startFullscreen = false, string titleName = "")
#pragma warning restore CS1998
{
if (AppHost != null)
{
await ContentDialogHelper.CreateInfoDialog(
LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedMessage],
LocaleManager.Instance[LocaleKeys.DialogLoadAppGameAlreadyLoadedSubMessage],
LocaleManager.Instance[LocaleKeys.InputDialogOk],
"",
LocaleManager.Instance[LocaleKeys.RyujinxInfo]);
return;
}
#if RELEASE
await PerformanceCheck();
#endif
Logger.RestartTime();
if (ViewModel.SelectedIcon == null)
{
ViewModel.SelectedIcon = ApplicationLibrary.GetApplicationIcon(path);
}
PrepareLoadScreen();
_mainViewContent = MainContent.Content as Control;
RendererControl = new RendererHost(ConfigurationState.Instance.Logger.GraphicsDebugLevel);
if (ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.OpenGl)
{
RendererControl.CreateOpenGL();
}
else
{
RendererControl.CreateVulkan();
}
AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
Dispatcher.UIThread.Post(async () =>
{
if (!await AppHost.LoadGuestApplication())
{
AppHost.DisposeContext();
AppHost = null;
return;
}
CanUpdate = false;
ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance[LocaleKeys.LoadingHeading], AppHost.Device.Application.TitleName) : titleName;
ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName;
SwitchToGameControl(startFullscreen);
_currentEmulatedGamePath = path;
Thread gameThread = new(InitializeGame)
{
Name = "GUI.WindowThread"
};
gameThread.Start();
});
}
private void InitializeGame()
{
RendererControl.RendererInitialized += GlRenderer_Created;
AppHost.StatusUpdatedEvent += Update_StatusBar;
AppHost.AppExit += AppHost_AppExit;
_rendererWaitEvent.WaitOne();
AppHost?.Start();
AppHost.DisposeContext();
}
private void HandleRelaunch()
{
if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart)
{
_userChannelPersistence.ShouldRestart = false;
Dispatcher.UIThread.Post(() =>
{
LoadApplication(_currentEmulatedGamePath);
});
}
else
{
// otherwise, clear state.
_userChannelPersistence = new UserChannelPersistence();
_currentEmulatedGamePath = null;
}
}
public void SwitchToGameControl(bool startFullscreen = false) public void SwitchToGameControl(bool startFullscreen = false)
{ {
ViewModel.ShowLoadProgress = false; ViewModel.ShowLoadProgress = false;
@@ -331,14 +194,10 @@ namespace Ryujinx.Ava.UI.Windows
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
MainContent.Content = RendererControl; if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
if (startFullscreen && WindowState != WindowState.FullScreen)
{ {
ViewModel.ToggleFullscreen(); ViewModel.ToggleFullscreen();
} }
RendererControl.Focus();
}); });
} }
@@ -350,71 +209,16 @@ namespace Ryujinx.Ava.UI.Windows
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
if (startFullscreen && WindowState != WindowState.FullScreen) if (startFullscreen && ViewModel.WindowState != WindowState.FullScreen)
{ {
ViewModel.ToggleFullscreen(); ViewModel.ToggleFullscreen();
} }
}); });
} }
private void GlRenderer_Created(object sender, EventArgs e)
{
ShowLoading();
_rendererWaitEvent.Set();
}
private void AppHost_AppExit(object sender, EventArgs e)
{
if (_isClosing)
{
return;
}
ViewModel.IsGameRunning = false;
Dispatcher.UIThread.InvokeAsync(() =>
{
ViewModel.ShowMenuAndStatusBar = true;
ViewModel.ShowContent = true;
ViewModel.ShowLoadProgress = false;
ViewModel.IsLoadingIndeterminate = false;
CanUpdate = true;
Cursor = Cursor.Default;
if (MainContent.Content != _mainViewContent)
{
MainContent.Content = _mainViewContent;
}
AppHost = null;
HandleRelaunch();
});
RendererControl.RendererInitialized -= GlRenderer_Created;
RendererControl = null;
ViewModel.SelectedIcon = null;
Dispatcher.UIThread.InvokeAsync(() =>
{
Title = $"Ryujinx {Program.Version}";
});
}
public void Sort_Checked(object sender, RoutedEventArgs args)
{
if (sender is RadioButton button)
{
var sort = Enum.Parse<ApplicationSort>(button.Tag.ToString());
ViewModel.Sort(sort);
}
}
protected override void HandleWindowStateChanged(WindowState state) protected override void HandleWindowStateChanged(WindowState state)
{ {
WindowState = state; ViewModel.WindowState = state;
if (state != WindowState.Minimized) if (state != WindowState.Minimized)
{ {
@@ -422,15 +226,6 @@ namespace Ryujinx.Ava.UI.Windows
} }
} }
public void Order_Checked(object sender, RoutedEventArgs args)
{
if (sender is RadioButton button)
{
var tag = button.Tag.ToString();
ViewModel.Sort(tag != "Descending");
}
}
private void Initialize() private void Initialize()
{ {
_userChannelPersistence = new UserChannelPersistence(); _userChannelPersistence = new UserChannelPersistence();
@@ -457,8 +252,6 @@ namespace Ryujinx.Ava.UI.Windows
VirtualFileSystem.ReloadKeySet(); VirtualFileSystem.ReloadKeySet();
ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient, this); ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient, this);
RefreshFirmwareStatus();
} }
protected void CheckLaunchState() protected void CheckLaunchState()
@@ -475,7 +268,7 @@ namespace Ryujinx.Ava.UI.Windows
{ {
_deferLoad = false; _deferLoad = false;
LoadApplication(_launchPath, _startFullscreen); ViewModel.LoadApplication(_launchPath, _startFullscreen);
} }
if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false, this)) if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false, this))
@@ -487,35 +280,9 @@ namespace Ryujinx.Ava.UI.Windows
} }
} }
public void RefreshFirmwareStatus()
{
SystemVersion version = null;
try
{
version = ContentManager.GetCurrentFirmwareVersion();
}
catch (Exception) { }
bool hasApplet = false;
if (version != null)
{
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion,
version.VersionString);
hasApplet = version.Major > 3;
}
else
{
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarSystemVersion, "0.0");
}
ViewModel.IsAppletMenuActive = hasApplet;
}
private void Load() private void Load()
{ {
VolumeStatus.Click += VolumeStatus_CheckedChanged; StatusBarView.VolumeStatus.Click += VolumeStatus_CheckedChanged;
GameGrid.ApplicationOpened += Application_Opened; GameGrid.ApplicationOpened += Application_Opened;
@@ -535,6 +302,19 @@ namespace Ryujinx.Ava.UI.Windows
CheckLaunchState(); CheckLaunchState();
} }
private void SetMainContent(Control content = null)
{
if (content == null)
{
content = GameLibrary;
}
if (MainContent.Content != content)
{
MainContent.Content = content;
}
}
public static void UpdateGraphicsConfig() public static void UpdateGraphicsConfig()
{ {
GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale; GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale;
@@ -553,99 +333,6 @@ namespace Ryujinx.Ava.UI.Windows
HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape));
} }
public static void SaveConfig()
{
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
}
public void UpdateGameMetadata(string titleId)
{
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
{
if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime))
{
double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
}
});
}
private void PrepareLoadScreen()
{
using MemoryStream stream = new MemoryStream(ViewModel.SelectedIcon);
using var gameIconBmp = SixLabors.ImageSharp.Image.Load<Bgra32>(stream);
var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel<Bgra32>();
const int ColorDivisor = 4;
Color progressFgColor = Color.FromRgb(dominantColor.R, dominantColor.G, dominantColor.B);
Color progressBgColor = Color.FromRgb(
(byte)(dominantColor.R / ColorDivisor),
(byte)(dominantColor.G / ColorDivisor),
(byte)(dominantColor.B / ColorDivisor));
ViewModel.ProgressBarForegroundColor = new SolidColorBrush(progressFgColor);
ViewModel.ProgressBarBackgroundColor = new SolidColorBrush(progressBgColor);
}
private void SearchBox_OnKeyUp(object sender, KeyEventArgs e)
{
ViewModel.SearchText = SearchBox.Text;
}
private async void StopEmulation_Click(object sender, RoutedEventArgs e)
{
if (AppHost != null)
{
await AppHost.ShowExitPrompt();
}
}
private async void PauseEmulation_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() =>
{
AppHost?.Pause();
});
}
private async void ResumeEmulation_Click(object sender, RoutedEventArgs e)
{
await Task.Run(() =>
{
AppHost?.Resume();
});
}
private void ScanAmiiboMenuItem_AttachedToVisualTree(object sender, VisualTreeAttachmentEventArgs e)
{
if (sender is MenuItem)
{
ViewModel.IsAmiiboRequested = AppHost.Device.System.SearchingForAmiibo(out _);
}
}
private void VsyncStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
{
AppHost.Device.EnableDeviceVsync = !AppHost.Device.EnableDeviceVsync;
Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {AppHost.Device.EnableDeviceVsync}");
}
private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
{
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
}
private void AspectRatioStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
{
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
}
private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e) private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e)
{ {
var volumeSplitButton = sender as ToggleSplitButton; var volumeSplitButton = sender as ToggleSplitButton;
@@ -653,20 +340,20 @@ namespace Ryujinx.Ava.UI.Windows
{ {
if (!volumeSplitButton.IsChecked) if (!volumeSplitButton.IsChecked)
{ {
AppHost.Device.SetVolume(ConfigurationState.Instance.System.AudioVolume); ViewModel.AppHost.Device.SetVolume(ConfigurationState.Instance.System.AudioVolume);
} }
else else
{ {
AppHost.Device.SetVolume(0); ViewModel.AppHost.Device.SetVolume(0);
} }
ViewModel.Volume = AppHost.Device.GetVolume(); ViewModel.Volume = ViewModel.AppHost.Device.GetVolume();
} }
} }
protected override void OnClosing(CancelEventArgs e) protected override void OnClosing(CancelEventArgs e)
{ {
if (!_isClosing && AppHost != null && ConfigurationState.Instance.ShowConfirmExit) if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit)
{ {
e.Cancel = true; e.Cancel = true;
@@ -675,14 +362,14 @@ namespace Ryujinx.Ava.UI.Windows
return; return;
} }
_isClosing = true; ViewModel.IsClosing = true;
if (AppHost != null) if (ViewModel.AppHost != null)
{ {
AppHost.AppExit -= AppHost_AppExit; ViewModel.AppHost.AppExit -= ViewModel.AppHost_AppExit;
AppHost.AppExit += (sender, e) => ViewModel.AppHost.AppExit += (sender, e) =>
{ {
AppHost = null; ViewModel.AppHost = null;
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
@@ -691,7 +378,7 @@ namespace Ryujinx.Ava.UI.Windows
Close(); Close();
}); });
}; };
AppHost?.Stop(); ViewModel.AppHost?.Stop();
e.Cancel = true; e.Cancel = true;
@@ -709,13 +396,43 @@ namespace Ryujinx.Ava.UI.Windows
{ {
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
{ {
_isClosing = await ContentDialogHelper.CreateExitDialog(); ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog();
if (_isClosing) if (ViewModel.IsClosing)
{ {
Close(); Close();
} }
}); });
} }
public async void LoadApplications()
{
await Dispatcher.UIThread.InvokeAsync(() =>
{
ViewModel.Applications.Clear();
StatusBarView.LoadProgressBar.IsVisible = true;
ViewModel.StatusBarProgressMaximum = 0;
ViewModel.StatusBarProgressValue = 0;
LocaleManager.Instance.UpdateDynamicValue(LocaleKeys.StatusBarGamesLoaded, 0, 0);
});
ReloadGameList();
}
private void ReloadGameList()
{
if (_isLoading)
{
return;
}
_isLoading = true;
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs.Value, ConfigurationState.Instance.System.Language);
_isLoading = false;
}
} }
} }

View File

@@ -6,13 +6,12 @@
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows" xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:settings="clr-namespace:Ryujinx.Ava.UI.Views.Settings"
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
Width="1100" Width="1100"
Height="768" Height="768"
d:DesignWidth="800"
d:DesignHeight="950"
MinWidth="800" MinWidth="800"
MinHeight="480" MinHeight="480"
WindowStartupLocation="CenterOwner" WindowStartupLocation="CenterOwner"
@@ -23,9 +22,6 @@
<Design.DataContext> <Design.DataContext>
<viewModels:SettingsViewModel /> <viewModels:SettingsViewModel />
</Design.DataContext> </Design.DataContext>
<Window.Resources>
<helpers:KeyValueConverter x:Key="Key" />
</Window.Resources>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="600"> <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinWidth="600">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -38,871 +34,15 @@
IsVisible="False" IsVisible="False"
KeyboardNavigation.IsTabStop="False"/> KeyboardNavigation.IsTabStop="False"/>
<Grid Name="Pages" IsVisible="False" Grid.Row="2"> <Grid Name="Pages" IsVisible="False" Grid.Row="2">
<ScrollViewer Name="UiPage" <settings:SettingsUIView Name="UiPage" />
Margin="0,0,2,0" <settings:SettingsInputView Name="InputPage" />
HorizontalAlignment="Stretch" <settings:SettingsHotkeysView Name="HotkeysPage" />
VerticalAlignment="Stretch" <settings:SettingsSystemView Name="SystemPage" />
HorizontalScrollBarVisibility="Disabled" <settings:SettingsCPUView Name="CpuPage" />
VerticalScrollBarVisibility="Auto"> <settings:SettingsGraphicsView Name="GraphicsPage" />
<Border Classes="settings"> <settings:SettingsAudioView Name="AudioPage" />
<StackPanel <settings:SettingsNetworkView Name="NetworkPage" />
Margin="10,5" <settings:SettingsLoggingView Name="LoggingPage" />
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGeneral}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableDiscordIntegration}">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ToggleDiscordTooltip}"
Text="{locale:Locale SettingsTabGeneralEnableDiscordRichPresence}" />
</CheckBox>
<CheckBox IsChecked="{Binding CheckUpdatesOnStart}">
<TextBlock Text="{locale:Locale SettingsTabGeneralCheckUpdatesOnLaunch}" />
</CheckBox>
<CheckBox IsChecked="{Binding ShowConfirmExit}">
<TextBlock Text="{locale:Locale SettingsTabGeneralShowConfirmExitDialog}" />
</CheckBox>
<CheckBox IsChecked="{Binding HideCursorOnIdle}">
<TextBlock Text="{locale:Locale SettingsTabGeneralHideCursorOnIdle}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralGameDirectories}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<ListBox
Name="GameList"
MinHeight="250"
Items="{Binding GameDirectories}" />
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox
Name="PathBox"
Margin="0"
ToolTip.Tip="{locale:Locale AddGameDirBoxTooltip}"
VerticalAlignment="Stretch" />
<Button
Name="AddButton"
Grid.Column="1"
MinWidth="90"
Margin="10,0,0,0"
ToolTip.Tip="{locale:Locale AddGameDirTooltip}"
Click="AddButton_OnClick">
<TextBlock HorizontalAlignment="Center"
Text="{locale:Locale SettingsTabGeneralAdd}" />
</Button>
<Button
Name="RemoveButton"
Grid.Column="2"
MinWidth="90"
Margin="10,0,0,0"
ToolTip.Tip="{locale:Locale RemoveGameDirTooltip}"
Click="RemoveButton_OnClick">
<TextBlock HorizontalAlignment="Center"
Text="{locale:Locale SettingsTabGeneralRemove}" />
</Button>
</Grid>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGeneralTheme}" />
<Grid Margin="10,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<CheckBox IsChecked="{Binding EnableCustomTheme}"
ToolTip.Tip="{locale:Locale CustomThemeCheckTooltip}">
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeEnableCustomTheme}" />
</CheckBox>
<TextBlock VerticalAlignment="Center"
Margin="0,10,0,0"
Grid.Row="1"
Text="{locale:Locale SettingsTabGeneralThemeCustomTheme}"
ToolTip.Tip="{locale:Locale CustomThemePathTooltip}" />
<TextBox Margin="0,10,0,0"
Grid.Row="1"
Grid.Column="1"
Text="{Binding CustomThemePath}" />
<Button Grid.Row="1"
Grid.Column="2"
Margin="10,10,0,0"
Command="{ReflectionBinding BrowseTheme}"
ToolTip.Tip="{locale:Locale CustomThemeBrowseTooltip}"
Content="{locale:Locale ButtonBrowse}" />
<TextBlock VerticalAlignment="Center"
Margin="0,10,0,0"
Grid.Row="2"
Text="{locale:Locale SettingsTabGeneralThemeBaseStyle}" />
<ComboBox VerticalAlignment="Center"
Margin="0,10,0,0"
Grid.Column="1"
Grid.Row="2"
MinWidth="100"
SelectedIndex="{Binding BaseStyleIndex}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleLight}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGeneralThemeBaseStyleDark}" />
</ComboBoxItem>
</ComboBox>
</Grid>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer Name="InputPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Padding="0,0,2,0"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel Margin="4" Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<CheckBox Margin="5,0"
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
IsChecked="{Binding EnableDockedMode}">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
</CheckBox>
<CheckBox Margin="5,0"
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
IsChecked="{Binding EnableKeyboard}">
<TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
</CheckBox>
<CheckBox Margin="5,0"
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
IsChecked="{Binding EnableMouse}">
<TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
</CheckBox>
</StackPanel>
<window:ControllerSettingsWindow Name="ControllerSettings" Margin="0,0,0,0" MinHeight="600" />
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer Name="HotkeysPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel Margin="10,5" Orientation="Vertical" Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleVsyncHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ToggleVsync, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.Screenshot, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ShowUi, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.Pause, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ToggleMute, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ResScaleUp, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.ResScaleDown, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.VolumeUp, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock
Text="{Binding KeyboardHotkeys.VolumeDown, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer Name="SystemPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemCore}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemRegion}"
Width="250" />
<ComboBox SelectedIndex="{Binding Region}"
ToolTip.Tip="{locale:Locale RegionTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionJapan}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionUSA}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionEurope}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionAustralia}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionChina}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionKorea}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemRegionTaiwan}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemLanguage}"
ToolTip.Tip="{locale:Locale LanguageTooltip}"
Width="250" />
<ComboBox SelectedIndex="{Binding Language}"
ToolTip.Tip="{locale:Locale LanguageTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageJapanese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageAmericanEnglish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageFrench}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageGerman}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageItalian}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageSpanish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageKorean}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageDutch}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguagePortuguese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageRussian}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemSystemLanguageTaiwanese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageBritishEnglish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageCanadianFrench}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageLatinAmericanSpanish}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageSimplifiedChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageTraditionalChinese}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabSystemSystemLanguageBrazilianPortuguese}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemTimeZone}"
ToolTip.Tip="{locale:Locale TimezoneTooltip}"
Width="250" />
<AutoCompleteBox
Name="TimeZoneBox"
Width="350"
MaxDropDownHeight="500"
FilterMode="Contains"
Items="{Binding TimeZones}"
SelectionChanged="TimeZoneBox_OnSelectionChanged"
Text="{Binding Path=TimeZone, Mode=OneWay}"
TextChanged="TimeZoneBox_OnTextChanged"
ValueMemberBinding="{ReflectionBinding TzMultiBinding}"
ToolTip.Tip="{locale:Locale TimezoneTooltip}" />
</StackPanel>
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemSystemTime}"
ToolTip.Tip="{locale:Locale TimeTooltip}"
Width="250"/>
<DatePicker VerticalAlignment="Center" SelectedDate="{Binding DateOffset}"
ToolTip.Tip="{locale:Locale TimeTooltip}"
Width="350" />
</StackPanel>
<StackPanel Margin="250,0,0,10" Orientation="Horizontal">
<TimePicker
VerticalAlignment="Center"
ClockIdentifier="24HourClock"
SelectedTime="{Binding TimeOffset}"
Width="350"
ToolTip.Tip="{locale:Locale TimeTooltip}" />
</StackPanel>
<CheckBox IsChecked="{Binding EnableVsync}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableVsync}"
ToolTip.Tip="{locale:Locale VSyncToggleTooltip}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableFsIntegrityChecks}"
ToolTip.Tip="{locale:Locale FsIntegrityToggleTooltip}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<StackPanel Orientation="Horizontal">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" />
<TextBlock Text="{locale:Locale SettingsTabSystemHacksNote}" />
</StackPanel>
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<CheckBox IsChecked="{Binding ExpandDramSize}"
ToolTip.Tip="{locale:Locale DRamTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemExpandDramSize}" />
</CheckBox>
<CheckBox IsChecked="{Binding IgnoreMissingServices}"
ToolTip.Tip="{locale:Locale IgnoreMissingServicesTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemIgnoreMissingServices}" />
</CheckBox>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer
Name="CpuPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuCache}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<CheckBox IsChecked="{Binding EnablePptc}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnablePptc}"
ToolTip.Tip="{locale:Locale PptcToggleTooltip}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabCpuMemory}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemMemoryManagerMode}"
ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
Width="250" />
<ComboBox SelectedIndex="{Binding MemoryMode}"
ToolTip.Tip="{locale:Locale MemoryManagerTooltip}"
HorizontalContentAlignment="Left"
Width="350">
<ComboBoxItem
ToolTip.Tip="{locale:Locale MemoryManagerSoftwareTooltip}">
<TextBlock
Text="{locale:Locale SettingsTabSystemMemoryManagerModeSoftware}" />
</ComboBoxItem>
<ComboBoxItem
ToolTip.Tip="{locale:Locale MemoryManagerHostTooltip}">
<TextBlock Text="{locale:Locale SettingsTabSystemMemoryManagerModeHost}" />
</ComboBoxItem>
<ComboBoxItem
ToolTip.Tip="{locale:Locale MemoryManagerUnsafeTooltip}">
<TextBlock
Text="{locale:Locale SettingsTabSystemMemoryManagerModeHostUnchecked}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer
Name="GraphicsPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsAPI}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
Text="{locale:Locale SettingsTabGraphicsBackend}"
Width="250" />
<ComboBox Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsBackendTooltip}"
SelectedIndex="{Binding GraphicsBackendIndex}">
<ComboBoxItem IsVisible="{Binding IsVulkanAvailable}">
<TextBlock Text="Vulkan" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsOpenGLAvailable}">
<TextBlock Text="OpenGL" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal" IsVisible="{Binding IsVulkanSelected}">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
Text="{locale:Locale SettingsTabGraphicsPreferredGpu}"
Width="250" />
<ComboBox Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale SettingsTabGraphicsPreferredGpuTooltip}"
SelectedIndex="{Binding PreferredGpuIndex}"
Items="{Binding AvailableGpus}"/>
</StackPanel>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsFeatures}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical" Spacing="10">
<StackPanel Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableShaderCache}"
ToolTip.Tip="{locale:Locale ShaderCacheToggleTooltip}">
<TextBlock Text="{locale:Locale SettingsTabGraphicsEnableShaderCache}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableTextureRecompression}"
ToolTip.Tip="{locale:Locale SettingsEnableTextureRecompressionTooltip}">
<TextBlock Text="{locale:Locale SettingsEnableTextureRecompression}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableMacroHLE}"
ToolTip.Tip="{locale:Locale SettingsEnableMacroHLETooltip}">
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
</CheckBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}"
Text="{locale:Locale SettingsTabGraphicsResolutionScale}"
Width="250" />
<ComboBox SelectedIndex="{Binding ResolutionScale}"
Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleCustom}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleNative}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale2x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale3x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale4x}" />
</ComboBoxItem>
</ComboBox>
<ui:NumberBox
Margin="10,0,0,0"
ToolTip.Tip="{locale:Locale ResolutionScaleEntryTooltip}"
MinWidth="150"
SmallChange="0.1"
LargeChange="1"
SimpleNumberFormat="F2"
SpinButtonPlacementMode="Inline"
IsVisible="{Binding IsCustomResolutionScaleActive}"
Maximum="100"
Minimum="0.1"
Value="{Binding CustomResolutionScale}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale AnisotropyTooltip}"
Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering}"
Width="250" />
<ComboBox SelectedIndex="{Binding MaxAnisotropy}"
Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale AnisotropyTooltip}">
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabGraphicsAnisotropicFilteringAuto}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering2x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering4x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering8x}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabGraphicsAnisotropicFiltering16x}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale AspectRatioTooltip}"
Text="{locale:Locale SettingsTabGraphicsAspectRatio}"
Width="250" />
<ComboBox SelectedIndex="{Binding AspectRatio}"
Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale AspectRatioTooltip}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio4x3}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x9}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio16x10}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio21x9}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatio32x9}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsAspectRatioStretch}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale GraphicsBackendThreadingTooltip}"
Text="{locale:Locale SettingsTabGraphicsBackendMultithreading}"
Width="250" />
<ComboBox Width="350"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale GalThreadingTooltip}"
SelectedIndex="{Binding GraphicsBackendMultithreadingIndex}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale CommonAuto}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale CommonOff}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale CommonOn}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabGraphicsDeveloperOptions}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}"
Text="{locale:Locale SettingsTabGraphicsShaderDumpPath}"
Width="250" />
<TextBox Text="{Binding ShaderDumpPath}"
Width="350"
ToolTip.Tip="{locale:Locale ShaderDumpPathTooltip}" />
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer
Name="AudioPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabAudio}" />
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemAudioBackend}"
ToolTip.Tip="{locale:Locale AudioBackendTooltip}"
Width="250" />
<ComboBox SelectedIndex="{Binding AudioBackend}"
Width="350"
HorizontalContentAlignment="Left">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendDummy}" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsOpenAlEnabled}">
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendOpenAL}" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsSoundIoEnabled}">
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSoundIO}" />
</ComboBoxItem>
<ComboBoxItem IsEnabled="{Binding IsSDL2Enabled}">
<TextBlock Text="{locale:Locale SettingsTabSystemAudioBackendSDL2}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabSystemAudioVolume}"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Width="250" />
<ui:NumberBox Value="{Binding Volume}"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Width="350"
SmallChange="1"
LargeChange="10"
SimpleNumberFormat="F0"
SpinButtonPlacementMode="Inline"
Minimum="0"
Maximum="100" />
</StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<Slider Value="{Binding Volume}"
Margin="250,0,0,0"
ToolTip.Tip="{locale:Locale AudioVolumeTooltip}"
Minimum="0"
Maximum="100"
SmallChange="5"
TickFrequency="5"
IsSnapToTickEnabled="True"
LargeChange="10"
Width="350" />
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer
Name="NetworkPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabNetworkConnection}" />
<CheckBox Margin="10,0,0,0" IsChecked="{Binding EnableInternetAccess}">
<TextBlock Text="{locale:Locale SettingsTabSystemEnableInternetAccess}"
ToolTip.Tip="{locale:Locale EnableInternetAccessTooltip}" />
</CheckBox>
</StackPanel>
</Border>
</ScrollViewer>
<ScrollViewer
Name="LoggingPage"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalScrollBarVisibility="Disabled"
VerticalScrollBarVisibility="Auto">
<Border Classes="settings">
<StackPanel
Margin="10,5"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingLogging}" />
<StackPanel Margin="10,0,0,0" Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableFileLog}"
ToolTip.Tip="{locale:Locale FileLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableLoggingToFile}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableStub}"
ToolTip.Tip="{locale:Locale StubLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableStubLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableInfo}"
ToolTip.Tip="{locale:Locale InfoLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableInfoLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableWarn}"
ToolTip.Tip="{locale:Locale WarnLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableWarningLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableError}"
ToolTip.Tip="{locale:Locale ErrorLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableErrorLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableTrace}"
ToolTip.Tip="{locale:Locale TraceLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableTraceLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableGuest}"
ToolTip.Tip="{locale:Locale GuestLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableGuestLogs}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical"
Spacing="10">
<StackPanel Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableDebug}"
ToolTip.Tip="{locale:Locale DebugLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableDebugLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableFsAccessLog}"
ToolTip.Tip="{locale:Locale FileAccessLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableFsAccessLogs}" />
</CheckBox>
<StackPanel Margin="0,10,0,0" Orientation="Horizontal" VerticalAlignment="Stretch">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale FSAccessLogModeTooltip}"
Text="{locale:Locale SettingsTabLoggingFsGlobalAccessLogMode}"
Width="285" />
<ui:NumberBox
Maximum="3"
Minimum="0"
Width="150"
SpinButtonPlacementMode="Inline"
SmallChange="1"
LargeChange="1"
Value="{Binding FsGlobalAccessLogMode}" />
</StackPanel>
<StackPanel Margin="0,10,0,0" Orientation="Horizontal">
<TextBlock VerticalAlignment="Center"
Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevel}"
ToolTip.Tip="{locale:Locale OpenGlLogLevel}"
Width="285" />
<ComboBox SelectedIndex="{Binding OpenglDebugLevel}"
Width="150"
HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale OpenGlLogLevel}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelNone}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelError}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock
Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelPerformance}" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelAll}" />
</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>
</Grid> </Grid>
<ui:NavigationView Grid.Row="1" <ui:NavigationView Grid.Row="1"
IsSettingsVisible="False" IsSettingsVisible="False"
@@ -962,7 +102,7 @@
Spacing="10" Spacing="10"
Orientation="Horizontal" Orientation="Horizontal"
HorizontalAlignment="Right" HorizontalAlignment="Right"
ReverseOrder="{ReflectionBinding IsMacOS}"> ReverseOrder="{Binding IsMacOS}">
<Button <Button
HotKey="Enter" HotKey="Enter"
Classes="accent" Classes="accent"

View File

@@ -1,49 +1,29 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Data;
using Avalonia.Data.Converters;
using Avalonia.Input;
using Avalonia.Interactivity;
using FluentAvalonia.Core; using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.Input;
using Ryujinx.Input.Assigner;
using System; using System;
using System.Collections.Generic; using System.ComponentModel;
using System.IO;
using System.Linq;
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
namespace Ryujinx.Ava.UI.Windows namespace Ryujinx.Ava.UI.Windows
{ {
public partial class SettingsWindow : StyleableWindow public partial class SettingsWindow : StyleableWindow
{ {
private ButtonKeyAssigner _currentAssigner;
internal SettingsViewModel ViewModel { get; set; } internal SettingsViewModel ViewModel { get; set; }
public SettingsWindow(VirtualFileSystem virtualFileSystem, ContentManager contentManager) public SettingsWindow(VirtualFileSystem virtualFileSystem, ContentManager contentManager)
{ {
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.Settings]}"; Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance[LocaleKeys.Settings]}";
ViewModel = new SettingsViewModel(virtualFileSystem, contentManager, this); ViewModel = new SettingsViewModel(virtualFileSystem, contentManager);
DataContext = ViewModel; DataContext = ViewModel;
ViewModel.CloseWindow += Close;
ViewModel.SaveSettingsEvent += SaveSettings;
InitializeComponent(); InitializeComponent();
Load(); Load();
FuncMultiValueConverter<string, string> converter = new(parts => string.Format("{0} {1} {2}", parts.ToArray()).Trim());
MultiBinding tzMultiBinding = new() { Converter = converter };
tzMultiBinding.Bindings.Add(new Binding("UtcDifference"));
tzMultiBinding.Bindings.Add(new Binding("Location"));
tzMultiBinding.Bindings.Add(new Binding("Abbreviation"));
TimeZoneBox.ValueMemberBinding = tzMultiBinding;
} }
public SettingsWindow() public SettingsWindow()
@@ -55,6 +35,16 @@ namespace Ryujinx.Ava.UI.Windows
Load(); Load();
} }
public void SaveSettings()
{
InputPage.ControllerSettings?.SaveCurrentProfile();
if (Owner is MainWindow window && ViewModel.DirectoryChanged)
{
window.ViewModel.LoadApplications();
}
}
private void Load() private void Load()
{ {
Pages.Children.Clear(); Pages.Children.Clear();
@@ -62,152 +52,52 @@ namespace Ryujinx.Ava.UI.Windows
NavPanel.SelectedItem = NavPanel.MenuItems.ElementAt(0); NavPanel.SelectedItem = NavPanel.MenuItems.ElementAt(0);
} }
private void Button_Checked(object sender, RoutedEventArgs e)
{
if (sender is ToggleButton button)
{
if (_currentAssigner != null && button == _currentAssigner.ToggledButton)
{
return;
}
if (_currentAssigner == null && (bool)button.IsChecked)
{
_currentAssigner = new ButtonKeyAssigner(button);
FocusManager.Instance.Focus(this, NavigationMethod.Pointer);
PointerPressed += MouseClick;
IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad(ViewModel.AvaloniaKeyboardDriver.GamepadsIds[0]);
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
_currentAssigner.GetInputAndAssign(assigner);
}
else
{
if (_currentAssigner != null)
{
ToggleButton oldButton = _currentAssigner.ToggledButton;
_currentAssigner.Cancel();
_currentAssigner = null;
button.IsChecked = false;
}
}
}
}
private void Button_Unchecked(object sender, RoutedEventArgs e)
{
_currentAssigner?.Cancel();
_currentAssigner = null;
}
private void MouseClick(object sender, PointerPressedEventArgs e)
{
bool shouldUnbind = false;
if (e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed)
{
shouldUnbind = true;
}
_currentAssigner?.Cancel(shouldUnbind);
PointerPressed -= MouseClick;
}
private void NavPanelOnSelectionChanged(object sender, NavigationViewSelectionChangedEventArgs e) private void NavPanelOnSelectionChanged(object sender, NavigationViewSelectionChangedEventArgs e)
{ {
if (e.SelectedItem is NavigationViewItem navitem) if (e.SelectedItem is NavigationViewItem navItem && navItem.Tag is not null)
{ {
NavPanel.Content = navitem.Tag.ToString() switch switch (navItem.Tag.ToString())
{ {
"UiPage" => UiPage, case "UiPage":
"InputPage" => InputPage, UiPage.ViewModel = ViewModel;
"HotkeysPage" => HotkeysPage, NavPanel.Content = UiPage;
"SystemPage" => SystemPage, break;
"CpuPage" => CpuPage, case "InputPage":
"GraphicsPage" => GraphicsPage, NavPanel.Content = InputPage;
"AudioPage" => AudioPage, break;
"NetworkPage" => NetworkPage, case "HotkeysPage":
"LoggingPage" => LoggingPage, NavPanel.Content = HotkeysPage;
_ => throw new NotImplementedException() break;
}; case "SystemPage":
} SystemPage.ViewModel = ViewModel;
} NavPanel.Content = SystemPage;
break;
private async void AddButton_OnClick(object sender, RoutedEventArgs e) case "CpuPage":
{ NavPanel.Content = CpuPage;
string path = PathBox.Text; break;
case "GraphicsPage":
if (!string.IsNullOrWhiteSpace(path) && Directory.Exists(path) && !ViewModel.GameDirectories.Contains(path)) NavPanel.Content = GraphicsPage;
{ break;
ViewModel.GameDirectories.Add(path); case "AudioPage":
ViewModel.DirectoryChanged = true; NavPanel.Content = AudioPage;
} break;
else case "NetworkPage":
{ NavPanel.Content = NetworkPage;
path = await new OpenFolderDialog().ShowAsync(this); break;
case "LoggingPage":
if (!string.IsNullOrWhiteSpace(path)) NavPanel.Content = LoggingPage;
{ break;
ViewModel.GameDirectories.Add(path); default:
ViewModel.DirectoryChanged = true; throw new NotImplementedException();
} }
} }
} }
private void RemoveButton_OnClick(object sender, RoutedEventArgs e) protected override void OnClosing(CancelEventArgs e)
{ {
int oldIndex = GameList.SelectedIndex; HotkeysPage.Dispose();
InputPage.Dispose();
foreach (string path in new List<string>(GameList.SelectedItems.Cast<string>())) base.OnClosing(e);
{
ViewModel.GameDirectories.Remove(path);
ViewModel.DirectoryChanged = true;
}
if (GameList.ItemCount > 0)
{
GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0;
}
}
private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems != null && e.AddedItems.Count > 0)
{
if (e.AddedItems[0] is TimeZone timeZone)
{
e.Handled = true;
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
}
}
}
private void TimeZoneBox_OnTextChanged(object sender, EventArgs e)
{
if (sender is AutoCompleteBox box)
{
if (box.SelectedItem != null && box.SelectedItem is TimeZone timeZone)
{
ViewModel.ValidateAndSetTimeZone(timeZone.Location);
}
}
}
protected override void OnClosed(EventArgs e)
{
ControllerSettings.Dispose();
_currentAssigner?.Cancel();
_currentAssigner = null;
base.OnClosed(e);
} }
} }
} }

View File

@@ -7,9 +7,9 @@ namespace Ryujinx.Common.Logging
AudioRenderer, AudioRenderer,
Configuration, Configuration,
Cpu, Cpu,
Font,
Emulation, Emulation,
FFmpeg, FFmpeg,
Font,
Gpu, Gpu,
Hid, Hid,
Host1x, Host1x,
@@ -66,6 +66,7 @@ namespace Ryujinx.Common.Logging
ServiceVi, ServiceVi,
SurfaceFlinger, SurfaceFlinger,
TamperMachine, TamperMachine,
Ui,
Vic Vic
} }
} }

View File

@@ -29,7 +29,7 @@ namespace Ryujinx.Common.Logging
files[i].Delete(); files[i].Delete();
} }
string version = ReleaseInformations.GetVersion(); string version = ReleaseInformation.GetVersion();
// Get path for the current time // Get path for the current time
path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.log"); path = Path.Combine(logDir.FullName, $"Ryujinx_{version}_{DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}.log");

View File

@@ -0,0 +1,51 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Common.Memory
{
public ref struct SpanReader
{
private ReadOnlySpan<byte> _input;
public int Length => _input.Length;
public SpanReader(ReadOnlySpan<byte> input)
{
_input = input;
}
public T Read<T>() where T : unmanaged
{
T value = MemoryMarshal.Cast<byte, T>(_input)[0];
_input = _input.Slice(Unsafe.SizeOf<T>());
return value;
}
public ReadOnlySpan<byte> GetSpan(int size)
{
ReadOnlySpan<byte> data = _input.Slice(0, size);
_input = _input.Slice(size);
return data;
}
public T ReadAt<T>(int offset) where T : unmanaged
{
return MemoryMarshal.Cast<byte, T>(_input.Slice(offset))[0];
}
public ReadOnlySpan<byte> GetSpanAt(int offset, int size)
{
return _input.Slice(offset, size);
}
public void Skip(int size)
{
_input = _input.Slice(size);
}
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Common.Memory
{
public ref struct SpanWriter
{
private Span<byte> _output;
public int Length => _output.Length;
public SpanWriter(Span<byte> output)
{
_output = output;
}
public void Write<T>(T value) where T : unmanaged
{
MemoryMarshal.Cast<byte, T>(_output)[0] = value;
_output = _output.Slice(Unsafe.SizeOf<T>());
}
public void Write(ReadOnlySpan<byte> data)
{
data.CopyTo(_output.Slice(0, data.Length));
_output = _output.Slice(data.Length);
}
public void WriteAt<T>(int offset, T value) where T : unmanaged
{
MemoryMarshal.Cast<byte, T>(_output.Slice(offset))[0] = value;
}
public void WriteAt(int offset, ReadOnlySpan<byte> data)
{
data.CopyTo(_output.Slice(offset, data.Length));
}
public void Skip(int size)
{
_output = _output.Slice(size);
}
}
}

View File

@@ -5,7 +5,7 @@ using System.Reflection;
namespace Ryujinx.Common namespace Ryujinx.Common
{ {
// DO NOT EDIT, filled by CI // DO NOT EDIT, filled by CI
public static class ReleaseInformations public static class ReleaseInformation
{ {
private const string FlatHubChannelOwner = "flathub"; private const string FlatHubChannelOwner = "flathub";
@@ -50,4 +50,4 @@ namespace Ryujinx.Common
return AppDomain.CurrentDomain.BaseDirectory; return AppDomain.CurrentDomain.BaseDirectory;
} }
} }
} }

View File

@@ -0,0 +1,57 @@
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Ryujinx.Common.Utilities
{
public static class BitfieldExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Extract<T>(this T value, int lsb) where T : IBinaryInteger<T>
{
int bitSize = Unsafe.SizeOf<T>() * 8;
lsb &= bitSize - 1;
return !T.IsZero((value >>> lsb) & T.One);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Extract<T>(this T value, int lsb, int length) where T : IBinaryInteger<T>
{
int bitSize = Unsafe.SizeOf<T>() * 8;
lsb &= bitSize - 1;
return (value >>> lsb) & (~T.Zero >>> (bitSize - length));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T ExtractSx<T>(this T value, int lsb, int length) where T : IBinaryInteger<T>
{
int bitSize = Unsafe.SizeOf<T>() * 8;
int shift = lsb & (bitSize - 1);
return (value << (bitSize - (shift + length))) >> (bitSize - length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Insert<T>(this T value, int lsb, bool toInsert) where T : IBinaryInteger<T>
{
int bitSize = Unsafe.SizeOf<T>() * 8;
lsb &= bitSize - 1;
T mask = T.One << lsb;
return (value & ~mask) | (toInsert ? mask : T.Zero);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Insert<T>(this T value, int lsb, int length, T toInsert) where T : IBinaryInteger<T>
{
int bitSize = Unsafe.SizeOf<T>() * 8;
lsb &= bitSize - 1;
T mask = (~T.Zero >>> (bitSize - length)) << lsb;
return (value & ~mask) | ((toInsert << lsb) & mask);
}
}
}

View File

@@ -35,5 +35,27 @@ namespace Ryujinx.Cpu
/// <param name="address">Address of the region to be invalidated</param> /// <param name="address">Address of the region to be invalidated</param>
/// <param name="size">Size of the region to be invalidated</param> /// <param name="size">Size of the region to be invalidated</param>
void InvalidateCacheRegion(ulong address, ulong size); void InvalidateCacheRegion(ulong address, ulong size);
/// <summary>
/// Loads cached code from disk for a given application.
/// </summary>
/// <remarks>
/// If the execution engine is recompiling guest code, this can be used to load cached code from disk.
/// </remarks>
/// <param name="titleIdText">Title ID of the application in padded hex form</param>
/// <param name="displayVersion">Version of the application</param>
/// <param name="enabled">True if the cache should be loaded from disk if it exists, false otherwise</param>
/// <returns>Disk cache load progress reporter and manager</returns>
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled);
/// <summary>
/// Indicates that code has been loaded into guest memory, and that it might be executed in the future.
/// </summary>
/// <remarks>
/// Some execution engines might use this information to cache recompiled code on disk or to ensure it can be executed.
/// </remarks>
/// <param name="address">CPU virtual address where the code starts</param>
/// <param name="size">Size of the code range in bytes</param>
void PrepareCodeRange(ulong address, ulong size);
} }
} }

View File

@@ -0,0 +1,20 @@
using System;
namespace Ryujinx.Cpu
{
/// <summary>
/// Disk cache load state report and management interface.
/// </summary>
public interface IDiskCacheLoadState
{
/// <summary>
/// Event used to report the cache load progress.
/// </summary>
event Action<LoadState, int, int> StateChanged;
/// <summary>
/// Cancels the disk cache load process.
/// </summary>
void Cancel();
}
}

View File

@@ -37,5 +37,17 @@ namespace Ryujinx.Cpu.Jit
{ {
_translator.InvalidateJitCacheRegion(address, size); _translator.InvalidateJitCacheRegion(address, size);
} }
/// <inheritdoc/>
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
{
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled));
}
/// <inheritdoc/>
public void PrepareCodeRange(ulong address, ulong size)
{
_translator.PrepareCodeRange(address, size);
}
} }
} }

View File

@@ -0,0 +1,38 @@
using ARMeilleure.Translation.PTC;
using System;
namespace Ryujinx.Cpu.Jit
{
public class JitDiskCacheLoadState : IDiskCacheLoadState
{
/// <inheritdoc/>
public event Action<LoadState, int, int> StateChanged;
private readonly IPtcLoadState _loadState;
public JitDiskCacheLoadState(IPtcLoadState loadState)
{
loadState.PtcStateChanged += LoadStateChanged;
_loadState = loadState;
}
private void LoadStateChanged(PtcLoadingState newState, int current, int total)
{
LoadState state = newState switch
{
PtcLoadingState.Start => LoadState.Unloaded,
PtcLoadingState.Loading => LoadState.Loading,
PtcLoadingState.Loaded => LoadState.Loaded,
_ => throw new ArgumentException($"Invalid load state \"{newState}\".")
};
StateChanged?.Invoke(state, current, total);
}
/// <inheritdoc/>
public void Cancel()
{
_loadState.Continue();
}
}
}

12
Ryujinx.Cpu/LoadState.cs Normal file
View File

@@ -0,0 +1,12 @@
namespace Ryujinx.Cpu
{
/// <summary>
/// Load state.
/// </summary>
public enum LoadState
{
Unloaded,
Loading,
Loaded
}
}

View File

@@ -1,15 +0,0 @@
namespace Ryujinx.Graphics.Shader.Decoders
{
static class BitfieldExtensions
{
public static bool Extract(this int value, int lsb)
{
return ((value >> lsb) & 1) != 0;
}
public static int Extract(this int value, int lsb, int length)
{
return (value >> lsb) & (int)(uint.MaxValue >> (32 - length));
}
}
}

View File

@@ -1,4 +1,4 @@
using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System; using System;

View File

@@ -1,4 +1,4 @@
using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Common.Utilities;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

View File

@@ -1,39 +0,0 @@
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Vic.Types
{
static class BitfieldExtensions
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Extract(this int value, int lsb)
{
return ((value >> (lsb & 0x1f)) & 1) != 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Extract(this int value, int lsb, int length)
{
return (value >> (lsb & 0x1f)) & (int)(uint.MaxValue >> (32 - length));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Extract(this long value, int lsb)
{
return ((int)(value >> (lsb & 0x3f)) & 1) != 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Extract(this long value, int lsb, int length)
{
return (int)(value >> (lsb & 0x3f)) & (int)(uint.MaxValue >> (32 - length));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ExtractSx(this long value, int lsb, int length)
{
int shift = lsb & 0x3f;
return (int)((value << (64 - (shift + length))) >> (64 - length));
}
}
}

View File

@@ -1,20 +1,22 @@
namespace Ryujinx.Graphics.Vic.Types using Ryujinx.Common.Utilities;
namespace Ryujinx.Graphics.Vic.Types
{ {
struct BlendingSlotStruct struct BlendingSlotStruct
{ {
private long _word0; private long _word0;
private long _word1; private long _word1;
public int AlphaK1 => _word0.Extract(0, 10); public int AlphaK1 => (int)_word0.Extract(0, 10);
public int AlphaK2 => _word0.Extract(16, 10); public int AlphaK2 => (int)_word0.Extract(16, 10);
public int SrcFactCMatchSelect => _word0.Extract(32, 3); public int SrcFactCMatchSelect => (int)_word0.Extract(32, 3);
public int DstFactCMatchSelect => _word0.Extract(36, 3); public int DstFactCMatchSelect => (int)_word0.Extract(36, 3);
public int SrcFactAMatchSelect => _word0.Extract(40, 3); public int SrcFactAMatchSelect => (int)_word0.Extract(40, 3);
public int DstFactAMatchSelect => _word0.Extract(44, 3); public int DstFactAMatchSelect => (int)_word0.Extract(44, 3);
public int OverrideR => _word1.Extract(66, 10); public int OverrideR => (int)_word1.Extract(66, 10);
public int OverrideG => _word1.Extract(76, 10); public int OverrideG => (int)_word1.Extract(76, 10);
public int OverrideB => _word1.Extract(86, 10); public int OverrideB => (int)_word1.Extract(86, 10);
public int OverrideA => _word1.Extract(96, 10); public int OverrideA => (int)_word1.Extract(96, 10);
public bool UseOverrideR => _word1.Extract(108); public bool UseOverrideR => _word1.Extract(108);
public bool UseOverrideG => _word1.Extract(109); public bool UseOverrideG => _word1.Extract(109);
public bool UseOverrideB => _word1.Extract(110); public bool UseOverrideB => _word1.Extract(110);

View File

@@ -1,4 +1,6 @@
namespace Ryujinx.Graphics.Vic.Types using Ryujinx.Common.Utilities;
namespace Ryujinx.Graphics.Vic.Types
{ {
struct ClearRectStruct struct ClearRectStruct
{ {
@@ -7,13 +9,13 @@
private long _word1; private long _word1;
#pragma warning restore CS0649 #pragma warning restore CS0649
public int ClearRect0Left => _word0.Extract(0, 14); public int ClearRect0Left => (int)_word0.Extract(0, 14);
public int ClearRect0Right => _word0.Extract(16, 14); public int ClearRect0Right => (int)_word0.Extract(16, 14);
public int ClearRect0Top => _word0.Extract(32, 14); public int ClearRect0Top => (int)_word0.Extract(32, 14);
public int ClearRect0Bottom => _word0.Extract(48, 14); public int ClearRect0Bottom => (int)_word0.Extract(48, 14);
public int ClearRect1Left => _word1.Extract(64, 14); public int ClearRect1Left => (int)_word1.Extract(64, 14);
public int ClearRect1Right => _word1.Extract(80, 14); public int ClearRect1Right => (int)_word1.Extract(80, 14);
public int ClearRect1Top => _word1.Extract(96, 14); public int ClearRect1Top => (int)_word1.Extract(96, 14);
public int ClearRect1Bottom => _word1.Extract(112, 14); public int ClearRect1Bottom => (int)_word1.Extract(112, 14);
} }
} }

View File

@@ -1,17 +1,19 @@
namespace Ryujinx.Graphics.Vic.Types using Ryujinx.Common.Utilities;
namespace Ryujinx.Graphics.Vic.Types
{ {
struct LumaKeyStruct struct LumaKeyStruct
{ {
private long _word0; private long _word0;
private long _word1; private long _word1;
public int LumaCoeff0 => _word0.Extract(0, 20); public int LumaCoeff0 => (int)_word0.Extract(0, 20);
public int LumaCoeff1 => _word0.Extract(20, 20); public int LumaCoeff1 => (int)_word0.Extract(20, 20);
public int LumaCoeff2 => _word0.Extract(40, 20); public int LumaCoeff2 => (int)_word0.Extract(40, 20);
public int LumaRShift => _word0.Extract(60, 4); public int LumaRShift => (int)_word0.Extract(60, 4);
public int LumaCoeff3 => _word1.Extract(64, 20); public int LumaCoeff3 => (int)_word1.Extract(64, 20);
public int LumaKeyLower => _word1.Extract(84, 10); public int LumaKeyLower => (int)_word1.Extract(84, 10);
public int LumaKeyUpper => _word1.Extract(94, 10); public int LumaKeyUpper => (int)_word1.Extract(94, 10);
public bool LumaKeyEnabled => _word1.Extract(104); public bool LumaKeyEnabled => _word1.Extract(104);
} }
} }

View File

@@ -1,4 +1,6 @@
namespace Ryujinx.Graphics.Vic.Types using Ryujinx.Common.Utilities;
namespace Ryujinx.Graphics.Vic.Types
{ {
struct MatrixStruct struct MatrixStruct
{ {
@@ -7,19 +9,19 @@
private long _word2; private long _word2;
private long _word3; private long _word3;
public int MatrixCoeff00 => _word0.ExtractSx(0, 20); public int MatrixCoeff00 => (int)_word0.ExtractSx(0, 20);
public int MatrixCoeff10 => _word0.ExtractSx(20, 20); public int MatrixCoeff10 => (int)_word0.ExtractSx(20, 20);
public int MatrixCoeff20 => _word0.ExtractSx(40, 20); public int MatrixCoeff20 => (int)_word0.ExtractSx(40, 20);
public int MatrixRShift => _word0.Extract(60, 4); public int MatrixRShift => (int)_word0.Extract(60, 4);
public int MatrixCoeff01 => _word1.ExtractSx(64, 20); public int MatrixCoeff01 => (int)_word1.ExtractSx(64, 20);
public int MatrixCoeff11 => _word1.ExtractSx(84, 20); public int MatrixCoeff11 => (int)_word1.ExtractSx(84, 20);
public int MatrixCoeff21 => _word1.ExtractSx(104, 20); public int MatrixCoeff21 => (int)_word1.ExtractSx(104, 20);
public bool MatrixEnable => _word1.Extract(127); public bool MatrixEnable => _word1.Extract(127);
public int MatrixCoeff02 => _word2.ExtractSx(128, 20); public int MatrixCoeff02 => (int)_word2.ExtractSx(128, 20);
public int MatrixCoeff12 => _word2.ExtractSx(148, 20); public int MatrixCoeff12 => (int)_word2.ExtractSx(148, 20);
public int MatrixCoeff22 => _word2.ExtractSx(168, 20); public int MatrixCoeff22 => (int)_word2.ExtractSx(168, 20);
public int MatrixCoeff03 => _word3.ExtractSx(192, 20); public int MatrixCoeff03 => (int)_word3.ExtractSx(192, 20);
public int MatrixCoeff13 => _word3.ExtractSx(212, 20); public int MatrixCoeff13 => (int)_word3.ExtractSx(212, 20);
public int MatrixCoeff23 => _word3.ExtractSx(232, 20); public int MatrixCoeff23 => (int)_word3.ExtractSx(232, 20);
} }
} }

View File

@@ -1,4 +1,6 @@
namespace Ryujinx.Graphics.Vic.Types using Ryujinx.Common.Utilities;
namespace Ryujinx.Graphics.Vic.Types
{ {
struct OutputConfig struct OutputConfig
{ {
@@ -7,19 +9,19 @@
private long _word1; private long _word1;
#pragma warning restore CS0649 #pragma warning restore CS0649
public int AlphaFillMode => _word0.Extract(0, 3); public int AlphaFillMode => (int)_word0.Extract(0, 3);
public int AlphaFillSlot => _word0.Extract(3, 3); public int AlphaFillSlot => (int)_word0.Extract(3, 3);
public int BackgroundAlpha => _word0.Extract(6, 10); public int BackgroundAlpha => (int)_word0.Extract(6, 10);
public int BackgroundR => _word0.Extract(16, 10); public int BackgroundR => (int)_word0.Extract(16, 10);
public int BackgroundG => _word0.Extract(26, 10); public int BackgroundG => (int)_word0.Extract(26, 10);
public int BackgroundB => _word0.Extract(36, 10); public int BackgroundB => (int)_word0.Extract(36, 10);
public int RegammaMode => _word0.Extract(46, 2); public int RegammaMode => (int)_word0.Extract(46, 2);
public bool OutputFlipX => _word0.Extract(48); public bool OutputFlipX => _word0.Extract(48);
public bool OutputFlipY => _word0.Extract(49); public bool OutputFlipY => _word0.Extract(49);
public bool OutputTranspose => _word0.Extract(50); public bool OutputTranspose => _word0.Extract(50);
public int TargetRectLeft => _word1.Extract(64, 14); public int TargetRectLeft => (int)_word1.Extract(64, 14);
public int TargetRectRight => _word1.Extract(80, 14); public int TargetRectRight => (int)_word1.Extract(80, 14);
public int TargetRectTop => _word1.Extract(96, 14); public int TargetRectTop => (int)_word1.Extract(96, 14);
public int TargetRectBottom => _word1.Extract(112, 14); public int TargetRectBottom => (int)_word1.Extract(112, 14);
} }
} }

View File

@@ -1,4 +1,6 @@
namespace Ryujinx.Graphics.Vic.Types using Ryujinx.Common.Utilities;
namespace Ryujinx.Graphics.Vic.Types
{ {
struct OutputSurfaceConfig struct OutputSurfaceConfig
{ {
@@ -8,15 +10,15 @@
#pragma warning restore CS0649 #pragma warning restore CS0649
public PixelFormat OutPixelFormat => (PixelFormat)_word0.Extract(0, 7); public PixelFormat OutPixelFormat => (PixelFormat)_word0.Extract(0, 7);
public int OutChromaLocHoriz => _word0.Extract(7, 2); public int OutChromaLocHoriz => (int)_word0.Extract(7, 2);
public int OutChromaLocVert => _word0.Extract(9, 2); public int OutChromaLocVert => (int)_word0.Extract(9, 2);
public int OutBlkKind => _word0.Extract(11, 4); public int OutBlkKind => (int)_word0.Extract(11, 4);
public int OutBlkHeight => _word0.Extract(15, 4); public int OutBlkHeight => (int)_word0.Extract(15, 4);
public int OutSurfaceWidth => _word0.Extract(32, 14); public int OutSurfaceWidth => (int)_word0.Extract(32, 14);
public int OutSurfaceHeight => _word0.Extract(46, 14); public int OutSurfaceHeight => (int)_word0.Extract(46, 14);
public int OutLumaWidth => _word1.Extract(64, 14); public int OutLumaWidth => (int)_word1.Extract(64, 14);
public int OutLumaHeight => _word1.Extract(78, 14); public int OutLumaHeight => (int)_word1.Extract(78, 14);
public int OutChromaWidth => _word1.Extract(96, 14); public int OutChromaWidth => (int)_word1.Extract(96, 14);
public int OutChromaHeight => _word1.Extract(110, 14); public int OutChromaHeight => (int)_word1.Extract(110, 14);
} }
} }

View File

@@ -1,4 +1,6 @@
namespace Ryujinx.Graphics.Vic.Types using Ryujinx.Common.Utilities;
namespace Ryujinx.Graphics.Vic.Types
{ {
struct PipeConfig struct PipeConfig
{ {
@@ -7,7 +9,7 @@
private long _word1; private long _word1;
#pragma warning restore CS0169, CS0649 #pragma warning restore CS0169, CS0649
public int DownsampleHoriz => _word0.Extract(0, 11); public int DownsampleHoriz => (int)_word0.Extract(0, 11);
public int DownsampleVert => _word0.Extract(16, 11); public int DownsampleVert => (int)_word0.Extract(16, 11);
} }
} }

View File

@@ -1,4 +1,6 @@
namespace Ryujinx.Graphics.Vic.Types using Ryujinx.Common.Utilities;
namespace Ryujinx.Graphics.Vic.Types
{ {
struct SlotConfig struct SlotConfig
{ {
@@ -28,36 +30,36 @@
public bool PpMotionFieldEnable => _word0.Extract(14); public bool PpMotionFieldEnable => _word0.Extract(14);
public bool CombMotionFieldEnable => _word0.Extract(15); public bool CombMotionFieldEnable => _word0.Extract(15);
public FrameFormat FrameFormat => (FrameFormat)_word0.Extract(16, 4); public FrameFormat FrameFormat => (FrameFormat)_word0.Extract(16, 4);
public int FilterLengthY => _word0.Extract(20, 2); public int FilterLengthY => (int)_word0.Extract(20, 2);
public int FilterLengthX => _word0.Extract(22, 2); public int FilterLengthX => (int)_word0.Extract(22, 2);
public int Panoramic => _word0.Extract(24, 12); public int Panoramic => (int)_word0.Extract(24, 12);
public int DetailFltClamp => _word0.Extract(58, 6); public int DetailFltClamp => (int)_word0.Extract(58, 6);
public int FilterNoise => _word1.Extract(64, 10); public int FilterNoise => (int)_word1.Extract(64, 10);
public int FilterDetail => _word1.Extract(74, 10); public int FilterDetail => (int)_word1.Extract(74, 10);
public int ChromaNoise => _word1.Extract(84, 10); public int ChromaNoise => (int)_word1.Extract(84, 10);
public int ChromaDetail => _word1.Extract(94, 10); public int ChromaDetail => (int)_word1.Extract(94, 10);
public DeinterlaceMode DeinterlaceMode => (DeinterlaceMode)_word1.Extract(104, 4); public DeinterlaceMode DeinterlaceMode => (DeinterlaceMode)_word1.Extract(104, 4);
public int MotionAccumWeight => _word1.Extract(108, 3); public int MotionAccumWeight => (int)_word1.Extract(108, 3);
public int NoiseIir => _word1.Extract(111, 11); public int NoiseIir => (int)_word1.Extract(111, 11);
public int LightLevel => _word1.Extract(122, 4); public int LightLevel => (int)_word1.Extract(122, 4);
public int SoftClampLow => _word2.Extract(128, 10); public int SoftClampLow => (int)_word2.Extract(128, 10);
public int SoftClampHigh => _word2.Extract(138, 10); public int SoftClampHigh => (int)_word2.Extract(138, 10);
public int PlanarAlpha => _word2.Extract(160, 10); public int PlanarAlpha => (int)_word2.Extract(160, 10);
public bool ConstantAlpha => _word2.Extract(170); public bool ConstantAlpha => _word2.Extract(170);
public int StereoInterleave => _word2.Extract(171, 3); public int StereoInterleave => (int)_word2.Extract(171, 3);
public bool ClipEnabled => _word2.Extract(174); public bool ClipEnabled => _word2.Extract(174);
public int ClearRectMask => _word2.Extract(175, 8); public int ClearRectMask => (int)_word2.Extract(175, 8);
public int DegammaMode => _word2.Extract(183, 2); public int DegammaMode => (int)_word2.Extract(183, 2);
public bool DecompressEnable => _word2.Extract(186); public bool DecompressEnable => _word2.Extract(186);
public int DecompressCtbCount => _word3.Extract(192, 8); public int DecompressCtbCount => (int)_word3.Extract(192, 8);
public int DecompressZbcColor => _word3.Extract(200, 32); public int DecompressZbcColor => (int)_word3.Extract(200, 32);
public int SourceRectLeft => _word4.Extract(256, 30); public int SourceRectLeft => (int)_word4.Extract(256, 30);
public int SourceRectRight => _word4.Extract(288, 30); public int SourceRectRight => (int)_word4.Extract(288, 30);
public int SourceRectTop => _word5.Extract(320, 30); public int SourceRectTop => (int)_word5.Extract(320, 30);
public int SourceRectBottom => _word5.Extract(352, 30); public int SourceRectBottom => (int)_word5.Extract(352, 30);
public int DstRectLeft => _word6.Extract(384, 14); public int DstRectLeft => (int)_word6.Extract(384, 14);
public int DstRectRight => _word6.Extract(400, 14); public int DstRectRight => (int)_word6.Extract(400, 14);
public int DstRectTop => _word6.Extract(416, 14); public int DstRectTop => (int)_word6.Extract(416, 14);
public int DstRectBottom => _word6.Extract(432, 14); public int DstRectBottom => (int)_word6.Extract(432, 14);
} }
} }

View File

@@ -1,4 +1,6 @@
namespace Ryujinx.Graphics.Vic.Types using Ryujinx.Common.Utilities;
namespace Ryujinx.Graphics.Vic.Types
{ {
struct SlotSurfaceConfig struct SlotSurfaceConfig
{ {
@@ -6,16 +8,16 @@
private long _word1; private long _word1;
public PixelFormat SlotPixelFormat => (PixelFormat)_word0.Extract(0, 7); public PixelFormat SlotPixelFormat => (PixelFormat)_word0.Extract(0, 7);
public int SlotChromaLocHoriz => _word0.Extract(7, 2); public int SlotChromaLocHoriz => (int)_word0.Extract(7, 2);
public int SlotChromaLocVert => _word0.Extract(9, 2); public int SlotChromaLocVert => (int)_word0.Extract(9, 2);
public int SlotBlkKind => _word0.Extract(11, 4); public int SlotBlkKind => (int)_word0.Extract(11, 4);
public int SlotBlkHeight => _word0.Extract(15, 4); public int SlotBlkHeight => (int)_word0.Extract(15, 4);
public int SlotCacheWidth => _word0.Extract(19, 3); public int SlotCacheWidth => (int)_word0.Extract(19, 3);
public int SlotSurfaceWidth => _word0.Extract(32, 14); public int SlotSurfaceWidth => (int)_word0.Extract(32, 14);
public int SlotSurfaceHeight => _word0.Extract(46, 14); public int SlotSurfaceHeight => (int)_word0.Extract(46, 14);
public int SlotLumaWidth => _word1.Extract(64, 14); public int SlotLumaWidth => (int)_word1.Extract(64, 14);
public int SlotLumaHeight => _word1.Extract(78, 14); public int SlotLumaHeight => (int)_word1.Extract(78, 14);
public int SlotChromaWidth => _word1.Extract(96, 14); public int SlotChromaWidth => (int)_word1.Extract(96, 14);
public int SlotChromaHeight => _word1.Extract(110, 14); public int SlotChromaHeight => (int)_word1.Extract(110, 14);
} }
} }

View File

@@ -1,4 +1,3 @@
using ARMeilleure.Translation.PTC;
using LibHac; using LibHac;
using LibHac.Account; using LibHac.Account;
using LibHac.Common; using LibHac.Common;
@@ -14,8 +13,8 @@ using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Cpu;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
@@ -67,6 +66,8 @@ namespace Ryujinx.HLE.HOS
public string TitleIdText => TitleId.ToString("x16"); public string TitleIdText => TitleId.ToString("x16");
public IDiskCacheLoadState DiskCacheLoadState { get; private set; }
public ApplicationLoader(Switch device) public ApplicationLoader(Switch device)
{ {
_device = device; _device = device;
@@ -94,7 +95,7 @@ namespace Ryujinx.HLE.HOS
EnsureSaveData(new ApplicationId(TitleId)); EnsureSaveData(new ApplicationId(TitleId));
} }
LoadExeFs(codeFs, metaData); LoadExeFs(codeFs, string.Empty, metaData);
} }
public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex) public static (Nca main, Nca patch, Nca control) GetGameData(VirtualFileSystem fileSystem, PartitionFileSystem pfs, int programIndex)
@@ -302,12 +303,6 @@ namespace Ryujinx.HLE.HOS
public void LoadServiceNca(string ncaFile) public void LoadServiceNca(string ncaFile)
{ {
// Disable PPTC here as it does not support multiple processes running.
// TODO: This should be eventually removed and it should stop using global state and
// instead manage the cache per process.
Ptc.Close();
PtcProfiler.Stop();
FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read); FileStream file = new FileStream(ncaFile, FileMode.Open, FileAccess.Read);
Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false)); Nca mainNca = new Nca(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
@@ -369,16 +364,12 @@ namespace Ryujinx.HLE.HOS
// Collect the nsos, ignoring ones that aren't used. // Collect the nsos, ignoring ones that aren't used.
NsoExecutable[] programs = nsos.Where(x => x != null).ToArray(); NsoExecutable[] programs = nsos.Where(x => x != null).ToArray();
MemoryManagerMode memoryManagerMode = _device.Configuration.MemoryManagerMode; string displayVersion = _device.System.ContentManager.GetCurrentFirmwareVersion().VersionString;
bool usePtc = _device.System.EnablePtc;
if (!MemoryBlock.SupportsFlags(MemoryAllocationFlags.ViewCompatible))
{
memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
}
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: false); ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit: false);
ProgramLoader.LoadNsos(_device.System.KernelContext, out _, metaData, programInfo, executables: programs); ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs);
string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16"); string titleIdText = npdm.Aci.Value.ProgramId.Value.ToString("x16");
bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0; bool titleIs64Bit = (npdm.Meta.Value.Flags & 1) != 0;
@@ -477,9 +468,11 @@ namespace Ryujinx.HLE.HOS
_device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(), _device.Configuration.VirtualFileSystem.ModLoader.GetModsBasePath(),
_device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath()); _device.Configuration.VirtualFileSystem.ModLoader.GetSdModsBasePath());
string displayVersion = string.Empty;
if (controlNca != null) if (controlNca != null)
{ {
ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref _displayVersion); ReadControlData(_device, controlNca, ref _controlData, ref _titleName, ref displayVersion);
} }
else else
{ {
@@ -493,9 +486,11 @@ namespace Ryujinx.HLE.HOS
string dummyTitleName = ""; string dummyTitleName = "";
BlitStruct<ApplicationControlProperty> dummyControl = new BlitStruct<ApplicationControlProperty>(1); BlitStruct<ApplicationControlProperty> dummyControl = new BlitStruct<ApplicationControlProperty>(1);
ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref _displayVersion); ReadControlData(_device, updateProgram0ControlNca, ref dummyControl, ref dummyTitleName, ref displayVersion);
} }
_displayVersion = displayVersion;
if (dataStorage == null) if (dataStorage == null)
{ {
Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA"); Logger.Warning?.Print(LogClass.Loader, "No RomFS found in NCA");
@@ -515,7 +510,7 @@ namespace Ryujinx.HLE.HOS
EnsureSaveData(new ApplicationId(TitleId & ~0xFul)); EnsureSaveData(new ApplicationId(TitleId & ~0xFul));
} }
LoadExeFs(codeFs, metaData); LoadExeFs(codeFs, displayVersion, metaData);
Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]"); Logger.Info?.Print(LogClass.Loader, $"Application Loaded: {TitleName} v{DisplayVersion} [{TitleIdText}] [{(TitleIs64Bit ? "64-bit" : "32-bit")}]");
} }
@@ -584,7 +579,7 @@ namespace Ryujinx.HLE.HOS
} }
} }
private void LoadExeFs(IFileSystem codeFs, MetaLoader metaData = null, bool isHomebrew = false) private void LoadExeFs(IFileSystem codeFs, string displayVersion, MetaLoader metaData = null, bool isHomebrew = false)
{ {
if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs)) if (_device.Configuration.VirtualFileSystem.ModLoader.ReplaceExefsPartition(TitleId, ref codeFs))
{ {
@@ -649,23 +644,23 @@ namespace Ryujinx.HLE.HOS
memoryManagerMode = MemoryManagerMode.SoftwarePageTable; memoryManagerMode = MemoryManagerMode.SoftwarePageTable;
} }
Ptc.Initialize(TitleIdText, DisplayVersion, usePtc, memoryManagerMode);
// We allow it for nx-hbloader because it can be used to launch homebrew. // We allow it for nx-hbloader because it can be used to launch homebrew.
bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew; bool allowCodeMemoryForJit = TitleId == 0x010000000000100DUL || isHomebrew;
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit); ProgramInfo programInfo = new ProgramInfo(in npdm, displayVersion, usePtc, allowCodeMemoryForJit);
ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: programs); ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: programs);
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine); DiskCacheLoadState = result.DiskCacheLoadState;
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine);
} }
public void LoadProgram(string filePath) public void LoadProgram(string filePath)
{ {
MetaLoader metaData = GetDefaultNpdm(); MetaLoader metaData = GetDefaultNpdm();
metaData.GetNpdm(out Npdm npdm).ThrowIfFailure(); metaData.GetNpdm(out Npdm npdm).ThrowIfFailure();
ProgramInfo programInfo = new ProgramInfo(in npdm, allowCodeMemoryForJit: true); ProgramInfo programInfo = new ProgramInfo(in npdm, string.Empty, diskCacheEnabled: false, allowCodeMemoryForJit: true);
bool isNro = Path.GetExtension(filePath).ToLower() == ".nro"; bool isNro = Path.GetExtension(filePath).ToLower() == ".nro";
@@ -761,9 +756,11 @@ namespace Ryujinx.HLE.HOS
Graphics.Gpu.GraphicsConfig.TitleId = null; Graphics.Gpu.GraphicsConfig.TitleId = null;
_device.Gpu.HostInitalized.Set(); _device.Gpu.HostInitalized.Set();
ProgramLoader.LoadNsos(_device.System.KernelContext, out ProcessTamperInfo tamperInfo, metaData, programInfo, executables: executable); ProgramLoadResult result = ProgramLoader.LoadNsos(_device.System.KernelContext, metaData, programInfo, executables: executable);
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, tamperInfo, _device.TamperMachine); DiskCacheLoadState = result.DiskCacheLoadState;
_device.Configuration.VirtualFileSystem.ModLoader.LoadCheats(TitleId, result.TamperInfo, _device.TamperMachine);
} }
private MetaLoader GetDefaultNpdm() private MetaLoader GetDefaultNpdm()

View File

@@ -6,7 +6,17 @@ using Ryujinx.Memory;
namespace Ryujinx.HLE.HOS namespace Ryujinx.HLE.HOS
{ {
class ArmProcessContext<T> : IProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager interface IArmProcessContext : IProcessContext
{
IDiskCacheLoadState Initialize(
string titleIdText,
string displayVersion,
bool diskCacheEnabled,
ulong codeAddress,
ulong codeSize);
}
class ArmProcessContext<T> : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
{ {
private readonly ulong _pid; private readonly ulong _pid;
private readonly GpuContext _gpuContext; private readonly GpuContext _gpuContext;
@@ -40,6 +50,17 @@ namespace Ryujinx.HLE.HOS
_cpuContext.Execute(context, codeAddress); _cpuContext.Execute(context, codeAddress);
} }
public IDiskCacheLoadState Initialize(
string titleIdText,
string displayVersion,
bool diskCacheEnabled,
ulong codeAddress,
ulong codeSize)
{
_cpuContext.PrepareCodeRange(codeAddress, codeSize);
return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled);
}
public void InvalidateCacheRegion(ulong address, ulong size) public void InvalidateCacheRegion(ulong address, ulong size)
{ {
_cpuContext.InvalidateCacheRegion(address, size); _cpuContext.InvalidateCacheRegion(address, size);

View File

@@ -13,11 +13,30 @@ namespace Ryujinx.HLE.HOS
{ {
private readonly ICpuEngine _cpuEngine; private readonly ICpuEngine _cpuEngine;
private readonly GpuContext _gpu; private readonly GpuContext _gpu;
private readonly string _titleIdText;
private readonly string _displayVersion;
private readonly bool _diskCacheEnabled;
private readonly ulong _codeAddress;
private readonly ulong _codeSize;
public ArmProcessContextFactory(ICpuEngine cpuEngine, GpuContext gpu) public IDiskCacheLoadState DiskCacheLoadState { get; private set; }
public ArmProcessContextFactory(
ICpuEngine cpuEngine,
GpuContext gpu,
string titleIdText,
string displayVersion,
bool diskCacheEnabled,
ulong codeAddress,
ulong codeSize)
{ {
_cpuEngine = cpuEngine; _cpuEngine = cpuEngine;
_gpu = gpu; _gpu = gpu;
_titleIdText = titleIdText;
_displayVersion = displayVersion;
_diskCacheEnabled = diskCacheEnabled;
_codeAddress = codeAddress;
_codeSize = codeSize;
} }
public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit) public IProcessContext Create(KernelContext context, ulong pid, ulong addressSpaceSize, InvalidAccessHandler invalidAccessHandler, bool for64Bit)
@@ -29,21 +48,29 @@ namespace Ryujinx.HLE.HOS
mode = MemoryManagerMode.SoftwarePageTable; mode = MemoryManagerMode.SoftwarePageTable;
} }
IArmProcessContext processContext;
switch (mode) switch (mode)
{ {
case MemoryManagerMode.SoftwarePageTable: case MemoryManagerMode.SoftwarePageTable:
var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler); var memoryManager = new MemoryManager(context.Memory, addressSpaceSize, invalidAccessHandler);
return new ArmProcessContext<MemoryManager>(pid, _cpuEngine, _gpu, memoryManager, for64Bit); processContext = new ArmProcessContext<MemoryManager>(pid, _cpuEngine, _gpu, memoryManager, for64Bit);
break;
case MemoryManagerMode.HostMapped: case MemoryManagerMode.HostMapped:
case MemoryManagerMode.HostMappedUnsafe: case MemoryManagerMode.HostMappedUnsafe:
bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe; bool unsafeMode = mode == MemoryManagerMode.HostMappedUnsafe;
var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler); var memoryManagerHostMapped = new MemoryManagerHostMapped(context.Memory, addressSpaceSize, unsafeMode, invalidAccessHandler);
return new ArmProcessContext<MemoryManagerHostMapped>(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit); processContext = new ArmProcessContext<MemoryManagerHostMapped>(pid, _cpuEngine, _gpu, memoryManagerHostMapped, for64Bit);
break;
default: default:
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize);
return processContext;
} }
} }
} }

View File

@@ -36,6 +36,7 @@ using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
using Ryujinx.HLE.HOS.Services.Time.Clock; using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.Horizon;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -122,6 +123,8 @@ namespace Ryujinx.HLE.HOS
internal LibHacHorizonManager LibHacHorizonManager { get; private set; } internal LibHacHorizonManager LibHacHorizonManager { get; private set; }
internal ServiceTable ServiceTable { get; private set; }
public bool IsPaused { get; private set; } public bool IsPaused { get; private set; }
public Horizon(Switch device) public Horizon(Switch device)
@@ -319,6 +322,43 @@ namespace Ryujinx.HLE.HOS
ViServer = new ServerBase(KernelContext, "ViServerU"); ViServer = new ServerBase(KernelContext, "ViServerU");
ViServerM = new ServerBase(KernelContext, "ViServerM"); ViServerM = new ServerBase(KernelContext, "ViServerM");
ViServerS = new ServerBase(KernelContext, "ViServerS"); ViServerS = new ServerBase(KernelContext, "ViServerS");
StartNewServices();
}
private void StartNewServices()
{
ServiceTable = new ServiceTable();
var services = ServiceTable.GetServices(new HorizonOptions(Device.Configuration.IgnoreMissingServices));
foreach (var service in services)
{
const ProcessCreationFlags flags =
ProcessCreationFlags.EnableAslr |
ProcessCreationFlags.AddressSpace64Bit |
ProcessCreationFlags.Is64Bit |
ProcessCreationFlags.PoolPartitionSystem;
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
int[] defaultCapabilities = new int[]
{
0x030363F7,
0x1FFFFFCF,
0x207FFFEF,
0x47E0060F,
0x0048BFFF,
0x01007FFF
};
// TODO:
// - Pass enough information (capabilities, process creation info, etc) on ServiceEntry for proper initialization.
// - Have the ThreadStart function take the syscall, address space and thread context parameters instead of passing them here.
KernelStatic.StartInitialProcess(KernelContext, creationInfo, defaultCapabilities, 44, () =>
{
service.Start(KernelContext.Syscall, KernelStatic.GetCurrentProcess().CpuMemory, KernelStatic.GetCurrentThread().ThreadContext);
});
}
} }
public void LoadKip(string kipPath) public void LoadKip(string kipPath)

View File

@@ -1,3 +1,4 @@
using Ryujinx.Horizon.Common;
using System.Diagnostics; using System.Diagnostics;
using System.Threading; using System.Threading;
@@ -16,24 +17,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
_referenceCount = 1; _referenceCount = 1;
} }
public virtual KernelResult SetName(string name) public virtual Result SetName(string name)
{ {
if (!KernelContext.AutoObjectNames.TryAdd(name, this)) if (!KernelContext.AutoObjectNames.TryAdd(name, this))
{ {
return KernelResult.InvalidState; return KernelResult.InvalidState;
} }
return KernelResult.Success; return Result.Success;
} }
public static KernelResult RemoveName(KernelContext context, string name) public static Result RemoveName(KernelContext context, string name)
{ {
if (!context.AutoObjectNames.TryRemove(name, out _)) if (!context.AutoObjectNames.TryRemove(name, out _))
{ {
return KernelResult.NotFound; return KernelResult.NotFound;
} }
return KernelResult.Success; return Result.Success;
} }
public static KAutoObject FindNamedObject(KernelContext context, string name) public static KAutoObject FindNamedObject(KernelContext context, string name)

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Common namespace Ryujinx.HLE.HOS.Kernel.Common
@@ -159,7 +160,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
} }
} }
public KernelResult SetLimitValue(LimitableResource resource, long limit) public Result SetLimitValue(LimitableResource resource, long limit)
{ {
int index = GetIndex(resource); int index = GetIndex(resource);
@@ -170,7 +171,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
_limit[index] = limit; _limit[index] = limit;
_peak[index] = _current[index]; _peak[index] = _current[index];
return KernelResult.Success; return Result.Success;
} }
else else
{ {

View File

@@ -44,7 +44,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public void ScheduleFutureInvocation(IKFutureSchedulerObject schedulerObj, long timeout) public void ScheduleFutureInvocation(IKFutureSchedulerObject schedulerObj, long timeout)
{ {
long timePoint = PerformanceCounter.ElapsedTicks + ConvertNanosecondsToHostTicks(timeout); long startTime = PerformanceCounter.ElapsedTicks;
long timePoint = startTime + ConvertNanosecondsToHostTicks(timeout);
if (timePoint < startTime)
{
timePoint = long.MaxValue;
}
lock (_context.CriticalSection.Lock) lock (_context.CriticalSection.Lock)
{ {

View File

@@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.Horizon.Common;
using System; using System;
namespace Ryujinx.HLE.HOS.Kernel.Common namespace Ryujinx.HLE.HOS.Kernel.Common
@@ -21,9 +22,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static void InitializeResourceLimit(KResourceLimit resourceLimit, MemorySize size) public static void InitializeResourceLimit(KResourceLimit resourceLimit, MemorySize size)
{ {
void EnsureSuccess(KernelResult result) void EnsureSuccess(Result result)
{ {
if (result != KernelResult.Success) if (result != Result.Success)
{ {
throw new InvalidOperationException($"Unexpected result \"{result}\"."); throw new InvalidOperationException($"Unexpected result \"{result}\".");
} }

View File

@@ -1,37 +0,0 @@
namespace Ryujinx.HLE.HOS.Kernel.Common
{
enum KernelResult
{
Success = 0,
SessionCountExceeded = 0xe01,
InvalidCapability = 0x1c01,
ThreadNotStarted = 0x7201,
ThreadTerminating = 0x7601,
InvalidSize = 0xca01,
InvalidAddress = 0xcc01,
OutOfResource = 0xce01,
OutOfMemory = 0xd001,
HandleTableFull = 0xd201,
InvalidMemState = 0xd401,
InvalidPermission = 0xd801,
InvalidMemRange = 0xdc01,
InvalidPriority = 0xe001,
InvalidCpuCore = 0xe201,
InvalidHandle = 0xe401,
UserCopyFailed = 0xe601,
InvalidCombination = 0xe801,
TimedOut = 0xea01,
Cancelled = 0xec01,
MaximumExceeded = 0xee01,
InvalidEnumValue = 0xf001,
NotFound = 0xf201,
InvalidThread = 0xf401,
PortRemoteClosed = 0xf601,
InvalidState = 0xfa01,
ReservedValue = 0xfc01,
PortClosed = 0x10601,
ResLimitExceeded = 0x10801,
OutOfVaSpace = 0x20601,
CmdBufferTooSmall = 0x20801
}
}

View File

@@ -1,11 +0,0 @@
using System;
namespace Ryujinx.HLE.HOS.Kernel.Common
{
readonly struct OnScopeExit : IDisposable
{
private readonly Action _action;
public OnScopeExit(Action action) => _action = action;
public void Dispose() => _action();
}
}

View File

@@ -1,6 +1,6 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.Horizon.Common;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Ipc namespace Ryujinx.HLE.HOS.Kernel.Ipc
@@ -20,38 +20,38 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
_exchangeBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount); _exchangeBufferDescriptors = new List<KBufferDescriptor>(MaxInternalBuffersCount);
} }
public KernelResult AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state) public Result AddSendBuffer(ulong src, ulong dst, ulong size, MemoryState state)
{ {
return Add(_sendBufferDescriptors, src, dst, size, state); return Add(_sendBufferDescriptors, src, dst, size, state);
} }
public KernelResult AddReceiveBuffer(ulong src, ulong dst, ulong size, MemoryState state) public Result AddReceiveBuffer(ulong src, ulong dst, ulong size, MemoryState state)
{ {
return Add(_receiveBufferDescriptors, src, dst, size, state); return Add(_receiveBufferDescriptors, src, dst, size, state);
} }
public KernelResult AddExchangeBuffer(ulong src, ulong dst, ulong size, MemoryState state) public Result AddExchangeBuffer(ulong src, ulong dst, ulong size, MemoryState state)
{ {
return Add(_exchangeBufferDescriptors, src, dst, size, state); return Add(_exchangeBufferDescriptors, src, dst, size, state);
} }
private KernelResult Add(List<KBufferDescriptor> list, ulong src, ulong dst, ulong size, MemoryState state) private Result Add(List<KBufferDescriptor> list, ulong src, ulong dst, ulong size, MemoryState state)
{ {
if (list.Count < MaxInternalBuffersCount) if (list.Count < MaxInternalBuffersCount)
{ {
list.Add(new KBufferDescriptor(src, dst, size, state)); list.Add(new KBufferDescriptor(src, dst, size, state));
return KernelResult.Success; return Result.Success;
} }
return KernelResult.OutOfMemory; return KernelResult.OutOfMemory;
} }
public KernelResult CopyBuffersToClient(KPageTableBase memoryManager) public Result CopyBuffersToClient(KPageTableBase memoryManager)
{ {
KernelResult result = CopyToClient(memoryManager, _receiveBufferDescriptors); Result result = CopyToClient(memoryManager, _receiveBufferDescriptors);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
@@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return CopyToClient(memoryManager, _exchangeBufferDescriptors); return CopyToClient(memoryManager, _exchangeBufferDescriptors);
} }
private KernelResult CopyToClient(KPageTableBase memoryManager, List<KBufferDescriptor> list) private Result CopyToClient(KPageTableBase memoryManager, List<KBufferDescriptor> list)
{ {
foreach (KBufferDescriptor desc in list) foreach (KBufferDescriptor desc in list)
{ {
@@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
copySize = desc.Size; copySize = desc.Size;
} }
KernelResult result = memoryManager.CopyDataFromCurrentProcess( Result result = memoryManager.CopyDataFromCurrentProcess(
desc.ClientAddress, desc.ClientAddress,
copySize, copySize,
stateMask, stateMask,
@@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
MemoryAttribute.None, MemoryAttribute.None,
desc.ServerAddress); desc.ServerAddress);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
@@ -120,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
if (clientEndAddrTruncated < clientEndAddrRounded && if (clientEndAddrTruncated < clientEndAddrRounded &&
(clientAddrTruncated == clientAddrRounded || clientAddrTruncated < clientEndAddrTruncated)) (clientAddrTruncated == clientAddrRounded || clientAddrTruncated < clientEndAddrTruncated))
{ {
KernelResult result = memoryManager.CopyDataFromCurrentProcess( Result result = memoryManager.CopyDataFromCurrentProcess(
clientEndAddrTruncated, clientEndAddrTruncated,
clientEndAddr - clientEndAddrTruncated, clientEndAddr - clientEndAddrTruncated,
stateMask, stateMask,
@@ -130,28 +130,28 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
MemoryAttribute.None, MemoryAttribute.None,
serverEndAddrTruncated); serverEndAddrTruncated);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
} }
} }
return KernelResult.Success; return Result.Success;
} }
public KernelResult UnmapServerBuffers(KPageTableBase memoryManager) public Result UnmapServerBuffers(KPageTableBase memoryManager)
{ {
KernelResult result = UnmapServer(memoryManager, _sendBufferDescriptors); Result result = UnmapServer(memoryManager, _sendBufferDescriptors);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
result = UnmapServer(memoryManager, _receiveBufferDescriptors); result = UnmapServer(memoryManager, _receiveBufferDescriptors);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
@@ -159,36 +159,36 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return UnmapServer(memoryManager, _exchangeBufferDescriptors); return UnmapServer(memoryManager, _exchangeBufferDescriptors);
} }
private KernelResult UnmapServer(KPageTableBase memoryManager, List<KBufferDescriptor> list) private Result UnmapServer(KPageTableBase memoryManager, List<KBufferDescriptor> list)
{ {
foreach (KBufferDescriptor descriptor in list) foreach (KBufferDescriptor descriptor in list)
{ {
KernelResult result = memoryManager.UnmapNoAttributeIfStateEquals( Result result = memoryManager.UnmapNoAttributeIfStateEquals(
descriptor.ServerAddress, descriptor.ServerAddress,
descriptor.Size, descriptor.Size,
descriptor.State); descriptor.State);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
} }
return KernelResult.Success; return Result.Success;
} }
public KernelResult RestoreClientBuffers(KPageTableBase memoryManager) public Result RestoreClientBuffers(KPageTableBase memoryManager)
{ {
KernelResult result = RestoreClient(memoryManager, _sendBufferDescriptors); Result result = RestoreClient(memoryManager, _sendBufferDescriptors);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
result = RestoreClient(memoryManager, _receiveBufferDescriptors); result = RestoreClient(memoryManager, _receiveBufferDescriptors);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
@@ -196,22 +196,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return RestoreClient(memoryManager, _exchangeBufferDescriptors); return RestoreClient(memoryManager, _exchangeBufferDescriptors);
} }
private KernelResult RestoreClient(KPageTableBase memoryManager, List<KBufferDescriptor> list) private Result RestoreClient(KPageTableBase memoryManager, List<KBufferDescriptor> list)
{ {
foreach (KBufferDescriptor descriptor in list) foreach (KBufferDescriptor descriptor in list)
{ {
KernelResult result = memoryManager.UnmapIpcRestorePermission( Result result = memoryManager.UnmapIpcRestorePermission(
descriptor.ClientAddress, descriptor.ClientAddress,
descriptor.Size, descriptor.Size,
descriptor.State); descriptor.State);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
} }
return KernelResult.Success; return Result.Success;
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.Horizon.Common;
using System.Threading; using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.Ipc namespace Ryujinx.HLE.HOS.Kernel.Ipc
@@ -19,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
_parent = parent; _parent = parent;
} }
public KernelResult Connect(out KClientSession clientSession) public Result Connect(out KClientSession clientSession)
{ {
clientSession = null; clientSession = null;
@@ -40,9 +41,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
KSession session = new KSession(KernelContext, this); KSession session = new KSession(KernelContext, this);
KernelResult result = _parent.EnqueueIncomingSession(session.ServerSession); Result result = _parent.EnqueueIncomingSession(session.ServerSession);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
session.ClientSession.DecrementReferenceCount(); session.ClientSession.DecrementReferenceCount();
session.ServerSession.DecrementReferenceCount(); session.ServerSession.DecrementReferenceCount();
@@ -55,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return result; return result;
} }
public KernelResult ConnectLight(out KLightClientSession clientSession) public Result ConnectLight(out KLightClientSession clientSession)
{ {
clientSession = null; clientSession = null;
@@ -76,9 +77,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
KLightSession session = new KLightSession(KernelContext); KLightSession session = new KLightSession(KernelContext);
KernelResult result = _parent.EnqueueIncomingLightSession(session.ServerSession); Result result = _parent.EnqueueIncomingLightSession(session.ServerSession);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
session.ClientSession.DecrementReferenceCount(); session.ClientSession.DecrementReferenceCount();
session.ServerSession.DecrementReferenceCount(); session.ServerSession.DecrementReferenceCount();
@@ -128,7 +129,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
} }
} }
public new static KernelResult RemoveName(KernelContext context, string name) public new static Result RemoveName(KernelContext context, string name)
{ {
KAutoObject foundObj = FindNamedObject(context, name); KAutoObject foundObj = FindNamedObject(context, name);

View File

@@ -1,6 +1,7 @@
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
namespace Ryujinx.HLE.HOS.Kernel.Ipc namespace Ryujinx.HLE.HOS.Kernel.Ipc
{ {
@@ -27,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
CreatorProcess.IncrementReferenceCount(); CreatorProcess.IncrementReferenceCount();
} }
public KernelResult SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) public Result SendSyncRequest(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{ {
KThread currentThread = KernelStatic.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
@@ -36,13 +37,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
currentThread.SignaledObj = null; currentThread.SignaledObj = null;
currentThread.ObjSyncResult = KernelResult.Success; currentThread.ObjSyncResult = Result.Success;
KernelResult result = _parent.ServerSession.EnqueueRequest(request); Result result = _parent.ServerSession.EnqueueRequest(request);
KernelContext.CriticalSection.Leave(); KernelContext.CriticalSection.Leave();
if (result == KernelResult.Success) if (result == Result.Success)
{ {
result = currentThread.ObjSyncResult; result = currentThread.ObjSyncResult;
} }
@@ -50,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return result; return result;
} }
public KernelResult SendAsyncRequest(KWritableEvent asyncEvent, ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) public Result SendAsyncRequest(KWritableEvent asyncEvent, ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{ {
KThread currentThread = KernelStatic.GetCurrentThread(); KThread currentThread = KernelStatic.GetCurrentThread();
@@ -58,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
KernelResult result = _parent.ServerSession.EnqueueRequest(request); Result result = _parent.ServerSession.EnqueueRequest(request);
KernelContext.CriticalSection.Leave(); KernelContext.CriticalSection.Leave();

View File

@@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.Horizon.Common;
namespace Ryujinx.HLE.HOS.Kernel.Ipc namespace Ryujinx.HLE.HOS.Kernel.Ipc
{ {
@@ -7,26 +8,26 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
public KServerPort ServerPort { get; } public KServerPort ServerPort { get; }
public KClientPort ClientPort { get; } public KClientPort ClientPort { get; }
private long _nameAddress; private string _name;
private ChannelState _state; private ChannelState _state;
public bool IsLight { get; private set; } public bool IsLight { get; private set; }
public KPort(KernelContext context, int maxSessions, bool isLight, long nameAddress) : base(context) public KPort(KernelContext context, int maxSessions, bool isLight, string name) : base(context)
{ {
ServerPort = new KServerPort(context, this); ServerPort = new KServerPort(context, this);
ClientPort = new KClientPort(context, this, maxSessions); ClientPort = new KClientPort(context, this, maxSessions);
IsLight = isLight; IsLight = isLight;
_nameAddress = nameAddress; _name = name;
_state = ChannelState.Open; _state = ChannelState.Open;
} }
public KernelResult EnqueueIncomingSession(KServerSession session) public Result EnqueueIncomingSession(KServerSession session)
{ {
KernelResult result; Result result;
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
@@ -34,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{ {
ServerPort.EnqueueIncomingSession(session); ServerPort.EnqueueIncomingSession(session);
result = KernelResult.Success; result = Result.Success;
} }
else else
{ {
@@ -46,9 +47,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return result; return result;
} }
public KernelResult EnqueueIncomingLightSession(KLightServerSession session) public Result EnqueueIncomingLightSession(KLightServerSession session)
{ {
KernelResult result; Result result;
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
@@ -56,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
{ {
ServerPort.EnqueueIncomingLightSession(session); ServerPort.EnqueueIncomingLightSession(session);
result = KernelResult.Success; result = Result.Success;
} }
else else
{ {

View File

@@ -3,6 +3,7 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Ipc namespace Ryujinx.HLE.HOS.Kernel.Ipc
@@ -178,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
_requests = new LinkedList<KSessionRequest>(); _requests = new LinkedList<KSessionRequest>();
} }
public KernelResult EnqueueRequest(KSessionRequest request) public Result EnqueueRequest(KSessionRequest request)
{ {
if (_parent.ClientSession.State != ChannelState.Open) if (_parent.ClientSession.State != ChannelState.Open)
{ {
@@ -203,10 +204,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
Signal(); Signal();
} }
return KernelResult.Success; return Result.Success;
} }
public KernelResult Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) public Result Receive(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{ {
KThread serverThread = KernelStatic.GetCurrentThread(); KThread serverThread = KernelStatic.GetCurrentThread();
KProcess serverProcess = serverThread.Owner; KProcess serverProcess = serverThread.Owner;
@@ -249,12 +250,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
MessageHeader clientHeader = GetClientMessageHeader(clientProcess, clientMsg); MessageHeader clientHeader = GetClientMessageHeader(clientProcess, clientMsg);
MessageHeader serverHeader = GetServerMessageHeader(serverMsg); MessageHeader serverHeader = GetServerMessageHeader(serverMsg);
KernelResult serverResult = KernelResult.NotFound; Result serverResult = KernelResult.NotFound;
KernelResult clientResult = KernelResult.Success; Result clientResult = Result.Success;
void CleanUpForError() void CleanUpForError()
{ {
if (request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager) == KernelResult.Success) if (request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager) == Result.Success)
{ {
request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager); request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager);
} }
@@ -348,7 +349,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
int newHandle = 0; int newHandle = 0;
int handle = clientProcess.CpuMemory.Read<int>(clientMsg.Address + offset * 4); int handle = clientProcess.CpuMemory.Read<int>(clientMsg.Address + offset * 4);
if (clientResult == KernelResult.Success && handle != 0) if (clientResult == Result.Success && handle != 0)
{ {
clientResult = GetCopyObjectHandle(clientThread, serverProcess, handle, out newHandle); clientResult = GetCopyObjectHandle(clientThread, serverProcess, handle, out newHandle);
} }
@@ -365,7 +366,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
if (handle != 0) if (handle != 0)
{ {
if (clientResult == KernelResult.Success) if (clientResult == Result.Success)
{ {
clientResult = GetMoveObjectHandle(clientProcess, serverProcess, handle, out newHandle); clientResult = GetMoveObjectHandle(clientProcess, serverProcess, handle, out newHandle);
} }
@@ -380,7 +381,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
offset++; offset++;
} }
if (clientResult != KernelResult.Success) if (clientResult != Result.Success)
{ {
CleanUpForError(); CleanUpForError();
@@ -412,7 +413,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
ref recvListDstOffset, ref recvListDstOffset,
out ulong recvListBufferAddress); out ulong recvListBufferAddress);
if (clientResult != KernelResult.Success) if (clientResult != Result.Success)
{ {
CleanUpForError(); CleanUpForError();
@@ -429,7 +430,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
MemoryAttribute.Uncached, MemoryAttribute.Uncached,
MemoryAttribute.None); MemoryAttribute.None);
if (clientResult != KernelResult.Success) if (clientResult != Result.Success)
{ {
CleanUpForError(); CleanUpForError();
@@ -498,7 +499,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
notReceiveDesc, notReceiveDesc,
out dstAddress); out dstAddress);
if (clientResult != KernelResult.Success) if (clientResult != Result.Success)
{ {
CleanUpForError(); CleanUpForError();
@@ -518,7 +519,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
clientResult = request.BufferDescriptorTable.AddExchangeBuffer(bufferAddress, dstAddress, bufferSize, state); clientResult = request.BufferDescriptorTable.AddExchangeBuffer(bufferAddress, dstAddress, bufferSize, state);
} }
if (clientResult != KernelResult.Success) if (clientResult != Result.Success)
{ {
CleanUpForError(); CleanUpForError();
@@ -573,7 +574,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
serverProcess.CpuMemory.Write(copyDst, clientProcess.CpuMemory.GetSpan(copySrc, (int)copySize)); serverProcess.CpuMemory.Write(copyDst, clientProcess.CpuMemory.GetSpan(copySrc, (int)copySize));
} }
if (clientResult != KernelResult.Success) if (clientResult != Result.Success)
{ {
CleanUpForError(); CleanUpForError();
@@ -581,10 +582,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
} }
} }
return KernelResult.Success; return Result.Success;
} }
public KernelResult Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0) public Result Reply(ulong customCmdBuffAddr = 0, ulong customCmdBuffSize = 0)
{ {
KThread serverThread = KernelStatic.GetCurrentThread(); KThread serverThread = KernelStatic.GetCurrentThread();
KProcess serverProcess = serverThread.Owner; KProcess serverProcess = serverThread.Owner;
@@ -618,8 +619,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
MessageHeader clientHeader = GetClientMessageHeader(clientProcess, clientMsg); MessageHeader clientHeader = GetClientMessageHeader(clientProcess, clientMsg);
MessageHeader serverHeader = GetServerMessageHeader(serverMsg); MessageHeader serverHeader = GetServerMessageHeader(serverMsg);
KernelResult clientResult = KernelResult.Success; Result clientResult = Result.Success;
KernelResult serverResult = KernelResult.Success; Result serverResult = Result.Success;
void CleanUpForError() void CleanUpForError()
{ {
@@ -683,7 +684,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
// Copy receive and exchange buffers. // Copy receive and exchange buffers.
clientResult = request.BufferDescriptorTable.CopyBuffersToClient(clientProcess.MemoryManager); clientResult = request.BufferDescriptorTable.CopyBuffersToClient(clientProcess.MemoryManager);
if (clientResult != KernelResult.Success) if (clientResult != Result.Success)
{ {
CleanUpForError(); CleanUpForError();
@@ -734,7 +735,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
if (handle != 0) if (handle != 0)
{ {
if (clientResult == KernelResult.Success) if (clientResult == Result.Success)
{ {
clientResult = GetMoveObjectHandle(serverProcess, clientProcess, handle, out newHandle); clientResult = GetMoveObjectHandle(serverProcess, clientProcess, handle, out newHandle);
} }
@@ -776,7 +777,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
ref recvListDstOffset, ref recvListDstOffset,
out recvListBufferAddress); out recvListBufferAddress);
if (clientResult != KernelResult.Success) if (clientResult != Result.Success)
{ {
CleanUpForError(); CleanUpForError();
@@ -793,7 +794,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
MemoryAttribute.None, MemoryAttribute.None,
descriptor.BufferAddress); descriptor.BufferAddress);
if (clientResult != KernelResult.Success) if (clientResult != Result.Success)
{ {
CleanUpForError(); CleanUpForError();
@@ -888,7 +889,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return new MessageHeader(word0, word1, word2); return new MessageHeader(word0, word1, word2);
} }
private KernelResult GetCopyObjectHandle(KThread srcThread, KProcess dstProcess, int srcHandle, out int dstHandle) private Result GetCopyObjectHandle(KThread srcThread, KProcess dstProcess, int srcHandle, out int dstHandle)
{ {
dstHandle = 0; dstHandle = 0;
@@ -919,7 +920,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
} }
} }
private KernelResult GetMoveObjectHandle(KProcess srcProcess, KProcess dstProcess, int srcHandle, out int dstHandle) private Result GetMoveObjectHandle(KProcess srcProcess, KProcess dstProcess, int srcHandle, out int dstHandle)
{ {
dstHandle = 0; dstHandle = 0;
@@ -927,7 +928,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
if (obj != null) if (obj != null)
{ {
KernelResult result = dstProcess.HandleTable.GenerateHandle(obj, out dstHandle); Result result = dstProcess.HandleTable.GenerateHandle(obj, out dstHandle);
srcProcess.HandleTable.CloseHandle(srcHandle); srcProcess.HandleTable.CloseHandle(srcHandle);
@@ -964,7 +965,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return receiveList; return receiveList;
} }
private KernelResult GetReceiveListAddress( private Result GetReceiveListAddress(
PointerBufferDesc descriptor, PointerBufferDesc descriptor,
Message message, Message message,
uint recvListType, uint recvListType,
@@ -1038,7 +1039,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
address = recvListBufferAddress; address = recvListBufferAddress;
return KernelResult.Success; return Result.Success;
} }
private void CloseAllHandles(Message message, MessageHeader header, KProcess process) private void CloseAllHandles(Message message, MessageHeader header, KProcess process)
@@ -1166,19 +1167,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
return hasRequest; return hasRequest;
} }
private void FinishRequest(KSessionRequest request, KernelResult result) private void FinishRequest(KSessionRequest request, Result result)
{ {
KProcess clientProcess = request.ClientThread.Owner; KProcess clientProcess = request.ClientThread.Owner;
KProcess serverProcess = request.ServerProcess; KProcess serverProcess = request.ServerProcess;
KernelResult unmapResult = KernelResult.Success; Result unmapResult = Result.Success;
if (serverProcess != null) if (serverProcess != null)
{ {
unmapResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager); unmapResult = request.BufferDescriptorTable.UnmapServerBuffers(serverProcess.MemoryManager);
} }
if (unmapResult == KernelResult.Success) if (unmapResult == Result.Success)
{ {
request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager); request.BufferDescriptorTable.RestoreClientBuffers(clientProcess.MemoryManager);
} }
@@ -1186,7 +1187,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
WakeClientThread(request, result); WakeClientThread(request, result);
} }
private void WakeClientThread(KSessionRequest request, KernelResult result) private void WakeClientThread(KSessionRequest request, Result result)
{ {
// Wait client thread waiting for a response for the given request. // Wait client thread waiting for a response for the given request.
if (request.AsyncEvent != null) if (request.AsyncEvent != null)
@@ -1203,16 +1204,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
} }
} }
private void SendResultToAsyncRequestClient(KSessionRequest request, KernelResult result) private void SendResultToAsyncRequestClient(KSessionRequest request, Result result)
{ {
KProcess clientProcess = request.ClientThread.Owner; KProcess clientProcess = request.ClientThread.Owner;
if (result != KernelResult.Success) if (result != Result.Success)
{ {
ulong address = request.CustomCmdBuffAddr; ulong address = request.CustomCmdBuffAddr;
clientProcess.CpuMemory.Write<ulong>(address, 0); clientProcess.CpuMemory.Write<ulong>(address, 0);
clientProcess.CpuMemory.Write(address + 8, (int)result); clientProcess.CpuMemory.Write(address + 8, result.ErrorCode);
} }
clientProcess.MemoryManager.UnborrowIpcBuffer(request.CustomCmdBuffAddr, request.CustomCmdBuffSize); clientProcess.MemoryManager.UnborrowIpcBuffer(request.CustomCmdBuffAddr, request.CustomCmdBuffSize);
@@ -1220,24 +1221,24 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
request.AsyncEvent.Signal(); request.AsyncEvent.Signal();
} }
private void WakeServerThreads(KernelResult result) private void WakeServerThreads(Result result)
{ {
// Wake all server threads waiting for requests. // Wake all server threads waiting for requests.
KernelContext.CriticalSection.Enter(); KernelContext.CriticalSection.Enter();
foreach (KThread thread in WaitingThreads) foreach (KThread thread in WaitingThreads)
{ {
WakeAndSetResult(thread, result); WakeAndSetResult(thread, result, this);
} }
KernelContext.CriticalSection.Leave(); KernelContext.CriticalSection.Leave();
} }
private void WakeAndSetResult(KThread thread, KernelResult result) private void WakeAndSetResult(KThread thread, Result result, KSynchronizationObject signaledObj = null)
{ {
if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused) if ((thread.SchedFlags & ThreadSchedState.LowMask) == ThreadSchedState.Paused)
{ {
thread.SignaledObj = null; thread.SignaledObj = signaledObj;
thread.ObjSyncResult = result; thread.ObjSyncResult = result;
thread.Reschedule(ThreadSchedState.Running); thread.Reschedule(ThreadSchedState.Running);

View File

@@ -2,6 +2,7 @@
using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
using System; using System;
using System.Threading; using System.Threading;
@@ -15,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Kernel
[ThreadStatic] [ThreadStatic]
private static KThread CurrentThread; private static KThread CurrentThread;
public static KernelResult StartInitialProcess( public static Result StartInitialProcess(
KernelContext context, KernelContext context,
ProcessCreationInfo creationInfo, ProcessCreationInfo creationInfo,
ReadOnlySpan<int> capabilities, ReadOnlySpan<int> capabilities,
@@ -24,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Kernel
{ {
KProcess process = new KProcess(context); KProcess process = new KProcess(context);
KernelResult result = process.Initialize( Result result = process.Initialize(
creationInfo, creationInfo,
capabilities, capabilities,
context.ResourceLimit, context.ResourceLimit,
@@ -32,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel
null, null,
customThreadStart); customThreadStart);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }

View File

@@ -1,6 +1,7 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.Horizon.Common;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@@ -21,13 +22,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_lock = new object(); _lock = new object();
} }
public KernelResult Initialize(ulong address, ulong size) public Result Initialize(ulong address, ulong size)
{ {
Owner = KernelStatic.GetCurrentProcess(); Owner = KernelStatic.GetCurrentProcess();
KernelResult result = Owner.MemoryManager.BorrowCodeMemory(_pageList, address, size); Result result = Owner.MemoryManager.BorrowCodeMemory(_pageList, address, size);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
@@ -39,10 +40,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_isMapped = false; _isMapped = false;
_isOwnerMapped = false; _isOwnerMapped = false;
return KernelResult.Success; return Result.Success;
} }
public KernelResult Map(ulong address, ulong size, KMemoryPermission perm) public Result Map(ulong address, ulong size, KMemoryPermission perm)
{ {
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize)) if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{ {
@@ -58,9 +59,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
KProcess process = KernelStatic.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KernelResult result = process.MemoryManager.MapPages(address, _pageList, MemoryState.CodeWritable, KMemoryPermission.ReadAndWrite); Result result = process.MemoryManager.MapPages(address, _pageList, MemoryState.CodeWritable, KMemoryPermission.ReadAndWrite);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
@@ -68,10 +69,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_isMapped = true; _isMapped = true;
} }
return KernelResult.Success; return Result.Success;
} }
public KernelResult MapToOwner(ulong address, ulong size, KMemoryPermission permission) public Result MapToOwner(ulong address, ulong size, KMemoryPermission permission)
{ {
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize)) if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{ {
@@ -87,9 +88,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
Debug.Assert(permission == KMemoryPermission.Read || permission == KMemoryPermission.ReadAndExecute); Debug.Assert(permission == KMemoryPermission.Read || permission == KMemoryPermission.ReadAndExecute);
KernelResult result = Owner.MemoryManager.MapPages(address, _pageList, MemoryState.CodeReadOnly, permission); Result result = Owner.MemoryManager.MapPages(address, _pageList, MemoryState.CodeReadOnly, permission);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
@@ -97,10 +98,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_isOwnerMapped = true; _isOwnerMapped = true;
} }
return KernelResult.Success; return Result.Success;
} }
public KernelResult Unmap(ulong address, ulong size) public Result Unmap(ulong address, ulong size)
{ {
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize)) if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{ {
@@ -111,9 +112,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{ {
KProcess process = KernelStatic.GetCurrentProcess(); KProcess process = KernelStatic.GetCurrentProcess();
KernelResult result = process.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeWritable); Result result = process.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeWritable);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
@@ -123,10 +124,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_isMapped = false; _isMapped = false;
} }
return KernelResult.Success; return Result.Success;
} }
public KernelResult UnmapFromOwner(ulong address, ulong size) public Result UnmapFromOwner(ulong address, ulong size)
{ {
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize)) if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
{ {
@@ -135,9 +136,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
lock (_lock) lock (_lock)
{ {
KernelResult result = Owner.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeReadOnly); Result result = Owner.MemoryManager.UnmapPages(address, _pageList, MemoryState.CodeReadOnly);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
return result; return result;
} }
@@ -147,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_isOwnerMapped = false; _isOwnerMapped = false;
} }
return KernelResult.Success; return Result.Success;
} }
protected override void Destroy() protected override void Destroy()
@@ -156,7 +157,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{ {
ulong size = _pageList.GetPagesCount() * KPageTableBase.PageSize; ulong size = _pageList.GetPagesCount() * KPageTableBase.PageSize;
if (Owner.MemoryManager.UnborrowCodeMemory(_address, size, _pageList) != KernelResult.Success) if (Owner.MemoryManager.UnborrowCodeMemory(_address, size, _pageList) != Result.Success)
{ {
throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes."); throw new InvalidOperationException("Unexpected failure restoring transfer memory attributes.");
} }

View File

@@ -1,5 +1,5 @@
using Ryujinx.Common.Collections; using Ryujinx.Common.Collections;
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.Horizon.Common;
using System.Diagnostics; using System.Diagnostics;
namespace Ryujinx.HLE.HOS.Kernel.Memory namespace Ryujinx.HLE.HOS.Kernel.Memory
@@ -22,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_blockTree = new IntrusiveRedBlackTree<KMemoryBlock>(); _blockTree = new IntrusiveRedBlackTree<KMemoryBlock>();
} }
public KernelResult Initialize(ulong addrSpaceStart, ulong addrSpaceEnd, KMemoryBlockSlabManager slabManager) public Result Initialize(ulong addrSpaceStart, ulong addrSpaceEnd, KMemoryBlockSlabManager slabManager)
{ {
_slabManager = slabManager; _slabManager = slabManager;
_addrSpaceStart = addrSpaceStart; _addrSpaceStart = addrSpaceStart;
@@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
KMemoryPermission.None, KMemoryPermission.None,
MemoryAttribute.None)); MemoryAttribute.None));
return KernelResult.Success; return Result.Success;
} }
public void InsertBlock( public void InsertBlock(

View File

@@ -1,4 +1,4 @@
using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.Horizon.Common;
using System.Diagnostics; using System.Diagnostics;
namespace Ryujinx.HLE.HOS.Kernel.Memory namespace Ryujinx.HLE.HOS.Kernel.Memory
@@ -25,20 +25,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_pageHeap.UpdateUsedSize(); _pageHeap.UpdateUsedSize();
} }
public KernelResult AllocatePages(out KPageList pageList, ulong pagesCount) public Result AllocatePages(out KPageList pageList, ulong pagesCount)
{ {
if (pagesCount == 0) if (pagesCount == 0)
{ {
pageList = new KPageList(); pageList = new KPageList();
return KernelResult.Success; return Result.Success;
} }
lock (_pageHeap) lock (_pageHeap)
{ {
KernelResult result = AllocatePagesImpl(out pageList, pagesCount, false); Result result = AllocatePagesImpl(out pageList, pagesCount, false);
if (result == KernelResult.Success) if (result == Result.Success)
{ {
foreach (var node in pageList) foreach (var node in pageList)
{ {
@@ -71,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
} }
} }
private KernelResult AllocatePagesImpl(out KPageList pageList, ulong pagesCount, bool random) private Result AllocatePagesImpl(out KPageList pageList, ulong pagesCount, bool random)
{ {
pageList = new KPageList(); pageList = new KPageList();
@@ -95,9 +95,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
break; break;
} }
KernelResult result = pageList.AddRange(allocatedBlock, pagesPerAlloc); Result result = pageList.AddRange(allocatedBlock, pagesPerAlloc);
if (result != KernelResult.Success) if (result != Result.Success)
{ {
FreePages(pageList); FreePages(pageList);
_pageHeap.Free(allocatedBlock, pagesPerAlloc); _pageHeap.Free(allocatedBlock, pagesPerAlloc);
@@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return KernelResult.OutOfMemory; return KernelResult.OutOfMemory;
} }
return KernelResult.Success; return Result.Success;
} }
private ulong AllocatePagesContiguousImpl(ulong pagesCount, ulong alignPages, bool random) private ulong AllocatePagesContiguousImpl(ulong pagesCount, ulong alignPages, bool random)

Some files were not shown because too many files have changed in this diff Show More