From 0428bc8fe3ce86444cf4f486078a44e72ec7d218 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 7 May 2020 19:26:02 -0700 Subject: [PATCH] Generate synthetic mouse button up if the mouse has left the window and the button is raised --- app/streaming/input/input.cpp | 19 +++++++++++++++++++ app/streaming/input/input.h | 3 +++ app/streaming/input/mouse.cpp | 28 ++++++++++++++++++++++++++++ app/streaming/session.cpp | 3 +++ 4 files changed, 53 insertions(+) diff --git a/app/streaming/input/input.cpp b/app/streaming/input/input.cpp index 8f563108..c837c167 100644 --- a/app/streaming/input/input.cpp +++ b/app/streaming/input/input.cpp @@ -19,6 +19,7 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s m_StreamHeight(streamHeight), m_AbsoluteMouseMode(prefs.absoluteMouseMode), m_AbsoluteTouchMode(prefs.absoluteTouchMode), + m_PendingMouseLeaveButtonUp(0), m_LeftButtonReleaseTimer(0), m_RightButtonReleaseTimer(0), m_DragTimer(0), @@ -192,6 +193,24 @@ void SdlInputHandler::raiseAllKeys() m_KeysDown.clear(); } +void SdlInputHandler::notifyMouseLeave() +{ +#ifdef Q_OS_WIN32 + // SDL on Windows doesn't send the mouse button up until the mouse re-enters the window + // after leaving it. This breaks some of the Aero snap gestures, so we'll fake it here. + if (m_AbsoluteMouseMode && isCaptureActive()) { + // NB: Not using SDL_GetGlobalMouseState() because we want our state not the system's + Uint32 mouseState = SDL_GetMouseState(nullptr, nullptr); + for (Uint32 button = SDL_BUTTON_LEFT; button <= SDL_BUTTON_X2; button++) { + if (mouseState & SDL_BUTTON(button)) { + m_PendingMouseLeaveButtonUp = button; + break; + } + } + } +#endif +} + void SdlInputHandler::notifyFocusLost() { // Release mouse cursor when another window is activated (e.g. by using ALT+TAB). diff --git a/app/streaming/input/input.h b/app/streaming/input/input.h index e861bf51..5078515f 100644 --- a/app/streaming/input/input.h +++ b/app/streaming/input/input.h @@ -69,6 +69,8 @@ public: void raiseAllKeys(); + void notifyMouseLeave(); + void notifyFocusLost(); bool isCaptureActive(); @@ -124,6 +126,7 @@ private: int m_StreamHeight; bool m_AbsoluteMouseMode; bool m_AbsoluteTouchMode; + Uint32 m_PendingMouseLeaveButtonUp; SDL_TouchFingerEvent m_TouchDownEvent[MAX_FINGERS]; SDL_TimerID m_LeftButtonReleaseTimer; diff --git a/app/streaming/input/mouse.cpp b/app/streaming/input/mouse.cpp index 923aa302..98502287 100644 --- a/app/streaming/input/mouse.cpp +++ b/app/streaming/input/mouse.cpp @@ -122,5 +122,33 @@ Uint32 SdlInputHandler::mouseMoveTimerCallback(Uint32 interval, void *param) LiSendMouseMoveEvent(deltaX, deltaY); } +#ifdef Q_OS_WIN32 + // See comment in SdlInputHandler::notifyMouseLeave() + if (me->m_AbsoluteMouseMode && me->m_PendingMouseLeaveButtonUp != 0 && me->isCaptureActive()) { + int mouseX, mouseY; + int windowX, windowY; + Uint32 mouseState = SDL_GetGlobalMouseState(&mouseX, &mouseY); + SDL_GetWindowPosition(me->m_Window, &windowX, &windowY); + + // If the button is now up, send the synthetic mouse up event + if ((mouseState & SDL_BUTTON(me->m_PendingMouseLeaveButtonUp)) == 0) { + SDL_Event event; + + event.button.type = SDL_MOUSEBUTTONUP; + event.button.timestamp = SDL_GetTicks(); + event.button.windowID = SDL_GetWindowID(me->m_Window); + event.button.which = 0; + event.button.button = me->m_PendingMouseLeaveButtonUp; + event.button.state = SDL_RELEASED; + event.button.clicks = 1; + event.button.x = mouseX - windowX; + event.button.y = mouseY - windowY; + SDL_PushEvent(&event); + + me->m_PendingMouseLeaveButtonUp = 0; + } + } +#endif + return interval; } diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index fb15373d..2f64e74c 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -1221,6 +1221,9 @@ void Session::exec(int displayOriginX, int displayOriginY) if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { m_InputHandler->notifyFocusLost(); } + else if (event.window.event == SDL_WINDOWEVENT_LEAVE) { + m_InputHandler->notifyMouseLeave(); + } // Capture the mouse on SDL_WINDOWEVENT_ENTER if needed if (needsFirstEnterCapture && event.window.event == SDL_WINDOWEVENT_ENTER) {