Rewrite the configuration system (#831)

The configuration system was quite fragile and too dependent on everything, this fix #812 .

The changes:

    The file configuration is now entirely independent from the internal configuration state.
    The file configuration is versioned (current version is 1).
    Every configuration elements are now reactive properties that the emulator can register on to handle initialization and configuration changes.
    The configuration system is now in Ryujinx.Common to be accessible on every projects.
    Discord integration is now independent from the UI and can be reloaded.
    The primary controller is now configurable at runtime (NOTE: the UI currently doesn't have any options to configure real controller).
    The logger is entirely reloadable.
    You can now hotplug your controller when the emulator is running.
    The logger now takes name for every LogTarget to make them removable at runtime.
    The logger now always add the default "console" target to avoid loosing early init logs.
    The configuration system now generates a default file configuration if it's missing or too new.
    General system stability improvements to enhance the user's experience
This commit is contained in:
Thog 2019-12-21 20:52:31 +01:00 committed by GitHub
parent e5858e2c7d
commit 01a4c80ed5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 1625 additions and 707 deletions

View file

@ -1,30 +1,22 @@
using JsonPrettyPrinterPlus; using JsonPrettyPrinterPlus;
using LibHac.FsSystem;
using OpenTK.Input;
using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.HOS.Services;
using Ryujinx.HLE.Input;
using Ryujinx.Ui;
using Ryujinx.Ui.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Threading.Tasks;
using Utf8Json; using Utf8Json;
using Utf8Json.Resolvers; using Utf8Json.Resolvers;
using Ryujinx.Configuration.System;
using Ryujinx.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.UI.Input;
using Ryujinx.Configuration.Ui;
namespace Ryujinx namespace Ryujinx.Configuration
{ {
public class Configuration public class ConfigurationFileFormat
{ {
/// <summary> public int Version { get; set; }
/// The default configuration instance
/// </summary>
public static Configuration Instance { get; private set; }
/// <summary> /// <summary>
/// Dumps shaders in this local directory /// Dumps shaders in this local directory
@ -79,7 +71,7 @@ namespace Ryujinx
/// <summary> /// <summary>
/// Change System Language /// Change System Language
/// </summary> /// </summary>
public SystemLanguage SystemLanguage { get; set; } public Language SystemLanguage { get; set; }
/// <summary> /// <summary>
/// Enables or disables Docked Mode /// Enables or disables Docked Mode
@ -119,7 +111,7 @@ namespace Ryujinx
/// <summary> /// <summary>
/// The primary controller's type /// The primary controller's type
/// </summary> /// </summary>
public ControllerStatus ControllerType { get; set; } public ControllerType ControllerType { get; set; }
/// <summary> /// <summary>
/// Used to toggle columns in the GUI /// Used to toggle columns in the GUI
@ -154,13 +146,13 @@ namespace Ryujinx
/// <summary> /// <summary>
/// Controller control bindings /// Controller control bindings
/// </summary> /// </summary>
public Ui.Input.NpadController JoystickControls { get; private set; } public NpadController JoystickControls { get; set; }
/// <summary> /// <summary>
/// Loads a configuration file from disk /// Loads a configuration file from disk
/// </summary> /// </summary>
/// <param name="path">The path to the JSON configuration file</param> /// <param name="path">The path to the JSON configuration file</param>
public static void Load(string path) public static ConfigurationFileFormat Load(string path)
{ {
var resolver = CompositeResolver.Create( var resolver = CompositeResolver.Create(
new[] { new ConfigurationEnumFormatter<Key>() }, new[] { new ConfigurationEnumFormatter<Key>() },
@ -169,24 +161,7 @@ namespace Ryujinx
using (Stream stream = File.OpenRead(path)) using (Stream stream = File.OpenRead(path))
{ {
Instance = JsonSerializer.Deserialize<Configuration>(stream, resolver); return JsonSerializer.Deserialize<ConfigurationFileFormat>(stream, resolver);
}
}
/// <summary>
/// Loads a configuration file asynchronously from disk
/// </summary>
/// <param name="path">The path to the JSON configuration file</param>
public static async Task LoadAsync(string path)
{
IJsonFormatterResolver resolver = CompositeResolver.Create(
new[] { new ConfigurationEnumFormatter<Key>() },
new[] { StandardResolver.AllowPrivateSnakeCase }
);
using (Stream stream = File.OpenRead(path))
{
Instance = await JsonSerializer.DeserializeAsync<Configuration>(stream, resolver);
} }
} }
@ -194,108 +169,17 @@ namespace Ryujinx
/// Save a configuration file to disk /// Save a configuration file to disk
/// </summary> /// </summary>
/// <param name="path">The path to the JSON configuration file</param> /// <param name="path">The path to the JSON configuration file</param>
public static void SaveConfig(Configuration config, string path) public void SaveConfig(string path)
{ {
IJsonFormatterResolver resolver = CompositeResolver.Create( IJsonFormatterResolver resolver = CompositeResolver.Create(
new[] { new ConfigurationEnumFormatter<Key>() }, new[] { new ConfigurationEnumFormatter<Key>() },
new[] { StandardResolver.AllowPrivateSnakeCase } new[] { StandardResolver.AllowPrivateSnakeCase }
); );
byte[] data = JsonSerializer.Serialize(config, resolver); byte[] data = JsonSerializer.Serialize(this, resolver);
File.WriteAllText(path, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson()); File.WriteAllText(path, Encoding.UTF8.GetString(data, 0, data.Length).PrettyPrintJson());
} }
/// <summary>
/// Configures a <see cref="Switch"/> instance
/// </summary>
/// <param name="device">The instance to configure</param>
public static void InitialConfigure(Switch device)
{
if (Instance == null)
{
throw new InvalidOperationException("Configuration has not been loaded yet.");
}
SwitchSettings.ConfigureSettings(Instance);
Logger.AddTarget(new AsyncLogTargetWrapper(
new ConsoleLogTarget(),
1000,
AsyncLogTargetOverflowAction.Block
));
if (Instance.EnableFileLog)
{
Logger.AddTarget(new AsyncLogTargetWrapper(
new FileLogTarget(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.log")),
1000,
AsyncLogTargetOverflowAction.Block
));
}
Configure(device, Instance);
}
public static void Configure(Switch device, Configuration SwitchConfig)
{
GraphicsConfig.ShadersDumpPath = SwitchConfig.GraphicsShadersDumpPath;
Logger.SetEnable(LogLevel.Debug, SwitchConfig.LoggingEnableDebug );
Logger.SetEnable(LogLevel.Stub, SwitchConfig.LoggingEnableStub );
Logger.SetEnable(LogLevel.Info, SwitchConfig.LoggingEnableInfo );
Logger.SetEnable(LogLevel.Warning, SwitchConfig.LoggingEnableWarn );
Logger.SetEnable(LogLevel.Error, SwitchConfig.LoggingEnableError );
Logger.SetEnable(LogLevel.Guest, SwitchConfig.LoggingEnableGuest );
Logger.SetEnable(LogLevel.AccessLog, SwitchConfig.LoggingEnableFsAccessLog);
if (SwitchConfig.LoggingFilteredClasses.Length > 0)
{
foreach (var logClass in EnumExtensions.GetValues<LogClass>())
{
Logger.SetEnable(logClass, false);
}
foreach (var logClass in SwitchConfig.LoggingFilteredClasses)
{
Logger.SetEnable(logClass, true);
}
}
MainWindow.DiscordIntegrationEnabled = SwitchConfig.EnableDiscordIntegration;
device.EnableDeviceVsync = SwitchConfig.EnableVsync;
device.System.State.DockedMode = SwitchConfig.DockedMode;
device.System.State.SetLanguage(SwitchConfig.SystemLanguage);
if (SwitchConfig.EnableMulticoreScheduling)
{
device.System.EnableMultiCoreScheduling();
}
device.System.FsIntegrityCheckLevel = SwitchConfig.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
device.System.GlobalAccessLogMode = SwitchConfig.FsGlobalAccessLogMode;
ServiceConfiguration.IgnoreMissingServices = SwitchConfig.IgnoreMissingServices;
}
public static void ConfigureHid(Switch device, Configuration SwitchConfig)
{
if (SwitchConfig.JoystickControls.Enabled)
{
if (!Joystick.GetState(SwitchConfig.JoystickControls.Index).IsConnected)
{
SwitchConfig.JoystickControls.SetEnabled(false);
}
}
device.Hid.InitializePrimaryController(SwitchConfig.ControllerType);
device.Hid.InitializeKeyboard();
}
private class ConfigurationEnumFormatter<T> : IJsonFormatter<T> private class ConfigurationEnumFormatter<T> : IJsonFormatter<T>
where T : struct where T : struct
{ {

View file

@ -0,0 +1,502 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.Configuration.Hid;
using Ryujinx.Configuration.System;
using Ryujinx.Configuration.Ui;
using Ryujinx.UI.Input;
using System;
using System.Collections.Generic;
namespace Ryujinx.Configuration
{
public class ConfigurationState
{
/// <summary>
/// UI configuration section
/// </summary>
public class UiSection
{
public class Columns
{
public ReactiveObject<bool> FavColumn { get; private set; }
public ReactiveObject<bool> IconColumn { get; private set; }
public ReactiveObject<bool> AppColumn { get; private set; }
public ReactiveObject<bool> DevColumn { get; private set; }
public ReactiveObject<bool> VersionColumn { get; private set; }
public ReactiveObject<bool> TimePlayedColumn { get; private set; }
public ReactiveObject<bool> LastPlayedColumn { get; private set; }
public ReactiveObject<bool> FileExtColumn { get; private set; }
public ReactiveObject<bool> FileSizeColumn { get; private set; }
public ReactiveObject<bool> PathColumn { get; private set; }
public Columns()
{
FavColumn = new ReactiveObject<bool>();
IconColumn = new ReactiveObject<bool>();
AppColumn = new ReactiveObject<bool>();
DevColumn = new ReactiveObject<bool>();
VersionColumn = new ReactiveObject<bool>();
TimePlayedColumn = new ReactiveObject<bool>();
LastPlayedColumn = new ReactiveObject<bool>();
FileExtColumn = new ReactiveObject<bool>();
FileSizeColumn = new ReactiveObject<bool>();
PathColumn = new ReactiveObject<bool>();
}
}
/// <summary>
/// Used to toggle columns in the GUI
/// </summary>
public Columns GuiColumns { get; private set; }
/// <summary>
/// A list of directories containing games to be used to load games into the games list
/// </summary>
public ReactiveObject<List<string>> GameDirs { get; private set; }
/// <summary>
/// Enable or disable custom themes in the GUI
/// </summary>
public ReactiveObject<bool> EnableCustomTheme { get; private set; }
/// <summary>
/// Path to custom GUI theme
/// </summary>
public ReactiveObject<string> CustomThemePath { get; private set; }
public UiSection()
{
GuiColumns = new Columns();
GameDirs = new ReactiveObject<List<string>>();
EnableCustomTheme = new ReactiveObject<bool>();
CustomThemePath = new ReactiveObject<string>();
}
}
/// <summary>
/// Logger configuration section
/// </summary>
public class LoggerSection
{
/// <summary>
/// Enables printing debug log messages
/// </summary>
public ReactiveObject<bool> EnableDebug { get; private set; }
/// <summary>
/// Enables printing stub log messages
/// </summary>
public ReactiveObject<bool> EnableStub { get; private set; }
/// <summary>
/// Enables printing info log messages
/// </summary>
public ReactiveObject<bool> EnableInfo { get; private set; }
/// <summary>
/// Enables printing warning log messages
/// </summary>
public ReactiveObject<bool> EnableWarn { get; private set; }
/// <summary>
/// Enables printing error log messages
/// </summary>
public ReactiveObject<bool> EnableError { get; private set; }
/// <summary>
/// Enables printing guest log messages
/// </summary>
public ReactiveObject<bool> EnableGuest { get; private set; }
/// <summary>
/// Enables printing FS access log messages
/// </summary>
public ReactiveObject<bool> EnableFsAccessLog { get; private set; }
/// <summary>
/// Controls which log messages are written to the log targets
/// </summary>
public ReactiveObject<LogClass[]> FilteredClasses { get; private set; }
/// <summary>
/// Enables or disables logging to a file on disk
/// </summary>
public ReactiveObject<bool> EnableFileLog { get; private set; }
public LoggerSection()
{
EnableDebug = new ReactiveObject<bool>();
EnableStub = new ReactiveObject<bool>();
EnableInfo = new ReactiveObject<bool>();
EnableWarn = new ReactiveObject<bool>();
EnableError = new ReactiveObject<bool>();
EnableGuest = new ReactiveObject<bool>();
EnableFsAccessLog = new ReactiveObject<bool>();
FilteredClasses = new ReactiveObject<LogClass[]>();
EnableFileLog = new ReactiveObject<bool>();
}
}
/// <summary>
/// System configuration section
/// </summary>
public class SystemSection
{
/// <summary>
/// Change System Language
/// </summary>
public ReactiveObject<Language> Language { get; private set; }
/// <summary>
/// Enables or disables Docked Mode
/// </summary>
public ReactiveObject<bool> EnableDockedMode { get; private set; }
/// <summary>
/// Enables or disables multi-core scheduling of threads
/// </summary>
public ReactiveObject<bool> EnableMulticoreScheduling { get; private set; }
/// <summary>
/// Enables integrity checks on Game content files
/// </summary>
public ReactiveObject<bool> EnableFsIntegrityChecks { get; private set; }
/// <summary>
/// Enables FS access log output to the console. Possible modes are 0-3
/// </summary>
public ReactiveObject<int> FsGlobalAccessLogMode { get; private set; }
/// <summary>
/// Enable or disable ignoring missing services
/// </summary>
public ReactiveObject<bool> IgnoreMissingServices { get; private set; }
public SystemSection()
{
Language = new ReactiveObject<Language>();
EnableDockedMode = new ReactiveObject<bool>();
EnableMulticoreScheduling = new ReactiveObject<bool>();
EnableFsIntegrityChecks = new ReactiveObject<bool>();
FsGlobalAccessLogMode = new ReactiveObject<int>();
IgnoreMissingServices = new ReactiveObject<bool>();
}
}
/// <summary>
/// Hid configuration section
/// </summary>
public class HidSection
{
/// <summary>
/// The primary controller's type
/// </summary>
public ReactiveObject<ControllerType> ControllerType { get; private set; }
/// <summary>
/// Enable or disable keyboard support (Independent from controllers binding)
/// </summary>
public ReactiveObject<bool> EnableKeyboard { get; private set; }
/// <summary>
/// Keyboard control bindings
/// </summary>
public ReactiveObject<NpadKeyboard> KeyboardControls { get; private set; }
/// <summary>
/// Controller control bindings
/// </summary>
public ReactiveObject<NpadController> JoystickControls { get; private set; }
public HidSection()
{
ControllerType = new ReactiveObject<ControllerType>();
EnableKeyboard = new ReactiveObject<bool>();
KeyboardControls = new ReactiveObject<NpadKeyboard>();
JoystickControls = new ReactiveObject<NpadController>();
}
}
/// <summary>
/// Graphics configuration section
/// </summary>
public class GraphicsSection
{
/// <summary>
/// Dumps shaders in this local directory
/// </summary>
public ReactiveObject<string> ShadersDumpPath { get; private set; }
/// <summary>
/// Enables or disables Vertical Sync
/// </summary>
public ReactiveObject<bool> EnableVsync { get; private set; }
public GraphicsSection()
{
ShadersDumpPath = new ReactiveObject<string>();
EnableVsync = new ReactiveObject<bool>();
}
}
/// <summary>
/// The default configuration instance
/// </summary>
public static ConfigurationState Instance { get; private set; }
/// <summary>
/// The Ui section
/// </summary>
public UiSection Ui { get; private set; }
/// <summary>
/// The Logger section
/// </summary>
public LoggerSection Logger { get; private set; }
/// <summary>
/// The System section
/// </summary>
public SystemSection System { get; private set; }
/// <summary>
/// The Graphics section
/// </summary>
public GraphicsSection Graphics { get; private set; }
/// <summary>
/// The Hid section
/// </summary>
public HidSection Hid { get; private set; }
/// <summary>
/// Enables or disables Discord Rich Presence
/// </summary>
public ReactiveObject<bool> EnableDiscordIntegration { get; private set; }
private ConfigurationState()
{
Ui = new UiSection();
Logger = new LoggerSection();
System = new SystemSection();
Graphics = new GraphicsSection();
Hid = new HidSection();
EnableDiscordIntegration = new ReactiveObject<bool>();
}
public ConfigurationFileFormat ToFileFormat()
{
ConfigurationFileFormat configurationFile = new ConfigurationFileFormat
{
Version = 1,
GraphicsShadersDumpPath = Graphics.ShadersDumpPath,
LoggingEnableDebug = Logger.EnableDebug,
LoggingEnableStub = Logger.EnableStub,
LoggingEnableInfo = Logger.EnableInfo,
LoggingEnableWarn = Logger.EnableWarn,
LoggingEnableError = Logger.EnableError,
LoggingEnableGuest = Logger.EnableGuest,
LoggingEnableFsAccessLog = Logger.EnableFsAccessLog,
LoggingFilteredClasses = Logger.FilteredClasses,
EnableFileLog = Logger.EnableFileLog,
SystemLanguage = System.Language,
DockedMode = System.EnableDockedMode,
EnableDiscordIntegration = EnableDiscordIntegration,
EnableVsync = Graphics.EnableVsync,
EnableMulticoreScheduling = System.EnableMulticoreScheduling,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
IgnoreMissingServices = System.IgnoreMissingServices,
ControllerType = Hid.ControllerType,
GuiColumns = new GuiColumns()
{
FavColumn = Ui.GuiColumns.FavColumn,
IconColumn = Ui.GuiColumns.IconColumn,
AppColumn = Ui.GuiColumns.AppColumn,
DevColumn = Ui.GuiColumns.DevColumn,
VersionColumn = Ui.GuiColumns.VersionColumn,
TimePlayedColumn = Ui.GuiColumns.TimePlayedColumn,
LastPlayedColumn = Ui.GuiColumns.LastPlayedColumn,
FileExtColumn = Ui.GuiColumns.FileExtColumn,
FileSizeColumn = Ui.GuiColumns.FileSizeColumn,
PathColumn = Ui.GuiColumns.PathColumn,
},
GameDirs = Ui.GameDirs,
EnableCustomTheme = Ui.EnableCustomTheme,
CustomThemePath = Ui.CustomThemePath,
EnableKeyboard = Hid.EnableKeyboard,
KeyboardControls = Hid.KeyboardControls,
JoystickControls = Hid.JoystickControls
};
return configurationFile;
}
public void LoadDefault()
{
Graphics.ShadersDumpPath.Value = "";
Logger.EnableDebug.Value = false;
Logger.EnableStub.Value = true;
Logger.EnableInfo.Value = true;
Logger.EnableWarn.Value = true;
Logger.EnableError.Value = true;
Logger.EnableGuest.Value = true;
Logger.EnableFsAccessLog.Value = false;
Logger.FilteredClasses.Value = new LogClass[] { };
Logger.EnableFileLog.Value = true;
System.Language.Value = Language.AmericanEnglish;
System.EnableDockedMode.Value = false;
EnableDiscordIntegration.Value = true;
Graphics.EnableVsync.Value = true;
System.EnableMulticoreScheduling.Value = true;
System.EnableFsIntegrityChecks.Value = true;
System.FsGlobalAccessLogMode.Value = 0;
System.IgnoreMissingServices.Value = false;
Hid.ControllerType.Value = ControllerType.Handheld;
Ui.GuiColumns.FavColumn.Value = true;
Ui.GuiColumns.IconColumn.Value = true;
Ui.GuiColumns.AppColumn.Value = true;
Ui.GuiColumns.DevColumn.Value = true;
Ui.GuiColumns.VersionColumn.Value = true;
Ui.GuiColumns.TimePlayedColumn.Value = true;
Ui.GuiColumns.LastPlayedColumn.Value = true;
Ui.GuiColumns.FileExtColumn.Value = true;
Ui.GuiColumns.FileSizeColumn.Value = true;
Ui.GuiColumns.PathColumn.Value = true;
Ui.GameDirs.Value = new List<string>();
Ui.EnableCustomTheme.Value = false;
Ui.CustomThemePath.Value = "";
Hid.EnableKeyboard.Value = false;
Hid.KeyboardControls.Value = new NpadKeyboard
{
LeftJoycon = new NpadKeyboardLeft
{
StickUp = Key.W,
StickDown = Key.S,
StickLeft = Key.A,
StickRight = Key.D,
StickButton = Key.F,
DPadUp = Key.Up,
DPadDown = Key.Down,
DPadLeft = Key.Left,
DPadRight = Key.Right,
ButtonMinus = Key.Minus,
ButtonL = Key.E,
ButtonZl = Key.Q,
},
RightJoycon = new NpadKeyboardRight
{
StickUp = Key.I,
StickDown = Key.K,
StickLeft = Key.J,
StickRight = Key.L,
StickButton = Key.H,
ButtonA = Key.Z,
ButtonB = Key.X,
ButtonX = Key.C,
ButtonY = Key.V,
ButtonPlus = Key.Plus,
ButtonR = Key.U,
ButtonZr = Key.O,
},
Hotkeys = new KeyboardHotkeys
{
ToggleVsync = Key.Tab
}
};
Hid.JoystickControls.Value = new NpadController
{
Enabled = true,
Index = 0,
Deadzone = 0.05f,
TriggerThreshold = 0.5f,
LeftJoycon = new NpadControllerLeft
{
Stick = ControllerInputId.Axis0,
StickButton = ControllerInputId.Button8,
DPadUp = ControllerInputId.Hat0Up,
DPadDown = ControllerInputId.Hat0Down,
DPadLeft = ControllerInputId.Hat0Left,
DPadRight = ControllerInputId.Hat0Right,
ButtonMinus = ControllerInputId.Button6,
ButtonL = ControllerInputId.Button4,
ButtonZl = ControllerInputId.Axis2,
},
RightJoycon = new NpadControllerRight
{
Stick = ControllerInputId.Axis3,
StickButton = ControllerInputId.Button9,
ButtonA = ControllerInputId.Button1,
ButtonB = ControllerInputId.Button0,
ButtonX = ControllerInputId.Button3,
ButtonY = ControllerInputId.Button2,
ButtonPlus = ControllerInputId.Button7,
ButtonR = ControllerInputId.Button5,
ButtonZr = ControllerInputId.Axis5,
}
};
}
public void Load(ConfigurationFileFormat configurationFileFormat)
{
if (configurationFileFormat.Version != 1 && configurationFileFormat.Version != 0)
{
Common.Logging.Logger.PrintWarning(LogClass.Application, $"Unsupported configuration version {configurationFileFormat.Version}, loading default.");
LoadDefault();
return;
}
Graphics.ShadersDumpPath.Value = configurationFileFormat.GraphicsShadersDumpPath;
Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug;
Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub;
Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo;
Logger.EnableWarn.Value = configurationFileFormat.LoggingEnableWarn;
Logger.EnableError.Value = configurationFileFormat.LoggingEnableError;
Logger.EnableGuest.Value = configurationFileFormat.LoggingEnableGuest;
Logger.EnableFsAccessLog.Value = configurationFileFormat.LoggingEnableFsAccessLog;
Logger.FilteredClasses.Value = configurationFileFormat.LoggingFilteredClasses;
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
System.Language.Value = configurationFileFormat.SystemLanguage;
System.EnableDockedMode.Value = configurationFileFormat.DockedMode;
System.EnableDockedMode.Value = configurationFileFormat.DockedMode;
EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration;
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
System.EnableMulticoreScheduling.Value = configurationFileFormat.EnableMulticoreScheduling;
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;
System.IgnoreMissingServices.Value = configurationFileFormat.IgnoreMissingServices;
Hid.ControllerType.Value = configurationFileFormat.ControllerType;
Ui.GuiColumns.FavColumn.Value = configurationFileFormat.GuiColumns.FavColumn;
Ui.GuiColumns.IconColumn.Value = configurationFileFormat.GuiColumns.IconColumn;
Ui.GuiColumns.AppColumn.Value = configurationFileFormat.GuiColumns.AppColumn;
Ui.GuiColumns.DevColumn.Value = configurationFileFormat.GuiColumns.DevColumn;
Ui.GuiColumns.VersionColumn.Value = configurationFileFormat.GuiColumns.VersionColumn;
Ui.GuiColumns.TimePlayedColumn.Value = configurationFileFormat.GuiColumns.TimePlayedColumn;
Ui.GuiColumns.LastPlayedColumn.Value = configurationFileFormat.GuiColumns.LastPlayedColumn;
Ui.GuiColumns.FileExtColumn.Value = configurationFileFormat.GuiColumns.FileExtColumn;
Ui.GuiColumns.FileSizeColumn.Value = configurationFileFormat.GuiColumns.FileSizeColumn;
Ui.GuiColumns.PathColumn.Value = configurationFileFormat.GuiColumns.PathColumn;
Ui.GameDirs.Value = configurationFileFormat.GameDirs;
Ui.EnableCustomTheme.Value = configurationFileFormat.EnableCustomTheme;
Ui.CustomThemePath.Value = configurationFileFormat.CustomThemePath;
Hid.EnableKeyboard.Value = configurationFileFormat.EnableKeyboard;
Hid.KeyboardControls.Value = configurationFileFormat.KeyboardControls;
Hid.JoystickControls.Value = configurationFileFormat.JoystickControls;
}
public static void Initialize()
{
if (Instance != null)
{
throw new InvalidOperationException("Configuration is already initialized");
}
Instance = new ConfigurationState();
}
}
}

View file

@ -0,0 +1,45 @@
namespace Ryujinx.Common.Configuration.Hid
{
public enum ControllerInputId
{
Button0,
Button1,
Button2,
Button3,
Button4,
Button5,
Button6,
Button7,
Button8,
Button9,
Button10,
Button11,
Button12,
Button13,
Button14,
Button15,
Button16,
Button17,
Button18,
Button19,
Button20,
Axis0,
Axis1,
Axis2,
Axis3,
Axis4,
Axis5,
Hat0Up,
Hat0Down,
Hat0Left,
Hat0Right,
Hat1Up,
Hat1Down,
Hat1Left,
Hat1Right,
Hat2Up,
Hat2Down,
Hat2Left,
Hat2Right
}
}

View file

@ -0,0 +1,11 @@
namespace Ryujinx.Configuration.Hid
{
public enum ControllerType
{
ProController,
Handheld,
NpadPair,
NpadLeft,
NpadRight
}
}

View file

@ -0,0 +1,153 @@
namespace Ryujinx.Configuration.Hid
{
public enum Key
{
Unknown = 0,
ShiftLeft = 1,
LShift = 1,
ShiftRight = 2,
RShift = 2,
ControlLeft = 3,
LControl = 3,
ControlRight = 4,
RControl = 4,
AltLeft = 5,
LAlt = 5,
AltRight = 6,
RAlt = 6,
WinLeft = 7,
LWin = 7,
WinRight = 8,
RWin = 8,
Menu = 9,
F1 = 10,
F2 = 11,
F3 = 12,
F4 = 13,
F5 = 14,
F6 = 15,
F7 = 16,
F8 = 17,
F9 = 18,
F10 = 19,
F11 = 20,
F12 = 21,
F13 = 22,
F14 = 23,
F15 = 24,
F16 = 25,
F17 = 26,
F18 = 27,
F19 = 28,
F20 = 29,
F21 = 30,
F22 = 31,
F23 = 32,
F24 = 33,
F25 = 34,
F26 = 35,
F27 = 36,
F28 = 37,
F29 = 38,
F30 = 39,
F31 = 40,
F32 = 41,
F33 = 42,
F34 = 43,
F35 = 44,
Up = 45,
Down = 46,
Left = 47,
Right = 48,
Enter = 49,
Escape = 50,
Space = 51,
Tab = 52,
BackSpace = 53,
Back = 53,
Insert = 54,
Delete = 55,
PageUp = 56,
PageDown = 57,
Home = 58,
End = 59,
CapsLock = 60,
ScrollLock = 61,
PrintScreen = 62,
Pause = 63,
NumLock = 64,
Clear = 65,
Sleep = 66,
Keypad0 = 67,
Keypad1 = 68,
Keypad2 = 69,
Keypad3 = 70,
Keypad4 = 71,
Keypad5 = 72,
Keypad6 = 73,
Keypad7 = 74,
Keypad8 = 75,
Keypad9 = 76,
KeypadDivide = 77,
KeypadMultiply = 78,
KeypadSubtract = 79,
KeypadMinus = 79,
KeypadAdd = 80,
KeypadPlus = 80,
KeypadDecimal = 81,
KeypadPeriod = 81,
KeypadEnter = 82,
A = 83,
B = 84,
C = 85,
D = 86,
E = 87,
F = 88,
G = 89,
H = 90,
I = 91,
J = 92,
K = 93,
L = 94,
M = 95,
N = 96,
O = 97,
P = 98,
Q = 99,
R = 100,
S = 101,
T = 102,
U = 103,
V = 104,
W = 105,
X = 106,
Y = 107,
Z = 108,
Number0 = 109,
Number1 = 110,
Number2 = 111,
Number3 = 112,
Number4 = 113,
Number5 = 114,
Number6 = 115,
Number7 = 116,
Number8 = 117,
Number9 = 118,
Tilde = 119,
Grave = 119,
Minus = 120,
Plus = 121,
BracketLeft = 122,
LBracket = 122,
BracketRight = 123,
RBracket = 123,
Semicolon = 124,
Quote = 125,
Comma = 126,
Period = 127,
Slash = 128,
BackSlash = 129,
NonUSBackSlash = 130,
LastKey = 131
}
}

View file

@ -0,0 +1,7 @@
namespace Ryujinx.Configuration.Hid
{
public struct KeyboardHotkeys
{
public Key ToggleVsync;
}
}

View file

@ -0,0 +1,35 @@
namespace Ryujinx.Common.Configuration.Hid
{
public class NpadController
{
/// <summary>
/// Enables or disables controller support
/// </summary>
public bool Enabled;
/// <summary>
/// Controller Device Index
/// </summary>
public int Index;
/// <summary>
/// Controller Analog Stick Deadzone
/// </summary>
public float Deadzone;
/// <summary>
/// Controller Trigger Threshold
/// </summary>
public float TriggerThreshold;
/// <summary>
/// Left JoyCon Controller Bindings
/// </summary>
public NpadControllerLeft LeftJoycon;
/// <summary>
/// Right JoyCon Controller Bindings
/// </summary>
public NpadControllerRight RightJoycon;
}
}

View file

@ -0,0 +1,15 @@
namespace Ryujinx.Common.Configuration.Hid
{
public struct NpadControllerLeft
{
public ControllerInputId Stick;
public ControllerInputId StickButton;
public ControllerInputId ButtonMinus;
public ControllerInputId ButtonL;
public ControllerInputId ButtonZl;
public ControllerInputId DPadUp;
public ControllerInputId DPadDown;
public ControllerInputId DPadLeft;
public ControllerInputId DPadRight;
}
}

View file

@ -0,0 +1,15 @@
namespace Ryujinx.Common.Configuration.Hid
{
public struct NpadControllerRight
{
public ControllerInputId Stick;
public ControllerInputId StickButton;
public ControllerInputId ButtonA;
public ControllerInputId ButtonB;
public ControllerInputId ButtonX;
public ControllerInputId ButtonY;
public ControllerInputId ButtonPlus;
public ControllerInputId ButtonR;
public ControllerInputId ButtonZr;
}
}

View file

@ -0,0 +1,20 @@
namespace Ryujinx.UI.Input
{
public class NpadKeyboard
{
/// <summary>
/// Left JoyCon Keyboard Bindings
/// </summary>
public Configuration.Hid.NpadKeyboardLeft LeftJoycon;
/// <summary>
/// Right JoyCon Keyboard Bindings
/// </summary>
public Configuration.Hid.NpadKeyboardRight RightJoycon;
/// <summary>
/// Hotkey Keyboard Bindings
/// </summary>
public Configuration.Hid.KeyboardHotkeys Hotkeys;
}
}

View file

@ -0,0 +1,18 @@
namespace Ryujinx.Configuration.Hid
{
public struct NpadKeyboardLeft
{
public Key StickUp;
public Key StickDown;
public Key StickLeft;
public Key StickRight;
public Key StickButton;
public Key DPadUp;
public Key DPadDown;
public Key DPadLeft;
public Key DPadRight;
public Key ButtonMinus;
public Key ButtonL;
public Key ButtonZl;
}
}

View file

@ -0,0 +1,18 @@
namespace Ryujinx.Configuration.Hid
{
public struct NpadKeyboardRight
{
public Key StickUp;
public Key StickDown;
public Key StickLeft;
public Key StickRight;
public Key StickButton;
public Key ButtonA;
public Key ButtonB;
public Key ButtonX;
public Key ButtonY;
public Key ButtonPlus;
public Key ButtonR;
public Key ButtonZr;
}
}

View file

@ -0,0 +1,109 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
using System.IO;
namespace Ryujinx.Configuration
{
public static class LoggerModule
{
public static void Initialize()
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
ConfigurationState.Instance.Logger.EnableDebug.Event += ReloadEnableDebug;
ConfigurationState.Instance.Logger.EnableStub.Event += ReloadEnableStub;
ConfigurationState.Instance.Logger.EnableInfo.Event += ReloadEnableInfo;
ConfigurationState.Instance.Logger.EnableWarn.Event += ReloadEnableWarning;
ConfigurationState.Instance.Logger.EnableError.Event += ReloadEnableError;
ConfigurationState.Instance.Logger.EnableGuest.Event += ReloadEnableGuest;
ConfigurationState.Instance.Logger.EnableFsAccessLog.Event += ReloadEnableFsAccessLog;
ConfigurationState.Instance.Logger.FilteredClasses.Event += ReloadFilteredClasses;
ConfigurationState.Instance.Logger.EnableFileLog.Event += ReloadFileLogger;
}
private static void ReloadEnableDebug(object sender, ReactiveEventArgs<bool> e)
{
Logger.SetEnable(LogLevel.Debug, e.NewValue);
}
private static void ReloadEnableStub(object sender, ReactiveEventArgs<bool> e)
{
Logger.SetEnable(LogLevel.Stub, e.NewValue);
}
private static void ReloadEnableInfo(object sender, ReactiveEventArgs<bool> e)
{
Logger.SetEnable(LogLevel.Info, e.NewValue);
}
private static void ReloadEnableWarning(object sender, ReactiveEventArgs<bool> e)
{
Logger.SetEnable(LogLevel.Warning, e.NewValue);
}
private static void ReloadEnableError(object sender, ReactiveEventArgs<bool> e)
{
Logger.SetEnable(LogLevel.Error, e.NewValue);
}
private static void ReloadEnableGuest(object sender, ReactiveEventArgs<bool> e)
{
Logger.SetEnable(LogLevel.Guest, e.NewValue);
}
private static void ReloadEnableFsAccessLog(object sender, ReactiveEventArgs<bool> e)
{
Logger.SetEnable(LogLevel.AccessLog, e.NewValue);
}
private static void ReloadFilteredClasses(object sender, ReactiveEventArgs<LogClass[]> e)
{
bool noFilter = e.NewValue.Length == 0;
foreach (var logClass in EnumExtensions.GetValues<LogClass>())
{
Logger.SetEnable(logClass, noFilter);
}
foreach (var logClass in e.NewValue)
{
Logger.SetEnable(logClass, true);
}
}
private static void ReloadFileLogger(object sender, ReactiveEventArgs<bool> e)
{
if (e.NewValue)
{
Logger.AddTarget(new AsyncLogTargetWrapper(
new FileLogTarget(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx.log"), "file"),
1000,
AsyncLogTargetOverflowAction.Block
));
}
else
{
Logger.RemoveTarget("file");
}
}
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
Logger.Shutdown();
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}");
if (e.IsTerminating)
{
Logger.Shutdown();
}
}
}
}

View file

@ -0,0 +1,23 @@
namespace Ryujinx.Configuration.System
{
public enum Language
{
Japanese,
AmericanEnglish,
French,
German,
Italian,
Spanish,
Chinese,
Korean,
Dutch,
Portuguese,
Russian,
Taiwanese,
BritishEnglish,
CanadianFrench,
LatinAmericanSpanish,
SimplifiedChinese,
TraditionalChinese
}
}

View file

@ -1,4 +1,4 @@
namespace Ryujinx.Ui namespace Ryujinx.Configuration.Ui
{ {
public struct GuiColumns public struct GuiColumns
{ {

View file

@ -37,6 +37,12 @@ namespace Ryujinx.Common.Logging
m_LogTargets = new List<ILogTarget>(); m_LogTargets = new List<ILogTarget>();
m_Time = Stopwatch.StartNew(); m_Time = Stopwatch.StartNew();
// Logger should log to console by default
AddTarget(new AsyncLogTargetWrapper(
new ConsoleLogTarget("console"),
1000,
AsyncLogTargetOverflowAction.Block));
} }
public static void RestartTime() public static void RestartTime()
@ -44,6 +50,19 @@ namespace Ryujinx.Common.Logging
m_Time.Restart(); m_Time.Restart();
} }
private static ILogTarget GetTarget(string targetName)
{
foreach (var target in m_LogTargets)
{
if (target.Name.Equals(targetName))
{
return target;
}
}
return null;
}
public static void AddTarget(ILogTarget target) public static void AddTarget(ILogTarget target)
{ {
m_LogTargets.Add(target); m_LogTargets.Add(target);
@ -51,6 +70,20 @@ namespace Ryujinx.Common.Logging
Updated += target.Log; Updated += target.Log;
} }
public static void RemoveTarget(string target)
{
ILogTarget logTarget = GetTarget(target);
if (logTarget != null)
{
Updated -= logTarget.Log;
m_LogTargets.Remove(logTarget);
logTarget.Dispose();
}
}
public static void Shutdown() public static void Shutdown()
{ {
Updated = null; Updated = null;

View file

@ -27,6 +27,8 @@ namespace Ryujinx.Common.Logging
private readonly int _overflowTimeout; private readonly int _overflowTimeout;
string ILogTarget.Name { get => _target.Name; }
public AsyncLogTargetWrapper(ILogTarget target) public AsyncLogTargetWrapper(ILogTarget target)
: this(target, -1, AsyncLogTargetOverflowAction.Block) : this(target, -1, AsyncLogTargetOverflowAction.Block)
{ } { }

View file

@ -9,6 +9,10 @@ namespace Ryujinx.Common.Logging
private readonly ILogFormatter _formatter; private readonly ILogFormatter _formatter;
private readonly string _name;
string ILogTarget.Name { get => _name; }
static ConsoleLogTarget() static ConsoleLogTarget()
{ {
_logColors = new ConcurrentDictionary<LogLevel, ConsoleColor> { _logColors = new ConcurrentDictionary<LogLevel, ConsoleColor> {
@ -19,9 +23,10 @@ namespace Ryujinx.Common.Logging
}; };
} }
public ConsoleLogTarget() public ConsoleLogTarget(string name)
{ {
_formatter = new DefaultLogFormatter(); _formatter = new DefaultLogFormatter();
_name = name;
} }
public void Log(object sender, LogEventArgs args) public void Log(object sender, LogEventArgs args)

View file

@ -1,4 +1,5 @@
using System.IO; using System;
using System.IO;
using System.Text; using System.Text;
namespace Ryujinx.Common.Logging namespace Ryujinx.Common.Logging
@ -9,13 +10,17 @@ namespace Ryujinx.Common.Logging
private readonly StreamWriter _logWriter; private readonly StreamWriter _logWriter;
private readonly ILogFormatter _formatter; private readonly ILogFormatter _formatter;
private readonly string _name;
public FileLogTarget(string path) string ILogTarget.Name { get => _name; }
: this(path, FileShare.Read, FileMode.Append)
public FileLogTarget(string path, string name)
: this(path, name, FileShare.Read, FileMode.Append)
{ } { }
public FileLogTarget(string path, FileShare fileShare, FileMode fileMode) public FileLogTarget(string path, string name, FileShare fileShare, FileMode fileMode)
{ {
_name = name;
_logWriter = new StreamWriter(File.Open(path, fileMode, FileAccess.Write, fileShare)); _logWriter = new StreamWriter(File.Open(path, fileMode, FileAccess.Write, fileShare));
_formatter = new DefaultLogFormatter(); _formatter = new DefaultLogFormatter();
} }

View file

@ -5,5 +5,7 @@ namespace Ryujinx.Common.Logging
public interface ILogTarget : IDisposable public interface ILogTarget : IDisposable
{ {
void Log(object sender, LogEventArgs args); void Log(object sender, LogEventArgs args);
string Name { get; }
} }
} }

View file

@ -1,4 +1,5 @@
using System.IO; using System;
using System.IO;
using Utf8Json; using Utf8Json;
namespace Ryujinx.Common.Logging namespace Ryujinx.Common.Logging
@ -7,10 +8,14 @@ namespace Ryujinx.Common.Logging
{ {
private Stream _stream; private Stream _stream;
private bool _leaveOpen; private bool _leaveOpen;
private string _name;
public JsonLogTarget(Stream stream) string ILogTarget.Name { get => _name; }
public JsonLogTarget(Stream stream, string name)
{ {
_stream = stream; _stream = stream;
_name = name;
} }
public JsonLogTarget(Stream stream, bool leaveOpen) public JsonLogTarget(Stream stream, bool leaveOpen)

View file

@ -0,0 +1,57 @@
using System;
using System.Threading;
namespace Ryujinx.Common
{
public class ReactiveObject<T>
{
private ReaderWriterLock _readerWriterLock = new ReaderWriterLock();
private T _value;
public event EventHandler<ReactiveEventArgs<T>> Event;
public T Value
{
get
{
_readerWriterLock.AcquireReaderLock(Timeout.Infinite);
T value = _value;
_readerWriterLock.ReleaseReaderLock();
return value;
}
set
{
_readerWriterLock.AcquireWriterLock(Timeout.Infinite);
T oldValue = _value;
_value = value;
_readerWriterLock.ReleaseWriterLock();
if (oldValue == null || !oldValue.Equals(_value))
{
Event?.Invoke(this, new ReactiveEventArgs<T>(oldValue, value));
}
}
}
public static implicit operator T(ReactiveObject<T> obj)
{
return obj.Value;
}
}
public class ReactiveEventArgs<T>
{
public T OldValue { get; }
public T NewValue { get; }
public ReactiveEventArgs(T oldValue, T newValue)
{
OldValue = oldValue;
NewValue = newValue;
}
}
}

View file

@ -27,6 +27,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="JsonPrettyPrinter" Version="1.0.1.1" />
<PackageReference Include="Utf8Json" Version="1.3.7" /> <PackageReference Include="Utf8Json" Version="1.3.7" />
</ItemGroup> </ItemGroup>

View file

@ -1,5 +1,7 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Configuration.Hid;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using System;
namespace Ryujinx.HLE.Input namespace Ryujinx.HLE.Input
{ {
@ -47,18 +49,31 @@ namespace Ryujinx.HLE.Input
_keyboardOffset = HidPosition + HidKeyboardOffset; _keyboardOffset = HidPosition + HidKeyboardOffset;
} }
public void InitializePrimaryController(ControllerStatus controllerType) private static ControllerStatus ConvertControllerTypeToState(ControllerType controllerType)
{ {
ControllerId controllerId = controllerType == ControllerStatus.Handheld ? switch (controllerType)
{
case ControllerType.Handheld: return ControllerStatus.Handheld;
case ControllerType.NpadLeft: return ControllerStatus.NpadLeft;
case ControllerType.NpadRight: return ControllerStatus.NpadRight;
case ControllerType.NpadPair: return ControllerStatus.NpadPair;
case ControllerType.ProController: return ControllerStatus.ProController;
default: throw new NotImplementedException();
}
}
public void InitializePrimaryController(ControllerType controllerType)
{
ControllerId controllerId = controllerType == ControllerType.Handheld ?
ControllerId.ControllerHandheld : ControllerId.ControllerPlayer1; ControllerId.ControllerHandheld : ControllerId.ControllerPlayer1;
if (controllerType == ControllerStatus.ProController) if (controllerType == ControllerType.ProController)
{ {
PrimaryController = new ProController(_device, NpadColor.Black, NpadColor.Black); PrimaryController = new ProController(_device, NpadColor.Black, NpadColor.Black);
} }
else else
{ {
PrimaryController = new NpadController(controllerType, PrimaryController = new NpadController(ConvertControllerTypeToState(controllerType),
_device, _device,
(NpadColor.BodyNeonRed, NpadColor.BodyNeonRed), (NpadColor.BodyNeonRed, NpadColor.BodyNeonRed),
(NpadColor.ButtonsNeonBlue, NpadColor.ButtonsNeonBlue)); (NpadColor.ButtonsNeonBlue, NpadColor.ButtonsNeonBlue));
@ -67,11 +82,6 @@ namespace Ryujinx.HLE.Input
PrimaryController.Connect(controllerId); PrimaryController.Connect(controllerId);
} }
public void InitializeKeyboard()
{
_device.Memory.FillWithZeros(HidPosition + HidKeyboardOffset, HidKeyboardSize);
}
public ControllerButtons UpdateStickButtons( public ControllerButtons UpdateStickButtons(
JoystickPosition leftStick, JoystickPosition leftStick,
JoystickPosition rightStick) JoystickPosition rightStick)

View file

@ -1,8 +1,12 @@
using LibHac.FsSystem;
using Ryujinx.Audio; using Ryujinx.Audio;
using Ryujinx.Configuration;
using Ryujinx.Graphics; using Ryujinx.Graphics;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Input; using Ryujinx.HLE.Input;
using System; using System;
using System.Threading; using System.Threading;
@ -60,6 +64,29 @@ namespace Ryujinx.HLE
VsyncEvent = new AutoResetEvent(true); VsyncEvent = new AutoResetEvent(true);
} }
public void Initialize()
{
System.State.SetLanguage((SystemLanguage)ConfigurationState.Instance.System.Language.Value);
EnableDeviceVsync = ConfigurationState.Instance.Graphics.EnableVsync;
// TODO: Make this reloadable and implement Docking/Undocking logic.
System.State.DockedMode = ConfigurationState.Instance.System.EnableDockedMode;
if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
{
System.EnableMultiCoreScheduling();
}
System.FsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None;
System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
ServiceConfiguration.IgnoreMissingServices = ConfigurationState.Instance.System.IgnoreMissingServices;
}
public void LoadCart(string exeFsDir, string romFsFile = null) public void LoadCart(string exeFsDir, string romFsFile = null)
{ {
System.LoadCart(exeFsDir, romFsFile); System.LoadCart(exeFsDir, romFsFile);

View file

@ -1,4 +1,5 @@
{ {
"version": 1,
"graphics_shaders_dump_path": "", "graphics_shaders_dump_path": "",
"logging_enable_debug": false, "logging_enable_debug": false,
"logging_enable_stub": true, "logging_enable_stub": true,
@ -7,9 +8,7 @@
"logging_enable_error": true, "logging_enable_error": true,
"logging_enable_guest": true, "logging_enable_guest": true,
"logging_enable_fs_access_log": false, "logging_enable_fs_access_log": false,
"logging_filtered_classes": [ "logging_filtered_classes": [],
],
"enable_file_log": true, "enable_file_log": true,
"system_language": "AmericanEnglish", "system_language": "AmericanEnglish",
"docked_mode": false, "docked_mode": false,
@ -32,9 +31,7 @@
"file_size_column": true, "file_size_column": true,
"path_column": true "path_column": true
}, },
"game_dirs": [ "game_dirs": [],
],
"enable_custom_theme": false, "enable_custom_theme": false,
"custom_theme_path": "", "custom_theme_path": "",
"enable_keyboard": false, "enable_keyboard": false,

View file

@ -0,0 +1,92 @@
using DiscordRPC;
using Ryujinx.Common;
using System;
using System.IO;
using System.Linq;
namespace Ryujinx.Configuration
{
static class DiscordIntegrationModule
{
private static DiscordRpcClient DiscordClient;
private static string LargeDescription = "Ryujinx is a Nintendo Switch emulator.";
public static RichPresence DiscordPresence { get; private set; }
public static void Initialize()
{
DiscordPresence = new RichPresence
{
Assets = new Assets
{
LargeImageKey = "ryujinx",
LargeImageText = LargeDescription
},
Details = "Main Menu",
State = "Idling",
Timestamps = new Timestamps(DateTime.UtcNow)
};
ConfigurationState.Instance.EnableDiscordIntegration.Event += Update;
}
private static void Update(object sender, ReactiveEventArgs<bool> e)
{
if (e.OldValue != e.NewValue)
{
// If the integration was active, disable it and unload everything
if (e.OldValue)
{
DiscordClient?.Dispose();
DiscordClient = null;
}
// If we need to activate it and the client isn't active, initialize it
if (e.NewValue && DiscordClient == null)
{
DiscordClient = new DiscordRpcClient("568815339807309834");
DiscordClient.Initialize();
DiscordClient.SetPresence(DiscordPresence);
}
}
}
public static void SwitchToPlayingState(string titleId, string titleName)
{
if (File.ReadAllLines(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RPsupported.dat")).Contains(titleId))
{
DiscordPresence.Assets.LargeImageKey = titleId;
}
string state = titleId;
if (state == null)
{
state = "Ryujinx";
}
else
{
state = state.ToUpper();
}
string details = "Idling";
if (titleName != null)
{
details = $"Playing {titleName}";
}
DiscordPresence.Details = details;
DiscordPresence.State = state;
DiscordPresence.Assets.LargeImageText = titleName;
DiscordPresence.Assets.SmallImageKey = "ryujinx";
DiscordPresence.Assets.SmallImageText = LargeDescription;
DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow);
DiscordClient?.SetPresence(DiscordPresence);
}
}
}

View file

@ -1,5 +1,6 @@
using Gtk; using Gtk;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Configuration;
using Ryujinx.Profiler; using Ryujinx.Profiler;
using Ryujinx.Ui; using Ryujinx.Ui;
using System; using System;
@ -16,10 +17,33 @@ namespace Ryujinx
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}"); Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}");
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
GLib.ExceptionManager.UnhandledException += Glib_UnhandledException; GLib.ExceptionManager.UnhandledException += Glib_UnhandledException;
// Initialize the configuration
ConfigurationState.Initialize();
// Initialize the logger system
LoggerModule.Initialize();
// Initialize Discord integration
DiscordIntegrationModule.Initialize();
string configurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json");
// Now load the configuration as the other subsystems are now registered
if (File.Exists(configurationPath))
{
ConfigurationFileFormat configurationFileFormat = ConfigurationFileFormat.Load(configurationPath);
ConfigurationState.Instance.Load(configurationFileFormat);
}
else
{
// No configuration, we load the default values and save it on disk
ConfigurationState.Instance.LoadDefault();
ConfigurationState.Instance.ToFileFormat().SaveConfig(configurationPath);
}
Profile.Initialize(); Profile.Initialize();
Application.Init(); Application.Init();
@ -42,23 +66,6 @@ namespace Ryujinx
Application.Run(); Application.Run();
} }
private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
Logger.Shutdown();
}
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Exception exception = e.ExceptionObject as Exception;
Logger.PrintError(LogClass.Emulation, $"Unhandled exception caught: {exception}");
if (e.IsTerminating)
{
Logger.Shutdown();
}
}
private static void Glib_UnhandledException(GLib.UnhandledExceptionArgs e) private static void Glib_UnhandledException(GLib.UnhandledExceptionArgs e)
{ {
Exception exception = e.ExceptionObject as Exception; Exception exception = e.ExceptionObject as Exception;

View file

@ -71,7 +71,6 @@
<PackageReference Include="DiscordRichPresence" Version="1.0.121" /> <PackageReference Include="DiscordRichPresence" Version="1.0.121" />
<PackageReference Include="GtkSharp" Version="3.22.25.24" /> <PackageReference Include="GtkSharp" Version="3.22.25.24" />
<PackageReference Include="GtkSharp.Dependencies" Version="1.1.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" /> <PackageReference Include="GtkSharp.Dependencies" Version="1.1.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
<PackageReference Include="JsonPrettyPrinter" Version="1.0.1.1" />
<PackageReference Include="OpenTK.NetStandard" Version="1.0.4" /> <PackageReference Include="OpenTK.NetStandard" Version="1.0.4" />
</ItemGroup> </ItemGroup>

View file

@ -1,10 +1,12 @@
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using OpenTK.Input; using OpenTK.Input;
using Ryujinx.Configuration;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.HLE.Input; using Ryujinx.HLE.Input;
using Ryujinx.Profiler.UI; using Ryujinx.Profiler.UI;
using Ryujinx.Ui;
using System; using System;
using System.Threading; using System.Threading;
@ -29,6 +31,8 @@ namespace Ryujinx.Ui
private MouseState? _mouse = null; private MouseState? _mouse = null;
private Input.NpadController _primaryController;
private Thread _renderThread; private Thread _renderThread;
private bool _resizeEvent; private bool _resizeEvent;
@ -50,6 +54,8 @@ namespace Ryujinx.Ui
_device = device; _device = device;
_renderer = renderer; _renderer = renderer;
_primaryController = new Input.NpadController(ConfigurationState.Instance.Hid.JoystickControls);
Location = new Point( Location = new Point(
(DisplayDevice.Default.Width / 2) - (Width / 2), (DisplayDevice.Default.Width / 2) - (Width / 2),
(DisplayDevice.Default.Height / 2) - (Height / 2)); (DisplayDevice.Default.Height / 2) - (Height / 2));
@ -162,16 +168,16 @@ namespace Ryujinx.Ui
#endif #endif
// Normal Input // Normal Input
currentHotkeyButtons = Configuration.Instance.KeyboardControls.GetHotkeyButtons(keyboard); currentHotkeyButtons = KeyboardControls.GetHotkeyButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
currentButton = Configuration.Instance.KeyboardControls.GetButtons(keyboard); currentButton = KeyboardControls.GetButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
if (Configuration.Instance.EnableKeyboard) if (ConfigurationState.Instance.Hid.EnableKeyboard)
{ {
hidKeyboard = Configuration.Instance.KeyboardControls.GetKeysDown(keyboard); hidKeyboard = KeyboardControls.GetKeysDown(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
} }
(leftJoystickDx, leftJoystickDy) = Configuration.Instance.KeyboardControls.GetLeftStick(keyboard); (leftJoystickDx, leftJoystickDy) = KeyboardControls.GetLeftStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
(rightJoystickDx, rightJoystickDy) = Configuration.Instance.KeyboardControls.GetRightStick(keyboard); (rightJoystickDx, rightJoystickDy) = KeyboardControls.GetRightStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
} }
if (!hidKeyboard.HasValue) if (!hidKeyboard.HasValue)
@ -183,17 +189,17 @@ namespace Ryujinx.Ui
}; };
} }
currentButton |= Configuration.Instance.JoystickControls.GetButtons(); currentButton |= _primaryController.GetButtons();
// Keyboard has priority stick-wise // Keyboard has priority stick-wise
if (leftJoystickDx == 0 && leftJoystickDy == 0) if (leftJoystickDx == 0 && leftJoystickDy == 0)
{ {
(leftJoystickDx, leftJoystickDy) = Configuration.Instance.JoystickControls.GetLeftStick(); (leftJoystickDx, leftJoystickDy) = _primaryController.GetLeftStick();
} }
if (rightJoystickDx == 0 && rightJoystickDy == 0) if (rightJoystickDx == 0 && rightJoystickDy == 0)
{ {
(rightJoystickDx, rightJoystickDy) = Configuration.Instance.JoystickControls.GetRightStick(); (rightJoystickDx, rightJoystickDy) = _primaryController.GetRightStick();
} }
leftJoystick = new JoystickPosition leftJoystick = new JoystickPosition
@ -269,7 +275,7 @@ namespace Ryujinx.Ui
_device.Hid.SetTouchPoints(); _device.Hid.SetTouchPoints();
} }
if (Configuration.Instance.EnableKeyboard && hidKeyboard.HasValue) if (ConfigurationState.Instance.Hid.EnableKeyboard && hidKeyboard.HasValue)
{ {
_device.Hid.WriteKeyboard(hidKeyboard.Value); _device.Hid.WriteKeyboard(hidKeyboard.Value);
} }

View file

@ -1,118 +1,67 @@
using OpenTK.Input; using OpenTK.Input;
using Ryujinx.HLE.Input; using Ryujinx.HLE.Input;
using Ryujinx.UI.Input;
namespace Ryujinx.Ui.Input namespace Ryujinx.Ui
{ {
public struct NpadKeyboardLeft public static class KeyboardControls
{ {
public Key StickUp; public static ControllerButtons GetButtons(NpadKeyboard npad, KeyboardState keyboard)
public Key StickDown;
public Key StickLeft;
public Key StickRight;
public Key StickButton;
public Key DPadUp;
public Key DPadDown;
public Key DPadLeft;
public Key DPadRight;
public Key ButtonMinus;
public Key ButtonL;
public Key ButtonZl;
}
public struct NpadKeyboardRight
{
public Key StickUp;
public Key StickDown;
public Key StickLeft;
public Key StickRight;
public Key StickButton;
public Key ButtonA;
public Key ButtonB;
public Key ButtonX;
public Key ButtonY;
public Key ButtonPlus;
public Key ButtonR;
public Key ButtonZr;
}
public struct KeyboardHotkeys
{
public Key ToggleVsync;
}
public class NpadKeyboard
{
/// <summary>
/// Left JoyCon Keyboard Bindings
/// </summary>
public NpadKeyboardLeft LeftJoycon { get; set; }
/// <summary>
/// Right JoyCon Keyboard Bindings
/// </summary>
public NpadKeyboardRight RightJoycon { get; set; }
/// <summary>
/// Hotkey Keyboard Bindings
/// </summary>
public KeyboardHotkeys Hotkeys { get; private set; }
public ControllerButtons GetButtons(KeyboardState keyboard)
{ {
ControllerButtons buttons = 0; ControllerButtons buttons = 0;
if (keyboard[(Key)LeftJoycon.StickButton]) buttons |= ControllerButtons.StickLeft; if (keyboard[(Key)npad.LeftJoycon.StickButton]) buttons |= ControllerButtons.StickLeft;
if (keyboard[(Key)LeftJoycon.DPadUp]) buttons |= ControllerButtons.DpadUp; if (keyboard[(Key)npad.LeftJoycon.DPadUp]) buttons |= ControllerButtons.DpadUp;
if (keyboard[(Key)LeftJoycon.DPadDown]) buttons |= ControllerButtons.DpadDown; if (keyboard[(Key)npad.LeftJoycon.DPadDown]) buttons |= ControllerButtons.DpadDown;
if (keyboard[(Key)LeftJoycon.DPadLeft]) buttons |= ControllerButtons.DpadLeft; if (keyboard[(Key)npad.LeftJoycon.DPadLeft]) buttons |= ControllerButtons.DpadLeft;
if (keyboard[(Key)LeftJoycon.DPadRight]) buttons |= ControllerButtons.DPadRight; if (keyboard[(Key)npad.LeftJoycon.DPadRight]) buttons |= ControllerButtons.DPadRight;
if (keyboard[(Key)LeftJoycon.ButtonMinus]) buttons |= ControllerButtons.Minus; if (keyboard[(Key)npad.LeftJoycon.ButtonMinus]) buttons |= ControllerButtons.Minus;
if (keyboard[(Key)LeftJoycon.ButtonL]) buttons |= ControllerButtons.L; if (keyboard[(Key)npad.LeftJoycon.ButtonL]) buttons |= ControllerButtons.L;
if (keyboard[(Key)LeftJoycon.ButtonZl]) buttons |= ControllerButtons.Zl; if (keyboard[(Key)npad.LeftJoycon.ButtonZl]) buttons |= ControllerButtons.Zl;
if (keyboard[(Key)RightJoycon.StickButton]) buttons |= ControllerButtons.StickRight; if (keyboard[(Key)npad.RightJoycon.StickButton]) buttons |= ControllerButtons.StickRight;
if (keyboard[(Key)RightJoycon.ButtonA]) buttons |= ControllerButtons.A; if (keyboard[(Key)npad.RightJoycon.ButtonA]) buttons |= ControllerButtons.A;
if (keyboard[(Key)RightJoycon.ButtonB]) buttons |= ControllerButtons.B; if (keyboard[(Key)npad.RightJoycon.ButtonB]) buttons |= ControllerButtons.B;
if (keyboard[(Key)RightJoycon.ButtonX]) buttons |= ControllerButtons.X; if (keyboard[(Key)npad.RightJoycon.ButtonX]) buttons |= ControllerButtons.X;
if (keyboard[(Key)RightJoycon.ButtonY]) buttons |= ControllerButtons.Y; if (keyboard[(Key)npad.RightJoycon.ButtonY]) buttons |= ControllerButtons.Y;
if (keyboard[(Key)RightJoycon.ButtonPlus]) buttons |= ControllerButtons.Plus; if (keyboard[(Key)npad.RightJoycon.ButtonPlus]) buttons |= ControllerButtons.Plus;
if (keyboard[(Key)RightJoycon.ButtonR]) buttons |= ControllerButtons.R; if (keyboard[(Key)npad.RightJoycon.ButtonR]) buttons |= ControllerButtons.R;
if (keyboard[(Key)RightJoycon.ButtonZr]) buttons |= ControllerButtons.Zr; if (keyboard[(Key)npad.RightJoycon.ButtonZr]) buttons |= ControllerButtons.Zr;
return buttons; return buttons;
} }
public (short, short) GetLeftStick(KeyboardState keyboard) public static (short, short) GetLeftStick(NpadKeyboard npad, KeyboardState keyboard)
{ {
short dx = 0; short dx = 0;
short dy = 0; short dy = 0;
if (keyboard[(Key)LeftJoycon.StickUp]) dy = short.MaxValue; if (keyboard[(Key)npad.LeftJoycon.StickUp]) dy = short.MaxValue;
if (keyboard[(Key)LeftJoycon.StickDown]) dy = -short.MaxValue; if (keyboard[(Key)npad.LeftJoycon.StickDown]) dy = -short.MaxValue;
if (keyboard[(Key)LeftJoycon.StickLeft]) dx = -short.MaxValue; if (keyboard[(Key)npad.LeftJoycon.StickLeft]) dx = -short.MaxValue;
if (keyboard[(Key)LeftJoycon.StickRight]) dx = short.MaxValue; if (keyboard[(Key)npad.LeftJoycon.StickRight]) dx = short.MaxValue;
return (dx, dy); return (dx, dy);
} }
public (short, short) GetRightStick(KeyboardState keyboard) public static (short, short) GetRightStick(NpadKeyboard npad, KeyboardState keyboard)
{ {
short dx = 0; short dx = 0;
short dy = 0; short dy = 0;
if (keyboard[(Key)RightJoycon.StickUp]) dy = short.MaxValue; if (keyboard[(Key)npad.RightJoycon.StickUp]) dy = short.MaxValue;
if (keyboard[(Key)RightJoycon.StickDown]) dy = -short.MaxValue; if (keyboard[(Key)npad.RightJoycon.StickDown]) dy = -short.MaxValue;
if (keyboard[(Key)RightJoycon.StickLeft]) dx = -short.MaxValue; if (keyboard[(Key)npad.RightJoycon.StickLeft]) dx = -short.MaxValue;
if (keyboard[(Key)RightJoycon.StickRight]) dx = short.MaxValue; if (keyboard[(Key)npad.RightJoycon.StickRight]) dx = short.MaxValue;
return (dx, dy); return (dx, dy);
} }
public HotkeyButtons GetHotkeyButtons(KeyboardState keyboard) public static HotkeyButtons GetHotkeyButtons(NpadKeyboard npad, KeyboardState keyboard)
{ {
HotkeyButtons buttons = 0; HotkeyButtons buttons = 0;
if (keyboard[(Key)Hotkeys.ToggleVsync]) buttons |= HotkeyButtons.ToggleVSync; if (keyboard[(Key)npad.Hotkeys.ToggleVsync]) buttons |= HotkeyButtons.ToggleVSync;
return buttons; return buttons;
} }
@ -267,7 +216,7 @@ namespace Ryujinx.Ui.Input
new KeyMappingEntry { TargetKey = Key.NumLock, Target = 10 }, new KeyMappingEntry { TargetKey = Key.NumLock, Target = 10 },
}; };
public HLE.Input.Keyboard GetKeysDown(KeyboardState keyboard) public static HLE.Input.Keyboard GetKeysDown(NpadKeyboard npad, KeyboardState keyboard)
{ {
HLE.Input.Keyboard hidKeyboard = new HLE.Input.Keyboard HLE.Input.Keyboard hidKeyboard = new HLE.Input.Keyboard
{ {

View file

@ -1,22 +1,22 @@
using DiscordRPC;
using Gtk; using Gtk;
using JsonPrettyPrinterPlus;
using Ryujinx.Audio; using Ryujinx.Audio;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gal.OpenGL;
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.HLE.FileSystem; using Ryujinx.Graphics.Gal.OpenGL;
using Ryujinx.Profiler; using Ryujinx.Profiler;
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading.Tasks;
using System.Threading; using System.Threading;
using Ryujinx.Configuration;
using System.Diagnostics;
using System.Threading.Tasks;
using Utf8Json; using Utf8Json;
using JsonPrettyPrinterPlus;
using Utf8Json.Resolvers; using Utf8Json.Resolvers;
using Ryujinx.HLE.FileSystem;
using GUI = Gtk.Builder.ObjectAttribute; using GUI = Gtk.Builder.ObjectAttribute;
@ -50,12 +50,6 @@ namespace Ryujinx.Ui
private static TreeView _treeView; private static TreeView _treeView;
public static bool DiscordIntegrationEnabled { get; set; }
public static DiscordRpcClient DiscordClient;
public static RichPresence DiscordPresence;
#pragma warning disable CS0649 #pragma warning disable CS0649
#pragma warning disable IDE0044 #pragma warning disable IDE0044
[GUI] Window _mainWin; [GUI] Window _mainWin;
@ -91,47 +85,26 @@ namespace Ryujinx.Ui
_audioOut = InitializeAudioEngine(); _audioOut = InitializeAudioEngine();
_device = new HLE.Switch(_renderer, _audioOut); // TODO: Initialization and dispose of HLE.Switch when starting/stoping emulation.
_device = InitializeSwitchInstance();
_treeView = _gameTable; _treeView = _gameTable;
Configuration.Load(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
Configuration.InitialConfigure(_device);
ApplyTheme(); ApplyTheme();
if (DiscordIntegrationEnabled)
{
DiscordClient = new DiscordRpcClient("568815339807309834");
DiscordPresence = new RichPresence
{
Assets = new Assets
{
LargeImageKey = "ryujinx",
LargeImageText = "Ryujinx is an emulator for the Nintendo Switch"
},
Details = "Main Menu",
State = "Idling",
Timestamps = new Timestamps(DateTime.UtcNow)
};
DiscordClient.Initialize();
DiscordClient.SetPresence(DiscordPresence);
}
_mainWin.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"); _mainWin.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
_stopEmulation.Sensitive = false; _stopEmulation.Sensitive = false;
if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn) { _favToggle.Active = true; } if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true;
if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn) { _iconToggle.Active = true; } if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _iconToggle.Active = true;
if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn) { _appToggle.Active = true; } if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appToggle.Active = true;
if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn) { _developerToggle.Active = true; } if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _developerToggle.Active = true;
if (SwitchSettings.SwitchConfig.GuiColumns.VersionColumn) { _versionToggle.Active = true; } if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _versionToggle.Active = true;
if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _timePlayedToggle.Active = true; } if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _timePlayedToggle.Active = true;
if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _lastPlayedToggle.Active = true; } if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _lastPlayedToggle.Active = true;
if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn) { _fileExtToggle.Active = true; } if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _fileExtToggle.Active = true;
if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn) { _fileSizeToggle.Active = true; } if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _fileSizeToggle.Active = true;
if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn) { _pathToggle.Active = true; } if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _pathToggle.Active = true;
_gameTable.Model = _tableStore = new ListStore( _gameTable.Model = _tableStore = new ListStore(
typeof(bool), typeof(bool),
@ -158,22 +131,22 @@ namespace Ryujinx.Ui
internal static void ApplyTheme() internal static void ApplyTheme()
{ {
if (!SwitchSettings.SwitchConfig.EnableCustomTheme) if (!ConfigurationState.Instance.Ui.EnableCustomTheme)
{ {
return; return;
} }
if (File.Exists(SwitchSettings.SwitchConfig.CustomThemePath) && (System.IO.Path.GetExtension(SwitchSettings.SwitchConfig.CustomThemePath) == ".css")) if (File.Exists(ConfigurationState.Instance.Ui.CustomThemePath) && (System.IO.Path.GetExtension(ConfigurationState.Instance.Ui.CustomThemePath) == ".css"))
{ {
CssProvider cssProvider = new CssProvider(); CssProvider cssProvider = new CssProvider();
cssProvider.LoadFromPath(SwitchSettings.SwitchConfig.CustomThemePath); cssProvider.LoadFromPath(ConfigurationState.Instance.Ui.CustomThemePath);
StyleContext.AddProviderForScreen(Gdk.Screen.Default, cssProvider, 800); StyleContext.AddProviderForScreen(Gdk.Screen.Default, cssProvider, 800);
} }
else else
{ {
Logger.PrintWarning(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{SwitchSettings.SwitchConfig.CustomThemePath}\"."); Logger.PrintWarning(LogClass.Application, $"The \"custom_theme_path\" section in \"Config.json\" contains an invalid path: \"{ConfigurationState.Instance.Ui.CustomThemePath}\".");
} }
} }
@ -187,39 +160,48 @@ namespace Ryujinx.Ui
CellRendererToggle favToggle = new CellRendererToggle(); CellRendererToggle favToggle = new CellRendererToggle();
favToggle.Toggled += FavToggle_Toggled; favToggle.Toggled += FavToggle_Toggled;
if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn) { _gameTable.AppendColumn("Fav", favToggle, "active", 0); } if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _gameTable.AppendColumn("Fav", favToggle, "active", 0);
if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn) { _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1); } if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1);
if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn) { _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2); } if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2);
if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn) { _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3); } if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3);
if (SwitchSettings.SwitchConfig.GuiColumns.VersionColumn) { _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4); } if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4);
if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5); } if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5);
if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6); } if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6);
if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn) { _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7); } if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7);
if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn) { _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8); } if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8);
if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn) { _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9); } if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9);
foreach (TreeViewColumn column in _gameTable.Columns) foreach (TreeViewColumn column in _gameTable.Columns)
{ {
if (column.Title == "Fav") { _favColumn = column; } if (column.Title == "Fav") _favColumn = column;
else if (column.Title == "Application") { _appColumn = column; } else if (column.Title == "Application") _appColumn = column;
else if (column.Title == "Developer") { _devColumn = column; } else if (column.Title == "Developer") _devColumn = column;
else if (column.Title == "Version") { _versionColumn = column; } else if (column.Title == "Version") _versionColumn = column;
else if (column.Title == "Time Played") { _timePlayedColumn = column; } else if (column.Title == "Time Played") _timePlayedColumn = column;
else if (column.Title == "Last Played") { _lastPlayedColumn = column; } else if (column.Title == "Last Played") _lastPlayedColumn = column;
else if (column.Title == "File Ext") { _fileExtColumn = column; } else if (column.Title == "File Ext") _fileExtColumn = column;
else if (column.Title == "File Size") { _fileSizeColumn = column; } else if (column.Title == "File Size") _fileSizeColumn = column;
else if (column.Title == "Path") { _pathColumn = column; } else if (column.Title == "Path") _pathColumn = column;
} }
if (SwitchSettings.SwitchConfig.GuiColumns.FavColumn) { _favColumn.SortColumnId = 0; } if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favColumn.SortColumnId = 0;
if (SwitchSettings.SwitchConfig.GuiColumns.IconColumn) { _appColumn.SortColumnId = 2; } if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appColumn.SortColumnId = 2;
if (SwitchSettings.SwitchConfig.GuiColumns.AppColumn) { _devColumn.SortColumnId = 3; } if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _devColumn.SortColumnId = 3;
if (SwitchSettings.SwitchConfig.GuiColumns.DevColumn) { _versionColumn.SortColumnId = 4; } if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _versionColumn.SortColumnId = 4;
if (SwitchSettings.SwitchConfig.GuiColumns.TimePlayedColumn) { _timePlayedColumn.SortColumnId = 5; } if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _timePlayedColumn.SortColumnId = 5;
if (SwitchSettings.SwitchConfig.GuiColumns.LastPlayedColumn) { _lastPlayedColumn.SortColumnId = 6; } if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _lastPlayedColumn.SortColumnId = 6;
if (SwitchSettings.SwitchConfig.GuiColumns.FileExtColumn) { _fileExtColumn.SortColumnId = 7; } if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _fileExtColumn.SortColumnId = 7;
if (SwitchSettings.SwitchConfig.GuiColumns.FileSizeColumn) { _fileSizeColumn.SortColumnId = 8; } if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _fileSizeColumn.SortColumnId = 8;
if (SwitchSettings.SwitchConfig.GuiColumns.PathColumn) { _pathColumn.SortColumnId = 9; } if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _pathColumn.SortColumnId = 9;
}
private HLE.Switch InitializeSwitchInstance()
{
HLE.Switch instance = new HLE.Switch(_renderer, _audioOut);
instance.Initialize();
return instance;
} }
internal static async Task UpdateGameTable() internal static async Task UpdateGameTable()
@ -233,7 +215,7 @@ namespace Ryujinx.Ui
_tableStore.Clear(); _tableStore.Clear();
await Task.Run(() => ApplicationLibrary.LoadApplications(SwitchSettings.SwitchConfig.GameDirs, _device.System.KeySet, _device.System.State.DesiredTitleLanguage)); await Task.Run(() => ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, _device.System.KeySet, _device.System.State.DesiredTitleLanguage));
_updatingGameTable = false; _updatingGameTable = false;
} }
@ -248,6 +230,9 @@ namespace Ryujinx.Ui
{ {
Logger.RestartTime(); Logger.RestartTime();
// TODO: Move this somewhere else + reloadable?
GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
if (Directory.Exists(path)) if (Directory.Exists(path))
{ {
string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); string[] romFsFiles = Directory.GetFiles(path, "*.istorage");
@ -313,40 +298,7 @@ namespace Ryujinx.Ui
_gameLoaded = true; _gameLoaded = true;
_stopEmulation.Sensitive = true; _stopEmulation.Sensitive = true;
if (DiscordIntegrationEnabled) DiscordIntegrationModule.SwitchToPlayingState(_device.System.TitleId, _device.System.TitleName);
{
if (File.ReadAllLines(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "RPsupported.dat")).Contains(_device.System.TitleId))
{
DiscordPresence.Assets.LargeImageKey = _device.System.TitleId;
}
string state = _device.System.TitleId;
if (state == null)
{
state = "Ryujinx";
}
else
{
state = state.ToUpper();
}
string details = "Idling";
if (_device.System.TitleName != null)
{
details = $"Playing {_device.System.TitleName}";
}
DiscordPresence.Details = details;
DiscordPresence.State = state;
DiscordPresence.Assets.LargeImageText = _device.System.TitleName;
DiscordPresence.Assets.SmallImageKey = "ryujinx";
DiscordPresence.Assets.SmallImageText = "Ryujinx is an emulator for the Nintendo Switch";
DiscordPresence.Timestamps = new Timestamps(DateTime.UtcNow);
DiscordClient.SetPresence(DiscordPresence);
}
string metadataFolder = System.IO.Path.Combine(new VirtualFileSystem().GetBasePath(), "games", _device.System.TitleId, "gui"); string metadataFolder = System.IO.Path.Combine(new VirtualFileSystem().GetBasePath(), "games", _device.System.TitleId, "gui");
string metadataFile = System.IO.Path.Combine(metadataFolder, "metadata.json"); string metadataFile = System.IO.Path.Combine(metadataFolder, "metadata.json");
@ -384,7 +336,7 @@ namespace Ryujinx.Ui
private static void CreateGameWindow() private static void CreateGameWindow()
{ {
Configuration.ConfigureHid(_device, SwitchSettings.SwitchConfig); _device.Hid.InitializePrimaryController(ConfigurationState.Instance.Hid.ControllerType);
using (_screen = new GlScreen(_device, _renderer)) using (_screen = new GlScreen(_device, _renderer))
{ {
@ -444,7 +396,6 @@ namespace Ryujinx.Ui
Profile.FinishProfiling(); Profile.FinishProfiling();
_device.Dispose(); _device.Dispose();
_audioOut.Dispose(); _audioOut.Dispose();
DiscordClient?.Dispose();
Logger.Shutdown(); Logger.Shutdown();
Environment.Exit(0); Environment.Exit(0);
} }
@ -607,7 +558,7 @@ namespace Ryujinx.Ui
private void Settings_Pressed(object sender, EventArgs args) private void Settings_Pressed(object sender, EventArgs args)
{ {
SwitchSettings settingsWin = new SwitchSettings(_device); SwitchSettings settingsWin = new SwitchSettings();
settingsWin.Show(); settingsWin.Show();
} }
@ -633,121 +584,81 @@ namespace Ryujinx.Ui
private void Fav_Toggled(object sender, EventArgs args) private void Fav_Toggled(object sender, EventArgs args)
{ {
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns; ConfigurationState.Instance.Ui.GuiColumns.FavColumn.Value = _favToggle.Active;
updatedColumns.FavColumn = _favToggle.Active;
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
SaveConfig();
UpdateColumns(); UpdateColumns();
} }
private void Icon_Toggled(object sender, EventArgs args) private void Icon_Toggled(object sender, EventArgs args)
{ {
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns; ConfigurationState.Instance.Ui.GuiColumns.IconColumn.Value = _iconToggle.Active;
updatedColumns.IconColumn = _iconToggle.Active;
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
SaveConfig();
UpdateColumns(); UpdateColumns();
} }
private void Title_Toggled(object sender, EventArgs args) private void Title_Toggled(object sender, EventArgs args)
{ {
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns; ConfigurationState.Instance.Ui.GuiColumns.AppColumn.Value = _appToggle.Active;
updatedColumns.AppColumn = _appToggle.Active;
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
SaveConfig();
UpdateColumns(); UpdateColumns();
} }
private void Developer_Toggled(object sender, EventArgs args) private void Developer_Toggled(object sender, EventArgs args)
{ {
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns; ConfigurationState.Instance.Ui.GuiColumns.DevColumn.Value = _developerToggle.Active;
updatedColumns.DevColumn = _developerToggle.Active;
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
SaveConfig();
UpdateColumns(); UpdateColumns();
} }
private void Version_Toggled(object sender, EventArgs args) private void Version_Toggled(object sender, EventArgs args)
{ {
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns; ConfigurationState.Instance.Ui.GuiColumns.VersionColumn.Value = _versionToggle.Active;
updatedColumns.VersionColumn = _versionToggle.Active;
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
SaveConfig();
UpdateColumns(); UpdateColumns();
} }
private void TimePlayed_Toggled(object sender, EventArgs args) private void TimePlayed_Toggled(object sender, EventArgs args)
{ {
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns; ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn.Value = _timePlayedToggle.Active;
updatedColumns.TimePlayedColumn = _timePlayedToggle.Active;
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
SaveConfig();
UpdateColumns(); UpdateColumns();
} }
private void LastPlayed_Toggled(object sender, EventArgs args) private void LastPlayed_Toggled(object sender, EventArgs args)
{ {
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns; ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn.Value = _lastPlayedToggle.Active;
updatedColumns.LastPlayedColumn = _lastPlayedToggle.Active;
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
SaveConfig();
UpdateColumns(); UpdateColumns();
} }
private void FileExt_Toggled(object sender, EventArgs args) private void FileExt_Toggled(object sender, EventArgs args)
{ {
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns; ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn.Value = _fileExtToggle.Active;
updatedColumns.FileExtColumn = _fileExtToggle.Active;
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
SaveConfig();
UpdateColumns(); UpdateColumns();
} }
private void FileSize_Toggled(object sender, EventArgs args) private void FileSize_Toggled(object sender, EventArgs args)
{ {
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns; ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn.Value = _fileSizeToggle.Active;
updatedColumns.FileSizeColumn = _fileSizeToggle.Active;
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
SaveConfig();
UpdateColumns(); UpdateColumns();
} }
private void Path_Toggled(object sender, EventArgs args) private void Path_Toggled(object sender, EventArgs args)
{ {
GuiColumns updatedColumns = SwitchSettings.SwitchConfig.GuiColumns; ConfigurationState.Instance.Ui.GuiColumns.PathColumn.Value = _pathToggle.Active;
updatedColumns.PathColumn = _pathToggle.Active;
SwitchSettings.SwitchConfig.GuiColumns = updatedColumns;
Configuration.SaveConfig(SwitchSettings.SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
SaveConfig();
UpdateColumns(); UpdateColumns();
} }
@ -865,5 +776,10 @@ namespace Ryujinx.Ui
return 0; return 0;
} }
} }
public static void SaveConfig()
{
ConfigurationState.Instance.ToFileFormat().SaveConfig(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
}
} }
} }

