Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwaylanddisplay.cpp
Go to the documentation of this file.
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
7#include "qwaylandwindow_p.h"
8#include "qwaylandsurface_p.h"
10#include "qwaylandscreen_p.h"
11#include "qwaylandcursor_p.h"
13#if QT_CONFIG(clipboard)
14#include "qwaylandclipboard_p.h"
15#endif
16#if QT_CONFIG(wayland_datadevice)
19#endif // QT_CONFIG(wayland_datadevice)
20#if QT_CONFIG(wayland_client_primary_selection)
22#endif // QT_CONFIG(wayland_client_primary_selection)
23#if QT_CONFIG(cursor)
24#include <wayland-cursor.h>
25#endif
32
36
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
69
70namespace QtWaylandClient {
71
72class EventThread : public QThread
73{
75public:
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,
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
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) {
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(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(m_pipefd[1], "\0", 1) == -1)
136 qWarning("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
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(fds) != 0)
162 qWarning("Pipe creation failed. Quitting may hang.");
163 }
164 ~Pipe()
165 {
166 if (fds[0] != -1) {
167 close(fds[0]);
168 close(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] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } };
182 poll(fds, 2, -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 {
204
205 m_reading.storeRelease(false);
206
207 if (m_mode == SelfDispatch) {
209 } else {
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(&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
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
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
304{
305 return mWaylandIntegration->shellIntegration();
306}
307
312
314{
315 return mGlobals.windowManagerIntegration.get();
316}
317
319 : mWaylandIntegration(waylandIntegration)
320{
321 qRegisterMetaType<uint32_t>("uint32_t");
322
323 mDisplay = wl_display_connect(nullptr);
324 if (mDisplay) {
325 setupConnection();
326 } else {
327 qErrnoWarning(errno, "Failed to create wl_display");
328 }
329
330 mWaylandTryReconnect = qEnvironmentVariableIsSet("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(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
340 if (!mXkbContext)
341 qCWarning(lcQpaWayland, "failed to create xkb context");
342#endif
343 if (mWaylandInputContextRequested)
344 checkTextInputProtocol();
345}
346
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(std::exchange(mInputDevices, {}));
359
360 for (QWaylandScreen *screen : std::exchange(mScreens, {})) {
362 }
363 qDeleteAll(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.
385{
386 if (!isInitialized())
387 return false;
388
390
391 emit connected();
392
393 if (!mWaitingScreens.isEmpty()) {
394 // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
396 }
397 if (mWaylandInputContextRequested)
398 mTextInputManagerIndex = INT_MAX;
399
400 return qEnvironmentVariableIntValue("QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration();
401}
402
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();
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(mWaitingScreens);
424 mWaitingScreens.clear();
425
426 while (!mScreens.isEmpty()) {
427 auto screen = mScreens.takeLast();
428 ensureScreen();
430 }
431
432 mCursorThemes.clear();
433 mCursor.reset();
434
435 mGlobals = GlobalHolder();
436
437 mWaylandIntegration->reset();
438
439 qDeleteAll(std::exchange(mInputDevices, {}));
440 mLastInputDevice = nullptr;
441
442 for (const RegistryGlobal &global : mRegistryGlobals) {
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(windows)) {
460 auto waylandWindow = static_cast<QWaylandWindow*>(window->handle());
461 if (waylandWindow && waylandWindow->wlSurface()) {
462 waylandWindow->reset();
463 recreateWindows.push_back(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(1);
477
478 setupConnection();
479 initialize();
480
481 if (m_frameEventQueue)
482 wl_event_queue_destroy(m_frameEventQueue);
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(window);
493 } else {
494 ++window;
495 }
496 if (window == recreateWindows.end())
497 window = recreateWindows.begin();
498 }
499
500 mWaylandIntegration->reconfigureInputContext();
501}
502
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.
512{
513 m_eventThread.reset(
514 new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch));
515 connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this,
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("The Wayland connection broke. Did the Wayland compositor die?");
533 if (mWaylandTryReconnect) {
534 reconnect();
535 return;
536 }
537 } else {
538 qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode));
539 }
540 _exit(-1);
541}
542
544{
545 if (wl_display_dispatch(mDisplay) < 0) {
546 int ecode = wl_display_get_error(mDisplay);
547 if ((ecode == EPIPE || ecode == ECONNRESET))
548 qWarning("The Wayland connection broke during blocking read event. Did the Wayland compositor die?");
549 else
550 qWarning("The Wayland connection experienced a fatal error during blocking read event: %s", strerror(ecode));
551 _exit(-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(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
572 qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
574 if (!tiProtocols.isEmpty()) {
575 keys = tiProtocols.split(QLatin1Char(';'));
577 while (it != keys.end()) {
578 if (tips.contains(*it))
579 mTextInputManagerList.append(timps.at(tips.indexOf(*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
590{
591 for (auto screen : std::as_const(mScreens)) {
592 if (screen->output() == output)
593 return screen;
594 }
595 return nullptr;
596}
597
599{
600 if (!mWaitingScreens.removeOne(screen))
601 return;
602 mScreens.append(screen);
604 if (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;
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("QT_WAYLAND_DISABLED_INTERFACES").split(u',');
626 if (interfaceBlacklist.contains(interface)) {
627 return;
628 }
629
630 if (interface == QLatin1String(QtWayland::wl_output::interface()->name)) {
631 mWaitingScreens << mWaylandIntegration->createPlatformScreen(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(this, version, id);
640 mInputDevices.append(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#endif
661 } else if (interface == QLatin1String(QWaylandPointerGestures::interface()->name)) {
662 mGlobals.pointerGestures.reset(new QWaylandPointerGestures(this, id, 1));
663#if QT_CONFIG(wayland_client_primary_selection)
664 } else if (interface == QLatin1String(QWaylandPrimarySelectionDeviceManagerV1::interface()->name)) {
665 mGlobals.primarySelectionManager.reset(
667 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
668 inputDevice->setPrimarySelectionDevice(
669 mGlobals.primarySelectionManager->createDevice(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 mGlobals.textInputManagerv1.reset();
676 mGlobals.textInputManagerv2.reset();
677 mGlobals.textInputManagerv3.reset();
678 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
679 inputDevice->setTextInput(nullptr);
680 }
681
682 mGlobals.textInputMethodManager.reset(
683 new WithDestructor<QtWayland::qt_text_input_method_manager_v1,
684 qt_text_input_method_manager_v1_destroy>(registry, id, 1));
685 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
686 inputDevice->setTextInputMethod(new QWaylandTextInputMethod(
687 this,
688 mGlobals.textInputMethodManager->get_text_input_method(
689 inputDevice->wl_seat())));
690 mWaylandIntegration->reconfigureInputContext();
691 mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
692 } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)
693 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
694 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1";
695 if (mTextInputManagerIndex < INT_MAX) {
696 mGlobals.textInputMethodManager.reset();
697 mGlobals.textInputManagerv2.reset();
698 mGlobals.textInputManagerv3.reset();
699 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
700 inputDevice->setTextInputMethod(nullptr);
701 }
702
703 mGlobals.textInputManagerv1.reset(
704 new WithDestructor<QtWayland::zwp_text_input_manager_v1,
705 zwp_text_input_manager_v1_destroy>(registry, id, 1));
706 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) {
707 auto textInput =
708 new QWaylandTextInputv1(this, mGlobals.textInputManagerv1->create_text_input());
709 textInput->setSeat(inputDevice->wl_seat());
710 inputDevice->setTextInput(textInput);
711 }
712
713 mWaylandIntegration->reconfigureInputContext();
714 mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
715 } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)
716 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
717 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
718 if (mTextInputManagerIndex < INT_MAX) {
719 mGlobals.textInputMethodManager.reset();
720 mGlobals.textInputManagerv1.reset();
721 mGlobals.textInputManagerv3.reset();
722 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
723 inputDevice->setTextInputMethod(nullptr);
724 }
725
726 mGlobals.textInputManagerv2.reset(
727 new WithDestructor<QtWayland::zwp_text_input_manager_v2,
728 zwp_text_input_manager_v2_destroy>(registry, id, 1));
729 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
730 inputDevice->setTextInput(new QWaylandTextInputv2(
731 this, mGlobals.textInputManagerv2->get_text_input(inputDevice->wl_seat())));
732 mWaylandIntegration->reconfigureInputContext();
733 mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
734 } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)
735 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
736 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v3";
737 if (mTextInputManagerIndex < INT_MAX) {
738 mGlobals.textInputMethodManager.reset();
739 mGlobals.textInputManagerv2.reset();
740 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
741 inputDevice->setTextInputMethod(nullptr);
742 }
743 mGlobals.textInputManagerv3.reset(
744 new WithDestructor<QtWayland::zwp_text_input_manager_v3,
745 zwp_text_input_manager_v3_destroy>(registry, id, 1));
746 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
747 inputDevice->setTextInput(new QWaylandTextInputv3(
748 this, mGlobals.textInputManagerv3->get_text_input(inputDevice->wl_seat())));
749
750 mWaylandIntegration->reconfigureInputContext();
751 mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
752 }else if (interface == QLatin1String(QWaylandHardwareIntegration::interface()->name)) {
753 bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
754 if (!disableHardwareIntegration) {
755 mGlobals.hardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id));
756 // make a roundtrip here since we need to receive the events sent by
757 // qt_hardware_integration before creating windows
759 }
761 mGlobals.xdgOutputManager.reset(new QWaylandXdgOutputManagerV1(this, id, version));
762 for (auto *screen : std::as_const(mWaitingScreens))
763 screen->initXdgOutput(xdgOutputManager());
764 } else if (interface == QLatin1String(QtWayland::wp_fractional_scale_manager_v1::interface()->name)) {
765 mGlobals.fractionalScaleManager.reset(
766 new WithDestructor<QtWayland::wp_fractional_scale_manager_v1,
767 wp_fractional_scale_manager_v1_destroy>(registry, id, 1));
768 } else if (interface == QLatin1String("wp_viewporter")) {
769 mGlobals.viewporter.reset(
770 new WithDestructor<QtWayland::wp_viewporter, wp_viewporter_destroy>(
771 registry, id, qMin(1u, version)));
772 } else if (interface == QLatin1String(QtWayland::wp_cursor_shape_manager_v1::interface()->name)) {
773 mGlobals.cursorShapeManager.reset(new WithDestructor<QtWayland::wp_cursor_shape_manager_v1,
774 wp_cursor_shape_manager_v1_destroy>(
775 registry, id, std::min(1u, version)));
776 } else if (
777 interface == QLatin1String(QtWayland::xdg_toplevel_drag_manager_v1::interface()->name)) {
778 mGlobals.xdgToplevelDragManager.reset(
779 new WithDestructor<QtWayland::xdg_toplevel_drag_manager_v1,
780 xdg_toplevel_drag_manager_v1_destroy>(registry, id, 1));
781 } else if (interface == QLatin1String(QtWayland::qt_windowmanager::interface()->name)) {
782 mGlobals.windowManagerIntegration.reset(
783 new QWaylandWindowManagerIntegration(this, id, version));
784 }
785
786 mRegistryGlobals.append(RegistryGlobal(id, interface, version, registry));
787 emit globalAdded(mRegistryGlobals.back());
788
789 const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
790 for (Listener l : copy)
791 (*l.listener)(l.data, registry, id, interface, version);
792}
793
795{
796 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
797 RegistryGlobal &global = mRegistryGlobals[i];
798 if (global.id == id) {
799 if (global.interface == QLatin1String(QtWayland::wl_output::interface()->name)) {
800 for (auto *screen : mWaitingScreens) {
801 if (screen->outputId() == id) {
802 mWaitingScreens.removeOne(screen);
803 delete screen;
804 break;
805 }
806 }
807
808 for (QWaylandScreen *screen : std::as_const(mScreens)) {
809 if (screen->outputId() == id) {
810 mScreens.removeOne(screen);
811 // If this is the last screen, we have to add a fake screen, or Qt will break.
812 ensureScreen();
814 break;
815 }
816 }
817 }
818 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)) {
819 mGlobals.textInputManagerv1.reset();
820 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
821 inputDevice->setTextInput(nullptr);
822 mWaylandIntegration->reconfigureInputContext();
823 }
824 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)) {
825 mGlobals.textInputManagerv2.reset();
826 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
827 inputDevice->setTextInput(nullptr);
828 mWaylandIntegration->reconfigureInputContext();
829 }
830 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v3::interface()->name)) {
831 mGlobals.textInputManagerv3.reset();
832 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
833 inputDevice->setTextInput(nullptr);
834 mWaylandIntegration->reconfigureInputContext();
835 }
836 if (global.interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)) {
837 mGlobals.textInputMethodManager.reset();
838 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
839 inputDevice->setTextInputMethod(nullptr);
840 mWaylandIntegration->reconfigureInputContext();
841 }
842#if QT_CONFIG(wayland_client_primary_selection)
843 if (global.interface == QLatin1String(QtWayland::zwp_primary_selection_device_manager_v1::interface()->name)) {
844 mGlobals.primarySelectionManager.reset();
845 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
846 inputDevice->setPrimarySelectionDevice(nullptr);
847 }
848#endif
849 emit globalRemoved(mRegistryGlobals.takeAt(i));
850 break;
851 }
852 }
853}
854
856{
857 for (const RegistryGlobal &global : mRegistryGlobals)
858 if (global.interface == interfaceName)
859 return true;
860
861 return false;
862}
863
865{
866 Listener l = { listener, data };
867 mRegistryListeners.append(l);
868 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i)
869 (*l.listener)(l.data, mRegistryGlobals[i].registry, mRegistryGlobals[i].id,
870 mRegistryGlobals[i].interface, mRegistryGlobals[i].version);
871}
872
874{
875 auto iter = std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){
876 return (l.listener == listener && l.data == data);
877 });
878 mRegistryListeners.erase(iter, mRegistryListeners.end());
879}
880
882{
883 wl_display_roundtrip(mDisplay);
884}
885
887{
888 static bool disabled = qgetenv("QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt();
889 // Stop early when disabled via the environment. Do not try to load the integration in
890 // order to play nice with SHM-only, buffer integration-less systems.
891 if (disabled)
892 return false;
893
894 // Don't initialize client buffer integration just to check whether it can have a decoration.
895 if (!mWaylandIntegration->mClientBufferIntegrationInitialized)
896 return true;
897
898 // We can do software-rendered decorations, only disable them if the integration explicitly says it can't.
899 static bool integrationSupport = !clientBufferIntegration() || clientBufferIntegration()->supportsWindowDecoration();
900 return integrationSupport;
901}
902
904{
905 return mLastInputWindow.data();
906}
907
909{
910 mLastInputDevice = device;
911 mLastInputSerial = serial;
912 mLastInputWindow = win;
913}
914
916{
917 return mActiveWindows.contains(const_cast<QWaylandWindow *>(window));
918}
919
921{
922 if (mActiveWindows.contains(window))
923 return;
924
925 mActiveWindows.append(window);
926 requestWaylandSync();
927
928 if (auto *decoration = window->decoration())
929 decoration->update();
930}
931
933{
934 Q_ASSERT(!mActiveWindows.empty());
935
936 if (mActiveWindows.last() == window)
937 requestWaylandSync();
938
939 mActiveWindows.removeOne(window);
940
942 return;
943
944 if (auto *decoration = window->decoration())
945 decoration->update();
946}
947
949{
950 QWaylandWindow *keyboardFocus = inputDevice->keyboardFocus();
951
952 if (mLastKeyboardFocus == keyboardFocus)
953 return;
954
955 if (keyboardFocus)
956 handleWindowActivated(keyboardFocus);
957 if (mLastKeyboardFocus)
958 handleWindowDeactivated(mLastKeyboardFocus);
959
960 mLastKeyboardFocus = keyboardFocus;
961}
962
968
969void QWaylandDisplay::handleWaylandSync()
970{
971 // This callback is used to set the window activation because we may get an activate/deactivate
972 // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the
973 // handleWindowActivated() calls immediately.
974 QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window();
975 if (activeWindow != QGuiApplication::focusWindow())
977
978 if (!activeWindow) {
979 if (lastInputDevice()) {
980#if QT_CONFIG(clipboard)
981 if (auto *dataDevice = lastInputDevice()->dataDevice())
982 dataDevice->invalidateSelectionOffer();
983#endif
984#if QT_CONFIG(wayland_client_primary_selection)
985 if (auto *device = lastInputDevice()->primarySelectionDevice())
986 device->invalidateSelectionOffer();
987#endif
988 }
989 }
990}
991
992const wl_callback_listener QWaylandDisplay::syncCallbackListener = {
993 [](void *data, struct wl_callback *callback, uint32_t time){
994 Q_UNUSED(time);
995 wl_callback_destroy(callback);
996 QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data);
997 display->mSyncCallback = nullptr;
998 display->handleWaylandSync();
999 }
1000};
1001
1002void QWaylandDisplay::requestWaylandSync()
1003{
1004 if (mSyncCallback)
1005 return;
1006
1007 mSyncCallback = wl_display_sync(mDisplay);
1008 wl_callback_add_listener(mSyncCallback, &syncCallbackListener, this);
1009}
1010
1012{
1013 return mInputDevices.isEmpty() ? nullptr : mInputDevices.first();
1014}
1015
1017{
1018 return std::any_of(
1019 mInputDevices.constBegin(), mInputDevices.constEnd(),
1020 [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
1021}
1022
1024 return mWaylandInputContextRequested;
1025}
1026
1027#if QT_CONFIG(cursor)
1028
1029QWaylandCursor *QWaylandDisplay::waylandCursor()
1030{
1031 if (!mCursor)
1032 mCursor.reset(mWaylandIntegration->createPlatformCursor(this));
1033 return mCursor.data();
1034}
1035
1036auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept
1037 -> FindExistingCursorThemeResult
1038{
1039 const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) {
1040 return std::tie(lhs.pixelSize, lhs.name) < std::tie(rhs.pixelSize, rhs.name);
1041 };
1042
1043 const WaylandCursorTheme prototype = {name, pixelSize, nullptr};
1044
1045 const auto it = std::lower_bound(mCursorThemes.cbegin(), mCursorThemes.cend(), prototype, byNameAndSize);
1046 if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize)
1047 return {it, true};
1048 else
1049 return {it, false};
1050}
1051
1052QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize)
1053{
1054 const auto result = findExistingCursorTheme(name, pixelSize);
1055 if (result.found)
1056 return result.theme();
1057
1058 if (auto theme = QWaylandCursorTheme::create(shm(), pixelSize, name))
1059 return mCursorThemes.insert(result.position, {name, pixelSize, std::move(theme)})->theme.get();
1060
1061 return nullptr;
1062}
1063
1064#endif // QT_CONFIG(cursor)
1065
1066} // namespace QtWaylandClient
1067
1069
1070#include "qwaylanddisplay.moc"
1071#include "moc_qwaylanddisplay_p.cpp"
IOBluetoothDevice * device
T loadAcquire() const noexcept
void storeRelease(T newValue) noexcept
T loadRelaxed() const noexcept
static bool closingDown()
Returns true if the application objects are being destroyed; otherwise returns false.
static QWindowList allWindows()
Returns a list of all the windows in the application.
static QWindow * focusWindow()
Returns the QWindow that receives events tied to focus, such as key events.
static QList< QScreen * > screens()
Returns a list of all the screens associated with the windowing system the application is connected t...
iterator erase(const_iterator begin, const_iterator end)
Definition qlist.h:889
iterator end()
Definition qlist.h:626
iterator begin()
Definition qlist.h:625
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:346
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
Definition qobject.h:127
The QPlatformWindow class provides an abstraction for top-level windows.
\inmodule QtCore\reentrant
Definition qrect.h:30
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
const_iterator cend() const noexcept
Definition qset.h:142
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5949
static QThread * currentThread()
Definition qthread.cpp:1039
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
Definition qthread.cpp:1023
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
static const wl_interface * interface()
Returns the Wayland interface for QWaylandXdgOutputManagerV1.
static void handleFocusWindowChanged(QWindow *window, Qt::FocusReason r=Qt::OtherFocusReason)
static void handleScreenAdded(QPlatformScreen *screen, bool isPrimary=false)
Should be called by the implementation whenever a new screen is added.
static void handleScreenRemoved(QPlatformScreen *screen)
Should be called by the implementation whenever a screen is removed.
\inmodule QtGui
Definition qwindow.h:63
EventThread(struct wl_display *wl, struct wl_event_queue *ev_queue, OperatingMode mode)
void removeListener(RegistryListener listener, void *data)
struct wl_surface * createSurface(void *handle)
QWaylandScreen * screenForOutput(struct wl_output *output) const
void globalAdded(const RegistryGlobal &global)
QWaylandClientBufferIntegration * clientBufferIntegration() const
struct::wl_region * createRegion(const QRegion &qregion)
QWaylandWindow * lastInputWindow() const
QWaylandDisplay(QWaylandIntegration *waylandIntegration)
struct::wp_viewport * createViewport(QWaylandWindow *window)
void handleWindowActivated(QWaylandWindow *window)
struct::wl_subsurface * createSubSurface(QWaylandWindow *window, QWaylandWindow *parent)
void handleWindowDestroyed(QWaylandWindow *window)
void registry_global(uint32_t id, const QString &interface, uint32_t version) override
bool isWindowActivated(const QWaylandWindow *window)
QWaylandShellIntegration * shellIntegration() const
QWaylandInputDevice * lastInputDevice() const
void handleWindowDeactivated(QWaylandWindow *window)
void setLastInputDevice(QWaylandInputDevice *device, uint32_t serial, QWaylandWindow *window)
void globalRemoved(const RegistryGlobal &global)
void handleScreenInitialized(QWaylandScreen *screen)
QWaylandWindowManagerIntegration * windowManagerIntegration() const
void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice)
bool hasRegistryGlobal(QStringView interfaceName) const
QWaylandXdgOutputManagerV1 * xdgOutputManager() const
QWaylandInputDevice * defaultInputDevice() const
void registry_global_remove(uint32_t id) override
void addRegistryListener(RegistryListener listener, void *data)
virtual QWaylandCursor * createPlatformCursor(QWaylandDisplay *display) const
virtual QWaylandScreen * createPlatformScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id) const
virtual QWaylandShellIntegration * shellIntegration() const
virtual QWaylandClientBufferIntegration * clientBufferIntegration() const
virtual QWaylandInputDevice * createInputDevice(QWaylandDisplay *display, int version, uint32_t id) const
EGLint EGLint EGLint EGLint int int int int * fds
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
rect
[4]
void qErrnoWarning(const char *msg,...)
struct wl_display * display
Definition linuxdmabuf.h:41
Combined button and popup list for selecting options.
void(* RegistryListener)(void *data, struct wl_registry *registry, uint32_t id, const QString &interface, uint32_t version)
@ QueuedConnection
static jboolean copy(JNIEnv *, jobject)
static int qt_safe_pipe(int pipefd[2], int flags=0)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter * iter
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char * interface
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCInfo(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLuint64 GLenum void * handle
GLenum mode
GLenum GLuint id
[7]
GLuint object
[3]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLuint name
GLuint64EXT * result
[6]
QAnyStringView interfaceName(const Interface &iface)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
QScreen * screen
[1]
Definition main.cpp:29
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_OBJECT
#define Q_EMIT
#define Q_SIGNALS
#define emit
#define Q_UNUSED(x)
QT_BEGIN_NAMESPACE typedef uchar * output
QtWaylandClient::QWaylandDisplay::RegistryGlobal RegistryGlobal
#define disabled
QWidget * win
Definition settings.cpp:6
gzip write("uncompressed data")
QStringList keys
QObject::connect nullptr
QReadWriteLock lock
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]
QJSValue global
\inmodule QtCore \reentrant
Definition qchar.h:18