mirror of
https://github.com/gnif/LookingGlass.git
synced 2024-12-22 13:33:40 +00:00
[client] spice/wayland: improve cursor tracking logic
One of the major issues with the old tracking code is a data race between the cursor thread updating g_cursor.guest and the app_handleMouseBasic function. Specifically, the latter may have sent mouse input via spice that has not been processed by the guest and updated g_cursor.guest, but the guest may overwrite g_cursor.guest to a previous state before the input is processed. This causes some movements to be doubled. Eventually, the cursor positions will synchronize, but this nevertheless causes a lot of jitter. In this commit, we introduce a new field g_cursor.projected, which is unambiguously the position of the cursor after taking into account all the input already sent via spice. This is synced up to the guest cursor upon entering the window and when the host restarts. Afterwards, all mouse movements will be based on this position. This eliminates all cursor jitter as far as I could tell. Also, the cursor is now synced to the host position when exiting capture mode. A downside of this commit is that if the 1:1 movement patch is not correctly applied, the cursor position would be wildly off instead of simply jittering, but that is an unsupported configuration and should not matter. Also unsupported is when an application in guest moves the cursor programmatically and bypassing spice. When using those applications, capture mode must be on. Before this commit, we try to move the guest cursor back to where it should be, but it's inherently fragile and may lead to scenarios such as wild movements in first-person shooters.
This commit is contained in:
parent
543d660ccc
commit
b0f9d2f713
@ -230,13 +230,11 @@ static const struct wl_registry_listener registryListener = {
|
||||
static void pointerMotionHandler(void * data, struct wl_pointer * pointer,
|
||||
uint32_t serial, wl_fixed_t sxW, wl_fixed_t syW)
|
||||
{
|
||||
if (wm.relativePointer)
|
||||
return;
|
||||
|
||||
int sx = wl_fixed_to_int(sxW);
|
||||
int sy = wl_fixed_to_int(syW);
|
||||
app_updateCursorPos(sx, sy);
|
||||
app_handleMouseBasic();
|
||||
if (!wm.relativePointer)
|
||||
app_handleMouseBasic();
|
||||
}
|
||||
|
||||
static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
|
||||
@ -253,8 +251,10 @@ static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
|
||||
|
||||
int sx = wl_fixed_to_int(sxW);
|
||||
int sy = wl_fixed_to_int(syW);
|
||||
app_resyncMouseBasic();
|
||||
app_updateCursorPos(sx, sy);
|
||||
app_handleMouseBasic();
|
||||
if (!wm.relativePointer)
|
||||
app_handleMouseBasic();
|
||||
}
|
||||
|
||||
static void pointerLeaveHandler(void * data, struct wl_pointer * pointer,
|
||||
@ -810,6 +810,9 @@ static void waylandUngrabPointer(void)
|
||||
zwp_confined_pointer_v1_destroy(wm.confinedPointer);
|
||||
wm.confinedPointer = NULL;
|
||||
}
|
||||
|
||||
app_resyncMouseBasic();
|
||||
app_handleMouseBasic();
|
||||
}
|
||||
|
||||
static void waylandGrabKeyboard(void)
|
||||
@ -837,7 +840,7 @@ static void waylandWarpPointer(int x, int y, bool exiting)
|
||||
|
||||
static void waylandRealignPointer(void)
|
||||
{
|
||||
app_handleMouseBasic();
|
||||
app_resyncMouseBasic();
|
||||
}
|
||||
|
||||
static bool waylandIsValidPointerPos(int x, int y)
|
||||
|
@ -46,6 +46,7 @@ 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_handleMouseBasic(void);
|
||||
void app_resyncMouseBasic(void);
|
||||
void app_handleButtonPress(int button);
|
||||
void app_handleButtonRelease(int button);
|
||||
void app_handleKeyPress(int scancode);
|
||||
|
@ -456,6 +456,12 @@ void app_handleMouseNormal(double ex, double ey)
|
||||
DEBUG_ERROR("failed to send mouse motion message");
|
||||
}
|
||||
|
||||
static inline double clamp(double x, double min, double max)
|
||||
{
|
||||
if (x < min) return min;
|
||||
if (x > max) return max;
|
||||
return x;
|
||||
}
|
||||
|
||||
// On some display servers normal cursor logic does not work due to the lack of
|
||||
// cursor warp support. Instead, we attempt a best-effort emulation which works
|
||||
@ -464,7 +470,7 @@ void app_handleMouseNormal(double ex, double ey)
|
||||
void app_handleMouseBasic()
|
||||
{
|
||||
/* do not pass mouse events to the guest if we do not have focus */
|
||||
if (!g_state.focused)
|
||||
if (!g_cursor.guest.valid || !g_state.haveSrcSize || !g_state.focused)
|
||||
return;
|
||||
|
||||
if (!app_inputEnabled())
|
||||
@ -478,39 +484,31 @@ void app_handleMouseBasic()
|
||||
|
||||
core_setCursorInView(inView);
|
||||
|
||||
if (g_cursor.guest.dpiScale == 0)
|
||||
return;
|
||||
/* translate the current position to guest coordinate space */
|
||||
struct DoublePoint guest;
|
||||
util_localCurToGuest(&guest);
|
||||
|
||||
double px = g_cursor.pos.x;
|
||||
double py = g_cursor.pos.y;
|
||||
|
||||
if (px < g_state.dstRect.x)
|
||||
px = g_state.dstRect.x;
|
||||
else if (px > g_state.dstRect.x + g_state.dstRect.w)
|
||||
px = g_state.dstRect.x + g_state.dstRect.w;
|
||||
|
||||
if (py < g_state.dstRect.y)
|
||||
py = g_state.dstRect.y;
|
||||
else if (py > g_state.dstRect.y + g_state.dstRect.h)
|
||||
py = g_state.dstRect.y + g_state.dstRect.h;
|
||||
|
||||
/* translate the guests position to our coordinate space */
|
||||
struct DoublePoint local;
|
||||
util_guestCurToLocal(&local);
|
||||
|
||||
int x = (int) round((px - local.x) / g_cursor.dpiScale);
|
||||
int y = (int) round((py - local.y) / g_cursor.dpiScale);
|
||||
int x = (int) round(clamp(guest.x, 0, g_state.srcSize.x) - g_cursor.projected.x);
|
||||
int y = (int) round(clamp(guest.y, 0, g_state.srcSize.y) - g_cursor.projected.y);
|
||||
|
||||
if (!x && !y)
|
||||
return;
|
||||
|
||||
g_cursor.guest.x += x;
|
||||
g_cursor.guest.y += y;
|
||||
g_cursor.projected.x += x;
|
||||
g_cursor.projected.y += y;
|
||||
|
||||
if (!spice_mouse_motion(x, y))
|
||||
DEBUG_ERROR("failed to send mouse motion message");
|
||||
}
|
||||
|
||||
void app_resyncMouseBasic()
|
||||
{
|
||||
if (!g_cursor.guest.valid)
|
||||
return;
|
||||
g_cursor.projected.x = g_cursor.guest.x + g_cursor.guest.hx;
|
||||
g_cursor.projected.y = g_cursor.guest.y + g_cursor.guest.hy;
|
||||
}
|
||||
|
||||
void app_updateWindowPos(int x, int y)
|
||||
{
|
||||
g_state.windowPos.x = x;
|
||||
|
@ -297,7 +297,10 @@ static int cursorThread(void * unused)
|
||||
|
||||
// if the state just became valid
|
||||
if (valid != true && app_inputEnabled())
|
||||
{
|
||||
core_alignToGuest();
|
||||
app_resyncMouseBasic();
|
||||
}
|
||||
}
|
||||
|
||||
lgmpClientMessageDone(queue);
|
||||
|
@ -234,6 +234,9 @@ struct CursorState
|
||||
|
||||
/* the guest's cursor position */
|
||||
struct CursorInfo guest;
|
||||
|
||||
/* the projected position after move, for app_handleMouseBasic only */
|
||||
struct Point projected;
|
||||
};
|
||||
|
||||
// forwards
|
||||
|
Loading…
Reference in New Issue
Block a user