View file

@ -1,160 +1,57 @@
using OpenTK; using OpenTK;
using OpenTK.Input; using OpenTK.Input;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.HLE.Input; using Ryujinx.HLE.Input;
using System; using System;
using InnerNpadController = Ryujinx.Common.Configuration.Hid.NpadController;
namespace Ryujinx.Ui.Input namespace Ryujinx.Ui.Input
{ {
public enum ControllerInputId
{
Button0,
Button1,
Button2,
Button3,
Button4,
Button5,
Button6,
Button7,
Button8,
Button9,
Button10,
Button11,
Button12,
Button13,
Button14,
Button15,
Button16,
Button17,
Button18,
Button19,
Button20,
Axis0,
Axis1,
Axis2,
Axis3,
Axis4,
Axis5,
Hat0Up,
Hat0Down,
Hat0Left,
Hat0Right,
Hat1Up,
Hat1Down,
Hat1Left,
Hat1Right,
Hat2Up,
Hat2Down,
Hat2Left,
Hat2Right,
}
public struct NpadControllerLeft
{
public ControllerInputId Stick;
public ControllerInputId StickButton;
public ControllerInputId ButtonMinus;
public ControllerInputId ButtonL;
public ControllerInputId ButtonZl;
public ControllerInputId DPadUp;
public ControllerInputId DPadDown;
public ControllerInputId DPadLeft;
public ControllerInputId DPadRight;
}
public struct NpadControllerRight
{
public ControllerInputId Stick;
public ControllerInputId StickButton;
public ControllerInputId ButtonA;
public ControllerInputId ButtonB;
public ControllerInputId ButtonX;
public ControllerInputId ButtonY;
public ControllerInputId ButtonPlus;
public ControllerInputId ButtonR;
public ControllerInputId ButtonZr;
}
public class NpadController public class NpadController
{ {
/// <summary> private InnerNpadController _inner;
/// Enables or disables controller support
/// </summary>
public bool Enabled { get; private set; }
/// <summary> // NOTE: This should be initialized AFTER GTK for compat reasons with OpenTK SDL2 backend and GTK on Linux.
/// Controller Device Index // BODY: Usage of Joystick.GetState must be defer to after GTK full initialization. Otherwise, GTK will segfault because SDL2 was already init *sighs*
/// </summary> public NpadController(InnerNpadController inner)
public int Index { get; private set; }
/// <summary>
/// Controller Analog Stick Deadzone
/// </summary>
public float Deadzone { get; private set; }
/// <summary>
/// Controller Trigger Threshold
/// </summary>
public float TriggerThreshold { get; private set; }
/// <summary>
/// Left JoyCon Controller Bindings
/// </summary>
public NpadControllerLeft LeftJoycon { get; private set; }
/// <summary>
/// Right JoyCon Controller Bindings
/// </summary>
public NpadControllerRight RightJoycon { get; private set; }
public NpadController(
bool enabled,
int index,
float deadzone,
float triggerThreshold,
NpadControllerLeft leftJoycon,
NpadControllerRight rightJoycon)
{ {
Enabled = enabled; _inner = inner;
Index = index;
Deadzone = deadzone;
TriggerThreshold = triggerThreshold;
LeftJoycon = leftJoycon;
RightJoycon = rightJoycon;
} }
public void SetEnabled(bool enabled) private bool IsEnabled()
{ {
Enabled = enabled; return _inner.Enabled && Joystick.GetState(_inner.Index).IsConnected;
} }
public ControllerButtons GetButtons() public ControllerButtons GetButtons()
{ {
if (!Enabled) if (!IsEnabled())
{ {
return 0; return 0;
} }
JoystickState joystickState = Joystick.GetState(Index); JoystickState joystickState = Joystick.GetState(_inner.Index);
ControllerButtons buttons = 0; ControllerButtons buttons = 0;
if (IsActivated(joystickState, LeftJoycon.DPadUp)) buttons |= ControllerButtons.DpadUp; if (IsActivated(joystickState, _inner.LeftJoycon.DPadUp)) buttons |= ControllerButtons.DpadUp;
if (IsActivated(joystickState, LeftJoycon.DPadDown)) buttons |= ControllerButtons.DpadDown; if (IsActivated(joystickState, _inner.LeftJoycon.DPadDown)) buttons |= ControllerButtons.DpadDown;
if (IsActivated(joystickState, LeftJoycon.DPadLeft)) buttons |= ControllerButtons.DpadLeft; if (IsActivated(joystickState, _inner.LeftJoycon.DPadLeft)) buttons |= ControllerButtons.DpadLeft;
if (IsActivated(joystickState, LeftJoycon.DPadRight)) buttons |= ControllerButtons.DPadRight; if (IsActivated(joystickState, _inner.LeftJoycon.DPadRight)) buttons |= ControllerButtons.DPadRight;
if (IsActivated(joystickState, LeftJoycon.StickButton)) buttons |= ControllerButtons.StickLeft; if (IsActivated(joystickState, _inner.LeftJoycon.StickButton)) buttons |= ControllerButtons.StickLeft;
if (IsActivated(joystickState, LeftJoycon.ButtonMinus)) buttons |= ControllerButtons.Minus; if (IsActivated(joystickState, _inner.LeftJoycon.ButtonMinus)) buttons |= ControllerButtons.Minus;
if (IsActivated(joystickState, LeftJoycon.ButtonL)) buttons |= ControllerButtons.L; if (IsActivated(joystickState, _inner.LeftJoycon.ButtonL)) buttons |= ControllerButtons.L;
if (IsActivated(joystickState, LeftJoycon.ButtonZl)) buttons |= ControllerButtons.Zl; if (IsActivated(joystickState, _inner.LeftJoycon.ButtonZl)) buttons |= ControllerButtons.Zl;
if (IsActivated(joystickState, RightJoycon.ButtonA)) buttons |= ControllerButtons.A; if (IsActivated(joystickState, _inner.RightJoycon.ButtonA)) buttons |= ControllerButtons.A;
if (IsActivated(joystickState, RightJoycon.ButtonB)) buttons |= ControllerButtons.B; if (IsActivated(joystickState, _inner.RightJoycon.ButtonB)) buttons |= ControllerButtons.B;
if (IsActivated(joystickState, RightJoycon.ButtonX)) buttons |= ControllerButtons.X; if (IsActivated(joystickState, _inner.RightJoycon.ButtonX)) buttons |= ControllerButtons.X;
if (IsActivated(joystickState, RightJoycon.ButtonY)) buttons |= ControllerButtons.Y; if (IsActivated(joystickState, _inner.RightJoycon.ButtonY)) buttons |= ControllerButtons.Y;
if (IsActivated(joystickState, RightJoycon.StickButton)) buttons |= ControllerButtons.StickRight; if (IsActivated(joystickState, _inner.RightJoycon.StickButton)) buttons |= ControllerButtons.StickRight;
if (IsActivated(joystickState, RightJoycon.ButtonPlus)) buttons |= ControllerButtons.Plus; if (IsActivated(joystickState, _inner.RightJoycon.ButtonPlus)) buttons |= ControllerButtons.Plus;
if (IsActivated(joystickState, RightJoycon.ButtonR)) buttons |= ControllerButtons.R; if (IsActivated(joystickState, _inner.RightJoycon.ButtonR)) buttons |= ControllerButtons.R;
if (IsActivated(joystickState, RightJoycon.ButtonZr)) buttons |= ControllerButtons.Zr; if (IsActivated(joystickState, _inner.RightJoycon.ButtonZr)) buttons |= ControllerButtons.Zr;
return buttons; return buttons;
} }
@ -169,7 +66,7 @@ namespace Ryujinx.Ui.Input
{ {
int axis = controllerInputId - ControllerInputId.Axis0; int axis = controllerInputId - ControllerInputId.Axis0;
return joystickState.GetAxis(axis) > TriggerThreshold; return joystickState.GetAxis(axis) > _inner.TriggerThreshold;
} }
else if (controllerInputId <= ControllerInputId.Hat2Right) else if (controllerInputId <= ControllerInputId.Hat2Right)
{ {
@ -190,22 +87,22 @@ namespace Ryujinx.Ui.Input
public (short, short) GetLeftStick() public (short, short) GetLeftStick()
{ {
if (!Enabled) if (!IsEnabled())
{ {
return (0, 0); return (0, 0);
} }
return GetStick(LeftJoycon.Stick); return GetStick(_inner.LeftJoycon.Stick);
} }
public (short, short) GetRightStick() public (short, short) GetRightStick()
{ {
if (!Enabled) if (!IsEnabled())
{ {
return (0, 0); return (0, 0);
} }
return GetStick(RightJoycon.Stick); return GetStick(_inner.RightJoycon.Stick);
} }
private (short, short) GetStick(ControllerInputId stickInputId) private (short, short) GetStick(ControllerInputId stickInputId)
@ -215,7 +112,7 @@ namespace Ryujinx.Ui.Input
return (0, 0); return (0, 0);
} }
JoystickState jsState = Joystick.GetState(Index); JoystickState jsState = Joystick.GetState(_inner.Index);
int xAxis = stickInputId - ControllerInputId.Axis0; int xAxis = stickInputId - ControllerInputId.Axis0;
@ -227,8 +124,8 @@ namespace Ryujinx.Ui.Input
private (short, short) ApplyDeadzone(Vector2 axis) private (short, short) ApplyDeadzone(Vector2 axis)
{ {
return (ClampAxis(MathF.Abs(axis.X) > Deadzone ? axis.X : 0f), return (ClampAxis(MathF.Abs(axis.X) > _inner.Deadzone ? axis.X : 0f),
ClampAxis(MathF.Abs(axis.Y) > Deadzone ? axis.Y : 0f)); ClampAxis(MathF.Abs(axis.Y) > _inner.Deadzone ? axis.Y : 0f));
} }
private static short ClampAxis(float value) private static short ClampAxis(float value)

View file

@ -1,12 +1,12 @@
using Gtk; using Gtk;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Input;
using Ryujinx.Ui.Input;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Ryujinx.Configuration;
using Ryujinx.Configuration.System;
using Ryujinx.Configuration.Hid;
using GUI = Gtk.Builder.ObjectAttribute; using GUI = Gtk.Builder.ObjectAttribute;
@ -14,10 +14,6 @@ namespace Ryujinx.Ui
{ {
public class SwitchSettings : Window public class SwitchSettings : Window
{ {
internal static Configuration SwitchConfig { get; set; }
private readonly HLE.Switch _device;
private static ListStore _gameDirsBoxStore; private static ListStore _gameDirsBoxStore;
private static bool _listeningForKeypress; private static bool _listeningForKeypress;
@ -83,16 +79,12 @@ namespace Ryujinx.Ui
#pragma warning restore CS0649 #pragma warning restore CS0649
#pragma warning restore IDE0044 #pragma warning restore IDE0044
public static void ConfigureSettings(Configuration instance) { SwitchConfig = instance; } public SwitchSettings() : this(new Builder("Ryujinx.Ui.SwitchSettings.glade")) { }
public SwitchSettings(HLE.Switch device) : this(new Builder("Ryujinx.Ui.SwitchSettings.glade"), device) { } private SwitchSettings(Builder builder) : base(builder.GetObject("_settingsWin").Handle)
private SwitchSettings(Builder builder, HLE.Switch device) : base(builder.GetObject("_settingsWin").Handle)
{ {
builder.Autoconnect(this); builder.Autoconnect(this);
_device = device;
_settingsWin.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"); _settingsWin.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
_controller1Image.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyCon.png", 500, 500); _controller1Image.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyCon.png", 500, 500);
@ -124,60 +116,123 @@ namespace Ryujinx.Ui
_controller1Type.Changed += (sender, args) => Controller_Changed(sender, args, _controller1Type.ActiveId, _controller1Image); _controller1Type.Changed += (sender, args) => Controller_Changed(sender, args, _controller1Type.ActiveId, _controller1Image);
//Setup Currents //Setup Currents
if (SwitchConfig.EnableFileLog) _fileLogToggle.Click(); if (ConfigurationState.Instance.Logger.EnableFileLog)
if (SwitchConfig.LoggingEnableError) _errorLogToggle.Click(); {
if (SwitchConfig.LoggingEnableWarn) _warningLogToggle.Click(); _fileLogToggle.Click();
if (SwitchConfig.LoggingEnableInfo) _infoLogToggle.Click(); }
if (SwitchConfig.LoggingEnableStub) _stubLogToggle.Click();
if (SwitchConfig.LoggingEnableDebug) _debugLogToggle.Click();
if (SwitchConfig.LoggingEnableGuest) _guestLogToggle.Click();
if (SwitchConfig.LoggingEnableFsAccessLog) _fsAccessLogToggle.Click();
if (SwitchConfig.DockedMode) _dockedModeToggle.Click();
if (SwitchConfig.EnableDiscordIntegration) _discordToggle.Click();
if (SwitchConfig.EnableVsync) _vSyncToggle.Click();
if (SwitchConfig.EnableMulticoreScheduling) _multiSchedToggle.Click();
if (SwitchConfig.EnableFsIntegrityChecks) _fsicToggle.Click();
if (SwitchConfig.IgnoreMissingServices) _ignoreToggle.Click();
if (SwitchConfig.EnableKeyboard) _directKeyboardAccess.Click();
if (SwitchConfig.EnableCustomTheme) _custThemeToggle.Click();
_systemLanguageSelect.SetActiveId(SwitchConfig.SystemLanguage.ToString()); if (ConfigurationState.Instance.Logger.EnableError)
_controller1Type .SetActiveId(SwitchConfig.ControllerType.ToString()); {
_errorLogToggle.Click();
}
if (ConfigurationState.Instance.Logger.EnableWarn)
{
_warningLogToggle.Click();
}
if (ConfigurationState.Instance.Logger.EnableInfo)
{
_infoLogToggle.Click();
}
if (ConfigurationState.Instance.Logger.EnableStub)
{
_stubLogToggle.Click();
}
if (ConfigurationState.Instance.Logger.EnableDebug)
{
_debugLogToggle.Click();
}
if (ConfigurationState.Instance.Logger.EnableGuest)
{
_guestLogToggle.Click();
}
if (ConfigurationState.Instance.Logger.EnableFsAccessLog)
{
_fsAccessLogToggle.Click();
}
if (ConfigurationState.Instance.System.EnableDockedMode)
{
_dockedModeToggle.Click();
}
if (ConfigurationState.Instance.EnableDiscordIntegration)
{
_discordToggle.Click();
}
if (ConfigurationState.Instance.Graphics.EnableVsync)
{
_vSyncToggle.Click();
}
if (ConfigurationState.Instance.System.EnableMulticoreScheduling)
{
_multiSchedToggle.Click();
}
if (ConfigurationState.Instance.System.EnableFsIntegrityChecks)
{
_fsicToggle.Click();
}
if (ConfigurationState.Instance.System.IgnoreMissingServices)
{
_ignoreToggle.Click();
}
if (ConfigurationState.Instance.Hid.EnableKeyboard)
{
_directKeyboardAccess.Click();
}
if (ConfigurationState.Instance.Ui.EnableCustomTheme)
{
_custThemeToggle.Click();
}
_systemLanguageSelect.SetActiveId(ConfigurationState.Instance.System.Language.Value.ToString());
_controller1Type .SetActiveId(ConfigurationState.Instance.Hid.ControllerType.Value.ToString());
Controller_Changed(null, null, _controller1Type.ActiveId, _controller1Image); Controller_Changed(null, null, _controller1Type.ActiveId, _controller1Image);
_lStickUp1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickUp.ToString(); _lStickUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickUp.ToString();
_lStickDown1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickDown.ToString(); _lStickDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickDown.ToString();
_lStickLeft1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickLeft.ToString(); _lStickLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickLeft.ToString();
_lStickRight1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickRight.ToString(); _lStickRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickRight.ToString();
_lStickButton1.Label = SwitchConfig.KeyboardControls.LeftJoycon.StickButton.ToString(); _lStickButton1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickButton.ToString();
_dpadUp1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadUp.ToString(); _dpadUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadUp.ToString();
_dpadDown1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadDown.ToString(); _dpadDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadDown.ToString();
_dpadLeft1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadLeft.ToString(); _dpadLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadLeft.ToString();
_dpadRight1.Label = SwitchConfig.KeyboardControls.LeftJoycon.DPadRight.ToString(); _dpadRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadRight.ToString();
_minus1.Label = SwitchConfig.KeyboardControls.LeftJoycon.ButtonMinus.ToString(); _minus1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonMinus.ToString();
_l1.Label = SwitchConfig.KeyboardControls.LeftJoycon.ButtonL.ToString(); _l1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonL.ToString();
_zL1.Label = SwitchConfig.KeyboardControls.LeftJoycon.ButtonZl.ToString(); _zL1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonZl.ToString();
_rStickUp1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickUp.ToString(); _rStickUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickUp.ToString();
_rStickDown1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickDown.ToString(); _rStickDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickDown.ToString();
_rStickLeft1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickLeft.ToString(); _rStickLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickLeft.ToString();
_rStickRight1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickRight.ToString(); _rStickRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickRight.ToString();
_rStickButton1.Label = SwitchConfig.KeyboardControls.RightJoycon.StickButton.ToString(); _rStickButton1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickButton.ToString();
_a1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonA.ToString(); _a1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonA.ToString();
_b1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonB.ToString(); _b1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonB.ToString();
_x1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonX.ToString(); _x1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonX.ToString();
_y1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonY.ToString(); _y1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonY.ToString();
_plus1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonPlus.ToString(); _plus1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonPlus.ToString();
_r1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonR.ToString(); _r1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonR.ToString();
_zR1.Label = SwitchConfig.KeyboardControls.RightJoycon.ButtonZr.ToString(); _zR1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonZr.ToString();
_custThemePath.Buffer.Text = SwitchConfig.CustomThemePath; _custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath;
_graphicsShadersDumpPath.Buffer.Text = SwitchConfig.GraphicsShadersDumpPath; _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath;
_fsLogSpinAdjustment.Value = SwitchConfig.FsGlobalAccessLogMode; _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
_gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0); _gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0);
_gameDirsBoxStore = new ListStore(typeof(string)); _gameDirsBoxStore = new ListStore(typeof(string));
_gameDirsBox.Model = _gameDirsBoxStore; _gameDirsBox.Model = _gameDirsBoxStore;
foreach (string gameDir in SwitchConfig.GameDirs) foreach (string gameDir in ConfigurationState.Instance.Ui.GameDirs.Value)
{ {
_gameDirsBoxStore.AppendValues(gameDir); _gameDirsBoxStore.AppendValues(gameDir);
} }
@ -208,7 +263,7 @@ namespace Ryujinx.Ui
string key = keyPressed.Event.Key.ToString(); string key = keyPressed.Event.Key.ToString();
string capKey = key.First().ToString().ToUpper() + key.Substring(1); string capKey = key.First().ToString().ToUpper() + key.Substring(1);
if (Enum.IsDefined(typeof(OpenTK.Input.Key), capKey)) if (Enum.IsDefined(typeof(Configuration.Hid.Key), capKey))
{ {
button.Label = capKey; button.Label = capKey;
} }
@ -325,65 +380,63 @@ namespace Ryujinx.Ui
_gameDirsBoxStore.IterNext(ref treeIter); _gameDirsBoxStore.IterNext(ref treeIter);
} }
SwitchConfig.LoggingEnableError = _errorLogToggle.Active; ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active;
SwitchConfig.LoggingEnableWarn = _warningLogToggle.Active; ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active;
SwitchConfig.LoggingEnableInfo = _infoLogToggle.Active; ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active;
SwitchConfig.LoggingEnableStub = _stubLogToggle.Active; ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active;
SwitchConfig.LoggingEnableDebug = _debugLogToggle.Active; ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active;
SwitchConfig.LoggingEnableGuest = _guestLogToggle.Active; ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active;
SwitchConfig.LoggingEnableFsAccessLog = _fsAccessLogToggle.Active; ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active;
SwitchConfig.EnableFileLog = _fileLogToggle.Active; ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active;
SwitchConfig.DockedMode = _dockedModeToggle.Active; ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active;
SwitchConfig.EnableDiscordIntegration = _discordToggle.Active; ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active;
SwitchConfig.EnableVsync = _vSyncToggle.Active; ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
SwitchConfig.EnableMulticoreScheduling = _multiSchedToggle.Active; ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.Active;
SwitchConfig.EnableFsIntegrityChecks = _fsicToggle.Active; ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
SwitchConfig.IgnoreMissingServices = _ignoreToggle.Active; ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active;
SwitchConfig.EnableKeyboard = _directKeyboardAccess.Active; ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active;
SwitchConfig.EnableCustomTheme = _custThemeToggle.Active; ConfigurationState.Instance.Ui.EnableCustomTheme.Value = _custThemeToggle.Active;
SwitchConfig.KeyboardControls.LeftJoycon = new NpadKeyboardLeft() ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon = new NpadKeyboardLeft()
{ {
StickUp = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickUp1.Label), StickUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickUp1.Label),
StickDown = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickDown1.Label), StickDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickDown1.Label),
StickLeft = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickLeft1.Label), StickLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickLeft1.Label),
StickRight = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickRight1.Label), StickRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickRight1.Label),
StickButton = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _lStickButton1.Label), StickButton = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickButton1.Label),
DPadUp = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadUp1.Label), DPadUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadUp1.Label),
DPadDown = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadDown1.Label), DPadDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadDown1.Label),
DPadLeft = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadLeft1.Label), DPadLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadLeft1.Label),
DPadRight = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _dpadRight1.Label), DPadRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadRight1.Label),
ButtonMinus = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _minus1.Label), ButtonMinus = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _minus1.Label),
ButtonL = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _l1.Label), ButtonL = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _l1.Label),
ButtonZl = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _zL1.Label), ButtonZl = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _zL1.Label),
}; };
SwitchConfig.KeyboardControls.RightJoycon = new NpadKeyboardRight() ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon = new NpadKeyboardRight()
{ {
StickUp = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickUp1.Label), StickUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickUp1.Label),
StickDown = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickDown1.Label), StickDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickDown1.Label),
StickLeft = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickLeft1.Label), StickLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickLeft1.Label),
StickRight = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickRight1.Label), StickRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickRight1.Label),
StickButton = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _rStickButton1.Label), StickButton = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickButton1.Label),
ButtonA = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _a1.Label), ButtonA = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _a1.Label),
ButtonB = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _b1.Label), ButtonB = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _b1.Label),
ButtonX = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _x1.Label), ButtonX = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _x1.Label),
ButtonY = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _y1.Label), ButtonY = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _y1.Label),
ButtonPlus = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _plus1.Label), ButtonPlus = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _plus1.Label),
ButtonR = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _r1.Label), ButtonR = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _r1.Label),
ButtonZr = (OpenTK.Input.Key)Enum.Parse(typeof(OpenTK.Input.Key), _zR1.Label), ButtonZr = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _zR1.Label),
}; };
SwitchConfig.SystemLanguage = (SystemLanguage)Enum.Parse(typeof(SystemLanguage), _systemLanguageSelect.ActiveId); ConfigurationState.Instance.System.Language.Value = (Language)Enum.Parse(typeof(Language), _systemLanguageSelect.ActiveId);
SwitchConfig.ControllerType = (ControllerStatus)Enum.Parse(typeof(ControllerStatus), _controller1Type.ActiveId); ConfigurationState.Instance.Hid.ControllerType.Value = (ControllerType)Enum.Parse(typeof(ControllerType), _controller1Type.ActiveId);
SwitchConfig.CustomThemePath = _custThemePath.Buffer.Text; ConfigurationState.Instance.Ui.CustomThemePath.Value = _custThemePath.Buffer.Text;
SwitchConfig.GraphicsShadersDumpPath = _graphicsShadersDumpPath.Buffer.Text; ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text;
SwitchConfig.GameDirs = gameDirs; ConfigurationState.Instance.Ui.GameDirs.Value = gameDirs;
SwitchConfig.FsGlobalAccessLogMode = (int)_fsLogSpinAdjustment.Value; ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value;
Configuration.SaveConfig(SwitchConfig, System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
Configuration.Configure(_device, SwitchConfig);
MainWindow.SaveConfig();
MainWindow.ApplyTheme(); MainWindow.ApplyTheme();
#pragma warning disable CS4014 #pragma warning disable CS4014
MainWindow.UpdateGameTable(); MainWindow.UpdateGameTable();