From 270631f1b99c5a5356e50716f516dd89c29f4fa2 Mon Sep 17 00:00:00 2001 From: Quantum Date: Mon, 15 Feb 2021 19:40:44 -0500 Subject: [PATCH] [client] ds: add surface-only warp variant This commit converts the output of ds->getProp(LG_DS_WARP_SUPPORT) to an enum containing three items: * LG_DS_WARP_NONE: warp is not supported at all * LG_DS_WARP_SURFACE: warp is possible, but only inside the window * LG_DS_WARP_SCREEN: warp is possible anywhere on the screen LG_DS_WARP_NONE corresponds to the old false return value, and LG_DS_WARP_SCREEN corresponds to the old true return value. LG_DS_WARP_SURFACE is designed for Wayland, where warping is possible, but only in our window. In this case, since we cannot warp outside the window, we can warp the cursor to the edge when we attempt to exit. If the cursor leaves, the normal leave routine gets called, and the cursor disappears. If the cursor does not end up leaving, we grab it again. --- client/displayservers/Wayland/wayland.c | 2 +- client/displayservers/X11/x11.c | 88 +++++++++++++----------- client/include/interface/displayserver.h | 7 ++ client/src/core.c | 76 +++++++++++++------- 4 files changed, 109 insertions(+), 64 deletions(-) diff --git a/client/displayservers/Wayland/wayland.c b/client/displayservers/Wayland/wayland.c index 864886ed..e6951e05 100644 --- a/client/displayservers/Wayland/wayland.c +++ b/client/displayservers/Wayland/wayland.c @@ -1015,7 +1015,7 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret) { if (prop == LG_DS_WARP_SUPPORT) { - *(bool*)ret = false; + *(enum LG_DSWarpSupport*)ret = LG_DS_WARP_NONE; return true; } diff --git a/client/displayservers/X11/x11.c b/client/displayservers/X11/x11.c index 09d5a18a..ab5ca01f 100644 --- a/client/displayservers/X11/x11.c +++ b/client/displayservers/X11/x11.c @@ -449,52 +449,62 @@ static void x11Free(void) static bool x11GetProp(LG_DSProperty prop, void *ret) { - if (prop != LG_DS_MAX_MULTISAMPLE) - return false; - - Display * dpy = XOpenDisplay(NULL); - if (!dpy) - return false; - - XVisualInfo queryTemplate; - queryTemplate.screen = 0; - - int visualCount; - int maxSamples = -1; - XVisualInfo * visuals = XGetVisualInfo(dpy, VisualScreenMask, - &queryTemplate, &visualCount); - - for (int i = 0; i < visualCount; i++) + switch (prop) { - XVisualInfo * visual = &visuals[i]; + case LG_DS_WARP_SUPPORT: + *(enum LG_DSWarpSupport*)ret = LG_DS_WARP_SCREEN; + return true; - int res, supportsGL; - // Some GLX visuals do not use GL, and these must be ignored in our search. - if ((res = glXGetConfig(dpy, visual, GLX_USE_GL, &supportsGL)) != 0 || - !supportsGL) - continue; + case LG_DS_MAX_MULTISAMPLE: + { + Display * dpy = XOpenDisplay(NULL); + if (!dpy) + return false; - int sampleBuffers, samples; - if ((res = glXGetConfig(dpy, visual, GLX_SAMPLE_BUFFERS, &sampleBuffers)) != 0) - continue; + XVisualInfo queryTemplate; + queryTemplate.screen = 0; - // Will be 1 if this visual supports multisampling - if (sampleBuffers != 1) - continue; + int visualCount; + int maxSamples = -1; + XVisualInfo * visuals = XGetVisualInfo(dpy, VisualScreenMask, + &queryTemplate, &visualCount); - if ((res = glXGetConfig(dpy, visual, GLX_SAMPLES, &samples)) != 0) - continue; + for (int i = 0; i < visualCount; i++) + { + XVisualInfo * visual = &visuals[i]; - // Track the largest number of samples supported - if (samples > maxSamples) - maxSamples = samples; + int res, supportsGL; + // Some GLX visuals do not use GL, and these must be ignored in our search. + if ((res = glXGetConfig(dpy, visual, GLX_USE_GL, &supportsGL)) != 0 || + !supportsGL) + continue; + + int sampleBuffers, samples; + if ((res = glXGetConfig(dpy, visual, GLX_SAMPLE_BUFFERS, &sampleBuffers)) != 0) + continue; + + // Will be 1 if this visual supports multisampling + if (sampleBuffers != 1) + continue; + + if ((res = glXGetConfig(dpy, visual, GLX_SAMPLES, &samples)) != 0) + continue; + + // Track the largest number of samples supported + if (samples > maxSamples) + maxSamples = samples; + } + + XFree(visuals); + XCloseDisplay(dpy); + + *(int*)ret = maxSamples; + return true; + } + + default: + return true; } - - XFree(visuals); - XCloseDisplay(dpy); - - *(int*)ret = maxSamples; - return true; } static int x11EventThread(void * unused) diff --git a/client/include/interface/displayserver.h b/client/include/interface/displayserver.h index ad5d5349..d3cf16d4 100644 --- a/client/include/interface/displayserver.h +++ b/client/include/interface/displayserver.h @@ -53,6 +53,13 @@ typedef enum LG_DSProperty } LG_DSProperty; +enum LG_DSWarpSupport +{ + LG_DS_WARP_NONE, + LG_DS_WARP_SURFACE, + LG_DS_WARP_SCREEN, +}; + typedef struct LG_DSInitParams { const char * title; diff --git a/client/src/core.c b/client/src/core.c index f026d163..60918aa3 100644 --- a/client/src/core.c +++ b/client/src/core.c @@ -57,7 +57,7 @@ void core_setCursorInView(bool enable) /* if the display server does not support warp, then we can not operate in * always relative mode and we should not grab the pointer */ - bool warpSupport = true; + enum LG_DSWarpSupport warpSupport = LG_DS_WARP_NONE; app_getProp(LG_DS_WARP_SUPPORT, &warpSupport); g_cursor.warpState = enable ? WARP_STATE_ON : WARP_STATE_OFF; @@ -67,7 +67,7 @@ void core_setCursorInView(bool enable) if (g_params.hideMouse) g_state.ds->showPointer(false); - if (warpSupport && !g_params.captureInputOnly) + if (warpSupport != LG_DS_WARP_NONE && !g_params.captureInputOnly) g_state.ds->grabPointer(); if (g_params.grabKeyboardOnFocus) @@ -78,7 +78,7 @@ void core_setCursorInView(bool enable) if (g_params.hideMouse) g_state.ds->showPointer(true); - if (warpSupport) + if (warpSupport != LG_DS_WARP_NONE) g_state.ds->ungrabPointer(); g_state.ds->ungrabKeyboard(); @@ -112,7 +112,7 @@ void core_setGrabQuiet(bool enable) /* if the display server does not support warp we need to ungrab the pointer * here instead of in the move handler */ - bool warpSupport = true; + enum LG_DSWarpSupport warpSupport = LG_DS_WARP_NONE; app_getProp(LG_DS_WARP_SUPPORT, &warpSupport); if (enable) @@ -134,7 +134,7 @@ void core_setGrabQuiet(bool enable) g_state.ds->ungrabKeyboard(); } - if (!warpSupport || g_params.captureInputOnly || !g_state.formatValid) + if (warpSupport != LG_DS_WARP_NONE || g_params.captureInputOnly || !g_state.formatValid) g_state.ds->ungrabPointer(); // if exiting capture when input on capture only, we want to show the cursor @@ -328,6 +328,15 @@ void core_handleMouseGrabbed(double ex, double ey) DEBUG_ERROR("failed to send mouse motion message"); } +static bool isInView(void) +{ + return + 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; +} + void core_handleMouseNormal(double ex, double ey) { // prevent cursor handling outside of capture if the position is not known @@ -340,19 +349,14 @@ void core_handleMouseNormal(double ex, double ey) /* 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; + ex *= g_cursor.scale.x; + ey *= g_cursor.scale.y; } 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; - + const bool inView = isInView(); core_setCursorInView(inView); if (inView) g_cursor.realign = true; @@ -398,6 +402,9 @@ void core_handleMouseNormal(double ex, double ey) if (testExit) { + enum LG_DSWarpSupport warpSupport = LG_DS_WARP_NONE; + app_getProp(LG_DS_WARP_SUPPORT, &warpSupport); + /* translate the move to the guests orientation */ struct DoublePoint move = {.x = ex, .y = ey}; util_rotatePoint(&move); @@ -418,22 +425,43 @@ void core_handleMouseNormal(double ex, double ey) 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)) + switch (warpSupport) { - core_setCursorInView(false); + case LG_DS_WARP_NONE: + break; - /* 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; + case LG_DS_WARP_SURFACE: + g_state.ds->ungrabPointer(); + core_warpPointer(tx, ty, true); - /* ungrab the pointer and move the local cursor to the exit point */ - g_state.ds->ungrabPointer(); - core_warpPointer(tx, ty, true); - return; + if (!isInView() && tx >= 0 && tx < g_state.windowW && ty >= 0 && ty < g_state.windowH) + core_setCursorInView(false); + break; + + case LG_DS_WARP_SCREEN: + 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; + } } } + else if (warpSupport == LG_DS_WARP_SURFACE && isInView()) + { + /* regrab the pointer in case the user did not move off the surface */ + g_state.ds->grabPointer(); + g_cursor.warpState = WARP_STATE_ON; + } } int x, y;