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

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