diff --git a/client/displayservers/SDL/sdl.c b/client/displayservers/SDL/sdl.c index 2379f7b7..adf873da 100644 --- a/client/displayservers/SDL/sdl.c +++ b/client/displayservers/SDL/sdl.c @@ -297,10 +297,8 @@ static int sdlEventFilter(void * userdata, SDL_Event * event) break; app_updateCursorPos(event->motion.x, event->motion.y); - if (app_cursorIsGrabbed()) - app_handleMouseGrabbed(event->motion.xrel, event->motion.yrel); - else - app_handleMouseNormal(event->motion.xrel, event->motion.yrel); + app_handleMouseRelitive(event->motion.xrel, event->motion.yrel, + event->motion.xrel, event->motion.yrel); break; case SDL_MOUSEBUTTONDOWN: @@ -457,8 +455,7 @@ static void sdlWarpPointer(int x, int y, bool exiting) static void sdlRealignPointer(void) { - // no need to care about grab, realign only happens in normal mode - app_handleMouseNormal(0, 0); + app_handleMouseRelitive(0.0, 0.0, 0.0, 0.0); } static bool sdlIsValidPointerPos(int x, int y) diff --git a/client/displayservers/X11/x11.c b/client/displayservers/X11/x11.c index 249ddf38..3e8baa2a 100644 --- a/client/displayservers/X11/x11.c +++ b/client/displayservers/X11/x11.c @@ -808,7 +808,7 @@ static void x11GenericEvent(XGenericEventCookie *cookie) app_updateCursorPos(device->event_x, device->event_y); if (!x11.pointerGrabbed) - app_handleMouseNormal(0.0, 0.0); + app_handleMouseRelitive(0.0, 0.0, 0.0, 0.0); return; } @@ -857,17 +857,7 @@ static void x11GenericEvent(XGenericEventCookie *cookie) prev_axis[0] = axis[0]; prev_axis[1] = axis[1]; - if (app_cursorIsGrabbed()) - { - if (app_cursorWantsRaw()) - app_handleMouseGrabbed(raw_axis[0], raw_axis[1]); - else - app_handleMouseGrabbed(axis[0], axis[1]); - } - else - if (app_cursorInWindow()) - app_handleMouseNormal(axis[0], axis[1]); - + app_handleMouseRelitive(axis[0], axis[1], raw_axis[0], raw_axis[1]); return; } } @@ -1077,7 +1067,7 @@ static void x11WarpPointer(int x, int y, bool exiting) static void x11RealignPointer(void) { - app_handleMouseNormal(0, 0); + app_handleMouseRelitive(0.0, 0.0, 0.0, 0.0); } static bool x11IsValidPointerPos(int x, int y) diff --git a/client/include/app.h b/client/include/app.h index 920b48b3..d2c68d58 100644 --- a/client/include/app.h +++ b/client/include/app.h @@ -37,16 +37,16 @@ LG_MsgAlert; bool app_isRunning(void); bool app_inputEnabled(void); -bool app_cursorIsGrabbed(void); -bool app_cursorWantsRaw(void); -bool app_cursorInWindow(void); void app_updateCursorPos(double x, double y); void app_updateWindowPos(int x, int y); void app_handleResizeEvent(int w, int h, const struct Border border); -void app_handleMouseGrabbed(double ex, double ey); -void app_handleMouseNormal(double ex, double ey); + +void app_handleMouseRelitive(double normx, double normy, + double rawx, double rawy); + void app_handleMouseBasic(void); void app_resyncMouseBasic(void); + void app_handleButtonPress(int button); void app_handleButtonRelease(int button); void app_handleKeyPress(int scancode); diff --git a/client/src/app.c b/client/src/app.c index a625c8ee..8adebaa5 100644 --- a/client/src/app.c +++ b/client/src/app.c @@ -40,27 +40,6 @@ bool app_isRunning(void) g_state.state == APP_STATE_RESTART; } -bool app_inputEnabled(void) -{ - return g_params.useSpiceInput && !g_state.ignoreInput && - ((g_cursor.grab && g_params.captureInputOnly) || !g_params.captureInputOnly); -} - -bool app_cursorInWindow(void) -{ - return g_cursor.inWindow; -} - -bool app_cursorIsGrabbed(void) -{ - return g_cursor.grab; -} - -bool app_cursorWantsRaw(void) -{ - return g_params.rawMouse; -} - void app_updateCursorPos(double x, double y) { g_cursor.pos.x = x; @@ -71,7 +50,7 @@ void app_updateCursorPos(double x, double y) void app_handleFocusEvent(bool focused) { g_state.focused = focused; - if (!app_inputEnabled()) + if (!core_inputEnabled()) return; if (!focused) @@ -89,7 +68,7 @@ void app_handleEnterEvent(bool entered) if (entered) { g_cursor.inWindow = true; - if (!app_inputEnabled()) + if (!core_inputEnabled()) return; g_cursor.realign = true; @@ -99,7 +78,7 @@ void app_handleEnterEvent(bool entered) g_cursor.inWindow = false; core_setCursorInView(false); - if (!app_inputEnabled()) + if (!core_inputEnabled()) return; if (!g_params.alwaysShowCursor) @@ -182,36 +161,9 @@ void spiceClipboardNotice(const SpiceDataType type) g_state.ds->cbNotice(cb_spiceTypeToLGType(type)); } -void app_handleMouseGrabbed(double ex, double ey) -{ - if (!app_inputEnabled()) - return; - - int x, y; - if (g_params.rawMouse && !g_cursor.sens) - { - /* raw unscaled input are always round numbers */ - x = floor(ex); - y = floor(ey); - } - else - { - /* apply sensitivity */ - ex = (ex / 10.0) * (g_cursor.sens + 10); - ey = (ey / 10.0) * (g_cursor.sens + 10); - util_cursorToInt(ex, ey, &x, &y); - } - - if (x == 0 && y == 0) - return; - - if (!spice_mouse_motion(x, y)) - DEBUG_ERROR("failed to send mouse motion message"); -} - void app_handleButtonPress(int button) { - if (!app_inputEnabled() || !g_cursor.inView) + if (!core_inputEnabled() || !g_cursor.inView) return; g_cursor.buttons |= (1U << button); @@ -222,7 +174,7 @@ void app_handleButtonPress(int button) void app_handleButtonRelease(int button) { - if (!app_inputEnabled()) + if (!core_inputEnabled()) return; g_cursor.buttons &= ~(1U << button); @@ -246,7 +198,7 @@ void app_handleKeyPress(int sc) return; } - if (!app_inputEnabled()) + if (!core_inputEnabled()) return; if (g_params.ignoreWindowsKeys && (sc == KEY_LEFTMETA || sc == KEY_RIGHTMETA)) @@ -291,7 +243,7 @@ void app_handleKeyRelease(int sc) g_state.escapeActive = false; } - if (!app_inputEnabled()) + if (!core_inputEnabled()) return; // avoid sending key up events when we didn't send a down @@ -314,146 +266,19 @@ void app_handleKeyRelease(int sc) } } -void app_handleMouseNormal(double ex, double ey) +void app_handleMouseRelitive(double normx, double normy, + double rawx, double rawy) { - // prevent cursor handling outside of capture if the position is not known - if (!g_cursor.guest.valid) - return; - - if (!app_inputEnabled()) - return; - - /* scale the movement to the guest */ - if (g_cursor.useScale && g_params.scaleMouseInput) + if (g_cursor.grab) { - ex *= g_cursor.scale.x / g_cursor.dpiScale; - ey *= g_cursor.scale.y / g_cursor.dpiScale; - } - - bool testExit = true; - if (!g_cursor.inView) - { - const bool inView = - g_cursor.pos.x >= g_state.dstRect.x && - g_cursor.pos.x < g_state.dstRect.x + g_state.dstRect.w && - g_cursor.pos.y >= g_state.dstRect.y && - g_cursor.pos.y < g_state.dstRect.y + g_state.dstRect.h; - - core_setCursorInView(inView); - if (inView) - g_cursor.realign = true; - } - - /* nothing to do if we are outside the viewport */ - if (!g_cursor.inView) - return; - - /* - * do not pass mouse events to the guest if we do not have focus, this must be - * done after the inView test has been performed so that when focus is gained - * we know if we should be drawing the cursor. - */ - if (!g_state.focused) - return; - - /* if we have been instructed to realign */ - if (g_cursor.realign) - { - g_cursor.realign = false; - - struct DoublePoint guest; - util_localCurToGuest(&guest); - - /* add the difference to the offset */ - ex += guest.x - (g_cursor.guest.x + g_cursor.guest.hx); - ey += guest.y - (g_cursor.guest.y + g_cursor.guest.hy); - - /* don't test for an exit as we just entered, we can get into a enter/exit - * loop otherwise */ - testExit = false; - } - - /* if we are in "autoCapture" and the delta was large don't test for exit */ - if (g_params.autoCapture && - (fabs(ex) > 100.0 / g_cursor.scale.x || fabs(ey) > 100.0 / g_cursor.scale.y)) - testExit = false; - - /* if any buttons are held we should not allow exit to happen */ - if (g_cursor.buttons) - testExit = false; - - if (testExit) - { - /* translate the move to the guests orientation */ - struct DoublePoint move = {.x = ex, .y = ey}; - util_rotatePoint(&move); - - /* translate the guests position to our coordinate space */ - struct DoublePoint local; - util_guestCurToLocal(&local); - - /* check if the move would push the cursor outside the guest's viewport */ - if ( - local.x + move.x < g_state.dstRect.x || - local.y + move.y < g_state.dstRect.y || - local.x + move.x >= g_state.dstRect.x + g_state.dstRect.w || - local.y + move.y >= g_state.dstRect.y + g_state.dstRect.h) - { - local.x += move.x; - local.y += move.y; - const int tx = (local.x <= 0.0) ? floor(local.x) : ceil(local.x); - const int ty = (local.y <= 0.0) ? floor(local.y) : ceil(local.y); - - if (core_isValidPointerPos( - g_state.windowPos.x + g_state.border.left + tx, - g_state.windowPos.y + g_state.border.top + ty)) - { - core_setCursorInView(false); - - /* preempt the window leave flag if the warp will leave our window */ - if (tx < 0 || ty < 0 || tx > g_state.windowW || ty > g_state.windowH) - g_cursor.inWindow = false; - - /* ungrab the pointer and move the local cursor to the exit point */ - g_state.ds->ungrabPointer(); - core_warpPointer(tx, ty, true); - return; - } - } - } - - int x, y; - util_cursorToInt(ex, ey, &x, &y); - - if (x == 0 && y == 0) - return; - - if (g_params.autoCapture) - { - g_cursor.delta.x += x; - g_cursor.delta.y += y; - - if (fabs(g_cursor.delta.x) > 50.0 || fabs(g_cursor.delta.y) > 50.0) - { - g_cursor.delta.x = 0; - g_cursor.delta.y = 0; - core_warpPointer(g_state.windowCX, g_state.windowCY, false); - } - - g_cursor.guest.x = g_state.srcSize.x / 2; - g_cursor.guest.y = g_state.srcSize.y / 2; + if (g_params.rawMouse) + core_handleMouseGrabbed(rawx, rawy); + else + core_handleMouseGrabbed(normx, normy); } else - { - /* assume the mouse will move to the location we attempt to move it to so we - * avoid warp out of window issues. The cursorThread will correct this if - * wrong after the movement has ocurred on the guest */ - g_cursor.guest.x += x; - g_cursor.guest.y += y; - } - - if (!spice_mouse_motion(x, y)) - DEBUG_ERROR("failed to send mouse motion message"); + if (g_cursor.inWindow) + core_handleMouseNormal(normx, normy); } static inline double clamp(double x, double min, double max) @@ -473,7 +298,7 @@ void app_handleMouseBasic() if (!g_cursor.guest.valid || !g_state.haveSrcSize || !g_state.focused) return; - if (!app_inputEnabled()) + if (!core_inputEnabled()) return; const bool inView = @@ -529,7 +354,7 @@ void app_handleResizeEvent(int w, int h, const struct Border border) g_state.windowCY = h / 2; core_updatePositionInfo(); - if (app_inputEnabled()) + if (core_inputEnabled()) { /* if the window is moved/resized causing a loss of focus while grabbed, it * makes it impossible to re-focus the window, so we quietly re-enter diff --git a/client/src/core.c b/client/src/core.c index 6233cb35..7feea218 100644 --- a/client/src/core.c +++ b/client/src/core.c @@ -30,6 +30,12 @@ Place, Suite 330, Boston, MA 02111-1307 USA #define RESIZE_TIMEOUT (10 * 1000) // 10ms +bool core_inputEnabled(void) +{ + return g_params.useSpiceInput && !g_state.ignoreInput && + ((g_cursor.grab && g_params.captureInputOnly) || !g_params.captureInputOnly); +} + void core_setCursorInView(bool enable) { // if the state has not changed, don't do anything else @@ -291,3 +297,172 @@ void core_stopFrameThread(void) g_state.frameThread = NULL; } + +void core_handleMouseGrabbed(double ex, double ey) +{ + if (!core_inputEnabled()) + return; + + int x, y; + if (g_params.rawMouse && !g_cursor.sens) + { + /* raw unscaled input are always round numbers */ + x = floor(ex); + y = floor(ey); + } + else + { + /* apply sensitivity */ + ex = (ex / 10.0) * (g_cursor.sens + 10); + ey = (ey / 10.0) * (g_cursor.sens + 10); + util_cursorToInt(ex, ey, &x, &y); + } + + if (x == 0 && y == 0) + return; + + if (!spice_mouse_motion(x, y)) + DEBUG_ERROR("failed to send mouse motion message"); +} + +void core_handleMouseNormal(double ex, double ey) +{ + // prevent cursor handling outside of capture if the position is not known + if (!g_cursor.guest.valid) + return; + + if (!core_inputEnabled()) + return; + + /* scale the movement to the guest */ + if (g_cursor.useScale && g_params.scaleMouseInput) + { + ex *= g_cursor.scale.x / g_cursor.dpiScale; + ey *= g_cursor.scale.y / g_cursor.dpiScale; + } + + bool testExit = true; + if (!g_cursor.inView) + { + const bool inView = + g_cursor.pos.x >= g_state.dstRect.x && + g_cursor.pos.x < g_state.dstRect.x + g_state.dstRect.w && + g_cursor.pos.y >= g_state.dstRect.y && + g_cursor.pos.y < g_state.dstRect.y + g_state.dstRect.h; + + core_setCursorInView(inView); + if (inView) + g_cursor.realign = true; + } + + /* nothing to do if we are outside the viewport */ + if (!g_cursor.inView) + return; + + /* + * do not pass mouse events to the guest if we do not have focus, this must be + * done after the inView test has been performed so that when focus is gained + * we know if we should be drawing the cursor. + */ + if (!g_state.focused) + return; + + /* if we have been instructed to realign */ + if (g_cursor.realign) + { + g_cursor.realign = false; + + struct DoublePoint guest; + util_localCurToGuest(&guest); + + /* add the difference to the offset */ + ex += guest.x - (g_cursor.guest.x + g_cursor.guest.hx); + ey += guest.y - (g_cursor.guest.y + g_cursor.guest.hy); + + /* don't test for an exit as we just entered, we can get into a enter/exit + * loop otherwise */ + testExit = false; + } + + /* if we are in "autoCapture" and the delta was large don't test for exit */ + if (g_params.autoCapture && + (fabs(ex) > 100.0 / g_cursor.scale.x || fabs(ey) > 100.0 / g_cursor.scale.y)) + testExit = false; + + /* if any buttons are held we should not allow exit to happen */ + if (g_cursor.buttons) + testExit = false; + + if (testExit) + { + /* translate the move to the guests orientation */ + struct DoublePoint move = {.x = ex, .y = ey}; + util_rotatePoint(&move); + + /* translate the guests position to our coordinate space */ + struct DoublePoint local; + util_guestCurToLocal(&local); + + /* check if the move would push the cursor outside the guest's viewport */ + if ( + local.x + move.x < g_state.dstRect.x || + local.y + move.y < g_state.dstRect.y || + local.x + move.x >= g_state.dstRect.x + g_state.dstRect.w || + local.y + move.y >= g_state.dstRect.y + g_state.dstRect.h) + { + local.x += move.x; + local.y += move.y; + const int tx = (local.x <= 0.0) ? floor(local.x) : ceil(local.x); + const int ty = (local.y <= 0.0) ? floor(local.y) : ceil(local.y); + + if (core_isValidPointerPos( + g_state.windowPos.x + g_state.border.left + tx, + g_state.windowPos.y + g_state.border.top + ty)) + { + core_setCursorInView(false); + + /* preempt the window leave flag if the warp will leave our window */ + if (tx < 0 || ty < 0 || tx > g_state.windowW || ty > g_state.windowH) + g_cursor.inWindow = false; + + /* ungrab the pointer and move the local cursor to the exit point */ + g_state.ds->ungrabPointer(); + core_warpPointer(tx, ty, true); + return; + } + } + } + + int x, y; + util_cursorToInt(ex, ey, &x, &y); + + if (x == 0 && y == 0) + return; + + if (g_params.autoCapture) + { + g_cursor.delta.x += x; + g_cursor.delta.y += y; + + if (fabs(g_cursor.delta.x) > 50.0 || fabs(g_cursor.delta.y) > 50.0) + { + g_cursor.delta.x = 0; + g_cursor.delta.y = 0; + core_warpPointer(g_state.windowCX, g_state.windowCY, false); + } + + g_cursor.guest.x = g_state.srcSize.x / 2; + g_cursor.guest.y = g_state.srcSize.y / 2; + } + else + { + /* assume the mouse will move to the location we attempt to move it to so we + * avoid warp out of window issues. The cursorThread will correct this if + * wrong after the movement has ocurred on the guest */ + g_cursor.guest.x += x; + g_cursor.guest.y += y; + } + + if (!spice_mouse_motion(x, y)) + DEBUG_ERROR("failed to send mouse motion message"); +} diff --git a/client/src/core.h b/client/src/core.h index 6b8d0d84..47e2d6bc 100644 --- a/client/src/core.h +++ b/client/src/core.h @@ -22,6 +22,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include +bool core_inputEnabled(void); void core_setCursorInView(bool enable); void core_setGrab(bool enable); void core_setGrabQuiet(bool enable); @@ -31,5 +32,8 @@ void core_alignToGuest(void); bool core_isValidPointerPos(int x, int y); bool core_startFrameThread(void); void core_stopFrameThread(void); +void core_handleMouseGrabbed(double ex, double ey); +void core_handleMouseNormal(double ex, double ey); + #endif diff --git a/client/src/main.c b/client/src/main.c index 5ced665c..7ed131e7 100644 --- a/client/src/main.c +++ b/client/src/main.c @@ -87,7 +87,7 @@ static void lgInit(void) g_cursor.guest.valid = false; // if spice is not in use, hide the local cursor - if (!app_inputEnabled() && g_params.hideMouse) + if (!core_inputEnabled() && g_params.hideMouse) g_state.ds->showPointer(false); else g_state.ds->showPointer(true); @@ -296,7 +296,7 @@ static int cursorThread(void * unused) g_cursor.guest.valid = true; // if the state just became valid - if (valid != true && app_inputEnabled()) + if (valid != true && core_inputEnabled()) { core_alignToGuest(); app_resyncMouseBasic();