From 6661ca17c224860c567d0bd7cb14acae1aa200c8 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 3 Oct 2018 18:27:12 -0700 Subject: [PATCH] Fix keys being stuck after Moonlight loses focus or is quit via OS shortcut (Alt+Tab/Alt+F4) --- app/streaming/input.cpp | 49 ++++++++++++++++++++++++++++----------- app/streaming/input.h | 3 +++ app/streaming/session.cpp | 23 ++++++++++++------ 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/app/streaming/input.cpp b/app/streaming/input.cpp index 88ec60a3..358b5a86 100644 --- a/app/streaming/input.cpp +++ b/app/streaming/input.cpp @@ -121,25 +121,11 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event) (event->keysym.mod & KMOD_CTRL) && (event->keysym.mod & KMOD_ALT) && (event->keysym.mod & KMOD_SHIFT)) { - - // Force raise all keys in the combo to avoid - // leaving them down after disconnecting - LiSendKeyboardEvent(0xA0, KEY_ACTION_UP, 0); - LiSendKeyboardEvent(0xA1, KEY_ACTION_UP, 0); - LiSendKeyboardEvent(0xA2, KEY_ACTION_UP, 0); - LiSendKeyboardEvent(0xA3, KEY_ACTION_UP, 0); - LiSendKeyboardEvent(0xA4, KEY_ACTION_UP, 0); - LiSendKeyboardEvent(0xA5, KEY_ACTION_UP, 0); - // Check for quit combo (Ctrl+Alt+Shift+Q) if (event->keysym.sym == SDLK_q) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Detected quit key combo"); - // Uncapture the mouse to avoid processing any - // further keyboard input - SDL_SetRelativeMouseMode(SDL_FALSE); - // Push a quit event to the main loop SDL_Event event; event.type = SDL_QUIT; @@ -151,7 +137,13 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event) else if (event->keysym.sym == SDLK_z) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Detected mouse capture toggle combo"); + + // Stop handling future input SDL_SetRelativeMouseMode((SDL_bool)!SDL_GetRelativeMouseMode()); + + // Force raise all keys to ensure they aren't stuck, + // since we won't get their key up events. + raiseAllKeys(); return; } // Check for the full-screen combo (Ctrl+Alt+Shift+X) @@ -159,6 +151,10 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event) SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Detected full-screen toggle combo"); Session::s_ActiveSession->toggleFullscreen(); + + // Force raise all keys just be safe across this full-screen/windowed + // transition just in case key events get lost. + raiseAllKeys(); return; } } @@ -384,6 +380,14 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event) } } + // Track the key state so we always know which keys are down + if (event->state == SDL_PRESSED) { + m_KeysDown.insert(keyCode); + } + else { + m_KeysDown.remove(keyCode); + } + LiSendKeyboardEvent(keyCode, event->state == SDL_PRESSED ? KEY_ACTION_DOWN : KEY_ACTION_UP, @@ -914,6 +918,23 @@ int SdlInputHandler::getAttachedGamepadMask() return mask; } +void SdlInputHandler::raiseAllKeys() +{ + if (m_KeysDown.isEmpty()) { + return; + } + + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Raising %d keys", + m_KeysDown.count()); + + for (auto keyDown : m_KeysDown) { + LiSendKeyboardEvent(keyDown, KEY_ACTION_UP, 0); + } + + m_KeysDown.clear(); +} + QString SdlInputHandler::getUnmappedGamepads() { QString ret; diff --git a/app/streaming/input.h b/app/streaming/input.h index 1884ee67..a3c38274 100644 --- a/app/streaming/input.h +++ b/app/streaming/input.h @@ -47,6 +47,8 @@ public: int getAttachedGamepadMask(); + void raiseAllKeys(); + static QString getUnmappedGamepads(); @@ -70,6 +72,7 @@ private: bool m_NeedsInputDelay; int m_GamepadMask; GamepadState m_GamepadState[MAX_GAMEPADS]; + QSet m_KeysDown; SDL_TouchFingerEvent m_TouchDownEvent[MAX_FINGERS]; float m_CumulativeDelta[MAX_FINGERS]; diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index 93eadf6a..c1fab60b 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -992,13 +992,19 @@ void Session::exec(int displayOriginX, int displayOriginY) } #endif - // Release mouse cursor when another window is activated (e.g. by using ALT+TAB). - // This lets user to interact with our window's title bar and with the buttons in it. - // Doing this while the window is full-screen breaks the transition out of FS - // (desktop and exclusive), so we must check for that before releasing mouse capture. - if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST && - !(SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN)) { - SDL_SetRelativeMouseMode(SDL_FALSE); + + if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { + // Release mouse cursor when another window is activated (e.g. by using ALT+TAB). + // This lets user to interact with our window's title bar and with the buttons in it. + // Doing this while the window is full-screen breaks the transition out of FS + // (desktop and exclusive), so we must check for that before releasing mouse capture. + if (!(SDL_GetWindowFlags(m_Window) & SDL_WINDOW_FULLSCREEN)) { + SDL_SetRelativeMouseMode(SDL_FALSE); + } + + // Raise all keys that are currently pressed. If we don't do this, certain keys + // used in shortcuts that cause focus loss (such as Alt+Tab) may get stuck down. + inputHandler.raiseAllKeys(); } // We want to recreate the decoder for resizes (full-screen toggles) and the initial shown event. @@ -1139,6 +1145,9 @@ DispatchDeferredCleanup: SDL_EnableScreenSaver(); SDL_SetHint(SDL_HINT_TIMER_RESOLUTION, "0"); + // Raise any keys that are still down + inputHandler.raiseAllKeys(); + // Destroy the decoder, since this must be done on the main thread SDL_AtomicLock(&m_DecoderLock); delete m_VideoDecoder;