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
63#include <QtCore/private/qcore_unix_p.h>
64
65#include <QtCore/QAbstractEventDispatcher>
66#include <QtGui/qpa/qwindowsysteminterface.h>
67#include <QtGui/private/qguiapplication_p.h>
68
69#include <QtCore/QDebug>
70
71#include <errno.h>
72
73#include <tuple> // for std::tie
74
75QT_BEGIN_NAMESPACE
76
77namespace QtWaylandClient {
78
79class EventThread : public QThread
80{
82public:
84 EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread.
85 SelfDispatch, // Dispatch the events inside this thread.
86 };
87
88 EventThread(struct wl_display * wl,
89 struct wl_event_queue * ev_queue,
90 OperatingMode mode,
91 QObject *parent = nullptr)
92 : QThread(parent)
93 , m_fd(wl_display_get_fd(wl))
94 , m_pipefd{ -1, -1 }
95 , m_wldisplay(wl)
96 , m_wlevqueue(ev_queue)
97 , m_mode(mode)
98 , m_reading(true)
99 , m_quitting(false)
100 {
101 setObjectName(QStringLiteral("WaylandEventThread"));
102 }
103
105 {
106 /*
107 * Dispatch pending events and flush the requests at least once. If the event thread
108 * is not reading, try to call _prepare_read() to allow the event thread to poll().
109 * If that fails, re-try dispatch & flush again until _prepare_read() is successful.
110 *
111 * This allow any call to readAndDispatchEvents() to start event thread's polling,
112 * not only the one issued from event thread's waitForReading(), which means functions
113 * called from dispatch_pending() can safely spin an event loop.
114 */
115 if (m_quitting.loadRelaxed())
116 return;
117
118 for (;;) {
119 if (dispatchQueuePending() < 0) {
120 Q_EMIT waylandError();
121 m_quitting.storeRelaxed(true);
122 return;
123 }
124
125 wl_display_flush(m_wldisplay);
126
127 // We have to check if event thread is reading every time we dispatch
128 // something, as that may recursively call this function.
129 if (m_reading.loadAcquire())
130 break;
131
132 if (prepareReadQueue() == 0) {
133 QMutexLocker l(&m_mutex);
134 m_reading.storeRelease(true);
135 m_cond.wakeOne();
136 break;
137 }
138 }
139 }
140
141 void stop()
142 {
143 // We have to both write to the pipe and set the flag, as the thread may be
144 // either in the poll() or waiting for _prepare_read().
145 if (m_pipefd[1] != -1 && write(m_pipefd[1], "\0", 1) == -1)
146 qWarning("Failed to write to the pipe: %s.", strerror(errno));
147
148 m_quitting.storeRelaxed(true);
149 m_cond.wakeOne();
150
151 wait();
152 }
153
157
158protected:
160 {
161 // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets
162 // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore.
163 struct Pipe
164 {
165 Pipe(int *fds)
166 : fds(fds)
167 {
168 if (qt_safe_pipe(fds) != 0)
169 qWarning("Pipe creation failed. Quitting may hang.");
170 }
171 ~Pipe()
172 {
173 if (fds[0] != -1) {
174 close(fds[0]);
175 close(fds[1]);
176 }
177 }
178
179 int *fds;
180 private:
181 Q_DISABLE_COPY(Pipe)
182 } pipe(m_pipefd);
183
184 // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the
185 // outbound ones. Wait until it's done before proceeding, unless we're told to quit.
186 while (waitForReading()) {
187 if (!m_reading.loadRelaxed())
188 break;
189
190 pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } };
191 poll(fds, 2, -1);
192
193 if (fds[1].revents & POLLIN) {
194 // we don't really care to read the byte that was written here since we're closing down
195 wl_display_cancel_read(m_wldisplay);
196 break;
197 }
198
199 if (fds[0].revents & POLLIN)
200 wl_display_read_events(m_wldisplay);
201 // The poll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop
202 // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which
203 // case we don't care anymore about them.
204 else
205 wl_display_cancel_read(m_wldisplay);
206 }
207 }
208
209private:
210 bool waitForReading()
211 {
212 Q_ASSERT(QThread::currentThread() == this);
213
214 m_reading.storeRelease(false);
215
216 if (m_mode == SelfDispatch) {
218 } else {
219 Q_EMIT needReadAndDispatch();
220
221 QMutexLocker lock(&m_mutex);
222 // m_reading might be set from our emit or some other invocation of
223 // readAndDispatchEvents().
224 while (!m_reading.loadRelaxed() && !m_quitting.loadRelaxed())
225 m_cond.wait(&m_mutex);
226 }
227
228 return !m_quitting.loadRelaxed();
229 }
230
231 int dispatchQueuePending()
232 {
233 if (m_wlevqueue)
234 return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue);
235 else
236 return wl_display_dispatch_pending(m_wldisplay);
237 }
238
239 int prepareReadQueue()
240 {
241 if (m_wlevqueue)
242 return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue);
243 else
244 return wl_display_prepare_read(m_wldisplay);
245 }
246
247 int m_fd;
248 int m_pipefd[2];
249 wl_display *m_wldisplay;
250 wl_event_queue *m_wlevqueue;
251 OperatingMode m_mode;
252
253 /* Concurrency note when operating in EmitToDispatch mode:
254 * m_reading is set to false inside event thread's waitForReading(), and is
255 * set to true inside main thread's readAndDispatchEvents().
256 * The lock is not taken when setting m_reading to false, as the main thread
257 * is not actively waiting for it to turn false. However, the lock is taken
258 * inside readAndDispatchEvents() before setting m_reading to true,
259 * as the event thread is actively waiting for it under the wait condition.
260 */
261
262 QAtomicInteger<bool> m_reading;
263 QAtomicInteger<bool> m_quitting;
264 QMutex m_mutex;
265 QWaitCondition m_cond;
266};
267
268Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging
269
276
277struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
278{
280
281 for (const QRect &rect : qregion)
283
284 return region;
285}
286
288{
289 if (!mGlobals.subCompositor) {
290 qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor.";
291 return nullptr;
292 }
293
294 // Make sure we don't pass NULL surfaces to libwayland (crashes)
297
299}
300
302{
303 if (!mGlobals.viewporter) {
304 qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor.";
305 return nullptr;
306 }
307
310}
311
316
321
326
328 : QObject(parent)
330{
331 qRegisterMetaType<uint32_t>("uint32_t");
332
333 mDisplay = wl_display_connect(nullptr);
334 if (mDisplay) {
336 } else {
337 qErrnoWarning(errno, "Failed to create wl_display");
338 }
339
340 mWaylandTryReconnect = qEnvironmentVariableIsSet("QT_WAYLAND_RECONNECT");
341 mPreferWlrDataControl = qEnvironmentVariableIntValue("QT_WAYLAND_USE_DATA_CONTROL") > 0;
342}
343
345{
347 init(registry);
348
349#if QT_CONFIG(xkbcommon)
351 if (!mXkbContext)
352 qCWarning(lcQpaWayland, "failed to create xkb context");
353#endif
356}
357
359{
360 if (m_eventThread)
362
365
366 if (mSyncCallback)
368
370
371 for (QWaylandScreen *screen : std::exchange(mScreens, {})) {
373 }
375
376#if QT_CONFIG(cursor)
378#endif
379
382
383 // Reset the globals manually since they need to be destroyed before the wl_display
384 mGlobals = {};
385
386 if (object()) {
387 if (mFixes)
389
391 }
392
393 mFixes.reset();
394
395 if (mDisplay)
397}
398
399// Steps which is called just after constructor. This separates registry_global() out of the constructor
400// so that factory functions in integration can be overridden.
402{
403 if (!isInitialized())
404 return false;
405
407
408 emit connected();
409
410 if (!mWaitingScreens.isEmpty()) {
411 // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
413 }
416
417 return qEnvironmentVariableIntValue("QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration();
418}
419
421{
423 return; // There are real screens or we already have a fake one
424
425 qCInfo(lcQpaWayland) << "There are no outputs - creating placeholder screen";
426
430}
431
433{
434 qCWarning(lcQpaWayland) << "Attempting wayland reconnect";
439
442
443 while (!mScreens.isEmpty()) {
444 auto screen = mScreens.takeLast();
445 ensureScreen();
447 }
448
450 mCursor.reset();
451
453
455
457 mLastInputDevice = nullptr;
458
459 for (const RegistryGlobal &global : mRegistryGlobals) {
461 }
463
469
470 const auto windows = QGuiApplication::allWindows();
472 for (auto window : windows) {
473 if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle())) {
476 }
477 }
478
479 // Remove windows that do not need to be recreated and now closed popups
481 for (auto window : std::as_const(allPlatformWindows)) {
484 }
485 window->reset();
486 }
487
488 if (mSyncCallback) {
490 mSyncCallback = nullptr;
491 }
492
493 if (object()) {
494 if (mFixes)
496
498 }
499
500 mFixes.reset();
501
502 mDisplay = wl_display_connect(nullptr);
503 if (!mDisplay)
504 _exit(1);
505
507 connect(
508 this, &QWaylandDisplay::connected, this,
509 [this, &allPlatformWindows] {
510 for (auto &window : std::as_const(allPlatformWindows)) {
512 }
513 forceRoundTrip(); // we need a roundtrip to receive the color space features the compositor supports
514 for (auto &window : std::as_const(allPlatformWindows)) {
516 }
517 },
519
521
525
526 initialize();
527
529 auto waylandWindow = static_cast<QWaylandWindow *>(window);
531 };
533 while (!recreateWindows.isEmpty()) {
535 (*window)->reinit();
537 } else {
538 ++window;
539 }
540 if (window == recreateWindows.end())
542 }
543
545
546 // Failsafe: Make sure we don't have a lingering connection referencing local variables
547 // (This will have been executed and disconnected in initialize())
549}
550
555
556// We have to wait until we have an eventDispatcher before creating the eventThread,
557// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
558// polling.
575
577{
579 if ((ecode == EPIPE || ecode == ECONNRESET)) {
580 qWarning("The Wayland connection broke. Did the Wayland compositor die?");
582 reconnect();
583 return;
584 }
585 } else {
586 qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
587 }
588 _exit(-1);
589}
590
592{
593 QStringList tips, timps; // for text input protocols and text input manager protocols
594 // zwp_text_input_v2 is preferred over zwp_text_input_v3 because:
595 // - Currently, v3 is not as feature rich as v2.
596 // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma
597 // Mobile uses some v2 only.
606
607 QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
608 qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
610 if (!tiProtocols.isEmpty()) {
613 while (it != keys.end()) {
614 if (tips.contains(*it))
616 else
617 qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it;
618 ++it;
619 }
620 }
621 if (mTextInputManagerList.isEmpty()) // fallback
623}
624
626{
627 for (auto screen : std::as_const(mScreens)) {
628 if (screen->output() == output)
629 return screen;
630 }
631 return nullptr;
632}
633
635{
637 return;
640 if (mPlaceholderScreen) {
641 // handleScreenRemoved deletes the platform screen
643 mPlaceholderScreen = nullptr;
645
646 }
647}
648
649template <typename T, auto f>
650struct WithDestructor : public T
651{
652 using T::T;
654 {
655 f(this->object());
656 }
657
658private:
660};
661
663{
664 struct ::wl_registry *registry = object();
665
666 static QStringList interfaceBlacklist = qEnvironmentVariable("QT_WAYLAND_DISABLED_INTERFACES").split(u',');
668 return;
669 }
670
676 registry, id, qMin((int)version, 6)));
677 } else if (interface == QLatin1String(QWaylandShm::interface()->name)) {
678 mGlobals.shm.reset(new QWaylandShm(this, version, id));
682#if QT_CONFIG(wayland_datadevice)
685#endif
689 id, 1));
690#if QT_CONFIG(tabletevent)
696#endif
699#if QT_CONFIG(wayland_client_primary_selection)
706#endif
709 qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
715 inputDevice->setTextInput(nullptr);
716 }
717
723 this,
725 inputDevice->wl_seat())));
730 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1";
737 }
738
743 auto textInput =
747 }
748
753 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
760 }
761
772 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3";
778 }
785
789 bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
792 // make a roundtrip here since we need to receive the events sent by
793 // qt_hardware_integration before creating windows
795 }
798 for (auto *screen : std::as_const(mWaitingScreens))
804 } else if (interface == QLatin1String("wp_viewporter")) {
807 registry, id, qMin(1u, version)));
811 registry, id, std::min(1u, version)));
812 } else if (
822 } else if (
825#if QT_CONFIG(clipboard)
830 }
831#endif
836 registry, id, 1));
837 }
838#ifndef QT_NO_SESSIONMANAGER
842 registry, id, 1));
843 }
844#endif
847 }
848
849
852
853 const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
854 for (Listener l : copy)
856}
857
859{
860 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
862 if (global.id == id) {
864 for (auto *screen : mWaitingScreens) {
865 if (screen->outputId() == id) {
867 delete screen;
868 break;
869 }
870 }
871
873 if (screen->outputId() == id) {
875 // If this is the last screen, we have to add a fake screen, or Qt will break.
876 ensureScreen();
878 break;
879 }
880 }
881 }
885 inputDevice->setTextInput(nullptr);
887 }
891 inputDevice->setTextInput(nullptr);
893 }
897 inputDevice->setTextInput(nullptr);
899 }
905 }
906#if QT_CONFIG(wayland_client_primary_selection)
911 }
912#endif
913#if QT_CONFIG(clipboard)
918 }
919#endif
921 break;
922 }
923 }
924
927}
928
930{
933 return true;
934
935 return false;
936}
937
946
954
959
961{
962 static bool disabled = qgetenv("QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt();
963 // Stop early when disabled via the environment. Do not try to load the integration in
964 // order to play nice with SHM-only, buffer integration-less systems.
965 if (disabled)
966 return false;
967
968 // Don't initialize client buffer integration just to check whether it can have a decoration.
970 return true;
971
972 // We can do software-rendered decorations, only disable them if the integration explicitly says it can't.
974 return integrationSupport;
975}
976
981
986
993
998
1011
1028
1043
1056
1062
1067
1069{
1070 // This callback is used to set the window activation because we may get an activate/deactivate
1071 // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the
1072 // handleWindowActivated() calls immediately.
1078}
1079
1081 [](void *data, struct wl_callback *callback, uint32_t time){
1082 Q_UNUSED(time);
1084 QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data);
1085 display->mSyncCallback = nullptr;
1087 }
1088};
1089
1091{
1092 if (mSyncCallback)
1093 return;
1094
1097}
1098
1103
1105{
1106 return std::any_of(
1108 [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
1109}
1110
1114
1115#if QT_CONFIG(cursor)
1116
1118{
1119 if (!mCursor)
1121 return mCursor.get();
1122}
1123
1124auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept
1126{
1127 const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) {
1129 };
1130
1131 const WaylandCursorTheme prototype = {name, pixelSize, nullptr};
1132
1134 if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize)
1135 return {it, true};
1136 else
1137 return {it, false};
1138}
1139
1141{
1143 if (result.found)
1144 return result.theme();
1145
1148
1149 return nullptr;
1150}
1151
1152#endif // QT_CONFIG(cursor)
1153
1154} // namespace QtWaylandClient
1155
1156QT_END_NAMESPACE
1157
1158#include "qwaylanddisplay.moc"
1159#include "moc_qwaylanddisplay_p.cpp"
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland")