Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylanddisplay.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2024 Jie Liu <liujie01@kylinos.cn>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:significant reason:default
5
7
16#if QT_CONFIG(clipboard)
17#include "qwaylandclipboard_p.h"
18#include "qwaylanddatacontrolv1_p.h"
19#endif
20#if QT_CONFIG(wayland_datadevice)
21#include "qwaylanddatadevicemanager_p.h"
22#include "qwaylanddatadevice_p.h"
23#endif // QT_CONFIG(wayland_datadevice)
24#if QT_CONFIG(wayland_client_primary_selection)
25#include "qwaylandprimaryselectionv1_p.h"
26#endif // QT_CONFIG(wayland_client_primary_selection)
27#if QT_CONFIG(cursor)
28#include <wayland-cursor.h>
29#endif
36
40
43#if QT_CONFIG(tabletevent)
44#include "qwaylandtabletv2_p.h"
45#endif
46
48
49#include <QtWaylandClient/private/qwayland-text-input-unstable-v1.h>
50#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
51#include <QtWaylandClient/private/qwayland-text-input-unstable-v3.h>
52#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
53#include <QtWaylandClient/private/qwayland-qt-text-input-method-unstable-v1.h>
54#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h>
55#include <QtWaylandClient/private/qwayland-viewporter.h>
56#include <QtWaylandClient/private/qwayland-cursor-shape-v1.h>
57#include <QtWaylandClient/private/qwayland-xdg-session-management-v1.h>
58#include <QtWaylandClient/private/qwayland-xdg-system-bell-v1.h>
59#include <QtWaylandClient/private/qwayland-xdg-toplevel-drag-v1.h>
60#include <QtWaylandClient/private/qwayland-wlr-data-control-unstable-v1.h>
61#include <QtWaylandClient/private/qwayland-pointer-warp-v1.h>
62#include <QtWaylandClient/private/qwayland-xx-cutouts-v1.h>
63
64#include <QtCore/private/qcore_unix_p.h>
65
66#include <QtCore/QAbstractEventDispatcher>
67#include <QtGui/qpa/qwindowsysteminterface.h>
68#include <QtGui/private/qguiapplication_p.h>
69
70#include <QtCore/QDebug>
71
72#include <errno.h>
73
74#include <tuple> // for std::tie
75
76QT_BEGIN_NAMESPACE
77
78namespace QtWaylandClient {
79
80class EventThread : public QThread
81{
83public:
85 EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread.
86 SelfDispatch, // Dispatch the events inside this thread.
87 };
88
89 EventThread(struct wl_display * wl,
90 struct wl_event_queue * ev_queue,
91 OperatingMode mode,
92 QObject *parent = nullptr)
93 : QThread(parent)
94 , m_fd(wl_display_get_fd(wl))
95 , m_pipefd{ -1, -1 }
96 , m_wldisplay(wl)
97 , m_wlevqueue(ev_queue)
98 , m_mode(mode)
99 , m_reading(true)
100 , m_quitting(false)
101 {
102 setObjectName(QStringLiteral("WaylandEventThread"));
103
104 // The pipe lets stop() interrupt the poll() in run(), which would otherwise never
105 // return once the compositor stops sending events.
106 if (qt_safe_pipe(m_pipefd) != 0)
107 qErrnoWarning("Pipe creation failed. Quitting may hang.");
108 }
109
111 {
112 // run()'s poll() uses the pipe, so the thread must be stopped by now.
113 Q_ASSERT(!isRunning());
114
115 if (m_pipefd[0] != -1) {
116 qt_safe_close(m_pipefd[0]);
117 qt_safe_close(m_pipefd[1]);
118 }
119 }
120
122 {
123 /*
124 * Dispatch pending events and flush the requests at least once. If the event thread
125 * is not reading, try to call _prepare_read() to allow the event thread to poll().
126 * If that fails, re-try dispatch & flush again until _prepare_read() is successful.
127 *
128 * This allow any call to readAndDispatchEvents() to start event thread's polling,
129 * not only the one issued from event thread's waitForReading(), which means functions
130 * called from dispatch_pending() can safely spin an event loop.
131 */
132 if (m_quitting.loadRelaxed())
133 return;
134
135 for (;;) {
136 if (dispatchQueuePending() < 0) {
137 Q_EMIT waylandError();
138 QMutexLocker lock(&m_mutex);
139 m_quitting.storeRelaxed(true);
140 m_cond.wakeOne();
141 return;
142 }
143
144 wl_display_flush(m_wldisplay);
145
146 // We have to check if event thread is reading every time we dispatch
147 // something, as that may recursively call this function.
148 if (m_reading.loadAcquire())
149 break;
150
151 if (prepareReadQueue() == 0) {
152 QMutexLocker l(&m_mutex);
153 m_reading.storeRelease(true);
154 m_cond.wakeOne();
155 break;
156 }
157 }
158 }
159
160 void stop()
161 {
162 // We have to both write to the pipe and set the flag, as the thread may be
163 // either in the poll() or waiting for _prepare_read().
164 if (m_pipefd[1] != -1 && qt_safe_write(m_pipefd[1], "\0", 1) == -1)
165 qErrnoWarning("Failed to write to the pipe");
166
167 {
168 // The lock makes sure the event thread doesn't start waiting just after it
169 // checked m_quitting, which would make it miss the wakeOne() here.
170 QMutexLocker lock(&m_mutex);
171 m_quitting.storeRelaxed(true);
172 m_cond.wakeOne();
173 }
174
175 wait();
176 }
177
181
182protected:
184 {
185 // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the
186 // outbound ones. Wait until it's done before proceeding, unless we're told to quit.
187 while (waitForReading()) {
188 if (!m_reading.loadRelaxed())
189 break;
190
191 // m_pipefd[0] is what makes this poll() return when stop() is called.
192 pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } };
193 poll(fds, 2, -1);
194
195 if (fds[1].revents & POLLIN) {
196 // we don't really care to read the byte that was written here since we're closing down.
197 // The pending read is cancelled after the loop.
198 break;
199 }
200
201 if (fds[0].revents & POLLIN)
202 wl_display_read_events(m_wldisplay);
203 // The poll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop
204 // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which
205 // case we don't care anymore about them.
206 else
207 wl_display_cancel_read(m_wldisplay);
208 }
209
210 // If a read was prepared (by us or by the main thread on our behalf) and we are
211 // exiting without consuming it, cancel it so that other readers are not blocked.
212 if (m_reading.loadAcquire())
213 wl_display_cancel_read(m_wldisplay);
214 }
215
216private:
217 bool waitForReading()
218 {
219 Q_ASSERT(isCurrentThread());
220
221 m_reading.storeRelease(false);
222
223 if (m_mode == SelfDispatch) {
225 } else {
226 Q_EMIT needReadAndDispatch();
227
228 QMutexLocker lock(&m_mutex);
229 // m_reading might be set from our emit or some other invocation of
230 // readAndDispatchEvents().
231 while (!m_reading.loadRelaxed() && !m_quitting.loadRelaxed())
232 m_cond.wait(&m_mutex);
233 }
234
235 return !m_quitting.loadRelaxed();
236 }
237
238 int dispatchQueuePending()
239 {
240 if (m_wlevqueue)
241 return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue);
242 else
243 return wl_display_dispatch_pending(m_wldisplay);
244 }
245
246 int prepareReadQueue()
247 {
248 if (m_wlevqueue)
249 return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue);
250 else
251 return wl_display_prepare_read(m_wldisplay);
252 }
253
254 int m_fd;
255 int m_pipefd[2]; // set in the constructor, closed in the destructor, constant in between
256 wl_display *m_wldisplay;
257 wl_event_queue *m_wlevqueue;
258 OperatingMode m_mode;
259
260 /* Concurrency note when operating in EmitToDispatch mode:
261 * m_reading is set to false inside event thread's waitForReading(), and is
262 * set to true inside main thread's readAndDispatchEvents().
263 * The lock is not taken when setting m_reading to false, as the main thread
264 * is not actively waiting for it to turn false. However, the lock is taken
265 * inside readAndDispatchEvents() before setting m_reading to true,
266 * as the event thread is actively waiting for it under the wait condition.
267 * m_quitting is set inside stop() and readAndDispatchEvents(), which both take the
268 * lock for the same reason: the event thread waits on it too.
269 */
270
271 QAtomicInteger<bool> m_reading;
272 QAtomicInteger<bool> m_quitting;
273 QMutex m_mutex;
274 QWaitCondition m_cond;
275};
276
277Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging
278
285
286struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
287{
289
290 for (const QRect &rect : qregion)
292
293 return region;
294}
295
297{
298 if (!mGlobals.subCompositor) {
299 qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor.";
300 return nullptr;
301 }
302
303 // Make sure we don't pass NULL surfaces to libwayland (crashes)
306
308}
309
311{
312 if (!mGlobals.viewporter) {
313 qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor.";
314 return nullptr;
315 }
316
319}
320
325
330
335
337 : QObject(parent)
339{
340 qRegisterMetaType<uint32_t>("uint32_t");
341
342 mDisplay = wl_display_connect(nullptr);
343 if (mDisplay) {
345 } else {
346 qErrnoWarning(errno, "Failed to create wl_display");
347 }
348
349 mWaylandTryReconnect = qEnvironmentVariableIsSet("QT_WAYLAND_RECONNECT");
350 mPreferWlrDataControl = qEnvironmentVariableIntValue("QT_WAYLAND_USE_DATA_CONTROL") > 0;
351}
352
354{
356 init(registry);
357
358#if QT_CONFIG(xkbcommon)
360 if (!mXkbContext)
361 qCWarning(lcQpaWayland, "failed to create xkb context");
362#endif
365}
366
368{
369 if (m_eventThread)
371
374
375 if (mSyncCallback)
377
379
380 for (QWaylandScreen *screen : std::exchange(mScreens, {})) {
382 }
384
385#if QT_CONFIG(cursor)
387#endif
388
391
392 // Reset the globals manually since they need to be destroyed before the wl_display
393 mGlobals = {};
394
395 if (object()) {
396 if (mFixes)
398
400 }
401
402 mFixes.reset();
403
404 if (mDisplay)
406}
407
408// Steps which is called just after constructor. This separates registry_global() out of the constructor
409// so that factory functions in integration can be overridden.
411{
412 if (!isInitialized())
413 return false;
414
416
417 emit connected();
418
419 if (!mWaitingScreens.isEmpty()) {
420 // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
422 }
425
426 return qEnvironmentVariableIntValue("QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration();
427}
428
430{
432 return; // There are real screens or we already have a fake one
433
434 qCInfo(lcQpaWayland) << "There are no outputs - creating placeholder screen";
435
439}
440
442{
443 qCWarning(lcQpaWayland) << "Attempting wayland reconnect";
448
451
452 while (!mScreens.isEmpty()) {
453 auto screen = mScreens.takeLast();
454 ensureScreen();
456 }
457
459 mCursor.reset();
460
462
464
466 mLastInputDevice = nullptr;
467
468 for (const RegistryGlobal &global : mRegistryGlobals) {
470 }
472
478
479 const auto windows = QGuiApplication::allWindows();
481 for (auto window : windows) {
482 if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle())) {
485 }
486 }
487
488 // Remove windows that do not need to be recreated and now closed popups
490 for (auto window : std::as_const(allPlatformWindows)) {
493 }
494 window->reset();
495 }
496
497 if (mSyncCallback) {
499 mSyncCallback = nullptr;
500 }
501
502 if (object()) {
503 if (mFixes)
505
507 }
508
509 mFixes.reset();
510
511 mDisplay = wl_display_connect(nullptr);
512 if (!mDisplay)
513 _exit(1);
514
516 connect(
517 this, &QWaylandDisplay::connected, this,
518 [this, &allPlatformWindows] {
519 for (auto &window : std::as_const(allPlatformWindows)) {
521 }
522 forceRoundTrip(); // we need a roundtrip to receive the color space features the compositor supports
523 for (auto &window : std::as_const(allPlatformWindows)) {
525 }
526 },
528
530
534
535 initialize();
536
538 auto waylandWindow = static_cast<QWaylandWindow *>(window);
540 };
542 while (!recreateWindows.isEmpty()) {
544 (*window)->reinit();
546 } else {
547 ++window;
548 }
549 if (window == recreateWindows.end())
551 }
552
554
555 // Failsafe: Make sure we don't have a lingering connection referencing local variables
556 // (This will have been executed and disconnected in initialize())
558}
559
564
565// We have to wait until we have an eventDispatcher before creating the eventThread,
566// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
567// polling.
584
586{
588 if ((ecode == EPIPE || ecode == ECONNRESET)) {
589 qWarning("The Wayland connection broke. Did the Wayland compositor die?");
591 reconnect();
592 return;
593 }
594 } else {
595 qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
596 }
597 _exit(-1);
598}
599
601{
602 QStringList tips, timps; // for text input protocols and text input manager protocols
603 // zwp_text_input_v2 is preferred over zwp_text_input_v3 because:
604 // - Currently, v3 is not as feature rich as v2.
605 // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma
606 // Mobile uses some v2 only.
615
616 QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
617 qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
619 if (!tiProtocols.isEmpty()) {
622 while (it != keys.end()) {
623 if (tips.contains(*it))
625 else
626 qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it;
627 ++it;
628 }
629 }
630 if (mTextInputManagerList.isEmpty()) // fallback
632}
633
635{
636 for (auto screen : std::as_const(mScreens)) {
637 if (screen->output() == output)
638 return screen;
639 }
640 return nullptr;
641}
642
644{
646 return;
649 if (mPlaceholderScreen) {
650 // handleScreenRemoved deletes the platform screen
652 mPlaceholderScreen = nullptr;
654
655 }
656}
657
658template <typename T, auto f>
659struct WithDestructor : public T
660{
661 using T::T;
663 {
664 f(this->object());
665 }
666
667private:
669};
670
672{
673 struct ::wl_registry *registry = object();
674
675 static QStringList interfaceBlacklist = qEnvironmentVariable("QT_WAYLAND_DISABLED_INTERFACES").split(u',');
677 return;
678 }
679
685 registry, id, qMin((int)version, 6)));
686 } else if (interface == QLatin1String(QWaylandShm::interface()->name)) {
687 mGlobals.shm.reset(new QWaylandShm(this, version, id));
691#if QT_CONFIG(wayland_datadevice)
694#endif
698 id, 1));
699#if QT_CONFIG(tabletevent)
705#endif
708#if QT_CONFIG(wayland_client_primary_selection)
715#endif
718 qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
724 inputDevice->setTextInput(nullptr);
725 }
726
732 this,
734 inputDevice->wl_seat())));
739 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1";
746 }
747
752 auto textInput =
756 }
757
762 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
769 }
770
781 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3";
787 }
794
798 bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
801 // make a roundtrip here since we need to receive the events sent by
802 // qt_hardware_integration before creating windows
804 }
807 for (auto *screen : std::as_const(mWaitingScreens))
813 } else if (interface == QLatin1String("wp_viewporter")) {
816 registry, id, qMin(1u, version)));
820 registry, id, std::min(1u, version)));
821 } else if (
831 } else if (
834#if QT_CONFIG(clipboard)
839 }
840#endif
845 registry, id, 1));
846 }
847#ifndef QT_NO_SESSIONMANAGER
851 registry, id, 1));
852 }
853#endif
858 }
859
860
863
864 const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
865 for (Listener l : copy)
867}
868
870{
871 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
873 if (global.id == id) {
875 for (auto *screen : mWaitingScreens) {
876 if (screen->outputId() == id) {
878 delete screen;
879 break;
880 }
881 }
882
884 if (screen->outputId() == id) {
886 // If this is the last screen, we have to add a fake screen, or Qt will break.
887 ensureScreen();
889 break;
890 }
891 }
892 }
896 inputDevice->setTextInput(nullptr);
898 }
902 inputDevice->setTextInput(nullptr);
904 }
908 inputDevice->setTextInput(nullptr);
910 }
916 }
917#if QT_CONFIG(wayland_client_primary_selection)
922 }
923#endif
924#if QT_CONFIG(clipboard)
929 }
930#endif
932 break;
933 }
934 }
935
938}
939
941{
944 return true;
945
946 return false;
947}
948
957
965
970
972{
973 static bool disabled = qgetenv("QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt();
974 // Stop early when disabled via the environment. Do not try to load the integration in
975 // order to play nice with SHM-only, buffer integration-less systems.
976 if (disabled)
977 return false;
978
979 // Don't initialize client buffer integration just to check whether it can have a decoration.
981 return true;
982
983 // We can do software-rendered decorations, only disable them if the integration explicitly says it can't.
985 return integrationSupport;
986}
987
992
997
1004
1009
1022
1039
1054
1067
1073
1078
1080{
1081 // This callback is used to set the window activation because we may get an activate/deactivate
1082 // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the
1083 // handleWindowActivated() calls immediately.
1089}
1090
1092 [](void *data, struct wl_callback *callback, uint32_t time){
1093 Q_UNUSED(time);
1095 QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data);
1096 display->mSyncCallback = nullptr;
1098 }
1099};
1100
1102{
1103 if (mSyncCallback)
1104 return;
1105
1108}
1109
1114
1116{
1117 return std::any_of(
1119 [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
1120}
1121
1125
1126#if QT_CONFIG(cursor)
1127
1129{
1130 if (!mCursor)
1132 return mCursor.get();
1133}
1134
1135auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept
1137{
1138 const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) {
1140 };
1141
1142 const WaylandCursorTheme prototype = {name, pixelSize, nullptr};
1143
1145 if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize)
1146 return {it, true};
1147 else
1148 return {it, false};
1149}
1150
1152{
1154 if (result.found)
1155 return result.theme();
1156
1159
1160 return nullptr;
1161}
1162
1163#endif // QT_CONFIG(cursor)
1164
1165} // namespace QtWaylandClient
1166
1167QT_END_NAMESPACE
1168
1169#include "qwaylanddisplay.moc"
1170#include "moc_qwaylanddisplay_p.cpp"
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland")