diff --git a/src/seat/helper.cpp b/src/seat/helper.cpp index 80291148e..5d3044319 100644 --- a/src/seat/helper.cpp +++ b/src/seat/helper.cpp @@ -79,7 +79,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -1340,6 +1342,8 @@ void Helper::init(Treeland::Treeland *treeland) m_foreignToplevel = m_server->attach(); m_extForeignToplevelListV1 = m_server->attach(); + m_server->attach(); + m_server->attach(); connect(m_shellHandler, &ShellHandler::surfaceWrapperAdded, diff --git a/waylib/src/server/CMakeLists.txt b/waylib/src/server/CMakeLists.txt index c3409b043..1836618c3 100644 --- a/waylib/src/server/CMakeLists.txt +++ b/waylib/src/server/CMakeLists.txt @@ -196,6 +196,8 @@ set(SOURCES protocols/private/wvirtualkeyboardv1.cpp protocols/ext_foreign_toplevel_image_capture_source.c #TODO: Remove after wlroots 0.20 protocols/wcursorshapemanagerv1.cpp + protocols/wrelativepointermanagerv1.cpp + protocols/wpointerconstraintsv1.cpp protocols/woutputmanagerv1.cpp protocols/wextforeigntoplevellistv1.cpp protocols/wsecuritycontextmanager.cpp @@ -291,6 +293,10 @@ set(HEADERS protocols/WInputPopupSurface protocols/wcursorshapemanagerv1.h protocols/WCursorShapeManagerV1 + protocols/wrelativepointermanagerv1.h + protocols/WRelativePointerManagerV1 + protocols/wpointerconstraintsv1.h + protocols/WPointerConstraintsV1 protocols/woutputmanagerv1.h protocols/WOutputManagerV1 protocols/wlayershell.h diff --git a/waylib/src/server/kernel/wcursor.cpp b/waylib/src/server/kernel/wcursor.cpp index f9557d24b..a8db83ea1 100644 --- a/waylib/src/server/kernel/wcursor.cpp +++ b/waylib/src/server/kernel/wcursor.cpp @@ -85,6 +85,17 @@ void WCursorPrivate::sendLeaveEvent(WInputDevice *device) void WCursorPrivate::on_motion(wlr_pointer_motion_event *event) { auto device = qw_pointer::from(event->pointer); + if (auto inputDevice = WInputDevice::fromHandle(device)) { + if (auto deviceSeat = inputDevice->seat()) { + deviceSeat->refreshPointerConstraint(); + deviceSeat->notifyRelativeMotion(event->time_msec, + QPointF(event->delta_x, event->delta_y), + QPointF(event->unaccel_dx, event->unaccel_dy)); + if (deviceSeat->pointerMotionLocked()) + return; + } + } + q_func()->move(device, QPointF(event->delta_x, event->delta_y)); processCursorMotion(device, event->time_msec); } diff --git a/waylib/src/server/kernel/wseat.cpp b/waylib/src/server/kernel/wseat.cpp index 19a36488e..e8fcf2188 100644 --- a/waylib/src/server/kernel/wseat.cpp +++ b/waylib/src/server/kernel/wseat.cpp @@ -5,6 +5,8 @@ #include "wcursor.h" #include "winputdevice.h" #include "woutput.h" +#include "wpointerconstraintsv1.h" +#include "wrelativepointermanagerv1.h" #include "wsurface.h" #include "wxdgsurface.h" #include "platformplugin/qwlrootsintegration.h" @@ -16,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +31,8 @@ #include #include +#include + #include #include #include @@ -132,6 +137,104 @@ class Q_DECL_HIDDEN WSeatPrivate : public WWrapObjectPrivate return nativeHandle()->keyboard_state.focused_surface; } + inline WPointerConstraintsV1 *pointerConstraints() const { + const auto *server = q_func()->server(); + return server ? server->findInterface() : nullptr; + } + + inline qw_pointer_constraint_v1 *constraintForSurface(WSurface *surface) const { + auto *constraints = pointerConstraints(); + return constraints ? constraints->constraintForSurface(surface, q_func()) : nullptr; + } + + inline bool pointerConstraintContains(qw_pointer_constraint_v1 *constraint, + const QPointF &surfacePos) const { + if (!constraint || !constraint->handle()) + return false; + + pixman_box32_t box; + return pixman_region32_contains_point(&constraint->handle()->region, + std::floor(surfacePos.x()), + std::floor(surfacePos.y()), + &box); + } + + inline bool pointerMotionLocked() const { + return activePointerConstraint + && activePointerConstraint->handle() + && activePointerConstraint->handle()->type == WLR_POINTER_CONSTRAINT_V1_LOCKED; + } + + inline void deactivatePointerConstraint() { + auto *constraint = activePointerConstraint.data(); + + activePointerConstraint.clear(); + QObject::disconnect(activePointerConstraintDestroyConnection); + QObject::disconnect(activePointerConstraintRegionConnection); + activePointerConstraintDestroyConnection = {}; + activePointerConstraintRegionConnection = {}; + + if (constraint && constraint->handle()) + constraint->send_deactivated(); + } + + inline void updatePointerConstraint(WSurface *surface, const QPointF &surfacePos) { + auto *constraint = constraintForSurface(surface); + if (!constraint) { + if (activePointerConstraint) + deactivatePointerConstraint(); + return; + } + + if (activePointerConstraint && activePointerConstraint->handle() == constraint->handle()) { + if (!pointerConstraintContains(constraint, surfacePos)) + deactivatePointerConstraint(); + return; + } + + if (activePointerConstraint) + deactivatePointerConstraint(); + + if (!pointerConstraintContains(constraint, surfacePos)) + return; + + activePointerConstraint = constraint; + activePointerConstraintDestroyConnection = + QObject::connect(constraint, &qw_pointer_constraint_v1::before_destroy, + q_func(), [this] { + activePointerConstraint.clear(); + QObject::disconnect(activePointerConstraintDestroyConnection); + QObject::disconnect(activePointerConstraintRegionConnection); + activePointerConstraintDestroyConnection = {}; + activePointerConstraintRegionConnection = {}; + }); + activePointerConstraintRegionConnection = + QObject::connect(constraint, &qw_pointer_constraint_v1::notify_set_region, + q_func(), [this] { + updatePointerConstraint(); + }); + constraint->send_activated(); + } + + inline void updatePointerConstraint() { + auto *focus = pointerFocusSurface(); + if (!focus) { + if (activePointerConstraint) + deactivatePointerConstraint(); + return; + } + + auto *surface = WSurface::fromHandle(focus); + if (!surface) { + if (activePointerConstraint) + deactivatePointerConstraint(); + return; + } + + const auto &pointerState = nativeHandle()->pointer_state; + updatePointerConstraint(surface, QPointF(pointerState.sx, pointerState.sy)); + } + inline bool doNotifyMotion(WSurface *target, QObject *eventObject, QPointF localPos, uint32_t timestamp) { if (target) { if (pointerFocusSurface()) { @@ -143,8 +246,13 @@ class Q_DECL_HIDDEN WSeatPrivate : public WWrapObjectPrivate // take pointer focus for this surface. doEnter(target, eventObject, localPos); } + + updatePointerConstraint(target, localPos); } + if (pointerMotionLocked()) + return true; + handle()->pointer_notify_motion(timestamp, localPos.x(), localPos.y()); return true; } @@ -178,6 +286,9 @@ class Q_DECL_HIDDEN WSeatPrivate : public WWrapObjectPrivate } auto tmp = oldPointerFocusSurface; oldPointerFocusSurface = handle()->handle()->pointer_state.focused_surface; + if (activePointerConstraint && activePointerConstraint->handle()->surface != surface->handle()->handle()) + deactivatePointerConstraint(); + handle()->pointer_notify_enter(surface->handle()->handle(), position.x(), position.y()); if (!pointerFocusSurface()) { // Because if the last pointer focus surface is a popup, the 'pointerNotifyEnter' @@ -202,9 +313,13 @@ class Q_DECL_HIDDEN WSeatPrivate : public WWrapObjectPrivate }); } + updatePointerConstraint(surface, position); + return true; } inline void doClearPointerFocus() { + if (activePointerConstraint) + deactivatePointerConstraint(); pointerFocusEventObject.clear(); handle()->pointer_notify_clear_focus(); Q_ASSERT(!handle()->handle()->pointer_state.focused_surface); @@ -369,7 +484,10 @@ class Q_DECL_HIDDEN WSeatPrivate : public WWrapObjectPrivate QPointer focusWindow; QPointer pointerFocusEventObject; QPointer m_keyboardFocusSurface; + QPointer activePointerConstraint; QMetaObject::Connection onEventObjectDestroy; + QMetaObject::Connection activePointerConstraintDestroyConnection; + QMetaObject::Connection activePointerConstraintRegionConnection; wlr_surface *oldPointerFocusSurface = nullptr; bool gestureActive = false; @@ -1167,6 +1285,25 @@ void WSeat::setAlwaysUpdateHoverTarget(bool newIgnoreSurfacePointerEventExclusiv Q_EMIT alwaysUpdateHoverTargetChanged(); } +void WSeat::notifyRelativeMotion(uint32_t timestamp, const QPointF &delta, + const QPointF &unacceleratedDelta) +{ + if (auto *manager = server() ? server()->findInterface() : nullptr) + manager->sendRelativeMotion(this, timestamp, delta, unacceleratedDelta); +} + +void WSeat::refreshPointerConstraint() +{ + W_D(WSeat); + d->updatePointerConstraint(); +} + +bool WSeat::pointerMotionLocked() const +{ + W_DC(WSeat); + return d->pointerMotionLocked(); +} + void WSeat::notifyMotion(WCursor *cursor, WInputDevice *device, uint32_t timestamp) { W_D(WSeat); diff --git a/waylib/src/server/kernel/wseat.h b/waylib/src/server/kernel/wseat.h index 8a58770e0..040f1f294 100644 --- a/waylib/src/server/kernel/wseat.h +++ b/waylib/src/server/kernel/wseat.h @@ -140,6 +140,10 @@ class WAYLIB_SERVER_EXPORT WSeat : public WWrapObject, public WServerInterface bool filterUnacceptedEvent(QWindow *targetWindow, QInputEvent *event); // pointer + void notifyRelativeMotion(uint32_t timestamp, const QPointF &delta, + const QPointF &unacceleratedDelta); + void refreshPointerConstraint(); + bool pointerMotionLocked() const; void notifyMotion(WCursor *cursor, WInputDevice *device, uint32_t timestamp); void notifyButton(WCursor *cursor, WInputDevice *device, Qt::MouseButton button, wl_pointer_button_state_t state, diff --git a/waylib/src/server/protocols/WPointerConstraintsV1 b/waylib/src/server/protocols/WPointerConstraintsV1 new file mode 100644 index 000000000..5d519a897 --- /dev/null +++ b/waylib/src/server/protocols/WPointerConstraintsV1 @@ -0,0 +1 @@ +#include "wpointerconstraintsv1.h" diff --git a/waylib/src/server/protocols/WRelativePointerManagerV1 b/waylib/src/server/protocols/WRelativePointerManagerV1 new file mode 100644 index 000000000..8708937e3 --- /dev/null +++ b/waylib/src/server/protocols/WRelativePointerManagerV1 @@ -0,0 +1 @@ +#include "wrelativepointermanagerv1.h" diff --git a/waylib/src/server/protocols/wpointerconstraintsv1.cpp b/waylib/src/server/protocols/wpointerconstraintsv1.cpp new file mode 100644 index 000000000..0d5b587d7 --- /dev/null +++ b/waylib/src/server/protocols/wpointerconstraintsv1.cpp @@ -0,0 +1,72 @@ +// 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 "wseat.h" +#include "wsurface.h" + +#include "private/wglobal_p.h" + +#include +#include +#include + +QW_USE_NAMESPACE +WAYLIB_SERVER_BEGIN_NAMESPACE + +class Q_DECL_HIDDEN WPointerConstraintsV1Private : public WObjectPrivate +{ +public: + explicit WPointerConstraintsV1Private(WPointerConstraintsV1 *qq) + : WObjectPrivate(qq) + { + } +}; + +WPointerConstraintsV1::WPointerConstraintsV1() + : WObject(*new WPointerConstraintsV1Private(this)) +{ +} + +qw_pointer_constraints_v1 *WPointerConstraintsV1::handle() const +{ + return nativeInterface(); +} + +qw_pointer_constraint_v1 *WPointerConstraintsV1::constraintForSurface(WSurface *surface, + const WSeat *seat) const +{ + if (!handle() || !surface || !seat) + return nullptr; + + return qw_pointer_constraint_v1::from( + handle()->constraint_for_surface(surface->handle()->handle(), seat->nativeHandle())); +} + +QByteArrayView WPointerConstraintsV1::interfaceName() const +{ + return "zwp_pointer_constraints_v1"; +} + +void WPointerConstraintsV1::create(WServer *server) +{ + if (m_handle) + return; + + 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(qw_pointer_constraint_v1::from(constraint)); + }); +} + +wl_global *WPointerConstraintsV1::global() const +{ + if (!handle()) + return nullptr; + + return handle()->handle()->global; +} + +WAYLIB_SERVER_END_NAMESPACE diff --git a/waylib/src/server/protocols/wpointerconstraintsv1.h b/waylib/src/server/protocols/wpointerconstraintsv1.h new file mode 100644 index 000000000..7a29bf22b --- /dev/null +++ b/waylib/src/server/protocols/wpointerconstraintsv1.h @@ -0,0 +1,42 @@ +// 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_constraint_v1; +class qw_pointer_constraints_v1; +QW_END_NAMESPACE + +struct wlr_pointer_constraint_v1; + +WAYLIB_SERVER_BEGIN_NAMESPACE + +class WSeat; +class WSurface; + +class WAYLIB_SERVER_EXPORT WPointerConstraintsV1 : public QObject, public WObject, public WServerInterface +{ + Q_OBJECT + +public: + explicit WPointerConstraintsV1(); + + QW_NAMESPACE::qw_pointer_constraints_v1 *handle() const; + QW_NAMESPACE::qw_pointer_constraint_v1 *constraintForSurface(WSurface *surface, const WSeat *seat) const; + + QByteArrayView interfaceName() const override; + +Q_SIGNALS: + void newConstraint(QW_NAMESPACE::qw_pointer_constraint_v1 *constraint); + +protected: + void create(WServer *server) override; + wl_global *global() const override; +}; + +WAYLIB_SERVER_END_NAMESPACE diff --git a/waylib/src/server/protocols/wrelativepointermanagerv1.cpp b/waylib/src/server/protocols/wrelativepointermanagerv1.cpp new file mode 100644 index 000000000..2e0b766d3 --- /dev/null +++ b/waylib/src/server/protocols/wrelativepointermanagerv1.cpp @@ -0,0 +1,66 @@ +// 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 "wrelativepointermanagerv1.h" + +#include "wseat.h" + +#include "private/wglobal_p.h" + +#include +#include + +QW_USE_NAMESPACE +WAYLIB_SERVER_BEGIN_NAMESPACE + +class Q_DECL_HIDDEN WRelativePointerManagerV1Private : public WObjectPrivate +{ +public: + explicit WRelativePointerManagerV1Private(WRelativePointerManagerV1 *qq) + : WObjectPrivate(qq) + { + } +}; + +WRelativePointerManagerV1::WRelativePointerManagerV1() + : WObject(*new WRelativePointerManagerV1Private(this)) +{ +} + +qw_relative_pointer_manager_v1 *WRelativePointerManagerV1::handle() const +{ + return nativeInterface(); +} + +void WRelativePointerManagerV1::sendRelativeMotion(WSeat *seat, uint32_t timeMsec, + const QPointF &delta, + const QPointF &unacceleratedDelta) +{ + if (!seat || !handle()) + return; + + handle()->send_relative_motion(seat->nativeHandle(), uint64_t(timeMsec) * 1000, + delta.x(), delta.y(), + unacceleratedDelta.x(), unacceleratedDelta.y()); +} + +QByteArrayView WRelativePointerManagerV1::interfaceName() const +{ + return "zwp_relative_pointer_manager_v1"; +} + +void WRelativePointerManagerV1::create(WServer *server) +{ + if (!m_handle) + m_handle = qw_relative_pointer_manager_v1::create(*server->handle()); +} + +wl_global *WRelativePointerManagerV1::global() const +{ + if (!handle()) + return nullptr; + + return handle()->handle()->global; +} + +WAYLIB_SERVER_END_NAMESPACE diff --git a/waylib/src/server/protocols/wrelativepointermanagerv1.h b/waylib/src/server/protocols/wrelativepointermanagerv1.h new file mode 100644 index 000000000..ec267d13d --- /dev/null +++ b/waylib/src/server/protocols/wrelativepointermanagerv1.h @@ -0,0 +1,38 @@ +// 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 + +WAYLIB_SERVER_BEGIN_NAMESPACE + +class WSeat; + +class WAYLIB_SERVER_EXPORT WRelativePointerManagerV1 : public QObject, public WObject, public WServerInterface +{ + Q_OBJECT + +public: + explicit WRelativePointerManagerV1(); + + QW_NAMESPACE::qw_relative_pointer_manager_v1 *handle() const; + + void sendRelativeMotion(WSeat *seat, uint32_t timeMsec, const QPointF &delta, + const QPointF &unacceleratedDelta); + + QByteArrayView interfaceName() const override; + +protected: + void create(WServer *server) override; + wl_global *global() const override; +}; + +WAYLIB_SERVER_END_NAMESPACE