Qt 6.x
The Qt SDK
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
29#if QT_WAYLAND_TEXT_INPUT_V4_WIP
30#include "qwaylandtextinputv4_p.h"
31#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
34
38
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
71
72namespace QtWaylandClient {
73
74class EventThread : public QThread
75{
77public:
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,
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
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) {
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(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(m_pipefd[1], "\0", 1) == -1)
138 qWarning("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
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(fds) != 0)
164 qWarning("Pipe creation failed. Quitting may hang.");
165 }
166 ~Pipe()
167 {
168 if (fds[0] != -1) {
169 close(fds[0]);
170 close(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] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } };
184 poll(fds, 2, -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 {
206
207 m_reading.storeRelease(false);
208
209 if (m_mode == SelfDispatch) {
211 } else {
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(&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
281{
282 if (!mGlobals.subCompositor) {
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 mGlobals.subCompositor->get_subsurface(window->wlSurface(), parent->wlSurface());
292}
293
295{
296 if (!mGlobals.viewporter) {
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 mGlobals.viewporter->get_viewport(window->wlSurface());
303}
304
306{
307 return mWaylandIntegration->shellIntegration();
308}
309
311{
312 return mWaylandIntegration->clientBufferIntegration();
313}
314
316{
317 return mWindowManagerIntegration.data();
318}
319
321 : mWaylandIntegration(waylandIntegration)
322{
323 qRegisterMetaType<uint32_t>("uint32_t");
324
325 mDisplay = wl_display_connect(nullptr);
326 if (mDisplay) {
327 setupConnection();
328 } else {
329 qErrnoWarning(errno, "Failed to create wl_display");
330 }
331
332 mWaylandTryReconnect = qEnvironmentVariableIsSet("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(new QWaylandWindowManagerIntegration(this));
341
342#if QT_CONFIG(xkbcommon)
343 mXkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
344 if (!mXkbContext)
345 qCWarning(lcQpaWayland, "failed to create xkb context");
346#endif
347 if (!mClientSideInputContextRequested)
348 checkTextInputProtocol();
349}
350
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(std::exchange(mInputDevices, {}));
363
364 for (QWaylandScreen *screen : std::exchange(mScreens, {})) {
366 }
367 qDeleteAll(mWaitingScreens);
368
369#if QT_CONFIG(cursor)
370 mCursorThemes.clear();
371#endif
372
373 if (m_frameEventQueue)
374 wl_event_queue_destroy(m_frameEventQueue);
375
376 mGlobals = {};
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 if (!mWaitingScreens.isEmpty()) {
392 // Give wl_output.done and zxdg_output_v1.done events a chance to arrive
394 }
395 if (!mClientSideInputContextRequested)
396 mTextInputManagerIndex = INT_MAX;
397
398 return qEnvironmentVariableIntValue("QT_WAYLAND_DONT_CHECK_SHELL_INTEGRATION") || shellIntegration();
399}
400
402{
403 if (!mScreens.empty() || mPlaceholderScreen)
404 return; // There are real screens or we already have a fake one
405
406 qCInfo(lcQpaWayland) << "Creating a fake screen in order for Qt not to crash";
407
408 mPlaceholderScreen = new QPlatformPlaceholderScreen();
411}
412
413void QWaylandDisplay::reconnect()
414{
415 qCWarning(lcQpaWayland) << "Attempting wayland reconnect";
416 m_eventThread->stop();
417 m_frameEventQueueThread->stop();
418 m_eventThread->wait();
419 m_frameEventQueueThread->wait();
420
421 qDeleteAll(mWaitingScreens);
422 mWaitingScreens.clear();
423
424 const auto screens = std::exchange(mScreens, {});
425 ensureScreen();
426 for (QWaylandScreen *screen : screens) {
428 }
429
430 // mCompositor
431 mShm.reset();
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 = dynamic_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 = dynamic_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 mDisplay = wl_display_connect(nullptr);
473 if (!mDisplay)
474 _exit(1);
475
476 setupConnection();
477 initialize();
478
479 if (m_frameEventQueue)
480 wl_event_queue_destroy(m_frameEventQueue);
482
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 tips << QLatin1String(QtWayland::qt_text_input_method_v1::interface()->name)
559 << QLatin1String(QtWayland::zwp_text_input_v2::interface()->name)
560 << QLatin1String(QtWayland::zwp_text_input_v1::interface()->name);
561 timps << QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)
562 << QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)
563 << QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name);
564#if QT_WAYLAND_TEXT_INPUT_V4_WIP
565 tips << QLatin1String(QtWayland::zwp_text_input_v4::interface()->name);
566 timps << QLatin1String(QtWayland::zwp_text_input_manager_v4::interface()->name);
567#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
568
569 QString tiProtocols = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_TEXT_INPUT_PROTOCOL"));
570 qCDebug(lcQpaWayland) << "QT_WAYLAND_TEXT_INPUT_PROTOCOL=" << tiProtocols;
572 if (!tiProtocols.isEmpty()) {
573 keys = tiProtocols.split(QLatin1Char(';'));
575 while (it != keys.end()) {
576 if (tips.contains(*it))
577 mTextInputManagerList.append(timps.at(tips.indexOf(*it)));
578 else
579 qCDebug(lcQpaWayland) << "text input: unknown protocol - " << *it;
580 ++it;
581 }
582 }
583 if (mTextInputManagerList.isEmpty()) // fallback
584 mTextInputManagerList = timps;
585}
586
588{
589 for (auto screen : std::as_const(mScreens)) {
590 if (screen->output() == output)
591 return screen;
592 }
593 return nullptr;
594}
595
597{
598 if (!mWaitingScreens.removeOne(screen))
599 return;
600 mScreens.append(screen);
602 if (mPlaceholderScreen) {
604 // handleScreenRemoved deletes the platform screen
605 mPlaceholderScreen = nullptr;
606 }
607}
608
609void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uint32_t version)
610{
611 struct ::wl_registry *registry = object();
612
613 static QStringList interfaceBlacklist = qEnvironmentVariable("QT_WAYLAND_DISABLED_INTERFACES").split(u',');
614 if (interfaceBlacklist.contains(interface)) {
615 return;
616 }
617
618 if (interface == QLatin1String(QtWayland::wl_output::interface()->name)) {
619 mWaitingScreens << mWaylandIntegration->createPlatformScreen(this, version, id);
620 } else if (interface == QLatin1String(QtWayland::wl_compositor::interface()->name)) {
621 mCompositor.init(registry, id, qMin((int)version, 4));
622 } else if (interface == QLatin1String(QWaylandShm::interface()->name)) {
623 mShm.reset(new QWaylandShm(this, version, id));
624 } else if (interface == QLatin1String(QWaylandInputDevice::interface()->name)) {
625 QWaylandInputDevice *inputDevice = mWaylandIntegration->createInputDevice(this, version, id);
626 mInputDevices.append(inputDevice);
627#if QT_CONFIG(wayland_datadevice)
628 } else if (interface == QLatin1String(QWaylandDataDeviceManager::interface()->name)) {
629 mGlobals.dndSelectionHandler.reset(new QWaylandDataDeviceManager(this, version, id));
630#endif
631 } else if (interface == QLatin1String(QtWayland::qt_surface_extension::interface()->name)) {
632 mGlobals.surfaceExtension.reset(new QtWayland::qt_surface_extension(registry, id, 1));
633 } else if (interface == QLatin1String(QtWayland::wl_subcompositor::interface()->name)) {
634 mGlobals.subCompositor.reset(new QtWayland::wl_subcompositor(registry, id, 1));
635 } else if (interface == QLatin1String(QWaylandTouchExtension::interface()->name)) {
636 mGlobals.touchExtension.reset(new QWaylandTouchExtension(this, id));
637 } else if (interface == QLatin1String(QWaylandQtKeyExtension::interface()->name)) {
638 mGlobals.qtKeyExtension.reset(new QWaylandQtKeyExtension(this, id));
639#if QT_CONFIG(tabletevent)
640 } else if (interface == QLatin1String(QWaylandTabletManagerV2::interface()->name)) {
641 mGlobals.tabletManager.reset(new QWaylandTabletManagerV2(this, id, qMin(1, int(version))));
642#endif
643 } else if (interface == QLatin1String(QWaylandPointerGestures::interface()->name)) {
644 mGlobals.pointerGestures.reset(new QWaylandPointerGestures(this, id, 1));
645#if QT_CONFIG(wayland_client_primary_selection)
646 } else if (interface == QLatin1String(QWaylandPrimarySelectionDeviceManagerV1::interface()->name)) {
647 mGlobals.primarySelectionManager.reset(
649 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
650 inputDevice->setPrimarySelectionDevice(
651 mGlobals.primarySelectionManager->createDevice(inputDevice));
652#endif
653 } else if (interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)
654 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
655 qCDebug(lcQpaWayland) << "text input: register qt_text_input_method_manager_v1";
656 if (mTextInputManagerIndex < INT_MAX) {
657 mGlobals.textInputManagerv1.reset();
658 mGlobals.textInputManagerv2.reset();
659#if QT_WAYLAND_TEXT_INPUT_V4_WIP
660 mGlobals.textInputManagerv4.reset();
661#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
662 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
663 inputDevice->setTextInput(nullptr);
664 }
665
666 mGlobals.textInputMethodManager.reset(
667 new QtWayland::qt_text_input_method_manager_v1(registry, id, 1));
668 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
669 inputDevice->setTextInputMethod(new QWaylandTextInputMethod(
670 this,
671 mGlobals.textInputMethodManager->get_text_input_method(
672 inputDevice->wl_seat())));
673 mWaylandIntegration->reconfigureInputContext();
674 mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
675 } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)
676 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
677 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v1";
678 if (mTextInputManagerIndex < INT_MAX) {
679 mGlobals.textInputMethodManager.reset();
680 mGlobals.textInputManagerv2.reset();
681#if QT_WAYLAND_TEXT_INPUT_V4_WIP
682 mGlobals.textInputManagerv4.reset();
683#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
684 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
685 inputDevice->setTextInputMethod(nullptr);
686 }
687
688 mGlobals.textInputManagerv1.reset(
689 new QtWayland::zwp_text_input_manager_v1(registry, id, 1));
690 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices)) {
691 auto textInput =
692 new QWaylandTextInputv1(this, mGlobals.textInputManagerv1->create_text_input());
693 textInput->setSeat(inputDevice->wl_seat());
694 inputDevice->setTextInput(textInput);
695 }
696
697 mWaylandIntegration->reconfigureInputContext();
698 mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
699 } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)
700 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
701 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v2";
702 if (mTextInputManagerIndex < INT_MAX) {
703 mGlobals.textInputMethodManager.reset();
704 mGlobals.textInputManagerv1.reset();
705#if QT_WAYLAND_TEXT_INPUT_V4_WIP
706 mGlobals.textInputManagerv4.reset();
707#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
708 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
709 inputDevice->setTextInputMethod(nullptr);
710 }
711
712 mGlobals.textInputManagerv2.reset(
713 new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
714 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
715 inputDevice->setTextInput(new QWaylandTextInputv2(
716 this, mGlobals.textInputManagerv2->get_text_input(inputDevice->wl_seat())));
717 mWaylandIntegration->reconfigureInputContext();
718 mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
719#if QT_WAYLAND_TEXT_INPUT_V4_WIP
720 } else if (interface == QLatin1String(QtWayland::zwp_text_input_manager_v4::interface()->name)
721 && (mTextInputManagerList.contains(interface) && mTextInputManagerList.indexOf(interface) < mTextInputManagerIndex)) {
722 qCDebug(lcQpaWayland) << "text input: register zwp_text_input_v4";
723 if (mTextInputManagerIndex < INT_MAX) {
724 mGlobals.textInputMethodManager.reset();
725 mGlobals.textInputManagerv2.reset();
726 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
727 inputDevice->setTextInputMethod(nullptr);
728 }
729
730 mGlobals.textInputManagerv4.reset(
731 new QtWayland::zwp_text_input_manager_v4(registry, id, 1));
732 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
733 inputDevice->setTextInput(new QWaylandTextInputv4(
734 this, mGlobals.textInputManagerv4->get_text_input(inputDevice->wl_seat())));
735 mWaylandIntegration->reconfigureInputContext();
736 mTextInputManagerIndex = mTextInputManagerList.indexOf(interface);
737#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
738 } else if (interface == QLatin1String(QWaylandHardwareIntegration::interface()->name)) {
739 bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
740 if (!disableHardwareIntegration) {
741 mGlobals.hardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id));
742 // make a roundtrip here since we need to receive the events sent by
743 // qt_hardware_integration before creating windows
745 }
747 mGlobals.xdgOutputManager.reset(new QWaylandXdgOutputManagerV1(this, id, version));
748 for (auto *screen : std::as_const(mWaitingScreens))
749 screen->initXdgOutput(xdgOutputManager());
750 } else if (interface == QLatin1String(QtWayland::wp_fractional_scale_manager_v1::interface()->name)) {
751 mGlobals.fractionalScaleManager.reset(
752 new QtWayland::wp_fractional_scale_manager_v1(registry, id, 1));
753 } else if (interface == QLatin1String("wp_viewporter")) {
754 mGlobals.viewporter.reset(new QtWayland::wp_viewporter(registry, id, qMin(1u, version)));
755 } else if (interface == QLatin1String(QtWayland::wp_cursor_shape_manager_v1::interface()->name)) {
756 mGlobals.cursorShapeManager.reset(
757 new QtWayland::wp_cursor_shape_manager_v1(registry, id, std::min(1u, version)));
758 } else if (
759 interface == QLatin1String(QtWayland::qt_toplevel_drag_manager_v1::interface()->name)) {
760 mGlobals.xdgToplevelDragManager.reset(
761 new QtWayland::qt_toplevel_drag_manager_v1(registry, id, 1));
762 }
763
764 mRegistryGlobals.append(RegistryGlobal(id, interface, version, registry));
765 emit globalAdded(mRegistryGlobals.back());
766
767 const auto copy = mRegistryListeners; // be prepared for listeners unregistering on notification
768 for (Listener l : copy)
769 (*l.listener)(l.data, registry, id, interface, version);
770}
771
773{
774 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i) {
775 RegistryGlobal &global = mRegistryGlobals[i];
776 if (global.id == id) {
777 if (global.interface == QLatin1String(QtWayland::wl_output::interface()->name)) {
778 for (auto *screen : mWaitingScreens) {
779 if (screen->outputId() == id) {
780 mWaitingScreens.removeOne(screen);
781 delete screen;
782 break;
783 }
784 }
785
786 for (QWaylandScreen *screen : std::as_const(mScreens)) {
787 if (screen->outputId() == id) {
788 mScreens.removeOne(screen);
789 // If this is the last screen, we have to add a fake screen, or Qt will break.
790 ensureScreen();
792 break;
793 }
794 }
795 }
796 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v1::interface()->name)) {
797 mGlobals.textInputManagerv1.reset();
798 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
799 inputDevice->setTextInput(nullptr);
800 mWaylandIntegration->reconfigureInputContext();
801 }
802 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v2::interface()->name)) {
803 mGlobals.textInputManagerv2.reset();
804 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
805 inputDevice->setTextInput(nullptr);
806 mWaylandIntegration->reconfigureInputContext();
807 }
808#if QT_WAYLAND_TEXT_INPUT_V4_WIP
809 if (global.interface == QLatin1String(QtWayland::zwp_text_input_manager_v4::interface()->name)) {
810 mGlobals.textInputManagerv4.reset();
811 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
812 inputDevice->setTextInput(nullptr);
813 mWaylandIntegration->reconfigureInputContext();
814 }
815#endif // QT_WAYLAND_TEXT_INPUT_V4_WIP
816 if (global.interface == QLatin1String(QtWayland::qt_text_input_method_manager_v1::interface()->name)) {
817 mGlobals.textInputMethodManager.reset();
818 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
819 inputDevice->setTextInputMethod(nullptr);
820 mWaylandIntegration->reconfigureInputContext();
821 }
822#if QT_CONFIG(wayland_client_primary_selection)
823 if (global.interface == QLatin1String(QtWayland::zwp_primary_selection_device_manager_v1::interface()->name)) {
824 mGlobals.primarySelectionManager.reset();
825 for (QWaylandInputDevice *inputDevice : std::as_const(mInputDevices))
826 inputDevice->setPrimarySelectionDevice(nullptr);
827 }
828#endif
829 emit globalRemoved(mRegistryGlobals.takeAt(i));
830 break;
831 }
832 }
833}
834
836{
837 for (const RegistryGlobal &global : mRegistryGlobals)
838 if (global.interface == interfaceName)
839 return true;
840
841 return false;
842}
843
845{
846 Listener l = { listener, data };
847 mRegistryListeners.append(l);
848 for (int i = 0, ie = mRegistryGlobals.size(); i != ie; ++i)
849 (*l.listener)(l.data, mRegistryGlobals[i].registry, mRegistryGlobals[i].id,
850 mRegistryGlobals[i].interface, mRegistryGlobals[i].version);
851}
852
854{
855 auto iter = std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){
856 return (l.listener == listener && l.data == data);
857 });
858 mRegistryListeners.erase(iter, mRegistryListeners.end());
859}
860
862{
863 //### we throw away the time information
864 struct timeval tv;
865 int ret = gettimeofday(&tv, nullptr);
866 if (ret == 0)
867 return tv.tv_sec*1000 + tv.tv_usec/1000;
868 return 0;
869}
870
872{
873 wl_display_roundtrip(mDisplay);
874}
875
877{
878 static bool disabled = qgetenv("QT_WAYLAND_DISABLE_WINDOWDECORATION").toInt();
879 // Stop early when disabled via the environment. Do not try to load the integration in
880 // order to play nice with SHM-only, buffer integration-less systems.
881 if (disabled)
882 return false;
883
884 static bool integrationSupport = clientBufferIntegration() && clientBufferIntegration()->supportsWindowDecoration();
885 return integrationSupport;
886}
887
889{
890 return mLastInputWindow.data();
891}
892
894{
895 mLastInputDevice = device;
896 mLastInputSerial = serial;
897 mLastInputWindow = win;
898}
899
901{
902 return mActiveWindows.contains(const_cast<QWaylandWindow *>(window));
903}
904
906{
907 if (mActiveWindows.contains(window))
908 return;
909
910 mActiveWindows.append(window);
911 requestWaylandSync();
912
913 if (auto *decoration = window->decoration())
914 decoration->update();
915}
916
918{
919 Q_ASSERT(!mActiveWindows.empty());
920
921 if (mActiveWindows.last() == window)
922 requestWaylandSync();
923
924 mActiveWindows.removeOne(window);
925
927 return;
928
929 if (auto *decoration = window->decoration())
930 decoration->update();
931}
932
934{
935 QWaylandWindow *keyboardFocus = inputDevice->keyboardFocus();
936
937 if (mLastKeyboardFocus == keyboardFocus)
938 return;
939
940 if (keyboardFocus)
941 handleWindowActivated(keyboardFocus);
942 if (mLastKeyboardFocus)
943 handleWindowDeactivated(mLastKeyboardFocus);
944
945 mLastKeyboardFocus = keyboardFocus;
946}
947
949{
950 if (mActiveWindows.contains(window))
952}
953
954void QWaylandDisplay::handleWaylandSync()
955{
956 // This callback is used to set the window activation because we may get an activate/deactivate
957 // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the
958 // handleWindowActivated() calls immediately.
959 QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window();
960 if (activeWindow != QGuiApplication::focusWindow())
962
963 if (!activeWindow) {
964 if (lastInputDevice()) {
965#if QT_CONFIG(clipboard)
966 if (auto *dataDevice = lastInputDevice()->dataDevice())
967 dataDevice->invalidateSelectionOffer();
968#endif
969#if QT_CONFIG(wayland_client_primary_selection)
970 if (auto *device = lastInputDevice()->primarySelectionDevice())
971 device->invalidateSelectionOffer();
972#endif
973 }
974 }
975}
976
977const wl_callback_listener QWaylandDisplay::syncCallbackListener = {
978 [](void *data, struct wl_callback *callback, uint32_t time){
979 Q_UNUSED(time);
980 wl_callback_destroy(callback);
981 QWaylandDisplay *display = static_cast<QWaylandDisplay *>(data);
982 display->mSyncCallback = nullptr;
983 display->handleWaylandSync();
984 }
985};
986
987void QWaylandDisplay::requestWaylandSync()
988{
989 if (mSyncCallback)
990 return;
991
992 mSyncCallback = wl_display_sync(mDisplay);
993 wl_callback_add_listener(mSyncCallback, &syncCallbackListener, this);
994}
995
997{
998 return mInputDevices.isEmpty() ? nullptr : mInputDevices.first();
999}
1000
1002{
1003 return std::any_of(
1004 mInputDevices.constBegin(), mInputDevices.constEnd(),
1005 [](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; });
1006}
1007
1009 return mClientSideInputContextRequested;
1010}
1011
1012#if QT_CONFIG(cursor)
1013
1014QWaylandCursor *QWaylandDisplay::waylandCursor()
1015{
1016 if (!mCursor)
1017 mCursor.reset(mWaylandIntegration->createPlatformCursor(this));
1018 return mCursor.data();
1019}
1020
1021auto QWaylandDisplay::findExistingCursorTheme(const QString &name, int pixelSize) const noexcept
1022 -> FindExistingCursorThemeResult
1023{
1024 const auto byNameAndSize = [](const WaylandCursorTheme &lhs, const WaylandCursorTheme &rhs) {
1025 return std::tie(lhs.pixelSize, lhs.name) < std::tie(rhs.pixelSize, rhs.name);
1026 };
1027
1028 const WaylandCursorTheme prototype = {name, pixelSize, nullptr};
1029
1030 const auto it = std::lower_bound(mCursorThemes.cbegin(), mCursorThemes.cend(), prototype, byNameAndSize);
1031 if (it != mCursorThemes.cend() && it->name == name && it->pixelSize == pixelSize)
1032 return {it, true};
1033 else
1034 return {it, false};
1035}
1036
1037QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize)
1038{
1039 const auto result = findExistingCursorTheme(name, pixelSize);
1040 if (result.found)
1041 return result.theme();
1042
1043 if (auto theme = QWaylandCursorTheme::create(shm(), pixelSize, name))
1044 return mCursorThemes.insert(result.position, {name, pixelSize, std::move(theme)})->theme.get();
1045
1046 return nullptr;
1047}
1048
1049#endif // QT_CONFIG(cursor)
1050
1051} // namespace QtWaylandClient
1052
1054
1055#include "qwaylanddisplay.moc"
1056#include "moc_qwaylanddisplay_p.cpp"
IOBluetoothDevice * device
\inmodule QtCore
Definition qatomic.h:18
T loadAcquire() const noexcept
void storeRelease(T newValue) noexcept
T loadRelaxed() const noexcept
int toInt(bool *ok=nullptr, int base=10) const
Returns the byte array converted to an int using base base, which is ten by default.
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...
Definition qlist.h:74
bool isEmpty() const noexcept
Definition qlist.h:390
iterator erase(const_iterator begin, const_iterator end)
Definition qlist.h:882
void push_back(parameter_type t)
Definition qlist.h:672
iterator end()
Definition qlist.h:609
iterator begin()
Definition qlist.h:608
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
Q_WEAK_OVERLOAD void setObjectName(const QString &name)
Sets the object's name to name.
Definition qobject.h:114
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:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
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:7956
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5788
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QThread * currentThread()
Definition qthread.cpp:966
bool wait(QDeadlineTimer deadline=QDeadlineTimer(QDeadlineTimer::Forever))
Definition qthread.cpp:950
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
static const wl_interface * interface()
Returns the Wayland interface for QWaylandXdgOutputManagerV1.
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.
static void handleWindowActivated(QWindow *window, Qt::FocusReason r=Qt::OtherFocusReason)
\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)
QList< QWaylandScreen * > screens() const
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:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCInfo(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
return ret
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
GLuint name
GLuint64EXT * result
[6]
#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:17
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent