From 05e82c24f867eb90c3c49165d9abfa5802cdf63d Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Wed, 6 May 2020 18:58:43 -0700 Subject: [PATCH] Pass through focus-gain clicks to the host in absolute mouse mode --- app/streaming/input/input.cpp | 38 ++++++++++++++++++++++-- app/streaming/input/input.h | 5 ++++ app/streaming/input/mouse.cpp | 55 +++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/app/streaming/input/input.cpp b/app/streaming/input/input.cpp index 6a230bd5..1ecadced 100644 --- a/app/streaming/input/input.cpp +++ b/app/streaming/input/input.cpp @@ -19,6 +19,8 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer*, int s m_StreamHeight(streamHeight), m_AbsoluteMouseMode(prefs.absoluteMouseMode), m_AbsoluteTouchMode(prefs.absoluteTouchMode), + m_PendingFocusGain(false), + m_PendingFocusButtonUp(0), m_LeftButtonReleaseTimer(0), m_RightButtonReleaseTimer(0), m_DragTimer(0), @@ -203,12 +205,44 @@ void SdlInputHandler::notifyFocusGained() #if defined(Q_OS_WIN32) || defined(Q_OS_DARWIN) int mouseX, mouseY; Uint32 mouseState = SDL_GetGlobalMouseState(&mouseX, &mouseY); - if (mouseState & SDL_BUTTON(SDL_BUTTON_LEFT)) { + if (mouseState != 0) { int x, y, width, height; SDL_GetWindowPosition(m_Window, &x, &y); SDL_GetWindowSize(m_Window, &width, &height); if (mouseX > x && mouseX < x+width && mouseY > y && mouseY < y+height) { - setCaptureActive(true); + if (m_AbsoluteMouseMode) { + // We won't receive mouse events until the mouse gesture ends after gaining + // focus. To allow users to click directly into the unfocused window, we'll + // emulate mouse events using SDL_GetGlobalMouseState(). + m_PendingFocusGain = true; + + if (mouseState & SDL_BUTTON_LMASK) { + m_PendingFocusButtonUp = SDL_BUTTON_LEFT; + } + else if (mouseState & SDL_BUTTON_RMASK) { + m_PendingFocusButtonUp = SDL_BUTTON_RIGHT; + } + else if (mouseState & SDL_BUTTON_MMASK) { + m_PendingFocusButtonUp = SDL_BUTTON_MIDDLE; + } + else if (mouseState & SDL_BUTTON_X1MASK) { + m_PendingFocusButtonUp = SDL_BUTTON_X1; + } + else if (mouseState & SDL_BUTTON_X2MASK) { + m_PendingFocusButtonUp = SDL_BUTTON_X2; + } + else { + SDL_assert(false); + m_PendingFocusGain = false; + } + + // Update the mouse position then send the button down event + sendSyntheticMouseState(SDL_MOUSEMOTION, 0); + sendSyntheticMouseState(SDL_MOUSEBUTTONDOWN, m_PendingFocusButtonUp); + } + else if (mouseState & SDL_BUTTON_LMASK) { + setCaptureActive(true); + } } } #endif diff --git a/app/streaming/input/input.h b/app/streaming/input/input.h index 975d10b8..ca180061 100644 --- a/app/streaming/input/input.h +++ b/app/streaming/input/input.h @@ -77,6 +77,8 @@ public: void setCaptureActive(bool active); + void sendSyntheticMouseState(Uint32 state, Uint32 button); + static QString getUnmappedGamepads(); @@ -127,6 +129,9 @@ private: bool m_AbsoluteMouseMode; bool m_AbsoluteTouchMode; + bool m_PendingFocusGain; + Uint32 m_PendingFocusButtonUp; + SDL_TouchFingerEvent m_TouchDownEvent[MAX_FINGERS]; SDL_TimerID m_LeftButtonReleaseTimer; SDL_TimerID m_RightButtonReleaseTimer; diff --git a/app/streaming/input/mouse.cpp b/app/streaming/input/mouse.cpp index 923aa302..d5c2fae9 100644 --- a/app/streaming/input/mouse.cpp +++ b/app/streaming/input/mouse.cpp @@ -111,6 +111,46 @@ void SdlInputHandler::handleMouseWheelEvent(SDL_MouseWheelEvent* event) } } +void SdlInputHandler::sendSyntheticMouseState(Uint32 type, Uint32 button) { + int mouseX, mouseY; + int windowX, windowY; + SDL_Event event; + Uint32 buttonState = SDL_GetGlobalMouseState(&mouseX, &mouseY); + SDL_GetWindowPosition(m_Window, &windowX, &windowY); + + switch (type) { + case SDL_MOUSEMOTION: + event.motion.type = type; + event.motion.timestamp = SDL_GetTicks(); + event.motion.windowID = SDL_GetWindowID(m_Window); + event.motion.which = 0; + event.motion.state = buttonState; + event.motion.x = mouseX - windowX; + event.motion.y = mouseY - windowY; + event.motion.xrel = 0; + event.motion.yrel = 0; + break; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + event.button.type = type; + event.button.timestamp = SDL_GetTicks(); + event.button.windowID = SDL_GetWindowID(m_Window); + event.button.which = 0; + event.button.button = button; + event.button.state = type == SDL_MOUSEBUTTONDOWN ? SDL_PRESSED : SDL_RELEASED; + event.button.clicks = 1; + event.button.x = mouseX - windowX; + event.button.y = mouseY - windowY; + break; + + default: + SDL_assert(false); + } + + SDL_PushEvent(&event); +} + Uint32 SdlInputHandler::mouseMoveTimerCallback(Uint32 interval, void *param) { auto me = reinterpret_cast(param); @@ -122,5 +162,20 @@ Uint32 SdlInputHandler::mouseMoveTimerCallback(Uint32 interval, void *param) LiSendMouseMoveEvent(deltaX, deltaY); } + if (me->m_PendingFocusGain && me->m_AbsoluteMouseMode) { + Uint32 buttonState = SDL_GetGlobalMouseState(NULL, NULL); + + // Update the position first + me->sendSyntheticMouseState(SDL_MOUSEMOTION, 0); + + // If the button has come up since last time, send that too + if ((buttonState & SDL_BUTTON(me->m_PendingFocusButtonUp)) == 0) { + me->sendSyntheticMouseState(SDL_MOUSEBUTTONUP, me->m_PendingFocusButtonUp); + + // Focus gain has completed + me->m_PendingFocusGain = false; + } + } + return interval; }