[client] reworked the mouse tracking logic

This commit is contained in:
Geoffrey McRae 2020-12-04 00:32:28 +11:00
parent e57f084c93
commit 3df23d6b73
3 changed files with 162 additions and 118 deletions

View File

@ -73,8 +73,8 @@ typedef struct LG_RendererRect
bool valid; bool valid;
int x; int x;
int y; int y;
unsigned int w; int w;
unsigned int h; int h;
} }
LG_RendererRect; LG_RendererRect;

View File

@ -77,9 +77,20 @@ struct AppState state;
struct AppParams params = { 0 }; struct AppParams params = { 0 };
static void handleMouseMoveEvent(int ex, int ey); static void handleMouseMoveEvent(int ex, int ey);
static void alignMouseWithGuest();
static void alignMouseWithHost(); static void alignMouseWithHost();
static void lgInit()
{
state.state = APP_STATE_RUNNING;
state.scaleX = 1.0f;
state.scaleY = 1.0f;
state.resizeDone = true;
state.drawCursor = true;
state.haveCursorPos = false;
state.cursorInView = true;
}
static void updatePositionInfo() static void updatePositionInfo()
{ {
if (state.haveSrcSize) if (state.haveSrcSize)
@ -334,9 +345,6 @@ static int cursorThread(void * unused)
state.cursor.x = cursor->x; state.cursor.x = cursor->x;
state.cursor.y = cursor->y; state.cursor.y = cursor->y;
state.haveCursorPos = true; state.haveCursorPos = true;
if (state.haveSrcSize && state.haveCurLocal && !state.serverMode)
alignMouseWithGuest();
} }
lgmpClientMessageDone(queue); lgmpClientMessageDone(queue);
@ -761,49 +769,73 @@ void spiceClipboardRequest(const SpiceDataType type)
static void warpMouse(int x, int y) static void warpMouse(int x, int y)
{ {
if (state.warpState != WARP_STATE_ON) if (!state.cursorInWindow)
return; return;
state.warpFromX = state.curLastX; if (state.warpState == WARP_STATE_WIN_EXIT)
state.warpFromY = state.curLastY; {
SDL_WarpMouseInWindow(state.window, x, y);
state.warpState = WARP_STATE_OFF;
return;
}
if (state.warpState == WARP_STATE_ON)
{
state.warpToX = x; state.warpToX = x;
state.warpToY = y; state.warpToY = y;
state.warpState = WARP_STATE_ACTIVE; state.warpState = WARP_STATE_ACTIVE;
SDL_WarpMouseInWindow(state.window, x, y); SDL_WarpMouseInWindow(state.window, x, y);
}
} }
static void handleMouseMoveEvent(int ex, int ey) static void handleMouseMoveEvent(int ex, int ey)
{ {
if (!state.cursorInWindow)
return;
if (state.ignoreInput || !params.useSpiceInput)
return;
state.curLocalX = ex; state.curLocalX = ex;
state.curLocalY = ey; state.curLocalY = ey;
state.haveCurLocal = true; state.haveCurLocal = true;
if (state.ignoreInput || !params.useSpiceInput) SDL_Point delta = {
.x = ex - state.curLastX,
.y = ey - state.curLastY
};
if (delta.x == 0 && delta.y == 0)
return; return;
if (state.warpState == WARP_STATE_ACTIVE) state.curLastX = ex;
state.curLastY = ey;
if (state.warpState == WARP_STATE_ACTIVE &&
ex == state.warpToX && ey == state.warpToY)
{ {
if (ex == state.warpToX && ey == state.warpToY) state.warpState = WARP_STATE_ON;
{ return;
state.curLastX += state.warpToX - state.warpFromX;
state.curLastY += state.warpToY - state.warpFromY;
state.warpState = state.serverMode ? WARP_STATE_ON : WARP_STATE_ARMED;
}
} }
if (state.serverMode) if (!state.cursorInWindow)
{ return;
if (
ex < 100 || ex > state.windowW - 100 || if ((state.haveCursorPos || state.grabMouse) &&
ey < 100 || ey > state.windowH - 100) (ex < 100 || ex > state.windowW - 100 ||
ey < 100 || ey > state.windowH - 100))
{ {
warpMouse(state.windowW / 2, state.windowH / 2); warpMouse(state.windowW / 2, state.windowH / 2);
return;
} }
}
else /* if we don't have the current cursor pos just send cursor movements */
if (!state.haveCursorPos)
{ {
spice_mouse_motion(delta.x, delta.y);
return;
}
if (ex < state.dstRect.x || if (ex < state.dstRect.x ||
ex > state.dstRect.x + state.dstRect.w || ex > state.dstRect.x + state.dstRect.w ||
ey < state.dstRect.y || ey < state.dstRect.y ||
@ -811,76 +843,97 @@ static void handleMouseMoveEvent(int ex, int ey)
{ {
state.cursorInView = false; state.cursorInView = false;
state.updateCursor = true; state.updateCursor = true;
state.warpState = WARP_STATE_OFF;
if (params.useSpiceInput && !params.alwaysShowCursor) if (params.useSpiceInput && !params.alwaysShowCursor)
state.drawCursor = false; state.drawCursor = false;
return; return;
} }
}
if (!state.cursorInView) if (!state.cursorInView)
{ {
state.cursorInView = true; state.cursorInView = true;
state.updateCursor = true; state.updateCursor = true;
state.drawCursor = true; state.drawCursor = true;
if (state.warpState == WARP_STATE_ARMED)
state.warpState = WARP_STATE_ON;
} }
int rx = ex - state.curLastX; if (params.scaleMouseInput && !state.grabMouse)
int ry = ey - state.curLastY; {
state.curLastX = ex; state.accX += (float)delta.x * state.scaleX;
state.curLastY = ey; state.accY += (float)delta.y * state.scaleY;
delta.x = floor(state.accX);
delta.y = floor(state.accY);
state.accX -= delta.x;
state.accY -= delta.y;
}
if (rx == 0 && ry == 0) if (state.grabMouse && state.mouseSens != 0)
{
state.sensX += ((float)delta.x / 10.0f) * (state.mouseSens + 10);
state.sensY += ((float)delta.y / 10.0f) * (state.mouseSens + 10);
delta.x = floor(state.sensX);
delta.y = floor(state.sensY);
state.sensX -= delta.x;
state.sensY -= delta.y;
}
if (!state.grabMouse && state.warpState == WARP_STATE_ON)
{
/* check if the movement would exit the window */
const SDL_Point newPos = {
.x = (float)(state.cursor.x + delta.x) / state.scaleX,
.y = (float)(state.cursor.y + delta.y) / state.scaleY
};
if (newPos.x < 0 || newPos.x >= state.dstRect.w ||
newPos.y < 0 || newPos.y >= state.dstRect.h)
{
/* determine where to move the cursor to taking into account any borders
* if the aspect ratio is not being forced */
int nx, ny;
if (newPos.x < 0)
{
nx = newPos.x;
ny = newPos.y + state.dstRect.y;
}
else if(newPos.x >= state.dstRect.w)
{
nx = newPos.x + state.dstRect.x * 2;
ny = newPos.y + state.dstRect.y;
}
else if (newPos.y < 0)
{
nx = newPos.x + state.dstRect.x;
ny = newPos.y;
}
else if (newPos.y >= state.dstRect.h)
{
nx = newPos.x + state.dstRect.x;
ny = newPos.y + state.dstRect.y * 2;
}
/* put the mouse where it should be and disable warp */
state.warpState = WARP_STATE_WIN_EXIT;
warpMouse(nx, ny);
return; return;
}
if (params.scaleMouseInput && !state.serverMode)
{
state.accX += (float)rx * state.scaleX;
state.accY += (float)ry * state.scaleY;
rx = floor(state.accX);
ry = floor(state.accY);
state.accX -= rx;
state.accY -= ry;
} }
if (state.serverMode && state.mouseSens != 0) /* send the movement to the guest */
{ if (!spice_mouse_motion(delta.x, delta.y))
state.sensX += ((float)rx / 10.0f) * (state.mouseSens + 10);
state.sensY += ((float)ry / 10.0f) * (state.mouseSens + 10);
rx = floor(state.sensX);
ry = floor(state.sensY);
state.sensX -= rx;
state.sensY -= ry;
}
if (!spice_mouse_motion(rx, ry))
DEBUG_ERROR("failed to send mouse motion message"); DEBUG_ERROR("failed to send mouse motion message");
} }
static void alignMouseWithGuest()
{
if (state.ignoreInput || !params.useSpiceInput)
return;
state.curLastX = (int)round((float)(state.cursor.x + state.cursor.hx) / state.scaleX) + state.dstRect.x;
state.curLastY = (int)round((float)(state.cursor.y + state.cursor.hy) / state.scaleY) + state.dstRect.y;
warpMouse(state.curLastX, state.curLastY);
}
static void alignMouseWithHost() static void alignMouseWithHost()
{ {
if (state.ignoreInput || !params.useSpiceInput) if (state.ignoreInput || !params.useSpiceInput)
return; return;
if (!state.haveCursorPos || state.serverMode) if (!state.haveCursorPos)
return; return;
state.curLastX = (int)round((float)(state.cursor.x + state.cursor.hx) / state.scaleX) + state.dstRect.x; const int dx = round((state.curLocalX - state.dstRect.x) * state.scaleX) - state.cursor.x;
state.curLastY = (int)round((float)(state.cursor.y + state.cursor.hy) / state.scaleY) + state.dstRect.y; const int dy = round((state.curLocalY - state.dstRect.y) * state.scaleY) - state.cursor.y;
handleMouseMoveEvent(state.curLocalX, state.curLocalY); spice_mouse_motion(dx, dy);
} }
static void handleResizeEvent(unsigned int w, unsigned int h) static void handleResizeEvent(unsigned int w, unsigned int h)
@ -895,6 +948,8 @@ static void handleResizeEvent(unsigned int w, unsigned int h)
static void handleWindowLeave() static void handleWindowLeave()
{ {
state.cursorInWindow = false;
if (!params.useSpiceInput) if (!params.useSpiceInput)
return; return;
@ -903,18 +958,23 @@ static void handleWindowLeave()
state.cursorInView = false; state.cursorInView = false;
state.updateCursor = true; state.updateCursor = true;
state.warpState = WARP_STATE_OFF;
} }
static void handleWindowEnter() static void handleWindowEnter()
{ {
state.cursorInWindow = true;
if (state.warpState == WARP_STATE_OFF)
state.warpState = WARP_STATE_ON;
if (!params.useSpiceInput) if (!params.useSpiceInput)
return; return;
if (!state.haveCursorPos)
return;
alignMouseWithHost(); alignMouseWithHost();
state.drawCursor = true; state.drawCursor = true;
state.updateCursor = true; state.updateCursor = true;
state.warpState = WARP_STATE_ARMED;
} }
// only called for X11 // only called for X11
@ -1096,19 +1156,13 @@ int eventFilter(void * userdata, SDL_Event * event)
{ {
if (params.useSpiceInput) if (params.useSpiceInput)
{ {
state.serverMode = !state.serverMode; state.grabMouse = !state.grabMouse;
SDL_SetWindowGrab(state.window, state.serverMode); SDL_SetWindowGrab(state.window, state.grabMouse);
DEBUG_INFO("Server Mode: %s", state.serverMode ? "on" : "off");
app_alert( app_alert(
state.serverMode ? LG_ALERT_SUCCESS : LG_ALERT_WARNING, state.grabMouse ? LG_ALERT_SUCCESS : LG_ALERT_WARNING,
state.serverMode ? "Capture Enabled" : "Capture Disabled" state.grabMouse ? "Capture Enabled" : "Capture Disabled"
); );
if (state.serverMode)
state.warpState = WARP_STATE_ON;
else
alignMouseWithGuest();
} }
} }
else else
@ -1166,10 +1220,7 @@ int eventFilter(void * userdata, SDL_Event * event)
if (button > 3) if (button > 3)
button += 2; button += 2;
if ( if (!spice_mouse_press(button))
!spice_mouse_position(event->button.x, event->button.y) ||
!spice_mouse_press(button)
)
{ {
DEBUG_ERROR("SDL_MOUSEBUTTONDOWN: failed to send message"); DEBUG_ERROR("SDL_MOUSEBUTTONDOWN: failed to send message");
break; break;
@ -1186,10 +1237,7 @@ int eventFilter(void * userdata, SDL_Event * event)
if (button > 3) if (button > 3)
button += 2; button += 2;
if ( if (!spice_mouse_release(button))
!spice_mouse_position(event->button.x, event->button.y) ||
!spice_mouse_release(button)
)
{ {
DEBUG_ERROR("SDL_MOUSEBUTTONUP: failed to send message"); DEBUG_ERROR("SDL_MOUSEBUTTONUP: failed to send message");
break; break;
@ -1363,11 +1411,7 @@ static void release_key_binds()
static int lg_run() static int lg_run()
{ {
memset(&state, 0, sizeof(state)); memset(&state, 0, sizeof(state));
state.state = APP_STATE_RUNNING; lgInit();
state.scaleX = 1.0f;
state.scaleY = 1.0f;
state.resizeDone = true;
state.drawCursor = true;
state.mouseSens = params.mouseSens; state.mouseSens = params.mouseSens;
if (state.mouseSens < -9) state.mouseSens = -9; if (state.mouseSens < -9) state.mouseSens = -9;
@ -1610,9 +1654,8 @@ static int lg_run()
if (params.captureOnStart) if (params.captureOnStart)
{ {
state.serverMode = true; state.grabMouse = true;
SDL_SetWindowGrab(state.window, state.serverMode); SDL_SetWindowGrab(state.window, true);
DEBUG_INFO("Server Mode: %s", state.serverMode ? "on" : "off");
} }
// setup the startup condition // setup the startup condition
@ -1754,7 +1797,8 @@ restart:
t_frame = NULL; t_frame = NULL;
t_cursor = NULL; t_cursor = NULL;
state.state = APP_STATE_RUNNING; lgInit();
state.lgr->on_restart(state.lgrData); state.lgr->on_restart(state.lgrData);
DEBUG_INFO("Waiting for the host to restart..."); DEBUG_INFO("Waiting for the host to restart...");

View File

@ -44,9 +44,9 @@ struct CursorInfo
enum WarpState enum WarpState
{ {
WARP_STATE_ARMED,
WARP_STATE_ON, WARP_STATE_ON,
WARP_STATE_ACTIVE, WARP_STATE_ACTIVE,
WARP_STATE_WIN_EXIT,
WARP_STATE_OFF WARP_STATE_OFF
}; };
@ -66,8 +66,9 @@ struct AppState
LG_RendererRect dstRect; LG_RendererRect dstRect;
struct CursorInfo cursor; struct CursorInfo cursor;
bool cursorVisible; bool cursorVisible;
bool cursorInWindow;
bool serverMode; bool grabMouse;
bool haveCursorPos; bool haveCursorPos;
bool drawCursor; bool drawCursor;
bool cursorInView; bool cursorInView;
@ -83,7 +84,6 @@ struct AppState
bool haveAligned; bool haveAligned;
enum WarpState warpState; enum WarpState warpState;
int warpFromX, warpFromY;
int warpToX , warpToY; int warpToX , warpToY;
const LG_Renderer * lgr; const LG_Renderer * lgr;