Compare commits

..

13 Commits

Author SHA1 Message Date
600f86dc7b Fix context menu locales (#4242) 2023-01-10 19:15:15 +01:00
7210c17c5e misc: Enforce LF (#4253)
Because we are building everything on Windows for release at the moment,
git default line ending to CRLF causing issues when packing the
Ryujinx.sh script.

This addresses this by enforcing all files to use LF via .gitattributes.
2023-01-10 19:00:14 +01:00
a16854e55a ava: Cleanup AppHost (#4240)
* ava: Cleanup AppHost

This PR cleaned up the AppHost file a bit (adding the infamous extra spaces to improve readability), resorting private vars, remove useless vars, and improve the code here and there, like the AudioBackend check.

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

* Remove 'renderer"

* Revert currentTime

* revert if condition

Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com>
2023-01-10 18:45:55 +01:00
3e455a90a1 Ava: Add missing null check to ContentDialogHelper.ShowAsync() (#4248)
* ava: Add missing null check to ContentDialogHelper.ShowAsync()

* Replace "is not" with != operator

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

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-01-10 09:22:25 +01:00
e4413542b2 Add command line arguments to override docked mode (#4239)
* Add command line args for docked mode

* Apply suggestions from code review

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

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-10 00:59:23 +01:00
8c720783f5 linux: Fix packaging step for CI & Add Ryujinx.Headless.SDL2 to Ryujinx.sh (#4249) 2023-01-09 23:45:46 +00:00
8734ea9dd4 Linux: Add Avalonia detection to Ryujinx.sh (#4224)
* Revert "ava: Fix regression caused by #4013 (#4222)"

This reverts commit b9f2a96595.

* linux: Detect Ryujinx.Ava and don't rename the Ryujinx.Ava assembly
2023-01-09 22:58:51 +01:00
c586e6d2b7 Replace tabs with spaces across the project (#4244)
* Replace tabs with spaces across the project

* Include AXAML files too
2023-01-09 22:58:29 +01:00
3a4eeb77fe headless: Change window icon size to 48x48 (#4247) 2023-01-09 18:02:41 +00:00
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
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
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
ee6e682ab4 Fix selection bar (#4236) 2023-01-09 03:36:11 +01:00
51 changed files with 2837 additions and 2637 deletions

61
.gitattributes vendored
View File

@ -1,63 +1,4 @@
############################################################################### ###############################################################################
# Set default behavior to automatically normalize line endings. # Set default behavior to automatically normalize line endings.
############################################################################### ###############################################################################
* text=auto * text=auto eol=lf
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain

View File

@ -69,12 +69,6 @@ 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

@ -46,13 +46,11 @@ jobs:
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
@ -73,26 +71,29 @@ 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 --exclude "publish/Ryujinx" -cvf ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish tar --exclude "publish/Ryujinx" --exclude "publish/Ryujinx.sh" -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" python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx" "publish/Ryujinx"
python3 ../distribution/misc/add_tar_exec.py ../release_output/ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
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 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 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 --exclude "publish/Ryujinx.Headless.SDL2" -cvf ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish tar --exclude "publish/Ryujinx.Headless.SDL2" --exclude "publish/Ryujinx.sh" -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" 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"
python3 ../distribution/misc/add_tar_exec.py ../release_output/sdl2-ryujinx-headless-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
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 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 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 --exclude "publish/Ryujinx" -cvf ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar publish tar --exclude "publish/Ryujinx.Ava" --exclude "publish/Ryujinx.sh" -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" python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.Ava" "publish/Ryujinx.Ava"
python3 ../distribution/misc/add_tar_exec.py ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar "publish/Ryujinx.sh" "publish/Ryujinx.sh"
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 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 rm ../release_output/test-ava-ryujinx-${{ steps.version_info.outputs.build_version }}-linux_x64.tar
popd popd

View File

@ -15,6 +15,8 @@ namespace Ryujinx.Audio.Backends.CompatLayer
{ {
private IHardwareDeviceDriver _realDriver; private IHardwareDeviceDriver _realDriver;
public static bool IsSupported => true;
public CompatLayerHardwareDeviceDriver(IHardwareDeviceDriver realDevice) public CompatLayerHardwareDeviceDriver(IHardwareDeviceDriver realDevice)
{ {
_realDriver = realDevice; _realDriver = realDevice;

View File

@ -12,6 +12,8 @@ namespace Ryujinx.Audio.Backends.Dummy
private ManualResetEvent _updateRequiredEvent; private ManualResetEvent _updateRequiredEvent;
private ManualResetEvent _pauseEvent; private ManualResetEvent _pauseEvent;
public static bool IsSupported => true;
public DummyHardwareDeviceDriver() public DummyHardwareDeviceDriver()
{ {
_updateRequiredEvent = new ManualResetEvent(false); _updateRequiredEvent = new ManualResetEvent(false);

View File

@ -26,6 +26,8 @@ namespace Ryujinx.Audio.Integration
bool SupportsSampleFormat(SampleFormat sampleFormat); bool SupportsSampleFormat(SampleFormat sampleFormat);
bool SupportsChannelCount(uint channelCount); bool SupportsChannelCount(uint channelCount);
static abstract bool IsSupported { get; }
IHardwareDeviceDriver GetRealDeviceDriver() IHardwareDeviceDriver GetRealDeviceDriver()
{ {
return this; return this;

View File

@ -41,6 +41,7 @@ using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SPB.Graphics.Vulkan; using SPB.Graphics.Vulkan;
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
@ -57,95 +58,96 @@ namespace Ryujinx.Ava
{ {
internal class AppHost internal class AppHost
{ {
private const int CursorHideIdleTime = 8; // Hide Cursor seconds private const int CursorHideIdleTime = 8; // Hide Cursor seconds.
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
private const int TargetFps = 60; private const int TargetFps = 60;
private const float VolumeDelta = 0.05f;
private const float VolumeDelta = 0.05f; private static readonly Cursor InvisibleCursor = new(StandardCursorType.None);
private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None); private readonly long _ticksPerFrame;
private readonly long _ticksPerFrame;
private readonly Stopwatch _chrono; private readonly Stopwatch _chrono;
private readonly AccountManager _accountManager; private long _ticks;
private readonly AccountManager _accountManager;
private readonly UserChannelPersistence _userChannelPersistence; private readonly UserChannelPersistence _userChannelPersistence;
private readonly InputManager _inputManager; private readonly InputManager _inputManager;
private readonly MainWindowViewModel _viewModel; private readonly MainWindowViewModel _viewModel;
private readonly IKeyboard _keyboardInterface; private readonly IKeyboard _keyboardInterface;
private readonly TopLevel _topLevel;
private readonly GraphicsDebugLevel _glLogLevel; private readonly GraphicsDebugLevel _glLogLevel;
private float _newVolume;
private KeyboardHotkeyState _prevHotkeyState;
private bool _hideCursorOnIdle; private bool _hideCursorOnIdle;
private long _lastCursorMoveTime;
private bool _isCursorInRenderer;
private bool _isStopped; private bool _isStopped;
private bool _isActive; private bool _isActive;
private long _lastCursorMoveTime;
private float _newVolume;
private long _ticks = 0;
private KeyboardHotkeyState _prevHotkeyState;
private IRenderer _renderer;
private readonly Thread _renderingThread;
private bool _isMouseInRenderer;
private bool _renderingStarted; private bool _renderingStarted;
private bool _dialogShown;
private IRenderer _renderer;
private readonly Thread _renderingThread;
private readonly CancellationTokenSource _gpuCancellationTokenSource;
private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
private readonly CancellationTokenSource _gpuCancellationTokenSource; private bool _dialogShown;
private readonly bool _isFirmwareTitle;
private readonly object _lockObject = new();
public event EventHandler AppExit; public event EventHandler AppExit;
public event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent; public event EventHandler<StatusUpdatedEventArgs> StatusUpdatedEvent;
public RendererHost Renderer { get; } public RendererHost Renderer { get; }
public VirtualFileSystem VirtualFileSystem { get; } public VirtualFileSystem VirtualFileSystem { get; }
public ContentManager ContentManager { get; } public ContentManager ContentManager { get; }
public Switch Device { get; set; } public NpadManager NpadManager { get; }
public NpadManager NpadManager { get; }
public TouchScreenManager TouchScreenManager { get; } public TouchScreenManager TouchScreenManager { get; }
public Switch Device { get; set; }
public int Width { get; private set; } public int Width { get; private set; }
public int Height { get; private set; } public int Height { get; private set; }
public string ApplicationPath { get; private set; } public string ApplicationPath { get; private set; }
public bool ScreenshotRequested { get; set; }
private bool _isFirmwareTitle;
public bool ScreenshotRequested { get; set; }
private object _lockObject = new();
private TopLevel _topLevel;
public AppHost( public AppHost(
RendererHost renderer, RendererHost renderer,
InputManager inputManager, InputManager inputManager,
string applicationPath, string applicationPath,
VirtualFileSystem virtualFileSystem, VirtualFileSystem virtualFileSystem,
ContentManager contentManager, ContentManager contentManager,
AccountManager accountManager, AccountManager accountManager,
UserChannelPersistence userChannelPersistence, UserChannelPersistence userChannelPersistence,
MainWindowViewModel viewmodel, MainWindowViewModel viewmodel,
TopLevel topLevel) TopLevel topLevel)
{ {
_viewModel = viewmodel; _viewModel = viewmodel;
_inputManager = inputManager; _inputManager = inputManager;
_accountManager = accountManager; _accountManager = accountManager;
_userChannelPersistence = userChannelPersistence; _userChannelPersistence = userChannelPersistence;
_renderingThread = new Thread(RenderLoop, 1 * 1024 * 1024) { Name = "GUI.RenderThread" }; _renderingThread = new Thread(RenderLoop, 1 * 1024 * 1024) { Name = "GUI.RenderThread" };
_hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle; _hideCursorOnIdle = ConfigurationState.Instance.HideCursorOnIdle;
_lastCursorMoveTime = Stopwatch.GetTimestamp(); _lastCursorMoveTime = Stopwatch.GetTimestamp();
_glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel; _glLogLevel = ConfigurationState.Instance.Logger.GraphicsDebugLevel;
_topLevel = topLevel; _topLevel = topLevel;
_inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer)); _inputManager.SetMouseDriver(new AvaloniaMouseDriver(_topLevel, renderer));
_keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0");
NpadManager = _inputManager.CreateNpadManager(); NpadManager = _inputManager.CreateNpadManager();
TouchScreenManager = _inputManager.CreateTouchScreenManager(); TouchScreenManager = _inputManager.CreateTouchScreenManager();
Renderer = renderer; Renderer = renderer;
ApplicationPath = applicationPath; ApplicationPath = applicationPath;
VirtualFileSystem = virtualFileSystem; VirtualFileSystem = virtualFileSystem;
ContentManager = contentManager; ContentManager = contentManager;
_chrono = new Stopwatch(); _chrono = new Stopwatch();
_ticksPerFrame = Stopwatch.Frequency / TargetFps; _ticksPerFrame = Stopwatch.Frequency / TargetFps;
if (ApplicationPath.StartsWith("@SystemContent")) if (ApplicationPath.StartsWith("@SystemContent"))
@ -161,9 +163,9 @@ namespace Ryujinx.Ava
_topLevel.PointerMoved += TopLevel_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;
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
_gpuCancellationTokenSource = new CancellationTokenSource(); _gpuCancellationTokenSource = new CancellationTokenSource();
} }
@ -173,15 +175,17 @@ namespace Ryujinx.Ava
if (sender is Control visual) if (sender is Control visual)
{ {
_lastCursorMoveTime = Stopwatch.GetTimestamp(); _lastCursorMoveTime = Stopwatch.GetTimestamp();
var point = e.GetCurrentPoint(visual).Position; var point = e.GetCurrentPoint(visual).Position;
_isMouseInRenderer = Equals(visual.InputHitTest(point), Renderer);
_isCursorInRenderer = Equals(visual.InputHitTest(point), Renderer);
} }
} }
private void TopLevel_PointerLeave(object sender, PointerEventArgs e) private void TopLevel_PointerLeave(object sender, PointerEventArgs e)
{ {
_isMouseInRenderer = false; _isCursorInRenderer = false;
_viewModel.Cursor = Cursor.Default; _viewModel.Cursor = Cursor.Default;
} }
private void SetRendererWindowSize(Size size) private void SetRendererWindowSize(Size size)
@ -189,6 +193,7 @@ namespace Ryujinx.Ava
if (_renderer != null) if (_renderer != null)
{ {
double scale = _topLevel.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));
} }
} }
@ -201,12 +206,13 @@ namespace Ryujinx.Ava
{ {
lock (_lockObject) lock (_lockObject)
{ {
var currentTime = DateTime.Now; DateTime currentTime = DateTime.Now;
string filename = $"ryujinx_capture_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; string filename = $"ryujinx_capture_{currentTime}-{currentTime:D2}-{currentTime:D2}_{currentTime:D2}-{currentTime:D2}-{currentTime:D2}.png";
string directory = AppDataManager.Mode switch string directory = AppDataManager.Mode switch
{ {
AppDataManager.LaunchMode.Portable => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), AppDataManager.LaunchMode.Portable => Path.Combine(AppDataManager.BaseDirPath, "screenshots"),
_ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx") _ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx")
}; };
string path = Path.Combine(directory, filename); string path = Path.Combine(directory, filename);
@ -266,21 +272,10 @@ namespace Ryujinx.Ava
_viewModel.IsGameRunning = true; _viewModel.IsGameRunning = true;
string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty : $" - {Device.Application.TitleName}";
? string.Empty string titleVersionSection = string.IsNullOrWhiteSpace(Device.Application.DisplayVersion) ? string.Empty : $" v{Device.Application.DisplayVersion}";
: $" - {Device.Application.TitleName}"; string titleIdSection = string.IsNullOrWhiteSpace(Device.Application.TitleIdText) ? string.Empty : $" ({Device.Application.TitleIdText.ToUpper()})";
string titleArchSection = Device.Application.TitleIs64Bit ? " (64-bit)" : " (32-bit)";
string titleVersionSection = string.IsNullOrWhiteSpace(Device.Application.DisplayVersion)
? string.Empty
: $" v{Device.Application.DisplayVersion}";
string titleIdSection = string.IsNullOrWhiteSpace(Device.Application.TitleIdText)
? string.Empty
: $" ({Device.Application.TitleIdText.ToUpper()})";
string titleArchSection = Device.Application.TitleIs64Bit
? " (64-bit)"
: " (32-bit)";
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
@ -326,9 +321,9 @@ namespace Ryujinx.Ava
private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e) private void UpdateAudioVolumeState(object sender, ReactiveEventArgs<float> e)
{ {
Device?.SetVolume(e.NewValue); Device?.SetVolume(e.NewValue);
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
var value = e.NewValue;
_viewModel.Volume = e.NewValue; _viewModel.Volume = e.NewValue;
}); });
} }
@ -348,7 +343,7 @@ namespace Ryujinx.Ava
} }
_isStopped = true; _isStopped = true;
_isActive = false; _isActive = false;
} }
public void DisposeContext() public void DisposeContext()
@ -381,9 +376,9 @@ namespace Ryujinx.Ava
} }
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState; ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
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.PointerLeave -= TopLevel_PointerLeave;
_topLevel.PointerMoved -= TopLevel_PointerMoved; _topLevel.PointerMoved -= TopLevel_PointerMoved;
@ -597,19 +592,23 @@ namespace Ryujinx.Ava
internal void Resume() internal void Resume()
{ {
Device?.System.TogglePauseEmulation(false); Device?.System.TogglePauseEmulation(false);
_viewModel.IsPaused = false; _viewModel.IsPaused = false;
} }
internal void Pause() internal void Pause()
{ {
Device?.System.TogglePauseEmulation(true); Device?.System.TogglePauseEmulation(true);
_viewModel.IsPaused = true; _viewModel.IsPaused = true;
} }
private void InitializeSwitchInstance() private void InitializeSwitchInstance()
{ {
// Initialize KeySet.
VirtualFileSystem.ReloadKeySet(); VirtualFileSystem.ReloadKeySet();
// Initialize Renderer.
IRenderer renderer; IRenderer renderer;
if (Renderer.IsVulkan) if (Renderer.IsVulkan)
@ -623,12 +622,9 @@ namespace Ryujinx.Ava
renderer = new OpenGLRenderer(); renderer = new OpenGLRenderer();
} }
IHardwareDeviceDriver deviceDriver = new DummyHardwareDeviceDriver();
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
var isGALthreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading); var isGALthreaded = threadingMode == BackendThreading.On || (threadingMode == BackendThreading.Auto && renderer.PreferThreading);
if (isGALthreaded) if (isGALthreaded)
{ {
renderer = new ThreadedRenderer(renderer); renderer = new ThreadedRenderer(renderer);
@ -636,159 +632,104 @@ namespace Ryujinx.Ava
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALthreaded}"); Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALthreaded}");
if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SDL2) // Initialize Configuration.
{
if (SDL2HardwareDeviceDriver.IsSupported)
{
deviceDriver = new SDL2HardwareDeviceDriver();
}
else
{
Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to OpenAL.");
if (OpenALHardwareDeviceDriver.IsSupported)
{
Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl;
MainWindowViewModel.SaveConfig();
deviceDriver = new OpenALHardwareDeviceDriver();
}
else
{
Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, trying to fall back to SoundIO.");
if (SoundIoHardwareDeviceDriver.IsSupported)
{
Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo;
MainWindowViewModel.SaveConfig();
deviceDriver = new SoundIoHardwareDeviceDriver();
}
else
{
Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, falling back to dummy audio out.");
}
}
}
}
else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo)
{
if (SoundIoHardwareDeviceDriver.IsSupported)
{
deviceDriver = new SoundIoHardwareDeviceDriver();
}
else
{
Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, trying to fall back to SDL2.");
if (SDL2HardwareDeviceDriver.IsSupported)
{
Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2;
MainWindowViewModel.SaveConfig();
deviceDriver = new SDL2HardwareDeviceDriver();
}
else
{
Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to OpenAL.");
if (OpenALHardwareDeviceDriver.IsSupported)
{
Logger.Warning?.Print(LogClass.Audio, "Found OpenAL, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.OpenAl;
MainWindowViewModel.SaveConfig();
deviceDriver = new OpenALHardwareDeviceDriver();
}
else
{
Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, falling back to dummy audio out.");
}
}
}
}
else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.OpenAl)
{
if (OpenALHardwareDeviceDriver.IsSupported)
{
deviceDriver = new OpenALHardwareDeviceDriver();
}
else
{
Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, trying to fall back to SDL2.");
if (SDL2HardwareDeviceDriver.IsSupported)
{
Logger.Warning?.Print(LogClass.Audio, "Found SDL2, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SDL2;
MainWindowViewModel.SaveConfig();
deviceDriver = new SDL2HardwareDeviceDriver();
}
else
{
Logger.Warning?.Print(LogClass.Audio, "SDL2 is not supported, trying to fall back to SoundIO.");
if (SoundIoHardwareDeviceDriver.IsSupported)
{
Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration.");
ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo;
MainWindowViewModel.SaveConfig();
deviceDriver = new SoundIoHardwareDeviceDriver();
}
else
{
Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, falling back to dummy audio out.");
}
}
}
}
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? HLE.MemoryConfiguration.MemoryConfiguration6GiB : HLE.MemoryConfiguration.MemoryConfiguration4GiB; var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? HLE.MemoryConfiguration.MemoryConfiguration6GiB : HLE.MemoryConfiguration.MemoryConfiguration4GiB;
IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; HLE.HLEConfiguration configuration = new(VirtualFileSystem,
_viewModel.LibHacHorizonManager,
HLE.HLEConfiguration configuration = new HLE.HLEConfiguration(VirtualFileSystem, ContentManager,
_viewModel.LibHacHorizonManager, _accountManager,
ContentManager, _userChannelPersistence,
_accountManager, renderer,
_userChannelPersistence, InitializeAudio(),
renderer, memoryConfiguration,
deviceDriver, _viewModel.UiHandler,
memoryConfiguration, (SystemLanguage)ConfigurationState.Instance.System.Language.Value,
_viewModel.UiHandler, (RegionCode)ConfigurationState.Instance.System.Region.Value,
(SystemLanguage)ConfigurationState.Instance.System.Language.Value, ConfigurationState.Instance.Graphics.EnableVsync,
(RegionCode)ConfigurationState.Instance.System.Region.Value, ConfigurationState.Instance.System.EnableDockedMode,
ConfigurationState.Instance.Graphics.EnableVsync, ConfigurationState.Instance.System.EnablePtc,
ConfigurationState.Instance.System.EnableDockedMode, ConfigurationState.Instance.System.EnableInternetAccess,
ConfigurationState.Instance.System.EnablePtc, ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
ConfigurationState.Instance.System.EnableInternetAccess, ConfigurationState.Instance.System.FsGlobalAccessLogMode,
fsIntegrityCheckLevel, ConfigurationState.Instance.System.SystemTimeOffset,
ConfigurationState.Instance.System.FsGlobalAccessLogMode, ConfigurationState.Instance.System.TimeZone,
ConfigurationState.Instance.System.SystemTimeOffset, ConfigurationState.Instance.System.MemoryManagerMode,
ConfigurationState.Instance.System.TimeZone, ConfigurationState.Instance.System.IgnoreMissingServices,
ConfigurationState.Instance.System.MemoryManagerMode, ConfigurationState.Instance.Graphics.AspectRatio,
ConfigurationState.Instance.System.IgnoreMissingServices, ConfigurationState.Instance.System.AudioVolume);
ConfigurationState.Instance.Graphics.AspectRatio,
ConfigurationState.Instance.System.AudioVolume);
Device = new Switch(configuration); Device = new Switch(configuration);
} }
private static IHardwareDeviceDriver InitializeAudio()
{
var availableBackends = new List<AudioBackend>()
{
AudioBackend.SDL2,
AudioBackend.SoundIo,
AudioBackend.OpenAl,
AudioBackend.Dummy
};
AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value;
for (int i = 0; i < availableBackends.Count; i++)
{
if (availableBackends[i] == preferredBackend)
{
availableBackends.RemoveAt(i);
availableBackends.Insert(0, preferredBackend);
break;
}
}
static IHardwareDeviceDriver InitializeAudioBackend<T>(AudioBackend backend, AudioBackend nextBackend) where T : IHardwareDeviceDriver, new()
{
if (T.IsSupported)
{
return new T();
}
else
{
Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}.");
return null;
}
}
IHardwareDeviceDriver deviceDriver = null;
for (int i = 0; i < availableBackends.Count; i++)
{
AudioBackend currentBackend = availableBackends[i];
AudioBackend nextBackend = i + 1 < availableBackends.Count ? availableBackends[i + 1] : AudioBackend.Dummy;
deviceDriver = currentBackend switch
{
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend),
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
_ => new DummyHardwareDeviceDriver()
};
if (deviceDriver != null)
{
ConfigurationState.Instance.System.AudioBackend.Value = currentBackend;
break;
}
}
MainWindowViewModel.SaveConfig();
return deviceDriver;
}
private void Window_SizeChanged(object sender, Size e) private void Window_SizeChanged(object sender, Size e)
{ {
Width = (int)e.Width; Width = (int)e.Width;
Height = (int)e.Height; Height = (int)e.Height;
SetRendererWindowSize(e); SetRendererWindowSize(e);
} }
@ -798,7 +739,7 @@ namespace Ryujinx.Ava
{ {
UpdateFrame(); UpdateFrame();
// Polling becomes expensive if it's not slept // Polling becomes expensive if it's not slept.
Thread.Sleep(1); Thread.Sleep(1);
} }
} }
@ -818,14 +759,7 @@ namespace Ryujinx.Ava
} }
}); });
IRenderer renderer = Device.Gpu.Renderer; _renderer = Device.Gpu.Renderer is ThreadedRenderer tr ? tr.BaseRenderer : Device.Gpu.Renderer;
if (renderer is ThreadedRenderer tr)
{
renderer = tr.BaseRenderer;
}
_renderer = renderer;
_renderer.ScreenCaptured += Renderer_ScreenCaptured; _renderer.ScreenCaptured += Renderer_ScreenCaptured;
@ -884,13 +818,12 @@ namespace Ryujinx.Ava
public void UpdateStatus() public void UpdateStatus()
{ {
// Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued.
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld];
float scale = GraphicsConfig.ResScale;
if (GraphicsConfig.ResScale != 1)
if (scale != 1)
{ {
dockedMode += $" ({scale}x)"; dockedMode += $" ({GraphicsConfig.ResScale}x)";
} }
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
@ -907,7 +840,6 @@ namespace Ryujinx.Ava
public async Task ShowExitPrompt() public async Task ShowExitPrompt()
{ {
bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit; bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit;
if (!shouldExit) if (!shouldExit)
{ {
if (_dialogShown) if (_dialogShown)
@ -916,6 +848,7 @@ namespace Ryujinx.Ava
} }
_dialogShown = true; _dialogShown = true;
shouldExit = await ContentDialogHelper.CreateStopEmulationDialog(); shouldExit = await ContentDialogHelper.CreateStopEmulationDialog();
_dialogShown = false; _dialogShown = false;
@ -933,7 +866,7 @@ namespace Ryujinx.Ava
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
_viewModel.Cursor = _isMouseInRenderer ? InvisibleCursor : Cursor.Default; _viewModel.Cursor = _isCursorInRenderer ? InvisibleCursor : Cursor.Default;
}); });
} }
else else
@ -1046,7 +979,7 @@ namespace Ryujinx.Ava
} }
} }
// Touchscreen // Touchscreen.
bool hasTouch = false; bool hasTouch = false;
if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse) if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse)

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Pfad", "GameListHeaderPath": "Pfad",
"GameListContextMenuOpenUserSaveDirectory": "Spielstand-Verzeichnis öffnen", "GameListContextMenuOpenUserSaveDirectory": "Spielstand-Verzeichnis öffnen",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Öffnet das Verzeichnis, welches den Benutzer-Spielstand beinhaltet", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Öffnet das Verzeichnis, welches den Benutzer-Spielstand beinhaltet",
"GameListContextMenuOpenUserDeviceDirectory": "Benutzer-Geräte-Verzeichnis öffnen", "GameListContextMenuOpenDeviceSaveDirectory": "Benutzer-Geräte-Verzeichnis öffnen",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Öffnet das Verzeichnis, welches den Geräte-Spielstände beinhaltet", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Öffnet das Verzeichnis, welches den Geräte-Spielstände beinhaltet",
"GameListContextMenuOpenUserBcatDirectory": "Benutzer-BCAT-Vezeichnis öffnen", "GameListContextMenuOpenBcatSaveDirectory": "Benutzer-BCAT-Vezeichnis öffnen",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Öffnet das Verzeichnis, welches den BCAT Cache des Spiels beinhaltet", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Öffnet das Verzeichnis, welches den BCAT Cache des Spiels beinhaltet",
"GameListContextMenuManageTitleUpdates": "Verwalten von Spiel Updates", "GameListContextMenuManageTitleUpdates": "Verwalten von Spiel Updates",
"GameListContextMenuManageTitleUpdatesToolTip": "Öffnet den Spiel-Update-Manager", "GameListContextMenuManageTitleUpdatesToolTip": "Öffnet den Spiel-Update-Manager",
"GameListContextMenuManageDlc": "Verwalten von DLC", "GameListContextMenuManageDlc": "Verwalten von DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Konto wiederherstellen", "UserProfilesRecoverLostAccounts": "Konto wiederherstellen",
"Recover": "Wiederherstellen", "Recover": "Wiederherstellen",
"UserProfilesRecoverHeading": "Speicherstände wurden für die folgenden Konten gefunden" "UserProfilesRecoverHeading": "Speicherstände wurden für die folgenden Konten gefunden"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Τοποθεσία", "GameListHeaderPath": "Τοποθεσία",
"GameListContextMenuOpenUserSaveDirectory": "Άνοιγμα Τοποθεσίας Αποθήκευσης Χρήστη", "GameListContextMenuOpenUserSaveDirectory": "Άνοιγμα Τοποθεσίας Αποθήκευσης Χρήστη",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση Χρήστη της εφαρμογής", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση Χρήστη της εφαρμογής",
"GameListContextMenuOpenUserDeviceDirectory": "Άνοιγμα Τοποθεσίας Συσκευής Χρήστη", "GameListContextMenuOpenDeviceSaveDirectory": "Άνοιγμα Τοποθεσίας Συσκευής Χρήστη",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση Συσκευής της εφαρμογής", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση Συσκευής της εφαρμογής",
"GameListContextMenuOpenUserBcatDirectory": "Άνοιγμα Τοποθεσίας BCAT", "GameListContextMenuOpenBcatSaveDirectory": "Άνοιγμα Τοποθεσίας BCAT",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση BCAT της εφαρμογής", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Ανοίγει την τοποθεσία που περιέχει την Αποθήκευση BCAT της εφαρμογής",
"GameListContextMenuManageTitleUpdates": "Διαχείριση Ενημερώσεων Παιχνιδιού", "GameListContextMenuManageTitleUpdates": "Διαχείριση Ενημερώσεων Παιχνιδιού",
"GameListContextMenuManageTitleUpdatesToolTip": "Ανοίγει το παράθυρο διαχείρισης Ενημερώσεων Παιχνιδιού", "GameListContextMenuManageTitleUpdatesToolTip": "Ανοίγει το παράθυρο διαχείρισης Ενημερώσεων Παιχνιδιού",
"GameListContextMenuManageDlc": "Διαχείριση DLC", "GameListContextMenuManageDlc": "Διαχείριση DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
"Recover": "Recover", "Recover": "Recover",
"UserProfilesRecoverHeading": "Saves were found for the following accounts" "UserProfilesRecoverHeading": "Saves were found for the following accounts"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Path", "GameListHeaderPath": "Path",
"GameListContextMenuOpenUserSaveDirectory": "Open User Save Directory", "GameListContextMenuOpenUserSaveDirectory": "Open User Save Directory",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Opens the directory which contains Application's User Save", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Opens the directory which contains Application's User Save",
"GameListContextMenuOpenUserDeviceDirectory": "Open User Device Directory", "GameListContextMenuOpenDeviceSaveDirectory": "Open Device Save Directory",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Opens the directory which contains Application's Device Save", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Opens the directory which contains Application's Device Save",
"GameListContextMenuOpenUserBcatDirectory": "Open User BCAT Directory", "GameListContextMenuOpenBcatSaveDirectory": "Open BCAT Save Directory",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Opens the directory which contains Application's BCAT Save", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Opens the directory which contains Application's BCAT Save",
"GameListContextMenuManageTitleUpdates": "Manage Title Updates", "GameListContextMenuManageTitleUpdates": "Manage Title Updates",
"GameListContextMenuManageTitleUpdatesToolTip": "Opens the Title Update management window", "GameListContextMenuManageTitleUpdatesToolTip": "Opens the Title Update management window",
"GameListContextMenuManageDlc": "Manage DLC", "GameListContextMenuManageDlc": "Manage DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
"Recover": "Recover", "Recover": "Recover",
"UserProfilesRecoverHeading" : "Saves were found for the following accounts" "UserProfilesRecoverHeading" : "Saves were found for the following accounts"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Directorio", "GameListHeaderPath": "Directorio",
"GameListContextMenuOpenUserSaveDirectory": "Abrir carpeta de guardado de este usuario", "GameListContextMenuOpenUserSaveDirectory": "Abrir carpeta de guardado de este usuario",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Abre la carpeta que contiene la partida guardada del usuario para esta aplicación", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Abre la carpeta que contiene la partida guardada del usuario para esta aplicación",
"GameListContextMenuOpenUserDeviceDirectory": "Abrir carpeta de guardado del sistema para el usuario actual", "GameListContextMenuOpenDeviceSaveDirectory": "Abrir carpeta de guardado del sistema para el usuario actual",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Abre la carpeta que contiene la partida guardada del sistema para esta aplicación", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Abre la carpeta que contiene la partida guardada del sistema para esta aplicación",
"GameListContextMenuOpenUserBcatDirectory": "Abrir carpeta de guardado BCAT del usuario", "GameListContextMenuOpenBcatSaveDirectory": "Abrir carpeta de guardado BCAT del usuario",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Abrir la carpeta que contiene el guardado BCAT de esta aplicación", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Abrir la carpeta que contiene el guardado BCAT de esta aplicación",
"GameListContextMenuManageTitleUpdates": "Gestionar actualizaciones del juego", "GameListContextMenuManageTitleUpdates": "Gestionar actualizaciones del juego",
"GameListContextMenuManageTitleUpdatesToolTip": "Abrir la ventana de gestión de actualizaciones de esta aplicación", "GameListContextMenuManageTitleUpdatesToolTip": "Abrir la ventana de gestión de actualizaciones de esta aplicación",
"GameListContextMenuManageDlc": "Gestionar DLC", "GameListContextMenuManageDlc": "Gestionar DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
"Recover": "Recover", "Recover": "Recover",
"UserProfilesRecoverHeading": "Saves were found for the following accounts" "UserProfilesRecoverHeading": "Saves were found for the following accounts"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Chemin", "GameListHeaderPath": "Chemin",
"GameListContextMenuOpenUserSaveDirectory": "Ouvrir le dossier de sauvegarde utilisateur", "GameListContextMenuOpenUserSaveDirectory": "Ouvrir le dossier de sauvegarde utilisateur",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde utilisateur du jeu", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde utilisateur du jeu",
"GameListContextMenuOpenUserDeviceDirectory": "Ouvrir le dossier de sauvegarde console", "GameListContextMenuOpenDeviceSaveDirectory": "Ouvrir le dossier de sauvegarde console",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde console du jeu", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde console du jeu",
"GameListContextMenuOpenUserBcatDirectory": "Ouvrir le dossier de sauvegarde BCAT", "GameListContextMenuOpenBcatSaveDirectory": "Ouvrir le dossier de sauvegarde BCAT",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde BCAT du jeu", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Ouvre le dossier contenant la sauvegarde BCAT du jeu",
"GameListContextMenuManageTitleUpdates": "Gérer les mises à jour", "GameListContextMenuManageTitleUpdates": "Gérer les mises à jour",
"GameListContextMenuManageTitleUpdatesToolTip": "Ouvre la fenêtre de gestion des mises à jour", "GameListContextMenuManageTitleUpdatesToolTip": "Ouvre la fenêtre de gestion des mises à jour",
"GameListContextMenuManageDlc": "Gérer les DLC", "GameListContextMenuManageDlc": "Gérer les DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Récupérer les comptes perdus", "UserProfilesRecoverLostAccounts": "Récupérer les comptes perdus",
"Recover": "Récupérer", "Recover": "Récupérer",
"UserProfilesRecoverHeading": "Des sauvegardes ont été trouvées pour les comptes suivants" "UserProfilesRecoverHeading": "Des sauvegardes ont été trouvées pour les comptes suivants"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Percorso", "GameListHeaderPath": "Percorso",
"GameListContextMenuOpenUserSaveDirectory": "Apri la cartella salvataggi dell'utente", "GameListContextMenuOpenUserSaveDirectory": "Apri la cartella salvataggi dell'utente",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Apre la cartella che contiene i salvataggi di gioco dell'utente", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Apre la cartella che contiene i salvataggi di gioco dell'utente",
"GameListContextMenuOpenUserDeviceDirectory": "Apri la cartella dispositivo dell'utente", "GameListContextMenuOpenDeviceSaveDirectory": "Apri la cartella dispositivo dell'utente",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Apre la cartella che contiene i salvataggi di gioco del dispositivo", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Apre la cartella che contiene i salvataggi di gioco del dispositivo",
"GameListContextMenuOpenUserBcatDirectory": "Apri la cartella BCAT dell'utente", "GameListContextMenuOpenBcatSaveDirectory": "Apri la cartella BCAT dell'utente",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Apre la cartella che contiene i salvataggi BCAT dell'applicazione", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Apre la cartella che contiene i salvataggi BCAT dell'applicazione",
"GameListContextMenuManageTitleUpdates": "Gestisci aggiornamenti del gioco", "GameListContextMenuManageTitleUpdates": "Gestisci aggiornamenti del gioco",
"GameListContextMenuManageTitleUpdatesToolTip": "Apre la finestra di gestione aggiornamenti del gioco", "GameListContextMenuManageTitleUpdatesToolTip": "Apre la finestra di gestione aggiornamenti del gioco",
"GameListContextMenuManageDlc": "Gestici DLC", "GameListContextMenuManageDlc": "Gestici DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Recupera il tuo account", "UserProfilesRecoverLostAccounts": "Recupera il tuo account",
"Recover": "Recupera", "Recover": "Recupera",
"UserProfilesRecoverHeading": "Sono stati trovati dei salvataggi per i seguenti account" "UserProfilesRecoverHeading": "Sono stati trovati dei salvataggi per i seguenti account"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "パス", "GameListHeaderPath": "パス",
"GameListContextMenuOpenUserSaveDirectory": "セーブディレクトリを開く", "GameListContextMenuOpenUserSaveDirectory": "セーブディレクトリを開く",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "アプリケーションのユーザセーブデータを格納するディレクトリを開きます", "GameListContextMenuOpenUserSaveDirectoryToolTip": "アプリケーションのユーザセーブデータを格納するディレクトリを開きます",
"GameListContextMenuOpenUserDeviceDirectory": "デバイスディレクトリを開く", "GameListContextMenuOpenDeviceSaveDirectory": "デバイスディレクトリを開く",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "アプリケーションのデバイスセーブデータを格納するディレクトリを開きます", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "アプリケーションのデバイスセーブデータを格納するディレクトリを開きます",
"GameListContextMenuOpenUserBcatDirectory": "BCATディレクトリを開く", "GameListContextMenuOpenBcatSaveDirectory": "BCATディレクトリを開く",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "アプリケーションの BCAT セーブデータを格納するディレクトリを開きます", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "アプリケーションの BCAT セーブデータを格納するディレクトリを開きます",
"GameListContextMenuManageTitleUpdates": "アップデートを管理", "GameListContextMenuManageTitleUpdates": "アップデートを管理",
"GameListContextMenuManageTitleUpdatesToolTip": "タイトルのアップデート管理ウインドウを開きます", "GameListContextMenuManageTitleUpdatesToolTip": "タイトルのアップデート管理ウインドウを開きます",
"GameListContextMenuManageDlc": "DLCを管理", "GameListContextMenuManageDlc": "DLCを管理",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "アカウントの復旧", "UserProfilesRecoverLostAccounts": "アカウントの復旧",
"Recover": "復旧", "Recover": "復旧",
"UserProfilesRecoverHeading": "以下のアカウントのセーブデータが見つかりました" "UserProfilesRecoverHeading": "以下のアカウントのセーブデータが見つかりました"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "경로", "GameListHeaderPath": "경로",
"GameListContextMenuOpenUserSaveDirectory": "사용자 저장 디렉토리 열기\n", "GameListContextMenuOpenUserSaveDirectory": "사용자 저장 디렉토리 열기\n",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "응용 프로그램의 사용자 저장이 포함된 디렉토리 열기\n", "GameListContextMenuOpenUserSaveDirectoryToolTip": "응용 프로그램의 사용자 저장이 포함된 디렉토리 열기\n",
"GameListContextMenuOpenUserDeviceDirectory": "사용자 장치 디렉토리 열기", "GameListContextMenuOpenDeviceSaveDirectory": "사용자 장치 디렉토리 열기",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "응용 프로그램의 장치 저장이 포함된 디렉토리 열기\n", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "응용 프로그램의 장치 저장이 포함된 디렉토리 열기\n",
"GameListContextMenuOpenUserBcatDirectory": "사용자의 BCAT 디렉토리 열기\n", "GameListContextMenuOpenBcatSaveDirectory": "사용자의 BCAT 디렉토리 열기\n",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "응용 프로그램의 BCAT 저장이 포함된 디렉토리 열기\n", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "응용 프로그램의 BCAT 저장이 포함된 디렉토리 열기\n",
"GameListContextMenuManageTitleUpdates": "타이틀 업데이트 관리\n", "GameListContextMenuManageTitleUpdates": "타이틀 업데이트 관리\n",
"GameListContextMenuManageTitleUpdatesToolTip": "타이틀 업데이트 관리 창 열기", "GameListContextMenuManageTitleUpdatesToolTip": "타이틀 업데이트 관리 창 열기",
"GameListContextMenuManageDlc": "DLC 관리", "GameListContextMenuManageDlc": "DLC 관리",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "잃어버린 계정 복구", "UserProfilesRecoverLostAccounts": "잃어버린 계정 복구",
"Recover": "복구", "Recover": "복구",
"UserProfilesRecoverHeading": "다음 계정에 대한 저장 발견" "UserProfilesRecoverHeading": "다음 계정에 대한 저장 발견"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Ścieżka", "GameListHeaderPath": "Ścieżka",
"GameListContextMenuOpenUserSaveDirectory": "Otwórz Katalog Zapisów Użytkownika", "GameListContextMenuOpenUserSaveDirectory": "Otwórz Katalog Zapisów Użytkownika",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Otwiera katalog, który zawiera Zapis Użytkownika Aplikacji", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Otwiera katalog, który zawiera Zapis Użytkownika Aplikacji",
"GameListContextMenuOpenUserDeviceDirectory": "Otwórz Katalog Urządzeń Użytkownika", "GameListContextMenuOpenDeviceSaveDirectory": "Otwórz Katalog Urządzeń Użytkownika",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Otwiera katalog, który zawiera Zapis Urządzenia Aplikacji", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Otwiera katalog, który zawiera Zapis Urządzenia Aplikacji",
"GameListContextMenuOpenUserBcatDirectory": "Otwórz Katalog BCAT Użytkownika", "GameListContextMenuOpenBcatSaveDirectory": "Otwórz Katalog BCAT Użytkownika",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Otwiera katalog, który zawiera Zapis BCAT Aplikacji", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Otwiera katalog, który zawiera Zapis BCAT Aplikacji",
"GameListContextMenuManageTitleUpdates": "Zarządzaj Aktualizacjami Tytułów", "GameListContextMenuManageTitleUpdates": "Zarządzaj Aktualizacjami Tytułów",
"GameListContextMenuManageTitleUpdatesToolTip": "Otwiera okno zarządzania Aktualizacjami Tytułu", "GameListContextMenuManageTitleUpdatesToolTip": "Otwiera okno zarządzania Aktualizacjami Tytułu",
"GameListContextMenuManageDlc": "Zarządzaj DLC", "GameListContextMenuManageDlc": "Zarządzaj DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Odzyskaj Utracone Konta", "UserProfilesRecoverLostAccounts": "Odzyskaj Utracone Konta",
"Recover": "Odzyskaj", "Recover": "Odzyskaj",
"UserProfilesRecoverHeading": "Znaleziono zapisy dla następujących kont" "UserProfilesRecoverHeading": "Znaleziono zapisy dla następujących kont"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Caminho", "GameListHeaderPath": "Caminho",
"GameListContextMenuOpenUserSaveDirectory": "Abrir diretório de saves do usuário", "GameListContextMenuOpenUserSaveDirectory": "Abrir diretório de saves do usuário",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Abre o diretório que contém jogos salvos para o usuário atual", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Abre o diretório que contém jogos salvos para o usuário atual",
"GameListContextMenuOpenUserDeviceDirectory": "Abrir diretório de saves de dispositivo do usuário", "GameListContextMenuOpenDeviceSaveDirectory": "Abrir diretório de saves de dispositivo do usuário",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Abre o diretório que contém saves do dispositivo para o usuário atual", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Abre o diretório que contém saves do dispositivo para o usuário atual",
"GameListContextMenuOpenUserBcatDirectory": "Abrir diretório de saves BCAT do usuário", "GameListContextMenuOpenBcatSaveDirectory": "Abrir diretório de saves BCAT do usuário",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Abre o diretório que contém saves BCAT para o usuário atual", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Abre o diretório que contém saves BCAT para o usuário atual",
"GameListContextMenuManageTitleUpdates": "Gerenciar atualizações do jogo", "GameListContextMenuManageTitleUpdates": "Gerenciar atualizações do jogo",
"GameListContextMenuManageTitleUpdatesToolTip": "Abre a janela de gerenciamento de atualizações", "GameListContextMenuManageTitleUpdatesToolTip": "Abre a janela de gerenciamento de atualizações",
"GameListContextMenuManageDlc": "Gerenciar DLCs", "GameListContextMenuManageDlc": "Gerenciar DLCs",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Recuperar contas perdidas", "UserProfilesRecoverLostAccounts": "Recuperar contas perdidas",
"Recover": "Recuperar", "Recover": "Recuperar",
"UserProfilesRecoverHeading": "Jogos salvos foram encontrados para as seguintes contas" "UserProfilesRecoverHeading": "Jogos salvos foram encontrados para as seguintes contas"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Путь", "GameListHeaderPath": "Путь",
"GameListContextMenuOpenUserSaveDirectory": "Открыть каталог сохранений пользователя", "GameListContextMenuOpenUserSaveDirectory": "Открыть каталог сохранений пользователя",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Открывает каталог, содержащий пользовательское сохранение приложения", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Открывает каталог, содержащий пользовательское сохранение приложения",
"GameListContextMenuOpenUserDeviceDirectory": "Открыть каталог пользовательских устройств", "GameListContextMenuOpenDeviceSaveDirectory": "Открыть каталог пользовательских устройств",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Открывает каталог, содержащий сохранение устройства приложения", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Открывает каталог, содержащий сохранение устройства приложения",
"GameListContextMenuOpenUserBcatDirectory": "Открыть каталог пользователей BCAT", "GameListContextMenuOpenBcatSaveDirectory": "Открыть каталог пользователей BCAT",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Открывает каталог, содержащий BCAT сохранения приложения.", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Открывает каталог, содержащий BCAT сохранения приложения.",
"GameListContextMenuManageTitleUpdates": "Управление обновлениями заголовков", "GameListContextMenuManageTitleUpdates": "Управление обновлениями заголовков",
"GameListContextMenuManageTitleUpdatesToolTip": "Открывает окно управления обновлением заголовков", "GameListContextMenuManageTitleUpdatesToolTip": "Открывает окно управления обновлением заголовков",
"GameListContextMenuManageDlc": "Управление DLC", "GameListContextMenuManageDlc": "Управление DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts", "UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
"Recover": "Recover", "Recover": "Recover",
"UserProfilesRecoverHeading": "Saves were found for the following accounts" "UserProfilesRecoverHeading": "Saves were found for the following accounts"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Yol", "GameListHeaderPath": "Yol",
"GameListContextMenuOpenUserSaveDirectory": "Kullanıcı Kayıt Dosyası Dizinini Aç", "GameListContextMenuOpenUserSaveDirectory": "Kullanıcı Kayıt Dosyası Dizinini Aç",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Uygulamanın Kullanıcı Kaydı'nın bulunduğu dizini açar", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Uygulamanın Kullanıcı Kaydı'nın bulunduğu dizini açar",
"GameListContextMenuOpenUserDeviceDirectory": "Kullanıcı Cihaz Dizinini Aç", "GameListContextMenuOpenDeviceSaveDirectory": "Kullanıcı Cihaz Dizinini Aç",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Uygulamanın Kullanıcı Cihaz Kaydı'nın bulunduğu dizini açar", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Uygulamanın Kullanıcı Cihaz Kaydı'nın bulunduğu dizini açar",
"GameListContextMenuOpenUserBcatDirectory": "Kullanıcı BCAT Dizinini Aç", "GameListContextMenuOpenBcatSaveDirectory": "Kullanıcı BCAT Dizinini Aç",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Uygulamanın Kullanıcı BCAT Kaydı'nın bulunduğu dizini açar", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Uygulamanın Kullanıcı BCAT Kaydı'nın bulunduğu dizini açar",
"GameListContextMenuManageTitleUpdates": "Oyun Güncellemelerini Yönet", "GameListContextMenuManageTitleUpdates": "Oyun Güncellemelerini Yönet",
"GameListContextMenuManageTitleUpdatesToolTip": "Oyun Güncelleme Yönetim Penceresini Açar", "GameListContextMenuManageTitleUpdatesToolTip": "Oyun Güncelleme Yönetim Penceresini Açar",
"GameListContextMenuManageDlc": "DLC'leri Yönet", "GameListContextMenuManageDlc": "DLC'leri Yönet",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Kayıp Hesapları Kurtar", "UserProfilesRecoverLostAccounts": "Kayıp Hesapları Kurtar",
"Recover": "Kurtar", "Recover": "Kurtar",
"UserProfilesRecoverHeading": "Aşağıdaki hesaplar için kayıtlar bulundu" "UserProfilesRecoverHeading": "Aşağıdaki hesaplar için kayıtlar bulundu"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "Шлях", "GameListHeaderPath": "Шлях",
"GameListContextMenuOpenUserSaveDirectory": "Відкрити каталог збереження користувача", "GameListContextMenuOpenUserSaveDirectory": "Відкрити каталог збереження користувача",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "Відкриває каталог, який містить збереження користувача програми", "GameListContextMenuOpenUserSaveDirectoryToolTip": "Відкриває каталог, який містить збереження користувача програми",
"GameListContextMenuOpenUserDeviceDirectory": "Відкрити каталог пристроїв користувача", "GameListContextMenuOpenDeviceSaveDirectory": "Відкрити каталог пристроїв користувача",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "Відкриває каталог, який містить збереження пристрою програми", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "Відкриває каталог, який містить збереження пристрою програми",
"GameListContextMenuOpenUserBcatDirectory": "Відкрити каталог користувача BCAT", "GameListContextMenuOpenBcatSaveDirectory": "Відкрити каталог користувача BCAT",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "Відкриває каталог, який містить BCAT-збереження програми", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "Відкриває каталог, який містить BCAT-збереження програми",
"GameListContextMenuManageTitleUpdates": "Керування оновленнями заголовків", "GameListContextMenuManageTitleUpdates": "Керування оновленнями заголовків",
"GameListContextMenuManageTitleUpdatesToolTip": "Відкриває вікно керування оновленням заголовка", "GameListContextMenuManageTitleUpdatesToolTip": "Відкриває вікно керування оновленням заголовка",
"GameListContextMenuManageDlc": "Керування DLC", "GameListContextMenuManageDlc": "Керування DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "Відновлення втрачених облікових записів", "UserProfilesRecoverLostAccounts": "Відновлення втрачених облікових записів",
"Recover": "Відновити", "Recover": "Відновити",
"UserProfilesRecoverHeading": "Знайдено збереження для наступних облікових записів" "UserProfilesRecoverHeading": "Знайдено збереження для наступних облікових записів"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "路径", "GameListHeaderPath": "路径",
"GameListContextMenuOpenUserSaveDirectory": "打开应用存档目录", "GameListContextMenuOpenUserSaveDirectory": "打开应用存档目录",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "打开储存游戏存档的目录", "GameListContextMenuOpenUserSaveDirectoryToolTip": "打开储存游戏存档的目录",
"GameListContextMenuOpenUserDeviceDirectory": "打开应用系统目录", "GameListContextMenuOpenDeviceSaveDirectory": "打开应用系统目录",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "打开包含游戏系统设置的目录", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "打开包含游戏系统设置的目录",
"GameListContextMenuOpenUserBcatDirectory": "打开 BCAT 目录", "GameListContextMenuOpenBcatSaveDirectory": "打开 BCAT 目录",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "打开包含游戏 BCAT 数据的目录", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "打开包含游戏 BCAT 数据的目录",
"GameListContextMenuManageTitleUpdates": "管理游戏更新", "GameListContextMenuManageTitleUpdates": "管理游戏更新",
"GameListContextMenuManageTitleUpdatesToolTip": "打开更新管理器", "GameListContextMenuManageTitleUpdatesToolTip": "打开更新管理器",
"GameListContextMenuManageDlc": "管理 DLC", "GameListContextMenuManageDlc": "管理 DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "恢复丢失的账户", "UserProfilesRecoverLostAccounts": "恢复丢失的账户",
"Recover": "恢复", "Recover": "恢复",
"UserProfilesRecoverHeading": "找到了这些用户的存档数据" "UserProfilesRecoverHeading": "找到了这些用户的存档数据"
} }

