diff --git a/Ryujinx/Ui/Input/ButtonAssigner.cs b/Ryujinx/Ui/Input/ButtonAssigner.cs
new file mode 100644
index 0000000000..ff32b10687
--- /dev/null
+++ b/Ryujinx/Ui/Input/ButtonAssigner.cs
@@ -0,0 +1,17 @@
+using Ryujinx.Common.Configuration.Hid;
+
+namespace Ryujinx.Ui.Input
+{
+    interface ButtonAssigner
+    {
+        void Init();
+
+        void ReadInput();
+
+        bool HasAnyButtonPressed();
+
+        bool ShouldCancel();
+
+        string GetPressedButton();
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Ui/Input/JoystickButtonAssigner.cs b/Ryujinx/Ui/Input/JoystickButtonAssigner.cs
new file mode 100644
index 0000000000..481221ac12
--- /dev/null
+++ b/Ryujinx/Ui/Input/JoystickButtonAssigner.cs
@@ -0,0 +1,227 @@
+using OpenTK.Input;
+using Ryujinx.Common.Configuration.Hid;
+using System.Collections.Generic;
+using System;
+using System.IO;
+
+namespace Ryujinx.Ui.Input
+{
+    class JoystickButtonAssigner : ButtonAssigner
+    {
+        private int _index;
+
+        private double _triggerThreshold;
+
+        private JoystickState _currState;
+
+        private JoystickState _prevState;
+
+        private JoystickButtonDetector _detector;
+
+        public JoystickButtonAssigner(int index, double triggerThreshold)
+        {
+            _index = index;
+            _triggerThreshold = triggerThreshold;
+            _detector = new JoystickButtonDetector();
+        }
+
+        public void Init()
+        {
+            _currState = Joystick.GetState(_index);
+            _prevState = _currState;
+        }
+
+        public void ReadInput()
+        {
+            _prevState = _currState;
+            _currState = Joystick.GetState(_index);
+
+            CollectButtonStats();
+        }
+
+        public bool HasAnyButtonPressed()
+        {
+            return _detector.HasAnyButtonPressed();
+        }
+
+        public bool ShouldCancel()
+        {
+            return Mouse.GetState().IsAnyButtonDown || Keyboard.GetState().IsAnyKeyDown;
+        }
+
+        public string GetPressedButton()
+        {
+            List<ControllerInputId> pressedButtons = _detector.GetPressedButtons();
+
+            // Reverse list so axis button take precedence when more than one button is recognized.
+            pressedButtons.Reverse();
+
+            return pressedButtons.Count > 0 ? pressedButtons[0].ToString() : "";
+        }
+
+        private void CollectButtonStats()
+        {
+            JoystickCapabilities capabilities = Joystick.GetCapabilities(_index);
+
+            ControllerInputId pressedButton;
+
+            // Buttons
+            for (int i = 0; i != capabilities.ButtonCount; i++)
+            {
+                if (_currState.IsButtonDown(i) && _prevState.IsButtonUp(i))
+                {
+                    Enum.TryParse($"Button{i}", out pressedButton);
+                    _detector.AddInput(pressedButton, 1);
+                }
+
+                if (_currState.IsButtonUp(i) && _prevState.IsButtonDown(i))
+                {
+                    Enum.TryParse($"Button{i}", out pressedButton);
+                    _detector.AddInput(pressedButton, -1);
+                }
+            }
+
+            // Axis
+            for (int i = 0; i != capabilities.AxisCount; i++)
+            {
+                float axisValue = _currState.GetAxis(i);
+
+                Enum.TryParse($"Axis{i}", out pressedButton);
+                _detector.AddInput(pressedButton, axisValue);
+            }
+
+            // Hats
+            for (int i = 0; i != capabilities.HatCount; i++)
+            {
+                string currPos = GetHatPosition(_currState.GetHat((JoystickHat)i));
+                string prevPos = GetHatPosition(_prevState.GetHat((JoystickHat)i));
+
+                if (currPos == prevPos)
+                {
+                    continue;
+                }
+
+                if (currPos != "")
+                {
+                    Enum.TryParse($"Hat{i}{currPos}", out pressedButton);
+                    _detector.AddInput(pressedButton, 1);
+                }
+
+                if (prevPos != "")
+                {
+                    Enum.TryParse($"Hat{i}{prevPos}", out pressedButton);
+                    _detector.AddInput(pressedButton, -1);
+                }
+            }
+        }
+
+        private string GetHatPosition(JoystickHatState hatState)
+        {
+            if (hatState.IsUp) return "Up";
+            if (hatState.IsDown) return "Down";
+            if (hatState.IsLeft) return "Left";
+            if (hatState.IsRight) return "Right";
+            return "";
+        }
+
+        private class JoystickButtonDetector
+        {
+            private Dictionary<ControllerInputId, InputSummary> _stats;
+
+            public JoystickButtonDetector()
+            {
+                _stats = new Dictionary<ControllerInputId, InputSummary>();
+            }
+
+            public bool HasAnyButtonPressed()
+            {
+                foreach (var inputSummary in _stats.Values)
+                {
+                    if (checkButtonPressed(inputSummary))
+                    {
+                        return true;
+                    }
+                }
+                
+                return false;
+            }
+
+            public List<ControllerInputId> GetPressedButtons()
+            {
+                List<ControllerInputId> pressedButtons = new List<ControllerInputId>();
+
+                foreach (var kvp in _stats)
+                {
+                    if (!checkButtonPressed(kvp.Value))
+                    {
+                        continue;
+                    }
+                    pressedButtons.Add(kvp.Key);
+                }
+
+                return pressedButtons;
+            }
+
+            public void AddInput(ControllerInputId button, float value)
+            {
+                InputSummary inputSummary;
+
+                if (!_stats.TryGetValue(button, out inputSummary))
+                {
+                    inputSummary = new InputSummary();
+                    _stats.Add(button, inputSummary);
+                }
+
+                inputSummary.AddInput(value);
+            }
+
+            public override string ToString()
+            {
+                TextWriter writer = new StringWriter();
+
+                foreach (var kvp in _stats)
+                {
+                    writer.WriteLine($"Button {kvp.Key} -> {kvp.Value}");
+                }
+
+                return writer.ToString();
+            }
+
+            private bool checkButtonPressed(InputSummary sequence)
+            {
+                float distance = Math.Abs(sequence.Min - sequence.Avg) + Math.Abs(sequence.Max - sequence.Avg);
+                return distance > 1.5; // distance range [0, 2]
+            }
+        }
+
+        private class InputSummary
+        {
+            public float Min, Max, Sum, Avg;
+
+            public int NumSamples;
+
+            public InputSummary()
+            {
+                Min = float.MaxValue;
+                Max = float.MinValue;
+                Sum = 0;
+                NumSamples = 0;
+                Avg = 0;
+            }
+
+            public void AddInput(float value)
+            {
+                Min = Math.Min(Min, value);
+                Max = Math.Max(Max, value);
+                Sum += value;
+                NumSamples += 1;
+                Avg = Sum / NumSamples;
+            }
+
+            public override string ToString()
+            {
+                return $"Avg: {Avg} Min: {Min} Max: {Max} Sum: {Sum} NumSamples: {NumSamples}";
+            }
+        }
+    }
+}
diff --git a/Ryujinx/Ui/Input/KeyboardKeyAssigner.cs b/Ryujinx/Ui/Input/KeyboardKeyAssigner.cs
new file mode 100644
index 0000000000..2a29c36696
--- /dev/null
+++ b/Ryujinx/Ui/Input/KeyboardKeyAssigner.cs
@@ -0,0 +1,51 @@
+using OpenTK.Input;
+using System;
+using Key = Ryujinx.Configuration.Hid.Key;
+
+namespace Ryujinx.Ui.Input
+{
+    class KeyboardKeyAssigner : ButtonAssigner
+    {
+        private int _index;
+
+        private KeyboardState _keyboardState;
+
+        public KeyboardKeyAssigner(int index)
+        {
+            _index = index;
+        }
+
+        public void Init() { }
+
+        public void ReadInput()
+        {
+            _keyboardState = KeyboardController.GetKeyboardState(_index);
+        }
+
+        public bool HasAnyButtonPressed()
+        {
+            return _keyboardState.IsAnyKeyDown;
+        }
+
+        public bool ShouldCancel()
+        {
+            return Mouse.GetState().IsAnyButtonDown || Keyboard.GetState().IsKeyDown(OpenTK.Input.Key.Escape);
+        }
+
+        public string GetPressedButton()
+        {
+            string keyPressed = "";
+
+            foreach (Key key in Enum.GetValues(typeof(Key)))
+            {
+                if (_keyboardState.IsKeyDown((OpenTK.Input.Key)key))
+                {
+                    keyPressed = key.ToString();
+                    break;
+                }
+            }
+
+            return !ShouldCancel() ? keyPressed : "";
+        }
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx/Ui/Windows/ControllerWindow.cs b/Ryujinx/Ui/Windows/ControllerWindow.cs
index e06c6c6be3..67c02a0b12 100644
--- a/Ryujinx/Ui/Windows/ControllerWindow.cs
+++ b/Ryujinx/Ui/Windows/ControllerWindow.cs
@@ -4,6 +4,7 @@ using Ryujinx.Common.Configuration;
 using Ryujinx.Common.Configuration.Hid;
 using Ryujinx.Common.Utilities;
 using Ryujinx.Configuration;
+using Ryujinx.Ui.Input;
 using Ryujinx.Ui.Widgets;
 using System;
 using System.Collections.Generic;
@@ -584,73 +585,6 @@ namespace Ryujinx.Ui.Windows
             return null;
         }
 
-        private static bool IsAnyKeyPressed(out Key pressedKey, int index)
-        {
-            KeyboardState keyboardState = KeyboardController.GetKeyboardState(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 = AppDataManager.ProfilesDirPath;
@@ -690,6 +624,31 @@ namespace Ryujinx.Ui.Windows
             _refreshInputDevicesButton.SetStateFlags(StateFlags.Normal, true);
         }
 
+        private ButtonAssigner CreateButtonAssigner()
+        {
+            int index = int.Parse(_inputDevice.ActiveId.Split("/")[1]);
+
+            ButtonAssigner assigner;
+
+            if (_inputDevice.ActiveId.StartsWith("keyboard"))
+            {
+                assigner = new KeyboardKeyAssigner(index);
+            }
+            else if (_inputDevice.ActiveId.StartsWith("controller"))
+            {
+                // TODO: triggerThresold is passed but not used by JoystickButtonAssigner. Should it be used for key binding?.
+                // Note that, like left and right sticks, ZL and ZR triggers are treated as axis.
+                // The problem is then how to decide which axis should use triggerThresold.
+                assigner = new JoystickButtonAssigner(index, _controllerTriggerThreshold.Value);
+            }
+            else
+            {
+                throw new Exception("Controller not supported");
+            }
+            
+            return assigner;
+        }
+
         private void Button_Pressed(object sender, EventArgs args)
         {
             if (_isWaitingForInput)
@@ -697,67 +656,41 @@ namespace Ryujinx.Ui.Windows
                 return;
             }
 
+            ButtonAssigner assigner = CreateButtonAssigner();
+
             _isWaitingForInput = true;
 
             Thread inputThread = new Thread(() =>
             {
-                Button button = (ToggleButton)sender;
+                assigner.Init();
 
-                if (_inputDevice.ActiveId.StartsWith("keyboard"))
+                while (true)
                 {
-                    Key pressedKey;
+                    Thread.Sleep(10);
+                    assigner.ReadInput();
 
-                    int index = int.Parse(_inputDevice.ActiveId.Split("/")[1]);
-                    while (!IsAnyKeyPressed(out pressedKey, index))
+                    if (assigner.HasAnyButtonPressed() || assigner.ShouldCancel())
                     {
-                        if (Mouse.GetState().IsAnyButtonDown || Keyboard.GetState().IsKeyDown(OpenTK.Input.Key.Escape))
-                        {
-                            Application.Invoke(delegate
-                            {
-                                button.SetStateFlags(StateFlags.Normal, true);
-                            });
-
-                            _isWaitingForInput = false;
-
-                            return;
-                        }
+                        break;
                     }
-
-                    Application.Invoke(delegate
-                    {
-                        button.Label = pressedKey.ToString();
-                        button.SetStateFlags(StateFlags.Normal, 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(StateFlags.Normal, true);
-                            });
-
-                            _isWaitingForInput = false;
-
-                            return;
-                        }
-                    }
-
-                    Application.Invoke(delegate
-                    {
-                        button.Label = pressedButton.ToString();
-                        button.SetStateFlags(StateFlags.Normal, true);
-                    });
                 }
 
-                _isWaitingForInput = false;
+                string pressedButton = assigner.GetPressedButton();
+
+                ToggleButton button = (ToggleButton) sender;
+
+                Application.Invoke(delegate
+                {
+                    if (pressedButton != "")
+                    {
+                        button.Label = pressedButton;
+                    }
+                    
+                    button.Active = false;
+                    _isWaitingForInput = false;   
+                });
             });
+
             inputThread.Name = "GUI.InputThread";
             inputThread.IsBackground = true;
             inputThread.Start();