[Ryujinx.Ava] Address dotnet-format issues (#5361)

* dotnet format style --severity info

Some changes were manually reverted.

* dotnet format analyzers --serverity info

Some changes have been minimally adapted.

* Restore a few unused methods and variables

* Silence dotnet format IDE0060 warnings

* Silence dotnet format IDE0052 warnings

* Silence dotnet format IDE0059 warnings

* Address or silence dotnet format IDE1006 warnings

* Address dotnet format CA1816 warnings

* Address dotnet format CA1822 warnings

* Address or silence dotnet format CA1069 warnings

* Make dotnet format succeed in style mode

* Address dotnet format CA1401 warnings

* Address remaining dotnet format analyzer warnings

* Address review comments

* dotnet-format fixes after rebase

* Address most dotnet format whitespace warnings

* Apply dotnet format whitespace formatting

A few of them have been manually reverted and the corresponding warning was silenced

* Format if-blocks correctly

* Another rebase, another dotnet format run

* Run dotnet format whitespace after rebase

* Run dotnet format style after rebase

* Run dotnet format after rebase and remove unused usings

- analyzers
- style
- whitespace

* Add comments to disabled warnings

* Remove a few unused parameters

* Simplify properties and array initialization, Use const when possible, Remove trailing commas

* Start working on disabled warnings

* Fix and silence a few dotnet-format warnings again

* Address IDE0260 warnings

* Address a few disabled IDE0060 warnings

* Silence IDE0060 in .editorconfig

* Revert "Simplify properties and array initialization, Use const when possible, Remove trailing commas"

This reverts commit 9462e4136c0a2100dc28b20cf9542e06790aa67e.

* dotnet format whitespace after rebase

* dotnet format pass with new editorconfig

* Fix naming style issues

* Apply suggestions from code review

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

* Revert one suggestion

* Second dotnet format pass and fix build issues

* Final pass of dotnet format

* Add trailing commas

* Fix formatting issues

* Keep unnecessary assignment in IconColorPicker.cs

* Use using declarations and extend resource lifetimes

* Fix rebase issues

* Adjust comment spacing

* Fix typo

* Fix naming issues

* Apply suggestions from code review

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

* Revert unintentional change

* Remove unused file

* Remove static keyword from ViewModels

Binding of static members doesn't work and is silently ignored.

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
This commit is contained in:
TSRBerry 2023-07-07 23:03:27 +02:00 committed by GitHub
parent 8a363b5df2
commit 6c515e1822
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
108 changed files with 1140 additions and 1503 deletions

View file

@ -5,7 +5,6 @@ using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.Styling; using FluentAvalonia.Styling;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common; using Ryujinx.Common;
@ -67,7 +66,7 @@ namespace Ryujinx.Ava
if (result == UserResult.Yes) if (result == UserResult.Yes)
{ {
var path = Process.GetCurrentProcess().MainModule.FileName; var path = Environment.ProcessPath;
var proc = Process.Start(path, CommandLineState.Arguments); var proc = Process.Start(path, CommandLineState.Arguments);
desktop.Shutdown(); desktop.Shutdown();
Environment.Exit(0); Environment.Exit(0);

View file

@ -1,4 +1,5 @@
using ARMeilleure.Translation; using ARMeilleure.Translation;
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input; using Avalonia.Input;
@ -26,6 +27,7 @@ using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
@ -41,7 +43,6 @@ using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using SPB.Graphics.Exceptions;
using SPB.Graphics.Vulkan; using SPB.Graphics.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -50,10 +51,12 @@ using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop; using static Ryujinx.Ava.UI.Helpers.Win32NativeInterop;
using AntiAliasing = Ryujinx.Common.Configuration.AntiAliasing;
using Image = SixLabors.ImageSharp.Image; using Image = SixLabors.ImageSharp.Image;
using InputManager = Ryujinx.Input.HLE.InputManager; using InputManager = Ryujinx.Input.HLE.InputManager;
using Key = Ryujinx.Input.Key; using Key = Ryujinx.Input.Key;
using MouseButton = Ryujinx.Input.MouseButton; using MouseButton = Ryujinx.Input.MouseButton;
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
using Size = Avalonia.Size; using Size = Avalonia.Size;
using Switch = Ryujinx.HLE.Switch; using Switch = Ryujinx.HLE.Switch;
@ -66,9 +69,9 @@ namespace Ryujinx.Ava
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(StandardCursorType.None);
private readonly IntPtr InvisibleCursorWin; private readonly IntPtr _invisibleCursorWin;
private readonly IntPtr DefaultCursorWin; private readonly IntPtr _defaultCursorWin;
private readonly long _ticksPerFrame; private readonly long _ticksPerFrame;
private readonly Stopwatch _chrono; private readonly Stopwatch _chrono;
@ -81,7 +84,7 @@ namespace Ryujinx.Ava
private readonly MainWindowViewModel _viewModel; private readonly MainWindowViewModel _viewModel;
private readonly IKeyboard _keyboardInterface; private readonly IKeyboard _keyboardInterface;
private readonly TopLevel _topLevel; private readonly TopLevel _topLevel;
public RendererHost _rendererHost; public RendererHost RendererHost;
private readonly GraphicsDebugLevel _glLogLevel; private readonly GraphicsDebugLevel _glLogLevel;
private float _newVolume; private float _newVolume;
@ -94,7 +97,7 @@ namespace Ryujinx.Ava
private bool _isActive; private bool _isActive;
private bool _renderingStarted; private bool _renderingStarted;
private ManualResetEvent _gpuDoneEvent; private readonly ManualResetEvent _gpuDoneEvent;
private IRenderer _renderer; private IRenderer _renderer;
private readonly Thread _renderingThread; private readonly Thread _renderingThread;
@ -150,14 +153,14 @@ namespace Ryujinx.Ava
VirtualFileSystem = virtualFileSystem; VirtualFileSystem = virtualFileSystem;
ContentManager = contentManager; ContentManager = contentManager;
_rendererHost = renderer; RendererHost = renderer;
_chrono = new Stopwatch(); _chrono = new Stopwatch();
_ticksPerFrame = Stopwatch.Frequency / TargetFps; _ticksPerFrame = Stopwatch.Frequency / TargetFps;
if (ApplicationPath.StartsWith("@SystemContent")) if (ApplicationPath.StartsWith("@SystemContent"))
{ {
ApplicationPath = _viewModel.VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath); ApplicationPath = VirtualFileSystem.SwitchPathToSystemPath(ApplicationPath);
_isFirmwareTitle = true; _isFirmwareTitle = true;
} }
@ -170,8 +173,8 @@ namespace Ryujinx.Ava
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
InvisibleCursorWin = CreateEmptyCursor(); _invisibleCursorWin = CreateEmptyCursor();
DefaultCursorWin = CreateArrowCursor(); _defaultCursorWin = CreateArrowCursor();
} }
ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState;
@ -196,10 +199,10 @@ namespace Ryujinx.Ava
{ {
_lastCursorMoveTime = Stopwatch.GetTimestamp(); _lastCursorMoveTime = Stopwatch.GetTimestamp();
if (_rendererHost.EmbeddedWindow.TransformedBounds != null) if (RendererHost.EmbeddedWindow.TransformedBounds != null)
{ {
var point = e.GetCurrentPoint(window).Position; var point = e.GetCurrentPoint(window).Position;
var bounds = _rendererHost.EmbeddedWindow.TransformedBounds.Value.Clip; var bounds = RendererHost.EmbeddedWindow.TransformedBounds.Value.Clip;
_isCursorInRenderer = point.X >= bounds.X && _isCursorInRenderer = point.X >= bounds.X &&
point.X <= bounds.Width + bounds.X && point.X <= bounds.Width + bounds.X &&
@ -220,7 +223,7 @@ namespace Ryujinx.Ava
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
} }
private void UpdateScalingFilter(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.ScalingFilter> e) private void UpdateScalingFilter(object sender, ReactiveEventArgs<ScalingFilter> e)
{ {
_renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
@ -234,7 +237,7 @@ namespace Ryujinx.Ava
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
SetCursor(DefaultCursorWin); SetCursor(_defaultCursorWin);
} }
}); });
} }
@ -243,11 +246,11 @@ namespace Ryujinx.Ava
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
{ {
_viewModel.Cursor = InvisibleCursor; _viewModel.Cursor = _invisibleCursor;
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
SetCursor(InvisibleCursorWin); SetCursor(_invisibleCursorWin);
} }
}); });
} }
@ -276,7 +279,7 @@ namespace Ryujinx.Ava
string directory = AppDataManager.Mode switch string directory = AppDataManager.Mode switch
{ {
AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"), AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => 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);
@ -305,9 +308,9 @@ namespace Ryujinx.Ava
image.Mutate(x => x.Flip(FlipMode.Vertical)); image.Mutate(x => x.Flip(FlipMode.Vertical));
} }
image.SaveAsPng(path, new PngEncoder() image.SaveAsPng(path, new PngEncoder
{ {
ColorType = PngColorType.Rgb ColorType = PngColorType.Rgb,
}); });
image.Dispose(); image.Dispose();
@ -348,9 +351,9 @@ namespace Ryujinx.Ava
_viewModel.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}"; _viewModel.Title = $"Ryujinx {Program.Version} -{titleNameSection}{titleVersionSection}{titleIdSection}{titleArchSection}";
}); });
_viewModel.SetUIProgressHandlers(Device); _viewModel.SetUiProgressHandlers(Device);
_rendererHost.SizeChanged += Window_SizeChanged; RendererHost.SizeChanged += Window_SizeChanged;
_isActive = true; _isActive = true;
@ -379,7 +382,7 @@ namespace Ryujinx.Ava
} }
} }
private void UpdateAntiAliasing(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.AntiAliasing> e) private void UpdateAntiAliasing(object sender, ReactiveEventArgs<AntiAliasing> e)
{ {
_renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue); _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue);
} }
@ -448,7 +451,7 @@ namespace Ryujinx.Ava
{ {
if (Device.Processes != null) if (Device.Processes != null)
{ {
_viewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText); MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText);
} }
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState; ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
@ -477,7 +480,7 @@ namespace Ryujinx.Ava
_windowsMultimediaTimerResolution = null; _windowsMultimediaTimerResolution = null;
} }
if (_rendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow) if (RendererHost.EmbeddedWindow is EmbeddedWindowOpenGL openGlWindow)
{ {
// Try to bind the OpenGL context before calling the shutdown event. // Try to bind the OpenGL context before calling the shutdown event.
openGlWindow.MakeCurrent(false, false); openGlWindow.MakeCurrent(false, false);
@ -508,7 +511,7 @@ namespace Ryujinx.Ava
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError)) if (!SetupValidator.CanStartApplication(ContentManager, ApplicationPath, out UserError userError))
{ {
@ -526,7 +529,7 @@ namespace Ryujinx.Ava
if (result != UserResult.Yes) if (result != UserResult.Yes)
{ {
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow)); await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose(); Device.Dispose();
return false; return false;
@ -535,7 +538,7 @@ namespace Ryujinx.Ava
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
{ {
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow)); await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose(); Device.Dispose();
return false; return false;
@ -558,7 +561,7 @@ namespace Ryujinx.Ava
} }
else else
{ {
await UserErrorDialog.ShowUserErrorDialog(userError, (desktop.MainWindow as MainWindow)); await UserErrorDialog.ShowUserErrorDialog(userError);
Device.Dispose(); Device.Dispose();
return false; return false;
@ -727,7 +730,7 @@ namespace Ryujinx.Ava
{ {
renderer = new VulkanRenderer( renderer = new VulkanRenderer(
Vk.GetApi(), Vk.GetApi(),
(_rendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface, (RendererHost.EmbeddedWindow as EmbeddedWindowVulkan).CreateSurface,
VulkanHelper.GetRequiredInstanceExtensions, VulkanHelper.GetRequiredInstanceExtensions,
ConfigurationState.Instance.Graphics.PreferredGpu.Value); ConfigurationState.Instance.Graphics.PreferredGpu.Value);
} }
@ -738,18 +741,18 @@ namespace Ryujinx.Ava
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);
} }
Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALthreaded}"); Logger.Info?.PrintMsg(LogClass.Gpu, $"Backend Threading ({threadingMode}): {isGALThreaded}");
// Initialize Configuration. // Initialize Configuration.
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? HLE.MemoryConfiguration.MemoryConfiguration6GiB : HLE.MemoryConfiguration.MemoryConfiguration4GiB; var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB;
HLE.HLEConfiguration configuration = new(VirtualFileSystem, HLEConfiguration configuration = new(VirtualFileSystem,
_viewModel.LibHacHorizonManager, _viewModel.LibHacHorizonManager,
ContentManager, ContentManager,
_accountManager, _accountManager,
@ -780,12 +783,12 @@ namespace Ryujinx.Ava
private static IHardwareDeviceDriver InitializeAudio() private static IHardwareDeviceDriver InitializeAudio()
{ {
var availableBackends = new List<AudioBackend>() var availableBackends = new List<AudioBackend>
{ {
AudioBackend.SDL2, AudioBackend.SDL2,
AudioBackend.SoundIo, AudioBackend.SoundIo,
AudioBackend.OpenAl, AudioBackend.OpenAl,
AudioBackend.Dummy AudioBackend.Dummy,
}; };
AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value; AudioBackend preferredBackend = ConfigurationState.Instance.System.AudioBackend.Value;
@ -806,13 +809,11 @@ namespace Ryujinx.Ava
{ {
return new T(); return new T();
} }
else
{
Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}."); Logger.Warning?.Print(LogClass.Audio, $"{backend} is not supported, falling back to {nextBackend}.");
return null; return null;
} }
}
IHardwareDeviceDriver deviceDriver = null; IHardwareDeviceDriver deviceDriver = null;
@ -826,7 +827,7 @@ namespace Ryujinx.Ava
AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend), AudioBackend.SDL2 => InitializeAudioBackend<SDL2HardwareDeviceDriver>(AudioBackend.SDL2, nextBackend),
AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend), AudioBackend.SoundIo => InitializeAudioBackend<SoundIoHardwareDeviceDriver>(AudioBackend.SoundIo, nextBackend),
AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend), AudioBackend.OpenAl => InitializeAudioBackend<OpenALHardwareDeviceDriver>(AudioBackend.OpenAl, nextBackend),
_ => new DummyHardwareDeviceDriver() _ => new DummyHardwareDeviceDriver(),
}; };
if (deviceDriver != null) if (deviceDriver != null)
@ -879,7 +880,7 @@ namespace Ryujinx.Ava
_renderer.ScreenCaptured += Renderer_ScreenCaptured; _renderer.ScreenCaptured += Renderer_ScreenCaptured;
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer); (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.InitializeBackgroundContext(_renderer);
Device.Gpu.Renderer.Initialize(_glLogLevel); Device.Gpu.Renderer.Initialize(_glLogLevel);
@ -887,8 +888,8 @@ namespace Ryujinx.Ava
_renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); _renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
_renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); _renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
Width = (int)_rendererHost.Bounds.Width; Width = (int)RendererHost.Bounds.Width;
Height = (int)_rendererHost.Bounds.Height; Height = (int)RendererHost.Bounds.Height;
_renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling)); _renderer.Window.SetSize((int)(Width * _topLevel.PlatformImpl.RenderScaling), (int)(Height * _topLevel.PlatformImpl.RenderScaling));
@ -923,7 +924,7 @@ namespace Ryujinx.Ava
_viewModel.SwitchToRenderer(false); _viewModel.SwitchToRenderer(false);
} }
Device.PresentFrame(() => (_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers()); Device.PresentFrame(() => (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.SwapBuffers());
} }
if (_ticks >= _ticksPerFrame) if (_ticks >= _ticksPerFrame)
@ -941,7 +942,7 @@ namespace Ryujinx.Ava
_gpuDoneEvent.Set(); _gpuDoneEvent.Set();
}); });
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true); (RendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(true);
} }
public void UpdateStatus() public void UpdateStatus()

View file

@ -18,7 +18,6 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
@ -27,6 +26,7 @@ using System.Buffers;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using ApplicationId = LibHac.Ncm.ApplicationId;
using Path = System.IO.Path; using Path = System.IO.Path;
namespace Ryujinx.Ava.Common namespace Ryujinx.Ava.Common
@ -57,7 +57,7 @@ namespace Ryujinx.Ava.Common
Logger.Info?.Print(LogClass.Application, $"Creating save directory for Title: {titleName} [{titleId:x16}]"); Logger.Info?.Print(LogClass.Application, $"Creating save directory for Title: {titleName} [{titleId:x16}]");
if (Utilities.IsZeros(controlHolder.ByteSpan)) if (controlHolder.ByteSpan.IsZeros())
{ {
// If the current application doesn't have a loaded control property, create a dummy one // If the current application doesn't have a loaded control property, create a dummy one
// and set the savedata sizes so a user savedata will be created. // and set the savedata sizes so a user savedata will be created.
@ -72,7 +72,7 @@ namespace Ryujinx.Ava.Common
Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low);
result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user); result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new ApplicationId(titleId), in control, in user);
if (result.IsFailure()) if (result.IsFailure())
{ {
Dispatcher.UIThread.InvokeAsync(async () => Dispatcher.UIThread.InvokeAsync(async () =>
@ -147,7 +147,7 @@ namespace Ryujinx.Ava.Common
{ {
OpenFolderDialog folderDialog = new() OpenFolderDialog folderDialog = new()
{ {
Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle] Title = LocaleManager.Instance[LocaleKeys.FolderDialogExtractTitle],
}; };
string destination = await folderDialog.ShowAsync(_owner); string destination = await folderDialog.ShowAsync(_owner);
@ -293,10 +293,11 @@ namespace Ryujinx.Ava.Common
await ContentDialogHelper.CreateErrorDialog(ex.Message); await ContentDialogHelper.CreateErrorDialog(ex.Message);
}); });
} }
}); })
{
extractorThread.Name = "GUI.NcaSectionExtractorThread"; Name = "GUI.NcaSectionExtractorThread",
extractorThread.IsBackground = true; IsBackground = true,
};
extractorThread.Start(); extractorThread.Start();
} }
} }

View file

@ -10,6 +10,6 @@ namespace Ryujinx.Ava.Common
FileType, FileType,
FileSize, FileSize,
Path, Path,
Favorite Favorite,
} }
} }

View file

@ -11,6 +11,6 @@
ResScaleUp, ResScaleUp,
ResScaleDown, ResScaleDown,
VolumeUp, VolumeUp,
VolumeDown VolumeDown,
} }
} }

View file

@ -21,7 +21,7 @@ namespace Ryujinx.Ava.Common.Locale
ReflectionBindingExtension binding = new($"[{keyToUse}]") ReflectionBindingExtension binding = new($"[{keyToUse}]")
{ {
Mode = BindingMode.OneWay, Mode = BindingMode.OneWay,
Source = LocaleManager.Instance Source = LocaleManager.Instance,
}; };
return binding.ProvideValue(serviceProvider); return binding.ProvideValue(serviceProvider);

View file

@ -13,11 +13,11 @@ namespace Ryujinx.Ava.Common.Locale
{ {
private const string DefaultLanguageCode = "en_US"; private const string DefaultLanguageCode = "en_US";
private Dictionary<LocaleKeys, string> _localeStrings; private readonly Dictionary<LocaleKeys, string> _localeStrings;
private Dictionary<LocaleKeys, string> _localeDefaultStrings; private Dictionary<LocaleKeys, string> _localeDefaultStrings;
private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues; private readonly ConcurrentDictionary<LocaleKeys, object[]> _dynamicValues;
public static LocaleManager Instance { get; } = new LocaleManager(); public static LocaleManager Instance { get; } = new();
public LocaleManager() public LocaleManager()
{ {
@ -126,7 +126,7 @@ namespace Ryujinx.Ava.Common.Locale
} }
} }
private Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode) private static Dictionary<LocaleKeys, string> LoadJsonLanguage(string languageCode = DefaultLanguageCode)
{ {
var localeStrings = new Dictionary<LocaleKeys, string>(); var localeStrings = new Dictionary<LocaleKeys, string>();
string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json"); string languageJson = EmbeddedResources.ReadAllText($"Ryujinx.Ava/Assets/Locales/{languageCode}.json");

View file

@ -120,6 +120,7 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping.Clear(); _buttonsUserMapping.Clear();
#pragma warning disable IDE0055 // Disable formatting
// Left JoyCon // Left JoyCon
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp));
@ -143,6 +144,7 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl));
#pragma warning restore IDE0055
} }
} }

