mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-08-09 00:31:31 +00:00
[client] input: track pointer confinement state
This commit is contained in:
@@ -196,6 +196,9 @@ static void confinedHandler(void * data,
|
||||
atomic_store_explicit(&wlWm.confActive, true, memory_order_release);
|
||||
LG_UNLOCK(wlWm.surfaceLock);
|
||||
MTRACE("conf active=1 id=%u valid=%d", proxyId(pointer), valid);
|
||||
|
||||
if (valid)
|
||||
app_handleGrabEvent(true);
|
||||
}
|
||||
|
||||
static void unconfinedHandler(void * data,
|
||||
@@ -208,6 +211,9 @@ static void unconfinedHandler(void * data,
|
||||
atomic_store_explicit(&wlWm.confActive, false, memory_order_release);
|
||||
LG_UNLOCK(wlWm.surfaceLock);
|
||||
MTRACE("conf active=0 id=%u valid=%d", proxyId(pointer), valid);
|
||||
|
||||
if (valid)
|
||||
app_handleGrabEvent(false);
|
||||
}
|
||||
|
||||
static const struct zwp_confined_pointer_v1_listener confinedListener = {
|
||||
@@ -215,6 +221,65 @@ static const struct zwp_confined_pointer_v1_listener confinedListener = {
|
||||
.unconfined = unconfinedHandler,
|
||||
};
|
||||
|
||||
static struct zwp_confined_pointer_v1 * createConfine(
|
||||
struct wl_region * region);
|
||||
static bool queueConfSync(void);
|
||||
|
||||
static void confSyncHandler(void * data, struct wl_callback * callback,
|
||||
uint32_t serial)
|
||||
{
|
||||
bool notify = false;
|
||||
bool valid = false;
|
||||
const uint32_t id = proxyId(callback);
|
||||
uint32_t confId = 0;
|
||||
uint64_t confSeq = 0;
|
||||
|
||||
LG_LOCK(wlWm.surfaceLock);
|
||||
if (callback == wlWm.confSync)
|
||||
{
|
||||
valid = true;
|
||||
wl_callback_destroy(callback);
|
||||
wlWm.confSync = NULL;
|
||||
atomic_store_explicit(&wlWm.confActive, false,
|
||||
memory_order_release);
|
||||
notify = wlWm.inputLive;
|
||||
|
||||
if (wlWm.inputLive && wlWm.confReq && wlWm.pointer &&
|
||||
wlWm.pointerConstraints && !wlWm.confinedPointer &&
|
||||
!wlWm.lockedPointer)
|
||||
{
|
||||
wlWm.confinedPointer = createConfine(NULL);
|
||||
confId = proxyId(wlWm.confinedPointer);
|
||||
confSeq = app_mouseSeq();
|
||||
}
|
||||
}
|
||||
LG_UNLOCK(wlWm.surfaceLock);
|
||||
|
||||
MTRACE("conf sync id=%u serial=%u valid=%d notify=%d", id, serial,
|
||||
valid, notify);
|
||||
MLOG(confSeq, "conf req id=%u why=sync", confId);
|
||||
|
||||
if (notify)
|
||||
app_handleGrabEvent(false);
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener confSyncListener = {
|
||||
.done = confSyncHandler,
|
||||
};
|
||||
|
||||
static bool queueConfSync(void)
|
||||
{
|
||||
if (wlWm.confSync)
|
||||
return true;
|
||||
|
||||
wlWm.confSync = wl_display_sync(wlWm.display);
|
||||
if (!wlWm.confSync)
|
||||
return false;
|
||||
|
||||
wl_callback_add_listener(wlWm.confSync, &confSyncListener, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void lockedHandler(void * data,
|
||||
struct zwp_locked_pointer_v1 * pointer)
|
||||
{
|
||||
@@ -437,14 +502,21 @@ static const struct wl_keyboard_listener keyboardListener = {
|
||||
.modifiers = keyboardModifiersHandler,
|
||||
};
|
||||
|
||||
static void waylandCleanUpPointer(void)
|
||||
static void waylandCleanUpPointer(bool notify)
|
||||
{
|
||||
bool event = false;
|
||||
uint32_t lockId = 0;
|
||||
uint32_t confId = 0;
|
||||
uint64_t lockSeq = 0;
|
||||
uint64_t confSeq = 0;
|
||||
INTERLOCKED_SECTION(wlWm.surfaceLock,
|
||||
{
|
||||
if (wlWm.confSync)
|
||||
{
|
||||
wl_callback_destroy(wlWm.confSync);
|
||||
wlWm.confSync = NULL;
|
||||
}
|
||||
|
||||
atomic_store_explicit(&wlWm.lockActive, false, memory_order_release);
|
||||
atomic_store_explicit(&wlWm.confActive, false, memory_order_release);
|
||||
|
||||
@@ -463,19 +535,25 @@ static void waylandCleanUpPointer(void)
|
||||
wlWm.confinedPointer = NULL;
|
||||
confSeq = app_mouseSeq();
|
||||
}
|
||||
|
||||
if (wlWm.relativePointer)
|
||||
{
|
||||
zwp_relative_pointer_v1_destroy(wlWm.relativePointer);
|
||||
wlWm.relativePointer = NULL;
|
||||
}
|
||||
|
||||
wl_pointer_destroy(wlWm.pointer);
|
||||
wlWm.pointer = NULL;
|
||||
wlWm.pointerInSurface = false;
|
||||
|
||||
event = notify && wlWm.inputLive;
|
||||
});
|
||||
|
||||
MLOG(lockSeq, "lock destroy id=%u why=pointer", lockId);
|
||||
MLOG(confSeq, "conf destroy id=%u why=pointer", confId);
|
||||
|
||||
if (wlWm.relativePointer)
|
||||
{
|
||||
zwp_relative_pointer_v1_destroy(wlWm.relativePointer);
|
||||
wlWm.relativePointer = NULL;
|
||||
}
|
||||
|
||||
wl_pointer_destroy(wlWm.pointer);
|
||||
wlWm.pointer = NULL;
|
||||
if (event)
|
||||
app_handleGrabEvent(false);
|
||||
}
|
||||
|
||||
// Seat-handling listeners.
|
||||
@@ -484,7 +562,7 @@ static void handlePointerCapability(uint32_t capabilities)
|
||||
{
|
||||
bool hasPointer = capabilities & WL_SEAT_CAPABILITY_POINTER;
|
||||
if (!hasPointer && wlWm.pointer)
|
||||
waylandCleanUpPointer();
|
||||
waylandCleanUpPointer(true);
|
||||
else if (hasPointer && !wlWm.pointer)
|
||||
{
|
||||
wlWm.pointer = wl_seat_get_pointer(wlWm.seat);
|
||||
@@ -502,6 +580,16 @@ static void handlePointerCapability(uint32_t capabilities)
|
||||
|
||||
if (app_isCaptureMode())
|
||||
waylandCapturePointer();
|
||||
else
|
||||
{
|
||||
bool confReq;
|
||||
INTERLOCKED_SECTION(wlWm.surfaceLock,
|
||||
{
|
||||
confReq = wlWm.confReq;
|
||||
});
|
||||
if (confReq)
|
||||
waylandGrabPointer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,6 +666,10 @@ bool waylandInputInit(bool allowNoInput)
|
||||
wlWm.warpSupport, !!wlWm.relativePointerManager,
|
||||
!!wlWm.pointerConstraints);
|
||||
|
||||
LG_LOCK(wlWm.surfaceLock);
|
||||
wlWm.inputLive = true;
|
||||
LG_UNLOCK(wlWm.surfaceLock);
|
||||
|
||||
wlWm.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
||||
if (!wlWm.xkb)
|
||||
DEBUG_WARN("Failed to initialize xkb, keyboard input will not work");
|
||||
@@ -590,13 +682,15 @@ bool waylandInputInit(bool allowNoInput)
|
||||
|
||||
void waylandInputFree(void)
|
||||
{
|
||||
LG_LOCK(wlWm.surfaceLock);
|
||||
wlWm.inputLive = false;
|
||||
LG_UNLOCK(wlWm.surfaceLock);
|
||||
|
||||
if (!wlWm.seat)
|
||||
return;
|
||||
|
||||
waylandUngrabPointer();
|
||||
|
||||
if (wlWm.pointer)
|
||||
waylandCleanUpPointer();
|
||||
waylandCleanUpPointer(false);
|
||||
|
||||
// The only legal way the keyboard can be null is if it never existed.
|
||||
// When unplugged, the compositor must have an inert object.
|
||||
@@ -617,59 +711,56 @@ void waylandInputFree(void)
|
||||
|
||||
void waylandGrabPointer(void)
|
||||
{
|
||||
if (!wlWm.pointer ||
|
||||
!wlWm.relativePointerManager || !wlWm.pointerConstraints)
|
||||
{
|
||||
MTRACE("grab drop pointer=%d relMgr=%d constraints=%d",
|
||||
!!wlWm.pointer, !!wlWm.relativePointerManager,
|
||||
!!wlWm.pointerConstraints);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!wlWm.warpSupport && !wlWm.relativePointer)
|
||||
{
|
||||
struct zwp_relative_pointer_v1 * relative =
|
||||
zwp_relative_pointer_manager_v1_get_relative_pointer(
|
||||
wlWm.relativePointerManager, wlWm.pointer);
|
||||
zwp_relative_pointer_v1_add_listener(relative,
|
||||
&relativePointerListener, NULL);
|
||||
const uint32_t relativeId = proxyId(relative);
|
||||
wlWm.relativePointer = relative;
|
||||
MTRACE("rel req id=%u why=grab", relativeId);
|
||||
}
|
||||
|
||||
uint32_t confId = 0;
|
||||
uint64_t confSeq = 0;
|
||||
bool haveConf = false;
|
||||
bool haveLock = false;
|
||||
uint32_t relativeId = 0;
|
||||
uint32_t confId = 0;
|
||||
uint64_t confSeq = 0;
|
||||
bool haveConf = false;
|
||||
bool haveLock = false;
|
||||
bool haveSync = false;
|
||||
bool havePointer = false;
|
||||
bool haveConstraints = false;
|
||||
INTERLOCKED_SECTION(wlWm.surfaceLock,
|
||||
{
|
||||
if (!wlWm.confinedPointer && !wlWm.lockedPointer)
|
||||
wlWm.confReq = true;
|
||||
|
||||
if (wlWm.pointer && !wlWm.warpSupport && !wlWm.relativePointer &&
|
||||
wlWm.relativePointerManager)
|
||||
{
|
||||
wlWm.relativePointer =
|
||||
zwp_relative_pointer_manager_v1_get_relative_pointer(
|
||||
wlWm.relativePointerManager, wlWm.pointer);
|
||||
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
|
||||
&relativePointerListener, NULL);
|
||||
relativeId = proxyId(wlWm.relativePointer);
|
||||
}
|
||||
|
||||
if (wlWm.pointer && wlWm.pointerConstraints &&
|
||||
!wlWm.confinedPointer && !wlWm.lockedPointer && !wlWm.confSync)
|
||||
{
|
||||
atomic_store_explicit(&wlWm.confActive, false,
|
||||
memory_order_release);
|
||||
wlWm.confinedPointer = createConfine(NULL);
|
||||
confId = proxyId(wlWm.confinedPointer);
|
||||
confSeq = app_mouseSeq();
|
||||
}
|
||||
haveConf = !!wlWm.confinedPointer;
|
||||
haveLock = !!wlWm.lockedPointer;
|
||||
haveConf = !!wlWm.confinedPointer;
|
||||
haveLock = !!wlWm.lockedPointer;
|
||||
haveSync = !!wlWm.confSync;
|
||||
havePointer = !!wlWm.pointer;
|
||||
haveConstraints = !!wlWm.pointerConstraints;
|
||||
});
|
||||
|
||||
if (relativeId)
|
||||
MTRACE("rel req id=%u why=grab", relativeId);
|
||||
|
||||
if (confId)
|
||||
MLOG(confSeq, "conf req id=%u why=grab", confId);
|
||||
else
|
||||
MTRACE("conf skip conf=%d lock=%d", haveConf, haveLock);
|
||||
MTRACE("conf skip pointer=%d constraints=%d conf=%d lock=%d sync=%d",
|
||||
havePointer, haveConstraints, haveConf, haveLock, haveSync);
|
||||
}
|
||||
|
||||
inline static uint32_t internalUngrabPointer(bool lock, uint64_t * traceSeq)
|
||||
inline static uint32_t destroyConfine(uint64_t * traceSeq)
|
||||
{
|
||||
*traceSeq = 0;
|
||||
if (!wlWm.pointer)
|
||||
return 0;
|
||||
|
||||
if (lock)
|
||||
LG_LOCK(wlWm.surfaceLock);
|
||||
|
||||
uint32_t confId = 0;
|
||||
if (wlWm.confinedPointer)
|
||||
@@ -679,34 +770,57 @@ inline static uint32_t internalUngrabPointer(bool lock, uint64_t * traceSeq)
|
||||
wlWm.confinedPointer = NULL;
|
||||
atomic_store_explicit(&wlWm.confActive, false, memory_order_release);
|
||||
*traceSeq = app_mouseSeq();
|
||||
}
|
||||
|
||||
if (lock)
|
||||
LG_UNLOCK(wlWm.surfaceLock);
|
||||
|
||||
if (!wlWm.warpSupport)
|
||||
{
|
||||
if (!wlWm.relativePointer)
|
||||
{
|
||||
wlWm.relativePointer =
|
||||
zwp_relative_pointer_manager_v1_get_relative_pointer(
|
||||
wlWm.relativePointerManager, wlWm.pointer);
|
||||
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
|
||||
&relativePointerListener, NULL);
|
||||
}
|
||||
|
||||
app_resyncMouseBasic();
|
||||
app_handleMouseBasic();
|
||||
if (!queueConfSync())
|
||||
DEBUG_ERROR("Failed to queue pointer release sync");
|
||||
}
|
||||
|
||||
return confId;
|
||||
}
|
||||
|
||||
static void ungrabBasic(void)
|
||||
{
|
||||
if (wlWm.warpSupport)
|
||||
return;
|
||||
|
||||
uint32_t relativeId = 0;
|
||||
LG_LOCK(wlWm.surfaceLock);
|
||||
if (wlWm.pointer && !wlWm.relativePointer &&
|
||||
wlWm.relativePointerManager)
|
||||
{
|
||||
wlWm.relativePointer =
|
||||
zwp_relative_pointer_manager_v1_get_relative_pointer(
|
||||
wlWm.relativePointerManager, wlWm.pointer);
|
||||
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
|
||||
&relativePointerListener, NULL);
|
||||
relativeId = proxyId(wlWm.relativePointer);
|
||||
}
|
||||
LG_UNLOCK(wlWm.surfaceLock);
|
||||
|
||||
if (relativeId)
|
||||
MTRACE("rel req id=%u why=ungrab", relativeId);
|
||||
|
||||
app_resyncMouseBasic();
|
||||
app_handleMouseBasic();
|
||||
}
|
||||
|
||||
void waylandUngrabPointer(void)
|
||||
{
|
||||
bool notify;
|
||||
uint64_t confSeq;
|
||||
const uint32_t confId = internalUngrabPointer(true, &confSeq);
|
||||
uint32_t confId;
|
||||
|
||||
LG_LOCK(wlWm.surfaceLock);
|
||||
wlWm.confReq = false;
|
||||
confId = destroyConfine(&confSeq);
|
||||
notify = !wlWm.confSync && wlWm.inputLive;
|
||||
LG_UNLOCK(wlWm.surfaceLock);
|
||||
|
||||
MLOG(confSeq, "conf destroy id=%u why=ungrab", confId);
|
||||
ungrabBasic();
|
||||
|
||||
if (notify)
|
||||
app_handleGrabEvent(false);
|
||||
}
|
||||
|
||||
void waylandCapturePointer(void)
|
||||
@@ -724,20 +838,17 @@ void waylandCapturePointer(void)
|
||||
uint64_t lockSeq = 0;
|
||||
INTERLOCKED_SECTION(wlWm.surfaceLock,
|
||||
{
|
||||
if (wlWm.confinedPointer)
|
||||
{
|
||||
confId = proxyId(wlWm.confinedPointer);
|
||||
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
|
||||
wlWm.confinedPointer = NULL;
|
||||
atomic_store_explicit(&wlWm.confActive, false,
|
||||
memory_order_release);
|
||||
confSeq = app_mouseSeq();
|
||||
}
|
||||
wlWm.confReq = true;
|
||||
confId = destroyConfine(&confSeq);
|
||||
|
||||
atomic_store_explicit(&wlWm.lockActive, false, memory_order_release);
|
||||
wlWm.lockedPointer = createLock();
|
||||
lockId = proxyId(wlWm.lockedPointer);
|
||||
lockSeq = app_mouseSeq();
|
||||
if (wlWm.pointer && !wlWm.lockedPointer)
|
||||
{
|
||||
atomic_store_explicit(&wlWm.lockActive, false,
|
||||
memory_order_release);
|
||||
wlWm.lockedPointer = createLock();
|
||||
lockId = proxyId(wlWm.lockedPointer);
|
||||
lockSeq = app_mouseSeq();
|
||||
}
|
||||
});
|
||||
|
||||
MLOG(confSeq, "conf destroy id=%u why=capture", confId);
|
||||
@@ -772,20 +883,31 @@ void waylandUncapturePointer(void)
|
||||
* - if the user has opted to use captureInputOnly mode.
|
||||
*/
|
||||
if (!wlWm.warpSupport || !app_isFormatValid() || app_isCaptureOnlyMode())
|
||||
confDropId = internalUngrabPointer(false, &confDropSeq);
|
||||
else if (wlWm.pointer)
|
||||
{
|
||||
atomic_store_explicit(&wlWm.confActive, false,
|
||||
memory_order_release);
|
||||
wlWm.confinedPointer = createConfine(NULL);
|
||||
confReqId = proxyId(wlWm.confinedPointer);
|
||||
confReqSeq = app_mouseSeq();
|
||||
wlWm.confReq = false;
|
||||
confDropId = destroyConfine(&confDropSeq);
|
||||
}
|
||||
else
|
||||
{
|
||||
wlWm.confReq = true;
|
||||
if (wlWm.pointer && !wlWm.confSync && !wlWm.confinedPointer)
|
||||
{
|
||||
wlWm.confinedPointer = createConfine(NULL);
|
||||
confReqId = proxyId(wlWm.confinedPointer);
|
||||
confReqSeq = app_mouseSeq();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
MLOG(lockSeq, "lock destroy id=%u why=uncapture", lockId);
|
||||
MLOG(confDropSeq, "conf destroy id=%u why=uncapture", confDropId);
|
||||
MLOG(confReqSeq, "conf req id=%u why=uncapture", confReqId);
|
||||
ungrabBasic();
|
||||
}
|
||||
|
||||
bool waylandIsPointerGrabbed(void)
|
||||
{
|
||||
return atomic_load_explicit(&wlWm.confActive, memory_order_acquire);
|
||||
}
|
||||
|
||||
bool waylandIsPointerCaptured(void)
|
||||
|
||||
@@ -321,6 +321,7 @@ struct LG_DisplayServerOps LGDS_Wayland =
|
||||
.setPointer = waylandSetPointer,
|
||||
.grabPointer = waylandGrabPointer,
|
||||
.ungrabPointer = waylandUngrabPointer,
|
||||
.isPointerGrabbed = waylandIsPointerGrabbed,
|
||||
.capturePointer = waylandCapturePointer,
|
||||
.uncapturePointer = waylandUncapturePointer,
|
||||
.isPointerCaptured = waylandIsPointerCaptured,
|
||||
|
||||
@@ -195,6 +195,9 @@ struct WaylandDSState
|
||||
struct zwp_relative_pointer_v1 * relativePointer;
|
||||
struct zwp_confined_pointer_v1 * confinedPointer;
|
||||
struct zwp_locked_pointer_v1 * lockedPointer;
|
||||
struct wl_callback * confSync;
|
||||
bool confReq;
|
||||
bool inputLive;
|
||||
bool showPointer;
|
||||
uint32_t pointerEnterSerial;
|
||||
|
||||
@@ -379,6 +382,7 @@ void waylandUngrabKeyboard(void);
|
||||
void waylandUngrabPointer(void);
|
||||
void waylandCapturePointer(void);
|
||||
void waylandUncapturePointer(void);
|
||||
bool waylandIsPointerGrabbed(void);
|
||||
bool waylandIsPointerCaptured(void);
|
||||
void waylandRealignPointer(void);
|
||||
void waylandWarpPointer(int x, int y, bool exiting);
|
||||
|
||||
@@ -341,6 +341,8 @@ static bool x11Init(const LG_DSInitParams params)
|
||||
XSetIOErrorHandler(x11IOErrorHandler);
|
||||
|
||||
memset(&x11, 0, sizeof(x11));
|
||||
atomic_init(&x11.captureActive, false);
|
||||
atomic_init(&x11.pointerGrabbed, false);
|
||||
x11.xValuator = -1;
|
||||
x11.yValuator = -1;
|
||||
x11.display = XOpenDisplay(NULL);
|
||||
@@ -1508,7 +1510,7 @@ static void x11XInputEvent(XGenericEventCookie *cookie)
|
||||
XIDeviceEvent *device = cookie->data;
|
||||
app_updateCursorPos(device->event_x, device->event_y);
|
||||
|
||||
if (!x11.pointerGrabbed)
|
||||
if (!atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire))
|
||||
app_handleMouseRelative(0.0, 0.0, 0.0, 0.0);
|
||||
return;
|
||||
}
|
||||
@@ -1886,7 +1888,7 @@ static void x11PrintGrabError(const char * type, int dev, Status ret)
|
||||
|
||||
static void x11GrabPointer(void)
|
||||
{
|
||||
if (x11.pointerGrabbed)
|
||||
if (atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire))
|
||||
return;
|
||||
|
||||
unsigned char mask_bits[XIMaskLen(XI_LASTEVENT)] = { 0 };
|
||||
@@ -1935,26 +1937,36 @@ static void x11GrabPointer(void)
|
||||
return;
|
||||
}
|
||||
|
||||
x11.pointerGrabbed = true;
|
||||
atomic_store_explicit(&x11.pointerGrabbed, true, memory_order_release);
|
||||
}
|
||||
|
||||
static void x11UngrabPointer(void)
|
||||
{
|
||||
atomic_store_explicit(&x11.captureActive, false, memory_order_release);
|
||||
|
||||
if (!x11.pointerGrabbed)
|
||||
if (!atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire))
|
||||
{
|
||||
app_handleGrabEvent(false);
|
||||
return;
|
||||
}
|
||||
|
||||
XIUngrabDevice(x11.display, x11.pointerDev, CurrentTime);
|
||||
XSync(x11.display, False);
|
||||
|
||||
x11.pointerGrabbed = false;
|
||||
atomic_store_explicit(&x11.pointerGrabbed, false, memory_order_release);
|
||||
app_handleGrabEvent(false);
|
||||
}
|
||||
|
||||
static bool x11IsPointerGrabbed(void)
|
||||
{
|
||||
return atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire);
|
||||
}
|
||||
|
||||
static void x11CapturePointer(void)
|
||||
{
|
||||
x11GrabPointer();
|
||||
atomic_store_explicit(&x11.captureActive, x11.pointerGrabbed,
|
||||
atomic_store_explicit(&x11.captureActive,
|
||||
atomic_load_explicit(&x11.pointerGrabbed, memory_order_acquire),
|
||||
memory_order_release);
|
||||
}
|
||||
|
||||
@@ -2152,6 +2164,7 @@ struct LG_DisplayServerOps LGDS_X11 =
|
||||
.setPointer = x11SetPointer,
|
||||
.grabPointer = x11GrabPointer,
|
||||
.ungrabPointer = x11UngrabPointer,
|
||||
.isPointerGrabbed = x11IsPointerGrabbed,
|
||||
.capturePointer = x11CapturePointer,
|
||||
.uncapturePointer = x11UncapturePointer,
|
||||
.isPointerCaptured = x11IsPointerCaptured,
|
||||
|
||||
@@ -94,7 +94,7 @@ struct X11DSState
|
||||
int yValuator;
|
||||
|
||||
_Atomic(bool) captureActive;
|
||||
bool pointerGrabbed;
|
||||
_Atomic(bool) pointerGrabbed;
|
||||
bool keyboardGrabbed;
|
||||
bool entered;
|
||||
bool focused;
|
||||
|
||||
@@ -84,6 +84,7 @@ void app_handleKeyRelease(int scancode);
|
||||
void app_handleKeyboardModifiers(bool ctrl, bool shift, bool alt, bool super);
|
||||
void app_handleKeyboardLEDs(bool numLock, bool capsLock, bool scrollLock);
|
||||
void app_handleEnterEvent(bool entered);
|
||||
void app_handleGrabEvent(bool active);
|
||||
void app_handleFocusEvent(bool focused);
|
||||
void app_handleCloseEvent(void);
|
||||
void app_handleRenderEvent(const uint64_t timeUs);
|
||||
|
||||
@@ -207,6 +207,7 @@ struct LG_DisplayServerOps
|
||||
/* (un)grabPointer is used to toggle cursor tracking/confine in normal mode */
|
||||
void (*grabPointer)(void);
|
||||
void (*ungrabPointer)(void);
|
||||
bool (*isPointerGrabbed)(void);
|
||||
/* (un)capturePointer is used do toggle special cursor tracking in capture mode */
|
||||
void (*capturePointer)(void);
|
||||
void (*uncapturePointer)(void);
|
||||
@@ -290,6 +291,7 @@ struct LG_DisplayServerOps
|
||||
DEBUG_ASSERT((x)->setPointer ); \
|
||||
DEBUG_ASSERT((x)->grabPointer ); \
|
||||
DEBUG_ASSERT((x)->ungrabPointer ); \
|
||||
DEBUG_ASSERT((x)->isPointerGrabbed ); \
|
||||
DEBUG_ASSERT((x)->capturePointer ); \
|
||||
DEBUG_ASSERT((x)->uncapturePointer ); \
|
||||
DEBUG_ASSERT((x)->isPointerCaptured ); \
|
||||
|
||||
@@ -469,7 +469,7 @@ void app_handleButtonPress(int button)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!core_inputEnabled() || !g_cursor.inView)
|
||||
if (!core_inputEnabled() || !g_cursor.inView || !g_cursor.viewReq)
|
||||
return;
|
||||
|
||||
if (!purespice_mousePress(button))
|
||||
@@ -674,6 +674,11 @@ void app_handleMouseRelative(double normx, double normy,
|
||||
core_handleMouseNormal(normx, normy);
|
||||
}
|
||||
|
||||
void app_handleGrabEvent(bool active)
|
||||
{
|
||||
core_handleGrabEvent(active);
|
||||
}
|
||||
|
||||
// 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
|
||||
// with a 1:1 mouse movement patch applied in the guest. For anything fancy, use
|
||||
|
||||
@@ -66,28 +66,21 @@ bool core_inputEnabled(void)
|
||||
((g_cursor.grab && g_params.captureInputOnly) || !g_params.captureInputOnly);
|
||||
}
|
||||
|
||||
void core_invalidatePointer(bool detectInView)
|
||||
static void applyView(bool active, bool force)
|
||||
{
|
||||
/* if the display server does not support warp, then we can not operate in
|
||||
* always relative mode and we should not grab the pointer */
|
||||
enum LG_DSWarpSupport warpSupport = LG_DS_WARP_NONE;
|
||||
app_getProp(LG_DS_WARP_SUPPORT, &warpSupport);
|
||||
|
||||
MTRACE("invalidate detect=%d inWin=%d inView=%d grab=%d warp=%d "
|
||||
"support=%d", detectInView, g_cursor.inWindow, g_cursor.inView,
|
||||
g_cursor.grab, g_cursor.warpState, warpSupport);
|
||||
|
||||
if (detectInView)
|
||||
if (active && !g_cursor.viewReq)
|
||||
{
|
||||
bool inView = isInView();
|
||||
// do not allow the view to become active if any mouse buttons are being held,
|
||||
// this fixes issues with meta window resizing.
|
||||
if (inView && g_cursor.buttons)
|
||||
return;
|
||||
|
||||
g_cursor.inView = inView;
|
||||
MTRACE("view active skip=req");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force && g_cursor.inView == active)
|
||||
return;
|
||||
|
||||
MTRACE("view active=%d old=%d req=%d force=%d", active,
|
||||
g_cursor.inView, g_cursor.viewReq, force);
|
||||
|
||||
g_cursor.inView = active;
|
||||
g_cursor.draw = (g_params.alwaysShowCursor || g_params.captureInputOnly)
|
||||
? true : g_cursor.inView;
|
||||
g_cursor.redraw = true;
|
||||
@@ -98,9 +91,6 @@ void core_invalidatePointer(bool detectInView)
|
||||
if (g_params.hideMouse)
|
||||
g_state.ds->setPointer(LG_POINTER_NONE);
|
||||
|
||||
if (warpSupport != LG_DS_WARP_NONE && !g_params.captureInputOnly)
|
||||
g_state.ds->grabPointer();
|
||||
|
||||
if (g_params.grabKeyboardOnFocus)
|
||||
g_state.ds->grabKeyboard();
|
||||
}
|
||||
@@ -109,36 +99,96 @@ void core_invalidatePointer(bool detectInView)
|
||||
if (g_params.hideMouse)
|
||||
g_state.ds->setPointer(LG_POINTER_SQUARE);
|
||||
|
||||
if (warpSupport != LG_DS_WARP_NONE)
|
||||
g_state.ds->ungrabPointer();
|
||||
|
||||
g_state.ds->ungrabKeyboard();
|
||||
}
|
||||
|
||||
g_cursor.warpState = WARP_STATE_ON;
|
||||
}
|
||||
|
||||
void core_setCursorInView(bool enable)
|
||||
void core_invalidatePointer(bool detectInView)
|
||||
{
|
||||
MTRACE("view req=%d old=%d focus=%d inWin=%d grab=%d",
|
||||
enable, g_cursor.inView, g_state.focused, g_cursor.inWindow,
|
||||
g_cursor.grab);
|
||||
MTRACE("invalidate detect=%d inWin=%d req=%d active=%d grab=%d "
|
||||
"warp=%d", detectInView, g_cursor.inWindow, g_cursor.viewReq,
|
||||
g_cursor.inView, g_cursor.grab, g_cursor.warpState);
|
||||
|
||||
// if the state has not changed, don't do anything else
|
||||
if (g_cursor.inView == enable)
|
||||
if (!detectInView)
|
||||
{
|
||||
MTRACE("view skip=same value=%d", enable);
|
||||
applyView(g_cursor.inView, true);
|
||||
return;
|
||||
}
|
||||
|
||||
const bool inView = isInView();
|
||||
|
||||
/* do not allow the view to become active if any mouse buttons are being
|
||||
* held, this fixes issues with meta window resizing. */
|
||||
if (inView && g_cursor.buttons)
|
||||
return;
|
||||
|
||||
core_setCursorInView(inView);
|
||||
}
|
||||
|
||||
void core_setCursorInView(bool enable)
|
||||
{
|
||||
MTRACE("view req=%d oldReq=%d active=%d focus=%d inWin=%d grab=%d",
|
||||
enable, g_cursor.viewReq, g_cursor.inView, g_state.focused,
|
||||
g_cursor.inWindow, g_cursor.grab);
|
||||
|
||||
if (enable && !g_state.focused)
|
||||
{
|
||||
MTRACE("view skip=focus");
|
||||
return;
|
||||
}
|
||||
|
||||
g_cursor.inView = enable;
|
||||
core_invalidatePointer(false);
|
||||
enum LG_DSWarpSupport warpSupport = LG_DS_WARP_NONE;
|
||||
app_getProp(LG_DS_WARP_SUPPORT, &warpSupport);
|
||||
|
||||
const bool immediate = g_cursor.grab || g_params.captureInputOnly ||
|
||||
warpSupport == LG_DS_WARP_NONE;
|
||||
|
||||
if (g_cursor.viewReq == enable)
|
||||
{
|
||||
if (immediate)
|
||||
applyView(enable, false);
|
||||
else if (enable && !g_cursor.inView)
|
||||
{
|
||||
g_state.ds->grabPointer();
|
||||
applyView(g_state.ds->isPointerGrabbed(), false);
|
||||
}
|
||||
else
|
||||
MTRACE("view skip=same value=%d", enable);
|
||||
return;
|
||||
}
|
||||
|
||||
g_cursor.viewReq = enable;
|
||||
|
||||
if (immediate)
|
||||
{
|
||||
applyView(enable, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
{
|
||||
g_state.ds->grabPointer();
|
||||
applyView(g_state.ds->isPointerGrabbed(), false);
|
||||
}
|
||||
else
|
||||
g_state.ds->ungrabPointer();
|
||||
}
|
||||
|
||||
void core_handleGrabEvent(bool active)
|
||||
{
|
||||
MTRACE("conf active=%d req=%d old=%d grab=%d", active,
|
||||
g_cursor.viewReq, g_cursor.inView, g_cursor.grab);
|
||||
|
||||
enum LG_DSWarpSupport warpSupport = LG_DS_WARP_NONE;
|
||||
app_getProp(LG_DS_WARP_SUPPORT, &warpSupport);
|
||||
|
||||
if (g_cursor.grab || g_params.captureInputOnly ||
|
||||
warpSupport == LG_DS_WARP_NONE)
|
||||
return;
|
||||
|
||||
applyView(active, false);
|
||||
}
|
||||
|
||||
void core_setGrab(bool enable)
|
||||
@@ -202,6 +252,9 @@ void core_setGrabQuiet(bool enable)
|
||||
|
||||
g_state.ds->uncapturePointer();
|
||||
|
||||
if (!g_params.captureInputOnly && warpSupport != LG_DS_WARP_NONE)
|
||||
applyView(g_state.ds->isPointerGrabbed(), false);
|
||||
|
||||
/* if exiting capture when input on capture only we need to align the local
|
||||
* cursor to the guest's location before it is shown. */
|
||||
if (g_params.captureInputOnly || !g_params.hideMouse)
|
||||
@@ -507,12 +560,13 @@ void core_handleGuestMouseUpdate(void)
|
||||
}
|
||||
|
||||
const bool overlay = app_isOverlayMode();
|
||||
if (overlay || !g_cursor.inView)
|
||||
if (overlay || !g_cursor.inView || !g_cursor.viewReq)
|
||||
{
|
||||
MTRACE("guest drop=%s guest=%d,%d local=%.3f,%.3f inWin=%d "
|
||||
"inView=%d", overlay ? "overlay" : "view", g_cursor.guest.x,
|
||||
"inView=%d req=%d", overlay ? "overlay" : "view",
|
||||
g_cursor.guest.x,
|
||||
g_cursor.guest.y, localPos.x, localPos.y, g_cursor.inWindow,
|
||||
g_cursor.inView);
|
||||
g_cursor.inView, g_cursor.viewReq);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -588,6 +642,13 @@ void core_handleMouseNormal(double ex, double ey)
|
||||
if (!core_inputEnabled())
|
||||
return;
|
||||
|
||||
if (g_cursor.viewReq != g_cursor.inView)
|
||||
{
|
||||
if (g_cursor.viewReq)
|
||||
core_setCursorInView(true);
|
||||
return;
|
||||
}
|
||||
|
||||
/* scale the movement to the guest */
|
||||
if (g_cursor.useScale && g_params.scaleMouseInput)
|
||||
{
|
||||
@@ -600,7 +661,9 @@ void core_handleMouseNormal(double ex, double ey)
|
||||
const bool inView = isInView();
|
||||
if (!g_cursor.inView)
|
||||
{
|
||||
if (inView)
|
||||
if (g_cursor.viewReq)
|
||||
return;
|
||||
else if (inView)
|
||||
g_cursor.realign = true;
|
||||
else /* nothing to do if we are outside the viewport */
|
||||
return;
|
||||
@@ -691,6 +754,9 @@ fallback:
|
||||
ex += guest.x - (g_cursor.guest.x + g_cursor.guest.hx);
|
||||
ey += guest.y - (g_cursor.guest.y + g_cursor.guest.hy);
|
||||
core_setCursorInView(true);
|
||||
|
||||
if (!g_cursor.inView)
|
||||
return;
|
||||
}
|
||||
|
||||
g_cursor.realign = false;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
bool core_inputEnabled(void);
|
||||
void core_invalidatePointer(bool detectInView);
|
||||
void core_setCursorInView(bool enable);
|
||||
void core_handleGrabEvent(bool active);
|
||||
void core_setGrab(bool enable);
|
||||
void core_setGrabQuiet(bool enable);
|
||||
bool core_warpPointer(int x, int y, bool exiting);
|
||||
|
||||
@@ -131,6 +131,7 @@ static void lgInit(void)
|
||||
g_cursor.scale.y = 1.0;
|
||||
g_cursor.draw = false;
|
||||
g_cursor.inView = false;
|
||||
g_cursor.viewReq = false;
|
||||
g_cursor.guest.valid = false;
|
||||
|
||||
// if spice is not in use, hide the local cursor
|
||||
|
||||
@@ -295,6 +295,9 @@ struct CursorState
|
||||
/* true if the cursor is currently in the guest view area */
|
||||
bool inView;
|
||||
|
||||
/* true if the cursor should be confined to the guest view area */
|
||||
bool viewReq;
|
||||
|
||||
/* true if the guest should be realigned to the host when next drawn */
|
||||
bool realign;
|
||||
|
||||
|
||||
@@ -130,7 +130,15 @@ static void ungrab(void)
|
||||
push(EV_UNGRAB, 0, 0, false);
|
||||
m.conf.req = false;
|
||||
if (!m.conf.hold)
|
||||
{
|
||||
m.conf.on = false;
|
||||
core_handleGrabEvent(false);
|
||||
}
|
||||
}
|
||||
|
||||
static bool grabbed(void)
|
||||
{
|
||||
return m.conf.on;
|
||||
}
|
||||
|
||||
static void capture(void)
|
||||
@@ -186,6 +194,7 @@ static struct LG_DisplayServerOps ds = {
|
||||
.ungrabKeyboard = keyNoop,
|
||||
.grabPointer = grab,
|
||||
.ungrabPointer = ungrab,
|
||||
.isPointerGrabbed = grabbed,
|
||||
.capturePointer = capture,
|
||||
.uncapturePointer = uncapture,
|
||||
.isPointerCaptured = captured,
|
||||
@@ -245,8 +254,10 @@ static void reset(void)
|
||||
memset(&g_cursor, 0, sizeof(g_cursor));
|
||||
memset(&g_params, 0, sizeof(g_params));
|
||||
|
||||
m.support = LG_DS_WARP_SURFACE;
|
||||
m.valid = true;
|
||||
m.support = LG_DS_WARP_SURFACE;
|
||||
m.valid = true;
|
||||
m.conf.req = true;
|
||||
m.conf.on = true;
|
||||
|
||||
g_state.ds = &ds;
|
||||
g_state.focused = true;
|
||||
@@ -269,6 +280,7 @@ static void reset(void)
|
||||
|
||||
g_cursor.inWindow = true;
|
||||
g_cursor.inView = true;
|
||||
g_cursor.viewReq = true;
|
||||
g_cursor.warpState = WARP_STATE_ON;
|
||||
g_cursor.guest.valid = true;
|
||||
g_cursor.scale = (struct DoublePoint) { 1.0, 1.0 };
|
||||
@@ -315,8 +327,6 @@ static void startExit(void)
|
||||
{
|
||||
reset();
|
||||
setLocal(109, 50);
|
||||
m.conf.req = true;
|
||||
m.conf.on = true;
|
||||
m.conf.hold = true;
|
||||
|
||||
core_handleMouseNormal(2, 0);
|
||||
@@ -343,17 +353,29 @@ static void startConf(void)
|
||||
{
|
||||
reset();
|
||||
setLocal(50, 50);
|
||||
g_cursor.inView = false;
|
||||
m.conf.req = false;
|
||||
m.conf.on = false;
|
||||
g_cursor.inView = false;
|
||||
g_cursor.viewReq = false;
|
||||
|
||||
core_handleMouseNormal(0, 0);
|
||||
}
|
||||
|
||||
static void setConf(bool active)
|
||||
{
|
||||
m.conf.on = active;
|
||||
core_handleGrabEvent(active);
|
||||
}
|
||||
|
||||
static void testConfWait(void)
|
||||
{
|
||||
startConf();
|
||||
CHECK(m.conf.req);
|
||||
CHECK(!m.conf.on);
|
||||
CHECK(!g_cursor.inView);
|
||||
|
||||
setConf(true);
|
||||
CHECK(g_cursor.inView);
|
||||
}
|
||||
|
||||
static void testConfGuest(void)
|
||||
@@ -362,6 +384,10 @@ static void testConfGuest(void)
|
||||
core_handleGuestMouseUpdate();
|
||||
CHECK(m.conf.req);
|
||||
CHECK(count(EV_GUEST) == 0);
|
||||
|
||||
setConf(true);
|
||||
core_handleGuestMouseUpdate();
|
||||
CHECK(count(EV_GUEST) == 1);
|
||||
}
|
||||
|
||||
static void testCapWait(void)
|
||||
|
||||
Reference in New Issue
Block a user