View File

@ -41,10 +41,10 @@
"GameListHeaderPath": "路徑", "GameListHeaderPath": "路徑",
"GameListContextMenuOpenUserSaveDirectory": "開啟使用者存檔資料夾", "GameListContextMenuOpenUserSaveDirectory": "開啟使用者存檔資料夾",
"GameListContextMenuOpenUserSaveDirectoryToolTip": "開啟儲存遊戲存檔的資料夾", "GameListContextMenuOpenUserSaveDirectoryToolTip": "開啟儲存遊戲存檔的資料夾",
"GameListContextMenuOpenUserDeviceDirectory": "開啟系統資料夾", "GameListContextMenuOpenDeviceSaveDirectory": "開啟系統資料夾",
"GameListContextMenuOpenUserDeviceDirectoryToolTip": "開啟包含遊戲系統設定的資料夾", "GameListContextMenuOpenDeviceSaveDirectoryToolTip": "開啟包含遊戲系統設定的資料夾",
"GameListContextMenuOpenUserBcatDirectory": "開啟 BCAT 資料夾", "GameListContextMenuOpenBcatSaveDirectory": "開啟 BCAT 資料夾",
"GameListContextMenuOpenUserBcatDirectoryToolTip": "開啟包含遊戲 BCAT 資料的資料夾", "GameListContextMenuOpenBcatSaveDirectoryToolTip": "開啟包含遊戲 BCAT 資料的資料夾",
"GameListContextMenuManageTitleUpdates": "管理遊戲更新", "GameListContextMenuManageTitleUpdates": "管理遊戲更新",
"GameListContextMenuManageTitleUpdatesToolTip": "開啟更新管理視窗", "GameListContextMenuManageTitleUpdatesToolTip": "開啟更新管理視窗",
"GameListContextMenuManageDlc": "管理 DLC", "GameListContextMenuManageDlc": "管理 DLC",
@ -610,4 +610,4 @@
"UserProfilesRecoverLostAccounts": "恢復遺失的帳號", "UserProfilesRecoverLostAccounts": "恢復遺失的帳號",
"Recover": "恢復", "Recover": "恢復",
"UserProfilesRecoverHeading": "在以下帳號找到了一些遊戲存檔" "UserProfilesRecoverHeading": "在以下帳號找到了一些遊戲存檔"
} }

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