View file

@ -143,7 +143,7 @@ namespace Ryujinx.Ava.Input
AvaKey.OemBackslash, AvaKey.OemBackslash,
// NOTE: invalid // NOTE: invalid
AvaKey.None AvaKey.None,
}; };
private static readonly Dictionary<AvaKey, Key> _avaKeyMapping; private static readonly Dictionary<AvaKey, Key> _avaKeyMapping;

View file

@ -31,13 +31,13 @@ namespace Ryujinx.Modules
{ {
internal static class Updater internal static class Updater
{ {
private const string GitHubApiURL = "https://api.github.com"; private const string GitHubApiUrl = "https://api.github.com";
private static readonly GithubReleasesJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
private static readonly string HomeDir = AppDomain.CurrentDomain.BaseDirectory; private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory;
private static readonly string UpdateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update");
private static readonly string UpdatePublishDir = Path.Combine(UpdateDir, "publish"); private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish");
private static readonly int ConnectionCount = 4; private static readonly int _connectionCount = 4;
private static string _buildVer; private static string _buildVer;
private static string _platformExt; private static string _platformExt;
@ -46,7 +46,7 @@ namespace Ryujinx.Modules
private static bool _updateSuccessful; private static bool _updateSuccessful;
private static bool _running; private static bool _running;
private static readonly string[] WindowsDependencyDirs = Array.Empty<string>(); private static readonly string[] _windowsDependencyDirs = Array.Empty<string>();
public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate) public static async Task BeginParse(Window mainWindow, bool showVersionUpToDate)
{ {
@ -99,9 +99,9 @@ namespace Ryujinx.Modules
{ {
using HttpClient jsonClient = ConstructHttpClient(); using HttpClient jsonClient = ConstructHttpClient();
string buildInfoURL = $"{GitHubApiURL}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest";
string fetchedJson = await jsonClient.GetStringAsync(buildInfoURL); string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl);
var fetched = JsonHelper.Deserialize(fetchedJson, SerializerContext.GithubReleasesJsonResponse); var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse);
_buildVer = fetched.Name; _buildVer = fetched.Name;
foreach (var asset in fetched.Assets) foreach (var asset in fetched.Assets)
@ -195,8 +195,7 @@ namespace Ryujinx.Modules
} }
// Fetch build size information to learn chunk sizes. // Fetch build size information to learn chunk sizes.
using (HttpClient buildSizeClient = ConstructHttpClient()) using HttpClient buildSizeClient = ConstructHttpClient();
{
try try
{ {
buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0"); buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0");
@ -212,7 +211,6 @@ namespace Ryujinx.Modules
_buildSize = -1; _buildSize = -1;
} }
}
Dispatcher.UIThread.Post(async () => Dispatcher.UIThread.Post(async () =>
{ {
@ -248,23 +246,22 @@ namespace Ryujinx.Modules
_updateSuccessful = false; _updateSuccessful = false;
// Empty update dir, although it shouldn't ever have anything inside it // Empty update dir, although it shouldn't ever have anything inside it
if (Directory.Exists(UpdateDir)) if (Directory.Exists(_updateDir))
{ {
Directory.Delete(UpdateDir, true); Directory.Delete(_updateDir, true);
} }
Directory.CreateDirectory(UpdateDir); Directory.CreateDirectory(_updateDir);
string updateFile = Path.Combine(UpdateDir, "update.bin"); string updateFile = Path.Combine(_updateDir, "update.bin");
TaskDialog taskDialog = new() TaskDialog taskDialog = new()
{ {
Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater], Header = LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading], SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterDownloading],
IconSource = new SymbolIconSource { Symbol = Symbol.Download }, IconSource = new SymbolIconSource { Symbol = Symbol.Download },
Buttons = { },
ShowProgressBar = true, ShowProgressBar = true,
XamlRoot = parent XamlRoot = parent,
}; };
taskDialog.Opened += (s, e) => taskDialog.Opened += (s, e) =>
@ -301,7 +298,7 @@ namespace Ryujinx.Modules
if (OperatingSystem.IsMacOS()) if (OperatingSystem.IsMacOS())
{ {
string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", "..")); string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", ".."));
string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app"); string newBundlePath = Path.Combine(_updateDir, "Ryujinx.app");
string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh"); string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh");
string currentPid = Environment.ProcessId.ToString(); string currentPid = Environment.ProcessId.ToString();
@ -328,7 +325,7 @@ namespace Ryujinx.Modules
ProcessStartInfo processStart = new(ryuName) ProcessStartInfo processStart = new(ryuName)
{ {
UseShellExecute = true, UseShellExecute = true,
WorkingDirectory = executableDirectory WorkingDirectory = executableDirectory,
}; };
foreach (string argument in CommandLineState.Arguments) foreach (string argument in CommandLineState.Arguments)
@ -347,22 +344,22 @@ namespace Ryujinx.Modules
private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile) private static void DoUpdateWithMultipleThreads(TaskDialog taskDialog, string downloadUrl, string updateFile)
{ {
// Multi-Threaded Updater // Multi-Threaded Updater
long chunkSize = _buildSize / ConnectionCount; long chunkSize = _buildSize / _connectionCount;
long remainderChunk = _buildSize % ConnectionCount; long remainderChunk = _buildSize % _connectionCount;
int completedRequests = 0; int completedRequests = 0;
int totalProgressPercentage = 0; int totalProgressPercentage = 0;
int[] progressPercentage = new int[ConnectionCount]; int[] progressPercentage = new int[_connectionCount];
List<byte[]> list = new(ConnectionCount); List<byte[]> list = new(_connectionCount);
List<WebClient> webClients = new(ConnectionCount); List<WebClient> webClients = new(_connectionCount);
for (int i = 0; i < ConnectionCount; i++) for (int i = 0; i < _connectionCount; i++)
{ {
list.Add(Array.Empty<byte>()); list.Add(Array.Empty<byte>());
} }
for (int i = 0; i < ConnectionCount; i++) for (int i = 0; i < _connectionCount; i++)
{ {
#pragma warning disable SYSLIB0014 #pragma warning disable SYSLIB0014
// TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient.
@ -371,7 +368,7 @@ namespace Ryujinx.Modules
webClients.Add(client); webClients.Add(client);
if (i == ConnectionCount - 1) if (i == _connectionCount - 1)
{ {
client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}");
} }
@ -388,7 +385,7 @@ namespace Ryujinx.Modules
Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage);
Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage);
taskDialog.SetProgressBarState(totalProgressPercentage / ConnectionCount, TaskDialogProgressState.Normal); taskDialog.SetProgressBarState(totalProgressPercentage / _connectionCount, TaskDialogProgressState.Normal);
}; };
client.DownloadDataCompleted += (_, args) => client.DownloadDataCompleted += (_, args) =>
@ -407,10 +404,10 @@ namespace Ryujinx.Modules
list[index] = args.Result; list[index] = args.Result;
Interlocked.Increment(ref completedRequests); Interlocked.Increment(ref completedRequests);
if (Equals(completedRequests, ConnectionCount)) if (Equals(completedRequests, _connectionCount))
{ {
byte[] mergedFileBytes = new byte[_buildSize]; byte[] mergedFileBytes = new byte[_buildSize];
for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < _connectionCount; connectionIndex++)
{ {
Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length);
destinationOffset += list[connectionIndex].Length; destinationOffset += list[connectionIndex].Length;
@ -421,11 +418,10 @@ namespace Ryujinx.Modules
// On macOS, ensure that we remove the quarantine bit to prevent Gatekeeper from blocking execution. // On macOS, ensure that we remove the quarantine bit to prevent Gatekeeper from blocking execution.
if (OperatingSystem.IsMacOS()) if (OperatingSystem.IsMacOS())
{ {
using (Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile })) using Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile });
{
xattrProcess.WaitForExit(); xattrProcess.WaitForExit();
} }
}
try try
{ {
@ -437,8 +433,6 @@ namespace Ryujinx.Modules
Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater.");
DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile); DoUpdateWithSingleThread(taskDialog, downloadUrl, updateFile);
return;
} }
} }
}; };
@ -470,9 +464,8 @@ namespace Ryujinx.Modules
// We do not want to timeout while downloading // We do not want to timeout while downloading
client.Timeout = TimeSpan.FromDays(1); client.Timeout = TimeSpan.FromDays(1);
using (HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result) using HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result;
using (Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result) using Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result;
{
using Stream updateFileStream = File.Open(updateFile, FileMode.Create); using Stream updateFileStream = File.Open(updateFile, FileMode.Create);
long totalBytes = response.Content.Headers.ContentLength.Value; long totalBytes = response.Content.Headers.ContentLength.Value;
@ -495,7 +488,6 @@ namespace Ryujinx.Modules
updateFileStream.Write(buffer, 0, readSize); updateFileStream.Write(buffer, 0, readSize);
} }
}
InstallUpdate(taskDialog, updateFile); InstallUpdate(taskDialog, updateFile);
} }
@ -510,7 +502,7 @@ namespace Ryujinx.Modules
{ {
Thread worker = new(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile)) Thread worker = new(() => DoUpdateWithSingleThreadWorker(taskDialog, downloadUrl, updateFile))
{ {
Name = "Updater.SingleThreadWorker" Name = "Updater.SingleThreadWorker",
}; };
worker.Start(); worker.Start();
@ -537,10 +529,8 @@ namespace Ryujinx.Modules
Directory.CreateDirectory(Path.GetDirectoryName(outPath)); Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (FileStream outStream = File.OpenWrite(outPath)) using FileStream outStream = File.OpenWrite(outPath);
{
tarStream.CopyEntryContents(outStream); tarStream.CopyEntryContents(outStream);
}
File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode);
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc));
@ -566,17 +556,19 @@ namespace Ryujinx.Modules
foreach (ZipEntry zipEntry in zipFile) foreach (ZipEntry zipEntry in zipFile)
{ {
count++; count++;
if (zipEntry.IsDirectory) continue; if (zipEntry.IsDirectory)
{
continue;
}
string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name); string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name);
Directory.CreateDirectory(Path.GetDirectoryName(outPath)); Directory.CreateDirectory(Path.GetDirectoryName(outPath));
using (Stream zipStream = zipFile.GetInputStream(zipEntry)) using Stream zipStream = zipFile.GetInputStream(zipEntry);
using (FileStream outStream = File.OpenWrite(outPath)) using FileStream outStream = File.OpenWrite(outPath);
{
zipStream.CopyTo(outStream); zipStream.CopyTo(outStream);
}
File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc));
@ -597,11 +589,11 @@ namespace Ryujinx.Modules
{ {
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
{ {
ExtractTarGzipFile(taskDialog, updateFile, UpdateDir); ExtractTarGzipFile(taskDialog, updateFile, _updateDir);
} }
else if (OperatingSystem.IsWindows()) else if (OperatingSystem.IsWindows())
{ {
ExtractZipFile(taskDialog, updateFile, UpdateDir); ExtractZipFile(taskDialog, updateFile, _updateDir);
} }
else else
{ {
@ -648,10 +640,10 @@ namespace Ryujinx.Modules
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal); taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
}); });
MoveAllFilesOver(UpdatePublishDir, HomeDir, taskDialog); MoveAllFilesOver(_updatePublishDir, _homeDir, taskDialog);
}); });
Directory.Delete(UpdateDir, true); Directory.Delete(_updateDir, true);
} }
_updateSuccessful = true; _updateSuccessful = true;
@ -738,15 +730,15 @@ namespace Ryujinx.Modules
// NOTE: This method should always reflect the latest build layout. // NOTE: This method should always reflect the latest build layout.
private static IEnumerable<string> EnumerateFilesToDelete() private static IEnumerable<string> EnumerateFilesToDelete()
{ {
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir. var files = Directory.EnumerateFiles(_homeDir); // All files directly in base dir.
// Determine and exclude user files only when the updater is running, not when cleaning old files // Determine and exclude user files only when the updater is running, not when cleaning old files
if (_running && !OperatingSystem.IsMacOS()) if (_running && !OperatingSystem.IsMacOS())
{ {
// Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list. // Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list.
var oldFiles = Directory.EnumerateFiles(HomeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); var oldFiles = Directory.EnumerateFiles(_homeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var newFiles = Directory.EnumerateFiles(UpdatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); var newFiles = Directory.EnumerateFiles(_updatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(HomeDir, filename)); var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(_homeDir, filename));
// Remove user files from the paths in files. // Remove user files from the paths in files.
files = files.Except(userFiles); files = files.Except(userFiles);
@ -754,9 +746,9 @@ namespace Ryujinx.Modules
if (OperatingSystem.IsWindows()) if (OperatingSystem.IsWindows())
{ {
foreach (string dir in WindowsDependencyDirs) foreach (string dir in _windowsDependencyDirs)
{ {
string dirPath = Path.Combine(HomeDir, dir); string dirPath = Path.Combine(_homeDir, dir);
if (Directory.Exists(dirPath)) if (Directory.Exists(dirPath))
{ {
files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories)); files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories));
@ -798,7 +790,7 @@ namespace Ryujinx.Modules
public static void CleanupUpdate() public static void CleanupUpdate()
{ {
foreach (string file in Directory.GetFiles(HomeDir, "*.ryuold", SearchOption.AllDirectories)) foreach (string file in Directory.GetFiles(_homeDir, "*.ryuold", SearchOption.AllDirectories))
{ {
File.Delete(file); File.Delete(file);
} }

View file

@ -31,7 +31,7 @@ namespace Ryujinx.Ava
[LibraryImport("user32.dll", SetLastError = true)] [LibraryImport("user32.dll", SetLastError = true)]
public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
private const uint MB_ICONWARNING = 0x30; private const uint MbIconwarning = 0x30;
public static void Main(string[] args) public static void Main(string[] args)
{ {
@ -39,7 +39,7 @@ namespace Ryujinx.Ava
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
{ {
_ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING); _ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MbIconwarning);
} }
PreviewerDetached = true; PreviewerDetached = true;
@ -60,7 +60,7 @@ namespace Ryujinx.Ava
EnableMultiTouch = true, EnableMultiTouch = true,
EnableIme = true, EnableIme = true,
UseEGL = false, UseEGL = false,
UseGpu = true UseGpu = true,
}) })
.With(new Win32PlatformOptions .With(new Win32PlatformOptions
{ {
@ -192,7 +192,7 @@ namespace Ryujinx.Ava
"never" => HideCursorMode.Never, "never" => HideCursorMode.Never,
"onidle" => HideCursorMode.OnIdle, "onidle" => HideCursorMode.OnIdle,
"always" => HideCursorMode.Always, "always" => HideCursorMode.Always,
_ => ConfigurationState.Instance.HideCursor.Value _ => ConfigurationState.Instance.HideCursor.Value,
}; };
} }
} }

View file

@ -64,7 +64,7 @@ namespace Ryujinx.Ava.UI.Applet
LocaleManager.Instance[LocaleKeys.SettingsButtonClose], LocaleManager.Instance[LocaleKeys.SettingsButtonClose],
(int)Symbol.Important, (int)Symbol.Important,
deferEvent, deferEvent,
async (window) => async window =>
{ {
if (opened) if (opened)
{ {
@ -112,7 +112,7 @@ namespace Ryujinx.Ava.UI.Applet
{ {
try try
{ {
var response = await SwkbdAppletDialog.ShowInputDialog(_parent, LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args); var response = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
if (response.Result == UserResult.Ok) if (response.Result == UserResult.Ok)
{ {
@ -142,10 +142,7 @@ namespace Ryujinx.Ava.UI.Applet
public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value) public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value)
{ {
device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value); device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value);
if (_parent.ViewModel.AppHost != null) _parent.ViewModel.AppHost?.Stop();
{
_parent.ViewModel.AppHost.Stop();
}
} }
public bool DisplayErrorAppletDialog(string title, string message, string[] buttons) public bool DisplayErrorAppletDialog(string title, string message, string[] buttons)
@ -162,7 +159,7 @@ namespace Ryujinx.Ava.UI.Applet
{ {
Title = title, Title = title,
WindowStartupLocation = WindowStartupLocation.CenterScreen, WindowStartupLocation = WindowStartupLocation.CenterScreen,
Width = 400 Width = 400,
}; };
object response = await msgDialog.Run(); object response = await msgDialog.Run();

View file

@ -3,13 +3,11 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Input; using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.Ui; using Ryujinx.HLE.Ui;
using System; using System;
using System.Threading; using System.Threading;
using HidKey = Ryujinx.Common.Configuration.Hid.Key; using HidKey = Ryujinx.Common.Configuration.Hid.Key;
namespace Ryujinx.Ava.UI.Applet namespace Ryujinx.Ava.UI.Applet
@ -17,7 +15,7 @@ namespace Ryujinx.Ava.UI.Applet
class AvaloniaDynamicTextInputHandler : IDynamicTextInputHandler class AvaloniaDynamicTextInputHandler : IDynamicTextInputHandler
{ {
private MainWindow _parent; private MainWindow _parent;
private OffscreenTextBox _hiddenTextBox; private readonly OffscreenTextBox _hiddenTextBox;
private bool _canProcessInput; private bool _canProcessInput;
private IDisposable _textChangedSubscription; private IDisposable _textChangedSubscription;
private IDisposable _selectionStartChangedSubscription; private IDisposable _selectionStartChangedSubscription;
@ -76,7 +74,7 @@ namespace Ryujinx.Ava.UI.Applet
return; return;
} }
e.RoutedEvent = _hiddenTextBox.GetKeyUpRoutedEvent(); e.RoutedEvent = OffscreenTextBox.GetKeyUpRoutedEvent();
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
@ -96,7 +94,7 @@ namespace Ryujinx.Ava.UI.Applet
return; return;
} }
e.RoutedEvent = _hiddenTextBox.GetKeyUpRoutedEvent(); e.RoutedEvent = OffscreenTextBox.GetKeyUpRoutedEvent();
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {

View file

@ -9,7 +9,7 @@ namespace Ryujinx.Ava.UI.Applet
{ {
public AvaloniaHostUiTheme(MainWindow parent) public AvaloniaHostUiTheme(MainWindow parent)
{ {
FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000, 0) ? "Segoe UI Variable" : parent.FontFamily.Name; FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000) ? "Segoe UI Variable" : parent.FontFamily.Name;
DefaultBackgroundColor = BrushToThemeColor(parent.Background); DefaultBackgroundColor = BrushToThemeColor(parent.Background);
DefaultForegroundColor = BrushToThemeColor(parent.Foreground); DefaultForegroundColor = BrushToThemeColor(parent.Foreground);
DefaultBorderColor = BrushToThemeColor(parent.BorderBrush); DefaultBorderColor = BrushToThemeColor(parent.BorderBrush);
@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Applet
public ThemeColor SelectionBackgroundColor { get; } public ThemeColor SelectionBackgroundColor { get; }
public ThemeColor SelectionForegroundColor { get; } public ThemeColor SelectionForegroundColor { get; }
private ThemeColor BrushToThemeColor(IBrush brush) private static ThemeColor BrushToThemeColor(IBrush brush)
{ {
if (brush is SolidColorBrush solidColor) if (brush is SolidColorBrush solidColor)
{ {
@ -34,10 +34,8 @@ namespace Ryujinx.Ava.UI.Applet
(float)solidColor.Color.G / 255, (float)solidColor.Color.G / 255,
(float)solidColor.Color.B / 255); (float)solidColor.Color.B / 255);
} }
else
{
return new ThemeColor(); return new ThemeColor();
} }
} }
}
} }

View file

@ -1,10 +1,12 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using System.Threading.Tasks; using System.Threading.Tasks;
#if DEBUG
using Avalonia;
#endif
namespace Ryujinx.Ava.UI.Applet namespace Ryujinx.Ava.UI.Applet
{ {

View file

@ -1,13 +1,10 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Media; using Avalonia.Media;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
using System; using System;
@ -22,7 +19,7 @@ namespace Ryujinx.Ava.UI.Controls
private Predicate<string> _checkInput = _ => true; private Predicate<string> _checkInput = _ => true;
private int _inputMax; private int _inputMax;
private int _inputMin; private int _inputMin;
private string _placeholder; private readonly string _placeholder;
private ContentDialog _host; private ContentDialog _host;
@ -57,13 +54,13 @@ namespace Ryujinx.Ava.UI.Controls
public string MainText { get; set; } = ""; public string MainText { get; set; } = "";
public string SecondaryText { get; set; } = ""; public string SecondaryText { get; set; } = "";
public static async Task<(UserResult Result, string Input)> ShowInputDialog(StyleableWindow window, string title, SoftwareKeyboardUiArgs args) public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUiArgs args)
{ {
ContentDialog contentDialog = new ContentDialog(); ContentDialog contentDialog = new();
UserResult result = UserResult.Cancel; UserResult result = UserResult.Cancel;
SwkbdAppletDialog content = new SwkbdAppletDialog(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText); SwkbdAppletDialog content = new(args.HeaderText, args.SubtitleText, args.GuideText, args.InitialText);
string input = string.Empty; string input = string.Empty;
@ -78,15 +75,16 @@ namespace Ryujinx.Ava.UI.Controls
contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel]; contentDialog.CloseButtonText = LocaleManager.Instance[LocaleKeys.InputDialogCancel];
contentDialog.Content = content; contentDialog.Content = content;
TypedEventHandler<ContentDialog, ContentDialogClosedEventArgs> handler = (sender, eventArgs) => void Handler(ContentDialog sender, ContentDialogClosedEventArgs eventArgs)
{ {
if (eventArgs.Result == ContentDialogResult.Primary) if (eventArgs.Result == ContentDialogResult.Primary)
{ {
result = UserResult.Ok; result = UserResult.Ok;
input = content.Input.Text; input = content.Input.Text;
} }
}; }
contentDialog.Closed += handler;
contentDialog.Closed += Handler;
await ContentDialogHelper.ShowAsync(contentDialog); await ContentDialogHelper.ShowAsync(contentDialog);

View file

@ -18,7 +18,6 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using Path = System.IO.Path; using Path = System.IO.Path;
using UserId = LibHac.Fs.UserId;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
{ {
@ -53,7 +52,7 @@ namespace Ryujinx.Ava.UI.Controls
public void OpenUserSaveDirectory_Click(object sender, RoutedEventArgs args) public void OpenUserSaveDirectory_Click(object sender, RoutedEventArgs args)
{ {
if ((sender as MenuItem)?.DataContext is MainWindowViewModel viewModel) if (sender is MenuItem { DataContext: MainWindowViewModel viewModel })
{ {
OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low)); OpenSaveDirectory(viewModel, SaveDataType.Account, userId: new UserId((ulong)viewModel.AccountManager.LastOpenedUser.UserId.High, (ulong)viewModel.AccountManager.LastOpenedUser.UserId.Low));
} }

View file

@ -2,7 +2,6 @@ using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Styling; using Avalonia.Styling;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.Core;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using LibHac; using LibHac;
using LibHac.Common; using LibHac.Common;
@ -10,7 +9,6 @@ using LibHac.Fs;
using LibHac.Fs.Shim; using LibHac.Fs.Shim;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Views.User; using Ryujinx.Ava.UI.Views.User;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
@ -19,6 +17,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls
@ -56,7 +55,7 @@ namespace Ryujinx.Ava.UI.Controls
InitializeComponent(); InitializeComponent();
} }
public void GoBack(object parameter = null) public void GoBack()
{ {
if (ContentFrame.BackStack.Count > 0) if (ContentFrame.BackStack.Count > 0)
{ {
@ -75,14 +74,14 @@ namespace Ryujinx.Ava.UI.Controls
VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient) VirtualFileSystem ownerVirtualFileSystem, HorizonClient ownerHorizonClient)
{ {
var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient); var content = new NavigationDialogHost(ownerAccountManager, ownerContentManager, ownerVirtualFileSystem, ownerHorizonClient);
ContentDialog contentDialog = new ContentDialog ContentDialog contentDialog = new()
{ {
Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle], Title = LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle],
PrimaryButtonText = "", PrimaryButtonText = "",
SecondaryButtonText = "", SecondaryButtonText = "",
CloseButtonText = "", CloseButtonText = "",
Content = content, Content = content,
Padding = new Thickness(0) Padding = new Thickness(0),
}; };
contentDialog.Closed += (sender, args) => contentDialog.Closed += (sender, args) =>
@ -125,7 +124,7 @@ namespace Ryujinx.Ava.UI.Controls
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10]; Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];
HashSet<HLE.HOS.Services.Account.Acc.UserId> lostAccounts = new(); HashSet<UserId> lostAccounts = new();
while (true) while (true)
{ {
@ -139,15 +138,15 @@ namespace Ryujinx.Ava.UI.Controls
for (int i = 0; i < readCount; i++) for (int i = 0; i < readCount; i++)
{ {
var save = saveDataInfo[i]; var save = saveDataInfo[i];
var id = new HLE.HOS.Services.Account.Acc.UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High); var id = new UserId((long)save.UserId.Id.Low, (long)save.UserId.Id.High);
if (ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault( x=> x.UserId == id) == null) if (ViewModel.Profiles.Cast<UserProfile>().FirstOrDefault(x => x.UserId == id) == null)
{ {
lostAccounts.Add(id); lostAccounts.Add(id);
} }
} }
} }
foreach(var account in lostAccounts) foreach (var account in lostAccounts)
{ {
ViewModel.LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, "", null), this)); ViewModel.LostProfiles.Add(new UserProfile(new HLE.HOS.Services.Account.Acc.UserProfile(account, "", null), this));
} }
@ -166,7 +165,7 @@ namespace Ryujinx.Ava.UI.Controls
if (profile == null) if (profile == null)
{ {
async void Action() static async void Action()
{ {
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUserProfileDeletionWarningMessage]);
} }

View file

@ -1,6 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Layout;
using Avalonia.Media; using Avalonia.Media;
using Avalonia.Threading; using Avalonia.Threading;
using FluentAvalonia.Core; using FluentAvalonia.Core;
@ -36,13 +37,12 @@ namespace Ryujinx.Ava.UI.Helpers
PrimaryButtonText = primaryButton, PrimaryButtonText = primaryButton,
SecondaryButtonText = secondaryButton, SecondaryButtonText = secondaryButton,
CloseButtonText = closeButton, CloseButtonText = closeButton,
Content = content Content = content,
}; PrimaryButtonCommand = MiniCommand.Create(() =>
contentDialog.PrimaryButtonCommand = MiniCommand.Create(() =>
{ {
result = primaryButtonResult; result = primaryButtonResult;
}); }),
};
contentDialog.SecondaryButtonCommand = MiniCommand.Create(() => contentDialog.SecondaryButtonCommand = MiniCommand.Create(() =>
{ {
@ -96,7 +96,6 @@ namespace Ryujinx.Ava.UI.Helpers
Func<Window, Task> doWhileDeferred = null) Func<Window, Task> doWhileDeferred = null)
{ {
bool startedDeferring = false; bool startedDeferring = false;
UserResult result = UserResult.None;
return await ShowTextDialog( return await ShowTextDialog(
title, title,
@ -123,8 +122,6 @@ namespace Ryujinx.Ava.UI.Helpers
var deferral = args.GetDeferral(); var deferral = args.GetDeferral();
result = primaryButton == LocaleManager.Instance[LocaleKeys.InputDialogYes] ? UserResult.Yes : UserResult.Ok;
sender.PrimaryButtonClick -= DeferClose; sender.PrimaryButtonClick -= DeferClose;
_ = Task.Run(() => _ = Task.Run(() =>
@ -150,10 +147,10 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
Grid content = new() Grid content = new()
{ {
RowDefinitions = new RowDefinitions() { new RowDefinition(), new RowDefinition() }, RowDefinitions = new RowDefinitions { new(), new() },
ColumnDefinitions = new ColumnDefinitions() { new ColumnDefinition(GridLength.Auto), new ColumnDefinition() }, ColumnDefinitions = new ColumnDefinitions { new(GridLength.Auto), new() },
MinHeight = 80 MinHeight = 80,
}; };
SymbolIcon icon = new() SymbolIcon icon = new()
@ -161,7 +158,7 @@ namespace Ryujinx.Ava.UI.Helpers
Symbol = (Symbol)symbol, Symbol = (Symbol)symbol,
Margin = new Thickness(10), Margin = new Thickness(10),
FontSize = 40, FontSize = 40,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center VerticalAlignment = VerticalAlignment.Center,
}; };
Grid.SetColumn(icon, 0); Grid.SetColumn(icon, 0);
@ -173,7 +170,7 @@ namespace Ryujinx.Ava.UI.Helpers
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 secondaryLabel = new()
@ -181,7 +178,7 @@ namespace Ryujinx.Ava.UI.Helpers
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);
@ -318,14 +315,14 @@ namespace Ryujinx.Ava.UI.Helpers
Window parent = GetMainWindow(); Window parent = GetMainWindow();
if (parent != null && parent.IsActive && (parent as MainWindow).ViewModel.IsGameRunning) if (parent is { IsActive: true } and MainWindow window && window.ViewModel.IsGameRunning)
{ {
contentDialogOverlayWindow = new() contentDialogOverlayWindow = new()
{ {
Height = parent.Bounds.Height, Height = parent.Bounds.Height,
Width = parent.Bounds.Width, Width = parent.Bounds.Width,
Position = parent.PointToScreen(new Point()), Position = parent.PointToScreen(new Point()),
ShowInTaskbar = false ShowInTaskbar = false,
}; };
parent.PositionChanged += OverlayOnPositionChanged; parent.PositionChanged += OverlayOnPositionChanged;
@ -389,7 +386,7 @@ namespace Ryujinx.Ava.UI.Helpers
private static Window GetMainWindow() private static Window GetMainWindow()
{ {
if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al) if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime al)
{ {
foreach (Window item in al.Windows) foreach (Window item in al.Windows)
{ {

View file

@ -1,5 +1,6 @@
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.MarkupExtensions;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -8,13 +9,13 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
public class GlyphValueConverter : MarkupExtension public class GlyphValueConverter : MarkupExtension
{ {
private string _key; private readonly string _key;
private static Dictionary<Glyph, string> _glyphs = new Dictionary<Glyph, string> private static readonly Dictionary<Glyph, string> _glyphs = new()
{ {
{ Glyph.List, char.ConvertFromUtf32((int)Symbol.List).ToString() }, { Glyph.List, char.ConvertFromUtf32((int)Symbol.List) },
{ Glyph.Grid, char.ConvertFromUtf32((int)Symbol.ViewAll).ToString() }, { Glyph.Grid, char.ConvertFromUtf32((int)Symbol.ViewAll) },
{ Glyph.Chip, char.ConvertFromUtf32(59748).ToString() } { Glyph.Chip, char.ConvertFromUtf32(59748) },
}; };
public GlyphValueConverter(string key) public GlyphValueConverter(string key)
@ -37,10 +38,10 @@ namespace Ryujinx.Ava.UI.Helpers
public override object ProvideValue(IServiceProvider serviceProvider) public override object ProvideValue(IServiceProvider serviceProvider)
{ {
Avalonia.Markup.Xaml.MarkupExtensions.ReflectionBindingExtension binding = new($"[{_key}]") ReflectionBindingExtension binding = new($"[{_key}]")
{ {
Mode = BindingMode.OneWay, Mode = BindingMode.OneWay,
Source = this Source = this,
}; };
return binding.ProvideValue(serviceProvider); return binding.ProvideValue(serviceProvider);

View file

@ -1,15 +1,17 @@
using Avalonia.Logging;
using Avalonia.Utilities; using Avalonia.Utilities;
using Ryujinx.Common.Logging;
using System; using System;
using System.Text; using System.Text;
namespace Ryujinx.Ava.UI.Helpers namespace Ryujinx.Ava.UI.Helpers
{ {
using AvaLogger = Avalonia.Logging.Logger; using AvaLogger = Avalonia.Logging.Logger;
using AvaLogLevel = Avalonia.Logging.LogEventLevel; using AvaLogLevel = LogEventLevel;
using RyuLogClass = Ryujinx.Common.Logging.LogClass; using RyuLogClass = LogClass;
using RyuLogger = Ryujinx.Common.Logging.Logger; using RyuLogger = Ryujinx.Common.Logging.Logger;
internal class LoggerAdapter : Avalonia.Logging.ILogSink internal class LoggerAdapter : ILogSink
{ {
public static void Register() public static void Register()
{ {
@ -26,7 +28,7 @@ namespace Ryujinx.Ava.UI.Helpers
AvaLogLevel.Warning => RyuLogger.Debug, AvaLogLevel.Warning => RyuLogger.Debug,
AvaLogLevel.Error => RyuLogger.Error, AvaLogLevel.Error => RyuLogger.Error,
AvaLogLevel.Fatal => RyuLogger.Error, AvaLogLevel.Fatal => RyuLogger.Error,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null) _ => throw new ArgumentOutOfRangeException(nameof(level), level, null),
}; };
} }

View file

@ -8,7 +8,7 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
private readonly Action<T> _callback; private readonly Action<T> _callback;
private bool _busy; private bool _busy;
private Func<T, Task> _asyncCallback; private readonly Func<T, Task> _asyncCallback;
public MiniCommand(Action<T> callback) public MiniCommand(Action<T> callback)
{ {

View file

@ -25,7 +25,7 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
Position = NotificationPosition.BottomRight, Position = NotificationPosition.BottomRight,
MaxItems = MaxNotifications, MaxItems = MaxNotifications,
Margin = new Thickness(0, 0, 15, 40) Margin = new Thickness(0, 0, 15, 40),
}; };
var maybeAsyncWorkQueue = new Lazy<AsyncWorkQueue<Notification>>( var maybeAsyncWorkQueue = new Lazy<AsyncWorkQueue<Notification>>(

View file

@ -6,12 +6,12 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
public class OffscreenTextBox : TextBox public class OffscreenTextBox : TextBox
{ {
public RoutedEvent<KeyEventArgs> GetKeyDownRoutedEvent() public static RoutedEvent<KeyEventArgs> GetKeyDownRoutedEvent()
{ {
return KeyDownEvent; return KeyDownEvent;
} }
public RoutedEvent<KeyEventArgs> GetKeyUpRoutedEvent() public static RoutedEvent<KeyEventArgs> GetKeyUpRoutedEvent()
{ {
return KeyUpEvent; return KeyUpEvent;
} }
@ -28,12 +28,12 @@ namespace Ryujinx.Ava.UI.Helpers
public void SendText(string text) public void SendText(string text)
{ {
OnTextInput(new TextInputEventArgs() OnTextInput(new TextInputEventArgs
{ {
Text = text, Text = text,
Device = KeyboardDevice.Instance, Device = KeyboardDevice.Instance,
Source = this, Source = this,
RoutedEvent = TextInputEvent RoutedEvent = TextInputEvent,
}); });
} }
} }

View file

@ -1,5 +1,4 @@
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ui.Common; using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -24,7 +23,7 @@ namespace Ryujinx.Ava.UI.Helpers
UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailed], UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailed],
UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFound], UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFound],
UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknown], UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknown],
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefined] _ => LocaleManager.Instance[LocaleKeys.UserErrorUndefined],
}; };
} }
@ -37,7 +36,7 @@ namespace Ryujinx.Ava.UI.Helpers
UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailedDescription], UserError.FirmwareParsingFailed => LocaleManager.Instance[LocaleKeys.UserErrorFirmwareParsingFailedDescription],
UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFoundDescription], UserError.ApplicationNotFound => LocaleManager.Instance[LocaleKeys.UserErrorApplicationNotFoundDescription],
UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknownDescription], UserError.Unknown => LocaleManager.Instance[LocaleKeys.UserErrorUnknownDescription],
_ => LocaleManager.Instance[LocaleKeys.UserErrorUndefinedDescription] _ => LocaleManager.Instance[LocaleKeys.UserErrorUndefinedDescription],
}; };
} }
@ -48,7 +47,7 @@ namespace Ryujinx.Ava.UI.Helpers
UserError.NoKeys or UserError.NoKeys or
UserError.NoFirmware or UserError.NoFirmware or
UserError.FirmwareParsingFailed => true, UserError.FirmwareParsingFailed => true,
_ => false _ => false,
}; };
} }
@ -63,11 +62,11 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys", UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys",
UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware", UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware",
_ => SetupGuideUrl _ => SetupGuideUrl,
}; };
} }
public static async Task ShowUserErrorDialog(UserError error, StyleableWindow owner) public static async Task ShowUserErrorDialog(UserError error)
{ {
string errorCode = GetErrorCode(error); string errorCode = GetErrorCode(error);

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning; using System.Runtime.Versioning;
@ -10,46 +11,47 @@ namespace Ryujinx.Ava.UI.Helpers
[Flags] [Flags]
public enum ClassStyles : uint public enum ClassStyles : uint
{ {
CS_CLASSDC = 0x40, CsClassdc = 0x40,
CS_OWNDC = 0x20, CsOwndc = 0x20,
} }
[Flags] [Flags]
public enum WindowStyles : uint public enum WindowStyles : uint
{ {
WS_CHILD = 0x40000000 WsChild = 0x40000000,
} }
public enum Cursors : uint public enum Cursors : uint
{ {
IDC_ARROW = 32512 IdcArrow = 32512,
} }
[SuppressMessage("Design", "CA1069: Enums values should not be duplicated")]
public enum WindowsMessages : uint public enum WindowsMessages : uint
{ {
MOUSEMOVE = 0x0200, Mousemove = 0x0200,
LBUTTONDOWN = 0x0201, Lbuttondown = 0x0201,
LBUTTONUP = 0x0202, Lbuttonup = 0x0202,
LBUTTONDBLCLK = 0x0203, Lbuttondblclk = 0x0203,
RBUTTONDOWN = 0x0204, Rbuttondown = 0x0204,
RBUTTONUP = 0x0205, Rbuttonup = 0x0205,
RBUTTONDBLCLK = 0x0206, Rbuttondblclk = 0x0206,
MBUTTONDOWN = 0x0207, Mbuttondown = 0x0207,
MBUTTONUP = 0x0208, Mbuttonup = 0x0208,
MBUTTONDBLCLK = 0x0209, Mbuttondblclk = 0x0209,
MOUSEWHEEL = 0x020A, Mousewheel = 0x020A,
XBUTTONDOWN = 0x020B, Xbuttondown = 0x020B,
XBUTTONUP = 0x020C, Xbuttonup = 0x020C,
XBUTTONDBLCLK = 0x020D, Xbuttondblclk = 0x020D,
MOUSEHWHEEL = 0x020E, Mousehwheel = 0x020E,
MOUSELAST = 0x020E Mouselast = 0x020E,
} }
[UnmanagedFunctionPointer(CallingConvention.Winapi)] [UnmanagedFunctionPointer(CallingConvention.Winapi)]
internal delegate IntPtr WindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam); internal delegate IntPtr WindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct WNDCLASSEX public struct WndClassEx
{ {
public int cbSize; public int cbSize;
public ClassStyles style; public ClassStyles style;
@ -64,9 +66,9 @@ namespace Ryujinx.Ava.UI.Helpers
public IntPtr lpszClassName; public IntPtr lpszClassName;
public IntPtr hIconSm; public IntPtr hIconSm;
public WNDCLASSEX() public WndClassEx()
{ {
cbSize = Marshal.SizeOf<WNDCLASSEX>(); cbSize = Marshal.SizeOf<WndClassEx>();
} }
} }
@ -77,17 +79,17 @@ namespace Ryujinx.Ava.UI.Helpers
public static IntPtr CreateArrowCursor() public static IntPtr CreateArrowCursor()
{ {
return LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW); return LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IdcArrow);
} }
[LibraryImport("user32.dll")] [LibraryImport("user32.dll")]
public static partial IntPtr SetCursor(IntPtr handle); public static partial IntPtr SetCursor(IntPtr handle);
[LibraryImport("user32.dll")] [LibraryImport("user32.dll")]
public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvANDPlane, byte[] pvXORPlane); public static partial IntPtr CreateCursor(IntPtr hInst, int xHotSpot, int yHotSpot, int nWidth, int nHeight, byte[] pvAndPlane, byte[] pvXorPlane);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")] [LibraryImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")]
public static partial ushort RegisterClassEx(ref WNDCLASSEX param); public static partial ushort RegisterClassEx(ref WndClassEx param);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "UnregisterClassW")] [LibraryImport("user32.dll", SetLastError = true, EntryPoint = "UnregisterClassW")]
public static partial short UnregisterClass([MarshalAs(UnmanagedType.LPWStr)] string lpClassName, IntPtr instance); public static partial short UnregisterClass([MarshalAs(UnmanagedType.LPWStr)] string lpClassName, IntPtr instance);

