Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/server/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions src/server/kernel/private/wcursor_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)

Expand All @@ -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
167 changes: 165 additions & 2 deletions src/server/kernel/wcursor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,81 @@
#include <QLoggingCategory>
#include <private/qcursor_p.h>

#include "wrelativepointerv1.h"
#include "wserver.h"

#include <wlr/types/wlr_pointer_constraints_v1.h>
#include <wlr/types/wlr_relative_pointer_v1.h>
#include <pixman.h>
#include <cmath>

// 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<double>(x, box->x1);
x = std::min<double>(x, box->x2);
y = std::max<double>(y, box->y1);
y = std::min<double>(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<double>(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<int>(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

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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<WRelativePointerManagerV1>()) {
relative->sendRelativeMotion(wlrSeat, static_cast<uint64_t>(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)
{
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions src/server/kernel/wcursor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
79 changes: 79 additions & 0 deletions src/server/protocols/wpointerconstraintsv1.cpp
Original file line number Diff line number Diff line change
@@ -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 <qwpointerconstraintsv1.h>
#include <qwdisplay.h>

#include <wlr/types/wlr_pointer_constraints_v1.h>

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<qw_pointer_constraints_v1>();
}

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<qw_pointer_constraints_v1>();
}

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
43 changes: 43 additions & 0 deletions src/server/protocols/wpointerconstraintsv1.h
Original file line number Diff line number Diff line change
@@ -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 <WServer>

#include <QObject>

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
Loading
Loading