1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qwaylanddisplay_p.h"
5
6#include "qwaylandintegration_p.h"
7#include "qwaylandwindow_p.h"
8#include "qwaylandsurface_p.h"
9#include "qwaylandabstractdecoration_p.h"
10#include "qwaylandscreen_p.h"
11#include "qwaylandcursor_p.h"
12#include "qwaylandinputdevice_p.h"
13#if QT_CONFIG(clipboard)
14#include "qwaylandclipboard_p.h"
15#endif
16#if QT_CONFIG(wayland_datadevice)
17#include "qwaylanddatadevicemanager_p.h"
18#include "qwaylanddatadevice_p.h"
19#endif // QT_CONFIG(wayland_datadevice)
20#if QT_CONFIG(wayland_client_primary_selection)
21#include "qwaylandprimaryselectionv1_p.h"
22#endif // QT_CONFIG(wayland_client_primary_selection)
23#if QT_CONFIG(cursor)
24#include <wayland-cursor.h>
25#endif
26#include "qwaylandhardwareintegration_p.h"
27#include "qwaylandtextinputv1_p.h"
28#include "qwaylandtextinputv2_p.h"
29#include "qwaylandtextinputv3_p.h"
30#include "qwaylandinputcontext_p.h"
31#include "qwaylandinputmethodcontext_p.h"
32
33#include "qwaylandwindowmanagerintegration_p.h"
34#include "qwaylandshellintegration_p.h"
35#include "qwaylandclientbufferintegration_p.h"
36
37#include "qwaylandextendedsurface_p.h"
38#include "qwaylandpointergestures_p.h"
39#include "qwaylandsubsurface_p.h"
40#include "qwaylandtouch_p.h"
41#if QT_CONFIG(tabletevent)
42#include "qwaylandtabletv2_p.h"
43#endif
44#include "qwaylandqtkey_p.h"
45
46#include <QtWaylandClient/private/qwayland-text-input-unstable-v1.h>
47#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
48#include <QtWaylandClient/private/qwayland-text-input-unstable-v3.h>
49#include <QtWaylandClient/private/qwayland-wp-primary-selection-unstable-v1.h>
50#include <QtWaylandClient/private/qwayland-qt-text-input-method-unstable-v1.h>
51#include <QtWaylandClient/private/qwayland-fractional-scale-v1.h>
52#include <QtWaylandClient/private/qwayland-viewporter.h>
53#include <QtWaylandClient/private/qwayland-cursor-shape-v1.h>
54#include <QtWaylandClient/private/qwayland-xdg-toplevel-drag-v1.h>
55
56#include <QtCore/private/qcore_unix_p.h>
57
58#include <QtCore/QAbstractEventDispatcher>
59#include <QtGui/qpa/qwindowsysteminterface.h>
60#include <QtGui/private/qguiapplication_p.h>
61
62#include <QtCore/QDebug>
63
64#include <errno.h>
65
66#include <tuple> // for std::tie
67
68QT_BEGIN_NAMESPACE
69
70namespace QtWaylandClient {
71
72class EventThread : public QThread
73{
74 Q_OBJECT
75public:
76 enum OperatingMode {
77 EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread.
78 SelfDispatch, // Dispatch the events inside this thread.
79 };
80
81 EventThread(struct wl_display * wl, struct wl_event_queue * ev_queue,
82 OperatingMode mode)
83 : m_fd(wl_display_get_fd(wl))
84 , m_pipefd{ -1, -1 }
85 , m_wldisplay(wl)
86 , m_wlevqueue(ev_queue)
87 , m_mode(mode)
88 , m_reading(true)
89 , m_quitting(false)
90 {
91 setObjectName(QStringLiteral("WaylandEventThread"));
92 }
93
94 void readAndDispatchEvents()
95 {
96 /*
97 * Dispatch pending events and flush the requests at least once. If the event thread
98 * is not reading, try to call _prepare_read() to allow the event thread to poll().
99 * If that fails, re-try dispatch & flush again until _prepare_read() is successful.
100 *
101 * This allow any call to readAndDispatchEvents() to start event thread's polling,
102 * not only the one issued from event thread's waitForReading(), which means functions
103 * called from dispatch_pending() can safely spin an event loop.
104 */
105 if (m_quitting)
106 return;
107
108 for (;;) {
109 if (dispatchQueuePending() < 0) {
110 Q_EMIT waylandError();
111 m_quitting = true;
112 return;
113 }
114
115 wl_display_flush(m_wldisplay);
116
117 // We have to check if event thread is reading every time we dispatch
118 // something, as that may recursively call this function.
119 if (m_reading.loadAcquire())
120 break;
121
122 if (prepareReadQueue() == 0) {
123 QMutexLocker l(&m_mutex);
124 m_reading.storeRelease(newValue: true);
125 m_cond.wakeOne();
126 break;
127 }
128 }
129 }
130
131 void stop()
132 {
133 // We have to both write to the pipe and set the flag, as the thread may be
134 // either in the poll() or waiting for _prepare_read().
135 if (m_pipefd[1] != -1 && write(fd: m_pipefd[1], buf: "\0", n: 1) == -1)
136 qWarning(msg: "Failed to write to the pipe: %s.", strerror(errno));
137
138 {
139 QMutexLocker l(&m_mutex);
140 m_quitting = true;
141 m_cond.wakeOne();
142 }
143
144 wait();
145 }
146
147Q_SIGNALS:
148 void needReadAndDispatch();
149 void waylandError();
150
151protected:
152 void run() override
153 {
154 // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets
155 // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore.
156 struct Pipe
157 {
158 Pipe(int *fds)
159 : fds(fds)
160 {
161 if (qt_safe_pipe(pipefd: fds) != 0)
162 qWarning(msg: "Pipe creation failed. Quitting may hang.");
163 }
164 ~Pipe()
165 {
166 if (fds[0] != -1) {
167 close(fd: fds[0]);
168 close(fd: fds[1]);
169 }
170 }
171
172 int *fds;
173 } pipe(m_pipefd);
174
175 // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the
176 // outbound ones. Wait until it's done before proceeding, unless we're told to quit.
177 while (waitForReading()) {
178 if (!m_reading.loadRelaxed())
179 break;
180
181 pollfd fds[2] = { { .fd: m_fd, POLLIN, .revents: 0 }, { .fd: m_pipefd[0], POLLIN, .revents: 0 } };
182 poll(fds: fds, nfds: 2, timeout: -1);
183
184 if (fds[1].revents & POLLIN) {
185 // we don't really care to read the byte that was written here since we're closing down
186 wl_display_cancel_read(m_wldisplay);
187 break;
188 }
189
190 if (fds[0].revents & POLLIN)
191 wl_display_read_events(m_wldisplay);
192 // The polll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop
193 // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which
194 // case we don't care anymore about them.
195 else
196 wl_display_cancel_read(m_wldisplay);
197 }
198 }
199
200private:
201 bool waitForReading()
202 {
203 Q_ASSERT(QThread::currentThread() == this);
204
205 m_reading.storeRelease(newValue: false);
206
207 if (m_mode == SelfDispatch) {
208 readAndDispatchEvents();
209 } else {
210 Q_EMIT needReadAndDispatch();
211
212 QMutexLocker lock(&m_mutex);
213 // m_reading might be set from our emit or some other invocation of
214 // readAndDispatchEvents().
215 while (!m_reading.loadRelaxed() && !m_quitting)
216 m_cond.wait(lockedMutex: &m_mutex);
217 }
218
219 return !m_quitting;
220 }
221
222 int dispatchQueuePending()
223 {
224 if (m_wlevqueue)
225 return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue);
226 else
227 return wl_display_dispatch_pending(m_wldisplay);
228 }
229
230 int prepareReadQueue()
231 {
232 if (m_wlevqueue)
233 return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue);
234 else
235 return wl_display_prepare_read(m_wldisplay);
236 }
237
238 int m_fd;
239 int m_pipefd[2];
240 wl_display *m_wldisplay;
241 wl_event_queue *m_wlevqueue;
242 OperatingMode m_mode;
243
244 /* Concurrency note when operating in EmitToDispatch mode:
245 * m_reading is set to false inside event thread's waitForReading(), and is
246 * set to true inside main thread's readAndDispatchEvents().
247 * The lock is not taken when setting m_reading to false, as the main thread
248 * is not actively waiting for it to turn false. However, the lock is taken
249 * inside readAndDispatchEvents() before setting m_reading to true,
250 * as the event thread is actively waiting for it under the wait condition.
251 */
252
253 QAtomicInteger<bool> m_reading;
254 bool m_quitting;
255 QMutex m_mutex;
256 QWaitCondition m_cond;
257};
258
259Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging
260
261struct wl_surface *QWaylandDisplay::createSurface(void *handle)
262{
263 struct wl_surface *surface = mGlobals.compositor->create_surface();
264 wl_surface_set_user_data(surface, handle);
265 return surface;
266}
267
268struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
269{
270 struct ::wl_region *region = mGlobals.compositor->create_region();
271
272 for (const QRect &rect : qregion)
273 wl_region_add(region, rect.x(), rect.y(), rect.width(), rect.height());
274
275 return region;
276}
277
278::wl_subsurface *QWaylandDisplay::createSubSurface(QWaylandWindow *window, QWaylandWindow *parent)
279{
280 if (!mGlobals.subCompositor) {
281 qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor.";
282 return nullptr;
283 }
284
285 // Make sure we don't pass NULL surfaces to libwayland (crashes)
286 Q_ASSERT(parent->wlSurface());
287 Q_ASSERT(window->wlSurface());
288
289 return mGlobals.subCompositor->get_subsurface(window->wlSurface(), parent->wlSurface());
290}
291
292::wp_viewport *QWaylandDisplay::createViewport(QWaylandWindow *window)
293{
294 if (!mGlobals.viewporter) {
295 qCWarning(lcQpaWayland) << "Can't create wp_viewport, not supported by the compositor.";
296 return nullptr;
297 }
298
299 Q_ASSERT(window->wlSurface());
300 return mGlobals.viewporter->get_viewport(window->wlSurface());
301}
302
303QWaylandShellIntegration *QWaylandDisplay::shellIntegration() const
304{
305 return mWaylandIntegration->shellIntegration();
306}
307
308QWaylandClientBufferIntegration * QWaylandDisplay::clientBufferIntegration() const
309{
310 return mWaylandIntegration->clientBufferIntegration();
311}
312
313QWaylandWindowManagerIntegration *QWaylandDisplay::windowManagerIntegration() const
314{
315 return mGlobals.windowManagerIntegration.get();
316}
317
318QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration)
319 : mWaylandIntegration(waylandIntegration)
320{
321 qRegisterMetaType<uint32_t>(typeName: "uint32_t");
322
323 mDisplay = wl_display_connect(nullptr);
324 if (mDisplay) {
325 setupConnection();
326 } else {
327 qErrnoWarning(errno, msg: "Failed to create wl_display");
328 }
329
330 mWaylandTryReconnect = qEnvironmentVariableIsSet(varName: "QT_WAYLAND_RECONNECT");
331}
332
333void QWaylandDisplay::setupConnection()
334{
335 struct ::wl_registry *registry = wl_display_get_registry(mDisplay);
336 init(registry);
337
338#if QT_CONFIG(xkbcommon)
339 mXkbContext.reset(p: xkb_context_new(flags: XKB_CONTEXT_NO_FLAGS));
340 if (!mXkbContext)
341 qCWarning(lcQpaWayland, "failed to create xkb context");
342#endif
343 if (mWaylandInputContextRequested)
344 checkTextInputProtocol();
345}
346
347QWaylandDisplay::~QWaylandDisplay(void)
348{
349 if (m_eventThread)
350 m_eventThread->stop();
351
352 if (m_frameEventQueueThread)
353 m_frameEventQueueThread->stop();
354
355 if (mSyncCallback)
356 wl_callback_destroy(mSyncCallback);
357
358 qDeleteAll(c: std::exchange(obj&: mInputDevices, new_val: {}));
359
360 for (QWaylandScreen *screen : std::exchange(obj&: mScreens, new_val: {})) {
361 QWindowSystemInterface::handleScreenRemoved(screen);
362 }
363 qDeleteAll(c: mWaitingScreens);
364
365#if QT_CONFIG(cursor)
366 mCursorThemes.clear();
367#endif
368
369 if (m_frameEventQueue)
370 wl_event_queue_destroy(m_frameEventQueue);
371
372 // Reset the globals manually since they need to be destroyed before the wl_display
373 mGlobals = {};
374
375 if (object())
376 wl_registry_destroy(object());
377
378 if (mDisplay)
379 wl_display_disconnect(mDisplay);
380}
381
382// Steps which is called just after constructor. This separates registry_global() out of the constructor
383// so that factory functions in integration can be overridden.
384bool QWaylandDisplay::initialize()
385{
386 if (!isInitialized())
387 return false;
388
389 forceRoundTrip();
390
391 emit connected();
392
393 if (!mWaitingScreens.isEmpty()) {
394 // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
395 forceRoundTrip();
396 }
397 if (mWaylandInputContextRequested)
398 mTextInputManagerIndex = INT_MAX;
399
400 return qEnvironmentVariableIntValue(varName: "QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration();
401}
402
403void QWaylandDisplay::ensureScreen()
404{
405 if (!mScreens.empty() || mPlaceholderScreen)
406 return; // There are real screens or we already have a fake one
407
408 qCInfo(lcQpaWayland) << "Creating a fake screen in order for Qt not to crash";
409
410 mPlaceholderScreen = new QPlatformPlaceholderScreen();
411 QWindowSystemInterface::handleScreenAdded(screen: mPlaceholderScreen);
412 Q_ASSERT(!QGuiApplication::screens().empty());
413}
414
415void QWaylandDisplay::reconnect()
416{
417 qCWarning(lcQpaWayland) << "Attempting wayland reconnect";
418 m_eventThread->stop();
419 m_frameEventQueueThread->stop();
420 m_eventThread->wait();
421 m_frameEventQueueThread->wait();
422
423 qDeleteAll(c: mWaitingScreens);
424 mWaitingScreens.clear();
425
426 while (!mScreens.isEmpty()) {
427 auto screen = mScreens.takeLast();
428 ensureScreen();
429 QWindowSystemInterface::handleScreenRemoved(screen);
430 }
431
432 mCursorThemes.clear();
433 mCursor.reset();
434
435 mGlobals = GlobalHolder();
436
437 mWaylandIntegration->reset();
438
439 qDeleteAll(c: std::exchange(obj&: mInputDevices, new_val: {}));
440 mLastInputDevice = nullptr;
441
442 for (const RegistryGlobal &global : mRegistryGlobals) {
443 emit globalRemoved(global);
444 }
445 mRegistryGlobals.clear();
446
447 mLastInputSerial = 0;
448 mLastInputWindow.clear();
449 mLastKeyboardFocus.clear();
450 mActiveWindows.clear();
451
452 const auto windows = QGuiApplication::allWindows();
453 for (auto window : windows) {
454 if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle()))
455 waylandWindow->closeChildPopups();
456 }
457 // Remove windows that do not need to be recreated and now closed popups
458 QList<QWaylandWindow *> recreateWindows;
459 for (auto window : std::as_const(t: windows)) {
460 auto waylandWindow = static_cast<QWaylandWindow*>(window->handle());
461 if (waylandWindow && waylandWindow->wlSurface()) {
462 waylandWindow->reset();
463 recreateWindows.push_back(t: waylandWindow);
464 }
465 }
466
467 if (mSyncCallback) {
468 wl_callback_destroy(mSyncCallback);
469 mSyncCallback = nullptr;
470 }
471
472 if (object())
473 wl_registry_destroy(object());
474 mDisplay = wl_display_connect(nullptr);
475 if (!mDisplay)
476 _exit(status: 1);
477
478 setupConnection();
479 initialize();
480
481 if (m_frameEventQueue)
482 wl_event_queue_destroy(m_frameEventQueue);
483 initEventThread();
484
485 auto needsRecreate = [](QPlatformWindow *window) {
486 return window && !static_cast<QWaylandWindow *>(window)->wlSurface();
487 };
488 auto window = recreateWindows.begin();
489 while (!recreateWindows.isEmpty()) {
490 if (!needsRecreate((*window)->QPlatformWindow::parent()) && !needsRecreate((*window)->transientParent())) {
491 (*window)->reinit();
492 window = recreateWindows.erase(pos: window);
493 } else {
494 ++window;
495 }
496 if (window == recreateWindows.end())
497 window = recreateWindows.begin();
498 }
499
500 mWaylandIntegration->reconfigureInputContext();
501}
502
503void QWaylandDisplay::flushRequests()
504{
505 m_eventThread->readAndDispatchEvents();
506}
507
508// We have to wait until we have an eventDispatcher before creating the eventThread,
509// otherwise forceRoundTrip() may block inside _events_read() because eventThread is
510// polling.
511void QWaylandDisplay::initEventThread()
512{
513 m_eventThread.reset(
514 p: new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch));
515 connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this,
516 &QWaylandDisplay::flushRequests, Qt::QueuedConnection);
517 connect(m_eventThread.get(), &EventThread::waylandError, this,
518 &QWaylandDisplay::checkWaylandError, Qt::QueuedConnection);
519 m_eventThread->start();
520
521 // wl_display_disconnect() free this.
522 m_frameEventQueue = wl_display_create_queue(mDisplay);
523 m_frameEventQueueThread.reset(
524 new EventThread(mDisplay, m_frameEventQueue, EventThread::SelfDispatch));
525 m_frameEventQueueThread->start();
526}
527
528void QWaylandDisplay::checkWaylandError()
529{
530 int ecode = wl_display_get_error(mDisplay);
531 if ((ecode == EPIPE || ecode == ECONNRESET)) {
532 qWarning(msg: "The Wayland connection broke. Did the Wayland compositor die?");
533 if (mWaylandTryReconnect) {
534 reconnect();
535 return;
536 }
537 } else {
538 qWarning(msg: "The Wayland connection experienced a fatal error: %s", strerror(errnum: ecode));
539 }
540 _exit(status: -1);
541}
542
543void QWaylandDisplay::blockingReadEvents()
544{
545 if (wl_display_dispatch(mDisplay) < 0) {
546 int ecode = wl_display_get_error(mDisplay);
547 if ((ecode == EPIPE || ecode == ECONNRESET))
548 qWarning(msg: "The Wayland connection broke during blocking read event. Did the Wayland compositor die?");
549 else
550 qWarning(msg: "The Wayland connection experienced a fatal error during blocking read event: %s", strerror(errnum: ecode));
551 _exit(status: -1);
552 }
553}
554
555void QWaylandDisplay::checkTextInputProtocol()
556{
557 QStringList tips, timps; // for text input protocols and text input manager protocols
558 // zwp_text_input_v2 is preferred over zwp_text_input_v3 because:
559 // - Currently, v3 is not as feature rich as v2.
560 // - While v2 is not upstreamed, it is well supported by KWin since Plasma 5 and Plasma
561 // Mobile uses some v2 only.
562 tips << QLatin1String(QtWayland::qt_text_input_method_v1::interface()->name)
563 << QLatin1String(QtWayland::zwp_text_input_v2::interface()->name)
564 << QLatin1String(QtWayland::zwp_text_input_v3::interface()->name)
565 << QLatin1String(QtWayland::zwp_text_input_v1::interface()->name);
566 timps << QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)
567 << QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)
568 << QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)
569 << QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name);
570
571 QString tiProtocols = QString::fromLocal8Bit(ba: qgetenv(varName: "QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
572 qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
573 QStringList keys;
574 if (!tiProtocols.isEmpty()) {
575 keys = tiProtocols.split(sep: QLatin1Char(';'));
576 QList<QString>::iterator it = keys.begin();
577 while (it != keys.end()) {
578 if (tips.contains(str: *it))
579 mTextInputManagerList.append(t: timps.at(i: tips.indexOf(str: *it)));
580 else
581 qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it;
582 ++it;
583 }
584 }
585 if (mTextInputManagerList.isEmpty()) // fallback
586 mTextInputManagerList = timps;
587}
588
589QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
590{
591 for (auto screen : std::as_const(t: mScreens)) {
592 if (screen->output() == output)
593 return screen;
594 }
595 return nullptr;
596}
597
598void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen)
599{
600 if (!mWaitingScreens.removeOne(t: screen))
601 return;
602 mScreens.append(t: screen);
603 QWindowSystemInterface::handleScreenAdded(screen);
604 if (mPlaceholderScreen) {
605 QWindowSystemInterface::handleScreenRemoved(screen: mPlaceholderScreen);
606 // handleScreenRemoved deletes the platform screen
607 mPlaceholderScreen = nullptr;
608 }
609}
610
611template <typename T, auto f>
612struct WithDestructor : public T
613{
614 using T::T;
615 ~WithDestructor()
616 {
617 f(this->object());
618 }
619};
620
621void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uint32_t version)
622{
623 struct ::wl_registry *registry = object();
624
625 static QStringList interfaceBlacklist = qEnvironmentVariable(varName: "QT_WAYLAND_DISABLED_INTERFACES").split(sep: u',');
626 if (interfaceBlacklist.contains(str: interface)) {
627 return;
628 }
629
630 if (interface == QLatin1String(QtWayland::wl_output::interface()->name)) {
631 mWaitingScreens << mWaylandIntegration->createPlatformScreen(waylandDisplay: this, version, id);
632 } else if (interface == QLatin1String(QtWayland::wl_compositor::interface()->name)) {
633 mGlobals.compositor.reset(
634 new WithDestructor<QtWayland::wl_compositor, wl_compositor_destroy>(
635 registry, id, qMin((int)version, 6)));
636 } else if (interface == QLatin1String(QWaylandShm::interface()->name)) {
637 mGlobals.shm.reset(new QWaylandShm(this, version, id));
638 } else if (interface == QLatin1String(QWaylandInputDevice::interface()->name)) {
639 QWaylandInputDevice *inputDevice = mWaylandIntegration->createInputDevice(display: this, version, id);
640 mInputDevices.append(t: inputDevice);
641#if QT_CONFIG(wayland_datadevice)
642 } else if (interface == QLatin1String(QWaylandDataDeviceManager::interface()->name)) {
643 mGlobals.dndSelectionHandler.reset(new QWaylandDataDeviceManager(this, version, id));
644#endif
645 } else if (interface == QLatin1String(QtWayland::qt_surface_extension::interface()->name)) {
646 mGlobals.surfaceExtension.reset(
647 new WithDestructor<QtWayland::qt_surface_extension, qt_surface_extension_destroy>(
648 registry, id, 1));
649 } else if (interface == QLatin1String(QtWayland::wl_subcompositor::interface()->name)) {
650 mGlobals.subCompositor.reset(
651 new WithDestructor<QtWayland::wl_subcompositor, wl_subcompositor_destroy>(registry,
652 id, 1));
653 } else if (interface == QLatin1String(QWaylandTouchExtension::interface()->name)) {
654 mGlobals.touchExtension.reset(new QWaylandTouchExtension(this, id));
655 } else if (interface == QLatin1String(QWaylandQtKeyExtension::interface()->name)) {
656 mGlobals.qtKeyExtension.reset(new QWaylandQtKeyExtension(this, id));
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 }
788
789 mRegistryGlobals.append(t: RegistryGlobal(id, interface, version, registry));
790 emit globalAdded(global: mRegistryGlobals.back());
791
792 const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
793 for (Listener l : copy)
794 (*l.listener)(l.data, registry, id, interface, version);
795}
796
797void QWaylandDisplay::registry_global_remove(uint32_t id)
798{
799 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
800 RegistryGlobal &global = mRegistryGlobals[i];
801 if (global.id == id) {
802 if (global.interface == QLatin1String(QtWayland::wl_output::interface()->name)) {
803 for (auto *screen : mWaitingScreens) {
804 if (screen->outputId() == id) {
805 mWaitingScreens.removeOne(t: screen);
806 delete screen;
807 break;
808 }
809 }
810
811 for (QWaylandScreen *screen : std::as_const(t&: mScreens)) {
812 if (screen->outputId() == id) {
813 mScreens.removeOne(t: screen);
814 // If this is the last screen, we have to add a fake screen, or Qt will break.
815 ensureScreen();
816 QWindowSystemInterface::handleScreenRemoved(screen);
817 break;
818 }
819 }
820 }
821 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)) {
822 mGlobals.textInputManagerv1.reset();
823 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
824 inputDevice->setTextInput(nullptr);
825 mWaylandIntegration->reconfigureInputContext();
826 }
827 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)) {
828 mGlobals.textInputManagerv2.reset();
829 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
830 inputDevice->setTextInput(nullptr);
831 mWaylandIntegration->reconfigureInputContext();
832 }
833 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)) {
834 mGlobals.textInputManagerv3.reset();
835 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
836 inputDevice->setTextInput(nullptr);
837 mWaylandIntegration->reconfigureInputContext();
838 }
839 if (global.interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)) {
840 mGlobals.textInputMethodManager.reset();
841 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
842 inputDevice->setTextInputMethod(nullptr);
843 mWaylandIntegration->reconfigureInputContext();
844 }
845#if QT_CONFIG(wayland_client_primary_selection)
846 if (global.interface == QLatin1String(QtWayland::zwp_primary_selection_device_manager_v1::interface()->name)) {
847 mGlobals.primarySelectionManager.reset();
848 for (QWaylandInputDevice *inputDevice : std::as_const(t&: mInputDevices))
849 inputDevice->setPrimarySelectionDevice(nullptr);
850 }
851#endif
852 emit globalRemoved(global: mRegistryGlobals.takeAt(i));
853 break;
854 }
855 }
856}
857
858bool QWaylandDisplay::hasRegistryGlobal(QStringView interfaceName) const
859{
860 for (const RegistryGlobal &global : mRegistryGlobals)
861 if (global.interface == interfaceName)
862 return true;
863
864 return false;
865}
866
867void QWaylandDisplay::addRegistryListener(RegistryListener listener, void *data)
868{
869 Listener l = { listener, data };
870 mRegistryListeners.append(t: l);
871 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i)
872 (*l.listener)(l.data, mRegistryGlobals[i].registry, mRegistryGlobals[i].id,
873 mRegistryGlobals[i].interface, mRegistryGlobals[i].version);
874}
875
876void QWaylandDisplay::removeListener(RegistryListener listener, void *data)
877{
878 auto iter = std::remove_if(first: mRegistryListeners.begin(), last: mRegistryListeners.end(), pred: [=](Listener l){
879 return (l.listener == listener && l.data == data);
880 });
881 mRegistryListeners.erase(abegin: iter, aend: mRegistryListeners.end());
882}
883
884void QWaylandDisplay::forceRoundTrip()
885{
886 wl_display_roundtrip(mDisplay);
887}
888
889bool QWaylandDisplay::supportsWindowDecoration() const
890{
891 static bool disabled = qgetenv(varName: "QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt();
892 // Stop early when disabled via the environment. Do not try to load the integration in
893 // order to play nice with SHM-only, buffer integration-less systems.
894 if (disabled)
895 return false;
896
897 // Don't initialize client buffer integration just to check whether it can have a decoration.
898 if (!mWaylandIntegration->mClientBufferIntegrationInitialized)
899 return true;
900
901 // We can do software-rendered decorations, only disable them if the integration explicitly says it can't.
902 static bool integrationSupport = !clientBufferIntegration() || clientBufferIntegration()->supportsWindowDecoration();
903 return integrationSupport;
904}
905
906QWaylandWindow *QWaylandDisplay::lastInputWindow() const
907{
908 return mLastInputWindow.data();
909}
910
911void QWaylandDisplay::setLastInputDevice(QWaylandInputDevice *device, uint32_t serial, QWaylandWindow *win)
912{
913 mLastInputDevice = device;
914 mLastInputSerial = serial;
915 mLastInputWindow = win;
916}
917
918bool QWaylandDisplay::isWindowActivated(const QWaylandWindow *window)
919{
920 return mActiveWindows.contains(t: const_cast<QWaylandWindow *>(window));
921}
922
923void QWaylandDisplay::handleWindowActivated(QWaylandWindow *window)
924{
925 if (mActiveWindows.contains(t: window))
926 return;
927
928 mActiveWindows.append(t: window);
929 requestWaylandSync();
930
931 if (auto *decoration = window->decoration())
932 decoration->update();
933}
934
935void QWaylandDisplay::handleWindowDeactivated(QWaylandWindow *window)
936{
937 Q_ASSERT(!mActiveWindows.empty());
938
939 if (mActiveWindows.last() == window)
940 requestWaylandSync();
941
942 mActiveWindows.removeOne(t: window);
943
944 if (QCoreApplication::closingDown())
945 return;
946
947 if (auto *decoration = window->decoration())
948 decoration->update();
949}
950
951void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice)
952{
953 QWaylandWindow *keyboardFocus = inputDevice->keyboardFocus();
954
955 if (mLastKeyboardFocus == keyboardFocus)
956 return;
957
958 if (keyboardFocus)
959 handleWindowActivated(window: keyboardFocus);
960 if (mLastKeyboardFocus)
961 handleWindowDeactivated(window: mLastKeyboardFocus);
962
963 mLastKeyboardFocus = keyboardFocus;
964}
965
966void QWaylandDisplay::handleWindowDestroyed(QWaylandWindow *window)
967{
968 if (mActiveWindows.contains(t: window))
969 handleWindowDeactivated(window);
970}
971
972void QWaylandDisplay::handleWaylandSync()
973{
974 // This callback is used to set the window activation because we may get an activate/deactivate
975 // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the
976 // handleWindowActivated() calls immediately.
977 QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window();
978 if (activeWindow != QGuiApplication::focusWindow())
979 QWindowSystemInterface::handleFocusWindowChanged(window: activeWindow);
980
981 if (!activeWindow) {
982 if (lastInputDevice()) {
983#if QT_CONFIG(clipboard)
984 if (auto *dataDevice = lastInputDevice()->dataDevice())
985 dataDevice->invalidateSelectionOffer();
986#endif
987#if QT_CONFIG(wayland_client_primary_selection)
988 if (auto *device = lastInputDevice()->primarySelectionDevice())
989 device->invalidateSelectionOffer();
990#endif
991 }
992 }
993}
994
995const wl_callback_listener QWaylandDisplay::syncCallbackListener = {
996 [](void *data, struct wl_callback *callback, uint32_t time){
997 Q_UNUSED(time);
998 wl_callback_destroy(callback);
999 QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data);
1000 display->mSyncCallback = nullptr;
1001 display->handleWaylandSync();
1002 }
1003};
1004
1005void QWaylandDisplay::requestWaylandSync()
1006{
1007 if (mSyncCallback)
1008 return;
1009
1010 mSyncCallback = wl_display_sync(mDisplay);
1011 wl_callback_add_listener(mSyncCallback, &syncCallbackListener, this);
1012}
1013
1014QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const
1015{
1016 return mInputDevices.isEmpty() ? nullptr : mInputDevices.first();
1017}
1018
1019bool QWaylandDisplay::isKeyboardAvailable() const
1020{
1021 return std::any_of(
1022 first: mInputDevices.constBegin(), last: mInputDevices.constEnd(),
1023 pred: [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
1024}
1025
1026bool QWaylandDisplay::isWaylandInputContextRequested() const {
1027 return mWaylandInputContextRequested;
1028}
1029
1030#if QT_CONFIG(cursor)
1031
1032QWaylandCursor *QWaylandDisplay::waylandCursor()
1033{
1034 if (!mCursor)
1035 mCursor.reset(other: mWaylandIntegration->createPlatformCursor(display: this));
1036 return mCursor.data();
1037}
1038
1039auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept
1040 -> FindExistingCursorThemeResult
1041{
1042 const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) {
1043 return std::tie(args: lhs.pixelSize, args: lhs.name) < std::tie(args: rhs.pixelSize, args: rhs.name);
1044 };
1045
1046 const WaylandCursorTheme prototype = {.name: name, .pixelSize: pixelSize, .theme: nullptr};
1047
1048 const auto it = std::lower_bound(first: mCursorThemes.cbegin(), last: mCursorThemes.cend(), val: prototype, comp: byNameAndSize);
1049 if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize)
1050 return {.position: it, .found: true};
1051 else
1052 return {.position: it, .found: false};
1053}
1054
1055QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize)
1056{
1057 const auto result = findExistingCursorTheme(name, pixelSize);
1058 if (result.found)
1059 return result.theme();
1060
1061 if (auto theme = QWaylandCursorTheme::create(shm: shm(), size: pixelSize, themeName: name))
1062 return mCursorThemes.insert(position: result.position, x: {.name: name, .pixelSize: pixelSize, .theme: std::move(theme)})->theme.get();
1063
1064 return nullptr;
1065}
1066
1067#endif // QT_CONFIG(cursor)
1068
1069} // namespace QtWaylandClient
1070
1071QT_END_NAMESPACE
1072
1073#include "qwaylanddisplay.moc"
1074#include "moc_qwaylanddisplay_p.cpp"
1075

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtwayland/src/client/qwaylanddisplay.cpp