@ -1,12 +1,13 @@
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;
using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Common.SystemInfo; using Ryujinx.Common.SystemInfo;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Modules; using Ryujinx.Modules;
using Ryujinx.SDL2.Common; using Ryujinx.SDL2.Common;
using Ryujinx.Ui.Common; using Ryujinx.Ui.Common;
@ -89,6 +90,8 @@ namespace Ryujinx.Ava
Initialize(args); Initialize(args);
LoggerAdapter.Register();
BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
} }
@ -110,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)
@ -225,6 +227,12 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan; ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan;
} }
} }
// Check if docked mode was overriden.
if (CommandLineState.OverrideDockedMode.HasValue)
{
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
}
} }
private static void PrintSystemInfo() private static void PrintSystemInfo()

View File

@ -26,12 +26,12 @@
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
<MenuItem <MenuItem
Command="{Binding OpenDeviceSaveDirectory}" Command="{Binding OpenDeviceSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserDeviceDirectory}" Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserDeviceDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
<MenuItem <MenuItem
Command="{Binding OpenBcatSaveDirectory}" Command="{Binding OpenBcatSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserBcatDirectory}" Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserBcatDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
<Separator /> <Separator />
<MenuItem <MenuItem
Command="{Binding OpenTitleUpdateManager}" Command="{Binding OpenTitleUpdateManager}"
@ -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

