[client] mouse: correct issues with cursor alignment on enter/exit/focus

This commit is contained in:
Geoffrey McRae 2021-01-17 12:53:55 +11:00
parent 76182bbeb8
commit e23d536af5
5 changed files with 107 additions and 64 deletions

View File

@ -146,6 +146,12 @@ static void sdlWarpPointer(int x, int y, bool exiting)
SDL_WarpMouseInWindow(app_getWindow(), x, y); SDL_WarpMouseInWindow(app_getWindow(), x, y);
} }
static void sdlRealignPointer(void)
{
// no need to care about grab, realign only happens in normal mode
app_handleMouseNormal(0, 0);
}
struct LG_DisplayServerOps LGDS_SDL = struct LG_DisplayServerOps LGDS_SDL =
{ {
.subsystem = SDL_SYSWM_UNKNOWN, .subsystem = SDL_SYSWM_UNKNOWN,
@ -161,6 +167,7 @@ struct LG_DisplayServerOps LGDS_SDL =
.grabKeyboard = sdlGrabKeyboard, .grabKeyboard = sdlGrabKeyboard,
.ungrabKeyboard = sdlUngrabKeyboard, .ungrabKeyboard = sdlUngrabKeyboard,
.warpPointer = sdlWarpPointer, .warpPointer = sdlWarpPointer,
.realignPointer = sdlRealignPointer,
/* SDL does not have clipboard support */ /* SDL does not have clipboard support */
.cbInit = NULL, .cbInit = NULL,

View File

@ -391,6 +391,11 @@ static void waylandWarpPointer(int x, int y, bool exiting)
// This is an unsupported operation on Wayland. // This is an unsupported operation on Wayland.
} }
static void waylandRealignPointer(void)
{
app_handleMouseBasic();
}
static void waylandFree(void) static void waylandFree(void)
{ {
waylandUngrabPointer(); waylandUngrabPointer();
@ -774,6 +779,7 @@ struct LG_DisplayServerOps LGDS_Wayland =
.grabKeyboard = waylandGrabKeyboard, .grabKeyboard = waylandGrabKeyboard,
.ungrabKeyboard = waylandUngrabKeyboard, .ungrabKeyboard = waylandUngrabKeyboard,
.warpPointer = waylandWarpPointer, .warpPointer = waylandWarpPointer,
.realignPointer = waylandRealignPointer,
.cbInit = waylandCBInit, .cbInit = waylandCBInit,
.cbNotice = waylandCBNotice, .cbNotice = waylandCBNotice,

View File

@ -92,9 +92,14 @@ struct LG_DisplayServerOps
void (*grabKeyboard)(); void (*grabKeyboard)();
void (*ungrabKeyboard)(); void (*ungrabKeyboard)();
//exiting = true if the warp is to leave the window /* exiting = true if the warp is to leave the window */
void (*warpPointer)(int x, int y, bool exiting); void (*warpPointer)(int x, int y, bool exiting);
/* called when the client needs to realign the pointer. This should simply
* call the appropriate app_handleMouse* method for the platform with zero
* deltas */
void (*realignPointer)();
/* clipboard support */ /* clipboard support */
bool (* cbInit)(void); bool (* cbInit)(void);
void (* cbNotice)(LG_ClipboardData type); void (* cbNotice)(LG_ClipboardData type);

View File

@ -133,6 +133,24 @@ void app_updateCursorPos(double x, double y)
g_cursor.pos.y = y; g_cursor.pos.y = y;
} }
void app_handleFocusEvent(bool focused)
{
if (!app_inputEnabled())
return;
if (params.grabKeyboardOnFocus)
{
if (focused)
g_state.ds->grabKeyboard();
else
g_state.ds->ungrabKeyboard();
}
g_state.focused = focused;
g_cursor.realign = true;
g_state.ds->realignPointer();
}
static void alignToGuest(void) static void alignToGuest(void)
{ {
if (SDL_HasEvent(e_SDLEvent)) if (SDL_HasEvent(e_SDLEvent))
@ -921,19 +939,22 @@ static void cursorToInt(double ex, double ey, int *x, int *y)
static void setCursorInView(bool enable) static void setCursorInView(bool enable)
{ {
// if we don't have focus don't do anything // if the state has not changed, don't do anything else
if (g_cursor.inView == enable)
return;
if (enable && !g_state.focused) if (enable && !g_state.focused)
return; return;
g_cursor.inView = enable;
g_cursor.draw = params.alwaysShowCursor ? true : enable;
g_cursor.redraw = true;
/* if the display server does not support warp, then we can not operate in /* if the display server does not support warp, then we can not operate in
* always relative mode and we should not grab the pointer */ * always relative mode and we should not grab the pointer */
bool warpSupport = true; bool warpSupport = true;
app_getProp(LG_DS_WARP_SUPPORT, &warpSupport); app_getProp(LG_DS_WARP_SUPPORT, &warpSupport);
g_cursor.inView = enable;
g_cursor.draw = params.alwaysShowCursor ? true : enable;
g_cursor.redraw = true;
g_cursor.warpState = enable ? WARP_STATE_ON : WARP_STATE_OFF; g_cursor.warpState = enable ? WARP_STATE_ON : WARP_STATE_OFF;
if (enable) if (enable)
@ -951,7 +972,12 @@ static void setCursorInView(bool enable)
if (warpSupport) if (warpSupport)
g_state.ds->ungrabPointer(); g_state.ds->ungrabPointer();
if (!g_cursor.grab)
g_cursor.realign = true;
} }
g_cursor.warpState = WARP_STATE_ON;
} }
void app_handleMouseGrabbed(double ex, double ey) void app_handleMouseGrabbed(double ex, double ey)
@ -985,16 +1011,14 @@ void app_handleMouseGrabbed(double ex, double ey)
static void guestCurToLocal(struct DoublePoint *local) static void guestCurToLocal(struct DoublePoint *local)
{ {
local->x = (g_cursor.guest.x + g_cursor.guest.hx) / g_cursor.scale.x; local->x = g_state.dstRect.x +
local->y = (g_cursor.guest.y + g_cursor.guest.hy) / g_cursor.scale.y; (g_cursor.guest.x + g_cursor.guest.hx) / g_cursor.scale.x;
local->y = g_state.dstRect.y +
(g_cursor.guest.y + g_cursor.guest.hy) / g_cursor.scale.y;
} }
void app_handleMouseNormal(double ex, double ey) void app_handleMouseNormal(double ex, double ey)
{ {
/* do not pass mouse events to the guest if we do not have focus */
if (!g_state.focused)
return;
// prevent cursor handling outside of capture if the position is not known // prevent cursor handling outside of capture if the position is not known
if (!g_cursor.guest.valid) if (!g_cursor.guest.valid)
return; return;
@ -1007,8 +1031,6 @@ void app_handleMouseNormal(double ex, double ey)
} }
bool testExit = true; bool testExit = true;
/* if the cursor was outside the viewport, check if it moved in */
if (!g_cursor.inView) if (!g_cursor.inView)
{ {
const bool inView = const bool inView =
@ -1017,32 +1039,42 @@ void app_handleMouseNormal(double ex, double ey)
g_cursor.pos.y >= g_state.dstRect.y && g_cursor.pos.y >= g_state.dstRect.y &&
g_cursor.pos.y < g_state.dstRect.y + g_state.dstRect.h; g_cursor.pos.y < g_state.dstRect.y + g_state.dstRect.h;
if (inView) setCursorInView(inView);
{
setCursorInView(true);
struct DoublePoint guest =
{
.x = (g_cursor.pos.x - g_state.dstRect.x) * g_cursor.scale.x,
.y = (g_cursor.pos.y - g_state.dstRect.y) * g_cursor.scale.y
};
/* 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;
}
else
{
/* nothing to do if the cursor is not in the guest window */
return;
}
} }
/* if we are in "autoCapture" and the delta was large don't test for exit */ /* 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 =
{
.x = (g_cursor.pos.x - g_state.dstRect.x) * g_cursor.scale.x,
.y = (g_cursor.pos.y - g_state.dstRect.y) * g_cursor.scale.y
};
/* 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 (params.autoCapture && if (params.autoCapture &&
(fabs(ex) > 100.0 / g_cursor.scale.x || fabs(ey) > 100.0 / g_cursor.scale.y)) (fabs(ex) > 100.0 / g_cursor.scale.x || fabs(ey) > 100.0 / g_cursor.scale.y))
testExit = false; testExit = false;
@ -1053,17 +1085,15 @@ void app_handleMouseNormal(double ex, double ey)
/* check if the move would push the cursor outside the guest's viewport */ /* check if the move would push the cursor outside the guest's viewport */
if (testExit && ( if (testExit && (
local.x + ex < 0.0 || local.x + ex < g_state.dstRect.x ||
local.y + ey < 0.0 || local.y + ey < g_state.dstRect.y ||
local.x + ex >= g_state.dstRect.w || local.x + ex >= g_state.dstRect.x + g_state.dstRect.w ||
local.y + ey >= g_state.dstRect.h)) local.y + ey >= g_state.dstRect.y + g_state.dstRect.h))
{ {
local.x += ex; local.x += ex;
local.y += ey; local.y += ey;
const int tx = ((local.x <= 0.0) ? floor(local.x) : ceil(local.x)) + const int tx = (local.x <= 0.0) ? floor(local.x) : ceil(local.x);
g_state.dstRect.x; const int ty = (local.y <= 0.0) ? floor(local.y) : ceil(local.y);
const int ty = ((local.y <= 0.0) ? floor(local.y) : ceil(local.y)) +
g_state.dstRect.y;
if (isValidCursorLocation( if (isValidCursorLocation(
g_state.windowPos.x + g_state.border.x + tx, g_state.windowPos.x + g_state.border.x + tx,
@ -1077,6 +1107,7 @@ void app_handleMouseNormal(double ex, double ey)
/* ungrab the pointer and move the local cursor to the exit point */ /* ungrab the pointer and move the local cursor to the exit point */
g_state.ds->ungrabPointer(); g_state.ds->ungrabPointer();
warpPointer(tx, ty, true); warpPointer(tx, ty, true);
return; return;
} }
@ -1133,8 +1164,7 @@ void app_handleMouseBasic()
g_cursor.pos.y >= g_state.dstRect.y && g_cursor.pos.y >= g_state.dstRect.y &&
g_cursor.pos.y < g_state.dstRect.y + g_state.dstRect.h; g_cursor.pos.y < g_state.dstRect.y + g_state.dstRect.h;
if (inView != g_cursor.inView) setCursorInView(inView);
setCursorInView(inView);
if (g_cursor.guest.dpiScale == 0) if (g_cursor.guest.dpiScale == 0)
return; return;
@ -1194,7 +1224,7 @@ void app_handleResizeEvent(int w, int h)
void app_handleWindowLeave(void) void app_handleWindowLeave(void)
{ {
g_cursor.inWindow = false; g_cursor.inWindow = false;
g_cursor.inView = false; setCursorInView(false);
if (!app_inputEnabled()) if (!app_inputEnabled())
return; return;
@ -1209,6 +1239,8 @@ void app_handleWindowEnter(void)
g_cursor.inWindow = true; g_cursor.inWindow = true;
if (!app_inputEnabled()) if (!app_inputEnabled())
return; return;
g_cursor.realign = true;
} }
static void setGrab(bool enable) static void setGrab(bool enable)
@ -1241,8 +1273,7 @@ static void setGrabQuiet(bool enable)
if (enable) if (enable)
{ {
if (!g_cursor.inView) setCursorInView(true);
setCursorInView(true);
if (params.grabKeyboard) if (params.grabKeyboard)
g_state.ds->grabKeyboard(); g_state.ds->grabKeyboard();
@ -1268,7 +1299,7 @@ static void setGrabQuiet(bool enable)
alignToGuest(); alignToGuest();
if (g_cursor.grab) if (g_cursor.grab)
g_cursor.inView = true; setCursorInView(true);
} }
int eventFilter(void * userdata, SDL_Event * event) int eventFilter(void * userdata, SDL_Event * event)
@ -1319,21 +1350,11 @@ int eventFilter(void * userdata, SDL_Event * event)
break; break;
case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_WINDOWEVENT_FOCUS_GAINED:
g_state.focused = true; app_handleFocusEvent(true);
if (!app_inputEnabled())
break;
if (params.grabKeyboardOnFocus)
g_state.ds->grabKeyboard();
break; break;
case SDL_WINDOWEVENT_FOCUS_LOST: case SDL_WINDOWEVENT_FOCUS_LOST:
g_state.focused = false; app_handleFocusEvent(false);
if (!app_inputEnabled())
break;
if (params.grabKeyboardOnFocus)
g_state.ds->ungrabKeyboard();
break; break;
case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_WINDOWEVENT_SIZE_CHANGED:
@ -1715,6 +1736,7 @@ static int lg_run(void)
SET_FALLBACK(grabPointer); SET_FALLBACK(grabPointer);
SET_FALLBACK(ungrabKeyboard); SET_FALLBACK(ungrabKeyboard);
SET_FALLBACK(warpPointer); SET_FALLBACK(warpPointer);
SET_FALLBACK(realignPointer);
SET_FALLBACK(cbInit); SET_FALLBACK(cbInit);
SET_FALLBACK(cbNotice); SET_FALLBACK(cbNotice);
SET_FALLBACK(cbRelease); SET_FALLBACK(cbRelease);

View File

@ -205,6 +205,9 @@ struct CursorState
/* true if the cursor is currently in the guest view area */ /* true if the cursor is currently in the guest view area */
bool inView; bool inView;
/* true if the guest should be realigned to the host when next drawn */
bool realign;
/* true if the cursor needs re-drawing/updating */ /* true if the cursor needs re-drawing/updating */
bool redraw; bool redraw;