diff --git a/client/src/app.c b/client/src/app.c index aff6d6a3..ba6c5b13 100644 --- a/client/src/app.c +++ b/client/src/app.c @@ -139,6 +139,8 @@ void app_updateCursorPos(double x, double y) if (app_isOverlayMode()) g_state.io->MousePos = (ImVec2) { x, y }; + else + core_handleMouseAbsolute(); } void app_updateMouseState(void) @@ -267,6 +269,7 @@ void app_handleEnterEvent(bool entered) return; g_cursor.realign = true; + core_handleMouseAbsolute(); } else { @@ -471,6 +474,7 @@ void app_handleButtonPress(int button) if (!core_inputEnabled() || !g_cursor.inView || !g_cursor.viewReq) return; + core_handleMouseAbsolute(); if (!lgInput_mousePress(button)) DEBUG_ERROR("app_handleButtonPress: failed to send message"); } @@ -662,6 +666,9 @@ void app_handleMouseBasic(void) if (!core_inputEnabled()) return; + if (lgInput_supports(LG_INPUT_SUPPORT_MOUSE_ABSOLUTE)) + return; + const bool inView = g_cursor.pos.x >= g_state.dstRect.x && g_cursor.pos.x < g_state.dstRect.x + g_state.dstRect.w && diff --git a/client/src/core.c b/client/src/core.c index 0ba73775..89b7d5e1 100644 --- a/client/src/core.c +++ b/client/src/core.c @@ -282,6 +282,8 @@ void core_handleGrabEvent(bool active) finishExit(); applyView(active, false); + if (active) + core_handleMouseAbsolute(); } void core_setGrab(bool enable) @@ -692,6 +694,38 @@ void core_handleGuestMouseUpdate(void) ); } +void core_handleMouseAbsolute(void) +{ + if (g_cursor.grab || !g_cursor.inWindow || !g_cursor.valid || + !g_state.haveSrcSize || !g_state.posInfoValid || + !g_state.focused || g_cursor.realigning || + app_isOverlayMode() || !core_inputEnabled() || + !lgInput_supports(LG_INPUT_SUPPORT_MOUSE_ABSOLUTE)) + return; + + const bool inView = isInView(); + if (g_cursor.viewReq != inView || g_cursor.inView != inView) + core_setCursorInView(inView); + if (!inView || !g_cursor.inView || !g_cursor.viewReq) + return; + + struct DoublePoint guest; + util_localCurToGuest(&guest); + + const uint32_t width = g_state.srcSize.x; + const uint32_t height = g_state.srcSize.y; + const uint32_t x = round(util_clamp(guest.x, 0, width - 1)); + const uint32_t y = round(util_clamp(guest.y, 0, height - 1)); + + MTRACE("absolute local=%.3f,%.3f guest=%u,%u size=%ux%u", + g_cursor.pos.x, g_cursor.pos.y, x, y, width, height); + + if (!lgInput_mousePosition(x, y, width, height)) + DEBUG_ERROR("failed to send absolute mouse position message"); + else + g_cursor.realign = false; +} + void core_handleMouseGrabbed(double ex, double ey) { const bool active = g_cursor.grab && g_state.ds->isPointerCaptured(); @@ -730,10 +764,15 @@ void core_handleMouseNormal(double ex, double ey) "inView=%d", ex, ey, g_cursor.guest.x, g_cursor.guest.y, g_cursor.guest.valid, g_cursor.inWindow, g_cursor.inView); + const bool absolute = + lgInput_supports(LG_INPUT_SUPPORT_MOUSE_ABSOLUTE); + // prevent cursor handling outside of capture if the position is not known if (!g_cursor.guest.valid) { - if (app_guestIsWindows()) + if (absolute) + core_handleMouseAbsolute(); + else if (app_guestIsWindows()) { // wiggle the mouse when the guest has not provided any information, we need // to do this because windows doesn't enable a cursor at all until it has @@ -793,7 +832,16 @@ void core_handleMouseNormal(double ex, double ey) } /* if we have been instructed to realign */ - if (g_cursor.realign) + if (g_cursor.realign && absolute) + { + g_cursor.realign = false; + core_handleMouseAbsolute(); + if (!g_cursor.inView) + return; + + testExit = false; + } + else if (g_cursor.realign) { struct DoublePoint guest; util_localCurToGuest(&guest); @@ -956,6 +1004,9 @@ fallback: } } + if (absolute) + return; + int x, y; util_cursorToInt(ex, ey, &x, &y); diff --git a/client/src/core.h b/client/src/core.h index 3ee8c201..1cc92a24 100644 --- a/client/src/core.h +++ b/client/src/core.h @@ -39,6 +39,7 @@ void core_stopCursorThread(void); bool core_startFrameThread(void); void core_stopFrameThread(void); void core_handleGuestMouseUpdate(void); +void core_handleMouseAbsolute(void); void core_handleMouseGrabbed(double ex, double ey); void core_handleMouseNormal(double ex, double ey); void core_resetOverlayInputState(void); diff --git a/client/tests/CMakeLists.txt b/client/tests/CMakeLists.txt index b18a9f10..7c3b91c0 100644 --- a/client/tests/CMakeLists.txt +++ b/client/tests/CMakeLists.txt @@ -50,6 +50,7 @@ set_tests_properties(wayland-motion-tests PROPERTIES add_executable(mouse-tests mouse_test.c ../src/core.c + ../src/input.c ../src/util.c ) target_compile_definitions(mouse-tests PRIVATE diff --git a/client/tests/mouse_test.c b/client/tests/mouse_test.c index 9b3764fe..ce5aa12a 100644 --- a/client/tests/mouse_test.c +++ b/client/tests/mouse_test.c @@ -20,6 +20,7 @@ #include "app.h" #include "core.h" +#include "input.h" #include "main.h" #include "test.h" #include "util.h" @@ -241,17 +242,44 @@ void app_mouseTrace(const char * file, unsigned int line, (void)format; } -bool lgInput_available(void) +static bool inputSupports(void * opaque, LG_InputSupport support) { - return g_params.useSpiceInput; + (void)opaque; + (void)support; + return false; } -bool lgInput_mouseMotion(int32_t x, int32_t y) +static bool inputKey(void * opaque, int key) { + (void)opaque; + (void)key; + return true; +} + +static bool inputMouseMotion(void * opaque, int32_t x, int32_t y) +{ + (void)opaque; push(EV_MOTION, x, y, false); return true; } +static bool inputMouseButton(void * opaque, unsigned int button) +{ + (void)opaque; + (void)button; + return true; +} + +static const LG_InputOps inputOps = { + .name = "test", + .supports = inputSupports, + .keyDown = inputKey, + .keyUp = inputKey, + .mouseMotion = inputMouseMotion, + .mousePress = inputMouseButton, + .mouseRelease = inputMouseButton, +}; + static void reset(void) { memset(&m, 0, sizeof(m)); @@ -280,8 +308,7 @@ static void reset(void) .h = 80, }; - g_params.useSpiceInput = true; - g_params.scaleMouseInput = true; + g_params.scaleMouseInput = true; g_cursor.inWindow = true; g_cursor.inView = true; @@ -843,23 +870,33 @@ static const struct Test tests[] = { int main(int argc, char ** argv) { + lgInput_init(); + lgInput_setFallback(&inputOps, NULL); + if (argc == 2) { for (unsigned int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) if (strcmp(argv[1], tests[i].name) == 0) { tests[i].run(); + lgInput_free(); return 0; } fprintf(stderr, "unknown test: %s\n", argv[1]); + lgInput_free(); return EXIT_FAILURE; } if (argc != 1) + { + lgInput_free(); return EXIT_FAILURE; + } for (unsigned int i = 0; i < sizeof(tests) / sizeof(tests[0]); ++i) tests[i].run(); + + lgInput_free(); return 0; }