@ -25,12 +25,12 @@
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserSaveDirectoryToolTip}" />
<MenuItem <MenuItem
Command="{Binding OpenDeviceSaveDirectory}" Command="{Binding OpenDeviceSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserDeviceDirectory}" Header="{locale:Locale GameListContextMenuOpenDeviceSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserDeviceDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenDeviceSaveDirectoryToolTip}" />
<MenuItem <MenuItem
Command="{Binding OpenBcatSaveDirectory}" Command="{Binding OpenBcatSaveDirectory}"
Header="{locale:Locale GameListContextMenuOpenUserBcatDirectory}" Header="{locale:Locale GameListContextMenuOpenBcatSaveDirectory}"
ToolTip.Tip="{locale:Locale GameListContextMenuOpenUserBcatDirectoryToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuOpenBcatSaveDirectoryToolTip}" />
<Separator /> <Separator />
<MenuItem <MenuItem
Command="{Binding OpenTitleUpdateManager}" Command="{Binding OpenTitleUpdateManager}"
@ -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 != null && 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

@ -87,8 +87,6 @@ namespace Ryujinx.Ava.UI.ViewModels
private float _volume; private float _volume;
private string _backendText; private string _backendText;
public ApplicationData ListSelectedApplication;
public ApplicationData GridSelectedApplication;
private bool _canUpdate; private bool _canUpdate;
private Cursor _cursor; private Cursor _cursor;
private string _title; private string _title;
@ -97,6 +95,9 @@ namespace Ryujinx.Ava.UI.ViewModels
private WindowState _windowState; private WindowState _windowState;
private bool _isActive; private bool _isActive;
public ApplicationData ListSelectedApplication;
public ApplicationData GridSelectedApplication;
public event Action ReloadGameList; public event Action ReloadGameList;
private string TitleName { get; set; } private string TitleName { get; set; }

