KWin
Loading...
Searching...
No Matches
wayland_display.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "wayland_display.h"
8#include "utils/memorymap.h"
9#include "wayland_logging.h"
10
11#include <KWayland/Client/compositor.h>
12#include <KWayland/Client/pointerconstraints.h>
13#include <KWayland/Client/pointergestures.h>
14#include <KWayland/Client/registry.h>
15#include <KWayland/Client/relativepointer.h>
16#include <KWayland/Client/seat.h>
17#include <KWayland/Client/subcompositor.h>
18#include <KWayland/Client/xdgdecoration.h>
19#include <KWayland/Client/xdgshell.h>
20
21#include <QMutex>
22#include <QThread>
23#include <QWaitCondition>
24
25#include <drm_fourcc.h>
26#include <fcntl.h>
27#include <poll.h>
28#include <span>
29#include <unistd.h>
30#include <wayland-client.h>
31#include <xf86drm.h>
32
33// Generated in src/wayland.
34#include "wayland-linux-dmabuf-unstable-v1-client-protocol.h"
35#include "wayland-pointer-constraints-unstable-v1-client-protocol.h"
36#include "wayland-pointer-gestures-unstable-v1-server-protocol.h"
37#include "wayland-relative-pointer-unstable-v1-client-protocol.h"
38#include "wayland-xdg-decoration-unstable-v1-client-protocol.h"
39#include "wayland-xdg-shell-client-protocol.h"
40
41namespace KWin
42{
43namespace Wayland
44{
45
46class WaylandEventThread : public QThread
47{
48 Q_OBJECT
49
50public:
51 WaylandEventThread(wl_display *display)
52 : m_display(display)
53 , m_fd(wl_display_get_fd(display))
54 , m_quitPipe{-1, -1}
55 , m_reading(true)
56 , m_quitting(false)
57 {
58 if (pipe2(m_quitPipe, O_CLOEXEC) == -1) {
59 qCWarning(KWIN_WAYLAND_BACKEND) << "Failed to create quite pipe in WaylandEventThread";
60 }
61 }
62
64 {
65 if (m_quitPipe[0] != -1) {
66 close(m_quitPipe[0]);
67 close(m_quitPipe[1]);
68 }
69 }
70
71 void dispatch()
72 {
73 while (true) {
74 if (wl_display_dispatch_pending(m_display) < 0) {
75 qFatal("Wayland connection broke");
76 }
77
78 wl_display_flush(m_display);
79
80 if (m_reading.loadAcquire()) {
81 break;
82 }
83
84 if (wl_display_prepare_read(m_display) == 0) {
85 QMutexLocker lock(&m_mutex);
86 m_reading.storeRelease(true);
87 m_cond.wakeOne();
88 break;
89 }
90 }
91 }
92
93 void stop()
94 {
95 if (m_quitPipe[1] != -1) {
96 write(m_quitPipe[1], "\0", 1);
97 }
98
99 m_mutex.lock();
100 m_quitting = true;
101 m_cond.wakeOne();
102 m_mutex.unlock();
103
104 wait();
105 }
106
107Q_SIGNALS:
108 void available();
109
110protected:
111 void run() override
112 {
113 while (true) {
114 m_reading.storeRelease(false);
115
116 Q_EMIT available();
117
118 m_mutex.lock();
119 while (!m_reading.loadRelaxed() && !m_quitting) {
120 m_cond.wait(&m_mutex);
121 }
122 m_mutex.unlock();
123
124 if (m_quitting) {
125 break;
126 }
127
128 pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_quitPipe[0], POLLIN, 0 } };
129 poll(fds, 2, -1);
130
131 if (fds[1].revents & POLLIN) {
132 wl_display_cancel_read(m_display);
133 break;
134 }
135
136 if (fds[0].revents & POLLIN) {
137 wl_display_read_events(m_display);
138 } else {
139 wl_display_cancel_read(m_display);
140 }
141 }
142 }
143
144private:
145 wl_display *const m_display;
146 int m_fd;
147 int m_quitPipe[2];
148 QAtomicInteger<bool> m_reading;
149 QMutex m_mutex;
150 QWaitCondition m_cond;
151 bool m_quitting;
152};
153
154static dev_t deserializeDeviceId(wl_array *data)
155{
156 Q_ASSERT(sizeof(dev_t) == data->size);
157 dev_t ret;
158 std::memcpy(&ret, data->data, data->size);
159 return ret;
160}
161
163{
164public:
165 WaylandLinuxDmabufFeedbackV1(zwp_linux_dmabuf_feedback_v1 *feedback)
167 {
168 static const struct zwp_linux_dmabuf_feedback_v1_listener feedbackListener = {
169 .done = done,
170 .format_table = format_table,
171 .main_device = main_device,
172 .tranche_done = tranche_done,
173 .tranche_target_device = tranche_target_device,
174 .tranche_formats = tranche_formats,
175 .tranche_flags = tranche_flags,
176 };
177 zwp_linux_dmabuf_feedback_v1_add_listener(feedback, &feedbackListener, this);
178 }
179
181 {
182 zwp_linux_dmabuf_feedback_v1_destroy(feedback);
183 }
184
185 zwp_linux_dmabuf_feedback_v1 *feedback;
186 QByteArray mainDevice;
187 dev_t mainDeviceId = 0;
190 QHash<uint32_t, QList<uint64_t>> formats;
191
192private:
193 static void done(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1)
194 {
195 // Nothing to do
196 }
197
198 static void format_table(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, int32_t fd, uint32_t size)
199 {
201
202 feedback->formatTable = MemoryMap(size, PROT_READ, MAP_PRIVATE, fd, 0);
203 close(fd);
204 }
205
206 static void main_device(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, wl_array *deviceId)
207 {
209
210 feedback->mainDeviceId = deserializeDeviceId(deviceId);
211
212 drmDevice *device = nullptr;
213 if (drmGetDeviceFromDevId(feedback->mainDeviceId, 0, &device) != 0) {
214 qCWarning(KWIN_WAYLAND_BACKEND) << "drmGetDeviceFromDevId() failed";
215 return;
216 }
217
218 if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
219 feedback->mainDevice = QByteArray(device->nodes[DRM_NODE_RENDER]);
220 } else if (device->available_nodes & (1 << DRM_NODE_PRIMARY)) {
221 // We can't reliably find the render node from the primary node if the display and
222 // render devices are split, so just fallback to the primary node.
223 feedback->mainDevice = QByteArray(device->nodes[DRM_NODE_PRIMARY]);
224 }
225
226 drmFreeDevice(&device);
227 }
228
229 static void tranche_done(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1)
230 {
232
233 feedback->trancheDeviceId = 0;
234 feedback->formatTable = MemoryMap{};
235 }
236
237 static void tranche_target_device(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, wl_array *deviceId)
238 {
240
241 feedback->trancheDeviceId = deserializeDeviceId(deviceId);
242 }
243
244 static void tranche_formats(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, wl_array *indices)
245 {
247 if (!feedback->formatTable.isValid()) {
248 return;
249 }
250 if (feedback->mainDeviceId != feedback->trancheDeviceId) {
251 return;
252 }
253
254 struct linux_dmabuf_feedback_v1_table_entry
255 {
256 uint32_t format;
257 uint32_t pad; // unused
258 uint64_t modifier;
259 };
260
261 const auto entries = static_cast<linux_dmabuf_feedback_v1_table_entry *>(feedback->formatTable.data());
262 for (const uint16_t &index : std::span(static_cast<uint16_t *>(indices->data), indices->size / sizeof(uint16_t))) {
263 const linux_dmabuf_feedback_v1_table_entry &entry = entries[index];
264 feedback->formats[entry.format].append(entry.modifier);
265 }
266 }
267
268 static void tranche_flags(void *data, zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, uint32_t flags)
269 {
270 // Nothing to do
271 }
272};
273
274WaylandLinuxDmabufV1::WaylandLinuxDmabufV1(wl_registry *registry, uint32_t name, uint32_t version)
275{
276 m_dmabuf = static_cast<zwp_linux_dmabuf_v1 *>(wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version));
277
278 static const struct zwp_linux_dmabuf_v1_listener dmabufListener = {
279 .format = format,
280 .modifier = modifier,
281 };
282 zwp_linux_dmabuf_v1_add_listener(m_dmabuf, &dmabufListener, this);
283
284 m_defaultFeedback = std::make_unique<WaylandLinuxDmabufFeedbackV1>(zwp_linux_dmabuf_v1_get_default_feedback(m_dmabuf));
285}
286
288{
289 zwp_linux_dmabuf_v1_destroy(m_dmabuf);
290}
291
292zwp_linux_dmabuf_v1 *WaylandLinuxDmabufV1::handle() const
293{
294 return m_dmabuf;
295}
296
298{
299 return m_defaultFeedback->mainDevice;
300}
301
302QHash<uint32_t, QList<uint64_t>> WaylandLinuxDmabufV1::formats() const
303{
304 return m_defaultFeedback->formats;
305}
306
307void WaylandLinuxDmabufV1::format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format)
308{
309 // Not sent in v4 and onward.
310}
311
312void WaylandLinuxDmabufV1::modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo)
313{
314 // Not sent in v4 and onward.
315}
316
320
322{
323 m_eventThread->stop();
324 m_eventThread.reset();
325
326 m_compositor.reset();
327 m_pointerConstraints.reset();
328 m_pointerGestures.reset();
329 m_relativePointerManager.reset();
330 m_seat.reset();
331 m_xdgDecorationManager.reset();
332 m_xdgShell.reset();
333 m_linuxDmabuf.reset();
334
335 if (m_shm) {
336 wl_shm_destroy(m_shm);
337 }
338 if (m_registry) {
339 wl_registry_destroy(m_registry);
340 }
341 if (m_display) {
342 wl_display_disconnect(m_display);
343 }
344}
345
347{
348 m_eventThread->dispatch();
349}
350
351bool WaylandDisplay::initialize(const QString &socketName)
352{
353 m_display = wl_display_connect(socketName.toUtf8());
354 if (!m_display) {
355 return false;
356 }
357
358 m_eventThread = std::make_unique<WaylandEventThread>(m_display);
359 connect(m_eventThread.get(), &WaylandEventThread::available, this, &WaylandDisplay::flush, Qt::QueuedConnection);
360 m_eventThread->start();
361
362 static wl_registry_listener registryListener {
363 .global = registry_global,
364 .global_remove = registry_global_remove,
365 };
366 m_registry = wl_display_get_registry(m_display);
367 wl_registry_add_listener(m_registry, &registryListener, this);
368 wl_display_roundtrip(m_display);
369 wl_display_roundtrip(m_display); // get dmabuf formats
370
371 return true;
372}
373
375{
376 return m_display;
377}
378
379KWayland::Client::Compositor *WaylandDisplay::compositor() const
380{
381 return m_compositor.get();
382}
383
384KWayland::Client::PointerConstraints *WaylandDisplay::pointerConstraints() const
385{
386 return m_pointerConstraints.get();
387}
388
389KWayland::Client::PointerGestures *WaylandDisplay::pointerGestures() const
390{
391 return m_pointerGestures.get();
392}
393
394KWayland::Client::RelativePointerManager *WaylandDisplay::relativePointerManager() const
395{
396 return m_relativePointerManager.get();
397}
398
399wl_shm *WaylandDisplay::shm() const
400{
401 return m_shm;
402}
403
404KWayland::Client::Seat *WaylandDisplay::seat() const
405{
406 return m_seat.get();
407}
408
409KWayland::Client::XdgShell *WaylandDisplay::xdgShell() const
410{
411 return m_xdgShell.get();
412}
413
414KWayland::Client::XdgDecorationManager *WaylandDisplay::xdgDecorationManager() const
415{
416 return m_xdgDecorationManager.get();
417}
418
420{
421 return m_linuxDmabuf.get();
422}
423
424void WaylandDisplay::registry_global(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version)
425{
426 WaylandDisplay *display = static_cast<WaylandDisplay *>(data);
427
428 if (strcmp(interface, wl_compositor_interface.name) == 0) {
429 if (version < 4) {
430 qFatal("wl_compositor version 4 or later is required");
431 }
432 display->m_compositor = std::make_unique<KWayland::Client::Compositor>();
433 display->m_compositor->setup(static_cast<wl_compositor *>(wl_registry_bind(registry, name, &wl_compositor_interface, std::min(version, 4u))));
434 } else if (strcmp(interface, wl_shm_interface.name) == 0) {
435 display->m_shm = static_cast<wl_shm *>(wl_registry_bind(registry, name, &wl_shm_interface, std::min(version, 1u)));
436 } else if (strcmp(interface, wl_seat_interface.name) == 0) {
437 display->m_seat = std::make_unique<KWayland::Client::Seat>();
438 display->m_seat->setup(static_cast<wl_seat *>(wl_registry_bind(registry, name, &wl_seat_interface, std::min(version, 5u))));
439 } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
440 display->m_xdgShell = std::make_unique<KWayland::Client::XdgShellStable>();
441 display->m_xdgShell->setup(static_cast<xdg_wm_base *>(wl_registry_bind(registry, name, &xdg_wm_base_interface, std::min(version, 1u))));
442 } else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) {
443 display->m_pointerConstraints = std::make_unique<KWayland::Client::PointerConstraints>();
444 display->m_pointerConstraints->setup(static_cast<zwp_pointer_constraints_v1 *>(wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, std::min(version, 1u))));
445 } else if (strcmp(interface, zwp_pointer_gestures_v1_interface.name) == 0) {
446 display->m_pointerGestures = std::make_unique<KWayland::Client::PointerGestures>();
447 display->m_pointerGestures->setup(static_cast<zwp_pointer_gestures_v1 *>(wl_registry_bind(registry, name, &zwp_pointer_gestures_v1_interface, std::min(version, 1u))));
448 } else if (strcmp(interface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
449 display->m_relativePointerManager = std::make_unique<KWayland::Client::RelativePointerManager>();
450 display->m_relativePointerManager->setup(static_cast<zwp_relative_pointer_manager_v1 *>(wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, std::min(version, 1u))));
451 } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
452 display->m_xdgDecorationManager = std::make_unique<KWayland::Client::XdgDecorationManager>();
453 display->m_xdgDecorationManager->setup(static_cast<zxdg_decoration_manager_v1 *>(wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, std::min(version, 1u))));
454 } else if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) {
455 if (version < 4) {
456 qWarning("zwp_linux_dmabuf_v1 v4 or newer is needed");
457 return;
458 }
459 display->m_linuxDmabuf = std::make_unique<WaylandLinuxDmabufV1>(registry, name, std::min(version, 4u));
460 }
461}
462
463void WaylandDisplay::registry_global_remove(void *data, wl_registry *registry, uint32_t name)
464{
465}
466
467}
468}
469
470#include "wayland_display.moc"
471
472#include "moc_wayland_display.cpp"
wl_display * nativeDisplay() const
KWayland::Client::PointerConstraints * pointerConstraints() const
KWayland::Client::RelativePointerManager * relativePointerManager() const
bool initialize(const QString &socketName)
WaylandLinuxDmabufV1 * linuxDmabuf() const
KWayland::Client::XdgDecorationManager * xdgDecorationManager() const
KWayland::Client::Compositor * compositor() const
KWayland::Client::PointerGestures * pointerGestures() const
KWayland::Client::Seat * seat() const
KWayland::Client::XdgShell * xdgShell() const
WaylandEventThread(wl_display *display)
QHash< uint32_t, QList< uint64_t > > formats
WaylandLinuxDmabufFeedbackV1(zwp_linux_dmabuf_feedback_v1 *feedback)
WaylandLinuxDmabufV1(wl_registry *registry, uint32_t name, uint32_t version)
QHash< uint32_t, QList< uint64_t > > formats() const
zwp_linux_dmabuf_v1 * handle() const
constexpr int version
GLenum format
Definition gltexture.cpp:49