View file

@ -35,6 +35,6 @@ namespace Ryujinx.Ava.UI.Models
public string Name { get; } public string Name { get; }
public string CleanName => Name.Substring(1, Name.Length - 8); public string CleanName => Name[1..^7];
} }
} }

View file

@ -4,6 +4,6 @@ namespace Ryujinx.Ava.UI.Models
{ {
None, None,
Keyboard, Keyboard,
Controller Controller,
} }
} }

View file

@ -7,16 +7,16 @@ using System;
namespace Ryujinx.Ava.UI.Models namespace Ryujinx.Ava.UI.Models
{ {
internal class InputConfiguration<Key, Stick> : BaseModel internal class InputConfiguration<TKey, TStick> : BaseModel
{ {
private float _deadzoneRight; private float _deadzoneRight;
private float _triggerThreshold; private float _triggerThreshold;
private float _deadzoneLeft; private float _deadzoneLeft;
private double _gyroDeadzone; private double _gyroDeadzone;
private int _sensitivity; private int _sensitivity;
private bool enableMotion; private bool _enableMotion;
private float weakRumble; private float _weakRumble;
private float strongRumble; private float _strongRumble;
private float _rangeLeft; private float _rangeLeft;
private float _rangeRight; private float _rangeRight;
@ -37,17 +37,17 @@ namespace Ryujinx.Ava.UI.Models
/// </summary> /// </summary>
public PlayerIndex PlayerIndex { get; set; } public PlayerIndex PlayerIndex { get; set; }
public Stick LeftJoystick { get; set; } public TStick LeftJoystick { get; set; }
public bool LeftInvertStickX { get; set; } public bool LeftInvertStickX { get; set; }
public bool LeftInvertStickY { get; set; } public bool LeftInvertStickY { get; set; }
public bool RightRotate90 { get; set; } public bool RightRotate90 { get; set; }
public Key LeftControllerStickButton { get; set; } public TKey LeftControllerStickButton { get; set; }
public Stick RightJoystick { get; set; } public TStick RightJoystick { get; set; }
public bool RightInvertStickX { get; set; } public bool RightInvertStickX { get; set; }
public bool RightInvertStickY { get; set; } public bool RightInvertStickY { get; set; }
public bool LeftRotate90 { get; set; } public bool LeftRotate90 { get; set; }
public Key RightControllerStickButton { get; set; } public TKey RightControllerStickButton { get; set; }
public float DeadzoneLeft public float DeadzoneLeft
{ {
@ -106,38 +106,37 @@ namespace Ryujinx.Ava.UI.Models
public MotionInputBackendType MotionBackend { get; set; } public MotionInputBackendType MotionBackend { get; set; }
public Key ButtonMinus { get; set; } public TKey ButtonMinus { get; set; }
public Key ButtonL { get; set; } public TKey ButtonL { get; set; }
public Key ButtonZl { get; set; } public TKey ButtonZl { get; set; }
public Key LeftButtonSl { get; set; } public TKey LeftButtonSl { get; set; }
public Key LeftButtonSr { get; set; } public TKey LeftButtonSr { get; set; }
public Key DpadUp { get; set; } public TKey DpadUp { get; set; }
public Key DpadDown { get; set; } public TKey DpadDown { get; set; }
public Key DpadLeft { get; set; } public TKey DpadLeft { get; set; }
public Key DpadRight { get; set; } public TKey DpadRight { get; set; }
public Key ButtonPlus { get; set; } public TKey ButtonPlus { get; set; }
public Key ButtonR { get; set; } public TKey ButtonR { get; set; }
public Key ButtonZr { get; set; } public TKey ButtonZr { get; set; }
public Key RightButtonSl { get; set; } public TKey RightButtonSl { get; set; }
public Key RightButtonSr { get; set; } public TKey RightButtonSr { get; set; }
public Key ButtonX { get; set; } public TKey ButtonX { get; set; }
public Key ButtonB { get; set; } public TKey ButtonB { get; set; }
public Key ButtonY { get; set; } public TKey ButtonY { get; set; }
public Key ButtonA { get; set; } public TKey ButtonA { get; set; }
public TKey LeftStickUp { get; set; }
public TKey LeftStickDown { get; set; }
public TKey LeftStickLeft { get; set; }
public TKey LeftStickRight { get; set; }
public TKey LeftKeyboardStickButton { get; set; }
public Key LeftStickUp { get; set; } public TKey RightStickUp { get; set; }
public Key LeftStickDown { get; set; } public TKey RightStickDown { get; set; }
public Key LeftStickLeft { get; set; } public TKey RightStickLeft { get; set; }
public Key LeftStickRight { get; set; } public TKey RightStickRight { get; set; }
public Key LeftKeyboardStickButton { get; set; } public TKey RightKeyboardStickButton { get; set; }
public Key RightStickUp { get; set; }
public Key RightStickDown { get; set; }
public Key RightStickLeft { get; set; }
public Key RightStickRight { get; set; }
public Key RightKeyboardStickButton { get; set; }
public int Sensitivity public int Sensitivity
{ {
@ -163,9 +162,9 @@ namespace Ryujinx.Ava.UI.Models
public bool EnableMotion public bool EnableMotion
{ {
get => enableMotion; set get => _enableMotion; set
{ {
enableMotion = value; _enableMotion = value;
OnPropertyChanged(); OnPropertyChanged();
} }
@ -181,18 +180,18 @@ namespace Ryujinx.Ava.UI.Models
public bool EnableRumble { get; set; } public bool EnableRumble { get; set; }
public float WeakRumble public float WeakRumble
{ {
get => weakRumble; set get => _weakRumble; set
{ {
weakRumble = value; _weakRumble = value;
OnPropertyChanged(); OnPropertyChanged();
} }
} }
public float StrongRumble public float StrongRumble
{ {
get => strongRumble; set get => _strongRumble; set
{ {
strongRumble = value; _strongRumble = value;
OnPropertyChanged(); OnPropertyChanged();
} }
@ -209,71 +208,71 @@ namespace Ryujinx.Ava.UI.Models
if (config is StandardKeyboardInputConfig keyboardConfig) if (config is StandardKeyboardInputConfig keyboardConfig)
{ {
LeftStickUp = (Key)(object)keyboardConfig.LeftJoyconStick.StickUp; LeftStickUp = (TKey)(object)keyboardConfig.LeftJoyconStick.StickUp;
LeftStickDown = (Key)(object)keyboardConfig.LeftJoyconStick.StickDown; LeftStickDown = (TKey)(object)keyboardConfig.LeftJoyconStick.StickDown;
LeftStickLeft = (Key)(object)keyboardConfig.LeftJoyconStick.StickLeft; LeftStickLeft = (TKey)(object)keyboardConfig.LeftJoyconStick.StickLeft;
LeftStickRight = (Key)(object)keyboardConfig.LeftJoyconStick.StickRight; LeftStickRight = (TKey)(object)keyboardConfig.LeftJoyconStick.StickRight;
LeftKeyboardStickButton = (Key)(object)keyboardConfig.LeftJoyconStick.StickButton; LeftKeyboardStickButton = (TKey)(object)keyboardConfig.LeftJoyconStick.StickButton;
RightStickUp = (Key)(object)keyboardConfig.RightJoyconStick.StickUp; RightStickUp = (TKey)(object)keyboardConfig.RightJoyconStick.StickUp;
RightStickDown = (Key)(object)keyboardConfig.RightJoyconStick.StickDown; RightStickDown = (TKey)(object)keyboardConfig.RightJoyconStick.StickDown;
RightStickLeft = (Key)(object)keyboardConfig.RightJoyconStick.StickLeft; RightStickLeft = (TKey)(object)keyboardConfig.RightJoyconStick.StickLeft;
RightStickRight = (Key)(object)keyboardConfig.RightJoyconStick.StickRight; RightStickRight = (TKey)(object)keyboardConfig.RightJoyconStick.StickRight;
RightKeyboardStickButton = (Key)(object)keyboardConfig.RightJoyconStick.StickButton; RightKeyboardStickButton = (TKey)(object)keyboardConfig.RightJoyconStick.StickButton;
ButtonA = (Key)(object)keyboardConfig.RightJoycon.ButtonA; ButtonA = (TKey)(object)keyboardConfig.RightJoycon.ButtonA;
ButtonB = (Key)(object)keyboardConfig.RightJoycon.ButtonB; ButtonB = (TKey)(object)keyboardConfig.RightJoycon.ButtonB;
ButtonX = (Key)(object)keyboardConfig.RightJoycon.ButtonX; ButtonX = (TKey)(object)keyboardConfig.RightJoycon.ButtonX;
ButtonY = (Key)(object)keyboardConfig.RightJoycon.ButtonY; ButtonY = (TKey)(object)keyboardConfig.RightJoycon.ButtonY;
ButtonR = (Key)(object)keyboardConfig.RightJoycon.ButtonR; ButtonR = (TKey)(object)keyboardConfig.RightJoycon.ButtonR;
RightButtonSl = (Key)(object)keyboardConfig.RightJoycon.ButtonSl; RightButtonSl = (TKey)(object)keyboardConfig.RightJoycon.ButtonSl;
RightButtonSr = (Key)(object)keyboardConfig.RightJoycon.ButtonSr; RightButtonSr = (TKey)(object)keyboardConfig.RightJoycon.ButtonSr;
ButtonZr = (Key)(object)keyboardConfig.RightJoycon.ButtonZr; ButtonZr = (TKey)(object)keyboardConfig.RightJoycon.ButtonZr;
ButtonPlus = (Key)(object)keyboardConfig.RightJoycon.ButtonPlus; ButtonPlus = (TKey)(object)keyboardConfig.RightJoycon.ButtonPlus;
DpadUp = (Key)(object)keyboardConfig.LeftJoycon.DpadUp; DpadUp = (TKey)(object)keyboardConfig.LeftJoycon.DpadUp;
DpadDown = (Key)(object)keyboardConfig.LeftJoycon.DpadDown; DpadDown = (TKey)(object)keyboardConfig.LeftJoycon.DpadDown;
DpadLeft = (Key)(object)keyboardConfig.LeftJoycon.DpadLeft; DpadLeft = (TKey)(object)keyboardConfig.LeftJoycon.DpadLeft;
DpadRight = (Key)(object)keyboardConfig.LeftJoycon.DpadRight; DpadRight = (TKey)(object)keyboardConfig.LeftJoycon.DpadRight;
ButtonMinus = (Key)(object)keyboardConfig.LeftJoycon.ButtonMinus; ButtonMinus = (TKey)(object)keyboardConfig.LeftJoycon.ButtonMinus;
LeftButtonSl = (Key)(object)keyboardConfig.LeftJoycon.ButtonSl; LeftButtonSl = (TKey)(object)keyboardConfig.LeftJoycon.ButtonSl;
LeftButtonSr = (Key)(object)keyboardConfig.LeftJoycon.ButtonSr; LeftButtonSr = (TKey)(object)keyboardConfig.LeftJoycon.ButtonSr;
ButtonZl = (Key)(object)keyboardConfig.LeftJoycon.ButtonZl; ButtonZl = (TKey)(object)keyboardConfig.LeftJoycon.ButtonZl;
ButtonL = (Key)(object)keyboardConfig.LeftJoycon.ButtonL; ButtonL = (TKey)(object)keyboardConfig.LeftJoycon.ButtonL;
} }
else if (config is StandardControllerInputConfig controllerConfig) else if (config is StandardControllerInputConfig controllerConfig)
{ {
LeftJoystick = (Stick)(object)controllerConfig.LeftJoyconStick.Joystick; LeftJoystick = (TStick)(object)controllerConfig.LeftJoyconStick.Joystick;
LeftInvertStickX = controllerConfig.LeftJoyconStick.InvertStickX; LeftInvertStickX = controllerConfig.LeftJoyconStick.InvertStickX;
LeftInvertStickY = controllerConfig.LeftJoyconStick.InvertStickY; LeftInvertStickY = controllerConfig.LeftJoyconStick.InvertStickY;
LeftRotate90 = controllerConfig.LeftJoyconStick.Rotate90CW; LeftRotate90 = controllerConfig.LeftJoyconStick.Rotate90CW;
LeftControllerStickButton = (Key)(object)controllerConfig.LeftJoyconStick.StickButton; LeftControllerStickButton = (TKey)(object)controllerConfig.LeftJoyconStick.StickButton;
RightJoystick = (Stick)(object)controllerConfig.RightJoyconStick.Joystick; RightJoystick = (TStick)(object)controllerConfig.RightJoyconStick.Joystick;
RightInvertStickX = controllerConfig.RightJoyconStick.InvertStickX; RightInvertStickX = controllerConfig.RightJoyconStick.InvertStickX;
RightInvertStickY = controllerConfig.RightJoyconStick.InvertStickY; RightInvertStickY = controllerConfig.RightJoyconStick.InvertStickY;
RightRotate90 = controllerConfig.RightJoyconStick.Rotate90CW; RightRotate90 = controllerConfig.RightJoyconStick.Rotate90CW;
RightControllerStickButton = (Key)(object)controllerConfig.RightJoyconStick.StickButton; RightControllerStickButton = (TKey)(object)controllerConfig.RightJoyconStick.StickButton;
ButtonA = (Key)(object)controllerConfig.RightJoycon.ButtonA; ButtonA = (TKey)(object)controllerConfig.RightJoycon.ButtonA;
ButtonB = (Key)(object)controllerConfig.RightJoycon.ButtonB; ButtonB = (TKey)(object)controllerConfig.RightJoycon.ButtonB;
ButtonX = (Key)(object)controllerConfig.RightJoycon.ButtonX; ButtonX = (TKey)(object)controllerConfig.RightJoycon.ButtonX;
ButtonY = (Key)(object)controllerConfig.RightJoycon.ButtonY; ButtonY = (TKey)(object)controllerConfig.RightJoycon.ButtonY;
ButtonR = (Key)(object)controllerConfig.RightJoycon.ButtonR; ButtonR = (TKey)(object)controllerConfig.RightJoycon.ButtonR;
RightButtonSl = (Key)(object)controllerConfig.RightJoycon.ButtonSl; RightButtonSl = (TKey)(object)controllerConfig.RightJoycon.ButtonSl;
RightButtonSr = (Key)(object)controllerConfig.RightJoycon.ButtonSr; RightButtonSr = (TKey)(object)controllerConfig.RightJoycon.ButtonSr;
ButtonZr = (Key)(object)controllerConfig.RightJoycon.ButtonZr; ButtonZr = (TKey)(object)controllerConfig.RightJoycon.ButtonZr;
ButtonPlus = (Key)(object)controllerConfig.RightJoycon.ButtonPlus; ButtonPlus = (TKey)(object)controllerConfig.RightJoycon.ButtonPlus;
DpadUp = (Key)(object)controllerConfig.LeftJoycon.DpadUp; DpadUp = (TKey)(object)controllerConfig.LeftJoycon.DpadUp;
DpadDown = (Key)(object)controllerConfig.LeftJoycon.DpadDown; DpadDown = (TKey)(object)controllerConfig.LeftJoycon.DpadDown;
DpadLeft = (Key)(object)controllerConfig.LeftJoycon.DpadLeft; DpadLeft = (TKey)(object)controllerConfig.LeftJoycon.DpadLeft;
DpadRight = (Key)(object)controllerConfig.LeftJoycon.DpadRight; DpadRight = (TKey)(object)controllerConfig.LeftJoycon.DpadRight;
ButtonMinus = (Key)(object)controllerConfig.LeftJoycon.ButtonMinus; ButtonMinus = (TKey)(object)controllerConfig.LeftJoycon.ButtonMinus;
LeftButtonSl = (Key)(object)controllerConfig.LeftJoycon.ButtonSl; LeftButtonSl = (TKey)(object)controllerConfig.LeftJoycon.ButtonSl;
LeftButtonSr = (Key)(object)controllerConfig.LeftJoycon.ButtonSr; LeftButtonSr = (TKey)(object)controllerConfig.LeftJoycon.ButtonSr;
ButtonZl = (Key)(object)controllerConfig.LeftJoycon.ButtonZl; ButtonZl = (TKey)(object)controllerConfig.LeftJoycon.ButtonZl;
ButtonL = (Key)(object)controllerConfig.LeftJoycon.ButtonL; ButtonL = (TKey)(object)controllerConfig.LeftJoycon.ButtonL;
DeadzoneLeft = controllerConfig.DeadzoneLeft; DeadzoneLeft = controllerConfig.DeadzoneLeft;
DeadzoneRight = controllerConfig.DeadzoneRight; DeadzoneRight = controllerConfig.DeadzoneRight;
@ -317,65 +316,66 @@ namespace Ryujinx.Ava.UI.Models
{ {
if (Backend == InputBackendType.WindowKeyboard) if (Backend == InputBackendType.WindowKeyboard)
{ {
return new StandardKeyboardInputConfig() return new StandardKeyboardInputConfig
{ {
Id = Id, Id = Id,
Backend = Backend, Backend = Backend,
PlayerIndex = PlayerIndex, PlayerIndex = PlayerIndex,
ControllerType = ControllerType, ControllerType = ControllerType,
LeftJoycon = new LeftJoyconCommonConfig<Ryujinx.Common.Configuration.Hid.Key>() LeftJoycon = new LeftJoyconCommonConfig<Key>
{ {
DpadUp = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadUp, DpadUp = (Key)(object)DpadUp,
DpadDown = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadDown, DpadDown = (Key)(object)DpadDown,
DpadLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadLeft, DpadLeft = (Key)(object)DpadLeft,
DpadRight = (Ryujinx.Common.Configuration.Hid.Key)(object)DpadRight, DpadRight = (Key)(object)DpadRight,
ButtonL = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonL, ButtonL = (Key)(object)ButtonL,
ButtonZl = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonZl, ButtonZl = (Key)(object)ButtonZl,
ButtonSl = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftButtonSl, ButtonSl = (Key)(object)LeftButtonSl,
ButtonSr = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftButtonSr, ButtonSr = (Key)(object)LeftButtonSr,
ButtonMinus = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonMinus ButtonMinus = (Key)(object)ButtonMinus,
}, },
RightJoycon = new RightJoyconCommonConfig<Ryujinx.Common.Configuration.Hid.Key>() RightJoycon = new RightJoyconCommonConfig<Key>
{ {
ButtonA = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonA, ButtonA = (Key)(object)ButtonA,
ButtonB = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonB, ButtonB = (Key)(object)ButtonB,
ButtonX = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonX, ButtonX = (Key)(object)ButtonX,
ButtonY = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonY, ButtonY = (Key)(object)ButtonY,
ButtonPlus = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonPlus, ButtonPlus = (Key)(object)ButtonPlus,
ButtonSl = (Ryujinx.Common.Configuration.Hid.Key)(object)RightButtonSl, ButtonSl = (Key)(object)RightButtonSl,
ButtonSr = (Ryujinx.Common.Configuration.Hid.Key)(object)RightButtonSr, ButtonSr = (Key)(object)RightButtonSr,
ButtonR = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonR, ButtonR = (Key)(object)ButtonR,
ButtonZr = (Ryujinx.Common.Configuration.Hid.Key)(object)ButtonZr ButtonZr = (Key)(object)ButtonZr,
}, },
LeftJoyconStick = new JoyconConfigKeyboardStick<Ryujinx.Common.Configuration.Hid.Key>() LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
{ {
StickUp = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickUp, StickUp = (Key)(object)LeftStickUp,
StickDown = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickDown, StickDown = (Key)(object)LeftStickDown,
StickRight = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickRight, StickRight = (Key)(object)LeftStickRight,
StickLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftStickLeft, StickLeft = (Key)(object)LeftStickLeft,
StickButton = (Ryujinx.Common.Configuration.Hid.Key)(object)LeftKeyboardStickButton StickButton = (Key)(object)LeftKeyboardStickButton,
}, },
RightJoyconStick = new JoyconConfigKeyboardStick<Ryujinx.Common.Configuration.Hid.Key>() RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{ {
StickUp = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickUp, StickUp = (Key)(object)RightStickUp,
StickDown = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickDown, StickDown = (Key)(object)RightStickDown,
StickLeft = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickLeft, StickLeft = (Key)(object)RightStickLeft,
StickRight = (Ryujinx.Common.Configuration.Hid.Key)(object)RightStickRight, StickRight = (Key)(object)RightStickRight,
StickButton = (Ryujinx.Common.Configuration.Hid.Key)(object)RightKeyboardStickButton StickButton = (Key)(object)RightKeyboardStickButton,
}, },
Version = InputConfig.CurrentVersion Version = InputConfig.CurrentVersion,
}; };
} }
else if (Backend == InputBackendType.GamepadSDL2)
if (Backend == InputBackendType.GamepadSDL2)
{ {
var config = new StandardControllerInputConfig() var config = new StandardControllerInputConfig
{ {
Id = Id, Id = Id,
Backend = Backend, Backend = Backend,
PlayerIndex = PlayerIndex, PlayerIndex = PlayerIndex,
ControllerType = ControllerType, ControllerType = ControllerType,
LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>() LeftJoycon = new LeftJoyconCommonConfig<GamepadInputId>
{ {
DpadUp = (GamepadInputId)(object)DpadUp, DpadUp = (GamepadInputId)(object)DpadUp,
DpadDown = (GamepadInputId)(object)DpadDown, DpadDown = (GamepadInputId)(object)DpadDown,
@ -387,7 +387,7 @@ namespace Ryujinx.Ava.UI.Models
ButtonSr = (GamepadInputId)(object)LeftButtonSr, ButtonSr = (GamepadInputId)(object)LeftButtonSr,
ButtonMinus = (GamepadInputId)(object)ButtonMinus, ButtonMinus = (GamepadInputId)(object)ButtonMinus,
}, },
RightJoycon = new RightJoyconCommonConfig<GamepadInputId>() RightJoycon = new RightJoyconCommonConfig<GamepadInputId>
{ {
ButtonA = (GamepadInputId)(object)ButtonA, ButtonA = (GamepadInputId)(object)ButtonA,
ButtonB = (GamepadInputId)(object)ButtonB, ButtonB = (GamepadInputId)(object)ButtonB,
@ -399,7 +399,7 @@ namespace Ryujinx.Ava.UI.Models
ButtonR = (GamepadInputId)(object)ButtonR, ButtonR = (GamepadInputId)(object)ButtonR,
ButtonZr = (GamepadInputId)(object)ButtonZr, ButtonZr = (GamepadInputId)(object)ButtonZr,
}, },
LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>() LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
{ {
Joystick = (StickInputId)(object)LeftJoystick, Joystick = (StickInputId)(object)LeftJoystick,
InvertStickX = LeftInvertStickX, InvertStickX = LeftInvertStickX,
@ -407,7 +407,7 @@ namespace Ryujinx.Ava.UI.Models
Rotate90CW = LeftRotate90, Rotate90CW = LeftRotate90,
StickButton = (GamepadInputId)(object)LeftControllerStickButton, StickButton = (GamepadInputId)(object)LeftControllerStickButton,
}, },
RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>() RightJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
{ {
Joystick = (StickInputId)(object)RightJoystick, Joystick = (StickInputId)(object)RightJoystick,
InvertStickX = RightInvertStickX, InvertStickX = RightInvertStickX,
@ -415,11 +415,11 @@ namespace Ryujinx.Ava.UI.Models
Rotate90CW = RightRotate90, Rotate90CW = RightRotate90,
StickButton = (GamepadInputId)(object)RightControllerStickButton, StickButton = (GamepadInputId)(object)RightControllerStickButton,
}, },
Rumble = new RumbleConfigController() Rumble = new RumbleConfigController
{ {
EnableRumble = EnableRumble, EnableRumble = EnableRumble,
WeakRumble = WeakRumble, WeakRumble = WeakRumble,
StrongRumble = StrongRumble StrongRumble = StrongRumble,
}, },
Version = InputConfig.CurrentVersion, Version = InputConfig.CurrentVersion,
DeadzoneLeft = DeadzoneLeft, DeadzoneLeft = DeadzoneLeft,
@ -428,19 +428,19 @@ namespace Ryujinx.Ava.UI.Models
RangeRight = RangeRight, RangeRight = RangeRight,
TriggerThreshold = TriggerThreshold, TriggerThreshold = TriggerThreshold,
Motion = EnableCemuHookMotion Motion = EnableCemuHookMotion
? new CemuHookMotionConfigController() ? new CemuHookMotionConfigController
{ {
DsuServerHost = DsuServerHost, DsuServerHost = DsuServerHost,
DsuServerPort = DsuServerPort, DsuServerPort = DsuServerPort,
Slot = Slot, Slot = Slot,
AltSlot = AltSlot, AltSlot = AltSlot,
MirrorInput = MirrorInput, MirrorInput = MirrorInput,
MotionBackend = MotionInputBackendType.CemuHook MotionBackend = MotionInputBackendType.CemuHook,
} }
: new StandardMotionConfigController() : new StandardMotionConfigController
{ {
MotionBackend = MotionInputBackendType.GamepadDriver MotionBackend = MotionInputBackendType.GamepadDriver,
} },
}; };
config.Motion.Sensitivity = Sensitivity; config.Motion.Sensitivity = Sensitivity;

View file

@ -2,12 +2,12 @@ using LibHac.Fs;
using LibHac.Ncm; using LibHac.Ncm;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Path = System.IO.Path;
namespace Ryujinx.Ava.UI.Models namespace Ryujinx.Ava.UI.Models
{ {
@ -58,7 +58,7 @@ namespace Ryujinx.Ava.UI.Models
return "0 KiB"; return "0 KiB";
} }
public SaveModel(SaveDataInfo info, VirtualFileSystem virtualFileSystem) public SaveModel(SaveDataInfo info)
{ {
SaveId = info.SaveDataId; SaveId = info.SaveDataId;
TitleId = info.ProgramId; TitleId = info.ProgramId;
@ -81,10 +81,11 @@ namespace Ryujinx.Ava.UI.Models
Task.Run(() => Task.Run(() =>
{ {
var saveRoot = System.IO.Path.Combine(virtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}"); var saveRoot = Path.Combine(MainWindow.MainWindowViewModel.VirtualFileSystem.GetNandPath(), $"user/save/{info.SaveDataId:x16}");
long total_size = GetDirectorySize(saveRoot); long totalSize = GetDirectorySize(saveRoot);
long GetDirectorySize(string path)
static long GetDirectorySize(string path)
{ {
long size = 0; long size = 0;
if (Directory.Exists(path)) if (Directory.Exists(path))
@ -105,7 +106,7 @@ namespace Ryujinx.Ava.UI.Models
return size; return size;
} }
Size = total_size; Size = totalSize;
}); });
} }

View file

@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.Models
private string _name = String.Empty; private string _name = String.Empty;
private UserId _userId; private UserId _userId;
public uint MaxProfileNameLength => 0x20; public static uint MaxProfileNameLength => 0x20;
public byte[] Image public byte[] Image
{ {

View file

@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Media; using Avalonia.Media;
using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
@ -87,7 +88,7 @@ namespace Ryujinx.Ava.UI.Models
private void UpdateBackground() private void UpdateBackground()
{ {
Avalonia.Application.Current.Styles.TryGetResource("ControlFillColorSecondary", out object color); Application.Current.Styles.TryGetResource("ControlFillColorSecondary", out object color);
if (color is not null) if (color is not null)
{ {

View file

@ -77,11 +77,13 @@ namespace Ryujinx.Ava.UI.Renderer
{ {
return CreateLinux(control); return CreateLinux(control);
} }
else if (OperatingSystem.IsWindows())
if (OperatingSystem.IsWindows())
{ {
return CreateWin32(control); return CreateWin32(control);
} }
else if (OperatingSystem.IsMacOS())
if (OperatingSystem.IsMacOS())
{ {
return CreateMacOS(); return CreateMacOS();
} }
@ -141,21 +143,21 @@ namespace Ryujinx.Ava.UI.Renderer
{ {
if (VisualRoot != null) if (VisualRoot != null)
{ {
if (msg == WindowsMessages.LBUTTONDOWN || if (msg == WindowsMessages.Lbuttondown ||
msg == WindowsMessages.RBUTTONDOWN || msg == WindowsMessages.Rbuttondown ||
msg == WindowsMessages.LBUTTONUP || msg == WindowsMessages.Lbuttonup ||
msg == WindowsMessages.RBUTTONUP || msg == WindowsMessages.Rbuttonup ||
msg == WindowsMessages.MOUSEMOVE) msg == WindowsMessages.Mousemove)
{ {
Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value; Point rootVisualPosition = this.TranslatePoint(new Point((long)lParam & 0xFFFF, (long)lParam >> 16 & 0xFFFF), VisualRoot).Value;
Pointer pointer = new(0, PointerType.Mouse, true); Pointer pointer = new(0, PointerType.Mouse, true);
switch (msg) switch (msg)
{ {
case WindowsMessages.LBUTTONDOWN: case WindowsMessages.Lbuttondown:
case WindowsMessages.RBUTTONDOWN: case WindowsMessages.Rbuttondown:
{ {
bool isLeft = msg == WindowsMessages.LBUTTONDOWN; bool isLeft = msg == WindowsMessages.Lbuttondown;
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed); PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonPressed : PointerUpdateKind.RightButtonPressed);
@ -172,10 +174,10 @@ namespace Ryujinx.Ava.UI.Renderer
break; break;
} }
case WindowsMessages.LBUTTONUP: case WindowsMessages.Lbuttonup:
case WindowsMessages.RBUTTONUP: case WindowsMessages.Rbuttonup:
{ {
bool isLeft = msg == WindowsMessages.LBUTTONUP; bool isLeft = msg == WindowsMessages.Lbuttonup;
RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton; RawInputModifiers pointerPointModifier = isLeft ? RawInputModifiers.LeftMouseButton : RawInputModifiers.RightMouseButton;
PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased); PointerPointProperties properties = new(pointerPointModifier, isLeft ? PointerUpdateKind.LeftButtonReleased : PointerUpdateKind.RightButtonReleased);
@ -193,7 +195,7 @@ namespace Ryujinx.Ava.UI.Renderer
break; break;
} }
case WindowsMessages.MOUSEMOVE: case WindowsMessages.Mousemove:
{ {
var evnt = new PointerEventArgs( var evnt = new PointerEventArgs(
PointerMovedEvent, PointerMovedEvent,
@ -216,19 +218,19 @@ namespace Ryujinx.Ava.UI.Renderer
return DefWindowProc(hWnd, msg, wParam, lParam); return DefWindowProc(hWnd, msg, wParam, lParam);
}; };
WNDCLASSEX wndClassEx = new() WndClassEx wndClassEx = new()
{ {
cbSize = Marshal.SizeOf<WNDCLASSEX>(), cbSize = Marshal.SizeOf<WndClassEx>(),
hInstance = GetModuleHandle(null), hInstance = GetModuleHandle(null),
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate), lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
style = ClassStyles.CS_OWNDC, style = ClassStyles.CsOwndc,
lpszClassName = Marshal.StringToHGlobalUni(_className), lpszClassName = Marshal.StringToHGlobalUni(_className),
hCursor = CreateArrowCursor() hCursor = CreateArrowCursor(),
}; };
RegisterClassEx(ref wndClassEx); RegisterClassEx(ref wndClassEx);
WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WS_CHILD, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); WindowHandle = CreateWindowEx(0, _className, "NativeWindow", WindowStyles.WsChild, 0, 0, 640, 480, control.Handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
Marshal.FreeHGlobal(wndClassEx.lpszClassName); Marshal.FreeHGlobal(wndClassEx.lpszClassName);
@ -280,9 +282,11 @@ namespace Ryujinx.Ava.UI.Renderer
} }
[SupportedOSPlatform("macos")] [SupportedOSPlatform("macos")]
#pragma warning disable CA1822 // Mark member as static
void DestroyMacOS() void DestroyMacOS()
{ {
// TODO // TODO
} }
#pragma warning restore CA1822
} }
} }

View file

@ -34,7 +34,7 @@ namespace Ryujinx.Ava.UI.Renderer
return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, nativeWindowBase)); return new SurfaceKHR((ulong?)VulkanHelper.CreateWindowSurface(instance.Handle, nativeWindowBase));
} }
public SurfaceKHR CreateSurface(Instance instance, Vk api) public SurfaceKHR CreateSurface(Instance instance, Vk _)
{ {
return CreateSurface(instance); return CreateSurface(instance);
} }

View file

@ -1,5 +1,6 @@
using Avalonia; using Avalonia;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
@ -87,7 +88,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
Version = Program.Version; Version = Program.Version;
var assets = AvaloniaLocator.Current.GetService<Avalonia.Platform.IAssetLoader>(); var assets = AvaloniaLocator.Current.GetService<IAssetLoader>();
if (ConfigurationState.Instance.Ui.BaseStyle.Value == "Light") if (ConfigurationState.Instance.Ui.BaseStyle.Value == "Light")
{ {

View file

@ -18,7 +18,6 @@ using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using AmiiboJsonSerializerContext = Ryujinx.Ui.Common.Models.Amiibo.AmiiboJsonSerializerContext;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
@ -44,7 +43,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private bool _useRandomUuid; private bool _useRandomUuid;
private string _usage; private string _usage;
private static readonly AmiiboJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly AmiiboJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId) public AmiiboWindowViewModel(StyleableWindow owner, string lastScannedAmiiboId, string titleId)
{ {
@ -52,7 +51,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_httpClient = new HttpClient _httpClient = new HttpClient
{ {
Timeout = TimeSpan.FromSeconds(30) Timeout = TimeSpan.FromSeconds(30),
}; };
LastScannedAmiiboId = lastScannedAmiiboId; LastScannedAmiiboId = lastScannedAmiiboId;
@ -185,6 +184,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this);
_httpClient.Dispose(); _httpClient.Dispose();
} }
@ -196,7 +196,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath); amiiboJsonString = await File.ReadAllTextAsync(_amiiboJsonPath);
if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).LastUpdated)) if (await NeedsUpdate(JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).LastUpdated))
{ {
amiiboJsonString = await DownloadAmiiboJson(); amiiboJsonString = await DownloadAmiiboJson();
} }
@ -215,7 +215,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
_amiiboList = JsonHelper.Deserialize(amiiboJsonString, SerializerContext.AmiiboJson).Amiibo; _amiiboList = JsonHelper.Deserialize(amiiboJsonString, _serializerContext.AmiiboJson).Amiibo;
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); _amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
ParseAmiiboData(); ParseAmiiboData();
@ -426,8 +426,8 @@ namespace Ryujinx.Ava.UI.ViewModels
if (response.IsSuccessStatusCode) if (response.IsSuccessStatusCode)
{ {
byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync(); byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync();
using (MemoryStream memoryStream = new(amiiboPreviewBytes)) using MemoryStream memoryStream = new(amiiboPreviewBytes);
{
Bitmap bitmap = new(memoryStream); Bitmap bitmap = new(memoryStream);
double ratio = Math.Min(AmiiboImageSize / bitmap.Size.Width, double ratio = Math.Min(AmiiboImageSize / bitmap.Size.Width,
@ -438,7 +438,6 @@ namespace Ryujinx.Ava.UI.ViewModels
AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight)); AmiiboImage = bitmap.CreateScaledBitmap(new PixelSize(resizeWidth, resizeHeight));
} }
}
else else
{ {
Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}"); Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}");
@ -447,15 +446,14 @@ namespace Ryujinx.Ava.UI.ViewModels
private void ResetAmiiboPreview() private void ResetAmiiboPreview()
{ {
using (MemoryStream memoryStream = new(_amiiboLogoBytes)) using MemoryStream memoryStream = new(_amiiboLogoBytes);
{
Bitmap bitmap = new(memoryStream); Bitmap bitmap = new(memoryStream);
AmiiboImage = bitmap; AmiiboImage = bitmap;
} }
}
private async void ShowInfoDialog() private static async void ShowInfoDialog()
{ {
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle], await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogAmiiboApiTitle],
LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage], LocaleManager.Instance[LocaleKeys.DialogAmiiboApiConnectErrorMessage],

View file

@ -1,363 +0,0 @@
using Avalonia.Media;
using DynamicData;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Color = Avalonia.Media.Color;
namespace Ryujinx.Ava.UI.ViewModels
{
internal class AvatarProfileViewModel : BaseModel, IDisposable
{
private const int MaxImageTasks = 4;
private static readonly Dictionary<string, byte[]> _avatarStore = new();
private static bool _isPreloading;
private static Action _loadCompleteAction;
private ObservableCollection<ProfileImageModel> _images;
private Color _backgroundColor = Colors.White;
private int _selectedIndex;
private int _imagesLoaded;
private bool _isActive;
private byte[] _selectedImage;
private bool _isIndeterminate = true;
public bool IsActive
{
get => _isActive;
set => _isActive = value;
}
public AvatarProfileViewModel()
{
_images = new ObservableCollection<ProfileImageModel>();
}
public AvatarProfileViewModel(Action loadCompleteAction)
{
_images = new ObservableCollection<ProfileImageModel>();
if (_isPreloading)
{
_loadCompleteAction = loadCompleteAction;
}
else
{
ReloadImages();
}
}
public Color BackgroundColor
{
get => _backgroundColor;
set
{
_backgroundColor = value;
IsActive = false;
ReloadImages();
}
}
public ObservableCollection<ProfileImageModel> Images
{
get => _images;
set
{
_images = value;
OnPropertyChanged();
}
}
public bool IsIndeterminate
{
get => _isIndeterminate;
set
{
_isIndeterminate = value;
OnPropertyChanged();
}
}
public int ImageCount => _avatarStore.Count;
public int ImagesLoaded
{
get => _imagesLoaded;
set
{
_imagesLoaded = value;
OnPropertyChanged();
}
}
public int SelectedIndex
{
get => _selectedIndex;
set
{
_selectedIndex = value;
if (_selectedIndex == -1)
{
SelectedImage = null;
}
else
{
SelectedImage = _images[_selectedIndex].Data;
}
OnPropertyChanged();
}
}
public byte[] SelectedImage
{
get => _selectedImage;
private set => _selectedImage = value;
}
public void ReloadImages()
{
if (_isPreloading)
{
IsIndeterminate = false;
return;
}
Task.Run(() =>
{
IsActive = true;
Images.Clear();
int selectedIndex = _selectedIndex;
int index = 0;
ImagesLoaded = 0;
IsIndeterminate = false;
var keys = _avatarStore.Keys.ToList();
var newImages = new List<ProfileImageModel>();
var tasks = new List<Task>();
for (int i = 0; i < MaxImageTasks; i++)
{
var start = i;
tasks.Add(Task.Run(() => ImageTask(start)));
}
Task.WaitAll(tasks.ToArray());
Images.AddRange(newImages);
void ImageTask(int start)
{
for (int i = start; i < keys.Count; i += MaxImageTasks)
{
if (!IsActive)
{
return;
}
var key = keys[i];
var image = _avatarStore[keys[i]];
var data = ProcessImage(image);
newImages.Add(new ProfileImageModel(key, data));
if (index++ == selectedIndex)
{
SelectedImage = data;
}
Interlocked.Increment(ref _imagesLoaded);
OnPropertyChanged(nameof(ImagesLoaded));
}
}
});
}
private byte[] ProcessImage(byte[] data)
{
using (MemoryStream streamJpg = new())
{
Image avatarImage = Image.Load(data, new PngDecoder());
avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(BackgroundColor.R,
BackgroundColor.G,
BackgroundColor.B,
BackgroundColor.A)));
avatarImage.SaveAsJpeg(streamJpg);
return streamJpg.ToArray();
}
}
public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem)
{
try
{
if (_avatarStore.Count > 0)
{
return;
}
_isPreloading = true;
string contentPath =
contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem,
NcaContentType.Data);
string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath);
if (!string.IsNullOrWhiteSpace(avatarPath))
{
using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open))
{
Nca nca = new(virtualFileSystem.KeySet, ncaFileStream);
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
foreach (DirectoryEntryEx item in romfs.EnumerateEntries())
{
// TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy.
if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") &&
item.FullPath.Contains("szs"))
{
using var file = new UniqueRef<IFile>();
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read)
.ThrowIfFailure();
using (MemoryStream stream = new())
using (MemoryStream streamPng = new())
{
file.Get.AsStream().CopyTo(stream);
stream.Position = 0;
Image avatarImage = Image.LoadPixelData<Rgba32>(DecompressYaz0(stream), 256, 256);
avatarImage.SaveAsPng(streamPng);
_avatarStore.Add(item.FullPath, streamPng.ToArray());
}
}
}
}
}
}
finally
{
_isPreloading = false;
_loadCompleteAction?.Invoke();
}
}
private static byte[] DecompressYaz0(Stream stream)
{
using (BinaryReader reader = new(stream))
{
reader.ReadInt32(); // Magic
uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32());
reader.ReadInt64(); // Padding
byte[] input = new byte[stream.Length - stream.Position];
stream.Read(input, 0, input.Length);
uint inputOffset = 0;
byte[] output = new byte[decodedLength];
uint outputOffset = 0;
ushort mask = 0;
byte header = 0;
while (outputOffset < decodedLength)
{
if ((mask >>= 1) == 0)
{
header = input[inputOffset++];
mask = 0x80;
}
if ((header & mask) != 0)
{
if (outputOffset == output.Length)
{
break;
}
output[outputOffset++] = input[inputOffset++];
}
else
{
byte byte1 = input[inputOffset++];
byte byte2 = input[inputOffset++];
uint dist = (uint)((byte1 & 0xF) << 8) | byte2;
uint position = outputOffset - (dist + 1);
uint length = (uint)byte1 >> 4;
if (length == 0)
{
length = (uint)input[inputOffset++] + 0x12;
}
else
{
length += 2;
}
uint gap = outputOffset - position;
uint nonOverlappingLength = length;
if (nonOverlappingLength > gap)
{
nonOverlappingLength = gap;
}
Buffer.BlockCopy(output, (int)position, output, (int)outputOffset, (int)nonOverlappingLength);
outputOffset += nonOverlappingLength;
position += nonOverlappingLength;
length -= nonOverlappingLength;
while (length-- > 0)
{
output[outputOffset++] = output[position++];
}
}
}
return output;
}
}
public void Dispose()
{
_loadCompleteAction = null;
IsActive = false;
}
}
}

View file

@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
@ -44,15 +45,14 @@ namespace Ryujinx.Ava.UI.ViewModels
private PlayerIndex _playerId; private PlayerIndex _playerId;
private int _controller; private int _controller;
private int _controllerNumber = 0; private int _controllerNumber;
private string _controllerImage; private string _controllerImage;
private int _device; private int _device;
private object _configuration; private object _configuration;
private string _profileName; private string _profileName;
private bool _isLoaded; private bool _isLoaded;
private readonly UserControl _owner;
private static readonly InputConfigJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public IGamepadDriver AvaloniaKeyboardDriver { get; } public IGamepadDriver AvaloniaKeyboardDriver { get; }
public IGamepad SelectedGamepad { get; private set; } public IGamepad SelectedGamepad { get; private set; }
@ -176,11 +176,11 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
get get
{ {
SvgImage image = new SvgImage(); SvgImage image = new();
if (!string.IsNullOrWhiteSpace(_controllerImage)) if (!string.IsNullOrWhiteSpace(_controllerImage))
{ {
SvgSource source = new SvgSource(); SvgSource source = new();
source.Load(EmbeddedResources.GetStream(_controllerImage)); source.Load(EmbeddedResources.GetStream(_controllerImage));
@ -234,22 +234,18 @@ namespace Ryujinx.Ava.UI.ViewModels
public ControllerInputViewModel(UserControl owner) : this() public ControllerInputViewModel(UserControl owner) : this()
{ {
_owner = owner;
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
_mainWindow = _mainWindow =
(MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current (MainWindow)((IClassicDesktopStyleApplicationLifetime)Application.Current
.ApplicationLifetime).MainWindow; .ApplicationLifetime).MainWindow;
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner); AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
if (_mainWindow.ViewModel.AppHost != null)
{ _mainWindow.ViewModel.AppHost?.NpadManager.BlockInputUpdates();
_mainWindow.ViewModel.AppHost.NpadManager.BlockInputUpdates();
}
_isLoaded = false; _isLoaded = false;
@ -351,7 +347,8 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
return; return;
} }
else if (type == DeviceType.Keyboard)
if (type == DeviceType.Keyboard)
{ {
if (_mainWindow.InputManager.KeyboardDriver is AvaloniaKeyboardDriver) if (_mainWindow.InputManager.KeyboardDriver is AvaloniaKeyboardDriver)
{ {
@ -448,7 +445,7 @@ namespace Ryujinx.Ava.UI.ViewModels
const string Hyphen = "-"; const string Hyphen = "-";
const int Offset = 1; const int Offset = 1;
return str.Substring(str.IndexOf(Hyphen) + Offset); return str[(str.IndexOf(Hyphen) + Offset)..];
} }
public void LoadDevices() public void LoadDevices()
@ -562,7 +559,7 @@ namespace Ryujinx.Ava.UI.ViewModels
ButtonL = Key.E, ButtonL = Key.E,
ButtonZl = Key.Q, ButtonZl = Key.Q,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound ButtonSr = Key.Unbound,
}, },
LeftJoyconStick = LeftJoyconStick =
new JoyconConfigKeyboardStick<Key> new JoyconConfigKeyboardStick<Key>
@ -571,7 +568,7 @@ namespace Ryujinx.Ava.UI.ViewModels
StickDown = Key.S, StickDown = Key.S,
StickLeft = Key.A, StickLeft = Key.A,
StickRight = Key.D, StickRight = Key.D,
StickButton = Key.F StickButton = Key.F,
}, },
RightJoycon = new RightJoyconCommonConfig<Key> RightJoycon = new RightJoyconCommonConfig<Key>
{ {
@ -583,7 +580,7 @@ namespace Ryujinx.Ava.UI.ViewModels
ButtonR = Key.U, ButtonR = Key.U,
ButtonZr = Key.O, ButtonZr = Key.O,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound ButtonSr = Key.Unbound,
}, },
RightJoyconStick = new JoyconConfigKeyboardStick<Key> RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{ {
@ -591,8 +588,8 @@ namespace Ryujinx.Ava.UI.ViewModels
StickDown = Key.K, StickDown = Key.K,
StickLeft = Key.J, StickLeft = Key.J,
StickRight = Key.L, StickRight = Key.L,
StickButton = Key.H StickButton = Key.H,
} },
}; };
} }
else if (activeDevice.Type == DeviceType.Controller) else if (activeDevice.Type == DeviceType.Controller)
@ -622,14 +619,14 @@ namespace Ryujinx.Ava.UI.ViewModels
ButtonL = ConfigGamepadInputId.LeftShoulder, ButtonL = ConfigGamepadInputId.LeftShoulder,
ButtonZl = ConfigGamepadInputId.LeftTrigger, ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound, ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound ButtonSr = ConfigGamepadInputId.Unbound,
}, },
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{ {
Joystick = ConfigStickInputId.Left, Joystick = ConfigStickInputId.Left,
StickButton = ConfigGamepadInputId.LeftStick, StickButton = ConfigGamepadInputId.LeftStick,
InvertStickX = false, InvertStickX = false,
InvertStickY = false InvertStickY = false,
}, },
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId> RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
{ {
@ -641,28 +638,28 @@ namespace Ryujinx.Ava.UI.ViewModels
ButtonR = ConfigGamepadInputId.RightShoulder, ButtonR = ConfigGamepadInputId.RightShoulder,
ButtonZr = ConfigGamepadInputId.RightTrigger, ButtonZr = ConfigGamepadInputId.RightTrigger,
ButtonSl = ConfigGamepadInputId.Unbound, ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound ButtonSr = ConfigGamepadInputId.Unbound,
}, },
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{ {
Joystick = ConfigStickInputId.Right, Joystick = ConfigStickInputId.Right,
StickButton = ConfigGamepadInputId.RightStick, StickButton = ConfigGamepadInputId.RightStick,
InvertStickX = false, InvertStickX = false,
InvertStickY = false InvertStickY = false,
}, },
Motion = new StandardMotionConfigController Motion = new StandardMotionConfigController
{ {
MotionBackend = MotionInputBackendType.GamepadDriver, MotionBackend = MotionInputBackendType.GamepadDriver,
EnableMotion = true, EnableMotion = true,
Sensitivity = 100, Sensitivity = 100,
GyroDeadzone = 1 GyroDeadzone = 1,
}, },
Rumble = new RumbleConfigController Rumble = new RumbleConfigController
{ {
StrongRumble = 1f, StrongRumble = 1f,
WeakRumble = 1f, WeakRumble = 1f,
EnableRumble = false EnableRumble = false,
} },
}; };
} }
else else
@ -709,7 +706,7 @@ namespace Ryujinx.Ava.UI.ViewModels
try try
{ {
config = JsonHelper.DeserializeFromFile(path, SerializerContext.InputConfig); config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig);
} }
catch (JsonException) { } catch (JsonException) { }
catch (InvalidOperationException) catch (InvalidOperationException)
@ -754,8 +751,7 @@ namespace Ryujinx.Ava.UI.ViewModels
return; return;
} }
else
{
bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1; bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
if (validFileName) if (validFileName)
@ -775,7 +771,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.ControllerType = Controllers[_controller].Type; config.ControllerType = Controllers[_controller].Type;
string jsonString = JsonHelper.Serialize(config, SerializerContext.InputConfig); string jsonString = JsonHelper.Serialize(config, _serializerContext.InputConfig);
await File.WriteAllTextAsync(path, jsonString); await File.WriteAllTextAsync(path, jsonString);
@ -786,7 +782,6 @@ namespace Ryujinx.Ava.UI.ViewModels
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]); await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogProfileInvalidProfileNameErrorMessage]);
} }
} }
}
public async void RemoveProfile() public async void RemoveProfile()
{ {
@ -887,6 +882,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public void Dispose() public void Dispose()
{ {
GC.SuppressFinalize(this);
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;

View file

@ -22,6 +22,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Application = Avalonia.Application;
using Path = System.IO.Path; using Path = System.IO.Path;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
@ -31,16 +32,15 @@ namespace Ryujinx.Ava.UI.ViewModels
private readonly List<DownloadableContentContainer> _downloadableContentContainerList; private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
private readonly string _downloadableContentJsonPath; private readonly string _downloadableContentJsonPath;
private VirtualFileSystem _virtualFileSystem; private readonly VirtualFileSystem _virtualFileSystem;
private AvaloniaList<DownloadableContentModel> _downloadableContents = new(); private AvaloniaList<DownloadableContentModel> _downloadableContents = new();
private AvaloniaList<DownloadableContentModel> _views = new(); private AvaloniaList<DownloadableContentModel> _views = new();
private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new(); private AvaloniaList<DownloadableContentModel> _selectedDownloadableContents = new();
private string _search; private string _search;
private ulong _titleId; private readonly ulong _titleId;
private string _titleName;
private static readonly DownloadableContentJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AvaloniaList<DownloadableContentModel> DownloadableContents public AvaloniaList<DownloadableContentModel> DownloadableContents
{ {
@ -90,18 +90,17 @@ namespace Ryujinx.Ava.UI.ViewModels
get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count); get => string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowHeading], DownloadableContents.Count);
} }
public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) public DownloadableContentManagerViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
{ {
_virtualFileSystem = virtualFileSystem; _virtualFileSystem = virtualFileSystem;
_titleId = titleId; _titleId = titleId;
_titleName = titleName;
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json"); _downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
try try
{ {
_downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, SerializerContext.ListDownloadableContentContainer); _downloadableContentContainerList = JsonHelper.DeserializeFromFile(_downloadableContentJsonPath, _serializerContext.ListDownloadableContentContainer);
} }
catch catch
{ {
@ -196,19 +195,19 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void Add() public async void Add()
{ {
OpenFileDialog dialog = new OpenFileDialog() OpenFileDialog dialog = new()
{ {
Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle], Title = LocaleManager.Instance[LocaleKeys.SelectDlcDialogTitle],
AllowMultiple = true AllowMultiple = true,
}; };
dialog.Filters.Add(new FileDialogFilter dialog.Filters.Add(new FileDialogFilter
{ {
Name = "NSP", Name = "NSP",
Extensions = { "nsp" } Extensions = { "nsp" },
}); });
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
string[] files = await dialog.ShowAsync(desktop.MainWindow); string[] files = await dialog.ShowAsync(desktop.MainWindow);
@ -314,7 +313,7 @@ namespace Ryujinx.Ava.UI.ViewModels
container = new DownloadableContentContainer container = new DownloadableContentContainer
{ {
ContainerPath = downloadableContent.ContainerPath, ContainerPath = downloadableContent.ContainerPath,
DownloadableContentNcaList = new List<DownloadableContentNca>() DownloadableContentNcaList = new List<DownloadableContentNca>(),
}; };
} }
@ -322,7 +321,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
Enabled = downloadableContent.Enabled, Enabled = downloadableContent.Enabled,
TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16), TitleId = Convert.ToUInt64(downloadableContent.TitleId, 16),
FullPath = downloadableContent.FullPath FullPath = downloadableContent.FullPath,
}); });
} }
@ -331,7 +330,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_downloadableContentContainerList.Add(container); _downloadableContentContainerList.Add(container);
} }
JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, SerializerContext.ListDownloadableContentContainer); JsonHelper.SerializeToFile(_downloadableContentJsonPath, _downloadableContentContainerList, _serializerContext.ListDownloadableContentContainer);
} }
} }