View File

@ -36,10 +36,7 @@ namespace Ryujinx.Ava.UI.Views.Main
private async void StopEmulation_Click(object sender, RoutedEventArgs e) private async void StopEmulation_Click(object sender, RoutedEventArgs e)
{ {
await Task.Run(() => await Window.ViewModel.AppHost?.ShowExitPrompt();
{
Window.ViewModel.AppHost?.ShowExitPrompt();
});
} }
private async void PauseEmulation_Click(object sender, RoutedEventArgs e) private async void PauseEmulation_Click(object sender, RoutedEventArgs e)

View File

@ -22,7 +22,7 @@ namespace Ryujinx.Ava.UI.Views.Main
{ {
base.OnAttachedToVisualTree(e); base.OnAttachedToVisualTree(e);
if (this.VisualRoot is MainWindow window) if (VisualRoot is MainWindow window)
{ {
Window = window; Window = window;
} }

View File

@ -22,7 +22,7 @@ namespace Ryujinx.Ava.UI.Views.Main
{ {
base.OnAttachedToVisualTree(e); base.OnAttachedToVisualTree(e);
if (this.VisualRoot is MainWindow window) if (VisualRoot is MainWindow window)
{ {
ViewModel = window.ViewModel; ViewModel = window.ViewModel;
} }

View File

@ -1,253 +1,247 @@
<UserControl <UserControl
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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox" xmlns:flex="clr-namespace:Avalonia.Flexbox;assembly=Avalonia.Flexbox"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:viewModel="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModel="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Width="550"
mc:Ignorable="d" Height="260"
Margin="0,-12,0,0"
d:DesignHeight="260" d:DesignHeight="260"
d:DesignWidth="550" d:DesignWidth="550"
Height="260"
Width="550"
x:Class="Ryujinx.Ava.UI.Windows.AboutWindow"
x:DataType="viewModel:AboutWindowViewModel"
x:CompileBindings="True" x:CompileBindings="True"
Margin="0 -12 0 0" x:DataType="viewModel:AboutWindowViewModel"
Focusable="True"> Focusable="True"
<Design.DataContext> mc:Ignorable="d">
<viewModel:AboutWindowViewModel /> <Design.DataContext>
</Design.DataContext> <viewModel:AboutWindowViewModel />
<Grid </Design.DataContext>
HorizontalAlignment="Stretch" <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
VerticalAlignment="Stretch"> <Grid.ColumnDefinitions>
<Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="*" /> </Grid.ColumnDefinitions>
</Grid.ColumnDefinitions> <Grid
<Grid
Grid.Column="0" Grid.Column="0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <RowDefinition Height="*" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Row="0" Grid.Row="0"
Spacing="10" HorizontalAlignment="Stretch"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
VerticalAlignment="Stretch"> Spacing="10">
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image <Image
Grid.Column="0" Grid.Column="0"
Height="80" Height="80"
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" /> Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
<flex:FlexPanel <flex:FlexPanel
Grid.Column="2" Grid.Column="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
Direction="Column" Direction="Column"
JustifyContent="SpaceAround" JustifyContent="SpaceAround"
RowSpacing="2"> RowSpacing="2">
<TextBlock <TextBlock
FontSize="28" FontSize="28"
FontWeight="Bold" FontWeight="Bold"
Text="Ryujinx" Text="Ryujinx"
TextAlignment="Left" /> TextAlignment="Left" />
<TextBlock <TextBlock Text="(REE-YOU-JINX)" TextAlignment="Left" />
Text="(REE-YOU-JINX)" </flex:FlexPanel>
TextAlignment="Left" /> </Grid>
</flex:FlexPanel> <TextBlock
</Grid> HorizontalAlignment="Center"
<TextBlock VerticalAlignment="Center"
HorizontalAlignment="Center" FontSize="10"
VerticalAlignment="Center" LineHeight="12"
Text="{Binding Version}" Text="{Binding Version}"
TextAlignment="Center" TextAlignment="Center" />
FontSize="10" </StackPanel>
LineHeight="12" /> <StackPanel
</StackPanel> Grid.Row="2"
<StackPanel HorizontalAlignment="Stretch"
Grid.Row="2" VerticalAlignment="Stretch"
Spacing="10" Spacing="10">
HorizontalAlignment="Stretch" <TextBlock
VerticalAlignment="Stretch"> Width="200"
<TextBlock HorizontalAlignment="Center"
HorizontalAlignment="Center" FontSize="10"
Width="200" LineHeight="12"
Text="{locale:Locale AboutDisclaimerMessage}" Text="{locale:Locale AboutDisclaimerMessage}"
TextAlignment="Center" TextAlignment="Center"
TextWrapping="Wrap" TextWrapping="Wrap" />
FontSize="10" <TextBlock
LineHeight="12" /> Name="AmiiboLabel"
<TextBlock Width="200"
Name="AmiiboLabel" HorizontalAlignment="Center"
HorizontalAlignment="Center" FontSize="10"
Width="200" LineHeight="12"
PointerPressed="AmiiboLabel_OnPointerPressed" PointerPressed="AmiiboLabel_OnPointerPressed"
Text="{locale:Locale AboutAmiiboDisclaimerMessage}" Text="{locale:Locale AboutAmiiboDisclaimerMessage}"
TextAlignment="Center" TextAlignment="Center"
TextWrapping="Wrap" TextWrapping="Wrap" />
FontSize="10" <StackPanel
LineHeight="12" /> HorizontalAlignment="Center"
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center" Spacing="10">
Orientation="Horizontal" <Button
Spacing="10"> MinWidth="30"
<Button MinHeight="30"
MaxHeight="30" MaxWidth="30"
MaxWidth="30" MaxHeight="30"
MinHeight="30" Padding="8"
MinWidth="30" Background="Transparent"
Padding="8" Click="Button_OnClick"
CornerRadius="15" CornerRadius="15"
Background="Transparent" Tag="https://www.patreon.com/ryujinx"
Click="Button_OnClick" ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}">
Tag="https://www.patreon.com/ryujinx" <Image Source="{Binding PatreonLogo}" />
ToolTip.Tip="{locale:Locale AboutPatreonUrlTooltipMessage}"> </Button>
<Image Source="{Binding PatreonLogo}" /> <Button
</Button> MinWidth="30"
<Button MinHeight="30"
MaxHeight="30" MaxWidth="30"
MaxWidth="30" MaxHeight="30"
MinHeight="30" Padding="8"
MinWidth="30" Background="Transparent"
Padding="8" Click="Button_OnClick"
CornerRadius="15" CornerRadius="15"
Background="Transparent" Tag="https://github.com/Ryujinx/Ryujinx"
Click="Button_OnClick" ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}">
Tag="https://github.com/Ryujinx/Ryujinx" <Image Source="{Binding GithubLogo}" />
ToolTip.Tip="{locale:Locale AboutGithubUrlTooltipMessage}"> </Button>
<Image Source="{Binding GithubLogo}" /> <Button
</Button> MinWidth="30"
<Button MinHeight="30"
MaxHeight="30" MaxWidth="30"
MaxWidth="30" MaxHeight="30"
MinHeight="30" Padding="8"
MinWidth="30" Background="Transparent"
Padding="8" Click="Button_OnClick"
CornerRadius="15" CornerRadius="15"
Background="Transparent" Tag="https://discordapp.com/invite/N2FmfVc"
Click="Button_OnClick" ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}">
Tag="https://discordapp.com/invite/N2FmfVc" <Image Source="{Binding DiscordLogo}" />
ToolTip.Tip="{locale:Locale AboutDiscordUrlTooltipMessage}"> </Button>
<Image Source="{Binding DiscordLogo}" /> <Button
</Button> MinWidth="30"
<Button MinHeight="30"
MaxHeight="30" MaxWidth="30"
MaxWidth="30" MaxHeight="30"
MinHeight="30" Padding="8"
MinWidth="30" Background="Transparent"
Padding="8" Click="Button_OnClick"
CornerRadius="15" CornerRadius="15"
Background="Transparent" Tag="https://twitter.com/RyujinxEmu"
Click="Button_OnClick" ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}">
Tag="https://twitter.com/RyujinxEmu" <Image Source="{Binding TwitterLogo}" />
ToolTip.Tip="{locale:Locale AboutTwitterUrlTooltipMessage}"> </Button>
<Image Source="{Binding TwitterLogo}" /> <Button
</Button> MinWidth="30"
<Button MinHeight="30"
MaxHeight="30" MaxWidth="30"
MaxWidth="30" MaxHeight="30"
MinHeight="30" Padding="8"
MinWidth="30" Background="Transparent"
Padding="8" Click="Button_OnClick"
CornerRadius="15" CornerRadius="15"
Background="Transparent" Tag="https://www.ryujinx.org"
Click="Button_OnClick" ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}">
Tag="https://www.ryujinx.org" <ui:SymbolIcon Foreground="{DynamicResource ThemeForegroundColor}" Symbol="Link" />
ToolTip.Tip="{locale:Locale AboutUrlTooltipMessage}"> </Button>
<ui:SymbolIcon </StackPanel>
Symbol="Link" </StackPanel>
Foreground="{DynamicResource ThemeForegroundColor}" /> </Grid>
</Button> <Border
</StackPanel>
</StackPanel>
</Grid>
<Border
Grid.Column="1" Grid.Column="1"
Width="1" Width="1"
Margin="20,0"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1,0,0,0" BorderThickness="1,0,0,0" />
Margin="20 0"/> <Grid
<Grid
Grid.Column="2" Grid.Column="2"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"> VerticalAlignment="Stretch">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
</Grid.RowDefinitions> </Grid.RowDefinitions>
<StackPanel <StackPanel
Grid.Row="0" Grid.Row="0"
Margin="0 10 0 0" Margin="0,10,0,0"
Spacing="2"> Spacing="2">
<TextBlock <TextBlock
FontWeight="Bold" FontSize="15"
FontSize="15" FontWeight="Bold"
Text="{locale:Locale AboutRyujinxAboutTitle}" /> Text="{locale:Locale AboutRyujinxAboutTitle}" />
<TextBlock <TextBlock
FontSize="10" FontSize="10"
TextWrapping="Wrap" Text="{locale:Locale AboutRyujinxAboutContent}"
Text="{locale:Locale AboutRyujinxAboutContent}" /> TextWrapping="Wrap" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Grid.Row="1" Grid.Row="1"
Margin="0 10 0 0" Margin="0,10,0,0"
Spacing="2"> Spacing="2">
<TextBlock <TextBlock
FontWeight="Bold" FontSize="15"
FontSize="15" FontWeight="Bold"
Text="{locale:Locale AboutRyujinxMaintainersTitle}" /> Text="{locale:Locale AboutRyujinxMaintainersTitle}" />
<TextBlock <TextBlock
FontSize="10" FontSize="10"
TextWrapping="Wrap" Text="{Binding Developers}"
Text="{Binding Developers}" /> TextWrapping="Wrap" />
<Button <Button
HorizontalAlignment="Left" Padding="5"
Background="Transparent" HorizontalAlignment="Left"
Click="Button_OnClick" Background="Transparent"
Padding="5" Click="Button_OnClick"
Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a"> Tag="https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a">
<TextBlock <TextBlock
FontSize="10" FontSize="10"
Text="{locale:Locale AboutRyujinxContributorsButtonHeader}" Text="{locale:Locale AboutRyujinxContributorsButtonHeader}"
TextAlignment="Right" TextAlignment="Right"
ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" /> ToolTip.Tip="{locale:Locale AboutRyujinxMaintainersContentTooltipMessage}" />
</Button> </Button>
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Grid.Row="2" Grid.Row="2"
Margin="0 10 0 0" Margin="0,10,0,0"
Spacing="2"> Spacing="2">
<TextBlock <TextBlock
FontWeight="Bold" FontSize="15"
FontSize="15" FontWeight="Bold"
Text="{locale:Locale AboutRyujinxSupprtersTitle}" /> Text="{locale:Locale AboutRyujinxSupprtersTitle}" />
<ScrollViewer <ScrollViewer
VerticalScrollBarVisibility="Visible" Height="70"
HorizontalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Disabled"
Height="70"> VerticalScrollBarVisibility="Visible">
<TextBlock <TextBlock
Name="SupportersTextBlock" Name="SupportersTextBlock"
VerticalAlignment="Top" VerticalAlignment="Top"
FontSize="10" FontSize="10"
TextWrapping="Wrap" Text="{Binding Supporters}"
Text="{Binding Supporters}" /> TextWrapping="Wrap" />
</ScrollViewer> </ScrollViewer>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -4,6 +4,7 @@ using Avalonia.Interactivity;
using Avalonia.Styling; using Avalonia.Styling;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -22,14 +23,12 @@ namespace Ryujinx.Ava.UI.Windows
public static async Task Show() public static async Task Show()
{ {
var content = new AboutWindow();
ContentDialog contentDialog = new() ContentDialog contentDialog = new()
{ {
PrimaryButtonText = "", PrimaryButtonText = "",
SecondaryButtonText = "", SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
Content = content Content = new AboutWindow()
}; };
Style closeButton = new(x => x.Name("CloseButton")); Style closeButton = new(x => x.Name("CloseButton"));
@ -41,7 +40,7 @@ namespace Ryujinx.Ava.UI.Windows
contentDialog.Styles.Add(closeButton); contentDialog.Styles.Add(closeButton);
contentDialog.Styles.Add(closeButtonParent); contentDialog.Styles.Add(closeButtonParent);
await contentDialog.ShowAsync(); await ContentDialogHelper.ShowAsync(contentDialog);
} }
private void Button_OnClick(object sender, RoutedEventArgs e) private void Button_OnClick(object sender, RoutedEventArgs e)

