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