diff --git a/app/streaming/input/input.cpp b/app/streaming/input/input.cpp index 7c61dc20..a8725e48 100644 --- a/app/streaming/input/input.cpp +++ b/app/streaming/input/input.cpp @@ -9,17 +9,12 @@ #include #include -#define MOUSE_POLLING_INTERVAL 5 - SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer* computer, int streamWidth, int streamHeight) : m_MultiController(prefs.multiController), m_GamepadMouse(prefs.gamepadMouse), m_SwapMouseButtons(prefs.swapMouseButtons), m_ReverseScrollDirection(prefs.reverseScrollDirection), m_SwapFaceButtons(prefs.swapFaceButtons), - m_BatchMouseMotion(computer->isNvidiaServerSoftware), - m_MouseMoveTimer(0), - m_MousePositionLock(0), m_MouseWasInVideoRegion(false), m_PendingMouseButtonsAllUpOnVideoRegionLeave(false), m_PointerRegionLockActive(false), @@ -188,25 +183,6 @@ SdlInputHandler::SdlInputHandler(StreamingPreferences& prefs, NvComputer* comput SDL_zero(m_LastTouchDownEvent); SDL_zero(m_LastTouchUpEvent); SDL_zero(m_TouchDownEvent); - SDL_zero(m_MousePositionReport); - - SDL_AtomicSet(&m_MouseDeltaX, 0); - SDL_AtomicSet(&m_MouseDeltaY, 0); - SDL_AtomicSet(&m_MousePositionUpdated, 0); - - if (m_BatchMouseMotion) { - Uint32 pollingInterval = QString(qgetenv("MOUSE_POLLING_INTERVAL")).toUInt(); - if (pollingInterval == 0) { - pollingInterval = MOUSE_POLLING_INTERVAL; - } - else { - SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, - "Using custom mouse polling interval: %u ms", - pollingInterval); - } - - m_MouseMoveTimer = SDL_AddTimer(pollingInterval, SdlInputHandler::mouseMoveTimerCallback, this); - } } SdlInputHandler::~SdlInputHandler() @@ -226,7 +202,6 @@ SdlInputHandler::~SdlInputHandler() } } - SDL_RemoveTimer(m_MouseMoveTimer); SDL_RemoveTimer(m_LongPressTimer); SDL_RemoveTimer(m_LeftButtonReleaseTimer); SDL_RemoveTimer(m_RightButtonReleaseTimer); @@ -405,7 +380,14 @@ void SdlInputHandler::setCaptureActive(bool active) mouseY -= windowY; if (isMouseInVideoRegion(mouseX, mouseY)) { - updateMousePositionReport(mouseX, mouseY); + // Synthesize a mouse event to synchronize the cursor + SDL_MouseMotionEvent motionEvent = {}; + motionEvent.type = SDL_MOUSEMOTION; + motionEvent.timestamp = SDL_GetTicks(); + motionEvent.windowID = SDL_GetWindowID(m_Window); + motionEvent.x = mouseX; + motionEvent.y = mouseY; + handleMouseMotionEvent(&motionEvent); } } } diff --git a/app/streaming/input/input.h b/app/streaming/input/input.h index ca46bca7..5e8ce9cb 100644 --- a/app/streaming/input/input.h +++ b/app/streaming/input/input.h @@ -5,10 +5,6 @@ #include -#define SDL_CODE_HIDE_CURSOR 1 -#define SDL_CODE_SHOW_CURSOR 2 -#define SDL_CODE_UNCAPTURE_MOUSE 3 - struct GamepadState { SDL_GameController* controller; SDL_JoystickID jsId; @@ -87,10 +83,6 @@ public: bool isMouseInVideoRegion(int mouseX, int mouseY, int windowWidth = -1, int windowHeight = -1); - void updateMousePositionReport(int mouseX, int mouseY); - - void flushMousePositionUpdate(); - void updateKeyboardGrabState(); void updatePointerRegionLock(); @@ -126,9 +118,6 @@ private: static Uint32 longPressTimerCallback(Uint32 interval, void* param); - static - Uint32 mouseMoveTimerCallback(Uint32 interval, void* param); - static Uint32 mouseEmulationTimerCallback(Uint32 interval, void* param); @@ -147,17 +136,7 @@ private: bool m_SwapMouseButtons; bool m_ReverseScrollDirection; bool m_SwapFaceButtons; - bool m_BatchMouseMotion; - SDL_TimerID m_MouseMoveTimer; - SDL_atomic_t m_MouseDeltaX; - SDL_atomic_t m_MouseDeltaY; - SDL_SpinLock m_MousePositionLock; - struct { - int x, y; - int windowWidth, windowHeight; - } m_MousePositionReport; - SDL_atomic_t m_MousePositionUpdated; bool m_MouseWasInVideoRegion; bool m_PendingMouseButtonsAllUpOnVideoRegionLeave; bool m_PointerRegionLockActive; diff --git a/app/streaming/input/mouse.cpp b/app/streaming/input/mouse.cpp index ac3c58d4..5e90e2dc 100644 --- a/app/streaming/input/mouse.cpp +++ b/app/streaming/input/mouse.cpp @@ -62,114 +62,12 @@ void SdlInputHandler::handleMouseButtonEvent(SDL_MouseButtonEvent* event) button = BUTTON_RIGHT; } - // Ensure any pending mouse position update has been sent before we send - // our button event. This ensures that the cursor is in the correct location - // when the button event is issued. - // - // On platforms like macOS, the mouse doesn't track when the window isn't - // focused. When we gain focus via mouse click, we immediately get a mouse - // move event and a mouse button event. If we don't flush here, the button - // will probably arrive before the mouse timer issues the position update. - flushMousePositionUpdate(); - LiSendMouseButtonEvent(event->state == SDL_PRESSED ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE, button); } -void SdlInputHandler::updateMousePositionReport(int mouseX, int mouseY) -{ - int windowWidth, windowHeight; - - // Call SDL_GetWindowSize() before entering the spinlock - SDL_GetWindowSize(m_Window, &windowWidth, &windowHeight); - - SDL_AtomicLock(&m_MousePositionLock); - m_MousePositionReport.x = mouseX; - m_MousePositionReport.y = mouseY; - m_MousePositionReport.windowWidth = windowWidth; - m_MousePositionReport.windowHeight = windowHeight; - SDL_AtomicUnlock(&m_MousePositionLock); - SDL_AtomicSet(&m_MousePositionUpdated, 1); -} - -void SdlInputHandler::flushMousePositionUpdate() -{ - bool hasNewPosition = SDL_AtomicSet(&m_MousePositionUpdated, 0) != 0; - if (hasNewPosition) { - // If the lock is held now, the main thread is trying to update - // the mouse position. We'll pick up the new position next time. - if (SDL_AtomicTryLock(&m_MousePositionLock)) { - SDL_Rect src, dst; - bool mouseInVideoRegion; - - src.x = src.y = 0; - src.w = m_StreamWidth; - src.h = m_StreamHeight; - - dst.x = dst.y = 0; - dst.w = m_MousePositionReport.windowWidth; - dst.h = m_MousePositionReport.windowHeight; - - // Use the stream and window sizes to determine the video region - StreamUtils::scaleSourceToDestinationSurface(&src, &dst); - - mouseInVideoRegion = isMouseInVideoRegion(m_MousePositionReport.x, - m_MousePositionReport.y, - m_MousePositionReport.windowWidth, - m_MousePositionReport.windowHeight); - - // Clamp motion to the video region - short x = qMin(qMax(m_MousePositionReport.x - dst.x, 0), dst.w); - short y = qMin(qMax(m_MousePositionReport.y - dst.y, 0), dst.h); - - // Release the spinlock to unblock the main thread - SDL_AtomicUnlock(&m_MousePositionLock); - - // Send the mouse position update if one of the following is true: - // a) it is in the video region now - // b) it just left the video region (to ensure the mouse is clamped to the video boundary) - // c) a mouse button is still down from before the cursor left the video region (to allow smooth dragging) - Uint32 buttonState = SDL_GetMouseState(nullptr, nullptr); - if (buttonState == 0) { - if (m_PendingMouseButtonsAllUpOnVideoRegionLeave) { - // Tell the main thread to stop capturing the mouse now - SDL_Event event; - event.type = SDL_USEREVENT; - event.user.code = SDL_CODE_UNCAPTURE_MOUSE; - SDL_PushEvent(&event); - - m_PendingMouseButtonsAllUpOnVideoRegionLeave = false; - } - } - if (mouseInVideoRegion || m_MouseWasInVideoRegion || m_PendingMouseButtonsAllUpOnVideoRegionLeave) { - LiSendMousePositionEvent(x, y, dst.w, dst.h); - } - - // Adjust the cursor visibility if applicable - if (mouseInVideoRegion ^ m_MouseWasInVideoRegion) { - // We must push an event for the main thread to process, because it's not safe - // to directly can SDL_ShowCursor() on the arbitrary thread on which this timer - // executes. - SDL_Event event; - event.type = SDL_USEREVENT; - event.user.code = (mouseInVideoRegion && m_MouseCursorCapturedVisibilityState == SDL_DISABLE) ? - SDL_CODE_HIDE_CURSOR : SDL_CODE_SHOW_CURSOR; - SDL_PushEvent(&event); - - if (!mouseInVideoRegion && buttonState != 0) { - // If we still have a button pressed on leave, wait for that to come up - // before we stop sending mouse position events. - m_PendingMouseButtonsAllUpOnVideoRegionLeave = true; - } - } - - m_MouseWasInVideoRegion = mouseInVideoRegion; - } - } -} - void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event) { if (!isCaptureActive()) { @@ -181,26 +79,63 @@ void SdlInputHandler::handleMouseMotionEvent(SDL_MouseMotionEvent* event) return; } - if (m_BatchMouseMotion) { - // Batch until the next mouse polling window or we'll get awful - // input lag everything except GFE 3.14 and 3.15. - if (m_AbsoluteMouseMode) { - updateMousePositionReport(event->x, event->y); + if (m_AbsoluteMouseMode) { + int windowWidth, windowHeight; + SDL_GetWindowSize(m_Window, &windowWidth, &windowHeight); + + SDL_Rect src, dst; + bool mouseInVideoRegion; + + src.x = src.y = 0; + src.w = m_StreamWidth; + src.h = m_StreamHeight; + + dst.x = dst.y = 0; + dst.w = windowWidth; + dst.h = windowHeight; + + // Use the stream and window sizes to determine the video region + StreamUtils::scaleSourceToDestinationSurface(&src, &dst); + + mouseInVideoRegion = isMouseInVideoRegion(event->x, + event->y, + windowWidth, + windowHeight); + + // Clamp motion to the video region + short x = qMin(qMax(event->x - dst.x, 0), dst.w); + short y = qMin(qMax(event->y - dst.y, 0), dst.h); + + // Send the mouse position update if one of the following is true: + // a) it is in the video region now + // b) it just left the video region (to ensure the mouse is clamped to the video boundary) + // c) a mouse button is still down from before the cursor left the video region (to allow smooth dragging) + Uint32 buttonState = SDL_GetMouseState(nullptr, nullptr); + if (buttonState == 0) { + if (m_PendingMouseButtonsAllUpOnVideoRegionLeave) { + // Stop capturing the mouse now + SDL_CaptureMouse(SDL_FALSE); + m_PendingMouseButtonsAllUpOnVideoRegionLeave = false; + } } - else { - SDL_AtomicAdd(&m_MouseDeltaX, event->xrel); - SDL_AtomicAdd(&m_MouseDeltaY, event->yrel); + if (mouseInVideoRegion || m_MouseWasInVideoRegion || m_PendingMouseButtonsAllUpOnVideoRegionLeave) { + LiSendMousePositionEvent(x, y, dst.w, dst.h); } + + // Adjust the cursor visibility if applicable + if (mouseInVideoRegion ^ m_MouseWasInVideoRegion) { + SDL_ShowCursor((mouseInVideoRegion && m_MouseCursorCapturedVisibilityState == SDL_DISABLE) ? SDL_DISABLE : SDL_ENABLE); + if (!mouseInVideoRegion && buttonState != 0) { + // If we still have a button pressed on leave, wait for that to come up + // before we stop sending mouse position events. + m_PendingMouseButtonsAllUpOnVideoRegionLeave = true; + } + } + + m_MouseWasInVideoRegion = mouseInVideoRegion; } else { - // On Sunshine, we can send input immediately - if (m_AbsoluteMouseMode) { - updateMousePositionReport(event->x, event->y); - flushMousePositionUpdate(); - } - else { - LiSendMouseMoveEvent(event->xrel, event->yrel); - } + LiSendMouseMoveEvent(event->xrel, event->yrel); } } @@ -308,23 +243,6 @@ bool SdlInputHandler::isMouseInVideoRegion(int mouseX, int mouseY, int windowWid (mouseY >= dst.y && mouseY <= dst.y + dst.h); } -Uint32 SdlInputHandler::mouseMoveTimerCallback(Uint32 interval, void *param) -{ - auto me = reinterpret_cast(param); - - short deltaX = (short)SDL_AtomicSet(&me->m_MouseDeltaX, 0); - short deltaY = (short)SDL_AtomicSet(&me->m_MouseDeltaY, 0); - - if (deltaX != 0 || deltaY != 0) { - LiSendMouseMoveEvent(deltaX, deltaY); - } - - // Send mouse position updates if applicable - me->flushMousePositionUpdate(); - - return interval; -} - void SdlInputHandler::updatePointerRegionLock() { // Pointer region lock is irrelevant in relative mouse mode diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index d4d355f9..8b93b8a3 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -1636,15 +1636,6 @@ void Session::execInternal() m_VideoDecoder->renderFrameOnMainThread(); } break; - case SDL_CODE_HIDE_CURSOR: - SDL_ShowCursor(SDL_DISABLE); - break; - case SDL_CODE_SHOW_CURSOR: - SDL_ShowCursor(SDL_ENABLE); - break; - case SDL_CODE_UNCAPTURE_MOUSE: - SDL_CaptureMouse(SDL_FALSE); - break; case SDL_CODE_FLUSH_WINDOW_EVENT_BARRIER: m_FlushingWindowEventsRef--; break;