View File

@ -1,4 +1,4 @@
<UserControl <UserControl
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"
@ -17,9 +17,9 @@
<Design.DataContext> <Design.DataContext>
<viewModels:AvatarProfileViewModel /> <viewModels:AvatarProfileViewModel />
</Design.DataContext> </Design.DataContext>
<UserControl.Resources> <UserControl.Resources>
<helpers:BitmapArrayValueConverter x:Key="ByteImage" /> <helpers:BitmapArrayValueConverter x:Key="ByteImage" />
</UserControl.Resources> </UserControl.Resources>
<Grid Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Grid Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />

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
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
namespace Ryujinx.Headless.SDL2
{
public enum HideCursor
{
Never,
OnIdle,
Always
}
}

View File

@ -5,7 +5,6 @@ using Ryujinx.Common.Logging;
using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.OpenGL;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using System; using System;
using static SDL2.SDL; using static SDL2.SDL;
namespace Ryujinx.Headless.SDL2.OpenGL namespace Ryujinx.Headless.SDL2.OpenGL
@ -103,7 +102,13 @@ namespace Ryujinx.Headless.SDL2.OpenGL
private GraphicsDebugLevel _glLogLevel; private GraphicsDebugLevel _glLogLevel;
private SDL2OpenGLContext _openGLContext; private SDL2OpenGLContext _openGLContext;
public OpenGLWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse) public OpenGLWindow(
InputManager inputManager,
GraphicsDebugLevel glLogLevel,
AspectRatio aspectRatio,
bool enableMouse,
HideCursor hideCursor)
: base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursor)
{ {
_glLogLevel = glLogLevel; _glLogLevel = glLogLevel;
} }
@ -161,4 +166,4 @@ namespace Ryujinx.Headless.SDL2.OpenGL
SDL_GL_SwapWindow(WindowHandle); SDL_GL_SwapWindow(WindowHandle);
} }
} }
} }

View File

