diff --git a/src/duckstation-nogui/sdl_host_interface.cpp b/src/duckstation-nogui/sdl_host_interface.cpp
index d162ebc54..96fd851ec 100644
--- a/src/duckstation-nogui/sdl_host_interface.cpp
+++ b/src/duckstation-nogui/sdl_host_interface.cpp
@@ -1,10 +1,10 @@
 #include "sdl_host_interface.h"
 #include "frontend-common/controller_interface.h"
+#include "frontend-common/fullscreen_ui.h"
 #include "frontend-common/icon.h"
 #include "frontend-common/ini_settings_interface.h"
 #include "frontend-common/sdl_controller_interface.h"
 #include "frontend-common/sdl_initializer.h"
-#include "frontend-common/fullscreen_ui.h"
 #include "imgui.h"
 #include "imgui_impl_sdl.h"
 #include "scmversion/scmversion.h"
@@ -307,7 +307,8 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
     case SDL_KEYUP:
     {
       // Binding mode
-      if (m_fullscreen_ui_enabled && m_controller_interface && m_controller_interface->HasHook() && event->key.repeat == 0)
+      if (m_fullscreen_ui_enabled && m_controller_interface && m_controller_interface->HasHook() &&
+          event->key.repeat == 0)
       {
         String keyName;
         if (!SDLKeyNames::KeyEventToString(event, keyName))
@@ -324,9 +325,9 @@ void SDLHostInterface::HandleSDLEvent(const SDL_Event* event)
 
       if (!ImGui::GetIO().WantCaptureKeyboard && event->key.repeat == 0)
       {
-        const HostKeyCode code = static_cast<HostKeyCode>(SDLKeyNames::KeyEventToInt(event));
+        const u32 code = SDLKeyNames::KeyEventToInt(event);
         const bool pressed = (event->type == SDL_KEYDOWN);
-        HandleHostKeyEvent(code, pressed);
+        HandleHostKeyEvent(code & SDLKeyNames::KEY_MASK, code & SDLKeyNames::MODIFIER_MASK, pressed);
       }
     }
     break;
diff --git a/src/duckstation-nogui/sdl_key_names.h b/src/duckstation-nogui/sdl_key_names.h
index acf32e478..3222d6e15 100644
--- a/src/duckstation-nogui/sdl_key_names.h
+++ b/src/duckstation-nogui/sdl_key_names.h
@@ -10,6 +10,13 @@
 
 namespace SDLKeyNames {
 
+enum : u32
+{
+  MODIFIER_SHIFT = 16,
+  KEY_MASK = ((1 << MODIFIER_SHIFT) - 1),
+  MODIFIER_MASK = ~KEY_MASK,
+};
+
 static const std::map<int, const char*> s_sdl_key_names = {{SDLK_RETURN, "Return"},
                                                            {SDLK_ESCAPE, "Escape"},
                                                            {SDLK_BACKSPACE, "Backspace"},
@@ -265,13 +272,13 @@ static const std::array<SDLKeyModifierEntry, 4> s_sdl_key_modifiers = {
    {KMOD_LALT, static_cast<SDL_Keymod>(KMOD_LALT | KMOD_RALT), SDLK_LALT, SDLK_RALT, "Alt"},
    {KMOD_LGUI, static_cast<SDL_Keymod>(KMOD_LGUI | KMOD_RGUI), SDLK_LGUI, SDLK_RGUI, "Meta"}}};
 
-const char* GetKeyName(SDL_Keycode key)
+static const char* GetKeyName(SDL_Keycode key)
 {
   const auto it = s_sdl_key_names.find(key);
   return it == s_sdl_key_names.end() ? nullptr : it->second;
 }
 
-std::optional<SDL_Keycode> GetKeyCodeForName(const std::string_view key_name)
+static std::optional<SDL_Keycode> GetKeyCodeForName(const std::string_view key_name)
 {
   for (const auto& it : s_sdl_key_names)
   {
@@ -282,24 +289,24 @@ std::optional<SDL_Keycode> GetKeyCodeForName(const std::string_view key_name)
   return std::nullopt;
 }
 
-u32 KeyEventToInt(const SDL_Event* event)
+static u32 KeyEventToInt(const SDL_Event* event)
 {
   u32 code = static_cast<u32>(event->key.keysym.sym);
 
   const SDL_Keymod mods = static_cast<SDL_Keymod>(event->key.keysym.mod);
   if (mods & (KMOD_LSHIFT | KMOD_RSHIFT))
-    code |= static_cast<u32>(KMOD_LSHIFT) << 16;
+    code |= static_cast<u32>(KMOD_LSHIFT) << MODIFIER_SHIFT;
   if (mods & (KMOD_LCTRL | KMOD_RCTRL))
-    code |= static_cast<u32>(KMOD_LCTRL) << 16;
+    code |= static_cast<u32>(KMOD_LCTRL) << MODIFIER_SHIFT;
   if (mods & (KMOD_LALT | KMOD_RALT))
-    code |= static_cast<u32>(KMOD_LALT) << 16;
+    code |= static_cast<u32>(KMOD_LALT) << MODIFIER_SHIFT;
   if (mods & (KMOD_LGUI | KMOD_RGUI))
-    code |= static_cast<u32>(KMOD_LGUI) << 16;
+    code |= static_cast<u32>(KMOD_LGUI) << MODIFIER_SHIFT;
 
   return code;
 }
 
-bool KeyEventToString(const SDL_Event* event, String& out_string)
+static bool KeyEventToString(const SDL_Event* event, String& out_string)
 {
   const SDL_Keycode key = event->key.keysym.sym;
   const SDL_Keymod mods = static_cast<SDL_Keymod>(event->key.keysym.mod);
@@ -322,7 +329,7 @@ bool KeyEventToString(const SDL_Event* event, String& out_string)
   return true;
 }
 
-std::optional<u32> ParseKeyString(const std::string_view key_str)
+static std::optional<u32> ParseKeyString(const std::string_view key_str)
 {
   u32 modifiers = 0;
   std::string_view::size_type pos = 0;
@@ -356,6 +363,6 @@ std::optional<u32> ParseKeyString(const std::string_view key_str)
   if (!key_code)
     return std::nullopt;
 
-  return static_cast<u32>(key_code.value()) | (modifiers << 16);
+  return static_cast<u32>(key_code.value()) | (modifiers << MODIFIER_SHIFT);
 }
 } // namespace SDLKeyNames
diff --git a/src/duckstation-qt/qthostinterface.cpp b/src/duckstation-qt/qthostinterface.cpp
index 960862a7f..3bb17fe42 100644
--- a/src/duckstation-qt/qthostinterface.cpp
+++ b/src/duckstation-qt/qthostinterface.cpp
@@ -381,7 +381,7 @@ void QtHostInterface::onDisplayWindowKeyEvent(int key, bool pressed)
   if (masked_key < countof(ImGuiIO::KeysDown))
     ImGui::GetIO().KeysDown[masked_key] = pressed;
 
-  HandleHostKeyEvent(key, pressed);
+  HandleHostKeyEvent(key & ~Qt::KeyboardModifierMask, key & Qt::KeyboardModifierMask, pressed);
 }
 
 void QtHostInterface::onDisplayWindowMouseMoveEvent(int x, int y)
diff --git a/src/frontend-common/common_host_interface.cpp b/src/frontend-common/common_host_interface.cpp
index ec1dbb1a5..ae715f721 100644
--- a/src/frontend-common/common_host_interface.cpp
+++ b/src/frontend-common/common_host_interface.cpp
@@ -1267,11 +1267,15 @@ void CommonHostInterface::RegisterHotkey(String category, String name, String di
   m_hotkeys.push_back(HotkeyInfo{std::move(category), std::move(name), std::move(display_name), std::move(handler)});
 }
 
-bool CommonHostInterface::HandleHostKeyEvent(HostKeyCode key, bool pressed)
+bool CommonHostInterface::HandleHostKeyEvent(HostKeyCode code, HostKeyCode modifiers, bool pressed)
 {
-  const auto iter = m_keyboard_input_handlers.find(key);
+  auto iter = m_keyboard_input_handlers.find(code | modifiers);
   if (iter == m_keyboard_input_handlers.end())
-    return false;
+  {
+    // try without the modifier
+    if (modifiers == 0 || (iter = m_keyboard_input_handlers.find(code)) == m_keyboard_input_handlers.end())
+      return false;
+  }
 
   iter->second(pressed);
   return true;
diff --git a/src/frontend-common/common_host_interface.h b/src/frontend-common/common_host_interface.h
index 3390f8956..9d4d832ac 100644
--- a/src/frontend-common/common_host_interface.h
+++ b/src/frontend-common/common_host_interface.h
@@ -347,7 +347,7 @@ protected:
   virtual bool AddRumbleToInputMap(const std::string& binding, u32 controller_index, u32 num_motors);
 
   void RegisterHotkey(String category, String name, String display_name, InputButtonHandler handler);
-  bool HandleHostKeyEvent(HostKeyCode code, bool pressed);
+  bool HandleHostKeyEvent(HostKeyCode code, HostKeyCode modifiers, bool pressed);
   bool HandleHostMouseEvent(HostMouseButton button, bool pressed);
   void UpdateInputMap(SettingsInterface& si);
   void ClearInputMap();
@@ -492,8 +492,8 @@ private:
     u32 controller_index;
     u32 num_motors;
     std::array<float, MAX_MOTORS> last_strength;
-    ControllerRumbleCallback update_callback;
     u64 last_update_time;
+    ControllerRumbleCallback update_callback;
   };
   std::vector<ControllerRumbleState> m_controller_vibration_motors;