Просмотр исходного кода

Improvements to input and input configuration in the GUI. (#849)

* Improvements to input and input configuration in the GUI

* Requested changes

* nits

* more nits
Xpl0itR 6 лет назад
Родитель
Сommit
538fba826b
50 измененных файлов с 5738 добавлено и 2366 удалено
  1. 3 10
      Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
  2. 135 92
      Ryujinx.Common/Configuration/ConfigurationState.cs
  3. 6 11
      Ryujinx.Common/Configuration/Hid/ControllerConfig.cs
  4. 2 1
      Ryujinx.Common/Configuration/Hid/ControllerInputId.cs
  5. 17 8
      Ryujinx.Common/Configuration/Hid/ControllerType.cs
  6. 20 0
      Ryujinx.Common/Configuration/Hid/InputConfig.cs
  7. 2 1
      Ryujinx.Common/Configuration/Hid/Key.cs
  8. 20 0
      Ryujinx.Common/Configuration/Hid/KeyboardConfig.cs
  9. 4 2
      Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs
  10. 7 2
      Ryujinx.Common/Configuration/Hid/NpadControllerLeft.cs
  11. 7 2
      Ryujinx.Common/Configuration/Hid/NpadControllerRight.cs
  12. 0 20
      Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs
  13. 6 2
      Ryujinx.Common/Configuration/Hid/NpadKeyboardLeft.cs
  14. 6 2
      Ryujinx.Common/Configuration/Hid/NpadKeyboardRight.cs
  15. 18 0
      Ryujinx.Common/Configuration/Hid/PlayerIndex.cs
  16. 15 15
      Ryujinx.HLE/HOS/Services/Hid/Hid.cs
  17. 44 44
      Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
  18. 1 1
      Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/ControllerConfig.cs
  19. 2 2
      Ryujinx.HLE/HOS/Services/Hid/HidDevices/Types/GamepadInput.cs
  20. 31 31
      Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs
  21. 8 8
      Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerType.cs
  22. 6 6
      Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayoutsIndex.cs
  23. 1 1
      Ryujinx.HLE/HOS/Services/Nfc/Nfp/UserManager/Types/Device.cs
  24. 43 62
      Ryujinx/Config.json
  25. 14 10
      Ryujinx/Ryujinx.csproj
  26. 1 2
      Ryujinx/Ui/AboutWindow.cs
  27. 1 1
      Ryujinx/Ui/ApplicationLibrary.cs
  28. 925 0
      Ryujinx/Ui/ControllerWindow.cs
  29. 1732 0
      Ryujinx/Ui/ControllerWindow.glade
  30. 120 109
      Ryujinx/Ui/GLRenderer.cs
  31. 33 36
      Ryujinx/Ui/GtkDialog.cs
  32. 149 0
      Ryujinx/Ui/JoystickController.cs
  33. 57 34
      Ryujinx/Ui/KeyboardController.cs
  34. 48 54
      Ryujinx/Ui/MainWindow.cs
  35. 0 143
      Ryujinx/Ui/NpadController.cs
  36. 58 0
      Ryujinx/Ui/ProfileDialog.cs
  37. 124 0
      Ryujinx/Ui/ProfileDialog.glade
  38. 409 0
      Ryujinx/Ui/SettingsWindow.cs
  39. 1067 1039
      Ryujinx/Ui/SettingsWindow.glade
  40. 0 611
      Ryujinx/Ui/SwitchSettings.cs
  41. 2 2
      Ryujinx/Ui/TitleUpdateWindow.cs
  42. BIN
      Ryujinx/Ui/assets/BlueCon.png
  43. BIN
      Ryujinx/Ui/assets/JoyCon.png
  44. 105 0
      Ryujinx/Ui/assets/JoyConLeft.svg
  45. 218 0
      Ryujinx/Ui/assets/JoyConPair.svg
  46. 120 0
      Ryujinx/Ui/assets/JoyConRight.svg
  47. BIN
      Ryujinx/Ui/assets/ProCon.png
  48. 149 0
      Ryujinx/Ui/assets/ProCon.svg
  49. BIN
      Ryujinx/Ui/assets/RedCon.png
  50. 2 2
      Ryujinx/_schema.json

+ 3 - 10
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; }
 