@ -6,6 +6,14 @@ namespace Ryujinx.Headless.SDL2
{ {
public class Options public class Options
{ {
// General
[Option("root-data-dir", Required = false, HelpText = "Set the custom folder path for Ryujinx data.")]
public string BaseDataDir { get; set; }
[Option("profile", Required = false, HelpText = "Set the user profile to launch the game with.")]
public string UserProfile { get; set; }
// Input // Input
[Option("input-profile-1", Required = false, HelpText = "Set the input profile in use for Player 1.")] [Option("input-profile-1", Required = false, HelpText = "Set the input profile in use for Player 1.")]
@ -23,7 +31,7 @@ namespace Ryujinx.Headless.SDL2
[Option("input-profile-5", Required = false, HelpText = "Set the input profile in use for Player 5.")] [Option("input-profile-5", Required = false, HelpText = "Set the input profile in use for Player 5.")]
public string InputProfile5Name { get; set; } public string InputProfile5Name { get; set; }
[Option("input-profile-6", Required = false, HelpText = "Set the input profile in use for Player 5.")] [Option("input-profile-6", Required = false, HelpText = "Set the input profile in use for Player 6.")]
public string InputProfile6Name { get; set; } public string InputProfile6Name { get; set; }
[Option("input-profile-7", Required = false, HelpText = "Set the input profile in use for Player 7.")] [Option("input-profile-7", Required = false, HelpText = "Set the input profile in use for Player 7.")]
@ -63,42 +71,45 @@ namespace Ryujinx.Headless.SDL2
public string InputIdHandheld { get; set; } public string InputIdHandheld { get; set; }
[Option("enable-keyboard", Required = false, Default = false, HelpText = "Enable or disable keyboard support (Independent from controllers binding).")] [Option("enable-keyboard", Required = false, Default = false, HelpText = "Enable or disable keyboard support (Independent from controllers binding).")]
public bool? EnableKeyboard { get; set; } public bool EnableKeyboard { get; set; }
[Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")] [Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")]
public bool? EnableMouse { get; set; } public bool EnableMouse { get; set; }
[Option("hide-cursor", Required = false, Default = HideCursor.OnIdle, HelpText = "Change when the cursor gets hidden.")]
public HideCursor HideCursor { get; set; }
[Option("list-input-profiles", Required = false, HelpText = "List inputs profiles.")] [Option("list-input-profiles", Required = false, HelpText = "List inputs profiles.")]
public bool? ListInputProfiles { get; set; } public bool ListInputProfiles { get; set; }
[Option("list-inputs-ids", Required = false, HelpText = "List inputs ids.")] [Option("list-inputs-ids", Required = false, HelpText = "List inputs ids.")]
public bool ListInputIds { get; set; } public bool ListInputIds { get; set; }
// System // System
[Option("enable-ptc", Required = false, Default = true, HelpText = "Enables profiled translation cache persistency.")] [Option("disable-ptc", Required = false, HelpText = "Disables profiled persistent translation cache.")]
public bool? EnablePtc { get; set; } public bool DisablePtc { get; set; }
[Option("enable-internet-connection", Required = false, Default = false, HelpText = "Enables guest Internet connection.")] [Option("enable-internet-connection", Required = false, Default = false, HelpText = "Enables guest Internet connection.")]
public bool? EnableInternetAccess { get; set; } public bool EnableInternetAccess { get; set; }
[Option("enable-fs-integrity-checks", Required = false, Default = true, HelpText = "Enables integrity checks on Game content files.")] [Option("disable-fs-integrity-checks", Required = false, HelpText = "Disables integrity checks on Game content files.")]
public bool? EnableFsIntegrityChecks { get; set; } public bool DisableFsIntegrityChecks { get; set; }
[Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")] [Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")]
public int FsGlobalAccessLogMode { get; set; } public int FsGlobalAccessLogMode { get; set; }
[Option("enable-vsync", Required = false, Default = true, HelpText = "Enables Vertical Sync.")] [Option("disable-vsync", Required = false, HelpText = "Disables Vertical Sync.")]
public bool? EnableVsync { get; set; } public bool DisableVsync { get; set; }
[Option("enable-shader-cache", Required = false, Default = true, HelpText = "Enables Shader cache.")] [Option("disable-shader-cache", Required = false, HelpText = "Disables Shader cache.")]
public bool? EnableShaderCache { get; set; } public bool DisableShaderCache { get; set; }
[Option("enable-texture-recompression", Required = false, Default = false, HelpText = "Enables Texture recompression.")] [Option("enable-texture-recompression", Required = false, Default = false, HelpText = "Enables Texture recompression.")]
public bool? EnableTextureRecompression { get; set; } public bool EnableTextureRecompression { get; set; }
[Option("enable-docked-mode", Required = false, Default = true, HelpText = "Enables Docked Mode.")] [Option("disable-docked-mode", Required = false, HelpText = "Disables Docked Mode.")]
public bool? EnableDockedMode { get; set; } public bool DisableDockedMode { get; set; }
[Option("system-language", Required = false, Default = SystemLanguage.AmericanEnglish, HelpText = "Change System Language.")] [Option("system-language", Required = false, Default = SystemLanguage.AmericanEnglish, HelpText = "Change System Language.")]
public SystemLanguage SystemLanguage { get; set; } public SystemLanguage SystemLanguage { get; set; }
@ -120,32 +131,32 @@ namespace Ryujinx.Headless.SDL2
// Logging // Logging
[Option("enable-file-logging", Required = false, Default = false, HelpText = "Enables logging to a file on disk.")] [Option("disable-file-logging", Required = false, Default = false, HelpText = "Disables logging to a file on disk.")]
public bool? EnableFileLog { get; set; } public bool DisableFileLog { get; set; }
[Option("enable-debug-logs", Required = false, Default = false, HelpText = "Enables printing debug log messages.")] [Option("enable-debug-logs", Required = false, Default = false, HelpText = "Enables printing debug log messages.")]
public bool? LoggingEnableDebug { get; set; } public bool LoggingEnableDebug { get; set; }
[Option("enable-stub-logs", Required = false, Default = true, HelpText = "Enables printing stub log messages.")] [Option("disable-stub-logs", Required = false, HelpText = "Disables printing stub log messages.")]
public bool? LoggingEnableStub { get; set; } public bool LoggingDisableStub { get; set; }
[Option("enable-info-logs", Required = false, Default = true, HelpText = "Enables printing info log messages.")] [Option("disable-info-logs", Required = false, HelpText = "Disables printing info log messages.")]
public bool? LoggingEnableInfo { get; set; } public bool LoggingDisableInfo { get; set; }
[Option("enable-warning-logs", Required = false, Default = true, HelpText = "Enables printing warning log messages.")] [Option("disable-warning-logs", Required = false, HelpText = "Disables printing warning log messages.")]
public bool? LoggingEnableWarning { get; set; } public bool LoggingDisableWarning { get; set; }
[Option("enable-error-logs", Required = false, Default = true, HelpText = "Enables printing error log messages.")] [Option("disable-error-logs", Required = false, HelpText = "Disables printing error log messages.")]
public bool? LoggingEnableError { get; set; } public bool LoggingEnableError { get; set; }
[Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")] [Option("enable-trace-logs", Required = false, Default = false, HelpText = "Enables printing trace log messages.")]
public bool? LoggingEnableTrace { get; set; } public bool LoggingEnableTrace { get; set; }
[Option("enable-guest-logs", Required = false, Default = true, HelpText = "Enables printing guest log messages.")] [Option("disable-guest-logs", Required = false, HelpText = "Disables printing guest log messages.")]
public bool? LoggingEnableGuest { get; set; } public bool LoggingDisableGuest { get; set; }
[Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")] [Option("enable-fs-access-logs", Required = false, Default = false, HelpText = "Enables printing FS access log messages.")]
public bool? LoggingEnableFsAccessLog { get; set; } public bool LoggingEnableFsAccessLog { get; set; }
[Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")] [Option("graphics-debug-level", Required = false, Default = GraphicsDebugLevel.None, HelpText = "Change Graphics API debug log level.")]
public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; } public GraphicsDebugLevel LoggingGraphicsDebugLevel { get; set; }
@ -164,6 +175,9 @@ namespace Ryujinx.Headless.SDL2
[Option("backend-threading", Required = false, Default = BackendThreading.Auto, HelpText = "Whether or not backend threading is enabled. The \"Auto\" setting will determine whether threading should be enabled at runtime.")] [Option("backend-threading", Required = false, Default = BackendThreading.Auto, HelpText = "Whether or not backend threading is enabled. The \"Auto\" setting will determine whether threading should be enabled at runtime.")]
public BackendThreading BackendThreading { get; set; } public BackendThreading BackendThreading { get; set; }
[Option("disable-macro-hle", Required= false, HelpText = "Disables high-level emulation of Macro code. Leaving this enabled improves performance but may cause graphical glitches in some games.")]
public bool DisableMacroHLE { get; set; }
[Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")] [Option("graphics-shaders-dump-path", Required = false, HelpText = "Dumps shaders in this local directory. (Developer only)")]
public string GraphicsShadersDumpPath { get; set; } public string GraphicsShadersDumpPath { get; set; }
@ -176,14 +190,14 @@ namespace Ryujinx.Headless.SDL2
// Hacks // Hacks
[Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 6GiB.")] [Option("expand-ram", Required = false, Default = false, HelpText = "Expands the RAM amount on the emulated system from 4GiB to 6GiB.")]
public bool? ExpandRam { get; set; } public bool ExpandRam { get; set; }
[Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")] [Option("ignore-missing-services", Required = false, Default = false, HelpText = "Enable ignoring missing services.")]
public bool? IgnoreMissingServices { get; set; } public bool IgnoreMissingServices { get; set; }
// Values // Values
[Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)] [Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)]
public string InputPath { get; set; } public string InputPath { get; set; }
} }
} }

View File

@ -33,7 +33,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.Json; using System.Text.Json;
using System.Threading; using System.Threading;
using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
using Key = Ryujinx.Common.Configuration.Hid.Key; using Key = Ryujinx.Common.Configuration.Hid.Key;
@ -63,20 +62,6 @@ namespace Ryujinx.Headless.SDL2
Console.Title = $"Ryujinx Console {Version} (Headless SDL2)"; Console.Title = $"Ryujinx Console {Version} (Headless SDL2)";
AppDataManager.Initialize(null);
_virtualFileSystem = VirtualFileSystem.CreateInstance();
_libHacHorizonManager = new LibHacHorizonManager();
_libHacHorizonManager.InitializeFsServer(_virtualFileSystem);
_libHacHorizonManager.InitializeArpServer();
_libHacHorizonManager.InitializeBcatServer();
_libHacHorizonManager.InitializeSystemClients();
_contentManager = new ContentManager(_virtualFileSystem);
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient);
_userChannelPersistence = new UserChannelPersistence();
if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux()) if (OperatingSystem.IsMacOS() || OperatingSystem.IsLinux())
{ {
AutoResetEvent invoked = new AutoResetEvent(false); AutoResetEvent invoked = new AutoResetEvent(false);
@ -97,15 +82,9 @@ namespace Ryujinx.Headless.SDL2
}; };
} }
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
GraphicsConfig.EnableShaderCache = true;
Parser.Default.ParseArguments<Options>(args) Parser.Default.ParseArguments<Options>(args)
.WithParsed(options => Load(options)) .WithParsed(Load)
.WithNotParsed(errors => errors.Output()); .WithNotParsed(errors => errors.Output());
_inputManager.Dispose();
} }
private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) private static InputConfig HandlePlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
@ -343,6 +322,24 @@ namespace Ryujinx.Headless.SDL2
static void Load(Options option) static void Load(Options option)
{ {
AppDataManager.Initialize(option.BaseDataDir);
_virtualFileSystem = VirtualFileSystem.CreateInstance();
_libHacHorizonManager = new LibHacHorizonManager();
_libHacHorizonManager.InitializeFsServer(_virtualFileSystem);
_libHacHorizonManager.InitializeArpServer();
_libHacHorizonManager.InitializeBcatServer();
_libHacHorizonManager.InitializeSystemClients();
_contentManager = new ContentManager(_virtualFileSystem);
_accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, option.UserProfile);
_userChannelPersistence = new UserChannelPersistence();
_inputManager = new InputManager(new SDL2KeyboardDriver(), new SDL2GamepadDriver());
GraphicsConfig.EnableShaderCache = true;
IGamepad gamepad; IGamepad gamepad;
if (option.ListInputIds) if (option.ListInputIds)
@ -378,8 +375,8 @@ namespace Ryujinx.Headless.SDL2
} }
_inputConfiguration = new List<InputConfig>(); _inputConfiguration = new List<InputConfig>();
_enableKeyboard = (bool)option.EnableKeyboard; _enableKeyboard = option.EnableKeyboard;
_enableMouse = (bool)option.EnableMouse; _enableMouse = option.EnableMouse;
void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index) void LoadPlayerConfiguration(string inputProfileName, string inputId, PlayerIndex index)
{ {
@ -407,16 +404,16 @@ namespace Ryujinx.Headless.SDL2
} }
// Setup logging level // Setup logging level
Logger.SetEnable(LogLevel.Debug, (bool)option.LoggingEnableDebug); Logger.SetEnable(LogLevel.Debug, option.LoggingEnableDebug);
Logger.SetEnable(LogLevel.Stub, (bool)option.LoggingEnableStub); Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub);
Logger.SetEnable(LogLevel.Info, (bool)option.LoggingEnableInfo); Logger.SetEnable(LogLevel.Info, !option.LoggingDisableInfo);
Logger.SetEnable(LogLevel.Warning, (bool)option.LoggingEnableWarning); Logger.SetEnable(LogLevel.Warning, !option.LoggingDisableWarning);
Logger.SetEnable(LogLevel.Error, (bool)option.LoggingEnableError); Logger.SetEnable(LogLevel.Error, option.LoggingEnableError);
Logger.SetEnable(LogLevel.Trace, (bool)option.LoggingEnableTrace); Logger.SetEnable(LogLevel.Trace, option.LoggingEnableTrace);
Logger.SetEnable(LogLevel.Guest, (bool)option.LoggingEnableGuest); Logger.SetEnable(LogLevel.Guest, !option.LoggingDisableGuest);
Logger.SetEnable(LogLevel.AccessLog, (bool)option.LoggingEnableFsAccessLog); Logger.SetEnable(LogLevel.AccessLog, option.LoggingEnableFsAccessLog);
if ((bool)option.EnableFileLog) if (!option.DisableFileLog)
{ {
Logger.AddTarget(new AsyncLogTargetWrapper( Logger.AddTarget(new AsyncLogTargetWrapper(
new FileLogTarget(ReleaseInformation.GetBaseApplicationDirectory(), "file"), new FileLogTarget(ReleaseInformation.GetBaseApplicationDirectory(), "file"),
@ -426,11 +423,12 @@ namespace Ryujinx.Headless.SDL2
} }
// Setup graphics configuration // Setup graphics configuration
GraphicsConfig.EnableShaderCache = (bool)option.EnableShaderCache; GraphicsConfig.EnableShaderCache = !option.DisableShaderCache;
GraphicsConfig.EnableTextureRecompression = (bool)option.EnableTextureRecompression; GraphicsConfig.EnableTextureRecompression = option.EnableTextureRecompression;
GraphicsConfig.ResScale = option.ResScale; GraphicsConfig.ResScale = option.ResScale;
GraphicsConfig.MaxAnisotropy = option.MaxAnisotropy; GraphicsConfig.MaxAnisotropy = option.MaxAnisotropy;
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath; GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
while (true) while (true)
{ {
@ -443,6 +441,8 @@ namespace Ryujinx.Headless.SDL2
_userChannelPersistence.ShouldRestart = false; _userChannelPersistence.ShouldRestart = false;
} }
_inputManager.Dispose();
} }
private static void SetupProgressHandler() private static void SetupProgressHandler()
@ -479,8 +479,8 @@ namespace Ryujinx.Headless.SDL2
private static WindowBase CreateWindow(Options options) private static WindowBase CreateWindow(Options options)
{ {
return options.GraphicsBackend == GraphicsBackend.Vulkan return options.GraphicsBackend == GraphicsBackend.Vulkan
? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse) ? new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursor)
: new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, (bool)options.EnableMouse); : new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursor);
} }
private static IRenderer CreateRenderer(Options options, WindowBase window) private static IRenderer CreateRenderer(Options options, WindowBase window)
@ -533,20 +533,20 @@ namespace Ryujinx.Headless.SDL2
_userChannelPersistence, _userChannelPersistence,
renderer, renderer,
new SDL2HardwareDeviceDriver(), new SDL2HardwareDeviceDriver(),
(bool)options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB, options.ExpandRam ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB,
window, window,
options.SystemLanguage, options.SystemLanguage,
options.SystemRegion, options.SystemRegion,
(bool)options.EnableVsync, !options.DisableVsync,
(bool)options.EnableDockedMode, !options.DisableDockedMode,
(bool)options.EnablePtc, !options.DisablePtc,
(bool)options.EnableInternetAccess, options.EnableInternetAccess,
(bool)options.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
options.FsGlobalAccessLogMode, options.FsGlobalAccessLogMode,
options.SystemTimeOffset, options.SystemTimeOffset,
options.SystemTimeZone, options.SystemTimeZone,
options.MemoryManagerMode, options.MemoryManagerMode,
(bool)options.IgnoreMissingServices, options.IgnoreMissingServices,
options.AspectRatio, options.AspectRatio,
options.AudioVolume); options.AudioVolume);
@ -649,7 +649,7 @@ namespace Ryujinx.Headless.SDL2
} }
else else
{ {
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); Logger.Warning?.Print(LogClass.Application, $"Couldn't load '{options.InputPath}'. Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
_emulationContext.Dispose(); _emulationContext.Dispose();

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
@ -31,16 +31,26 @@
<PackageReference Include="CommandLineParser" /> <PackageReference Include="CommandLineParser" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="..\distribution\legal\THIRDPARTY.md"> <Content Include="..\distribution\legal\THIRDPARTY.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>THIRDPARTY.md</TargetPath> <TargetPath>THIRDPARTY.md</TargetPath>
</Content> </Content>
<Content Include="..\LICENSE.txt"> <Content Include="..\LICENSE.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
<TargetPath>LICENSE.txt</TargetPath> <TargetPath>LICENSE.txt</TargetPath>
</Content> </Content>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(RuntimeIdentifier)' == 'linux-x64'">
<Content Include="..\distribution\linux\Ryujinx.sh">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Ryujinx.bmp" />
</ItemGroup>
<!-- Due to .net core 3.1 embedded resource loading --> <!-- Due to .net core 3.1 embedded resource loading -->
<PropertyGroup> <PropertyGroup>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -10,7 +10,12 @@ namespace Ryujinx.Headless.SDL2
{ {
class SDL2MouseDriver : IGamepadDriver class SDL2MouseDriver : IGamepadDriver
{ {
private const int CursorHideIdleTime = 8; // seconds
private bool _isDisposed; private bool _isDisposed;
private HideCursor _hideCursor;
private bool _isHidden;
private long _lastCursorMoveTime;
public bool[] PressedButtons { get; } public bool[] PressedButtons { get; }
@ -18,9 +23,16 @@ namespace Ryujinx.Headless.SDL2
public Vector2 Scroll { get; private set; } public Vector2 Scroll { get; private set; }
public Size _clientSize; public Size _clientSize;
public SDL2MouseDriver() public SDL2MouseDriver(HideCursor hideCursor)
{ {
PressedButtons = new bool[(int)MouseButton.Count]; PressedButtons = new bool[(int)MouseButton.Count];
_hideCursor = hideCursor;
if (_hideCursor == HideCursor.Always)
{
SDL_ShowCursor(SDL_DISABLE);
_isHidden = true;
}
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -31,26 +43,75 @@ namespace Ryujinx.Headless.SDL2
return (MouseButton)(rawButton - 1); return (MouseButton)(rawButton - 1);
} }
public void Update(SDL_Event evnt) public void UpdatePosition()
{ {
if (evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN || evnt.type == SDL_EventType.SDL_MOUSEBUTTONUP) SDL_GetMouseState(out int posX, out int posY);
Vector2 position = new(posX, posY);
if (CurrentPosition != position)
{ {
uint rawButton = evnt.button.button; CurrentPosition = position;
_lastCursorMoveTime = Stopwatch.GetTimestamp();
}
if (rawButton > 0 && rawButton <= (int)MouseButton.Count) CheckIdle();
}
private void CheckIdle()
{
if (_hideCursor != HideCursor.OnIdle)
{
return;
}
long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime;
if (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency)
{
if (!_isHidden)
{ {
PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN; SDL_ShowCursor(SDL_DISABLE);
_isHidden = true;
CurrentPosition = new Vector2(evnt.button.x, evnt.button.y);
} }
} }
else if (evnt.type == SDL_EventType.SDL_MOUSEMOTION) else
{ {
CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y); if (_isHidden)
{
SDL_ShowCursor(SDL_ENABLE);
_isHidden = false;
}
} }
else if (evnt.type == SDL_EventType.SDL_MOUSEWHEEL) }
public void Update(SDL_Event evnt)
{
switch (evnt.type)
{ {
Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y); case SDL_EventType.SDL_MOUSEBUTTONDOWN:
case SDL_EventType.SDL_MOUSEBUTTONUP:
uint rawButton = evnt.button.button;
if (rawButton > 0 && rawButton <= (int)MouseButton.Count)
{
PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN;
CurrentPosition = new Vector2(evnt.button.x, evnt.button.y);
}
break;
// NOTE: On Linux using Wayland mouse motion events won't be received at all.
case SDL_EventType.SDL_MOUSEMOTION:
CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y);
_lastCursorMoveTime = Stopwatch.GetTimestamp();
break;
case SDL_EventType.SDL_MOUSEWHEEL:
Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y);
break;
} }
} }
@ -100,4 +161,4 @@ namespace Ryujinx.Headless.SDL2
_isDisposed = true; _isDisposed = true;
} }
} }
} }

