From 2eb4769bbcd7690185eaaf7caeb5a9f7eee7776c Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 24 Jan 2021 13:02:42 -0600 Subject: [PATCH] Only trigger special key combos when all keys are up --- app/streaming/input/input.cpp | 38 +++++ app/streaming/input/input.h | 21 +++ app/streaming/input/keyboard.cpp | 282 ++++++++++++------------------- 3 files changed, 171 insertions(+), 170 deletions(-) diff --git a/app/streaming/input/input.cpp b/app/streaming/input/input.cpp index 1a6aaac1..1a9055f4 100644 --- a/app/streaming/input/input.cpp +++ b/app/streaming/input/input.cpp @@ -7,6 +7,7 @@ #include #include +#include #define MOUSE_POLLING_INTERVAL 5 @@ -26,6 +27,7 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s m_PendingMouseButtonsAllUpOnVideoRegionLeave(false), m_FakeCaptureActive(false), m_CaptureSystemKeysEnabled(prefs.captureSysKeys || !WMUtils::isRunningWindowManager()), + m_PendingKeyCombo(KeyComboMax), m_LongPressTimer(0), m_StreamWidth(streamWidth), m_StreamHeight(streamHeight), @@ -67,6 +69,42 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s SDL_SetHint("SDL_JOYSTICK_HIDAPI_PS4_RUMBLE", "1"); SDL_SetHint("SDL_JOYSTICK_HIDAPI_PS5_RUMBLE", "1"); + // Populate special key combo configuration + m_SpecialKeyCombos[KeyComboQuit].keyCombo = KeyComboQuit; + m_SpecialKeyCombos[KeyComboQuit].keyCode = SDLK_q; + m_SpecialKeyCombos[KeyComboQuit].scanCode = SDL_SCANCODE_Q; + m_SpecialKeyCombos[KeyComboQuit].enabled = true; + + m_SpecialKeyCombos[KeyComboUngrabInput].keyCombo = KeyComboUngrabInput; + m_SpecialKeyCombos[KeyComboUngrabInput].keyCode = SDLK_z; + m_SpecialKeyCombos[KeyComboUngrabInput].scanCode = SDL_SCANCODE_Z; + m_SpecialKeyCombos[KeyComboUngrabInput].enabled = QGuiApplication::platformName() != "eglfs"; + + m_SpecialKeyCombos[KeyComboToggleFullScreen].keyCombo = KeyComboToggleFullScreen; + m_SpecialKeyCombos[KeyComboToggleFullScreen].keyCode = SDLK_x; + m_SpecialKeyCombos[KeyComboToggleFullScreen].scanCode = SDL_SCANCODE_X; + m_SpecialKeyCombos[KeyComboToggleFullScreen].enabled = QGuiApplication::platformName() != "eglfs"; + + m_SpecialKeyCombos[KeyComboToggleStatsOverlay].keyCombo = KeyComboToggleStatsOverlay; + m_SpecialKeyCombos[KeyComboToggleStatsOverlay].keyCode = SDLK_s; + m_SpecialKeyCombos[KeyComboToggleStatsOverlay].scanCode = SDL_SCANCODE_S; + m_SpecialKeyCombos[KeyComboToggleStatsOverlay].enabled = true; + + m_SpecialKeyCombos[KeyComboToggleMouseMode].keyCombo = KeyComboToggleMouseMode; + m_SpecialKeyCombos[KeyComboToggleMouseMode].keyCode = SDLK_m; + m_SpecialKeyCombos[KeyComboToggleMouseMode].scanCode = SDL_SCANCODE_M; + m_SpecialKeyCombos[KeyComboToggleMouseMode].enabled = QGuiApplication::platformName() != "eglfs"; + + m_SpecialKeyCombos[KeyComboToggleCursorHide].keyCombo = KeyComboToggleCursorHide; + m_SpecialKeyCombos[KeyComboToggleCursorHide].keyCode = SDLK_c; + m_SpecialKeyCombos[KeyComboToggleCursorHide].scanCode = SDL_SCANCODE_C; + m_SpecialKeyCombos[KeyComboToggleCursorHide].enabled = true; + + m_SpecialKeyCombos[KeyComboToggleMinimize].keyCombo = KeyComboToggleMinimize; + m_SpecialKeyCombos[KeyComboToggleMinimize].keyCode = SDLK_d; + m_SpecialKeyCombos[KeyComboToggleMinimize].scanCode = SDL_SCANCODE_D; + m_SpecialKeyCombos[KeyComboToggleMinimize].enabled = QGuiApplication::platformName() != "eglfs"; + m_OldIgnoreDevices = SDL_GetHint(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES); m_OldIgnoreDevicesExcept = SDL_GetHint(SDL_HINT_GAMECONTROLLER_IGNORE_DEVICES_EXCEPT); diff --git a/app/streaming/input/input.h b/app/streaming/input/input.h index d2919dce..98e0532d 100644 --- a/app/streaming/input/input.h +++ b/app/streaming/input/input.h @@ -97,6 +97,17 @@ public: QString getUnmappedGamepads(); private: + enum KeyCombo { + KeyComboQuit, + KeyComboUngrabInput, + KeyComboToggleFullScreen, + KeyComboToggleStatsOverlay, + KeyComboToggleMouseMode, + KeyComboToggleCursorHide, + KeyComboToggleMinimize, + KeyComboMax + }; + GamepadState* findStateForGamepad(SDL_JoystickID id); @@ -106,6 +117,8 @@ private: void handleRelativeFingerEvent(SDL_TouchFingerEvent* event); + void performPendingSpecialKeyCombo(); + static Uint32 longPressTimerCallback(Uint32 interval, void* param); @@ -156,6 +169,14 @@ private: QString m_OldIgnoreDevicesExcept; bool m_CaptureSystemKeysEnabled; + struct { + KeyCombo keyCombo; + SDL_Keycode keyCode; + SDL_Scancode scanCode; + bool enabled; + } m_SpecialKeyCombos[KeyComboMax]; + KeyCombo m_PendingKeyCombo; + SDL_TouchFingerEvent m_LastTouchDownEvent; SDL_TouchFingerEvent m_LastTouchUpEvent; SDL_TimerID m_LongPressTimer; diff --git a/app/streaming/input/keyboard.cpp b/app/streaming/input/keyboard.cpp index 6c182400..926a9de8 100644 --- a/app/streaming/input/keyboard.cpp +++ b/app/streaming/input/keyboard.cpp @@ -3,8 +3,6 @@ #include #include -#include - #define VK_0 0x30 #define VK_A 0x41 @@ -15,6 +13,85 @@ #define VK_NUMPAD0 0x60 #endif +void SdlInputHandler::performPendingSpecialKeyCombo() +{ + // The caller must ensure all keys are up + Q_ASSERT(m_KeysDown.isEmpty()); + + switch (m_PendingKeyCombo) { + case KeyComboQuit: + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Detected quit key combo"); + + // Push a quit event to the main loop + SDL_Event event; + event.type = SDL_QUIT; + event.quit.timestamp = SDL_GetTicks(); + SDL_PushEvent(&event); + break; + + case KeyComboUngrabInput: + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Detected mouse capture toggle combo"); + + // Stop handling future input + setCaptureActive(!isCaptureActive()); + break; + + case KeyComboToggleFullScreen: + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Detected full-screen toggle combo"); + Session::s_ActiveSession->toggleFullscreen(); + break; + + case KeyComboToggleStatsOverlay: + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Detected stats toggle combo"); + + // Toggle the stats overlay + Session::get()->getOverlayManager().setOverlayState(Overlay::OverlayDebug, + !Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayDebug)); + break; + + case KeyComboToggleMouseMode: + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Detected mouse mode toggle combo"); + + // Uncapture input + setCaptureActive(false); + + // Toggle mouse mode + m_AbsoluteMouseMode = !m_AbsoluteMouseMode; + + // Recapture input + setCaptureActive(true); + break; + + case KeyComboToggleCursorHide: + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Detected show mouse combo"); + + if (!SDL_GetRelativeMouseMode()) { + SDL_ShowCursor(!SDL_ShowCursor(SDL_QUERY)); + } + else { + SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, + "Cursor can only be shown in remote desktop mouse mode"); + } + break; + + case KeyComboToggleMinimize: + SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, + "Detected minimize combo"); + + SDL_MinimizeWindow(m_Window); + break; + } + + // Reset pending key combo + m_PendingKeyCombo = KeyComboMax; +} + void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event) { short keyCode; @@ -36,178 +113,28 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event) // where the SDLK for one shortcut collides with // the scancode of another. - // Check for quit combo (Ctrl+Alt+Shift+Q) - if (event->keysym.sym == SDLK_q) { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected quit key combo (SDLK)"); - - // Push a quit event to the main loop - SDL_Event event; - event.type = SDL_QUIT; - event.quit.timestamp = SDL_GetTicks(); - SDL_PushEvent(&event); - return; - } - // Check for the unbind combo (Ctrl+Alt+Shift+Z) unless on EGLFS which has no window manager - else if (event->keysym.sym == SDLK_z && QGuiApplication::platformName() != "eglfs") { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected mouse capture toggle combo (SDLK)"); - - // Stop handling future input - setCaptureActive(!isCaptureActive()); - - // Force raise all keys to ensure they aren't stuck, - // since we won't get their key up events. - raiseAllKeys(); - return; - } - // Check for the mouse mode combo (Ctrl+Alt+Shift+M) unless on EGLFS which has no window manager - else if (event->keysym.sym == SDLK_m && QGuiApplication::platformName() != "eglfs") { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected mouse mode toggle combo (SDLK)"); - - // Uncapture input - setCaptureActive(false); - - // Toggle mouse mode - m_AbsoluteMouseMode = !m_AbsoluteMouseMode; - - // Recapture input - setCaptureActive(true); - return; - } - else if (event->keysym.sym == SDLK_x && QGuiApplication::platformName() != "eglfs") { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected full-screen toggle combo (SDLK)"); - 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; - } - // Check for the mouse mode toggle combo (Ctrl+Alt+Shift+D) unless on EGLFS which has no window manager - else if (event->keysym.sym == SDLK_d && QGuiApplication::platformName() != "eglfs") { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected minimize combo (SDLK)"); - - SDL_MinimizeWindow(m_Window); - return; - } - // Check for overlay combo (Ctrl+Alt+Shift+S) - else if (event->keysym.sym == SDLK_s) { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected stats toggle combo (SDLK)"); - - // Toggle the stats overlay - Session::get()->getOverlayManager().setOverlayState(Overlay::OverlayDebug, - !Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayDebug)); - - // Force raise all keys just be safe across this full-screen/windowed - // transition just in case key events get lost. - raiseAllKeys(); - return; - } - // Check for mouse show combo (Ctrl+Alt+Shift+C) - else if (event->keysym.sym == SDLK_c) { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected show mouse combo (SDLK)"); - - if (!SDL_GetRelativeMouseMode()) { - SDL_ShowCursor(!SDL_ShowCursor(SDL_QUERY)); + if (m_PendingKeyCombo == KeyComboMax) { + for (int i = 0; i < KeyComboMax; i++) { + if (m_SpecialKeyCombos[i].enabled && event->keysym.sym == m_SpecialKeyCombos[i].keyCode) { + m_PendingKeyCombo = m_SpecialKeyCombos[i].keyCombo; + break; + } } - else { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Cursor can only be shown in remote desktop mouse mode"); + } + + if (m_PendingKeyCombo == KeyComboMax) { + for (int i = 0; i < KeyComboMax; i++) { + if (m_SpecialKeyCombos[i].enabled && event->keysym.scancode == m_SpecialKeyCombos[i].scanCode) { + m_PendingKeyCombo = m_SpecialKeyCombos[i].keyCombo; + break; + } } - return; } - // Check for quit combo (Ctrl+Alt+Shift+Q) - else if (event->keysym.scancode == SDL_SCANCODE_Q) { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected quit key combo (scancode)"); + } - // Push a quit event to the main loop - SDL_Event event; - event.type = SDL_QUIT; - event.quit.timestamp = SDL_GetTicks(); - SDL_PushEvent(&event); - return; - } - // Check for the unbind combo (Ctrl+Alt+Shift+Z) - else if (event->keysym.scancode == SDL_SCANCODE_Z && QGuiApplication::platformName() != "eglfs") { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected mouse capture toggle combo (scancode)"); - - // Stop handling future input - setCaptureActive(!isCaptureActive()); - - // 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) unless on EGLFS which has no window manager - else if (event->keysym.scancode == SDL_SCANCODE_X && QGuiApplication::platformName() != "eglfs") { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected full-screen toggle combo (scancode)"); - 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; - } - // Check for the mouse mode toggle combo (Ctrl+Alt+Shift+M) unless on EGLFS which has no window manager - else if (event->keysym.scancode == SDL_SCANCODE_M && QGuiApplication::platformName() != "eglfs") { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected mouse mode toggle combo (scancode)"); - - // Uncapture input - setCaptureActive(false); - - // Toggle mouse mode - m_AbsoluteMouseMode = !m_AbsoluteMouseMode; - - // Recapture input - setCaptureActive(true); - return; - } - // Check for the mouse mode toggle combo (Ctrl+Alt+Shift+D) unless on EGLFS which has no window manager - else if (event->keysym.scancode == SDL_SCANCODE_D && QGuiApplication::platformName() != "eglfs") { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected minimize combo (scancode)"); - - SDL_MinimizeWindow(m_Window); - return; - } - else if (event->keysym.scancode == SDL_SCANCODE_S) { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected stats toggle combo (scancode)"); - - // Toggle the stats overlay - Session::get()->getOverlayManager().setOverlayState(Overlay::OverlayDebug, - !Session::get()->getOverlayManager().isOverlayEnabled(Overlay::OverlayDebug)); - - // Force raise all keys just be safe across this full-screen/windowed - // transition just in case key events get lost. - raiseAllKeys(); - return; - } - // Check for mouse show combo (Ctrl+Alt+Shift+C) - else if (event->keysym.sym == SDL_SCANCODE_C) { - SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, - "Detected show mouse combo (scancode)"); - - if (!SDL_GetRelativeMouseMode()) { - SDL_ShowCursor(!SDL_ShowCursor(SDL_QUERY)); - } - else { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Cursor can only be shown in remote desktop mouse mode"); - } - return; - } + if (event->state == SDL_PRESSED && m_PendingKeyCombo != KeyComboMax) { + // Ignore further key presses until the special combo is raised + return; } if (event->repeat) { @@ -461,4 +388,19 @@ void SdlInputHandler::handleKeyEvent(SDL_KeyboardEvent* event) event->state == SDL_PRESSED ? KEY_ACTION_DOWN : KEY_ACTION_UP, modifiers); + + if (m_PendingKeyCombo != KeyComboMax && m_KeysDown.isEmpty()) { + int keys; + const Uint8 *keyState = SDL_GetKeyboardState(&keys); + + // Make sure all client keys are up before we process the special key combo + for (int i = 0; i < keys; i++) { + if (keyState[i] == SDL_PRESSED) { + return; + } + } + + // If we made it this far, no keys are pressed + performPendingSpecialKeyCombo(); + } }