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
6
15#if QT_CONFIG(clipboard)
16#include "qwaylandclipboard_p.h"
17#include "qwaylanddatacontrolv1_p.h"
18#endif
19#if QT_CONFIG(wayland_datadevice)
20#include "qwaylanddatadevicemanager_p.h"
21#include "qwaylanddatadevice_p.h"
22#endif // QT_CONFIG(wayland_datadevice)
23#if QT_CONFIG(wayland_client_primary_selection)
24#include "qwaylandprimaryselectionv1_p.h"
25#endif // QT_CONFIG(wayland_client_primary_selection)
26#if QT_CONFIG(cursor)
27#include <wayland-cursor.h>
28#endif
35
39
42#if QT_CONFIG(tabletevent)
43#include "qwaylandtabletv2_p.h"
44#endif
45
47
48#include <QtWaylandClient/private/qwayland-text-input-unstable-v1.h>
49#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
50#include <QtWaylandClient/private/qwayland-text-input-unstable-v3.h>
51#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
52#include <QtWaylandClient/private/qwayland-qt-text-input-method-unstable-v1.h>
53#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h>
54#include <QtWaylandClient/private/qwayland-viewporter.h>
55#include <QtWaylandClient/private/qwayland-cursor-shape-v1.h>
56#include <QtWaylandClient/private/qwayland-xx-session-management-v1.h>
57#include <QtWaylandClient/private/qwayland-xdg-system-bell-v1.h>
58#include <QtWaylandClient/private/qwayland-xdg-toplevel-drag-v1.h>
59#include <QtWaylandClient/private/qwayland-wlr-data-control-unstable-v1.h>
60#include <QtWaylandClient/private/qwayland-pointer-warp-v1.h>
61
62#include <QtCore/private/qcore_unix_p.h>
63
64#include <QtCore/QAbstractEventDispatcher>
65#include <QtGui/qpa/qwindowsysteminterface.h>
66#include <QtGui/private/qguiapplication_p.h>
67
68#include <QtCore/QDebug>
69
70#include <errno.h>
71
72#include <tuple> // for std::tie
73
74QT_BEGIN_NAMESPACE
75
76namespace QtWaylandClient {
77
78class EventThread : public QThread
79{
81public:
83 EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread.
84 SelfDispatch, // Dispatch the events inside this thread.
85 };
86
87 EventThread(struct wl_display * wl, struct wl_event_queue * ev_queue,
88 OperatingMode mode)
89 : m_fd(wl_display_get_fd(wl))
90 , m_pipefd{ -1, -1 }
91 , m_wldisplay(wl)
92 , m_wlevqueue(ev_queue)
93 , m_mode(mode)
94 , m_reading(true)
95 , m_quitting(false)
96 {
97 setObjectName(QStringLiteral("WaylandEventThread"));
98 }
99
101 {
102 /*
103 * Dispatch pending events and flush the requests at least once. If the event thread
104 * is not reading, try to call _prepare_read() to allow the event thread to poll().
105 * If that fails, re-try dispatch & flush again until _prepare_read() is successful.
106 *
107 * This allow any call to readAndDispatchEvents() to start event thread's polling,
108 * not only the one issued from event thread's waitForReading(), which means functions
109 * called from dispatch_pending() can safely spin an event loop.
110 */
111 if (m_quitting.loadRelaxed())
112 return;
113
114 for (;;) {
115 if (dispatchQueuePending() < 0) {
116 Q_EMIT waylandError();
117 m_quitting.storeRelaxed(true);
118 return;
119 }
120
121 wl_display_flush(m_wldisplay);
122
123 // We have to check if event thread is reading every time we dispatch
124 // something, as that may recursively call this function.
125 if (m_reading.loadAcquire())
126 break;
127
128 if (prepareReadQueue() == 0) {
129 QMutexLocker l(&m_mutex);
130 m_reading.storeRelease(true);
131 m_cond.wakeOne();
132 break;
133 }
134 }
135 }
136
137 void stop()
138 {
139 // We have to both write to the pipe and set the flag, as the thread may be
140 // either in the poll() or waiting for _prepare_read().
141 if (m_pipefd[1] != -1 && write(m_pipefd[1], "\0", 1) == -1)
142 qWarning("Failed to write to the pipe: %s.", strerror(errno));
143
144 m_quitting.storeRelaxed(true);
145 m_cond.wakeOne();
146
147 wait();
148 }
149
153
154protected:
156 {
157 // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets
158 // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore.
159 struct Pipe
160 {
161 Pipe(int *fds)
162 : fds(fds)
163 {
164 if (qt_safe_pipe(fds) != 0)
165 qWarning("Pipe creation failed. Quitting may hang.");
166 }
167 ~Pipe()
168 {
169 if (fds[0] != -1) {
170 close(fds[0]);
171 close(fds[1]);
172 }
173 }
174
175 int *fds;
176 } pipe(m_pipefd);
177
178 // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the
179 // outbound ones. Wait until it's done before proceeding, unless we're told to quit.
180 while (waitForReading()) {
181 if (!m_reading.loadRelaxed())
182 break;
183
184 pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } };
185 poll(fds, 2, -1);
186
187 if (fds[1].revents & POLLIN) {
188 // we don't really care to read the byte that was written here since we're closing down
189 wl_display_cancel_read(m_wldisplay);
190 break;
191 }
192
193 if (fds[0].revents & POLLIN)
194 wl_display_read_events(m_wldisplay);
195 // The poll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop
196 // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which
197 // case we don't care anymore about them.
198 else
199 wl_display_cancel_read(m_wldisplay);
200 }
201 }
202
203private:
204 bool waitForReading()
205 {
206 Q_ASSERT(QThread::currentThread() == this);
207
208 m_reading.storeRelease(false);
209
210 if (m_mode == SelfDispatch) {
212 } else {
213 Q_EMIT needReadAndDispatch();
214
215 QMutexLocker lock(&m_mutex);
216 // m_reading might be set from our emit or some other invocation of
217 // readAndDispatchEvents().
218 while (!m_reading.loadRelaxed() && !m_quitting.loadRelaxed())
219 m_cond.wait(&m_mutex);
220 }
221
222 return !m_quitting.loadRelaxed();
223 }
224
225 int dispatchQueuePending()
226 {
227 if (m_wlevqueue)
228 return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue);
229 else
230 return wl_display_dispatch_pending(m_wldisplay);
231 }
232
233 int prepareReadQueue()
234 {
235 if (m_wlevqueue)
236 return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue);
237 else
238 return wl_display_prepare_read(m_wldisplay);
239 }
240
241 int m_fd;
242 int m_pipefd[2];
243 wl_display *m_wldisplay;
244 wl_event_queue *m_wlevqueue;
245 OperatingMode m_mode;
246
247 /* Concurrency note when operating in EmitToDispatch mode:
248 * m_reading is set to false inside event thread's waitForReading(), and is
249 * set to true inside main thread's readAndDispatchEvents().
250 * The lock is not taken when setting m_reading to false, as the main thread
251 * is not actively waiting for it to turn false. However, the lock is taken
252 * inside readAndDispatchEvents() before setting m_reading to true,
253 * as the event thread is actively waiting for it under the wait condition.
254 */
255
256 QAtomicInteger<bool> m_reading;
257 QAtomicInteger<bool> m_quitting;
258 QMutex m_mutex;
259 QWaitCondition m_cond;
260};
261
262Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging
263
270
271struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
272{
274
275 for (const QRect &rect : qregion)
277
278 return region;
279}
280
282{
283 if (!mGlobals.subCompositor) {
284 qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor.";
285 return nullptr;
286 }
287
288 // Make sure we don't pass NULL surfaces to libwayland (crashes)
291
293}
294
296{
297 if (!mGlobals.viewporter) {
298 qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor.";
299 return nullptr;
300 }
301
304}
305
310
315
320
323{
324 qRegisterMetaType<uint32_t>("uint32_t");
325
326 mDisplay = wl_display_connect(nullptr);
327 if (mDisplay) {
329 } else {
330 qErrnoWarning(errno, "Failed to create wl_display");
331 }
332
333 mWaylandTryReconnect = qEnvironmentVariableIsSet("QT_WAYLAND_RECONNECT");
334 mPreferWlrDataControl = qEnvironmentVariableIntValue("QT_WAYLAND_USE_DATA_CONTROL") > 0;
335}
336
338{
340 init(registry);
341
342#if QT_CONFIG(xkbcommon)
344 if (!mXkbContext)
345 qCWarning(lcQpaWayland, "failed to create xkb context");
346#endif
349}
350
352{
353 if (m_eventThread)
355
358
359 if (mSyncCallback)
361
363
364 for (QWaylandScreen *screen : std::exchange(mScreens, {})) {
366 }
368
369#if QT_CONFIG(cursor)
371#endif
372
375
376 // Reset the globals manually since they need to be destroyed before the wl_display
377 mGlobals = {};
378
379 if (object())
381
382 if (mDisplay)
384}
385
386// Steps which is called just after constructor. This separates registry_global() out of the constructor
387// so that factory functions in integration can be overridden.
389{
390 if (!isInitialized())
391 return false;
392
394
395 emit connected();
396
397 if (!mWaitingScreens.isEmpty()) {
398 // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
400 }
403
404 return qEnvironmentVariableIntValue("QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration();
405}
406
408{
410 return; // There are real screens or we already have a fake one
411
412 qCInfo(lcQpaWayland) << "There are no outputs - creating placeholder screen";
413
417}
418
420{
421 qCWarning(lcQpaWayland) << "Attempting wayland reconnect";
426
429
430 while (!mScreens.isEmpty()) {
431 auto screen = mScreens.takeLast();
432 ensureScreen();
434 }
435
437 mCursor.reset();
438
440
442
444 mLastInputDevice = nullptr;
445
446 for (const RegistryGlobal &global : mRegistryGlobals) {
448 }
450
455
456 const auto windows = QGuiApplication::allWindows();
458 for (auto window : windows) {
459 if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle())) {
462 }
463 }
464
465 // Remove windows that do not need to be recreated and now closed popups
467 for (auto window : std::as_const(allPlatformWindows)) {
470 }
471 window->reset();
472 }
473
474 if (mSyncCallback) {
476 mSyncCallback = nullptr;
477 }
478
479 if (object())
481 mDisplay = wl_display_connect(nullptr);
482 if (!mDisplay)
483 _exit(1);
484
485 connect(
486 this, &QWaylandDisplay::connected, this,
487 [this, &allPlatformWindows] {
488 for (auto &window : std::as_const(allPlatformWindows)) {
490 }
491 forceRoundTrip(); // we need a roundtrip to receive the color space features the compositor supports
492 for (auto &window : std::as_const(allPlatformWindows)) {
494 }
495 },
497
499
503
504 initialize();
505
507 auto waylandWindow = static_cast<QWaylandWindow *>(window);
509 };
511 while (!recreateWindows.isEmpty()) {
513 (*window)->reinit();
515 } else {
516 ++window;
517 }
518 if (window == recreateWindows.end())
520 }
521
523}
524
529
530// We have to wait until we have an eventDispatcher before creating the eventThread,
531// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
532// polling.
549
551{
553 if ((ecode == EPIPE || ecode == ECONNRESET)) {
554 qWarning("The Wayland connection broke. Did the Wayland compositor die?");
556 reconnect();
557 return;
558 }
559 } else {
560 qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
561 }
562 _exit(-1);
563}
564
566{
567 QStringList tips, timps; // for text input protocols and text input manager protocols
568 // zwp_text_input_v2 is preferred over zwp_text_input_v3 because:
569 // - Currently, v3 is not as feature rich as v2.
570 // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma
571 // Mobile uses some v2 only.
580
581 QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
582 qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
584 if (!tiProtocols.isEmpty()) {
587 while (it != keys.end()) {
588 if (tips.contains(*it))
590 else
591 qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it;
592 ++it;
593 }
594 }
595 if (mTextInputManagerList.isEmpty()) // fallback
597}
598
600{
601 for (auto screen : std::as_const(mScreens)) {
602 if (screen->output() == output)
603 return screen;
604 }
605 return nullptr;
606}
607
609{
611 return;
614 if (mPlaceholderScreen) {
615 // handleScreenRemoved deletes the platform screen
617 mPlaceholderScreen = nullptr;
619
620 }
621}
622
623template <typename T, auto f>
624struct WithDestructor : public T
625{
626 using T::T;
628 {
629 f(this->object());
630 }
631};
632
634{
635 struct ::wl_registry *registry = object();
636
637 static QStringList interfaceBlacklist = qEnvironmentVariable("QT_WAYLAND_DISABLED_INTERFACES").split(u',');
639 return;
640 }
641
647 registry, id, qMin((int)version, 6)));
648 } else if (interface == QLatin1String(QWaylandShm::interface()->name)) {
649 mGlobals.shm.reset(new QWaylandShm(this, version, id));
653#if QT_CONFIG(wayland_datadevice)
656#endif
660 id, 1));
661#if QT_CONFIG(tabletevent)
667#endif
670#if QT_CONFIG(wayland_client_primary_selection)
677#endif
680 qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
686 inputDevice->setTextInput(nullptr);
687 }
688
694 this,
696 inputDevice->wl_seat())));
701 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1";
708 }
709
714 auto textInput =
718 }
719
724 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
731 }
732
743 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3";
749 }
756
760 bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
763 // make a roundtrip here since we need to receive the events sent by
764 // qt_hardware_integration before creating windows
766 }
769 for (auto *screen : std::as_const(mWaitingScreens))
775 } else if (interface == QLatin1String("wp_viewporter")) {
778 registry, id, qMin(1u, version)));
782 registry, id, std::min(1u, version)));
783 } else if (
793 } else if (
796#if QT_CONFIG(clipboard)
801 }
802#endif
807 registry, id, 1));
808 }
809#ifndef QT_NO_SESSIONMANAGER
811 && qEnvironmentVariableIntValue("QT_WAYLAND_ENABLE_XX_SESSION_MANAGER") > 0) {
814 registry, id, 1));
815 }
816#endif
817
818
821
822 const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
823 for (Listener l : copy)
825}
826
828{
829 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
831 if (global.id == id) {
833 for (auto *screen : mWaitingScreens) {
834 if (screen->outputId() == id) {
836 delete screen;
837 break;
838 }
839 }
840
842 if (screen->outputId() == id) {
844 // If this is the last screen, we have to add a fake screen, or Qt will break.
845 ensureScreen();
847 break;
848 }
849 }
850 }
854 inputDevice->setTextInput(nullptr);
856 }
860 inputDevice->setTextInput(nullptr);
862 }
866 inputDevice->setTextInput(nullptr);
868 }
874 }
875#if QT_CONFIG(wayland_client_primary_selection)
880 }
881#endif
882#if QT_CONFIG(clipboard)
887 }
888#endif
890 break;
891 }
892 }
893}
894
896{
899 return true;
900
901 return false;
902}
903
912
920
925
927{
928 static bool disabled = qgetenv("QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt();
929 // Stop early when disabled via the environment. Do not try to load the integration in
930 // order to play nice with SHM-only, buffer integration-less systems.
931 if (disabled)
932 return false;
933
934 // Don't initialize client buffer integration just to check whether it can have a decoration.
936 return true;
937
938 // We can do software-rendered decorations, only disable them if the integration explicitly says it can't.
940 return integrationSupport;
941}
942
947
954
959
971
987
1002
1008
1010{
1011 // This callback is used to set the window activation because we may get an activate/deactivate
1012 // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the
1013 // handleWindowActivated() calls immediately.
1017
1018 if (!activeWindow) {
1019 if (lastInputDevice()) {
1020#if QT_CONFIG(clipboard)
1021 if (auto *dataDevice = lastInputDevice()->dataDevice())
1023#endif
1024#if QT_CONFIG(wayland_client_primary_selection)
1027#endif
1028 }
1029 }
1030}
1031
1033 [](void *data, struct wl_callback *callback, uint32_t time){
1034 Q_UNUSED(time);
1036 QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data);
1037 display->mSyncCallback = nullptr;
1039 }
1040};
1041
1043{
1044 if (mSyncCallback)
1045 return;
1046
1049}
1050
1055
1057{
1058 return std::any_of(
1060 [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
1061}
1062
1066
1067#if QT_CONFIG(cursor)
1068
1070{
1071 if (!mCursor)
1073 return mCursor.data();
1074}
1075
1076auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept
1078{
1079 const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) {
1081 };
1082
1083 const WaylandCursorTheme prototype = {name, pixelSize, nullptr};
1084
1086 if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize)
1087 return {it, true};
1088 else
1089 return {it, false};
1090}
1091
1093{
1095 if (result.found)
1096 return result.theme();
1097
1100
1101 return nullptr;
1102}
1103
1104#endif // QT_CONFIG(cursor)
1105
1106} // namespace QtWaylandClient
1107
1108QT_END_NAMESPACE
1109
1110#include "qwaylanddisplay.moc"
1111#include "moc_qwaylanddisplay_p.cpp"
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland")