Changeset View
Changeset View
Standalone View
Standalone View
plugins/platforms/wayland/wayland_backend.cpp
1 | /******************************************************************** | 1 | /******************************************************************** | ||
---|---|---|---|---|---|
2 | KWin - the KDE window manager | 2 | KWin - the KDE window manager | ||
3 | This file is part of the KDE project. | 3 | This file is part of the KDE project. | ||
4 | 4 | | |||
5 | Copyright (C) 2013 Martin Gräßlin <mgraesslin@kde.org> | 5 | Copyright 2019 Roman Gilg <subdiff@gmail.com> | ||
6 | Copyright 2013 Martin Gräßlin <mgraesslin@kde.org> | ||||
6 | 7 | | |||
7 | This program is free software; you can redistribute it and/or modify | 8 | This program is free software; you can redistribute it and/or modify | ||
8 | it under the terms of the GNU General Public License as published by | 9 | it under the terms of the GNU General Public License as published by | ||
9 | the Free Software Foundation; either version 2 of the License, or | 10 | the Free Software Foundation; either version 2 of the License, or | ||
10 | (at your option) any later version. | 11 | (at your option) any later version. | ||
11 | 12 | | |||
12 | This program is distributed in the hope that it will be useful, | 13 | This program is distributed in the hope that it will be useful, | ||
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | GNU General Public License for more details. | 16 | GNU General Public License for more details. | ||
16 | 17 | | |||
17 | You should have received a copy of the GNU General Public License | 18 | You should have received a copy of the GNU General Public License | ||
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. | 19 | along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
19 | *********************************************************************/ | 20 | *********************************************************************/ | ||
20 | // own | | |||
21 | #include "wayland_backend.h" | 21 | #include "wayland_backend.h" | ||
22 | #include <config-kwin.h> | 22 | | ||
23 | // KWin | 23 | #if HAVE_WAYLAND_EGL | ||
24 | #include "cursor.h" | 24 | #include "egl_wayland_backend.h" | ||
25 | #endif | ||||
25 | #include "logging.h" | 26 | #include "logging.h" | ||
26 | #include "main.h" | | |||
27 | #include "scene_qpainter_wayland_backend.h" | 27 | #include "scene_qpainter_wayland_backend.h" | ||
28 | #include "wayland_output.h" | ||||
29 | | ||||
30 | #include "composite.h" | ||||
31 | #include "cursor.h" | ||||
32 | #include "input.h" | ||||
33 | #include "main.h" | ||||
34 | #include "outputscreens.h" | ||||
35 | #include "pointer_input.h" | ||||
28 | #include "screens.h" | 36 | #include "screens.h" | ||
29 | #include "wayland_server.h" | | |||
30 | #include "wayland_cursor_theme.h" | 37 | #include "wayland_cursor_theme.h" | ||
31 | #if HAVE_WAYLAND_EGL | 38 | #include "wayland_server.h" | ||
32 | #include "egl_wayland_backend.h" | 39 | | ||
33 | #endif | 40 | #include <config-kwin.h> | ||
41 | | ||||
34 | #include <KWayland/Client/buffer.h> | 42 | #include <KWayland/Client/buffer.h> | ||
35 | #include <KWayland/Client/compositor.h> | 43 | #include <KWayland/Client/compositor.h> | ||
36 | #include <KWayland/Client/connection_thread.h> | 44 | #include <KWayland/Client/connection_thread.h> | ||
37 | #include <KWayland/Client/event_queue.h> | 45 | #include <KWayland/Client/event_queue.h> | ||
38 | #include <KWayland/Client/keyboard.h> | 46 | #include <KWayland/Client/keyboard.h> | ||
39 | #include <KWayland/Client/pointer.h> | 47 | #include <KWayland/Client/pointer.h> | ||
40 | #include <KWayland/Client/pointerconstraints.h> | 48 | #include <KWayland/Client/pointerconstraints.h> | ||
41 | #include <KWayland/Client/pointergestures.h> | 49 | #include <KWayland/Client/pointergestures.h> | ||
42 | #include <KWayland/Client/region.h> | | |||
43 | #include <KWayland/Client/registry.h> | 50 | #include <KWayland/Client/registry.h> | ||
51 | #include <KWayland/Client/relativepointer.h> | ||||
44 | #include <KWayland/Client/seat.h> | 52 | #include <KWayland/Client/seat.h> | ||
45 | #include <KWayland/Client/server_decoration.h> | 53 | #include <KWayland/Client/server_decoration.h> | ||
46 | #include <KWayland/Client/shell.h> | 54 | #include <KWayland/Client/shell.h> | ||
47 | #include <KWayland/Client/shm_pool.h> | 55 | #include <KWayland/Client/shm_pool.h> | ||
56 | #include <KWayland/Client/subcompositor.h> | ||||
57 | #include <KWayland/Client/subsurface.h> | ||||
48 | #include <KWayland/Client/surface.h> | 58 | #include <KWayland/Client/surface.h> | ||
49 | #include <KWayland/Client/touch.h> | 59 | #include <KWayland/Client/touch.h> | ||
50 | #include <KWayland/Client/xdgshell.h> | 60 | #include <KWayland/Client/xdgshell.h> | ||
51 | #include <KWayland/Server/buffer_interface.h> | 61 | | ||
52 | #include <KWayland/Server/display.h> | | |||
53 | #include <KWayland/Server/seat_interface.h> | 62 | #include <KWayland/Server/seat_interface.h> | ||
54 | #include <KWayland/Server/surface_interface.h> | | |||
55 | 63 | | |||
56 | #include <KLocalizedString> | | |||
57 | // Qt | | |||
58 | #include <QMetaMethod> | 64 | #include <QMetaMethod> | ||
59 | #include <QThread> | 65 | #include <QThread> | ||
60 | // Wayland | | |||
61 | #include <wayland-cursor.h> | | |||
62 | 66 | | |||
63 | #include <linux/input.h> | 67 | #include <linux/input.h> | ||
68 | #include <wayland-cursor.h> | ||||
64 | 69 | | |||
65 | namespace KWin | 70 | namespace KWin | ||
66 | { | 71 | { | ||
67 | namespace Wayland | 72 | namespace Wayland | ||
68 | { | 73 | { | ||
69 | 74 | | |||
70 | using namespace KWayland::Client; | 75 | using namespace KWayland::Client; | ||
71 | 76 | | |||
77 | WaylandCursor::WaylandCursor(WaylandBackend *backend) | ||||
78 | : QObject(backend) | ||||
79 | , m_backend(backend) | ||||
80 | { | ||||
81 | resetSurface(); | ||||
82 | } | ||||
83 | | ||||
84 | void WaylandCursor::resetSurface() | ||||
85 | { | ||||
86 | delete m_surface; | ||||
87 | m_surface = backend()->compositor()->createSurface(this); | ||||
88 | } | ||||
89 | | ||||
90 | void WaylandCursor::init() | ||||
91 | { | ||||
92 | installImage(); | ||||
93 | } | ||||
94 | | ||||
95 | WaylandCursor::~WaylandCursor() | ||||
96 | { | ||||
97 | delete m_surface; | ||||
98 | } | ||||
99 | | ||||
100 | void WaylandCursor::installImage() | ||||
101 | { | ||||
102 | const QImage image = m_backend->softwareCursor(); | ||||
103 | if (image.isNull() || image.size().isEmpty()) { | ||||
104 | doInstallImage(nullptr, QSize()); | ||||
105 | return; | ||||
106 | } | ||||
107 | wl_buffer *imageBuffer = *(m_backend->shmPool()->createBuffer(image).data()); | ||||
108 | doInstallImage(imageBuffer, image.size()); | ||||
109 | } | ||||
110 | | ||||
111 | void WaylandCursor::doInstallImage(wl_buffer *image, const QSize &size) | ||||
112 | { | ||||
113 | auto *pointer = m_backend->seat()->pointer(); | ||||
114 | if (!pointer || !pointer->isValid()) { | ||||
115 | return; | ||||
116 | } | ||||
117 | pointer->setCursor(m_surface, image ? m_backend->softwareCursorHotspot() : QPoint()); | ||||
118 | drawSurface(image, size); | ||||
119 | } | ||||
120 | | ||||
121 | void WaylandCursor::drawSurface(wl_buffer *image, const QSize &size) | ||||
122 | { | ||||
123 | m_surface->attachBuffer(image); | ||||
124 | m_surface->damage(QRect(QPoint(0,0), size)); | ||||
125 | m_surface->commit(Surface::CommitFlag::None); | ||||
126 | m_backend->flush(); | ||||
127 | } | ||||
128 | | ||||
129 | WaylandSubSurfaceCursor::WaylandSubSurfaceCursor(WaylandBackend *backend) | ||||
130 | : WaylandCursor(backend) | ||||
131 | { | ||||
132 | } | ||||
133 | | ||||
134 | void WaylandSubSurfaceCursor::init() | ||||
135 | { | ||||
136 | if (auto *pointer = backend()->seat()->pointer()) { | ||||
137 | pointer->hideCursor(); | ||||
138 | } | ||||
139 | } | ||||
140 | | ||||
141 | WaylandSubSurfaceCursor::~WaylandSubSurfaceCursor() | ||||
142 | { | ||||
143 | delete m_subSurface; | ||||
144 | } | ||||
145 | | ||||
146 | void WaylandSubSurfaceCursor::changeOutput(WaylandOutput *output) | ||||
147 | { | ||||
148 | delete m_subSurface; | ||||
149 | m_subSurface = nullptr; | ||||
150 | m_output = output; | ||||
151 | if (!output) { | ||||
152 | return; | ||||
153 | } | ||||
154 | createSubSurface(); | ||||
155 | surface()->commit(); | ||||
156 | } | ||||
157 | | ||||
158 | void WaylandSubSurfaceCursor::createSubSurface() | ||||
159 | { | ||||
160 | if (m_subSurface) { | ||||
161 | return; | ||||
162 | } | ||||
163 | if (!m_output) { | ||||
164 | return; | ||||
165 | } | ||||
166 | resetSurface(); | ||||
167 | m_subSurface = backend()->subCompositor()->createSubSurface(surface(), m_output->surface(), this); | ||||
168 | m_subSurface->setMode(SubSurface::Mode::Desynchronized); | ||||
169 | } | ||||
170 | | ||||
171 | void WaylandSubSurfaceCursor::doInstallImage(wl_buffer *image, const QSize &size) | ||||
172 | { | ||||
173 | if (!image) { | ||||
174 | delete m_subSurface; | ||||
175 | m_subSurface = nullptr; | ||||
176 | return; | ||||
177 | } | ||||
178 | createSubSurface(); | ||||
179 | // cursor position might have changed due to different cursor hot spot | ||||
180 | move(input()->pointer()->pos()); | ||||
181 | drawSurface(image, size); | ||||
182 | } | ||||
183 | | ||||
184 | QPointF WaylandSubSurfaceCursor::absoluteToRelativePosition(const QPointF &position) | ||||
185 | { | ||||
186 | auto ret = position - m_output->geometry().topLeft() - backend()->softwareCursorHotspot(); | ||||
187 | return ret; | ||||
188 | } | ||||
189 | | ||||
190 | void WaylandSubSurfaceCursor::move(const QPointF &globalPosition) | ||||
191 | { | ||||
192 | auto *output = backend()->getOutputAt(globalPosition.toPoint()); | ||||
193 | if (!m_output || (output && m_output != output)) { | ||||
194 | changeOutput(output); | ||||
195 | if (!m_output) { | ||||
196 | // cursor might be off the grid | ||||
197 | return; | ||||
198 | } | ||||
199 | installImage(); | ||||
200 | return; | ||||
201 | } | ||||
202 | if (!m_subSurface) { | ||||
203 | return; | ||||
204 | } | ||||
205 | // place the sub-surface relative to the output it is on and factor in the hotspot | ||||
206 | const auto relativePosition = globalPosition.toPoint() - backend()->softwareCursorHotspot() - m_output->geometry().topLeft(); | ||||
207 | m_subSurface->setPosition(relativePosition); | ||||
208 | Compositor::self()->addRepaintFull(); | ||||
209 | } | ||||
210 | | ||||
72 | WaylandSeat::WaylandSeat(wl_seat *seat, WaylandBackend *backend) | 211 | WaylandSeat::WaylandSeat(wl_seat *seat, WaylandBackend *backend) | ||
73 | : QObject(NULL) | 212 | : QObject(NULL) | ||
74 | , m_seat(new Seat(this)) | 213 | , m_seat(new Seat(this)) | ||
75 | , m_pointer(NULL) | 214 | , m_pointer(NULL) | ||
76 | , m_keyboard(NULL) | 215 | , m_keyboard(NULL) | ||
77 | , m_touch(nullptr) | 216 | , m_touch(nullptr) | ||
78 | , m_cursor(NULL) | | |||
79 | , m_enteredSerial(0) | 217 | , m_enteredSerial(0) | ||
80 | , m_backend(backend) | 218 | , m_backend(backend) | ||
81 | , m_installCursor(false) | | |||
82 | { | 219 | { | ||
83 | m_seat->setup(seat); | 220 | m_seat->setup(seat); | ||
84 | connect(m_seat, &Seat::hasKeyboardChanged, this, | 221 | connect(m_seat, &Seat::hasKeyboardChanged, this, | ||
85 | [this](bool hasKeyboard) { | 222 | [this](bool hasKeyboard) { | ||
86 | if (hasKeyboard) { | 223 | if (hasKeyboard) { | ||
87 | m_keyboard = m_seat->createKeyboard(this); | 224 | m_keyboard = m_seat->createKeyboard(this); | ||
88 | connect(m_keyboard, &Keyboard::keyChanged, this, | 225 | connect(m_keyboard, &Keyboard::keyChanged, this, | ||
89 | [this](quint32 key, Keyboard::KeyState state, quint32 time) { | 226 | [this](quint32 key, Keyboard::KeyState state, quint32 time) { | ||
90 | switch (state) { | 227 | switch (state) { | ||
91 | case Keyboard::KeyState::Pressed: | 228 | case Keyboard::KeyState::Pressed: | ||
92 | if (key == KEY_RIGHTCTRL) { | 229 | if (key == KEY_RIGHTCTRL) { | ||
93 | m_backend->togglePointerConfinement(); | 230 | m_backend->togglePointerLock(); | ||
94 | } | 231 | } | ||
95 | m_backend->keyboardKeyPressed(key, time); | 232 | m_backend->keyboardKeyPressed(key, time); | ||
96 | break; | 233 | break; | ||
97 | case Keyboard::KeyState::Released: | 234 | case Keyboard::KeyState::Released: | ||
98 | m_backend->keyboardKeyReleased(key, time); | 235 | m_backend->keyboardKeyReleased(key, time); | ||
99 | break; | 236 | break; | ||
100 | default: | 237 | default: | ||
101 | Q_UNREACHABLE(); | 238 | Q_UNREACHABLE(); | ||
Show All 16 Lines | |||||
118 | } | 255 | } | ||
119 | ); | 256 | ); | ||
120 | connect(m_seat, &Seat::hasPointerChanged, this, | 257 | connect(m_seat, &Seat::hasPointerChanged, this, | ||
121 | [this](bool hasPointer) { | 258 | [this](bool hasPointer) { | ||
122 | if (hasPointer && !m_pointer) { | 259 | if (hasPointer && !m_pointer) { | ||
123 | m_pointer = m_seat->createPointer(this); | 260 | m_pointer = m_seat->createPointer(this); | ||
124 | setupPointerGestures(); | 261 | setupPointerGestures(); | ||
125 | connect(m_pointer, &Pointer::entered, this, | 262 | connect(m_pointer, &Pointer::entered, this, | ||
126 | [this](quint32 serial) { | 263 | [this](quint32 serial, const QPointF &relativeToSurface) { | ||
264 | Q_UNUSED(relativeToSurface) | ||||
127 | m_enteredSerial = serial; | 265 | m_enteredSerial = serial; | ||
128 | if (!m_installCursor) { | | |||
129 | // explicitly hide cursor | | |||
130 | m_pointer->hideCursor(); | | |||
131 | } | | |||
132 | } | 266 | } | ||
133 | ); | 267 | ); | ||
134 | connect(m_pointer, &Pointer::motion, this, | 268 | connect(m_pointer, &Pointer::motion, this, | ||
135 | [this](const QPointF &relativeToSurface, quint32 time) { | 269 | [this](const QPointF &relativeToSurface, quint32 time) { | ||
136 | m_backend->pointerMotion(relativeToSurface, time); | 270 | m_backend->pointerMotionRelativeToOutput(relativeToSurface, time); | ||
137 | } | 271 | } | ||
138 | ); | 272 | ); | ||
139 | connect(m_pointer, &Pointer::buttonStateChanged, this, | 273 | connect(m_pointer, &Pointer::buttonStateChanged, this, | ||
140 | [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState state) { | 274 | [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState state) { | ||
141 | Q_UNUSED(serial) | 275 | Q_UNUSED(serial) | ||
142 | switch (state) { | 276 | switch (state) { | ||
143 | case Pointer::ButtonState::Pressed: | 277 | case Pointer::ButtonState::Pressed: | ||
144 | m_backend->pointerButtonPressed(button, time); | 278 | m_backend->pointerButtonPressed(button, time); | ||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Line(s) | 339 | if (server) { | |||
207 | SeatInterface *si = server->seat(); | 341 | SeatInterface *si = server->seat(); | ||
208 | connect(m_seat, &Seat::hasKeyboardChanged, si, &SeatInterface::setHasKeyboard); | 342 | connect(m_seat, &Seat::hasKeyboardChanged, si, &SeatInterface::setHasKeyboard); | ||
209 | connect(m_seat, &Seat::hasPointerChanged, si, &SeatInterface::setHasPointer); | 343 | connect(m_seat, &Seat::hasPointerChanged, si, &SeatInterface::setHasPointer); | ||
210 | connect(m_seat, &Seat::hasTouchChanged, si, &SeatInterface::setHasTouch); | 344 | connect(m_seat, &Seat::hasTouchChanged, si, &SeatInterface::setHasTouch); | ||
211 | connect(m_seat, &Seat::nameChanged, si, &SeatInterface::setName); | 345 | connect(m_seat, &Seat::nameChanged, si, &SeatInterface::setName); | ||
212 | } | 346 | } | ||
213 | } | 347 | } | ||
214 | 348 | | |||
349 | void WaylandBackend::pointerMotionRelativeToOutput(const QPointF &position, quint32 time) | ||||
350 | { | ||||
351 | auto outputIt = std::find_if(m_outputs.begin(), m_outputs.end(), [this](WaylandOutput *wo) { | ||||
352 | return wo->surface() == m_seat->pointer()->enteredSurface(); | ||||
353 | }); | ||||
354 | Q_ASSERT(outputIt != m_outputs.end()); | ||||
355 | const QPointF outputPosition = (*outputIt)->geometry().topLeft() + position; | ||||
356 | Platform::pointerMotion(outputPosition, time); | ||||
357 | } | ||||
358 | | ||||
215 | WaylandSeat::~WaylandSeat() | 359 | WaylandSeat::~WaylandSeat() | ||
216 | { | 360 | { | ||
217 | destroyPointer(); | 361 | destroyPointer(); | ||
218 | destroyKeyboard(); | 362 | destroyKeyboard(); | ||
219 | destroyTouch(); | 363 | destroyTouch(); | ||
220 | } | 364 | } | ||
221 | 365 | | |||
222 | void WaylandSeat::destroyPointer() | 366 | void WaylandSeat::destroyPointer() | ||
Show All 13 Lines | |||||
236 | } | 380 | } | ||
237 | 381 | | |||
238 | void WaylandSeat::destroyTouch() | 382 | void WaylandSeat::destroyTouch() | ||
239 | { | 383 | { | ||
240 | delete m_touch; | 384 | delete m_touch; | ||
241 | m_touch = nullptr; | 385 | m_touch = nullptr; | ||
242 | } | 386 | } | ||
243 | 387 | | |||
244 | void WaylandSeat::installCursorImage(wl_buffer *image, const QSize &size, const QPoint &hotSpot) | | |||
245 | { | | |||
246 | if (!m_installCursor) { | | |||
247 | return; | | |||
248 | } | | |||
249 | if (!m_pointer || !m_pointer->isValid()) { | | |||
250 | return; | | |||
251 | } | | |||
252 | if (!m_cursor) { | | |||
253 | m_cursor = m_backend->compositor()->createSurface(this); | | |||
254 | } | | |||
255 | if (!m_cursor || !m_cursor->isValid()) { | | |||
256 | return; | | |||
257 | } | | |||
258 | m_pointer->setCursor(m_cursor, hotSpot); | | |||
259 | m_cursor->attachBuffer(image); | | |||
260 | m_cursor->damage(QRect(QPoint(0,0), size)); | | |||
261 | m_cursor->commit(Surface::CommitFlag::None); | | |||
262 | m_backend->flush(); | | |||
263 | } | | |||
264 | | ||||
265 | void WaylandSeat::installCursorImage(const QImage &image, const QPoint &hotSpot) | | |||
266 | { | | |||
267 | if (image.isNull()) { | | |||
268 | installCursorImage(nullptr, QSize(), QPoint()); | | |||
269 | return; | | |||
270 | } | | |||
271 | installCursorImage(*(m_backend->shmPool()->createBuffer(image).data()), image.size(), hotSpot); | | |||
272 | } | | |||
273 | | ||||
274 | void WaylandSeat::setInstallCursor(bool install) | | |||
275 | { | | |||
276 | // TODO: remove, add? | | |||
277 | m_installCursor = install; | | |||
278 | } | | |||
279 | | ||||
280 | void WaylandSeat::setupPointerGestures() | 388 | void WaylandSeat::setupPointerGestures() | ||
281 | { | 389 | { | ||
282 | if (!m_pointer || !m_gesturesInterface) { | 390 | if (!m_pointer || !m_gesturesInterface) { | ||
283 | return; | 391 | return; | ||
284 | } | 392 | } | ||
285 | if (m_pinchGesture || m_swipeGesture) { | 393 | if (m_pinchGesture || m_swipeGesture) { | ||
286 | return; | 394 | return; | ||
287 | } | 395 | } | ||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Line(s) | |||||
333 | } | 441 | } | ||
334 | 442 | | |||
335 | WaylandBackend::WaylandBackend(QObject *parent) | 443 | WaylandBackend::WaylandBackend(QObject *parent) | ||
336 | : Platform(parent) | 444 | : Platform(parent) | ||
337 | , m_display(nullptr) | 445 | , m_display(nullptr) | ||
338 | , m_eventQueue(new EventQueue(this)) | 446 | , m_eventQueue(new EventQueue(this)) | ||
339 | , m_registry(new Registry(this)) | 447 | , m_registry(new Registry(this)) | ||
340 | , m_compositor(new KWayland::Client::Compositor(this)) | 448 | , m_compositor(new KWayland::Client::Compositor(this)) | ||
449 | , m_subCompositor(new KWayland::Client::SubCompositor(this)) | ||||
davidedmundson: where do we use this? | |||||
romangg: If the cursor is locked, it will be painted to a sub-surface. | |||||
341 | , m_shell(new Shell(this)) | 450 | , m_shell(new Shell(this)) | ||
342 | , m_surface(nullptr) | | |||
343 | , m_shellSurface(NULL) | | |||
344 | , m_seat() | | |||
345 | , m_shm(new ShmPool(this)) | 451 | , m_shm(new ShmPool(this)) | ||
346 | , m_connectionThreadObject(new ConnectionThread(nullptr)) | 452 | , m_connectionThreadObject(new ConnectionThread(nullptr)) | ||
347 | , m_connectionThread(nullptr) | 453 | , m_connectionThread(nullptr) | ||
348 | { | 454 | { | ||
349 | connect(this, &WaylandBackend::connectionFailed, this, &WaylandBackend::initFailed); | 455 | connect(this, &WaylandBackend::connectionFailed, this, &WaylandBackend::initFailed); | ||
350 | connect(this, &WaylandBackend::shellSurfaceSizeChanged, this, &WaylandBackend::screenSizeChanged); | | |||
351 | } | 456 | } | ||
352 | 457 | | |||
353 | WaylandBackend::~WaylandBackend() | 458 | WaylandBackend::~WaylandBackend() | ||
354 | { | 459 | { | ||
355 | if (m_pointerConstraints) { | 460 | if (m_pointerConstraints) { | ||
356 | m_pointerConstraints->release(); | 461 | m_pointerConstraints->release(); | ||
357 | } | 462 | } | ||
358 | if (m_xdgShellSurface) { | 463 | delete m_waylandCursor; | ||
359 | m_xdgShellSurface->release(); | 464 | | ||
360 | } | 465 | qDeleteAll(m_outputs); | ||
361 | if (m_shellSurface) { | 466 | | ||
362 | m_shellSurface->release(); | | |||
363 | } | | |||
364 | if (m_surface) { | | |||
365 | m_surface->release(); | | |||
366 | } | | |||
367 | if (m_xdgShell) { | 467 | if (m_xdgShell) { | ||
368 | m_xdgShell->release(); | 468 | m_xdgShell->release(); | ||
369 | } | 469 | } | ||
370 | m_shell->release(); | 470 | m_shell->release(); | ||
471 | m_subCompositor->release(); | ||||
371 | m_compositor->release(); | 472 | m_compositor->release(); | ||
372 | m_registry->release(); | 473 | m_registry->release(); | ||
373 | m_seat.reset(); | 474 | delete m_seat; | ||
374 | m_shm->release(); | 475 | m_shm->release(); | ||
375 | m_eventQueue->release(); | 476 | m_eventQueue->release(); | ||
376 | 477 | | |||
377 | m_connectionThreadObject->deleteLater(); | 478 | m_connectionThreadObject->deleteLater(); | ||
378 | m_connectionThread->quit(); | 479 | m_connectionThread->quit(); | ||
379 | m_connectionThread->wait(); | 480 | m_connectionThread->wait(); | ||
380 | 481 | | |||
381 | qCDebug(KWIN_WAYLAND_BACKEND) << "Destroyed Wayland display"; | 482 | qCDebug(KWIN_WAYLAND_BACKEND) << "Destroyed Wayland display"; | ||
382 | } | 483 | } | ||
383 | 484 | | |||
384 | void WaylandBackend::init() | 485 | void WaylandBackend::init() | ||
385 | { | 486 | { | ||
386 | connect(m_registry, &Registry::compositorAnnounced, this, | 487 | connect(m_registry, &Registry::compositorAnnounced, this, | ||
387 | [this](quint32 name) { | 488 | [this](quint32 name) { | ||
388 | m_compositor->setup(m_registry->bindCompositor(name, 1)); | 489 | m_compositor->setup(m_registry->bindCompositor(name, 1)); | ||
389 | } | 490 | } | ||
390 | ); | 491 | ); | ||
492 | connect(m_registry, &Registry::subCompositorAnnounced, this, | ||||
493 | [this](quint32 name) { | ||||
494 | m_subCompositor->setup(m_registry->bindSubCompositor(name, 1)); | ||||
495 | } | ||||
496 | ); | ||||
391 | connect(m_registry, &Registry::shellAnnounced, this, | 497 | connect(m_registry, &Registry::shellAnnounced, this, | ||
392 | [this](quint32 name) { | 498 | [this](quint32 name) { | ||
393 | m_shell->setup(m_registry->bindShell(name, 1)); | 499 | m_shell->setup(m_registry->bindShell(name, 1)); | ||
394 | } | 500 | } | ||
395 | ); | 501 | ); | ||
396 | connect(m_registry, &Registry::seatAnnounced, this, | 502 | connect(m_registry, &Registry::seatAnnounced, this, | ||
397 | [this](quint32 name) { | 503 | [this](quint32 name) { | ||
398 | if (Application::usesLibinput()) { | 504 | if (Application::usesLibinput()) { | ||
399 | return; | 505 | return; | ||
400 | } | 506 | } | ||
401 | m_seat.reset(new WaylandSeat(m_registry->bindSeat(name, 2), this)); | 507 | m_seat = new WaylandSeat(m_registry->bindSeat(name, 2), this); | ||
402 | } | 508 | } | ||
403 | ); | 509 | ); | ||
404 | connect(m_registry, &Registry::shmAnnounced, this, | 510 | connect(m_registry, &Registry::shmAnnounced, this, | ||
405 | [this](quint32 name) { | 511 | [this](quint32 name) { | ||
406 | m_shm->setup(m_registry->bindShm(name, 1)); | 512 | m_shm->setup(m_registry->bindShm(name, 1)); | ||
407 | } | 513 | } | ||
408 | ); | 514 | ); | ||
515 | connect(m_registry, &Registry::relativePointerManagerUnstableV1Announced, this, | ||||
516 | [this](quint32 name, quint32 version) { | ||||
517 | if (m_relativePointerManager) { | ||||
518 | return; | ||||
519 | } | ||||
520 | m_relativePointerManager = m_registry->createRelativePointerManager(name, version, this); | ||||
521 | if (m_pointerConstraints) { | ||||
522 | emit pointerLockSupportedChanged(); | ||||
523 | } | ||||
524 | } | ||||
525 | ); | ||||
409 | connect(m_registry, &Registry::pointerConstraintsUnstableV1Announced, this, | 526 | connect(m_registry, &Registry::pointerConstraintsUnstableV1Announced, this, | ||
410 | [this](quint32 name, quint32 version) { | 527 | [this](quint32 name, quint32 version) { | ||
411 | if (m_pointerConstraints) { | 528 | if (m_pointerConstraints) { | ||
412 | return; | 529 | return; | ||
413 | } | 530 | } | ||
414 | m_pointerConstraints = m_registry->createPointerConstraints(name, version, this); | 531 | m_pointerConstraints = m_registry->createPointerConstraints(name, version, this); | ||
415 | updateWindowTitle(); | 532 | if (m_relativePointerManager) { | ||
533 | emit pointerLockSupportedChanged(); | ||||
534 | } | ||||
416 | } | 535 | } | ||
417 | ); | 536 | ); | ||
418 | connect(m_registry, &Registry::interfacesAnnounced, this, &WaylandBackend::createSurface); | 537 | connect(m_registry, &Registry::interfacesAnnounced, this, &WaylandBackend::createOutputs); | ||
419 | connect(m_registry, &Registry::interfacesAnnounced, this, | 538 | connect(m_registry, &Registry::interfacesAnnounced, this, | ||
420 | [this] { | 539 | [this] { | ||
421 | if (!m_seat) { | 540 | if (!m_seat) { | ||
422 | return; | 541 | return; | ||
423 | } | 542 | } | ||
424 | const auto gi = m_registry->interface(Registry::Interface::PointerGesturesUnstableV1); | 543 | const auto gi = m_registry->interface(Registry::Interface::PointerGesturesUnstableV1); | ||
425 | if (gi.name == 0) { | 544 | if (gi.name == 0) { | ||
426 | return; | 545 | return; | ||
427 | } | 546 | } | ||
428 | auto gesturesInterface = m_registry->createPointerGestures(gi.name, gi.version, m_seat.data()); | 547 | auto gesturesInterface = m_registry->createPointerGestures(gi.name, gi.version, m_seat); | ||
429 | m_seat->installGesturesInterface(gesturesInterface); | 548 | m_seat->installGesturesInterface(gesturesInterface); | ||
549 | | ||||
550 | m_waylandCursor = new WaylandCursor(this); | ||||
430 | } | 551 | } | ||
431 | ); | 552 | ); | ||
432 | if (!deviceIdentifier().isEmpty()) { | 553 | if (!deviceIdentifier().isEmpty()) { | ||
433 | m_connectionThreadObject->setSocketName(deviceIdentifier()); | 554 | m_connectionThreadObject->setSocketName(deviceIdentifier()); | ||
434 | } | 555 | } | ||
435 | connect(this, &WaylandBackend::cursorChanged, this, | 556 | connect(this, &WaylandBackend::cursorChanged, this, | ||
436 | [this] { | 557 | [this] { | ||
437 | if (m_seat.isNull() || !m_seat->isInstallCursor()) { | 558 | if (!m_seat) { | ||
438 | return; | 559 | return; | ||
439 | } | 560 | } | ||
440 | m_seat->installCursorImage(softwareCursor(), softwareCursorHotspot()); | 561 | m_waylandCursor->installImage(); | ||
441 | markCursorAsRendered(); | 562 | markCursorAsRendered(); | ||
442 | } | 563 | } | ||
443 | ); | 564 | ); | ||
565 | connect(this, &WaylandBackend::pointerLockChanged, this, [this](bool locked) { | ||||
566 | delete m_waylandCursor; | ||||
567 | if (locked) { | ||||
568 | Q_ASSERT(!m_relativePointer); | ||||
569 | m_waylandCursor = new WaylandSubSurfaceCursor(this); | ||||
570 | m_waylandCursor->move(input()->pointer()->pos()); | ||||
571 | m_relativePointer = m_relativePointerManager->createRelativePointer(m_seat->pointer(), this); | ||||
572 | if (!m_relativePointer->isValid()) { | ||||
573 | return; | ||||
574 | } | ||||
575 | connect(m_relativePointer, &RelativePointer::relativeMotion, | ||||
576 | this, &WaylandBackend::relativeMotionHandler); | ||||
577 | } else { | ||||
578 | delete m_relativePointer; | ||||
579 | m_relativePointer = nullptr; | ||||
580 | m_waylandCursor = new WaylandCursor(this); | ||||
581 | } | ||||
582 | m_waylandCursor->init(); | ||||
583 | }); | ||||
444 | initConnection(); | 584 | initConnection(); | ||
445 | } | 585 | } | ||
446 | 586 | | |||
587 | void WaylandBackend::relativeMotionHandler(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 timestamp) | ||||
588 | { | ||||
589 | Q_UNUSED(deltaNonAccelerated) | ||||
590 | Q_ASSERT(m_waylandCursor); | ||||
591 | | ||||
592 | const auto oldGlobalPos = input()->pointer()->pos(); | ||||
593 | const QPointF newPos = oldGlobalPos + QPointF(delta.width(), delta.height()); | ||||
594 | m_waylandCursor->move(newPos); | ||||
595 | Platform::pointerMotion(newPos, timestamp); | ||||
596 | } | ||||
597 | | ||||
447 | void WaylandBackend::initConnection() | 598 | void WaylandBackend::initConnection() | ||
448 | { | 599 | { | ||
449 | connect(m_connectionThreadObject, &ConnectionThread::connected, this, | 600 | connect(m_connectionThreadObject, &ConnectionThread::connected, this, | ||
450 | [this]() { | 601 | [this]() { | ||
451 | // create the event queue for the main gui thread | 602 | // create the event queue for the main gui thread | ||
452 | m_display = m_connectionThreadObject->display(); | 603 | m_display = m_connectionThreadObject->display(); | ||
453 | m_eventQueue->setup(m_connectionThreadObject); | 604 | m_eventQueue->setup(m_connectionThreadObject); | ||
454 | m_registry->setEventQueue(m_eventQueue); | 605 | m_registry->setEventQueue(m_eventQueue); | ||
455 | // setup registry | 606 | // setup registry | ||
456 | m_registry->create(m_display); | 607 | m_registry->create(m_display); | ||
457 | m_registry->setup(); | 608 | m_registry->setup(); | ||
458 | }, | 609 | }, | ||
459 | Qt::QueuedConnection); | 610 | Qt::QueuedConnection); | ||
460 | connect(m_connectionThreadObject, &ConnectionThread::connectionDied, this, | 611 | connect(m_connectionThreadObject, &ConnectionThread::connectionDied, this, | ||
461 | [this]() { | 612 | [this]() { | ||
462 | setReady(false); | 613 | setReady(false); | ||
463 | emit systemCompositorDied(); | 614 | emit systemCompositorDied(); | ||
464 | m_seat.reset(); | 615 | delete m_seat; | ||
465 | m_shm->destroy(); | 616 | m_shm->destroy(); | ||
466 | if (m_xdgShellSurface) { | 617 | | ||
467 | m_xdgShellSurface->destroy(); | 618 | qDeleteAll(m_outputs); | ||
468 | delete m_xdgShellSurface; | 619 | m_outputs.clear(); | ||
469 | m_xdgShellSurface = nullptr; | 620 | | ||
470 | } | | |||
471 | if (m_shellSurface) { | | |||
472 | m_shellSurface->destroy(); | | |||
473 | delete m_shellSurface; | | |||
474 | m_shellSurface = nullptr; | | |||
475 | } | | |||
476 | if (m_surface) { | | |||
477 | m_surface->destroy(); | | |||
478 | delete m_surface; | | |||
479 | m_surface = nullptr; | | |||
480 | } | | |||
481 | if (m_shell) { | 621 | if (m_shell) { | ||
482 | m_shell->destroy(); | 622 | m_shell->destroy(); | ||
483 | } | 623 | } | ||
484 | if (m_xdgShell) { | 624 | if (m_xdgShell) { | ||
485 | m_xdgShell->destroy(); | 625 | m_xdgShell->destroy(); | ||
486 | } | 626 | } | ||
627 | m_subCompositor->destroy(); | ||||
487 | m_compositor->destroy(); | 628 | m_compositor->destroy(); | ||
488 | m_registry->destroy(); | 629 | m_registry->destroy(); | ||
489 | m_eventQueue->destroy(); | 630 | m_eventQueue->destroy(); | ||
490 | if (m_display) { | 631 | if (m_display) { | ||
491 | m_display = nullptr; | 632 | m_display = nullptr; | ||
492 | } | 633 | } | ||
493 | }, | 634 | }, | ||
494 | Qt::QueuedConnection); | 635 | Qt::QueuedConnection); | ||
495 | connect(m_connectionThreadObject, &ConnectionThread::failed, this, &WaylandBackend::connectionFailed, Qt::QueuedConnection); | 636 | connect(m_connectionThreadObject, &ConnectionThread::failed, this, &WaylandBackend::connectionFailed, Qt::QueuedConnection); | ||
496 | 637 | | |||
497 | m_connectionThread = new QThread(this); | 638 | m_connectionThread = new QThread(this); | ||
498 | m_connectionThreadObject->moveToThread(m_connectionThread); | 639 | m_connectionThreadObject->moveToThread(m_connectionThread); | ||
499 | m_connectionThread->start(); | 640 | m_connectionThread->start(); | ||
500 | 641 | | |||
501 | m_connectionThreadObject->initConnection(); | 642 | m_connectionThreadObject->initConnection(); | ||
502 | } | 643 | } | ||
503 | 644 | | |||
504 | void WaylandBackend::createSurface() | 645 | void WaylandBackend::updateScreenSize(WaylandOutput *output) | ||
646 | { | ||||
647 | auto it = std::find(m_outputs.begin(), m_outputs.end(), output); | ||||
648 | | ||||
649 | int nextLogicalPosition = output->geometry().topRight().x(); | ||||
650 | while (++it != m_outputs.end()) { | ||||
651 | const QRect geo = (*it)->geometry(); | ||||
652 | (*it)->setGeometry(QPoint(nextLogicalPosition, 0), geo.size()); | ||||
653 | nextLogicalPosition = geo.topRight().x(); | ||||
654 | } | ||||
655 | } | ||||
656 | | ||||
657 | void WaylandBackend::createOutputs() | ||||
505 | { | 658 | { | ||
506 | m_surface = m_compositor->createSurface(this); | 659 | using namespace KWayland::Client; | ||
507 | if (!m_surface || !m_surface->isValid()) { | 660 | | ||
661 | const auto ssdManagerIface = m_registry->interface(Registry::Interface::ServerSideDecorationManager); | ||||
662 | ServerSideDecorationManager *ssdManager = ssdManagerIface.name == 0 ? nullptr : | ||||
663 | m_registry->createServerSideDecorationManager(ssdManagerIface.name, ssdManagerIface.version, this); | ||||
664 | | ||||
665 | | ||||
666 | const auto xdgIface = m_registry->interface(Registry::Interface::XdgShellUnstableV6); | ||||
667 | if (xdgIface.name != 0) { | ||||
668 | m_xdgShell = m_registry->createXdgShell(xdgIface.name, xdgIface.version, this); | ||||
669 | } | ||||
670 | | ||||
671 | // we need to multiply the initial window size with the scale in order to | ||||
672 | // create an output window of this size in the end | ||||
673 | const int pixelWidth = initialWindowSize().width() * initialOutputScale() + 0.5; | ||||
674 | const int pixelHeight = initialWindowSize().height() * initialOutputScale() + 0.5; | ||||
675 | const int logicalWidth = initialWindowSize().width(); | ||||
676 | | ||||
677 | int logicalWidthSum = 0; | ||||
678 | for (int i = 0; i < initialOutputCount(); i++) { | ||||
679 | auto surface = m_compositor->createSurface(this); | ||||
680 | if (!surface || !surface->isValid()) { | ||||
508 | qCCritical(KWIN_WAYLAND_BACKEND) << "Creating Wayland Surface failed"; | 681 | qCCritical(KWIN_WAYLAND_BACKEND) << "Creating Wayland Surface failed"; | ||
509 | return; | 682 | return; | ||
510 | } | 683 | } | ||
511 | using namespace KWayland::Client; | 684 | | ||
512 | auto iface = m_registry->interface(Registry::Interface::ServerSideDecorationManager); | 685 | if (ssdManager) { | ||
513 | if (iface.name != 0) { | 686 | auto decoration = ssdManager->create(surface, this); | ||
514 | auto manager = m_registry->createServerSideDecorationManager(iface.name, iface.version, this); | | |||
515 | auto decoration = manager->create(m_surface, this); | | |||
516 | connect(decoration, &ServerSideDecoration::modeChanged, this, | 687 | connect(decoration, &ServerSideDecoration::modeChanged, this, | ||
517 | [this, decoration] { | 688 | [this, decoration] { | ||
518 | if (decoration->mode() != ServerSideDecoration::Mode::Server) { | 689 | if (decoration->mode() != ServerSideDecoration::Mode::Server) { | ||
519 | decoration->requestMode(ServerSideDecoration::Mode::Server); | 690 | decoration->requestMode(ServerSideDecoration::Mode::Server); | ||
520 | } | 691 | } | ||
521 | } | 692 | } | ||
522 | ); | 693 | ); | ||
523 | } | 694 | } | ||
524 | if (m_seat) { | 695 | | ||
525 | m_seat->setInstallCursor(true); | 696 | WaylandOutput *waylandOutput = nullptr; | ||
526 | } | 697 | | ||
527 | // check for xdg shell | | |||
528 | auto xdgIface = m_registry->interface(Registry::Interface::XdgShellUnstableV6); | | |||
529 | if (xdgIface.name != 0) { | | |||
530 | m_xdgShell = m_registry->createXdgShell(xdgIface.name, xdgIface.version, this); | | |||
531 | if (m_xdgShell && m_xdgShell->isValid()) { | 698 | if (m_xdgShell && m_xdgShell->isValid()) { | ||
532 | m_xdgShellSurface = m_xdgShell->createSurface(m_surface, this); | 699 | waylandOutput = new XdgShellOutput(surface, m_xdgShell, this, i+1); | ||
533 | connect(m_xdgShellSurface, &XdgShellSurface::closeRequested, qApp, &QCoreApplication::quit); | 700 | } else if (m_shell->isValid()) { | ||
534 | setupSurface(m_xdgShellSurface); | 701 | waylandOutput = new ShellOutput(surface, m_shell, this); | ||
535 | return; | | |||
536 | } | | |||
537 | } | | |||
538 | if (m_shell->isValid()) { | | |||
539 | m_shellSurface = m_shell->createSurface(m_surface, this); | | |||
540 | setupSurface(m_shellSurface); | | |||
541 | m_shellSurface->setToplevel(); | | |||
542 | } | | |||
543 | } | 702 | } | ||
544 | 703 | | |||
545 | template <class T> | 704 | if (!waylandOutput) { | ||
546 | void WaylandBackend::setupSurface(T *surface) | 705 | qCCritical(KWIN_WAYLAND_BACKEND) << "Binding to all shell interfaces failed for output" << i; | ||
547 | { | 706 | return; | ||
548 | connect(surface, &T::sizeChanged, this, &WaylandBackend::shellSurfaceSizeChanged); | | |||
549 | surface->setSize(initialWindowSize()); | | |||
550 | updateWindowTitle(); | | |||
551 | setReady(true); | | |||
552 | emit screensQueried(); | | |||
553 | } | 707 | } | ||
554 | 708 | | |||
555 | QSize WaylandBackend::shellSurfaceSize() const | 709 | waylandOutput->setScale(initialOutputScale()); | ||
556 | { | 710 | waylandOutput->setGeometry(QPoint(logicalWidthSum, 0), QSize(pixelWidth, pixelHeight)); | ||
557 | if (m_shellSurface) { | 711 | | ||
558 | return m_shellSurface->size(); | 712 | connect(waylandOutput, &WaylandOutput::sizeChanged, this, [this, waylandOutput](const QSize &size) { | ||
559 | } | 713 | Q_UNUSED(size) | ||
560 | if (m_xdgShellSurface) { | 714 | updateScreenSize(waylandOutput); | ||
561 | return m_xdgShellSurface->size(); | 715 | Compositor::self()->addRepaintFull(); | ||
716 | }); | ||||
717 | connect(waylandOutput, &WaylandOutput::frameRendered, this, &WaylandBackend::checkBufferSwap); | ||||
718 | | ||||
719 | logicalWidthSum += logicalWidth; | ||||
720 | m_outputs << waylandOutput; | ||||
562 | } | 721 | } | ||
563 | return QSize(); | 722 | setReady(true); | ||
723 | emit screensQueried(); | ||||
564 | } | 724 | } | ||
565 | 725 | | |||
566 | Screens *WaylandBackend::createScreens(QObject *parent) | 726 | Screens *WaylandBackend::createScreens(QObject *parent) | ||
567 | { | 727 | { | ||
568 | return new BasicScreens(this, parent); | 728 | return new OutputScreens(this, parent); | ||
569 | } | 729 | } | ||
570 | 730 | | |||
571 | OpenGLBackend *WaylandBackend::createOpenGLBackend() | 731 | OpenGLBackend *WaylandBackend::createOpenGLBackend() | ||
572 | { | 732 | { | ||
573 | #if HAVE_WAYLAND_EGL | 733 | #if HAVE_WAYLAND_EGL | ||
574 | return new EglWaylandBackend(this); | 734 | return new EglWaylandBackend(this); | ||
575 | #else | 735 | #else | ||
576 | return nullptr; | 736 | return nullptr; | ||
577 | #endif | 737 | #endif | ||
578 | } | 738 | } | ||
579 | 739 | | |||
580 | QPainterBackend *WaylandBackend::createQPainterBackend() | 740 | QPainterBackend *WaylandBackend::createQPainterBackend() | ||
581 | { | 741 | { | ||
582 | return new WaylandQPainterBackend(this); | 742 | return new WaylandQPainterBackend(this); | ||
583 | } | 743 | } | ||
584 | 744 | | |||
745 | void WaylandBackend::checkBufferSwap() | ||||
746 | { | ||||
747 | const bool allRendered = std::all_of(m_outputs.begin(), m_outputs.end(), [](WaylandOutput *o) { | ||||
748 | return o->rendered(); | ||||
749 | }); | ||||
750 | if (!allRendered) { | ||||
751 | // need to wait more | ||||
752 | // TODO: what if one does not need to be rendered (no damage)? | ||||
753 | return; | ||||
754 | } | ||||
755 | | ||||
756 | for (auto *output : m_outputs) { | ||||
757 | if (!output->rendered()) { | ||||
758 | return; | ||||
759 | } | ||||
760 | } | ||||
761 | Compositor::self()->bufferSwapComplete(); | ||||
762 | | ||||
763 | for (auto *output : m_outputs) { | ||||
764 | output->resetRendered(); | ||||
765 | } | ||||
766 | } | ||||
767 | | ||||
585 | void WaylandBackend::flush() | 768 | void WaylandBackend::flush() | ||
586 | { | 769 | { | ||
587 | if (m_connectionThreadObject) { | 770 | if (m_connectionThreadObject) { | ||
588 | m_connectionThreadObject->flush(); | 771 | m_connectionThreadObject->flush(); | ||
589 | } | 772 | } | ||
590 | } | 773 | } | ||
591 | 774 | | |||
592 | void WaylandBackend::togglePointerConfinement() | 775 | WaylandOutput* WaylandBackend::getOutputAt(const QPointF globalPosition) | ||
776 | { | ||||
777 | const auto pos = globalPosition.toPoint(); | ||||
778 | auto checkPosition = [pos](WaylandOutput *output) { | ||||
779 | return output->geometry().contains(pos); | ||||
780 | }; | ||||
781 | auto it = std::find_if(m_outputs.begin(), m_outputs.end(), checkPosition); | ||||
782 | return it == m_outputs.end() ? nullptr : *it; | ||||
783 | } | ||||
784 | | ||||
785 | bool WaylandBackend::supportsPointerLock() | ||||
786 | { | ||||
787 | return m_pointerConstraints && m_relativePointerManager; | ||||
788 | } | ||||
789 | | ||||
790 | void WaylandBackend::togglePointerLock() | ||||
593 | { | 791 | { | ||
594 | if (!m_pointerConstraints) { | 792 | if (!m_pointerConstraints) { | ||
595 | return; | 793 | return; | ||
596 | } | 794 | } | ||
597 | if (!m_seat) { | 795 | if (!m_relativePointerManager) { | ||
598 | return; | 796 | return; | ||
599 | } | 797 | } | ||
600 | auto p = m_seat->pointer(); | 798 | if (!m_seat) { | ||
601 | if (!p) { | | |||
602 | return; | 799 | return; | ||
603 | } | 800 | } | ||
604 | if (!m_surface) { | 801 | auto pointer = m_seat->pointer(); | ||
802 | if (!pointer) { | ||||
605 | return; | 803 | return; | ||
606 | } | 804 | } | ||
607 | if (m_pointerConfinement && m_isPointerConfined) { | 805 | if (m_outputs.isEmpty()) { | ||
608 | delete m_pointerConfinement; | | |||
609 | m_pointerConfinement = nullptr; | | |||
610 | m_isPointerConfined = false; | | |||
611 | updateWindowTitle(); | | |||
612 | flush(); | | |||
613 | return; | | |||
614 | } else if (m_pointerConfinement) { | | |||
615 | return; | 806 | return; | ||
616 | } | 807 | } | ||
617 | m_pointerConfinement = m_pointerConstraints->confinePointer(m_surface, p, nullptr, PointerConstraints::LifeTime::Persistent, this); | 808 | | ||
618 | connect(m_pointerConfinement, &ConfinedPointer::confined, this, | 809 | for (auto output : m_outputs) { | ||
619 | [this] { | 810 | output->lockPointer(m_seat->pointer(), !m_pointerLockRequested); | ||
620 | m_isPointerConfined = true; | | |||
621 | updateWindowTitle(); | | |||
622 | } | | |||
623 | ); | | |||
624 | connect(m_pointerConfinement, &ConfinedPointer::unconfined, this, | | |||
625 | [this] { | | |||
626 | m_isPointerConfined = false; | | |||
627 | updateWindowTitle(); | | |||
628 | } | 811 | } | ||
629 | ); | 812 | m_pointerLockRequested = !m_pointerLockRequested; | ||
630 | updateWindowTitle(); | | |||
631 | flush(); | 813 | flush(); | ||
632 | } | 814 | } | ||
633 | 815 | | |||
634 | void WaylandBackend::updateWindowTitle() | 816 | bool WaylandBackend::pointerIsLocked() | ||
635 | { | 817 | { | ||
636 | if (!m_xdgShellSurface) { | 818 | for (auto *output : m_outputs) { | ||
637 | return; | 819 | if (output->pointerIsLocked()) { | ||
638 | } | 820 | return true; | ||
639 | QString grab; | | |||
640 | if (m_isPointerConfined) { | | |||
641 | grab = i18n("Press right control to ungrab pointer"); | | |||
642 | } else { | | |||
643 | if (!m_pointerConfinement && m_pointerConstraints) { | | |||
644 | grab = i18n("Press right control key to grab pointer"); | | |||
645 | } | 821 | } | ||
646 | } | 822 | } | ||
647 | const QString title = i18nc("Title of nested KWin Wayland with Wayland socket identifier as argument", | 823 | return false; | ||
648 | "KDE Wayland Compositor (%1)", waylandServer()->display()->socketName()); | | |||
649 | if (grab.isEmpty()) { | | |||
650 | m_xdgShellSurface->setTitle(title); | | |||
651 | } else { | | |||
652 | m_xdgShellSurface->setTitle(title + QStringLiteral(" - ") + grab); | | |||
653 | } | | |||
654 | } | 824 | } | ||
655 | 825 | | |||
656 | QVector<CompositingType> WaylandBackend::supportedCompositors() const | 826 | QVector<CompositingType> WaylandBackend::supportedCompositors() const | ||
657 | { | 827 | { | ||
658 | if (selectedCompositor() != NoCompositing) { | 828 | if (selectedCompositor() != NoCompositing) { | ||
659 | return {selectedCompositor()}; | 829 | return {selectedCompositor()}; | ||
660 | } | 830 | } | ||
661 | #if HAVE_WAYLAND_EGL | 831 | #if HAVE_WAYLAND_EGL | ||
662 | return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing}; | 832 | return QVector<CompositingType>{OpenGLCompositing, QPainterCompositing}; | ||
663 | #else | 833 | #else | ||
664 | return QVector<CompositingType>{QPainterCompositing}; | 834 | return QVector<CompositingType>{QPainterCompositing}; | ||
665 | #endif | 835 | #endif | ||
666 | } | 836 | } | ||
667 | 837 | | |||
838 | Outputs WaylandBackend::outputs() const | ||||
839 | { | ||||
840 | return m_outputs; | ||||
841 | } | ||||
842 | | ||||
843 | Outputs WaylandBackend::enabledOutputs() const | ||||
844 | { | ||||
845 | // all outputs are enabled | ||||
846 | return m_outputs; | ||||
847 | } | ||||
668 | 848 | | |||
669 | } | 849 | } | ||
670 | 850 | | |||
671 | } // KWin | 851 | } // KWin |
where do we use this?