View file

@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input; using Avalonia.Input;
@ -12,6 +13,7 @@ using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Models.Generic;
using Ryujinx.Ava.UI.Renderer; using Ryujinx.Ava.UI.Renderer;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common; using Ryujinx.Common;
@ -23,6 +25,7 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.Ui; using Ryujinx.HLE.Ui;
using Ryujinx.Modules;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common; using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
@ -34,7 +37,10 @@ using System.Collections.ObjectModel;
using System.IO; using System.IO;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Path = System.IO.Path; using Image = SixLabors.ImageSharp.Image;
using InputManager = Ryujinx.Input.HLE.InputManager;
using Key = Ryujinx.Input.Key;
using MissingKeyException = LibHac.Common.Keys.MissingKeyException;
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
@ -89,7 +95,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private Cursor _cursor; private Cursor _cursor;
private string _title; private string _title;
private string _currentEmulatedGamePath; private string _currentEmulatedGamePath;
private AutoResetEvent _rendererWaitEvent; private readonly AutoResetEvent _rendererWaitEvent;
private WindowState _windowState; private WindowState _windowState;
private double _windowWidth; private double _windowWidth;
private double _windowHeight; private double _windowHeight;
@ -128,7 +134,7 @@ namespace Ryujinx.Ava.UI.ViewModels
ApplicationLibrary applicationLibrary, ApplicationLibrary applicationLibrary,
VirtualFileSystem virtualFileSystem, VirtualFileSystem virtualFileSystem,
AccountManager accountManager, AccountManager accountManager,
Ryujinx.Input.HLE.InputManager inputManager, InputManager inputManager,
UserChannelPersistence userChannelPersistence, UserChannelPersistence userChannelPersistence,
LibHacHorizonManager libHacHorizonManager, LibHacHorizonManager libHacHorizonManager,
IHostUiHandler uiHandler, IHostUiHandler uiHandler,
@ -152,7 +158,7 @@ namespace Ryujinx.Ava.UI.ViewModels
TopLevel = topLevel; TopLevel = topLevel;
} }
#region Properties #region Properties
public string SearchText public string SearchText
{ {
@ -177,7 +183,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool CanUpdate public bool CanUpdate
{ {
get => _canUpdate && EnableNonGameRunningControls && Modules.Updater.CanUpdate(false); get => _canUpdate && EnableNonGameRunningControls && Updater.CanUpdate(false);
set set
{ {
_canUpdate = value; _canUpdate = value;
@ -343,11 +349,11 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public bool OpenUserSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0; public bool OpenUserSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.UserAccountSaveDataSize > 0;
public bool OpenDeviceSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0; public bool OpenDeviceSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.DeviceSaveDataSize > 0;
public bool OpenBcatSaveDirectoryEnabled => !Utilities.IsZeros(SelectedApplication.ControlHolder.ByteSpan) && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; public bool OpenBcatSaveDirectoryEnabled => !SelectedApplication.ControlHolder.ByteSpan.IsZeros() && SelectedApplication.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0;
public string LoadHeading public string LoadHeading
{ {
@ -888,7 +894,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public ApplicationLibrary ApplicationLibrary { get; private set; } public ApplicationLibrary ApplicationLibrary { get; private set; }
public VirtualFileSystem VirtualFileSystem { get; private set; } public VirtualFileSystem VirtualFileSystem { get; private set; }
public AccountManager AccountManager { get; private set; } public AccountManager AccountManager { get; private set; }
public Ryujinx.Input.HLE.InputManager InputManager { get; private set; } public InputManager InputManager { get; private set; }
public UserChannelPersistence UserChannelPersistence { get; private set; } public UserChannelPersistence UserChannelPersistence { get; private set; }
public Action<bool> ShowLoading { get; private set; } public Action<bool> ShowLoading { get; private set; }
public Action<bool> SwitchToGameControl { get; private set; } public Action<bool> SwitchToGameControl { get; private set; }
@ -911,15 +917,16 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3; public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3;
public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4; public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4;
#endregion #endregion
#region PrivateMethods #region PrivateMethods
private IComparer<ApplicationData> GetComparer() private IComparer<ApplicationData> GetComparer()
{ {
return SortMode switch return SortMode switch
{ {
ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending), #pragma warning disable IDE0055 // Disable formatting
ApplicationSort.LastPlayed => new LastPlayedSortComparer(IsAscending),
ApplicationSort.FileSize => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileSizeBytes) ApplicationSort.FileSize => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileSizeBytes)
: SortExpressionComparer<ApplicationData>.Descending(app => app.FileSizeBytes), : SortExpressionComparer<ApplicationData>.Descending(app => app.FileSizeBytes),
ApplicationSort.TotalTimePlayed => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TimePlayedNum) ApplicationSort.TotalTimePlayed => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TimePlayedNum)
@ -935,6 +942,7 @@ namespace Ryujinx.Ava.UI.ViewModels
ApplicationSort.Path => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path) ApplicationSort.Path => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path)
: SortExpressionComparer<ApplicationData>.Descending(app => app.Path), : SortExpressionComparer<ApplicationData>.Descending(app => app.Path),
_ => null, _ => null,
#pragma warning restore IDE0055
}; };
} }
@ -1023,7 +1031,7 @@ namespace Ryujinx.Ava.UI.ViewModels
// Purge Applet Cache. // Purge Applet Cache.
DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); DirectoryInfo miiEditorCacheFolder = new(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache"));
if (miiEditorCacheFolder.Exists) if (miiEditorCacheFolder.Exists)
{ {
@ -1044,18 +1052,21 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
RefreshFirmwareStatus(); RefreshFirmwareStatus();
} }
}) { Name = "GUI.FirmwareInstallerThread" }; })
{
Name = "GUI.FirmwareInstallerThread",
};
thread.Start(); thread.Start();
} }
} }
catch (LibHac.Common.Keys.MissingKeyException ex) catch (MissingKeyException ex)
{ {
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
Logger.Error?.Print(LogClass.Application, ex.ToString()); Logger.Error?.Print(LogClass.Application, ex.ToString());
async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, (desktop.MainWindow as MainWindow)); static async void Action() => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys);
Dispatcher.UIThread.Post(Action); Dispatcher.UIThread.Post(Action);
} }
@ -1120,7 +1131,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private void PrepareLoadScreen() private void PrepareLoadScreen()
{ {
using MemoryStream stream = new(SelectedIcon); using MemoryStream stream = new(SelectedIcon);
using var gameIconBmp = SixLabors.ImageSharp.Image.Load<Bgra32>(stream); using var gameIconBmp = Image.Load<Bgra32>(stream);
var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel<Bgra32>(); var dominantColor = IconColorPicker.GetFilteredColor(gameIconBmp).ToPixel<Bgra32>();
@ -1175,7 +1186,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
Dispatcher.UIThread.InvokeAsync(() => Dispatcher.UIThread.InvokeAsync(() =>
{ {
Avalonia.Application.Current.Styles.TryGetResource(args.VSyncEnabled Application.Current.Styles.TryGetResource(args.VSyncEnabled
? "VsyncEnabled" ? "VsyncEnabled"
: "VsyncDisabled", out object color); : "VsyncDisabled", out object color);
@ -1204,11 +1215,11 @@ namespace Ryujinx.Ava.UI.ViewModels
_rendererWaitEvent.Set(); _rendererWaitEvent.Set();
} }
#endregion #endregion
#region PublicMethods #region PublicMethods
public void SetUIProgressHandlers(Switch emulationContext) public void SetUiProgressHandlers(Switch emulationContext)
{ {
if (emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null) if (emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null)
{ {
@ -1222,17 +1233,17 @@ namespace Ryujinx.Ava.UI.ViewModels
public void LoadConfigurableHotKeys() public void LoadConfigurableHotKeys()
{ {
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey)) if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey))
{ {
ShowUiKey = new KeyGesture(showUiKey); ShowUiKey = new KeyGesture(showUiKey);
} }
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey)) if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
{ {
ScreenshotKey = new KeyGesture(screenshotKey); ScreenshotKey = new KeyGesture(screenshotKey);
} }
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey)) if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
{ {
PauseKey = new KeyGesture(pauseKey); PauseKey = new KeyGesture(pauseKey);
} }
@ -1260,7 +1271,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void InstallFirmwareFromFile() public async void InstallFirmwareFromFile()
{ {
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
OpenFileDialog dialog = new() { AllowMultiple = false }; OpenFileDialog dialog = new() { AllowMultiple = false };
dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.FileDialogAllTypes], Extensions = { "xci", "zip" } }); dialog.Filters.Add(new FileDialogFilter { Name = LocaleManager.Instance[LocaleKeys.FileDialogAllTypes], Extensions = { "xci", "zip" } });
@ -1278,7 +1289,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void InstallFirmwareFromFolder() public async void InstallFirmwareFromFolder()
{ {
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
OpenFolderDialog dialog = new(); OpenFolderDialog dialog = new();
@ -1327,7 +1338,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public void ChangeLanguage(object languageCode) public static void ChangeLanguage(object languageCode)
{ {
LocaleManager.Instance.LoadLanguage((string)languageCode); LocaleManager.Instance.LoadLanguage((string)languageCode);
@ -1342,6 +1353,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
_ = fileType switch _ = fileType switch
{ {
#pragma warning disable IDE0055 // Disable formatting
"NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP, "NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP,
"PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0, "PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0,
"XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI, "XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI,
@ -1349,6 +1361,7 @@ namespace Ryujinx.Ava.UI.ViewModels
"NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO, "NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO,
"NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO, "NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO,
_ => throw new ArgumentOutOfRangeException(fileType), _ => throw new ArgumentOutOfRangeException(fileType),
#pragma warning restore IDE0055
}; };
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
@ -1383,11 +1396,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void OpenFile() public async void OpenFile()
{ {
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
OpenFileDialog dialog = new() OpenFileDialog dialog = new()
{ {
Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle] Title = LocaleManager.Instance[LocaleKeys.OpenFileDialogTitle],
}; };
dialog.Filters.Add(new FileDialogFilter dialog.Filters.Add(new FileDialogFilter
@ -1400,16 +1413,18 @@ namespace Ryujinx.Ava.UI.ViewModels
"xci", "xci",
"nca", "nca",
"nro", "nro",
"nso" "nso",
} },
}); });
#pragma warning disable IDE0055 // Disable formatting
dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } }); dialog.Filters.Add(new FileDialogFilter { Name = "NSP", Extensions = { "nsp" } });
dialog.Filters.Add(new FileDialogFilter { Name = "PFS0", Extensions = { "pfs0" } }); dialog.Filters.Add(new FileDialogFilter { Name = "PFS0", Extensions = { "pfs0" } });
dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } }); dialog.Filters.Add(new FileDialogFilter { Name = "XCI", Extensions = { "xci" } });
dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } }); dialog.Filters.Add(new FileDialogFilter { Name = "NCA", Extensions = { "nca" } });
dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } }); dialog.Filters.Add(new FileDialogFilter { Name = "NRO", Extensions = { "nro" } });
dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } }); dialog.Filters.Add(new FileDialogFilter { Name = "NSO", Extensions = { "nso" } });
#pragma warning restore IDE0055
string[] files = await dialog.ShowAsync(desktop.MainWindow); string[] files = await dialog.ShowAsync(desktop.MainWindow);
@ -1422,11 +1437,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public async void OpenFolder() public async void OpenFolder()
{ {
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
OpenFolderDialog dialog = new() OpenFolderDialog dialog = new()
{ {
Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle] Title = LocaleManager.Instance[LocaleKeys.OpenFolderDialogTitle],
}; };
string folder = await dialog.ShowAsync(desktop.MainWindow); string folder = await dialog.ShowAsync(desktop.MainWindow);
@ -1458,10 +1473,7 @@ namespace Ryujinx.Ava.UI.ViewModels
Logger.RestartTime(); Logger.RestartTime();
if (SelectedIcon == null) SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(path);
{
SelectedIcon = ApplicationLibrary.GetApplicationIcon(path);
}
PrepareLoadScreen(); PrepareLoadScreen();
@ -1521,7 +1533,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}); });
} }
public void UpdateGameMetadata(string titleId) public static void UpdateGameMetadata(string titleId)
{ {
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
{ {
@ -1675,6 +1687,6 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
#endregion #endregion
} }
} }

View file

@ -18,7 +18,6 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.Ui.Common.Configuration.System;
using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@ -248,7 +247,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public AvaloniaList<string> NetworkInterfaceList public AvaloniaList<string> NetworkInterfaceList
{ {
get => new AvaloniaList<string>(_networkInterfaces.Keys); get => new(_networkInterfaces.Keys);
} }
public KeyboardHotkeys KeyboardHotkeys public KeyboardHotkeys KeyboardHotkeys
@ -561,7 +560,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_directoryChanged = false; _directoryChanged = false;
} }
public void RevertIfNotSaved() private static void RevertIfNotSaved()
{ {
Program.ReloadConfig(); Program.ReloadConfig();
} }

View file

@ -1,3 +1,4 @@
using Avalonia;
using Avalonia.Collections; using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
@ -16,7 +17,6 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -29,17 +29,16 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
public class TitleUpdateViewModel : BaseModel public class TitleUpdateViewModel : BaseModel
{ {
public TitleUpdateMetadata _titleUpdateWindowData; public TitleUpdateMetadata TitleUpdateWindowData;
public readonly string _titleUpdateJsonPath; public readonly string TitleUpdateJsonPath;
private VirtualFileSystem _virtualFileSystem { get; } private VirtualFileSystem VirtualFileSystem { get; }
private ulong _titleId { get; } private ulong TitleId { get; }
private string _titleName { get; }
private AvaloniaList<TitleUpdateModel> _titleUpdates = new(); private AvaloniaList<TitleUpdateModel> _titleUpdates = new();
private AvaloniaList<object> _views = new(); private AvaloniaList<object> _views = new();
private object _selectedUpdate; private object _selectedUpdate;
private static readonly TitleUpdateMetadataJsonSerializerContext SerializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
public AvaloniaList<TitleUpdateModel> TitleUpdates public AvaloniaList<TitleUpdateModel> TitleUpdates
{ {
@ -71,27 +70,26 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) public TitleUpdateViewModel(VirtualFileSystem virtualFileSystem, ulong titleId)
{ {
_virtualFileSystem = virtualFileSystem; VirtualFileSystem = virtualFileSystem;
_titleId = titleId; TitleId = titleId;
_titleName = titleName;
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json"); TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
try try
{ {
_titleUpdateWindowData = JsonHelper.DeserializeFromFile(_titleUpdateJsonPath, SerializerContext.TitleUpdateMetadata); TitleUpdateWindowData = JsonHelper.DeserializeFromFile(TitleUpdateJsonPath, _serializerContext.TitleUpdateMetadata);
} }
catch catch
{ {
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {_titleId} at {_titleUpdateJsonPath}"); Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {TitleId} at {TitleUpdateJsonPath}");
_titleUpdateWindowData = new TitleUpdateMetadata TitleUpdateWindowData = new TitleUpdateMetadata
{ {
Selected = "", Selected = "",
Paths = new List<string>() Paths = new List<string>(),
}; };
Save(); Save();
@ -102,12 +100,12 @@ namespace Ryujinx.Ava.UI.ViewModels
private void LoadUpdates() private void LoadUpdates()
{ {
foreach (string path in _titleUpdateWindowData.Paths) foreach (string path in TitleUpdateWindowData.Paths)
{ {
AddUpdate(path); AddUpdate(path);
} }
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected, null); TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == TitleUpdateWindowData.Selected, null);
SelectedUpdate = selected; SelectedUpdate = selected;
@ -126,7 +124,8 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
return -1; return -1;
} }
else if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
{ {
return 1; return 1;
} }
@ -163,7 +162,7 @@ namespace Ryujinx.Ava.UI.ViewModels
try try
{ {
(Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0); (Nca patchNca, Nca controlNca) = ApplicationLibrary.GetGameUpdateDataFromPartition(VirtualFileSystem, new PartitionFileSystem(file.AsStorage()), TitleId.ToString("x16"), 0);
if (controlNca != null && patchNca != null) if (controlNca != null && patchNca != null)
{ {
@ -206,16 +205,16 @@ namespace Ryujinx.Ava.UI.ViewModels
OpenFileDialog dialog = new() OpenFileDialog dialog = new()
{ {
Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle], Title = LocaleManager.Instance[LocaleKeys.SelectUpdateDialogTitle],
AllowMultiple = true AllowMultiple = true,
}; };
dialog.Filters.Add(new FileDialogFilter dialog.Filters.Add(new FileDialogFilter
{ {
Name = "NSP", Name = "NSP",
Extensions = { "nsp" } Extensions = { "nsp" },
}); });
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
string[] files = await dialog.ShowAsync(desktop.MainWindow); string[] files = await dialog.ShowAsync(desktop.MainWindow);
@ -233,20 +232,20 @@ namespace Ryujinx.Ava.UI.ViewModels
public void Save() public void Save()
{ {
_titleUpdateWindowData.Paths.Clear(); TitleUpdateWindowData.Paths.Clear();
_titleUpdateWindowData.Selected = ""; TitleUpdateWindowData.Selected = "";
foreach (TitleUpdateModel update in TitleUpdates) foreach (TitleUpdateModel update in TitleUpdates)
{ {
_titleUpdateWindowData.Paths.Add(update.Path); TitleUpdateWindowData.Paths.Add(update.Path);
if (update == SelectedUpdate) if (update == SelectedUpdate)
{ {
_titleUpdateWindowData.Selected = update.Path; TitleUpdateWindowData.Selected = update.Path;
} }
} }
JsonHelper.SerializeToFile(_titleUpdateJsonPath, _titleUpdateWindowData, SerializerContext.TitleUpdateMetadata); JsonHelper.SerializeToFile(TitleUpdateJsonPath, TitleUpdateWindowData, _serializerContext.TitleUpdateMetadata);
} }
} }
} }

View file

@ -28,7 +28,6 @@ namespace Ryujinx.Ava.UI.ViewModels
private Color _backgroundColor = Colors.White; private Color _backgroundColor = Colors.White;
private int _selectedIndex; private int _selectedIndex;
private byte[] _selectedImage;
public UserFirmwareAvatarSelectorViewModel() public UserFirmwareAvatarSelectorViewModel()
{ {
@ -78,11 +77,7 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
public byte[] SelectedImage public byte[] SelectedImage { get; private set; }
{
get => _selectedImage;
private set => _selectedImage = value;
}
private void LoadImagesFromStore() private void LoadImagesFromStore()
{ {
@ -114,8 +109,8 @@ namespace Ryujinx.Ava.UI.ViewModels
if (!string.IsNullOrWhiteSpace(avatarPath)) if (!string.IsNullOrWhiteSpace(avatarPath))
{ {
using (IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open)) using IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open);
{
Nca nca = new(virtualFileSystem.KeySet, ncaFileStream); Nca nca = new(virtualFileSystem.KeySet, ncaFileStream);
IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid);
@ -128,9 +123,9 @@ namespace Ryujinx.Ava.UI.ViewModels
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
using (MemoryStream stream = new()) using MemoryStream stream = new();
using (MemoryStream streamPng = new()) using MemoryStream streamPng = new();
{
file.Get.AsStream().CopyTo(stream); file.Get.AsStream().CopyTo(stream);
stream.Position = 0; stream.Position = 0;
@ -144,13 +139,11 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
} }
} }
}
}
private static byte[] DecompressYaz0(Stream stream) private static byte[] DecompressYaz0(Stream stream)
{ {
using (BinaryReader reader = new(stream)) using BinaryReader reader = new(stream);
{
reader.ReadInt32(); // Magic reader.ReadInt32(); // Magic
uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32());
@ -226,5 +219,4 @@ namespace Ryujinx.Ava.UI.ViewModels
return output; return output;
} }
} }
}
} }

View file

@ -1,7 +1,7 @@
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Ryujinx.Ava.UI.Models;
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.ViewModels namespace Ryujinx.Ava.UI.ViewModels
{ {
@ -20,6 +20,9 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsEmpty { get; set; } public bool IsEmpty { get; set; }
public void Dispose() { } public void Dispose()
{
GC.SuppressFinalize(this);
}
} }
} }

View file

@ -15,7 +15,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private string _search; private string _search;
private ObservableCollection<SaveModel> _saves = new(); private ObservableCollection<SaveModel> _saves = new();
private ObservableCollection<SaveModel> _views = new(); private ObservableCollection<SaveModel> _views = new();
private AccountManager _accountManager; private readonly AccountManager _accountManager;
public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId); public string SaveManagerHeading => LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SaveManagerHeading, _accountManager.LastOpenedUser.Name, _accountManager.LastOpenedUser.UserId);
@ -102,19 +102,16 @@ namespace Ryujinx.Ava.UI.ViewModels
private IComparer<SaveModel> GetComparer() private IComparer<SaveModel> GetComparer()
{ {
switch (SortIndex) return SortIndex switch
{ {
case 0: 0 => OrderIndex == 0
return OrderIndex == 0
? SortExpressionComparer<SaveModel>.Ascending(save => save.Title) ? SortExpressionComparer<SaveModel>.Ascending(save => save.Title)
: SortExpressionComparer<SaveModel>.Descending(save => save.Title); : SortExpressionComparer<SaveModel>.Descending(save => save.Title),
case 1: 1 => OrderIndex == 0
return OrderIndex == 0
? SortExpressionComparer<SaveModel>.Ascending(save => save.Size) ? SortExpressionComparer<SaveModel>.Ascending(save => save.Size)
: SortExpressionComparer<SaveModel>.Descending(save => save.Size); : SortExpressionComparer<SaveModel>.Descending(save => save.Size),
default: _ => null,
return null; };
}
} }
} }
} }

View file

