aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.Ava/Ui/ViewModels
diff options
context:
space:
mode:
authorEmmanuel Hansen <emmausssss@gmail.com>2022-07-05 18:06:31 +0000
committerGitHub <noreply@github.com>2022-07-05 20:06:31 +0200
commit594246ea4727c9377b1c916934d9b257a1b5d0d0 (patch)
treed3a8538f729575262543bcdb16e5c2c01522796b /Ryujinx.Ava/Ui/ViewModels
parentd21b403886fdc00dac2493532b3e8ff53d5882c4 (diff)
UI - Avalonia Part 2 (#3351)
* add settings windows and children views * Expose hotkeys configuration on the UI * Remove double spacing from locale JSON * simplify button assigner * add cemuhook buttons and title to locale * move common button assigner to own class * cancel button assigner when window is closed * remove unused setting * address review. fix controller profile not loading default when switching devices * fix updater file name * Input cleanup (#37) * addressed review * add device type to controller device checks * change accessibility modifier of public classes to internal * Update Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs Co-authored-by: gdkchan <gab.dark.100@gmail.com> * Update de_DE.json * Update de_DE.json * Update tr_TR.json Translated newly added lines * Update it_IT.json * fix rebase * update avalonia * fix wrong key used for button text * Align settings window elements * Tabs to spaces * Update brazilian portuguese translation * Minor improvement on brazilian portuguese translation * fix turkish translation * remove unused text * change view related classes to public * unsubscribe from deferred event if dialog is closed * Load the default language before loading any other when switching languages * Make controller settings more compact * increase default width of settings window, reduce profile buttons width Co-authored-by: gdk <gab.dark.100@gmail.com> Co-authored-by: MutantAura <44103205+MutantAura@users.noreply.github.com> Co-authored-by: Niwu34 <67392333+Niwu34@users.noreply.github.com> Co-authored-by: aegiff <99728970+aegiff@users.noreply.github.com> Co-authored-by: Antonio Brugnolo <36473846+AntoSkate@users.noreply.github.com>
Diffstat (limited to 'Ryujinx.Ava/Ui/ViewModels')
-rw-r--r--Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs887
-rw-r--r--Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs40
-rw-r--r--Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs403
3 files changed, 1322 insertions, 8 deletions
diff --git a/Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs
new file mode 100644
index 00000000..8811a3c5
--- /dev/null
+++ b/Ryujinx.Ava/Ui/ViewModels/ControllerSettingsViewModel.cs
@@ -0,0 +1,887 @@
+using Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Svg.Skia;
+using Avalonia.Threading;
+using Avalonia.VisualTree;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.Input;
+using Ryujinx.Ava.Ui.Controls;
+using Ryujinx.Ava.Ui.Models;
+using Ryujinx.Ava.Ui.Windows;
+using Ryujinx.Common;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common.Configuration.Hid.Controller;
+using Ryujinx.Common.Configuration.Hid.Controller.Motion;
+using Ryujinx.Common.Configuration.Hid.Keyboard;
+using Ryujinx.Common.Logging;
+using Ryujinx.Common.Utilities;
+using Ryujinx.Input;
+using Ryujinx.Ui.Common.Configuration;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.Json;
+using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId;
+using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId;
+using Key = Ryujinx.Common.Configuration.Hid.Key;
+
+namespace Ryujinx.Ava.Ui.ViewModels
+{
+ public class ControllerSettingsViewModel : BaseModel, IDisposable
+ {
+ private const string Disabled = "disabled";
+ private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg";
+ private const string JoyConPairResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConPair.svg";
+ private const string JoyConLeftResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConLeft.svg";
+ private const string JoyConRightResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConRight.svg";
+ private const string KeyboardString = "keyboard";
+ private const string ControllerString = "controller";
+ private readonly MainWindow _mainWindow;
+
+ private PlayerIndex _playerId;
+ private int _controller;
+ private string _controllerImage;
+ private int _device;
+ private object _configuration;
+ private string _profileName;
+ private bool _isLoaded;
+ private readonly UserControl _owner;
+
+ public IGamepadDriver AvaloniaKeyboardDriver { get; }
+ public IGamepad SelectedGamepad { get; private set; }
+
+ public ObservableCollection<PlayerModel> PlayerIndexes { get; set; }
+ public ObservableCollection<(DeviceType Type, string Id, string Name)> Devices { get; set; }
+ internal ObservableCollection<ControllerModel> Controllers { get; set; }
+ public AvaloniaList<string> ProfilesList { get; set; }
+ public AvaloniaList<string> DeviceList { get; set; }
+
+ // XAML Flags
+ public bool ShowSettings => _device > 0;
+ public bool IsController => _device > 1;
+ public bool IsKeyboard => !IsController;
+ public bool IsRight { get; set; }
+ public bool IsLeft { get; set; }
+
+ public bool IsModified { get; set; }
+
+ public object Configuration
+ {
+ get => _configuration;
+ set
+ {
+ _configuration = value;
+
+ OnPropertyChanged();
+ }
+ }
+
+ public PlayerIndex PlayerId
+ {
+ get => _playerId;
+ set
+ {
+ if (IsModified)
+ {
+ return;
+ }
+
+ IsModified = false;
+ _playerId = value;
+
+ if (!Enum.IsDefined(typeof(PlayerIndex), _playerId))
+ {
+ _playerId = PlayerIndex.Player1;
+ }
+
+ LoadConfiguration();
+ LoadDevice();
+ LoadProfiles();
+
+ _isLoaded = true;
+
+ OnPropertyChanged();
+ }
+ }
+
+ public int Controller
+ {
+ get => _controller;
+ set
+ {
+ _controller = value;
+
+ if (_controller == -1)
+ {
+ _controller = 0;
+ }
+
+ if (Controllers.Count > 0 && value < Controllers.Count && _controller > -1)
+ {
+ ControllerType controller = Controllers[_controller].Type;
+
+ IsLeft = true;
+ IsRight = true;
+
+ switch (controller)
+ {
+ case ControllerType.Handheld:
+ ControllerImage = JoyConPairResource;
+ break;
+ case ControllerType.ProController:
+ ControllerImage = ProControllerResource;
+ break;
+ case ControllerType.JoyconPair:
+ ControllerImage = JoyConPairResource;
+ break;
+ case ControllerType.JoyconLeft:
+ ControllerImage = JoyConLeftResource;
+ IsRight = false;
+ break;
+ case ControllerType.JoyconRight:
+ ControllerImage = JoyConRightResource;
+ IsLeft = false;
+ break;
+ }
+
+ LoadInputDriver();
+ LoadProfiles();
+ }
+
+ OnPropertyChanged();
+ NotifyChanges();
+ }
+ }
+
+ public string ControllerImage
+ {
+ get => _controllerImage;
+ set
+ {
+ _controllerImage = value;
+
+ OnPropertyChanged();
+ OnPropertyChanged(nameof(Image));
+ }
+ }
+
+ public SvgImage Image
+ {
+ get
+ {
+ SvgImage image = new SvgImage();
+
+ if (!string.IsNullOrWhiteSpace(_controllerImage))
+ {
+ SvgSource source = new SvgSource();
+
+ source.Load(EmbeddedResources.GetStream(_controllerImage));
+
+ image.Source = source;
+ }
+
+ return image;
+ }
+ }
+
+ public string ProfileName
+ {
+ get => _profileName; set
+ {
+ _profileName = value;
+
+ OnPropertyChanged();
+ }
+ }
+
+ public int Device
+ {
+ get => _device;
+ set
+ {
+ _device = value < 0 ? 0 : value;
+
+ if (_device >= Devices.Count)
+ {
+ return;
+ }
+
+ var selected = Devices[_device].Type;
+
+ if (selected != DeviceType.None)
+ {
+ LoadControllers();
+
+ if (_isLoaded)
+ {
+ LoadConfiguration(LoadDefaultConfiguration());
+ }
+ }
+
+ OnPropertyChanged();
+ NotifyChanges();
+ }
+ }
+
+ public InputConfig Config { get; set; }
+
+ public ControllerSettingsViewModel(UserControl owner) : this()
+ {
+ _owner = owner;
+
+ if (Program.PreviewerDetached)
+ {
+ _mainWindow =
+ (MainWindow)((IClassicDesktopStyleApplicationLifetime)Avalonia.Application.Current
+ .ApplicationLifetime).MainWindow;
+
+ AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
+
+ _mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected;
+ _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected;
+ if (_mainWindow.AppHost != null)
+ {
+ _mainWindow.AppHost.NpadManager.BlockInputUpdates();
+ }
+
+ _isLoaded = false;
+
+ LoadDevices();
+
+ PlayerId = PlayerIndex.Player1;
+ }
+ }
+
+ public ControllerSettingsViewModel()
+ {
+ PlayerIndexes = new ObservableCollection<PlayerModel>();
+ Controllers = new ObservableCollection<ControllerModel>();
+ Devices = new ObservableCollection<(DeviceType Type, string Id, string Name)>();
+ ProfilesList = new AvaloniaList<string>();
+ DeviceList = new AvaloniaList<string>();
+
+ ControllerImage = ProControllerResource;
+
+ PlayerIndexes.Add(new(PlayerIndex.Player1, LocaleManager.Instance["ControllerSettingsPlayer1"]));
+ PlayerIndexes.Add(new(PlayerIndex.Player2, LocaleManager.Instance["ControllerSettingsPlayer2"]));
+ PlayerIndexes.Add(new(PlayerIndex.Player3, LocaleManager.Instance["ControllerSettingsPlayer3"]));
+ PlayerIndexes.Add(new(PlayerIndex.Player4, LocaleManager.Instance["ControllerSettingsPlayer4"]));
+ PlayerIndexes.Add(new(PlayerIndex.Player5, LocaleManager.Instance["ControllerSettingsPlayer5"]));
+ PlayerIndexes.Add(new(PlayerIndex.Player6, LocaleManager.Instance["ControllerSettingsPlayer6"]));
+ PlayerIndexes.Add(new(PlayerIndex.Player7, LocaleManager.Instance["ControllerSettingsPlayer7"]));
+ PlayerIndexes.Add(new(PlayerIndex.Player8, LocaleManager.Instance["ControllerSettingsPlayer8"]));
+ PlayerIndexes.Add(new(PlayerIndex.Handheld, LocaleManager.Instance["ControllerSettingsHandheld"]));
+ }
+
+ private void LoadConfiguration(InputConfig inputConfig = null)
+ {
+ Config = inputConfig ?? ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerId);
+
+ if (Config is StandardKeyboardInputConfig keyboardInputConfig)
+ {
+ Configuration = new InputConfiguration<Key, ConfigStickInputId>(keyboardInputConfig);
+ }
+
+ if (Config is StandardControllerInputConfig controllerInputConfig)
+ {
+ Configuration = new InputConfiguration<ConfigGamepadInputId, ConfigStickInputId>(controllerInputConfig);
+ }
+ }
+
+ public void LoadDevice()
+ {
+ if (Config == null || Config.Backend == InputBackendType.Invalid)
+ {
+ Device = 0;
+ }
+ else
+ {
+ var type = DeviceType.None;
+
+ if (Config is StandardKeyboardInputConfig)
+ {
+ type = DeviceType.Keyboard;
+ }
+
+ if (Config is StandardControllerInputConfig)
+ {
+ type = DeviceType.Controller;
+ }
+
+ var item = Devices.FirstOrDefault(x => x.Type == type && x.Id == Config.Id);
+ if (item != default)
+ {
+ Device = Devices.ToList().FindIndex(x => x.Id == item.Id);
+ }
+ else
+ {
+ Device = 0;
+ }
+ }
+ }
+
+ public async void ShowMotionConfig()
+ {
+ await MotionSettingsWindow.Show(this, _owner.GetVisualRoot() as StyleableWindow);
+ }
+
+ public async void ShowRumbleConfig()
+ {
+ await RumbleSettingsWindow.Show(this, _owner.GetVisualRoot() as StyleableWindow);
+ }
+
+ private void LoadInputDriver()
+ {
+ if (_device < 0)
+ {
+ return;
+ }
+
+ string id = GetCurrentGamepadId();
+ var type = Devices[Device].Type;
+
+ if (type == DeviceType.None)
+ {
+ return;
+ }
+ else if (type == DeviceType.Keyboard)
+ {
+ if (_mainWindow.InputManager.KeyboardDriver is AvaloniaKeyboardDriver)
+ {
+ // NOTE: To get input in this window, we need to bind a custom keyboard driver instead of using the InputManager one as the main window isn't focused...
+ SelectedGamepad = AvaloniaKeyboardDriver.GetGamepad(id);
+ }
+ else
+ {
+ SelectedGamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
+ }
+ }
+ else
+ {
+ SelectedGamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
+ }
+ }
+
+ private void HandleOnGamepadDisconnected(string id)
+ {
+ Dispatcher.UIThread.Post(() =>
+ {
+ LoadDevices();
+ });
+ }
+
+ private void HandleOnGamepadConnected(string id)
+ {
+ Dispatcher.UIThread.Post(() =>
+ {
+ LoadDevices();
+ });
+ }
+
+ private string GetCurrentGamepadId()
+ {
+ if (_device < 0)
+ {
+ return string.Empty;
+ }
+
+ var device = Devices[Device];
+
+ if (device.Type == DeviceType.None)
+ {
+ return null;
+ }
+
+ return device.Id.Split(" ")[0];
+ }
+
+ public void LoadControllers()
+ {
+ Controllers.Clear();
+
+ if (_playerId == PlayerIndex.Handheld)
+ {
+ Controllers.Add(new(ControllerType.Handheld, LocaleManager.Instance["ControllerSettingsControllerTypeHandheld"]));
+
+ Controller = 0;
+ }
+ else
+ {
+ Controllers.Add(new(ControllerType.ProController, LocaleManager.Instance["ControllerSettingsControllerTypeProController"]));
+ Controllers.Add(new(ControllerType.JoyconPair, LocaleManager.Instance["ControllerSettingsControllerTypeJoyConPair"]));
+ Controllers.Add(new(ControllerType.JoyconLeft, LocaleManager.Instance["ControllerSettingsControllerTypeJoyConLeft"]));
+ Controllers.Add(new(ControllerType.JoyconRight, LocaleManager.Instance["ControllerSettingsControllerTypeJoyConRight"]));
+
+ if (Config != null && Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType) != -1)
+ {
+ Controller = Controllers.ToList().FindIndex(x => x.Type == Config.ControllerType);
+ }
+ else
+ {
+ Controller = 0;
+ }
+ }
+ }
+
+ private static string GetShortGamepadName(string str)
+ {
+ const string Ellipsis = "...";
+ const int MaxSize = 50;
+
+ if (str.Length > MaxSize)
+ {
+ return str.Substring(0, MaxSize - Ellipsis.Length) + Ellipsis;
+ }
+
+ return str;
+ }
+
+ public void LoadDevices()
+ {
+ lock (Devices)
+ {
+ Devices.Clear();
+ DeviceList.Clear();
+ Devices.Add((DeviceType.None, Disabled, LocaleManager.Instance["ControllerSettingsDeviceDisabled"]));
+
+ foreach (string id in _mainWindow.InputManager.KeyboardDriver.GamepadsIds)
+ {
+ using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
+
+ if (gamepad != null)
+ {
+ Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)} ({id})"));
+ }
+ }
+
+ foreach (string id in _mainWindow.InputManager.GamepadDriver.GamepadsIds)
+ {
+ using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
+
+ if (gamepad != null)
+ {
+ Devices.Add((DeviceType.Controller, id, $"{GetShortGamepadName(gamepad.Name)} ({id})"));
+ }
+ }
+
+ DeviceList.AddRange(Devices.Select(x => x.Name));
+ Device = Math.Min(Device, DeviceList.Count);
+ }
+ }
+
+ private string GetProfileBasePath()
+ {
+ string path = AppDataManager.ProfilesDirPath;
+ var type = Devices[Device == -1 ? 0 : Device].Type;
+
+ if (type == DeviceType.Keyboard)
+ {
+ path = Path.Combine(path, KeyboardString);
+ }
+ else if (type == DeviceType.Controller)
+ {
+ path = Path.Combine(path, ControllerString);
+ }
+
+ return path;
+ }
+
+ private void LoadProfiles()
+ {
+ ProfilesList.Clear();
+
+ string basePath = GetProfileBasePath();
+
+ if (!Directory.Exists(basePath))
+ {
+ Directory.CreateDirectory(basePath);
+ }
+
+ ProfilesList.Add((LocaleManager.Instance["ControllerSettingsProfileDefault"]));
+
+ foreach (string profile in Directory.GetFiles(basePath, "*.json", SearchOption.AllDirectories))
+ {
+ ProfilesList.Add(Path.GetFileNameWithoutExtension(profile));
+ }
+
+ if (string.IsNullOrWhiteSpace(ProfileName))
+ {
+ ProfileName = LocaleManager.Instance["ControllerSettingsProfileDefault"];
+ }
+ }
+
+ public InputConfig LoadDefaultConfiguration()
+ {
+ var activeDevice = Devices.FirstOrDefault();
+
+ if (Devices.Count > 0 && Device < Devices.Count && Device >= 0)
+ {
+ activeDevice = Devices[Device];
+ }
+
+ InputConfig config;
+ if (activeDevice.Type == DeviceType.Keyboard)
+ {
+ string id = activeDevice.Id;
+
+ config = new StandardKeyboardInputConfig
+ {
+ Version = Ryujinx.Common.Configuration.Hid.InputConfig.CurrentVersion,
+ Backend = InputBackendType.WindowKeyboard,
+ Id = id,
+ ControllerType = ControllerType.ProController,
+ LeftJoycon = new LeftJoyconCommonConfig<Key>
+ {
+ DpadUp = Key.Up,
+ DpadDown = Key.Down,
+ DpadLeft = Key.Left,
+ DpadRight = Key.Right,
+ ButtonMinus = Key.Minus,
+ ButtonL = Key.E,
+ ButtonZl = Key.Q,
+ ButtonSl = Key.Unbound,
+ ButtonSr = Key.Unbound
+ },
+ LeftJoyconStick =
+ new JoyconConfigKeyboardStick<Key>
+ {
+ StickUp = Key.W,
+ StickDown = Key.S,
+ StickLeft = Key.A,
+ StickRight = Key.D,
+ StickButton = Key.F
+ },
+ RightJoycon = new RightJoyconCommonConfig<Key>
+ {
+ ButtonA = Key.Z,
+ ButtonB = Key.X,
+ ButtonX = Key.C,
+ ButtonY = Key.V,
+ ButtonPlus = Key.Plus,
+ ButtonR = Key.U,
+ ButtonZr = Key.O,
+ ButtonSl = Key.Unbound,
+ ButtonSr = Key.Unbound
+ },
+ RightJoyconStick = new JoyconConfigKeyboardStick<Key>
+ {
+ StickUp = Key.I,
+ StickDown = Key.K,
+ StickLeft = Key.J,
+ StickRight = Key.L,
+ StickButton = Key.H
+ }
+ };
+ }
+ else if (activeDevice.Type == DeviceType.Controller)
+ {
+ bool isNintendoStyle = Devices.ToList().Find(x => x.Id == activeDevice.Id).Name.Contains("Nintendo");
+
+ string id = activeDevice.Id.Split(" ")[0];
+
+ config = new StandardControllerInputConfig
+ {
+ Version = Ryujinx.Common.Configuration.Hid.InputConfig.CurrentVersion,
+ Backend = InputBackendType.GamepadSDL2,
+ Id = id,
+ ControllerType = ControllerType.ProController,
+ DeadzoneLeft = 0.1f,
+ DeadzoneRight = 0.1f,
+ RangeLeft = 1.0f,
+ RangeRight = 1.0f,
+ TriggerThreshold = 0.5f,
+ LeftJoycon = new LeftJoyconCommonConfig<ConfigGamepadInputId>
+ {
+ DpadUp = ConfigGamepadInputId.DpadUp,
+ DpadDown = ConfigGamepadInputId.DpadDown,
+ DpadLeft = ConfigGamepadInputId.DpadLeft,
+ DpadRight = ConfigGamepadInputId.DpadRight,
+ ButtonMinus = ConfigGamepadInputId.Minus,
+ ButtonL = ConfigGamepadInputId.LeftShoulder,
+ ButtonZl = ConfigGamepadInputId.LeftTrigger,
+ ButtonSl = ConfigGamepadInputId.Unbound,
+ ButtonSr = ConfigGamepadInputId.Unbound
+ },
+ LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
+ {
+ Joystick = ConfigStickInputId.Left,
+ StickButton = ConfigGamepadInputId.LeftStick,
+ InvertStickX = false,
+ InvertStickY = false
+ },
+ RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
+ {
+ ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B,
+ ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A,
+ ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y,
+ ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X,
+ ButtonPlus = ConfigGamepadInputId.Plus,
+ ButtonR = ConfigGamepadInputId.RightShoulder,
+ ButtonZr = ConfigGamepadInputId.RightTrigger,
+ ButtonSl = ConfigGamepadInputId.Unbound,
+ ButtonSr = ConfigGamepadInputId.Unbound
+ },
+ RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
+ {
+ Joystick = ConfigStickInputId.Right,
+ StickButton = ConfigGamepadInputId.RightStick,
+ InvertStickX = false,
+ InvertStickY = false
+ },
+ Motion = new StandardMotionConfigController
+ {
+ MotionBackend = MotionInputBackendType.GamepadDriver,
+ EnableMotion = true,
+ Sensitivity = 100,
+ GyroDeadzone = 1
+ },
+ Rumble = new RumbleConfigController
+ {
+ StrongRumble = 1f,
+ WeakRumble = 1f,
+ EnableRumble = false
+ }
+ };
+ }
+ else
+ {
+ config = new InputConfig();
+ }
+
+ config.PlayerIndex = _playerId;
+
+ return config;
+ }
+
+ public void LoadProfile()
+ {
+ if (Device == 0)
+ {
+ return;
+ }
+
+ InputConfig config = null;
+
+ if (string.IsNullOrWhiteSpace(ProfileName))
+ {
+ return;
+ }
+
+ if (ProfileName == LocaleManager.Instance["ControllerSettingsProfileDefault"])
+ {
+ config = LoadDefaultConfiguration();
+ }
+ else
+ {
+ string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
+
+ if (!File.Exists(path))
+ {
+ var index = ProfilesList.IndexOf(ProfileName);
+ if (index != -1)
+ {
+ ProfilesList.RemoveAt(index);
+ }
+ return;
+ }
+
+ try
+ {
+ using (Stream stream = File.OpenRead(path))
+ {
+ config = JsonHelper.Deserialize<InputConfig>(stream);
+ }
+ }
+ catch (JsonException) { }
+ catch (InvalidOperationException)
+ {
+ ContentDialogHelper.CreateErrorDialog(_owner.GetVisualRoot() as StyleableWindow,
+ String.Format(LocaleManager.Instance["DialogProfileInvalidProfileErrorMessage"], ProfileName));
+ Logger.Error?.Print(LogClass.Configuration, $"Profile {ProfileName} is incompatible with the current input configuration system.");
+
+ return;
+ }
+ }
+
+ if (config != null)
+ {
+ _isLoaded = false;
+
+ LoadConfiguration(config);
+
+ LoadDevice();
+
+ _isLoaded = true;
+
+ NotifyChanges();
+ }
+ }
+
+ public async void SaveProfile()
+ {
+ if (Device == 0)
+ {
+ return;
+ }
+
+ if (Configuration == null)
+ {
+ return;
+ }
+
+ if (ProfileName == LocaleManager.Instance["ControllerSettingsProfileDefault"])
+ {
+ ContentDialogHelper.CreateErrorDialog(_owner.GetVisualRoot() as StyleableWindow, LocaleManager.Instance["DialogProfileDefaultProfileOverwriteErrorMessage"]);
+
+ return;
+ }
+ else
+ {
+ bool validFileName = ProfileName.IndexOfAny(Path.GetInvalidFileNameChars()) == -1;
+
+ if (validFileName)
+ {
+ string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
+
+ InputConfig config = null;
+
+ if (IsKeyboard)
+ {
+ config = (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig();
+ }
+ else if (IsController)
+ {
+ config = (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig();
+ }
+
+ config.ControllerType = Controllers[_controller].Type;
+
+ string jsonString = JsonHelper.Serialize(config, true);
+
+ await File.WriteAllTextAsync(path, jsonString);
+
+ LoadProfiles();
+ }
+ else
+ {
+ ContentDialogHelper.CreateErrorDialog(_owner.GetVisualRoot() as StyleableWindow, LocaleManager.Instance["DialogProfileInvalidProfileNameErrorMessage"]);
+ }
+ }
+ }
+
+ public async void RemoveProfile()
+ {
+ if (Device == 0 || ProfileName == LocaleManager.Instance["ControllerSettingsProfileDefault"] || ProfilesList.IndexOf(ProfileName) == -1)
+ {
+ return;
+ }
+
+ UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
+ _owner.GetVisualRoot() as StyleableWindow,
+ LocaleManager.Instance["DialogProfileDeleteProfileTitle"],
+ LocaleManager.Instance["DialogProfileDeleteProfileMessage"],
+ LocaleManager.Instance["InputDialogYes"],
+ LocaleManager.Instance["InputDialogNo"],
+ LocaleManager.Instance["RyujinxConfirm"]);
+
+ if (result == UserResult.Yes)
+ {
+ string path = Path.Combine(GetProfileBasePath(), ProfileName + ".json");
+
+ if (File.Exists(path))
+ {
+ File.Delete(path);
+ }
+
+ LoadProfiles();
+ }
+ }
+
+ public void Save()
+ {
+ IsModified = false;
+
+ List<InputConfig> newConfig = new();
+
+ newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
+
+ newConfig.Remove(newConfig.Find(x => x == null));
+
+ if (Device == 0)
+ {
+ newConfig.Remove(newConfig.Find(x => x.PlayerIndex == this.PlayerId));
+ }
+ else
+ {
+ var device = Devices[Device];
+
+ if (device.Type == DeviceType.Keyboard)
+ {
+ var inputConfig = Configuration as InputConfiguration<Key, ConfigStickInputId>;
+ inputConfig.Id = device.Id;
+ }
+ else
+ {
+ var inputConfig = Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>;
+ inputConfig.Id = device.Id.Split(" ")[0];
+ }
+
+ var config = !IsController
+ ? (Configuration as InputConfiguration<Key, ConfigStickInputId>).GetConfig()
+ : (Configuration as InputConfiguration<GamepadInputId, ConfigStickInputId>).GetConfig();
+ config.ControllerType = Controllers[_controller].Type;
+ config.PlayerIndex = _playerId;
+
+ int i = newConfig.FindIndex(x => x.PlayerIndex == PlayerId);
+ if (i == -1)
+ {
+ newConfig.Add(config);
+ }
+ else
+ {
+ newConfig[i] = config;
+ }
+ }
+
+ _mainWindow.AppHost?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse);
+
+ // Atomically replace and signal input change.
+ // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
+ ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
+
+ ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
+ }
+
+ public void NotifyChange(string property)
+ {
+ OnPropertyChanged(property);
+ }
+
+ public void NotifyChanges()
+ {
+ OnPropertyChanged(nameof(Configuration));
+ OnPropertyChanged(nameof(IsController));
+ OnPropertyChanged(nameof(ShowSettings));
+ OnPropertyChanged(nameof(IsKeyboard));
+ OnPropertyChanged(nameof(IsRight));
+ OnPropertyChanged(nameof(IsLeft));
+ }
+
+ public void Dispose()
+ {
+ _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
+ _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
+
+ _mainWindow.AppHost?.NpadManager.UnblockInputUpdates();
+
+ SelectedGamepad?.Dispose();
+
+ AvaloniaKeyboardDriver.Dispose();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs
index dcb015bb..dbc19f75 100644
--- a/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs
+++ b/Ryujinx.Ava/Ui/ViewModels/MainWindowViewModel.cs
@@ -11,6 +11,7 @@ using LibHac.FsSystem;
using LibHac.Ncm;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.Input;
using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common;
@@ -35,7 +36,7 @@ using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
namespace Ryujinx.Ava.Ui.ViewModels
{
- public class MainWindowViewModel : BaseModel
+ internal class MainWindowViewModel : BaseModel
{
private readonly MainWindow _owner;
private ObservableCollection<ApplicationData> _applications;
@@ -86,9 +87,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
if (Program.PreviewerDetached)
{
- ShowUiKey = KeyGesture.Parse(ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi.ToString());
- ScreenshotKey = KeyGesture.Parse(ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot.ToString());
- PauseKey = KeyGesture.Parse(ConfigurationState.Instance.Hid.Hotkeys.Value.Pause.ToString());
+ LoadConfigurableHotKeys();
Volume = ConfigurationState.Instance.System.AudioVolume;
}
@@ -836,6 +835,22 @@ namespace Ryujinx.Ava.Ui.ViewModels
}
}
+ public void LoadConfigurableHotKeys()
+ {
+ if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey))
+ {
+ ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None);
+ }
+ if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
+ {
+ ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None);
+ }
+ if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
+ {
+ PauseKey = new KeyGesture(pauseKey, KeyModifiers.None);
+ }
+ }
+
public void TakeScreenshot()
{
_owner.AppHost.ScreenshotRequested = true;
@@ -930,10 +945,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
}
}
- public void OpenSettings()
+ public async void OpenSettings()
{
- // TODO : Implement Settings window
- ContentDialogHelper.ShowNotAvailableMessage(_owner);
+ _owner.SettingsWindow = new(_owner.VirtualFileSystem, _owner.ContentManager);
+
+ await _owner.SettingsWindow.ShowDialog(_owner);
+ LoadConfigurableHotKeys();
}
public void ManageProfiles()
@@ -951,6 +968,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
public void ChangeLanguage(object obj)
{
+ LocaleManager.Instance.LoadDefaultLanguage();
LocaleManager.Instance.LoadLanguage((string)obj);
}
@@ -1350,7 +1368,13 @@ namespace Ryujinx.Ava.Ui.ViewModels
dialogMessage += LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallConfirmMessage"];
- UserResult result = await ContentDialogHelper.CreateConfirmationDialog(_owner, dialogTitle, dialogMessage, LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
+ UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
+ _owner,
+ dialogTitle,
+ dialogMessage,
+ LocaleManager.Instance["InputDialogYes"],
+ LocaleManager.Instance["InputDialogNo"],
+ LocaleManager.Instance["RyujinxConfirm"]);
UpdateWaitWindow waitingDialog = ContentDialogHelper.CreateWaitingDialog(dialogTitle, LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallWaitMessage"]);
diff --git a/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
new file mode 100644
index 00000000..ef7a53e6
--- /dev/null
+++ b/Ryujinx.Ava/Ui/ViewModels/SettingsViewModel.cs
@@ -0,0 +1,403 @@
+using Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Threading;
+using LibHac.Tools.FsSystem;
+using Ryujinx.Audio.Backends.OpenAL;
+using Ryujinx.Audio.Backends.SDL2;
+using Ryujinx.Audio.Backends.SoundIo;
+using Ryujinx.Ava.Common.Locale;
+using Ryujinx.Ava.Input;
+using Ryujinx.Ava.Ui.Controls;
+using Ryujinx.Ava.Ui.Windows;
+using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common.GraphicsDriver;
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.FileSystem;
+using Ryujinx.HLE.HOS.Services.Time.TimeZone;
+using Ryujinx.Input;
+using Ryujinx.Ui.Common.Configuration;
+using Ryujinx.Ui.Common.Configuration.System;
+using System;
+using System.Collections.Generic;
+using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
+
+namespace Ryujinx.Ava.Ui.ViewModels
+{
+ internal class SettingsViewModel : BaseModel
+ {
+ private readonly VirtualFileSystem _virtualFileSystem;
+ private readonly ContentManager _contentManager;
+ private readonly StyleableWindow _owner;
+ private TimeZoneContentManager _timeZoneContentManager;
+
+ private readonly List<string> _validTzRegions;
+
+ private float _customResolutionScale;
+ private int _resolutionScale;
+ private int _graphicsBackendMultithreadingIndex;
+ private float _previousVolumeLevel;
+ private float _volume;
+
+ public int ResolutionScale
+ {
+ get => _resolutionScale;
+ set
+ {
+ _resolutionScale = value;
+
+ OnPropertyChanged(nameof(CustomResolutionScale));
+ OnPropertyChanged(nameof(IsCustomResolutionScaleActive));
+ }
+ }
+ public int GraphicsBackendMultithreadingIndex
+ {
+ get => _graphicsBackendMultithreadingIndex;
+ set
+ {
+ _graphicsBackendMultithreadingIndex = value;
+
+ if (_owner != null)
+ {
+ if (_graphicsBackendMultithreadingIndex != (int)ConfigurationState.Instance.Graphics.BackendThreading.Value)
+ {
+ Dispatcher.UIThread.Post(async () =>
+ {
+ await ContentDialogHelper.CreateInfoDialog(_owner,
+ LocaleManager.Instance["DialogSettingsBackendThreadingWarningMessage"],
+ "",
+ "",
+ LocaleManager.Instance["InputDialogOk"],
+ LocaleManager.Instance["DialogSettingsBackendThreadingWarningTitle"]);
+ });
+ }
+ }
+
+ OnPropertyChanged();
+ }
+ }
+
+ public float CustomResolutionScale
+ {
+ get => _customResolutionScale;
+ set
+ {
+ _customResolutionScale = MathF.Round(value, 1);
+
+ OnPropertyChanged();
+ }
+ }
+
+ public bool EnableDiscordIntegration { get; set; }
+ public bool CheckUpdatesOnStart { get; set; }
+ public bool ShowConfirmExit { get; set; }
+ public bool HideCursorOnIdle { get; set; }
+ public bool EnableDockedMode { get; set; }
+ public bool EnableKeyboard { get; set; }
+ public bool EnableMouse { get; set; }
+ public bool EnableVsync { get; set; }
+ public bool EnablePptc { get; set; }
+ public bool EnableInternetAccess { get; set; }
+ public bool EnableFsIntegrityChecks { get; set; }
+ public bool IgnoreMissingServices { get; set; }
+ public bool ExpandDramSize { get; set; }
+ public bool EnableShaderCache { get; set; }
+ public bool EnableFileLog { get; set; }
+ public bool EnableStub { get; set; }
+ public bool EnableInfo { get; set; }
+ public bool EnableWarn { get; set; }
+ public bool EnableError { get; set; }
+ public bool EnableTrace { get; set; }
+ public bool EnableGuest { get; set; }
+ public bool EnableFsAccessLog { get; set; }
+ public bool EnableDebug { get; set; }
+ public bool IsOpenAlEnabled { get; set; }
+ public bool IsSoundIoEnabled { get; set; }
+ public bool IsSDL2Enabled { get; set; }
+ public bool EnableCustomTheme { get; set; }
+ public bool IsCustomResolutionScaleActive => _resolutionScale == 0;
+
+ public string TimeZone { get; set; }
+ public string ShaderDumpPath { get; set; }
+ public string CustomThemePath { get; set; }
+
+ public int Language { get; set; }
+ public int Region { get; set; }
+ public int FsGlobalAccessLogMode { get; set; }
+ public int AudioBackend { get; set; }
+ public int MaxAnisotropy { get; set; }
+ public int AspectRatio { get; set; }
+ public int OpenglDebugLevel { get; set; }
+ public int MemoryMode { get; set; }
+ public int BaseStyleIndex { get; set; }
+
+ public float Volume
+ {
+ get => _volume;
+ set
+ {
+ _volume = value;
+
+ ConfigurationState.Instance.System.AudioVolume.Value = (float)(_volume / 100);
+
+ OnPropertyChanged();
+ }
+ }
+
+ public DateTimeOffset DateOffset { get; set; }
+ public TimeSpan TimeOffset { get; set; }
+ public AvaloniaList<TimeZone> TimeZones { get; set; }
+
+ public AvaloniaList<string> GameDirectories { get; set; }
+
+ private KeyboardHotkeys _keyboardHotkeys;
+
+ public KeyboardHotkeys KeyboardHotkeys
+ {
+ get => _keyboardHotkeys;
+ set
+ {
+ _keyboardHotkeys = value;
+
+ OnPropertyChanged();
+ }
+ }
+
+ public IGamepadDriver AvaloniaKeyboardDriver { get; }
+
+ public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager, StyleableWindow owner) : this()
+ {
+ _virtualFileSystem = virtualFileSystem;
+ _contentManager = contentManager;
+ _owner = owner;
+ if (Program.PreviewerDetached)
+ {
+ LoadTimeZones();
+ AvaloniaKeyboardDriver = new AvaloniaKeyboardDriver(owner);
+ }
+ }
+
+ public SettingsViewModel()
+ {
+ GameDirectories = new AvaloniaList<string>();
+ TimeZones = new AvaloniaList<TimeZone>();
+ _validTzRegions = new List<string>();
+
+ CheckSoundBackends();
+
+ if (Program.PreviewerDetached)
+ {
+ LoadCurrentConfiguration();
+ }
+ }
+
+ public void CheckSoundBackends()
+ {
+ IsOpenAlEnabled = OpenALHardwareDeviceDriver.IsSupported;
+ IsSoundIoEnabled = SoundIoHardwareDeviceDriver.IsSupported;
+ IsSDL2Enabled = SDL2HardwareDeviceDriver.IsSupported;
+ }
+
+ public void LoadTimeZones()
+ {
+ _timeZoneContentManager = new TimeZoneContentManager();
+
+ _timeZoneContentManager.InitializeInstance(_virtualFileSystem, _contentManager, IntegrityCheckLevel.None);
+
+ foreach ((int offset, string location, string abbr) in _timeZoneContentManager.ParseTzOffsets())
+ {
+ int hours = Math.DivRem(offset, 3600, out int seconds);
+ int minutes = Math.Abs(seconds) / 60;
+
+ string abbr2 = abbr.StartsWith('+') || abbr.StartsWith('-') ? string.Empty : abbr;
+
+ TimeZones.Add(new TimeZone($"UTC{hours:+0#;-0#;+00}:{minutes:D2}", location, abbr2));
+
+ _validTzRegions.Add(location);
+ }
+ }
+
+ public void ValidateAndSetTimeZone(string location)
+ {
+ if (_validTzRegions.Contains(location))
+ {
+ TimeZone = location;
+
+ OnPropertyChanged(nameof(TimeZone));
+ }
+ }
+
+ public async void BrowseTheme()
+ {
+ var dialog = new OpenFileDialog()
+ {
+ Title = LocaleManager.Instance["SettingsSelectThemeFileDialogTitle"],
+ AllowMultiple = false
+ };
+
+ dialog.Filters.Add(new FileDialogFilter() { Extensions = { "xaml" }, Name = LocaleManager.Instance["SettingsXamlThemeFile"] });
+
+ var file = await dialog.ShowAsync(_owner);
+
+ if (file != null && file.Length > 0)
+ {
+ CustomThemePath = file[0];
+ OnPropertyChanged(nameof(CustomThemePath));
+ }
+ }
+
+ public void LoadCurrentConfiguration()
+ {
+ ConfigurationState config = ConfigurationState.Instance;
+
+ GameDirectories.Clear();
+ GameDirectories.AddRange(config.Ui.GameDirs.Value);
+
+ EnableDiscordIntegration = config.EnableDiscordIntegration;
+ CheckUpdatesOnStart = config.CheckUpdatesOnStart;
+ ShowConfirmExit = config.ShowConfirmExit;
+ HideCursorOnIdle = config.HideCursorOnIdle;
+ EnableDockedMode = config.System.EnableDockedMode;
+ EnableKeyboard = config.Hid.EnableKeyboard;
+ EnableMouse = config.Hid.EnableMouse;
+ EnableVsync = config.Graphics.EnableVsync;
+ EnablePptc = config.System.EnablePtc;
+ EnableInternetAccess = config.System.EnableInternetAccess;
+ EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
+ IgnoreMissingServices = config.System.IgnoreMissingServices;
+ ExpandDramSize = config.System.ExpandRam;
+ EnableShaderCache = config.Graphics.EnableShaderCache;
+ EnableFileLog = config.Logger.EnableFileLog;
+ EnableStub = config.Logger.EnableStub;
+ EnableInfo = config.Logger.EnableInfo;
+ EnableWarn = config.Logger.EnableWarn;
+ EnableError = config.Logger.EnableError;
+ EnableTrace = config.Logger.EnableTrace;
+ EnableGuest = config.Logger.EnableGuest;
+ EnableDebug = config.Logger.EnableDebug;
+ EnableFsAccessLog = config.Logger.EnableFsAccessLog;
+ EnableCustomTheme = config.Ui.EnableCustomTheme;
+ Volume = config.System.AudioVolume * 100;
+
+ GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
+
+ OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
+
+ TimeZone = config.System.TimeZone;
+ ShaderDumpPath = config.Graphics.ShadersDumpPath;
+ CustomThemePath = config.Ui.CustomThemePath;
+ BaseStyleIndex = config.Ui.BaseStyle == "Light" ? 0 : 1;
+
+ Language = (int)config.System.Language.Value;
+ Region = (int)config.System.Region.Value;
+ FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
+ AudioBackend = (int)config.System.AudioBackend.Value;
+ MemoryMode = (int)config.System.MemoryManagerMode.Value;
+
+ float anisotropy = config.Graphics.MaxAnisotropy;
+
+ MaxAnisotropy = anisotropy == -1 ? 0 : (int)(MathF.Log2(anisotropy));
+ AspectRatio = (int)config.Graphics.AspectRatio.Value;
+
+ int resolution = config.Graphics.ResScale;
+
+ ResolutionScale = resolution == -1 ? 0 : resolution;
+ CustomResolutionScale = config.Graphics.ResScaleCustom;
+
+ DateTime dateTimeOffset = DateTime.Now.AddSeconds(config.System.SystemTimeOffset);
+
+ DateOffset = dateTimeOffset.Date;
+ TimeOffset = dateTimeOffset.TimeOfDay;
+
+ KeyboardHotkeys = config.Hid.Hotkeys.Value;
+
+ _previousVolumeLevel = Volume;
+ }
+
+ public void SaveSettings()
+ {
+ List<string> gameDirs = new List<string>(GameDirectories);
+
+ ConfigurationState config = ConfigurationState.Instance;
+
+ if (_validTzRegions.Contains(TimeZone))
+ {
+ config.System.TimeZone.Value = TimeZone;
+ }
+
+ config.Logger.EnableError.Value = EnableError;
+ config.Logger.EnableTrace.Value = EnableTrace;
+ config.Logger.EnableWarn.Value = EnableWarn;
+ config.Logger.EnableInfo.Value = EnableInfo;
+ config.Logger.EnableStub.Value = EnableStub;
+ config.Logger.EnableDebug.Value = EnableDebug;
+ config.Logger.EnableGuest.Value = EnableGuest;
+ config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
+ config.Logger.EnableFileLog.Value = EnableFileLog;
+ config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
+ config.System.EnableDockedMode.Value = EnableDockedMode;
+ config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
+ config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
+ config.ShowConfirmExit.Value = ShowConfirmExit;
+ config.HideCursorOnIdle.Value = HideCursorOnIdle;
+ config.Graphics.EnableVsync.Value = EnableVsync;
+ config.Graphics.EnableShaderCache.Value = EnableShaderCache;
+ config.System.EnablePtc.Value = EnablePptc;
+ config.System.EnableInternetAccess.Value = EnableInternetAccess;
+ config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
+ config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
+ config.System.ExpandRam.Value = ExpandDramSize;
+ config.Hid.EnableKeyboard.Value = EnableKeyboard;
+ config.Hid.EnableMouse.Value = EnableMouse;
+ config.Ui.CustomThemePath.Value = CustomThemePath;
+ config.Ui.EnableCustomTheme.Value = EnableCustomTheme;
+ config.Ui.BaseStyle.Value = BaseStyleIndex == 0 ? "Light" : "Dark";
+ config.System.Language.Value = (Language)Language;
+ config.System.Region.Value = (Region)Region;
+
+ if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex)
+ {
+ DriverUtilities.ToggleOGLThreading(GraphicsBackendMultithreadingIndex == (int)BackendThreading.Off);
+ }
+
+ config.Graphics.BackendThreading.Value = (BackendThreading)GraphicsBackendMultithreadingIndex;
+
+ TimeSpan systemTimeOffset = DateOffset - DateTime.Now;
+
+ config.System.SystemTimeOffset.Value = systemTimeOffset.Seconds;
+ config.Graphics.ShadersDumpPath.Value = ShaderDumpPath;
+ config.Ui.GameDirs.Value = gameDirs;
+ config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
+ config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode;
+
+ float anisotropy = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
+
+ config.Graphics.MaxAnisotropy.Value = anisotropy;
+ config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio;
+ config.Graphics.ResScale.Value = ResolutionScale == 0 ? -1 : ResolutionScale;
+ config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
+ config.System.AudioVolume.Value = Volume / 100;
+
+ AudioBackend audioBackend = (AudioBackend)AudioBackend;
+ if (audioBackend != config.System.AudioBackend.Value)
+ {
+ config.System.AudioBackend.Value = audioBackend;
+
+ Logger.Info?.Print(LogClass.Application, $"AudioBackend toggled to: {audioBackend}");
+ }
+
+ config.Hid.Hotkeys.Value = KeyboardHotkeys;
+
+ config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
+
+ MainWindow.UpdateGraphicsConfig();
+
+ _previousVolumeLevel = Volume;
+ }
+
+ public void RevertIfNotSaved()
+ {
+ Program.ReloadConfig();
+ }
+ }
+} \ No newline at end of file