8#include <KWayland/Client/compositor.h>
9#include <KWayland/Client/connection_thread.h>
10#include <KWayland/Client/pointer.h>
11#include <KWayland/Client/pointerconstraints.h>
12#include <KWayland/Client/region.h>
13#include <KWayland/Client/registry.h>
14#include <KWayland/Client/seat.h>
15#include <KWayland/Client/surface.h>
18#include <QGuiApplication>
24#include <xcb/xproto.h>
30 , m_connectionThreadObject(ConnectionThread::fromApplication(this))
39 Registry *registry =
new Registry(
this);
40 setupRegistry(registry);
43void WaylandBackend::setupRegistry(Registry *registry)
45 connect(registry, &Registry::compositorAnnounced,
this,
46 [
this, registry](quint32 name, quint32
version) {
47 m_compositor = registry->createCompositor(name,
version,
this);
49 connect(registry, &Registry::seatAnnounced,
this,
50 [
this, registry](quint32 name, quint32
version) {
52 if (m_seat->hasPointer()) {
53 m_pointer = m_seat->createPointer(this);
55 connect(m_seat, &Seat::hasPointerChanged,
this,
58 m_pointer = m_seat->createPointer(
this);
61 connect(registry, &Registry::pointerConstraintsUnstableV1Announced,
this,
62 [
this, registry](quint32 name, quint32
version) {
63 m_pointerConstraints =
registry->createPointerConstraints(name,
version,
this);
65 connect(registry, &Registry::interfacesAnnounced,
this,
67 Q_ASSERT(m_compositor);
69 Q_ASSERT(m_pointerConstraints);
71 registry->create(m_connectionThreadObject);
75bool WaylandBackend::isLocked()
77 return m_lockedPointer && m_lockedPointer->isValid();
80bool WaylandBackend::isConfined()
82 return m_confinedPointer && m_confinedPointer->isValid();
85static PointerConstraints::LifeTime lifeTime(
bool persistent)
87 return persistent ? PointerConstraints::LifeTime::Persistent : PointerConstraints::LifeTime::OneShot;
94 qDebug() <<
"Abort locking because already locked. Allow errors to test relocking (and crashing).";
97 qDebug() <<
"Trying to lock although already locked. Crash expected.";
101 qDebug() <<
"Abort locking because already confined. Allow errors to test locking while being confined (and crashing).";
104 qDebug() <<
"Trying to lock although already confined. Crash expected.";
106 qDebug() <<
"------ Lock requested ------";
107 qDebug() <<
"Persistent:" << persistent <<
"| Region:" << region;
108 std::unique_ptr<Surface> winSurface(Surface::fromWindow(
view()));
109 std::unique_ptr<Region> wlRegion(m_compositor->createRegion(
this));
110 wlRegion->add(region);
112 auto *lockedPointer = m_pointerConstraints->lockPointer(winSurface.get(),
115 lifeTime(persistent),
118 if (!lockedPointer) {
119 qDebug() <<
"ERROR when receiving locked pointer!";
122 m_lockedPointer = lockedPointer;
123 m_lockedPointerPersistent = persistent;
125 connect(lockedPointer, &LockedPointer::locked,
this, [
this]() {
126 qDebug() <<
"------ LOCKED! ------";
128 m_lockedPointer->setCursorPositionHint(QPointF(10., 10.));
134 connect(lockedPointer, &LockedPointer::unlocked,
this, [
this]() {
135 qDebug() <<
"------ UNLOCKED! ------";
136 if (!m_lockedPointerPersistent) {
145 if (!m_lockedPointer) {
146 qDebug() <<
"Unlock requested, but there is no lock. Abort.";
149 qDebug() <<
"------ Unlock requested ------";
153void WaylandBackend::cleanupLock()
155 if (!m_lockedPointer) {
158 m_lockedPointer->release();
159 m_lockedPointer->deleteLater();
160 m_lockedPointer =
nullptr;
167 qDebug() <<
"Abort confining because already confined. Allow errors to test reconfining (and crashing).";
170 qDebug() <<
"Trying to lock although already locked. Crash expected.";
174 qDebug() <<
"Abort confining because already locked. Allow errors to test confining while being locked (and crashing).";
177 qDebug() <<
"Trying to confine although already locked. Crash expected.";
179 qDebug() <<
"------ Confine requested ------";
180 qDebug() <<
"Persistent:" << persistent <<
"| Region:" << region;
181 std::unique_ptr<Surface> winSurface(Surface::fromWindow(
view()));
182 std::unique_ptr<Region> wlRegion(m_compositor->createRegion(
this));
183 wlRegion->add(region);
185 auto *confinedPointer = m_pointerConstraints->confinePointer(winSurface.get(),
188 lifeTime(persistent),
191 if (!confinedPointer) {
192 qDebug() <<
"ERROR when receiving confined pointer!";
195 m_confinedPointer = confinedPointer;
196 m_confinedPointerPersistent = persistent;
197 connect(confinedPointer, &ConfinedPointer::confined,
this, [
this]() {
198 qDebug() <<
"------ CONFINED! ------";
201 connect(confinedPointer, &ConfinedPointer::unconfined,
this, [
this]() {
202 qDebug() <<
"------ UNCONFINED! ------";
203 if (!m_confinedPointerPersistent) {
211 if (!m_confinedPointer) {
212 qDebug() <<
"Unconfine requested, but there is no confine. Abort.";
215 qDebug() <<
"------ Unconfine requested ------";
219void WaylandBackend::cleanupConfine()
221 if (!m_confinedPointer) {
224 m_confinedPointer->release();
225 m_confinedPointer->deleteLater();
226 m_confinedPointer =
nullptr;
234 xcb_disconnect(m_xcbConn);
242 m_xcbConn = xcb_connect(
nullptr,
nullptr);
244 qDebug() <<
"Could not open XCB connection.";
250 auto winId =
view()->winId();
253 QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
255 auto cookie = xcb_warp_pointer_checked(m_xcbConn,
265 xcb_flush(m_xcbConn);
267 xcb_generic_error_t *error = xcb_request_check(m_xcbConn, cookie);
269 qDebug() <<
"Lock (warp) failed with XCB error:" << error->error_code;
273 qDebug() <<
"LOCK (warp)";
280 QGuiApplication::restoreOverrideCursor();
281 qDebug() <<
"------ Unlock requested ------";
288 if (!tryConfine(error)) {
289 qDebug() <<
"Confine (grab) failed with XCB error:" << error;
292 qDebug() <<
"CONFINE (grab)";
298 auto cookie = xcb_ungrab_pointer_checked(m_xcbConn, XCB_CURRENT_TIME);
299 xcb_flush(m_xcbConn);
301 xcb_generic_error_t *error = xcb_request_check(m_xcbConn, cookie);
303 qDebug() <<
"Unconfine failed with XCB error:" << error->error_code;
307 qDebug() <<
"UNCONFINE (ungrab)";
313 if (!confineBeforeHide) {
314 QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
318 if (!tryConfine(error)) {
319 qDebug() <<
"Confine failed with XCB error:" << error;
320 if (!confineBeforeHide) {
321 QGuiApplication::restoreOverrideCursor();
325 if (confineBeforeHide) {
326 QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
328 qDebug() <<
"HIDE AND CONFINE (lock)";
334 QGuiApplication::restoreOverrideCursor();
335 qDebug() <<
"UNDO HIDE AND CONFINE (unlock)";
338bool XBackend::tryConfine(
int &error)
340 auto winId =
view()->winId();
342 auto cookie = xcb_grab_pointer(m_xcbConn,
352 xcb_flush(m_xcbConn);
354 xcb_generic_error_t *e =
nullptr;
355 auto *reply = xcb_grab_pointer_reply(m_xcbConn, cookie, &e);
357 error = e->error_code;
367 QGuiApplication app(argc, argv);
370 if (app.platformName() == QStringLiteral(
"wayland")) {
371 qDebug() <<
"Starting up: Wayland native mode";
374 qDebug() <<
"Starting up: Xserver/Xwayland legacy mode";
380 QQmlContext *context = view.engine()->rootContext();
381 context->setContextProperty(QStringLiteral(
"org_kde_kwin_tests_pointerconstraints_backend"), backend);
383 view.setSource(QUrl::fromLocalFile(QStringLiteral(DIR) + QStringLiteral(
"/pointerconstraintstest.qml")));
386 backend->
init(&view);
391#include "moc_pointerconstraintstest.cpp"
QQuickView * view() const
void lockChanged(bool locked)
void forceSurfaceCommit()
virtual void init(QQuickView *view)
void confineChanged(bool confined)
void confineRequest(bool persistent, QRect region) override
void init(QQuickView *view) override
void unlockRequest() override
WaylandBackend(QObject *parent=nullptr)
void unconfineRequest() override
void lockRequest(bool persistent, QRect region) override
void undoHideRequest() override
void unlockRequest() override
void confineRequest(bool persistent, QRect region) override
void init(QQuickView *view) override
void unconfineRequest() override
XBackend(QObject *parent=nullptr)
void hideAndConfineRequest(bool confineBeforeHide) override
void lockRequest(bool persistent, QRect region) override
KWayland::Client::Registry * registry