@ -29,7 +29,7 @@ namespace Ryujinx.Ava.UI.Views.Input
foreach (ILogical visual in SettingButtons.GetLogicalDescendants()) foreach (ILogical visual in SettingButtons.GetLogicalDescendants())
{ {
if (visual is ToggleButton button && !(visual is CheckBox)) if (visual is ToggleButton button && visual is not CheckBox)
{ {
button.Checked += Button_Checked; button.Checked += Button_Checked;
button.Unchecked += Button_Unchecked; button.Unchecked += Button_Unchecked;

View file

@ -10,7 +10,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class MotionInputView : UserControl public partial class MotionInputView : UserControl
{ {
private MotionInputViewModel _viewModel; private readonly MotionInputViewModel _viewModel;
public MotionInputView() public MotionInputView()
{ {
@ -30,7 +30,7 @@ namespace Ryujinx.Ava.UI.Views.Input
MirrorInput = config.MirrorInput, MirrorInput = config.MirrorInput,
Sensitivity = config.Sensitivity, Sensitivity = config.Sensitivity,
GyroDeadzone = config.GyroDeadzone, GyroDeadzone = config.GyroDeadzone,
EnableCemuHookMotion = config.EnableCemuHookMotion EnableCemuHookMotion = config.EnableCemuHookMotion,
}; };
InitializeComponent(); InitializeComponent();
@ -47,7 +47,7 @@ namespace Ryujinx.Ava.UI.Views.Input
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave], PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
SecondaryButtonText = "", SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
Content = content Content = content,
}; };
contentDialog.PrimaryButtonClick += (sender, args) => contentDialog.PrimaryButtonClick += (sender, args) =>
{ {

View file

@ -10,7 +10,7 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
public partial class RumbleInputView : UserControl public partial class RumbleInputView : UserControl
{ {
private RumbleInputViewModel _viewModel; private readonly RumbleInputViewModel _viewModel;
public RumbleInputView() public RumbleInputView()
{ {
@ -24,7 +24,7 @@ namespace Ryujinx.Ava.UI.Views.Input
_viewModel = new RumbleInputViewModel _viewModel = new RumbleInputViewModel
{ {
StrongRumble = config.StrongRumble, StrongRumble = config.StrongRumble,
WeakRumble = config.WeakRumble WeakRumble = config.WeakRumble,
}; };
InitializeComponent(); InitializeComponent();

View file

@ -38,21 +38,21 @@ namespace Ryujinx.Ava.UI.Views.Main
{ {
List<CheckBox> checkBoxes = new(); List<CheckBox> checkBoxes = new();
foreach (var item in Enum.GetValues(typeof (FileTypes))) foreach (var item in Enum.GetValues(typeof(FileTypes)))
{ {
string fileName = Enum.GetName(typeof (FileTypes), item); string fileName = Enum.GetName(typeof(FileTypes), item);
checkBoxes.Add(new CheckBox() checkBoxes.Add(new CheckBox
{ {
Content = $".{fileName}", Content = $".{fileName}",
IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes), IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes),
Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName)) Command = MiniCommand.Create(() => ViewModel.ToggleFileType(fileName)),
}); });
} }
return checkBoxes.ToArray(); return checkBoxes.ToArray();
} }
private MenuItem[] GenerateLanguageMenuItems() private static MenuItem[] GenerateLanguageMenuItems()
{ {
List<MenuItem> menuItems = new(); List<MenuItem> menuItems = new();
@ -79,8 +79,8 @@ namespace Ryujinx.Ava.UI.Views.Main
Header = languageName, Header = languageName,
Command = MiniCommand.Create(() => Command = MiniCommand.Create(() =>
{ {
ViewModel.ChangeLanguage(languageCode); MainWindowViewModel.ChangeLanguage(languageCode);
}) }),
}; };
menuItems.Add(menuItem); menuItems.Add(menuItem);

View file

@ -12,12 +12,12 @@ namespace Ryujinx.Ava.UI.Views.Settings
public partial class SettingsHotkeysView : UserControl public partial class SettingsHotkeysView : UserControl
{ {
private ButtonKeyAssigner _currentAssigner; private ButtonKeyAssigner _currentAssigner;
private IGamepadDriver AvaloniaKeyboardDriver; private readonly IGamepadDriver _avaloniaKeyboardDriver;
public SettingsHotkeysView() public SettingsHotkeysView()
{ {
InitializeComponent(); InitializeComponent();
AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this); _avaloniaKeyboardDriver = new AvaloniaKeyboardDriver(this);
} }
private void MouseClick(object sender, PointerPressedEventArgs e) private void MouseClick(object sender, PointerPressedEventArgs e)
@ -46,7 +46,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
PointerPressed += MouseClick; PointerPressed += MouseClick;
var keyboard = (IKeyboard)AvaloniaKeyboardDriver.GetGamepad(AvaloniaKeyboardDriver.GamepadsIds[0]); var keyboard = (IKeyboard)_avaloniaKeyboardDriver.GetGamepad(_avaloniaKeyboardDriver.GamepadsIds[0]);
IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard); IButtonAssigner assigner = new KeyboardKeyAssigner(keyboard);
_currentAssigner.GetInputAndAssign(assigner); _currentAssigner.GetInputAndAssign(assigner);

View file

@ -1,5 +1,5 @@
<UserControl <UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsUIView" x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsUiView"
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"

View file

@ -1,4 +1,5 @@
using Avalonia.Controls; using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
@ -9,11 +10,11 @@ using System.Linq;
namespace Ryujinx.Ava.UI.Views.Settings namespace Ryujinx.Ava.UI.Views.Settings
{ {
public partial class SettingsUIView : UserControl public partial class SettingsUiView : UserControl
{ {
public SettingsViewModel ViewModel; public SettingsViewModel ViewModel;
public SettingsUIView() public SettingsUiView()
{ {
InitializeComponent(); InitializeComponent();
} }
@ -29,7 +30,7 @@ namespace Ryujinx.Ava.UI.Views.Settings
} }
else else
{ {
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
path = await new OpenFolderDialog().ShowAsync(desktop.MainWindow); path = await new OpenFolderDialog().ShowAsync(desktop.MainWindow);
@ -60,15 +61,15 @@ namespace Ryujinx.Ava.UI.Views.Settings
public async void BrowseTheme(object sender, RoutedEventArgs e) public async void BrowseTheme(object sender, RoutedEventArgs e)
{ {
var dialog = new OpenFileDialog() var dialog = new OpenFileDialog
{ {
Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle], Title = LocaleManager.Instance[LocaleKeys.SettingsSelectThemeFileDialogTitle],
AllowMultiple = false AllowMultiple = false,
}; };
dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] }); dialog.Filters.Add(new FileDialogFilter { Extensions = { "xaml" }, Name = LocaleManager.Instance[LocaleKeys.SettingsXamlThemeFile] });
if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
var file = await dialog.ShowAsync(desktop.MainWindow); var file = await dialog.ShowAsync(desktop.MainWindow);

View file

@ -20,7 +20,7 @@ namespace Ryujinx.Ava.UI.Views.User
private bool _isNewUser; private bool _isNewUser;
public TempProfile TempProfile { get; set; } public TempProfile TempProfile { get; set; }
public uint MaxProfileNameLength => 0x20; public static uint MaxProfileNameLength => 0x20;
public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId; public bool IsDeletable => _profile.UserId != AccountManager.DefaultUserId;
public UserEditorView() public UserEditorView()
@ -39,17 +39,17 @@ namespace Ryujinx.Ava.UI.Views.User
switch (arg.NavigationMode) switch (arg.NavigationMode)
{ {
case NavigationMode.New: case NavigationMode.New:
var args = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter; var (parent, profile, isNewUser) = ((NavigationDialogHost parent, UserProfile profile, bool isNewUser))arg.Parameter;
_isNewUser = args.isNewUser; _isNewUser = isNewUser;
_profile = args.profile; _profile = profile;
TempProfile = new TempProfile(_profile); TempProfile = new TempProfile(_profile);
_parent = args.parent; _parent = parent;
break; break;
} }
((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " + ((ContentDialog)_parent.Parent).Title = $"{LocaleManager.Instance[LocaleKeys.UserProfileWindowTitle]} - " +
$"{ (_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}"; $"{(_isNewUser ? LocaleManager.Instance[LocaleKeys.UserEditorTitleCreate] : LocaleManager.Instance[LocaleKeys.UserEditorTitle])}";
DataContext = TempProfile; DataContext = TempProfile;

View file

@ -11,6 +11,7 @@ using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing;
using System.IO; using System.IO;
using Image = SixLabors.ImageSharp.Image;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
@ -70,7 +71,7 @@ namespace Ryujinx.Ava.UI.Views.User
if (ViewModel.SelectedImage != null) if (ViewModel.SelectedImage != null)
{ {
MemoryStream streamJpg = new(); MemoryStream streamJpg = new();
SixLabors.ImageSharp.Image avatarImage = SixLabors.ImageSharp.Image.Load(ViewModel.SelectedImage, new PngDecoder()); Image avatarImage = Image.Load(ViewModel.SelectedImage, new PngDecoder());
avatarImage.Mutate(x => x.BackgroundColor(new Rgba32( avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(
ViewModel.BackgroundColor.R, ViewModel.BackgroundColor.R,

View file

@ -67,7 +67,7 @@ namespace Ryujinx.Ava.UI.Views.User
dialog.Filters.Add(new FileDialogFilter dialog.Filters.Add(new FileDialogFilter
{ {
Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats], Name = LocaleManager.Instance[LocaleKeys.AllSupportedFormats],
Extensions = { "jpg", "jpeg", "png", "bmp" } Extensions = { "jpg", "jpeg", "png", "bmp" },
}); });
dialog.Filters.Add(new FileDialogFilter { Name = "JPEG", Extensions = { "jpg", "jpeg" } }); dialog.Filters.Add(new FileDialogFilter { Name = "JPEG", Extensions = { "jpg", "jpeg" } });
dialog.Filters.Add(new FileDialogFilter { Name = "PNG", Extensions = { "png" } }); dialog.Filters.Add(new FileDialogFilter { Name = "PNG", Extensions = { "png" } });
@ -108,17 +108,15 @@ namespace Ryujinx.Ava.UI.Views.User
private static byte[] ProcessProfileImage(byte[] buffer) private static byte[] ProcessProfileImage(byte[] buffer)
{ {
using (Image image = Image.Load(buffer)) using Image image = Image.Load(buffer);
{
image.Mutate(x => x.Resize(256, 256)); image.Mutate(x => x.Resize(256, 256));
using (MemoryStream streamJpg = new()) using MemoryStream streamJpg = new();
{
image.SaveAsJpeg(streamJpg); image.SaveAsJpeg(streamJpg);
return streamJpg.ToArray(); return streamJpg.ToArray();
} }
} }
}
}
} }

View file

@ -18,6 +18,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Threading.Tasks; using System.Threading.Tasks;
using Button = Avalonia.Controls.Button;
using UserId = LibHac.Fs.UserId; using UserId = LibHac.Fs.UserId;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
@ -47,12 +48,12 @@ namespace Ryujinx.Ava.UI.Views.User
switch (arg.NavigationMode) switch (arg.NavigationMode)
{ {
case NavigationMode.New: case NavigationMode.New:
var args = ((NavigationDialogHost parent, AccountManager accountManager, HorizonClient client, VirtualFileSystem virtualFileSystem))arg.Parameter; var (parent, accountManager, client, virtualFileSystem) = ((NavigationDialogHost parent, AccountManager accountManager, HorizonClient client, VirtualFileSystem virtualFileSystem))arg.Parameter;
_accountManager = args.accountManager; _accountManager = accountManager;
_horizonClient = args.client; _horizonClient = client;
_virtualFileSystem = args.virtualFileSystem; _virtualFileSystem = virtualFileSystem;
_parent = args.parent; _parent = parent;
break; break;
} }
@ -94,7 +95,7 @@ namespace Ryujinx.Ava.UI.Views.User
var save = saveDataInfo[i]; var save = saveDataInfo[i];
if (save.ProgramId.Value != 0) if (save.ProgramId.Value != 0)
{ {
var saveModel = new SaveModel(save, _virtualFileSystem); var saveModel = new SaveModel(save);
saves.Add(saveModel); saves.Add(saveModel);
} }
} }
@ -114,7 +115,7 @@ namespace Ryujinx.Ava.UI.Views.User
private void OpenLocation(object sender, RoutedEventArgs e) private void OpenLocation(object sender, RoutedEventArgs e)
{ {
if (sender is Avalonia.Controls.Button button) if (sender is Button button)
{ {
if (button.DataContext is SaveModel saveModel) if (button.DataContext is SaveModel saveModel)
{ {
@ -125,7 +126,7 @@ namespace Ryujinx.Ava.UI.Views.User
private async void Delete(object sender, RoutedEventArgs e) private async void Delete(object sender, RoutedEventArgs e)
{ {
if (sender is Avalonia.Controls.Button button) if (sender is Button button)
{ {
if (button.DataContext is SaveModel saveModel) if (button.DataContext is SaveModel saveModel)
{ {

View file

@ -5,8 +5,9 @@ using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation; using FluentAvalonia.UI.Navigation;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls; using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile; using Button = Avalonia.Controls.Button;
namespace Ryujinx.Ava.UI.Views.User namespace Ryujinx.Ava.UI.Views.User
{ {
@ -101,7 +102,7 @@ namespace Ryujinx.Ava.UI.Views.User
private void EditUser(object sender, RoutedEventArgs e) private void EditUser(object sender, RoutedEventArgs e)
{ {
if (sender is Avalonia.Controls.Button button) if (sender is Button button)
{ {
if (button.DataContext is UserProfile userProfile) if (button.DataContext is UserProfile userProfile)
{ {

View file

@ -1,6 +1,7 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Styling; using Avalonia.Styling;
using FluentAvalonia.UI.Controls; using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
@ -28,14 +29,14 @@ namespace Ryujinx.Ava.UI.Windows
PrimaryButtonText = "", PrimaryButtonText = "",
SecondaryButtonText = "", SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose], CloseButtonText = LocaleManager.Instance[LocaleKeys.UserProfilesClose],
Content = new AboutWindow() Content = new AboutWindow(),
}; };
Style closeButton = new(x => x.Name("CloseButton")); Style closeButton = new(x => x.Name("CloseButton"));
closeButton.Setters.Add(new Setter(WidthProperty, 80d)); closeButton.Setters.Add(new Setter(WidthProperty, 80d));
Style closeButtonParent = new(x => x.Name("CommandSpace")); Style closeButtonParent = new(x => x.Name("CommandSpace"));
closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, Avalonia.Layout.HorizontalAlignment.Right)); closeButtonParent.Setters.Add(new Setter(HorizontalAlignmentProperty, HorizontalAlignment.Right));
contentDialog.Styles.Add(closeButton); contentDialog.Styles.Add(closeButton);
contentDialog.Styles.Add(closeButtonParent); contentDialog.Styles.Add(closeButtonParent);

View file

@ -9,9 +9,10 @@ namespace Ryujinx.Ava.UI.Windows
{ {
public AmiiboWindow(bool showAll, string lastScannedAmiiboId, string titleId) public AmiiboWindow(bool showAll, string lastScannedAmiiboId, string titleId)
{ {
ViewModel = new AmiiboWindowViewModel(this, lastScannedAmiiboId, titleId); ViewModel = new AmiiboWindowViewModel(this, lastScannedAmiiboId, titleId)
{
ViewModel.ShowAllAmiibo = showAll; ShowAllAmiibo = showAll,
};
DataContext = ViewModel; DataContext = ViewModel;

View file

@ -1,11 +1,12 @@
using Avalonia; using Avalonia.Collections;
using Avalonia.Collections;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common; using Ryujinx.Ui.App.Common;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -41,11 +42,11 @@ namespace Ryujinx.Ava.UI.Windows
string modsBasePath = ModLoader.GetModsBasePath(); string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId); string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId);
ulong titleIdValue = ulong.Parse(titleId, System.Globalization.NumberStyles.HexNumber); ulong titleIdValue = ulong.Parse(titleId, NumberStyles.HexNumber);
_enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt"); _enabledCheatsPath = Path.Combine(titleModsPath, "cheats", "enabled.txt");
string[] enabled = { }; string[] enabled = Array.Empty<string>();
if (File.Exists(_enabledCheatsPath)) if (File.Exists(_enabledCheatsPath))
{ {
@ -60,7 +61,6 @@ namespace Ryujinx.Ava.UI.Windows
string currentCheatFile = string.Empty; string currentCheatFile = string.Empty;
string buildId = string.Empty; string buildId = string.Empty;
string parentPath = string.Empty;
CheatsList currentGroup = null; CheatsList currentGroup = null;
@ -69,7 +69,7 @@ namespace Ryujinx.Ava.UI.Windows
if (cheat.Path.FullName != currentCheatFile) if (cheat.Path.FullName != currentCheatFile)
{ {
currentCheatFile = cheat.Path.FullName; currentCheatFile = cheat.Path.FullName;
parentPath = currentCheatFile.Replace(titleModsPath, ""); string parentPath = currentCheatFile.Replace(titleModsPath, "");
buildId = Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper(); buildId = Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper();
currentGroup = new CheatsList(buildId, parentPath); currentGroup = new CheatsList(buildId, parentPath);
@ -100,7 +100,7 @@ namespace Ryujinx.Ava.UI.Windows
return; return;
} }
List<string> enabledCheats = new List<string>(); List<string> enabledCheats = new();
foreach (var cheats in LoadedCheats) foreach (var cheats in LoadedCheats)
{ {

View file

@ -1,7 +1,8 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media; using Avalonia.Media;
#if DEBUG
using Avalonia;
#endif
namespace Ryujinx.Ava.UI.Windows namespace Ryujinx.Ava.UI.Windows
{ {

View file

@ -89,8 +89,8 @@
<Grid <Grid
Grid.Column="0"> Grid.Column="0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"></ColumnDefinition> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock <TextBlock
Grid.Column="0" Grid.Column="0"

View file

@ -24,9 +24,9 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent(); InitializeComponent();
} }
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId)
{ {
DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId, titleName); DataContext = ViewModel = new DownloadableContentManagerViewModel(virtualFileSystem, titleId);
InitializeComponent(); InitializeComponent();
} }
@ -38,8 +38,8 @@ namespace Ryujinx.Ava.UI.Windows
PrimaryButtonText = "", PrimaryButtonText = "",
SecondaryButtonText = "", SecondaryButtonText = "",
CloseButtonText = "", CloseButtonText = "",
Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId, titleName), Content = new DownloadableContentManagerWindow(virtualFileSystem, titleId),
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16")) Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], titleName, titleId.ToString("X16")),
}; };
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());

View file

@ -68,8 +68,10 @@ namespace Ryujinx.Ava.UI.Windows
int w8 = w << 8; int w8 = w << 8;
int h8 = image.Height << 8; int h8 = image.Height << 8;
#pragma warning disable IDE0059 // Unnecessary assignment
int xStep = w8 / ColorsPerLine; int xStep = w8 / ColorsPerLine;
int yStep = h8 / ColorsPerLine; int yStep = h8 / ColorsPerLine;
#pragma warning restore IDE0059
int i = 0; int i = 0;
int maxHitCount = 0; int maxHitCount = 0;

View file

@ -323,7 +323,7 @@ namespace Ryujinx.Ava.UI.Windows
ShowKeyErrorOnLoad = false; ShowKeyErrorOnLoad = false;
Dispatcher.UIThread.Post(async () => await Dispatcher.UIThread.Post(async () => await
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, this)); UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys));
} }
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount) if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
@ -373,7 +373,7 @@ namespace Ryujinx.Ava.UI.Windows
private void SetWindowSizePosition() private void SetWindowSizePosition()
{ {
PixelPoint SavedPoint = new PixelPoint(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, PixelPoint savedPoint = new(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX,
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY); ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY);
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor; ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
@ -381,12 +381,14 @@ namespace Ryujinx.Ava.UI.Windows
ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value is true ? WindowState.Maximized : WindowState.Normal; ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value is true ? WindowState.Maximized : WindowState.Normal;
if (CheckScreenBounds(SavedPoint)) if (CheckScreenBounds(savedPoint))
{ {
Position = SavedPoint; Position = savedPoint;
}
else
{
WindowStartupLocation = WindowStartupLocation.CenterScreen;
} }
else WindowStartupLocation = WindowStartupLocation.CenterScreen;
} }
private bool CheckScreenBounds(PixelPoint configPoint) private bool CheckScreenBounds(PixelPoint configPoint)
@ -399,7 +401,7 @@ namespace Ryujinx.Ava.UI.Windows
} }
} }
Logger.Warning?.Print(LogClass.Application, $"Failed to find valid start-up coordinates. Defaulting to primary monitor center."); Logger.Warning?.Print(LogClass.Application, "Failed to find valid start-up coordinates. Defaulting to primary monitor center.");
return false; return false;
} }
@ -425,10 +427,7 @@ namespace Ryujinx.Ava.UI.Windows
private void SetMainContent(Control content = null) private void SetMainContent(Control content = null)
{ {
if (content == null) content ??= GameLibrary;
{
content = GameLibrary;
}
if (MainContent.Content != content) if (MainContent.Content != content)
{ {
@ -438,21 +437,25 @@ namespace Ryujinx.Ava.UI.Windows
public static void UpdateGraphicsConfig() public static void UpdateGraphicsConfig()
{ {
#pragma warning disable IDE0055 // Disable formatting
GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale; GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale;
GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy;
GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression;
GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE;
#pragma warning restore IDE0055
} }
public void LoadHotKeys() public void LoadHotKeys()
{ {
#pragma warning disable IDE0055 // Disable formatting
HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt)); HotKeyManager.SetHotKey(FullscreenHotKey, new KeyGesture(Key.Enter, KeyModifiers.Alt));
HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11)); HotKeyManager.SetHotKey(FullscreenHotKey2, new KeyGesture(Key.F11));
HotKeyManager.SetHotKey(FullscreenHotKeyMacOS, new KeyGesture(Key.F, KeyModifiers.Control | KeyModifiers.Meta)); HotKeyManager.SetHotKey(FullscreenHotKeyMacOS, new KeyGesture(Key.F, KeyModifiers.Control | KeyModifiers.Meta));
HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9)); HotKeyManager.SetHotKey(DockToggleHotKey, new KeyGesture(Key.F9));
HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape)); HotKeyManager.SetHotKey(ExitHotKey, new KeyGesture(Key.Escape));
#pragma warning restore IDE0055
} }
private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e) private void VolumeStatus_CheckedChanged(object sender, SplitButtonClickEventArgs e)

View file

@ -34,7 +34,7 @@
IsVisible="False" IsVisible="False"
KeyboardNavigation.IsTabStop="False"/> KeyboardNavigation.IsTabStop="False"/>
<Grid Name="Pages" IsVisible="False" Grid.Row="2"> <Grid Name="Pages" IsVisible="False" Grid.Row="2">
<settings:SettingsUIView Name="UiPage" /> <settings:SettingsUiView Name="UiPage" />
<settings:SettingsInputView Name="InputPage" /> <settings:SettingsInputView Name="InputPage" />
<settings:SettingsHotkeysView Name="HotkeysPage" /> <settings:SettingsHotkeysView Name="HotkeysPage" />
<settings:SettingsSystemView Name="SystemPage" /> <settings:SettingsSystemView Name="SystemPage" />

View file

@ -2,6 +2,7 @@
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Ryujinx.Ui.Common.Configuration;
using System; using System;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
@ -17,7 +18,7 @@ namespace Ryujinx.Ava.UI.Windows
WindowStartupLocation = WindowStartupLocation.CenterOwner; WindowStartupLocation = WindowStartupLocation.CenterOwner;
TransparencyLevelHint = WindowTransparencyLevel.None; TransparencyLevelHint = WindowTransparencyLevel.None;
using Stream stream = Assembly.GetAssembly(typeof(Ryujinx.Ui.Common.Configuration.ConfigurationState)).GetManifestResourceStream("Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png"); using Stream stream = Assembly.GetAssembly(typeof(ConfigurationState)).GetManifestResourceStream("Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
Icon = new WindowIcon(stream); Icon = new WindowIcon(stream);
stream.Position = 0; stream.Position = 0;

View file

@ -24,9 +24,9 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent(); InitializeComponent();
} }
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId)
{ {
DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId, titleName); DataContext = ViewModel = new TitleUpdateViewModel(virtualFileSystem, titleId);
InitializeComponent(); InitializeComponent();
} }
@ -38,8 +38,8 @@ namespace Ryujinx.Ava.UI.Windows
PrimaryButtonText = "", PrimaryButtonText = "",
SecondaryButtonText = "", SecondaryButtonText = "",
CloseButtonText = "", CloseButtonText = "",
Content = new TitleUpdateWindow(virtualFileSystem, titleId, titleName), Content = new TitleUpdateWindow(virtualFileSystem, titleId),
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16")) Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, titleName, titleId.ToString("X16")),
}; };
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>()); Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());

View file

@ -21,7 +21,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Path = System.IO.Path; using Path = System.IO.Path;
using RightsId = LibHac.Fs.RightsId;
namespace Ryujinx.HLE.FileSystem namespace Ryujinx.HLE.FileSystem
{ {