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
5#include "qwaylanddisplay_p.h"
6
7#include "qwaylandappmenu_p.h"
8#include "qwaylandintegration_p.h"
9#include "qwaylandwindow_p.h"
10#include "qwaylandsurface_p.h"
11#include "qwaylandabstractdecoration_p.h"
12#include "qwaylandscreen_p.h"
13#include "qwaylandcursor_p.h"
14#include "qwaylandinputdevice_p.h"
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
29#include "qwaylandhardwareintegration_p.h"
30#include "qwaylandtextinputv1_p.h"
31#include "qwaylandtextinputv2_p.h"
32#include "qwaylandtextinputv3_p.h"
33#include "qwaylandinputcontext_p.h"
34#include "qwaylandinputmethodcontext_p.h"
35#include "qwaylandeventdispatcher_p.h"
36
37#include "qwaylandwindowmanagerintegration_p.h"
38#include "qwaylandshellintegration_p.h"
39#include "qwaylandclientbufferintegration_p.h"
40
41#include "qwaylandpointergestures_p.h"
42#include "qwaylandsubsurface_p.h"
43#if QT_CONFIG(tabletevent)
44#include "qwaylandtabletv2_p.h"
45#endif
46
47#include "qwaylandcolormanagement_p.h"
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-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{
80 Q_OBJECT
81public:
82 enum OperatingMode {
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
100 void readAndDispatchEvents()
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(newValue: 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(newValue: 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(fd: m_pipefd[1], buf: "\0", n: 1) == -1)
142 qWarning(msg: "Failed to write to the pipe: %s.", strerror(errno));
143
144 m_quitting.storeRelaxed(newValue: true);
145 m_cond.wakeOne();
146
147 wait();
148 }
149
150Q_SIGNALS:
151 void needReadAndDispatch();
152 void waylandError();
153
154protected:
155 void run() override
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(pipefd: fds) != 0)
165 qWarning(msg: "Pipe creation failed. Quitting may hang.");
166 }
167 ~Pipe()
168 {
169 if (fds[0] != -1) {
170 close(fd: fds[0]);
171 close(fd: 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] = { { .fd: m_fd, POLLIN, .revents: 0 }, { .fd: m_pipefd[0], POLLIN, .revents: 0 } };
185 poll(fds: fds, nfds: 2, timeout: -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(newValue: false);
209
210 if (m_mode == SelfDispatch) {
211 readAndDispatchEvents();
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(lockedMutex: &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
264struct wl_surface *QWaylandDisplay::createSurface(void *handle)
265{
266 struct wl_surface *surface = mGlobals.compositor->create_surface();
267 wl_surface_set_user_data(surface, handle);
268 return surface;
269}
270
271struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
272{
273 struct ::wl_region *region = mGlobals.compositor->create_region();
274
275 for (const QRect &rect : qregion)
276 wl_region_add(region, rect.x(), rect.y(), rect.width(), rect.height());
277
278 return region;
279}
280
281::wl_subsurface *QWaylandDisplay::createSubSurface(QWaylandWindow *window, QWaylandWindow *parent)
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)
289 Q_ASSERT(parent->wlSurface());
290 Q_ASSERT(window->wlSurface());
291
292 return mGlobals.subCompositor->get_subsurface(window->wlSurface(), parent->wlSurface());
293}
294
295::wp_viewport *QWaylandDisplay::createViewport(QWaylandWindow *window)
296{
297 if (!mGlobals.viewporter) {
298 qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor.";
299 return nullptr;
300 }
301
302 Q_ASSERT(window->wlSurface());
303 return mGlobals.viewporter->get_viewport(window->wlSurface());
304}
305
306QWaylandShellIntegration *QWaylandDisplay::shellIntegration() const
307{
308 return mWaylandIntegration->shellIntegration();
309}
310
311QWaylandClientBufferIntegration * QWaylandDisplay::clientBufferIntegration() const
312{
313 return mWaylandIntegration->clientBufferIntegration();
314}
315
316QWaylandWindowManagerIntegration *QWaylandDisplay::windowManagerIntegration() const
317{
318 return mGlobals.windowManagerIntegration.get();
319}
320
321QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration)
322 : mWaylandIntegration(waylandIntegration)
323{
324 qRegisterMetaType<uint32_t>(typeName: "uint32_t");
325
326 mDisplay = wl_display_connect(nullptr);
327 if (mDisplay) {
328 setupConnection();
329 } else {
330 qErrnoWarning(errno, msg: "Failed to create wl_display");
331 }
332
333 mWaylandTryReconnect = qEnvironmentVariableIsSet(varName: "QT_WAYLAND_RECONNECT");
334 mPreferWlrDataControl = qEnvironmentVariableIntValue(varName: "QT_WAYLAND_USE_DATA_CONTROL") > 0;
335}
336
337void QWaylandDisplay::setupConnection()
338{
339 struct ::wl_registry *registry = wl_display_get_registry(mDisplay);
340 init(registry);
341
342#if QT_CONFIG(xkbcommon)
343 mXkbContext.reset(p: xkb_context_new(flags: XKB_CONTEXT_NO_FLAGS));
344 if (!mXkbContext)
345 qCWarning(lcQpaWayland, "failed to create xkb context");
346#endif
347 if (mWaylandInputContextRequested)
348 checkTextInputProtocol();
349}
350
351QWaylandDisplay::~QWaylandDisplay(void)
352{
353 if (m_eventThread)
354 m_eventThread->stop();
355
356 if (m_frameEventQueueThread)
357 m_frameEventQueueThread->stop();
358
359 if (mSyncCallback)
360 wl_callback_destroy(mSyncCallback);
361
362 qDeleteAll(c: std::exchange(obj&: mInputDevices, new_val: {}));
363
364 for (QWaylandScreen *screen : std::exchange(obj&: mScreens, new_val: {})) {
365 QWindowSystemInterface::handleScreenRemoved(screen);
366 }
367 qDeleteAll(c: mWaitingScreens);
368
369#if QT_CONFIG(cursor)
370 mCursorThemes.clear();
371#endif
372
373 if (m_frameEventQueue)
374 wl_event_queue_destroy(m_frameEventQueue);
375
376 // Reset the globals manually since they need to be destroyed before the wl_display
377 mGlobals = {};
378
379 if (object())
380 wl_registry_destroy(object());
381
382 if (mDisplay)
383 wl_display_disconnect(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.
388bool QWaylandDisplay::initialize()
389{
390 if (!isInitialized())
391 return false;
392
393 forceRoundTrip();
394
395 emit connected();
396
397 if (!mWaitingScreens.isEmpty()) {
398 // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
399 forceRoundTrip();
400 }
401 if (mWaylandInputContextRequested)
402 mTextInputManagerIndex = INT_MAX;
403
404 return qEnvironmentVariableIntValue(varName: "QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration();
405}
406
407void QWaylandDisplay::ensureScreen()
408{
409 if (!mScreens.empty() || mPlaceholderScreen)
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
414 mPlaceholderScreen = new QPlatformPlaceholderScreen();
415 QWindowSystemInterface::handleScreenAdded(screen: mPlaceholderScreen);
416 Q_ASSERT(!QGuiApplication::screens().empty());
417}
418
419void QWaylandDisplay::reconnect()
420{
421 qCWarning(lcQpaWayland) << "Attempting wayland reconnect";
422 m_eventThread->stop();
423 m_frameEventQueueThread->stop();
424 m_eventThread->wait();
425 m_frameEventQueueThread->wait();
426
427 qDeleteAll(c: mWaitingScreens);
428 mWaitingScreens.clear();
429
430 while (!mScreens.isEmpty()) {
431 auto screen = mScreens.takeLast();
432 ensureScreen();
433 QWindowSystemInterface::handleScreenRemoved(screen);
434 }
435
436 mCursorThemes.clear();
437 mCursor.reset();
438
439 mGlobals = GlobalHolder();
440
441 mWaylandIntegration->reset();
442
443 qDeleteAll(c: std::exchange(obj&: mInputDevices, new_val: {}));
444 mLastInputDevice = nullptr;
445
446 for (const RegistryGlobal &global : mRegistryGlobals) {
447 emit globalRemoved(global);
448 }
449 mRegistryGlobals.clear();
450
451 mLastInputSerial = 0;
452 mLastInputWindow.clear();
453 mLastKeyboardFocus.clear();
454 mActiveWindows.clear();
455
456 const auto windows = QGuiApplication::allWindows();
457 QList<QWaylandWindow *> allPlatformWindows;
458 for (auto window : windows) {
459 if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle())) {
460 waylandWindow->closeChildPopups();
461 allPlatformWindows.push_back(t: waylandWindow);
462 }
463 }
464
465 // Remove windows that do not need to be recreated and now closed popups
466 QList<QWaylandWindow *> recreateWindows;
467 for (auto window : std::as_const(t&: allPlatformWindows)) {
468 if (window->subSurfaceWindow() || window->shellSurface()) {
469 recreateWindows.push_back(t: window);
470 }
471 window->reset();
472 }
473
474 if (mSyncCallback) {
475 wl_callback_destroy(mSyncCallback);
476 mSyncCallback = nullptr;
477 }
478
479 if (object())
480 wl_registry_destroy(object());
481 mDisplay = wl_display_connect(nullptr);
482 if (!mDisplay)
483 _exit(status: 1);
484
485 connect(
486 this, &QWaylandDisplay::connected, this,
487 [&allPlatformWindows] {
488 for (auto &window : std::as_const(t&: allPlatformWindows)) {
489 window->initializeWlSurface();
490 }
491 },
492 Qt::SingleShotConnection);
493
494 setupConnection();
495 initialize();
496
497 if (m_frameEventQueue)
498 wl_event_queue_destroy(m_frameEventQueue);
499 initEventThread();
500
501 auto needsRecreate = [](QPlatformWindow *window) {
502 auto waylandWindow = static_cast<QWaylandWindow *>(window);
503 return waylandWindow && !waylandWindow->subSurfaceWindow() && !waylandWindow->shellSurface();
504 };
505 auto window = recreateWindows.begin();
506 while (!recreateWindows.isEmpty()) {
507 if (!needsRecreate((*window)->QPlatformWindow::parent()) && !needsRecreate((*window)->transientParent())) {
508 (*window)->reinit();
509 window = recreateWindows.erase(pos: window);
510 } else {
511 ++window;
512 }
513 if (window == recreateWindows.end())
514 window = recreateWindows.begin();
515 }
516
517 mWaylandIntegration->reconfigureInputContext();
518}
519
520void QWaylandDisplay::flushRequests()
521{
522 m_eventThread->readAndDispatchEvents();
523 QWindowSystemInterface::flushWindowSystemEvents(flags: QWaylandEventDispatcher::eventDispatcher->flags());
524}
525
526// We have to wait until we have an eventDispatcher before creating the eventThread,
527// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
528// polling.
529void QWaylandDisplay::initEventThread()
530{
531 m_eventThread.reset(
532 p: new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch));
533 connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this,
534 &QWaylandDisplay::flushRequests, Qt::QueuedConnection);
535 connect(m_eventThread.get(), &EventThread::waylandError, this,
536 &QWaylandDisplay::checkWaylandError, Qt::QueuedConnection);
537 m_eventThread->start();
538
539 // wl_display_disconnect() free this.
540 m_frameEventQueue = wl_display_create_queue(mDisplay);
541 m_frameEventQueueThread.reset(
542 new EventThread(mDisplay, m_frameEventQueue, EventThread::SelfDispatch));
543 m_frameEventQueueThread->start();
544}
545
546void QWaylandDisplay::checkWaylandError()
547{
548 int ecode = wl_display_get_error(mDisplay);
549 if ((ecode == EPIPE || ecode == ECONNRESET)) {
550 qWarning(msg: "The Wayland connection broke. Did the Wayland compositor die?");
551 if (mWaylandTryReconnect) {
552 reconnect();
553 return;
554 }
555 } else {
556 qWarning(msg: "The Wayland connection experienced a fatal error: %s", strerror(errnum: ecode));
557 }
558 _exit(status: -1);
559}
560
561void QWaylandDisplay::checkTextInputProtocol()
562{
563 QStringList tips, timps; // for text input protocols and text input manager protocols
564 // zwp_text_input_v2 is preferred over zwp_text_input_v3 because:
565 // - Currently, v3 is not as feature rich as v2.
566 // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma
567 // Mobile uses some v2 only.
568 tips << QLatin1String(QtWayland::qt_text_input_method_v1::interface()->name)
569 << QLatin1String(QtWayland::zwp_text_input_v2::interface()->name)
570 << QLatin1String(QtWayland::zwp_text_input_v3::interface()->name)
571 << QLatin1String(QtWayland::zwp_text_input_v1::interface()->name);
572 timps << QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)
573 << QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)
574 << QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)
575 << QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name);
576
577 QString tiProtocols = QString::fromLocal8Bit(ba: qgetenv(varName: "QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
578 qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
579 QStringList keys;
580 if (!tiProtocols.isEmpty()) {
581 keys = tiProtocols.split(sep: QLatin1Char(';'));
582 QList<QString>::iterator it = keys.begin();
583 while (it != keys.end()) {
584 if (tips.contains(str: *it))
585 mTextInputManagerList.append(t: timps.at(i: tips.indexOf(str: *it)));
586 else
587 qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it;
588 ++it;
589 }
590 }
591 if (mTextInputManagerList.isEmpty()) // fallback
592 mTextInputManagerList = timps;
593}
594
595QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
596{
597 for (auto screen : std::as_const(t: mScreens)) {
598 if (screen->output() == output)
599 return screen;
600 }
601 return nullptr;
602}
603
604void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen)
605{
606 if (!mWaitingScreens.removeOne(t: screen))
607 return;
608 mScreens.append(t: screen);
609 QWindowSystemInterface::handleScreenAdded(screen);
610 if (mPlaceholderScreen) {
611 // handleScreenRemoved deletes the platform screen
612 QPlatformScreen *s = mPlaceholderScreen;
613 mPlaceholderScreen = nullptr;
614 QWindowSystemInterface::handleScreenRemoved(screen: s);
615
616 }
617}
618
619template <typename T, auto f>
620struct WithDestructor : public T
621{
622 using T::T;
623 ~WithDestructor()
624 {
625 f(this->object());
626 }
627};
628
629void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uint32_t version)
630{
631 struct ::wl_registry *registry = object();
632
633 static QStringList interfaceBlacklist = qEnvironmentVariable(varName: "QT_WAYLAND_DISABLED_INTERFACES").split(sep: u',');
634 if (interfaceBlacklist.contains(str: interface)) {
635 return;
636 }
637
638 if (interface == QLatin1String(QtWayland::wl_output::interface()->name)) {
639 mWaitingScreens << mWaylandIntegration->createPlatformScreen(waylandDisplay: this, version, id);
640 } else if (interface == QLatin1String(QtWayland::wl_compositor::interface()->name)) {
641 mGlobals.compositor.reset(
642 new WithDestructor<QtWayland::wl_compositor, wl_compositor_destroy>(
643 registry, id, qMin((int)version, 6)));
644 } else if (interface == QLatin1String(QWaylandShm::interface()->name)) {
645 mGlobals.shm.reset(new QWaylandShm(this, version, id));
646 } else if (interface == QLatin1String(QWaylandInputDevice::interface()->name)) {
647 QWaylandInputDevice *inputDevice = mWaylandIntegration->createInputDevice(display: this, version, id);
648 mInputDevices.append(t: inputDevice);
649#if QT_CONFIG(wayland_datadevice)
650 } else if (interface == QLatin1String(QWaylandDataDeviceManager::interface()->name)) {
651 mGlobals.dndSelectionHandler.reset(new QWaylandDataDeviceManager(this, version, id));
652#endif
653 } else if (interface == QLatin1String(QtWayland::wl_subcompositor::interface()->name)) {
654 mGlobals.subCompositor.reset(
655 new WithDestructor<QtWayland::wl_subcompositor, wl_subcompositor_destroy>(registry,
656 id, 1));
657#if QT_CONFIG(tabletevent)
658 } else if (interface == QLatin1String(QWaylandTabletManagerV2::interface()->name)) {
659 mGlobals.tabletManager.reset(new QWaylandTabletManagerV2(this, id, qMin(1, int(version))));
660 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
661 inputDevice->setTabletSeat(
662 new QWaylandTabletSeatV2(mGlobals.tabletManager.get(), inputDevice));
663#endif
664 } else if (interface == QLatin1String(QWaylandPointerGestures::interface()->name)) {
665 mGlobals.pointerGestures.reset(new QWaylandPointerGestures(this, id, 1));
666#if QT_CONFIG(wayland_client_primary_selection)
667 } else if (interface == QLatin1String(QWaylandPrimarySelectionDeviceManagerV1::interface()->name)) {
668 mGlobals.primarySelectionManager.reset(
669 new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
670 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
671 inputDevice->setPrimarySelectionDevice(
672 mGlobals.primarySelectionManager->createDevice(inputDevice));
673#endif
674 } else if (interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)
675 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
676 qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
677 if (mTextInputManagerIndex < INT_MAX) {
678 mGlobals.textInputManagerv1.reset();
679 mGlobals.textInputManagerv2.reset();
680 mGlobals.textInputManagerv3.reset();
681 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
682 inputDevice->setTextInput(nullptr);
683 }
684
685 mGlobals.textInputMethodManager.reset(
686 new WithDestructor<QtWayland::qt_text_input_method_manager_v1,
687 qt_text_input_method_manager_v1_destroy>(registry, id, 1));
688 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
689 inputDevice->setTextInputMethod(new QWaylandTextInputMethod(
690 this,
691 mGlobals.textInputMethodManager->get_text_input_method(
692 inputDevice->wl_seat())));
693 mWaylandIntegration->reconfigureInputContext();
694 mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface);
695 } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)
696 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
697 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1";
698 if (mTextInputManagerIndex < INT_MAX) {
699 mGlobals.textInputMethodManager.reset();
700 mGlobals.textInputManagerv2.reset();
701 mGlobals.textInputManagerv3.reset();
702 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
703 inputDevice->setTextInputMethod(nullptr);
704 }
705
706 mGlobals.textInputManagerv1.reset(
707 new WithDestructor<QtWayland::zwp_text_input_manager_v1,
708 zwp_text_input_manager_v1_destroy>(registry, id, 1));
709 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) {
710 auto textInput =
711 new QWaylandTextInputv1(this, mGlobals.textInputManagerv1->create_text_input());
712 textInput->setSeat(inputDevice->wl_seat());
713 inputDevice->setTextInput(textInput);
714 }
715
716 mWaylandIntegration->reconfigureInputContext();
717 mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface);
718 } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)
719 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
720 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
721 if (mTextInputManagerIndex < INT_MAX) {
722 mGlobals.textInputMethodManager.reset();
723 mGlobals.textInputManagerv1.reset();
724 mGlobals.textInputManagerv3.reset();
725 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
726 inputDevice->setTextInputMethod(nullptr);
727 }
728
729 mGlobals.textInputManagerv2.reset(
730 new WithDestructor<QtWayland::zwp_text_input_manager_v2,
731 zwp_text_input_manager_v2_destroy>(registry, id, 1));
732 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
733 inputDevice->setTextInput(new QWaylandTextInputv2(
734 this, mGlobals.textInputManagerv2->get_text_input(inputDevice->wl_seat())));
735 mWaylandIntegration->reconfigureInputContext();
736 mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface);
737 } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)
738 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
739 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3";
740 if (mTextInputManagerIndex < INT_MAX) {
741 mGlobals.textInputMethodManager.reset();
742 mGlobals.textInputManagerv2.reset();
743 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
744 inputDevice->setTextInputMethod(nullptr);
745 }
746 mGlobals.textInputManagerv3.reset(
747 new WithDestructor<QtWayland::zwp_text_input_manager_v3,
748 zwp_text_input_manager_v3_destroy>(registry, id, 1));
749 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
750 inputDevice->setTextInput(new QWaylandTextInputv3(
751 this, mGlobals.textInputManagerv3->get_text_input(inputDevice->wl_seat())));
752
753 mWaylandIntegration->reconfigureInputContext();
754 mTextInputManagerIndex = mTextInputManagerList.indexOf(str: interface);
755 }else if (interface == QLatin1String(QWaylandHardwareIntegration::interface()->name)) {
756 bool disableHardwareIntegration = qEnvironmentVariableIntValue(varName: "QT_WAYLAND_DISABLE_HW_INTEGRATION");
757 if (!disableHardwareIntegration) {
758 mGlobals.hardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id));
759 // make a roundtrip here since we need to receive the events sent by
760 // qt_hardware_integration before creating windows
761 forceRoundTrip();
762 }
763 } else if (interface == QLatin1String(QWaylandXdgOutputManagerV1::interface()->name)) {
764 mGlobals.xdgOutputManager.reset(new QWaylandXdgOutputManagerV1(this, id, version));
765 for (auto *screen : std::as_const(t&: mWaitingScreens))
766 screen->initXdgOutput(xdgOutputManager: xdgOutputManager());
767 } else if (interface == QLatin1String(QtWayland::wp_fractional_scale_manager_v1::interface()->name)) {
768 mGlobals.fractionalScaleManager.reset(
769 new WithDestructor<QtWayland::wp_fractional_scale_manager_v1,
770 wp_fractional_scale_manager_v1_destroy>(registry, id, 1));
771 } else if (interface == QLatin1String("wp_viewporter")) {
772 mGlobals.viewporter.reset(
773 new WithDestructor<QtWayland::wp_viewporter, wp_viewporter_destroy>(
774 registry, id, qMin(1u, version)));
775 } else if (interface == QLatin1String(QtWayland::wp_cursor_shape_manager_v1::interface()->name)) {
776 mGlobals.cursorShapeManager.reset(new WithDestructor<QtWayland::wp_cursor_shape_manager_v1,
777 wp_cursor_shape_manager_v1_destroy>(
778 registry, id, std::min(1u, version)));
779 } else if (
780 interface == QLatin1String(QtWayland::xdg_toplevel_drag_manager_v1::interface()->name)) {
781 mGlobals.xdgToplevelDragManager.reset(
782 new WithDestructor<QtWayland::xdg_toplevel_drag_manager_v1,
783 xdg_toplevel_drag_manager_v1_destroy>(registry, id, 1));
784 } else if (interface == QLatin1String(QtWayland::qt_windowmanager::interface()->name)) {
785 mGlobals.windowManagerIntegration.reset(
786 new QWaylandWindowManagerIntegration(this, id, version));
787 } else if (interface == QLatin1String(QtWayland::xdg_system_bell_v1::interface()->name)) {
788 mGlobals.systemBell.reset(new WithDestructor<QtWayland::xdg_system_bell_v1, xdg_system_bell_v1_destroy>(registry, id, 1));
789 } else if (
790 interface == QLatin1String(QtWayland::org_kde_kwin_appmenu_manager::interface()->name)) {
791 mGlobals.appMenuManager.reset(new QWaylandAppMenuManager(registry, id, 1));
792#if QT_CONFIG(clipboard)
793 } else if (mPreferWlrDataControl && interface == QLatin1String(QWaylandDataControlManagerV1::interface()->name)) {
794 mGlobals.dataControlManager.reset(new QWaylandDataControlManagerV1(this, id, 2));
795 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices)) {
796 inputDevice->setDataControlDevice(mGlobals.dataControlManager->createDevice(inputDevice));
797 }
798#endif
799 } else if (interface == QLatin1String(QtWayland::xx_color_manager_v4::interface()->name)) {
800 mGlobals.colorManager = std::make_unique<ColorManager>(registry, id, 1);
801 // we need a roundtrip to receive the features the compositor supports
802 forceRoundTrip();
803 } else if (interface == QLatin1String(QtWayland::wp_pointer_warp_v1::interface()->name)) {
804 mGlobals.pointerWarp.reset(new WithDestructor<QtWayland::wp_pointer_warp_v1, wp_pointer_warp_v1_destroy>(
805 registry, id, 1));
806 }
807
808
809 mRegistryGlobals.append(t: RegistryGlobal(id, interface, version, registry));
810 emit globalAdded(global: mRegistryGlobals.back());
811
812 const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
813 for (Listener l : copy)
814 (*l.listener)(l.data, registry, id, interface, version);
815}
816
817void QWaylandDisplay::registry_global_remove(uint32_t id)
818{
819 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
820 RegistryGlobal &global = mRegistryGlobals[i];
821 if (global.id == id) {
822 if (global.interface == QLatin1String(QtWayland::wl_output::interface()->name)) {
823 for (auto *screen : mWaitingScreens) {
824 if (screen->outputId() == id) {
825 mWaitingScreens.removeOne(t: screen);
826 delete screen;
827 break;
828 }
829 }
830
831 for (QWaylandScreen *screen : std::as_const(t&: mScreens)) {
832 if (screen->outputId() == id) {
833 mScreens.removeOne(t: screen);
834 // If this is the last screen, we have to add a fake screen, or Qt will break.
835 ensureScreen();
836 QWindowSystemInterface::handleScreenRemoved(screen);
837 break;
838 }
839 }
840 }
841 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)) {
842 mGlobals.textInputManagerv1.reset();
843 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
844 inputDevice->setTextInput(nullptr);
845 mWaylandIntegration->reconfigureInputContext();
846 }
847 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)) {
848 mGlobals.textInputManagerv2.reset();
849 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
850 inputDevice->setTextInput(nullptr);
851 mWaylandIntegration->reconfigureInputContext();
852 }
853 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)) {
854 mGlobals.textInputManagerv3.reset();
855 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
856 inputDevice->setTextInput(nullptr);
857 mWaylandIntegration->reconfigureInputContext();
858 }
859 if (global.interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)) {
860 mGlobals.textInputMethodManager.reset();
861 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
862 inputDevice->setTextInputMethod(nullptr);
863 mWaylandIntegration->reconfigureInputContext();
864 }
865#if QT_CONFIG(wayland_client_primary_selection)
866 if (global.interface == QLatin1String(QtWayland::zwp_primary_selection_device_manager_v1::interface()->name)) {
867 mGlobals.primarySelectionManager.reset();
868 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
869 inputDevice->setPrimarySelectionDevice(nullptr);
870 }
871#endif
872#if QT_CONFIG(clipboard)
873 if (global.interface == QLatin1String(QtWayland::zwlr_data_control_manager_v1::interface()->name)) {
874 mGlobals.dataControlManager.reset();
875 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
876 inputDevice->setDataControlDevice(nullptr);
877 }
878#endif
879 emit globalRemoved(global: mRegistryGlobals.takeAt(i));
880 break;
881 }
882 }
883}
884
885bool QWaylandDisplay::hasRegistryGlobal(QStringView interfaceName) const
886{
887 for (const RegistryGlobal &global : mRegistryGlobals)
888 if (global.interface == interfaceName)
889 return true;
890
891 return false;
892}
893
894void QWaylandDisplay::addRegistryListener(RegistryListener listener, void *data)
895{
896 Listener l = { listener, data };
897 mRegistryListeners.append(t: l);
898 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i)
899 (*l.listener)(l.data, mRegistryGlobals[i].registry, mRegistryGlobals[i].id,
900 mRegistryGlobals[i].interface, mRegistryGlobals[i].version);
901}
902
903void QWaylandDisplay::removeListener(RegistryListener listener, void *data)
904{
905 auto iter = std::remove_if(first: mRegistryListeners.begin(), last: mRegistryListeners.end(), pred: [=](Listener l){
906 return (l.listener == listener && l.data == data);
907 });
908 mRegistryListeners.erase(abegin: iter, aend: mRegistryListeners.end());
909}
910
911void QWaylandDisplay::forceRoundTrip()
912{
913 wl_display_roundtrip(mDisplay);
914}
915
916bool QWaylandDisplay::supportsWindowDecoration() const
917{
918 static bool disabled = qgetenv(varName: "QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt();
919 // Stop early when disabled via the environment. Do not try to load the integration in
920 // order to play nice with SHM-only, buffer integration-less systems.
921 if (disabled)
922 return false;
923
924 // Don't initialize client buffer integration just to check whether it can have a decoration.
925 if (!mWaylandIntegration->mClientBufferIntegrationInitialized)
926 return true;
927
928 // We can do software-rendered decorations, only disable them if the integration explicitly says it can't.
929 static bool integrationSupport = !clientBufferIntegration() || clientBufferIntegration()->supportsWindowDecoration();
930 return integrationSupport;
931}
932
933QWaylandWindow *QWaylandDisplay::lastInputWindow() const
934{
935 return mLastInputWindow.data();
936}
937
938void QWaylandDisplay::setLastInputDevice(QWaylandInputDevice *device, uint32_t serial, QWaylandWindow *win)
939{
940 mLastInputDevice = device;
941 mLastInputSerial = serial;
942 mLastInputWindow = win;
943}
944
945bool QWaylandDisplay::isWindowActivated(const QWaylandWindow *window)
946{
947 return mActiveWindows.contains(t: const_cast<QWaylandWindow *>(window));
948}
949
950void QWaylandDisplay::handleWindowActivated(QWaylandWindow *window)
951{
952 if (mActiveWindows.contains(t: window))
953 return;
954
955 mActiveWindows.append(t: window);
956 requestWaylandSync();
957
958 if (auto *decoration = window->decoration())
959 decoration->update();
960}
961
962void QWaylandDisplay::handleWindowDeactivated(QWaylandWindow *window)
963{
964 Q_ASSERT(!mActiveWindows.empty());
965
966 if (mActiveWindows.last() == window)
967 requestWaylandSync();
968
969 mActiveWindows.removeOne(t: window);
970
971 if (QCoreApplication::closingDown())
972 return;
973
974 if (auto *decoration = window->decoration())
975 decoration->update();
976}
977
978void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice)
979{
980 QWaylandWindow *keyboardFocus = inputDevice->keyboardFocus();
981
982 if (mLastKeyboardFocus == keyboardFocus)
983 return;
984
985 if (keyboardFocus)
986 handleWindowActivated(window: keyboardFocus);
987 if (mLastKeyboardFocus)
988 handleWindowDeactivated(window: mLastKeyboardFocus);
989
990 mLastKeyboardFocus = keyboardFocus;
991}
992
993void QWaylandDisplay::handleWindowDestroyed(QWaylandWindow *window)
994{
995 if (mActiveWindows.contains(t: window))
996 handleWindowDeactivated(window);
997}
998
999void QWaylandDisplay::handleWaylandSync()
1000{
1001 // This callback is used to set the window activation because we may get an activate/deactivate
1002 // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the
1003 // handleWindowActivated() calls immediately.
1004 QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window();
1005 if (activeWindow != QGuiApplication::focusWindow())
1006 QWindowSystemInterface::handleFocusWindowChanged(window: activeWindow);
1007
1008 if (!activeWindow) {
1009 if (lastInputDevice()) {
1010#if QT_CONFIG(clipboard)
1011 if (auto *dataDevice = lastInputDevice()->dataDevice())
1012 dataDevice->invalidateSelectionOffer();
1013#endif
1014#if QT_CONFIG(wayland_client_primary_selection)
1015 if (auto *device = lastInputDevice()->primarySelectionDevice())
1016 device->invalidateSelectionOffer();
1017#endif
1018 }
1019 }
1020}
1021
1022const wl_callback_listener QWaylandDisplay::syncCallbackListener = {
1023 [](void *data, struct wl_callback *callback, uint32_t time){
1024 Q_UNUSED(time);
1025 wl_callback_destroy(callback);
1026 QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data);
1027 display->mSyncCallback = nullptr;
1028 display->handleWaylandSync();
1029 }
1030};
1031
1032void QWaylandDisplay::requestWaylandSync()
1033{
1034 if (mSyncCallback)
1035 return;
1036
1037 mSyncCallback = wl_display_sync(mDisplay);
1038 wl_callback_add_listener(mSyncCallback, &syncCallbackListener, this);
1039}
1040
1041QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const
1042{
1043 return mInputDevices.isEmpty() ? nullptr : mInputDevices.first();
1044}
1045
1046bool QWaylandDisplay::isKeyboardAvailable() const
1047{
1048 return std::any_of(
1049 first: mInputDevices.constBegin(), last: mInputDevices.constEnd(),
1050 pred: [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
1051}
1052
1053bool QWaylandDisplay::isWaylandInputContextRequested() const {
1054 return mWaylandInputContextRequested;
1055}
1056
1057#if QT_CONFIG(cursor)
1058
1059QWaylandCursor *QWaylandDisplay::waylandCursor()
1060{
1061 if (!mCursor)
1062 mCursor.reset(other: mWaylandIntegration->createPlatformCursor(display: this));
1063 return mCursor.data();
1064}
1065
1066auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept
1067 -> FindExistingCursorThemeResult
1068{
1069 const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) {
1070 return std::tie(args: lhs.pixelSize, args: lhs.name) < std::tie(args: rhs.pixelSize, args: rhs.name);
1071 };
1072
1073 const WaylandCursorTheme prototype = {.name: name, .pixelSize: pixelSize, .theme: nullptr};
1074
1075 const auto it = std::lower_bound(first: mCursorThemes.cbegin(), last: mCursorThemes.cend(), val: prototype, comp: byNameAndSize);
1076 if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize)
1077 return {.position: it, .found: true};
1078 else
1079 return {.position: it, .found: false};
1080}
1081
1082QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize)
1083{
1084 const auto result = findExistingCursorTheme(name, pixelSize);
1085 if (result.found)
1086 return result.theme();
1087
1088 if (auto theme = QWaylandCursorTheme::create(shm: shm(), size: pixelSize, themeName: name))
1089 return mCursorThemes.insert(position: result.position, x: {.name: name, .pixelSize: pixelSize, .theme: std::move(theme)})->theme.get();
1090
1091 return nullptr;
1092}
1093
1094#endif // QT_CONFIG(cursor)
1095
1096} // namespace QtWaylandClient
1097
1098QT_END_NAMESPACE
1099
1100#include "qwaylanddisplay.moc"
1101#include "moc_qwaylanddisplay_p.cpp"
1102

source code of qtbase/src/plugins/platforms/wayland/qwaylanddisplay.cpp