1 /*
2 SPDX-FileCopyrightText: 2018 Roman Gilg <subdiff@gmail.com>
3 SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7
8 #include "pointerlockerwayland.h"
9
10 #include <QDebug>
11
12 #include <KWayland/Client/compositor.h>
13 #include <KWayland/Client/connection_thread.h>
14 #include <KWayland/Client/pointer.h>
15 #include <KWayland/Client/pointerconstraints.h>
16 #include <KWayland/Client/registry.h>
17 #include <KWayland/Client/region.h>
18 #include <KWayland/Client/relativepointer.h>
19 #include <KWayland/Client/seat.h>
20 #include <KWayland/Client/surface.h>
21
22 using namespace KWayland::Client;
23
PointerLockerWayland(QObject * parent)24 PointerLockerWayland::PointerLockerWayland(QObject *parent)
25 : AbstractPointerLocker(parent)
26 , m_connectionThreadObject(ConnectionThread::fromApplication(this))
27 {
28 setupRegistry();
29 }
30
setupRegistry()31 void PointerLockerWayland::setupRegistry()
32 {
33 Registry *registry = new Registry(this);
34
35 connect(registry, &Registry::compositorAnnounced, this,
36 [this, registry](quint32 name, quint32 version) {
37 m_compositor = registry->createCompositor(name, version, this);
38 }
39 );
40 connect(registry, &Registry::relativePointerManagerUnstableV1Announced, this,
41 [this, registry](quint32 name, quint32 version) {
42 Q_ASSERT(!m_relativePointerManager);
43 m_relativePointerManager = registry->createRelativePointerManager(name, version, this);
44 }
45 );
46 connect(registry, &Registry::seatAnnounced, this,
47 [this, registry](quint32 name, quint32 version) {
48 m_seat = registry->createSeat(name, version, this);
49 if (m_seat->hasPointer()) {
50 m_pointer = m_seat->createPointer(this);
51 }
52 connect(m_seat, &Seat::hasPointerChanged, this,
53 [this] (bool hasPointer) {
54 delete m_pointer;
55
56 if (!hasPointer)
57 return;
58
59 m_pointer = m_seat->createPointer(this);
60
61 delete m_relativePointer;
62 m_relativePointer = m_relativePointerManager->createRelativePointer(m_pointer, this);
63 connect(m_relativePointer, &RelativePointer::relativeMotion,
64 this, [this] (const QSizeF &delta) {
65 Q_EMIT pointerMoved({delta.width(), delta.height()});
66 });
67 }
68 );
69 }
70 );
71 connect(registry, &Registry::pointerConstraintsUnstableV1Announced, this,
72 [this, registry](quint32 name, quint32 version) {
73 m_pointerConstraints = registry->createPointerConstraints(name, version, this);
74 }
75 );
76 connect(registry, &Registry::interfacesAnnounced, this,
77 [this] {
78 Q_ASSERT(m_compositor);
79 Q_ASSERT(m_seat);
80 Q_ASSERT(m_pointerConstraints);
81 }
82 );
83 registry->create(m_connectionThreadObject);
84 registry->setup();
85 }
86
isLockEffective() const87 bool PointerLockerWayland::isLockEffective() const
88 {
89 return m_lockedPointer && m_lockedPointer->isValid();
90 }
91
enforceLock()92 void PointerLockerWayland::enforceLock()
93 {
94 if (!m_isLocked) {
95 return;
96 }
97
98 QScopedPointer<Surface> winSurface(Surface::fromWindow(m_window));
99 if (!winSurface) {
100 qWarning() << "Locking a window that is not mapped";
101 return;
102 }
103 auto *lockedPointer = m_pointerConstraints->lockPointer(winSurface.data(),
104 m_pointer,
105 nullptr,
106 PointerConstraints::LifeTime::Persistent,
107 this);
108
109 if (!lockedPointer) {
110 qDebug() << "ERROR when receiving locked pointer!";
111 return;
112 }
113 m_lockedPointer = lockedPointer;
114
115 connect(lockedPointer, &LockedPointer::locked, this, [this] {
116 Q_EMIT lockEffectiveChanged(true);
117 });
118 connect(lockedPointer, &LockedPointer::unlocked, this, [this] {
119 Q_EMIT lockEffectiveChanged(false);
120 });
121 }
122
setLocked(bool lock)123 void PointerLockerWayland::setLocked(bool lock)
124 {
125 if (m_isLocked == lock) {
126 return;
127 }
128
129 if (!isSupported()) {
130 qWarning() << "Locking before having our interfaces announced";
131 return;
132 }
133
134 m_isLocked = lock;
135 if (lock) {
136 enforceLock();
137 } else {
138 cleanupLock();
139 }
140 Q_EMIT lockedChanged(lock);
141 }
142
cleanupLock()143 void PointerLockerWayland::cleanupLock()
144 {
145 if (!m_lockedPointer) {
146 return;
147 }
148 m_lockedPointer->release();
149 m_lockedPointer->deleteLater();
150 m_lockedPointer = nullptr;
151 Q_EMIT lockEffectiveChanged(false);
152 }
153
setWindow(QWindow * window)154 void PointerLockerWayland::setWindow(QWindow* window)
155 {
156 if (m_window == window) {
157 return;
158 }
159 cleanupLock();
160
161 if (m_window) {
162 disconnect(m_window, &QWindow::visibleChanged, this, &PointerLockerWayland::enforceLock);
163 }
164 AbstractPointerLocker::setWindow(window);
165 connect(m_window, &QWindow::visibleChanged, this, &PointerLockerWayland::enforceLock);
166
167 if (m_isLocked) {
168 enforceLock();
169 }
170 }
171