View File

@ -12,7 +12,13 @@ namespace Ryujinx.Headless.SDL2.Vulkan
{ {
private GraphicsDebugLevel _glLogLevel; private GraphicsDebugLevel _glLogLevel;
public VulkanWindow(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) : base(inputManager, glLogLevel, aspectRatio, enableMouse) public VulkanWindow(
InputManager inputManager,
GraphicsDebugLevel glLogLevel,
AspectRatio aspectRatio,
bool enableMouse,
HideCursor hideCursor)
: base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursor)
{ {
_glLogLevel = glLogLevel; _glLogLevel = glLogLevel;
} }
@ -95,4 +101,4 @@ namespace Ryujinx.Headless.SDL2.Vulkan
protected override void SwapBuffers() { } protected override void SwapBuffers() { }
} }
} }

View File

@ -14,13 +14,16 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using static SDL2.SDL; using static SDL2.SDL;
using Switch = Ryujinx.HLE.Switch; using Switch = Ryujinx.HLE.Switch;
namespace Ryujinx.Headless.SDL2 namespace Ryujinx.Headless.SDL2
{ {
abstract class WindowBase : IHostUiHandler, IDisposable abstract partial class WindowBase : IHostUiHandler, IDisposable
{ {
protected const int DefaultWidth = 1280; protected const int DefaultWidth = 1280;
protected const int DefaultHeight = 720; protected const int DefaultHeight = 720;
@ -29,6 +32,10 @@ namespace Ryujinx.Headless.SDL2
private static ConcurrentQueue<Action> MainThreadActions = new ConcurrentQueue<Action>(); private static ConcurrentQueue<Action> MainThreadActions = new ConcurrentQueue<Action>();
[LibraryImport("SDL2")]
// TODO: Remove this as soon as SDL2-CS was updated to expose this method publicly
private static partial IntPtr SDL_LoadBMP_RW(IntPtr src, int freesrc);
public static void QueueMainThreadAction(Action action) public static void QueueMainThreadAction(Action action)
{ {
MainThreadActions.Enqueue(action); MainThreadActions.Enqueue(action);
@ -66,9 +73,14 @@ namespace Ryujinx.Headless.SDL2
private AspectRatio _aspectRatio; private AspectRatio _aspectRatio;
private bool _enableMouse; private bool _enableMouse;
public WindowBase(InputManager inputManager, GraphicsDebugLevel glLogLevel, AspectRatio aspectRatio, bool enableMouse) public WindowBase(
InputManager inputManager,
GraphicsDebugLevel glLogLevel,
AspectRatio aspectRatio,
bool enableMouse,
HideCursor hideCursor)
{ {
MouseDriver = new SDL2MouseDriver(); MouseDriver = new SDL2MouseDriver(hideCursor);
_inputManager = inputManager; _inputManager = inputManager;
_inputManager.SetMouseDriver(MouseDriver); _inputManager.SetMouseDriver(MouseDriver);
NpadManager = _inputManager.CreateNpadManager(); NpadManager = _inputManager.CreateNpadManager();
@ -103,6 +115,34 @@ namespace Ryujinx.Headless.SDL2
TouchScreenManager.Initialize(device); TouchScreenManager.Initialize(device);
} }
private void SetWindowIcon()
{
Stream iconStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.Headless.SDL2.Ryujinx.bmp");
byte[] iconBytes = new byte[iconStream!.Length];
if (iconStream.Read(iconBytes, 0, iconBytes.Length) != iconBytes.Length)
{
Logger.Error?.Print(LogClass.Application, "Failed to read icon to byte array.");
iconStream.Close();
return;
}
iconStream.Close();
unsafe
{
fixed (byte* iconPtr = iconBytes)
{
IntPtr rwOpsStruct = SDL_RWFromConstMem((IntPtr)iconPtr, iconBytes.Length);
IntPtr iconHandle = SDL_LoadBMP_RW(rwOpsStruct, 1);
SDL_SetWindowIcon(WindowHandle, iconHandle);
SDL_FreeSurface(iconHandle);
}
}
}
private void InitializeWindow() private void InitializeWindow()
{ {
string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty string titleNameSection = string.IsNullOrWhiteSpace(Device.Application.TitleName) ? string.Empty
@ -127,6 +167,8 @@ namespace Ryujinx.Headless.SDL2
throw new Exception(errorMessage); throw new Exception(errorMessage);
} }
SetWindowIcon();
_windowId = SDL_GetWindowID(WindowHandle); _windowId = SDL_GetWindowID(WindowHandle);
SDL2Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent); SDL2Driver.Instance.RegisterWindow(_windowId, HandleWindowEvent);
@ -146,9 +188,11 @@ namespace Ryujinx.Headless.SDL2
Renderer?.Window.SetSize(Width, Height); Renderer?.Window.SetSize(Width, Height);
MouseDriver.SetClientSize(Width, Height); MouseDriver.SetClientSize(Width, Height);
break; break;
case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE: case SDL_WindowEventID.SDL_WINDOWEVENT_CLOSE:
Exit(); Exit();
break; break;
default: default:
break; break;
} }
@ -331,6 +375,9 @@ namespace Ryujinx.Headless.SDL2
Device.Hid.DebugPad.Update(); Device.Hid.DebugPad.Update();
// TODO: Replace this with MouseDriver.CheckIdle() when mouse motion events are received on every supported platform.
MouseDriver.UpdatePosition();
return true; return true;
} }
@ -451,4 +498,4 @@ namespace Ryujinx.Headless.SDL2
} }
} }
} }
} }

View File

@ -163,7 +163,7 @@ namespace Ryujinx.Input.HLE
ReloadConfiguration(inputConfig, enableKeyboard, enableMouse); ReloadConfiguration(inputConfig, enableKeyboard, enableMouse);
} }
public void Update(float aspectRatio = 0) public void Update(float aspectRatio = 1)
{ {
lock (_lock) lock (_lock)
{ {

View File

@ -7,6 +7,7 @@ namespace Ryujinx.Ui.Common.Helper
{ {
public static string[] Arguments { get; private set; } public static string[] Arguments { get; private set; }
public static bool? OverrideDockedMode { get; private set; }
public static string OverrideGraphicsBackend { get; private set; } public static string OverrideGraphicsBackend { get; private set; }
public static string BaseDirPathArg { get; private set; } public static string BaseDirPathArg { get; private set; }
public static string Profile { get; private set; } public static string Profile { get; private set; }
@ -69,6 +70,12 @@ namespace Ryujinx.Ui.Common.Helper
OverrideGraphicsBackend = args[++i]; OverrideGraphicsBackend = args[++i];
break; break;
case "--docked-mode":
OverrideDockedMode = true;
break;
case "--handheld-mode":
OverrideDockedMode = false;
break;
default: default:
LaunchPathArg = arg; LaunchPathArg = arg;
break; break;

View File

@ -3,8 +3,8 @@ using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Common.SystemInfo; using Ryujinx.Common.SystemInfo;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Modules; using Ryujinx.Modules;
using Ryujinx.SDL2.Common; using Ryujinx.SDL2.Common;
using Ryujinx.Ui; using Ryujinx.Ui;
@ -271,6 +271,12 @@ namespace Ryujinx
} }
} }
// Check if docked mode was overriden.
if (CommandLineState.OverrideDockedMode.HasValue)
{
ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value;
}
// Logging system information. // Logging system information.
PrintSystemInfo(); PrintSystemInfo();

View File

@ -1,5 +1,14 @@
#!/bin/sh #!/bin/sh
SCRIPT_DIR=$(dirname $(realpath $0)) SCRIPT_DIR=$(dirname $(realpath $0))
RYUJINX_BIN="Ryujinx"
env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/Ryujinx" "$@" if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then
RYUJINX_BIN="Ryujinx.Ava"
fi
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
RYUJINX_BIN="Ryujinx.Headless.SDL2"
fi
env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/$RYUJINX_BIN" "$@"