diff --git a/autotests/client/test_pointer_constraints.cpp b/autotests/client/test_pointer_constraints.cpp --- a/autotests/client/test_pointer_constraints.cpp +++ b/autotests/client/test_pointer_constraints.cpp @@ -250,9 +250,18 @@ QVERIFY(lockedSpy.wait()); QVERIFY(unlockedSpy.isEmpty()); + const QPointF hint = QPointF(1.5, 0.5); + QSignalSpy hintChangedSpy(serverLockedPointer.data(), &LockedPointerInterface::cursorPositionHintChanged); + lockedPointer->setCursorPositionHint(hint); + QCOMPARE(serverLockedPointer->cursorPositionHint(), QPointF(-1., -1.)); + surface->commit(Surface::CommitFlag::None); + QVERIFY(hintChangedSpy.wait()); + QCOMPARE(serverLockedPointer->cursorPositionHint(), hint); + // and unlock again serverLockedPointer->setLocked(false); QCOMPARE(serverLockedPointer->isLocked(), false); + QCOMPARE(serverLockedPointer->cursorPositionHint(), QPointF(-1., -1.)); QCOMPARE(lockedChangedSpy.count(), 2); QTEST(!serverSurface->lockedPointer().isNull(), "hasConstraintAfterUnlock"); QTEST(pointerConstraintsChangedSpy.count(), "pointerChangedCount"); diff --git a/src/server/pointerconstraints_interface.h b/src/server/pointerconstraints_interface.h --- a/src/server/pointerconstraints_interface.h +++ b/src/server/pointerconstraints_interface.h @@ -124,6 +124,24 @@ **/ QRegion region() const; + /** + * Indicates where the mouse cursor should be positioned after it has been unlocked again. + * The compositor can warp the cursor at this moment to the position. For that it + * will not emit any relative motion events. The hint is relative to the top-left + * corner of the surface the lock was applied to. Only non-negative x and y values + * are allowed. Otherwise the hint is invalid and should be ignored by the compositor. + * + * In case the client never set the hint, an invalid one will be returned. + * + * This function should be called when the compositor decides to break the lock or the + * client unbinds the resource. To set the position in this case the compositor should + * call this function when the aboutToBeUnbound signal has been emitted. + * + * @see cursorPositionHintChanged + * @since 5.49 + **/ + QPointF cursorPositionHint() const; + /** * Whether the Compositor set this pointer lock to be active. * @see setLocked @@ -138,6 +156,8 @@ * this LockedPointerInterface was created for has pointer focus * and the pointer is inside the {@link region}. * + * Unlocking resets the cursor position hint. + * * @param locked Whether the lock should be active * @see isLocked * @see lockedChanged @@ -152,6 +172,14 @@ **/ void regionChanged(); + /** + * Emitted whenever the cursor position hint changes. + * This happens when the parent SurfaceInterface gets committed + * @see cursorPositionHint + * @since 5.49 + **/ + void cursorPositionHintChanged(); + /** * Emitted whenever the {@link isLocked} state changes. * @see isLocked diff --git a/src/server/pointerconstraints_interface.cpp b/src/server/pointerconstraints_interface.cpp --- a/src/server/pointerconstraints_interface.cpp +++ b/src/server/pointerconstraints_interface.cpp @@ -67,13 +67,17 @@ void LockedPointerInterface::Private::commit() { - if (!regionIsSet) { - return; + if (regionIsSet) { + region = pendingRegion; + pendingRegion = QRegion(); + regionIsSet = false; + emit q_func()->regionChanged(); + } + if (hintIsSet) { + hint = pendingHint; + hintIsSet = false; + emit q_func()->cursorPositionHintChanged(); } - region = pendingRegion; - pendingRegion = QRegion(); - regionIsSet = false; - emit q_func()->regionChanged(); } LockedPointerInterface::LockedPointerInterface(Private *p, QObject *parent) @@ -102,6 +106,12 @@ return d->region; } +QPointF LockedPointerInterface::cursorPositionHint() const +{ + Q_D(); + return d->hint; +} + bool LockedPointerInterface::isLocked() const { Q_D(); @@ -114,6 +124,9 @@ if (locked == d->locked) { return; } + if (!locked) { + d->hint = QPointF(-1., -1.); + } d->locked = locked; d->updateLocked(); emit lockedChanged(); diff --git a/src/server/pointerconstraints_interface_p.h b/src/server/pointerconstraints_interface_p.h --- a/src/server/pointerconstraints_interface_p.h +++ b/src/server/pointerconstraints_interface_p.h @@ -64,13 +64,17 @@ LifeTime lifeTime; QRegion region; bool locked = false; + QPointF hint = QPointF(-1., -1.); protected: Private(PointerConstraintsInterfaceVersion interfaceVersion, LockedPointerInterface *q, Global *c, wl_resource *parentResource, const wl_interface *interface, const void *implementation); QRegion pendingRegion; bool regionIsSet = false; + QPointF pendingHint; + bool hintIsSet = false; + private: LockedPointerInterface *q_func() { return reinterpret_cast(q); diff --git a/src/server/pointerconstraints_interface_v1.cpp b/src/server/pointerconstraints_interface_v1.cpp --- a/src/server/pointerconstraints_interface_v1.cpp +++ b/src/server/pointerconstraints_interface_v1.cpp @@ -171,11 +171,9 @@ void LockedPointerUnstableV1Interface::Private::setCursorPositionHintCallback(wl_client *client, wl_resource *resource, wl_fixed_t surface_x, wl_fixed_t surface_y) { Q_UNUSED(client) - Q_UNUSED(resource) - Q_UNUSED(surface_x) - Q_UNUSED(surface_y) - // double buffered - // TODO: implement + auto p = cast(resource); + p->pendingHint = QPointF(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)); + p->hintIsSet = true; } void LockedPointerUnstableV1Interface::Private::setRegionCallback(wl_client *client, wl_resource *resource, wl_resource * region)