@@ -129,11 +127,6 @@ namespace Ryujinx.Configuration
         /// </summary>
         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>
@@ -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

+ 135 - 92
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; }
 
@@ -207,32 +206,22 @@ namespace Ryujinx.Configuration
         /// </summary>
         public class HidSection
         {
-            /// <summary>
-            ///  The primary controller's type
-            /// </summary>
-            public ReactiveObject<ControllerType> ControllerType { get; private set; }
-
             /// <summary>
             /// Enable or disable keyboard support (Independent from controllers binding)
             /// </summary>
             public ReactiveObject<bool> EnableKeyboard { get; private set; }
 
             /// <summary>
-            /// Keyboard control bindings
+            /// 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!");
             }
         }
 

+ 6 - 11
Ryujinx.Common/Configuration/Hid/NpadController.cs → 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; }
     }
-}
+}

+ 2 - 1
Ryujinx.Common/Configuration/Hid/ControllerInputId.cs

@@ -40,6 +40,7 @@
         Hat2Up,
         Hat2Down,
         Hat2Left,
-        Hat2Right
+        Hat2Right,
+        Unbound
     }
 }

+ 17 - 8
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
     }
-}
+}

+ 20 - 0
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; }
+    }
+}

+ 2 - 1
Ryujinx.Common/Configuration/Hid/Key.cs

@@ -148,6 +148,7 @@
         Slash          = 128,
         BackSlash      = 129,
         NonUSBackSlash = 130,
-        LastKey        = 131
+        LastKey        = 131,
+        Unbound
     }
 }

+ 20 - 0
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; }
+    }
+}

+ 4 - 2
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; }
     }
-}
+}

+ 7 - 2
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; }
     }
-}
+}

+ 7 - 2
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; }
     }
-}
+}

+ 0 - 20
Ryujinx.Common/Configuration/Hid/NpadKeyboard.cs

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

+ 6 - 2
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; }
     }
-}
+}

+ 6 - 2
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; }
     }
-}
+}

+ 18 - 0
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
+    }
+}

+ 15 - 15
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;
         }

+ 44 - 44
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)

+ 1 - 1
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;
     }
 }

+ 2 - 2
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;
     }

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

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

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

+ 1 - 1
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;
     }
 }

+ 43 - 62
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": []
 }

+ 14 - 10
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>
 

+ 1 - 2
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 );

+ 1 - 1
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,

+ 925 - 0
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();
+        }
+    }
+}

+ 1732 - 0
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>

+ 120 - 109
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;
         }
     }

+ 33 - 36
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;
         }
     }
-}
+}

+ 149 - 0
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);
+            }
+        }
+    }
+}

+ 57 - 34
Ryujinx/Ui/KeyboardControls.cs → 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)

+ 48 - 54
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);
-        }
     }
 }

+ 0 - 143
Ryujinx/Ui/NpadController.cs

@@ -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);
-            }
-        }
-    }
-}

+ 58 - 0
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);
+        }
+    }
+}

+ 124 - 0
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>

+ 409 - 0
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();
+        }
+    }
+}

Разница между файлами не показана из-за своего большого размера
+ 1067 - 1039
Ryujinx/Ui/SettingsWindow.glade


+ 0 - 611
Ryujinx/Ui/SwitchSettings.cs

@@ -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"        },
-        };
-    }
-}

+ 2 - 2
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;

BIN
Ryujinx/Ui/assets/BlueCon.png


BIN
Ryujinx/Ui/assets/JoyCon.png


+ 105 - 0
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>

+ 218 - 0
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>

+ 120 - 0
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>

BIN
Ryujinx/Ui/assets/ProCon.png


+ 149 - 0
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>

BIN
Ryujinx/Ui/assets/RedCon.png


+ 2 - 2
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
           ]

Некоторые файлы не были показаны из-за большого количества измененных файлов