diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt index 16cdcac64..4cae7752d 100644 --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -143,6 +143,8 @@ set(SOURCES protocols/private/wtextinputv3.cpp protocols/private/wvirtualkeyboardv1.cpp protocols/wcursorshapemanagerv1.cpp + protocols/wpointerconstraintsv1.cpp + protocols/wrelativepointerv1.cpp protocols/woutputmanagerv1.cpp protocols/wextforeigntoplevellistv1.cpp @@ -228,6 +230,8 @@ set(HEADERS protocols/winputpopupsurface.h protocols/WInputPopupSurface protocols/wcursorshapemanagerv1.h + protocols/wpointerconstraintsv1.h + protocols/wrelativepointerv1.h protocols/WCursorShapeManagerV1 protocols/woutputmanagerv1.h protocols/WOutputManagerV1 diff --git a/src/server/kernel/private/wcursor_p.h b/src/server/kernel/private/wcursor_p.h index 01301df52..712e7585f 100644 --- a/src/server/kernel/private/wcursor_p.h +++ b/src/server/kernel/private/wcursor_p.h @@ -29,6 +29,7 @@ struct wlr_pointer_pinch_end_event; struct wlr_pointer_hold_begin_event; struct wlr_pointer_hold_end_event; struct wlr_cursor; +struct wlr_pointer_constraint_v1; struct wlr_touch_down_event; struct wlr_touch_up_event; struct wlr_touch_motion_event; @@ -72,6 +73,9 @@ class Q_DECL_HIDDEN WCursorPrivate : public WWrapObjectPrivate void connect(); void processCursorMotion(QW_NAMESPACE::qw_pointer *device, uint32_t time); + bool applyPointerConstraint(QW_NAMESPACE::qw_pointer *device, uint32_t timeMsec, + double dx, double dy, double dxUnaccel, double dyUnaccel, + const QPointF &oldPos); W_DECLARE_PUBLIC(WCursor) @@ -88,6 +92,9 @@ class Q_DECL_HIDDEN WCursorPrivate : public WWrapObjectPrivate Qt::MouseButton button = Qt::NoButton; QPointF lastPressedOrTouchDownPosition; bool visible = true; + wlr_pointer_constraint_v1 *activeConstraint = nullptr; + QPointF lockedWarpTarget; + bool cursorVisibleBeforeLock = true; }; WAYLIB_SERVER_END_NAMESPACE diff --git a/src/server/kernel/wcursor.cpp b/src/server/kernel/wcursor.cpp index a19b3e78e..a0522bc02 100644 --- a/src/server/kernel/wcursor.cpp +++ b/src/server/kernel/wcursor.cpp @@ -30,6 +30,81 @@ #include #include +#include "wrelativepointerv1.h" +#include "wserver.h" + +#include +#include +#include +#include + +// Inline replacement for wlr_region_confine() to avoid linking the wlroots +// util library directly from libwaylibserver (it is only available indirectly +// via QWlroots). The implementation mirrors wlr_region_confine from +// wlroots/util/region.c, using only the pixman API that waylibserver already +// links against. +static void regionConfine(const pixman_region32_t *region, double x1, double y1, + double x2, double y2, double *x2_out, double *y2_out, + const pixman_box32_t *box) +{ + // Clamp the target to the box. + double x = x2; + double y = y2; + x = std::max(x, box->x1); + x = std::min(x, box->x2); + y = std::max(y, box->y1); + y = std::min(y, box->y2); + + // If the clamped point is inside the region we're done. + if (pixman_region32_contains_point(region, std::lround(x), std::lround(y), nullptr)) { + *x2_out = x; + *y2_out = y; + return; + } + + // Calculate the delta from the source point. + const double dx = x - x1; + const double dy = y - y1; + const double delta = std::max(std::fabs(dx), std::fabs(dy)); + if (delta < 1e-9) { + *x2_out = x1; + *y2_out = y1; + return; + } + + // Step along the line from (x1,y1) to (x,y) pixel by pixel. + const double step_x = dx / delta; + const double step_y = dy / delta; + for (int i = static_cast(delta); i > 0; i--) { + x -= step_x; + y -= step_y; + if (pixman_region32_contains_point(region, std::lround(x), std::lround(y), nullptr)) { + *x2_out = x; + *y2_out = y; + return; + } + } + + *x2_out = x1; + *y2_out = y1; +} + +static bool regionConfineWrapper(const pixman_region32_t *region, double x1, double y1, + double x2, double y2, double *x2_out, double *y2_out) +{ + int nrects = 0; + const pixman_box32_t *rects = pixman_region32_rectangles(region, &nrects); + if (nrects == 0) + return false; + + for (int i = 0; i < nrects; i++) { + const pixman_box32_t *box = &rects[i]; + regionConfine(region, x1, y1, x2, y2, x2_out, y2_out, box); + return true; + } + return false; +} + QW_USE_NAMESPACE WAYLIB_SERVER_BEGIN_NAMESPACE @@ -80,15 +155,22 @@ void WCursorPrivate::sendLeaveEvent(WInputDevice *device) void WCursorPrivate::on_motion(wlr_pointer_motion_event *event) { auto device = qw_pointer::from(event->pointer); + const QPointF oldPos = q_func()->position(); q_func()->move(device, QPointF(event->delta_x, event->delta_y)); - processCursorMotion(device, event->time_msec); + if (!applyPointerConstraint(device, event->time_msec, event->delta_x, event->delta_y, + event->unaccel_dx, event->unaccel_dy, oldPos)) + processCursorMotion(device, event->time_msec); } void WCursorPrivate::on_motion_absolute(wlr_pointer_motion_absolute_event *event) { auto device = qw_pointer::from(event->pointer); + const QPointF oldPos = q_func()->position(); q_func()->setScalePosition(device, QPointF(event->x, event->y)); - processCursorMotion(device, event->time_msec); + const QPointF delta = q_func()->position() - oldPos; + if (!applyPointerConstraint(device, event->time_msec, delta.x(), delta.y(), + delta.x(), delta.y(), oldPos)) + processCursorMotion(device, event->time_msec); } void WCursorPrivate::on_button(wlr_pointer_button_event *event) @@ -321,6 +403,50 @@ void WCursorPrivate::processCursorMotion(qw_pointer *device, uint32_t time) seat->notifyMotion(q, WInputDevice::fromHandle(device), time); } +bool WCursorPrivate::applyPointerConstraint(qw_pointer *device, uint32_t timeMsec, + double dx, double dy, + double dxUnaccel, double dyUnaccel, + const QPointF &oldPos) +{ + W_Q(WCursor); + if (!activeConstraint) + return false; + + wlr_seat *wlrSeat = seat ? seat->handle()->handle() : nullptr; + if (!wlrSeat) + return false; + + if (activeConstraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) { + if (auto *server = seat->server()) { + if (auto *relative = server->findInterface()) { + relative->sendRelativeMotion(wlrSeat, static_cast(timeMsec) * 1000, + dx, dy, dxUnaccel, dyUnaccel); + } + } + // Warp back to the anchor so physical movement does not accumulate. + q->setPosition(device, lockedWarpTarget); + return true; + } + + if (activeConstraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) { + // The constraint region and the seat's last surface-local position are + // both in logical coordinates, so the output-layout delta maps directly. + const double sx = wlrSeat->pointer_state.sx; + const double sy = wlrSeat->pointer_state.sy; + double confinedSx = sx + dx; + double confinedSy = sy + dy; + if (regionConfineWrapper(&activeConstraint->region, sx, sy, + confinedSx, confinedSy, &confinedSx, &confinedSy)) { + q->setPosition(device, oldPos + QPointF(confinedSx - sx, confinedSy - sy)); + } else { + q->setPosition(device, oldPos); + } + return false; + } + + return false; +} + WCursor::WCursor(WCursorPrivate &dd, QObject *parent) : WWrapObject(dd, parent) { @@ -660,6 +786,43 @@ void WCursor::setVisible(bool visible) Q_EMIT visibleChanged(); } +void WCursor::setActivePointerConstraint(wlr_pointer_constraint_v1 *constraint) +{ + W_D(WCursor); + if (d->activeConstraint == constraint) + return; + + if (d->activeConstraint + && d->activeConstraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) + setVisible(d->cursorVisibleBeforeLock); + + d->activeConstraint = constraint; + + if (!constraint) + return; + + if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) { + d->cursorVisibleBeforeLock = isVisible(); + setVisible(false); + wlr_seat *wlrSeat = d->seat ? d->seat->handle()->handle() : nullptr; + if (wlrSeat && constraint->current.cursor_hint.enabled) { + const double sx = wlrSeat->pointer_state.sx; + const double sy = wlrSeat->pointer_state.sy; + d->lockedWarpTarget = position() + + QPointF(constraint->current.cursor_hint.x - sx, + constraint->current.cursor_hint.y - sy); + } else { + d->lockedWarpTarget = position(); + } + } +} + +wlr_pointer_constraint_v1 *WCursor::activePointerConstraint() const +{ + W_DC(WCursor); + return d->activeConstraint; +} + QPointF WCursor::position() const { W_DC(WCursor); diff --git a/src/server/kernel/wcursor.h b/src/server/kernel/wcursor.h index 3755e3670..4a1bb9b7c 100644 --- a/src/server/kernel/wcursor.h +++ b/src/server/kernel/wcursor.h @@ -22,6 +22,8 @@ class qw_output_cursor; class qw_surface; QW_END_NAMESPACE +struct wlr_pointer_constraint_v1; + WAYLIB_SERVER_BEGIN_NAMESPACE class WSeat; @@ -76,6 +78,8 @@ class WAYLIB_SERVER_EXPORT WCursor : public WWrapObject bool isVisible() const; void setVisible(bool visible); + void setActivePointerConstraint(wlr_pointer_constraint_v1 *constraint); + wlr_pointer_constraint_v1 *activePointerConstraint() const; QPointF position() const; QPointF lastPressedOrTouchDownPosition() const; diff --git a/src/server/protocols/wpointerconstraintsv1.cpp b/src/server/protocols/wpointerconstraintsv1.cpp new file mode 100644 index 000000000..8529f7205 --- /dev/null +++ b/src/server/protocols/wpointerconstraintsv1.cpp @@ -0,0 +1,79 @@ +// Copyright (C) 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "wpointerconstraintsv1.h" +#include "private/wglobal_p.h" + +#include +#include + +#include + +WAYLIB_SERVER_BEGIN_NAMESPACE + +using QW_NAMESPACE::qw_pointer_constraints_v1; + +class Q_DECL_HIDDEN WPointerConstraintsV1Private : public WObjectPrivate +{ +public: + WPointerConstraintsV1Private(WPointerConstraintsV1 *qq) + : WObjectPrivate(qq) + { + } + + inline qw_pointer_constraints_v1 *handle() const { + return q_func()->nativeInterface(); + } + + inline wlr_pointer_constraints_v1 *nativeHandle() const { + Q_ASSERT(handle()); + return handle()->handle(); + } + + W_DECLARE_PUBLIC(WPointerConstraintsV1) +}; + +WPointerConstraintsV1::WPointerConstraintsV1() + : WObject(*new WPointerConstraintsV1Private(this)) +{ + +} + +qw_pointer_constraints_v1 *WPointerConstraintsV1::handle() const +{ + return nativeInterface(); +} + +QByteArrayView WPointerConstraintsV1::interfaceName() const +{ + return "zwp_pointer_constraints_v1"; +} + +wlr_pointer_constraint_v1 *WPointerConstraintsV1::constraintForSurface(wlr_surface *surface, wlr_seat *seat) const +{ + if (!m_handle) + return nullptr; + + return handle()->constraint_for_surface(surface, seat); +} + +void WPointerConstraintsV1::create(WServer *server) +{ + if (!m_handle) { + m_handle = qw_pointer_constraints_v1::create(*server->handle()); + QObject::connect(handle(), &qw_pointer_constraints_v1::notify_new_constraint, this, [this] (wlr_pointer_constraint_v1 *constraint) { + Q_EMIT newConstraint(constraint); + }); + } +} + +wl_global *WPointerConstraintsV1::global() const +{ + W_D(const WPointerConstraintsV1); + if (m_handle) + return d->nativeHandle()->global; + + return nullptr; +} + +WAYLIB_SERVER_END_NAMESPACE diff --git a/src/server/protocols/wpointerconstraintsv1.h b/src/server/protocols/wpointerconstraintsv1.h new file mode 100644 index 000000000..54150f79c --- /dev/null +++ b/src/server/protocols/wpointerconstraintsv1.h @@ -0,0 +1,43 @@ +// Copyright (C) 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#pragma once + +#include + +#include + +QW_BEGIN_NAMESPACE +class qw_pointer_constraints_v1; +QW_END_NAMESPACE + +struct wlr_pointer_constraint_v1; +struct wlr_seat; +struct wlr_surface; + +WAYLIB_SERVER_BEGIN_NAMESPACE + +class WPointerConstraintsV1Private; +class WAYLIB_SERVER_EXPORT WPointerConstraintsV1 : public QObject, public WObject, public WServerInterface +{ + Q_OBJECT + W_DECLARE_PRIVATE(WPointerConstraintsV1) + +public: + explicit WPointerConstraintsV1(); + + QW_NAMESPACE::qw_pointer_constraints_v1 *handle() const; + + QByteArrayView interfaceName() const override; + + wlr_pointer_constraint_v1 *constraintForSurface(wlr_surface *surface, wlr_seat *seat) const; + +Q_SIGNALS: + void newConstraint(wlr_pointer_constraint_v1 *constraint); + +protected: + void create(WServer *server) override; + wl_global *global() const override; +}; + +WAYLIB_SERVER_END_NAMESPACE diff --git a/src/server/protocols/wrelativepointerv1.cpp b/src/server/protocols/wrelativepointerv1.cpp new file mode 100644 index 000000000..7a9f40d48 --- /dev/null +++ b/src/server/protocols/wrelativepointerv1.cpp @@ -0,0 +1,78 @@ +// Copyright (C) 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "wrelativepointerv1.h" +#include "private/wglobal_p.h" + +#include +#include + +#include + +WAYLIB_SERVER_BEGIN_NAMESPACE + +using QW_NAMESPACE::qw_relative_pointer_manager_v1; + +class Q_DECL_HIDDEN WRelativePointerManagerV1Private : public WObjectPrivate +{ +public: + WRelativePointerManagerV1Private(WRelativePointerManagerV1 *qq) + : WObjectPrivate(qq) + { + } + + inline qw_relative_pointer_manager_v1 *handle() const { + return q_func()->nativeInterface(); + } + + inline wlr_relative_pointer_manager_v1 *nativeHandle() const { + Q_ASSERT(handle()); + return handle()->handle(); + } + + W_DECLARE_PUBLIC(WRelativePointerManagerV1) +}; + +WRelativePointerManagerV1::WRelativePointerManagerV1() + : WObject(*new WRelativePointerManagerV1Private(this)) +{ + +} + +qw_relative_pointer_manager_v1 *WRelativePointerManagerV1::handle() const +{ + return nativeInterface(); +} + +QByteArrayView WRelativePointerManagerV1::interfaceName() const +{ + return "zwp_relative_pointer_manager_v1"; +} + +void WRelativePointerManagerV1::sendRelativeMotion(wlr_seat *seat, uint64_t timeUsec, + double dx, double dy, + double dxUnaccel, double dyUnaccel) const +{ + if (!m_handle || !seat) + return; + + handle()->send_relative_motion(seat, timeUsec, dx, dy, dxUnaccel, dyUnaccel); +} + +void WRelativePointerManagerV1::create(WServer *server) +{ + if (!m_handle) { + m_handle = qw_relative_pointer_manager_v1::create(*server->handle()); + } +} + +wl_global *WRelativePointerManagerV1::global() const +{ + W_D(const WRelativePointerManagerV1); + if (m_handle) + return d->nativeHandle()->global; + + return nullptr; +} + +WAYLIB_SERVER_END_NAMESPACE diff --git a/src/server/protocols/wrelativepointerv1.h b/src/server/protocols/wrelativepointerv1.h new file mode 100644 index 000000000..68ea489f2 --- /dev/null +++ b/src/server/protocols/wrelativepointerv1.h @@ -0,0 +1,41 @@ +// Copyright (C) 2026 UnionTech Software Technology Co., Ltd. +// SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#pragma once + +#include + +#include + +#include + +QW_BEGIN_NAMESPACE +class qw_relative_pointer_manager_v1; +QW_END_NAMESPACE + +struct wlr_seat; + +WAYLIB_SERVER_BEGIN_NAMESPACE + +class WRelativePointerManagerV1Private; +class WAYLIB_SERVER_EXPORT WRelativePointerManagerV1 : public QObject, public WObject, public WServerInterface +{ + Q_OBJECT + W_DECLARE_PRIVATE(WRelativePointerManagerV1) + +public: + explicit WRelativePointerManagerV1(); + + QW_NAMESPACE::qw_relative_pointer_manager_v1 *handle() const; + + QByteArrayView interfaceName() const override; + + void sendRelativeMotion(wlr_seat *seat, uint64_t timeUsec, + double dx, double dy, double dxUnaccel, double dyUnaccel) const; + +protected: + void create(WServer *server) override; + wl_global *global() const override; +}; + +WAYLIB_SERVER_END_NAMESPACE