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 wl_display_cancel_read(m_wldisplay);
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
211private:
212 bool waitForReading()
213 {
214 Q_ASSERT(isCurrentThread());
215
216 m_reading.storeRelease(false);
217
218 if (m_mode == SelfDispatch) {
220 } else {
221 Q_EMIT needReadAndDispatch();
222
223 QMutexLocker lock(&m_mutex);
224 // m_reading might be set from our emit or some other invocation of
225 // readAndDispatchEvents().
226 while (!m_reading.loadRelaxed() && !m_quitting.loadRelaxed())
227 m_cond.wait(&m_mutex);
228 }
229
230 return !m_quitting.loadRelaxed();
231 }
232
233 int dispatchQueuePending()
234 {
235 if (m_wlevqueue)
236 return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue);
237 else
238 return wl_display_dispatch_pending(m_wldisplay);
239 }
240
241 int prepareReadQueue()
242 {
243 if (m_wlevqueue)
244 return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue);
245 else
246 return wl_display_prepare_read(m_wldisplay);
247 }
248
249 int m_fd;
250 int m_pipefd[2]; // set in the constructor, closed in the destructor, constant in between
251 wl_display *m_wldisplay;
252 wl_event_queue *m_wlevqueue;
253 OperatingMode m_mode;
254
255 /* Concurrency note when operating in EmitToDispatch mode:
256 * m_reading is set to false inside event thread's waitForReading(), and is
257 * set to true inside main thread's readAndDispatchEvents().
258 * The lock is not taken when setting m_reading to false, as the main thread
259 * is not actively waiting for it to turn false. However, the lock is taken
260 * inside readAndDispatchEvents() before setting m_reading to true,
261 * as the event thread is actively waiting for it under the wait condition.
262 * m_quitting is set inside stop() and readAndDispatchEvents(), which both take the
263 * lock for the same reason: the event thread waits on it too.
264 */
265
266 QAtomicInteger<bool> m_reading;
267 QAtomicInteger<bool> m_quitting;
268 QMutex m_mutex;
269 QWaitCondition m_cond;
270};
271
272Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging
273
280
281struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
282{
284
285 for (const QRect &rect : qregion)
287
288 return region;
289}
290
292{
293 if (!mGlobals.subCompositor) {
294 qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor.";
295 return nullptr;
296 }
297
298 // Make sure we don't pass NULL surfaces to libwayland (crashes)
301
303}
304
306{
307 if (!mGlobals.viewporter) {
308 qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor.";
309 return nullptr;
310 }
311
314}
315
320
325
330
332 : QObject(parent)
334{
335 qRegisterMetaType<uint32_t>("uint32_t");
336
337 mDisplay = wl_display_connect(nullptr);
338 if (mDisplay) {
340 } else {
341 qErrnoWarning(errno, "Failed to create wl_display");
342 }
343
344 mWaylandTryReconnect = qEnvironmentVariableIsSet("QT_WAYLAND_RECONNECT");
345 mPreferWlrDataControl = qEnvironmentVariableIntValue("QT_WAYLAND_USE_DATA_CONTROL") > 0;
346}
347
349{
351 init(registry);
352
353#if QT_CONFIG(xkbcommon)
355 if (!mXkbContext)
356 qCWarning(lcQpaWayland, "failed to create xkb context");
357#endif
360}
361
363{
364 if (m_eventThread)
366
369
370 if (mSyncCallback)
372
374
375 for (QWaylandScreen *screen : std::exchange(mScreens, {})) {
377 }
379
380#if QT_CONFIG(cursor)
382#endif
383
386
387 // Reset the globals manually since they need to be destroyed before the wl_display
388 mGlobals = {};
389
390 if (object()) {
391 if (mFixes)
393
395 }
396
397 mFixes.reset();
398
399 if (mDisplay)
401}
402
403// Steps which is called just after constructor. This separates registry_global() out of the constructor
404// so that factory functions in integration can be overridden.
406{
407 if (!isInitialized())
408 return false;
409
411
412 emit connected();
413
414 if (!mWaitingScreens.isEmpty()) {
415 // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
417 }
420
421 return qEnvironmentVariableIntValue("QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration();
422}
423
425{
427 return; // There are real screens or we already have a fake one
428
429 qCInfo(lcQpaWayland) << "There are no outputs - creating placeholder screen";
430
434}
435
437{
438 qCWarning(lcQpaWayland) << "Attempting wayland reconnect";
443
446
447 while (!mScreens.isEmpty()) {
448 auto screen = mScreens.takeLast();
449 ensureScreen();
451 }
452
454 mCursor.reset();
455
457
459
461 mLastInputDevice = nullptr;
462
463 for (const RegistryGlobal &global : mRegistryGlobals) {
465 }
467
473
474 const auto windows = QGuiApplication::allWindows();
476 for (auto window : windows) {
477 if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle())) {
480 }
481 }
482
483 // Remove windows that do not need to be recreated and now closed popups
485 for (auto window : std::as_const(allPlatformWindows)) {
488 }
489 window->reset();
490 }
491
492 if (mSyncCallback) {
494 mSyncCallback = nullptr;
495 }
496
497 if (object()) {
498 if (mFixes)
500
502 }
503
504 mFixes.reset();
505
506 mDisplay = wl_display_connect(nullptr);
507 if (!mDisplay)
508 _exit(1);
509
511 connect(
512 this, &QWaylandDisplay::connected, this,
513 [this, &allPlatformWindows] {
514 for (auto &window : std::as_const(allPlatformWindows)) {
516 }
517 forceRoundTrip(); // we need a roundtrip to receive the color space features the compositor supports
518 for (auto &window : std::as_const(allPlatformWindows)) {
520 }
521 },
523
525
529
530 initialize();
531
533 auto waylandWindow = static_cast<QWaylandWindow *>(window);
535 };
537 while (!recreateWindows.isEmpty()) {
539 (*window)->reinit();
541 } else {
542 ++window;
543 }
544 if (window == recreateWindows.end())
546 }
547
549
550 // Failsafe: Make sure we don't have a lingering connection referencing local variables
551 // (This will have been executed and disconnected in initialize())
553}
554
559
560// We have to wait until we have an eventDispatcher before creating the eventThread,
561// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
562// polling.
579
581{
583 if ((ecode == EPIPE || ecode == ECONNRESET)) {
584 qWarning("The Wayland connection broke. Did the Wayland compositor die?");
586 reconnect();
587 return;
588 }
589 } else {
590 qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
591 }
592 _exit(-1);
593}
594
596{
597 QStringList tips, timps; // for text input protocols and text input manager protocols
598 // zwp_text_input_v2 is preferred over zwp_text_input_v3 because:
599 // - Currently, v3 is not as feature rich as v2.
600 // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma
601 // Mobile uses some v2 only.
610
611 QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
612 qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
614 if (!tiProtocols.isEmpty()) {
617 while (it != keys.end()) {
618 if (tips.contains(*it))
620 else
621 qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it;
622 ++it;
623 }
624 }
625 if (mTextInputManagerList.isEmpty()) // fallback
627}
628
630{
631 for (auto screen : std::as_const(mScreens)) {
632 if (screen->output() == output)
633 return screen;
634 }
635 return nullptr;
636}
637
639{
641 return;
644 if (mPlaceholderScreen) {
645 // handleScreenRemoved deletes the platform screen
647 mPlaceholderScreen = nullptr;
649
650 }
651}
652
653template <typename T, auto f>
654struct WithDestructor : public T
655{
656 using T::T;
658 {
659 f(this->object());
660 }
661
662private:
664};
665
667{
668 struct ::wl_registry *registry = object();
669
670 static QStringList interfaceBlacklist = qEnvironmentVariable("QT_WAYLAND_DISABLED_INTERFACES").split(u',');
672 return;
673 }
674
680 registry, id, qMin((int)version, 6)));
681 } else if (interface == QLatin1String(QWaylandShm::interface()->name)) {
682 mGlobals.shm.reset(new QWaylandShm(this, version, id));
686#if QT_CONFIG(wayland_datadevice)
689#endif
693 id, 1));
694#if QT_CONFIG(tabletevent)
700#endif
703#if QT_CONFIG(wayland_client_primary_selection)
710#endif
713 qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
719 inputDevice->setTextInput(nullptr);
720 }
721
727 this,
729 inputDevice->wl_seat())));
734 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1";
741 }
742
747 auto textInput =
751 }
752
757 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
764 }
765
776 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3";
782 }
789
793 bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
796 // make a roundtrip here since we need to receive the events sent by
797 // qt_hardware_integration before creating windows
799 }
802 for (auto *screen : std::as_const(mWaitingScreens))
808 } else if (interface == QLatin1String("wp_viewporter")) {
811 registry, id, qMin(1u, version)));
815 registry, id, std::min(1u, version)));
816 } else if (
826 } else if (
829#if QT_CONFIG(clipboard)
834 }
835#endif
840 registry, id, 1));
841 }
842#ifndef QT_NO_SESSIONMANAGER
846 registry, id, 1));
847 }
848#endif
853 }
854
855
858
859 const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
860 for (Listener l : copy)
862}
863
865{
866 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
868 if (global.id == id) {
870 for (auto *screen : mWaitingScreens) {
871 if (screen->outputId() == id) {
873 delete screen;
874 break;
875 }
876 }
877
879 if (screen->outputId() == id) {
881 // If this is the last screen, we have to add a fake screen, or Qt will break.
882 ensureScreen();
884 break;
885 }
886 }
887 }
891 inputDevice->setTextInput(nullptr);
893 }
897 inputDevice->setTextInput(nullptr);
899 }
903 inputDevice->setTextInput(nullptr);
905 }
911 }
912#if QT_CONFIG(wayland_client_primary_selection)
917 }
918#endif
919#if QT_CONFIG(clipboard)
924 }
925#endif
927 break;
928 }
929 }
930
933}
934
936{
939 return true;
940
941 return false;
942}
943
952
960
965
967{
968 static bool disabled = qgetenv("QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt();
969 // Stop early when disabled via the environment. Do not try to load the integration in
970 // order to play nice with SHM-only, buffer integration-less systems.
971 if (disabled)
972 return false;
973
974 // Don't initialize client buffer integration just to check whether it can have a decoration.
976 return true;
977
978 // We can do software-rendered decorations, only disable them if the integration explicitly says it can't.
980 return integrationSupport;
981}
982
987
992
999
1004
1017
1034
1049
1062
1068
1073
1075{
1076 // This callback is used to set the window activation because we may get an activate/deactivate
1077 // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the
1078 // handleWindowActivated() calls immediately.
1084}
1085
1087 [](void *data, struct wl_callback *callback, uint32_t time){
1088 Q_UNUSED(time);
1090 QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data);
1091 display->mSyncCallback = nullptr;
1093 }
1094};
1095
1097{
1098 if (mSyncCallback)
1099 return;
1100
1103}
1104
1109
1111{
1112 return std::any_of(
1114 [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
1115}
1116
1120
1121#if QT_CONFIG(cursor)
1122
1124{
1125 if (!mCursor)
1127 return mCursor.get();
1128}
1129
1130auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept
1132{
1133 const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) {
1135 };
1136
1137 const WaylandCursorTheme prototype = {name, pixelSize, nullptr};
1138
1140 if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize)
1141 return {it, true};
1142 else
1143 return {it, false};
1144}
1145
1147{
1149 if (result.found)
1150 return result.theme();
1151
1154
1155 return nullptr;
1156}
1157
1158#endif // QT_CONFIG(cursor)
1159
1160} // namespace QtWaylandClient
1161
1162QT_END_NAMESPACE
1163
1164#include "qwaylanddisplay.moc"
1165#include "moc_qwaylanddisplay_p.cpp"
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland")