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,
489 for (auto &window : std::as_const(allPlatformWindows)) {
491 }
492 },
494
496 initialize();
497
501
503 auto waylandWindow = static_cast<QWaylandWindow *>(window);
505 };
507 while (!recreateWindows.isEmpty()) {
509 (*window)->reinit();
511 } else {
512 ++window;
513 }
514 if (window == recreateWindows.end())
516 }
517
519}
520
526
527// We have to wait until we have an eventDispatcher before creating the eventThread,
528// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
529// polling.
546
548{
550 if ((ecode == EPIPE || ecode == ECONNRESET)) {
551 qWarning("The Wayland connection broke. Did the Wayland compositor die?");
553 reconnect();
554 return;
555 }
556 } else {
557 qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
558 }
559 _exit(-1);
560}
561
563{
564 QStringList tips, timps; // for text input protocols and text input manager protocols
565 // zwp_text_input_v2 is preferred over zwp_text_input_v3 because:
566 // - Currently, v3 is not as feature rich as v2.
567 // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma
568 // Mobile uses some v2 only.
577
578 QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
579 qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
581 if (!tiProtocols.isEmpty()) {
584 while (it != keys.end()) {
585 if (tips.contains(*it))
587 else
588 qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it;
589 ++it;
590 }
591 }
592 if (mTextInputManagerList.isEmpty()) // fallback
594}
595
597{
598 for (auto screen : std::as_const(mScreens)) {
599 if (screen->output() == output)
600 return screen;
601 }
602 return nullptr;
603}
604
606{
608 return;
611 if (mPlaceholderScreen) {
612 // handleScreenRemoved deletes the platform screen
614 mPlaceholderScreen = nullptr;
616
617 }
618}
619
620template <typename T, auto f>
621struct WithDestructor : public T
622{
623 using T::T;
625 {
626 f(this->object());
627 }
628};
629
631{
632 struct ::wl_registry *registry = object();
633
634 static QStringList interfaceBlacklist = qEnvironmentVariable("QT_WAYLAND_DISABLED_INTERFACES").split(u',');
636 return;
637 }
638
644 registry, id, qMin((int)version, 6)));
645 } else if (interface == QLatin1String(QWaylandShm::interface()->name)) {
646 mGlobals.shm.reset(new QWaylandShm(this, version, id));
650#if QT_CONFIG(wayland_datadevice)
653#endif
657 id, 1));
658#if QT_CONFIG(tabletevent)
664#endif
667#if QT_CONFIG(wayland_client_primary_selection)
674#endif
677 qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
683 inputDevice->setTextInput(nullptr);
684 }
685
691 this,
693 inputDevice->wl_seat())));
698 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1";
705 }
706
711 auto textInput =
715 }
716
721 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
728 }
729
740 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3";
746 }
753
757 bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
760 // make a roundtrip here since we need to receive the events sent by
761 // qt_hardware_integration before creating windows
763 }
766 for (auto *screen : std::as_const(mWaitingScreens))
772 } else if (interface == QLatin1String("wp_viewporter")) {
775 registry, id, qMin(1u, version)));
779 registry, id, std::min(1u, version)));
780 } else if (
790 } else if (
793#if QT_CONFIG(clipboard)
798 }
799#endif
802 // we need a roundtrip to receive the features the compositor supports
806 registry, id, 1));
807 }
808#ifndef QT_NO_SESSIONMANAGER
810 && qEnvironmentVariableIntValue("QT_WAYLAND_ENABLE_XX_SESSION_MANAGER") > 0) {
813 registry, id, 1));
814 }
815#endif
816
817
820
821 const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
822 for (Listener l : copy)
824}
825
827{
828 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
830 if (global.id == id) {
832 for (auto *screen : mWaitingScreens) {
833 if (screen->outputId() == id) {
835 delete screen;
836 break;
837 }
838 }
839
841 if (screen->outputId() == id) {
843 // If this is the last screen, we have to add a fake screen, or Qt will break.
844 ensureScreen();
846 break;
847 }
848 }
849 }
853 inputDevice->setTextInput(nullptr);
855 }
859 inputDevice->setTextInput(nullptr);
861 }
865 inputDevice->setTextInput(nullptr);
867 }
873 }
874#if QT_CONFIG(wayland_client_primary_selection)
879 }
880#endif
881#if QT_CONFIG(clipboard)
886 }
887#endif
889 break;
890 }
891 }
892}
893
895{
898 return true;
899
900 return false;
901}
902
911
919
924
926{
927 static bool disabled = qgetenv("QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt();
928 // Stop early when disabled via the environment. Do not try to load the integration in
929 // order to play nice with SHM-only, buffer integration-less systems.
930 if (disabled)
931 return false;
932
933 // Don't initialize client buffer integration just to check whether it can have a decoration.
935 return true;
936
937 // We can do software-rendered decorations, only disable them if the integration explicitly says it can't.
939 return integrationSupport;
940}
941
946
953
958
970
986
1001
1007
1009{
1010 // This callback is used to set the window activation because we may get an activate/deactivate
1011 // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the
1012 // handleWindowActivated() calls immediately.
1016
1017 if (!activeWindow) {
1018 if (lastInputDevice()) {
1019#if QT_CONFIG(clipboard)
1020 if (auto *dataDevice = lastInputDevice()->dataDevice())
1022#endif
1023#if QT_CONFIG(wayland_client_primary_selection)
1026#endif
1027 }
1028 }
1029}
1030
1032 [](void *data, struct wl_callback *callback, uint32_t time){
1033 Q_UNUSED(time);
1035 QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data);
1036 display->mSyncCallback = nullptr;
1038 }
1039};
1040
1042{
1043 if (mSyncCallback)
1044 return;
1045
1048}
1049
1054
1056{
1057 return std::any_of(
1059 [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
1060}
1061
1065
1066#if QT_CONFIG(cursor)
1067
1069{
1070 if (!mCursor)
1072 return mCursor.data();
1073}
1074
1075auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept
1077{
1078 const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) {
1080 };
1081
1082 const WaylandCursorTheme prototype = {name, pixelSize, nullptr};
1083
1085 if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize)
1086 return {it, true};
1087 else
1088 return {it, false};
1089}
1090
1092{
1094 if (result.found)
1095 return result.theme();
1096
1099
1100 return nullptr;
1101}
1102
1103#endif // QT_CONFIG(cursor)
1104
1105} // namespace QtWaylandClient
1106
1107QT_END_NAMESPACE
1108
1109#include "qwaylanddisplay.moc"
1110#include "moc_qwaylanddisplay_p.cpp"
Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland")