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
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-xx-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, struct wl_event_queue * ev_queue,
89 OperatingMode mode)
90 : m_fd(wl_display_get_fd(wl))
91 , m_pipefd{ -1, -1 }
92 , m_wldisplay(wl)
93 , m_wlevqueue(ev_queue)
94 , m_mode(mode)
95 , m_reading(true)
96 , m_quitting(false)
97 {
98 setObjectName(QStringLiteral("WaylandEventThread"));
99 }
100
102 {
103 /*
104 * Dispatch pending events and flush the requests at least once. If the event thread
105 * is not reading, try to call _prepare_read() to allow the event thread to poll().
106 * If that fails, re-try dispatch & flush again until _prepare_read() is successful.
107 *
108 * This allow any call to readAndDispatchEvents() to start event thread's polling,
109 * not only the one issued from event thread's waitForReading(), which means functions
110 * called from dispatch_pending() can safely spin an event loop.
111 */
112 if (m_quitting.loadRelaxed())
113 return;
114
115 for (;;) {
116 if (dispatchQueuePending() < 0) {
117 Q_EMIT waylandError();
118 m_quitting.storeRelaxed(true);
119 return;
120 }
121
122 wl_display_flush(m_wldisplay);
123
124 // We have to check if event thread is reading every time we dispatch
125 // something, as that may recursively call this function.
126 if (m_reading.loadAcquire())
127 break;
128
129 if (prepareReadQueue() == 0) {
130 QMutexLocker l(&m_mutex);
131 m_reading.storeRelease(true);
132 m_cond.wakeOne();
133 break;
134 }
135 }
136 }
137
138 void stop()
139 {
140 // We have to both write to the pipe and set the flag, as the thread may be
141 // either in the poll() or waiting for _prepare_read().
142 if (m_pipefd[1] != -1 && write(m_pipefd[1], "\0", 1) == -1)
143 qWarning("Failed to write to the pipe: %s.", strerror(errno));
144
145 m_quitting.storeRelaxed(true);
146 m_cond.wakeOne();
147
148 wait();
149 }
150
154
155protected:
157 {
158 // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets
159 // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore.
160 struct Pipe
161 {
162 Pipe(int *fds)
163 : fds(fds)
164 {
165 if (qt_safe_pipe(fds) != 0)
166 qWarning("Pipe creation failed. Quitting may hang.");
167 }
168 ~Pipe()
169 {
170 if (fds[0] != -1) {
171 close(fds[0]);
172 close(fds[1]);
173 }
174 }
175
176 int *fds;
177 } pipe(m_pipefd);
178
179 // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the
180 // outbound ones. Wait until it's done before proceeding, unless we're told to quit.
181 while (waitForReading()) {
182 if (!m_reading.loadRelaxed())
183 break;
184
185 pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } };
186 poll(fds, 2, -1);
187
188 if (fds[1].revents & POLLIN) {
189 // we don't really care to read the byte that was written here since we're closing down
190 wl_display_cancel_read(m_wldisplay);
191 break;
192 }
193
194 if (fds[0].revents & POLLIN)
195 wl_display_read_events(m_wldisplay);
196 // The poll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop
197 // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which
198 // case we don't care anymore about them.
199 else
200 wl_display_cancel_read(m_wldisplay);
201 }
202 }
203
204private:
205 bool waitForReading()
206 {
207 Q_ASSERT(QThread::currentThread() == this);
208
209 m_reading.storeRelease(false);
210
211 if (m_mode == SelfDispatch) {
213 } else {
214 Q_EMIT needReadAndDispatch();
215
216 QMutexLocker lock(&m_mutex);
217 // m_reading might be set from our emit or some other invocation of
218 // readAndDispatchEvents().
219 while (!m_reading.loadRelaxed() && !m_quitting.loadRelaxed())
220 m_cond.wait(&m_mutex);
221 }
222
223 return !m_quitting.loadRelaxed();
224 }
225
226 int dispatchQueuePending()
227 {
228 if (m_wlevqueue)
229 return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue);
230 else
231 return wl_display_dispatch_pending(m_wldisplay);
232 }
233
234 int prepareReadQueue()
235 {
236 if (m_wlevqueue)
237 return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue);
238 else
239 return wl_display_prepare_read(m_wldisplay);
240 }
241
242 int m_fd;
243 int m_pipefd[2];
244 wl_display *m_wldisplay;
245 wl_event_queue *m_wlevqueue;
246 OperatingMode m_mode;
247
248 /* Concurrency note when operating in EmitToDispatch mode:
249 * m_reading is set to false inside event thread's waitForReading(), and is
250 * set to true inside main thread's readAndDispatchEvents().
251 * The lock is not taken when setting m_reading to false, as the main thread
252 * is not actively waiting for it to turn false. However, the lock is taken
253 * inside readAndDispatchEvents() before setting m_reading to true,
254 * as the event thread is actively waiting for it under the wait condition.
255 */
256
257 QAtomicInteger<bool> m_reading;
258 QAtomicInteger<bool> m_quitting;
259 QMutex m_mutex;
260 QWaitCondition m_cond;
261};
262
263Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging
264
271
272struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
273{
275
276 for (const QRect &rect : qregion)
278
279 return region;
280}
281
283{
284 if (!mGlobals.subCompositor) {
285 qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor.";
286 return nullptr;
287 }
288
289 // Make sure we don't pass NULL surfaces to libwayland (crashes)
292
294}
295
297{
298 if (!mGlobals.viewporter) {
299 qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor.";
300 return nullptr;
301 }
302
305}
306
311
316
321
324{
325 qRegisterMetaType<uint32_t>("uint32_t");
326
327 mDisplay = wl_display_connect(nullptr);
328 if (mDisplay) {
330 } else {
331 qErrnoWarning(errno, "Failed to create wl_display");
332 }
333
334 mWaylandTryReconnect = qEnvironmentVariableIsSet("QT_WAYLAND_RECONNECT");
335 mPreferWlrDataControl = qEnvironmentVariableIntValue("QT_WAYLAND_USE_DATA_CONTROL") > 0;
336}
337
339{
341 init(registry);
342
343#if QT_CONFIG(xkbcommon)
345 if (!mXkbContext)
346 qCWarning(lcQpaWayland, "failed to create xkb context");
347#endif
350}
351
353{
354 if (m_eventThread)
356
359
360 if (mSyncCallback)
362
364
365 for (QWaylandScreen *screen : std::exchange(mScreens, {})) {
367 }
369
370#if QT_CONFIG(cursor)
372#endif
373
376
377 // Reset the globals manually since they need to be destroyed before the wl_display
378 mGlobals = {};
379
380 if (object())
382
383 if (mDisplay)
385}
386
387// Steps which is called just after constructor. This separates registry_global() out of the constructor
388// so that factory functions in integration can be overridden.
390{
391 if (!isInitialized())
392 return false;
393
395
396 emit connected();
397
398 if (!mWaitingScreens.isEmpty()) {
399 // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
401 }
404
405 return qEnvironmentVariableIntValue("QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration();
406}
407
409{
411 return; // There are real screens or we already have a fake one
412
413 qCInfo(lcQpaWayland) << "There are no outputs - creating placeholder screen";
414
418}
419
421{
422 qCWarning(lcQpaWayland) << "Attempting wayland reconnect";
427
430
431 while (!mScreens.isEmpty()) {
432 auto screen = mScreens.takeLast();
433 ensureScreen();
435 }
436
438 mCursor.reset();
439
441
443
445 mLastInputDevice = nullptr;
446
447 for (const RegistryGlobal &global : mRegistryGlobals) {
449 }
451
456
457 const auto windows = QGuiApplication::allWindows();
459 for (auto window : windows) {
460 if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle())) {
463 }
464 }
465
466 // Remove windows that do not need to be recreated and now closed popups
468 for (auto window : std::as_const(allPlatformWindows)) {
471 }
472 window->reset();
473 }
474
475 if (mSyncCallback) {
477 mSyncCallback = nullptr;
478 }
479
480 if (object())
482 mDisplay = wl_display_connect(nullptr);
483 if (!mDisplay)
484 _exit(1);
485
486 connect(
487 this, &QWaylandDisplay::connected, this,
488 [this, &allPlatformWindows] {
489 for (auto &window : std::as_const(allPlatformWindows)) {
491 }
492 forceRoundTrip(); // we need a roundtrip to receive the color space features the compositor supports
493 for (auto &window : std::as_const(allPlatformWindows)) {
495 }
496 },
498
500 initialize();
501
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
530
531// We have to wait until we have an eventDispatcher before creating the eventThread,
532// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
533// polling.
550
552{
554 if ((ecode == EPIPE || ecode == ECONNRESET)) {
555 qWarning("The Wayland connection broke. Did the Wayland compositor die?");
557 reconnect();
558 return;
559 }
560 } else {
561 qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
562 }
563 _exit(-1);
564}
565
567{
568 QStringList tips, timps; // for text input protocols and text input manager protocols
569 // zwp_text_input_v2 is preferred over zwp_text_input_v3 because:
570 // - Currently, v3 is not as feature rich as v2.
571 // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma
572 // Mobile uses some v2 only.
581
582 QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
583 qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
585 if (!tiProtocols.isEmpty()) {
588 while (it != keys.end()) {
589 if (tips.contains(*it))
591 else
592 qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it;
593 ++it;
594 }
595 }
596 if (mTextInputManagerList.isEmpty()) // fallback
598}
599
601{
602 for (auto screen : std::as_const(mScreens)) {
603 if (screen->output() == output)
604 return screen;
605 }
606 return nullptr;
607}
608
610{
612 return;
615 if (mPlaceholderScreen) {
616 // handleScreenRemoved deletes the platform screen
618 mPlaceholderScreen = nullptr;
620
621 }
622}
623
624template <typename T, auto f>
625struct WithDestructor : public T
626{
627 using T::T;
629 {
630 f(this->object());
631 }
632};
633
635{
636 struct ::wl_registry *registry = object();
637
638 static QStringList interfaceBlacklist = qEnvironmentVariable("QT_WAYLAND_DISABLED_INTERFACES").split(u',');
640 return;
641 }
642
648 registry, id, qMin((int)version, 6)));
649 } else if (interface == QLatin1String(QWaylandShm::interface()->name)) {
650 mGlobals.shm.reset(new QWaylandShm(this, version, id));
654#if QT_CONFIG(wayland_datadevice)
657#endif
661 id, 1));
662#if QT_CONFIG(tabletevent)
668#endif
671#if QT_CONFIG(wayland_client_primary_selection)
678#endif
681 qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
687 inputDevice->setTextInput(nullptr);
688 }
689
695 this,
697 inputDevice->wl_seat())));
702 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1";
709 }
710
715 auto textInput =
719 }
720
725 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
732 }
733
744 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3";
750 }
757
761 bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
764 // make a roundtrip here since we need to receive the events sent by
765 // qt_hardware_integration before creating windows
767 }
770 for (auto *screen : std::as_const(mWaitingScreens))
776 } else if (interface == QLatin1String("wp_viewporter")) {
779 registry, id, qMin(1u, version)));
783 registry, id, std::min(1u, version)));
784 } else if (
794 } else if (
797#if QT_CONFIG(clipboard)
802 }
803#endif
808 registry, id, 1));
809 }
810#ifndef QT_NO_SESSIONMANAGER
812 && qEnvironmentVariableIntValue("QT_WAYLAND_ENABLE_XX_SESSION_MANAGER") > 0) {
815 registry, id, 1));
816 }
817#endif
818
819
822
823 const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
824 for (Listener l : copy)
826}
827
829{
830 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
832 if (global.id == id) {
834 for (auto *screen : mWaitingScreens) {
835 if (screen->outputId() == id) {
837 delete screen;
838 break;
839 }
840 }
841
843 if (screen->outputId() == id) {
845 // If this is the last screen, we have to add a fake screen, or Qt will break.
846 ensureScreen();
848 break;
849 }
850 }
851 }
855 inputDevice->setTextInput(nullptr);
857 }
861 inputDevice->setTextInput(nullptr);
863 }
867 inputDevice->setTextInput(nullptr);
869 }
875 }
876#if QT_CONFIG(wayland_client_primary_selection)
881 }
882#endif
883#if QT_CONFIG(clipboard)
888 }
889#endif
891 break;
892 }
893 }
894}
895
897{
900 return true;
901
902 return false;
903}
904
913
921
926
928{
929 static bool disabled = qgetenv("QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt();
930 // Stop early when disabled via the environment. Do not try to load the integration in
931 // order to play nice with SHM-only, buffer integration-less systems.
932 if (disabled)
933 return false;
934
935 // Don't initialize client buffer integration just to check whether it can have a decoration.
937 return true;
938
939 // We can do software-rendered decorations, only disable them if the integration explicitly says it can't.
941 return integrationSupport;
942}
943
948
955
960
972
988
1003
1009
1011{
1012 // This callback is used to set the window activation because we may get an activate/deactivate
1013 // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the
1014 // handleWindowActivated() calls immediately.
1018
1019 if (!activeWindow) {
1020 if (lastInputDevice()) {
1021#if QT_CONFIG(clipboard)
1022 if (auto *dataDevice = lastInputDevice()->dataDevice())
1024#endif
1025#if QT_CONFIG(wayland_client_primary_selection)
1028#endif
1029 }
1030 }
1031}
1032
1034 [](void *data, struct wl_callback *callback, uint32_t time){
1035 Q_UNUSED(time);
1037 QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data);
1038 display->mSyncCallback = nullptr;
1040 }
1041};
1042
1044{
1045 if (mSyncCallback)
1046 return;
1047
1050}
1051
1056
1058{
1059 return std::any_of(
1061 [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
1062}
1063
1067
1068#if QT_CONFIG(cursor)
1069
1071{
1072 if (!mCursor)
1074 return mCursor.data();
1075}
1076
1077auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept
1079{
1080 const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) {
1082 };
1083
1084 const WaylandCursorTheme prototype = {name, pixelSize, nullptr};
1085
1087 if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize)
1088 return {it, true};
1089 else
1090 return {it, false};
1091}
1092
1094{
1096 if (result.found)
1097 return result.theme();
1098
1101
1102 return nullptr;
1103}
1104
1105#endif // QT_CONFIG(cursor)
1106
1107} // namespace QtWaylandClient
1108
1109QT_END_NAMESPACE
1110
1111#include "qwaylanddisplay.moc"
1112#include "moc_qwaylanddisplay_p.cpp"
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland")