mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-22 15:11:31 +00:00
[client] input: fix automatic keyboard capture
Treat autoCapture as keyboard ownership while the pointer is inside the guest viewport. Predict the next local position for both absolute and relative motion, then release the keyboard before the pointer crosses an edge. Keep capture, focus, and deferred evdev transitions consistent while retaining the latest local position as the prediction baseline.
This commit is contained in:
@@ -130,17 +130,22 @@ bool app_isOverlayMode(void)
|
|||||||
|
|
||||||
void app_updateCursorPos(double x, double y)
|
void app_updateCursorPos(double x, double y)
|
||||||
{
|
{
|
||||||
|
const bool overlay = app_isOverlayMode();
|
||||||
|
if (overlay)
|
||||||
|
{
|
||||||
g_cursor.pos.x = x;
|
g_cursor.pos.x = x;
|
||||||
g_cursor.pos.y = y;
|
g_cursor.pos.y = y;
|
||||||
g_cursor.valid = true;
|
g_cursor.valid = true;
|
||||||
|
g_cursor.motionValid = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
core_handleMousePosition(x, y);
|
||||||
|
|
||||||
MTRACE("pos pos=%.3f,%.3f inWin=%d inView=%d grab=%d",
|
MTRACE("pos pos=%.3f,%.3f inWin=%d inView=%d grab=%d",
|
||||||
x, y, g_cursor.inWindow, g_cursor.inView, g_cursor.grab);
|
x, y, g_cursor.inWindow, g_cursor.inView, g_cursor.grab);
|
||||||
|
|
||||||
if (app_isOverlayMode())
|
if (overlay)
|
||||||
g_state.io->MousePos = (ImVec2) { x, y };
|
g_state.io->MousePos = (ImVec2) { x, y };
|
||||||
else
|
|
||||||
core_handleMouseAbsolute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void app_updateMouseState(void)
|
void app_updateMouseState(void)
|
||||||
@@ -221,12 +226,20 @@ void app_handleFocusEvent(bool focused)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
g_state.focused = focused;
|
g_state.focused = focused;
|
||||||
|
g_cursor.motionValid = false;
|
||||||
|
const bool inputEnabled = core_inputEnabled();
|
||||||
|
|
||||||
// release any imgui buttons/keys if we lost focus
|
// release any imgui buttons/keys if we lost focus
|
||||||
if (!focused && app_isOverlayMode())
|
if (!focused && app_isOverlayMode())
|
||||||
core_resetOverlayInputState();
|
core_resetOverlayInputState();
|
||||||
|
|
||||||
if (!core_inputEnabled())
|
if (!focused)
|
||||||
|
{
|
||||||
|
core_setGrabQuiet(false);
|
||||||
|
core_setCursorInView(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inputEnabled)
|
||||||
{
|
{
|
||||||
if (!focused && g_params.minimizeOnFocusLoss && app_getFullscreen())
|
if (!focused && g_params.minimizeOnFocusLoss && app_getFullscreen())
|
||||||
g_state.ds->minimize();
|
g_state.ds->minimize();
|
||||||
@@ -235,9 +248,6 @@ void app_handleFocusEvent(bool focused)
|
|||||||
|
|
||||||
if (!focused)
|
if (!focused)
|
||||||
{
|
{
|
||||||
core_setGrabQuiet(false);
|
|
||||||
core_setCursorInView(false);
|
|
||||||
|
|
||||||
if (g_params.releaseKeysOnFocusLoss)
|
if (g_params.releaseKeysOnFocusLoss)
|
||||||
lgInput_releaseKeys();
|
lgInput_releaseKeys();
|
||||||
|
|
||||||
@@ -262,6 +272,8 @@ void app_handleEnterEvent(bool entered)
|
|||||||
MTRACE("enter set=%d inWin=%d inView=%d grab=%d",
|
MTRACE("enter set=%d inWin=%d inView=%d grab=%d",
|
||||||
entered, g_cursor.inWindow, g_cursor.inView, g_cursor.grab);
|
entered, g_cursor.inWindow, g_cursor.inView, g_cursor.grab);
|
||||||
|
|
||||||
|
g_cursor.motionValid = false;
|
||||||
|
|
||||||
if (entered)
|
if (entered)
|
||||||
{
|
{
|
||||||
g_cursor.inWindow = true;
|
g_cursor.inWindow = true;
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ static struct Option options[] =
|
|||||||
{
|
{
|
||||||
.module = "input",
|
.module = "input",
|
||||||
.name = "autoCapture",
|
.name = "autoCapture",
|
||||||
.description = "Try to keep the mouse captured when needed",
|
.description = "Grab the keyboard in the guest view and release it before exit",
|
||||||
.type = OPTION_TYPE_BOOL,
|
.type = OPTION_TYPE_BOOL,
|
||||||
.value.x_bool = false
|
.value.x_bool = false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -137,6 +137,34 @@ bool core_inputEnabled(void)
|
|||||||
((g_cursor.grab && g_params.captureInputOnly) || !g_params.captureInputOnly);
|
((g_cursor.grab && g_params.captureInputOnly) || !g_params.captureInputOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void updateKeyboardGrab(void)
|
||||||
|
{
|
||||||
|
const bool capture = g_cursor.grab && g_params.grabKeyboard;
|
||||||
|
const bool view = g_cursor.inView && g_params.grabKeyboardOnFocus;
|
||||||
|
const bool automatic = !g_cursor.grab &&
|
||||||
|
g_cursor.autoCaptureActive && !g_params.captureInputOnly;
|
||||||
|
const bool active = g_state.focused && core_inputEnabled() &&
|
||||||
|
!app_isOverlayMode() && (capture || view || automatic);
|
||||||
|
|
||||||
|
if (active)
|
||||||
|
g_state.ds->grabKeyboard();
|
||||||
|
else
|
||||||
|
g_state.ds->ungrabKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setAutoCapture(bool active)
|
||||||
|
{
|
||||||
|
active = active && g_params.autoCapture && g_cursor.inView &&
|
||||||
|
!g_cursor.grab && !g_params.captureInputOnly;
|
||||||
|
if (g_cursor.autoCaptureActive == active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
MTRACE("auto capture active=%d old=%d", active,
|
||||||
|
g_cursor.autoCaptureActive);
|
||||||
|
g_cursor.autoCaptureActive = active;
|
||||||
|
updateKeyboardGrab();
|
||||||
|
}
|
||||||
|
|
||||||
static void applyView(bool active, bool force)
|
static void applyView(bool active, bool force)
|
||||||
{
|
{
|
||||||
if (active && !g_cursor.viewReq)
|
if (active && !g_cursor.viewReq)
|
||||||
@@ -146,33 +174,36 @@ static void applyView(bool active, bool force)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!force && g_cursor.inView == active)
|
if (!force && g_cursor.inView == active)
|
||||||
|
{
|
||||||
|
updateKeyboardGrab();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
MTRACE("view active=%d old=%d req=%d force=%d", active,
|
MTRACE("view active=%d old=%d req=%d force=%d", active,
|
||||||
g_cursor.inView, g_cursor.viewReq, force);
|
g_cursor.inView, g_cursor.viewReq, force);
|
||||||
|
|
||||||
g_cursor.inView = active;
|
g_cursor.inView = active;
|
||||||
g_cursor.draw = (g_params.alwaysShowCursor || g_params.captureInputOnly)
|
g_cursor.draw =
|
||||||
|
(g_params.alwaysShowCursor || g_params.captureInputOnly)
|
||||||
? true : g_cursor.inView;
|
? true : g_cursor.inView;
|
||||||
g_cursor.redraw = true;
|
g_cursor.redraw = true;
|
||||||
|
g_cursor.motionValid = false;
|
||||||
|
g_cursor.autoCaptureActive = active && g_params.autoCapture &&
|
||||||
|
!g_cursor.grab && !g_params.captureInputOnly;
|
||||||
|
|
||||||
g_cursor.warpState = g_cursor.inView ? WARP_STATE_ON : WARP_STATE_OFF;
|
g_cursor.warpState = g_cursor.inView ? WARP_STATE_ON : WARP_STATE_OFF;
|
||||||
if (g_cursor.inView)
|
if (g_cursor.inView)
|
||||||
{
|
{
|
||||||
if (g_params.hideMouse)
|
if (g_params.hideMouse)
|
||||||
g_state.ds->setPointer(LG_POINTER_NONE);
|
g_state.ds->setPointer(LG_POINTER_NONE);
|
||||||
|
|
||||||
if (g_params.grabKeyboardOnFocus)
|
|
||||||
g_state.ds->grabKeyboard();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (g_params.hideMouse)
|
if (g_params.hideMouse)
|
||||||
g_state.ds->setPointer(LG_POINTER_SQUARE);
|
g_state.ds->setPointer(LG_POINTER_SQUARE);
|
||||||
|
|
||||||
g_state.ds->ungrabKeyboard();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateKeyboardGrab();
|
||||||
g_cursor.warpState = WARP_STATE_ON;
|
g_cursor.warpState = WARP_STATE_ON;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,9 +308,9 @@ void core_setGrabQuiet(bool enable)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
cancelSurfaceExit("capture");
|
cancelSurfaceExit("capture");
|
||||||
g_cursor.grab = enable;
|
|
||||||
g_cursor.acc.x = 0.0;
|
g_cursor.acc.x = 0.0;
|
||||||
g_cursor.acc.y = 0.0;
|
g_cursor.acc.y = 0.0;
|
||||||
|
g_cursor.motionValid = false;
|
||||||
|
|
||||||
/* if the display server does not support warp we need to ungrab the pointer
|
/* if the display server does not support warp we need to ungrab the pointer
|
||||||
* here instead of in the move handler */
|
* here instead of in the move handler */
|
||||||
@@ -288,8 +319,11 @@ void core_setGrabQuiet(bool enable)
|
|||||||
|
|
||||||
if (enable)
|
if (enable)
|
||||||
{
|
{
|
||||||
core_setCursorInView(true);
|
|
||||||
g_state.ignoreInput = false;
|
g_state.ignoreInput = false;
|
||||||
|
g_cursor.grab = true;
|
||||||
|
g_cursor.autoCaptureActive = false;
|
||||||
|
updateKeyboardGrab();
|
||||||
|
core_setCursorInView(true);
|
||||||
|
|
||||||
/* ensure the local mouse is inside the window before we capture, this fixes
|
/* ensure the local mouse is inside the window before we capture, this fixes
|
||||||
* odd UI behaviour if the user is using focus follows mouse and the window
|
* odd UI behaviour if the user is using focus follows mouse and the window
|
||||||
@@ -302,29 +336,21 @@ void core_setGrabQuiet(bool enable)
|
|||||||
core_warpPointer(local.x, local.y, true);
|
core_warpPointer(local.x, local.y, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_params.grabKeyboard)
|
|
||||||
g_state.ds->grabKeyboard();
|
|
||||||
|
|
||||||
g_state.ds->capturePointer();
|
g_state.ds->capturePointer();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (g_params.grabKeyboard)
|
|
||||||
{
|
|
||||||
if (!g_params.grabKeyboardOnFocus ||
|
|
||||||
!g_state.focused || g_params.captureInputOnly)
|
|
||||||
g_state.ds->ungrabKeyboard();
|
|
||||||
}
|
|
||||||
|
|
||||||
g_state.ds->uncapturePointer();
|
g_state.ds->uncapturePointer();
|
||||||
|
g_cursor.grab = false;
|
||||||
|
g_cursor.autoCaptureActive = g_params.autoCapture &&
|
||||||
|
g_cursor.inView && !g_params.captureInputOnly;
|
||||||
|
updateKeyboardGrab();
|
||||||
|
|
||||||
if (warpSupport == LG_DS_WARP_NONE)
|
if (warpSupport == LG_DS_WARP_NONE)
|
||||||
core_handleMouseAbsolute();
|
core_handleMouseAbsolute();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!g_params.captureInputOnly)
|
g_cursor.warpState = WARP_STATE_ON;
|
||||||
applyView(g_state.ds->isPointerGrabbed(), false);
|
|
||||||
|
|
||||||
core_alignToGuest();
|
core_alignToGuest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,6 +395,7 @@ bool core_warpPointer(int x, int y, bool exiting)
|
|||||||
|
|
||||||
MTRACE("warp send target=%d,%d exit=%d pos=%.3f,%.3f",
|
MTRACE("warp send target=%d,%d exit=%d pos=%.3f,%.3f",
|
||||||
x, y, exiting, g_cursor.pos.x, g_cursor.pos.y);
|
x, y, exiting, g_cursor.pos.x, g_cursor.pos.y);
|
||||||
|
g_cursor.motionValid = false;
|
||||||
g_state.ds->warpPointer(x, y, exiting);
|
g_state.ds->warpPointer(x, y, exiting);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -397,6 +424,7 @@ void core_onWindowSizeChanged(unsigned width, unsigned height)
|
|||||||
|
|
||||||
void core_updatePositionInfo(void)
|
void core_updatePositionInfo(void)
|
||||||
{
|
{
|
||||||
|
g_cursor.motionValid = false;
|
||||||
cancelExit("geometry");
|
cancelExit("geometry");
|
||||||
|
|
||||||
if (g_params.setGuestRes &&
|
if (g_params.setGuestRes &&
|
||||||
@@ -679,13 +707,12 @@ void core_handleGuestMouseUpdate(void)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void core_handleMouseAbsolute(void)
|
static void handleMouseAbsolute(bool inputEnabled, bool absolute)
|
||||||
{
|
{
|
||||||
if (g_cursor.grab || !g_cursor.inWindow || !g_cursor.valid ||
|
if (g_cursor.grab || !g_cursor.inWindow || !g_cursor.valid ||
|
||||||
!g_state.haveSrcSize || !g_state.posInfoValid ||
|
!g_state.haveSrcSize || !g_state.posInfoValid ||
|
||||||
!g_state.focused || g_cursor.realigning ||
|
!g_state.focused || g_cursor.realigning ||
|
||||||
app_isOverlayMode() || !core_inputEnabled() ||
|
app_isOverlayMode() || !inputEnabled || !absolute)
|
||||||
!lgInput_supports(LG_INPUT_SUPPORT_MOUSE_ABSOLUTE))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const bool inView = isInView();
|
const bool inView = isInView();
|
||||||
@@ -711,6 +738,79 @@ void core_handleMouseAbsolute(void)
|
|||||||
g_cursor.realign = false;
|
g_cursor.realign = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void core_handleMouseAbsolute(void)
|
||||||
|
{
|
||||||
|
const bool inputEnabled = core_inputEnabled();
|
||||||
|
const bool absolute = inputEnabled &&
|
||||||
|
lgInput_supports(LG_INPUT_SUPPORT_MOUSE_ABSOLUTE);
|
||||||
|
handleMouseAbsolute(inputEnabled, absolute);
|
||||||
|
}
|
||||||
|
|
||||||
|
void core_handleMousePosition(double x, double y)
|
||||||
|
{
|
||||||
|
bool allowPrediction = g_cursor.motionValid && !g_cursor.realign;
|
||||||
|
const double ex = allowPrediction ? x - g_cursor.pos.x : 0.0;
|
||||||
|
const double ey = allowPrediction ? y - g_cursor.pos.y : 0.0;
|
||||||
|
|
||||||
|
g_cursor.pos.x = x;
|
||||||
|
g_cursor.pos.y = y;
|
||||||
|
g_cursor.valid = true;
|
||||||
|
g_cursor.motionValid = true;
|
||||||
|
|
||||||
|
const bool inputEnabled = core_inputEnabled();
|
||||||
|
const bool absolute = inputEnabled &&
|
||||||
|
lgInput_supports(LG_INPUT_SUPPORT_MOUSE_ABSOLUTE);
|
||||||
|
const bool canTrack = !g_cursor.grab && g_cursor.inWindow &&
|
||||||
|
g_state.haveSrcSize && g_state.posInfoValid &&
|
||||||
|
g_state.dstRect.valid && g_state.focused &&
|
||||||
|
!app_isOverlayMode() && inputEnabled;
|
||||||
|
if (canTrack && !g_cursor.realigning)
|
||||||
|
{
|
||||||
|
const bool inView = isInView();
|
||||||
|
if (g_cursor.viewReq != inView || g_cursor.inView != inView)
|
||||||
|
{
|
||||||
|
core_setCursorInView(inView);
|
||||||
|
allowPrediction = false;
|
||||||
|
g_cursor.motionValid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMouseAbsolute(inputEnabled, absolute);
|
||||||
|
|
||||||
|
if (!canTrack)
|
||||||
|
{
|
||||||
|
g_cursor.motionValid = false;
|
||||||
|
setAutoCapture(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_cursor.realigning)
|
||||||
|
{
|
||||||
|
g_cursor.motionValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool active = g_cursor.inView && g_cursor.viewReq;
|
||||||
|
if (!allowPrediction || !g_params.autoCapture ||
|
||||||
|
!active || g_cursor.buttons)
|
||||||
|
{
|
||||||
|
setAutoCapture(active);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const double nextX = x + ex;
|
||||||
|
const double nextY = y + ey;
|
||||||
|
active =
|
||||||
|
nextX >= g_state.dstRect.x &&
|
||||||
|
nextX < g_state.dstRect.x + g_state.dstRect.w &&
|
||||||
|
nextY >= g_state.dstRect.y &&
|
||||||
|
nextY < g_state.dstRect.y + g_state.dstRect.h;
|
||||||
|
|
||||||
|
MTRACE("auto capture move=%.3f,%.3f next=%.3f,%.3f inView=%d",
|
||||||
|
ex, ey, nextX, nextY, active);
|
||||||
|
setAutoCapture(active);
|
||||||
|
}
|
||||||
|
|
||||||
void core_handleMouseGrabbed(double ex, double ey)
|
void core_handleMouseGrabbed(double ex, double ey)
|
||||||
{
|
{
|
||||||
const bool active = g_cursor.grab && g_state.ds->isPointerCaptured();
|
const bool active = g_cursor.grab && g_state.ds->isPointerCaptured();
|
||||||
@@ -911,11 +1011,6 @@ fallback:
|
|||||||
testExit = false;
|
testExit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if we are in "autoCapture" and the delta was large don't test for exit */
|
|
||||||
if (g_params.autoCapture &&
|
|
||||||
(fabs(ex) > 20.0 / g_cursor.scale.x || fabs(ey) > 20.0 / g_cursor.scale.y))
|
|
||||||
testExit = false;
|
|
||||||
|
|
||||||
/* if any buttons are held we should not allow exit to happen */
|
/* if any buttons are held we should not allow exit to happen */
|
||||||
if (g_cursor.buttons)
|
if (g_cursor.buttons)
|
||||||
testExit = false;
|
testExit = false;
|
||||||
@@ -989,6 +1084,9 @@ fallback:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g_params.autoCapture && testExit)
|
||||||
|
setAutoCapture(!didExit);
|
||||||
|
|
||||||
if (absolute)
|
if (absolute)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -998,25 +1096,11 @@ fallback:
|
|||||||
if (x == 0 && y == 0)
|
if (x == 0 && y == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (g_params.autoCapture)
|
|
||||||
{
|
|
||||||
g_cursor.delta.x += x;
|
|
||||||
g_cursor.delta.y += y;
|
|
||||||
|
|
||||||
if (fabs(g_cursor.delta.x) > 50.0 || fabs(g_cursor.delta.y) > 50.0)
|
|
||||||
{
|
|
||||||
g_cursor.delta.x = 0;
|
|
||||||
g_cursor.delta.y = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* assume the mouse will move to the location we attempt to move it to so we
|
/* assume the mouse will move to the location we attempt to move it to so we
|
||||||
* avoid warp out of window issues. The cursorThread will correct this if
|
* avoid warp out of window issues. The cursorThread will correct this if
|
||||||
* wrong after the movement has ocurred on the guest */
|
* wrong after the movement has ocurred on the guest */
|
||||||
g_cursor.guest.x += x;
|
g_cursor.guest.x += x;
|
||||||
g_cursor.guest.y += y;
|
g_cursor.guest.y += y;
|
||||||
}
|
|
||||||
|
|
||||||
MTRACE("motion delta=%d,%d guest=%d,%d exit=%d test=%d warp=%d",
|
MTRACE("motion delta=%d,%d guest=%d,%d exit=%d test=%d warp=%d",
|
||||||
x, y, g_cursor.guest.x, g_cursor.guest.y, didExit, testExit,
|
x, y, g_cursor.guest.x, g_cursor.guest.y, didExit, testExit,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ bool core_startFrameThread(void);
|
|||||||
void core_stopFrameThread(void);
|
void core_stopFrameThread(void);
|
||||||
void core_handleGuestMouseUpdate(void);
|
void core_handleGuestMouseUpdate(void);
|
||||||
void core_handleMouseAbsolute(void);
|
void core_handleMouseAbsolute(void);
|
||||||
|
void core_handleMousePosition(double x, double y);
|
||||||
void core_handleMouseGrabbed(double ex, double ey);
|
void core_handleMouseGrabbed(double ex, double ey);
|
||||||
void core_handleMouseNormal(double ex, double ey);
|
void core_handleMouseNormal(double ex, double ey);
|
||||||
void core_resetOverlayInputState(void);
|
void core_resetOverlayInputState(void);
|
||||||
|
|||||||
@@ -381,6 +381,7 @@ void evdev_stop(void)
|
|||||||
|
|
||||||
void evdev_grabKeyboard(void)
|
void evdev_grabKeyboard(void)
|
||||||
{
|
{
|
||||||
|
state.pending = PENDING_NONE;
|
||||||
if (state.grabbed)
|
if (state.grabbed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -405,6 +406,7 @@ void evdev_grabKeyboard(void)
|
|||||||
|
|
||||||
void evdev_ungrabKeyboard(void)
|
void evdev_ungrabKeyboard(void)
|
||||||
{
|
{
|
||||||
|
state.pending = PENDING_NONE;
|
||||||
if (!state.grabbed)
|
if (!state.grabbed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -316,12 +316,15 @@ struct CursorState
|
|||||||
/* true if the position is valid */
|
/* true if the position is valid */
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
|
/* true if the last local position can be used to predict motion */
|
||||||
|
bool motionValid;
|
||||||
|
|
||||||
|
/* true if auto capture currently requests the keyboard */
|
||||||
|
bool autoCaptureActive;
|
||||||
|
|
||||||
/* the button state */
|
/* the button state */
|
||||||
unsigned int buttons;
|
unsigned int buttons;
|
||||||
|
|
||||||
/* the delta since last warp when in auto capture mode */
|
|
||||||
struct DoublePoint delta;
|
|
||||||
|
|
||||||
/* the scale factor for the mouse sensitiviy */
|
/* the scale factor for the mouse sensitiviy */
|
||||||
int sens;
|
int sens;
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,7 @@ set(MOUSE_CASES
|
|||||||
exit-immediate
|
exit-immediate
|
||||||
exit-guest
|
exit-guest
|
||||||
absolute-sync
|
absolute-sync
|
||||||
|
auto-capture
|
||||||
exit-reentry
|
exit-reentry
|
||||||
view-immediate
|
view-immediate
|
||||||
capture-pending
|
capture-pending
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ enum EvType
|
|||||||
EV_GUEST,
|
EV_GUEST,
|
||||||
EV_MOTION,
|
EV_MOTION,
|
||||||
EV_VALID,
|
EV_VALID,
|
||||||
|
EV_KEY_GRAB,
|
||||||
|
EV_KEY_UNGRAB,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Ev
|
struct Ev
|
||||||
@@ -68,6 +70,7 @@ static struct
|
|||||||
struct Trap lock;
|
struct Trap lock;
|
||||||
bool valid;
|
bool valid;
|
||||||
bool captureGrab;
|
bool captureGrab;
|
||||||
|
bool keyboardGrabbed;
|
||||||
struct Ev ev[256];
|
struct Ev ev[256];
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
}
|
}
|
||||||
@@ -119,8 +122,22 @@ static void setPointer(LG_DSPointer pointer)
|
|||||||
(void)pointer;
|
(void)pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void keyNoop(void)
|
static void keyGrab(void)
|
||||||
{
|
{
|
||||||
|
if (m.keyboardGrabbed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m.keyboardGrabbed = true;
|
||||||
|
push(EV_KEY_GRAB, 0, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void keyUngrab(void)
|
||||||
|
{
|
||||||
|
if (!m.keyboardGrabbed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m.keyboardGrabbed = false;
|
||||||
|
push(EV_KEY_UNGRAB, 0, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void grab(void)
|
static void grab(void)
|
||||||
@@ -209,8 +226,8 @@ static struct LG_DisplayServerOps ds = {
|
|||||||
.name = "test",
|
.name = "test",
|
||||||
.guestPointerUpdated = guest,
|
.guestPointerUpdated = guest,
|
||||||
.setPointer = setPointer,
|
.setPointer = setPointer,
|
||||||
.grabKeyboard = keyNoop,
|
.grabKeyboard = keyGrab,
|
||||||
.ungrabKeyboard = keyNoop,
|
.ungrabKeyboard = keyUngrab,
|
||||||
.grabPointer = grab,
|
.grabPointer = grab,
|
||||||
.ungrabPointer = ungrab,
|
.ungrabPointer = ungrab,
|
||||||
.isPointerGrabbed = grabbed,
|
.isPointerGrabbed = grabbed,
|
||||||
@@ -511,6 +528,141 @@ static void testAbsoluteSync(void)
|
|||||||
lgInput_setFallback(&inputOps, NULL);
|
lgInput_setFallback(&inputOps, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void testAutoCapture(void)
|
||||||
|
{
|
||||||
|
static const enum LG_DSWarpSupport supports[] = {
|
||||||
|
LG_DS_WARP_SURFACE,
|
||||||
|
LG_DS_WARP_SCREEN,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < sizeof(supports) / sizeof(supports[0]); ++i)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
m.support = supports[i];
|
||||||
|
g_params.autoCapture = true;
|
||||||
|
g_cursor.inView = false;
|
||||||
|
g_cursor.viewReq = false;
|
||||||
|
setLocal(50, 50);
|
||||||
|
|
||||||
|
core_invalidatePointer(true);
|
||||||
|
CHECK(m.keyboardGrabbed);
|
||||||
|
CHECK(count(EV_KEY_GRAB) == 1);
|
||||||
|
|
||||||
|
setLocal(109, 50);
|
||||||
|
m.count = 0;
|
||||||
|
core_handleMouseNormal(1, 0);
|
||||||
|
CHECK(!m.keyboardGrabbed);
|
||||||
|
CHECK(count(EV_KEY_UNGRAB) == 1);
|
||||||
|
CHECK(g_cursor.exit);
|
||||||
|
CHECK(!g_cursor.inView);
|
||||||
|
CHECK(!g_cursor.viewReq);
|
||||||
|
|
||||||
|
setLocal(108, 50);
|
||||||
|
m.count = 0;
|
||||||
|
core_handleMouseNormal(-1, 0);
|
||||||
|
CHECK(m.keyboardGrabbed);
|
||||||
|
CHECK(count(EV_KEY_GRAB) == 1);
|
||||||
|
CHECK(!g_cursor.exit);
|
||||||
|
CHECK(g_cursor.inView);
|
||||||
|
CHECK(g_cursor.viewReq);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool absolute = true;
|
||||||
|
lgInput_setFallback(&inputOps, &absolute);
|
||||||
|
reset();
|
||||||
|
g_params.autoCapture = true;
|
||||||
|
g_cursor.inView = false;
|
||||||
|
g_cursor.viewReq = false;
|
||||||
|
g_cursor.valid = true;
|
||||||
|
setLocal(50, 50);
|
||||||
|
|
||||||
|
core_handleMousePosition(50, 50);
|
||||||
|
CHECK(m.keyboardGrabbed);
|
||||||
|
CHECK(count(EV_KEY_GRAB) == 1);
|
||||||
|
|
||||||
|
g_cursor.motionValid = false;
|
||||||
|
core_handleMousePosition(108, 50);
|
||||||
|
m.count = 0;
|
||||||
|
core_handleMousePosition(109, 50);
|
||||||
|
CHECK(!m.keyboardGrabbed);
|
||||||
|
CHECK(count(EV_KEY_UNGRAB) == 1);
|
||||||
|
CHECK(g_cursor.inView);
|
||||||
|
CHECK(g_cursor.viewReq);
|
||||||
|
|
||||||
|
m.count = 0;
|
||||||
|
core_handleMousePosition(108, 50);
|
||||||
|
CHECK(m.keyboardGrabbed);
|
||||||
|
CHECK(count(EV_KEY_GRAB) == 1);
|
||||||
|
CHECK(g_cursor.inView);
|
||||||
|
CHECK(g_cursor.viewReq);
|
||||||
|
|
||||||
|
reset();
|
||||||
|
g_params.autoCapture = true;
|
||||||
|
g_cursor.inView = false;
|
||||||
|
g_cursor.viewReq = false;
|
||||||
|
g_cursor.valid = true;
|
||||||
|
g_cursor.motionValid = true;
|
||||||
|
setLocal(9, 50);
|
||||||
|
|
||||||
|
core_handleMousePosition(109, 50);
|
||||||
|
CHECK(m.keyboardGrabbed);
|
||||||
|
CHECK(count(EV_KEY_GRAB) == 1);
|
||||||
|
CHECK(g_cursor.motionValid);
|
||||||
|
|
||||||
|
m.count = 0;
|
||||||
|
core_handleMousePosition(109.5, 50);
|
||||||
|
CHECK(!m.keyboardGrabbed);
|
||||||
|
CHECK(count(EV_KEY_UNGRAB) == 1);
|
||||||
|
|
||||||
|
reset();
|
||||||
|
g_params.autoCapture = true;
|
||||||
|
g_cursor.inView = false;
|
||||||
|
g_cursor.viewReq = false;
|
||||||
|
g_cursor.valid = true;
|
||||||
|
g_state.posInfoValid = false;
|
||||||
|
setLocal(50, 50);
|
||||||
|
|
||||||
|
core_handleMousePosition(50, 50);
|
||||||
|
CHECK(!m.keyboardGrabbed);
|
||||||
|
CHECK(!g_cursor.inView);
|
||||||
|
CHECK(!g_cursor.viewReq);
|
||||||
|
|
||||||
|
g_state.posInfoValid = true;
|
||||||
|
g_cursor.realigning = true;
|
||||||
|
core_handleMousePosition(50, 50);
|
||||||
|
CHECK(!m.keyboardGrabbed);
|
||||||
|
CHECK(!g_cursor.inView);
|
||||||
|
CHECK(!g_cursor.viewReq);
|
||||||
|
|
||||||
|
lgInput_setFallback(&inputOps, NULL);
|
||||||
|
|
||||||
|
reset();
|
||||||
|
g_params.grabKeyboard = true;
|
||||||
|
g_state.ignoreInput = true;
|
||||||
|
|
||||||
|
core_setGrabQuiet(true);
|
||||||
|
CHECK(!g_state.ignoreInput);
|
||||||
|
CHECK(m.keyboardGrabbed);
|
||||||
|
CHECK(count(EV_KEY_GRAB) == 1);
|
||||||
|
|
||||||
|
reset();
|
||||||
|
m.support = LG_DS_WARP_SCREEN;
|
||||||
|
m.captureGrab = true;
|
||||||
|
m.conf.req = false;
|
||||||
|
m.conf.on = false;
|
||||||
|
g_params.autoCapture = true;
|
||||||
|
g_params.grabKeyboard = true;
|
||||||
|
g_cursor.autoCaptureActive = true;
|
||||||
|
keyGrab();
|
||||||
|
|
||||||
|
core_setGrabQuiet(true);
|
||||||
|
m.count = 0;
|
||||||
|
core_setGrabQuiet(false);
|
||||||
|
CHECK(m.keyboardGrabbed);
|
||||||
|
CHECK(count(EV_KEY_GRAB) == 0);
|
||||||
|
CHECK(count(EV_KEY_UNGRAB) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
static void testExitReentry(void)
|
static void testExitReentry(void)
|
||||||
{
|
{
|
||||||
startExit();
|
startExit();
|
||||||
@@ -624,7 +776,6 @@ static void testCapAlign(void)
|
|||||||
|
|
||||||
m.lock.on = true;
|
m.lock.on = true;
|
||||||
g_cursor.guest.x += 10;
|
g_cursor.guest.x += 10;
|
||||||
g_cursor.warpState = WARP_STATE_ON;
|
|
||||||
m.count = 0;
|
m.count = 0;
|
||||||
core_setGrabQuiet(false);
|
core_setGrabQuiet(false);
|
||||||
|
|
||||||
@@ -925,6 +1076,7 @@ static const struct Test tests[] = {
|
|||||||
{ "exit-immediate" , testExitImmediate},
|
{ "exit-immediate" , testExitImmediate},
|
||||||
{ "exit-guest" , testExitGuest },
|
{ "exit-guest" , testExitGuest },
|
||||||
{ "absolute-sync" , testAbsoluteSync },
|
{ "absolute-sync" , testAbsoluteSync },
|
||||||
|
{ "auto-capture" , testAutoCapture },
|
||||||
{ "exit-reentry" , testExitReentry },
|
{ "exit-reentry" , testExitReentry },
|
||||||
{ "view-immediate" , testViewImmediate},
|
{ "view-immediate" , testViewImmediate},
|
||||||
{ "capture-pending" , testCapWait },
|
{ "capture-pending" , testCapWait },
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ All command line options
|
|||||||
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
|
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
|
||||||
| input:mouseTrace | | no | Enable mouse input diagnostics |
|
| input:mouseTrace | | no | Enable mouse input diagnostics |
|
||||||
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
|
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
|
||||||
| input:autoCapture | | no | Try to keep the mouse captured when needed |
|
| input:autoCapture | | no | Grab the keyboard in the guest view and release it before exit |
|
||||||
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
|
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
|
||||||
| input:captureOnly | | no | Only enable input via SPICE if in capture mode |
|
| input:captureOnly | | no | Only enable input via SPICE if in capture mode |
|
||||||
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
|
+------------------------------+-------+---------------------+----------------------------------------------------------------------------------------------------------+
|
||||||
|
|||||||
Reference in New Issue
Block a user