diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
index 39503157f..4f8062255 100644
--- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
@@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Applets
             byte[] controllerSupportArgPrivate = _normalSession.Pop();
             ControllerSupportArgPrivate privateArg = IApplet.ReadStruct<ControllerSupportArgPrivate>(controllerSupportArgPrivate);
 
-            Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet ArgPriv {privateArg.PrivateSize} {privateArg.ArgSize} {privateArg.Mode}" +
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet ArgPriv {privateArg.PrivateSize} {privateArg.ArgSize} {privateArg.Mode} " +
                         $"HoldType:{(NpadJoyHoldType)privateArg.NpadJoyHoldType} StyleSets:{(ControllerType)privateArg.NpadStyleSet}");
 
             if (privateArg.Mode != ControllerSupportMode.ShowControllerSupport)
@@ -47,33 +47,57 @@ namespace Ryujinx.HLE.HOS.Applets
 
             ControllerSupportArgHeader argHeader;
 
-            if (privateArg.ArgSize == Marshal.SizeOf<ControllerSupportArg>())
+            if (privateArg.ArgSize == Marshal.SizeOf<ControllerSupportArgV7>())
             {
-                ControllerSupportArg arg = IApplet.ReadStruct<ControllerSupportArg>(controllerSupportArg);
+                ControllerSupportArgV7 arg = IApplet.ReadStruct<ControllerSupportArgV7>(controllerSupportArg);
                 argHeader = arg.Header;
+
+                Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerSupportArg Version 7 EnableExplainText={arg.EnableExplainText != 0}");
+                // Read enable text here?
+            }
+            else if (privateArg.ArgSize == Marshal.SizeOf<ControllerSupportArgVPre7>())
+            {
+                ControllerSupportArgVPre7 arg = IApplet.ReadStruct<ControllerSupportArgVPre7>(controllerSupportArg);
+                argHeader = arg.Header;
+
+                Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerSupportArg Version Pre-7 EnableExplainText={arg.EnableExplainText != 0}");
                 // Read enable text here?
             }
             else
             {
-                Logger.Stub?.PrintStub(LogClass.ServiceHid, $"Unknown revision of ControllerSupportArg.");
+                Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerSupportArg Version Unknown");
 
                 argHeader = IApplet.ReadStruct<ControllerSupportArgHeader>(controllerSupportArg); // Read just the header
             }
 
-            Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet Arg {argHeader.PlayerCountMin} {argHeader.PlayerCountMax} {argHeader.EnableTakeOverConnection} {argHeader.EnableSingleMode}");
+            int playerMin = argHeader.PlayerCountMin;
+            int playerMax = argHeader.PlayerCountMax;
 
-            // Currently, the only purpose of this applet is to help 
-            // choose the primary input controller for the game
-            // TODO: Ideally should hook back to HID.Controller. When applet is called, can choose appropriate controller and attach to appropriate id.
-            if (argHeader.PlayerCountMin > 1)
+            Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet Arg {playerMin} {playerMax} {argHeader.EnableTakeOverConnection} {argHeader.EnableSingleMode}");
+
+            int configuredCount = 0;
+            PlayerIndex primaryIndex = PlayerIndex.Unknown;
+            while (!_system.Device.Hid.Npads.Validate(playerMin, playerMax, (ControllerType)privateArg.NpadStyleSet, out configuredCount, out primaryIndex))
             {
-                Logger.Warning?.Print(LogClass.ServiceHid, "More than one controller was requested.");
+                ControllerAppletUiArgs uiArgs = new ControllerAppletUiArgs
+                {
+                    PlayerCountMin = playerMin,
+                    PlayerCountMax = playerMax,
+                    SupportedStyles = (ControllerType)privateArg.NpadStyleSet,
+                    SupportedPlayers = _system.Device.Hid.Npads.GetSupportedPlayers(),
+                    IsDocked = _system.State.DockedMode
+                };
+
+                if (!_system.Device.UiHandler.DisplayMessageDialog(uiArgs))
+                {
+                    break;
+                }
             }
 
             ControllerSupportResultInfo result = new ControllerSupportResultInfo
             {
-                PlayerCount = 1,
-                SelectedId = (uint)GetNpadIdTypeFromIndex(_system.Device.Hid.Npads.PrimaryController)
+                PlayerCount = (sbyte)configuredCount,
+                SelectedId = (uint)GetNpadIdTypeFromIndex(primaryIndex)
             };
 
             Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet ReturnResult {result.PlayerCount} {result.SelectedId}");
diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs
new file mode 100644
index 000000000..cc15a406a
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerAppletUiArgs.cs
@@ -0,0 +1,14 @@
+using Ryujinx.HLE.HOS.Services.Hid;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.HOS.Applets
+{
+    public struct ControllerAppletUiArgs
+    {
+        public int PlayerCountMin;
+        public int PlayerCountMax;
+        public ControllerType SupportedStyles;
+        public IEnumerable<PlayerIndex> SupportedPlayers;
+        public bool IsDocked;
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs
index 945f0ef63..8eaf1d440 100644
--- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs
+++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgHeader.cs
@@ -1,6 +1,9 @@
+using System.Runtime.InteropServices;
+
 namespace Ryujinx.HLE.HOS.Applets
 {
 #pragma warning disable CS0649
+    [StructLayout(LayoutKind.Sequential, Pack=1)]
     struct ControllerSupportArgHeader
     {
         public sbyte PlayerCountMin;
diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArg.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs
similarity index 71%
rename from Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArg.cs
rename to Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs
index 908e90496..a01e7c043 100644
--- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArg.cs
+++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgV7.cs
@@ -1,8 +1,11 @@
+using System.Runtime.InteropServices;
+
 namespace Ryujinx.HLE.HOS.Applets
 {
 #pragma warning disable CS0649
     // (8.0.0+ version)
-    unsafe struct ControllerSupportArg
+    [StructLayout(LayoutKind.Sequential, Pack=1)]
+    unsafe struct ControllerSupportArgV7
     {
         public ControllerSupportArgHeader Header;
         public fixed uint IdentificationColor[8];
diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs
new file mode 100644
index 000000000..6d46aea56
--- /dev/null
+++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportArgVPre7.cs
@@ -0,0 +1,16 @@
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.HLE.HOS.Applets
+{
+#pragma warning disable CS0649
+    // (1.0.0+ version)
+    [StructLayout(LayoutKind.Sequential, Pack=1)]
+    unsafe struct ControllerSupportArgVPre7
+    {
+        public ControllerSupportArgHeader Header;
+        public fixed uint IdentificationColor[4];
+        public byte EnableExplainText;
+        public fixed byte ExplainText[4 * 0x81];
+    }
+#pragma warning restore CS0649
+}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs
index 09a19bf0d..c213e5926 100644
--- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs
+++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerSupportResultInfo.cs
@@ -1,6 +1,9 @@
+using System.Runtime.InteropServices;
+
 namespace Ryujinx.HLE.HOS.Applets
 {
 #pragma warning disable CS0649
+    [StructLayout(LayoutKind.Sequential, Pack=1)]
     unsafe struct ControllerSupportResultInfo
     {
         public sbyte PlayerCount;
diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 22152e778..eab6256e7 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -249,6 +249,9 @@ namespace Ryujinx.HLE.HOS
                 AppletState.EnqueueMessage(MessageInfo.OperationModeChanged);
                 AppletState.EnqueueMessage(MessageInfo.PerformanceModeChanged);
                 SignalDisplayResolutionChange();
+
+                // Reconfigure controllers
+                Device.Hid.RefreshInputConfig(ConfigurationState.Instance.Hid.InputConfig.Value);
             }
         }
 
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
index c355a050c..2db67619a 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Hid.cs
@@ -1,5 +1,7 @@
 using Ryujinx.Common;
 using Ryujinx.HLE.Exceptions;
+using Ryujinx.Common.Configuration.Hid;
+using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 
 namespace Ryujinx.HLE.HOS.Services.Hid
@@ -65,6 +67,24 @@ namespace Ryujinx.HLE.HOS.Services.Hid
             Npads       = new NpadDevices(_device, true);
         }
 
+        internal void RefreshInputConfig(List<InputConfig> inputConfig)
+        {
+            ControllerConfig[] npadConfig = new ControllerConfig[inputConfig.Count];
+
+            for (int i = 0; i < npadConfig.Length; ++i)
+            {
+                npadConfig[i].Player = (PlayerIndex)inputConfig[i].PlayerIndex;
+                npadConfig[i].Type = (ControllerType)inputConfig[i].ControllerType;
+            }
+
+            _device.Hid.Npads.Configure(npadConfig);
+        }
+
+        internal void RefreshInputConfigEvent(object _, ReactiveEventArgs<List<InputConfig>> args)
+        {
+            RefreshInputConfig(args.NewValue);
+        }
+
         public ControllerKeys UpdateStickButtons(JoystickPosition leftStick, JoystickPosition rightStick)
         {
             ControllerKeys result = 0;
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
index 1c8828876..334af975b 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs
@@ -1,74 +1,118 @@
-using System;
+using Ryujinx.Common;
 using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
 using Ryujinx.HLE.HOS.Kernel.Threading;
+using System;
+using System.Collections.Generic;
 
 namespace Ryujinx.HLE.HOS.Services.Hid
 {
     public class NpadDevices : BaseDevice
     {
-        internal NpadJoyHoldType JoyHold = NpadJoyHoldType.Vertical;
-        internal bool SixAxisActive = false; // TODO: link to hidserver when implemented
+        private const BatteryCharge DefaultBatteryCharge = BatteryCharge.Percent100;
 
-        private enum FilterState
-        {
-            Unconfigured = 0,
-            Configured   = 1,
-            Accepted     = 2
-        }
-
-        private struct NpadConfig
-        {
-            public ControllerType ConfiguredType;
-            public FilterState State;
-        }
-
-        private const int _maxControllers = 9; // Players1-8 and Handheld
-        private NpadConfig[] _configuredNpads;
-
-        private ControllerType _supportedStyleSets = ControllerType.ProController |
-                                                     ControllerType.JoyconPair |
-                                                     ControllerType.JoyconLeft |
-                                                     ControllerType.JoyconRight |
-                                                     ControllerType.Handheld;
-
-        public ControllerType SupportedStyleSets
-        {
-            get => _supportedStyleSets;
-            set
-            {
-                if (_supportedStyleSets != value) // Deal with spamming
-                {
-                    _supportedStyleSets = value;
-                    MatchControllers();
-                }
-            }
-        }
-
-        public PlayerIndex PrimaryController { get; set; } = PlayerIndex.Unknown;
+        private const int NoMatchNotifyFrequencyMs = 2000;
+        private int _activeCount;
+        private long _lastNotifyTimestamp;
 
+        public const int MaxControllers = 9; // Players 1-8 and Handheld
+        private ControllerType[] _configuredTypes;
         private KEvent[] _styleSetUpdateEvents;
+        private bool[] _supportedPlayers;
 
-        private static readonly Array3<BatteryCharge> _fullBattery;
+        internal NpadJoyHoldType JoyHold { get; set; }
+        internal bool SixAxisActive = false; // TODO: link to hidserver when implemented
+        internal ControllerType SupportedStyleSets { get; set; }
 
         public NpadDevices(Switch device, bool active = true) : base(device, active)
         {
-            _configuredNpads = new NpadConfig[_maxControllers];
+            _configuredTypes = new ControllerType[MaxControllers];
 
-            _styleSetUpdateEvents = new KEvent[_maxControllers];
+            SupportedStyleSets = ControllerType.Handheld | ControllerType.JoyconPair |
+                                 ControllerType.JoyconLeft | ControllerType.JoyconRight |
+                                 ControllerType.ProController;
 
+            _supportedPlayers = new bool[MaxControllers];
+            _supportedPlayers.AsSpan().Fill(true);
+
+            _styleSetUpdateEvents = new KEvent[MaxControllers];
             for (int i = 0; i < _styleSetUpdateEvents.Length; ++i)
             {
                 _styleSetUpdateEvents[i] = new KEvent(_device.System.KernelContext);
             }
 
-            _fullBattery[0] = _fullBattery[1] = _fullBattery[2] = BatteryCharge.Percent100;
+            _activeCount = 0;
+
+            JoyHold = NpadJoyHoldType.Vertical;
         }
 
-        public void AddControllers(params ControllerConfig[] configs)
+        internal ref KEvent GetStyleSetUpdateEvent(PlayerIndex player)
         {
+            return ref _styleSetUpdateEvents[(int)player];
+        }
+
+        internal void ClearSupportedPlayers()
+        {
+            _supportedPlayers.AsSpan().Clear();
+        }
+
+        internal void SetSupportedPlayer(PlayerIndex player, bool supported = true)
+        {
+            _supportedPlayers[(int)player] = supported;
+        }
+
+        internal IEnumerable<PlayerIndex> GetSupportedPlayers()
+        {
+            for (int i = 0; i < _supportedPlayers.Length; ++i)
+            {
+                if (_supportedPlayers[i])
+                {
+                    yield return (PlayerIndex)i;
+                }
+            }
+        }
+
+        public bool Validate(int playerMin, int playerMax, ControllerType acceptedTypes, out int configuredCount, out PlayerIndex primaryIndex)
+        {
+            primaryIndex = PlayerIndex.Unknown;
+            configuredCount = 0;
+
+            for (int i = 0; i < MaxControllers; ++i)
+            {
+                ControllerType npad = _configuredTypes[i];
+
+                if (npad == ControllerType.Handheld && _device.System.State.DockedMode)
+                {
+                    continue;
+                }
+
+                ControllerType currentType = _device.Hid.SharedMemory.Npads[i].Header.Type;
+
+                if (currentType != ControllerType.None && (npad & acceptedTypes) != 0 && _supportedPlayers[i])
+                {
+                    configuredCount++;
+                    if (primaryIndex == PlayerIndex.Unknown)
+                    {
+                        primaryIndex = (PlayerIndex)i;
+                    }
+                }
+            }
+
+            if (configuredCount < playerMin || configuredCount > playerMax || primaryIndex == PlayerIndex.Unknown)
+            {
+                return false;
+            }
+
+            return true;
+        }
+
+        public void Configure(params ControllerConfig[] configs)
+        {
+            _configuredTypes = new ControllerType[MaxControllers];
+
             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)
@@ -81,77 +125,87 @@ namespace Ryujinx.HLE.HOS.Services.Hid
                     player = PlayerIndex.Handheld;
                 }
 
-                _configuredNpads[(int)player] = new NpadConfig { ConfiguredType = controllerType, State = FilterState.Configured };
-            }
+                _configuredTypes[(int)player] = controllerType;
 
-            MatchControllers();
-        }
-
-        private void MatchControllers()
-        {
-            PrimaryController = PlayerIndex.Unknown;
-
-            for (int i = 0; i < _configuredNpads.Length; ++i)
-            {
-                ref NpadConfig config = ref _configuredNpads[i];
-
-                if (config.State == FilterState.Unconfigured)
-                {
-                    continue; // Ignore unconfigured
-                }
-
-                if ((config.ConfiguredType & _supportedStyleSets) == 0)
-                {
-                    Logger.Warning?.Print(LogClass.Hid, $"ControllerType {config.ConfiguredType} (connected to {(PlayerIndex)i}) not supported by game. Removing...");
-
-                    config.State = FilterState.Configured;
-                    _device.Hid.SharedMemory.Npads[i] = new ShMemNpad(); // Zero it
-
-                    continue;
-                }
-
-                InitController((PlayerIndex)i, config.ConfiguredType);
-            }
-
-            // Couldn't find any matching configuration. Reassign to something that works.
-            if (PrimaryController == PlayerIndex.Unknown)
-            {
-                ControllerType[] npadsTypeList = (ControllerType[])Enum.GetValues(typeof(ControllerType));
-
-                // Skip None Type
-                for (int i = 1; i < npadsTypeList.Length; ++i)
-                {
-                    ControllerType controllerType = npadsTypeList[i];
-                    if ((controllerType & _supportedStyleSets) != 0)
-                    {
-                        Logger.Warning?.Print(LogClass.Hid, $"No matching controllers found. Reassigning input as ControllerType {controllerType}...");
-
-                        InitController(controllerType == ControllerType.Handheld ? PlayerIndex.Handheld : PlayerIndex.Player1, controllerType);
-
-                        return;
-                    }
-                }
-
-                Logger.Error?.Print(LogClass.Hid, "Couldn't find any appropriate controller.");
+                Logger.Info?.Print(LogClass.Hid, $"Configured Controller {controllerType} to {player}");
             }
         }
 
-        internal ref KEvent GetStyleSetUpdateEvent(PlayerIndex player)
+        public void Update(IList<GamepadInput> states)
         {
-            return ref _styleSetUpdateEvents[(int)player];
+            Remap();
+
+            UpdateAllEntries();
+
+            // Update configured inputs
+            for (int i = 0; i < states.Count; ++i)
+            {
+                UpdateInput(states[i]);
+            }
         }
 
-        private void InitController(PlayerIndex player, ControllerType type)
+        private void Remap()
         {
-            if (type == ControllerType.Handheld)
+            // Remap/Init if necessary
+            for (int i = 0; i < MaxControllers; ++i)
             {
-                player = PlayerIndex.Handheld;
+                ControllerType config = _configuredTypes[i];
+
+                // Remove Handheld config when Docked
+                if (config == ControllerType.Handheld && _device.System.State.DockedMode)
+                {
+                    config = ControllerType.None;
+                }
+
+                // Auto-remap ProController and JoyconPair
+                if (config == ControllerType.JoyconPair && (SupportedStyleSets & ControllerType.JoyconPair) == 0 && (SupportedStyleSets & ControllerType.ProController) != 0)
+                {
+                    config = ControllerType.ProController;
+                }
+                else if (config == ControllerType.ProController && (SupportedStyleSets & ControllerType.ProController) == 0 && (SupportedStyleSets & ControllerType.JoyconPair) != 0)
+                {
+                    config = ControllerType.JoyconPair;
+                }
+
+                // Check StyleSet and PlayerSet
+                if ((config & SupportedStyleSets) == 0 || !_supportedPlayers[i])
+                {
+                    config = ControllerType.None;
+                }
+
+                SetupNpad((PlayerIndex)i, config);
             }
 
+            if (_activeCount == 0 && PerformanceCounter.ElapsedMilliseconds > _lastNotifyTimestamp + NoMatchNotifyFrequencyMs)
+            {
+                Logger.Warning?.Print(LogClass.Hid, $"No matching controllers found. Application requests '{SupportedStyleSets}' on '{string.Join(", ", GetSupportedPlayers())}'");
+                _lastNotifyTimestamp = PerformanceCounter.ElapsedMilliseconds;
+            }
+        }
+
+        private void SetupNpad(PlayerIndex player, ControllerType type)
+        {
             ref ShMemNpad controller = ref _device.Hid.SharedMemory.Npads[(int)player];
 
+            ControllerType oldType = controller.Header.Type;
+
+            if (oldType == type)
+            {
+                return; // Already configured
+            }
+
             controller = new ShMemNpad(); // Zero it
 
+            if (type == ControllerType.None)
+            {
+                _styleSetUpdateEvents[(int)player].ReadableEvent.Signal(); // Signal disconnect
+                _activeCount--;
+
+                Logger.Info?.Print(LogClass.Hid, $"Disconnected Controller {oldType} from {player}");
+
+                return;
+            }
+
             // TODO: Allow customizing colors at config
             NpadStateHeader defaultHeader = new NpadStateHeader
             {
@@ -168,7 +222,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
                                           NpadSystemProperties.PowerInfo1Connected |
                                           NpadSystemProperties.PowerInfo2Connected;
 
-            controller.BatteryState = _fullBattery;
+            controller.BatteryState.ToSpan().Fill(DefaultBatteryCharge);
 
             switch (type)
             {
@@ -217,19 +271,13 @@ namespace Ryujinx.HLE.HOS.Services.Hid
 
             controller.Header = defaultHeader;
 
-            if (PrimaryController == PlayerIndex.Unknown)
-            {
-                PrimaryController = player;
-            }
-
-            _configuredNpads[(int)player].State = FilterState.Accepted;
-
             _styleSetUpdateEvents[(int)player].ReadableEvent.Signal();
+            _activeCount++;
 
-            Logger.Info?.Print(LogClass.Hid, $"Connected ControllerType {type} to PlayerIndex {player}");
+            Logger.Info?.Print(LogClass.Hid, $"Connected Controller {type} to {player}");
         }
 
-        private static NpadLayoutsIndex ControllerTypeToLayout(ControllerType controllerType)
+        private static NpadLayoutsIndex ControllerTypeToNpadLayout(ControllerType controllerType)
         => controllerType switch
         {
             ControllerType.ProController => NpadLayoutsIndex.ProController,
@@ -241,43 +289,28 @@ namespace Ryujinx.HLE.HOS.Services.Hid
             _                            => NpadLayoutsIndex.SystemExternal
         };
 
-        public void SetGamepadsInput(params GamepadInput[] states)
+        private void UpdateInput(GamepadInput state)
         {
-            UpdateAllEntries();
-
-            for (int i = 0; i < states.Length; ++i)
-            {
-                SetGamepadState(states[i].PlayerId, states[i].Buttons, states[i].LStick, states[i].RStick);
-            }
-        }
-
-        private void SetGamepadState(PlayerIndex player, ControllerKeys buttons,
-            JoystickPosition leftJoystick, JoystickPosition rightJoystick)
-        {
-            if (player == PlayerIndex.Auto)
-            {
-                player = PrimaryController;
-            }
-
-            if (player == PlayerIndex.Unknown)
+            if (state.PlayerId == PlayerIndex.Unknown)
             {
                 return;
             }
 
-            if (_configuredNpads[(int)player].State != FilterState.Accepted)
+            ref ShMemNpad currentNpad = ref _device.Hid.SharedMemory.Npads[(int)state.PlayerId];
+
+            if (currentNpad.Header.Type == ControllerType.None)
             {
                 return;
             }
 
-            ref ShMemNpad  currentNpad   = ref _device.Hid.SharedMemory.Npads[(int)player];
-            ref NpadLayout currentLayout = ref currentNpad.Layouts[(int)ControllerTypeToLayout(currentNpad.Header.Type)];
+            ref NpadLayout currentLayout = ref currentNpad.Layouts[(int)ControllerTypeToNpadLayout(currentNpad.Header.Type)];
             ref NpadState  currentEntry  = ref currentLayout.Entries[(int)currentLayout.Header.LatestEntry];
 
-            currentEntry.Buttons = buttons;
-            currentEntry.LStickX = leftJoystick.Dx;
-            currentEntry.LStickY = leftJoystick.Dy;
-            currentEntry.RStickX = rightJoystick.Dx;
-            currentEntry.RStickY = rightJoystick.Dy;
+            currentEntry.Buttons = state.Buttons;
+            currentEntry.LStickX = state.LStick.Dx;
+            currentEntry.LStickY = state.LStick.Dy;
+            currentEntry.RStickX = state.RStick.Dx;
+            currentEntry.RStickY = state.RStick.Dy;
 
             // Mirror data to Default layout just in case
             ref NpadLayout mainLayout = ref currentNpad.Layouts[(int)NpadLayoutsIndex.SystemExternal];
diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs b/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs
index c2cd84329..9db5b5181 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/HidServer/HidUtils.cs
@@ -35,5 +35,19 @@ namespace Ryujinx.HLE.HOS.Services.Hid.HidServer
             PlayerIndex.Unknown  => NpadIdType.Unknown,
             _                    => throw new ArgumentOutOfRangeException(nameof(index))
         };
+
+        public static long GetLedPatternFromNpadId(NpadIdType npadIdType)
+        => npadIdType switch
+        {
+            NpadIdType.Player1  => 0b0001,
+            NpadIdType.Player2  => 0b0011,
+            NpadIdType.Player3  => 0b0111,
+            NpadIdType.Player4  => 0b1111,
+            NpadIdType.Player5  => 0b1001,
+            NpadIdType.Player6  => 0b0101,
+            NpadIdType.Player7  => 0b1101,
+            NpadIdType.Player8  => 0b0110,
+            _                   => 0b0000
+        };
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
index 3059d9471..ec4c0980a 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs
@@ -594,9 +594,18 @@ namespace Ryujinx.HLE.HOS.Services.Hid
 
             NpadIdType[] supportedPlayerIds = new NpadIdType[arraySize];
 
+            context.Device.Hid.Npads.ClearSupportedPlayers();
+
             for (int i = 0; i < arraySize; ++i)
             {
-                supportedPlayerIds[i] = context.Memory.Read<NpadIdType>((ulong)(context.Request.PtrBuff[0].Position + i * 4));
+                NpadIdType id = context.Memory.Read<NpadIdType>((ulong)(context.Request.PtrBuff[0].Position + i * 4));
+
+                if (id >= 0)
+                {
+                    context.Device.Hid.Npads.SetSupportedPlayer(HidUtils.GetIndexFromNpadIdType(id));
+                }
+
+                supportedPlayerIds[i] = id;
             }
 
             Logger.Stub?.PrintStub(LogClass.ServiceHid, $"{arraySize} " + string.Join(",", supportedPlayerIds));
@@ -665,9 +674,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
         // GetPlayerLedPattern(uint NpadId) -> ulong LedPattern
         public ResultCode GetPlayerLedPattern(ServiceCtx context)
         {
-            int npadId = context.RequestData.ReadInt32();
+            NpadIdType npadId = (NpadIdType)context.RequestData.ReadInt32();
 
-            long ledPattern = 0;
+            long ledPattern = HidUtils.GetLedPatternFromNpadId(npadId);
 
             context.ResponseData.Write(ledPattern);
 
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs
index e68924daf..3fbaa304c 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/DebugPad/DebugPad.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common.Memory;
+
 namespace Ryujinx.HLE.HOS.Services.Hid
 {
     unsafe struct ShMemDebugPad
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs
index d2bd8a233..d950425d9 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/HidSharedMemory.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Runtime.InteropServices;
+using Ryujinx.Common.Memory;
 
 namespace Ryujinx.HLE.HOS.Services.Hid
 {
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs
index dcfa5aa15..e2c1844f4 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Keyboard/Keyboard.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common.Memory;
+
 namespace Ryujinx.HLE.HOS.Services.Hid
 {
     unsafe struct ShMemKeyboard
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs
index c1f45c5c8..6b99e04a8 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Mouse/Mouse.cs
@@ -1,4 +1,6 @@
 
+using Ryujinx.Common.Memory;
+
 namespace Ryujinx.HLE.HOS.Services.Hid
 {
     unsafe struct ShMemMouse
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs
index 0bb2628f3..4ef83f3d1 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/Npad.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common.Memory;
+
 namespace Ryujinx.HLE.HOS.Services.Hid
 {
     // TODO: Add missing structs
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs
index 9851a6b11..24c4f4d44 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadLayout.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common.Memory;
+
 namespace Ryujinx.HLE.HOS.Services.Hid
 {
     struct NpadLayout
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs
index 5f65db390..a0a39fdca 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Npad/NpadSixAxis.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common.Memory;
+
 namespace Ryujinx.HLE.HOS.Services.Hid
 {
     struct NpadSixAxis
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/StructArrayHelpers.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/StructArrayHelpers.cs
deleted file mode 100644
index f40d16a0d..000000000
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/StructArrayHelpers.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System.Runtime.InteropServices;
-
-namespace Ryujinx.HLE.HOS.Services.Hid
-{
-#pragma warning disable CS0169
-    struct Array2<T> where T : unmanaged
-    {
-        T e0, e1;
-        public ref T this[int index] => ref MemoryMarshal.CreateSpan(ref e0, 2)[index];
-        public int Length => 2;
-    }
-
-    struct Array3<T> where T : unmanaged
-    {
-        T e0, e1, e2;
-        public ref T this[int index] => ref MemoryMarshal.CreateSpan(ref e0, 3)[index];
-        public int Length => 3;
-    }
-
-    struct Array6<T> where T : unmanaged
-    {
-        T e0, e1, e2, e3, e4, e5;
-        public ref T this[int index] => ref MemoryMarshal.CreateSpan(ref e0, 6)[index];
-        public int Length => 6;
-    }
-
-    struct Array7<T> where T : unmanaged
-    {
-        T e0, e1, e2, e3, e4, e5, e6;
-        public ref T this[int index] => ref MemoryMarshal.CreateSpan(ref e0, 7)[index];
-        public int Length => 7;
-    }
-
-    struct Array10<T> where T : unmanaged
-    {
-        T e0, e1, e2, e3, e4, e5, e6, e7, e8, e9;
-        public ref T this[int index] => ref MemoryMarshal.CreateSpan(ref e0, 10)[index];
-        public int Length => 10;
-    }
-
-    struct Array16<T> where T : unmanaged
-    {
-        T e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15;
-        public ref T this[int index] => ref MemoryMarshal.CreateSpan(ref e0, 16)[index];
-        public int Length => 16;
-    }
-
-    struct Array17<T> where T : unmanaged
-    {
-        T e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16;
-        public ref T this[int index] => ref MemoryMarshal.CreateSpan(ref e0, 17)[index];
-        public int Length => 17;
-    }
-#pragma warning restore CS0169
-}
\ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs
index 618ddd989..5f12295cf 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreen.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common.Memory;
+
 namespace Ryujinx.HLE.HOS.Services.Hid
 {
     unsafe struct ShMemTouchScreen
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs
index a0a9cf23b..1c85e2914 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenState.cs
@@ -1,3 +1,5 @@
+using Ryujinx.Common.Memory;
+
 namespace Ryujinx.HLE.HOS.Services.Hid
 {
     struct TouchScreenState
diff --git a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs
index 872064b31..4d4c48d1a 100644
--- a/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs
+++ b/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMem/Touchscreen/TouchScreenStateData.cs
@@ -3,13 +3,17 @@ namespace Ryujinx.HLE.HOS.Services.Hid
     struct TouchScreenStateData
     {
         public ulong SampleTimestamp;
+#pragma warning disable CS0169
         uint _padding;
+#pragma warning restore CS0169
         public uint TouchIndex;
         public uint X;
         public uint Y;
         public uint DiameterX;
         public uint DiameterY;
         public uint Angle;
+#pragma warning disable CS0169
         uint _padding2;
+#pragma warning restore CS0169
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/IHostUiHandler.cs b/Ryujinx.HLE/IHostUiHandler.cs
index 13b4b4c1c..bd64da87f 100644
--- a/Ryujinx.HLE/IHostUiHandler.cs
+++ b/Ryujinx.HLE/IHostUiHandler.cs
@@ -10,5 +10,17 @@ namespace Ryujinx.HLE
         /// <param name="userText">Text that the user entered. Set to `null` on internal errors</param>
         /// <returns>True when OK is pressed, False otherwise. Also returns True on internal errors</returns>
         bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText);
+
+        /// <summary>
+        /// Displays a Message Dialog box to the user and blocks until it is closed.
+        /// </summary>
+        /// <returns>True when OK is pressed, False otherwise.</returns>
+        bool DisplayMessageDialog(string title, string message);
+
+        /// <summary>
+        /// Displays a Message Dialog box specific to Controller Applet and blocks until it is closed.
+        /// </summary>
+        /// <returns>True when OK is pressed, False otherwise.</returns>
+        bool DisplayMessageDialog(ControllerAppletUiArgs args);
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs
index 0bdcdabd2..df02e5e57 100644
--- a/Ryujinx.HLE/Switch.cs
+++ b/Ryujinx.HLE/Switch.cs
@@ -114,6 +114,10 @@ namespace Ryujinx.HLE
             System.GlobalAccessLogMode = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
 
             ServiceConfiguration.IgnoreMissingServices = ConfigurationState.Instance.System.IgnoreMissingServices;
+
+            // Configure controllers
+            Hid.RefreshInputConfig(ConfigurationState.Instance.Hid.InputConfig.Value);
+            ConfigurationState.Instance.Hid.InputConfig.Event += Hid.RefreshInputConfigEvent;
         }
 
         public static IntegrityCheckLevel GetIntegrityCheckLevel()
@@ -177,6 +181,8 @@ namespace Ryujinx.HLE
         {
             if (disposing)
             {
+                ConfigurationState.Instance.Hid.InputConfig.Event -= Hid.RefreshInputConfigEvent;
+
                 System.Dispose();
                 Host1x.Dispose();
                 AudioOut.Dispose();
diff --git a/Ryujinx/Ui/ControllerWindow.cs b/Ryujinx/Ui/ControllerWindow.cs
index 35c3859fc..9518ba98d 100644
--- a/Ryujinx/Ui/ControllerWindow.cs
+++ b/Ryujinx/Ui/ControllerWindow.cs
@@ -5,6 +5,7 @@ using Ryujinx.Common.Utilities;
 using Ryujinx.Configuration;
 using Ryujinx.HLE.FileSystem;
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Reflection;
 using System.Text.Json;
@@ -91,6 +92,23 @@ namespace Ryujinx.Ui
             _virtualFileSystem = virtualFileSystem;
             _inputConfig       = ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerIndex);
 
+            Title = $"Ryujinx - Controller Settings - {_playerIndex}";
+
+            if (_playerIndex == PlayerIndex.Handheld)
+            {
+                _controllerType.Append(ControllerType.Handheld.ToString(), "Handheld");
+                _controllerType.Sensitive = false;
+            }
+            else
+            {
+                _controllerType.Append(ControllerType.ProController.ToString(), "Pro Controller");
+                _controllerType.Append(ControllerType.JoyconPair.ToString(), "Joycon Pair");
+                _controllerType.Append(ControllerType.JoyconLeft.ToString(), "Joycon Left");
+                _controllerType.Append(ControllerType.JoyconRight.ToString(), "Joycon Right");
+            }
+
+            _controllerType.Active = 0; // Set initial value to first in list.
+
             //Bind Events
             _lStickX.Clicked        += Button_Pressed;
             _lStickY.Clicked        += Button_Pressed;
@@ -278,7 +296,12 @@ namespace Ryujinx.Ui
             switch (config)
             {
                 case KeyboardConfig keyboardConfig:
-                    _controllerType.SetActiveId(keyboardConfig.ControllerType.ToString());
+                    if (!_controllerType.SetActiveId(keyboardConfig.ControllerType.ToString()))
+                    {
+                        _controllerType.SetActiveId(_playerIndex == PlayerIndex.Handheld 
+                            ? ControllerType.Handheld.ToString() 
+                            : ControllerType.ProController.ToString());
+                    }
 
                     _lStickUp.Label     = keyboardConfig.LeftJoycon.StickUp.ToString();
                     _lStickDown.Label   = keyboardConfig.LeftJoycon.StickDown.ToString();
@@ -310,7 +333,12 @@ namespace Ryujinx.Ui
                     _rSr.Label          = keyboardConfig.RightJoycon.ButtonSr.ToString();
                     break;
                 case ControllerConfig controllerConfig:
-                    _controllerType.SetActiveId(controllerConfig.ControllerType.ToString());
+                    if (!_controllerType.SetActiveId(controllerConfig.ControllerType.ToString()))
+                    {
+                        _controllerType.SetActiveId(_playerIndex == PlayerIndex.Handheld 
+                            ? ControllerType.Handheld.ToString() 
+                            : ControllerType.ProController.ToString());
+                    }
 
                     _lStickX.Label                    = controllerConfig.LeftJoycon.StickX.ToString();
                     _invertLStickX.Active             = controllerConfig.LeftJoycon.InvertStickX;
@@ -894,24 +922,31 @@ namespace Ryujinx.Ui
         {
             InputConfig inputConfig = GetValues();
 
+            var newConfig = new List<InputConfig>();
+            newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);
+
             if (_inputConfig == null && inputConfig != null)
             {
-                ConfigurationState.Instance.Hid.InputConfig.Value.Add(inputConfig);
+                newConfig.Add(inputConfig);
             }
             else
             {
                 if (_inputDevice.ActiveId == "disabled")
                 {
-                    ConfigurationState.Instance.Hid.InputConfig.Value.Remove(_inputConfig);
+                    newConfig.Remove(_inputConfig);
                 }
                 else if (inputConfig != null)
                 {
-                    int index = ConfigurationState.Instance.Hid.InputConfig.Value.IndexOf(_inputConfig);        
+                    int index = newConfig.IndexOf(_inputConfig);
 
-                    ConfigurationState.Instance.Hid.InputConfig.Value[index] = inputConfig;
+                    newConfig[index] = inputConfig;
                 }
             }
 
+            // Atomically replace and signal input change.
+            // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event.
+            ConfigurationState.Instance.Hid.InputConfig.Value = newConfig;
+
             MainWindow.SaveConfig();
 
             Dispose();
diff --git a/Ryujinx/Ui/ControllerWindow.glade b/Ryujinx/Ui/ControllerWindow.glade
index 2b780f13b..c0532d907 100644
--- a/Ryujinx/Ui/ControllerWindow.glade
+++ b/Ryujinx/Ui/ControllerWindow.glade
@@ -138,13 +138,6 @@
                                 <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>
diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs
index 867401ada..203df72ad 100644
--- a/Ryujinx/Ui/GLRenderer.cs
+++ b/Ryujinx/Ui/GLRenderer.cs
@@ -405,9 +405,9 @@ namespace Ryujinx.Ui
                 });
             }
 
-            List<GamepadInput> gamepadInputs = new List<GamepadInput>();
+            List<GamepadInput> gamepadInputs = new List<GamepadInput>(NpadDevices.MaxControllers);
 
-            foreach (InputConfig inputConfig in ConfigurationState.Instance.Hid.InputConfig.Value.ToArray())
+            foreach (InputConfig inputConfig in ConfigurationState.Instance.Hid.InputConfig.Value)
             {
                 ControllerKeys   currentButton = 0;
                 JoystickPosition leftJoystick  = new JoystickPosition();
@@ -497,18 +497,21 @@ namespace Ryujinx.Ui
                 });
             }
 
-            _device.Hid.Npads.SetGamepadsInput(gamepadInputs.ToArray());
+            _device.Hid.Npads.Update(gamepadInputs);
 
-            // Hotkeys
-            HotkeyButtons currentHotkeyButtons = KeyboardController.GetHotkeyButtons(OpenTK.Input.Keyboard.GetState());
-
-            if (currentHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync) &&
-                !_prevHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync))
+            if(IsFocused)
             {
-                _device.EnableDeviceVsync = !_device.EnableDeviceVsync;
-            }
+                // Hotkeys
+                HotkeyButtons currentHotkeyButtons = KeyboardController.GetHotkeyButtons(OpenTK.Input.Keyboard.GetState());
 
-            _prevHotkeyButtons = currentHotkeyButtons;
+                if (currentHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync) &&
+                    !_prevHotkeyButtons.HasFlag(HotkeyButtons.ToggleVSync))
+                {
+                    _device.EnableDeviceVsync = !_device.EnableDeviceVsync;
+                }
+
+                _prevHotkeyButtons = currentHotkeyButtons;
+            }
 
             //Touchscreen
             bool hasTouch = false;
diff --git a/Ryujinx/Ui/GtkHostUiHandler.cs b/Ryujinx/Ui/GtkHostUiHandler.cs
index 989fe14d8..90830056d 100644
--- a/Ryujinx/Ui/GtkHostUiHandler.cs
+++ b/Ryujinx/Ui/GtkHostUiHandler.cs
@@ -16,6 +16,62 @@ namespace Ryujinx.Ui
             _parent = parent;
         }
 
+        public bool DisplayMessageDialog(ControllerAppletUiArgs args)
+        {
+            string playerCount = args.PlayerCountMin == args.PlayerCountMax
+                ? $"exactly {args.PlayerCountMin}"
+                : $"{args.PlayerCountMin}-{args.PlayerCountMax}";
+
+            string message =
+                $"Application requests <b>{playerCount}</b> player(s) with:\n\n"
+                + $"<tt><b>TYPES:</b> {args.SupportedStyles}</tt>\n\n"
+                + $"<tt><b>PLAYERS:</b> {string.Join(", ", args.SupportedPlayers)}</tt>\n\n"
+                + (args.IsDocked ? "Docked mode set. <tt>Handheld</tt> is also invalid.\n\n" : "")
+                + "<i>Please reconfigure Input now and then press OK.</i>";
+
+            return DisplayMessageDialog("Controller Applet", message);
+        }
+
+        public bool DisplayMessageDialog(string title, string message)
+        {
+            ManualResetEvent dialogCloseEvent = new ManualResetEvent(false);
+            bool okPressed = false;
+
+            Application.Invoke(delegate
+            {
+                MessageDialog msgDialog = null;
+                try
+                {
+                    msgDialog = new MessageDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null)
+                    {
+                        Title = title,
+                        Text = message,
+                        UseMarkup = true
+                    };
+
+                    msgDialog.SetDefaultSize(400, 0);
+
+                    msgDialog.Response += (object o, ResponseArgs args) =>
+                    {
+                        if (args.ResponseId == ResponseType.Ok) okPressed = true;
+                        dialogCloseEvent.Set();
+                        msgDialog?.Dispose();
+                    };
+
+                    msgDialog.Show();
+                }
+                catch (Exception e)
+                {
+                    Logger.Error?.Print(LogClass.Application, $"Error displaying Message Dialog: {e}");
+                    dialogCloseEvent.Set();
+                }
+            });
+
+            dialogCloseEvent.WaitOne();
+
+            return okPressed;
+        }
+
         public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)
         {
             ManualResetEvent dialogCloseEvent = new ManualResetEvent(false);
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index f077e9bd3..d2b303e42 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -507,14 +507,6 @@ namespace Ryujinx.Ui
                 _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1);
             }
 
-            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, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
 
             Application.Invoke(delegate