[client] wayland: constrain pointer only during capture
Some checks are pending
build / client (Debug, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Debug, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], libdecor) (push) Waiting to run
build / client (Release, map[cc:clang cxx:clang++], xdg-shell) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], libdecor) (push) Waiting to run
build / client (Release, map[cc:gcc cxx:g++], xdg-shell) (push) Waiting to run
build / module (push) Waiting to run
build / host-linux (push) Waiting to run
build / host-windows-cross (push) Waiting to run
build / host-windows-native (push) Waiting to run
build / idd (push) Waiting to run
build / obs (clang) (push) Waiting to run
build / obs (gcc) (push) Waiting to run
build / docs (push) Waiting to run

Remove the persistent confined-pointer workaround that emulated cursor
warping on Wayland. Normal pointer motion now uses absolute input only.

Create relative and locked pointer objects only during capture. Preserve
the request across pointer capability changes, and suppress relative
events until the compositor confirms the lock.

On uncapture, publish the current absolute position instead of trying to
warp the host cursor.
This commit is contained in:
Geoffrey McRae
2026-08-09 03:32:38 +10:00
parent 48f0b66a4f
commit 9f2d6f6413
9 changed files with 124 additions and 531 deletions

View File

@@ -21,7 +21,6 @@
#include "wayland.h" #include "wayland.h"
#include <errno.h> #include <errno.h>
#include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
#include <sys/mman.h> #include <sys/mman.h>
@@ -70,9 +69,6 @@ static void pointerMotionHandler(void * data, struct wl_pointer * pointer,
MTRACE("abs time=%u pos=%.3f,%.3f", time, wlWm.motion.x, MTRACE("abs time=%u pos=%.3f,%.3f", time, wlWm.motion.x,
wlWm.motion.y); wlWm.motion.y);
app_updateCursorPos(wlWm.motion.x, wlWm.motion.y); app_updateCursorPos(wlWm.motion.x, wlWm.motion.y);
if (!wlWm.warpSupport && !wlWm.relativePointer)
app_handleMouseBasic();
} }
static void pointerEnterHandler(void * data, struct wl_pointer * pointer, static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
@@ -95,18 +91,6 @@ static void pointerEnterHandler(void * data, struct wl_pointer * pointer,
wlMotionAbs(&wlWm.motion, wl_fixed_to_double(sxW), wlMotionAbs(&wlWm.motion, wl_fixed_to_double(sxW),
wl_fixed_to_double(syW)); wl_fixed_to_double(syW));
app_updateCursorPos(wlWm.motion.x, wlWm.motion.y); app_updateCursorPos(wlWm.motion.x, wlWm.motion.y);
if (wlWm.warpSupport)
{
app_handleMouseRelative(0.0, 0.0, 0.0, 0.0);
return;
}
if (wlWm.relativePointer)
return;
app_resyncMouseBasic();
app_handleMouseBasic();
} }
static void pointerLeaveHandler(void * data, struct wl_pointer * pointer, static void pointerLeaveHandler(void * data, struct wl_pointer * pointer,
@@ -195,100 +179,6 @@ static const struct wl_pointer_listener pointerListener = {
.axis = pointerAxisHandler, .axis = pointerAxisHandler,
}; };
static void confinedHandler(void * data,
struct zwp_confined_pointer_v1 * pointer)
{
bool valid;
LG_LOCK(wlWm.surfaceLock);
valid = pointer == wlWm.confinedPointer;
if (valid)
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,
struct zwp_confined_pointer_v1 * pointer)
{
bool valid;
LG_LOCK(wlWm.surfaceLock);
valid = pointer == wlWm.confinedPointer;
if (valid)
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 = {
.confined = confinedHandler,
.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, static void lockedHandler(void * data,
struct zwp_locked_pointer_v1 * pointer) struct zwp_locked_pointer_v1 * pointer)
{ {
@@ -318,17 +208,6 @@ static const struct zwp_locked_pointer_v1_listener lockedListener = {
.unlocked = unlockedHandler, .unlocked = unlockedHandler,
}; };
static struct zwp_confined_pointer_v1 * createConfine(
struct wl_region * region)
{
struct zwp_confined_pointer_v1 * pointer =
zwp_pointer_constraints_v1_confine_pointer(
wlWm.pointerConstraints, wlWm.surface, wlWm.pointer, region,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
zwp_confined_pointer_v1_add_listener(pointer, &confinedListener, NULL);
return pointer;
}
static struct zwp_locked_pointer_v1 * createLock(void) static struct zwp_locked_pointer_v1 * createLock(void)
{ {
struct zwp_locked_pointer_v1 * pointer = struct zwp_locked_pointer_v1 * pointer =
@@ -344,6 +223,9 @@ static void relativePointerMotionHandler(void * data,
wl_fixed_t dxW, wl_fixed_t dyW, wl_fixed_t dxUnaccelW, wl_fixed_t dxW, wl_fixed_t dyW, wl_fixed_t dxUnaccelW,
wl_fixed_t dyUnaccelW) wl_fixed_t dyUnaccelW)
{ {
if (!atomic_load_explicit(&wlWm.lockActive, memory_order_acquire))
return;
const double dx = wl_fixed_to_double(dxW); const double dx = wl_fixed_to_double(dxW);
const double dy = wl_fixed_to_double(dyW); const double dy = wl_fixed_to_double(dyW);
MTRACE("rel time=%u:%u delta=%.3f,%.3f raw=%.3f,%.3f " MTRACE("rel time=%u:%u delta=%.3f,%.3f raw=%.3f,%.3f "
@@ -509,23 +391,66 @@ static const struct wl_keyboard_listener keyboardListener = {
.modifiers = keyboardModifiersHandler, .modifiers = keyboardModifiersHandler,
}; };
static void waylandCleanUpPointer(bool notify) static void ensureCapturePointer(bool requestCapture)
{ {
bool event = false; uint32_t relativeId = 0;
uint32_t lockId = 0; uint32_t lockId = 0;
uint32_t confId = 0;
uint64_t lockSeq = 0; uint64_t lockSeq = 0;
uint64_t confSeq = 0; bool captureRequested = false;
bool havePointer = false;
bool haveRelativeManager = false;
bool haveConstraints = false;
INTERLOCKED_SECTION(wlWm.surfaceLock, INTERLOCKED_SECTION(wlWm.surfaceLock,
{ {
if (wlWm.confSync) if (requestCapture)
wlWm.captureRequested = true;
captureRequested = wlWm.captureRequested;
havePointer = !!wlWm.pointer;
haveRelativeManager = !!wlWm.relativePointerManager;
haveConstraints = !!wlWm.pointerConstraints;
if (captureRequested && havePointer && haveRelativeManager &&
haveConstraints)
{ {
wl_callback_destroy(wlWm.confSync); if (!wlWm.relativePointer)
wlWm.confSync = NULL; {
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.lockedPointer)
{
atomic_store_explicit(&wlWm.lockActive, false,
memory_order_release);
wlWm.lockedPointer = createLock();
lockId = proxyId(wlWm.lockedPointer);
lockSeq = app_mouseSeq();
}
}
});
if (relativeId)
MTRACE("rel req id=%u why=capture", relativeId);
MLOG(lockSeq, "lock req id=%u why=capture", lockId);
if (captureRequested &&
(!havePointer || !haveRelativeManager || !haveConstraints))
MTRACE("capture skip pointer=%d relMgr=%d constraints=%d",
havePointer, haveRelativeManager, haveConstraints);
}
static void waylandCleanUpPointer(bool notify)
{
uint32_t lockId = 0;
uint64_t lockSeq = 0;
INTERLOCKED_SECTION(wlWm.surfaceLock,
{
atomic_store_explicit(&wlWm.lockActive, false, memory_order_release); atomic_store_explicit(&wlWm.lockActive, false, memory_order_release);
atomic_store_explicit(&wlWm.confActive, false, memory_order_release);
if (wlWm.lockedPointer) if (wlWm.lockedPointer)
{ {
@@ -535,14 +460,6 @@ static void waylandCleanUpPointer(bool notify)
lockSeq = app_mouseSeq(); lockSeq = app_mouseSeq();
} }
if (wlWm.confinedPointer)
{
confId = proxyId(wlWm.confinedPointer);
zwp_confined_pointer_v1_destroy(wlWm.confinedPointer);
wlWm.confinedPointer = NULL;
confSeq = app_mouseSeq();
}
if (wlWm.relativePointer) if (wlWm.relativePointer)
{ {
zwp_relative_pointer_v1_destroy(wlWm.relativePointer); zwp_relative_pointer_v1_destroy(wlWm.relativePointer);
@@ -552,18 +469,12 @@ static void waylandCleanUpPointer(bool notify)
wl_pointer_destroy(wlWm.pointer); wl_pointer_destroy(wlWm.pointer);
wlWm.pointer = NULL; wlWm.pointer = NULL;
wlWm.pointerInSurface = false; wlWm.pointerInSurface = false;
event = notify && wlWm.inputLive;
}); });
MLOG(lockSeq, "lock destroy id=%u why=pointer", lockId); MLOG(lockSeq, "lock destroy id=%u why=pointer", lockId);
MLOG(confSeq, "conf destroy id=%u why=pointer", confId);
if (event) if (notify)
{
app_handleEnterEvent(false); app_handleEnterEvent(false);
app_handleGrabEvent(false);
}
} }
// Seat-handling listeners. // Seat-handling listeners.
@@ -575,31 +486,12 @@ static void handlePointerCapability(uint32_t capabilities)
waylandCleanUpPointer(true); waylandCleanUpPointer(true);
else if (hasPointer && !wlWm.pointer) else if (hasPointer && !wlWm.pointer)
{ {
LG_LOCK(wlWm.surfaceLock);
wlWm.pointer = wl_seat_get_pointer(wlWm.seat); wlWm.pointer = wl_seat_get_pointer(wlWm.seat);
wl_pointer_add_listener(wlWm.pointer, &pointerListener, NULL); wl_pointer_add_listener(wlWm.pointer, &pointerListener, NULL);
LG_UNLOCK(wlWm.surfaceLock);
waylandSetPointer(wlWm.cursorId); waylandSetPointer(wlWm.cursorId);
ensureCapturePointer(false);
if (wlWm.warpSupport)
{
wlWm.relativePointer =
zwp_relative_pointer_manager_v1_get_relative_pointer(
wlWm.relativePointerManager, wlWm.pointer);
zwp_relative_pointer_v1_add_listener(wlWm.relativePointer,
&relativePointerListener, NULL);
}
if (app_isCaptureMode())
waylandCapturePointer();
else
{
bool confReq;
INTERLOCKED_SECTION(wlWm.surfaceLock,
{
confReq = wlWm.confReq;
});
if (confReq)
waylandGrabPointer();
}
} }
} }
@@ -645,7 +537,6 @@ bool waylandInputInit(bool allowNoInput)
if (allowNoInput) if (allowNoInput)
{ {
DEBUG_WARN("Compositor missing wl_seat, input will be disabled"); DEBUG_WARN("Compositor missing wl_seat, input will be disabled");
wlWm.warpSupport = false;
return true; return true;
} }
@@ -653,13 +544,6 @@ bool waylandInputInit(bool allowNoInput)
return false; return false;
} }
if (wlWm.warpSupport && (!wlWm.relativePointerManager || !wlWm.pointerConstraints))
{
DEBUG_WARN("Cursor warp is requested, but cannot be honoured due to lack "
"of zwp_relative_pointer_manager_v1 or zwp_pointer_constraints_v1");
wlWm.warpSupport = false;
}
if (!wlWm.relativePointerManager) if (!wlWm.relativePointerManager)
DEBUG_WARN("zwp_relative_pointer_manager_v1 not exported by compositor, " DEBUG_WARN("zwp_relative_pointer_manager_v1 not exported by compositor, "
"mouse will not be captured"); "mouse will not be captured");
@@ -672,14 +556,10 @@ bool waylandInputInit(bool allowNoInput)
DEBUG_WARN("zwp_keyboard_shortcuts_inhibit_manager_v1 not exported by " DEBUG_WARN("zwp_keyboard_shortcuts_inhibit_manager_v1 not exported by "
"compositor, keyboard will not be grabbed"); "compositor, keyboard will not be grabbed");
MTRACE("input seat=1 warp=%d relMgr=%d constraints=%d", MTRACE("input seat=1 relMgr=%d constraints=%d",
wlWm.warpSupport, !!wlWm.relativePointerManager, !!wlWm.relativePointerManager,
!!wlWm.pointerConstraints); !!wlWm.pointerConstraints);
LG_LOCK(wlWm.surfaceLock);
wlWm.inputLive = true;
LG_UNLOCK(wlWm.surfaceLock);
wlWm.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); wlWm.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
if (!wlWm.xkb) if (!wlWm.xkb)
DEBUG_WARN("Failed to initialize xkb, keyboard input will not work"); DEBUG_WARN("Failed to initialize xkb, keyboard input will not work");
@@ -692,10 +572,6 @@ bool waylandInputInit(bool allowNoInput)
void waylandInputFree(void) void waylandInputFree(void)
{ {
LG_LOCK(wlWm.surfaceLock);
wlWm.inputLive = false;
LG_UNLOCK(wlWm.surfaceLock);
if (!wlWm.seat) if (!wlWm.seat)
return; return;
@@ -721,210 +597,56 @@ void waylandInputFree(void)
void waylandGrabPointer(void) void waylandGrabPointer(void)
{ {
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,
{
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)
{
wlWm.confinedPointer = createConfine(NULL);
confId = proxyId(wlWm.confinedPointer);
confSeq = app_mouseSeq();
}
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 pointer=%d constraints=%d conf=%d lock=%d sync=%d",
havePointer, haveConstraints, haveConf, haveLock, haveSync);
}
inline static uint32_t destroyConfine(uint64_t * traceSeq)
{
*traceSeq = 0;
uint32_t confId = 0;
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);
*traceSeq = app_mouseSeq();
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) void waylandUngrabPointer(void)
{ {
bool notify;
uint64_t 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) void waylandCapturePointer(void)
{ {
if (!wlWm.warpSupport) ensureCapturePointer(true);
{
MTRACE("capture fallback=confine");
waylandGrabPointer();
return;
}
uint32_t confId = 0;
uint32_t lockId = 0;
uint64_t confSeq = 0;
uint64_t lockSeq = 0;
INTERLOCKED_SECTION(wlWm.surfaceLock,
{
wlWm.confReq = true;
confId = destroyConfine(&confSeq);
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);
MLOG(lockSeq, "lock req id=%u why=capture", lockId);
} }
void waylandUncapturePointer(void) void waylandUncapturePointer(void)
{ {
uint32_t lockId = 0; uint32_t lockId = 0;
uint32_t confDropId = 0; uint32_t relativeId = 0;
uint32_t confReqId = 0;
uint64_t lockSeq = 0; uint64_t lockSeq = 0;
uint64_t confDropSeq = 0;
uint64_t confReqSeq = 0;
INTERLOCKED_SECTION(wlWm.surfaceLock, INTERLOCKED_SECTION(wlWm.surfaceLock,
{ {
wlWm.captureRequested = false;
atomic_store_explicit(&wlWm.lockActive, false, memory_order_release);
if (wlWm.lockedPointer) if (wlWm.lockedPointer)
{ {
lockId = proxyId(wlWm.lockedPointer); lockId = proxyId(wlWm.lockedPointer);
zwp_locked_pointer_v1_destroy(wlWm.lockedPointer); zwp_locked_pointer_v1_destroy(wlWm.lockedPointer);
wlWm.lockedPointer = NULL; wlWm.lockedPointer = NULL;
atomic_store_explicit(&wlWm.lockActive, false,
memory_order_release);
lockSeq = app_mouseSeq(); lockSeq = app_mouseSeq();
} }
/* we need to ungrab the pointer on the following conditions when exiting capture mode: if (wlWm.relativePointer)
* - if warp is not supported, exit via window edge detection will never work
* as the cursor can not be warped out of the window when we release it.
* - if the format is invalid as we do not know where the guest cursor is,
* which also breaks edge detection.
* - if the user has opted to use captureInputOnly mode.
*/
if (!wlWm.warpSupport || !app_isFormatValid() || app_isCaptureOnlyMode())
{ {
wlWm.confReq = false; relativeId = proxyId(wlWm.relativePointer);
confDropId = destroyConfine(&confDropSeq); zwp_relative_pointer_v1_destroy(wlWm.relativePointer);
} wlWm.relativePointer = NULL;
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(lockSeq, "lock destroy id=%u why=uncapture", lockId);
MLOG(confDropSeq, "conf destroy id=%u why=uncapture", confDropId); if (relativeId)
MLOG(confReqSeq, "conf req id=%u why=uncapture", confReqId); MTRACE("rel destroy id=%u why=uncapture", relativeId);
ungrabBasic();
} }
bool waylandIsPointerGrabbed(void) bool waylandIsPointerGrabbed(void)
{ {
return atomic_load_explicit(&wlWm.confActive, memory_order_acquire); return false;
} }
bool waylandIsPointerCaptured(void) bool waylandIsPointerCaptured(void)
{ {
const atomic_bool * active = wlWm.warpSupport ? return atomic_load_explicit(&wlWm.lockActive, memory_order_acquire);
&wlWm.lockActive : &wlWm.confActive;
return atomic_load_explicit(active, memory_order_acquire);
} }
void waylandGrabKeyboard(void) void waylandGrabKeyboard(void)
@@ -948,91 +670,22 @@ void waylandUngrabKeyboard(void)
void waylandWarpPointer(int x, int y, bool exiting) void waylandWarpPointer(int x, int y, bool exiting)
{ {
const int reqX = x; (void)x;
const int reqY = y; (void)y;
if (!wlWm.pointerInSurface) (void)exiting;
{
MTRACE("warp drop=surface target=%d,%d exit=%d", x, y, exiting);
return;
}
if (wlWm.lockedPointer)
{
MTRACE("warp drop=lock target=%d,%d exit=%d", x, y, exiting);
return;
}
LG_LOCK(wlWm.surfaceLock);
if (wlWm.lockedPointer)
{
LG_UNLOCK(wlWm.surfaceLock);
MTRACE("warp drop=lock-race target=%d,%d exit=%d", x, y, exiting);
return;
}
int width, height;
wlWm.desktop->getSize(&width, &height);
if (x < 0) x = 0;
else if (x >= width) x = width - 1;
if (y < 0) y = 0;
else if (y >= height) y = height - 1;
struct wl_region * region = wl_compositor_create_region(wlWm.compositor);
wl_region_add(region, x, y, 1, 1);
const uint32_t confId = proxyId(wlWm.confinedPointer);
uint32_t tempId = 0;
uint64_t warpSeq = 0;
if (wlWm.confinedPointer)
{
zwp_confined_pointer_v1_set_region(wlWm.confinedPointer, region);
wl_surface_commit(wlWm.surface);
zwp_confined_pointer_v1_set_region(wlWm.confinedPointer, NULL);
}
else
{
struct zwp_confined_pointer_v1 * confine = createConfine(region);
tempId = proxyId(confine);
wl_surface_commit(wlWm.surface);
zwp_confined_pointer_v1_destroy(confine);
}
wl_surface_commit(wlWm.surface);
wl_region_destroy(region);
warpSeq = app_mouseSeq();
LG_UNLOCK(wlWm.surfaceLock);
MLOG(warpSeq, "warp req=%d,%d target=%d,%d exit=%d conf=%u temp=%u",
reqX, reqY, x, y, exiting, confId, tempId);
} }
void waylandRealignPointer(void) void waylandRealignPointer(void)
{ {
if (!wlWm.warpSupport) if (wlWm.pointerInSurface)
app_resyncMouseBasic(); app_updateCursorPos(wlWm.motion.x, wlWm.motion.y);
} }
void waylandGuestPointerUpdated(double x, double y, double localX, double localY) void waylandGuestPointerUpdated(double x, double y, double localX,
double localY)
{ {
const char * drop = NULL; (void)x;
if (!wlWm.pointer) (void)y;
drop = "pointer"; (void)localX;
else if (!wlWm.warpSupport) (void)localY;
drop = "support";
else if (!wlWm.pointerInSurface)
drop = "surface";
else if (wlWm.lockedPointer)
drop = "lock";
if (drop)
{
MTRACE("guest drop=%s guest=%.3f,%.3f local=%.3f,%.3f",
drop, x, y, localX, localY);
return;
}
MTRACE("guest warp guest=%.3f,%.3f local=%.3f,%.3f",
x, y, localX, localY);
waylandWarpPointer((int) round(localX), (int) round(localY), false);
} }

View File

@@ -34,13 +34,6 @@
static struct Option waylandOptions[] = static struct Option waylandOptions[] =
{ {
{
.module = "wayland",
.name = "warpSupport",
.description = "Enable cursor warping",
.type = OPTION_TYPE_BOOL,
.value.x_bool = true,
},
{ {
.module = "wayland", .module = "wayland",
.name = "fractionScale", .name = "fractionScale",
@@ -112,7 +105,6 @@ static bool waylandInit(const LG_DSInitParams params)
LG_LOCK_INIT(wlWm.pendingHDRLock); LG_LOCK_INIT(wlWm.pendingHDRLock);
LG_LOCK_INIT(wlWm.hdrLock); LG_LOCK_INIT(wlWm.hdrLock);
wlWm.desktop = WL_Desktops[0]; wlWm.desktop = WL_Desktops[0];
atomic_init(&wlWm.confActive, false);
atomic_init(&wlWm.lockActive, false); atomic_init(&wlWm.lockActive, false);
atomic_init(&wlWm.cmFeaturesDone, false); atomic_init(&wlWm.cmFeaturesDone, false);
atomic_init(&wlWm.cmCanDoHDR, false); atomic_init(&wlWm.cmCanDoHDR, false);
@@ -141,7 +133,6 @@ static bool waylandInit(const LG_DSInitParams params)
wl_list_init(&wlWm.surfaceOutputs); wl_list_init(&wlWm.surfaceOutputs);
wlWm.warpSupport = option_get_bool("wayland", "warpSupport");
wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale"); wlWm.useFractionalScale = option_get_bool("wayland", "fractionScale");
if (!waylandPollInit()) if (!waylandPollInit())
@@ -224,7 +215,7 @@ static bool waylandGetProp(LG_DSProperty prop, void * ret)
{ {
if (prop == LG_DS_WARP_SUPPORT) if (prop == LG_DS_WARP_SUPPORT)
{ {
*(enum LG_DSWarpSupport*)ret = wlWm.warpSupport ? LG_DS_WARP_SURFACE : LG_DS_WARP_NONE; *(enum LG_DSWarpSupport*)ret = LG_DS_WARP_NONE;
return true; return true;
} }

View File

@@ -123,7 +123,6 @@ struct WaylandHDRParameters
struct WaylandDSState struct WaylandDSState
{ {
_Atomic(bool) confActive;
_Atomic(bool) lockActive; _Atomic(bool) lockActive;
bool keyboardGrabbed; bool keyboardGrabbed;
bool pointerInSurface; bool pointerInSurface;
@@ -143,7 +142,6 @@ struct WaylandDSState
bool fractionalScale; bool fractionalScale;
bool needsResize; bool needsResize;
bool configured; bool configured;
bool warpSupport;
struct WlMotion motion; struct WlMotion motion;
double scrollState; double scrollState;
@@ -196,11 +194,8 @@ struct WaylandDSState
struct zwp_relative_pointer_manager_v1 * relativePointerManager; struct zwp_relative_pointer_manager_v1 * relativePointerManager;
struct zwp_pointer_constraints_v1 * pointerConstraints; struct zwp_pointer_constraints_v1 * pointerConstraints;
struct zwp_relative_pointer_v1 * relativePointer; struct zwp_relative_pointer_v1 * relativePointer;
struct zwp_confined_pointer_v1 * confinedPointer;
struct zwp_locked_pointer_v1 * lockedPointer; struct zwp_locked_pointer_v1 * lockedPointer;
struct wl_callback * confSync; bool captureRequested;
bool confReq;
bool inputLive;
bool showPointer; bool showPointer;
uint32_t pointerEnterSerial; uint32_t pointerEnterSerial;
@@ -392,7 +387,8 @@ bool waylandIsPointerGrabbed(void);
bool waylandIsPointerCaptured(void); bool waylandIsPointerCaptured(void);
void waylandRealignPointer(void); void waylandRealignPointer(void);
void waylandWarpPointer(int x, int y, bool exiting); void waylandWarpPointer(int x, int y, bool exiting);
void waylandGuestPointerUpdated(double x, double y, double localX, double localY); void waylandGuestPointerUpdated(double x, double y, double localX,
double localY);
bool waylandGetKeyLabel(int key, char * label, size_t size); bool waylandGetKeyLabel(int key, char * label, size_t size);
// output module // output module
bool waylandOutputInit(void); bool waylandOutputInit(void);

View File

@@ -72,9 +72,6 @@ void app_invalidateWindow(bool full);
void app_handleMouseRelative(double normx, double normy, void app_handleMouseRelative(double normx, double normy,
double rawx, double rawy); double rawx, double rawy);
void app_handleMouseBasic(void);
void app_resyncMouseBasic(void);
void app_handleButtonPress(int button); void app_handleButtonPress(int button);
void app_handleButtonRelease(int button); void app_handleButtonRelease(int button);
void app_handleWheelMotion(double motion); void app_handleWheelMotion(double motion);

View File

@@ -269,6 +269,9 @@ void app_handleEnterEvent(bool entered)
return; return;
g_cursor.realign = true; g_cursor.realign = true;
if (g_cursor.grab)
core_setCursorInView(true);
else
core_handleMouseAbsolute(); core_handleMouseAbsolute();
} }
else else
@@ -652,58 +655,6 @@ void app_handleGrabEvent(bool active)
core_handleGrabEvent(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
// capture mode.
void app_handleMouseBasic(void)
{
/* do not pass mouse events to the guest if we do not have focus */
if (!g_cursor.guest.valid || !g_state.haveSrcSize || !g_state.focused ||
app_isOverlayMode())
return;
if (!core_inputEnabled())
return;
if (lgInput_supports(LG_INPUT_SUPPORT_MOUSE_ABSOLUTE))
return;
const bool inView =
g_cursor.pos.x >= g_state.dstRect.x &&
g_cursor.pos.x < g_state.dstRect.x + g_state.dstRect.w &&
g_cursor.pos.y >= g_state.dstRect.y &&
g_cursor.pos.y < g_state.dstRect.y + g_state.dstRect.h;
core_setCursorInView(inView);
/* translate the current position to guest coordinate space */
struct DoublePoint guest;
util_localCurToGuest(&guest);
int x = (int) round(util_clamp(guest.x, 0, g_state.srcSize.x) -
g_cursor.projected.x);
int y = (int) round(util_clamp(guest.y, 0, g_state.srcSize.y) -
g_cursor.projected.y);
if (!x && !y)
return;
g_cursor.projected.x += x;
g_cursor.projected.y += y;
if (!lgInput_mouseMotion(x, y))
DEBUG_ERROR("failed to send mouse motion message");
}
void app_resyncMouseBasic(void)
{
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) void app_updateWindowPos(int x, int y)
{ {
g_state.windowPos.x = x; g_state.windowPos.x = x;

View File

@@ -327,10 +327,13 @@ void core_setGrabQuiet(bool enable)
/* 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
* was focused without the cursor being in window already */ * was focused without the cursor being in window already */
if (warpSupport != LG_DS_WARP_NONE)
{
struct DoublePoint local; struct DoublePoint local;
const bool valid = util_guestCurToLocal(&local); const bool valid = util_guestCurToLocal(&local);
MTRACE("grab align valid=%d", valid); MTRACE("grab align valid=%d", valid);
core_warpPointer(local.x, local.y, true); core_warpPointer(local.x, local.y, true);
}
if (g_params.grabKeyboard) if (g_params.grabKeyboard)
g_state.ds->grabKeyboard(); g_state.ds->grabKeyboard();
@@ -348,14 +351,19 @@ void core_setGrabQuiet(bool enable)
g_state.ds->uncapturePointer(); g_state.ds->uncapturePointer();
if (!g_params.captureInputOnly && warpSupport != LG_DS_WARP_NONE) if (warpSupport == LG_DS_WARP_NONE)
core_handleMouseAbsolute();
else
{
if (!g_params.captureInputOnly)
applyView(g_state.ds->isPointerGrabbed(), false); applyView(g_state.ds->isPointerGrabbed(), false);
/* if exiting capture when input on capture only we need to align the local /* if exiting capture when input on capture only we need to align the
* cursor to the guest's location before it is shown. */ * local cursor to the guest's location before it is shown. */
if (g_params.captureInputOnly || !g_params.hideMouse) if (g_params.captureInputOnly || !g_params.hideMouse)
core_alignToGuest(); core_alignToGuest();
} }
}
} }
bool core_warpPointer(int x, int y, bool exiting) bool core_warpPointer(int x, int y, bool exiting)
@@ -372,6 +380,14 @@ bool core_warpPointer(int x, int y, bool exiting)
return false; return false;
} }
enum LG_DSWarpSupport warpSupport = LG_DS_WARP_NONE;
app_getProp(LG_DS_WARP_SUPPORT, &warpSupport);
if (warpSupport == LG_DS_WARP_NONE)
{
MTRACE("warp drop=support target=%d,%d exit=%d", x, y, exiting);
return false;
}
if (!exiting && g_cursor.warpState == WARP_STATE_OFF) if (!exiting && g_cursor.warpState == WARP_STATE_OFF)
{ {
MTRACE("warp drop=state target=%d,%d exit=%d", x, y, exiting); MTRACE("warp drop=state target=%d,%d exit=%d", x, y, exiting);

View File

@@ -1150,14 +1150,8 @@ int main_cursorThread(void * unused)
g_cursor.guest.y = pointer.y; g_cursor.guest.y = pointer.y;
g_cursor.guest.valid = true; g_cursor.guest.valid = true;
if (!wasValid && core_inputEnabled()) if (!wasValid && core_inputEnabled())
{
core_alignToGuest(); core_alignToGuest();
app_resyncMouseBasic();
} }
}
if (hotspotChanged)
app_resyncMouseBasic();
if ((pointer.flags & LG_TRANSPORT_POINTER_POSITION) || hotspotChanged) if ((pointer.flags & LG_TRANSPORT_POINTER_POSITION) || hotspotChanged)
core_handleGuestMouseUpdate(); core_handleGuestMouseUpdate();

View File

@@ -343,9 +343,6 @@ struct CursorState
/* the guest's cursor position */ /* the guest's cursor position */
struct CursorInfo guest; struct CursorInfo guest;
/* the projected position after move, for app_handleMouseBasic only */
struct Point projected;
}; };
// forwards // forwards

View File

@@ -432,8 +432,6 @@ All command line options
+-----------------------+-------+-------+-------------------------+ +-----------------------+-------+-------+-------------------------+
| Long | Short | Value | Description | | Long | Short | Value | Description |
+=======================+=======+=======+=========================+ +=======================+=======+=======+=========================+
| wayland:warpSupport | | yes | Enable cursor warping |
+-----------------------+-------+-------+-------------------------+
| wayland:fractionScale | | yes | Enable fractional scale | | wayland:fractionScale | | yes | Enable fractional scale |
+-----------------------+-------+-------+-------------------------+ +-----------------------+-------+-------+-------------------------+