aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXpl0itR <xpl0itr@outlook.com>2020-05-03 03:00:53 +0100
committerGitHub <noreply@github.com>2020-05-03 04:00:53 +0200
commit538fba826b75cdd2feffaeac684044246590cb29 (patch)
tree4f9b09aaaeec6249b49d665012dd4e585f3e67f0
parent5f3558fd51a0920966e5341101c4233a02c98307 (diff)
Improvements to input and input configuration in the GUI. (#849)
* Improvements to input and input configuration in the GUI * Requested changes * nits * more nits
-rw-r--r--Ryujinx.Common/Configuration/ConfigurationFileFormat.cs13
-rw-r--r--Ryujinx.Common/Configuration/ConfigurationState.cs227
-rw-r--r--Ryujinx.Common/Configuration/Hid/ControllerConfig.cs (renamed from Ryujinx.Common/Configuration/Hid/NpadController.cs)17
-rw-r--r--Ryujinx.Common/Configuration/Hid/ControllerInputId.cs3
-rw-r--r--Ryujinx.Common/Configuration/Hid/ControllerType.cs25
-rw-r--r--Ryujinx.Common/Configuration/Hid/InputConfig.cs20
-rw-r--r--Ryujinx.Common/Configuration/Hid/Key.cs3
-rw-r--r--Ryujinx.Common/Configuration/Hid/KeyboardConfig.cs20
-rw-r--r--Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs6
-rw-r--r--Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs9
-rw-r--r--Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs9
-rw-r--r--Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs20
-rw-r--r--Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs8
-rw-r--r--Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs8
-rw-r--r--Ryujinx.Common/Configuration/Hid/PlayerIndex.cs18
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Hid.cs30
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs88
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs4
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs62
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs16
-rw-r--r--Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs12
-rw-r--r--Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/Device.cs2
-rw-r--r--Ryujinx/Config.json105
-rw-r--r--Ryujinx/Ryujinx.csproj24
-rw-r--r--Ryujinx/Ui/AboutWindow.cs3
-rw-r--r--Ryujinx/Ui/ApplicationLibrary.cs2
-rw-r--r--Ryujinx/Ui/ControllerWindow.cs925
-rw-r--r--Ryujinx/Ui/ControllerWindow.glade1732
-rw-r--r--Ryujinx/Ui/GLRenderer.cs229
-rw-r--r--Ryujinx/Ui/GtkDialog.cs69
-rw-r--r--Ryujinx/Ui/JoystickController.cs149
-rw-r--r--Ryujinx/Ui/KeyboardController.cs (renamed from Ryujinx/Ui/KeyboardControls.cs)91
-rw-r--r--Ryujinx/Ui/MainWindow.cs102
-rw-r--r--Ryujinx/Ui/NpadController.cs143
-rw-r--r--Ryujinx/Ui/ProfileDialog.cs58
-rw-r--r--Ryujinx/Ui/ProfileDialog.glade124
-rw-r--r--Ryujinx/Ui/SettingsWindow.cs409
-rw-r--r--Ryujinx/Ui/SettingsWindow.glade (renamed from Ryujinx/Ui/SwitchSettings.glade)2250
-rw-r--r--Ryujinx/Ui/SwitchSettings.cs611
-rw-r--r--Ryujinx/Ui/TitleUpdateWindow.cs4
-rw-r--r--Ryujinx/Ui/assets/BlueCon.pngbin164842 -> 0 bytes
-rw-r--r--Ryujinx/Ui/assets/JoyCon.pngbin331451 -> 0 bytes
-rw-r--r--Ryujinx/Ui/assets/JoyConLeft.svg105
-rw-r--r--Ryujinx/Ui/assets/JoyConPair.svg218
-rw-r--r--Ryujinx/Ui/assets/JoyConRight.svg120
-rw-r--r--Ryujinx/Ui/assets/ProCon.pngbin324289 -> 0 bytes
-rw-r--r--Ryujinx/Ui/assets/ProCon.svg149
-rw-r--r--Ryujinx/Ui/assets/RedCon.pngbin177647 -> 0 bytes
-rw-r--r--Ryujinx/_schema.json4
50 files changed, 5810 insertions, 2438 deletions
diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
index ff5a67c4..09252b77 100644
--- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
@@ -4,9 +4,7 @@ using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
using Ryujinx.Configuration.System;
-using Ryujinx.Configuration.Hid;
using Ryujinx.Configuration.Ui;
-using Ryujinx.UI.Input;
namespace Ryujinx.Configuration
{
@@ -15,7 +13,7 @@ namespace Ryujinx.Configuration
/// <summary>
/// The current version of the file format
/// </summary>
- public const int CurrentVersion = 5;
+ public const int CurrentVersion = 6;
public int Version { get; set; }
@@ -130,11 +128,6 @@ namespace Ryujinx.Configuration
public bool IgnoreMissingServices { get; set; }
/// <summary>
- /// The primary controller's type
- /// </summary>
- public ControllerType ControllerType { get; set; }
-
- /// <summary>
/// Used to toggle columns in the GUI
/// </summary>
public GuiColumns GuiColumns { get; set; }
@@ -162,12 +155,12 @@ namespace Ryujinx.Configuration
/// <summary>
/// Keyboard control bindings
/// </summary>
- public NpadKeyboard KeyboardControls { get; set; }
+ public List<KeyboardConfig> KeyboardConfig { get; set; }
/// <summary>
/// Controller control bindings
/// </summary>
- public NpadController JoystickControls { get; set; }
+ public List<ControllerConfig> ControllerConfig { get; set; }
/// <summary>
/// Loads a configuration file from disk
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs
index d2826d36..a994e6d5 100644
--- a/Ryujinx.Common/Configuration/ConfigurationState.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationState.cs
@@ -1,10 +1,9 @@
-using Ryujinx.Common;
+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;
@@ -159,7 +158,7 @@ namespace Ryujinx.Configuration
public ReactiveObject<string> TimeZone { get; private set; }
/// <summary>
- /// System Time Offset in seconds
+ /// System Time Offset in Seconds
/// </summary>
public ReactiveObject<long> SystemTimeOffset { get; private set; }
@@ -208,31 +207,21 @@ namespace Ryujinx.Configuration
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
+ /// Input device configuration.
+ /// NOTE: This ReactiveObject won't issue an event when the List has elements added or removed.
+ /// TODO: Implement a ReactiveList class.
/// </summary>
- public ReactiveObject<NpadKeyboard> KeyboardControls { get; private set; }
-
- /// <summary>
- /// Controller control bindings
- /// </summary>
- public ReactiveObject<NpadController> JoystickControls { get; private set; }
+ public ReactiveObject<List<InputConfig>> InputConfig { get; private set; }
public HidSection()
{
- ControllerType = new ReactiveObject<ControllerType>();
- EnableKeyboard = new ReactiveObject<bool>();
- KeyboardControls = new ReactiveObject<NpadKeyboard>();
- JoystickControls = new ReactiveObject<NpadController>();
+ EnableKeyboard = new ReactiveObject<bool>();
+ InputConfig = new ReactiveObject<List<InputConfig>>();
}
}
@@ -311,6 +300,21 @@ namespace Ryujinx.Configuration
public ConfigurationFileFormat ToFileFormat()
{
+ List<ControllerConfig> controllerConfigList = new List<ControllerConfig>();
+ List<KeyboardConfig> keyboardConfigList = new List<KeyboardConfig>();
+
+ foreach (InputConfig inputConfig in Hid.InputConfig.Value)
+ {
+ if (inputConfig is ControllerConfig controllerConfig)
+ {
+ controllerConfigList.Add(controllerConfig);
+ }
+ else if (inputConfig is KeyboardConfig keyboardConfig)
+ {
+ keyboardConfigList.Add(keyboardConfig);
+ }
+ }
+
ConfigurationFileFormat configurationFile = new ConfigurationFileFormat
{
Version = ConfigurationFileFormat.CurrentVersion,
@@ -336,7 +340,6 @@ namespace Ryujinx.Configuration
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
IgnoreMissingServices = System.IgnoreMissingServices,
- ControllerType = Hid.ControllerType,
GuiColumns = new GuiColumns()
{
FavColumn = Ui.GuiColumns.FavColumn,
@@ -354,8 +357,8 @@ namespace Ryujinx.Configuration
EnableCustomTheme = Ui.EnableCustomTheme,
CustomThemePath = Ui.CustomThemePath,
EnableKeyboard = Hid.EnableKeyboard,
- KeyboardControls = Hid.KeyboardControls,
- JoystickControls = Hid.JoystickControls
+ KeyboardConfig = keyboardConfigList,
+ ControllerConfig = controllerConfigList
};
return configurationFile;
@@ -385,7 +388,6 @@ namespace Ryujinx.Configuration
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;
@@ -401,73 +403,51 @@ namespace Ryujinx.Configuration
Ui.CustomThemePath.Value = "";
Hid.EnableKeyboard.Value = false;
- Hid.KeyboardControls.Value = new NpadKeyboard
+ Hid.InputConfig.Value = new List<InputConfig>
{
- 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
+ new KeyboardConfig
{
- 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,
+ Index = 0,
+ ControllerType = ControllerType.JoyconPair,
+ PlayerIndex = PlayerIndex.Player1,
+ 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,
+ ButtonSl = Key.Home,
+ ButtonSr = Key.End
+ },
+ 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,
+ ButtonSl = Key.PageUp,
+ ButtonSr = Key.PageDown
+ },
+ Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = Key.Tab
+ }
}
};
}
@@ -521,6 +501,71 @@ namespace Ryujinx.Configuration
configurationFileUpdated = true;
}
+ if (configurationFileFormat.Version < 6)
+ {
+ Common.Logging.Logger.PrintWarning(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 6.");
+
+ configurationFileFormat.ControllerConfig = new List<ControllerConfig>();
+ configurationFileFormat.KeyboardConfig = new List<KeyboardConfig>{
+ new KeyboardConfig
+ {
+ Index = 0,
+ ControllerType = ControllerType.JoyconPair,
+ PlayerIndex = PlayerIndex.Player1,
+ 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,
+ ButtonSl = Key.Unbound,
+ ButtonSr = Key.Unbound
+ },
+ 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,
+ ButtonSl = Key.Unbound,
+ ButtonSr = Key.Unbound
+ },
+ Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = Key.Tab
+ }
+ }
+ };
+
+ configurationFileUpdated = true;
+ }
+
+ List<InputConfig> inputConfig = new List<InputConfig>();
+ foreach (ControllerConfig controllerConfig in configurationFileFormat.ControllerConfig)
+ {
+ inputConfig.Add(controllerConfig);
+ }
+ foreach (KeyboardConfig keyboardConfig in configurationFileFormat.KeyboardConfig)
+ {
+ inputConfig.Add(keyboardConfig);
+ }
+
Graphics.MaxAnisotropy.Value = configurationFileFormat.MaxAnisotropy;
Graphics.ShadersDumpPath.Value = configurationFileFormat.GraphicsShadersDumpPath;
Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug;
@@ -544,7 +589,6 @@ namespace Ryujinx.Configuration
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;
@@ -559,14 +603,13 @@ namespace Ryujinx.Configuration
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;
+ Hid.InputConfig.Value = inputConfig;
if (configurationFileUpdated)
{
ToFileFormat().SaveConfig(configurationFilePath);
- Common.Logging.Logger.PrintWarning(LogClass.Application, "Configuration file is updated!");
+ Common.Logging.Logger.PrintWarning(LogClass.Application, "Configuration file has been updated!");
}
}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadController.cs b/Ryujinx.Common/Configuration/Hid/ControllerConfig.cs
index 94b985d5..3e414055 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadController.cs
+++ b/Ryujinx.Common/Configuration/Hid/ControllerConfig.cs
@@ -1,21 +1,16 @@
namespace Ryujinx.Common.Configuration.Hid
{
- public class NpadController
+ public class ControllerConfig : InputConfig
{
/// <summary>
- /// Enables or disables controller support
+ /// Controller Left Analog Stick Deadzone
/// </summary>
- public bool Enabled { get; set; }
+ public float DeadzoneLeft { get; set; }
/// <summary>
- /// Controller Device Index
+ /// Controller Right Analog Stick Deadzone
/// </summary>
- public int Index { get; set; }
-
- /// <summary>
- /// Controller Analog Stick Deadzone
- /// </summary>
- public float Deadzone { get; set; }
+ public float DeadzoneRight { get; set; }
/// <summary>
/// Controller Trigger Threshold
@@ -32,4 +27,4 @@
/// </summary>
public NpadControllerRight RightJoycon { get; set; }
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/Hid/ControllerInputId.cs b/Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
index 8969b6a4..606a1b0c 100644
--- a/Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
+++ b/Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
@@ -40,6 +40,7 @@
Hat2Up,
Hat2Down,
Hat2Left,
- Hat2Right
+ Hat2Right,
+ Unbound
}
}
diff --git a/Ryujinx.Common/Configuration/Hid/ControllerType.cs b/Ryujinx.Common/Configuration/Hid/ControllerType.cs
index b0613b2d..0ad01bbb 100644
--- a/Ryujinx.Common/Configuration/Hid/ControllerType.cs
+++ b/Ryujinx.Common/Configuration/Hid/ControllerType.cs
@@ -1,11 +1,20 @@
-namespace Ryujinx.Configuration.Hid
+using System;
+
+namespace Ryujinx.Common.Configuration.Hid
{
- public enum ControllerType
+ [Flags]
+ // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
+ public enum ControllerType : int
{
- ProController,
- Handheld,
- NpadPair,
- NpadLeft,
- NpadRight
+ None,
+ ProController = 1 << 0,
+ Handheld = 1 << 1,
+ JoyconPair = 1 << 2,
+ JoyconLeft = 1 << 3,
+ JoyconRight = 1 << 4,
+ Invalid = 1 << 5,
+ Pokeball = 1 << 6,
+ SystemExternal = 1 << 29,
+ System = 1 << 30
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/Hid/InputConfig.cs b/Ryujinx.Common/Configuration/Hid/InputConfig.cs
new file mode 100644
index 00000000..540506d5
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/InputConfig.cs
@@ -0,0 +1,20 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+ public class InputConfig
+ {
+ /// <summary>
+ /// Controller Device Index
+ /// </summary>
+ public int Index { get; set; }
+
+ /// <summary>
+ /// Controller's Type
+ /// </summary>
+ public ControllerType ControllerType { get; set; }
+
+ /// <summary>
+ /// Player's Index for the controller
+ /// </summary>
+ public PlayerIndex PlayerIndex { get; set; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/Hid/Key.cs b/Ryujinx.Common/Configuration/Hid/Key.cs
index b658396b..67177eec 100644
--- a/Ryujinx.Common/Configuration/Hid/Key.cs
+++ b/Ryujinx.Common/Configuration/Hid/Key.cs
@@ -148,6 +148,7 @@
Slash = 128,
BackSlash = 129,
NonUSBackSlash = 130,
- LastKey = 131
+ LastKey = 131,
+ Unbound
}
}
diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardConfig.cs b/Ryujinx.Common/Configuration/Hid/KeyboardConfig.cs
new file mode 100644
index 00000000..664fdff0
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/KeyboardConfig.cs
@@ -0,0 +1,20 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+ public class KeyboardConfig : InputConfig
+ {
+ /// <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; set; }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
index 30cc8d84..19cc0487 100644
--- a/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
+++ b/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
@@ -1,7 +1,9 @@
-namespace Ryujinx.Configuration.Hid
+using Ryujinx.Configuration.Hid;
+
+namespace Ryujinx.Common.Configuration.Hid
{
public struct KeyboardHotkeys
{
public Key ToggleVsync { get; set; }
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs b/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
index c221b5e8..00816e56 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
+++ b/Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
@@ -2,14 +2,19 @@
{
public struct NpadControllerLeft
{
- public ControllerInputId Stick { get; set; }
+ public ControllerInputId StickX { get; set; }
+ public bool InvertStickX { get; set; }
+ public ControllerInputId StickY { get; set; }
+ public bool InvertStickY { get; set; }
public ControllerInputId StickButton { get; set; }
public ControllerInputId ButtonMinus { get; set; }
public ControllerInputId ButtonL { get; set; }
public ControllerInputId ButtonZl { get; set; }
+ public ControllerInputId ButtonSl { get; set; }
+ public ControllerInputId ButtonSr { get; set; }
public ControllerInputId DPadUp { get; set; }
public ControllerInputId DPadDown { get; set; }
public ControllerInputId DPadLeft { get; set; }
public ControllerInputId DPadRight { get; set; }
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs b/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
index f52f6f16..b7b289cc 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
+++ b/Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
@@ -2,7 +2,10 @@
{
public struct NpadControllerRight
{
- public ControllerInputId Stick { get; set; }
+ public ControllerInputId StickX { get; set; }
+ public bool InvertStickX { get; set; }
+ public ControllerInputId StickY { get; set; }
+ public bool InvertStickY { get; set; }
public ControllerInputId StickButton { get; set; }
public ControllerInputId ButtonA { get; set; }
public ControllerInputId ButtonB { get; set; }
@@ -11,5 +14,7 @@
public ControllerInputId ButtonPlus { get; set; }
public ControllerInputId ButtonR { get; set; }
public ControllerInputId ButtonZr { get; set; }
+ public ControllerInputId ButtonSl { get; set; }
+ public ControllerInputId ButtonSr { get; set; }
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
deleted file mode 100644
index 5ae82756..00000000
--- a/Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Ryujinx.UI.Input
-{
- public class NpadKeyboard
- {
- /// <summary>
- /// Left JoyCon Keyboard Bindings
- /// </summary>
- public Configuration.Hid.NpadKeyboardLeft LeftJoycon { get; set; }
-
- /// <summary>
- /// Right JoyCon Keyboard Bindings
- /// </summary>
- public Configuration.Hid.NpadKeyboardRight RightJoycon { get; set; }
-
- /// <summary>
- /// Hotkey Keyboard Bindings
- /// </summary>
- public Configuration.Hid.KeyboardHotkeys Hotkeys { get; set; }
- }
-}
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
index 4a61d932..6b78f5b6 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
@@ -1,4 +1,6 @@
-namespace Ryujinx.Configuration.Hid
+using Ryujinx.Configuration.Hid;
+
+namespace Ryujinx.Common.Configuration.Hid
{
public struct NpadKeyboardLeft
{
@@ -14,5 +16,7 @@
public Key ButtonMinus { get; set; }
public Key ButtonL { get; set; }
public Key ButtonZl { get; set; }
+ public Key ButtonSl { get; set; }
+ public Key ButtonSr { get; set; }
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs b/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
index 0677b573..e2109902 100644
--- a/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
+++ b/Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
@@ -1,4 +1,6 @@
-namespace Ryujinx.Configuration.Hid
+using Ryujinx.Configuration.Hid;
+
+namespace Ryujinx.Common.Configuration.Hid
{
public struct NpadKeyboardRight
{
@@ -14,5 +16,7 @@
public Key ButtonPlus { get; set; }
public Key ButtonR { get; set; }
public Key ButtonZr { get; set; }
+ public Key ButtonSl { get; set; }
+ public Key ButtonSr { get; set; }
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs b/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs
new file mode 100644
index 00000000..2e34cb96
--- /dev/null
+++ b/Ryujinx.Common/Configuration/Hid/PlayerIndex.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.Common.Configuration.Hid
+{
+ // This enum was duplicated from Ryujinx.HLE.HOS.Services.Hid.PlayerIndex and should be kept identical
+ public enum PlayerIndex : int
+ {
+ Player1 = 0,
+ Player2 = 1,
+ Player3 = 2,
+ Player4 = 3,
+ Player5 = 4,
+ Player6 = 5,
+ Player7 = 6,
+ Player8 = 7,
+ Handheld = 8,
+ Unknown = 9,
+ Auto = 10 // Shouldn't be used directly
+ }
+} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
index e07577ed..c4935a64 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
@@ -7,16 +7,16 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public class Hid
{
private readonly Switch _device;
- private long _hidMemoryAddress;
+ private readonly long _hidMemoryAddress;
internal ref HidSharedMemory SharedMemory => ref _device.Memory.GetStructRef<HidSharedMemory>(_hidMemoryAddress);
internal const int SharedMemEntryCount = 17;
public DebugPadDevice DebugPad;
- public TouchDevice Touchscreen;
- public MouseDevice Mouse;
+ public TouchDevice Touchscreen;
+ public MouseDevice Mouse;
public KeyboardDevice Keyboard;
- public NpadDevices Npads;
+ public NpadDevices Npads;
static Hid()
{
@@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public Hid(in Switch device, long sharedHidMemoryAddress)
{
- _device = device;
+ _device = device;
_hidMemoryAddress = sharedHidMemoryAddress;
device.Memory.FillWithZeros(sharedHidMemoryAddress, Horizon.HidSize);
@@ -56,26 +56,26 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public void InitDevices()
{
- DebugPad = new DebugPadDevice(_device, true);
+ DebugPad = new DebugPadDevice(_device, true);
Touchscreen = new TouchDevice(_device, true);
- Mouse = new MouseDevice(_device, false);
- Keyboard = new KeyboardDevice(_device, false);
- Npads = new NpadDevices(_device, true);
+ Mouse = new MouseDevice(_device, false);
+ Keyboard = new KeyboardDevice(_device, false);
+ Npads = new NpadDevices(_device, true);
}
public ControllerKeys UpdateStickButtons(JoystickPosition leftStick, JoystickPosition rightStick)
{
ControllerKeys result = 0;
- result |= (leftStick.Dx < 0) ? ControllerKeys.LStickLeft : result;
+ result |= (leftStick.Dx < 0) ? ControllerKeys.LStickLeft : result;
result |= (leftStick.Dx > 0) ? ControllerKeys.LStickRight : result;
- result |= (leftStick.Dy < 0) ? ControllerKeys.LStickDown : result;
- result |= (leftStick.Dy > 0) ? ControllerKeys.LStickUp : result;
+ result |= (leftStick.Dy < 0) ? ControllerKeys.LStickDown : result;
+ result |= (leftStick.Dy > 0) ? ControllerKeys.LStickUp : result;
- result |= (rightStick.Dx < 0) ? ControllerKeys.RStickLeft : result;
+ result |= (rightStick.Dx < 0) ? ControllerKeys.RStickLeft : result;
result |= (rightStick.Dx > 0) ? ControllerKeys.RStickRight : result;
- result |= (rightStick.Dy < 0) ? ControllerKeys.RStickDown : result;
- result |= (rightStick.Dy > 0) ? ControllerKeys.RStickUp : result;
+ result |= (rightStick.Dy < 0) ? ControllerKeys.RStickDown : result;
+ result |= (rightStick.Dy > 0) ? ControllerKeys.RStickUp : result;
return result;
}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
index ff330312..c4c9d095 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
@@ -1,6 +1,6 @@
using System;
-using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Kernel.Threading;
namespace Ryujinx.HLE.HOS.Services.Hid
{
@@ -9,14 +9,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid
internal NpadJoyHoldType JoyHold = NpadJoyHoldType.Vertical;
internal bool SixAxisActive = false; // TODO: link to hidserver when implemented
- enum FilterState
+ private enum FilterState
{
Unconfigured = 0,
- Configured = 1,
- Accepted = 2
+ Configured = 1,
+ Accepted = 2
}
- struct NpadConfig
+ private struct NpadConfig
{
public ControllerType ConfiguredType;
public FilterState State;
@@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public ControllerType SupportedStyleSets
{
- get { return _supportedStyleSets; }
+ get => _supportedStyleSets;
set
{
if (_supportedStyleSets != value) // Deal with spamming
@@ -46,9 +46,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public PlayerIndex PrimaryController { get; set; } = PlayerIndex.Unknown;
- KEvent[] _styleSetUpdateEvents;
+ private KEvent[] _styleSetUpdateEvents;
- static readonly Array3<BatteryCharge> _fullBattery;
+ private static readonly Array3<BatteryCharge> _fullBattery;
public NpadDevices(Switch device, bool active = true) : base(device, active)
{
@@ -68,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
{
for (int i = 0; i < configs.Length; ++i)
{
- PlayerIndex player = configs[i].Player;
+ PlayerIndex player = configs[i].Player;
ControllerType controllerType = configs[i].Type;
if (player > PlayerIndex.Handheld)
@@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
MatchControllers();
}
- void MatchControllers()
+ private void MatchControllers()
{
PrimaryController = PlayerIndex.Unknown;
@@ -141,7 +141,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return ref _styleSetUpdateEvents[(int)player];
}
- void InitController(PlayerIndex player, ControllerType type)
+ private void InitController(PlayerIndex player, ControllerType type)
{
if (type == ControllerType.Handheld)
{
@@ -155,13 +155,13 @@ namespace Ryujinx.HLE.HOS.Services.Hid
// TODO: Allow customizing colors at config
NpadStateHeader defaultHeader = new NpadStateHeader
{
- IsHalf = false,
- SingleColorBody = NpadColor.BodyGray,
+ IsHalf = false,
+ SingleColorBody = NpadColor.BodyGray,
SingleColorButtons = NpadColor.ButtonGray,
- LeftColorBody = NpadColor.BodyNeonBlue,
- LeftColorButtons = NpadColor.ButtonGray,
- RightColorBody = NpadColor.BodyNeonRed,
- RightColorButtons = NpadColor.ButtonGray
+ LeftColorBody = NpadColor.BodyNeonBlue,
+ LeftColorButtons = NpadColor.ButtonGray,
+ RightColorBody = NpadColor.BodyNeonRed,
+ RightColorButtons = NpadColor.ButtonGray
};
controller.SystemProperties = NpadSystemProperties.PowerInfo0Connected |
@@ -173,44 +173,44 @@ namespace Ryujinx.HLE.HOS.Services.Hid
switch (type)
{
case ControllerType.ProController:
- defaultHeader.Type = ControllerType.ProController;
- controller.DeviceType = DeviceType.FullKey;
+ defaultHeader.Type = ControllerType.ProController;
+ controller.DeviceType = DeviceType.FullKey;
controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
NpadSystemProperties.PlusButtonCapability |
NpadSystemProperties.MinusButtonCapability;
break;
case ControllerType.Handheld:
- defaultHeader.Type = ControllerType.Handheld;
- controller.DeviceType = DeviceType.HandheldLeft |
+ defaultHeader.Type = ControllerType.Handheld;
+ controller.DeviceType = DeviceType.HandheldLeft |
DeviceType.HandheldRight;
controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
NpadSystemProperties.PlusButtonCapability |
NpadSystemProperties.MinusButtonCapability;
break;
case ControllerType.JoyconPair:
- defaultHeader.Type = ControllerType.JoyconPair;
- controller.DeviceType = DeviceType.JoyLeft |
+ defaultHeader.Type = ControllerType.JoyconPair;
+ controller.DeviceType = DeviceType.JoyLeft |
DeviceType.JoyRight;
controller.SystemProperties |= NpadSystemProperties.AbxyButtonOriented |
NpadSystemProperties.PlusButtonCapability |
NpadSystemProperties.MinusButtonCapability;
break;
case ControllerType.JoyconLeft:
- defaultHeader.Type = ControllerType.JoyconLeft;
- defaultHeader.IsHalf = true;
- controller.DeviceType = DeviceType.JoyLeft;
+ defaultHeader.Type = ControllerType.JoyconLeft;
+ defaultHeader.IsHalf = true;
+ controller.DeviceType = DeviceType.JoyLeft;
controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
NpadSystemProperties.MinusButtonCapability;
break;
case ControllerType.JoyconRight:
- defaultHeader.Type = ControllerType.JoyconRight;
- defaultHeader.IsHalf = true;
- controller.DeviceType = DeviceType.JoyRight;
+ defaultHeader.Type = ControllerType.JoyconRight;
+ defaultHeader.IsHalf = true;
+ controller.DeviceType = DeviceType.JoyRight;
controller.SystemProperties |= NpadSystemProperties.SlSrButtonOriented |
NpadSystemProperties.PlusButtonCapability;
break;
case ControllerType.Pokeball:
- defaultHeader.Type = ControllerType.Pokeball;
+ defaultHeader.Type = ControllerType.Pokeball;
controller.DeviceType = DeviceType.Palma;
break;
}
@@ -229,16 +229,16 @@ namespace Ryujinx.HLE.HOS.Services.Hid
Logger.PrintInfo(LogClass.Hid, $"Connected ControllerType {type} to PlayerIndex {player}");
}
- static NpadLayoutsIndex ControllerTypeToLayout(ControllerType controllerType)
+ private static NpadLayoutsIndex ControllerTypeToLayout(ControllerType controllerType)
=> controllerType switch
{
ControllerType.ProController => NpadLayoutsIndex.ProController,
- ControllerType.Handheld => NpadLayoutsIndex.Handheld,
- ControllerType.JoyconPair => NpadLayoutsIndex.JoyDual,
- ControllerType.JoyconLeft => NpadLayoutsIndex.JoyLeft,
- ControllerType.JoyconRight => NpadLayoutsIndex.JoyRight,
- ControllerType.Pokeball => NpadLayoutsIndex.Pokeball,
- _ => NpadLayoutsIndex.SystemExternal
+ ControllerType.Handheld => NpadLayoutsIndex.Handheld,
+ ControllerType.JoyconPair => NpadLayoutsIndex.JoyDual,
+ ControllerType.JoyconLeft => NpadLayoutsIndex.JoyLeft,
+ ControllerType.JoyconRight => NpadLayoutsIndex.JoyRight,
+ ControllerType.Pokeball => NpadLayoutsIndex.Pokeball,
+ _ => NpadLayoutsIndex.SystemExternal
};
public void SetGamepadsInput(params GamepadInput[] states)
@@ -251,8 +251,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
}
}
- void SetGamepadState(PlayerIndex player, ControllerKeys buttons,
- JoystickPosition leftJoystick, JoystickPosition rightJoystick)
+ private void SetGamepadState(PlayerIndex player, ControllerKeys buttons,
+ JoystickPosition leftJoystick, JoystickPosition rightJoystick)
{
if (player == PlayerIndex.Auto)
{
@@ -269,9 +269,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
return;
}
- ref ShMemNpad currentNpad = ref _device.Hid.SharedMemory.Npads[(int)player];
+ ref ShMemNpad currentNpad = ref _device.Hid.SharedMemory.Npads[(int)player];
ref NpadLayout currentLayout = ref currentNpad.Layouts[(int)ControllerTypeToLayout(currentNpad.Header.Type)];
- ref NpadState currentEntry = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry];
+ ref NpadState currentEntry = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry];
currentEntry.Buttons = buttons;
currentEntry.LStickX = leftJoystick.Dx;
@@ -284,7 +284,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
mainLayout.Entries[(int)mainLayout.Header.LatestEntry] = currentEntry;
}
- void UpdateAllEntries()
+ private void UpdateAllEntries()
{
ref Array10<ShMemNpad> controllers = ref _device.Hid.SharedMemory.Npads;
for (int i = 0; i < controllers.Length; ++i)
@@ -296,9 +296,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
int currentIndex = UpdateEntriesHeader(ref currentLayout.Header, out int previousIndex);
ref NpadState currentEntry = ref currentLayout.Entries[currentIndex];
- NpadState previousEntry = currentLayout.Entries[previousIndex];
+ NpadState previousEntry = currentLayout.Entries[previousIndex];
- currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
+ currentEntry.SampleTimestamp = previousEntry.SampleTimestamp + 1;
currentEntry.SampleTimestamp2 = previousEntry.SampleTimestamp2 + 1;
if (controllers[i].Header.Type == ControllerType.None)
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs
index e59ba312..477e1a84 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs
@@ -2,7 +2,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
{
public struct ControllerConfig
{
- public PlayerIndex Player;
+ public PlayerIndex Player;
public ControllerType Type;
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs
index 2488057e..633671df 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs
@@ -2,8 +2,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid
{
public struct GamepadInput
{
- public PlayerIndex PlayerId;
- public ControllerKeys Buttons;
+ public PlayerIndex PlayerId;
+ public ControllerKeys Buttons;
public JoystickPosition LStick;
public JoystickPosition RStick;
}
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs
index db0319ed..c91636b2 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs
@@ -5,41 +5,41 @@ namespace Ryujinx.HLE.HOS.Services.Hid
[Flags]
public enum ControllerKeys : long
{
- A = 1 << 0,
- B = 1 << 1,
- X = 1 << 2,
- Y = 1 << 3,
- LStick = 1 << 4,
- RStick = 1 << 5,
- L = 1 << 6,
- R = 1 << 7,
- Zl = 1 << 8,
- Zr = 1 << 9,
- Plus = 1 << 10,
- Minus = 1 << 11,
- DpadLeft = 1 << 12,
- DpadUp = 1 << 13,
- DpadRight = 1 << 14,
- DpadDown = 1 << 15,
- LStickLeft = 1 << 16,
- LStickUp = 1 << 17,
+ A = 1 << 0,
+ B = 1 << 1,
+ X = 1 << 2,
+ Y = 1 << 3,
+ LStick = 1 << 4,
+ RStick = 1 << 5,
+ L = 1 << 6,
+ R = 1 << 7,
+ Zl = 1 << 8,
+ Zr = 1 << 9,
+ Plus = 1 << 10,
+ Minus = 1 << 11,
+ DpadLeft = 1 << 12,
+ DpadUp = 1 << 13,
+ DpadRight = 1 << 14,
+ DpadDown = 1 << 15,
+ LStickLeft = 1 << 16,
+ LStickUp = 1 << 17,
LStickRight = 1 << 18,
- LStickDown = 1 << 19,
- RStickLeft = 1 << 20,
- RStickUp = 1 << 21,
+ LStickDown = 1 << 19,
+ RStickLeft = 1 << 20,
+ RStickUp = 1 << 21,
RStickRight = 1 << 22,
- RStickDown = 1 << 23,
- SlLeft = 1 << 24,
- SrLeft = 1 << 25,
- SlRight = 1 << 26,
- SrRight = 1 << 27,
+ RStickDown = 1 << 23,
+ SlLeft = 1 << 24,
+ SrLeft = 1 << 25,
+ SlRight = 1 << 26,
+ SrRight = 1 << 27,
// Generic Catch-all
- Up = DpadUp | LStickUp | RStickUp,
- Down = DpadDown | LStickDown | RStickDown,
- Left = DpadLeft | LStickLeft | RStickLeft,
+ Up = DpadUp | LStickUp | RStickUp,
+ Down = DpadDown | LStickDown | RStickDown,
+ Left = DpadLeft | LStickLeft | RStickLeft,
Right = DpadRight | LStickRight | RStickRight,
- Sl = SlLeft | SlRight,
- Sr = SrLeft | SrRight
+ Sl = SlLeft | SlRight,
+ Sr = SrLeft | SrRight
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs
index f65c3079..b2d34e8e 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs
@@ -6,14 +6,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid
public enum ControllerType : int
{
None,
- ProController = 1 << 0,
- Handheld = 1 << 1,
- JoyconPair = 1 << 2,
- JoyconLeft = 1 << 3,
- JoyconRight = 1 << 4,
- Invalid = 1 << 5,
- Pokeball = 1 << 6,
+ ProController = 1 << 0,
+ Handheld = 1 << 1,
+ JoyconPair = 1 << 2,
+ JoyconLeft = 1 << 3,
+ JoyconRight = 1 << 4,
+ Invalid = 1 << 5,
+ Pokeball = 1 << 6,
SystemExternal = 1 << 29,
- System = 1 << 30
+ System = 1 << 30
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs
index 29eb8d3d..c4419336 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs
@@ -2,12 +2,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid
{
enum NpadLayoutsIndex : int
{
- ProController = 0,
- Handheld = 1,
- JoyDual = 2,
- JoyLeft = 3,
- JoyRight = 4,
- Pokeball = 5,
+ ProController = 0,
+ Handheld = 1,
+ JoyDual = 2,
+ JoyLeft = 3,
+ JoyRight = 4,
+ Pokeball = 5,
SystemExternal = 6
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/Device.cs b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/Device.cs
index 7eaf4ac8..3ff3489b 100644
--- a/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/Device.cs
+++ b/Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/Device.cs
@@ -14,6 +14,6 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp.UserManager
public DeviceState State = DeviceState.Unavailable;
public PlayerIndex Handle;
- public NpadIdType NpadIdType;
+ public NpadIdType NpadIdType;
}
} \ No newline at end of file
diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json
index 8df901e2..c5d08f9f 100644
--- a/Ryujinx/Config.json
+++ b/Ryujinx/Config.json
@@ -1,5 +1,5 @@
{
- "version": 5,
+ "version": 6,
"max_anisotropy": -1,
"graphics_shaders_dump_path": "",
"logging_enable_debug": false,
@@ -22,7 +22,6 @@
"enable_fs_integrity_checks": true,
"fs_global_access_log_mode": 0,
"ignore_missing_services": false,
- "controller_type": "Handheld",
"gui_columns": {
"fav_column": true,
"icon_column": true,
@@ -39,65 +38,47 @@
"enable_custom_theme": false,
"custom_theme_path": "",
"enable_keyboard": false,
- "keyboard_controls": {
- "left_joycon": {
- "stick_up": "W",
- "stick_down": "S",
- "stick_left": "A",
- "stick_right": "D",
- "stick_button": "F",
- "dpad_up": "Up",
- "dpad_down": "Down",
- "dpad_left": "Left",
- "dpad_right": "Right",
- "button_minus": "Minus",
- "button_l": "E",
- "button_zl": "Q"
- },
- "right_joycon": {
- "stick_up": "I",
- "stick_down": "K",
- "stick_left": "J",
- "stick_right": "L",
- "stick_button": "H",
- "button_a": "Z",
- "button_b": "X",
- "button_x": "C",
- "button_y": "V",
- "button_plus": "Plus",
- "button_r": "U",
- "button_zr": "O"
- },
- "hotkeys": {
- "toggle_vsync": "Tab"
+ "keyboard_config": [
+ {
+ "index": 0,
+ "controller_type": "JoyconPair",
+ "player_index": "Player1",
+ "left_joycon": {
+ "stick_up": "W",
+ "stick_down": "S",
+ "stick_left": "A",
+ "stick_right": "D",
+ "stick_button": "F",
+ "dpad_up": "Up",
+ "dpad_down": "Down",
+ "dpad_left": "Left",
+ "dpad_right": "Right",
+ "button_minus": "Minus",
+ "button_l": "E",
+ "button_zl": "Q",
+ "button_sl": "Unbound",
+ "button_sr": "Unbound"
+ },
+ "right_joycon": {
+ "stick_up": "I",
+ "stick_down": "K",
+ "stick_left": "J",
+ "stick_right": "L",
+ "stick_button": "H",
+ "button_a": "Z",
+ "button_b": "X",
+ "button_x": "C",
+ "button_y": "V",
+ "button_plus": "Plus",
+ "button_r": "U",
+ "button_zr": "O",
+ "button_sl": "Unbound",
+ "button_sr": "Unbound"
+ },
+ "hotkeys": {
+ "toggle_vsync": "Tab"
+ }
}
- },
- "joystick_controls": {
- "enabled": true,
- "index": 0,
- "deadzone": 0.05,
- "trigger_threshold": 0.5,
- "left_joycon": {
- "stick": "Axis0",
- "stick_button": "Button8",
- "button_minus": "Button6",
- "button_l": "Button4",
- "button_zl": "Axis2",
- "dpad_up": "Hat0Up",
- "dpad_down": "Hat0Down",
- "dpad_left": "Hat0Left",
- "dpad_right": "Hat0Right"
- },
- "right_joycon": {
- "stick": "Axis3",
- "stick_button": "Button9",
- "button_a": "Button1",
- "button_b": "Button0",
- "button_x": "Button3",
- "button_y": "Button2",
- "button_plus": "Button7",
- "button_r": "Button5",
- "button_zr": "Axis5"
- }
- }
+ ],
+ "controller_config": []
} \ No newline at end of file
diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj
index 28a0b601..b8258f58 100644
--- a/Ryujinx/Ryujinx.csproj
+++ b/Ryujinx/Ryujinx.csproj
@@ -31,9 +31,10 @@
<ItemGroup>
<None Remove="Ui\AboutWindow.glade" />
- <None Remove="Ui\assets\BlueCon.png" />
- <None Remove="Ui\assets\ProCon.png" />
- <None Remove="Ui\assets\RedCon.png" />
+ <None Remove="Ui\assets\JoyConLeft.svg" />
+ <None Remove="Ui\assets\JoyConPair.svg" />
+ <None Remove="Ui\assets\JoyConRight.svg" />
+ <None Remove="Ui\assets\ProCon.svg" />
<None Remove="Ui\assets\NCAIcon.png" />
<None Remove="Ui\assets\NROIcon.png" />
<None Remove="Ui\assets\NSOIcon.png" />
@@ -41,21 +42,23 @@
<None Remove="Ui\assets\XCIIcon.png" />
<None Remove="Ui\assets\DiscordLogo.png" />
<None Remove="Ui\assets\GitHubLogo.png" />
- <None Remove="Ui\assets\JoyCon.png" />
<None Remove="Ui\assets\PatreonLogo.png" />
<None Remove="Ui\assets\Icon.png" />
<None Remove="Ui\assets\TwitterLogo.png" />
+ <None Remove="Ui\ControllerWindow.glade" />
<None Remove="Ui\GameTableContextMenu.glade" />
<None Remove="Ui\MainWindow.glade" />
- <None Remove="Ui\SwitchSettings.glade" />
+ <None Remove="Ui\ProfileDialog.glade" />
+ <None Remove="Ui\SettingsWindow.glade" />
<None Remove="Ui\TitleUpdateWindow.glade" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Ui\AboutWindow.glade" />
- <EmbeddedResource Include="Ui\assets\BlueCon.png" />
- <EmbeddedResource Include="Ui\assets\ProCon.png" />
- <EmbeddedResource Include="Ui\assets\RedCon.png" />
+ <EmbeddedResource Include="Ui\assets\JoyConLeft.svg" />
+ <EmbeddedResource Include="Ui\assets\JoyConPair.svg" />
+ <EmbeddedResource Include="Ui\assets\JoyConRight.svg" />
+ <EmbeddedResource Include="Ui\assets\ProCon.svg" />
<EmbeddedResource Include="Ui\assets\NCAIcon.png" />
<EmbeddedResource Include="Ui\assets\NROIcon.png" />
<EmbeddedResource Include="Ui\assets\NSOIcon.png" />
@@ -63,13 +66,14 @@
<EmbeddedResource Include="Ui\assets\XCIIcon.png" />
<EmbeddedResource Include="Ui\assets\DiscordLogo.png" />
<EmbeddedResource Include="Ui\assets\GitHubLogo.png" />
- <EmbeddedResource Include="Ui\assets\JoyCon.png" />
<EmbeddedResource Include="Ui\assets\PatreonLogo.png" />
<EmbeddedResource Include="Ui\assets\Icon.png" />
<EmbeddedResource Include="Ui\assets\TwitterLogo.png" />
+ <EmbeddedResource Include="Ui\ControllerWindow.glade" />
<EmbeddedResource Include="Ui\GameTableContextMenu.glade" />
<EmbeddedResource Include="Ui\MainWindow.glade" />
- <EmbeddedResource Include="Ui\SwitchSettings.glade" />
+ <EmbeddedResource Include="Ui\ProfileDialog.glade" />
+ <EmbeddedResource Include="Ui\SettingsWindow.glade" />
<EmbeddedResource Include="Ui\TitleUpdateWindow.glade" />
</ItemGroup>
diff --git a/Ryujinx/Ui/AboutWindow.cs b/Ryujinx/Ui/AboutWindow.cs
index 6a18058a..5f1645da 100644
--- a/Ryujinx/Ui/AboutWindow.cs
+++ b/Ryujinx/Ui/AboutWindow.cs
@@ -12,7 +12,6 @@ namespace Ryujinx.Ui
{
#pragma warning disable CS0649
#pragma warning disable IDE0044
- [GUI] Window _aboutWin;
[GUI] Label _versionText;
[GUI] Image _ryujinxLogo;
[GUI] Image _patreonLogo;
@@ -28,7 +27,7 @@ namespace Ryujinx.Ui
{
builder.Autoconnect(this);
- _aboutWin.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
+ this.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
_ryujinxLogo.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png" , 100, 100);
_patreonLogo.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.PatreonLogo.png", 30 , 30 );
_gitHubLogo.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.GitHubLogo.png" , 30 , 30 );
diff --git a/Ryujinx/Ui/ApplicationLibrary.cs b/Ryujinx/Ui/ApplicationLibrary.cs
index 02b6541f..2dd88196 100644
--- a/Ryujinx/Ui/ApplicationLibrary.cs
+++ b/Ryujinx/Ui/ApplicationLibrary.cs
@@ -413,7 +413,7 @@ namespace Ryujinx.Ui
Version = version,
TimePlayed = ConvertSecondsToReadableString(appMetadata.TimePlayed),
LastPlayed = appMetadata.LastPlayed,
- FileExtension = Path.GetExtension(applicationPath).ToUpper().Remove(0 ,1),
+ FileExtension = Path.GetExtension(applicationPath).ToUpper().Remove(0, 1),
FileSize = (fileSize < 1) ? (fileSize * 1024).ToString("0.##") + "MB" : fileSize.ToString("0.##") + "GB",
Path = applicationPath,
SaveDataPath = saveDataPath,
diff --git a/Ryujinx/Ui/ControllerWindow.cs b/Ryujinx/Ui/ControllerWindow.cs
new file mode 100644
index 00000000..581c7d56
--- /dev/null
+++ b/Ryujinx/Ui/ControllerWindow.cs
@@ -0,0 +1,925 @@
+using Gtk;
+using OpenTK.Input;
+using System;
+using System.IO;
+using System.Reflection;
+using System.Threading;
+using Ryujinx.Configuration;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.Common.Utilities;
+using Ryujinx.HLE.FileSystem;
+
+using GUI = Gtk.Builder.ObjectAttribute;
+using Key = Ryujinx.Configuration.Hid.Key;
+
+namespace Ryujinx.Ui
+{
+ public class ControllerWindow : Window
+ {
+ private PlayerIndex _playerIndex;
+ private InputConfig _inputConfig;
+ private bool _isWaitingForInput;
+ private VirtualFileSystem _virtualFileSystem;
+
+#pragma warning disable CS0649, IDE0044
+ [GUI] Adjustment _controllerDeadzoneLeft;
+ [GUI] Adjustment _controllerDeadzoneRight;
+ [GUI] Adjustment _controllerTriggerThreshold;
+ [GUI] ComboBoxText _inputDevice;
+ [GUI] ComboBoxText _profile;
+ [GUI] ToggleButton _refreshInputDevicesButton;
+ [GUI] Box _settingsBox;
+ [GUI] Grid _leftStickKeyboard;
+ [GUI] Grid _leftStickController;
+ [GUI] Box _deadZoneLeftBox;
+ [GUI] Grid _rightStickKeyboard;
+ [GUI] Grid _rightStickController;
+ [GUI] Box _deadZoneRightBox;
+ [GUI] Grid _leftSideTriggerBox;
+ [GUI] Grid _rightSideTriggerBox;
+ [GUI] Box _triggerThresholdBox;
+ [GUI] ComboBoxText _controllerType;
+ [GUI] ToggleButton _lStickX;
+ [GUI] CheckButton _invertLStickX;
+ [GUI] ToggleButton _lStickY;
+ [GUI] CheckButton _invertLStickY;
+ [GUI] ToggleButton _lStickUp;
+ [GUI] ToggleButton _lStickDown;
+ [GUI] ToggleButton _lStickLeft;
+ [GUI] ToggleButton _lStickRight;
+ [GUI] ToggleButton _lStickButton;
+ [GUI] ToggleButton _dpadUp;
+ [GUI] ToggleButton _dpadDown;
+ [GUI] ToggleButton _dpadLeft;
+ [GUI] ToggleButton _dpadRight;
+ [GUI] ToggleButton _minus;
+ [GUI] ToggleButton _l;
+ [GUI] ToggleButton _zL;
+ [GUI] ToggleButton _rStickX;
+ [GUI] CheckButton _invertRStickX;
+ [GUI] ToggleButton _rStickY;
+ [GUI] CheckButton _invertRStickY;
+ [GUI] ToggleButton _rStickUp;
+ [GUI] ToggleButton _rStickDown;
+ [GUI] ToggleButton _rStickLeft;
+ [GUI] ToggleButton _rStickRight;
+ [GUI] ToggleButton _rStickButton;
+ [GUI] ToggleButton _a;
+ [GUI] ToggleButton _b;
+ [GUI] ToggleButton _x;
+ [GUI] ToggleButton _y;
+ [GUI] ToggleButton _plus;
+ [GUI] ToggleButton _r;
+ [GUI] ToggleButton _zR;
+ [GUI] ToggleButton _lSl;
+ [GUI] ToggleButton _lSr;
+ [GUI] ToggleButton _rSl;
+ [GUI] ToggleButton _rSr;
+ [GUI] Image _controllerImage;
+#pragma warning restore CS0649, IDE0044
+
+ public ControllerWindow(PlayerIndex controllerId, VirtualFileSystem virtualFileSystem) : this(new Builder("Ryujinx.Ui.ControllerWindow.glade"), controllerId, virtualFileSystem) { }
+
+ private ControllerWindow(Builder builder, PlayerIndex controllerId, VirtualFileSystem virtualFileSystem) : base(builder.GetObject("_controllerWin").Handle)
+ {
+ builder.Autoconnect(this);
+
+ this.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
+
+ _playerIndex = controllerId;
+ _virtualFileSystem = virtualFileSystem;
+ _inputConfig = ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerIndex);
+
+ //Bind Events
+ _lStickX.Clicked += Button_Pressed;
+ _lStickY.Clicked += Button_Pressed;
+ _lStickUp.Clicked += Button_Pressed;
+ _lStickDown.Clicked += Button_Pressed;
+ _lStickLeft.Clicked += Button_Pressed;
+ _lStickRight.Clicked += Button_Pressed;
+ _lStickButton.Clicked += Button_Pressed;
+ _dpadUp.Clicked += Button_Pressed;
+ _dpadDown.Clicked += Button_Pressed;
+ _dpadLeft.Clicked += Button_Pressed;
+ _dpadRight.Clicked += Button_Pressed;
+ _minus.Clicked += Button_Pressed;
+ _l.Clicked += Button_Pressed;
+ _zL.Clicked += Button_Pressed;
+ _lSl.Clicked += Button_Pressed;
+ _lSr.Clicked += Button_Pressed;
+ _rStickX.Clicked += Button_Pressed;
+ _rStickY.Clicked += Button_Pressed;
+ _rStickUp.Clicked += Button_Pressed;
+ _rStickDown.Clicked += Button_Pressed;
+ _rStickLeft.Clicked += Button_Pressed;
+ _rStickRight.Clicked += Button_Pressed;
+ _rStickButton.Clicked += Button_Pressed;
+ _a.Clicked += Button_Pressed;
+ _b.Clicked += Button_Pressed;
+ _x.Clicked += Button_Pressed;
+ _y.Clicked += Button_Pressed;
+ _plus.Clicked += Button_Pressed;
+ _r.Clicked += Button_Pressed;
+ _zR.Clicked += Button_Pressed;
+ _rSl.Clicked += Button_Pressed;
+ _rSr.Clicked += Button_Pressed;
+
+ // Setup current values
+ UpdateInputDeviceList();
+ SetAvailableOptions();
+
+ ClearValues();
+ if (_inputDevice.ActiveId != null) SetCurrentValues();
+ }
+
+ private void UpdateInputDeviceList()
+ {
+ _inputDevice.RemoveAll();
+ _inputDevice.Append("disabled", "Disabled");
+ _inputDevice.SetActiveId("disabled");
+
+ for (int i = 0; i < 20; i++)
+ {
+ if (Keyboard.GetState(i).IsConnected)
+ _inputDevice.Append($"keyboard/{i}", $"Keyboard/{i}");
+
+ if (GamePad.GetState(i).IsConnected)
+ _inputDevice.Append($"controller/{i}", $"Controller/{i} ({GamePad.GetName(i)})");
+ }
+
+ switch (_inputConfig)
+ {
+ case KeyboardConfig keyboard:
+ _inputDevice.SetActiveId($"keyboard/{keyboard.Index}");
+ break;
+ case ControllerConfig controller:
+ _inputDevice.SetActiveId($"controller/{controller.Index}");
+ break;
+ }
+ }
+
+ private void SetAvailableOptions()
+ {
+ if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("keyboard"))
+ {
+ this.ShowAll();
+ _leftStickController.Hide();
+ _rightStickController.Hide();
+ _deadZoneLeftBox.Hide();
+ _deadZoneRightBox.Hide();
+ _triggerThresholdBox.Hide();
+ }
+ else if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("controller"))
+ {
+ this.ShowAll();
+ _leftStickKeyboard.Hide();
+ _rightStickKeyboard.Hide();
+ }
+ else
+ {
+ _settingsBox.Hide();
+ }
+
+ ClearValues();
+ }
+
+ private void SetCurrentValues()
+ {
+ SetControllerSpecificFields();
+
+ SetProfiles();
+
+ if (_inputDevice.ActiveId.StartsWith("keyboard") && _inputConfig is KeyboardConfig)
+ {
+ SetValues(_inputConfig);
+ }
+ else if (_inputDevice.ActiveId.StartsWith("controller") && _inputConfig is ControllerConfig)
+ {
+ SetValues(_inputConfig);
+ }
+ }
+
+ private void SetControllerSpecificFields()
+ {
+ _leftSideTriggerBox.Hide();
+ _rightSideTriggerBox.Hide();
+
+ switch (_controllerType.ActiveId)
+ {
+ case "JoyconLeft":
+ _leftSideTriggerBox.Show();
+ break;
+ case "JoyconRight":
+ _rightSideTriggerBox.Show();
+ break;
+ }
+
+ switch (_controllerType.ActiveId)
+ {
+ case "ProController":
+ _controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.ProCon.svg", 400, 400);
+ break;
+ case "JoyconLeft":
+ _controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyConLeft.svg", 400, 400);
+ break;
+ case "JoyconRight":
+ _controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyConRight.svg", 400, 400);
+ break;
+ default:
+ _controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyConPair.svg", 400, 400);
+ break;
+ }
+ }
+
+ private void ClearValues()
+ {
+ _lStickX.Label = "Unbound";
+ _lStickY.Label = "Unbound";
+ _lStickUp.Label = "Unbound";
+ _lStickDown.Label = "Unbound";
+ _lStickLeft.Label = "Unbound";
+ _lStickRight.Label = "Unbound";
+ _lStickButton.Label = "Unbound";
+ _dpadUp.Label = "Unbound";
+ _dpadDown.Label = "Unbound";
+ _dpadLeft.Label = "Unbound";
+ _dpadRight.Label = "Unbound";
+ _minus.Label = "Unbound";
+ _l.Label = "Unbound";
+ _zL.Label = "Unbound";
+ _lSl.Label = "Unbound";
+ _lSr.Label = "Unbound";
+ _rStickX.Label = "Unbound";
+ _rStickY.Label = "Unbound";
+ _rStickUp.Label = "Unbound";
+ _rStickDown.Label = "Unbound";
+ _rStickLeft.Label = "Unbound";
+ _rStickRight.Label = "Unbound";
+ _rStickButton.Label = "Unbound";
+ _a.Label = "Unbound";
+ _b.Label = "Unbound";
+ _x.Label = "Unbound";
+ _y.Label = "Unbound";
+ _plus.Label = "Unbound";
+ _r.Label = "Unbound";
+ _zR.Label = "Unbound";
+ _rSl.Label = "Unbound";
+ _rSr.Label = "Unbound";
+ _controllerDeadzoneLeft.Value = 0;
+ _controllerDeadzoneRight.Value = 0;
+ _controllerTriggerThreshold.Value = 0;
+ }
+
+ private void SetValues(InputConfig config)
+ {
+ switch (config)
+ {
+ case KeyboardConfig keyboardConfig:
+ _controllerType.SetActiveId(keyboardConfig.ControllerType.ToString());
+
+ _lStickUp.Label = keyboardConfig.LeftJoycon.StickUp.ToString();
+ _lStickDown.Label = keyboardConfig.LeftJoycon.StickDown.ToString();
+ _lStickLeft.Label = keyboardConfig.LeftJoycon.StickLeft.ToString();
+ _lStickRight.Label = keyboardConfig.LeftJoycon.StickRight.ToString();
+ _lStickButton.Label = keyboardConfig.LeftJoycon.StickButton.ToString();
+ _dpadUp.Label = keyboardConfig.LeftJoycon.DPadUp.ToString();
+ _dpadDown.Label = keyboardConfig.LeftJoycon.DPadDown.ToString();
+ _dpadLeft.Label = keyboardConfig.LeftJoycon.DPadLeft.ToString();
+ _dpadRight.Label = keyboardConfig.LeftJoycon.DPadRight.ToString();
+ _minus.Label = keyboardConfig.LeftJoycon.ButtonMinus.ToString();
+ _l.Label = keyboardConfig.LeftJoycon.ButtonL.ToString();
+ _zL.Label = keyboardConfig.LeftJoycon.ButtonZl.ToString();
+ _lSl.Label = keyboardConfig.LeftJoycon.ButtonSl.ToString();
+ _lSr.Label = keyboardConfig.LeftJoycon.ButtonSr.ToString();
+ _rStickUp.Label = keyboardConfig.RightJoycon.StickUp.ToString();
+ _rStickDown.Label = keyboardConfig.RightJoycon.StickDown.ToString();
+ _rStickLeft.Label = keyboardConfig.RightJoycon.StickLeft.ToString();
+ _rStickRight.Label = keyboardConfig.RightJoycon.StickRight.ToString();
+ _rStickButton.Label = keyboardConfig.RightJoycon.StickButton.ToString();
+ _a.Label = keyboardConfig.RightJoycon.ButtonA.ToString();
+ _b.Label = keyboardConfig.RightJoycon.ButtonB.ToString();
+ _x.Label = keyboardConfig.RightJoycon.ButtonX.ToString();
+ _y.Label = keyboardConfig.RightJoycon.ButtonY.ToString();
+ _plus.Label = keyboardConfig.RightJoycon.ButtonPlus.ToString();
+ _r.Label = keyboardConfig.RightJoycon.ButtonR.ToString();
+ _zR.Label = keyboardConfig.RightJoycon.ButtonZr.ToString();
+ _rSl.Label = keyboardConfig.RightJoycon.ButtonSl.ToString();
+ _rSr.Label = keyboardConfig.RightJoycon.ButtonSr.ToString();
+ break;
+ case ControllerConfig controllerConfig:
+ _controllerType.SetActiveId(controllerConfig.ControllerType.ToString());
+
+ _lStickX.Label = controllerConfig.LeftJoycon.StickX.ToString();
+ _invertLStickX.Active = controllerConfig.LeftJoycon.InvertStickX;
+ _lStickY.Label = controllerConfig.LeftJoycon.StickY.ToString();
+ _invertLStickY.Active = controllerConfig.LeftJoycon.InvertStickY;
+ _lStickButton.Label = controllerConfig.LeftJoycon.StickButton.ToString();
+ _dpadUp.Label = controllerConfig.LeftJoycon.DPadUp.ToString();
+ _dpadDown.Label = controllerConfig.LeftJoycon.DPadDown.ToString();
+ _dpadLeft.Label = controllerConfig.LeftJoycon.DPadLeft.ToString();
+ _dpadRight.Label = controllerConfig.LeftJoycon.DPadRight.ToString();
+ _minus.Label = controllerConfig.LeftJoycon.ButtonMinus.ToString();
+ _l.Label = controllerConfig.LeftJoycon.ButtonL.ToString();
+ _zL.Label = controllerConfig.LeftJoycon.ButtonZl.ToString();
+ _lSl.Label = controllerConfig.LeftJoycon.ButtonSl.ToString();
+ _lSr.Label = controllerConfig.LeftJoycon.ButtonSr.ToString();
+ _rStickX.Label = controllerConfig.RightJoycon.StickX.ToString();
+ _invertRStickX.Active = controllerConfig.RightJoycon.InvertStickX;
+ _rStickY.Label = controllerConfig.RightJoycon.StickY.ToString();
+ _invertRStickY.Active = controllerConfig.RightJoycon.InvertStickY;
+ _rStickButton.Label = controllerConfig.RightJoycon.StickButton.ToString();
+ _a.Label = controllerConfig.RightJoycon.ButtonA.ToString();
+ _b.Label = controllerConfig.RightJoycon.ButtonB.ToString();
+ _x.Label = controllerConfig.RightJoycon.ButtonX.ToString();
+ _y.Label = controllerConfig.RightJoycon.ButtonY.ToString();
+ _plus.Label = controllerConfig.RightJoycon.ButtonPlus.ToString();
+ _r.Label = controllerConfig.RightJoycon.ButtonR.ToString();
+ _zR.Label = controllerConfig.RightJoycon.ButtonZr.ToString();
+ _rSl.Label = controllerConfig.RightJoycon.ButtonSl.ToString();
+ _rSr.Label = controllerConfig.RightJoycon.ButtonSr.ToString();
+ _controllerDeadzoneLeft.Value = controllerConfig.DeadzoneLeft;
+ _controllerDeadzoneRight.Value = controllerConfig.DeadzoneRight;
+ _controllerTriggerThreshold.Value = controllerConfig.TriggerThreshold;
+ break;
+ }
+ }
+
+ private InputConfig GetValues()
+ {
+ if (_inputDevice.ActiveId.StartsWith("keyboard"))
+ {
+ Enum.TryParse(_lStickUp.Label, out Key lStickUp);
+ Enum.TryParse(_lStickDown.Label, out Key lStickDown);
+ Enum.TryParse(_lStickLeft.Label, out Key lStickLeft);
+ Enum.TryParse(_lStickRight.Label, out Key lStickRight);
+ Enum.TryParse(_lStickButton.Label, out Key lStickButton);
+ Enum.TryParse(_dpadUp.Label, out Key lDPadUp);
+ Enum.TryParse(_dpadDown.Label, out Key lDPadDown);
+ Enum.TryParse(_dpadLeft.Label, out Key lDPadLeft);
+ Enum.TryParse(_dpadRight.Label, out Key lDPadRight);
+ Enum.TryParse(_minus.Label, out Key lButtonMinus);
+ Enum.TryParse(_l.Label, out Key lButtonL);
+ Enum.TryParse(_zL.Label, out Key lButtonZl);
+ Enum.TryParse(_lSl.Label, out Key lButtonSl);
+ Enum.TryParse(_lSr.Label, out Key lButtonSr);
+
+ Enum.TryParse(_rStickUp.Label, out Key rStickUp);
+ Enum.TryParse(_rStickDown.Label, out Key rStickDown);
+ Enum.TryParse(_rStickLeft.Label, out Key rStickLeft);
+ Enum.TryParse(_rStickRight.Label, out Key rStickRight);
+ Enum.TryParse(_rStickButton.Label, out Key rStickButton);
+ Enum.TryParse(_a.Label, out Key rButtonA);
+ Enum.TryParse(_b.Label, out Key rButtonB);
+ Enum.TryParse(_x.Label, out Key rButtonX);
+ Enum.TryParse(_y.Label, out Key rButtonY);
+ Enum.TryParse(_plus.Label, out Key rButtonPlus);
+ Enum.TryParse(_r.Label, out Key rButtonR);
+ Enum.TryParse(_zR.Label, out Key rButtonZr);
+ Enum.TryParse(_rSl.Label, out Key rButtonSl);
+ Enum.TryParse(_rSr.Label, out Key rButtonSr);
+
+ return new KeyboardConfig
+ {
+ Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]),
+ ControllerType = Enum.Parse<ControllerType>(_controllerType.ActiveId),
+ PlayerIndex = _playerIndex,
+ LeftJoycon = new NpadKeyboardLeft
+ {
+ StickUp = lStickUp,
+ StickDown = lStickDown,
+ StickLeft = lStickLeft,
+ StickRight = lStickRight,
+ StickButton = lStickButton,
+ DPadUp = lDPadUp,
+ DPadDown = lDPadDown,
+ DPadLeft = lDPadLeft,
+ DPadRight = lDPadRight,
+ ButtonMinus = lButtonMinus,
+ ButtonL = lButtonL,
+ ButtonZl = lButtonZl,
+ ButtonSl = lButtonSl,
+ ButtonSr = lButtonSr
+ },
+ RightJoycon = new NpadKeyboardRight
+ {
+ StickUp = rStickUp,
+ StickDown = rStickDown,
+ StickLeft = rStickLeft,
+ StickRight = rStickRight,
+ StickButton = rStickButton,
+ ButtonA = rButtonA,
+ ButtonB = rButtonB,
+ ButtonX = rButtonX,
+ ButtonY = rButtonY,
+ ButtonPlus = rButtonPlus,
+ ButtonR = rButtonR,
+ ButtonZr = rButtonZr,
+ ButtonSl = rButtonSl,
+ ButtonSr = rButtonSr
+ },
+ Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = Key.Tab //TODO: Make this an option in the GUI
+ }
+ };
+ }
+
+ if (_inputDevice.ActiveId.StartsWith("controller"))
+ {
+ Enum.TryParse(_lStickX.Label, out ControllerInputId lStickX);
+ Enum.TryParse(_lStickY.Label, out ControllerInputId lStickY);
+ Enum.TryParse(_lStickButton.Label, out ControllerInputId lStickButton);
+ Enum.TryParse(_minus.Label, out ControllerInputId lButtonMinus);
+ Enum.TryParse(_l.Label, out ControllerInputId lButtonL);
+ Enum.TryParse(_zL.Label, out ControllerInputId lButtonZl);
+ Enum.TryParse(_lSl.Label, out ControllerInputId lButtonSl);
+ Enum.TryParse(_lSr.Label, out ControllerInputId lButtonSr);
+ Enum.TryParse(_dpadUp.Label, out ControllerInputId lDPadUp);
+ Enum.TryParse(_dpadDown.Label, out ControllerInputId lDPadDown);
+ Enum.TryParse(_dpadLeft.Label, out ControllerInputId lDPadLeft);
+ Enum.TryParse(_dpadRight.Label, out ControllerInputId lDPadRight);
+
+ Enum.TryParse(_rStickX.Label, out ControllerInputId rStickX);
+ Enum.TryParse(_rStickY.Label, out ControllerInputId rStickY);
+ Enum.TryParse(_rStickButton.Label, out ControllerInputId rStickButton);
+ Enum.TryParse(_a.Label, out ControllerInputId rButtonA);
+ Enum.TryParse(_b.Label, out ControllerInputId rButtonB);
+ Enum.TryParse(_x.Label, out ControllerInputId rButtonX);
+ Enum.TryParse(_y.Label, out ControllerInputId rButtonY);
+ Enum.TryParse(_plus.Label, out ControllerInputId rButtonPlus);
+ Enum.TryParse(_r.Label, out ControllerInputId rButtonR);
+ Enum.TryParse(_zR.Label, out ControllerInputId rButtonZr);
+ Enum.TryParse(_rSl.Label, out ControllerInputId rButtonSl);
+ Enum.TryParse(_rSr.Label, out ControllerInputId rButtonSr);
+
+ return new ControllerConfig
+ {
+ Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]),
+ ControllerType = Enum.Parse<ControllerType>(_controllerType.ActiveId),
+ PlayerIndex = _playerIndex,
+ DeadzoneLeft = (float)_controllerDeadzoneLeft.Value,
+ DeadzoneRight = (float)_controllerDeadzoneRight.Value,
+ TriggerThreshold = (float)_controllerTriggerThreshold.Value,
+ LeftJoycon = new NpadControllerLeft
+ {
+ InvertStickX = _invertLStickX.Active,
+ StickX = lStickX,
+ InvertStickY = _invertLStickY.Active,
+ StickY = lStickY,
+ StickButton = lStickButton,
+ ButtonMinus = lButtonMinus,
+ ButtonL = lButtonL,
+ ButtonZl = lButtonZl,
+ ButtonSl = lButtonSl,
+ ButtonSr = lButtonSr,
+ DPadUp = lDPadUp,
+ DPadDown = lDPadDown,
+ DPadLeft = lDPadLeft,
+ DPadRight = lDPadRight
+ },
+ RightJoycon = new NpadControllerRight
+ {
+ InvertStickX = _invertRStickX.Active,
+ StickX = rStickX,
+ InvertStickY = _invertRStickY.Active,
+ StickY = rStickY,
+ StickButton = rStickButton,
+ ButtonA = rButtonA,
+ ButtonB = rButtonB,
+ ButtonX = rButtonX,
+ ButtonY = rButtonY,
+ ButtonPlus = rButtonPlus,
+ ButtonR = rButtonR,
+ ButtonZr = rButtonZr,
+ ButtonSl = rButtonSl,
+ ButtonSr = rButtonSr
+ }
+ };
+ }
+
+ if (!_inputDevice.ActiveId.StartsWith("disabled"))
+ {
+ GtkDialog.CreateErrorDialog("Some fields entered where invalid and therefore your config was not saved.");
+ }
+
+ return null;
+ }
+
+ private static bool IsAnyKeyPressed(out Key pressedKey, int index = 0)
+ {
+ KeyboardState keyboardState = Keyboard.GetState(index);
+
+ foreach (Key key in Enum.GetValues(typeof(Key)))
+ {
+ if (keyboardState.IsKeyDown((OpenTK.Input.Key)key))
+ {
+ pressedKey = key;
+
+ return true;
+ }
+ }
+
+ pressedKey = Key.Unbound;
+
+ return false;
+ }
+
+ private static bool IsAnyButtonPressed(out ControllerInputId pressedButton, int index, double triggerThreshold)
+ {
+ JoystickState joystickState = Joystick.GetState(index);
+ JoystickCapabilities joystickCapabilities = Joystick.GetCapabilities(index);
+
+ //Buttons
+ for (int i = 0; i != joystickCapabilities.ButtonCount; i++)
+ {
+ if (joystickState.IsButtonDown(i))
+ {
+ Enum.TryParse($"Button{i}", out pressedButton);
+
+ return true;
+ }
+ }
+
+ //Axis
+ for (int i = 0; i != joystickCapabilities.AxisCount; i++)
+ {
+ if (joystickState.GetAxis(i) > 0.5f && joystickState.GetAxis(i) > triggerThreshold)
+ {
+ Enum.TryParse($"Axis{i}", out pressedButton);
+
+ return true;
+ }
+ }
+
+ //Hats
+ for (int i = 0; i != joystickCapabilities.HatCount; i++)
+ {
+ JoystickHatState hatState = joystickState.GetHat((JoystickHat)i);
+ string pos = null;
+
+ if (hatState.IsUp) pos = "Up";
+ if (hatState.IsDown) pos = "Down";
+ if (hatState.IsLeft) pos = "Left";
+ if (hatState.IsRight) pos = "Right";
+ if (pos == null) continue;
+
+ Enum.TryParse($"Hat{i}{pos}", out pressedButton);
+
+ return true;
+ }
+
+ pressedButton = ControllerInputId.Unbound;
+
+ return false;
+ }
+
+ private string GetProfileBasePath()
+ {
+ string path = System.IO.Path.Combine(_virtualFileSystem.GetBasePath(), "profiles");
+
+ if (_inputDevice.ActiveId.StartsWith("keyboard"))
+ {
+ path = System.IO.Path.Combine(path, "keyboard");
+ }
+ else if (_inputDevice.ActiveId.StartsWith("controller"))
+ {
+ path = System.IO.Path.Combine(path, "controller");
+ }
+
+ return path;
+ }
+
+ //Events
+ private void InputDevice_Changed(object sender, EventArgs args)
+ {
+ SetAvailableOptions();
+ SetControllerSpecificFields();
+
+ if (_inputDevice.ActiveId != null) SetProfiles();
+ }
+
+ private void Controller_Changed(object sender, EventArgs args)
+ {
+ SetControllerSpecificFields();
+ }
+
+ private void RefreshInputDevicesButton_Pressed(object sender, EventArgs args)
+ {
+ UpdateInputDeviceList();
+
+ _refreshInputDevicesButton.SetStateFlags(0, true);
+ }
+
+ private void Button_Pressed(object sender, EventArgs args)
+ {
+ if (_isWaitingForInput)
+ {
+ return;
+ }
+
+ _isWaitingForInput = true;
+
+ Thread inputThread = new Thread(() =>
+ {
+ Button button = (ToggleButton)sender;
+
+ if (_inputDevice.ActiveId.StartsWith("keyboard"))
+ {
+ Key pressedKey;
+
+ int index = int.Parse(_inputDevice.ActiveId.Split("/")[1]);
+ while (!IsAnyKeyPressed(out pressedKey, index))
+ {
+ if (Mouse.GetState().IsAnyButtonDown || Keyboard.GetState().IsKeyDown(OpenTK.Input.Key.Escape))
+ {
+ Application.Invoke(delegate
+ {
+ button.SetStateFlags(0, true);
+ });
+
+ _isWaitingForInput = false;
+
+ return;
+ }
+ }
+
+ Application.Invoke(delegate
+ {
+ button.Label = pressedKey.ToString();
+ button.SetStateFlags(0, true);
+ });
+ }
+ else if (_inputDevice.ActiveId.StartsWith("controller"))
+ {
+ ControllerInputId pressedButton;
+
+ int index = int.Parse(_inputDevice.ActiveId.Split("/")[1]);
+ while (!IsAnyButtonPressed(out pressedButton, index, _controllerTriggerThreshold.Value))
+ {
+ if (Mouse.GetState().IsAnyButtonDown || Keyboard.GetState().IsAnyKeyDown)
+ {
+ Application.Invoke(delegate
+ {
+ button.SetStateFlags(0, true);
+ });
+
+ _isWaitingForInput = false;
+
+ return;
+ }
+ }
+
+ Application.Invoke(delegate
+ {
+ button.Label = pressedButton.ToString();
+ button.SetStateFlags(0, true);
+ });
+ }
+
+ _isWaitingForInput = false;
+ });
+ inputThread.Name = "GUI.InputThread";
+ inputThread.IsBackground = true;
+ inputThread.Start();
+ }
+
+ private void SetProfiles()
+ {
+ string basePath = GetProfileBasePath();
+
+ if (!Directory.Exists(basePath))
+ {
+ Directory.CreateDirectory(basePath);
+ }
+
+ _profile.RemoveAll();
+ _profile.Append("default", "Default");
+
+ foreach (string profile in Directory.GetFiles(basePath, "*.*", SearchOption.AllDirectories))
+ {
+ _profile.Append(System.IO.Path.GetFileName(profile), System.IO.Path.GetFileNameWithoutExtension(profile));
+ }
+ }
+
+ private void ProfileLoad_Activated(object sender, EventArgs args)
+ {
+ ((ToggleButton)sender).SetStateFlags(0, true);
+
+ if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == null) return;
+
+ InputConfig config = null;
+ int pos = _profile.Active;
+
+ if (_profile.ActiveId == "default")
+ {
+ if (_inputDevice.ActiveId.StartsWith("keyboard"))
+ {
+ config = new KeyboardConfig
+ {
+ Index = 0,
+ ControllerType = ControllerType.JoyconPair,
+ 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,
+ ButtonSl = Key.Unbound,
+ ButtonSr = Key.Unbound
+ },
+ 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,
+ ButtonSl = Key.Unbound,
+ ButtonSr = Key.Unbound
+ },
+ Hotkeys = new KeyboardHotkeys
+ {
+ ToggleVsync = Key.Tab
+ }
+ };
+ }
+ else if (_inputDevice.ActiveId.StartsWith("controller"))
+ {
+ config = new ControllerConfig
+ {
+ Index = 0,
+ ControllerType = ControllerType.ProController,
+ DeadzoneLeft = 0.1f,
+ DeadzoneRight = 0.1f,
+ TriggerThreshold = 0.5f,
+ LeftJoycon = new NpadControllerLeft
+ {
+ StickX = ControllerInputId.Axis0,
+ StickY = ControllerInputId.Axis1,
+ StickButton = ControllerInputId.Button8,
+ DPadUp = ControllerInputId.Hat0Up,
+ DPadDown = ControllerInputId.Hat0Down,
+ DPadLeft = ControllerInputId.Hat0Left,
+ DPadRight = ControllerInputId.Hat0Right,
+ ButtonMinus = ControllerInputId.Button6,
+ ButtonL = ControllerInputId.Button4,
+ ButtonZl = ControllerInputId.Axis2,
+ ButtonSl = ControllerInputId.Unbound,
+ ButtonSr = ControllerInputId.Unbound,
+ InvertStickX = false,
+ InvertStickY = false
+ },
+ RightJoycon = new NpadControllerRight
+ {
+ StickX = ControllerInputId.Axis3,
+ StickY = ControllerInputId.Axis4,
+ StickButton = ControllerInputId.Button9,
+ ButtonA = ControllerInputId.Button1,
+ ButtonB = ControllerInputId.Button0,
+ ButtonX = ControllerInputId.Button3,
+ ButtonY = ControllerInputId.Button2,
+ ButtonPlus = ControllerInputId.Button7,
+ ButtonR = ControllerInputId.Button5,
+ ButtonZr = ControllerInputId.Axis5,
+ ButtonSl = ControllerInputId.Unbound,
+ ButtonSr = ControllerInputId.Unbound,
+ InvertStickX = false,
+ InvertStickY = false
+ }
+ };
+ }
+ }
+ else
+ {
+ string path = System.IO.Path.Combine(GetProfileBasePath(), _profile.ActiveId);
+
+ if (!File.Exists(path))
+ {
+ if (pos >= 0)
+ {
+ _profile.Remove(pos);
+ }
+
+ return;
+ }
+
+ using (Stream stream = File.OpenRead(path))
+ {
+ try
+ {
+ config = JsonHelper.Deserialize<ControllerConfig>(stream);
+ }
+ catch (ArgumentException)
+ {
+ try
+ {
+ config = JsonHelper.Deserialize<KeyboardConfig>(stream);
+ }
+ catch { }
+ }
+ }
+ }
+
+ SetValues(config);
+ }
+
+ private void ProfileAdd_Activated(object sender, EventArgs args)
+ {
+ ((ToggleButton)sender).SetStateFlags(0, true);
+
+ if (_inputDevice.ActiveId == "disabled") return;
+
+ InputConfig inputConfig = GetValues();
+ ProfileDialog profileDialog = new ProfileDialog();
+
+ if (inputConfig == null) return;
+
+ if (profileDialog.Run() == (int)ResponseType.Ok)
+ {
+ string path = System.IO.Path.Combine(GetProfileBasePath(), profileDialog.FileName);
+ string jsonString;
+
+ if (inputConfig is KeyboardConfig keyboardConfig)
+ {
+ jsonString = JsonHelper.Serialize(keyboardConfig, true);
+ }
+ else
+ {
+ jsonString = JsonHelper.Serialize(inputConfig as ControllerConfig, true);
+ }
+
+ File.WriteAllText(path, jsonString);
+ }
+
+ profileDialog.Dispose();
+
+ SetProfiles();
+ }
+
+ private void ProfileRemove_Activated(object sender, EventArgs args)
+ {
+ ((ToggleButton) sender).SetStateFlags(0, true);
+
+ if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == "default" || _profile.ActiveId == null) return;
+
+ MessageDialog confirmDialog = GtkDialog.CreateConfirmationDialog("Deleting Profile", "This action is irreversible, are your sure you want to continue?");
+
+ if (confirmDialog.Run() == (int)ResponseType.Yes)
+ {
+ string path = System.IO.Path.Combine(GetProfileBasePath(), _profile.ActiveId);
+
+ if (File.Exists(path))
+ {
+ File.Delete(path);
+ }
+
+ SetProfiles();
+ }
+ }
+
+ private void SaveToggle_Activated(object sender, EventArgs args)
+ {
+ InputConfig inputConfig = GetValues();
+
+ if (_inputConfig == null && inputConfig != null)
+ {
+ ConfigurationState.Instance.Hid.InputConfig.Value.Add(inputConfig);
+ }
+ else
+ {
+ if (_inputDevice.ActiveId == "disabled")
+ {
+ ConfigurationState.Instance.Hid.InputConfig.Value.Remove(_inputConfig);
+ }
+ else if (inputConfig != null)
+ {
+ int index = ConfigurationState.Instance.Hid.InputConfig.Value.IndexOf(_inputConfig);
+
+ ConfigurationState.Instance.Hid.InputConfig.Value[index] = inputConfig;
+ }
+ }
+
+ Dispose();
+ }
+
+ private void CloseToggle_Activated(object sender, EventArgs args)
+ {
+ Dispose();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx/Ui/ControllerWindow.glade b/Ryujinx/Ui/ControllerWindow.glade
new file mode 100644
index 00000000..2b780f13
--- /dev/null
+++ b/Ryujinx/Ui/ControllerWindow.glade
@@ -0,0 +1,1732 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkAdjustment" id="_controllerDeadzoneLeft">
+ <property name="upper">1</property>
+ <property name="value">0.050000000000000003</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkAdjustment" id="_controllerDeadzoneRight">
+ <property name="upper">1</property>
+ <property name="value">0.050000000000000003</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkAdjustment" id="_controllerTriggerThreshold">
+ <property name="upper">1</property>
+ <property name="value">0.5</property>
+ <property name="step_increment">0.01</property>
+ <property name="page_increment">0.10000000000000001</property>
+ </object>
+ <object class="GtkWindow" id="_controllerWin">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">Ryujinx - Controller Settings</property>
+ <property name="modal">True</property>
+ <property name="window_position">center</property>
+ <property name="default_width">1100</property>
+ <property name="default_height">600</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="HeadBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_top">10</property>
+ <property name="margin_bottom">10</property>
+ <child>
+ <object class="GtkBox" id="DeviceBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">5</property>
+ <property name="label" translatable="yes">Input Device</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="_inputDevice">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="active">0</property>
+ <property name="active_id">disabled</property>
+ <items>
+ <item id="disabled" translatable="yes">Disabled</item>
+ </items>
+ <signal name="changed" handler="InputDevice_Changed" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_refreshInputDevicesButton">
+ <property name="label" translatable="yes">Refresh</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">5</property>
+ <signal name="toggled" handler="RefreshInputDevicesButton_Pressed" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="ControllerTypeBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">20</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">The controller's type</property>
+ <property name="halign">center</property>
+ <property name="margin_right">5</property>
+ <property name="label" translatable="yes">Controller Type:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="_controllerType">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">The controller's type</property>
+ <property name="active">0</property>
+ <items>
+ <item id="Handheld" translatable="yes">Handheld</item>
+ <item id="ProController" translatable="yes">Pro Controller</item>
+ <item id="JoyconPair" translatable="yes">Paired Joycons</item>
+ <item id="JoyconLeft" translatable="yes">Left Joycon</item>
+ <item id="JoyconRight" translatable="yes">Right Joycon</item>
+ </items>
+ <signal name="changed" handler="Controller_Changed" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="ProfileBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">20</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">5</property>
+ <property name="label" translatable="yes">Profile:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBoxText" id="_profile">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">5</property>
+ <property name="active">0</property>
+ <property name="active_id">default</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton">
+ <property name="label" translatable="yes">Load</property>
+ <property name="width_request">60</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_right">5</property>
+ <signal name="toggled" handler="ProfileLoad_Activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton">
+ <property name="label" translatable="yes">Add</property>
+ <property name="width_request">60</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_right">5</property>
+ <signal name="toggled" handler="ProfileAdd_Activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton">
+ <property name="label" translatable="yes">Remove</property>
+ <property name="width_request">60</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="toggled" handler="ProfileRemove_Activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="_settingsBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_top">5</property>
+ <child>
+ <object class="GtkBox" id="ButtonsBox">
+ <property name="width_request">156</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="label" translatable="yes">Buttons</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">A</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">B</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">X</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Y</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_a">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">70</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_b">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">70</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_x">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">70</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_y">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">70</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">+</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">-</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_minus">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">70</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_plus">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">70</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="LeftStickBox">
+ <property name="width_request">160</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="label" translatable="yes">Left Stick</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">5</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">LStick Button</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_lStickButton">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="_leftStickKeyboard">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkToggleButton" id="_lStickDown">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_lStickUp">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_lStickLeft">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_lStickRight">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">LStick Down</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">LStick Up</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">LStick Right</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">LStick Left</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="_leftStickController">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">LStick Lt/Rt</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">LStick Up/Dn</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_lStickX">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_lStickY">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="_invertLStickX">
+ <property name="label" translatable="yes">Invert</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="_invertLStickY">
+ <property name="label" translatable="yes">Invert</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="_deadZoneLeftBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Deadzone Left</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">_controllerDeadzoneLeft</property>
+ <property name="round_digits">2</property>
+ <property name="digits">2</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="TriggerBox">
+ <property name="width_request">150</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="label" translatable="yes">Triggers</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">L</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">R</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_l">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_r">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">ZL</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">ZR</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_zL">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_zR">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="_leftSideTriggerBox">
+ <property name="name">_sideTriggerBox</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Left SL</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Left SR</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_lSl">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_lSr">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="_rightSideTriggerBox">
+ <property name="name">_sideTriggerBox</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Right SL</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Right SR</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_rSl">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_rSr">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="_triggerThresholdBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin_left">10</property>
+ <property name="label" translatable="yes">Trigger Threshold</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">_controllerTriggerThreshold</property>
+ <property name="round_digits">2</property>
+ <property name="digits">2</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <child>
+ <object class="GtkBox" id="DPadBox">
+ <property name="width_request">156</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">10</property>
+ <property name="margin_top">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="label" translatable="yes">Directional Pad</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Dpad Up</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Dpad Down</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Dpad Left</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Dpad Right</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_dpadUp">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">70</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_dpadDown">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">70</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_dpadLeft">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">70</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_dpadRight">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">70</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="RightStickBox">
+ <property name="width_request">160</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="label" translatable="yes">Right Stick</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">5</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">RStick Button</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_rStickButton">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="_rightStickKeyboard">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">RStick Up</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">RStick Down</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">RStick Left</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">RStick Right</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_rStickUp">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_rStickDown">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_rStickLeft">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_rStickRight">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="_rightStickController">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">3</property>
+ <property name="column_spacing">10</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">RStick Lt/Rt</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="width_request">80</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">RStick Up/Dn</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_rStickX">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_rStickY">
+ <property name="label" translatable="yes"> </property>
+ <property name="width_request">65</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="_invertRStickX">
+ <property name="label" translatable="yes">Invert</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="_invertRStickY">
+ <property name="label" translatable="yes">Invert</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="_deadZoneRightBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Deadzone Right</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScale">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="adjustment">_controllerDeadzoneRight</property>
+ <property name="round_digits">2</property>
+ <property name="digits">2</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="_controllerImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButtonBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">5</property>
+ <property name="margin_top">3</property>
+ <property name="margin_bottom">3</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkToggleButton" id="SaveToggle">
+ <property name="label" translatable="yes">Save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="toggled" handler="SaveToggle_Activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="CloseToggle">
+ <property name="label" translatable="yes">Close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">4</property>
+ <signal name="toggled" handler="CloseToggle_Activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs
index d266eefc..b37ab448 100644
--- a/Ryujinx/Ui/GLRenderer.cs
+++ b/Ryujinx/Ui/GLRenderer.cs
@@ -1,26 +1,24 @@
-using Gdk;
+using Gdk;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;
-using OpenTK.Platform;
using Ryujinx.Configuration;
+using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.HLE;
using Ryujinx.HLE.HOS.Services.Hid;
-using Ryujinx.Ui;
using System;
using System.Collections.Generic;
-using System.Text;
using System.Threading;
namespace Ryujinx.Ui
{
- public class GLRenderer : GLWidget
+ public class GlRenderer : GLWidget
{
- private const int SwitchPanelWidth = 1280;
+ private const int SwitchPanelWidth = 1280;
private const int SwitchPanelHeight = 720;
- private const int TargetFps = 60;
+ private const int TargetFps = 60;
public ManualResetEvent WaitEvent { get; set; }
@@ -32,7 +30,7 @@ namespace Ryujinx.Ui
private double _mouseX;
private double _mouseY;
- private bool _mousePressed;
+ private bool _mousePressed;
private bool _toggleFullscreen;
@@ -46,11 +44,9 @@ namespace Ryujinx.Ui
private Renderer _renderer;
- private HotkeyButtons _prevHotkeyButtons = 0;
+ private HotkeyButtons _prevHotkeyButtons;
- private Input.NpadController _primaryController;
-
- public GLRenderer(Switch device)
+ public GlRenderer(Switch device)
: base (GetGraphicsMode(),
3, 3,
GraphicsContextFlags.ForwardCompatible)
@@ -59,8 +55,8 @@ namespace Ryujinx.Ui
_device = device;
- this.Initialized += GLRenderer_Initialized;
- this.Destroyed += GLRenderer_Destroyed;
+ this.Initialized += GLRenderer_Initialized;
+ this.Destroyed += GLRenderer_Destroyed;
this.ShuttingDown += GLRenderer_ShuttingDown;
Initialize();
@@ -69,25 +65,18 @@ namespace Ryujinx.Ui
_ticksPerFrame = System.Diagnostics.Stopwatch.Frequency / TargetFps;
- _primaryController = new Input.NpadController(ConfigurationState.Instance.Hid.JoystickControls);
-
- AddEvents((int)(Gdk.EventMask.ButtonPressMask
- | Gdk.EventMask.ButtonReleaseMask
- | Gdk.EventMask.PointerMotionMask
- | Gdk.EventMask.KeyPressMask
- | Gdk.EventMask.KeyReleaseMask));
+ AddEvents((int)(EventMask.ButtonPressMask
+ | EventMask.ButtonReleaseMask
+ | EventMask.PointerMotionMask
+ | EventMask.KeyPressMask
+ | EventMask.KeyReleaseMask));
this.Shown += Renderer_Shown;
}
private static GraphicsMode GetGraphicsMode()
{
- if (Environment.OSVersion.Platform == PlatformID.Unix)
- {
- return new GraphicsMode(new ColorFormat(24));
- }
-
- return new GraphicsMode(new ColorFormat());
+ return Environment.OSVersion.Platform == PlatformID.Unix ? new GraphicsMode(new ColorFormat(24)) : new GraphicsMode(new ColorFormat());
}
private void GLRenderer_ShuttingDown(object sender, EventArgs args)
@@ -117,11 +106,11 @@ namespace Ryujinx.Ui
public void HandleScreenState(KeyboardState keyboard)
{
- bool toggleFullscreen = keyboard.IsKeyDown(OpenTK.Input.Key.F11)
+ bool toggleFullscreen = keyboard.IsKeyDown(OpenTK.Input.Key.F11)
|| ((keyboard.IsKeyDown(OpenTK.Input.Key.AltLeft)
|| keyboard.IsKeyDown(OpenTK.Input.Key.AltRight))
&& keyboard.IsKeyDown(OpenTK.Input.Key.Enter))
- || keyboard.IsKeyDown(OpenTK.Input.Key.Escape);
+ || keyboard.IsKeyDown(OpenTK.Input.Key.Escape);
bool fullScreenToggled = ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen);
@@ -165,7 +154,7 @@ namespace Ryujinx.Ui
protected override bool OnConfigureEvent(EventConfigure evnt)
{
- var result = base.OnConfigureEvent(evnt);
+ bool result = base.OnConfigureEvent(evnt);
Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window);
@@ -184,7 +173,7 @@ namespace Ryujinx.Ui
Gtk.Window parent = this.Toplevel as Gtk.Window;
- parent.FocusInEvent += Parent_FocusInEvent;
+ parent.FocusInEvent += Parent_FocusInEvent;
parent.FocusOutEvent += Parent_FocusOutEvent;
Gtk.Application.Invoke(delegate
@@ -347,7 +336,7 @@ namespace Ryujinx.Ui
_device.EnableDeviceVsync,
$"Host: {_device.Statistics.GetSystemFrameRate():00.00} FPS",
$"Game: {_device.Statistics.GetGameFrameRate():00.00} FPS",
- $"GPU: {_renderer.GpuVendor}"));
+ $"GPU: {_renderer.GpuVendor}"));
_ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
}
@@ -382,83 +371,127 @@ namespace Ryujinx.Ui
return false;
}
- HotkeyButtons currentHotkeyButtons = 0;
- ControllerKeys currentButton = 0;
- JoystickPosition leftJoystick;
- JoystickPosition rightJoystick;
- KeyboardInput? hidKeyboard = null;
-
- int leftJoystickDx = 0;
- int leftJoystickDy = 0;
- int rightJoystickDx = 0;
- int rightJoystickDy = 0;
-
- // OpenTK always captures keyboard events, even if out of focus, so check if window is focused.
if (IsFocused)
{
- KeyboardState keyboard = OpenTK.Input.Keyboard.GetState();
-
Gtk.Application.Invoke(delegate
{
- HandleScreenState(keyboard);
+ HandleScreenState(OpenTK.Input.Keyboard.GetState());
});
+ }
- // Normal Input
- currentHotkeyButtons = KeyboardControls.GetHotkeyButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
- currentButton = KeyboardControls.GetButtons(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
+ List<GamepadInput> gamepadInputs = new List<GamepadInput>();
- if (ConfigurationState.Instance.Hid.EnableKeyboard)
+ foreach (InputConfig inputConfig in ConfigurationState.Instance.Hid.InputConfig.Value)
+ {
+ ControllerKeys currentButton = 0;
+ JoystickPosition leftJoystick = new JoystickPosition();
+ JoystickPosition rightJoystick = new JoystickPosition();
+ KeyboardInput? hidKeyboard = null;
+
+ int leftJoystickDx = 0;
+ int leftJoystickDy = 0;
+ int rightJoystickDx = 0;
+ int rightJoystickDy = 0;
+
+ if (inputConfig is KeyboardConfig keyboardConfig)
{
- hidKeyboard = KeyboardControls.GetKeysDown(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
- }
+ if (IsFocused)
+ {
+ // Keyboard Input
+ KeyboardController keyboardController = new KeyboardController(keyboardConfig);
- (leftJoystickDx, leftJoystickDy) = KeyboardControls.GetLeftStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
- (rightJoystickDx, rightJoystickDy) = KeyboardControls.GetRightStick(ConfigurationState.Instance.Hid.KeyboardControls, keyboard);
- }
+ currentButton = keyboardController.GetButtons();
- if (!hidKeyboard.HasValue)
- {
- hidKeyboard = new KeyboardInput
+ (leftJoystickDx, leftJoystickDy) = keyboardController.GetLeftStick();
+ (rightJoystickDx, rightJoystickDy) = keyboardController.GetRightStick();
+
+ leftJoystick = new JoystickPosition
+ {
+ Dx = leftJoystickDx,
+ Dy = leftJoystickDy
+ };
+
+ rightJoystick = new JoystickPosition
+ {
+ Dx = rightJoystickDx,
+ Dy = rightJoystickDy
+ };
+
+ if (ConfigurationState.Instance.Hid.EnableKeyboard)
+ {
+ hidKeyboard = keyboardController.GetKeysDown();
+ }
+
+ if (!hidKeyboard.HasValue)
+ {
+ hidKeyboard = new KeyboardInput
+ {
+ Modifier = 0,
+ Keys = new int[0x8]
+ };
+ }
+
+ if (ConfigurationState.Instance.Hid.EnableKeyboard)
+ {
+ _device.Hid.Keyboard.Update(hidKeyboard.Value);
+ }
+
+ // Toggle vsync
+ HotkeyButtons currentHotkeyButtons = keyboardController.GetHotkeyButtons();
+
+ if (currentHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync) &&
+ !_prevHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync))
+ {
+ _device.EnableDeviceVsync = !_device.EnableDeviceVsync;
+ }
+
+ _prevHotkeyButtons = currentHotkeyButtons;
+ }
+ }
+ else if (inputConfig is Common.Configuration.Hid.ControllerConfig controllerConfig)
{
- Modifier = 0,
- Keys = new int[0x8]
- };
- }
+ // Controller Input
+ JoystickController joystickController = new JoystickController(controllerConfig);
- currentButton |= _primaryController.GetButtons();
+ currentButton |= joystickController.GetButtons();
- // Keyboard has priority stick-wise
- if (leftJoystickDx == 0 && leftJoystickDy == 0)
- {
- (leftJoystickDx, leftJoystickDy) = _primaryController.GetLeftStick();
- }
+ (leftJoystickDx, leftJoystickDy) = joystickController.GetLeftStick();
+ (rightJoystickDx, rightJoystickDy) = joystickController.GetRightStick();
- if (rightJoystickDx == 0 && rightJoystickDy == 0)
- {
- (rightJoystickDx, rightJoystickDy) = _primaryController.GetRightStick();
- }
+ leftJoystick = new JoystickPosition
+ {
+ Dx = controllerConfig.LeftJoycon.InvertStickX ? -leftJoystickDx : leftJoystickDx,
+ Dy = controllerConfig.LeftJoycon.InvertStickY ? -leftJoystickDy : leftJoystickDy
+ };
- leftJoystick = new JoystickPosition
- {
- Dx = leftJoystickDx,
- Dy = leftJoystickDy
- };
+ rightJoystick = new JoystickPosition
+ {
+ Dx = controllerConfig.RightJoycon.InvertStickX ? -rightJoystickDx : rightJoystickDx,
+ Dy = controllerConfig.RightJoycon.InvertStickY ? -rightJoystickDy : rightJoystickDy
+ };
+ }
- rightJoystick = new JoystickPosition
- {
- Dx = rightJoystickDx,
- Dy = rightJoystickDy
- };
+ currentButton |= _device.Hid.UpdateStickButtons(leftJoystick, rightJoystick);
+
+ gamepadInputs.Add(new GamepadInput
+ {
+ PlayerId = (HLE.HOS.Services.Hid.PlayerIndex)inputConfig.PlayerIndex,
+ Buttons = currentButton,
+ LStick = leftJoystick,
+ RStick = rightJoystick
+ });
+ }
- currentButton |= _device.Hid.UpdateStickButtons(leftJoystick, rightJoystick);
+ _device.Hid.Npads.SetGamepadsInput(gamepadInputs.ToArray());
+ //Touchscreen
bool hasTouch = false;
// Get screen touch position from left mouse click
// OpenTK always captures mouse events, even if out of focus, so check if window is focused.
if (IsFocused && _mousePressed)
{
- int screenWidth = AllocatedWidth;
+ int screenWidth = AllocatedWidth;
int screenHeight = AllocatedHeight;
if (AllocatedWidth > (AllocatedHeight * SwitchPanelWidth) / SwitchPanelHeight)
@@ -470,7 +503,7 @@ namespace Ryujinx.Ui
screenHeight = (AllocatedWidth * SwitchPanelHeight) / SwitchPanelWidth;
}
- int startX = (AllocatedWidth - screenWidth) >> 1;
+ int startX = (AllocatedWidth - screenWidth) >> 1;
int startY = (AllocatedHeight - screenHeight) >> 1;
int endX = startX + screenWidth;
@@ -496,7 +529,7 @@ namespace Ryujinx.Ui
// Placeholder values till more data is acquired
DiameterX = 10,
DiameterY = 10,
- Angle = 90
+ Angle = 90
};
hasTouch = true;
@@ -510,30 +543,8 @@ namespace Ryujinx.Ui
_device.Hid.Touchscreen.Update();
}
- if (ConfigurationState.Instance.Hid.EnableKeyboard && hidKeyboard.HasValue)
- {
- _device.Hid.Keyboard.Update(hidKeyboard.Value);
- }
-
_device.Hid.DebugPad.Update();
- _device.Hid.Npads.SetGamepadsInput(new GamepadInput
- {
- PlayerId = PlayerIndex.Auto,
- Buttons = currentButton,
- LStick = leftJoystick,
- RStick = rightJoystick
- });
-
- // Toggle vsync
- if (currentHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync) &&
- !_prevHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync))
- {
- _device.EnableDeviceVsync = !_device.EnableDeviceVsync;
- }
-
- _prevHotkeyButtons = currentHotkeyButtons;
-
return true;
}
}
diff --git a/Ryujinx/Ui/GtkDialog.cs b/Ryujinx/Ui/GtkDialog.cs
index 698b8b7e..9a14f63d 100644
--- a/Ryujinx/Ui/GtkDialog.cs
+++ b/Ryujinx/Ui/GtkDialog.cs
@@ -3,33 +3,46 @@ using System.Reflection;
namespace Ryujinx.Ui
{
- internal class GtkDialog
+ internal class GtkDialog : MessageDialog
{
internal static bool _isExitDialogOpen = false;
- internal static void CreateDialog(string title, string text, string secondaryText)
+ private GtkDialog(string title, string mainText, string secondaryText,
+ MessageType messageType = MessageType.Other, ButtonsType buttonsType = ButtonsType.Ok) : base(null, DialogFlags.Modal, messageType, buttonsType, null)
{
- MessageDialog errorDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Error, ButtonsType.Ok, null)
- {
- Title = title,
- Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
- Text = text,
- SecondaryText = secondaryText,
- WindowPosition = WindowPosition.Center
- };
- errorDialog.SetSizeRequest(100, 20);
- errorDialog.Run();
- errorDialog.Dispose();
+ Title = title;
+ Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
+ Text = mainText;
+ SecondaryText = secondaryText;
+ WindowPosition = WindowPosition.Center;
+ Response += GtkDialog_Response;
+
+ SetSizeRequest(100, 20);
+ }
+
+ private void GtkDialog_Response(object sender, ResponseArgs args)
+ {
+ Dispose();
}
- internal static void CreateWarningDialog(string text, string secondaryText)
+ internal static void CreateInfoDialog(string title, string mainText, string secondaryText)
{
- CreateDialog("Ryujinx - Warning", text, secondaryText);
+ new GtkDialog(title, mainText, secondaryText, MessageType.Info).Run();
+ }
+
+ internal static void CreateWarningDialog(string mainText, string secondaryText)
+ {
+ new GtkDialog("Ryujinx - Warning", mainText, secondaryText, MessageType.Warning).Run();
}
internal static void CreateErrorDialog(string errorMessage)
{
- CreateDialog("Ryujinx - Error", "Ryujinx has encountered an error", errorMessage);
+ new GtkDialog("Ryujinx - Error", "Ryujinx has encountered an error", errorMessage, MessageType.Error).Run();
+ }
+
+ internal static MessageDialog CreateConfirmationDialog(string mainText, string secondaryText = "")
+ {
+ return new GtkDialog("Ryujinx - Confirmation", mainText, secondaryText, MessageType.Question, ButtonsType.YesNo);
}
internal static bool CreateExitDialog()
@@ -40,27 +53,11 @@ namespace Ryujinx.Ui
}
_isExitDialogOpen = true;
-
- MessageDialog messageDialog = new MessageDialog(null, DialogFlags.Modal, MessageType.Question, ButtonsType.OkCancel, null)
- {
- Title = "Ryujinx - Exit",
- Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png"),
- Text = "Are you sure you want to stop emulation?",
- SecondaryText = "All unsaved data will be lost",
- WindowPosition = WindowPosition.Center
- };
-
- messageDialog.SetSizeRequest(100, 20);
- ResponseType res = (ResponseType)messageDialog.Run();
- messageDialog.Dispose();
+ ResponseType res = (ResponseType)new GtkDialog("Ryujinx - Exit", "Are you sure you want to stop emulation?",
+ "All unsaved data will be lost", MessageType.Question, ButtonsType.YesNo).Run();
_isExitDialogOpen = false;
- if (res == ResponseType.Ok)
- {
- return true;
- }
-
- return false;
+ return res == ResponseType.Yes;
}
}
-}
+} \ No newline at end of file
diff --git a/Ryujinx/Ui/JoystickController.cs b/Ryujinx/Ui/JoystickController.cs
new file mode 100644
index 00000000..f5d7450a
--- /dev/null
+++ b/Ryujinx/Ui/JoystickController.cs
@@ -0,0 +1,149 @@
+using OpenTK;
+using OpenTK.Input;
+using Ryujinx.Common.Configuration.Hid;
+using Ryujinx.HLE.HOS.Services.Hid;
+using System;
+
+using ControllerConfig = Ryujinx.Common.Configuration.Hid.ControllerConfig;
+
+namespace Ryujinx.Ui
+{
+ public class JoystickController
+ {
+ private readonly ControllerConfig _config;
+
+ // NOTE: This should be initialized AFTER GTK for compat reasons with OpenTK SDL2 backend and GTK on Linux.
+ // BODY: Usage of Joystick.GetState must be defer to after GTK full initialization. Otherwise, GTK will segfault because SDL2 was already init *sighs*
+ public JoystickController(ControllerConfig config)
+ {
+ _config = config;
+ }
+
+ private bool IsEnabled()
+ {
+ return Joystick.GetState(_config.Index).IsConnected;
+ }
+
+ public ControllerKeys GetButtons()
+ {
+ if (!IsEnabled())
+ {
+ return 0;
+ }
+
+ JoystickState joystickState = Joystick.GetState(_config.Index);
+
+ ControllerKeys buttons = 0;
+
+ if (IsActivated(joystickState, _config.LeftJoycon.DPadUp)) buttons |= ControllerKeys.DpadUp;
+ if (IsActivated(joystickState, _config.LeftJoycon.DPadDown)) buttons |= ControllerKeys.DpadDown;
+ if (IsActivated(joystickState, _config.LeftJoycon.DPadLeft)) buttons |= ControllerKeys.DpadLeft;
+ if (IsActivated(joystickState, _config.LeftJoycon.DPadRight)) buttons |= ControllerKeys.DpadRight;
+ if (IsActivated(joystickState, _config.LeftJoycon.StickButton)) buttons |= ControllerKeys.LStick;
+ if (IsActivated(joystickState, _config.LeftJoycon.ButtonMinus)) buttons |= ControllerKeys.Minus;
+ if (IsActivated(joystickState, _config.LeftJoycon.ButtonL)) buttons |= ControllerKeys.L;
+ if (IsActivated(joystickState, _config.LeftJoycon.ButtonZl)) buttons |= ControllerKeys.Zl;
+ if (IsActivated(joystickState, _config.LeftJoycon.ButtonSl)) buttons |= ControllerKeys.SlLeft;
+ if (IsActivated(joystickState, _config.LeftJoycon.ButtonSr)) buttons |= ControllerKeys.SrLeft;
+
+ if (IsActivated(joystickState, _config.RightJoycon.ButtonA)) buttons |= ControllerKeys.A;
+ if (IsActivated(joystickState, _config.RightJoycon.ButtonB)) buttons |= ControllerKeys.B;
+ if (IsActivated(joystickState, _config.RightJoycon.ButtonX)) buttons |= ControllerKeys.X;
+ if (IsActivated(joystickState, _config.RightJoycon.ButtonY)) buttons |= ControllerKeys.Y;
+ if (IsActivated(joystickState, _config.RightJoycon.StickButton)) buttons |= ControllerKeys.RStick;
+ if (IsActivated(joystickState, _config.RightJoycon.ButtonPlus)) buttons |= ControllerKeys.Plus;
+ if (IsActivated(joystickState, _config.RightJoycon.ButtonR)) buttons |= ControllerKeys.R;
+ if (IsActivated(joystickState, _config.RightJoycon.ButtonZr)) buttons |= ControllerKeys.Zr;
+ if (IsActivated(joystickState, _config.RightJoycon.ButtonSl)) buttons |= ControllerKeys.SlRight;
+ if (IsActivated(joystickState, _config.RightJoycon.ButtonSr)) buttons |= ControllerKeys.SrRight;
+
+ return buttons;
+ }
+
+ private bool IsActivated(JoystickState joystickState, ControllerInputId controllerInputId)
+ {
+ if (controllerInputId <= ControllerInputId.Button20)
+ {
+ return joystickState.IsButtonDown((int)controllerInputId);
+ }
+ else if (controllerInputId <= ControllerInputId.Axis5)
+ {
+ int axis = controllerInputId - ControllerInputId.Axis0;
+
+ return joystickState.GetAxis(axis) > _config.TriggerThreshold;
+ }
+ else if (controllerInputId <= ControllerInputId.Hat2Right)
+ {
+ int hat = (controllerInputId - ControllerInputId.Hat0Up) / 4;
+
+ int baseHatId = (int)ControllerInputId.Hat0Up + (hat * 4);
+
+ JoystickHatState hatState = joystickState.GetHat((JoystickHat)hat);
+
+ if (hatState.IsUp && ((int)controllerInputId % baseHatId == 0)) return true;
+ if (hatState.IsDown && ((int)controllerInputId % baseHatId == 1)) return true;
+ if (hatState.IsLeft && ((int)controllerInputId % baseHatId == 2)) return true;
+ if (hatState.IsRight && ((int)controllerInputId % baseHatId == 3)) return true;
+ }
+
+ return false;
+ }
+
+ public (short, short) GetLeftStick()
+ {
+ if (!IsEnabled())
+ {
+ return (0, 0);
+ }
+
+ return GetStick(_config.LeftJoycon.StickX, _config.LeftJoycon.StickY, _config.DeadzoneLeft);
+ }
+
+ public (short, short) GetRightStick()
+ {
+ if (!IsEnabled())
+ {
+ return (0, 0);
+ }
+
+ return GetStick(_config.RightJoycon.StickX, _config.RightJoycon.StickY, _config.DeadzoneRight);
+ }
+
+ private (short, short) GetStick(ControllerInputId stickXInputId, ControllerInputId stickYInputId, float deadzone)
+ {
+ if (stickXInputId < ControllerInputId.Axis0 || stickXInputId > ControllerInputId.Axis5 ||
+ stickYInputId < ControllerInputId.Axis0 || stickYInputId > ControllerInputId.Axis5)
+ {
+ return (0, 0);
+ }
+
+ JoystickState jsState = Joystick.GetState(_config.Index);
+
+ int xAxis = stickXInputId - ControllerInputId.Axis0;
+ int yAxis = stickYInputId - ControllerInputId.Axis0;
+
+ float xValue = jsState.GetAxis(xAxis);
+ float yValue = -jsState.GetAxis(yAxis); // Invert Y-axis
+
+ return ApplyDeadzone(new Vector2(xValue, yValue), deadzone);
+ }
+
+ private (short, short) ApplyDeadzone(Vector2 axis, float deadzone)
+ {
+ return (ClampAxis(MathF.Abs(axis.X) > deadzone ? axis.X : 0f),
+ ClampAxis(MathF.Abs(axis.Y) > deadzone ? axis.Y : 0f));
+ }
+
+ private static short ClampAxis(float value)
+ {
+ if (value <= -short.MaxValue)
+ {
+ return -short.MaxValue;
+ }
+ else
+ {
+ return (short)(value * short.MaxValue);
+ }
+ }
+ }
+}
diff --git a/Ryujinx/Ui/KeyboardControls.cs b/Ryujinx/Ui/KeyboardController.cs
index 1f19fe32..ed4a4b3f 100644
--- a/Ryujinx/Ui/KeyboardControls.cs
+++ b/Ryujinx/Ui/KeyboardController.cs
@@ -1,7 +1,7 @@
using System;
using OpenTK.Input;
+using Ryujinx.Common.Configuration.Hid;
using Ryujinx.HLE.HOS.Services.Hid;
-using Ryujinx.UI.Input;
namespace Ryujinx.Ui
{
@@ -11,64 +11,85 @@ namespace Ryujinx.Ui
ToggleVSync = 1 << 0,
}
- public static class KeyboardControls
+ public class KeyboardController
{
- public static ControllerKeys GetButtons(NpadKeyboard npad, KeyboardState keyboard)
+ private readonly KeyboardConfig _config;
+
+ // NOTE: This should be initialized AFTER GTK for compat reasons with OpenTK SDL2 backend and GTK on Linux.
+ // BODY: Usage of Joystick.GetState must be defer to after GTK full initialization. Otherwise, GTK will segfault because SDL2 was already init *sighs*
+ public KeyboardController(KeyboardConfig config)
{
+ _config = config;
+ }
+
+ public ControllerKeys GetButtons()
+ {
+ KeyboardState keyboard = Keyboard.GetState(_config.Index);
+
ControllerKeys buttons = 0;
- if (keyboard[(Key)npad.LeftJoycon.StickButton]) buttons |= ControllerKeys.LStick;
- if (keyboard[(Key)npad.LeftJoycon.DPadUp]) buttons |= ControllerKeys.DpadUp;
- if (keyboard[(Key)npad.LeftJoycon.DPadDown]) buttons |= ControllerKeys.DpadDown;
- if (keyboard[(Key)npad.LeftJoycon.DPadLeft]) buttons |= ControllerKeys.DpadLeft;
- if (keyboard[(Key)npad.LeftJoycon.DPadRight]) buttons |= ControllerKeys.DpadRight;
- if (keyboard[(Key)npad.LeftJoycon.ButtonMinus]) buttons |= ControllerKeys.Minus;
- if (keyboard[(Key)npad.LeftJoycon.ButtonL]) buttons |= ControllerKeys.L | ControllerKeys.Sl;
- if (keyboard[(Key)npad.LeftJoycon.ButtonZl]) buttons |= ControllerKeys.Zl;
+ if (keyboard[(Key)_config.LeftJoycon.StickButton]) buttons |= ControllerKeys.LStick;
+ if (keyboard[(Key)_config.LeftJoycon.DPadUp]) buttons |= ControllerKeys.DpadUp;
+ if (keyboard[(Key)_config.LeftJoycon.DPadDown]) buttons |= ControllerKeys.DpadDown;
+ if (keyboard[(Key)_config.LeftJoycon.DPadLeft]) buttons |= ControllerKeys.DpadLeft;
+ if (keyboard[(Key)_config.LeftJoycon.DPadRight]) buttons |= ControllerKeys.DpadRight;
+ if (keyboard[(Key)_config.LeftJoycon.ButtonMinus]) buttons |= ControllerKeys.Minus;
+ if (keyboard[(Key)_config.LeftJoycon.ButtonL]) buttons |= ControllerKeys.L;
+ if (keyboard[(Key)_config.LeftJoycon.ButtonZl]) buttons |= ControllerKeys.Zl;
+ if (keyboard[(Key)_config.LeftJoycon.ButtonSl]) buttons |= ControllerKeys.SlLeft;
+ if (keyboard[(Key)_config.LeftJoycon.ButtonSr]) buttons |= ControllerKeys.SlRight;
- if (keyboard[(Key)npad.RightJoycon.StickButton]) buttons |= ControllerKeys.RStick;
- if (keyboard[(Key)npad.RightJoycon.ButtonA]) buttons |= ControllerKeys.A;
- if (keyboard[(Key)npad.RightJoycon.ButtonB]) buttons |= ControllerKeys.B;
- if (keyboard[(Key)npad.RightJoycon.ButtonX]) buttons |= ControllerKeys.X;
- if (keyboard[(Key)npad.RightJoycon.ButtonY]) buttons |= ControllerKeys.Y;
- if (keyboard[(Key)npad.RightJoycon.ButtonPlus]) buttons |= ControllerKeys.Plus;
- if (keyboard[(Key)npad.RightJoycon.ButtonR]) buttons |= ControllerKeys.R | ControllerKeys.Sr;
- if (keyboard[(Key)npad.RightJoycon.ButtonZr]) buttons |= ControllerKeys.Zr;
+ if (keyboard[(Key)_config.RightJoycon.StickButton]) buttons |= ControllerKeys.RStick;
+ if (keyboard[(Key)_config.RightJoycon.ButtonA]) buttons |= ControllerKeys.A;
+ if (keyboard[(Key)_config.RightJoycon.ButtonB]) buttons |= ControllerKeys.B;
+ if (keyboard[(Key)_config.RightJoycon.ButtonX]) buttons |= ControllerKeys.X;
+ if (keyboard[(Key)_config.RightJoycon.ButtonY]) buttons |= ControllerKeys.Y;
+ if (keyboard[(Key)_config.RightJoycon.ButtonPlus]) buttons |= ControllerKeys.Plus;
+ if (keyboard[(Key)_config.RightJoycon.ButtonR]) buttons |= ControllerKeys.R;
+ if (keyboard[(Key)_config.RightJoycon.ButtonZr]) buttons |= ControllerKeys.Zr;
+ if (keyboard[(Key)_config.RightJoycon.ButtonSl]) buttons |= ControllerKeys.SrLeft;
+ if (keyboard[(Key)_config.RightJoycon.ButtonSr]) buttons |= ControllerKeys.SrRight;
return buttons;
}
- public static (short, short) GetLeftStick(NpadKeyboard npad, KeyboardState keyboard)
+ public (short, short) GetLeftStick()
{
+ KeyboardState keyboard = Keyboard.GetState(_config.Index);
+
short dx = 0;
short dy = 0;
- if (keyboard[(Key)npad.LeftJoycon.StickUp]) dy = short.MaxValue;
- if (keyboard[(Key)npad.LeftJoycon.StickDown]) dy = -short.MaxValue;
- if (keyboard[(Key)npad.LeftJoycon.StickLeft]) dx = -short.MaxValue;
- if (keyboard[(Key)npad.LeftJoycon.StickRight]) dx = short.MaxValue;
+ if (keyboard[(Key)_config.LeftJoycon.StickUp]) dy = short.MaxValue;
+ if (keyboard[(Key)_config.LeftJoycon.StickDown]) dy = -short.MaxValue;
+ if (keyboard[(Key)_config.LeftJoycon.StickLeft]) dx = -short.MaxValue;
+ if (keyboard[(Key)_config.LeftJoycon.StickRight]) dx = short.MaxValue;
return (dx, dy);
}
- public static (short, short) GetRightStick(NpadKeyboard npad, KeyboardState keyboard)
+ public (short, short) GetRightStick()
{
+ KeyboardState keyboard = Keyboard.GetState(_config.Index);
+
short dx = 0;
short dy = 0;
- if (keyboard[(Key)npad.RightJoycon.StickUp]) dy = short.MaxValue;
- if (keyboard[(Key)npad.RightJoycon.StickDown]) dy = -short.MaxValue;
- if (keyboard[(Key)npad.RightJoycon.StickLeft]) dx = -short.MaxValue;
- if (keyboard[(Key)npad.RightJoycon.StickRight]) dx = short.MaxValue;
+ if (keyboard[(Key)_config.RightJoycon.StickUp]) dy = short.MaxValue;
+ if (keyboard[(Key)_config.RightJoycon.StickDown]) dy = -short.MaxValue;
+ if (keyboard[(Key)_config.RightJoycon.StickLeft]) dx = -short.MaxValue;
+ if (keyboard[(Key)_config.RightJoycon.StickRight]) dx = short.MaxValue;
return (dx, dy);
}
- public static HotkeyButtons GetHotkeyButtons(NpadKeyboard npad, KeyboardState keyboard)
+ public HotkeyButtons GetHotkeyButtons()
{
+ KeyboardState keyboard = Keyboard.GetState(_config.Index);
+
HotkeyButtons buttons = 0;
- if (keyboard[(Key)npad.Hotkeys.ToggleVsync]) buttons |= HotkeyButtons.ToggleVSync;
+ if (keyboard[(Key)_config.Hotkeys.ToggleVsync]) buttons |= HotkeyButtons.ToggleVSync;
return buttons;
}
@@ -223,12 +244,14 @@ namespace Ryujinx.Ui
new KeyMappingEntry { TargetKey = Key.NumLock, Target = 10 },
};
- public static KeyboardInput GetKeysDown(NpadKeyboard npad, KeyboardState keyboard)
+ public KeyboardInput GetKeysDown()
{
+ KeyboardState keyboard = Keyboard.GetState(_config.Index);
+
KeyboardInput hidKeyboard = new KeyboardInput
{
- Modifier = 0,
- Keys = new int[0x8]
+ Modifier = 0,
+ Keys = new int[0x8]
};
foreach (KeyMappingEntry entry in KeyMapping)
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index eb8077e5..756ca231 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -13,6 +13,7 @@ using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
@@ -28,7 +29,7 @@ namespace Ryujinx.Ui
private static HLE.Switch _emulationContext;
- private static GLRenderer _gLWidget;
+ private static GlRenderer _glWidget;
private static AutoResetEvent _deviceExitStatus = new AutoResetEvent(false);
@@ -37,40 +38,38 @@ namespace Ryujinx.Ui
private static bool _updatingGameTable;
private static bool _gameLoaded;
private static bool _ending;
+
#pragma warning disable CS0169
private static bool _debuggerOpened;
-#pragma warning restore CS0169
-#pragma warning disable CS0169
- private static Ryujinx.Debugger.Debugger _debugger;
+ private static Debugger.Debugger _debugger;
#pragma warning restore CS0169
#pragma warning disable CS0169, CS0649, IDE0044
- [GUI] Window _mainWin;
[GUI] MenuBar _menuBar;
[GUI] Box _footerBox;
- [GUI] MenuItem _fullScreen;
[GUI] Box _statusBar;
[GUI] MenuItem _stopEmulation;
+ [GUI] MenuItem _fullScreen;
[GUI] CheckMenuItem _favToggle;
- [GUI] MenuItem _firmwareInstallFile;
[GUI] MenuItem _firmwareInstallDirectory;
+ [GUI] MenuItem _firmwareInstallFile;
[GUI] Label _hostStatus;
[GUI] MenuItem _openDebugger;
[GUI] CheckMenuItem _iconToggle;
- [GUI] CheckMenuItem _appToggle;
[GUI] CheckMenuItem _developerToggle;
- [GUI] CheckMenuItem _versionToggle;
+ [GUI] CheckMenuItem _appToggle;
[GUI] CheckMenuItem _timePlayedToggle;
+ [GUI] CheckMenuItem _versionToggle;
[GUI] CheckMenuItem _lastPlayedToggle;
[GUI] CheckMenuItem _fileExtToggle;
- [GUI] CheckMenuItem _fileSizeToggle;
[GUI] CheckMenuItem _pathToggle;
+ [GUI] CheckMenuItem _fileSizeToggle;
[GUI] Label _gameStatus;
[GUI] TreeView _gameTable;
- [GUI] ScrolledWindow _gameTableWindow;
[GUI] TreeSelection _gameTableSelection;
+ [GUI] ScrolledWindow _gameTableWindow;
[GUI] Label _gpuName;
[GUI] Label _progressLabel;
[GUI] Label _firmwareVersionLabel;
@@ -96,9 +95,12 @@ namespace Ryujinx.Ui
this.DeleteEvent += Window_Close;
_fullScreen.Activated += FullScreen_Toggled;
+ this.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
+ this.Title = $"Ryujinx {Program.Version}";
+
ApplicationLibrary.ApplicationAdded += Application_Added;
ApplicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated;
- GLRenderer.StatusUpdatedEvent += Update_StatusBar;
+ GlRenderer.StatusUpdatedEvent += Update_StatusBar;
_gameTable.ButtonReleaseEvent += Row_Clicked;
@@ -127,8 +129,6 @@ namespace Ryujinx.Ui
ApplyTheme();
- _mainWin.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
- _mainWin.Title = $"Ryujinx {Program.Version}";
_stopEmulation.Sensitive = false;
if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true;
@@ -302,7 +302,7 @@ namespace Ryujinx.Ui
{
if (_gameLoaded)
{
- GtkDialog.CreateDialog("Ryujinx", "A game has already been loaded", "Please close it first and try again.");
+ GtkDialog.CreateInfoDialog("Ryujinx", "A game has already been loaded", "Please close it first and try again.");
}
else
{
@@ -415,7 +415,7 @@ namespace Ryujinx.Ui
#if MACOS_BUILD
CreateGameWindow(device);
#else
- var windowThread = new Thread(() =>
+ Thread windowThread = new Thread(() =>
{
CreateGameWindow(device);
})
@@ -443,35 +443,29 @@ namespace Ryujinx.Ui
private void CreateGameWindow(HLE.Switch device)
{
- ControllerType type = (Ryujinx.Configuration.Hid.ControllerType)ConfigurationState.Instance.Hid.ControllerType switch {
- Ryujinx.Configuration.Hid.ControllerType.ProController => ControllerType.ProController,
- Ryujinx.Configuration.Hid.ControllerType.Handheld => ControllerType.Handheld,
- Ryujinx.Configuration.Hid.ControllerType.NpadPair => ControllerType.JoyconPair,
- Ryujinx.Configuration.Hid.ControllerType.NpadLeft => ControllerType.JoyconLeft,
- Ryujinx.Configuration.Hid.ControllerType.NpadRight => ControllerType.JoyconRight,
- _ => ControllerType.Handheld
- };
-
- device.Hid.Npads.AddControllers(new ControllerConfig {
- Player = PlayerIndex.Player1,
- Type = type
- });
+ device.Hid.Npads.AddControllers(ConfigurationState.Instance.Hid.InputConfig.Value.Select(inputConfig =>
+ new HLE.HOS.Services.Hid.ControllerConfig
+ {
+ Player = (PlayerIndex)inputConfig.PlayerIndex,
+ Type = (ControllerType)inputConfig.ControllerType
+ }
+ ).ToArray());
- _gLWidget = new GLRenderer(_emulationContext);
+ _glWidget = new GlRenderer(_emulationContext);
Application.Invoke(delegate
{
_viewBox.Remove(_gameTableWindow);
- _gLWidget.Expand = true;
- _viewBox.Child = _gLWidget;
+ _glWidget.Expand = true;
+ _viewBox.Child = _glWidget;
- _gLWidget.ShowAll();
+ _glWidget.ShowAll();
EditFooterForGameRender();
});
- _gLWidget.WaitEvent.WaitOne();
+ _glWidget.WaitEvent.WaitOne();
- _gLWidget.Start();
+ _glWidget.Start();
device.Dispose();
_deviceExitStatus.Set();
@@ -479,15 +473,15 @@ namespace Ryujinx.Ui
// NOTE: Everything that is here will not be executed when you close the UI.
Application.Invoke(delegate
{
- _viewBox.Remove(_gLWidget);
- _gLWidget.Exit();
+ _viewBox.Remove(_glWidget);
+ _glWidget.Exit();
- if(_gLWidget.Window != this.Window && _gLWidget.Window != null)
+ if(_glWidget.Window != this.Window && _glWidget.Window != null)
{
- _gLWidget.Window.Dispose();
+ _glWidget.Window.Dispose();
}
- _gLWidget.Dispose();
+ _glWidget.Dispose();
_viewBox.Add(_gameTableWindow);
@@ -497,7 +491,7 @@ namespace Ryujinx.Ui
_emulationContext = null;
_gameLoaded = false;
- _gLWidget = null;
+ _glWidget = null;
DiscordIntegrationModule.SwitchToMainMenu();
@@ -528,7 +522,7 @@ namespace Ryujinx.Ui
public void ToggleExtraWidgets(bool show)
{
- if (_gLWidget != null)
+ if (_glWidget != null)
{
if (show)
{
@@ -562,6 +556,11 @@ namespace Ryujinx.Ui
}
}
+ public static void SaveConfig()
+ {
+ ConfigurationState.Instance.ToFileFormat().SaveConfig(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config.json"));
+ }
+
private void End(HLE.Switch device)
{
@@ -580,10 +579,10 @@ namespace Ryujinx.Ui
{
UpdateGameMetadata(device.System.TitleIdText);
- if (_gLWidget != null)
+ if (_glWidget != null)
{
// We tell the widget that we are exiting
- _gLWidget.Exit();
+ _glWidget.Exit();
// Wait for the other thread to dispose the HLE context before exiting.
_deviceExitStatus.WaitOne();
@@ -773,7 +772,7 @@ namespace Ryujinx.Ui
private void StopEmulation_Pressed(object sender, EventArgs args)
{
- _gLWidget?.Exit();
+ _glWidget?.Exit();
}
private void Installer_File_Pressed(object o, EventArgs args)
@@ -808,7 +807,7 @@ namespace Ryujinx.Ui
private void RefreshFirmwareLabel()
{
- var currentFirmware = _contentManager.GetCurrentFirmwareVersion();
+ SystemVersion currentFirmware = _contentManager.GetCurrentFirmwareVersion();
GLib.Idle.Add(new GLib.IdleHandler(() =>
{
@@ -830,7 +829,7 @@ namespace Ryujinx.Ui
fileChooser.Dispose();
- var firmwareVersion = _contentManager.VerifyFirmwarePackage(filename);
+ SystemVersion firmwareVersion = _contentManager.VerifyFirmwarePackage(filename);
if (firmwareVersion == null)
{
@@ -849,7 +848,7 @@ namespace Ryujinx.Ui
return;
}
- var currentVersion = _contentManager.GetCurrentFirmwareVersion();
+ SystemVersion currentVersion = _contentManager.GetCurrentFirmwareVersion();
string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed.";
@@ -989,7 +988,7 @@ namespace Ryujinx.Ui
private void Settings_Pressed(object sender, EventArgs args)
{
- SwitchSettings settingsWin = new SwitchSettings(_virtualFileSystem, _contentManager);
+ SettingsWindow settingsWin = new SettingsWindow(_virtualFileSystem, _contentManager);
settingsWin.Show();
}
@@ -1205,10 +1204,5 @@ namespace Ryujinx.Ui
return 0;
}
}
-
- public static void SaveConfig()
- {
- ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
- }
}
}
diff --git a/Ryujinx/Ui/NpadController.cs b/Ryujinx/Ui/NpadController.cs
deleted file mode 100644
index b92f687c..00000000
--- a/Ryujinx/Ui/NpadController.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using OpenTK;
-using OpenTK.Input;
-using Ryujinx.Common.Configuration.Hid;
-using Ryujinx.HLE.HOS.Services.Hid;
-using System;
-
-using InnerNpadController = Ryujinx.Common.Configuration.Hid.NpadController;
-
-namespace Ryujinx.Ui.Input
-{
- public class NpadController
- {
- private InnerNpadController _inner;
-
- // NOTE: This should be initialized AFTER GTK for compat reasons with OpenTK SDL2 backend and GTK on Linux.
- // BODY: Usage of Joystick.GetState must be defer to after GTK full initialization. Otherwise, GTK will segfault because SDL2 was already init *sighs*
- public NpadController(InnerNpadController inner)
- {
- _inner = inner;
- }
-
- private bool IsEnabled()
- {
- return _inner.Enabled && Joystick.GetState(_inner.Index).IsConnected;
- }
-
- public ControllerKeys GetButtons()
- {
- if (!IsEnabled())
- {
- return 0;
- }
-
- JoystickState joystickState = Joystick.GetState(_inner.Index);
-
- ControllerKeys buttons = 0;
-
- if (IsActivated(joystickState, _inner.LeftJoycon.DPadUp)) buttons |= ControllerKeys.DpadUp;
- if (IsActivated(joystickState, _inner.LeftJoycon.DPadDown)) buttons |= ControllerKeys.DpadDown;
- if (IsActivated(joystickState, _inner.LeftJoycon.DPadLeft)) buttons |= ControllerKeys.DpadLeft;
- if (IsActivated(joystickState, _inner.LeftJoycon.DPadRight)) buttons |= ControllerKeys.DpadRight;
- if (IsActivated(joystickState, _inner.LeftJoycon.StickButton)) buttons |= ControllerKeys.LStick;
- if (IsActivated(joystickState, _inner.LeftJoycon.ButtonMinus)) buttons |= ControllerKeys.Minus;
- if (IsActivated(joystickState, _inner.LeftJoycon.ButtonL)) buttons |= ControllerKeys.L | ControllerKeys.Sl;
- if (IsActivated(joystickState, _inner.LeftJoycon.ButtonZl)) buttons |= ControllerKeys.Zl;
-
- if (IsActivated(joystickState, _inner.RightJoycon.ButtonA)) buttons |= ControllerKeys.A;
- if (IsActivated(joystickState, _inner.RightJoycon.ButtonB)) buttons |= ControllerKeys.B;
- if (IsActivated(joystickState, _inner.RightJoycon.ButtonX)) buttons |= ControllerKeys.X;
- if (IsActivated(joystickState, _inner.RightJoycon.ButtonY)) buttons |= ControllerKeys.Y;
- if (IsActivated(joystickState, _inner.RightJoycon.StickButton)) buttons |= ControllerKeys.RStick;
- if (IsActivated(joystickState, _inner.RightJoycon.ButtonPlus)) buttons |= ControllerKeys.Plus;
- if (IsActivated(joystickState, _inner.RightJoycon.ButtonR)) buttons |= ControllerKeys.R | ControllerKeys.Sr;
- if (IsActivated(joystickState, _inner.RightJoycon.ButtonZr)) buttons |= ControllerKeys.Zr;
-
- return buttons;
- }
-
- private bool IsActivated(JoystickState joystickState, ControllerInputId controllerInputId)
- {
- if (controllerInputId <= ControllerInputId.Button20)
- {
- return joystickState.IsButtonDown((int)controllerInputId);
- }
- else if (controllerInputId <= ControllerInputId.Axis5)
- {
- int axis = controllerInputId - ControllerInputId.Axis0;
-
- return joystickState.GetAxis(axis) > _inner.TriggerThreshold;
- }
- else if (controllerInputId <= ControllerInputId.Hat2Right)
- {
- int hat = (controllerInputId - ControllerInputId.Hat0Up) / 4;
-
- int baseHatId = (int)ControllerInputId.Hat0Up + (hat * 4);
-
- JoystickHatState hatState = joystickState.GetHat((JoystickHat)hat);
-
- if (hatState.IsUp && ((int)controllerInputId % baseHatId == 0)) return true;
- if (hatState.IsDown && ((int)controllerInputId % baseHatId == 1)) return true;
- if (hatState.IsLeft && ((int)controllerInputId % baseHatId == 2)) return true;
- if (hatState.IsRight && ((int)controllerInputId % baseHatId == 3)) return true;
- }
-
- return false;
- }
-
- public (short, short) GetLeftStick()
- {
- if (!IsEnabled())
- {
- return (0, 0);
- }
-
- return GetStick(_inner.LeftJoycon.Stick);
- }
-
- public (short, short) GetRightStick()
- {
- if (!IsEnabled())
- {
- return (0, 0);
- }
-
- return GetStick(_inner.RightJoycon.Stick);
- }
-
- private (short, short) GetStick(ControllerInputId stickInputId)
- {
- if (stickInputId < ControllerInputId.Axis0 || stickInputId > ControllerInputId.Axis5)
- {
- return (0, 0);
- }
-
- JoystickState jsState = Joystick.GetState(_inner.Index);
-
- int xAxis = stickInputId - ControllerInputId.Axis0;
-
- float xValue = jsState.GetAxis(xAxis);
- float yValue = 0 - jsState.GetAxis(xAxis + 1); // Invert Y-axis
-
- return ApplyDeadzone(new Vector2(xValue, yValue));
- }
-
- private (short, short) ApplyDeadzone(Vector2 axis)
- {
- return (ClampAxis(MathF.Abs(axis.X) > _inner.Deadzone ? axis.X : 0f),
- ClampAxis(MathF.Abs(axis.Y) > _inner.Deadzone ? axis.Y : 0f));
- }
-
- private static short ClampAxis(float value)
- {
- if (value <= -short.MaxValue)
- {
- return -short.MaxValue;
- }
- else
- {
- return (short)(value * short.MaxValue);
- }
- }
- }
-}
diff --git a/Ryujinx/Ui/ProfileDialog.cs b/Ryujinx/Ui/ProfileDialog.cs
new file mode 100644
index 00000000..2b26cbef
--- /dev/null
+++ b/Ryujinx/Ui/ProfileDialog.cs
@@ -0,0 +1,58 @@
+using Gtk;
+using System;
+using System.Reflection;
+
+using GUI = Gtk.Builder.ObjectAttribute;
+
+namespace Ryujinx.Ui
+{
+ public class ProfileDialog : Dialog
+ {
+ public string FileName { get; private set; }
+
+#pragma warning disable CS0649, IDE0044
+ [GUI] Entry _profileEntry;
+ [GUI] Label _errorMessage;
+#pragma warning restore CS0649, IDE0044
+
+ public ProfileDialog() : this(new Builder("Ryujinx.Ui.ProfileDialog.glade")) { }
+
+ private ProfileDialog(Builder builder) : base(builder.GetObject("_profileDialog").Handle)
+ {
+ builder.Autoconnect(this);
+
+ Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
+ }
+
+ private void OkToggle_Activated(object sender, EventArgs args)
+ {
+ ((ToggleButton)sender).SetStateFlags(0, true);
+
+ bool validFileName = true;
+
+ foreach (char invalidChar in System.IO.Path.GetInvalidFileNameChars())
+ {
+ if (_profileEntry.Text.Contains(invalidChar))
+ {
+ validFileName = false;
+ }
+ }
+
+ if (validFileName && !string.IsNullOrEmpty(_profileEntry.Text))
+ {
+ FileName = $"{_profileEntry.Text}.json";
+
+ Respond(ResponseType.Ok);
+ }
+ else
+ {
+ _errorMessage.Text = "The file name contains invalid characters. Please try again.";
+ }
+ }
+
+ private void CancelToggle_Activated(object sender, EventArgs args)
+ {
+ Respond(ResponseType.Cancel);
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx/Ui/ProfileDialog.glade b/Ryujinx/Ui/ProfileDialog.glade
new file mode 100644
index 00000000..adaf6608
--- /dev/null
+++ b/Ryujinx/Ui/ProfileDialog.glade
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkDialog" id="_profileDialog">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">Ryujinx - Profile Dialog</property>
+ <property name="modal">True</property>
+ <property name="window_position">center</property>
+ <property name="default_width">400</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkToggleButton" id="OkToggle">
+ <property name="label" translatable="yes">OK</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="toggled" handler="OkToggle_Activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="CancelToggle">
+ <property name="label" translatable="yes">Cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="toggled" handler="CancelToggle_Activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">10</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">10</property>
+ <property name="label" translatable="yes">Enter a name for the new profile:</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="_profileEntry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="_errorMessage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">10</property>
+ <property name="margin_top">10</property>
+ <property name="margin_bottom">10</property>
+ <attributes>
+ <attribute name="foreground" value="#ffff00000000"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/Ryujinx/Ui/SettingsWindow.cs b/Ryujinx/Ui/SettingsWindow.cs
new file mode 100644
index 00000000..1b24e72b
--- /dev/null
+++ b/Ryujinx/Ui/SettingsWindow.cs
@@ -0,0 +1,409 @@
+using Gtk;
+using Ryujinx.Configuration;
+using Ryujinx.Configuration.System;
+using Ryujinx.HLE.HOS.Services.Time.TimeZone;
+using Ryujinx.HLE.FileSystem;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using Ryujinx.Common.Configuration.Hid;
+using GUI = Gtk.Builder.ObjectAttribute;
+
+namespace Ryujinx.Ui
+{
+ public class SettingsWindow : Window
+ {
+ private static ListStore _gameDirsBoxStore;
+ private static VirtualFileSystem _virtualFileSystem;
+
+ private long _systemTimeOffset;
+
+#pragma warning disable CS0649, IDE0044
+ [GUI] CheckButton _errorLogToggle;
+ [GUI] CheckButton _warningLogToggle;
+ [GUI] CheckButton _infoLogToggle;
+ [GUI] CheckButton _stubLogToggle;
+ [GUI] CheckButton _debugLogToggle;
+ [GUI] CheckButton _fileLogToggle;
+ [GUI] CheckButton _guestLogToggle;
+ [GUI] CheckButton _fsAccessLogToggle;
+ [GUI] Adjustment _fsLogSpinAdjustment;
+ [GUI] CheckButton _dockedModeToggle;
+ [GUI] CheckButton _discordToggle;
+ [GUI] CheckButton _vSyncToggle;
+ [GUI] CheckButton _multiSchedToggle;
+ [GUI] CheckButton _fsicToggle;
+ [GUI] CheckButton _ignoreToggle;
+ [GUI] CheckButton _directKeyboardAccess;
+ [GUI] ComboBoxText _systemLanguageSelect;
+ [GUI] ComboBoxText _systemRegionSelect;
+ [GUI] ComboBoxText _systemTimeZoneSelect;
+ [GUI] SpinButton _systemTimeYearSpin;
+ [GUI] SpinButton _systemTimeMonthSpin;
+ [GUI] SpinButton _systemTimeDaySpin;
+ [GUI] SpinButton _systemTimeHourSpin;
+ [GUI] SpinButton _systemTimeMinuteSpin;
+ [GUI] Adjustment _systemTimeYearSpinAdjustment;
+ [GUI] Adjustment _systemTimeMonthSpinAdjustment;
+ [GUI] Adjustment _systemTimeDaySpinAdjustment;
+ [GUI] Adjustment _systemTimeHourSpinAdjustment;
+ [GUI] Adjustment _systemTimeMinuteSpinAdjustment;
+ [GUI] CheckButton _custThemeToggle;
+ [GUI] Entry _custThemePath;
+ [GUI] ToggleButton _browseThemePath;
+ [GUI] Label _custThemePathLabel;
+ [GUI] TreeView _gameDirsBox;
+ [GUI] Entry _addGameDirBox;
+ [GUI] Entry _graphicsShadersDumpPath;
+ [GUI] ComboBoxText _anisotropy;
+ [GUI] ToggleButton _configureController1;
+ [GUI] ToggleButton _configureController2;
+ [GUI] ToggleButton _configureController3;
+ [GUI] ToggleButton _configureController4;
+ [GUI] ToggleButton _configureController5;
+ [GUI] ToggleButton _configureController6;
+ [GUI] ToggleButton _configureController7;
+ [GUI] ToggleButton _configureController8;
+ [GUI] ToggleButton _configureControllerH;
+#pragma warning restore CS0649, IDE0044
+
+ public SettingsWindow(VirtualFileSystem virtualFileSystem, HLE.FileSystem.Content.ContentManager contentManager) : this(new Builder("Ryujinx.Ui.SettingsWindow.glade"), virtualFileSystem, contentManager) { }
+
+ private SettingsWindow(Builder builder, VirtualFileSystem virtualFileSystem, HLE.FileSystem.Content.ContentManager contentManager) : base(builder.GetObject("_settingsWin").Handle)
+ {
+ builder.Autoconnect(this);
+
+ this.Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.Icon.png");
+
+ _virtualFileSystem = virtualFileSystem;
+
+ //Bind Events
+ _configureController1.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player1);
+ _configureController2.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player2);
+ _configureController3.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player3);
+ _configureController4.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player4);
+ _configureController5.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player5);
+ _configureController6.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player6);
+ _configureController7.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player7);
+ _configureController8.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Player8);
+ _configureControllerH.Pressed += (sender, args) => ConfigureController_Pressed(sender, args, PlayerIndex.Handheld);
+
+ //Setup Currents
+ if (ConfigurationState.Instance.Logger.EnableFileLog)
+ {
+ _fileLogToggle.Click();
+ }
+
+ if (ConfigurationState.Instance.Logger.EnableError)
+ {
+ _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();
+ }
+
+ TimeZoneContentManager timeZoneContentManager = new TimeZoneContentManager();
+
+ timeZoneContentManager.InitializeInstance(virtualFileSystem, contentManager, LibHac.FsSystem.IntegrityCheckLevel.None);
+
+ List<string> locationNames = timeZoneContentManager.LocationNameCache.ToList();
+
+ locationNames.Sort();
+
+ foreach (string locationName in locationNames)
+ {
+ _systemTimeZoneSelect.Append(locationName, locationName);
+ }
+
+ _systemLanguageSelect.SetActiveId(ConfigurationState.Instance.System.Language.Value.ToString());
+ _systemRegionSelect.SetActiveId(ConfigurationState.Instance.System.Region.Value.ToString());
+ _systemTimeZoneSelect.SetActiveId(timeZoneContentManager.SanityCheckDeviceLocationName());
+ _anisotropy.SetActiveId(ConfigurationState.Instance.Graphics.MaxAnisotropy.Value.ToString());
+
+ _custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath;
+ _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath;
+ _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
+ _systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset;
+
+ _gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0);
+ _gameDirsBoxStore = new ListStore(typeof(string));
+ _gameDirsBox.Model = _gameDirsBoxStore;
+
+ foreach (string gameDir in ConfigurationState.Instance.Ui.GameDirs.Value)
+ {
+ _gameDirsBoxStore.AppendValues(gameDir);
+ }
+
+ if (_custThemeToggle.Active == false)
+ {
+ _custThemePath.Sensitive = false;
+ _custThemePathLabel.Sensitive = false;
+ _browseThemePath.Sensitive = false;
+ }
+
+ //Setup system time spinners
+ UpdateSystemTimeSpinners();
+ }
+
+ private void UpdateSystemTimeSpinners()
+ {
+ //Bind system time events
+ _systemTimeYearSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
+ _systemTimeMonthSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
+ _systemTimeDaySpin.ValueChanged -= SystemTimeSpin_ValueChanged;
+ _systemTimeHourSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
+ _systemTimeMinuteSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
+
+ //Apply actual system time + SystemTimeOffset to system time spin buttons
+ DateTime systemTime = DateTime.Now.AddSeconds(_systemTimeOffset);
+
+ _systemTimeYearSpinAdjustment.Value = systemTime.Year;
+ _systemTimeMonthSpinAdjustment.Value = systemTime.Month;
+ _systemTimeDaySpinAdjustment.Value = systemTime.Day;
+ _systemTimeHourSpinAdjustment.Value = systemTime.Hour;
+ _systemTimeMinuteSpinAdjustment.Value = systemTime.Minute;
+
+ //Format spin buttons text to include leading zeros
+ _systemTimeYearSpin.Text = systemTime.Year.ToString("0000");
+ _systemTimeMonthSpin.Text = systemTime.Month.ToString("00");
+ _systemTimeDaySpin.Text = systemTime.Day.ToString("00");
+ _systemTimeHourSpin.Text = systemTime.Hour.ToString("00");
+ _systemTimeMinuteSpin.Text = systemTime.Minute.ToString("00");
+
+ //Bind system time events
+ _systemTimeYearSpin.ValueChanged += SystemTimeSpin_ValueChanged;
+ _systemTimeMonthSpin.ValueChanged += SystemTimeSpin_ValueChanged;
+ _systemTimeDaySpin.ValueChanged += SystemTimeSpin_ValueChanged;
+ _systemTimeHourSpin.ValueChanged += SystemTimeSpin_ValueChanged;
+ _systemTimeMinuteSpin.ValueChanged += SystemTimeSpin_ValueChanged;
+ }
+
+ //Events
+ private void SystemTimeSpin_ValueChanged(Object sender, EventArgs e)
+ {
+ int year = _systemTimeYearSpin.ValueAsInt;
+ int month = _systemTimeMonthSpin.ValueAsInt;
+ int day = _systemTimeDaySpin.ValueAsInt;
+ int hour = _systemTimeHourSpin.ValueAsInt;
+ int minute = _systemTimeMinuteSpin.ValueAsInt;
+
+ if (!DateTime.TryParse(year + "-" + month + "-" + day + " " + hour + ":" + minute, out DateTime newTime))
+ {
+ UpdateSystemTimeSpinners();
+
+ return;
+ }
+
+ newTime = newTime.AddSeconds(DateTime.Now.Second).AddMilliseconds(DateTime.Now.Millisecond);
+
+ long systemTimeOffset = (long)Math.Ceiling((newTime - DateTime.Now).TotalMinutes) * 60L;
+
+ if (_systemTimeOffset != systemTimeOffset)
+ {
+ _systemTimeOffset = systemTimeOffset;
+ UpdateSystemTimeSpinners();
+ }
+ }
+
+ private void AddDir_Pressed(object sender, EventArgs args)
+ {
+ if (Directory.Exists(_addGameDirBox.Buffer.Text))
+ {
+ _gameDirsBoxStore.AppendValues(_addGameDirBox.Buffer.Text);
+ }
+ else
+ {
+ FileChooserDialog fileChooser = new FileChooserDialog("Choose the game directory to add to the list", this, FileChooserAction.SelectFolder, "Cancel", ResponseType.Cancel, "Add", ResponseType.Accept);
+
+ if (fileChooser.Run() == (int)ResponseType.Accept)
+ {
+ _gameDirsBoxStore.AppendValues(fileChooser.Filename);
+ }
+
+ fileChooser.Dispose();
+ }
+
+ _addGameDirBox.Buffer.Text = "";
+
+ ((ToggleButton)sender).SetStateFlags(0, true);
+ }
+
+ private void RemoveDir_Pressed(object sender, EventArgs args)
+ {
+ TreeSelection selection = _gameDirsBox.Selection;
+
+ if (selection.GetSelected(out TreeIter treeIter))
+ {
+ _gameDirsBoxStore.Remove(ref treeIter);
+ }
+
+ ((ToggleButton)sender).SetStateFlags(0, true);
+ }
+
+ private void CustThemeToggle_Activated(object sender, EventArgs args)
+ {
+ _custThemePath.Sensitive = _custThemeToggle.Active;
+ _custThemePathLabel.Sensitive = _custThemeToggle.Active;
+ _browseThemePath.Sensitive = _custThemeToggle.Active;
+ }
+
+ private void BrowseThemeDir_Pressed(object sender, EventArgs args)
+ {
+ FileChooserDialog fileChooser = new FileChooserDialog("Choose the theme to load", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Select", ResponseType.Accept);
+
+ fileChooser.Filter = new FileFilter();
+ fileChooser.Filter.AddPattern("*.css");
+
+ if (fileChooser.Run() == (int)ResponseType.Accept)
+ {
+ _custThemePath.Buffer.Text = fileChooser.Filename;
+ }
+
+ fileChooser.Dispose();
+
+ _browseThemePath.SetStateFlags(0, true);
+ }
+
+ private void OpenLogsFolder_Pressed(object sender, EventArgs args)
+ {
+ string logPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
+
+ DirectoryInfo directory = new DirectoryInfo(logPath);
+ directory.Create();
+
+ Process.Start(new ProcessStartInfo()
+ {
+ FileName = logPath,
+ UseShellExecute = true,
+ Verb = "open"
+ });
+ }
+
+ private void ConfigureController_Pressed(object sender, EventArgs args, PlayerIndex playerIndex)
+ {
+ ((ToggleButton)sender).SetStateFlags(0, true);
+
+ ControllerWindow controllerWin = new ControllerWindow(playerIndex, _virtualFileSystem);
+ controllerWin.Show();
+ }
+
+ private void SaveToggle_Activated(object sender, EventArgs args)
+ {
+ List<string> gameDirs = new List<string>();
+
+ _gameDirsBoxStore.GetIterFirst(out TreeIter treeIter);
+ for (int i = 0; i < _gameDirsBoxStore.IterNChildren(); i++)
+ {
+ gameDirs.Add((string)_gameDirsBoxStore.GetValue(treeIter, 0));
+
+ _gameDirsBoxStore.IterNext(ref treeIter);
+ }
+
+ ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active;
+ ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active;
+ ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active;
+ ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active;
+ ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
+ ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.Active;
+ ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
+ ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active;
+ ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active;
+ ConfigurationState.Instance.Ui.EnableCustomTheme.Value = _custThemeToggle.Active;
+ ConfigurationState.Instance.System.Language.Value = Enum.Parse<Language>(_systemLanguageSelect.ActiveId);
+ ConfigurationState.Instance.System.Region.Value = Enum.Parse<Configuration.System.Region>(_systemRegionSelect.ActiveId);
+ ConfigurationState.Instance.System.TimeZone.Value = _systemTimeZoneSelect.ActiveId;
+ ConfigurationState.Instance.System.SystemTimeOffset.Value = _systemTimeOffset;
+ ConfigurationState.Instance.Ui.CustomThemePath.Value = _custThemePath.Buffer.Text;
+ ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text;
+ ConfigurationState.Instance.Ui.GameDirs.Value = gameDirs;
+ ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value;
+ ConfigurationState.Instance.Graphics.MaxAnisotropy.Value = float.Parse(_anisotropy.ActiveId);
+
+ MainWindow.SaveConfig();
+ MainWindow.ApplyTheme();
+ MainWindow.UpdateGameTable();
+ Dispose();
+ }
+
+ private void CloseToggle_Activated(object sender, EventArgs args)
+ {
+ Dispose();
+ }
+ }
+} \ No newline at end of file
diff --git a/Ryujinx/Ui/SwitchSettings.glade b/Ryujinx/Ui/SettingsWindow.glade
index 7415e76e..ea662de0 100644
--- a/Ryujinx/Ui/SwitchSettings.glade
+++ b/Ryujinx/Ui/SettingsWindow.glade
@@ -7,95 +7,21 @@
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
- <object class="GtkAdjustment" id="_systemTimeYearSpinAdjustment">
- <property name="lower">2000</property>
- <property name="upper">2060</property>
- <property name="step_increment">1</property>
- <property name="page_increment">10</property>
- </object>
- <object class="GtkAdjustment" id="_systemTimeMonthSpinAdjustment">
- <property name="lower">1</property>
- <property name="upper">12</property>
- <property name="step_increment">1</property>
- <property name="page_increment">5</property>
- </object>
- <object class="GtkAdjustment" id="_systemTimeDaySpinAdjustment">
- <property name="lower">1</property>
- <property name="upper">31</property>
- <property name="step_increment">1</property>
- <property name="page_increment">5</property>
- </object>
- <object class="GtkAdjustment" id="_systemTimeHourSpinAdjustment">
- <property name="lower">0</property>
- <property name="upper">23</property>
- <property name="step_increment">1</property>
- <property name="page_increment">5</property>
- </object>
- <object class="GtkAdjustment" id="_systemTimeMinuteSpinAdjustment">
- <property name="lower">0</property>
- <property name="upper">59</property>
- <property name="step_increment">1</property>
- <property name="page_increment">5</property>
- </object>
- <object class="GtkDialog" id="_settingsWin">
+ <object class="GtkWindow" id="_settingsWin">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Ryujinx - Settings</property>
<property name="modal">True</property>
<property name="window_position">center</property>
- <property name="default_width">910</property>
- <property name="default_height">790</property>
- <property name="type_hint">dialog</property>
+ <property name="default_width">650</property>
+ <property name="default_height">520</property>
<child>
<placeholder/>
</child>
- <child internal-child="vbox">
+ <child>
<object class="GtkBox">
+ <property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
- <property name="spacing">2</property>
- <child internal-child="action_area">
- <object class="GtkButtonBox">
- <property name="can_focus">False</property>
- <property name="margin_right">5</property>
- <property name="margin_top">3</property>
- <property name="margin_bottom">3</property>
- <property name="layout_style">end</property>
- <child>
- <object class="GtkToggleButton" id="SaveToggle">
- <property name="label" translatable="yes">Save</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="toggled" handler="SaveToggle_Activated" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="CloseToggle">
- <property name="label" translatable="yes">Close</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="toggled" handler="CloseToggle_Activated" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="padding">5</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
@@ -142,303 +68,19 @@
</packing>
</child>
<child>
- <object class="GtkBox" id="box1">
+ <object class="GtkBox" id="General">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">10</property>
<property name="orientation">vertical</property>
<child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Change System Language</property>
- <property name="halign">end</property>
- <property name="label" translatable="yes">System Language:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBoxText" id="_systemLanguageSelect">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Change System Language</property>
- <property name="margin_left">5</property>
- <items>
- <item id="AmericanEnglish" translatable="yes">American English</item>
- <item id="BritishEnglish" translatable="yes">British English</item>
- <item id="CanadianFrench" translatable="yes">Canadian French</item>
- <item id="Chinese" translatable="yes">Chinese</item>
- <item id="Dutch" translatable="yes">Dutch</item>
- <item id="French" translatable="yes">French</item>
- <item id="German" translatable="yes">German</item>
- <item id="Italian" translatable="yes">Italian</item>
- <item id="Japanese" translatable="yes">Japanese</item>
- <item id="Korean" translatable="yes">Korean</item>
- <item id="LatinAmericanSpanish" translatable="yes">Latin American Spanish</item>
- <item id="Portuguese" translatable="yes">Portuguese</item>
- <item id="Russian" translatable="yes">Russian</item>
- <item id="SimplifiedChinese" translatable="yes">Simplified Chinese</item>
- <item id="Spanish" translatable="yes">Spanish</item>
- <item id="Taiwanese" translatable="yes">Taiwanese</item>
- <item id="TraditionalChinese" translatable="yes">Traditional Chinese</item>
- </items>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Change System Region</property>
- <property name="halign">end</property>
- <property name="label" translatable="yes">System Region:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="padding">5</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBoxText" id="_systemRegionSelect">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Change System Region</property>
- <property name="margin_left">5</property>
- <items>
- <item id="Japan" translatable="yes">Japan</item>
- <item id="USA" translatable="yes">USA</item>
- <item id="Europe" translatable="yes">Europe</item>
- <item id="Australia" translatable="yes">Australia</item>
- <item id="China" translatable="yes">China</item>
- <item id="Korea" translatable="yes">Korea</item>
- <item id="Taiwan" translatable="yes">Taiwan</item>
- </items>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Change System TimeZone</property>
- <property name="halign">end</property>
- <property name="label" translatable="yes">System TimeZone:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkComboBoxText" id="_systemTimeZoneSelect">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Change System TimeZone</property>
- <property name="margin_left">5</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="label" translatable="yes">System Time:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="padding">5</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkSpinButton" id="_systemTimeYearSpin">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">5</property>
- <property name="orientation">vertical</property>
- <property name="adjustment">_systemTimeYearSpinAdjustment</property>
- <property name="wrap">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="label" translatable="no">-</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="padding">5</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkSpinButton" id="_systemTimeMonthSpin">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">0</property>
- <property name="orientation">vertical</property>
- <property name="adjustment">_systemTimeMonthSpinAdjustment</property>
- <property name="wrap">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="label" translatable="no">-</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="padding">5</property>
- <property name="position">4</property>
- </packing>
- </child>
- <child>
- <object class="GtkSpinButton" id="_systemTimeDaySpin">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">0</property>
- <property name="orientation">vertical</property>
- <property name="adjustment">_systemTimeDaySpinAdjustment</property>
- <property name="wrap">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">5</property>
- </packing>
- </child>
- <child>
- <object class="GtkSpinButton" id="_systemTimeHourSpin">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">25</property>
- <property name="orientation">vertical</property>
- <property name="adjustment">_systemTimeHourSpinAdjustment</property>
- <property name="wrap">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">6</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="label" translatable="no">:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="padding">5</property>
- <property name="position">7</property>
- </packing>
- </child>
- <child>
- <object class="GtkSpinButton" id="_systemTimeMinuteSpin">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">0</property>
- <property name="orientation">vertical</property>
- <property name="adjustment">_systemTimeMinuteSpinAdjustment</property>
- <property name="wrap">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">8</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
<object class="GtkCheckButton" id="_discordToggle">
<property name="label" translatable="yes">Enable Discord Rich Presence</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">Enables or disables Discord Rich Presense</property>
+ <property name="tooltip_text" translatable="yes">Enables or disables Discord Rich Presence</property>
<property name="halign">start</property>
<property name="draw_indicator">True</property>
</object>
@@ -446,7 +88,7 @@
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
- <property name="position">3</property>
+ <property name="position">2</property>
</packing>
</child>
</object>
@@ -570,23 +212,6 @@
</packing>
</child>
<child>
- <object class="GtkToggleButton" id="_browseDir">
- <property name="label" translatable="yes">Browse...</property>
- <property name="width_request">80</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">Browse for a game directory</property>
- <property name="margin_left">5</property>
- <signal name="toggled" handler="BrowseDir_Pressed" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
<object class="GtkToggleButton" id="_removeDir">
<property name="label" translatable="yes">Remove</property>
<property name="width_request">80</property>
@@ -775,6 +400,9 @@
<object class="GtkBox" id="TabInput">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="margin_left">5</property>
+ <property name="margin_right">10</property>
+ <property name="margin_top">5</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox">
@@ -834,629 +462,1029 @@
</packing>
</child>
<child>
- <object class="GtkBox" id="Controller1Box">
+ <object class="GtkGrid" id="ControllerGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="margin_left">10</property>
- <property name="margin_right">10</property>
- <property name="margin_bottom">5</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="column_spacing">20</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
- <object class="GtkBox">
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">Player 1</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_configureController1">
+ <property name="label" translatable="yes">Configure</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">Player 3</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_configureController3">
+ <property name="label" translatable="yes">Configure</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">Player 2</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_configureController2">
+ <property name="label" translatable="yes">Configure</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">Handheld</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_configureControllerH">
+ <property name="label" translatable="yes">Configure</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">Player 6</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_configureController6">
+ <property name="label" translatable="yes">Configure</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">Player 5</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_configureController5">
+ <property name="label" translatable="yes">Configure</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">Player 7</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_configureController7">
+ <property name="label" translatable="yes">Configure</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">Player 4</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_configureController4">
+ <property name="label" translatable="yes">Configure</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ <property name="label" translatable="yes">Player 8</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="_configureController8">
+ <property name="label" translatable="yes">Configure</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">20</property>
+ <property name="margin_right">20</property>
+ <property name="margin_top">20</property>
+ <property name="margin_bottom">20</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">3</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">2</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="left_attach">4</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Input</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="TabSystem">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">5</property>
+ <property name="margin_right">10</property>
+ <property name="margin_top">5</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="CatCore">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="margin_left">5</property>
+ <property name="margin_right">5</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin_bottom">5</property>
+ <property name="label" translatable="yes">Core</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="RegionBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">The primary controller's type</property>
- <property name="halign">center</property>
- <property name="margin_top">5</property>
- <property name="margin_bottom">5</property>
- <property name="label" translatable="yes">Controller Type:</property>
+ <property name="tooltip_text" translatable="yes">Change System Region</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">System Region:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">0</property>
+ <property name="padding">5</property>
+ <property name="position">2</property>
</packing>
</child>
<child>
- <object class="GtkComboBoxText" id="_controller1Type">
+ <object class="GtkComboBoxText" id="_systemRegionSelect">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">The primary controller's type</property>
+ <property name="tooltip_text" translatable="yes">Change System Region</property>
<property name="margin_left">5</property>
- <property name="active">0</property>
<items>
- <item id="Handheld" translatable="yes">Handheld</item>
- <item id="ProController" translatable="yes">Pro Controller</item>
- <item id="NpadPair" translatable="yes">Paired Joycons</item>
- <item id="NpadLeft" translatable="yes">Left Joycon</item>
- <item id="NpadRight" translatable="yes">Right Joycon</item>
+ <item id="Japan" translatable="yes">Japan</item>
+ <item id="USA" translatable="yes">USA</item>
+ <item id="Europe" translatable="yes">Europe</item>
+ <item id="Australia" translatable="yes">Australia</item>
+ <item id="China" translatable="yes">China</item>
+ <item id="Korea" translatable="yes">Korea</item>
+ <item id="Taiwan" translatable="yes">Taiwan</item>
</items>
</object>
<packing>
- <property name="expand">True</property>
+ <property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">1</property>
+ <property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="padding">10</property>
+ <property name="padding">5</property>
<property name="position">0</property>
</packing>
</child>
<child>
- <object class="GtkGrid">
+ <object class="GtkBox" id="LanguageBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="row_spacing">2</property>
- <property name="column_spacing">5</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">LStick Up</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">LStick Down</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">LStick Left</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">LStick Right</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">LStick Button</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">4</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Dpad Up</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">5</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Dpad Down</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">6</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Dpad Left</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">7</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Dpad Right</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">8</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">-</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">9</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">L</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">10</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">ZL</property>
- </object>
- <packing>
- <property name="left_attach">0</property>
- <property name="top_attach">11</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">ZR</property>
- </object>
- <packing>
- <property name="left_attach">2</property>
- <property name="top_attach">11</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">R</property>
- </object>
- <packing>
- <property name="left_attach">2</property>
- <property name="top_attach">10</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">+</property>
- </object>
- <packing>
- <property name="left_attach">2</property>
- <property name="top_attach">9</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Y</property>
+ <property name="tooltip_text" translatable="yes">Change System Language</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">System Language:</property>
</object>
<packing>
- <property name="left_attach">2</property>
- <property name="top_attach">8</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">0</property>
</packing>
</child>
<child>
- <object class="GtkLabel">
+ <object class="GtkComboBoxText" id="_systemLanguageSelect">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">X</property>
+ <property name="tooltip_text" translatable="yes">Change System Language</property>
+ <items>
+ <item id="AmericanEnglish" translatable="yes">American English</item>
+ <item id="BritishEnglish" translatable="yes">British English</item>
+ <item id="CanadianFrench" translatable="yes">Canadian French</item>
+ <item id="Chinese" translatable="yes">Chinese</item>
+ <item id="Dutch" translatable="yes">Dutch</item>
+ <item id="French" translatable="yes">French</item>
+ <item id="German" translatable="yes">German</item>
+ <item id="Italian" translatable="yes">Italian</item>
+ <item id="Japanese" translatable="yes">Japanese</item>
+ <item id="Korean" translatable="yes">Korean</item>
+ <item id="LatinAmericanSpanish" translatable="yes">Latin American Spanish</item>
+ <item id="Portuguese" translatable="yes">Portuguese</item>
+ <item id="Russian" translatable="yes">Russian</item>
+ <item id="SimplifiedChinese" translatable="yes">Simplified Chinese</item>
+ <item id="Spanish" translatable="yes">Spanish</item>
+ <item id="Taiwanese" translatable="yes">Taiwanese</item>
+ <item id="TraditionalChinese" translatable="yes">Traditional Chinese</item>
+ </items>
</object>
<packing>
- <property name="left_attach">2</property>
- <property name="top_attach">7</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
</packing>
</child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="TimeZoneBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">B</property>
+ <property name="tooltip_text" translatable="yes">Change System TimeZone</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">System TimeZone:</property>
</object>
<packing>
- <property name="left_attach">2</property>
- <property name="top_attach">6</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">1</property>
</packing>
</child>
<child>
- <object class="GtkLabel">
+ <object class="GtkComboBoxText" id="_systemTimeZoneSelect">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">A</property>
+ <property name="tooltip_text" translatable="yes">Change System TimeZone</property>
+ <property name="margin_left">5</property>
</object>
<packing>
- <property name="left_attach">2</property>
- <property name="top_attach">5</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
</packing>
</child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="TimeBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">RStick Button</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">System Time:</property>
</object>
<packing>
- <property name="left_attach">2</property>
- <property name="top_attach">4</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">0</property>
</packing>
</child>
<child>
- <object class="GtkLabel">
+ <object class="GtkSpinButton" id="_systemTimeYearSpin">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">RStick Right</property>
+ <property name="can_focus">True</property>
+ <property name="text" translatable="yes">2000</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">_systemTimeYearSpinAdjustment</property>
+ <property name="wrap">True</property>
+ <property name="value">2000</property>
</object>
<packing>
- <property name="left_attach">2</property>
- <property name="top_attach">3</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">RStick Left</property>
+ <property name="halign">end</property>
+ <property name="label">-</property>
</object>
<packing>
- <property name="left_attach">2</property>
- <property name="top_attach">2</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">2</property>
</packing>
</child>
<child>
- <object class="GtkLabel">
+ <object class="GtkSpinButton" id="_systemTimeMonthSpin">
<property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">RStick Down</property>
+ <property name="can_focus">True</property>
+ <property name="text" translatable="yes">1</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">_systemTimeMonthSpinAdjustment</property>
+ <property name="wrap">True</property>
+ <property name="value">1</property>
</object>
<packing>
- <property name="left_attach">2</property>
- <property name="top_attach">1</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">RStick Up</property>
- </object>
- <packing>
- <property name="left_attach">2</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_lStickUp1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_lStickDown1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_lStickLeft1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_lStickRight1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_lStickButton1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">4</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_dpadUp1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">5</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_dpadDown1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">6</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_dpadLeft1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">7</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_dpadRight1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">8</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_minus1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">9</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_l1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">10</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_zL1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">1</property>
- <property name="top_attach">11</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_rStickUp1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">3</property>
- <property name="top_attach">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_rStickDown1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">3</property>
- <property name="top_attach">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_rStickLeft1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">3</property>
- <property name="top_attach">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_rStickRight1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">3</property>
- <property name="top_attach">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_rStickButton1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">3</property>
- <property name="top_attach">4</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_a1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">3</property>
- <property name="top_attach">5</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_b1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="left_attach">3</property>
- <property name="top_attach">6</property>
- </packing>
- </child>
- <child>
- <object class="GtkToggleButton" id="_x1">
- <property name="label" translatable="yes"> </property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
+ <property name="halign">end</property>
+ <property name="label">-</property>
</object>
<packing>
- <property name="left_attach">3</property>
- <property name="top_attach">7</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">4</property>
</packing>
</child>
<child>
- <object class="GtkToggleButton" id="_y1">
- <property name="label" translatable="yes"> </property>
+ <object class="GtkSpinButton" id="_systemTimeDaySpin">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="receives_default">True</property>
+ <property name="text" translatable="yes">1</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">_systemTimeDaySpinAdjustment</property>
+ <property name="wrap">True</property>
+ <property name="value">1</property>
</object>
<packing>
- <property name="left_attach">3</property>
- <property name="top_attach">8</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
</packing>
</child>
<child>
- <object class="GtkToggleButton" id="_plus1">
- <property name="label" translatable="yes"> </property>
+ <object class="GtkSpinButton" id="_systemTimeHourSpin">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="receives_default">True</property>
+ <property name="text" translatable="yes">0</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">_systemTimeHourSpinAdjustment</property>
+ <property name="wrap">True</property>
</object>
<packing>
- <property name="left_attach">3</property>
- <property name="top_attach">9</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
</packing>
</child>
<child>
- <object class="GtkToggleButton" id="_r1">
- <property name="label" translatable="yes"> </property>
+ <object class="GtkLabel">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ <property name="label">:</property>
</object>
<packing>
- <property name="left_attach">3</property>
- <property name="top_attach">10</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">7</property>
</packing>
</child>
<child>
- <object class="GtkToggleButton" id="_zR1">
- <property name="label" translatable="yes"> </property>
+ <object class="GtkSpinButton" id="_systemTimeMinuteSpin">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="receives_default">True</property>
+ <property name="text" translatable="yes">0</property>
+ <property name="orientation">vertical</property>
+ <property name="adjustment">_systemTimeMinuteSpinAdjustment</property>
+ <property name="wrap">True</property>
</object>
<packing>
- <property name="left_attach">3</property>
- <property name="top_attach">11</property>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">8</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="padding">10</property>
+ <property name="padding">5</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="_vSyncToggle">
+ <property name="label" translatable="yes">Enable VSync</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Enables or disables Vertical Sync</property>
+ <property name="halign">start</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="_multiSchedToggle">
+ <property name="label" translatable="yes">Enable Multicore Scheduling</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Enables or disables multi-core scheduling of threads</property>
+ <property name="halign">start</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="_fsicToggle">
+ <property name="label" translatable="yes">Enable FS Integrity Checks</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Enables integrity checks on Game content files</property>
+ <property name="halign">start</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSeparator">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">5</property>
+ <property name="margin_right">5</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="CatHacks">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="margin_left">5</property>
+ <property name="margin_right">5</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin_bottom">5</property>
+ <property name="label" translatable="yes">Hacks</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin_bottom">5</property>
+ <property name="label" translatable="yes"> - These may cause instability</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
@@ -1464,56 +1492,76 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">0</property>
+ <property name="position">1</property>
</packing>
</child>
<child>
- <object class="GtkImage" id="_controller1Image">
+ <object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkCheckButton" id="_ignoreToggle">
+ <property name="label" translatable="yes">Ignore Missing Services</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Enable or disable ignoring missing services</property>
+ <property name="halign">start</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
- <property name="position">1</property>
+ <property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
- <property name="position">2</property>
+ <property name="padding">5</property>
+ <property name="position">4</property>
</packing>
</child>
</object>
<packing>
- <property name="position">1</property>
+ <property name="position">2</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="label" translatable="yes">Input</property>
+ <property name="halign">end</property>
+ <property name="label" translatable="yes">System</property>
</object>
<packing>
- <property name="position">1</property>
+ <property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
- <object class="GtkBox" id="TabSystem">
+ <object class="GtkBox" id="TabGraphics">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="margin_left">5</property>
- <property name="margin_right">10</property>
<property name="margin_top">5</property>
<property name="orientation">vertical</property>
<child>
- <object class="GtkBox" id="CatCore">
+ <object class="GtkBox" id="CatEnhancements">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="valign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="orientation">vertical</property>
@@ -1523,8 +1571,9 @@
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
+ <property name="margin_right">5</property>
<property name="margin_bottom">5</property>
- <property name="label" translatable="yes">Core</property>
+ <property name="label" translatable="yes">Enhancements</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
@@ -1536,110 +1585,18 @@
</packing>
</child>
<child>
- <object class="GtkBox">
+ <object class="GtkBox" id="EnhancementOptions">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="orientation">vertical</property>
<child>
- <object class="GtkCheckButton" id="_vSyncToggle">
- <property name="label" translatable="yes">Enable VSync</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">Enables or disables Vertical Sync</property>
- <property name="halign">start</property>
- <property name="margin_top">5</property>
- <property name="margin_bottom">5</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="_multiSchedToggle">
- <property name="label" translatable="yes">Enable Multicore Scheduling</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">Enables or disables multi-core scheduling of threads</property>
- <property name="halign">start</property>
- <property name="margin_top">5</property>
- <property name="margin_bottom">5</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="_fsicToggle">
- <property name="label" translatable="yes">Enable FS Integrity Checks</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">Enables integrity checks on Game content files</property>
- <property name="halign">start</property>
- <property name="margin_top">5</property>
- <property name="margin_bottom">5</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="tooltip_text" translatable="yes">Graphics Shaders Dump Path</property>
- <property name="label" translatable="yes">Graphics Shaders Dump Path:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="padding">5</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="_graphicsShadersDumpPath">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="tooltip_text" translatable="yes">Graphics Shaders Dump Path</property>
- <property name="valign">center</property>
- <property name="caps_lock_warning">False</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="padding">5</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
@@ -1679,12 +1636,12 @@
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
- <property name="position">4</property>
+ <property name="position">0</property>
</packing>
</child>
</object>
<packing>
- <property name="expand">True</property>
+ <property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
@@ -1701,8 +1658,6 @@
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="margin_left">5</property>
- <property name="margin_right">5</property>
</object>
<packing>
<property name="expand">False</property>
@@ -1712,7 +1667,7 @@
</packing>
</child>
<child>
- <object class="GtkBox" id="CatLog">
+ <object class="GtkBox" id="CatDev">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
@@ -1723,8 +1678,10 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
+ <property name="margin_left">5</property>
+ <property name="margin_right">5</property>
<property name="margin_bottom">5</property>
- <property name="label" translatable="yes">Logging</property>
+ <property name="label" translatable="yes">Developer Options</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
@@ -1736,7 +1693,7 @@
</packing>
</child>
<child>
- <object class="GtkBox">
+ <object class="GtkBox" id="DevOptions">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
@@ -1746,38 +1703,32 @@
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
<child>
- <object class="GtkCheckButton" id="_fileLogToggle">
- <property name="label" translatable="yes">Enable Logging to File</property>
+ <object class="GtkLabel">
<property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">Enables or disables logging to a file on disk</property>
- <property name="halign">start</property>
- <property name="margin_top">5</property>
- <property name="margin_bottom">5</property>
- <property name="draw_indicator">True</property>
+ <property name="can_focus">False</property>
+ <property name="tooltip_text" translatable="yes">Graphics Shaders Dump Path</property>
+ <property name="label" translatable="yes">Graphics Shaders Dump Path:</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
+ <property name="padding">5</property>
<property name="position">0</property>
</packing>
</child>
<child>
- <object class="GtkButton" id="_openLogsFolderButton">
- <property name="label" translatable="yes">Open Logs Folder</property>
+ <object class="GtkEntry" id="_graphicsShadersDumpPath">
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="tooltip_text" translatable="yes">Opens the folder where logs are written to.</property>
- <property name="margin_left">5</property>
- <property name="margin_top">5</property>
- <property name="margin_bottom">5</property>
- <signal name="clicked" handler="OpenLogsFolder_Pressed" swapped="no"/>
+ <property name="tooltip_text" translatable="yes">Graphics Shaders Dump Path</property>
+ <property name="valign">center</property>
+ <property name="caps_lock_warning">False</property>
</object>
<packing>
- <property name="expand">False</property>
+ <property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
@@ -1786,9 +1737,116 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
+ <property name="padding">5</property>
<property name="position">0</property>
</packing>
</child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Graphics</property>
+ </object>
+ <packing>
+ <property name="position">3</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="TabLogging">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">5</property>
+ <property name="margin_right">10</property>
+ <property name="margin_top">5</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkBox" id="CatLogging">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">5</property>
+ <property name="margin_right">5</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="margin_bottom">5</property>
+ <property name="label" translatable="yes">Logging</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="LogggingOptions">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="valign">start</property>
+ <property name="margin_left">10</property>
+ <property name="margin_right">10</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkCheckButton" id="_fileLogToggle">
+ <property name="label" translatable="yes">Enable Logging to File</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="tooltip_text" translatable="yes">Enables or disables logging to a file on disk</property>
+ <property name="halign">start</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="_openLogsFolderButton">
+ <property name="label" translatable="yes">Open Logs Folder</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="tooltip_text" translatable="yes">Opens the folder where logs are written to.</property>
+ <property name="halign">start</property>
+ <property name="margin_bottom">10</property>
+ <signal name="clicked" handler="OpenLogsFolder_Pressed" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
<child>
<object class="GtkCheckButton" id="_debugLogToggle">
<property name="label" translatable="yes">Enable Debug Logs</property>
@@ -1938,6 +1996,7 @@
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Enables FS access log output to the console. Possible modes are 0-3</property>
+ <property name="text" translatable="yes">0</property>
<property name="adjustment">_fsLogSpinAdjustment</property>
</object>
<packing>
@@ -1966,126 +2025,22 @@
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkSeparator">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">5</property>
- <property name="margin_right">5</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="padding">5</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="CatHacks">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">5</property>
- <property name="margin_right">5</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="margin_bottom">5</property>
- <property name="label" translatable="yes">Hacks</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="margin_bottom">5</property>
- <property name="label" translatable="yes"> - These may cause instability</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">10</property>
- <property name="margin_right">10</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkCheckButton" id="_ignoreToggle">
- <property name="label" translatable="yes">Ignore Missing Services</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="tooltip_text" translatable="yes">Enable or disable ignoring missing services</property>
- <property name="halign">start</property>
- <property name="margin_top">5</property>
- <property name="margin_bottom">5</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="padding">5</property>
- <property name="position">4</property>
+ <property name="position">0</property>
</packing>
</child>
</object>
<packing>
- <property name="position">2</property>
+ <property name="position">4</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
- <property name="halign">end</property>
- <property name="label" translatable="yes">System</property>
+ <property name="label" translatable="yes">Logging</property>
</object>
<packing>
- <property name="position">2</property>
+ <property name="position">4</property>
<property name="tab_fill">False</property>
</packing>
</child>
@@ -2097,10 +2052,83 @@
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButtonBox" id="_buttonBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_right">5</property>
+ <property name="margin_top">3</property>
+ <property name="margin_bottom">3</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkToggleButton" id="SaveToggle">
+ <property name="label" translatable="yes">Save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="toggled" handler="SaveToggle_Activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToggleButton" id="CloseToggle">
+ <property name="label" translatable="yes">Close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="margin_left">4</property>
+ <signal name="toggled" handler="CloseToggle_Activated" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">5</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
+ <object class="GtkAdjustment" id="_systemTimeDaySpinAdjustment">
+ <property name="lower">1</property>
+ <property name="upper">31</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">5</property>
+ </object>
+ <object class="GtkAdjustment" id="_systemTimeHourSpinAdjustment">
+ <property name="upper">23</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">5</property>
+ </object>
+ <object class="GtkAdjustment" id="_systemTimeMinuteSpinAdjustment">
+ <property name="upper">59</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">5</property>
+ </object>
+ <object class="GtkAdjustment" id="_systemTimeMonthSpinAdjustment">
+ <property name="lower">1</property>
+ <property name="upper">12</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">5</property>
+ </object>
+ <object class="GtkAdjustment" id="_systemTimeYearSpinAdjustment">
+ <property name="lower">2000</property>
+ <property name="upper">2060</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
</interface>
diff --git a/Ryujinx/Ui/SwitchSettings.cs b/Ryujinx/Ui/SwitchSettings.cs
deleted file mode 100644
index 28435572..00000000
--- a/Ryujinx/Ui/SwitchSettings.cs
+++ /dev/null
@@ -1,611 +0,0 @@
-using Gtk;
-using Ryujinx.Configuration;
-using Ryujinx.Configuration.Hid;
-using Ryujinx.Configuration.System;
-using Ryujinx.HLE.HOS.Services.Time.TimeZone;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-
-using GUI = Gtk.Builder.ObjectAttribute;
-
-namespace Ryujinx.Ui
-{
- public class SwitchSettings : Window
- {
- private static ListStore _gameDirsBoxStore;
-
- private static bool _listeningForKeypress;
-
- private long _systemTimeOffset;
-
-#pragma warning disable CS0649
-#pragma warning disable IDE0044
- [GUI] Window _settingsWin;
- [GUI] CheckButton _errorLogToggle;
- [GUI] CheckButton _warningLogToggle;
- [GUI] CheckButton _infoLogToggle;
- [GUI] CheckButton _stubLogToggle;
- [GUI] CheckButton _debugLogToggle;
- [GUI] CheckButton _fileLogToggle;
- [GUI] CheckButton _guestLogToggle;
- [GUI] CheckButton _fsAccessLogToggle;
- [GUI] Adjustment _fsLogSpinAdjustment;
- [GUI] CheckButton _dockedModeToggle;
- [GUI] CheckButton _discordToggle;
- [GUI] CheckButton _vSyncToggle;
- [GUI] CheckButton _multiSchedToggle;
- [GUI] CheckButton _fsicToggle;
- [GUI] CheckButton _ignoreToggle;
- [GUI] CheckButton _directKeyboardAccess;
- [GUI] ComboBoxText _systemLanguageSelect;
- [GUI] ComboBoxText _systemRegionSelect;
- [GUI] ComboBoxText _systemTimeZoneSelect;
- [GUI] SpinButton _systemTimeYearSpin;
- [GUI] SpinButton _systemTimeMonthSpin;
- [GUI] SpinButton _systemTimeDaySpin;
- [GUI] SpinButton _systemTimeHourSpin;
- [GUI] SpinButton _systemTimeMinuteSpin;
- [GUI] Adjustment _systemTimeYearSpinAdjustment;
- [GUI] Adjustment _systemTimeMonthSpinAdjustment;
- [GUI] Adjustment _systemTimeDaySpinAdjustment;
- [GUI] Adjustment _systemTimeHourSpinAdjustment;
- [GUI] Adjustment _systemTimeMinuteSpinAdjustment;
- [GUI] CheckButton _custThemeToggle;
- [GUI] Entry _custThemePath;
- [GUI] ToggleButton _browseThemePath;
- [GUI] Label _custThemePathLabel;
- [GUI] TreeView _gameDirsBox;
- [GUI] Entry _addGameDirBox;
- [GUI] ToggleButton _addDir;
- [GUI] ToggleButton _browseDir;
- [GUI] ToggleButton _removeDir;
- [GUI] Entry _graphicsShadersDumpPath;
- [GUI] ComboBoxText _anisotropy;
- [GUI] Image _controller1Image;
-
- [GUI] ComboBoxText _controller1Type;
- [GUI] ToggleButton _lStickUp1;
- [GUI] ToggleButton _lStickDown1;
- [GUI] ToggleButton _lStickLeft1;
- [GUI] ToggleButton _lStickRight1;
- [GUI] ToggleButton _lStickButton1;
- [GUI] ToggleButton _dpadUp1;
- [GUI] ToggleButton _dpadDown1;
- [GUI] ToggleButton _dpadLeft1;
- [GUI] ToggleButton _dpadRight1;
- [GUI] ToggleButton _minus1;
- [GUI] ToggleButton _l1;
- [GUI] ToggleButton _zL1;
- [GUI] ToggleButton _rStickUp1;
- [GUI] ToggleButton _rStickDown1;
- [GUI] ToggleButton _rStickLeft1;
- [GUI] ToggleButton _rStickRight1;
- [GUI] ToggleButton _rStickButton1;
- [GUI] ToggleButton _a1;
- [GUI] ToggleButton _b1;
- [GUI] ToggleButton _x1;
- [GUI] ToggleButton _y1;
- [GUI] ToggleButton _plus1;
- [GUI] ToggleButton _r1;
- [GUI] ToggleButton _zR1;
-#pragma warning restore CS0649
-#pragma warning restore IDE0044
-
- public SwitchSettings(HLE.FileSystem.VirtualFileSystem virtualFileSystem, HLE.FileSystem.Content.ContentManager contentManager) : this(new Builder("Ryujinx.Ui.SwitchSettings.glade"), virtualFileSystem, contentManager) { }
-
- private SwitchSettings(Builder builder, HLE.FileSystem.VirtualFileSystem virtualFileSystem, HLE.FileSystem.Content.ContentManager contentManager) : base(builder.GetObject("_settingsWin").Handle)
- {
- builder.Autoconnect(this);
-
- _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);
-
- //Bind Events
- _lStickUp1.Clicked += (sender, args) => Button_Pressed(sender, args, _lStickUp1);
- _lStickDown1.Clicked += (sender, args) => Button_Pressed(sender, args, _lStickDown1);
- _lStickLeft1.Clicked += (sender, args) => Button_Pressed(sender, args, _lStickLeft1);
- _lStickRight1.Clicked += (sender, args) => Button_Pressed(sender, args, _lStickRight1);
- _lStickButton1.Clicked += (sender, args) => Button_Pressed(sender, args, _lStickButton1);
- _dpadUp1.Clicked += (sender, args) => Button_Pressed(sender, args, _dpadUp1);
- _dpadDown1.Clicked += (sender, args) => Button_Pressed(sender, args, _dpadDown1);
- _dpadLeft1.Clicked += (sender, args) => Button_Pressed(sender, args, _dpadLeft1);
- _dpadRight1.Clicked += (sender, args) => Button_Pressed(sender, args, _dpadRight1);
- _minus1.Clicked += (sender, args) => Button_Pressed(sender, args, _minus1);
- _l1.Clicked += (sender, args) => Button_Pressed(sender, args, _l1);
- _zL1.Clicked += (sender, args) => Button_Pressed(sender, args, _zL1);
- _rStickUp1.Clicked += (sender, args) => Button_Pressed(sender, args, _rStickUp1);
- _rStickDown1.Clicked += (sender, args) => Button_Pressed(sender, args, _rStickDown1);
- _rStickLeft1.Clicked += (sender, args) => Button_Pressed(sender, args, _rStickLeft1);
- _rStickRight1.Clicked += (sender, args) => Button_Pressed(sender, args, _rStickRight1);
- _rStickButton1.Clicked += (sender, args) => Button_Pressed(sender, args, _rStickButton1);
- _a1.Clicked += (sender, args) => Button_Pressed(sender, args, _a1);
- _b1.Clicked += (sender, args) => Button_Pressed(sender, args, _b1);
- _x1.Clicked += (sender, args) => Button_Pressed(sender, args, _x1);
- _y1.Clicked += (sender, args) => Button_Pressed(sender, args, _y1);
- _plus1.Clicked += (sender, args) => Button_Pressed(sender, args, _plus1);
- _r1.Clicked += (sender, args) => Button_Pressed(sender, args, _r1);
- _zR1.Clicked += (sender, args) => Button_Pressed(sender, args, _zR1);
- _controller1Type.Changed += (sender, args) => Controller_Changed(sender, args, _controller1Type.ActiveId, _controller1Image);
-
- //Setup Currents
- if (ConfigurationState.Instance.Logger.EnableFileLog)
- {
- _fileLogToggle.Click();
- }
-
- if (ConfigurationState.Instance.Logger.EnableError)
- {
- _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();
- }
-
- TimeZoneContentManager timeZoneContentManager = new TimeZoneContentManager();
-
- timeZoneContentManager.InitializeInstance(virtualFileSystem, contentManager, LibHac.FsSystem.IntegrityCheckLevel.None);
-
- List<string> locationNames = timeZoneContentManager.LocationNameCache.ToList();
-
- locationNames.Sort();
-
- foreach (string locationName in locationNames)
- {
- _systemTimeZoneSelect.Append(locationName, locationName);
- }
-
- _systemLanguageSelect.SetActiveId(ConfigurationState.Instance.System.Language.Value.ToString());
- _systemRegionSelect .SetActiveId(ConfigurationState.Instance.System.Region.Value.ToString());
- _systemTimeZoneSelect.SetActiveId(timeZoneContentManager.SanityCheckDeviceLocationName());
- _anisotropy .SetActiveId(ConfigurationState.Instance.Graphics.MaxAnisotropy.Value.ToString());
- _controller1Type .SetActiveId(ConfigurationState.Instance.Hid.ControllerType.Value.ToString());
- Controller_Changed(null, null, _controller1Type.ActiveId, _controller1Image);
-
- _lStickUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickUp.ToString();
- _lStickDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickDown.ToString();
- _lStickLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickLeft.ToString();
- _lStickRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickRight.ToString();
- _lStickButton1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.StickButton.ToString();
- _dpadUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadUp.ToString();
- _dpadDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadDown.ToString();
- _dpadLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadLeft.ToString();
- _dpadRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.DPadRight.ToString();
- _minus1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonMinus.ToString();
- _l1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonL.ToString();
- _zL1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon.ButtonZl.ToString();
- _rStickUp1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickUp.ToString();
- _rStickDown1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickDown.ToString();
- _rStickLeft1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickLeft.ToString();
- _rStickRight1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickRight.ToString();
- _rStickButton1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.StickButton.ToString();
- _a1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonA.ToString();
- _b1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonB.ToString();
- _x1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonX.ToString();
- _y1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonY.ToString();
- _plus1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonPlus.ToString();
- _r1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonR.ToString();
- _zR1.Label = ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon.ButtonZr.ToString();
-
- _custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath;
- _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath;
- _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
- _systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset;
-
- _gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0);
- _gameDirsBoxStore = new ListStore(typeof(string));
- _gameDirsBox.Model = _gameDirsBoxStore;
-
- foreach (string gameDir in ConfigurationState.Instance.Ui.GameDirs.Value)
- {
- _gameDirsBoxStore.AppendValues(gameDir);
- }
-
- if (_custThemeToggle.Active == false)
- {
- _custThemePath.Sensitive = false;
- _custThemePathLabel.Sensitive = false;
- _browseThemePath.Sensitive = false;
- }
-
- _listeningForKeypress = false;
-
- //Setup system time spinners
- UpdateSystemTimeSpinners();
- }
-
- private void UpdateSystemTimeSpinners()
- {
- //Unbind system time spin events
- _systemTimeYearSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
- _systemTimeMonthSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
- _systemTimeDaySpin.ValueChanged -= SystemTimeSpin_ValueChanged;
- _systemTimeHourSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
- _systemTimeMinuteSpin.ValueChanged -= SystemTimeSpin_ValueChanged;
-
- //Apply actual system time + SystemTimeOffset to system time spin buttons
- DateTime systemTime = DateTime.Now.AddSeconds(_systemTimeOffset);
-
- _systemTimeYearSpinAdjustment.Value = systemTime.Year;
- _systemTimeMonthSpinAdjustment.Value = systemTime.Month;
- _systemTimeDaySpinAdjustment.Value = systemTime.Day;
- _systemTimeHourSpinAdjustment.Value = systemTime.Hour;
- _systemTimeMinuteSpinAdjustment.Value = systemTime.Minute;
-
- //Format spin buttons text to include leading zeros
- _systemTimeYearSpin.Text = systemTime.Year.ToString("0000");
- _systemTimeMonthSpin.Text = systemTime.Month.ToString("00");
- _systemTimeDaySpin.Text = systemTime.Day.ToString("00");
- _systemTimeHourSpin.Text = systemTime.Hour.ToString("00");
- _systemTimeMinuteSpin.Text = systemTime.Minute.ToString("00");
-
- //Bind system time spin button events
- _systemTimeYearSpin.ValueChanged += SystemTimeSpin_ValueChanged;
- _systemTimeMonthSpin.ValueChanged += SystemTimeSpin_ValueChanged;
- _systemTimeDaySpin.ValueChanged += SystemTimeSpin_ValueChanged;
- _systemTimeHourSpin.ValueChanged += SystemTimeSpin_ValueChanged;
- _systemTimeMinuteSpin.ValueChanged += SystemTimeSpin_ValueChanged;
- }
-
- //Events
- private void SystemTimeSpin_ValueChanged(Object sender, EventArgs e)
- {
- int year = _systemTimeYearSpin.ValueAsInt;
- int month = _systemTimeMonthSpin.ValueAsInt;
- int day = _systemTimeDaySpin.ValueAsInt;
- int hour = _systemTimeHourSpin.ValueAsInt;
- int minute = _systemTimeMinuteSpin.ValueAsInt;
-
- if (!DateTime.TryParse(year + "-" + month + "-" + day + " " + hour + ":" + minute, out DateTime newTime))
- {
- UpdateSystemTimeSpinners();
-
- return;
- }
-
- newTime = newTime.AddSeconds(DateTime.Now.Second).AddMilliseconds(DateTime.Now.Millisecond);
-
- long systemTimeOffset = (long)Math.Ceiling((newTime - DateTime.Now).TotalMinutes) * 60L;
-
- if (_systemTimeOffset != systemTimeOffset)
- {
- _systemTimeOffset = systemTimeOffset;
- UpdateSystemTimeSpinners();
- }
- }
-
- private void Button_Pressed(object sender, EventArgs args, ToggleButton button)
- {
- if (_listeningForKeypress == false)
- {
- KeyPressEvent += On_KeyPress;
-
- _listeningForKeypress = true;
-
- void On_KeyPress(object o, KeyPressEventArgs keyPressed)
- {
- string key = keyPressed.Event.Key.ToString();
- string capKey = key.First().ToString().ToUpper() + key.Substring(1);
-
- if (Enum.IsDefined(typeof(Configuration.Hid.Key), capKey))
- {
- button.Label = capKey;
- }
- else if (GdkToOpenTkInput.ContainsKey(key))
- {
- button.Label = GdkToOpenTkInput[key];
- }
- else
- {
- button.Label = "Space";
- }
-
- button.SetStateFlags(0, true);
-
- KeyPressEvent -= On_KeyPress;
-
- _listeningForKeypress = false;
- }
- }
- else
- {
- button.SetStateFlags(0, true);
- }
- }
-
- private void Controller_Changed(object sender, EventArgs args, string controllerType, Image controllerImage)
- {
- switch (controllerType)
- {
- case "ProController":
- controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.ProCon.png", 500, 500);
- break;
- case "NpadLeft":
- controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.BlueCon.png", 500, 500);
- break;
- case "NpadRight":
- controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.RedCon.png", 500, 500);
- break;
- default:
- controllerImage.Pixbuf = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.assets.JoyCon.png", 500, 500);
- break;
- }
- }
-
- private void AddDir_Pressed(object sender, EventArgs args)
- {
- if (Directory.Exists(_addGameDirBox.Buffer.Text))
- {
- _gameDirsBoxStore.AppendValues(_addGameDirBox.Buffer.Text);
- }
-
- _addDir.SetStateFlags(0, true);
- }
-
- private void BrowseDir_Pressed(object sender, EventArgs args)
- {
- FileChooserDialog fileChooser = new FileChooserDialog("Choose the game directory to add to the list", this, FileChooserAction.SelectFolder, "Cancel", ResponseType.Cancel, "Add", ResponseType.Accept);
-
- if (fileChooser.Run() == (int)ResponseType.Accept)
- {
- _gameDirsBoxStore.AppendValues(fileChooser.Filename);
- }
-
- fileChooser.Dispose();
-
- _browseDir.SetStateFlags(0, true);
- }
-
- private void RemoveDir_Pressed(object sender, EventArgs args)
- {
- TreeSelection selection = _gameDirsBox.Selection;
-
- selection.GetSelected(out TreeIter treeIter);
- _gameDirsBoxStore.Remove(ref treeIter);
-
- _removeDir.SetStateFlags(0, true);
- }
-
- private void CustThemeToggle_Activated(object sender, EventArgs args)
- {
- _custThemePath.Sensitive = _custThemeToggle.Active;
- _custThemePathLabel.Sensitive = _custThemeToggle.Active;
- _browseThemePath.Sensitive = _custThemeToggle.Active;
- }
-
- private void BrowseThemeDir_Pressed(object sender, EventArgs args)
- {
- FileChooserDialog fileChooser = new FileChooserDialog("Choose the theme to load", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Select", ResponseType.Accept);
-
- fileChooser.Filter = new FileFilter();
- fileChooser.Filter.AddPattern("*.css");
-
- if (fileChooser.Run() == (int)ResponseType.Accept)
- {
- _custThemePath.Buffer.Text = fileChooser.Filename;
- }
-
- fileChooser.Dispose();
-
- _browseThemePath.SetStateFlags(0, true);
- }
-
- private void OpenLogsFolder_Pressed(object sender, EventArgs args)
- {
- string logPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
-
- DirectoryInfo directory = new DirectoryInfo(logPath);
- directory.Create();
-
- Process.Start(new ProcessStartInfo()
- {
- FileName = logPath,
- UseShellExecute = true,
- Verb = "open"
- });
- }
-
- private void SaveToggle_Activated(object sender, EventArgs args)
- {
- List<string> gameDirs = new List<string>();
-
- _gameDirsBoxStore.GetIterFirst(out TreeIter treeIter);
- for (int i = 0; i < _gameDirsBoxStore.IterNChildren(); i++)
- {
- _gameDirsBoxStore.GetValue(treeIter, i);
-
- gameDirs.Add((string)_gameDirsBoxStore.GetValue(treeIter, 0));
-
- _gameDirsBoxStore.IterNext(ref treeIter);
- }
-
- ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active;
- ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active;
- ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active;
- ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active;
- ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active;
- ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active;
- ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active;
- ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active;
- ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active;
- ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active;
- ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
- ConfigurationState.Instance.System.EnableMulticoreScheduling.Value = _multiSchedToggle.Active;
- ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active;
- ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active;
- ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active;
- ConfigurationState.Instance.Ui.EnableCustomTheme.Value = _custThemeToggle.Active;
-
- ConfigurationState.Instance.Hid.KeyboardControls.Value.LeftJoycon = new NpadKeyboardLeft()
- {
- StickUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickUp1.Label),
- StickDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickDown1.Label),
- StickLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickLeft1.Label),
- StickRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickRight1.Label),
- StickButton = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _lStickButton1.Label),
- DPadUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadUp1.Label),
- DPadDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadDown1.Label),
- DPadLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadLeft1.Label),
- DPadRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _dpadRight1.Label),
- ButtonMinus = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _minus1.Label),
- ButtonL = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _l1.Label),
- ButtonZl = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _zL1.Label),
- };
-
- ConfigurationState.Instance.Hid.KeyboardControls.Value.RightJoycon = new NpadKeyboardRight()
- {
- StickUp = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickUp1.Label),
- StickDown = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickDown1.Label),
- StickLeft = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickLeft1.Label),
- StickRight = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickRight1.Label),
- StickButton = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _rStickButton1.Label),
- ButtonA = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _a1.Label),
- ButtonB = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _b1.Label),
- ButtonX = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _x1.Label),
- ButtonY = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _y1.Label),
- ButtonPlus = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _plus1.Label),
- ButtonR = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _r1.Label),
- ButtonZr = (Configuration.Hid.Key)Enum.Parse(typeof(Configuration.Hid.Key), _zR1.Label),
- };
-
- ConfigurationState.Instance.System.Language.Value = (Language)Enum.Parse(typeof(Language), _systemLanguageSelect.ActiveId);
- ConfigurationState.Instance.System.Region.Value = (Configuration.System.Region)Enum.Parse(typeof(Configuration.System.Region), _systemRegionSelect.ActiveId);
- ConfigurationState.Instance.Graphics.MaxAnisotropy.Value = float.Parse(_anisotropy.ActiveId);
- ConfigurationState.Instance.Hid.ControllerType.Value = (ControllerType)Enum.Parse(typeof(ControllerType), _controller1Type.ActiveId);
- ConfigurationState.Instance.Ui.CustomThemePath.Value = _custThemePath.Buffer.Text;
- ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text;
- ConfigurationState.Instance.Ui.GameDirs.Value = gameDirs;
- ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value;
-
- ConfigurationState.Instance.System.TimeZone.Value = _systemTimeZoneSelect.ActiveId;
- ConfigurationState.Instance.System.SystemTimeOffset.Value = _systemTimeOffset;
-
- MainWindow.SaveConfig();
- MainWindow.ApplyTheme();
- MainWindow.UpdateGameTable();
- Dispose();
- }
-
- private void CloseToggle_Activated(object sender, EventArgs args)
- {
- Dispose();
- }
-
- public readonly Dictionary<string, string> GdkToOpenTkInput = new Dictionary<string, string>()
- {
- { "Key_0", "Number0" },
- { "Key_1", "Number1" },
- { "Key_2", "Number2" },
- { "Key_3", "Number3" },
- { "Key_4", "Number4" },
- { "Key_5", "Number5" },
- { "Key_6", "Number6" },
- { "Key_7", "Number7" },
- { "Key_8", "Number8" },
- { "Key_9", "Number9" },
- { "equal", "Plus" },
- { "uparrow", "Up" },
- { "downarrow", "Down" },
- { "leftarrow", "Left" },
- { "rightarrow", "Right" },
- { "Control_L", "ControlLeft" },
- { "Control_R", "ControlRight" },
- { "Shift_L", "ShiftLeft" },
- { "Shift_R", "ShiftRight" },
- { "Alt_L", "AltLeft" },
- { "Alt_R", "AltRight" },
- { "Page_Up", "PageUp" },
- { "Page_Down", "PageDown" },
- { "KP_Enter", "KeypadEnter" },
- { "KP_Up", "Up" },
- { "KP_Down", "Down" },
- { "KP_Left", "Left" },
- { "KP_Right", "Right" },
- { "KP_Divide", "KeypadDivide" },
- { "KP_Multiply", "KeypadMultiply" },
- { "KP_Subtract", "KeypadSubtract" },
- { "KP_Add", "KeypadAdd" },
- { "KP_Decimal", "KeypadDecimal" },
- { "KP_0", "Keypad0" },
- { "KP_1", "Keypad1" },
- { "KP_2", "Keypad2" },
- { "KP_3", "Keypad3" },
- { "KP_4", "Keypad4" },
- { "KP_5", "Keypad5" },
- { "KP_6", "Keypad6" },
- { "KP_7", "Keypad7" },
- { "KP_8", "Keypad8" },
- { "KP_9", "Keypad9" },
- };
- }
-}
diff --git a/Ryujinx/Ui/TitleUpdateWindow.cs b/Ryujinx/Ui/TitleUpdateWindow.cs
index 6808b4da..06d0dcdb 100644
--- a/Ryujinx/Ui/TitleUpdateWindow.cs
+++ b/Ryujinx/Ui/TitleUpdateWindow.cs
@@ -133,7 +133,7 @@ namespace Ryujinx.Ui
if (showErrorDialog)
{
- GtkDialog.CreateDialog("Ryujinx - Error", "Add Update Failed!", "The NCA header content type check has failed. This is usually because the header key is incorrect or missing.");
+ GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add Update Failed!", "The NCA header content type check has failed. This is usually because the header key is incorrect or missing.");
}
break;
@@ -144,7 +144,7 @@ namespace Ryujinx.Ui
if (showErrorDialog)
{
- GtkDialog.CreateDialog("Ryujinx - Error", "Add Update Failed!", $"Your key set is missing a key with the name: {exception.Name}");
+ GtkDialog.CreateInfoDialog("Ryujinx - Error", "Add Update Failed!", $"Your key set is missing a key with the name: {exception.Name}");
}
break;
diff --git a/Ryujinx/Ui/assets/BlueCon.png b/Ryujinx/Ui/assets/BlueCon.png
deleted file mode 100644
index 25691957..00000000
--- a/Ryujinx/Ui/assets/BlueCon.png
+++ /dev/null
Binary files differ
diff --git a/Ryujinx/Ui/assets/JoyCon.png b/Ryujinx/Ui/assets/JoyCon.png
deleted file mode 100644
index ec745863..00000000
--- a/Ryujinx/Ui/assets/JoyCon.png
+++ /dev/null
Binary files differ
diff --git a/Ryujinx/Ui/assets/JoyConLeft.svg b/Ryujinx/Ui/assets/JoyConLeft.svg
new file mode 100644
index 00000000..40d06136
--- /dev/null
+++ b/Ryujinx/Ui/assets/JoyConLeft.svg
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 1000.8 1000" style="enable-background:new 0 0 1000.8 1000;" xml:space="preserve">
+<style type="text/css">
+ .st0{opacity:0.1;}
+ .st1{fill:#02C5E5;}
+ .st2{fill:#FFFFFF;}
+</style>
+<g class="st0">
+ <path class="st1" d="M419.1,642.6v67.6c0,3.3-2.7,6-6,6h-4.2v-79.5h4.2C416.4,636.6,419.1,639.3,419.1,642.6z"/>
+ <path class="st1" d="M419.1,239.8v67.6c0,3.3-2.7,6-6,6h-4.2v-79.5h4.2C416.4,233.9,419.1,236.5,419.1,239.8z"/>
+ <path class="st1" d="M330.1,7v2.6h-54.2c-84.8,0-161.4,50.7-194.6,128.7l-3.4-3.4c-1.8-1.7-2.3-4.4-1.3-6.6
+ C111.2,50.8,188.1,1,272.9,1h51.2C327.4,1,330.1,3.7,330.1,7z"/>
+ <path class="st1" d="M359.6,115.1h-46.9c-1.6,0-3-1.3-3-3v-11.7c0-1.6,1.3-3,3-3h46.9c1.6,0,3,1.3,3,3v11.7
+ C362.6,113.8,361.3,115.1,359.6,115.1z"/>
+ <circle class="st1" cx="237.9" cy="464.4" r="37.5"/>
+ <circle class="st1" cx="237.9" cy="611.3" r="37.5"/>
+ <circle class="st1" cx="311.4" cy="537.9" r="37.5"/>
+ <ellipse class="st1" cx="164.5" cy="537.9" rx="37.5" ry="37.5"/>
+ <path class="st1" d="M269.1,689.9h45c4.9,0,8.9,4,8.9,8.9v45c0,4.9-4,8.9-8.9,8.9h-45c-4.9,0-8.9-4-8.9-8.9v-45
+ C260.2,693.9,264.2,689.9,269.1,689.9z"/>
+ <circle class="st1" cx="291.6" cy="721.3" r="19.4"/>
+ <path class="st1" d="M234.6,187.1v12.3c0,3-2.2,5.5-5.2,5.9c-25.2,3.7-45,23.5-48.7,48.7c-0.4,2.9-2.9,5.1-5.9,5.2h-12.3
+ C164.3,220.1,195.5,188.9,234.6,187.1z"/>
+ <path class="st1" d="M234.6,325.6v12.3c-39.1-1.7-70.3-33-72.1-72h12.3c3,0,5.5,2.2,5.9,5.2c3.7,25.2,23.5,45,48.7,48.7
+ C232.4,320.1,234.6,322.6,234.6,325.6z"/>
+ <path class="st1" d="M313.3,265.9c-1.7,39.1-33,70.3-72.1,72v-12.3c0-3,2.2-5.5,5.2-5.9c25.2-3.7,45-23.5,48.7-48.7
+ c0.4-2.9,2.9-5.1,5.9-5.2L313.3,265.9z"/>
+ <path class="st1" d="M313.3,259.2H301c-3,0-5.5-2.2-5.9-5.2c-3.7-25.2-23.5-45-48.7-48.7c-2.9-0.4-5.1-2.9-5.2-5.9v-12.3
+ C280.3,188.9,311.6,220.1,313.3,259.2z"/>
+ <path class="st1" d="M313.4,262.5c0,1.1,0,2.2-0.1,3.3H301c-3,0-5.5,2.2-5.9,5.2c-3.7,25.2-23.5,45-48.7,48.7
+ c-2.9,0.4-5.1,2.9-5.2,5.9v12.3c-1.1,0.1-2.2,0.1-3.3,0.1s-2.2,0-3.3-0.1v-12.3c0-3-2.2-5.5-5.2-5.9c-25.2-3.7-45-23.5-48.7-48.7
+ c-0.4-2.9-2.9-5.1-5.9-5.2h-12.3c-0.1-1.1-0.1-2.2-0.1-3.3s0-2.2,0.1-3.3h12.3c3,0,5.5-2.2,5.9-5.2c3.7-25.2,23.5-45,48.7-48.7
+ c2.9-0.4,5.1-2.9,5.2-5.9v-12.3c1.1-0.1,2.2-0.1,3.3-0.1s2.2,0,3.3,0.1v12.3c0,3,2.2,5.5,5.2,5.9c25.2,3.7,45,23.5,48.7,48.7
+ c0.4,2.9,2.9,5.1,5.9,5.2h12.3C313.4,260.3,313.4,261.4,313.4,262.5z"/>
+</g>
+<path class="st2" d="M413.1,906.6h-7.9c-3.6,0-6.4-2.9-6.5-6.5V71.2c0-3.6,2.9-6.4,6.5-6.5h7.9c3.6,0,6.4,2.9,6.5,6.5V207
+ c0,4.9-1.2,9.6-3.4,14l-6.7,13v79.2l6.7,13c2.2,4.3,3.4,9.1,3.4,13.9v269.7c0,4.9-1.2,9.6-3.4,14l-6.7,13V716l6.7,13
+ c2.2,4.3,3.4,9.1,3.4,13.9v157.2C419.6,903.7,416.7,906.6,413.1,906.6z M405.2,65.7c-3,0-5.5,2.4-5.5,5.5v828.9c0,3,2.4,5.5,5.5,5.5
+ h7.9c3,0,5.5-2.4,5.5-5.5V742.9c0-4.7-1.1-9.3-3.3-13.5l-6.8-13.1c0-0.1-0.1-0.2-0.1-0.2v-79.5c0-0.1,0-0.2,0.1-0.2l6.8-13.1
+ c2.2-4.2,3.3-8.8,3.3-13.5V340.1c0-4.7-1.1-9.3-3.3-13.5l-6.8-13.1c0-0.1-0.1-0.2-0.1-0.2v-79.5c0-0.1,0-0.2,0.1-0.2l6.8-13.1
+ c2.2-4.2,3.3-8.8,3.3-13.5V71.2c0-3-2.4-5.5-5.5-5.5H405.2z"/>
+<path class="st2" d="M399.3,858.9h-11.2c-0.3,0-0.5-0.2-0.5-0.5V72c0-0.3,0.2-0.5,0.5-0.5h11.2c0.3,0,0.5,0.2,0.5,0.5v786.4
+ C399.8,858.7,399.6,858.9,399.3,858.9z M388.6,857.9h10.2V72.5h-10.2V857.9z"/>
+<path class="st2" d="M382.1,1000H275.9C158.9,1000,64,905.2,64,788.1l0,0V220.9C64,104.1,159.1,9.1,275.9,9.1h106.2
+ c3.6,0,6.5,2.9,6.5,6.5v978C388.6,997.1,385.7,1000,382.1,1000z M275.9,10.1C159.6,10.1,65,104.7,65,220.9v567.2
+ C65,904.4,159.6,999,275.9,999h106.2c3,0,5.5-2.4,5.5-5.5v-978c0-3-2.4-5.5-5.5-5.5H275.9V10.1z"/>
+<polygon class="st1" points="237.9,448.9 225.8,469.9 250,469.9 "/>
+<polygon class="st1" points="237.9,626.9 225.8,605.9 250,605.9 "/>
+<polygon class="st1" points="148.9,537.9 169.9,550 169.9,525.8 "/>
+<polygon class="st1" points="326.9,537.9 305.9,550 305.9,525.8 "/>
+<path class="st1" d="M413.1,717.1h-4.2c-0.6,0-1-0.4-1-1l0,0v-79.5c0-0.6,0.4-1,1-1l0,0h4.2c3.8,0,6.9,3.1,7,7v67.6
+ C420.1,714,417,717.1,413.1,717.1z M409.9,715.1h3.2c2.7,0,5-2.2,5-5v-67.6c0-2.7-2.2-5-5-5h-3.2V715.1z"/>
+<path class="st1" d="M413.1,314.3h-4.2c-0.6,0-1-0.4-1-1v-79.5c0-0.6,0.4-1,1-1h4.2c3.8,0,6.9,3.1,7,7v67.6
+ C420.1,311.2,417,314.3,413.1,314.3z M409.9,312.3h3.2c2.7,0,5-2.2,5-5v-67.6c0-2.7-2.2-5-5-5h-3.2V312.3z"/>
+<path class="st1" d="M81.3,139.3c-0.3,0-0.5-0.1-0.7-0.3l-3.4-3.4c-2-2-2.6-5.1-1.5-7.8C110.5,50.1,187.7,0.1,272.9,0h51.2
+ c3.8,0,6.9,3.1,7,7v2.6c0,0.6-0.4,1-1,1h-54.2C191.4,10.5,115.1,61,82.2,138.7c-0.1,0.3-0.4,0.5-0.7,0.6
+ C81.4,139.3,81.3,139.3,81.3,139.3z M272.9,2C188.5,2.1,112,51.7,77.5,128.7c-0.8,1.9-0.4,4.1,1.1,5.5l2.4,2.4
+ C114.6,58.8,191.3,8.5,276,8.6h53.2V7c0-2.7-2.2-5-5-5H272.9z"/>
+<path class="st1" d="M359.6,116.1h-46.9c-2.2,0-4-1.8-4-4v-11.7c0-2.2,1.8-4,4-4h46.9c2.2,0,4,1.8,4,4v11.7
+ C363.6,114.3,361.8,116.1,359.6,116.1z M312.7,98.5c-1.1,0-2,0.9-2,2v11.7c0,1.1,0.9,2,2,2h46.9c1.1,0,2-0.9,2-2v-11.7
+ c0-1.1-0.9-2-2-2H312.7z"/>
+<path class="st1" d="M237.9,502.9c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C276.4,485.7,259.2,502.9,237.9,502.9z M237.9,428c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5S258.1,428,237.9,428z"/>
+<path class="st1" d="M237.9,649.8c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ S259.2,649.8,237.9,649.8z M237.9,574.9c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5l0,0
+ C274.4,591.2,258.1,574.9,237.9,574.9z"/>
+<path class="st1" d="M311.4,576.3c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5l0,0
+ C349.8,559.1,332.6,576.3,311.4,576.3z M311.4,501.4c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5c20.1,0,36.5-16.3,36.5-36.5
+ l0,0C347.8,517.7,331.5,501.4,311.4,501.4L311.4,501.4z"/>
+<path class="st1" d="M164.5,576.3c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5l0,0
+ C202.9,559.1,185.7,576.3,164.5,576.3z M164.5,501.4c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5l0,0C200.9,517.7,184.6,501.4,164.5,501.4L164.5,501.4z"/>
+<path class="st1" d="M314.1,753.7h-45c-5.5,0-9.9-4.4-9.9-9.9v-45c0-5.5,4.4-9.9,9.9-9.9h45c5.5,0,9.9,4.4,9.9,9.9v45
+ C324,749.3,319.5,753.7,314.1,753.7z M269.1,690.9c-4.4,0-7.9,3.6-7.9,7.9v45c0,4.4,3.6,7.9,7.9,7.9h45c4.4,0,7.9-3.6,7.9-7.9v-45
+ c0-4.4-3.6-7.9-7.9-7.9H269.1z"/>
+<path class="st1" d="M291.6,741.7c-11.3,0-20.4-9.2-20.4-20.4c0-11.3,9.2-20.4,20.4-20.4c11.3,0,20.4,9.2,20.4,20.4l0,0
+ C312,732.6,302.9,741.7,291.6,741.7z M291.6,702.8c-10.2,0-18.4,8.3-18.4,18.4s8.3,18.4,18.4,18.4c10.2,0,18.4-8.3,18.4-18.4
+ S301.8,702.9,291.6,702.8z"/>
+<path class="st1" d="M174.8,260.2h-12.3c-0.6,0-1-0.4-1-1l0,0c1.7-39.6,33.4-71.3,73-73c0.3,0,0.5,0.1,0.7,0.3s0.3,0.4,0.3,0.7v12.3
+ c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9C181.2,257.6,178.3,260.2,174.8,260.2z M163.6,258.2h11.2c2.5,0,4.6-1.8,4.9-4.3
+ c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-11.2C196.2,190.3,165.7,220.8,163.6,258.2L163.6,258.2z"/>
+<path class="st1" d="M234.6,338.9L234.6,338.9c-39.6-1.7-71.3-33.4-73-73c0-0.6,0.4-1,1-1l0,0h12.3c3.5,0,6.4,2.6,6.9,6
+ c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9V338C235.6,338.5,235.1,338.9,234.6,338.9L234.6,338.9z M163.6,266.9
+ c2.2,37.4,32.6,67.8,70,70v-11.2c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3H163.6z"/>
+<path class="st1" d="M241.3,338.9c-0.6,0-1-0.4-1-1v-12.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6
+ h12.3c0.6,0,1,0.4,1,1l0,0C312.6,305.5,280.9,337.2,241.3,338.9L241.3,338.9z M301,266.9c-2.5,0-4.6,1.8-4.9,4.3
+ c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v11.2c37.4-2.2,67.8-32.6,70-70H301z"/>
+<path class="st1" d="M313.3,260.2H301c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-12.3
+ c0-0.3,0.1-0.5,0.3-0.7s0.5-0.3,0.7-0.3c39.6,1.7,71.3,33.4,73,73C314.3,259.7,313.9,260.2,313.3,260.2L313.3,260.2L313.3,260.2z
+ M242.3,188.2v11.2c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h11.2
+ C310.1,220.8,279.6,190.3,242.3,188.2L242.3,188.2z"/>
+<path class="st1" d="M237.9,339c-1.2,0-2.3,0-3.4-0.1c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9
+ c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-12.3c-0.5,0-1-0.4-1-0.9c-0.1-1.1-0.1-2.2-0.1-3.4s0-2.3,0.1-3.4
+ c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1
+ c2.3-0.1,4.5-0.1,6.8,0c0.5,0,0.9,0.5,0.9,1v12.3c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h12.3
+ c0.5,0,1,0.4,1,0.9c0.1,1.1,0.1,2.2,0.1,3.4s0,2.3-0.1,3.4c0,0.5-0.5,0.9-1,0.9H301c-2.5,0-4.6,1.8-4.9,4.3
+ c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v12.3c0,0.5-0.4,1-0.9,1C240.2,339,239.1,339,237.9,339z M235.6,337
+ c1.5,0.1,3.1,0.1,4.7,0v-11.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6h11.3c0-0.8,0-1.5,0-2.3
+ s0-1.6,0-2.3H301c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3c-1.5-0.1-3.1-0.1-4.7,0v11.3
+ c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-11.3c0,0.8,0,1.5,0,2.3s0,1.6,0,2.3h11.3
+ c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9L235.6,337z"/>
+</svg>
diff --git a/Ryujinx/Ui/assets/JoyConPair.svg b/Ryujinx/Ui/assets/JoyConPair.svg
new file mode 100644
index 00000000..fca94d18
--- /dev/null
+++ b/Ryujinx/Ui/assets/JoyConPair.svg
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 1000.8 1000" style="enable-background:new 0 0 1000.8 1000;" xml:space="preserve">
+<style type="text/css">
+ .st0{opacity:0.1;}
+ .st1{fill:#02C5E5;}
+ .st2{fill:#FF5F55;}
+ .st3{fill:#FFFFFF;}
+</style>
+<g class="st0">
+ <path class="st1" d="M419.1,642.6v67.6c0,3.3-2.7,6-6,6h-4.2v-79.5h4.2C416.4,636.6,419.1,639.3,419.1,642.6z"/>
+ <path class="st1" d="M419.1,239.8v67.6c0,3.3-2.7,6-6,6h-4.2v-79.5h4.2C416.4,233.9,419.1,236.5,419.1,239.8z"/>
+ <path class="st1" d="M330.1,7v2.6h-54.2c-84.8,0-161.4,50.7-194.6,128.7l-3.4-3.4c-1.8-1.7-2.3-4.4-1.3-6.6
+ C111.2,50.8,188.1,1,272.9,1h51.2C327.4,1,330.1,3.7,330.1,7z"/>
+ <path class="st1" d="M359.6,115.1h-46.9c-1.6,0-3-1.3-3-3v-11.7c0-1.6,1.3-3,3-3h46.9c1.6,0,3,1.3,3,3v11.7
+ C362.6,113.8,361.3,115.1,359.6,115.1z"/>
+ <circle class="st1" cx="237.9" cy="464.4" r="37.5"/>
+ <circle class="st1" cx="237.9" cy="611.3" r="37.5"/>
+ <circle class="st1" cx="311.4" cy="537.9" r="37.5"/>
+
+ <ellipse transform="matrix(0.9951 -9.853756e-02 9.853756e-02 0.9951 -52.201 18.8252)" class="st1" cx="164.5" cy="537.9" rx="37.5" ry="37.5"/>
+ <path class="st1" d="M269.1,689.9h45c4.9,0,8.9,4,8.9,8.9v45c0,4.9-4,8.9-8.9,8.9h-45c-4.9,0-8.9-4-8.9-8.9v-45
+ C260.2,693.9,264.2,689.9,269.1,689.9z"/>
+ <circle class="st1" cx="291.6" cy="721.3" r="19.4"/>
+ <path class="st1" d="M234.6,187.1v12.3c0,3-2.2,5.5-5.2,5.9c-25.2,3.7-45,23.5-48.7,48.7c-0.4,2.9-2.9,5.1-5.9,5.2h-12.3
+ C164.3,220.1,195.5,188.9,234.6,187.1z"/>
+ <path class="st1" d="M234.6,325.6v12.3c-39.1-1.7-70.3-33-72.1-72h12.3c3,0,5.5,2.2,5.9,5.2c3.7,25.2,23.5,45,48.7,48.7
+ C232.4,320.1,234.6,322.6,234.6,325.6z"/>
+ <path class="st1" d="M313.3,265.9c-1.7,39.1-33,70.3-72.1,72v-12.3c0-3,2.2-5.5,5.2-5.9c25.2-3.7,45-23.5,48.7-48.7
+ c0.4-2.9,2.9-5.1,5.9-5.2L313.3,265.9z"/>
+ <path class="st1" d="M313.3,259.2H301c-3,0-5.5-2.2-5.9-5.2c-3.7-25.2-23.5-45-48.7-48.7c-2.9-0.4-5.1-2.9-5.2-5.9v-12.3
+ C280.3,188.9,311.6,220.1,313.3,259.2z"/>
+ <path class="st1" d="M313.4,262.5c0,1.1,0,2.2-0.1,3.3H301c-3,0-5.5,2.2-5.9,5.2c-3.7,25.2-23.5,45-48.7,48.7
+ c-2.9,0.4-5.1,2.9-5.2,5.9v12.3c-1.1,0.1-2.2,0.1-3.3,0.1s-2.2,0-3.3-0.1v-12.3c0-3-2.2-5.5-5.2-5.9c-25.2-3.7-45-23.5-48.7-48.7
+ c-0.4-2.9-2.9-5.1-5.9-5.2h-12.3c-0.1-1.1-0.1-2.2-0.1-3.3s0-2.2,0.1-3.3h12.3c3,0,5.5-2.2,5.9-5.2c3.7-25.2,23.5-45,48.7-48.7
+ c2.9-0.4,5.1-2.9,5.2-5.9v-12.3c1.1-0.1,2.2-0.1,3.3-0.1s2.2,0,3.3,0.1v12.3c0,3,2.2,5.5,5.2,5.9c25.2,3.7,45,23.5,48.7,48.7
+ c0.4,2.9,2.9,5.1,5.9,5.2h12.3C313.4,260.3,313.4,261.4,313.4,262.5z"/>
+</g>
+<g class="st0">
+ <path class="st2" d="M597.9,233.9v79.5h-4.2c-3.3,0-6-2.7-6-6v-67.6c0-3.3,2.7-6,6-6H597.9z"/>
+ <path class="st2" d="M597.9,636.6v79.5h-4.2c-3.3,0-6-2.7-6-6v-67.6c0-3.3,2.7-6,6-6H597.9z"/>
+ <path class="st2" d="M929,134.9l-3.4,3.4C892.4,60.3,815.8,9.6,730.9,9.6h-54.2V7c0-3.3,2.7-6,6-6c0,0,0,0,0,0h51.2
+ c84.8,0,161.7,49.8,196.4,127.2C931.3,130.5,930.8,133.1,929,134.9z"/>
+ <path class="st2" d="M679.5,94.5V82.8c0-1.6-1.3-3-3-3l0,0h-11.7c-1.6,0-3,1.3-3,3c0,0,0,0,0,0v11.7c0,1.6-1.3,3-3,3l0,0h-11.7
+ c-1.6,0-3,1.3-3,3c0,0,0,0,0,0v11.7c0,1.6,1.3,3,3,3l0,0h11.7c1.6,0,3,1.3,3,3c0,0,0,0,0,0v11.7c0,1.6,1.3,3,3,3l0,0h11.7
+ c1.6,0,3-1.3,3-3v0v-11.7c0-1.6,1.3-3,3-3l0,0h11.7c1.6,0,3-1.3,3-3c0,0,0,0,0,0v-11.7c0-1.6-1.3-3-3-3l0,0h-11.7
+ C680.8,97.5,679.5,96.1,679.5,94.5C679.5,94.5,679.5,94.5,679.5,94.5z"/>
+ <circle class="st2" cx="768.9" cy="333.9" r="37.5"/>
+ <circle class="st2" cx="768.9" cy="187.1" r="37.5"/>
+ <circle class="st2" cx="842.3" cy="260.5" r="37.5"/>
+ <circle class="st2" cx="695.5" cy="260.5" r="37.5"/>
+ <circle class="st2" cx="715" cy="721.3" r="27.9"/>
+ <path class="st2" d="M765.6,460.3v12.3c0,3-2.2,5.5-5.2,5.9c-25.2,3.7-45,23.5-48.7,48.7c-0.4,2.9-2.9,5.1-5.9,5.2h-12.3
+ C695.2,493.3,726.5,462,765.6,460.3z"/>
+ <path class="st2" d="M765.6,598.8v12.3c-39.1-1.7-70.3-33-72.1-72h12.3c3,0,5.5,2.2,5.9,5.2c3.7,25.2,23.5,45,48.7,48.7
+ C763.4,593.3,765.6,595.8,765.6,598.8z"/>
+ <path class="st2" d="M844.3,539c-1.7,39.1-33,70.3-72.1,72v-12.3c0-3,2.2-5.5,5.2-5.9c25.2-3.7,45-23.5,48.7-48.7
+ c0.4-2.9,2.9-5.1,5.9-5.2L844.3,539z"/>
+ <path class="st2" d="M844.3,532.4H832c-3,0-5.5-2.2-5.9-5.2c-3.7-25.2-23.5-45-48.7-48.7c-2.9-0.4-5.1-2.9-5.2-5.9v-12.3
+ C811.3,462,842.6,493.3,844.3,532.4z"/>
+ <path class="st2" d="M844.4,535.7c0,1.1,0,2.2-0.1,3.3H832c-3,0-5.5,2.2-5.9,5.2c-3.7,25.2-23.5,45-48.7,48.7
+ c-2.9,0.4-5.1,2.9-5.2,5.9v12.3c-1.1,0.1-2.2,0.1-3.3,0.1s-2.2,0-3.3-0.1v-12.3c0-3-2.2-5.5-5.2-5.9c-25.2-3.7-45-23.5-48.7-48.7
+ c-0.4-2.9-2.9-5.1-5.9-5.2h-12.3c-0.1-1.1-0.1-2.2-0.1-3.3s0-2.2,0.1-3.3h12.3c3,0,5.5-2.2,5.9-5.2c3.7-25.2,23.5-45,48.7-48.7
+ c2.9-0.4,5.1-2.9,5.2-5.9v-12.3c1.1-0.1,2.2-0.1,3.3-0.1s2.2,0,3.3,0.1v12.3c0,3,2.2,5.5,5.2,5.9c25.2,3.7,45,23.5,48.7,48.7
+ c0.4,2.9,2.9,5.1,5.9,5.2h12.3C844.3,533.5,844.4,534.6,844.4,535.7z"/>
+</g>
+<path class="st3" d="M413.1,906.6h-7.9c-3.6,0-6.4-2.9-6.5-6.5V71.2c0-3.6,2.9-6.4,6.5-6.5h7.9c3.6,0,6.4,2.9,6.5,6.5V207
+ c0,4.9-1.2,9.6-3.4,14l-6.7,13v79.2l6.7,13c2.2,4.3,3.4,9.1,3.4,13.9v269.7c0,4.9-1.2,9.6-3.4,14l-6.7,13V716l6.7,13
+ c2.2,4.3,3.4,9.1,3.4,13.9v157.2C419.6,903.7,416.7,906.6,413.1,906.6z M405.2,65.7c-3,0-5.5,2.4-5.5,5.5v828.9c0,3,2.4,5.5,5.5,5.5
+ h7.9c3,0,5.5-2.4,5.5-5.5V742.9c0-4.7-1.1-9.3-3.3-13.5l-6.8-13.1c0-0.1-0.1-0.2-0.1-0.2v-79.5c0-0.1,0-0.2,0.1-0.2l6.8-13.1
+ c2.2-4.2,3.3-8.8,3.3-13.5V340.1c0-4.7-1.1-9.3-3.3-13.5l-6.8-13.1c0-0.1-0.1-0.2-0.1-0.2v-79.5c0-0.1,0-0.2,0.1-0.2l6.8-13.1
+ c2.2-4.2,3.3-8.8,3.3-13.5V71.2c0-3-2.4-5.5-5.5-5.5L405.2,65.7z"/>
+<path class="st3" d="M399.3,858.9h-11.2c-0.3,0-0.5-0.2-0.5-0.5V72c0-0.3,0.2-0.5,0.5-0.5h11.2c0.3,0,0.5,0.2,0.5,0.5v786.4
+ C399.8,858.7,399.6,858.9,399.3,858.9z M388.6,857.9h10.2V72.5h-10.2V857.9z"/>
+<path class="st3" d="M382.1,1000H275.9C158.9,1000,64,905.2,64,788.1c0,0,0,0,0,0V220.9C64,104.1,159.1,9.1,275.9,9.1h106.2
+ c3.6,0,6.5,2.9,6.5,6.5v978C388.6,997.1,385.7,1000,382.1,1000z M275.9,10.1C159.6,10.1,65,104.7,65,220.9v567.2
+ C65,904.4,159.6,999,275.9,999h106.2c3,0,5.5-2.4,5.5-5.5v-978c0-3-2.4-5.5-5.5-5.5H275.9z"/>
+<path class="st3" d="M601.6,906.6h-7.9c-3.6,0-6.4-2.9-6.5-6.5V742.9c0-4.9,1.2-9.6,3.4-13.9l6.7-13v-79.2l-6.7-13
+ c-2.2-4.3-3.4-9.1-3.4-14V340.1c0-4.9,1.2-9.6,3.4-13.9l6.7-13V234l-6.7-13c-2.2-4.3-3.4-9.1-3.4-14V71.2c0-3.6,2.9-6.4,6.5-6.5h7.9
+ c3.6,0,6.4,2.9,6.5,6.5v828.9C608,903.7,605.1,906.6,601.6,906.6z M593.7,65.7c-3,0-5.5,2.4-5.5,5.5V207c0,4.7,1.1,9.3,3.3,13.5
+ l6.8,13.1c0,0.1,0.1,0.1,0.1,0.2v79.5c0,0.1,0,0.2-0.1,0.2l-6.8,13.1c-2.2,4.2-3.3,8.8-3.3,13.5v269.7c0,4.7,1.1,9.3,3.3,13.5
+ l6.8,13.1c0,0.1,0.1,0.1,0.1,0.2v79.5c0,0.1,0,0.2-0.1,0.2l-6.8,13.1c-2.2,4.2-3.3,8.8-3.3,13.5v157.2c0,3,2.4,5.5,5.5,5.5h7.9
+ c3,0,5.5-2.4,5.5-5.5V71.2c0-3-2.4-5.5-5.5-5.5L593.7,65.7z"/>
+<path class="st3" d="M618.8,858.9h-11.3c-0.3,0-0.5-0.2-0.5-0.5c0,0,0,0,0,0V72c0-0.3,0.2-0.5,0.5-0.5h11.3c0.3,0,0.5,0.2,0.5,0.5
+ v786.4C619.3,858.7,619.1,858.9,618.8,858.9C618.8,858.9,618.8,858.9,618.8,858.9z M608,857.9h10.3V72.5H608V857.9z"/>
+<path class="st3" d="M730.9,1000H624.7c-3.6,0-6.5-2.9-6.5-6.5v-978c0-3.6,2.9-6.5,6.5-6.5h106.2c116.8,0,211.9,95.1,211.9,211.9
+ v567.2C942.8,905.1,848,1000,730.9,1000C730.9,1000,730.9,1000,730.9,1000z M624.7,10.1c-3,0-5.5,2.4-5.5,5.5v978
+ c0,3,2.4,5.5,5.5,5.5h106.2c116.3,0,210.9-94.6,210.9-210.9V220.9c0-116.3-94.6-210.9-210.9-210.9L624.7,10.1z"/>
+<path class="st3" d="M715,763.2c-23.1,0-41.9-18.7-41.9-41.9s18.7-41.9,41.9-41.9s41.9,18.7,41.9,41.9l0,0
+ C756.8,744.4,738.1,763.1,715,763.2z M715,680.4c-22.6,0-40.9,18.3-40.9,40.9c0,22.6,18.3,40.9,40.9,40.9
+ c22.6,0,40.9-18.3,40.9-40.9v0C755.8,698.7,737.6,680.4,715,680.4z"/>
+<polygon class="st1" points="237.9,448.9 225.8,469.9 250,469.9 "/>
+<polygon class="st1" points="237.9,626.9 225.8,605.9 250,605.9 "/>
+<polygon class="st1" points="148.9,537.9 169.9,550 169.9,525.8 "/>
+<polygon class="st1" points="326.9,537.9 305.9,550 305.9,525.8 "/>
+<path class="st2" d="M782.2,203.2h-5.5l-7.8-12.9l-7.8,12.9h-5.4l10.6-16.3l-9.8-15.6h5.2l7.3,12l7.4-12h5l-9.8,15.4L782.2,203.2z"
+ />
+<path class="st2" d="M709.2,244.5l-11.6,20.6v11.4h-4.4V265l-11.6-20.5h5.3l6.4,11.7l2.3,4.7l2.2-4.3l6.4-12.1L709.2,244.5z"/>
+<path class="st2" d="M855.9,276.5h-4.7l-2.2-7h-13.3l-2.3,7h-4.5l10.6-32h6L855.9,276.5z M847.7,265.6l-5.4-17.1l-5.4,17.1
+ L847.7,265.6z"/>
+<path class="st2" d="M779.4,340.4c0,1.4-0.3,2.8-0.9,4.1c-0.6,1.2-1.5,2.2-2.5,3c-1.2,0.9-2.6,1.5-4,1.9c-1.7,0.4-3.4,0.7-5.2,0.6
+ h-8.4v-32h9.2c7.1,0,10.7,2.6,10.7,7.8c0,1.6-0.4,3.1-1.2,4.5c-1,1.4-2.4,2.3-4,2.8c0.9,0.2,1.7,0.4,2.5,0.8c0.8,0.4,1.5,0.9,2,1.5
+ c0.6,0.6,1.1,1.4,1.4,2.2C779.2,338.4,779.4,339.4,779.4,340.4z M773.7,326.3c0-0.6-0.1-1.3-0.3-1.8c-0.2-0.6-0.6-1.1-1-1.5
+ c-0.6-0.5-1.3-0.8-2-1c-1-0.3-2.1-0.4-3.2-0.4h-4.5v10h4.4c0.9,0,1.8-0.1,2.7-0.3c0.8-0.2,1.5-0.5,2.1-1c0.6-0.4,1-1,1.3-1.7
+ C773.6,327.9,773.7,327.1,773.7,326.3L773.7,326.3z M774.8,340.5c0-0.8-0.2-1.5-0.5-2.2c-0.4-0.7-0.9-1.2-1.5-1.7
+ c-0.7-0.5-1.5-0.8-2.4-1c-1-0.3-2.1-0.4-3.2-0.4h-4.5v11h4.6c2.5,0,4.4-0.5,5.6-1.4C774.2,343.8,774.9,342.2,774.8,340.5
+ L774.8,340.5z"/>
+<path class="st2" d="M715,701.3L695.4,721h5.6v16.8h28.2V721h5.3L715,701.3z M720.7,731.8h-11.1V721h11.1V731.8z"/>
+<path class="st1" d="M413.1,717.1h-4.2c-0.6,0-1-0.4-1-1c0,0,0,0,0,0v-79.5c0-0.6,0.4-1,1-1c0,0,0,0,0,0h4.2c3.8,0,6.9,3.1,7,7v67.6
+ C420.1,714,417,717.1,413.1,717.1z M409.9,715.1h3.2c2.7,0,5-2.2,5-5v-67.6c0-2.7-2.2-5-5-5h-3.2V715.1z"/>
+<path class="st1" d="M413.1,314.3h-4.2c-0.6,0-1-0.4-1-1v-79.5c0-0.6,0.4-1,1-1h4.2c3.8,0,6.9,3.1,7,7v67.6
+ C420.1,311.2,417,314.3,413.1,314.3z M409.9,312.3h3.2c2.7,0,5-2.2,5-5v-67.6c0-2.7-2.2-5-5-5h-3.2V312.3z"/>
+<path class="st1" d="M81.3,139.3c-0.3,0-0.5-0.1-0.7-0.3l-3.4-3.4c-2-2-2.6-5.1-1.5-7.8C110.5,50.1,187.7,0.1,272.9,0h51.2
+ c3.8,0,6.9,3.1,7,7v2.6c0,0.6-0.4,1-1,1h-54.2C191.4,10.5,115.1,61,82.2,138.7c-0.1,0.3-0.4,0.5-0.7,0.6
+ C81.4,139.3,81.3,139.3,81.3,139.3z M272.9,2C188.5,2.1,112,51.7,77.5,128.7c-0.8,1.9-0.4,4.1,1.1,5.5l2.4,2.4
+ c33.6-77.8,110.3-128.1,195-128h53.2V7c0-2.7-2.2-5-5-5L272.9,2z"/>
+<path class="st1" d="M359.6,116.1h-46.9c-2.2,0-4-1.8-4-4v-11.7c0-2.2,1.8-4,4-4h46.9c2.2,0,4,1.8,4,4v11.7
+ C363.6,114.3,361.8,116.1,359.6,116.1z M312.7,98.5c-1.1,0-2,0.9-2,2v11.7c0,1.1,0.9,2,2,2h46.9c1.1,0,2-0.9,2-2v-11.7
+ c0-1.1-0.9-2-2-2H312.7z"/>
+<path class="st1" d="M237.9,502.9c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C276.4,485.7,259.2,502.9,237.9,502.9z M237.9,428c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5C274.4,444.3,258.1,428,237.9,428z"/>
+<path class="st1" d="M237.9,649.8c-21.2,0-38.5-17.2-38.5-38.5s17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C276.4,632.6,259.2,649.8,237.9,649.8z M237.9,574.9c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5s36.5-16.3,36.5-36.5
+ c0,0,0,0,0,0C274.4,591.2,258.1,574.9,237.9,574.9z"/>
+<path class="st1" d="M311.4,576.3c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5l0,0
+ C349.8,559.1,332.6,576.3,311.4,576.3z M311.4,501.4c-20.1,0-36.5,16.3-36.5,36.5s16.3,36.5,36.5,36.5c20.1,0,36.5-16.3,36.5-36.5
+ c0,0,0,0,0,0C347.8,517.7,331.5,501.4,311.4,501.4L311.4,501.4z"/>
+<path class="st1" d="M164.5,576.3c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5s38.5,17.2,38.5,38.5l0,0
+ C202.9,559.1,185.7,576.3,164.5,576.3z M164.5,501.4c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5c0,0,0,0,0,0C200.9,517.7,184.6,501.4,164.5,501.4L164.5,501.4z"/>
+<path class="st1" d="M314.1,753.7h-45c-5.5,0-9.9-4.4-9.9-9.9v-45c0-5.5,4.4-9.9,9.9-9.9h45c5.5,0,9.9,4.4,9.9,9.9v45
+ C324,749.3,319.5,753.7,314.1,753.7z M269.1,690.9c-4.4,0-7.9,3.6-7.9,7.9v45c0,4.4,3.6,7.9,7.9,7.9h45c4.4,0,7.9-3.6,7.9-7.9v-45
+ c0-4.4-3.6-7.9-7.9-7.9H269.1z"/>
+<path class="st1" d="M291.6,741.7c-11.3,0-20.4-9.2-20.4-20.4c0-11.3,9.2-20.4,20.4-20.4c11.3,0,20.4,9.2,20.4,20.4c0,0,0,0,0,0
+ C312,732.6,302.9,741.7,291.6,741.7z M291.6,702.8c-10.2,0-18.4,8.3-18.4,18.4s8.3,18.4,18.4,18.4c10.2,0,18.4-8.3,18.4-18.4
+ C310,711.1,301.8,702.9,291.6,702.8z"/>
+<path class="st1" d="M174.8,260.2h-12.3c-0.6,0-1-0.4-1-1c0,0,0,0,0,0c1.7-39.6,33.4-71.3,73-73c0.3,0,0.5,0.1,0.7,0.3
+ c0.2,0.2,0.3,0.4,0.3,0.7v12.3c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9C181.2,257.6,178.3,260.2,174.8,260.2z
+ M163.6,258.2h11.2c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-11.2
+ C196.2,190.3,165.7,220.8,163.6,258.2L163.6,258.2z"/>
+<path class="st1" d="M234.6,338.9L234.6,338.9c-39.6-1.7-71.3-33.4-73-73c0-0.6,0.4-1,1-1c0,0,0,0,0,0h12.3c3.5,0,6.4,2.6,6.9,6
+ c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9v12.3C235.6,338.5,235.1,338.9,234.6,338.9L234.6,338.9z M163.6,266.9
+ c2.2,37.4,32.6,67.8,70,70v-11.2c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3L163.6,266.9z"/>
+<path class="st1" d="M241.3,338.9c-0.6,0-1-0.4-1-1v-12.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6
+ h12.3c0.6,0,1,0.4,1,1c0,0,0,0,0,0C312.6,305.5,280.9,337.2,241.3,338.9L241.3,338.9z M301,266.9c-2.5,0-4.6,1.8-4.9,4.3
+ c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v11.2c37.4-2.2,67.8-32.6,70-70L301,266.9z"/>
+<path class="st1" d="M313.3,260.2H301c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-12.3
+ c0-0.3,0.1-0.5,0.3-0.7c0.2-0.2,0.5-0.3,0.7-0.3c39.6,1.7,71.3,33.4,73,73C314.3,259.7,313.9,260.2,313.3,260.2
+ C313.3,260.2,313.3,260.2,313.3,260.2L313.3,260.2z M242.3,188.2v11.2c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6
+ c0.3,2.5,2.4,4.3,4.9,4.3h11.2C310.1,220.8,279.6,190.3,242.3,188.2L242.3,188.2z"/>
+<path class="st1" d="M237.9,339c-1.2,0-2.3,0-3.4-0.1c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9
+ c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-12.3c-0.5,0-1-0.4-1-0.9c-0.1-1.1-0.1-2.2-0.1-3.4s0-2.3,0.1-3.4
+ c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1
+ c2.3-0.1,4.5-0.1,6.8,0c0.5,0,0.9,0.5,0.9,1v12.3c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h12.3
+ c0.5,0,1,0.4,1,0.9c0.1,1.1,0.1,2.2,0.1,3.4s0,2.3-0.1,3.4c0,0.5-0.5,0.9-1,0.9H301c-2.5,0-4.6,1.8-4.9,4.3
+ c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v12.3c0,0.5-0.4,1-0.9,1C240.2,339,239.1,339,237.9,339z M235.6,337
+ c1.5,0.1,3.1,0.1,4.7,0v-11.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6h11.3c0-0.8,0-1.5,0-2.3
+ s0-1.6,0-2.3H301c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3c-1.5-0.1-3.1-0.1-4.7,0v11.3
+ c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-11.3c0,0.8,0,1.5,0,2.3s0,1.6,0,2.3h11.3
+ c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9L235.6,337z"/>
+<path class="st2" d="M597.9,314.3h-4.2c-3.8,0-6.9-3.1-7-7v-67.6c0-3.8,3.1-6.9,7-7h4.2c0.6,0,1,0.4,1,1c0,0,0,0,0,0v79.5
+ C598.9,313.9,598.4,314.3,597.9,314.3C597.9,314.3,597.9,314.3,597.9,314.3z M593.7,234.8c-2.7,0-5,2.2-5,5v67.6c0,2.7,2.2,5,5,5
+ h3.2v-77.5H593.7z"/>
+<path class="st2" d="M597.9,717.1h-4.2c-3.8,0-6.9-3.1-7-7v-67.6c0-3.8,3.1-6.9,7-7h4.2c0.6,0,1,0.4,1,1c0,0,0,0,0,0v79.5
+ C598.9,716.6,598.4,717.1,597.9,717.1C597.9,717.1,597.9,717.1,597.9,717.1z M593.7,637.6c-2.7,0-5,2.2-5,5v67.6c0,2.7,2.2,5,5,5
+ h3.2v-77.5H593.7z"/>
+<path class="st2" d="M925.6,139.3c-0.1,0-0.1,0-0.2,0c-0.3-0.1-0.6-0.3-0.7-0.6C891.7,61,815.4,10.5,730.9,10.6h-54.2
+ c-0.6,0-1-0.4-1-1c0,0,0,0,0,0V7c0-3.8,3.1-6.9,7-7h51.2c85.2,0.1,162.4,50.1,197.3,127.8c1.2,2.6,0.6,5.7-1.5,7.8l-3.4,3.4
+ C926.1,139.2,925.8,139.3,925.6,139.3z M677.7,8.6h53.2c84.7-0.1,161.3,50.2,195,128l2.4-2.4l0,0c1.5-1.4,1.9-3.6,1.1-5.5
+ C894.8,51.7,818.3,2.1,733.9,2h-51.2c-2.7,0-5,2.2-5,5V8.6z"/>
+<path class="st2" d="M676.5,133.7h-11.7c-2.2,0-4-1.8-4-4v-11.7c0-1.1-0.9-2-2-2h-11.7c-2.2,0-4-1.8-4-4v-11.7c0-2.2,1.8-4,4-4h11.7
+ c1.1,0,2-0.9,2-2V82.8c0-2.2,1.8-4,4-4h11.7c2.2,0,4,1.8,4,4v11.7c0,1.1,0.9,2,2,2h11.7c2.2,0,4,1.8,4,4v11.7c0,2.2-1.8,4-4,4h-11.7
+ c-1.1,0-2,0.9-2,2v11.7C680.5,131.9,678.7,133.7,676.5,133.7z M647.2,98.5c-1.1,0-2,0.9-2,2v11.7c0,1.1,0.9,2,2,2h11.7
+ c2.2,0,4,1.8,4,4v11.7c0,1.1,0.9,2,2,2h11.7c1.1,0,2-0.9,2-2v-11.7c0-2.2,1.8-4,4-4h11.7c1.1,0,2-0.9,2-2v-11.7c0-1.1-0.9-2-2-2
+ h-11.7c-2.2,0-4-1.8-4-4V82.8c0-1.1-0.9-2-2-2h-11.7c-1.1,0-2,0.9-2,2v11.7c0,2.2-1.8,4-4,4L647.2,98.5z"/>
+<path class="st2" d="M768.9,372.4c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C807.3,355.2,790.1,372.4,768.9,372.4z M768.9,297.5c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5C805.3,313.8,789,297.5,768.9,297.5L768.9,297.5z"/>
+<path class="st2" d="M768.9,225.5c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C807.3,208.3,790.1,225.5,768.9,225.5z M768.9,150.6c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5C805.3,166.9,789,150.6,768.9,150.6L768.9,150.6z"/>
+<path class="st2" d="M842.3,299c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C880.8,281.7,863.6,298.9,842.3,299z M842.3,224c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5C878.8,240.4,862.5,224,842.3,224L842.3,224z"/>
+<path class="st2" d="M695.5,299c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C733.9,281.7,716.7,298.9,695.5,299z M695.5,224c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5C731.9,240.4,715.6,224,695.5,224L695.5,224z"/>
+<path class="st2" d="M715,750.2c-16,0-28.9-13-28.9-28.9s13-28.9,28.9-28.9c16,0,28.9,13,28.9,28.9c0,0,0,0,0,0
+ C743.9,737.3,731,750.2,715,750.2z M715,694.3c-14.9,0-26.9,12.1-26.9,26.9s12.1,26.9,26.9,26.9c14.9,0,26.9-12.1,26.9-26.9
+ C741.9,706.4,729.9,694.4,715,694.3z"/>
+<path class="st2" d="M705.8,533.4h-12.3c-0.6,0-1-0.4-1-1c0,0,0,0,0,0c1.7-39.6,33.4-71.3,73-73c0.3,0,0.5,0.1,0.7,0.3
+ c0.2,0.2,0.3,0.4,0.3,0.7v12.3c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9C712.2,530.8,709.3,533.4,705.8,533.4z
+ M694.6,531.4h11.2c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-11.2
+ C727.2,463.5,696.7,494,694.6,531.4z"/>
+<path class="st2" d="M765.6,612.1C765.6,612.1,765.5,612.1,765.6,612.1c-39.6-1.7-71.3-33.4-73-73c0-0.6,0.4-1,1-1c0,0,0,0,0,0h12.3
+ c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9v12.3C766.6,611.6,766.1,612.1,765.6,612.1
+ C765.6,612.1,765.6,612.1,765.6,612.1L765.6,612.1z M694.6,540c2.2,37.4,32.6,67.8,70,70v-11.2c0-2.5-1.8-4.6-4.3-4.9
+ c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3H694.6z"/>
+<path class="st2" d="M772.2,612.1c-0.6,0-1-0.4-1-1c0,0,0,0,0,0v-12.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9
+ c0.5-3.4,3.4-6,6.9-6h12.3c0.6,0,1,0.4,1,1c0,0,0,0,0,0C843.5,578.7,811.9,610.3,772.2,612.1C772.3,612.1,772.3,612.1,772.2,612.1z
+ M832,540c-2.5,0-4.6,1.8-4.9,4.3c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9V610c37.4-2.2,67.8-32.6,70-70H832z"/>
+<path class="st2" d="M844.3,533.4H832c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-12.3
+ c0-0.3,0.1-0.5,0.3-0.7c0.2-0.2,0.5-0.3,0.7-0.3c39.6,1.7,71.3,33.4,73,73C845.3,532.9,844.9,533.3,844.3,533.4
+ C844.3,533.4,844.3,533.4,844.3,533.4L844.3,533.4z M773.2,461.4v11.2c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6
+ c0.3,2.5,2.4,4.3,4.9,4.3h11.2C841.1,494,810.6,463.5,773.2,461.4z"/>
+<path class="st2" d="M768.9,612.2c-1.2,0-2.3,0-3.4-0.1c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9
+ c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-12.3c-0.5,0-1-0.4-1-0.9c-0.1-1.1-0.1-2.2-0.1-3.4s0-2.3,0.1-3.4
+ c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1
+ c2.3-0.1,4.5-0.1,6.8,0c0.5,0,0.9,0.5,0.9,1v12.3c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h12.3
+ c0.5,0,1,0.4,1,0.9c0.1,1.1,0.1,2.2,0.1,3.4s0,2.3-0.1,3.4c0,0.5-0.5,0.9-1,0.9H832c-2.5,0-4.6,1.8-4.9,4.3
+ c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v12.3c0,0.5-0.4,1-0.9,1C771.2,612.1,770.1,612.2,768.9,612.2z M766.6,610.1
+ c1.5,0.1,3.1,0.1,4.7,0v-11.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6h11.3c0-0.8,0-1.5,0-2.3
+ s0-1.6,0-2.3H832c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3c-1.5-0.1-3.1-0.1-4.7,0v11.3
+ c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-11.3c0,0.8,0,1.5,0,2.3s0,1.6,0,2.3h11.3
+ c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9V610.1z"/>
+</svg>
diff --git a/Ryujinx/Ui/assets/JoyConRight.svg b/Ryujinx/Ui/assets/JoyConRight.svg
new file mode 100644
index 00000000..014c0ae3
--- /dev/null
+++ b/Ryujinx/Ui/assets/JoyConRight.svg
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 1000.8 1000" style="enable-background:new 0 0 1000.8 1000;" xml:space="preserve">
+<style type="text/css">
+ .st0{opacity:0.1;}
+ .st1{fill:#FF5F55;}
+ .st2{fill:#FFFFFF;}
+</style>
+<g class="st0">
+ <path class="st1" d="M597.9,233.9v79.5h-4.2c-3.3,0-6-2.7-6-6v-67.6c0-3.3,2.7-6,6-6h4.2V233.9z"/>
+ <path class="st1" d="M597.9,636.6v79.5h-4.2c-3.3,0-6-2.7-6-6v-67.6c0-3.3,2.7-6,6-6L597.9,636.6L597.9,636.6z"/>
+ <path class="st1" d="M929,134.9l-3.4,3.4C892.4,60.3,815.8,9.6,730.9,9.6h-54.2V7c0-3.3,2.7-6,6-6l0,0h51.2
+ c84.8,0,161.7,49.8,196.4,127.2C931.3,130.5,930.8,133.1,929,134.9z"/>
+ <path class="st1" d="M679.5,94.5V82.8c0-1.6-1.3-3-3-3l0,0h-11.7c-1.6,0-3,1.3-3,3l0,0v11.7c0,1.6-1.3,3-3,3l0,0h-11.7
+ c-1.6,0-3,1.3-3,3l0,0v11.7c0,1.6,1.3,3,3,3l0,0h11.7c1.6,0,3,1.3,3,3l0,0v11.7c0,1.6,1.3,3,3,3l0,0h11.7c1.6,0,3-1.3,3-3l0,0
+ v-11.7c0-1.6,1.3-3,3-3l0,0h11.7c1.6,0,3-1.3,3-3l0,0v-11.7c0-1.6-1.3-3-3-3l0,0h-11.7C680.8,97.5,679.5,96.1,679.5,94.5
+ L679.5,94.5z"/>
+ <circle class="st1" cx="768.9" cy="333.9" r="37.5"/>
+ <circle class="st1" cx="768.9" cy="187.1" r="37.5"/>
+ <circle class="st1" cx="842.3" cy="260.5" r="37.5"/>
+ <circle class="st1" cx="695.5" cy="260.5" r="37.5"/>
+ <circle class="st1" cx="715" cy="721.3" r="27.9"/>
+ <path class="st1" d="M765.6,460.3v12.3c0,3-2.2,5.5-5.2,5.9c-25.2,3.7-45,23.5-48.7,48.7c-0.4,2.9-2.9,5.1-5.9,5.2h-12.3
+ C695.2,493.3,726.5,462,765.6,460.3z"/>
+ <path class="st1" d="M765.6,598.8v12.3c-39.1-1.7-70.3-33-72.1-72h12.3c3,0,5.5,2.2,5.9,5.2c3.7,25.2,23.5,45,48.7,48.7
+ C763.4,593.3,765.6,595.8,765.6,598.8z"/>
+ <path class="st1" d="M844.3,539c-1.7,39.1-33,70.3-72.1,72v-12.3c0-3,2.2-5.5,5.2-5.9c25.2-3.7,45-23.5,48.7-48.7
+ c0.4-2.9,2.9-5.1,5.9-5.2L844.3,539z"/>
+ <path class="st1" d="M844.3,532.4H832c-3,0-5.5-2.2-5.9-5.2c-3.7-25.2-23.5-45-48.7-48.7c-2.9-0.4-5.1-2.9-5.2-5.9v-12.3
+ C811.3,462,842.6,493.3,844.3,532.4z"/>
+ <path class="st1" d="M844.4,535.7c0,1.1,0,2.2-0.1,3.3H832c-3,0-5.5,2.2-5.9,5.2c-3.7,25.2-23.5,45-48.7,48.7
+ c-2.9,0.4-5.1,2.9-5.2,5.9v12.3c-1.1,0.1-2.2,0.1-3.3,0.1s-2.2,0-3.3-0.1v-12.3c0-3-2.2-5.5-5.2-5.9c-25.2-3.7-45-23.5-48.7-48.7
+ c-0.4-2.9-2.9-5.1-5.9-5.2h-12.3c-0.1-1.1-0.1-2.2-0.1-3.3s0-2.2,0.1-3.3h12.3c3,0,5.5-2.2,5.9-5.2c3.7-25.2,23.5-45,48.7-48.7
+ c2.9-0.4,5.1-2.9,5.2-5.9v-12.3c1.1-0.1,2.2-0.1,3.3-0.1s2.2,0,3.3,0.1v12.3c0,3,2.2,5.5,5.2,5.9c25.2,3.7,45,23.5,48.7,48.7
+ c0.4,2.9,2.9,5.1,5.9,5.2h12.3C844.3,533.5,844.4,534.6,844.4,535.7z"/>
+</g>
+<path class="st2" d="M601.6,906.6h-7.9c-3.6,0-6.4-2.9-6.5-6.5V742.9c0-4.9,1.2-9.6,3.4-13.9l6.7-13v-79.2l-6.7-13
+ c-2.2-4.3-3.4-9.1-3.4-14V340.1c0-4.9,1.2-9.6,3.4-13.9l6.7-13V234l-6.7-13c-2.2-4.3-3.4-9.1-3.4-14V71.2c0-3.6,2.9-6.4,6.5-6.5h7.9
+ c3.6,0,6.4,2.9,6.5,6.5v828.9C608,903.7,605.1,906.6,601.6,906.6z M593.7,65.7c-3,0-5.5,2.4-5.5,5.5V207c0,4.7,1.1,9.3,3.3,13.5
+ l6.8,13.1c0,0.1,0.1,0.1,0.1,0.2v79.5c0,0.1,0,0.2-0.1,0.2l-6.8,13.1c-2.2,4.2-3.3,8.8-3.3,13.5v269.7c0,4.7,1.1,9.3,3.3,13.5
+ l6.8,13.1c0,0.1,0.1,0.1,0.1,0.2v79.5c0,0.1,0,0.2-0.1,0.2l-6.8,13.1c-2.2,4.2-3.3,8.8-3.3,13.5v157.2c0,3,2.4,5.5,5.5,5.5h7.9
+ c3,0,5.5-2.4,5.5-5.5V71.2c0-3-2.4-5.5-5.5-5.5H593.7z"/>
+<path class="st2" d="M618.8,858.9h-11.3c-0.3,0-0.5-0.2-0.5-0.5l0,0V72c0-0.3,0.2-0.5,0.5-0.5h11.3c0.3,0,0.5,0.2,0.5,0.5v786.4
+ C619.3,858.7,619.1,858.9,618.8,858.9L618.8,858.9z M608,857.9h10.3V72.5H608V857.9z"/>
+<path class="st2" d="M730.9,1000H624.7c-3.6,0-6.5-2.9-6.5-6.5v-978c0-3.6,2.9-6.5,6.5-6.5h106.2c116.8,0,211.9,95.1,211.9,211.9
+ v567.2C942.8,905.1,848,1000,730.9,1000L730.9,1000z M624.7,10.1c-3,0-5.5,2.4-5.5,5.5v978c0,3,2.4,5.5,5.5,5.5h106.2
+ c116.3,0,210.9-94.6,210.9-210.9V220.9C941.8,104.6,847.2,10,730.9,10L624.7,10.1z"/>
+<path class="st2" d="M715,763.2c-23.1,0-41.9-18.7-41.9-41.9s18.7-41.9,41.9-41.9s41.9,18.7,41.9,41.9l0,0
+ C756.8,744.4,738.1,763.1,715,763.2z M715,680.4c-22.6,0-40.9,18.3-40.9,40.9c0,22.6,18.3,40.9,40.9,40.9
+ c22.6,0,40.9-18.3,40.9-40.9l0,0C755.8,698.7,737.6,680.4,715,680.4z"/>
+<path class="st1" d="M782.2,203.2h-5.5l-7.8-12.9l-7.8,12.9h-5.4l10.6-16.3l-9.8-15.6h5.2l7.3,12l7.4-12h5l-9.8,15.4L782.2,203.2z"
+ />
+<path class="st1" d="M709.2,244.5l-11.6,20.6v11.4h-4.4V265l-11.6-20.5h5.3l6.4,11.7l2.3,4.7l2.2-4.3l6.4-12.1L709.2,244.5z"/>
+<path class="st1" d="M855.9,276.5h-4.7l-2.2-7h-13.3l-2.3,7h-4.5l10.6-32h6L855.9,276.5z M847.7,265.6l-5.4-17.1l-5.4,17.1H847.7z"
+ />
+<path class="st1" d="M779.4,340.4c0,1.4-0.3,2.8-0.9,4.1c-0.6,1.2-1.5,2.2-2.5,3c-1.2,0.9-2.6,1.5-4,1.9c-1.7,0.4-3.4,0.7-5.2,0.6
+ h-8.4v-32h9.2c7.1,0,10.7,2.6,10.7,7.8c0,1.6-0.4,3.1-1.2,4.5c-1,1.4-2.4,2.3-4,2.8c0.9,0.2,1.7,0.4,2.5,0.8s1.5,0.9,2,1.5
+ c0.6,0.6,1.1,1.4,1.4,2.2C779.2,338.4,779.4,339.4,779.4,340.4z M773.7,326.3c0-0.6-0.1-1.3-0.3-1.8c-0.2-0.6-0.6-1.1-1-1.5
+ c-0.6-0.5-1.3-0.8-2-1c-1-0.3-2.1-0.4-3.2-0.4h-4.5v10h4.4c0.9,0,1.8-0.1,2.7-0.3c0.8-0.2,1.5-0.5,2.1-1c0.6-0.4,1-1,1.3-1.7
+ C773.6,327.9,773.7,327.1,773.7,326.3L773.7,326.3z M774.8,340.5c0-0.8-0.2-1.5-0.5-2.2c-0.4-0.7-0.9-1.2-1.5-1.7
+ c-0.7-0.5-1.5-0.8-2.4-1c-1-0.3-2.1-0.4-3.2-0.4h-4.5v11h4.6c2.5,0,4.4-0.5,5.6-1.4C774.2,343.8,774.9,342.2,774.8,340.5
+ L774.8,340.5z"/>
+<path class="st1" d="M715,701.3L695.4,721h5.6v16.8h28.2V721h5.3L715,701.3z M720.7,731.8h-11.1V721h11.1V731.8z"/>
+<path class="st1" d="M597.9,314.3h-4.2c-3.8,0-6.9-3.1-7-7v-67.6c0-3.8,3.1-6.9,7-7h4.2c0.6,0,1,0.4,1,1l0,0v79.5
+ C598.9,313.9,598.4,314.3,597.9,314.3L597.9,314.3z M593.7,234.8c-2.7,0-5,2.2-5,5v67.6c0,2.7,2.2,5,5,5h3.2v-77.5L593.7,234.8
+ L593.7,234.8z"/>
+<path class="st1" d="M597.9,717.1h-4.2c-3.8,0-6.9-3.1-7-7v-67.6c0-3.8,3.1-6.9,7-7h4.2c0.6,0,1,0.4,1,1l0,0V716
+ C598.9,716.6,598.4,717.1,597.9,717.1L597.9,717.1z M593.7,637.6c-2.7,0-5,2.2-5,5v67.6c0,2.7,2.2,5,5,5h3.2v-77.5L593.7,637.6
+ L593.7,637.6z"/>
+<path class="st1" d="M925.6,139.3c-0.1,0-0.1,0-0.2,0c-0.3-0.1-0.6-0.3-0.7-0.6C891.7,61,815.4,10.5,730.9,10.6h-54.2
+ c-0.6,0-1-0.4-1-1l0,0V7c0-3.8,3.1-6.9,7-7h51.2c85.2,0.1,162.4,50.1,197.3,127.8c1.2,2.6,0.6,5.7-1.5,7.8l-3.4,3.4
+ C926.1,139.2,925.8,139.3,925.6,139.3z M677.7,8.6h53.2c84.7-0.1,161.3,50.2,195,128l2.4-2.4l0,0c1.5-1.4,1.9-3.6,1.1-5.5
+ C894.8,51.7,818.3,2.1,733.9,2h-51.2c-2.7,0-5,2.2-5,5V8.6z"/>
+<path class="st1" d="M676.5,133.7h-11.7c-2.2,0-4-1.8-4-4V118c0-1.1-0.9-2-2-2h-11.7c-2.2,0-4-1.8-4-4v-11.7c0-2.2,1.8-4,4-4h11.7
+ c1.1,0,2-0.9,2-2V82.8c0-2.2,1.8-4,4-4h11.7c2.2,0,4,1.8,4,4v11.7c0,1.1,0.9,2,2,2h11.7c2.2,0,4,1.8,4,4v11.7c0,2.2-1.8,4-4,4h-11.7
+ c-1.1,0-2,0.9-2,2v11.7C680.5,131.9,678.7,133.7,676.5,133.7z M647.2,98.5c-1.1,0-2,0.9-2,2v11.7c0,1.1,0.9,2,2,2h11.7
+ c2.2,0,4,1.8,4,4v11.7c0,1.1,0.9,2,2,2h11.7c1.1,0,2-0.9,2-2v-11.7c0-2.2,1.8-4,4-4h11.7c1.1,0,2-0.9,2-2v-11.7c0-1.1-0.9-2-2-2
+ h-11.7c-2.2,0-4-1.8-4-4V82.8c0-1.1-0.9-2-2-2h-11.7c-1.1,0-2,0.9-2,2v11.7c0,2.2-1.8,4-4,4H647.2z"/>
+<path class="st1" d="M768.9,372.4c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C807.3,355.2,790.1,372.4,768.9,372.4z M768.9,297.5c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5C805.3,313.8,789,297.5,768.9,297.5L768.9,297.5z"/>
+<path class="st1" d="M768.9,225.5c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C807.3,208.3,790.1,225.5,768.9,225.5z M768.9,150.6c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5C805.3,166.9,789,150.6,768.9,150.6L768.9,150.6z"/>
+<path class="st1" d="M842.3,299c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C880.8,281.7,863.6,298.9,842.3,299z M842.3,224c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5C878.8,240.4,862.5,224,842.3,224L842.3,224z"/>
+<path class="st1" d="M695.5,299c-21.2,0-38.5-17.2-38.5-38.5c0-21.2,17.2-38.5,38.5-38.5c21.2,0,38.5,17.2,38.5,38.5
+ C733.9,281.7,716.7,298.9,695.5,299z M695.5,224c-20.1,0-36.5,16.3-36.5,36.5c0,20.1,16.3,36.5,36.5,36.5
+ c20.1,0,36.5-16.3,36.5-36.5C731.9,240.4,715.6,224,695.5,224L695.5,224z"/>
+<path class="st1" d="M715,750.2c-16,0-28.9-13-28.9-28.9s13-28.9,28.9-28.9c16,0,28.9,13,28.9,28.9l0,0
+ C743.9,737.3,731,750.2,715,750.2z M715,694.3c-14.9,0-26.9,12.1-26.9,26.9s12.1,26.9,26.9,26.9c14.9,0,26.9-12.1,26.9-26.9
+ C741.9,706.4,729.9,694.4,715,694.3z"/>
+<path class="st1" d="M705.8,533.4h-12.3c-0.6,0-1-0.4-1-1l0,0c1.7-39.6,33.4-71.3,73-73c0.3,0,0.5,0.1,0.7,0.3
+ c0.2,0.2,0.3,0.4,0.3,0.7v12.3c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9C712.2,530.8,709.3,533.4,705.8,533.4z
+ M694.6,531.4h11.2c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-11.2
+ C727.2,463.5,696.7,494,694.6,531.4z"/>
+<path class="st1" d="M765.6,612.1C765.6,612.1,765.5,612.1,765.6,612.1c-39.6-1.7-71.3-33.4-73-73c0-0.6,0.4-1,1-1l0,0h12.3
+ c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9v12.3C766.6,611.6,766.1,612.1,765.6,612.1L765.6,612.1
+ L765.6,612.1z M694.6,540c2.2,37.4,32.6,67.8,70,70v-11.2c0-2.5-1.8-4.6-4.3-4.9c-25.6-3.9-45.7-24-49.6-49.6
+ c-0.3-2.5-2.4-4.3-4.9-4.3H694.6z"/>
+<path class="st1" d="M772.2,612.1c-0.6,0-1-0.4-1-1l0,0v-12.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9
+ c0.5-3.4,3.4-6,6.9-6h12.3c0.6,0,1,0.4,1,1l0,0C843.5,578.7,811.9,610.3,772.2,612.1C772.3,612.1,772.3,612.1,772.2,612.1z M832,540
+ c-2.5,0-4.6,1.8-4.9,4.3c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9V610c37.4-2.2,67.8-32.6,70-70H832z"/>
+<path class="st1" d="M844.3,533.4H832c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-12.3
+ c0-0.3,0.1-0.5,0.3-0.7s0.5-0.3,0.7-0.3c39.6,1.7,71.3,33.4,73,73C845.3,532.9,844.9,533.3,844.3,533.4L844.3,533.4L844.3,533.4z
+ M773.2,461.4v11.2c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h11.2
+ C841.1,494,810.6,463.5,773.2,461.4z"/>
+<path class="st1" d="M768.9,612.2c-1.2,0-2.3,0-3.4-0.1c-0.5,0-0.9-0.5-0.9-1v-12.3c0-2.5-1.8-4.6-4.3-4.9
+ c-25.6-3.9-45.7-24-49.6-49.6c-0.3-2.5-2.4-4.3-4.9-4.3h-12.3c-0.5,0-1-0.4-1-0.9c-0.1-1.1-0.1-2.2-0.1-3.4s0-2.3,0.1-3.4
+ c0-0.5,0.5-0.9,1-0.9h12.3c2.5,0,4.6-1.8,4.9-4.3c3.9-25.6,24-45.7,49.6-49.6c2.5-0.3,4.3-2.4,4.3-4.9v-12.3c0-0.5,0.4-1,0.9-1
+ c2.3-0.1,4.5-0.1,6.8,0c0.5,0,0.9,0.5,0.9,1v12.3c0,2.5,1.8,4.6,4.3,4.9c25.6,3.9,45.7,24,49.6,49.6c0.3,2.5,2.4,4.3,4.9,4.3h12.3
+ c0.5,0,1,0.4,1,0.9c0.1,1.1,0.1,2.2,0.1,3.4s0,2.3-0.1,3.4c0,0.5-0.5,0.9-1,0.9H832c-2.5,0-4.6,1.8-4.9,4.3
+ c-3.9,25.6-24,45.7-49.6,49.6c-2.5,0.3-4.3,2.4-4.3,4.9v12.3c0,0.5-0.4,1-0.9,1C771.2,612.1,770.1,612.2,768.9,612.2z M766.6,610.1
+ c1.5,0.1,3.1,0.1,4.7,0v-11.3c0-3.5,2.6-6.4,6-6.9c24.7-3.7,44.2-23.1,47.9-47.9c0.5-3.4,3.4-6,6.9-6h11.3c0-0.8,0-1.5,0-2.3
+ s0-1.6,0-2.3H832c-3.5,0-6.4-2.6-6.9-6c-3.7-24.7-23.1-44.2-47.9-47.9c-3.4-0.5-6-3.4-6-6.9v-11.3c-1.5-0.1-3.1-0.1-4.7,0v11.3
+ c0,3.5-2.6,6.4-6,6.9c-24.7,3.7-44.2,23.1-47.9,47.9c-0.5,3.4-3.4,6-6.9,6h-11.3c0,0.8,0,1.5,0,2.3s0,1.6,0,2.3h11.3
+ c3.5,0,6.4,2.6,6.9,6c3.7,24.7,23.1,44.2,47.9,47.9c3.4,0.5,6,3.4,6,6.9v11.3H766.6z"/>
+</svg>
diff --git a/Ryujinx/Ui/assets/ProCon.png b/Ryujinx/Ui/assets/ProCon.png
deleted file mode 100644
index 85636226..00000000
--- a/Ryujinx/Ui/assets/ProCon.png
+++ /dev/null
Binary files differ
diff --git a/Ryujinx/Ui/assets/ProCon.svg b/Ryujinx/Ui/assets/ProCon.svg
new file mode 100644
index 00000000..8c2b879f
--- /dev/null
+++ b/Ryujinx/Ui/assets/ProCon.svg
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 1000 1000.5" style="enable-background:new 0 0 1000 1000.5;" xml:space="preserve">
+<style type="text/css">
+ .st0{opacity:0.1;}
+ .st1{fill:#1ABC9C;}
+ .st2{fill:#CCCCCC;}
+ .st3{fill:#FFFFFF;}
+</style>
+<g class="st0">
+ <path class="st1" d="M259.4,630.9c-22.4,12.5-32.3,39.7-49,69.8c-35.7,64.1-50.5,128.9-116.6,128.9C40.8,829.5,1,776.1,1,705.8
+ c0-48.3,29.9-226.9,55.6-347.6C113.4,453.9,219.5,604.4,259.4,630.9z"/>
+ <path class="st1" d="M999,705.8c0,70.4-39.9,123.8-92.7,123.8c-66.1,0-80.9-64.8-116.6-128.9c-16.7-30-26.7-57.3-49-69.8
+ c39.8-26.5,146-177,202.7-272.7C969.1,478.9,999,657.5,999,705.8z"/>
+ <circle class="st1" cx="630.3" cy="482.7" r="56.2"/>
+ <circle class="st1" cx="630.3" cy="482.7" r="40.5"/>
+ <circle class="st1" cx="764.9" cy="276.6" r="36.6"/>
+ <circle class="st1" cx="764.9" cy="412.6" r="36.6"/>
+ <circle class="st1" cx="223.9" cy="344.8" r="56.2"/>
+ <circle class="st1" cx="223.9" cy="344.8" r="40.5"/>
+ <circle class="st1" cx="843.1" cy="344.6" r="36.6"/>
+ <circle class="st1" cx="686.7" cy="344.6" r="36.6"/>
+ <circle class="st1" cx="624.1" cy="269.3" r="22.1"/>
+ <circle class="st1" cx="571.3" cy="344.6" r="22.1"/>
+ <circle class="st1" cx="375.9" cy="269.3" r="22.1"/>
+ <circle class="st1" cx="428.7" cy="344.6" r="14"/>
+ <path class="st1" d="M414.6,326.5h28.2c2.2,0,4.1,1.8,4.1,4.1v28.2c0,2.2-1.8,4.1-4.1,4.1h-28.2c-2.2,0-4.1-1.8-4.1-4.1v-28.2
+ C410.6,328.3,412.4,326.5,414.6,326.5z"/>
+ <path class="st1" d="M351.6,158.8c-88.4,3.8-169.6,13.7-207,39.4c-14.9,10.2-26.4,16.9-32.9,20.5C142.6,135.9,282,138,299,138
+ c6.5,0,26.6,8.7,39.9,14.8h0C346.3,156.2,351.6,158.8,351.6,158.8z"/>
+ <path class="st1" d="M888.3,218.7c-6.5-3.6-18-10.3-32.9-20.6c-37.4-25.7-118.7-35.6-207-39.4c0,0,5.3-2.6,12.6-6
+ c13.4-6.1,33.4-14.8,40-14.8C717.9,138,857.4,135.9,888.3,218.7z"/>
+ <path class="st1" d="M414.6,461.1H373c-2.2,0-4.1-1.8-4.1-4.1v-41.6c0-2.2-1.8-4.1-4.1-4.1c0,0,0,0,0,0h-36.4
+ c-2.2,0-4.1,1.8-4.1,4.1c0,0,0,0,0,0v41.6c0,2.2-1.8,4.1-4.1,4.1h-41.6c-2.2,0-4.1,1.8-4.1,4.1v36.4c0,2.2,1.8,4.1,4.1,4.1h41.6
+ c2.2,0,4.1,1.8,4.1,4.1l0,0v41.6c0,2.2,1.8,4.1,4.1,4.1c0,0,0,0,0,0h36.4c2.2,0,4.1-1.8,4.1-4.1l0,0v-41.6c0-2.2,1.8-4.1,4.1-4.1
+ l0,0h41.6c2.2,0,4.1-1.8,4.1-4.1v-36.4C418.6,462.9,416.8,461.1,414.6,461.1z"/>
+</g>
+<path class="st2" d="M661,153.1L661,153.1c-0.2,0-0.3-0.1-0.3-0.3c0-0.1,0.1-0.2,0.2-0.2c0.1,0,0.3,0.1,0.3,0.2
+ C661.3,153,661.2,153.1,661,153.1C661,153.1,661,153.1,661,153.1z"/>
+<path class="st3" d="M630.3,560.6c-43,0-77.9-34.9-77.9-77.9s34.9-77.9,77.9-77.9c43,0,77.9,34.9,77.9,77.9
+ C708.2,525.7,673.3,560.6,630.3,560.6z M630.3,405.8c-42.5,0-76.9,34.4-76.9,76.9s34.4,76.9,76.9,76.9c42.5,0,76.9-34.4,76.9-76.9
+ C707.2,440.3,672.8,405.9,630.3,405.8L630.3,405.8z"/>
+<path class="st3" d="M223.9,422.7c-43,0-77.9-34.9-77.9-77.9c0-43,34.9-77.9,77.9-77.9s77.9,34.9,77.9,77.9l0,0
+ C301.7,387.8,266.9,422.7,223.9,422.7z M223.9,267.9c-42.5,0-76.9,34.4-76.9,76.9c0,42.5,34.4,76.9,76.9,76.9s76.9-34.4,76.9-76.9
+ l0,0C300.7,302.4,266.3,268,223.9,267.9z"/>
+<path class="st3" d="M648.4,159.3L648.4,159.3c-49.9-2.1-102.8-2.3-148.4-2.3c-45.6,0-98.4,0.2-148.4,2.3c-0.1,0-0.2,0-0.2-0.1
+ c-0.1,0-5.4-2.6-12.6-6c-0.3-0.1-0.4-0.4-0.2-0.7c0.1-0.2,0.2-0.3,0.4-0.3c2-0.2,50-4.9,161.1-4.9c111.7,0,159.1,4.7,161,4.9
+ c0.3,0,0.5,0.3,0.4,0.5c0,0.2-0.1,0.3-0.3,0.4c-7.2,3.3-12.6,5.9-12.6,6C648.5,159.3,648.4,159.3,648.4,159.3z M340.9,153.2
+ c5.8,2.7,10.1,4.8,10.8,5.1c49.9-2.1,102.7-2.3,148.3-2.3c45.6,0,98.4,0.2,148.3,2.3c0.7-0.4,5-2.4,10.8-5.1
+ c-9.8-0.9-58.2-4.7-159.1-4.7C399.6,148.5,350.7,152.3,340.9,153.2L340.9,153.2z"/>
+<path class="st3" d="M740.6,631.4c-0.1,0-0.2,0-0.2-0.1c-8.6-4.8-18.6-7.2-30.7-7.2H290.3c-12,0-22.1,2.4-30.7,7.2
+ c-0.2,0.1-0.4,0.1-0.5,0C218.3,604.2,110,449.1,56.2,358.4c-0.1-0.1-0.1-0.2-0.1-0.4c11.3-53.3,20.7-90,27.1-106.1
+ c1.2-3.1,2.9-5.9,4.9-8.5l12.7-16.2c2.9-3.7,6.5-6.8,10.6-9c0,0,0,0,0,0l0,0c7.1-3.9,18.5-10.6,32.8-20.5
+ c32-22,99.8-34.9,207.3-39.4c49.5-2.1,100.5-2.3,148.4-2.3c47.9,0,98.8,0.2,148.4,2.3c107.5,4.6,175.3,17.5,207.3,39.4
+ c14.4,9.9,25.8,16.6,32.9,20.5c4.1,2.2,7.7,5.3,10.6,9l12.7,16.2c2.1,2.6,3.7,5.5,4.9,8.6c6.4,16.1,15.7,52.8,27.1,106.1
+ c0,0.1,0,0.3-0.1,0.4C890,449.1,781.7,604.2,740.9,631.3C740.8,631.4,740.7,631.4,740.6,631.4z M57.2,358.1
+ c53.6,90.4,161.4,244.7,202.2,272.2c8.7-4.8,18.8-7.2,30.9-7.2h419.4c12.1,0,22.2,2.3,30.9,7.2c40.8-27.5,148.6-181.8,202.2-272.2
+ c-11.3-53.1-20.6-89.7-27-105.7c-1.2-3-2.8-5.8-4.8-8.3l-12.7-16.2c-2.8-3.6-6.3-6.5-10.3-8.7c-7.1-3.9-18.5-10.7-33-20.6
+ c-31.8-21.9-99.4-34.7-206.8-39.3c-49.5-2.1-100.4-2.3-148.3-2.3c-47.9,0-98.8,0.2-148.4,2.3c-107.3,4.6-174.9,17.4-206.7,39.3
+ c-14.4,9.9-25.8,16.6-32.9,20.5c0,0-0.1,0-0.1,0.1c-4,2.2-7.5,5.2-10.3,8.7l-12.7,16.2c-2,2.5-3.6,5.3-4.8,8.3
+ C77.8,268.4,68.5,305,57.2,358.1z"/>
+<path class="st1" d="M93.7,830.5C40.3,830.5,0,776.9,0,705.8C0,657.1,29.9,478.8,55.6,358c0.1-0.5,0.6-0.9,1.2-0.8
+ c0.3,0.1,0.5,0.2,0.7,0.5c53.7,90.5,161.9,245.4,202.4,272.4c0.5,0.3,0.6,0.9,0.3,1.4c-0.1,0.1-0.2,0.2-0.3,0.3
+ c-17.4,9.7-27.3,28.8-38.8,50.9c-3.1,6-6.3,12.1-9.8,18.4c-7.3,13.1-13.7,26.1-19.8,38.7C167.5,788.5,146.9,830.5,93.7,830.5z
+ M57.1,360.9C31.5,481.6,2,657.5,2,705.8c0,70,39.4,122.8,91.7,122.8c52,0,72.3-41.5,95.9-89.6c6.2-12.6,12.6-25.7,19.9-38.8
+ c3.5-6.3,6.7-12.4,9.8-18.4c11.3-21.8,21.2-40.7,38.2-51C216.1,601.5,110.7,450.9,57.1,360.9z"/>
+<path class="st1" d="M906.3,830.5c-53.3,0-73.9-42-97.7-90.7c-6.2-12.6-12.5-25.6-19.8-38.7c-3.5-6.3-6.7-12.5-9.8-18.4
+ c-11.5-22.1-21.4-41.2-38.8-50.9c-0.5-0.3-0.7-0.9-0.4-1.4c0.1-0.1,0.2-0.3,0.3-0.3c40.6-27,148.7-181.8,202.4-272.4
+ c0.3-0.5,0.9-0.6,1.4-0.3c0.2,0.1,0.4,0.4,0.5,0.7c25.7,120.9,55.6,299.1,55.6,347.8C1000,776.9,959.7,830.5,906.3,830.5z
+ M742.5,630.8c17.1,10.2,26.9,29.2,38.2,51c3.1,6,6.3,12.1,9.8,18.4c7.3,13.1,13.7,26.1,19.9,38.7c23.5,48.1,43.9,89.6,95.9,89.6
+ c52.3,0,91.7-52.8,91.7-122.8c0-48.2-29.5-224.2-55.1-344.9C889.3,450.9,783.9,601.5,742.5,630.8z"/>
+<path class="st1" d="M630.3,539.9c-31.6,0-57.2-25.6-57.2-57.2c0-31.6,25.6-57.2,57.2-57.2c31.6,0,57.2,25.6,57.2,57.2l0,0
+ C687.5,514.3,661.9,539.9,630.3,539.9z M630.3,427.5c-30.5,0-55.2,24.7-55.2,55.2c0,30.5,24.7,55.2,55.2,55.2
+ c30.5,0,55.2-24.7,55.2-55.2C685.5,452.2,660.8,427.5,630.3,427.5L630.3,427.5z"/>
+<path class="st1" d="M630.3,524.2c-22.9,0-41.5-18.6-41.5-41.5s18.6-41.5,41.5-41.5c22.9,0,41.5,18.6,41.5,41.5
+ C671.8,505.6,653.2,524.2,630.3,524.2z M630.3,443.2c-21.8,0-39.5,17.7-39.5,39.5c0,21.8,17.7,39.5,39.5,39.5
+ c21.8,0,39.5-17.7,39.5-39.5C669.8,460.9,652.1,443.2,630.3,443.2z"/>
+<path class="st1" d="M764.9,314.2c-20.7,0-37.6-16.8-37.5-37.6c0-20.7,16.8-37.6,37.6-37.5c20.7,0,37.5,16.8,37.5,37.6
+ C802.4,297.4,785.6,314.1,764.9,314.2z M764.9,241.1c-19.6,0-35.6,15.9-35.6,35.6c0,19.6,15.9,35.6,35.6,35.6
+ c19.6,0,35.6-15.9,35.6-35.6C800.4,257,784.5,241.1,764.9,241.1L764.9,241.1z"/>
+<path class="st1" d="M764.9,450.2c-20.7,0-37.6-16.8-37.5-37.6c0-20.7,16.8-37.6,37.6-37.5c20.7,0,37.5,16.8,37.5,37.6
+ C802.4,433.4,785.6,450.2,764.9,450.2z M764.9,377.1c-19.6,0-35.6,15.9-35.6,35.6c0,19.6,15.9,35.6,35.6,35.6
+ c19.6,0,35.6-15.9,35.6-35.6C800.4,393,784.5,377.1,764.9,377.1z"/>
+<path class="st1" d="M223.9,402.1c-31.6,0-57.2-25.6-57.2-57.2c0-31.6,25.6-57.2,57.2-57.2s57.2,25.6,57.2,57.2
+ C281.1,376.4,255.5,402,223.9,402.1z M223.9,289.6c-30.5,0-55.2,24.7-55.2,55.2c0,30.5,24.7,55.2,55.2,55.2
+ c30.5,0,55.2-24.7,55.2-55.2c0,0,0,0,0,0C279.1,314.4,254.4,289.7,223.9,289.6L223.9,289.6z"/>
+<path class="st1" d="M223.9,386.4c-22.9,0-41.5-18.6-41.5-41.5c0-22.9,18.6-41.5,41.5-41.5s41.5,18.6,41.5,41.5
+ C265.4,367.8,246.8,386.3,223.9,386.4z M223.9,305.3c-21.8,0-39.5,17.7-39.5,39.5c0,21.8,17.7,39.5,39.5,39.5s39.5-17.7,39.5-39.5
+ l0,0C263.4,323,245.7,305.3,223.9,305.3L223.9,305.3z"/>
+<path class="st1" d="M843.1,382.2c-20.7,0-37.6-16.8-37.6-37.5s16.8-37.6,37.5-37.6c20.7,0,37.6,16.8,37.6,37.5c0,0,0,0,0,0
+ C880.6,365.4,863.8,382.2,843.1,382.2z M843.1,309.1c-19.6,0-35.6,15.9-35.6,35.6s15.9,35.6,35.6,35.6c19.6,0,35.6-15.9,35.6-35.6
+ c0,0,0,0,0,0C878.6,325,862.7,309.1,843.1,309.1z"/>
+<path class="st1" d="M686.7,382.2c-20.7,0-37.6-16.8-37.6-37.6s16.8-37.6,37.6-37.6c20.7,0,37.6,16.8,37.6,37.6
+ C724.2,365.4,707.4,382.2,686.7,382.2z M686.7,309.1c-19.6,0-35.6,15.9-35.6,35.6s15.9,35.6,35.6,35.6c19.6,0,35.6-15.9,35.6-35.6
+ l0,0C722.2,325,706.3,309.1,686.7,309.1z"/>
+<path class="st1" d="M624.1,292.4c-12.8,0-23.1-10.3-23.1-23.1c0-12.8,10.3-23.1,23.1-23.1c12.8,0,23.1,10.3,23.1,23.1
+ C647.2,282,636.8,292.4,624.1,292.4z M624.1,248.2c-11.7,0-21.1,9.5-21.1,21.1c0,11.7,9.5,21.1,21.1,21.1c11.7,0,21.1-9.5,21.1-21.1
+ C645.2,257.6,635.7,248.2,624.1,248.2z"/>
+<path class="st1" d="M571.3,367.7c-12.8,0-23.1-10.3-23.1-23.1c0-12.8,10.3-23.1,23.1-23.1c12.8,0,23.1,10.3,23.1,23.1
+ C594.3,357.4,584,367.7,571.3,367.7z M571.3,323.5c-11.7,0-21.1,9.5-21.1,21.1c0,11.7,9.5,21.1,21.1,21.1c11.7,0,21.1-9.5,21.1-21.1
+ C592.3,333,582.9,323.5,571.3,323.5L571.3,323.5z"/>
+<path class="st1" d="M375.9,292.4c-12.8,0-23.1-10.3-23.1-23.1c0-12.8,10.3-23.1,23.1-23.1c12.8,0,23.1,10.3,23.1,23.1
+ C399,282,388.7,292.4,375.9,292.4z M375.9,248.2c-11.7,0-21.1,9.5-21.1,21.1c0,11.7,9.5,21.1,21.1,21.1c11.7,0,21.1-9.5,21.1-21.1
+ C397,257.6,387.6,248.2,375.9,248.2z"/>
+<path class="st1" d="M428.7,359.6c-8.3,0-15-6.7-15-15s6.7-15,15-15s15,6.7,15,15l0,0C443.7,352.9,437,359.6,428.7,359.6z
+ M428.7,331.7c-7.2,0-13,5.8-13,13c0,7.2,5.8,13,13,13c7.2,0,13-5.8,13-13c0,0,0,0,0,0C441.7,337.5,435.9,331.7,428.7,331.7
+ L428.7,331.7z"/>
+<path class="st1" d="M442.8,363.8h-28.2c-2.8,0-5.1-2.3-5.1-5.1v-28.2c0-2.8,2.3-5.1,5.1-5.1h28.2c2.8,0,5.1,2.3,5.1,5.1v28.2
+ C447.9,361.5,445.6,363.8,442.8,363.8z M414.6,327.5c-1.7,0-3.1,1.4-3.1,3.1v28.2c0,1.7,1.4,3.1,3.1,3.1h28.2c1.7,0,3.1-1.4,3.1-3.1
+ v-28.2c0-1.7-1.4-3.1-3.1-3.1H414.6z"/>
+<path class="st1" d="M111.7,219.7c-0.6,0-1-0.4-1-1c0-0.1,0-0.2,0.1-0.3c27.3-73.2,138.4-81.3,186-81.3c0.3,0,0.6,0,1,0l1.2,0
+ c6.6,0,25.5,8.1,40.3,14.9c0,0,0.1,0,0.1,0c7.2,3.3,12.6,6,12.7,6c0.5,0.2,0.7,0.8,0.5,1.3c-0.2,0.3-0.5,0.5-0.9,0.6
+ c-107.2,4.6-174.8,17.4-206.5,39.2c-14.6,10-26,16.7-33,20.6C112.1,219.6,111.9,219.7,111.7,219.7z M296.8,139
+ c-46.6,0-154.6,7.8-183.1,77.5c7-4,17.5-10.3,30.4-19.1c31.6-21.7,98.2-34.6,203.6-39.4c-2.2-1.1-5.4-2.5-8.9-4.2c0,0-0.1,0-0.1,0
+ c-20.6-9.5-34.7-14.7-39.5-14.7l-1.2,0C297.5,139,297.1,139,296.8,139L296.8,139z"/>
+<path class="st1" d="M888.3,219.7c-0.2,0-0.3,0-0.5-0.1c-7-3.9-18.5-10.7-33-20.6c-31.7-21.8-99.3-34.6-206.5-39.2c-0.6,0-1-0.5-1-1
+ c0-0.4,0.2-0.7,0.6-0.9c0.1,0,5.4-2.6,12.7-6C682.2,142,695.8,137,701,137l1.2,0c0.3,0,0.6,0,1,0c47.6,0,158.7,8.1,186.1,81.4
+ c0.2,0.5-0.1,1.1-0.6,1.3C888.5,219.7,888.4,219.7,888.3,219.7L888.3,219.7z M652.4,158c105.3,4.7,171.9,17.6,203.6,39.4
+ c12.9,8.8,23.3,15.1,30.4,19.1C857.8,146.8,749.8,139,703.2,139c-0.3,0-0.6,0-1,0l-1.2,0c-4.8,0-18.8,5.2-39.5,14.7
+ C657.8,155.4,654.6,156.9,652.4,158z"/>
+<path class="st1" d="M364.9,556.4h-36.4c-2.8,0-5.1-2.3-5.1-5.1v-41.6c0-1.7-1.4-3.1-3.1-3.1h-41.6c-2.8,0-5.1-2.3-5.1-5.1v-36.4
+ c0-2.8,2.3-5.1,5.1-5.1h41.6c1.7,0,3.1-1.4,3.1-3.1v-41.6c0-2.8,2.3-5.1,5.1-5.1h36.4c2.8,0,5.1,2.3,5.1,5.1v41.6
+ c0,1.7,1.4,3.1,3.1,3.1h41.6c2.8,0,5.1,2.3,5.1,5.1v36.4c0,2.8-2.3,5.1-5.1,5.1H373c-1.7,0-3.1,1.4-3.1,3.1v41.6
+ C369.9,554.1,367.7,556.4,364.9,556.4z M278.8,462.1c-1.7,0-3.1,1.4-3.1,3.1v36.4c0,1.7,1.4,3.1,3.1,3.1h41.6c2.8,0,5.1,2.3,5.1,5.1
+ v41.6c0,1.7,1.4,3.1,3.1,3.1h36.4c1.7,0,3.1-1.4,3.1-3.1v-41.6c0-2.8,2.3-5.1,5.1-5.1h41.6c1.7,0,3.1-1.4,3.1-3.1v-36.4
+ c0-1.7-1.4-3.1-3.1-3.1H373c-2.8,0-5.1-2.3-5.1-5.1v-41.6c0-1.7-1.4-3.1-3.1-3.1h-36.4c-1.7,0-3.1,1.4-3.1,3.1v41.6
+ c0,2.8-2.3,5.1-5.1,5.1H278.8z"/>
+<rect x="363.7" y="267.5" class="st1" width="24.4" height="3.5"/>
+<polygon class="st1" points="636.3,267.5 625.8,267.5 625.8,257.1 622.3,257.1 622.3,267.5 611.9,267.5 611.9,271 622.3,271
+ 622.3,281.4 625.8,281.4 625.8,271 636.3,271 "/>
+<path class="st1" d="M775.5,289.9H771l-6.3-10.4l-6.3,10.4h-4.4l8.6-13.2l-7.9-12.6h4.2l5.9,9.7l5.9-9.7h4l-7.9,12.4L775.5,289.9z"
+ />
+<path class="st1" d="M697.8,331.8l-9.3,16.5v9.2h-3.5v-9.3l-9.3-16.5h4.2l5.1,9.4l1.9,3.8l1.7-3.4l5.2-9.8L697.8,331.8z"/>
+<path class="st1" d="M854,357.5h-3.8l-1.8-5.6h-10.7l-1.8,5.6h-3.6l8.5-25.7h4.8L854,357.5z M847.4,348.8L843,335l-4.4,13.8H847.4z"
+ />
+<path class="st1" d="M773.3,417.8c0,1.1-0.2,2.3-0.7,3.3c-0.5,1-1.2,1.8-2,2.4c-1,0.7-2.1,1.2-3.2,1.5c-1.4,0.4-2.8,0.5-4.2,0.5
+ h-6.7v-25.7h7.4c5.7,0,8.6,2.1,8.6,6.3c0,1.3-0.3,2.5-1,3.6c-0.8,1.1-1.9,1.9-3.2,2.2c0.7,0.1,1.4,0.4,2,0.7
+ c0.6,0.3,1.2,0.7,1.6,1.2c0.5,0.5,0.9,1.1,1.1,1.8C773.2,416.2,773.4,417,773.3,417.8z M768.8,406.5c0-0.5-0.1-1-0.2-1.5
+ c-0.2-0.5-0.4-0.9-0.8-1.2c-0.5-0.4-1-0.6-1.6-0.8c-0.8-0.2-1.7-0.3-2.5-0.3H760v8.1h3.5c0.7,0,1.5-0.1,2.2-0.3
+ c0.6-0.1,1.2-0.4,1.7-0.8c0.5-0.3,0.8-0.8,1.1-1.3C768.7,407.8,768.8,407.2,768.8,406.5L768.8,406.5z M769.6,417.9
+ c0-0.6-0.1-1.2-0.4-1.8c-0.3-0.5-0.7-1-1.2-1.3c-0.6-0.4-1.2-0.7-1.9-0.8c-0.8-0.2-1.7-0.3-2.5-0.3H760v8.9h3.7
+ c1.6,0.1,3.2-0.3,4.5-1.1C769.2,420.6,769.7,419.3,769.6,417.9L769.6,417.9z"/>
+<path class="st1" d="M571.3,329.3L557.7,343h3.9v11.6h19.5V343h3.7L571.3,329.3z M575.2,350.4h-7.7V343h7.7L575.2,350.4z"/>
+<polygon class="st1" points="346.7,426.4 339.2,439.4 354.2,439.4 "/>
+<polygon class="st1" points="346.7,540.4 339.2,527.4 354.2,527.4 "/>
+<polygon class="st1" points="403.7,483.4 390.7,475.9 390.7,490.9 "/>
+<polygon class="st1" points="289.6,483.4 302.6,475.9 302.6,490.9 "/>
+</svg>
diff --git a/Ryujinx/Ui/assets/RedCon.png b/Ryujinx/Ui/assets/RedCon.png
deleted file mode 100644
index 6094b2e8..00000000
--- a/Ryujinx/Ui/assets/RedCon.png
+++ /dev/null
Binary files differ
diff --git a/Ryujinx/_schema.json b/Ryujinx/_schema.json
index f075b608..e89e2bf7 100644
--- a/Ryujinx/_schema.json
+++ b/Ryujinx/_schema.json
@@ -840,8 +840,8 @@
"title": "Joystick Deadzone",
"description": "Controller Analog Stick Deadzone",
"default": 0.05,
- "minimum": -32768.0,
- "maximum": 32767.0,
+ "minimum": 0.00,
+ "maximum": 1.00,
"examples": [
0.05
]