commit d9ff623c4df6d1e45c21ee38deafe961856911e5 Author: MSVSphere Packaging Team Date: Fri Sep 22 20:03:31 2023 +0300 import qt5-qtwayland-5.15.9-1.el9 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6520d65 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/qtwayland-everywhere-opensource-src-5.15.9.tar.xz diff --git a/.qt5-qtwayland.metadata b/.qt5-qtwayland.metadata new file mode 100644 index 0000000..a04bb33 --- /dev/null +++ b/.qt5-qtwayland.metadata @@ -0,0 +1 @@ +dcaac92b52d681c9d13330e78fcd745005269fdc SOURCES/qtwayland-everywhere-opensource-src-5.15.9.tar.xz diff --git a/SOURCES/0001-Client-Announce-an-output-after-receiving-more-compl.patch b/SOURCES/0001-Client-Announce-an-output-after-receiving-more-compl.patch new file mode 100644 index 0000000..2142e9d --- /dev/null +++ b/SOURCES/0001-Client-Announce-an-output-after-receiving-more-compl.patch @@ -0,0 +1,146 @@ +From 4ddf70b1ed7c5baddef41c549d04917b9dec7d0f Mon Sep 17 00:00:00 2001 +From: Vlad Zahorodnii +Date: Wed, 5 May 2021 20:49:26 +0300 +Subject: [PATCH 01/55] Client: Announce an output after receiving more + complete state + +Output initialization is not atomic, meaning that the compositor may +process a wl_output bind request in one event loop cycle, and the +xdg_output_manager.get_xdg_output in another event loop cycle. + +This means that xdg_output properties may arrive in another wl_output +done frame. Prior to xdg-output v3, that wasn't an issue because the +compositor is required to send an xdg_output.done event after sending +xdg_output properties. + +Starting with v3, the compositor may choose not to send an +xdg_output.done event after sending xdg_output properties. Therefore, +as is, QtWayland may announce an output with bad logical geometry or +even worse without name assigned by the compositor. + +Unfortunately, that breaks applications such as plasmashell. Plasma uses +output names as a criterion to determine what kind of contents should be +displayed on a particular output. + +In order to fix the initialization sequence, this change makes every +QWaylandScreen track processed events. After all required events have +been received, the screen can be announced to the rest of Qt. + +Change-Id: If5da747edd7af277ec1364cbea105c6994f47402 +Reviewed-by: David Edmundson +(cherry picked from commit 69ea480f2e53ad4a5bbca78cde044eb8d4c48896) + +Original Ticket: https://codereview.qt-project.org/c/qt/qtwayland/+/347774 +CCBUG: 435124 +--- + src/client/qwaylandscreen.cpp | 32 +++++++++++++++++++++++--------- + src/client/qwaylandscreen_p.h | 10 ++++++++-- + 2 files changed, 31 insertions(+), 11 deletions(-) + +diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp +index 6cb337de..7c2d9be3 100644 +--- a/src/client/qwaylandscreen.cpp ++++ b/src/client/qwaylandscreen.cpp +@@ -72,7 +72,7 @@ QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uin + qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor," + << "QScreen may not work correctly"; + mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc. +- mOutputDone = true; // Fake the done event ++ mProcessedEvents |= OutputDoneEvent; // Fake the done event + maybeInitialize(); + } + } +@@ -83,14 +83,25 @@ QWaylandScreen::~QWaylandScreen() + zxdg_output_v1::destroy(); + } + ++uint QWaylandScreen::requiredEvents() const ++{ ++ uint ret = OutputDoneEvent; ++ ++ if (mWaylandDisplay->xdgOutputManager()) { ++ ret |= XdgOutputNameEvent; ++ ++ if (mWaylandDisplay->xdgOutputManager()->version() < 3) ++ ret |= XdgOutputDoneEvent; ++ } ++ return ret; ++} ++ + void QWaylandScreen::maybeInitialize() + { + Q_ASSERT(!mInitialized); + +- if (!mOutputDone) +- return; +- +- if (mWaylandDisplay->xdgOutputManager() && !mXdgOutputDone) ++ const uint requiredEvents = this->requiredEvents(); ++ if ((mProcessedEvents & requiredEvents) != requiredEvents) + return; + + mInitialized = true; +@@ -276,9 +287,8 @@ void QWaylandScreen::output_scale(int32_t factor) + + void QWaylandScreen::output_done() + { +- mOutputDone = true; +- if (zxdg_output_v1::isInitialized() && mWaylandDisplay->xdgOutputManager()->version() >= 3) +- mXdgOutputDone = true; ++ mProcessedEvents |= OutputDoneEvent; ++ + if (mInitialized) { + updateOutputProperties(); + if (zxdg_output_v1::isInitialized()) +@@ -339,7 +349,7 @@ void QWaylandScreen::zxdg_output_v1_done() + if (Q_UNLIKELY(mWaylandDisplay->xdgOutputManager()->version() >= 3)) + qWarning(lcQpaWayland) << "zxdg_output_v1.done received on version 3 or newer, this is most likely a bug in the compositor"; + +- mXdgOutputDone = true; ++ mProcessedEvents |= XdgOutputDoneEvent; + if (mInitialized) + updateXdgOutputProperties(); + else +@@ -348,7 +358,11 @@ void QWaylandScreen::zxdg_output_v1_done() + + void QWaylandScreen::zxdg_output_v1_name(const QString &name) + { ++ if (Q_UNLIKELY(mInitialized)) ++ qWarning(lcQpaWayland) << "zxdg_output_v1.name received after output has been initialized, this is most likely a bug in the compositor"; ++ + mOutputName = name; ++ mProcessedEvents |= XdgOutputNameEvent; + } + + void QWaylandScreen::updateXdgOutputProperties() +diff --git a/src/client/qwaylandscreen_p.h b/src/client/qwaylandscreen_p.h +index df1c94f2..050cfdc0 100644 +--- a/src/client/qwaylandscreen_p.h ++++ b/src/client/qwaylandscreen_p.h +@@ -116,6 +116,13 @@ public: + static QWaylandScreen *fromWlOutput(::wl_output *output); + + private: ++ enum Event : uint { ++ XdgOutputDoneEvent = 0x1, ++ OutputDoneEvent = 0x2, ++ XdgOutputNameEvent = 0x4, ++ }; ++ uint requiredEvents() const; ++ + void output_mode(uint32_t flags, int width, int height, int refresh) override; + void output_geometry(int32_t x, int32_t y, + int32_t width, int32_t height, +@@ -148,8 +155,7 @@ private: + QSize mPhysicalSize; + QString mOutputName; + Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; +- bool mOutputDone = false; +- bool mXdgOutputDone = false; ++ uint mProcessedEvents = 0; + bool mInitialized = false; + + #if QT_CONFIG(cursor) +-- +2.40.0 + diff --git a/SOURCES/0002-Fix-issue-with-repeated-window-size-changes.patch b/SOURCES/0002-Fix-issue-with-repeated-window-size-changes.patch new file mode 100644 index 0000000..58b41d1 --- /dev/null +++ b/SOURCES/0002-Fix-issue-with-repeated-window-size-changes.patch @@ -0,0 +1,58 @@ +From a9bcc5dc553b5a307a5bdcb6e1d92058e0257c7c Mon Sep 17 00:00:00 2001 +From: Jaeyoon Jung +Date: Mon, 15 Feb 2021 08:31:06 +0900 +Subject: [PATCH 02/55] Fix issue with repeated window size changes + +Check if the new window size is different from the size requested +previously before calling wl_egl_window_resize. It addresses the issue +where repeated setGeometry calls between two sizes might not work as +expected. The problem occurs when wl_egl_window_get_attached_size does +not get the same size that was requested by the previous setGeometry +call. If the returned size happened to match the new size instead, +we would mistakenly skip the resize. + +Change-Id: Iafe4a91cc707f854b9099b6109b6be1423d7bd29 +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 14d066c61025e548227ccd8d655e80ffa31fa15e) +--- + .../client/wayland-egl/qwaylandeglwindow.cpp | 4 +++- + .../client/wayland-egl/qwaylandeglwindow.h | 1 + + 2 files changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp +index 57d4eb6b..13dd747a 100644 +--- a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp ++++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp +@@ -122,14 +122,16 @@ void QWaylandEglWindow::updateSurface(bool create) + if (!disableResizeCheck) { + wl_egl_window_get_attached_size(m_waylandEglWindow, ¤t_width, ¤t_height); + } +- if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height())) { ++ if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height()) || m_requestedSize != sizeWithMargins) { + wl_egl_window_resize(m_waylandEglWindow, sizeWithMargins.width(), sizeWithMargins.height(), mOffset.x(), mOffset.y()); ++ m_requestedSize = sizeWithMargins; + mOffset = QPoint(); + + m_resize = true; + } + } else if (create && wlSurface()) { + m_waylandEglWindow = wl_egl_window_create(wlSurface(), sizeWithMargins.width(), sizeWithMargins.height()); ++ m_requestedSize = sizeWithMargins; + } + + if (!m_eglSurface && m_waylandEglWindow && create) { +diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h +index 6c8f04ec..94c56325 100644 +--- a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h ++++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h +@@ -87,6 +87,7 @@ private: + mutable QOpenGLFramebufferObject *m_contentFBO = nullptr; + + QSurfaceFormat m_format; ++ QSize m_requestedSize; + }; + + } +-- +2.40.0 + diff --git a/SOURCES/0003-Client-Connect-drags-being-accepted-to-updating-the-.patch b/SOURCES/0003-Client-Connect-drags-being-accepted-to-updating-the-.patch new file mode 100644 index 0000000..80f81ae --- /dev/null +++ b/SOURCES/0003-Client-Connect-drags-being-accepted-to-updating-the-.patch @@ -0,0 +1,39 @@ +From eb8e933841d66f0ebe071a170a9af995c2b07b0d Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Tue, 9 Feb 2021 16:09:21 +0000 +Subject: [PATCH 03/55] Client: Connect drags being accepted to updating the + source drag icon + +Currently in a multi-process drag and drop when the other client accepts +a given mimetype for dropping it calls accept, which is received by the +client, but the drag cursor is never updated. + +Instead the drag cursor was updated in the data_device_enter events +which only works if we are operating within one process. + +The code existed to handle this existed but both the targetChanged +signal and the dragSourceTargetChanged were unused. + +Change-Id: I443f31f1b2ef72d4b5eadaf7115f97544dac883a +Reviewed-by: Vlad Zahorodnii +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 08e478448a97a440d5a968a5d797f0d7302140c2) +--- + src/client/qwaylanddatadevice.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/client/qwaylanddatadevice.cpp b/src/client/qwaylanddatadevice.cpp +index 7e2e3308..bbd2d568 100644 +--- a/src/client/qwaylanddatadevice.cpp ++++ b/src/client/qwaylanddatadevice.cpp +@@ -124,6 +124,7 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) + + m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData)); + connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled); ++ connect(m_dragSource.data(), &QWaylandDataSource::targetChanged, this, &QWaylandDataDevice::dragSourceTargetChanged); + + start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial()); + return true; +-- +2.40.0 + diff --git a/SOURCES/0004-Client-Disconnect-registry-listener-on-destruction.patch b/SOURCES/0004-Client-Disconnect-registry-listener-on-destruction.patch new file mode 100644 index 0000000..51b5718 --- /dev/null +++ b/SOURCES/0004-Client-Disconnect-registry-listener-on-destruction.patch @@ -0,0 +1,49 @@ +From 9d16197f5e888b68cdf98165f46dcc9ac4dd9820 Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Fri, 14 May 2021 13:23:24 +0100 +Subject: [PATCH 04/55] Client: Disconnect registry listener on destruction + +If a display outlives a QWaylandClientExtension and a new global is +announced we end up delivering an event to a now deleted extension which +will crash. + +Change-Id: Idc0de40be61a2f7627ab4963e1fe29b22fbf3f04 +(cherry picked from commit c4ba37cd2f8cb81b9438b56ac604fc2f3e45083c) +--- + src/client/global/qwaylandclientextension.cpp | 7 +++++++ + src/client/global/qwaylandclientextension.h | 1 + + 2 files changed, 8 insertions(+) + +diff --git a/src/client/global/qwaylandclientextension.cpp b/src/client/global/qwaylandclientextension.cpp +index 125b1e19..797b06fe 100644 +--- a/src/client/global/qwaylandclientextension.cpp ++++ b/src/client/global/qwaylandclientextension.cpp +@@ -88,6 +88,13 @@ QWaylandClientExtension::QWaylandClientExtension(const int ver) + QMetaObject::invokeMethod(this, "addRegistryListener", Qt::QueuedConnection); + } + ++QWaylandClientExtension::~QWaylandClientExtension() ++{ ++ Q_D(QWaylandClientExtension); ++ if (d->registered && !QCoreApplication::closingDown()) ++ d->waylandIntegration->display()->removeListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this); ++} ++ + QtWaylandClient::QWaylandIntegration *QWaylandClientExtension::integration() const + { + Q_D(const QWaylandClientExtension); +diff --git a/src/client/global/qwaylandclientextension.h b/src/client/global/qwaylandclientextension.h +index 98272e57..5bd28398 100644 +--- a/src/client/global/qwaylandclientextension.h ++++ b/src/client/global/qwaylandclientextension.h +@@ -63,6 +63,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtension : public QObject + Q_PROPERTY(bool active READ isActive NOTIFY activeChanged) + public: + QWaylandClientExtension(const int version); ++ ~QWaylandClientExtension(); + + QtWaylandClient::QWaylandIntegration *integration() const; + int version() const; +-- +2.40.0 + diff --git a/SOURCES/0005-Client-Set-XdgShell-size-hints-before-the-first-comm.patch b/SOURCES/0005-Client-Set-XdgShell-size-hints-before-the-first-comm.patch new file mode 100644 index 0000000..e77a833 --- /dev/null +++ b/SOURCES/0005-Client-Set-XdgShell-size-hints-before-the-first-comm.patch @@ -0,0 +1,58 @@ +From 8256c6d8bb0f456ad27031bad1cce916c0c29740 Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Mon, 3 May 2021 23:01:53 +0100 +Subject: [PATCH 05/55] Client: Set XdgShell size hints before the first commit + +propagateSizeHints is only called in QWindow we have platform window and +minimumSizeHint is then sent. We also need to send existing hints when +we create the shell window. + +Sending them when we apply configure is too late, we need these hints +available for the compositor to correctly configure the window. + +Change-Id: I6cbb294b11db06ecd87535fa4816bb8ad34a29c6 +Reviewed-by: Vlad Zahorodnii +Reviewed-by: Aleix Pol Gonzalez +(cherry picked from commit d6e074d0d35221b0fac14c94fc79c98363f2f6c3) +--- + src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp | 3 +-- + tests/auto/client/xdgshell/tst_xdgshell.cpp | 2 +- + 2 files changed, 2 insertions(+), 3 deletions(-) + +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +index f50e1c0e..1f51562b 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +@@ -105,8 +105,6 @@ void QWaylandXdgSurface::Toplevel::applyConfigure() + m_xdgSurface->m_window->resizeFromApplyConfigure(m_pending.size); + } + +- m_xdgSurface->setSizeHints(); +- + m_applied = m_pending; + qCDebug(lcQpaWayland) << "Applied pending xdg_toplevel configure event:" << m_applied.size << m_applied.states; + } +@@ -257,6 +255,7 @@ QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *s + m_toplevel->set_parent(parentXdgSurface->m_toplevel->object()); + } + } ++ setSizeHints(); + } + + QWaylandXdgSurface::~QWaylandXdgSurface() +diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp +index 2277bbb8..2fdd0a7c 100644 +--- a/tests/auto/client/xdgshell/tst_xdgshell.cpp ++++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp +@@ -505,7 +505,7 @@ void tst_xdgshell::minMaxSize() + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + +- exec([=] { xdgToplevel()->sendCompleteConfigure(); }); ++ // we don't roundtrip with our configuration the initial commit should be correct + + QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(100, 100)); + QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(1000, 1000)); +-- +2.40.0 + diff --git a/SOURCES/0006-Fix-build.patch b/SOURCES/0006-Fix-build.patch new file mode 100644 index 0000000..41ba119 --- /dev/null +++ b/SOURCES/0006-Fix-build.patch @@ -0,0 +1,46 @@ +From bea02a7b4bce8cc3b2a7bce10c53fccc9aedef1e Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Mon, 14 Jun 2021 12:45:37 +0100 +Subject: [PATCH 06/55] Fix build + +1b5e43a593e917610e6245f7a272ac081c508ba4 relied on a patch that we can't +backport. + +This adds that extra internal boolean backporting just the tiny part of +d6ac8cf6. +--- + src/client/global/qwaylandclientextension.cpp | 5 ++++- + src/client/global/qwaylandclientextension_p.h | 1 + + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/client/global/qwaylandclientextension.cpp b/src/client/global/qwaylandclientextension.cpp +index 797b06fe..edccfe63 100644 +--- a/src/client/global/qwaylandclientextension.cpp ++++ b/src/client/global/qwaylandclientextension.cpp +@@ -74,7 +74,10 @@ void QWaylandClientExtensionPrivate::handleRegistryGlobal(void *data, ::wl_regis + void QWaylandClientExtension::addRegistryListener() + { + Q_D(QWaylandClientExtension); +- d->waylandIntegration->display()->addRegistryListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this); ++ if (!d->registered) { ++ d->waylandIntegration->display()->addRegistryListener(&QWaylandClientExtensionPrivate::handleRegistryGlobal, this); ++ d->registered = true; ++ } + } + + QWaylandClientExtension::QWaylandClientExtension(const int ver) +diff --git a/src/client/global/qwaylandclientextension_p.h b/src/client/global/qwaylandclientextension_p.h +index 69cc46a0..9091efbe 100644 +--- a/src/client/global/qwaylandclientextension_p.h ++++ b/src/client/global/qwaylandclientextension_p.h +@@ -68,6 +68,7 @@ public: + QtWaylandClient::QWaylandIntegration *waylandIntegration = nullptr; + int version = -1; + bool active = false; ++ bool registered = false; + }; + + class Q_WAYLAND_CLIENT_EXPORT QWaylandClientExtensionTemplatePrivate : public QWaylandClientExtensionPrivate +-- +2.40.0 + diff --git a/SOURCES/0007-Fix-remove-listener.patch b/SOURCES/0007-Fix-remove-listener.patch new file mode 100644 index 0000000..958c895 --- /dev/null +++ b/SOURCES/0007-Fix-remove-listener.patch @@ -0,0 +1,33 @@ +From 418580420c34c1a81fd5a4d872e41dc467c124d0 Mon Sep 17 00:00:00 2001 +From: Zhang Liang +Date: Mon, 1 Feb 2021 19:29:43 +0800 +Subject: [PATCH 07/55] Fix: remove listener + +Add the operation for removing the listener form listener list + +Change-Id: Ief2ff1303b607eee499543303fe80e51f8f10cc5 +Reviewed-by: David Edmundson +(cherry picked from commit 16760280fd04cf70255bab16d9acecad254fdd8f) +--- + src/client/qwaylanddisplay.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp +index 78e387bc..280e63bd 100644 +--- a/src/client/qwaylanddisplay.cpp ++++ b/src/client/qwaylanddisplay.cpp +@@ -456,9 +456,10 @@ void QWaylandDisplay::addRegistryListener(RegistryListener listener, void *data) + + void QWaylandDisplay::removeListener(RegistryListener listener, void *data) + { +- std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){ ++ auto iter = std::remove_if(mRegistryListeners.begin(), mRegistryListeners.end(), [=](Listener l){ + return (l.listener == listener && l.data == data); + }); ++ mRegistryListeners.erase(iter, mRegistryListeners.end()); + } + + uint32_t QWaylandDisplay::currentTimeMillisec() +-- +2.40.0 + diff --git a/SOURCES/0008-Hook-up-queryKeyboardModifers.patch b/SOURCES/0008-Hook-up-queryKeyboardModifers.patch new file mode 100644 index 0000000..c121bf4 --- /dev/null +++ b/SOURCES/0008-Hook-up-queryKeyboardModifers.patch @@ -0,0 +1,55 @@ +From 0bd8b57955c74fd7cadc3ceb1409c1705208f91e Mon Sep 17 00:00:00 2001 +From: David Redondo +Date: Wed, 26 May 2021 14:49:40 +0200 +Subject: [PATCH 08/55] Hook up queryKeyboardModifers + +Can be useful when upon enter a modifiers event is received but no key +event so no QKeyEvent is generated. + +Fixes: QTBUG-62786 +Change-Id: I30b57fc78ce6d54d8f644ca95ba40e7e26eb24ed +Reviewed-by: Marco Martin +Reviewed-by: David Edmundson + + +(cherry picked from commit 4fa2baba8181ade4958a94e9531ec4f6919438a9) +--- + src/client/qwaylandintegration.cpp | 8 ++++++++ + src/client/qwaylandintegration_p.h | 2 ++ + 2 files changed, 10 insertions(+) + +diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp +index d257e2e3..cd8569b1 100644 +--- a/src/client/qwaylandintegration.cpp ++++ b/src/client/qwaylandintegration.cpp +@@ -262,6 +262,14 @@ QWaylandDisplay *QWaylandIntegration::display() const + return mDisplay.data(); + } + ++Qt::KeyboardModifiers QWaylandIntegration::queryKeyboardModifiers() const ++{ ++ if (auto *seat = mDisplay->currentInputDevice()) { ++ return seat->modifiers(); ++ } ++ return Qt::NoModifier; ++} ++ + QList QWaylandIntegration::possibleKeys(const QKeyEvent *event) const + { + if (auto *seat = mDisplay->currentInputDevice()) +diff --git a/src/client/qwaylandintegration_p.h b/src/client/qwaylandintegration_p.h +index ff70ae25..73b80658 100644 +--- a/src/client/qwaylandintegration_p.h ++++ b/src/client/qwaylandintegration_p.h +@@ -107,6 +107,8 @@ public: + + QWaylandDisplay *display() const; + ++ Qt::KeyboardModifiers queryKeyboardModifiers() const override; ++ + QList possibleKeys(const QKeyEvent *event) const override; + + QStringList themeNames() const override; +-- +2.40.0 + diff --git a/SOURCES/0009-Correctly-detect-if-image-format-is-supported-by-QIm.patch b/SOURCES/0009-Correctly-detect-if-image-format-is-supported-by-QIm.patch new file mode 100644 index 0000000..674bde0 --- /dev/null +++ b/SOURCES/0009-Correctly-detect-if-image-format-is-supported-by-QIm.patch @@ -0,0 +1,68 @@ +From 046a799372b78ab843b26c2312bb563c0d50adcb Mon Sep 17 00:00:00 2001 +From: Jan Blackquill +Date: Tue, 24 Aug 2021 14:36:34 -0400 +Subject: [PATCH 09/55] Correctly detect if image format is supported by + QImageWriter + +The code queries potential image formats by stripping a mimetype of its +'image/' prefix and making the rest of the mimetype capitalised, such as +'image/png' -> 'PNG'. The problem is that this is then searched for in +QImageWriter::supportedImageFormats() by simple equality. The method +returns a list of lowercase byte arrays, not uppercase. As the codepath +can never match due to checking for an uppercase word in an array of +lowercase words, this means that images are effectively always sent as +BMP format, even if they should be sent in other formats, such as PNG +or JPEG. + +A simple inspection with GDB (or a qDebug) reveals this: + +``` +(gdb) p QImageWriter::supportedImageFormats() +$31 = {"bmp" = {...}, "bw" = {...}, "cur" = {...}, "eps" = {...}, + "epsf" = {...}, "epsi" = {...}, "icns" = {...}, + "ico" = {...}, "jp2" = {...}, "jpeg" = {...}, "jpg" = {...}, + "pbm" = {...}, "pcx" = {...}, "pgm" = {...}, + "pic" = {...}, "png" = {...}, "ppm" = {...}, + "rgb" = {...}, "rgba" = {...}, "sgi" = {...}, + "tga" = {...}, "tif" = {...}, "tiff" = {...}, + "wbmp" = {...}, "webp" = {...}, "xbm" = {...}, "xpm" = {...}} +``` + +``` +(gdb) p QImageWriter::supportedImageFormats().contains("PNG") +$32 = false +``` + +``` +(gdb) p QImageWriter::supportedImageFormats().contains("png") +$33 = true +``` + +The fix for this is simple: lowercase the remainder of the mimetype, +instead of uppercasing it, and we can start hitting the codepath that's +supposed to write non-BMP formats. + +Change-Id: Id3e9b730b7edcabcb2f1b04d8ef0a4c1fb9c9159 +Reviewed-by: David Edmundson +Reviewed-by: Qt CI Bot +(cherry picked from commit 6072c1dc87e185f30c014f764737ac97b906640f) +--- + src/shared/qwaylandmimehelper.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/shared/qwaylandmimehelper.cpp b/src/shared/qwaylandmimehelper.cpp +index a5fdd34d..051a91dc 100644 +--- a/src/shared/qwaylandmimehelper.cpp ++++ b/src/shared/qwaylandmimehelper.cpp +@@ -60,7 +60,7 @@ QByteArray QWaylandMimeHelper::getByteArray(QMimeData *mimeData, const QString & + buf.open(QIODevice::ReadWrite); + QByteArray fmt = "BMP"; + if (mimeType.startsWith(QLatin1String("image/"))) { +- QByteArray imgFmt = mimeType.mid(6).toUpper().toLatin1(); ++ QByteArray imgFmt = mimeType.mid(6).toLower().toLatin1(); + if (QImageWriter::supportedImageFormats().contains(imgFmt)) + fmt = imgFmt; + } +-- +2.40.0 + diff --git a/SOURCES/0010-Client-Don-t-always-recreate-frame-callbacks.patch b/SOURCES/0010-Client-Don-t-always-recreate-frame-callbacks.patch new file mode 100644 index 0000000..bfe00e5 --- /dev/null +++ b/SOURCES/0010-Client-Don-t-always-recreate-frame-callbacks.patch @@ -0,0 +1,77 @@ +From 3bb880d6b828f934f69608af958963c9eedc4e74 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Thu, 27 May 2021 19:55:04 -0300 +Subject: [PATCH 10/55] Client: Don't always recreate frame callbacks + +The main QWaylandWindow method that is executed when handling updates is +QWaylandWindow::handleUpdate(). This method always, unconditionally queues +a frame callback, regardless of whether any other one is already queued. + +On some circumstances, e.g. when a window is hidden or completely obscured +by other windows, it stops receiving frame callbacks from the compositor. +However, QWaylandWindow would continue to request for them, which eventually +fills up the Wayland socket, and causes the application to crash. + +This can be avoided by checking if the platform window is already waiting +for a frame callback, before queueing another one. + +In QWaylandWindow::handleUpdate(), check if mWaitingForFrameCallback is true +before queueing frame callbacks, and early return if that's the case. + +The XDG-shell test needed to be updated for this: The mock compositor is +not responding to any frame callbacks, so the window will be unexposed, +no longer get paint events and therefore not trigger any commit. This +worked by accident before because we were issuing updates quickly enough +to reset the timer before it had a chance to unexpose the window. The +easiest fix is just to disable the dependency on frame callbacks in +this test, since that is clearly not what it's testing. + +Task-number: QTBUG-81504 +Change-Id: Ieacb05c7d5a5fcf662243d9177ebcc308cb9ca84 +Reviewed-by: Qt CI Bot +Reviewed-by: Georges Basile Stavracas Neto +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit cbc74ba6d7186457d8d07183272e952dee5f34f9) +--- + src/client/qwaylandwindow.cpp | 4 ++++ + tests/auto/client/xdgshell/tst_xdgshell.cpp | 2 ++ + 2 files changed, 6 insertions(+) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index df2dcdaa..23816895 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -1192,6 +1192,10 @@ void QWaylandWindow::requestUpdate() + void QWaylandWindow::handleUpdate() + { + qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread(); ++ ++ if (mWaitingForFrameCallback) ++ return; ++ + // TODO: Should sync subsurfaces avoid requesting frame callbacks? + QReadLocker lock(&mSurfaceLock); + if (!mSurface) +diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp +index 2fdd0a7c..e2593314 100644 +--- a/tests/auto/client/xdgshell/tst_xdgshell.cpp ++++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp +@@ -138,6 +138,7 @@ void tst_xdgshell::configureSize() + + void tst_xdgshell::configureStates() + { ++ QVERIFY(qputenv("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT", "0")); + QRasterWindow window; + window.resize(64, 48); + window.show(); +@@ -186,6 +187,7 @@ void tst_xdgshell::configureStates() + QCOMPARE(window.windowStates(), Qt::WindowNoState); + QCOMPARE(window.frameGeometry().size(), windowedSize); + // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled ++ QVERIFY(qunsetenv("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT")); + } + + void tst_xdgshell::popup() +-- +2.40.0 + diff --git a/SOURCES/0011-Client-Always-destroy-frame-callback-in-the-actual-c.patch b/SOURCES/0011-Client-Always-destroy-frame-callback-in-the-actual-c.patch new file mode 100644 index 0000000..6eb5579 --- /dev/null +++ b/SOURCES/0011-Client-Always-destroy-frame-callback-in-the-actual-c.patch @@ -0,0 +1,58 @@ +From a0ab21120ae0d0976d1c554aa72f00d4b3d35de9 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Thu, 27 May 2021 20:02:53 -0300 +Subject: [PATCH 11/55] Client: Always destroy frame callback in the actual + callback + +It's good hygiene to destroy all frame callbacks. Destroy the +frame callback and cleanup the mFrameCallback class member in +the callback itself. The callback destruction happens before +calling handleFrameCallback() to avoid the theoretical case +where another frame callback is queued by handleFrameCallback(), +and then immediately destroyed in the callback handler. + +* asturmlechner 2021-09-27: + Conflict resolved from non-backported commit in dev branch: + 93058de8d7e7c2f320c22b3bd898aa06cf5babcd + +Change-Id: Ide6dc95e3402932c58bfc088a9d471fda821e9a1 +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 42cdc61a93cf2acb09936aebb5e431fdbc0a26c6) +--- + src/client/qwaylandwindow.cpp | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 23816895..d3f28d68 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -635,9 +635,13 @@ void QWaylandWindow::commit() + + const wl_callback_listener QWaylandWindow::callbackListener = { + [](void *data, wl_callback *callback, uint32_t time) { +- Q_UNUSED(callback); + Q_UNUSED(time); + auto *window = static_cast(data); ++ ++ Q_ASSERT(callback == window->mFrameCallback); ++ wl_callback_destroy(callback); ++ window->mFrameCallback = nullptr; ++ + window->handleFrameCallback(); + } + }; +@@ -1201,11 +1205,6 @@ void QWaylandWindow::handleUpdate() + if (!mSurface) + return; + +- if (mFrameCallback) { +- wl_callback_destroy(mFrameCallback); +- mFrameCallback = nullptr; +- } +- + QMutexLocker locker(mFrameQueue.mutex); + struct ::wl_surface *wrappedSurface = reinterpret_cast(wl_proxy_create_wrapper(mSurface->object())); + wl_proxy_set_queue(reinterpret_cast(wrappedSurface), mFrameQueue.queue); +-- +2.40.0 + diff --git a/SOURCES/0012-Wayland-client-use-wl_keyboard-to-determine-active-s.patch b/SOURCES/0012-Wayland-client-use-wl_keyboard-to-determine-active-s.patch new file mode 100644 index 0000000..1a0cff0 --- /dev/null +++ b/SOURCES/0012-Wayland-client-use-wl_keyboard-to-determine-active-s.patch @@ -0,0 +1,341 @@ +From d40acfcc33ae9f8d105f196d1af03a640a02db0b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A9ven=20Car?= +Date: Wed, 18 Aug 2021 18:28:20 +0200 +Subject: [PATCH 12/55] Wayland client: use wl_keyboard to determine active + state + +Commit f497a5bb87270174b8e0106b7eca1992d44ff15d made QWaylandDisplay +use the xdgshell's active state for QWindow::isActive(), instead of +using wl_keyboard activate/deactivate events. + +That seems to have been a misunderstanding, since xdgshell activation +is only supposed to be used to determine visual appearance, and there +is an explicit warning not to assume it means focus. + +This commit reverts this logic back to listening to wl_keyboard. +It adds a fallback when there is no wl_keyboard available to handle +activated/deactivated events through xdg-shell, in order to fix +QTBUG-53702. + +windowStates is handled so that we're not using the Xdg hint for +anything with QWindowSystemInterface::handleWindowStateChanged or +anything where we need to track only having one active. + +We are still exposing it for decorations, which is the only reason to +use the Xdghint over keyboard focus - so you can keep the toplevel +active whilst you show a popup. + +cherry-pick 40036a1b80e5234e6db7d5cbeff122aa7ee13e20 + +Change-Id: I4343d2ed9fb5b066cde95628ed0b4ccc84a424db +Reviewed-by: Eskil Abrahamsen Blomfeldt +--- + src/client/qwaylanddisplay.cpp | 19 +++++++++++-------- + src/client/qwaylanddisplay_p.h | 1 + + src/client/qwaylandwindow.cpp | 13 +++++++++++-- + src/client/qwaylandwindow_p.h | 1 + + .../qwaylandshellintegration_p.h | 7 +++---- + .../qwaylandxdgshellv5integration.cpp | 7 ------- + .../qwaylandxdgshellv5integration_p.h | 1 - + .../qwaylandxdgshellv6integration.cpp | 14 -------------- + .../qwaylandxdgshellv6integration_p.h | 1 - + .../xdg-shell/qwaylandxdgshell.cpp | 16 +++++----------- + .../xdg-shell/qwaylandxdgshellintegration.cpp | 14 -------------- + .../xdg-shell/qwaylandxdgshellintegration_p.h | 1 - + tests/auto/client/xdgshell/tst_xdgshell.cpp | 10 +++++++--- + 13 files changed, 39 insertions(+), 66 deletions(-) + +diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp +index 280e63bd..1568052e 100644 +--- a/src/client/qwaylanddisplay.cpp ++++ b/src/client/qwaylanddisplay.cpp +@@ -579,14 +579,10 @@ void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevic + if (mLastKeyboardFocus == keyboardFocus) + return; + +- if (mWaylandIntegration->mShellIntegration) { +- mWaylandIntegration->mShellIntegration->handleKeyboardFocusChanged(keyboardFocus, mLastKeyboardFocus); +- } else { +- if (keyboardFocus) +- handleWindowActivated(keyboardFocus); +- if (mLastKeyboardFocus) +- handleWindowDeactivated(mLastKeyboardFocus); +- } ++ if (keyboardFocus) ++ handleWindowActivated(keyboardFocus); ++ if (mLastKeyboardFocus) ++ handleWindowDeactivated(mLastKeyboardFocus); + + mLastKeyboardFocus = keyboardFocus; + } +@@ -631,6 +627,13 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const + return mInputDevices.isEmpty() ? 0 : mInputDevices.first(); + } + ++bool QWaylandDisplay::isKeyboardAvailable() const ++{ ++ return std::any_of( ++ mInputDevices.constBegin(), mInputDevices.constEnd(), ++ [this](const QWaylandInputDevice *device) { return device->keyboard() != nullptr; }); ++} ++ + #if QT_CONFIG(cursor) + + QWaylandCursor *QWaylandDisplay::waylandCursor() +diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h +index 1bad8b67..15104d65 100644 +--- a/src/client/qwaylanddisplay_p.h ++++ b/src/client/qwaylanddisplay_p.h +@@ -219,6 +219,7 @@ public: + void destroyFrameQueue(const FrameQueue &q); + void dispatchQueueWhile(wl_event_queue *queue, std::function condition, int timeout = -1); + ++ bool isKeyboardAvailable() const; + public slots: + void blockingReadEvents(); + void flushRequests(); +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index d3f28d68..b363c352 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -96,7 +96,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) + QWaylandWindow::~QWaylandWindow() + { + mDisplay->destroyFrameQueue(mFrameQueue); +- mDisplay->handleWindowDestroyed(this); + + delete mWindowDecoration; + +@@ -266,6 +265,8 @@ void QWaylandWindow::reset() + + mMask = QRegion(); + mQueuedBuffer = nullptr; ++ ++ mDisplay->handleWindowDestroyed(this); + } + + QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface) +@@ -1105,10 +1106,18 @@ bool QWaylandWindow::setMouseGrabEnabled(bool grab) + return true; + } + ++Qt::WindowStates QWaylandWindow::windowStates() const ++{ ++ return mLastReportedWindowStates; ++} ++ + void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states) + { + createDecoration(); +- QWindowSystemInterface::handleWindowStateChanged(window(), states, mLastReportedWindowStates); ++ Qt::WindowStates statesWithoutActive = states & ~Qt::WindowActive; ++ Qt::WindowStates lastStatesWithoutActive = mLastReportedWindowStates & ~Qt::WindowActive; ++ QWindowSystemInterface::handleWindowStateChanged(window(), statesWithoutActive, ++ lastStatesWithoutActive); + mLastReportedWindowStates = states; + } + +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index 01337cff..fb3ed606 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -148,6 +148,7 @@ public: + void setWindowState(Qt::WindowStates states) override; + void setWindowFlags(Qt::WindowFlags flags) override; + void handleWindowStatesChanged(Qt::WindowStates states); ++ Qt::WindowStates windowStates() const; + + void raise() override; + void lower() override; +diff --git a/src/client/shellintegration/qwaylandshellintegration_p.h b/src/client/shellintegration/qwaylandshellintegration_p.h +index ccad0048..4cc9b3b8 100644 +--- a/src/client/shellintegration/qwaylandshellintegration_p.h ++++ b/src/client/shellintegration/qwaylandshellintegration_p.h +@@ -73,11 +73,10 @@ public: + return true; + } + virtual QWaylandShellSurface *createShellSurface(QWaylandWindow *window) = 0; ++ // kept for binary compat with layer-shell-qt + virtual void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) { +- if (newFocus) +- m_display->handleWindowActivated(newFocus); +- if (oldFocus) +- m_display->handleWindowDeactivated(oldFocus); ++ Q_UNUSED(newFocus); ++ Q_UNUSED(oldFocus); + } + virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) { + Q_UNUSED(resource); +diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp +index 4e25949f..cfc60939 100644 +--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp ++++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp +@@ -85,13 +85,6 @@ QWaylandShellSurface *QWaylandXdgShellV5Integration::createShellSurface(QWayland + return m_xdgShell->createXdgSurface(window); + } + +-void QWaylandXdgShellV5Integration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) { +- if (newFocus && qobject_cast(newFocus->shellSurface())) +- m_display->handleWindowActivated(newFocus); +- if (oldFocus && qobject_cast(oldFocus->shellSurface())) +- m_display->handleWindowDeactivated(oldFocus); +-} +- + } + + QT_END_NAMESPACE +diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h +index ce6bdb9e..aed88670 100644 +--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h ++++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h +@@ -67,7 +67,6 @@ public: + QWaylandXdgShellV5Integration() {} + bool initialize(QWaylandDisplay *display) override; + QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; +- void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; + + private: + QScopedPointer m_xdgShell; +diff --git a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp +index 03164316..e8da8ba1 100644 +--- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp ++++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp +@@ -68,20 +68,6 @@ QWaylandShellSurface *QWaylandXdgShellV6Integration::createShellSurface(QWayland + return m_xdgShell->getXdgSurface(window); + } + +-void QWaylandXdgShellV6Integration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) +-{ +- if (newFocus) { +- auto *xdgSurface = qobject_cast(newFocus->shellSurface()); +- if (xdgSurface && !xdgSurface->handlesActiveState()) +- m_display->handleWindowActivated(newFocus); +- } +- if (oldFocus && qobject_cast(oldFocus->shellSurface())) { +- auto *xdgSurface = qobject_cast(oldFocus->shellSurface()); +- if (xdgSurface && !xdgSurface->handlesActiveState()) +- m_display->handleWindowDeactivated(oldFocus); +- } +-} +- + } + + QT_END_NAMESPACE +diff --git a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h +index 261f8cbb..c1bcd5c6 100644 +--- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h ++++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration_p.h +@@ -65,7 +65,6 @@ public: + QWaylandXdgShellV6Integration() {} + bool initialize(QWaylandDisplay *display) override; + QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; +- void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; + + private: + QScopedPointer m_xdgShell; +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +index 1f51562b..b7383e19 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +@@ -67,11 +67,6 @@ QWaylandXdgSurface::Toplevel::Toplevel(QWaylandXdgSurface *xdgSurface) + + QWaylandXdgSurface::Toplevel::~Toplevel() + { +- if (m_applied.states & Qt::WindowActive) { +- QWaylandWindow *window = m_xdgSurface->window(); +- window->display()->handleWindowDeactivated(window); +- } +- + // The protocol spec requires that the decoration object is deleted before xdg_toplevel. + delete m_decoration; + m_decoration = nullptr; +@@ -85,16 +80,15 @@ void QWaylandXdgSurface::Toplevel::applyConfigure() + if (!(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen))) + m_normalSize = m_xdgSurface->m_window->windowFrameGeometry().size(); + +- if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive)) ++ if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive) ++ && !m_xdgSurface->m_window->display()->isKeyboardAvailable()) + m_xdgSurface->m_window->display()->handleWindowActivated(m_xdgSurface->m_window); + +- if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive)) ++ if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive) ++ && !m_xdgSurface->m_window->display()->isKeyboardAvailable()) + m_xdgSurface->m_window->display()->handleWindowDeactivated(m_xdgSurface->m_window); + +- // TODO: none of the other plugins send WindowActive either, but is it on purpose? +- Qt::WindowStates statesWithoutActive = m_pending.states & ~Qt::WindowActive; +- +- m_xdgSurface->m_window->handleWindowStatesChanged(statesWithoutActive); ++ m_xdgSurface->m_window->handleWindowStatesChanged(m_pending.states); + + if (m_pending.size.isEmpty()) { + // An empty size in the configure means it's up to the client to choose the size +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp +index 8769d971..da0dd6a7 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp +@@ -69,20 +69,6 @@ QWaylandShellSurface *QWaylandXdgShellIntegration::createShellSurface(QWaylandWi + return m_xdgShell->getXdgSurface(window); + } + +-void QWaylandXdgShellIntegration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) +-{ +- if (newFocus) { +- auto *xdgSurface = qobject_cast(newFocus->shellSurface()); +- if (xdgSurface && !xdgSurface->handlesActiveState()) +- m_display->handleWindowActivated(newFocus); +- } +- if (oldFocus && qobject_cast(oldFocus->shellSurface())) { +- auto *xdgSurface = qobject_cast(oldFocus->shellSurface()); +- if (xdgSurface && !xdgSurface->handlesActiveState()) +- m_display->handleWindowDeactivated(oldFocus); +- } +-} +- + } + + QT_END_NAMESPACE +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h +index b6caa6c9..2f929f98 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h +@@ -65,7 +65,6 @@ public: + QWaylandXdgShellIntegration() {} + bool initialize(QWaylandDisplay *display) override; + QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; +- void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; + + private: + QScopedPointer m_xdgShell; +diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp +index e2593314..73d1eb9c 100644 +--- a/tests/auto/client/xdgshell/tst_xdgshell.cpp ++++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp +@@ -31,6 +31,7 @@ + #include + #include + #include ++#include + + using namespace MockCompositor; + +@@ -155,9 +156,12 @@ void tst_xdgshell::configureStates() + // Toplevel windows don't know their position on xdg-shell + // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled + +-// QEXPECT_FAIL("", "configure has already been acked, we shouldn't have to wait for isActive", Continue); +-// QVERIFY(window.isActive()); +- QTRY_VERIFY(window.isActive()); // Just make sure it eventually get's set correctly ++ // window.windowstate() is driven by keyboard focus, however for decorations we want to follow ++ // XDGShell this is internal to QtWayland so it is queried directly ++ auto waylandWindow = static_cast(window.handle()); ++ Q_ASSERT(waylandWindow); ++ QTRY_VERIFY(waylandWindow->windowStates().testFlag( ++ Qt::WindowActive)); // Just make sure it eventually get's set correctly + + const QSize screenSize(640, 480); + const uint maximizedSerial = exec([=] { +-- +2.40.0 + diff --git a/SOURCES/0013-Client-do-not-empty-clipboard-when-a-new-popup-windo.patch b/SOURCES/0013-Client-do-not-empty-clipboard-when-a-new-popup-windo.patch new file mode 100644 index 0000000..fb12a01 --- /dev/null +++ b/SOURCES/0013-Client-do-not-empty-clipboard-when-a-new-popup-windo.patch @@ -0,0 +1,68 @@ +From 23327bb4cdc00b47e0f96efca2fe494540f01601 Mon Sep 17 00:00:00 2001 +From: Jan Grulich +Date: Fri, 16 Jul 2021 13:00:03 +0200 +Subject: [PATCH 13/55] Client: do not empty clipboard when a new popup/window + is opened + +If we open a new popup or a window within the same app we have to avoid +invalidating selection offer when losing focus, because it's still the +same client who has the focus and we might not get a new selection offer +by the compositor and therefore we would lose clipboard content. + +Fixes: QTBUG-93474 +Change-Id: Ia2ef826c2967b1daf1cdeb085e8dae66d090dbcf +Reviewed-by: Qt CI Bot +Reviewed-by: David Edmundson + +Cherry-pick: 1e57ebd501cfc2255300392cd4565cd034efeed8 +--- + src/client/qwaylanddisplay.cpp | 13 +++++++++++++ + src/client/qwaylandinputdevice.cpp | 8 -------- + 2 files changed, 13 insertions(+), 8 deletions(-) + +diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp +index 1568052e..aa8808e9 100644 +--- a/src/client/qwaylanddisplay.cpp ++++ b/src/client/qwaylanddisplay.cpp +@@ -601,6 +601,19 @@ void QWaylandDisplay::handleWaylandSync() + QWindow *activeWindow = mActiveWindows.empty() ? nullptr : mActiveWindows.last()->window(); + if (activeWindow != QGuiApplication::focusWindow()) + QWindowSystemInterface::handleWindowActivated(activeWindow); ++ ++ if (!activeWindow) { ++ if (lastInputDevice()) { ++#if QT_CONFIG(clipboard) ++ if (auto *dataDevice = lastInputDevice()->dataDevice()) ++ dataDevice->invalidateSelectionOffer(); ++#endif ++#if QT_CONFIG(wayland_client_primary_selection) ++ if (auto *device = lastInputDevice()->primarySelectionDevice()) ++ device->invalidateSelectionOffer(); ++#endif ++ } ++ } + } + + const wl_callback_listener QWaylandDisplay::syncCallbackListener = { +diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp +index e931d1f5..5d704795 100644 +--- a/src/client/qwaylandinputdevice.cpp ++++ b/src/client/qwaylandinputdevice.cpp +@@ -1304,14 +1304,6 @@ void QWaylandInputDevice::Keyboard::handleFocusDestroyed() + void QWaylandInputDevice::Keyboard::handleFocusLost() + { + mFocus = nullptr; +-#if QT_CONFIG(clipboard) +- if (auto *dataDevice = mParent->dataDevice()) +- dataDevice->invalidateSelectionOffer(); +-#endif +-#if QT_CONFIG(wayland_client_primary_selection) +- if (auto *device = mParent->primarySelectionDevice()) +- device->invalidateSelectionOffer(); +-#endif + mParent->mQDisplay->handleKeyboardFocusChanged(mParent); + mRepeatTimer.stop(); + } +-- +2.40.0 + diff --git a/SOURCES/0014-Client-Implement-DataDeviceV3.patch b/SOURCES/0014-Client-Implement-DataDeviceV3.patch new file mode 100644 index 0000000..76c63d1 --- /dev/null +++ b/SOURCES/0014-Client-Implement-DataDeviceV3.patch @@ -0,0 +1,513 @@ +From e0e004c77140da5e454dadf4d6809c1603b5735d Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Tue, 16 Feb 2021 09:51:47 +0000 +Subject: [PATCH 14/55] Client: Implement DataDeviceV3 + +DataDeviceV2 fixes a leak of DataDevice resources. + +DataDeviceV3 brings multiple improvements: + +Action negotiation. The source announces which actions are supported, +the target then announces which subset of those action the target +supports and a preferred action. After negotiation both the source and +target are notified of which action is to be performed. + +Drag sources are now notified when contents are dropped and when a +client has finished with the drag and drop operation. + +A good test is the draggableicons example in QtBase. + +Change-Id: I55e9759ca5a2e4218d02d863144a64ade53ef764 +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 283a2d61d03315495a52d82f356e7cb5292f4bb4) +--- + src/client/qwaylanddatadevice.cpp | 84 ++++++++++++++----- + src/client/qwaylanddatadevice_p.h | 8 +- + src/client/qwaylanddatadevicemanager.cpp | 4 +- + src/client/qwaylanddatadevicemanager_p.h | 2 +- + src/client/qwaylanddataoffer.cpp | 25 ++++++ + src/client/qwaylanddataoffer_p.h | 4 + + src/client/qwaylanddatasource.cpp | 27 +++++- + src/client/qwaylanddatasource_p.h | 10 ++- + src/client/qwaylanddisplay.cpp | 2 +- + src/client/qwaylanddnd.cpp | 24 +++--- + src/client/qwaylanddnd_p.h | 7 +- + .../client/datadevicev1/tst_datadevicev1.cpp | 2 +- + 12 files changed, 153 insertions(+), 46 deletions(-) + +diff --git a/src/client/qwaylanddatadevice.cpp b/src/client/qwaylanddatadevice.cpp +index bbd2d568..fbb5aa91 100644 +--- a/src/client/qwaylanddatadevice.cpp ++++ b/src/client/qwaylanddatadevice.cpp +@@ -72,6 +72,8 @@ QWaylandDataDevice::QWaylandDataDevice(QWaylandDataDeviceManager *manager, QWayl + + QWaylandDataDevice::~QWaylandDataDevice() + { ++ if (wl_data_device_get_version(object()) >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION) ++ release(); + } + + QWaylandDataOffer *QWaylandDataDevice::selectionOffer() const +@@ -110,7 +112,7 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const + return m_dragOffer.data(); + } + +-bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) ++bool QWaylandDataDevice::startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon) + { + auto *seat = m_display->currentInputDevice(); + auto *origin = seat->pointerFocus(); +@@ -123,8 +125,28 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) + } + + m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData)); ++ ++ if (wl_data_device_get_version(object()) >= 3) ++ m_dragSource->set_actions(dropActionsToWl(supportedActions)); ++ + connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled); +- connect(m_dragSource.data(), &QWaylandDataSource::targetChanged, this, &QWaylandDataDevice::dragSourceTargetChanged); ++ connect(m_dragSource.data(), &QWaylandDataSource::dndResponseUpdated, this, [this](bool accepted, Qt::DropAction action) { ++ auto drag = static_cast(QGuiApplicationPrivate::platformIntegration()->drag()); ++ // in old versions drop action is not set, so we guess ++ if (wl_data_source_get_version(m_dragSource->object()) < 3) { ++ drag->setResponse(accepted); ++ } else { ++ QPlatformDropQtResponse response(accepted, action); ++ drag->setResponse(response); ++ } ++ }); ++ connect(m_dragSource.data(), &QWaylandDataSource::dndDropped, this, [](bool accepted, Qt::DropAction action) { ++ QPlatformDropQtResponse response(accepted, action); ++ static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setDropResponse(response); ++ }); ++ connect(m_dragSource.data(), &QWaylandDataSource::finished, this, []() { ++ static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(); ++ }); + + start_drag(m_dragSource->object(), origin->wlSurface(), icon->wlSurface(), m_display->currentInputDevice()->serial()); + return true; +@@ -153,7 +175,7 @@ void QWaylandDataDevice::data_device_drop() + supportedActions = drag->supportedActions(); + } else if (m_dragOffer) { + dragData = m_dragOffer->mimeData(); +- supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; ++ supportedActions = m_dragOffer->supportedActions(); + } else { + return; + } +@@ -163,7 +185,11 @@ void QWaylandDataDevice::data_device_drop() + QGuiApplication::keyboardModifiers()); + + if (drag) { +- static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(response); ++ auto drag = static_cast(QGuiApplicationPrivate::platformIntegration()->drag()); ++ drag->setDropResponse(response); ++ drag->finishDrag(); ++ } else if (m_dragOffer) { ++ m_dragOffer->finish(); + } + } + +@@ -187,7 +213,7 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, + supportedActions = drag->supportedActions(); + } else if (m_dragOffer) { + dragData = m_dragOffer->mimeData(); +- supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; ++ supportedActions = m_dragOffer->supportedActions(); + } + + const QPlatformDragQtResponse &response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions, +@@ -198,11 +224,7 @@ void QWaylandDataDevice::data_device_enter(uint32_t serial, wl_surface *surface, + static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response); + } + +- if (response.isAccepted()) { +- wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, m_dragOffer->firstFormat().toUtf8().constData()); +- } else { +- wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, nullptr); +- } ++ sendResponse(supportedActions, response); + } + + void QWaylandDataDevice::data_device_leave() +@@ -236,10 +258,10 @@ void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixe + supportedActions = drag->supportedActions(); + } else { + dragData = m_dragOffer->mimeData(); +- supportedActions = Qt::CopyAction | Qt::MoveAction | Qt::LinkAction; ++ supportedActions = m_dragOffer->supportedActions(); + } + +- QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions, ++ const QPlatformDragQtResponse response = QWindowSystemInterface::handleDrag(m_dragWindow, dragData, m_dragPoint, supportedActions, + QGuiApplication::mouseButtons(), + QGuiApplication::keyboardModifiers()); + +@@ -247,11 +269,7 @@ void QWaylandDataDevice::data_device_motion(uint32_t time, wl_fixed_t x, wl_fixe + static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->setResponse(response); + } + +- if (response.isAccepted()) { +- wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, m_dragOffer->firstFormat().toUtf8().constData()); +- } else { +- wl_data_offer_accept(m_dragOffer->object(), m_enterSerial, nullptr); +- } ++ sendResponse(supportedActions, response); + } + #endif // QT_CONFIG(draganddrop) + +@@ -281,11 +299,6 @@ void QWaylandDataDevice::dragSourceCancelled() + m_dragSource.reset(); + } + +-void QWaylandDataDevice::dragSourceTargetChanged(const QString &mimeType) +-{ +- static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->updateTarget(mimeType); +-} +- + QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) const + { + QPoint pnt(wl_fixed_to_int(x), wl_fixed_to_int(y)); +@@ -298,6 +311,33 @@ QPoint QWaylandDataDevice::calculateDragPosition(int x, int y, QWindow *wnd) con + } + return pnt; + } ++ ++void QWaylandDataDevice::sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response) ++{ ++ if (response.isAccepted()) { ++ if (wl_data_device_get_version(object()) >= 3) ++ m_dragOffer->set_actions(dropActionsToWl(supportedActions), dropActionsToWl(response.acceptedAction())); ++ ++ m_dragOffer->accept(m_enterSerial, m_dragOffer->firstFormat()); ++ } else { ++ m_dragOffer->accept(m_enterSerial, QString()); ++ } ++} ++ ++int QWaylandDataDevice::dropActionsToWl(Qt::DropActions actions) ++{ ++ ++ int wlActions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; ++ if (actions & Qt::CopyAction) ++ wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; ++ if (actions & (Qt::MoveAction | Qt::TargetMoveAction)) ++ wlActions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; ++ ++ // wayland does not support LinkAction at the time of writing ++ return wlActions; ++} ++ ++ + #endif // QT_CONFIG(draganddrop) + + } +diff --git a/src/client/qwaylanddatadevice_p.h b/src/client/qwaylanddatadevice_p.h +index 16c3ad28..801dcc2c 100644 +--- a/src/client/qwaylanddatadevice_p.h ++++ b/src/client/qwaylanddatadevice_p.h +@@ -64,6 +64,7 @@ QT_REQUIRE_CONFIG(wayland_datadevice); + QT_BEGIN_NAMESPACE + + class QMimeData; ++class QPlatformDragQtResponse; + class QWindow; + + namespace QtWaylandClient { +@@ -89,7 +90,7 @@ public: + + #if QT_CONFIG(draganddrop) + QWaylandDataOffer *dragOffer() const; +- bool startDrag(QMimeData *mimeData, QWaylandWindow *icon); ++ bool startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon); + void cancelDrag(); + #endif + +@@ -109,13 +110,16 @@ private Q_SLOTS: + + #if QT_CONFIG(draganddrop) + void dragSourceCancelled(); +- void dragSourceTargetChanged(const QString &mimeType); + #endif + + private: + #if QT_CONFIG(draganddrop) + QPoint calculateDragPosition(int x, int y, QWindow *wnd) const; + #endif ++ void sendResponse(Qt::DropActions supportedActions, const QPlatformDragQtResponse &response); ++ ++ static int dropActionsToWl(Qt::DropActions dropActions); ++ + + QWaylandDisplay *m_display = nullptr; + QWaylandInputDevice *m_inputDevice = nullptr; +diff --git a/src/client/qwaylanddatadevicemanager.cpp b/src/client/qwaylanddatadevicemanager.cpp +index 35d67307..6dc4f77f 100644 +--- a/src/client/qwaylanddatadevicemanager.cpp ++++ b/src/client/qwaylanddatadevicemanager.cpp +@@ -50,8 +50,8 @@ QT_BEGIN_NAMESPACE + + namespace QtWaylandClient { + +-QWaylandDataDeviceManager::QWaylandDataDeviceManager(QWaylandDisplay *display, uint32_t id) +- : wl_data_device_manager(display->wl_registry(), id, 1) ++QWaylandDataDeviceManager::QWaylandDataDeviceManager(QWaylandDisplay *display, int version, uint32_t id) ++ : wl_data_device_manager(display->wl_registry(), id, qMin(version, 3)) + , m_display(display) + { + // Create transfer devices for all input devices. +diff --git a/src/client/qwaylanddatadevicemanager_p.h b/src/client/qwaylanddatadevicemanager_p.h +index bd05c0fb..510d9be4 100644 +--- a/src/client/qwaylanddatadevicemanager_p.h ++++ b/src/client/qwaylanddatadevicemanager_p.h +@@ -68,7 +68,7 @@ class QWaylandInputDevice; + class Q_WAYLAND_CLIENT_EXPORT QWaylandDataDeviceManager : public QtWayland::wl_data_device_manager + { + public: +- QWaylandDataDeviceManager(QWaylandDisplay *display, uint32_t id); ++ QWaylandDataDeviceManager(QWaylandDisplay *display, int version, uint32_t id); + ~QWaylandDataDeviceManager() override; + + QWaylandDataDevice *getDataDevice(QWaylandInputDevice *inputDevice); +diff --git a/src/client/qwaylanddataoffer.cpp b/src/client/qwaylanddataoffer.cpp +index 2297e8a1..c9e158cc 100644 +--- a/src/client/qwaylanddataoffer.cpp ++++ b/src/client/qwaylanddataoffer.cpp +@@ -82,6 +82,15 @@ QMimeData *QWaylandDataOffer::mimeData() + return m_mimeData.data(); + } + ++Qt::DropActions QWaylandDataOffer::supportedActions() const ++{ ++ if (wl_data_offer_get_version(const_cast<::wl_data_offer*>(object())) < 3) { ++ return Qt::MoveAction | Qt::CopyAction; ++ } ++ ++ return m_supportedActions; ++} ++ + void QWaylandDataOffer::startReceiving(const QString &mimeType, int fd) + { + receive(mimeType, fd); +@@ -93,6 +102,22 @@ void QWaylandDataOffer::data_offer_offer(const QString &mime_type) + m_mimeData->appendFormat(mime_type); + } + ++void QWaylandDataOffer::data_offer_action(uint32_t dnd_action) ++{ ++ Q_UNUSED(dnd_action); ++ // This is the compositor telling the drag target what action it should perform ++ // It does not map nicely into Qt final drop semantics, other than pretending there is only one supported action? ++} ++ ++void QWaylandDataOffer::data_offer_source_actions(uint32_t source_actions) ++{ ++ m_supportedActions = Qt::DropActions(); ++ if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) ++ m_supportedActions |= Qt::MoveAction; ++ if (source_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) ++ m_supportedActions |= Qt::CopyAction; ++} ++ + QWaylandMimeData::QWaylandMimeData(QWaylandAbstractDataOffer *dataOffer) + : m_dataOffer(dataOffer) + { +diff --git a/src/client/qwaylanddataoffer_p.h b/src/client/qwaylanddataoffer_p.h +index 9cf1483c..6f667398 100644 +--- a/src/client/qwaylanddataoffer_p.h ++++ b/src/client/qwaylanddataoffer_p.h +@@ -82,6 +82,7 @@ public: + explicit QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer); + ~QWaylandDataOffer() override; + QMimeData *mimeData() override; ++ Qt::DropActions supportedActions() const; + + QString firstFormat() const; + +@@ -89,10 +90,13 @@ public: + + protected: + void data_offer_offer(const QString &mime_type) override; ++ void data_offer_source_actions(uint32_t source_actions) override; ++ void data_offer_action(uint32_t dnd_action) override; + + private: + QWaylandDisplay *m_display = nullptr; + QScopedPointer m_mimeData; ++ Qt::DropActions m_supportedActions; + }; + + +diff --git a/src/client/qwaylanddatasource.cpp b/src/client/qwaylanddatasource.cpp +index f45122fb..5599cbd4 100644 +--- a/src/client/qwaylanddatasource.cpp ++++ b/src/client/qwaylanddatasource.cpp +@@ -101,7 +101,32 @@ void QWaylandDataSource::data_source_send(const QString &mime_type, int32_t fd) + + void QWaylandDataSource::data_source_target(const QString &mime_type) + { +- Q_EMIT targetChanged(mime_type); ++ m_accepted = !mime_type.isEmpty(); ++ Q_EMIT dndResponseUpdated(m_accepted, m_dropAction); ++} ++ ++void QWaylandDataSource::data_source_action(uint32_t action) ++{ ++ Qt::DropAction qtAction = Qt::IgnoreAction; ++ ++ if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) ++ qtAction = Qt::MoveAction; ++ else if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) ++ qtAction = Qt::CopyAction; ++ ++ m_dropAction = qtAction; ++ Q_EMIT dndResponseUpdated(m_accepted, m_dropAction); ++} ++ ++void QWaylandDataSource::data_source_dnd_finished() ++{ ++ Q_EMIT finished(); ++} ++ ++void QWaylandDataSource::data_source_dnd_drop_performed() ++{ ++ ++ Q_EMIT dndDropped(m_accepted, m_dropAction); + } + + } +diff --git a/src/client/qwaylanddatasource_p.h b/src/client/qwaylanddatasource_p.h +index 25afff79..96f07bc3 100644 +--- a/src/client/qwaylanddatasource_p.h ++++ b/src/client/qwaylanddatasource_p.h +@@ -77,17 +77,25 @@ public: + QMimeData *mimeData() const; + + Q_SIGNALS: +- void targetChanged(const QString &mime_type); + void cancelled(); ++ void finished(); ++ ++ void dndResponseUpdated(bool accepted, Qt::DropAction action); ++ void dndDropped(bool accepted, Qt::DropAction action); + + protected: + void data_source_cancelled() override; + void data_source_send(const QString &mime_type, int32_t fd) override; + void data_source_target(const QString &mime_type) override; ++ void data_source_dnd_drop_performed() override; ++ void data_source_dnd_finished() override; ++ void data_source_action(uint32_t action) override; + + private: + QWaylandDisplay *m_display = nullptr; + QMimeData *m_mime_data = nullptr; ++ bool m_accepted = false; ++ Qt::DropAction m_dropAction = Qt::IgnoreAction; + }; + + } +diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp +index aa8808e9..2d298532 100644 +--- a/src/client/qwaylanddisplay.cpp ++++ b/src/client/qwaylanddisplay.cpp +@@ -356,7 +356,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin + mInputDevices.append(inputDevice); + #if QT_CONFIG(wayland_datadevice) + } else if (interface == QStringLiteral("wl_data_device_manager")) { +- mDndSelectionHandler.reset(new QWaylandDataDeviceManager(this, id)); ++ mDndSelectionHandler.reset(new QWaylandDataDeviceManager(this, version, id)); + #endif + } else if (interface == QStringLiteral("qt_surface_extension")) { + mWindowExtension.reset(new QtWayland::qt_surface_extension(registry, id, 1)); +diff --git a/src/client/qwaylanddnd.cpp b/src/client/qwaylanddnd.cpp +index 6535aa16..97ee5b2e 100644 +--- a/src/client/qwaylanddnd.cpp ++++ b/src/client/qwaylanddnd.cpp +@@ -66,7 +66,7 @@ void QWaylandDrag::startDrag() + { + QBasicDrag::startDrag(); + QWaylandWindow *icon = static_cast(shapedPixmapWindow()->handle()); +- if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon)) { ++ if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), drag()->supportedActions(), icon)) { + icon->addAttachOffset(-drag()->hotSpot()); + } else { + // Cancelling immediately does not work, since the event loop for QDrag::exec is started +@@ -103,31 +103,31 @@ void QWaylandDrag::endDrag() + m_display->currentInputDevice()->handleEndDrag(); + } + +-void QWaylandDrag::updateTarget(const QString &mimeType) ++void QWaylandDrag::setResponse(bool accepted) + { +- setCanDrop(!mimeType.isEmpty()); +- +- if (canDrop()) { +- updateCursor(defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers())); +- } else { +- updateCursor(Qt::IgnoreAction); +- } ++ // This method is used for old DataDevices where the drag action is not communicated ++ Qt::DropAction action = defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers()); ++ setResponse(QPlatformDropQtResponse(accepted, action)); + } + +-void QWaylandDrag::setResponse(const QPlatformDragQtResponse &response) ++void QWaylandDrag::setResponse(const QPlatformDropQtResponse &response) + { + setCanDrop(response.isAccepted()); + + if (canDrop()) { +- updateCursor(defaultAction(drag()->supportedActions(), m_display->currentInputDevice()->modifiers())); ++ updateCursor(response.acceptedAction()); + } else { + updateCursor(Qt::IgnoreAction); + } + } + +-void QWaylandDrag::finishDrag(const QPlatformDropQtResponse &response) ++void QWaylandDrag::setDropResponse(const QPlatformDropQtResponse &response) + { + setExecutedDropAction(response.acceptedAction()); ++} ++ ++void QWaylandDrag::finishDrag() ++{ + QKeyEvent event(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); + eventFilter(shapedPixmapWindow(), &event); + } +diff --git a/src/client/qwaylanddnd_p.h b/src/client/qwaylanddnd_p.h +index 474fe2ab..747f0190 100644 +--- a/src/client/qwaylanddnd_p.h ++++ b/src/client/qwaylanddnd_p.h +@@ -71,9 +71,10 @@ public: + QWaylandDrag(QWaylandDisplay *display); + ~QWaylandDrag() override; + +- void updateTarget(const QString &mimeType); +- void setResponse(const QPlatformDragQtResponse &response); +- void finishDrag(const QPlatformDropQtResponse &response); ++ void setResponse(bool accepted); ++ void setResponse(const QPlatformDropQtResponse &response); ++ void setDropResponse(const QPlatformDropQtResponse &response); ++ void finishDrag(); + + protected: + void startDrag() override; +diff --git a/tests/auto/client/datadevicev1/tst_datadevicev1.cpp b/tests/auto/client/datadevicev1/tst_datadevicev1.cpp +index 1568b3b9..067410d0 100644 +--- a/tests/auto/client/datadevicev1/tst_datadevicev1.cpp ++++ b/tests/auto/client/datadevicev1/tst_datadevicev1.cpp +@@ -35,7 +35,7 @@ + + using namespace MockCompositor; + +-constexpr int dataDeviceVersion = 1; ++constexpr int dataDeviceVersion = 3; + + class DataDeviceCompositor : public DefaultCompositor { + public: +-- +2.40.0 + diff --git a/SOURCES/0015-Client-Delay-deletion-of-QDrag-object-until-after-we.patch b/SOURCES/0015-Client-Delay-deletion-of-QDrag-object-until-after-we.patch new file mode 100644 index 0000000..a07907b --- /dev/null +++ b/SOURCES/0015-Client-Delay-deletion-of-QDrag-object-until-after-we.patch @@ -0,0 +1,67 @@ +From f3900786ebe46aefbecbb2a37cb175846ca0dfc2 Mon Sep 17 00:00:00 2001 +From: Arjen Hiemstra +Date: Thu, 18 Nov 2021 13:05:30 +0100 +Subject: [PATCH 15/55] Client: Delay deletion of QDrag object until after + we're done with it + +In certain cases, most notably when performing drag and drop operations +with touch, the QDrag object gets deleted before data_source_send is +executed. This then tries to access a deleted data_source, crashing the +client. + +To avoid this, we indicate we want the QDrag object to stay around and +then delete it in QWaylandDrag::finishDrag, which with data_device v3 is +guaranteed to be called after everyone is done with the data source. + +Change-Id: I6a2f5a219f58d1b721a9fec33c57d26d2c522ec9 +Reviewed-by: David Edmundson +(cherry picked from commit 39e3290efa2dd40722fa3322284cae3b01ccedf4) +--- + src/client/qwaylanddnd.cpp | 11 +++++++++++ + src/client/qwaylanddnd_p.h | 1 + + 2 files changed, 12 insertions(+) + +diff --git a/src/client/qwaylanddnd.cpp b/src/client/qwaylanddnd.cpp +index 97ee5b2e..7c53f5fa 100644 +--- a/src/client/qwaylanddnd.cpp ++++ b/src/client/qwaylanddnd.cpp +@@ -80,6 +80,9 @@ void QWaylandDrag::cancel() + QBasicDrag::cancel(); + + m_display->currentInputDevice()->dataDevice()->cancelDrag(); ++ ++ if (drag()) ++ drag()->deleteLater(); + } + + void QWaylandDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) +@@ -130,6 +133,14 @@ void QWaylandDrag::finishDrag() + { + QKeyEvent event(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); + eventFilter(shapedPixmapWindow(), &event); ++ ++ if (drag()) ++ drag()->deleteLater(); ++} ++ ++bool QWaylandDrag::ownsDragObject() const ++{ ++ return true; + } + + } +diff --git a/src/client/qwaylanddnd_p.h b/src/client/qwaylanddnd_p.h +index 747f0190..46f629ac 100644 +--- a/src/client/qwaylanddnd_p.h ++++ b/src/client/qwaylanddnd_p.h +@@ -83,6 +83,7 @@ protected: + void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; + void endDrag() override; + ++ bool ownsDragObject() const override; + + private: + QWaylandDisplay *m_display = nullptr; +-- +2.40.0 + diff --git a/SOURCES/0016-Client-Avoid-processing-of-events-when-showing-windo.patch b/SOURCES/0016-Client-Avoid-processing-of-events-when-showing-windo.patch new file mode 100644 index 0000000..de9483c --- /dev/null +++ b/SOURCES/0016-Client-Avoid-processing-of-events-when-showing-windo.patch @@ -0,0 +1,38 @@ +From 9edd2a05c79a1c40867440826c830c76136152da Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Sun, 14 Nov 2021 13:54:19 +0000 +Subject: [PATCH 16/55] Client: Avoid processing of events when showing windows + +The only time we want to dispatch events from the wayland socket is when +the application is waiting for external events. Doing so at any other +time will cause unpredictable behavior in client code. + +This caused a crash downstream where we had outputs get altered whilst +itterating through outputs, which shouldn't happen. + +There is no benefit to flushing here, it won't make anything appear +faster as we haven't attached the buffer yet. + +Change-Id: Ie13eae4012dab96a93d8810f468d1343402b8c28 +Reviewed-by: Qt CI Bot +Reviewed-by: Aleix Pol Gonzalez +(cherry picked from commit 46ed85a80b28d519cf5887bbdce55d1bf57886c3) +--- + src/client/qwaylandwindow.cpp | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index b363c352..b98435ed 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -437,7 +437,6 @@ void QWaylandWindow::setVisible(bool visible) + if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip) + activePopups << this; + initWindow(); +- mDisplay->flushRequests(); + + setGeometry(windowGeometry()); + // Don't flush the events here, or else the newly visible window may start drawing, but since +-- +2.40.0 + diff --git a/SOURCES/0017-Handle-registry_global-out-of-constructor.patch b/SOURCES/0017-Handle-registry_global-out-of-constructor.patch new file mode 100644 index 0000000..96206d3 --- /dev/null +++ b/SOURCES/0017-Handle-registry_global-out-of-constructor.patch @@ -0,0 +1,85 @@ +From 5889bb845d951a7877201bca97ffe4bb2491d966 Mon Sep 17 00:00:00 2001 +From: Elvis Lee +Date: Thu, 18 Feb 2021 15:45:49 +0900 +Subject: [PATCH 17/55] Handle registry_global out of constructor + +Factory functions in QWaylandDisplay::registry_global() can be overridden. +Later, other classes instantiated in the registry_global can support +platform specific implementation with inheritance and some factory function. + +Change-Id: I92ce574e049b8c91587687cc7c30611f3dfdbe56 +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 3793a82038682db77966ea5daf8e75964e4250fe) +--- + src/client/qwaylanddisplay.cpp | 19 ++++++++++++------- + src/client/qwaylanddisplay_p.h | 2 ++ + src/client/qwaylandintegration.cpp | 3 +++ + 3 files changed, 17 insertions(+), 7 deletions(-) + +diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp +index 2d298532..97fb8cbe 100644 +--- a/src/client/qwaylanddisplay.cpp ++++ b/src/client/qwaylanddisplay.cpp +@@ -160,13 +160,6 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration) + if (!mXkbContext) + qCWarning(lcQpaWayland, "failed to create xkb context"); + #endif +- +- forceRoundTrip(); +- +- if (!mWaitingScreens.isEmpty()) { +- // Give wl_output.done and zxdg_output_v1.done events a chance to arrive +- forceRoundTrip(); +- } + } + + QWaylandDisplay::~QWaylandDisplay(void) +@@ -191,6 +184,18 @@ QWaylandDisplay::~QWaylandDisplay(void) + wl_display_disconnect(mDisplay); + } + ++// Steps which is called just after constructor. This separates registry_global() out of the constructor ++// so that factory functions in integration can be overridden. ++void QWaylandDisplay::initialize() ++{ ++ forceRoundTrip(); ++ ++ if (!mWaitingScreens.isEmpty()) { ++ // Give wl_output.done and zxdg_output_v1.done events a chance to arrive ++ forceRoundTrip(); ++ } ++} ++ + void QWaylandDisplay::ensureScreen() + { + if (!mScreens.empty() || mPlaceholderScreen) +diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h +index 15104d65..49820255 100644 +--- a/src/client/qwaylanddisplay_p.h ++++ b/src/client/qwaylanddisplay_p.h +@@ -131,6 +131,8 @@ public: + QWaylandDisplay(QWaylandIntegration *waylandIntegration); + ~QWaylandDisplay(void) override; + ++ void initialize(); ++ + #if QT_CONFIG(xkbcommon) + struct xkb_context *xkbContext() const { return mXkbContext.get(); } + #endif +diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp +index cd8569b1..8afecb31 100644 +--- a/src/client/qwaylandintegration.cpp ++++ b/src/client/qwaylandintegration.cpp +@@ -200,6 +200,9 @@ void QWaylandIntegration::initialize() + QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data()); + QObject::connect(sn, SIGNAL(activated(QSocketDescriptor)), mDisplay.data(), SLOT(flushRequests())); + ++ // Call after eventDispatcher is fully connected, for QWaylandDisplay::forceRoundTrip() ++ mDisplay->initialize(); ++ + // Qt does not support running with no screens + mDisplay->ensureScreen(); + } +-- +2.40.0 + diff --git a/SOURCES/0018-Connect-flushRequest-after-forceRoundTrip.patch b/SOURCES/0018-Connect-flushRequest-after-forceRoundTrip.patch new file mode 100644 index 0000000..3b9af70 --- /dev/null +++ b/SOURCES/0018-Connect-flushRequest-after-forceRoundTrip.patch @@ -0,0 +1,47 @@ +From 2e03c3b47fa91eb8db5b9a635c2f9dca2b4f5158 Mon Sep 17 00:00:00 2001 +From: Elvis Lee +Date: Wed, 17 Mar 2021 16:31:10 +0900 +Subject: [PATCH 18/55] Connect flushRequest after forceRoundTrip + +If flushRequest is connected with aboutToBlock, the flushRequest +may consumes all events so that processEvents might be blocked in forceRoundTrip. + +Change-Id: I12b2c506e8442bf0e75f6ab6e418d3e1eea6d68c +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 654a54755138c520c3a41210d8078196e9a2c1bf) +--- + src/client/qwaylandintegration.cpp | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp +index 8afecb31..661cea53 100644 +--- a/src/client/qwaylandintegration.cpp ++++ b/src/client/qwaylandintegration.cpp +@@ -192,10 +192,6 @@ QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const + + void QWaylandIntegration::initialize() + { +- QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher; +- QObject::connect(dispatcher, SIGNAL(aboutToBlock()), mDisplay.data(), SLOT(flushRequests())); +- QObject::connect(dispatcher, SIGNAL(awake()), mDisplay.data(), SLOT(flushRequests())); +- + int fd = wl_display_get_fd(mDisplay->wl_display()); + QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data()); + QObject::connect(sn, SIGNAL(activated(QSocketDescriptor)), mDisplay.data(), SLOT(flushRequests())); +@@ -203,6 +199,13 @@ void QWaylandIntegration::initialize() + // Call after eventDispatcher is fully connected, for QWaylandDisplay::forceRoundTrip() + mDisplay->initialize(); + ++ // But the aboutToBlock() and awake() should be connected after initializePlatform(). ++ // Otherwise the connected flushRequests() may consumes up all events before processEvents starts to wait, ++ // so that processEvents(QEventLoop::WaitForMoreEvents) may be blocked in the forceRoundTrip(). ++ QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher; ++ QObject::connect(dispatcher, SIGNAL(aboutToBlock()), mDisplay.data(), SLOT(flushRequests())); ++ QObject::connect(dispatcher, SIGNAL(awake()), mDisplay.data(), SLOT(flushRequests())); ++ + // Qt does not support running with no screens + mDisplay->ensureScreen(); + } +-- +2.40.0 + diff --git a/SOURCES/0019-Move-the-wayland-socket-polling-to-a-separate-event-.patch b/SOURCES/0019-Move-the-wayland-socket-polling-to-a-separate-event-.patch new file mode 100644 index 0000000..5e045bf --- /dev/null +++ b/SOURCES/0019-Move-the-wayland-socket-polling-to-a-separate-event-.patch @@ -0,0 +1,574 @@ +From d89cc9deab3f61cacfad71e8aa32d83d421d49da Mon Sep 17 00:00:00 2001 +From: Adrien Faveraux +Date: Fri, 26 Nov 2021 09:18:58 +0100 +Subject: [PATCH 19/55] Move the wayland socket polling to a separate event + thread + +New event threads is introduced which calls poll() on the wayland fd, +instead of relying on the event dispatcher by using the QSocketNotifier. +This allows to call in the proper order the wl_display_prepare_read(), +poll() and wl_display_read_events() functions. + +One thread is responsible for the default queue; when needed, it emit +a signal so that the main thread can dispatch the queue. Another thread +is responsible for the dedicated queue for frame callbacks; this thread +will dispatch events on the thread itself. + +QWaylandWindow is updated to, instead of each window's dedicated event +queue, use this queue for frame callbacks. + +Co-authored-by: Ratchanan Srirattanamet +Task-number: QTBUG-66075 +Change-Id: Ibb33ad7f4193b866d1b8d7a0405a94d59dcad5eb +Reviewed-by: Qt CI Bot +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 92a7904d9651348b0c307e84251c8440c6f75b22) +--- + src/client/qwaylanddisplay.cpp | 302 +++++++++++++++++++++-------- + src/client/qwaylanddisplay_p.h | 21 +- + src/client/qwaylandintegration.cpp | 4 +- + src/client/qwaylandwindow.cpp | 34 +++- + src/client/qwaylandwindow_p.h | 2 +- + 5 files changed, 255 insertions(+), 108 deletions(-) + +diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp +index 97fb8cbe..ebcdbd22 100644 +--- a/src/client/qwaylanddisplay.cpp ++++ b/src/client/qwaylanddisplay.cpp +@@ -87,10 +87,203 @@ + + #include + ++#include // for std::tie ++ ++static void checkWaylandError(struct wl_display *display) ++{ ++ int ecode = wl_display_get_error(display); ++ if ((ecode == EPIPE || ecode == ECONNRESET)) { ++ // special case this to provide a nicer error ++ qWarning("The Wayland connection broke. Did the Wayland compositor die?"); ++ } else { ++ qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode)); ++ } ++ _exit(1); ++} ++ + QT_BEGIN_NAMESPACE + + namespace QtWaylandClient { + ++class EventThread : public QThread ++{ ++ Q_OBJECT ++public: ++ enum OperatingMode { ++ EmitToDispatch, // Emit the signal, allow dispatching in a differnt thread. ++ SelfDispatch, // Dispatch the events inside this thread. ++ }; ++ ++ EventThread(struct wl_display * wl, struct wl_event_queue * ev_queue, ++ OperatingMode mode) ++ : m_fd(wl_display_get_fd(wl)) ++ , m_pipefd{ -1, -1 } ++ , m_wldisplay(wl) ++ , m_wlevqueue(ev_queue) ++ , m_mode(mode) ++ , m_reading(true) ++ , m_quitting(false) ++ { ++ setObjectName(QStringLiteral("WaylandEventThread")); ++ } ++ ++ void readAndDispatchEvents() ++ { ++ /* ++ * Dispatch pending events and flush the requests at least once. If the event thread ++ * is not reading, try to call _prepare_read() to allow the event thread to poll(). ++ * If that fails, re-try dispatch & flush again until _prepare_read() is successful. ++ * ++ * This allow any call to readAndDispatchEvents() to start event thread's polling, ++ * not only the one issued from event thread's waitForReading(), which means functions ++ * called from dispatch_pending() can safely spin an event loop. ++ */ ++ for (;;) { ++ if (dispatchQueuePending() < 0) { ++ checkWaylandError(m_wldisplay); ++ return; ++ } ++ ++ wl_display_flush(m_wldisplay); ++ ++ // We have to check if event thread is reading every time we dispatch ++ // something, as that may recursively call this function. ++ if (m_reading.loadAcquire()) ++ break; ++ ++ if (prepareReadQueue() == 0) { ++ QMutexLocker l(&m_mutex); ++ m_reading.storeRelease(true); ++ m_cond.wakeOne(); ++ break; ++ } ++ } ++ } ++ ++ void stop() ++ { ++ // We have to both write to the pipe and set the flag, as the thread may be ++ // either in the poll() or waiting for _prepare_read(). ++ if (m_pipefd[1] != -1 && write(m_pipefd[1], "\0", 1) == -1) ++ qWarning("Failed to write to the pipe: %s.", strerror(errno)); ++ ++ { ++ QMutexLocker l(&m_mutex); ++ m_quitting = true; ++ m_cond.wakeOne(); ++ } ++ ++ wait(); ++ } ++ ++Q_SIGNALS: ++ void needReadAndDispatch(); ++ ++protected: ++ void run() override ++ { ++ // we use this pipe to make the loop exit otherwise if we simply used a flag on the loop condition, if stop() gets ++ // called while poll() is blocking the thread will never quit since there are no wayland messages coming anymore. ++ struct Pipe ++ { ++ Pipe(int *fds) ++ : fds(fds) ++ { ++ if (qt_safe_pipe(fds) != 0) ++ qWarning("Pipe creation failed. Quitting may hang."); ++ } ++ ~Pipe() ++ { ++ if (fds[0] != -1) { ++ close(fds[0]); ++ close(fds[1]); ++ } ++ } ++ ++ int *fds; ++ } pipe(m_pipefd); ++ ++ // Make the main thread call wl_prepare_read(), dispatch the pending messages and flush the ++ // outbound ones. Wait until it's done before proceeding, unless we're told to quit. ++ while (waitForReading()) { ++ pollfd fds[2] = { { m_fd, POLLIN, 0 }, { m_pipefd[0], POLLIN, 0 } }; ++ poll(fds, 2, -1); ++ ++ if (fds[1].revents & POLLIN) { ++ // we don't really care to read the byte that was written here since we're closing down ++ wl_display_cancel_read(m_wldisplay); ++ break; ++ } ++ ++ if (fds[0].revents & POLLIN) ++ wl_display_read_events(m_wldisplay); ++ // The polll was succesfull and the event thread did the wl_display_read_events(). On the next iteration of the loop ++ // the event sent to the main thread will cause it to dispatch the messages just read, unless the loop exits in which ++ // case we don't care anymore about them. ++ else ++ wl_display_cancel_read(m_wldisplay); ++ } ++ } ++ ++private: ++ bool waitForReading() ++ { ++ Q_ASSERT(QThread::currentThread() == this); ++ ++ m_reading.storeRelease(false); ++ ++ if (m_mode == SelfDispatch) { ++ readAndDispatchEvents(); ++ } else { ++ Q_EMIT needReadAndDispatch(); ++ ++ QMutexLocker lock(&m_mutex); ++ // m_reading might be set from our emit or some other invocation of ++ // readAndDispatchEvents(). ++ while (!m_reading.loadRelaxed() && !m_quitting) ++ m_cond.wait(&m_mutex); ++ } ++ ++ return !m_quitting; ++ } ++ ++ int dispatchQueuePending() ++ { ++ if (m_wlevqueue) ++ return wl_display_dispatch_queue_pending(m_wldisplay, m_wlevqueue); ++ else ++ return wl_display_dispatch_pending(m_wldisplay); ++ } ++ ++ int prepareReadQueue() ++ { ++ if (m_wlevqueue) ++ return wl_display_prepare_read_queue(m_wldisplay, m_wlevqueue); ++ else ++ return wl_display_prepare_read(m_wldisplay); ++ } ++ ++ int m_fd; ++ int m_pipefd[2]; ++ wl_display *m_wldisplay; ++ wl_event_queue *m_wlevqueue; ++ OperatingMode m_mode; ++ ++ /* Concurrency note when operating in EmitToDispatch mode: ++ * m_reading is set to false inside event thread's waitForReading(), and is ++ * set to true inside main thread's readAndDispatchEvents(). ++ * The lock is not taken when setting m_reading to false, as the main thread ++ * is not actively waiting for it to turn false. However, the lock is taken ++ * inside readAndDispatchEvents() before setting m_reading to true, ++ * as the event thread is actively waiting for it under the wait condition. ++ */ ++ ++ QAtomicInteger m_reading; ++ bool m_quitting; ++ QMutex m_mutex; ++ QWaitCondition m_cond; ++}; ++ + Q_LOGGING_CATEGORY(lcQpaWayland, "qt.qpa.wayland"); // for general (uncategorized) Wayland platform logging + + struct wl_surface *QWaylandDisplay::createSurface(void *handle) +@@ -164,6 +357,12 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration) + + QWaylandDisplay::~QWaylandDisplay(void) + { ++ if (m_eventThread) ++ m_eventThread->stop(); ++ ++ if (m_frameEventQueueThread) ++ m_frameEventQueueThread->stop(); ++ + if (mSyncCallback) + wl_callback_destroy(mSyncCallback); + +@@ -210,98 +409,37 @@ void QWaylandDisplay::ensureScreen() + + void QWaylandDisplay::checkError() const + { +- int ecode = wl_display_get_error(mDisplay); +- if ((ecode == EPIPE || ecode == ECONNRESET)) { +- // special case this to provide a nicer error +- qWarning("The Wayland connection broke. Did the Wayland compositor die?"); +- } else { +- qWarning("The Wayland connection experienced a fatal error: %s", strerror(ecode)); +- } +- _exit(1); ++ checkWaylandError(mDisplay); + } + ++// Called in main thread, either from queued signal or directly. + void QWaylandDisplay::flushRequests() + { +- if (wl_display_prepare_read(mDisplay) == 0) { +- wl_display_read_events(mDisplay); +- } +- +- if (wl_display_dispatch_pending(mDisplay) < 0) +- checkError(); +- +- { +- QReadLocker locker(&m_frameQueueLock); +- for (const FrameQueue &q : mExternalQueues) { +- QMutexLocker locker(q.mutex); +- while (wl_display_prepare_read_queue(mDisplay, q.queue) != 0) +- wl_display_dispatch_queue_pending(mDisplay, q.queue); +- wl_display_read_events(mDisplay); +- wl_display_dispatch_queue_pending(mDisplay, q.queue); +- } +- } +- +- wl_display_flush(mDisplay); +-} +- +-void QWaylandDisplay::blockingReadEvents() +-{ +- if (wl_display_dispatch(mDisplay) < 0) +- checkError(); +-} +- +-void QWaylandDisplay::destroyFrameQueue(const QWaylandDisplay::FrameQueue &q) +-{ +- QWriteLocker locker(&m_frameQueueLock); +- auto it = std::find_if(mExternalQueues.begin(), +- mExternalQueues.end(), +- [&q] (const QWaylandDisplay::FrameQueue &other){ return other.queue == q.queue; }); +- Q_ASSERT(it != mExternalQueues.end()); +- mExternalQueues.erase(it); +- if (q.queue != nullptr) +- wl_event_queue_destroy(q.queue); +- delete q.mutex; ++ m_eventThread->readAndDispatchEvents(); + } + +-QWaylandDisplay::FrameQueue QWaylandDisplay::createFrameQueue() ++// We have to wait until we have an eventDispatcher before creating the eventThread, ++// otherwise forceRoundTrip() may block inside _events_read() because eventThread is ++// polling. ++void QWaylandDisplay::initEventThread() + { +- QWriteLocker locker(&m_frameQueueLock); +- FrameQueue q{createEventQueue()}; +- mExternalQueues.append(q); +- return q; +-} ++ m_eventThread.reset( ++ new EventThread(mDisplay, /* default queue */ nullptr, EventThread::EmitToDispatch)); ++ connect(m_eventThread.get(), &EventThread::needReadAndDispatch, this, ++ &QWaylandDisplay::flushRequests, Qt::QueuedConnection); ++ m_eventThread->start(); + +-wl_event_queue *QWaylandDisplay::createEventQueue() +-{ +- return wl_display_create_queue(mDisplay); ++ // wl_display_disconnect() free this. ++ m_frameEventQueue = wl_display_create_queue(mDisplay); ++ m_frameEventQueueThread.reset( ++ new EventThread(mDisplay, m_frameEventQueue, EventThread::SelfDispatch)); ++ m_frameEventQueueThread->start(); + } + +-void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function condition, int timeout) ++void QWaylandDisplay::blockingReadEvents() + { +- if (!condition()) +- return; +- +- QElapsedTimer timer; +- timer.start(); +- struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN); +- while (timeout == -1 || timer.elapsed() < timeout) { +- while (wl_display_prepare_read_queue(mDisplay, queue) != 0) +- wl_display_dispatch_queue_pending(mDisplay, queue); +- +- wl_display_flush(mDisplay); +- +- const int remaining = qMax(timeout - timer.elapsed(), 0ll); +- const int pollTimeout = timeout == -1 ? -1 : remaining; +- if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0) +- wl_display_read_events(mDisplay); +- else +- wl_display_cancel_read(mDisplay); +- +- if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0) +- checkError(); +- +- if (!condition()) +- break; +- } ++ if (wl_display_dispatch(mDisplay) < 0) ++ checkWaylandError(mDisplay); + } + + QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const +@@ -678,4 +816,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p + + } // namespace QtWaylandClient + ++#include "qwaylanddisplay.moc" ++ + QT_END_NAMESPACE +diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h +index 49820255..cf91b924 100644 +--- a/src/client/qwaylanddisplay_p.h ++++ b/src/client/qwaylanddisplay_p.h +@@ -111,6 +111,7 @@ class QWaylandSurface; + class QWaylandShellIntegration; + class QWaylandCursor; + class QWaylandCursorTheme; ++class EventThread; + + typedef void (*RegistryListener)(void *data, + struct wl_registry *registry, +@@ -122,12 +123,6 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandDisplay : public QObject, public QtWayland + Q_OBJECT + + public: +- struct FrameQueue { +- FrameQueue(wl_event_queue *q = nullptr) : queue(q), mutex(new QMutex) {} +- wl_event_queue *queue; +- QMutex *mutex; +- }; +- + QWaylandDisplay(QWaylandIntegration *waylandIntegration); + ~QWaylandDisplay(void) override; + +@@ -216,12 +211,11 @@ public: + void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice); + void handleWindowDestroyed(QWaylandWindow *window); + +- wl_event_queue *createEventQueue(); +- FrameQueue createFrameQueue(); +- void destroyFrameQueue(const FrameQueue &q); +- void dispatchQueueWhile(wl_event_queue *queue, std::function condition, int timeout = -1); ++ wl_event_queue *frameEventQueue() { return m_frameEventQueue; }; + + bool isKeyboardAvailable() const; ++ ++ void initEventThread(); + public slots: + void blockingReadEvents(); + void flushRequests(); +@@ -244,6 +238,9 @@ private: + }; + + struct wl_display *mDisplay = nullptr; ++ QScopedPointer m_eventThread; ++ wl_event_queue *m_frameEventQueue = nullptr; ++ QScopedPointer m_frameEventQueueThread; + QtWayland::wl_compositor mCompositor; + QScopedPointer mShm; + QList mWaitingScreens; +@@ -282,11 +279,9 @@ private: + QWaylandInputDevice *mLastInputDevice = nullptr; + QPointer mLastInputWindow; + QPointer mLastKeyboardFocus; +- QVector mActiveWindows; +- QVector mExternalQueues; ++ QList mActiveWindows; + struct wl_callback *mSyncCallback = nullptr; + static const wl_callback_listener syncCallbackListener; +- QReadWriteLock m_frameQueueLock; + + bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull(); + +diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp +index 661cea53..fbf00c6b 100644 +--- a/src/client/qwaylandintegration.cpp ++++ b/src/client/qwaylandintegration.cpp +@@ -192,9 +192,7 @@ QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const + + void QWaylandIntegration::initialize() + { +- int fd = wl_display_get_fd(mDisplay->wl_display()); +- QSocketNotifier *sn = new QSocketNotifier(fd, QSocketNotifier::Read, mDisplay.data()); +- QObject::connect(sn, SIGNAL(activated(QSocketDescriptor)), mDisplay.data(), SLOT(flushRequests())); ++ mDisplay->initEventThread(); + + // Call after eventDispatcher is fully connected, for QWaylandDisplay::forceRoundTrip() + mDisplay->initialize(); +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index b98435ed..292dd023 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -76,7 +76,6 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr; + QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) + : QPlatformWindow(window) + , mDisplay(display) +- , mFrameQueue(mDisplay->createFrameQueue()) + , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP")) + { + { +@@ -95,8 +94,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) + + QWaylandWindow::~QWaylandWindow() + { +- mDisplay->destroyFrameQueue(mFrameQueue); +- + delete mWindowDecoration; + + if (mSurface) +@@ -648,6 +645,8 @@ const wl_callback_listener QWaylandWindow::callbackListener = { + + void QWaylandWindow::handleFrameCallback() + { ++ QMutexLocker locker(&mFrameSyncMutex); ++ + mWaitingForFrameCallback = false; + mFrameCallbackElapsedTimer.invalidate(); + +@@ -669,12 +668,16 @@ void QWaylandWindow::handleFrameCallback() + mWaitingForUpdateDelivery = true; + QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection); + } ++ ++ mFrameSyncWait.notify_all(); + } + + bool QWaylandWindow::waitForFrameSync(int timeout) + { +- QMutexLocker locker(mFrameQueue.mutex); +- mDisplay->dispatchQueueWhile(mFrameQueue.queue, [&]() { return mWaitingForFrameCallback; }, timeout); ++ QMutexLocker locker(&mFrameSyncMutex); ++ ++ QDeadlineTimer deadline(timeout); ++ while (mWaitingForFrameCallback && mFrameSyncWait.wait(&mFrameSyncMutex, deadline)) { } + + if (mWaitingForFrameCallback) { + qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; +@@ -1179,8 +1182,11 @@ void QWaylandWindow::requestUpdate() + Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA + + // If we have a frame callback all is good and will be taken care of there +- if (mWaitingForFrameCallback) +- return; ++ { ++ QMutexLocker locker(&mFrameSyncMutex); ++ if (mWaitingForFrameCallback) ++ return; ++ } + + // If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet + // This is a somewhat redundant behavior and might indicate a bug in the calling code, so log +@@ -1193,7 +1199,12 @@ void QWaylandWindow::requestUpdate() + // so use invokeMethod to delay the delivery a bit. + QMetaObject::invokeMethod(this, [this] { + // Things might have changed in the meantime +- if (hasPendingUpdateRequest() && !mWaitingForFrameCallback) ++ { ++ QMutexLocker locker(&mFrameSyncMutex); ++ if (mWaitingForFrameCallback) ++ return; ++ } ++ if (hasPendingUpdateRequest()) + deliverUpdateRequest(); + }, Qt::QueuedConnection); + } +@@ -1213,9 +1224,10 @@ void QWaylandWindow::handleUpdate() + if (!mSurface) + return; + +- QMutexLocker locker(mFrameQueue.mutex); ++ QMutexLocker locker(&mFrameSyncMutex); ++ + struct ::wl_surface *wrappedSurface = reinterpret_cast(wl_proxy_create_wrapper(mSurface->object())); +- wl_proxy_set_queue(reinterpret_cast(wrappedSurface), mFrameQueue.queue); ++ wl_proxy_set_queue(reinterpret_cast(wrappedSurface), mDisplay->frameEventQueue()); + mFrameCallback = wl_surface_frame(wrappedSurface); + wl_proxy_wrapper_destroy(wrappedSurface); + wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this); +@@ -1225,6 +1237,8 @@ void QWaylandWindow::handleUpdate() + // Start a timer for handling the case when the compositor stops sending frame callbacks. + if (mFrameCallbackTimeout > 0) { + QMetaObject::invokeMethod(this, [this] { ++ QMutexLocker locker(&mFrameSyncMutex); ++ + if (mWaitingForFrameCallback) { + if (mFrameCallbackCheckIntervalTimerId < 0) + mFrameCallbackCheckIntervalTimerId = startTimer(mFrameCallbackTimeout); +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index fb3ed606..54ac67a9 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -232,7 +232,7 @@ protected: + int mFrameCallbackCheckIntervalTimerId = -1; + QElapsedTimer mFrameCallbackElapsedTimer; + struct ::wl_callback *mFrameCallback = nullptr; +- QWaylandDisplay::FrameQueue mFrameQueue; ++ QMutex mFrameSyncMutex; + QWaitCondition mFrameSyncWait; + + // True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer +-- +2.40.0 + diff --git a/SOURCES/0020-Client-Remove-mWaitingForUpdateDelivery.patch b/SOURCES/0020-Client-Remove-mWaitingForUpdateDelivery.patch new file mode 100644 index 0000000..3074807 --- /dev/null +++ b/SOURCES/0020-Client-Remove-mWaitingForUpdateDelivery.patch @@ -0,0 +1,79 @@ +From 58c67c6e8f2d94cef7f8eec072ebad031769e144 Mon Sep 17 00:00:00 2001 +From: Vlad Zahorodnii +Date: Tue, 1 Feb 2022 13:05:36 +0200 +Subject: [PATCH 20/55] Client: Remove mWaitingForUpdateDelivery + +Currently, mWaitingForUpdateDelivery is shared between the main thread +(doHandleFrameCallback()) and the frame callback event thread +(handleFrameCallback()), however the access to it is not synchronized +between both threads. On the other hand, QWaylandWindow +already ensures not to create a frame callback if there's already one +pending. + +This change removes mWaitingForUpdateDelivery flag because it should be +already covered by mWaitingForFrameCallback and to remove unsynchronized +shared state between threads. + +Change-Id: I0e5a25d18d1e66c4d7683e7e972330c4d7cbbf38 +Reviewed-by: David Edmundson +(cherry picked from commit feb1a5c207c13d0bf87c0d8ad039279dbf8cee9e) +--- + src/client/qwaylandwindow.cpp | 29 ++++++++++++----------------- + src/client/qwaylandwindow_p.h | 1 - + 2 files changed, 12 insertions(+), 18 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 292dd023..de5af1bd 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -651,23 +651,18 @@ void QWaylandWindow::handleFrameCallback() + mFrameCallbackElapsedTimer.invalidate(); + + // The rest can wait until we can run it on the correct thread +- if (!mWaitingForUpdateDelivery) { +- auto doHandleExpose = [this]() { +- bool wasExposed = isExposed(); +- mFrameCallbackTimedOut = false; +- if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? +- sendExposeEvent(QRect(QPoint(), geometry().size())); +- if (wasExposed && hasPendingUpdateRequest()) +- deliverUpdateRequest(); +- +- mWaitingForUpdateDelivery = false; +- }; +- +- // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync() +- // in the single-threaded case. +- mWaitingForUpdateDelivery = true; +- QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection); +- } ++ auto doHandleExpose = [this]() { ++ bool wasExposed = isExposed(); ++ mFrameCallbackTimedOut = false; ++ if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? ++ sendExposeEvent(QRect(QPoint(), geometry().size())); ++ if (wasExposed && hasPendingUpdateRequest()) ++ deliverUpdateRequest(); ++ }; ++ ++ // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync() ++ // in the single-threaded case. ++ QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection); + + mFrameSyncWait.notify_all(); + } +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index 54ac67a9..cf7ce879 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -228,7 +228,6 @@ protected: + WId mWindowId; + bool mWaitingForFrameCallback = false; + bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out +- bool mWaitingForUpdateDelivery = false; + int mFrameCallbackCheckIntervalTimerId = -1; + QElapsedTimer mFrameCallbackElapsedTimer; + struct ::wl_callback *mFrameCallback = nullptr; +-- +2.40.0 + diff --git a/SOURCES/0021-client-Simplify-round-trip-behavior.patch b/SOURCES/0021-client-Simplify-round-trip-behavior.patch new file mode 100644 index 0000000..b6b1d10 --- /dev/null +++ b/SOURCES/0021-client-Simplify-round-trip-behavior.patch @@ -0,0 +1,82 @@ +From 71a1a9791df20307dd4eb688b0957d0f695823c5 Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Wed, 9 Feb 2022 17:20:48 +0000 +Subject: [PATCH 21/55] client: Simplify round trip behavior + +The custom event queue was removed in +302d4ffb8549214eb4028dc3e47ec4ee4e12ffbd (2015) so the comment about not +being able to use the inbuilt round trip method no longer applies. + +This fixes a real world problem. Use of a blocking round trip should not +process non wayland events. Doing so can lead to misbehaviour client +side as things happen out of order. The move to the event thread created +several regressions as we now get events before the QGuiApplication is +fully constructed. + +Change-Id: I650481f49a47ed1a9778c7e1bc3c48db6e8f0031 +Reviewed-by: Vlad Zahorodnii +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 62646d9122845d7bd9104b610478cebde3e769c7) +--- + src/client/qwaylanddisplay.cpp | 43 +--------------------------------- + 1 file changed, 1 insertion(+), 42 deletions(-) + +diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp +index ebcdbd22..d371ffec 100644 +--- a/src/client/qwaylanddisplay.cpp ++++ b/src/client/qwaylanddisplay.cpp +@@ -615,50 +615,9 @@ uint32_t QWaylandDisplay::currentTimeMillisec() + return 0; + } + +-static void +-sync_callback(void *data, struct wl_callback *callback, uint32_t serial) +-{ +- Q_UNUSED(serial) +- bool *done = static_cast(data); +- +- *done = true; +- +- // If the wl_callback done event is received after the condition check in the while loop in +- // forceRoundTrip(), but before the call to processEvents, the call to processEvents may block +- // forever if no more events are posted (eventhough the callback is handled in response to the +- // aboutToBlock signal). Hence, we wake up the event dispatcher so forceRoundTrip may return. +- // (QTBUG-64696) +- if (auto *dispatcher = QThread::currentThread()->eventDispatcher()) +- dispatcher->wakeUp(); +- +- wl_callback_destroy(callback); +-} +- +-static const struct wl_callback_listener sync_listener = { +- sync_callback +-}; +- + void QWaylandDisplay::forceRoundTrip() + { +- // wl_display_roundtrip() works on the main queue only, +- // but we use a separate one, so basically reimplement it here +- int ret = 0; +- bool done = false; +- wl_callback *callback = wl_display_sync(mDisplay); +- wl_callback_add_listener(callback, &sync_listener, &done); +- flushRequests(); +- if (QThread::currentThread()->eventDispatcher()) { +- while (!done && ret >= 0) { +- QThread::currentThread()->eventDispatcher()->processEvents(QEventLoop::WaitForMoreEvents); +- ret = wl_display_dispatch_pending(mDisplay); +- } +- } else { +- while (!done && ret >= 0) +- ret = wl_display_dispatch(mDisplay); +- } +- +- if (ret == -1 && !done) +- wl_callback_destroy(callback); ++ wl_display_roundtrip(mDisplay); + } + + bool QWaylandDisplay::supportsWindowDecoration() const +-- +2.40.0 + diff --git a/SOURCES/0022-Client-Fix-opaque-region-setter.patch b/SOURCES/0022-Client-Fix-opaque-region-setter.patch new file mode 100644 index 0000000..6c6c41d --- /dev/null +++ b/SOURCES/0022-Client-Fix-opaque-region-setter.patch @@ -0,0 +1,31 @@ +From f584a8350503e349669744a1587634431ac0dc82 Mon Sep 17 00:00:00 2001 +From: Vlad Zahorodnii +Date: Sat, 19 Feb 2022 17:01:04 +0200 +Subject: [PATCH 22/55] Client: Fix opaque region setter + +The rect is in the global coordinate system, while the opaque region +must be in the surface local coordinate system. + +Change-Id: I75042b4d779dfd4dfe610aad1f0387879f11b048 +Reviewed-by: Aleix Pol Gonzalez +(cherry picked from commit f9425f573b18c0b66fd9ad9c2805e8b8b9a3ec77) +--- + src/client/qwaylandwindow.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index de5af1bd..69319228 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -372,7 +372,7 @@ void QWaylandWindow::setGeometry(const QRect &rect) + mShellSurface->setWindowGeometry(windowContentGeometry()); + + if (isOpaque() && mMask.isEmpty()) +- setOpaqueArea(rect); ++ setOpaqueArea(QRect(QPoint(0, 0), rect.size())); + } + + void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) +-- +2.40.0 + diff --git a/SOURCES/0023-Use-proper-dependencies-in-compile-tests.patch b/SOURCES/0023-Use-proper-dependencies-in-compile-tests.patch new file mode 100644 index 0000000..0023433 --- /dev/null +++ b/SOURCES/0023-Use-proper-dependencies-in-compile-tests.patch @@ -0,0 +1,126 @@ +From a9831916ac5463682b7a27db3ffcadfe0ea07ab9 Mon Sep 17 00:00:00 2001 +From: Fabian Vogt +Date: Fri, 4 Feb 2022 11:07:36 +0100 +Subject: [PATCH 23/55] Use proper dependencies in compile tests + +Use the dependencies as found by the "libraries" section instead of relying +on them being available in the default location (e.g. "-ldrm"). + +Additionally, VK_USE_PLATFORM_WAYLAND_KHR requires , so +add the wayland-client dependency. + +This fixes those tests if e.g. wayland-client headers need to be found through +pkgconfig. + +This part of the code changed completely in Qt 6, so this is a totally +different patch and not a cherry-pick of 5fc2e1915c3a +("CMake: Fix qtwayland feature detection"). + +Fixes: QTBUG-100475 +--- + src/client/configure.json | 8 ++++---- + src/compositor/configure.json | 34 +++++++++++++++++++++++++++++----- + 2 files changed, 33 insertions(+), 9 deletions(-) + +diff --git a/src/client/configure.json b/src/client/configure.json +index 2f424580..29222357 100644 +--- a/src/client/configure.json ++++ b/src/client/configure.json +@@ -149,8 +149,7 @@ + "#endif" + ] + }, +- "libs": "-ldrm", +- "use": "egl" ++ "use": "drm egl" + }, + "vulkan-server-buffer": { + "label": "Vulkan Buffer Sharing", +@@ -168,7 +167,8 @@ + "exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;", + "return 0;" + ] +- } ++ }, ++ "use": "wayland-client" + }, + "egl_1_5-wayland": { + "label": "EGL 1.5 with Wayland Platform", +@@ -183,7 +183,7 @@ + "eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_EXT, (struct wl_display *)(nullptr), nullptr);" + ] + }, +- "use": "egl" ++ "use": "egl wayland-client" + } + }, + +diff --git a/src/compositor/configure.json b/src/compositor/configure.json +index bcfd5215..da95d07b 100644 +--- a/src/compositor/configure.json ++++ b/src/compositor/configure.json +@@ -7,6 +7,31 @@ + "testDir": "../../config.tests", + + "libraries": { ++ "wayland-client": { ++ "label": "Wayland client library", ++ "headers": "wayland-version.h", ++ "test": { ++ "main": [ ++ "#if WAYLAND_VERSION_MAJOR < 1", ++ "# error Wayland 1.8.0 or higher required", ++ "#endif", ++ "#if WAYLAND_VERSION_MAJOR == 1", ++ "# if WAYLAND_VERSION_MINOR < 8", ++ "# error Wayland 1.8.0 or higher required", ++ "# endif", ++ "# if WAYLAND_VERSION_MINOR == 8", ++ "# if WAYLAND_VERSION_MICRO < 0", ++ "# error Wayland 1.8.0 or higher required", ++ "# endif", ++ "# endif", ++ "#endif" ++ ] ++ }, ++ "sources": [ ++ { "type": "pkgConfig", "args": "wayland-client" }, ++ "-lwayland-client" ++ ] ++ }, + "wayland-server": { + "label": "wayland-server", + "headers": "wayland-version.h", +@@ -151,8 +176,7 @@ + "#endif" + ] + }, +- "libs": "-ldrm", +- "use": "egl" ++ "use": "drm egl" + }, + "dmabuf-client-buffer": { + "label": "Linux Client dma-buf Buffer Sharing", +@@ -176,8 +200,7 @@ + "return 0;" + ] + }, +- "libs": "-ldrm", +- "use": "egl" ++ "use": "drm egl" + }, + "vulkan-server-buffer": { + "label": "Vulkan Buffer Sharing", +@@ -195,7 +218,8 @@ + "exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;", + "return 0;" + ] +- } ++ }, ++ "use": "wayland-client" + } + }, + +-- +2.40.0 + diff --git a/SOURCES/0024-Revert-Client-Remove-mWaitingForUpdateDelivery.patch b/SOURCES/0024-Revert-Client-Remove-mWaitingForUpdateDelivery.patch new file mode 100644 index 0000000..1cc3e5c --- /dev/null +++ b/SOURCES/0024-Revert-Client-Remove-mWaitingForUpdateDelivery.patch @@ -0,0 +1,59 @@ +From c6639e225f45564def8f6729c83887ab73495e59 Mon Sep 17 00:00:00 2001 +From: Paul Olav Tvete +Date: Tue, 15 Mar 2022 15:59:15 +0100 +Subject: [PATCH 24/55] Revert "Client: Remove mWaitingForUpdateDelivery" + +The reverted commit introduces a severe performance regression +when a client window is resized while a QtQuick renderthread +animation is running. + +This reverts commit feb1a5c207c13d0bf87c0d8ad039279dbf8cee9e. + +Fixes: QTBUG-101726 +Change-Id: Ib5b52ce06efec8c86fada1623c2af82099e57fc6 +Reviewed-by: Eskil Abrahamsen Blomfeldt +--- + src/client/qwaylandwindow.cpp | 12 +++++++++--- + src/client/qwaylandwindow_p.h | 1 + + 2 files changed, 10 insertions(+), 3 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 69319228..a87e11aa 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -658,11 +658,17 @@ void QWaylandWindow::handleFrameCallback() + sendExposeEvent(QRect(QPoint(), geometry().size())); + if (wasExposed && hasPendingUpdateRequest()) + deliverUpdateRequest(); ++ ++ mWaitingForUpdateDelivery = false; + }; + +- // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync() +- // in the single-threaded case. +- QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection); ++ if (!mWaitingForUpdateDelivery) { ++ // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync() ++ // in the single-threaded case. ++ mWaitingForUpdateDelivery = true; ++ QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection); ++ } ++ + + mFrameSyncWait.notify_all(); + } +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index cf7ce879..54ac67a9 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -228,6 +228,7 @@ protected: + WId mWindowId; + bool mWaitingForFrameCallback = false; + bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out ++ bool mWaitingForUpdateDelivery = false; + int mFrameCallbackCheckIntervalTimerId = -1; + QElapsedTimer mFrameCallbackElapsedTimer; + struct ::wl_callback *mFrameCallback = nullptr; +-- +2.40.0 + diff --git a/SOURCES/0025-Fix-race-condition-on-mWaitingForUpdateDelivery.patch b/SOURCES/0025-Fix-race-condition-on-mWaitingForUpdateDelivery.patch new file mode 100644 index 0000000..9f03187 --- /dev/null +++ b/SOURCES/0025-Fix-race-condition-on-mWaitingForUpdateDelivery.patch @@ -0,0 +1,59 @@ +From 2c403a8522db9aa6a9f365dba5f8ef5af205295c Mon Sep 17 00:00:00 2001 +From: Paul Olav Tvete +Date: Tue, 15 Mar 2022 16:53:04 +0100 +Subject: [PATCH 25/55] Fix race condition on mWaitingForUpdateDelivery + +Change-Id: I0e91bda73722468b9339fc434fe04420b5e7d3da +Reviewed-by: David Edmundson +--- + src/client/qwaylandwindow.cpp | 7 ++----- + src/client/qwaylandwindow_p.h | 2 +- + 2 files changed, 3 insertions(+), 6 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index a87e11aa..264ca59b 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -652,24 +652,21 @@ void QWaylandWindow::handleFrameCallback() + + // The rest can wait until we can run it on the correct thread + auto doHandleExpose = [this]() { ++ mWaitingForUpdateDelivery.storeRelease(false); + bool wasExposed = isExposed(); + mFrameCallbackTimedOut = false; + if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed? + sendExposeEvent(QRect(QPoint(), geometry().size())); + if (wasExposed && hasPendingUpdateRequest()) + deliverUpdateRequest(); +- +- mWaitingForUpdateDelivery = false; + }; + +- if (!mWaitingForUpdateDelivery) { ++ if (mWaitingForUpdateDelivery.testAndSetAcquire(false, true)) { + // Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync() + // in the single-threaded case. +- mWaitingForUpdateDelivery = true; + QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection); + } + +- + mFrameSyncWait.notify_all(); + } + +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index 54ac67a9..c0a76345 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -228,7 +228,7 @@ protected: + WId mWindowId; + bool mWaitingForFrameCallback = false; + bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out +- bool mWaitingForUpdateDelivery = false; ++ QAtomicInt mWaitingForUpdateDelivery = false; + int mFrameCallbackCheckIntervalTimerId = -1; + QElapsedTimer mFrameCallbackElapsedTimer; + struct ::wl_callback *mFrameCallback = nullptr; +-- +2.40.0 + diff --git a/SOURCES/0026-use-poll-2-when-reading-from-clipboard.patch b/SOURCES/0026-use-poll-2-when-reading-from-clipboard.patch new file mode 100644 index 0000000..358dc32 --- /dev/null +++ b/SOURCES/0026-use-poll-2-when-reading-from-clipboard.patch @@ -0,0 +1,48 @@ +From 2f0528fc51a378bdd8a3ff93d996765fccf54cce Mon Sep 17 00:00:00 2001 +From: Kenneth Topp +Date: Mon, 4 Apr 2022 09:36:21 -0400 +Subject: [PATCH 26/55] use poll(2) when reading from clipboard + +change clipboard read away from select(2) call which can fail when +an application has large number of open files + +Change-Id: I6d98c6bb11cdd5b6171b01cfeb0044dd41cf9fb5 +Reviewed-by: Thiago Macieira +(cherry picked from commit 829a9f62a96721c142f53e12a8812e8231b20317) +--- + src/client/qwaylanddataoffer.cpp | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/src/client/qwaylanddataoffer.cpp b/src/client/qwaylanddataoffer.cpp +index c9e158cc..fe0ea8c9 100644 +--- a/src/client/qwaylanddataoffer.cpp ++++ b/src/client/qwaylanddataoffer.cpp +@@ -188,17 +188,18 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T + + int QWaylandMimeData::readData(int fd, QByteArray &data) const + { +- fd_set readset; +- FD_ZERO(&readset); +- FD_SET(fd, &readset); +- struct timeval timeout; ++ struct pollfd readset; ++ readset.fd = fd; ++ readset.events = POLLIN; ++ struct timespec timeout; + timeout.tv_sec = 1; +- timeout.tv_usec = 0; ++ timeout.tv_nsec = 0; ++ + + Q_FOREVER { +- int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout); ++ int ready = qt_safe_poll(&readset, 1, &timeout); + if (ready < 0) { +- qWarning() << "QWaylandDataOffer: select() failed"; ++ qWarning() << "QWaylandDataOffer: qt_safe_poll() failed"; + return -1; + } else if (ready == 0) { + qWarning("QWaylandDataOffer: timeout reading from pipe"); +-- +2.40.0 + diff --git a/SOURCES/0027-Reduce-memory-leakage.patch b/SOURCES/0027-Reduce-memory-leakage.patch new file mode 100644 index 0000000..e647362 --- /dev/null +++ b/SOURCES/0027-Reduce-memory-leakage.patch @@ -0,0 +1,31 @@ +From 2197557879ae1e3459e715dbc20b21e8652cb972 Mon Sep 17 00:00:00 2001 +From: Ulf Hermann +Date: Tue, 22 Feb 2022 12:31:08 +0100 +Subject: [PATCH 27/55] Reduce memory leakage + +We need to clean up the event queue when we're done. + +Change-Id: I13a9eb77e978f4eab227a3a28dab8ebc8de94405 +Reviewed-by: David Edmundson +(cherry picked from commit 1264e5f565d8fb7ac200e4b00531ab876922458f) +--- + src/client/qwaylanddisplay.cpp | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp +index d371ffec..1b9ec699 100644 +--- a/src/client/qwaylanddisplay.cpp ++++ b/src/client/qwaylanddisplay.cpp +@@ -381,6 +381,9 @@ QWaylandDisplay::~QWaylandDisplay(void) + #endif + if (mDisplay) + wl_display_disconnect(mDisplay); ++ ++ if (m_frameEventQueue) ++ wl_event_queue_destroy(m_frameEventQueue); + } + + // Steps which is called just after constructor. This separates registry_global() out of the constructor +-- +2.40.0 + diff --git a/SOURCES/0028-Fix-build-with-libcxx-missing-array-include.patch b/SOURCES/0028-Fix-build-with-libcxx-missing-array-include.patch new file mode 100644 index 0000000..a731873 --- /dev/null +++ b/SOURCES/0028-Fix-build-with-libcxx-missing-array-include.patch @@ -0,0 +1,32 @@ +From d9b04ba05ed479b577579e6a34d97fb906791f69 Mon Sep 17 00:00:00 2001 +From: Sam James +Date: Sat, 18 Jun 2022 17:11:11 +0100 +Subject: [PATCH 28/55] Fix build with libcxx (missing array include) + +Bug: https://bugs.gentoo.org/833488 + +Task-number: QTBUG-104435 +Change-Id: I06384761a5560b81b675e6c4ae498bb93dcb4f4f +Pick-to: 5.15 6.2 6.3 6.4 +Reviewed-by: Marc Mutz +(cherry picked from commit 5065013b0c2346b5918a2681ae2e58046140e8a7) +--- + .../compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h +index 56a710c3..c6a8b6c6 100644 +--- a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h ++++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h +@@ -41,6 +41,8 @@ + #include + #include + ++#include ++ + #include + #include + +-- +2.40.0 + diff --git a/SOURCES/0029-Only-close-popup-in-the-the-hierchary.patch b/SOURCES/0029-Only-close-popup-in-the-the-hierchary.patch new file mode 100644 index 0000000..7378cfe --- /dev/null +++ b/SOURCES/0029-Only-close-popup-in-the-the-hierchary.patch @@ -0,0 +1,401 @@ +From 9265dae0d87eaf2a0dfc8a67c46f6c11bd14d2ab Mon Sep 17 00:00:00 2001 +From: Weng Xuetian +Date: Wed, 20 Jul 2022 15:57:40 -0700 +Subject: [PATCH 29/55] Only close popup in the the hierchary + +Imagine following event sequences: +1. a tooltip is shown. activePopups = {tooltip} +2. user click menu bar to show the menu, QMenu::setVisible is called. + now activePopups(tooltip, menu} +3. tooltip visibility changed to false. +4. closePopups() close both tooltip and menu. + +This is a common pattern under wayland that menu is shown as a invisible +state. This patch tries to memorize the surface hierchary used to create +the popup role. And only close those popups whose ancesotor is hidden. + +Pick-to: 6.4 +Change-Id: I78aa0b4e32a5812603e003e756d8bcd202e94af4 +Reviewed-by: David Edmundson +(cherry picked from commit f8e3257e9b1e22d52e9c221c62b8d9b6dd1151a3) +--- + src/client/qwaylandwindow.cpp | 33 ++++--- + src/client/qwaylandwindow_p.h | 6 ++ + .../xdg-shell-v5/qwaylandxdgpopupv5.cpp | 5 +- + .../xdg-shell-v5/qwaylandxdgpopupv5_p.h | 3 +- + .../xdg-shell-v5/qwaylandxdgshellv5.cpp | 2 +- + .../xdg-shell-v6/qwaylandxdgshellv6.cpp | 3 + + .../xdg-shell/qwaylandxdgshell.cpp | 22 +++-- + .../xdg-shell/qwaylandxdgshell_p.h | 5 +- + tests/auto/client/xdgshell/tst_xdgshell.cpp | 87 +++++++++++++++++++ + 9 files changed, 136 insertions(+), 30 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 264ca59b..9e82c174 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -239,6 +239,7 @@ bool QWaylandWindow::shouldCreateSubSurface() const + + void QWaylandWindow::reset() + { ++ closeChildPopups(); + delete mShellSurface; + mShellSurface = nullptr; + delete mSubSurfaceWindow; +@@ -397,21 +398,6 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect) + mLastExposeGeometry = rect; + } + +- +-static QVector> activePopups; +- +-void QWaylandWindow::closePopups(QWaylandWindow *parent) +-{ +- while (!activePopups.isEmpty()) { +- auto popup = activePopups.takeLast(); +- if (popup.isNull()) +- continue; +- if (popup.data() == parent) +- return; +- popup->reset(); +- } +-} +- + QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const + { + QReadLocker lock(&mSurfaceLock); +@@ -431,8 +417,6 @@ void QWaylandWindow::setVisible(bool visible) + lastVisible = visible; + + if (visible) { +- if (window()->type() == Qt::Popup || window()->type() == Qt::ToolTip) +- activePopups << this; + initWindow(); + + setGeometry(windowGeometry()); +@@ -441,7 +425,6 @@ void QWaylandWindow::setVisible(bool visible) + // QWaylandShmBackingStore::beginPaint(). + } else { + sendExposeEvent(QRect()); +- closePopups(this); + reset(); + } + } +@@ -1297,6 +1280,20 @@ void QWaylandWindow::setOpaqueArea(const QRegion &opaqueArea) + wl_region_destroy(region); + } + ++void QWaylandWindow::addChildPopup(QWaylandWindow *surface) { ++ mChildPopups.append(surface); ++} ++ ++void QWaylandWindow::removeChildPopup(QWaylandWindow *surface) { ++ mChildPopups.removeAll(surface); ++} ++ ++void QWaylandWindow::closeChildPopups() { ++ while (!mChildPopups.isEmpty()) { ++ auto popup = mChildPopups.takeLast(); ++ popup->reset(); ++ } ++} + } + + QT_END_NAMESPACE +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index c0a76345..2be87bc0 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -207,6 +207,10 @@ public: + void handleUpdate(); + void deliverUpdateRequest() override; + ++ void addChildPopup(QWaylandWindow* child); ++ void removeChildPopup(QWaylandWindow* child); ++ void closeChildPopups(); ++ + public slots: + void applyConfigure(); + +@@ -262,6 +266,8 @@ protected: + QWaylandBuffer *mQueuedBuffer = nullptr; + QRegion mQueuedBufferDamage; + ++ QList> mChildPopups; ++ + private: + void setGeometry_helper(const QRect &rect); + void initWindow(); +diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp +index 85d25e3c..60bdd491 100644 +--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp ++++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp +@@ -47,18 +47,21 @@ QT_BEGIN_NAMESPACE + + namespace QtWaylandClient { + +-QWaylandXdgPopupV5::QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow *window) ++QWaylandXdgPopupV5::QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow* parent, QWaylandWindow *window) + : QWaylandShellSurface(window) + , QtWayland::xdg_popup_v5(popup) ++ , m_parent(parent) + , m_window(window) + { + if (window->display()->windowExtension()) + m_extendedWindow = new QWaylandExtendedSurface(window); ++ m_parent->addChildPopup(m_window); + } + + QWaylandXdgPopupV5::~QWaylandXdgPopupV5() + { + xdg_popup_destroy(object()); ++ m_parent->removeChildPopup(m_window); + delete m_extendedWindow; + } + +diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h +index 7494f6a6..d85f130b 100644 +--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h ++++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h +@@ -70,7 +70,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgPopupV5 : public QWaylandShellSurface + { + Q_OBJECT + public: +- QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow *window); ++ QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow* parent, QWaylandWindow *window); + ~QWaylandXdgPopupV5() override; + + protected: +@@ -78,6 +78,7 @@ protected: + + private: + QWaylandExtendedSurface *m_extendedWindow = nullptr; ++ QWaylandWindow *m_parent = nullptr; + QWaylandWindow *m_window = nullptr; + }; + +diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp +index 7e242c4a..def8452a 100644 +--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp ++++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp +@@ -84,7 +84,7 @@ QWaylandXdgPopupV5 *QWaylandXdgShellV5::createXdgPopup(QWaylandWindow *window, Q + int x = position.x() + parentWindow->frameMargins().left(); + int y = position.y() + parentWindow->frameMargins().top(); + +- auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->wlSurface(), parentSurface, seat, m_popupSerial, x, y), window); ++ auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->wlSurface(), parentSurface, seat, m_popupSerial, x, y), parentWindow, window); + m_popups.append(window); + QObject::connect(popup, &QWaylandXdgPopupV5::destroyed, [this, window](){ + m_popups.removeOne(window); +diff --git a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp +index 8c371661..151c78e3 100644 +--- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp ++++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp +@@ -174,6 +174,7 @@ QWaylandXdgSurfaceV6::Popup::Popup(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdg + , m_xdgSurface(xdgSurface) + , m_parent(parent) + { ++ m_parent->window()->addChildPopup(m_xdgSurface->window()); + } + + QWaylandXdgSurfaceV6::Popup::~Popup() +@@ -181,6 +182,8 @@ QWaylandXdgSurfaceV6::Popup::~Popup() + if (isInitialized()) + destroy(); + ++ m_parent->window()->removeChildPopup(m_xdgSurface->window()); ++ + if (m_grabbing) { + auto *shell = m_xdgSurface->m_shell; + Q_ASSERT(shell->m_topmostGrabbingPopup == this); +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +index b7383e19..962001b3 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +@@ -195,12 +195,17 @@ QtWayland::xdg_toplevel::resize_edge QWaylandXdgSurface::Toplevel::convertToResi + | ((edges & Qt::RightEdge) ? resize_edge_right : 0)); + } + +-QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent, ++QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent, + QtWayland::xdg_positioner *positioner) +- : xdg_popup(xdgSurface->get_popup(parent->object(), positioner->object())) +- , m_xdgSurface(xdgSurface) ++ : m_xdgSurface(xdgSurface) ++ , m_parentXdgSurface(qobject_cast(parent->shellSurface())) + , m_parent(parent) + { ++ ++ init(xdgSurface->get_popup(m_parentXdgSurface ? m_parentXdgSurface->object() : nullptr, positioner->object())); ++ if (m_parent) { ++ m_parent->addChildPopup(m_xdgSurface->window()); ++ } + } + + QWaylandXdgSurface::Popup::~Popup() +@@ -208,10 +213,14 @@ QWaylandXdgSurface::Popup::~Popup() + if (isInitialized()) + destroy(); + ++ if (m_parent) { ++ m_parent->removeChildPopup(m_xdgSurface->window()); ++ } ++ + if (m_grabbing) { + auto *shell = m_xdgSurface->m_shell; + Q_ASSERT(shell->m_topmostGrabbingPopup == this); +- shell->m_topmostGrabbingPopup = m_parent->m_popup; ++ shell->m_topmostGrabbingPopup = m_parentXdgSurface ? m_parentXdgSurface->m_popup : nullptr; + } + } + +@@ -393,8 +402,6 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent) + { + Q_ASSERT(!m_toplevel && !m_popup); + +- auto parentXdgSurface = static_cast(parent->shellSurface()); +- + auto positioner = new QtWayland::xdg_positioner(m_shell->create_positioner()); + // set_popup expects a position relative to the parent + QPoint transientPos = m_window->geometry().topLeft(); // this is absolute +@@ -411,8 +418,9 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent) + | QtWayland::xdg_positioner::constraint_adjustment_slide_y + | QtWayland::xdg_positioner::constraint_adjustment_flip_x + | QtWayland::xdg_positioner::constraint_adjustment_flip_y); +- m_popup = new Popup(this, parentXdgSurface, positioner); ++ m_popup = new Popup(this, parent, positioner); + positioner->destroy(); ++ + delete positioner; + } + +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +index 96785205..4b518f0a 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +@@ -131,14 +131,15 @@ private: + + class Popup : public QtWayland::xdg_popup { + public: +- Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent, QtWayland::xdg_positioner *positioner); ++ Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent, QtWayland::xdg_positioner *positioner); + ~Popup() override; + + void grab(QWaylandInputDevice *seat, uint serial); + void xdg_popup_popup_done() override; + + QWaylandXdgSurface *m_xdgSurface = nullptr; +- QWaylandXdgSurface *m_parent = nullptr; ++ QWaylandXdgSurface *m_parentXdgSurface = nullptr; ++ QWaylandWindow *m_parent = nullptr; + bool m_grabbing = false; + }; + +diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp +index 73d1eb9c..747875b4 100644 +--- a/tests/auto/client/xdgshell/tst_xdgshell.cpp ++++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp +@@ -46,6 +46,7 @@ private slots: + void configureStates(); + void popup(); + void tooltipOnPopup(); ++ void tooltipAndSiblingPopup(); + void switchPopups(); + void hidePopupParent(); + void pongs(); +@@ -346,6 +347,92 @@ void tst_xdgshell::tooltipOnPopup() + QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); + } + ++void tst_xdgshell::tooltipAndSiblingPopup() ++{ ++ class ToolTip : public QRasterWindow { ++ public: ++ explicit ToolTip(QWindow *parent) { ++ setTransientParent(parent); ++ setFlags(Qt::ToolTip); ++ resize(100, 100); ++ show(); ++ } ++ void mousePressEvent(QMouseEvent *event) override { ++ QRasterWindow::mousePressEvent(event); ++ m_popup = new QRasterWindow; ++ m_popup->setTransientParent(transientParent()); ++ m_popup->setFlags(Qt::Popup); ++ m_popup->resize(100, 100); ++ m_popup->show(); ++ } ++ ++ QRasterWindow *m_popup = nullptr; ++ }; ++ ++ class Window : public QRasterWindow { ++ public: ++ void mousePressEvent(QMouseEvent *event) override { ++ QRasterWindow::mousePressEvent(event); ++ m_tooltip = new ToolTip(this); ++ } ++ ToolTip *m_tooltip = nullptr; ++ }; ++ ++ Window window; ++ window.resize(200, 200); ++ window.show(); ++ ++ QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); ++ exec([=] { xdgToplevel()->sendCompleteConfigure(); }); ++ QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial); ++ ++ exec([=] { ++ auto *surface = xdgToplevel()->surface(); ++ auto *p = pointer(); ++ auto *c = client(); ++ p->sendEnter(surface, {100, 100}); ++ p->sendFrame(c); ++ p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); ++ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); ++ p->sendFrame(c); ++ p->sendLeave(surface); ++ p->sendFrame(c); ++ }); ++ ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup()); ++ exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial); ++ QCOMPOSITOR_TRY_VERIFY(!xdgPopup()->m_grabbed); ++ ++ exec([=] { ++ auto *surface = xdgPopup()->surface(); ++ auto *p = pointer(); ++ auto *c = client(); ++ p->sendEnter(surface, {100, 100}); ++ p->sendFrame(c); ++ p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); ++ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); ++ p->sendFrame(c); ++ }); ++ ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)); ++ exec([=] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_xdgSurface->m_committedConfigureSerial); ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_grabbed); ++ ++ // Close the middle tooltip (it should not close the sibling popup) ++ window.m_tooltip->close(); ++ ++ QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr); ++ // Verify the remaining xdg surface is a grab popup.. ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)); ++ QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)->m_grabbed); ++ ++ window.m_tooltip->m_popup->close(); ++ QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr); ++ QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); ++} ++ + // QTBUG-65680 + void tst_xdgshell::switchPopups() + { +-- +2.40.0 + diff --git a/SOURCES/0030-Build-fixes-for-GCC-11.patch b/SOURCES/0030-Build-fixes-for-GCC-11.patch new file mode 100644 index 0000000..28ad68d --- /dev/null +++ b/SOURCES/0030-Build-fixes-for-GCC-11.patch @@ -0,0 +1,28 @@ +From 08ce7a21017e1f9553cd0f4af1316e38709d5553 Mon Sep 17 00:00:00 2001 +From: Ville Voutilainen +Date: Mon, 18 Jan 2021 12:31:31 +0200 +Subject: [PATCH 30/55] Build fixes for GCC 11 + +Task-number: QTBUG-89977 +Change-Id: I7e3d0964087865e8062f539f851a61f3df017dae +Reviewed-by: David Edmundson +(cherry picked from commit 1aa6ec2c778504d96543f6cdc2b9199a7b066fc1) +--- + tests/auto/client/shared/corecompositor.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tests/auto/client/shared/corecompositor.cpp b/tests/auto/client/shared/corecompositor.cpp +index 5c6c83ba..fa9b7662 100644 +--- a/tests/auto/client/shared/corecompositor.cpp ++++ b/tests/auto/client/shared/corecompositor.cpp +@@ -27,6 +27,7 @@ + ****************************************************************************/ + + #include "corecompositor.h" ++#include + + namespace MockCompositor { + +-- +2.40.0 + diff --git a/SOURCES/0031-Check-pointer-for-null-before-use-in-ASSERT.patch b/SOURCES/0031-Check-pointer-for-null-before-use-in-ASSERT.patch new file mode 100644 index 0000000..c6a4793 --- /dev/null +++ b/SOURCES/0031-Check-pointer-for-null-before-use-in-ASSERT.patch @@ -0,0 +1,34 @@ +From ec0171610350305be51c3e4be7c6f086cb360fe6 Mon Sep 17 00:00:00 2001 +From: Roman Genkhel +Date: Thu, 12 Nov 2020 12:21:51 +0300 +Subject: [PATCH 31/55] Check pointer for null before use in ASSERT + +Task-number: QTBUG-85195 +Change-Id: I331e54f6e58aa9d536351a55223610c60b3cb414 +Reviewed-by: David Edmundson +(cherry picked from commit e235e8ddb1fc3cc5ab3b70b1fb285770b2c8c9ca) +--- + src/client/qwaylandwindow.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 9e82c174..0a5fc15b 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -536,12 +536,12 @@ void QWaylandWindow::sendRecursiveExposeEvent() + + void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y) + { +- Q_ASSERT(!buffer->committed()); + QReadLocker locker(&mSurfaceLock); + if (mSurface == nullptr) + return; + + if (buffer) { ++ Q_ASSERT(!buffer->committed()); + handleUpdate(); + buffer->setBusy(); + +-- +2.40.0 + diff --git a/SOURCES/0032-Use-wl_surface.damage_buffer-on-the-client-side.patch b/SOURCES/0032-Use-wl_surface.damage_buffer-on-the-client-side.patch new file mode 100644 index 0000000..5f02727 --- /dev/null +++ b/SOURCES/0032-Use-wl_surface.damage_buffer-on-the-client-side.patch @@ -0,0 +1,131 @@ +From 2d3d929c86c5aab6dd30e02ade25697d49617335 Mon Sep 17 00:00:00 2001 +From: Paul Olav Tvete +Date: Mon, 6 Jul 2020 14:37:35 +0200 +Subject: [PATCH 32/55] Use wl_surface.damage_buffer on the client side + +Prefer the newer, recommended damage_buffer when the compositor +supports it. + +Fixes: QTBUG-74929 +Change-Id: I9107966910b616a666931404a7b41bfac14c22c0 +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 314fd6db51277224cdc799b039ef79db1101f5cd) +--- + src/client/qwaylanddisplay.cpp | 2 +- + src/client/qwaylandwindow.cpp | 16 +++++++++++++--- + tests/auto/client/shared/coreprotocol.h | 2 +- + tests/auto/client/shared_old/mockcompositor.cpp | 2 +- + tests/auto/client/shared_old/mocksurface.cpp | 10 ++++++++++ + tests/auto/client/shared_old/mocksurface.h | 2 ++ + 6 files changed, 28 insertions(+), 6 deletions(-) + +diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp +index 1b9ec699..6898a881 100644 +--- a/src/client/qwaylanddisplay.cpp ++++ b/src/client/qwaylanddisplay.cpp +@@ -493,7 +493,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin + if (interface == QStringLiteral("wl_output")) { + mWaitingScreens << new QWaylandScreen(this, version, id); + } else if (interface == QStringLiteral("wl_compositor")) { +- mCompositorVersion = qMin((int)version, 3); ++ mCompositorVersion = qMin((int)version, 4); + mCompositor.init(registry, id, mCompositorVersion); + } else if (interface == QStringLiteral("wl_shm")) { + mShm.reset(new QWaylandShm(this, version, id)); +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 0a5fc15b..5b7f9df9 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -563,7 +563,11 @@ void QWaylandWindow::damage(const QRect &rect) + if (mSurface == nullptr) + return; + +- mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); ++ const int s = scale(); ++ if (mDisplay->compositorVersion() >= 4) ++ mSurface->damage_buffer(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height()); ++ else ++ mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); + } + + void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage) +@@ -599,8 +603,14 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage) + return; + + attachOffset(buffer); +- for (const QRect &rect: damage) +- mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); ++ if (mDisplay->compositorVersion() >= 4) { ++ const int s = scale(); ++ for (const QRect &rect: damage) ++ mSurface->damage_buffer(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height()); ++ } else { ++ for (const QRect &rect: damage) ++ mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height()); ++ } + Q_ASSERT(!buffer->committed()); + buffer->setCommitted(); + mSurface->commit(); +diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h +index a1af137a..296dbf47 100644 +--- a/tests/auto/client/shared/coreprotocol.h ++++ b/tests/auto/client/shared/coreprotocol.h +@@ -158,7 +158,7 @@ class WlCompositor : public Global, public QtWaylandServer::wl_compositor + { + Q_OBJECT + public: +- explicit WlCompositor(CoreCompositor *compositor, int version = 3) ++ explicit WlCompositor(CoreCompositor *compositor, int version = 4) + : QtWaylandServer::wl_compositor(compositor->m_display, version) + , m_compositor(compositor) + {} +diff --git a/tests/auto/client/shared_old/mockcompositor.cpp b/tests/auto/client/shared_old/mockcompositor.cpp +index a415cbf5..b1d3d07d 100644 +--- a/tests/auto/client/shared_old/mockcompositor.cpp ++++ b/tests/auto/client/shared_old/mockcompositor.cpp +@@ -342,7 +342,7 @@ Compositor::Compositor(MockCompositor *mockCompositor) + exit(EXIT_FAILURE); + } + +- wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor); ++ wl_global_create(m_display, &wl_compositor_interface, 4, this, bindCompositor); + + m_data_device_manager.reset(new DataDeviceManager(this, m_display)); + +diff --git a/tests/auto/client/shared_old/mocksurface.cpp b/tests/auto/client/shared_old/mocksurface.cpp +index e9df5f90..c3246e4a 100644 +--- a/tests/auto/client/shared_old/mocksurface.cpp ++++ b/tests/auto/client/shared_old/mocksurface.cpp +@@ -125,6 +125,16 @@ void Surface::surface_damage(Resource *resource, + Q_UNUSED(height); + } + ++void Surface::surface_damage_buffer(Resource *resource, ++ int32_t x, int32_t y, int32_t width, int32_t height) ++{ ++ Q_UNUSED(resource); ++ Q_UNUSED(x); ++ Q_UNUSED(y); ++ Q_UNUSED(width); ++ Q_UNUSED(height); ++} ++ + void Surface::surface_frame(Resource *resource, + uint32_t callback) + { +diff --git a/tests/auto/client/shared_old/mocksurface.h b/tests/auto/client/shared_old/mocksurface.h +index 949dc23d..d176837e 100644 +--- a/tests/auto/client/shared_old/mocksurface.h ++++ b/tests/auto/client/shared_old/mocksurface.h +@@ -65,6 +65,8 @@ protected: + struct wl_resource *buffer, int x, int y) override; + void surface_damage(Resource *resource, + int32_t x, int32_t y, int32_t width, int32_t height) override; ++ void surface_damage_buffer(Resource *resource, ++ int32_t x, int32_t y, int32_t width, int32_t height) override; + void surface_frame(Resource *resource, + uint32_t callback) override; + void surface_commit(Resource *resource) override; +-- +2.40.0 + diff --git a/SOURCES/0033-Client-clear-focus-on-touch-cancel.patch b/SOURCES/0033-Client-clear-focus-on-touch-cancel.patch new file mode 100644 index 0000000..7513b34 --- /dev/null +++ b/SOURCES/0033-Client-clear-focus-on-touch-cancel.patch @@ -0,0 +1,116 @@ +From bdf04e01153aeda2c2bafa059dd21dc377eb82e1 Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Fri, 5 Aug 2022 15:00:31 +0100 +Subject: [PATCH 33/55] Client: clear focus on touch cancel + +When we get a touch_cancel event all touches should be treated as +lifted. + +The next frame call focus is set, with no pending touch points but +without having gone through touch_up. We call mPendingTouchPoints.last() +without guards even though it is potentially now empty. + +Change-Id: I3719f9507c5d397d8641692271d878076b7c23b8 +Reviewed-by: Shawn Rutledge +Reviewed-by: Liang Qi +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit dbdcd92363b44d89440dcb195d8cb9e6c34f0ddf) +--- + src/client/qwaylandinputdevice.cpp | 1 + + tests/auto/client/seatv5/tst_seatv5.cpp | 30 +++++++++++++++++++++++ + tests/auto/client/shared/coreprotocol.cpp | 7 ++++++ + tests/auto/client/shared/coreprotocol.h | 1 + + 4 files changed, 39 insertions(+) + +diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp +index 5d704795..5b880984 100644 +--- a/src/client/qwaylandinputdevice.cpp ++++ b/src/client/qwaylandinputdevice.cpp +@@ -1392,6 +1392,7 @@ void QWaylandInputDevice::Touch::touch_cancel() + if (touchExt) + touchExt->touchCanceled(); + ++ mFocus = nullptr; + QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice); + } + +diff --git a/tests/auto/client/seatv5/tst_seatv5.cpp b/tests/auto/client/seatv5/tst_seatv5.cpp +index 9312c2e5..b063e0d9 100644 +--- a/tests/auto/client/seatv5/tst_seatv5.cpp ++++ b/tests/auto/client/seatv5/tst_seatv5.cpp +@@ -73,6 +73,7 @@ private slots: + void multiTouch(); + void multiTouchUpAndMotionFrame(); + void tapAndMoveInSameFrame(); ++ void cancelTouch(); + }; + + void tst_seatv5::bindsToSeat() +@@ -646,5 +647,34 @@ void tst_seatv5::tapAndMoveInSameFrame() + QTRY_COMPARE(window.m_events.last().touchPoints.first().state(), Qt::TouchPointState::TouchPointReleased); + } + ++void tst_seatv5::cancelTouch() ++{ ++ TouchWindow window; ++ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); ++ ++ exec([=] { ++ auto *t = touch(); ++ auto *c = client(); ++ t->sendDown(xdgToplevel()->surface(), {32, 32}, 1); ++ t->sendFrame(c); ++ t->sendCancel(c); ++ t->sendFrame(c); ++ }); ++ ++ QTRY_VERIFY(!window.m_events.empty()); ++ { ++ auto e = window.m_events.takeFirst(); ++ QCOMPARE(e.type, QEvent::TouchBegin); ++ QCOMPARE(e.touchPointStates, QEventPoint::State::Pressed); ++ QCOMPARE(e.touchPoints.length(), 1); ++ QCOMPARE(e.touchPoints.first().position(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top())); ++ } ++ { ++ auto e = window.m_events.takeFirst(); ++ QCOMPARE(e.type, QEvent::TouchCancel); ++ QCOMPARE(e.touchPoints.length(), 0); ++ } ++} ++ + QCOMPOSITOR_TEST_MAIN(tst_seatv5) + #include "tst_seatv5.moc" +diff --git a/tests/auto/client/shared/coreprotocol.cpp b/tests/auto/client/shared/coreprotocol.cpp +index 0d988521..d1a2e7cb 100644 +--- a/tests/auto/client/shared/coreprotocol.cpp ++++ b/tests/auto/client/shared/coreprotocol.cpp +@@ -451,6 +451,13 @@ void Touch::sendFrame(wl_client *client) + send_frame(r->handle); + } + ++void Touch::sendCancel(wl_client *client) ++{ ++ const auto touchResources = resourceMap().values(client); ++ for (auto *r : touchResources) ++ send_cancel(r->handle); ++} ++ + uint Keyboard::sendEnter(Surface *surface) + { + auto serial = m_seat->m_compositor->nextSerial(); +diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h +index 296dbf47..210d8ddb 100644 +--- a/tests/auto/client/shared/coreprotocol.h ++++ b/tests/auto/client/shared/coreprotocol.h +@@ -364,6 +364,7 @@ public: + uint sendUp(wl_client *client, int id); + void sendMotion(wl_client *client, const QPointF &position, int id); + void sendFrame(wl_client *client); ++ void sendCancel(wl_client *client); + + Seat *m_seat = nullptr; + }; +-- +2.40.0 + diff --git a/SOURCES/0034-Guard-mResizeDirty-by-the-correctMutex.patch b/SOURCES/0034-Guard-mResizeDirty-by-the-correctMutex.patch new file mode 100644 index 0000000..82ee313 --- /dev/null +++ b/SOURCES/0034-Guard-mResizeDirty-by-the-correctMutex.patch @@ -0,0 +1,39 @@ +From e920173ee9cef08d10ad8a7e21ae7a5a2c2d7530 Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Thu, 3 Feb 2022 19:42:33 +0000 +Subject: [PATCH 34/55] Guard mResizeDirty by the correctMutex + +mResizeDirty is used in the GUI thread in setCanResize which can be +called from the GUI thread. It is queried and set whilst the resizeLock +is held. We need to guard our usage. + +Change-Id: I5f8dcf8aa2cb2c4bb6274103df1da9e3e268605a +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 4ac96662c936821efff2875bbe555b40612caf8a) +--- + src/client/qwaylandwindow.cpp | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 5b7f9df9..117e3383 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -358,11 +358,12 @@ void QWaylandWindow::setGeometry(const QRect &rect) + if (mWindowDecoration) + mWindowDecoration->update(); + +- if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize) ++ if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize) { ++ QMutexLocker lock(&mResizeLock); + mResizeDirty = true; +- else ++ } else { + QWindowSystemInterface::handleGeometryChange(window(), geometry()); +- ++ } + mSentInitialResize = true; + } + QRect exposeGeometry(QPoint(), geometry().size()); +-- +2.40.0 + diff --git a/SOURCES/0035-client-Synthesize-enter-leave-event-for-popup-in-xdg.patch b/SOURCES/0035-client-Synthesize-enter-leave-event-for-popup-in-xdg.patch new file mode 100644 index 0000000..9cfd102 --- /dev/null +++ b/SOURCES/0035-client-Synthesize-enter-leave-event-for-popup-in-xdg.patch @@ -0,0 +1,72 @@ +From 1ff4219d8c58f8356d9d12824948013cdfde6ac5 Mon Sep 17 00:00:00 2001 +From: Liang Qi +Date: Fri, 11 Mar 2022 09:17:25 +0100 +Subject: [PATCH 35/55] client: Synthesize enter/leave event for popup in + xdg-shell +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fixes: QTBUG-100148 +Pick-to: 6.3 6.2 5.15 +Change-Id: I45e3156d7942cff9968674c0b253d15be7235921 +Reviewed-by: Tang Haixiang +Reviewed-by: Qt CI Bot +Reviewed-by: Tor Arne Vestbø +(cherry picked from commit 73d35d3117722cef8e94f0d2036c56ad0a5ddae9) + +* asturmlechner 2022-09-08: Resolve conflict with dev branch commits + b6a3a938abd4a7fdb7ea96a38485b53f394fba17 and + f8e3257e9b1e22d52e9c221c62b8d9b6dd1151a3 +--- + .../xdg-shell/qwaylandxdgshell.cpp | 27 +++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +index 962001b3..ead99989 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +@@ -221,6 +221,16 @@ QWaylandXdgSurface::Popup::~Popup() + auto *shell = m_xdgSurface->m_shell; + Q_ASSERT(shell->m_topmostGrabbingPopup == this); + shell->m_topmostGrabbingPopup = m_parentXdgSurface ? m_parentXdgSurface->m_popup : nullptr; ++ m_grabbing = false; ++ ++ // Synthesize Qt enter/leave events for popup ++ QWindow *leave = nullptr; ++ if (m_xdgSurface && m_xdgSurface->window()) ++ leave = m_xdgSurface->window()->window(); ++ QWindowSystemInterface::handleLeaveEvent(leave); ++ ++ if (QWindow *enter = QGuiApplication::topLevelAt(QCursor::pos())) ++ QWindowSystemInterface::handleEnterEvent(enter, enter->mapFromGlobal(QCursor::pos()), QCursor::pos()); + } + } + +@@ -442,6 +452,23 @@ void QWaylandXdgSurface::setGrabPopup(QWaylandWindow *parent, QWaylandInputDevic + } + setPopup(parent); + m_popup->grab(device, serial); ++ ++ // Synthesize Qt enter/leave events for popup ++ if (!parent) ++ return; ++ QWindow *current = QGuiApplication::topLevelAt(QCursor::pos()); ++ QWindow *leave = parent->window(); ++ if (current != leave) ++ return; ++ ++ QWindowSystemInterface::handleLeaveEvent(leave); ++ ++ QWindow *enter = nullptr; ++ if (m_popup && m_popup->m_xdgSurface && m_popup->m_xdgSurface->window()) ++ enter = m_popup->m_xdgSurface->window()->window(); ++ ++ if (enter) ++ QWindowSystemInterface::handleEnterEvent(enter, enter->mapFromGlobal(QCursor::pos()), QCursor::pos()); + } + + void QWaylandXdgSurface::xdg_surface_configure(uint32_t serial) +-- +2.40.0 + diff --git a/SOURCES/0036-Fix-compile-tests.patch b/SOURCES/0036-Fix-compile-tests.patch new file mode 100644 index 0000000..83acc1f --- /dev/null +++ b/SOURCES/0036-Fix-compile-tests.patch @@ -0,0 +1,29 @@ +From bf6b509011c4086cc7b1a7cfdc78dd50e446b15f Mon Sep 17 00:00:00 2001 +From: Albert Astals Cid +Date: Fri, 9 Sep 2022 15:37:49 +0200 +Subject: [PATCH 36/55] Fix compile tests + +Broken in c618467da4c06528537026e2b78f92265bce446f +--- + tests/auto/client/seatv5/tst_seatv5.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tests/auto/client/seatv5/tst_seatv5.cpp b/tests/auto/client/seatv5/tst_seatv5.cpp +index b063e0d9..2ea382f1 100644 +--- a/tests/auto/client/seatv5/tst_seatv5.cpp ++++ b/tests/auto/client/seatv5/tst_seatv5.cpp +@@ -665,9 +665,9 @@ void tst_seatv5::cancelTouch() + { + auto e = window.m_events.takeFirst(); + QCOMPARE(e.type, QEvent::TouchBegin); +- QCOMPARE(e.touchPointStates, QEventPoint::State::Pressed); ++ QCOMPARE(e.touchPointStates, Qt::TouchPointPressed); + QCOMPARE(e.touchPoints.length(), 1); +- QCOMPARE(e.touchPoints.first().position(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top())); ++ QCOMPARE(e.touchPoints.first().pos(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top())); + } + { + auto e = window.m_events.takeFirst(); +-- +2.40.0 + diff --git a/SOURCES/0037-Use-CRLF-line-delimiter-for-text-uri-list-data.patch b/SOURCES/0037-Use-CRLF-line-delimiter-for-text-uri-list-data.patch new file mode 100644 index 0000000..3664c5c --- /dev/null +++ b/SOURCES/0037-Use-CRLF-line-delimiter-for-text-uri-list-data.patch @@ -0,0 +1,40 @@ +From 137958eec28cb8209069f9a3a3ab778202773ff6 Mon Sep 17 00:00:00 2001 +From: Alexandros Frantzis +Date: Wed, 11 May 2022 17:12:52 +0300 +Subject: [PATCH 37/55] Use CRLF line delimiter for text/uri-list data + +According to RFC 2483, which describes text/uri-list, the line delimiter +must be CRLF (instead of the currently used LF). Some applications +strictly expect the CRLF delimiter and fail to properly parse the +uri-list otherwise (e.g., WineX11/XWayland). + +https://datatracker.ietf.org/doc/html/rfc2483 + +5. The text/uri-list Internet Media Type +The format of text/uri-list resources is: +3) As for all text/* formats, lines are terminated with a CRLF pair. + +Pick-to: 6.4 6.3 6.2 5.15 +Change-Id: I7c062224a9060028ab6293fdf172692ade28cca5 +Reviewed-by: David Edmundson +(cherry picked from commit bd5b0a804b91b9fbd0ce44d5d6765e07d0a50b4f) +--- + src/shared/qwaylandmimehelper.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/shared/qwaylandmimehelper.cpp b/src/shared/qwaylandmimehelper.cpp +index 051a91dc..e2fe1928 100644 +--- a/src/shared/qwaylandmimehelper.cpp ++++ b/src/shared/qwaylandmimehelper.cpp +@@ -74,7 +74,7 @@ QByteArray QWaylandMimeHelper::getByteArray(QMimeData *mimeData, const QString & + QList urls = mimeData->urls(); + for (int i = 0; i < urls.count(); ++i) { + content.append(urls.at(i).toEncoded()); +- content.append('\n'); ++ content.append("\r\n"); + } + } else { + content = mimeData->data(mimeType); +-- +2.40.0 + diff --git a/SOURCES/0038-Avoid-calling-requestUpdate-from-wrong-thread.patch b/SOURCES/0038-Avoid-calling-requestUpdate-from-wrong-thread.patch new file mode 100644 index 0000000..dc29759 --- /dev/null +++ b/SOURCES/0038-Avoid-calling-requestUpdate-from-wrong-thread.patch @@ -0,0 +1,43 @@ +From 0064749af275016ae3b4b09964d8d31d756d3468 Mon Sep 17 00:00:00 2001 +From: Eskil Abrahamsen Blomfeldt +Date: Mon, 8 Aug 2022 12:14:01 +0200 +Subject: [PATCH 38/55] Avoid calling requestUpdate from wrong thread + +In certain circumstances, we can get to createDecoration() +from the render thread (from QWaylandGLContext::makeCurrent) + +Calling requestUpdate() from this secondary thread would +cause an assert, so we queue the call on the appropriate +thread instead. + +This amends af7b60ade5c4be81cbc58eb18307c017d5594071. + +Pick-to: 5.15 6.2 6.3 6.3.2 6.4 +Fixes: QTBUG-105308 +Change-Id: I4805265f39e24eb1464897532be2025bc3c27728 +Reviewed-by: Inho Lee +(cherry picked from commit a0c0b5b42335808c2222cbf72c1758e955731ed9) +--- + src/client/qwaylandwindow.cpp | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 117e3383..4ddf9fbe 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -873,7 +873,11 @@ bool QWaylandWindow::createDecoration() + // size and are not redrawn, leaving the new buffer empty. As a simple + // work-around, we trigger a full extra update whenever the client-side + // window decorations are toggled while the window is showing. +- window()->requestUpdate(); ++ // Note: createDecoration() is sometimes called from the render thread ++ // of Qt Quick. This is essentially wrong and could potentially cause problems, ++ // but until the underlying issue has been fixed, we have to use invokeMethod() ++ // here to avoid asserts. ++ QMetaObject::invokeMethod(window(), &QWindow::requestUpdate); + } + + return mWindowDecoration; +-- +2.40.0 + diff --git a/SOURCES/0039-Call-finishDrag-in-QWaylandDataDevice-dragSourceCanc.patch b/SOURCES/0039-Call-finishDrag-in-QWaylandDataDevice-dragSourceCanc.patch new file mode 100644 index 0000000..777e40d --- /dev/null +++ b/SOURCES/0039-Call-finishDrag-in-QWaylandDataDevice-dragSourceCanc.patch @@ -0,0 +1,38 @@ +From 35c03e72007630040659188b2bc7f47b1df82577 Mon Sep 17 00:00:00 2001 +From: Fushan Wen +Date: Sun, 18 Sep 2022 18:17:18 +0800 +Subject: [PATCH 39/55] Call `finishDrag()` in + `QWaylandDataDevice::dragSourceCancelled()` + +Drags can either get finished or cancelled. If a drag is finished +successfully we call finish on the QBasicDrag instance, which quits +the nested event loop. This patch adds the connection for cancelled +drags. + +See also: https://bugs.kde.org/show_bug.cgi?id=446111 + +Pick-to: 6.4 6.2 5.15 +Change-Id: Ib93040648da88a433d647c87adcb7a7fabcaef6c +Reviewed-by: Liang Qi +(cherry picked from commit c92282b865efcf8c571bb52b5f96d8ad260a1cda) + +BUG: 446111 +--- + src/client/qwaylanddatadevice.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/client/qwaylanddatadevice.cpp b/src/client/qwaylanddatadevice.cpp +index fbb5aa91..e3e60ed5 100644 +--- a/src/client/qwaylanddatadevice.cpp ++++ b/src/client/qwaylanddatadevice.cpp +@@ -296,6 +296,7 @@ void QWaylandDataDevice::selectionSourceCancelled() + #if QT_CONFIG(draganddrop) + void QWaylandDataDevice::dragSourceCancelled() + { ++ static_cast(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(); + m_dragSource.reset(); + } + +-- +2.40.0 + diff --git a/SOURCES/0040-Hold-surface-read-lock-throughout-QWaylandEglWindow-.patch b/SOURCES/0040-Hold-surface-read-lock-throughout-QWaylandEglWindow-.patch new file mode 100644 index 0000000..bb0cfdc --- /dev/null +++ b/SOURCES/0040-Hold-surface-read-lock-throughout-QWaylandEglWindow-.patch @@ -0,0 +1,85 @@ +From 37ad4aeaa6dce748f5f6bcc5030be187e6320ec0 Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Mon, 12 Sep 2022 13:28:08 +0100 +Subject: [PATCH 40/55] Hold surface read lock throughout + QWaylandEglWindow::updateSurface + +QWaylandEGLWindow::updateSurface is called from both the main and render +threads. It is called on the render thread when making the surface +current, which could be after the window is hidden if there are cleanup +jobs to be done. + +Whilst the getter wlSurface() holds a read lock, it's not enough as we +need the instance alive between the two calls and throughout the mesa +code. + +This potentially fixes a crash seen in mesa where we crash creating a +surface for an invalid wl_surface object. + +Change-Id: I497356e752ffaf3549d174f10c4c268234b02cbd +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 50f1ccc66c68f9f4c0b08400747942109c16b2be) +--- + src/client/qwaylandwindow_p.h | 6 ++++-- + .../client/wayland-egl/qwaylandeglwindow.cpp | 6 ++++-- + 2 files changed, 8 insertions(+), 4 deletions(-) + +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index 2be87bc0..ea3d1995 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -220,7 +220,11 @@ signals: + + protected: + QWaylandDisplay *mDisplay = nullptr; ++ ++ // mSurface can be written by the main thread. Other threads should claim a read lock for access ++ mutable QReadWriteLock mSurfaceLock; + QScopedPointer mSurface; ++ + QWaylandShellSurface *mShellSurface = nullptr; + QWaylandSubSurface *mSubSurfaceWindow = nullptr; + QVector mChildren; +@@ -294,8 +298,6 @@ private: + + static QWaylandWindow *mMouseGrab; + +- mutable QReadWriteLock mSurfaceLock; +- + friend class QWaylandSubSurface; + }; + +diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp +index 13dd747a..872a6237 100644 +--- a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp ++++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp +@@ -40,6 +40,7 @@ + #include "qwaylandeglwindow.h" + + #include ++#include + #include "qwaylandglcontext.h" + + #include +@@ -115,6 +116,7 @@ void QWaylandEglWindow::updateSurface(bool create) + } + mOffset = QPoint(); + } else { ++ QReadLocker locker(&mSurfaceLock); + if (m_waylandEglWindow) { + int current_width, current_height; + static bool disableResizeCheck = qgetenv("QT_WAYLAND_DISABLE_RESIZECHECK").toInt(); +@@ -129,8 +131,8 @@ void QWaylandEglWindow::updateSurface(bool create) + + m_resize = true; + } +- } else if (create && wlSurface()) { +- m_waylandEglWindow = wl_egl_window_create(wlSurface(), sizeWithMargins.width(), sizeWithMargins.height()); ++ } else if (create && mSurface) { ++ m_waylandEglWindow = wl_egl_window_create(mSurface->object(), sizeWithMargins.width(), sizeWithMargins.height()); + m_requestedSize = sizeWithMargins; + } + +-- +2.40.0 + diff --git a/SOURCES/0041-Client-Ensure-that-wl_surface-lives-as-long-as-qtqui.patch b/SOURCES/0041-Client-Ensure-that-wl_surface-lives-as-long-as-qtqui.patch new file mode 100644 index 0000000..95aa134 --- /dev/null +++ b/SOURCES/0041-Client-Ensure-that-wl_surface-lives-as-long-as-qtqui.patch @@ -0,0 +1,109 @@ +From 1012f1d4b5753ad63da3cca1226fb034e297ae6d Mon Sep 17 00:00:00 2001 +From: Vlad Zahorodnii +Date: Tue, 8 Nov 2022 16:10:18 +0200 +Subject: [PATCH 41/55] Client: Ensure that wl_surface lives as long as qtquick + render thread needs it + +wl_surface can be destroyed while qtquick render thread still uses it. +That can end up in eglSwapBuffers() using defunct wl_surface, which will +eventually lead to a crash due to the compositor posting an error. + +This is partially cherry-pick of dff579147b07cd15888a47c303e36684e9930f9f + +Change-Id: I044f40dd64e6672027a833379b57ccd9973d8305 +--- + src/client/qwaylandwindow.cpp | 13 ++++++++++++- + src/client/qwaylandwindow_p.h | 3 +++ + .../client/wayland-egl/qwaylandglcontext.cpp | 6 +++++- + 3 files changed, 20 insertions(+), 2 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 4ddf9fbe..1f2d56b5 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -76,6 +76,7 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr; + QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) + : QPlatformWindow(window) + , mDisplay(display) ++ , mSurfaceLock(QReadWriteLock::Recursive) + , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP")) + { + { +@@ -237,6 +238,16 @@ bool QWaylandWindow::shouldCreateSubSurface() const + return QPlatformWindow::parent() != nullptr; + } + ++void QWaylandWindow::beginFrame() ++{ ++ mSurfaceLock.lockForRead(); ++} ++ ++void QWaylandWindow::endFrame() ++{ ++ mSurfaceLock.unlock(); ++} ++ + void QWaylandWindow::reset() + { + closeChildPopups(); +@@ -245,10 +256,10 @@ void QWaylandWindow::reset() + delete mSubSurfaceWindow; + mSubSurfaceWindow = nullptr; + +- invalidateSurface(); + if (mSurface) { + emit wlSurfaceDestroyed(); + QWriteLocker lock(&mSurfaceLock); ++ invalidateSurface(); + mSurface.reset(); + } + +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index ea3d1995..e18609d9 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -207,6 +207,9 @@ public: + void handleUpdate(); + void deliverUpdateRequest() override; + ++ void beginFrame(); ++ void endFrame(); ++ + void addChildPopup(QWaylandWindow* child); + void removeChildPopup(QWaylandWindow* child); + void closeChildPopups(); +diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp +index c1f45fa6..5d6fb2bf 100644 +--- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp ++++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp +@@ -432,8 +432,10 @@ bool QWaylandGLContext::makeCurrent(QPlatformSurface *surface) + return true; + } + +- if (window->isExposed()) ++ if (window->isExposed()) { ++ window->beginFrame(); + window->setCanResize(false); ++ } + if (m_decorationsContext != EGL_NO_CONTEXT && !window->decoration()) + window->createDecoration(); + +@@ -449,6 +451,7 @@ bool QWaylandGLContext::makeCurrent(QPlatformSurface *surface) + if (!eglMakeCurrent(m_eglDisplay, eglSurface, eglSurface, m_context)) { + qWarning("QWaylandGLContext::makeCurrent: eglError: %x, this: %p \n", eglGetError(), this); + window->setCanResize(true); ++ window->endFrame(); + return false; + } + +@@ -502,6 +505,7 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface) + eglSwapBuffers(m_eglDisplay, eglSurface); + + window->setCanResize(true); ++ window->endFrame(); + } + + GLuint QWaylandGLContext::defaultFramebufferObject(QPlatformSurface *surface) const +-- +2.40.0 + diff --git a/SOURCES/0042-Keep-toplevel-windows-in-the-top-left-corner-of-the-.patch b/SOURCES/0042-Keep-toplevel-windows-in-the-top-left-corner-of-the-.patch new file mode 100644 index 0000000..cc97967 --- /dev/null +++ b/SOURCES/0042-Keep-toplevel-windows-in-the-top-left-corner-of-the-.patch @@ -0,0 +1,90 @@ +From 61fccee064cd8cea698a530c76113f8487d11d68 Mon Sep 17 00:00:00 2001 +From: David Redondo +Date: Wed, 8 Jun 2022 11:25:59 +0200 +Subject: [PATCH 42/55] Keep toplevel windows in the top left corner of the + screen + +We can't know the actual position of a window on the screen. This causes +an issue when Widgets try to position a popup/menu absolutely and keep +it on the screen when the screen geometry doesn't include (0,0). +Instead report their positions always as the top left corner of +the screen that they are on. +This new behavior can be disabled for qt-shell or via an environment +variable by users that rely on the old behavior. + +Fixes: QTBUG-85297 +Change-Id: Iacb91cb03a0df87af950115760d2f41124ac06a3 +Reviewed-by: Qt CI Bot +Reviewed-by: David Edmundson +Reviewed-by: Aleix Pol Gonzalez +(cherry picked from commit a46795a22e05722917c6ebc60ed01bebf49898ae) +--- + src/client/qwaylandintegration.cpp | 3 +++ + src/client/qwaylandwindow.cpp | 14 +++++++++++++- + src/client/qwaylandwindow_p.h | 3 +++ + 3 files changed, 19 insertions(+), 1 deletion(-) + +diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp +index fbf00c6b..54861600 100644 +--- a/src/client/qwaylandintegration.cpp ++++ b/src/client/qwaylandintegration.cpp +@@ -125,6 +125,9 @@ QWaylandIntegration::QWaylandIntegration() + #endif + + reconfigureInputContext(); ++ ++ QWaylandWindow::fixedToplevelPositions = ++ !qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_FIXED_POSITIONS"); + } + + QWaylandIntegration::~QWaylandIntegration() +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 1f2d56b5..d3459168 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -361,8 +361,13 @@ void QWaylandWindow::setGeometry_helper(const QRect &rect) + } + } + +-void QWaylandWindow::setGeometry(const QRect &rect) ++void QWaylandWindow::setGeometry(const QRect &r) + { ++ auto rect = r; ++ if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup ++ && window()->type() != Qt::ToolTip) { ++ rect.moveTo(screen()->geometry().topLeft()); ++ } + setGeometry_helper(rect); + + if (window()->isVisible() && rect.isValid()) { +@@ -1044,6 +1049,13 @@ void QWaylandWindow::handleScreensChanged() + + QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); + mLastReportedScreen = newScreen; ++ if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup ++ && window()->type() != Qt::ToolTip ++ && geometry().topLeft() != newScreen->geometry().topLeft()) { ++ auto geometry = this->geometry(); ++ geometry.moveTo(newScreen->geometry().topLeft()); ++ setGeometry(geometry); ++ } + + int scale = newScreen->isPlaceholder() ? 1 : static_cast(newScreen)->scale(); + if (scale != mScale) { +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index e18609d9..a8ee2696 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -98,6 +98,9 @@ public: + QWaylandWindow(QWindow *window, QWaylandDisplay *display); + ~QWaylandWindow() override; + ++ // Keep Toplevels position on the top left corner of their screen ++ static inline bool fixedToplevelPositions = true; ++ + virtual WindowType windowType() const = 0; + virtual void ensureSize(); + WId winId() const override; +-- +2.40.0 + diff --git a/SOURCES/0043-Revert-Client-Ensure-that-wl_surface-lives-as-long-a.patch b/SOURCES/0043-Revert-Client-Ensure-that-wl_surface-lives-as-long-a.patch new file mode 100644 index 0000000..ffe2322 --- /dev/null +++ b/SOURCES/0043-Revert-Client-Ensure-that-wl_surface-lives-as-long-a.patch @@ -0,0 +1,103 @@ +From 3813da4b6d88320b42a7d91ae100e1567113ee72 Mon Sep 17 00:00:00 2001 +From: David Edmundson +Date: Mon, 14 Nov 2022 10:43:25 +0000 +Subject: [PATCH 43/55] Revert "Client: Ensure that wl_surface lives as long as + qtquick render thread needs it" + +This reverts commit 81a7702a87f386a60a0ac8c902e203daae044d81 +--- + src/client/qwaylandwindow.cpp | 13 +------------ + src/client/qwaylandwindow_p.h | 3 --- + .../client/wayland-egl/qwaylandglcontext.cpp | 6 +----- + 3 files changed, 2 insertions(+), 20 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index d3459168..f322a8d6 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -76,7 +76,6 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr; + QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display) + : QPlatformWindow(window) + , mDisplay(display) +- , mSurfaceLock(QReadWriteLock::Recursive) + , mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP")) + { + { +@@ -238,16 +237,6 @@ bool QWaylandWindow::shouldCreateSubSurface() const + return QPlatformWindow::parent() != nullptr; + } + +-void QWaylandWindow::beginFrame() +-{ +- mSurfaceLock.lockForRead(); +-} +- +-void QWaylandWindow::endFrame() +-{ +- mSurfaceLock.unlock(); +-} +- + void QWaylandWindow::reset() + { + closeChildPopups(); +@@ -256,10 +245,10 @@ void QWaylandWindow::reset() + delete mSubSurfaceWindow; + mSubSurfaceWindow = nullptr; + ++ invalidateSurface(); + if (mSurface) { + emit wlSurfaceDestroyed(); + QWriteLocker lock(&mSurfaceLock); +- invalidateSurface(); + mSurface.reset(); + } + +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index a8ee2696..487a91a6 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -210,9 +210,6 @@ public: + void handleUpdate(); + void deliverUpdateRequest() override; + +- void beginFrame(); +- void endFrame(); +- + void addChildPopup(QWaylandWindow* child); + void removeChildPopup(QWaylandWindow* child); + void closeChildPopups(); +diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp +index 5d6fb2bf..c1f45fa6 100644 +--- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp ++++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp +@@ -432,10 +432,8 @@ bool QWaylandGLContext::makeCurrent(QPlatformSurface *surface) + return true; + } + +- if (window->isExposed()) { +- window->beginFrame(); ++ if (window->isExposed()) + window->setCanResize(false); +- } + if (m_decorationsContext != EGL_NO_CONTEXT && !window->decoration()) + window->createDecoration(); + +@@ -451,7 +449,6 @@ bool QWaylandGLContext::makeCurrent(QPlatformSurface *surface) + if (!eglMakeCurrent(m_eglDisplay, eglSurface, eglSurface, m_context)) { + qWarning("QWaylandGLContext::makeCurrent: eglError: %x, this: %p \n", eglGetError(), this); + window->setCanResize(true); +- window->endFrame(); + return false; + } + +@@ -505,7 +502,6 @@ void QWaylandGLContext::swapBuffers(QPlatformSurface *surface) + eglSwapBuffers(m_eglDisplay, eglSurface); + + window->setCanResize(true); +- window->endFrame(); + } + + GLuint QWaylandGLContext::defaultFramebufferObject(QPlatformSurface *surface) const +-- +2.40.0 + diff --git a/SOURCES/0044-Client-Add-F_SEAL_SHRINK-seal-to-shm-backing-file.patch b/SOURCES/0044-Client-Add-F_SEAL_SHRINK-seal-to-shm-backing-file.patch new file mode 100644 index 0000000..2f9ec44 --- /dev/null +++ b/SOURCES/0044-Client-Add-F_SEAL_SHRINK-seal-to-shm-backing-file.patch @@ -0,0 +1,53 @@ +From dd605d5fec6921a7befbfe2c40c2ae87935aa9c4 Mon Sep 17 00:00:00 2001 +From: Vlad Zahorodnii +Date: Thu, 17 Nov 2022 15:25:37 +0200 +Subject: [PATCH 44/55] Client: Add F_SEAL_SHRINK seal to shm backing file + +This lets libwayland-server avoid installing a SIGBUS handler when it +wants to mmap() the backing file and access the contents of shared +memory client buffers. + +Change-Id: Id0b17f729799535d73e8700c5a99c32fd88a068a +Reviewed-by: Qt CI Bot +Reviewed-by: David Edmundson +(cherry picked from commit 0c1cbb376e0cf878e3a91ab4917fe784a3b4c547) +--- + src/client/qwaylandshmbackingstore.cpp | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp +index dc7ff670..98acd42d 100644 +--- a/src/client/qwaylandshmbackingstore.cpp ++++ b/src/client/qwaylandshmbackingstore.cpp +@@ -52,6 +52,7 @@ + + #include + ++#include + #include + #include + +@@ -61,6 +62,9 @@ + # ifndef MFD_CLOEXEC + # define MFD_CLOEXEC 0x0001U + # endif ++# ifndef MFD_ALLOW_SEALING ++# define MFD_ALLOW_SEALING 0x0002U ++# endif + #endif + + QT_BEGIN_NAMESPACE +@@ -75,7 +79,9 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, + int fd = -1; + + #ifdef SYS_memfd_create +- fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC); ++ fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING); ++ if (fd >= 0) ++ fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); + #endif + + QScopedPointer filePointer; +-- +2.40.0 + diff --git a/SOURCES/0045-Client-Call-wl_output_release-upon-QWaylandScreen-de.patch b/SOURCES/0045-Client-Call-wl_output_release-upon-QWaylandScreen-de.patch new file mode 100644 index 0000000..3d44293 --- /dev/null +++ b/SOURCES/0045-Client-Call-wl_output_release-upon-QWaylandScreen-de.patch @@ -0,0 +1,31 @@ +From dea12f0bb5729de6c1a31b03b7e79710baf96b63 Mon Sep 17 00:00:00 2001 +From: Vlad Zahorodnii +Date: Mon, 21 Nov 2022 18:39:40 +0200 +Subject: [PATCH 45/55] Client: Call wl_output_release() upon QWaylandScreen + destruction + +It ensures that the proxy gets destroyed. + +Change-Id: I915cc8814e33dd3b0405b2bf82bd12ce6b5f785b +Reviewed-by: David Edmundson +(cherry picked from commit 054e54759dbd6c3e76b55d5c4a9a54f62967ad1a) +--- + src/client/qwaylandscreen.cpp | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp +index 7c2d9be3..64ae4fe7 100644 +--- a/src/client/qwaylandscreen.cpp ++++ b/src/client/qwaylandscreen.cpp +@@ -81,6 +81,8 @@ QWaylandScreen::~QWaylandScreen() + { + if (zxdg_output_v1::isInitialized()) + zxdg_output_v1::destroy(); ++ if (wl_output::isInitialized() && wl_output_get_version(wl_output::object()) >= WL_OUTPUT_RELEASE_SINCE_VERSION) ++ wl_output::release(); + } + + uint QWaylandScreen::requiredEvents() const +-- +2.40.0 + diff --git a/SOURCES/0046-Client-Bump-wl_output-version.patch b/SOURCES/0046-Client-Bump-wl_output-version.patch new file mode 100644 index 0000000..29740c9 --- /dev/null +++ b/SOURCES/0046-Client-Bump-wl_output-version.patch @@ -0,0 +1,30 @@ +From 8071f24324ffa8c556068816e009a5887e1a6ddd Mon Sep 17 00:00:00 2001 +From: Vlad Zahorodnii +Date: Tue, 22 Nov 2022 12:33:41 +0200 +Subject: [PATCH 46/55] Client: Bump wl_output version + +wl_output_release is available starting with wl_output v3. + +Change-Id: I21822b26728ffb9318f1f8b4bd82ef7329682838 +Reviewed-by: David Edmundson +(cherry picked from commit c14916f5fd84f6b5483024b3df77592661a0f04e) +--- + src/client/qwaylandscreen.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp +index 64ae4fe7..5537dafd 100644 +--- a/src/client/qwaylandscreen.cpp ++++ b/src/client/qwaylandscreen.cpp +@@ -60,7 +60,7 @@ QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandDisplay* display, + } + + QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id) +- : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 2)) ++ : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 3)) + , m_outputId(id) + , mWaylandDisplay(waylandDisplay) + , mOutputName(QStringLiteral("Screen%1").arg(id)) +-- +2.40.0 + diff --git a/SOURCES/0047-Fix-frame-sync-related-to-unprotected-multithread-ac.patch b/SOURCES/0047-Fix-frame-sync-related-to-unprotected-multithread-ac.patch new file mode 100644 index 0000000..723d07c --- /dev/null +++ b/SOURCES/0047-Fix-frame-sync-related-to-unprotected-multithread-ac.patch @@ -0,0 +1,166 @@ +From ef5b1f40b684927f73bc04ab84c396be074cb61e Mon Sep 17 00:00:00 2001 +From: Weng Xuetian +Date: Sun, 27 Nov 2022 12:44:40 -0800 +Subject: [PATCH 47/55] Fix frame sync related to unprotected multithread + access + +There is a few crashes happens in real life that frame callback is +double-free'd and hit an assertion in wayland-client. e.g. +https://bugs.kde.org/show_bug.cgi?id=450003 + +This is due to the WaylandEventThread and calls to QWaylandWindow::reset +may free and unset the mFrameCallback at the same time. mFrameSyncMutex +should be used to protect such access. + +Pick-to: 6.4 +Change-Id: Ie01d08d07a2f10f70606ed1935caac09cb4f0382 +(cherry picked from commit b6cbb5e323822d6e3ef5ed4dd5a4c4cc1ea85038) +--- + src/client/qwaylandwindow.cpp | 64 ++++++++++++++++++++--------------- + src/client/qwaylandwindow_p.h | 11 +++--- + 2 files changed, 43 insertions(+), 32 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index f322a8d6..6337db00 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -252,13 +252,16 @@ void QWaylandWindow::reset() + mSurface.reset(); + } + +- if (mFrameCallback) { +- wl_callback_destroy(mFrameCallback); +- mFrameCallback = nullptr; +- } ++ { ++ QMutexLocker lock(&mFrameSyncMutex); ++ if (mFrameCallback) { ++ wl_callback_destroy(mFrameCallback); ++ mFrameCallback = nullptr; ++ } + +- mFrameCallbackElapsedTimer.invalidate(); +- mWaitingForFrameCallback = false; ++ mFrameCallbackElapsedTimer.invalidate(); ++ mWaitingForFrameCallback = false; ++ } + mFrameCallbackTimedOut = false; + + mMask = QRegion(); +@@ -633,18 +636,21 @@ const wl_callback_listener QWaylandWindow::callbackListener = { + [](void *data, wl_callback *callback, uint32_t time) { + Q_UNUSED(time); + auto *window = static_cast(data); +- +- Q_ASSERT(callback == window->mFrameCallback); +- wl_callback_destroy(callback); +- window->mFrameCallback = nullptr; +- +- window->handleFrameCallback(); ++ window->handleFrameCallback(callback); + } + }; + +-void QWaylandWindow::handleFrameCallback() ++void QWaylandWindow::handleFrameCallback(wl_callback* callback) + { + QMutexLocker locker(&mFrameSyncMutex); ++ if (!mFrameCallback) { ++ // This means the callback is already unset by QWaylandWindow::reset. ++ // The wl_callback object will be destroyed there too. ++ return; ++ } ++ Q_ASSERT(callback == mFrameCallback); ++ wl_callback_destroy(callback); ++ mFrameCallback = nullptr; + + mWaitingForFrameCallback = false; + mFrameCallbackElapsedTimer.invalidate(); +@@ -1169,19 +1175,24 @@ void QWaylandWindow::timerEvent(QTimerEvent *event) + if (event->timerId() != mFrameCallbackCheckIntervalTimerId) + return; + +- bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout); +- if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) { +- killTimer(mFrameCallbackCheckIntervalTimerId); +- mFrameCallbackCheckIntervalTimerId = -1; +- } +- if (mFrameCallbackElapsedTimer.isValid() && callbackTimerExpired) { +- mFrameCallbackElapsedTimer.invalidate(); ++ { ++ QMutexLocker lock(&mFrameSyncMutex); + +- qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; +- mFrameCallbackTimedOut = true; +- mWaitingForUpdate = false; +- sendExposeEvent(QRect()); ++ bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout); ++ if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) { ++ killTimer(mFrameCallbackCheckIntervalTimerId); ++ mFrameCallbackCheckIntervalTimerId = -1; ++ } ++ if (!mFrameCallbackElapsedTimer.isValid() || !callbackTimerExpired) { ++ return; ++ } ++ mFrameCallbackElapsedTimer.invalidate(); + } ++ ++ qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed"; ++ mFrameCallbackTimedOut = true; ++ mWaitingForUpdate = false; ++ sendExposeEvent(QRect()); + } + + void QWaylandWindow::requestUpdate() +@@ -1224,15 +1235,14 @@ void QWaylandWindow::handleUpdate() + { + qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread(); + +- if (mWaitingForFrameCallback) +- return; +- + // TODO: Should sync subsurfaces avoid requesting frame callbacks? + QReadLocker lock(&mSurfaceLock); + if (!mSurface) + return; + + QMutexLocker locker(&mFrameSyncMutex); ++ if (mWaitingForFrameCallback) ++ return; + + struct ::wl_surface *wrappedSurface = reinterpret_cast(wl_proxy_create_wrapper(mSurface->object())); + wl_proxy_set_queue(reinterpret_cast(wrappedSurface), mDisplay->frameEventQueue()); +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index 487a91a6..2f219d8c 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -237,12 +237,13 @@ protected: + Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton; + + WId mWindowId; +- bool mWaitingForFrameCallback = false; + bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out +- QAtomicInt mWaitingForUpdateDelivery = false; + int mFrameCallbackCheckIntervalTimerId = -1; +- QElapsedTimer mFrameCallbackElapsedTimer; +- struct ::wl_callback *mFrameCallback = nullptr; ++ QAtomicInt mWaitingForUpdateDelivery = false; ++ ++ bool mWaitingForFrameCallback = false; // Protected by mFrameSyncMutex ++ QElapsedTimer mFrameCallbackElapsedTimer; // Protected by mFrameSyncMutex ++ struct ::wl_callback *mFrameCallback = nullptr; // Protected by mFrameSyncMutex + QMutex mFrameSyncMutex; + QWaitCondition mFrameSyncWait; + +@@ -297,7 +298,7 @@ private: + QRect mLastExposeGeometry; + + static const wl_callback_listener callbackListener; +- void handleFrameCallback(); ++ void handleFrameCallback(struct ::wl_callback* callback); + + static QWaylandWindow *mMouseGrab; + +-- +2.40.0 + diff --git a/SOURCES/0048-Client-Handle-zwp_primary_selection_device_manager_v.patch b/SOURCES/0048-Client-Handle-zwp_primary_selection_device_manager_v.patch new file mode 100644 index 0000000..727159b --- /dev/null +++ b/SOURCES/0048-Client-Handle-zwp_primary_selection_device_manager_v.patch @@ -0,0 +1,66 @@ +From 21e354e7b1878a243d29466b19084083df3d0db9 Mon Sep 17 00:00:00 2001 +From: Vlad Zahorodnii +Date: Tue, 27 Sep 2022 22:05:07 +0300 +Subject: [PATCH 48/55] Client: Handle zwp_primary_selection_device_manager_v1 + global removal + +The zwp_primary_selection_device_manager_v1 global can be withdrawn if +the compositor disables the primary selection, i.e. middle click to +paste selected text. QtWayland needs to handle that; otherwise the app +can crash. + +Pick-to: 6.5 +Change-Id: Idbb4db18b605f85a5951fa12c1bdf61898b0d123 +Reviewed-by: Eskil Abrahamsen Blomfeldt +(cherry picked from commit 45163234a4e4baad0012d3ee07501093d98ba91c) +--- + src/client/qwaylanddisplay.cpp | 9 +++++++++ + src/client/qwaylandprimaryselectionv1.cpp | 5 ----- + 2 files changed, 9 insertions(+), 5 deletions(-) + +diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp +index 6898a881..27f55965 100644 +--- a/src/client/qwaylanddisplay.cpp ++++ b/src/client/qwaylanddisplay.cpp +@@ -519,6 +519,8 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin + #if QT_CONFIG(wayland_client_primary_selection) + } else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) { + mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1)); ++ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices)) ++ inputDevice->setPrimarySelectionDevice(mPrimarySelectionManager->createDevice(inputDevice)); + #endif + } else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) { + mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1)); +@@ -577,6 +579,13 @@ void QWaylandDisplay::registry_global_remove(uint32_t id) + inputDevice->setTextInput(nullptr); + mWaylandIntegration->reconfigureInputContext(); + } ++#if QT_CONFIG(wayland_client_primary_selection) ++ if (global.interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) { ++ mPrimarySelectionManager.reset(); ++ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices)) ++ inputDevice->setPrimarySelectionDevice(nullptr); ++ } ++#endif + mGlobals.removeAt(i); + break; + } +diff --git a/src/client/qwaylandprimaryselectionv1.cpp b/src/client/qwaylandprimaryselectionv1.cpp +index 832f9678..ea508771 100644 +--- a/src/client/qwaylandprimaryselectionv1.cpp ++++ b/src/client/qwaylandprimaryselectionv1.cpp +@@ -54,11 +54,6 @@ QWaylandPrimarySelectionDeviceManagerV1::QWaylandPrimarySelectionDeviceManagerV1 + : zwp_primary_selection_device_manager_v1(display->wl_registry(), id, qMin(version, uint(1))) + , m_display(display) + { +- // Create devices for all seats. +- // This only works if we get the global before all devices +- const auto seats = m_display->inputDevices(); +- for (auto *seat : seats) +- seat->setPrimarySelectionDevice(createDevice(seat)); + } + + QWaylandPrimarySelectionDeviceV1 *QWaylandPrimarySelectionDeviceManagerV1::createDevice(QWaylandInputDevice *seat) +-- +2.40.0 + diff --git a/SOURCES/0049-Fixes-the-build-on-CentOS.patch b/SOURCES/0049-Fixes-the-build-on-CentOS.patch new file mode 100644 index 0000000..0d15bba --- /dev/null +++ b/SOURCES/0049-Fixes-the-build-on-CentOS.patch @@ -0,0 +1,26 @@ +From c12b0ed6db709e2af40ab3687d880aff5c56b5c1 Mon Sep 17 00:00:00 2001 +From: Aleix Pol +Date: Mon, 19 Dec 2022 15:31:03 +0100 +Subject: [PATCH 49/55] Fixes the build on CentOS + +Change-Id: I3c21972e7681be99b0f45c3ea3a57be285e4ff8e +--- + src/client/qwaylandshmbackingstore.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp +index 98acd42d..41cffdf7 100644 +--- a/src/client/qwaylandshmbackingstore.cpp ++++ b/src/client/qwaylandshmbackingstore.cpp +@@ -78,7 +78,7 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display, + int alloc = stride * size.height(); + int fd = -1; + +-#ifdef SYS_memfd_create ++#if defined(SYS_memfd_create) && defined(F_SEAL_SEAL) + fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING); + if (fd >= 0) + fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL); +-- +2.40.0 + diff --git a/SOURCES/0050-client-Avoid-protocol-error-with-invalid-min-max-siz.patch b/SOURCES/0050-client-Avoid-protocol-error-with-invalid-min-max-siz.patch new file mode 100644 index 0000000..de04d80 --- /dev/null +++ b/SOURCES/0050-client-Avoid-protocol-error-with-invalid-min-max-siz.patch @@ -0,0 +1,56 @@ +From d04d4c7fe2e00285e7d70da42094f213e13c6ed8 Mon Sep 17 00:00:00 2001 +From: Eskil Abrahamsen Blomfeldt +Date: Mon, 23 May 2022 09:47:24 +0200 +Subject: [PATCH 50/55] client: Avoid protocol error with invalid min/max size + +If the application sets an invalid minimum and maximum size +(where the minimum is higher than the maximum), then we +would blindly send this over the protocol, which is a protocol +error according to the spec. Qt compositors will warn about +this and ignore the size, but mainly because "but there's no +matching error defined" according to the comment. Other +compositors may close the connection when this happens. + +To avoid crashing the app based on bogus min/max size, we +make sure we never send a maximum size which is less than +the minimum size. This corresponds to the behavior of +compositors which accept the size without raising an error: +the minimum size takes precedence. + +Note that 0 means "no maximum size" in the protocol, so we +cap the value before applying this logic. + +[ChangeLog][Client] Fixed an issue where setting an invalid +minimum and maximum size on a window would cause some +compositors to raise a protocol error. + +Pick-to: 6.2 6.3 +Fixes: QTBUG-102626 +Fixes: QTBUG-103391 +Change-Id: I4004a4550a9fe3dae6a27169b4d1a9a616e21841 +Reviewed-by: David Edmundson +(cherry picked from commit 487de47277ccc31891f6340ce4c971c91336d9a4) +--- + src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +index ead99989..ad666129 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +@@ -384,10 +384,10 @@ void QWaylandXdgSurface::setSizeHints() + const int minHeight = qMax(0, m_window->windowMinimumSize().height()); + m_toplevel->set_min_size(minWidth, minHeight); + +- int maxWidth = qMax(0, m_window->windowMaximumSize().width()); ++ int maxWidth = qMax(minWidth, m_window->windowMaximumSize().width()); + if (maxWidth == QWINDOWSIZE_MAX) + maxWidth = 0; +- int maxHeight = qMax(0, m_window->windowMaximumSize().height()); ++ int maxHeight = qMax(minHeight, m_window->windowMaximumSize().height()); + if (maxHeight == QWINDOWSIZE_MAX) + maxHeight = 0; + m_toplevel->set_max_size(maxWidth, maxHeight); +-- +2.40.0 + diff --git a/SOURCES/0051-Client-Fix-handling-of-Qt-BlankCursor.patch b/SOURCES/0051-Client-Fix-handling-of-Qt-BlankCursor.patch new file mode 100644 index 0000000..de310e5 --- /dev/null +++ b/SOURCES/0051-Client-Fix-handling-of-Qt-BlankCursor.patch @@ -0,0 +1,38 @@ +From fbb9d65cf158bcf63440e9839acae9238ad4e0a7 Mon Sep 17 00:00:00 2001 +From: Vlad Zahorodnii +Date: Tue, 22 Nov 2022 23:27:34 +0200 +Subject: [PATCH 51/55] Client: Fix handling of Qt::BlankCursor + +The cursor may not be properly set when a window has Qt::BlankCursor and +it's shown. In that case, the cursor surface may not be present and +wl_pointer.set_cursor won't be called. + +On the other hand, wl_pointer.set_cursor must be always called when +wl_pointer.enter is received. + +Pick-to: 6.5 +Change-Id: I8540e7a02df1579b3380a1a1d4cfab42c1ab3104 +Reviewed-by: David Edmundson +Reviewed-by: Qt CI Bot +(cherry picked from commit e954853f0e68d78ac1a98bc3533713881496064c) +--- + src/client/qwaylandinputdevice.cpp | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp +index 5b880984..9a0fe49d 100644 +--- a/src/client/qwaylandinputdevice.cpp ++++ b/src/client/qwaylandinputdevice.cpp +@@ -310,8 +310,7 @@ void QWaylandInputDevice::Pointer::updateCursor() + auto shape = seat()->mCursor.shape; + + if (shape == Qt::BlankCursor) { +- if (mCursor.surface) +- mCursor.surface->hide(); ++ getOrCreateCursorSurface()->hide(); + return; + } + +-- +2.40.0 + diff --git a/SOURCES/0052-client-Force-a-roundtrip-when-an-XdgOutput-is-not-re.patch b/SOURCES/0052-client-Force-a-roundtrip-when-an-XdgOutput-is-not-re.patch new file mode 100644 index 0000000..28e517b --- /dev/null +++ b/SOURCES/0052-client-Force-a-roundtrip-when-an-XdgOutput-is-not-re.patch @@ -0,0 +1,117 @@ +From 6897f62f603df049135347cf402a7a4037092263 Mon Sep 17 00:00:00 2001 +From: Marco Martin +Date: Fri, 24 Feb 2023 17:40:48 +0100 +Subject: [PATCH 52/55] client: Force a roundtrip when an XdgOutput is not + ready yet + +Is possible that the server sends a surface_enter before +all the information of the XdgOutput have been processed by the client. +in this case the associated QScreen doesn't exist yet, causing a +QWindow::SetScreen(nullptr), which will fall back to +QGuiApplication::primaryScreen(), having the QWindow being assigned the +wrong screen + +Change-Id: I923d5d3a35484deafa6f0572f79c16c27b1f87f0 +Reviewed-by: David Edmundson +--- + src/client/qwaylandwindow.cpp | 2 ++ + tests/auto/client/shared/coreprotocol.cpp | 2 ++ + tests/auto/client/shared/coreprotocol.h | 3 ++ + tests/auto/client/xdgoutput/tst_xdgoutput.cpp | 35 +++++++++++++++++++ + 4 files changed, 42 insertions(+) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index 6337db00..3b700002 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -1042,6 +1042,8 @@ void QWaylandWindow::handleScreensChanged() + if (newScreen == mLastReportedScreen) + return; + ++ if (!newScreen->isPlaceholder() && !newScreen->QPlatformScreen::screen()) ++ mDisplay->forceRoundTrip(); + QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen()); + mLastReportedScreen = newScreen; + if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup +diff --git a/tests/auto/client/shared/coreprotocol.cpp b/tests/auto/client/shared/coreprotocol.cpp +index d1a2e7cb..53e12291 100644 +--- a/tests/auto/client/shared/coreprotocol.cpp ++++ b/tests/auto/client/shared/coreprotocol.cpp +@@ -185,6 +185,8 @@ void Output::output_bind_resource(QtWaylandServer::wl_output::Resource *resource + + if (m_version >= WL_OUTPUT_DONE_SINCE_VERSION) + wl_output::send_done(resource->handle); ++ ++ Q_EMIT outputBound(resource); + } + + // Seat stuff +diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h +index 210d8ddb..00c439e1 100644 +--- a/tests/auto/client/shared/coreprotocol.h ++++ b/tests/auto/client/shared/coreprotocol.h +@@ -273,6 +273,9 @@ public: + OutputData m_data; + int m_version = 1; // TODO: remove on libwayland upgrade + ++Q_SIGNALS: ++ void outputBound(Resource *resource); ++ + protected: + void output_bind_resource(Resource *resource) override; + }; +diff --git a/tests/auto/client/xdgoutput/tst_xdgoutput.cpp b/tests/auto/client/xdgoutput/tst_xdgoutput.cpp +index 80429608..68e8d77a 100644 +--- a/tests/auto/client/xdgoutput/tst_xdgoutput.cpp ++++ b/tests/auto/client/xdgoutput/tst_xdgoutput.cpp +@@ -55,6 +55,7 @@ private slots: + void primaryScreen(); + void overrideGeometry(); + void changeGeometry(); ++ void outputCreateEnterRace(); + }; + + void tst_xdgoutput::cleanup() +@@ -134,5 +135,39 @@ void tst_xdgoutput::changeGeometry() + exec([=] { remove(output(1)); }); + } + ++void tst_xdgoutput::outputCreateEnterRace() ++{ ++ m_config.autoConfigure = true; ++ m_config.autoEnter = false; ++ QRasterWindow window; ++ QSignalSpy screenChanged(&window, &QWindow::screenChanged); ++ window.resize(400, 320); ++ window.show(); ++ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); ++ exec([=] { xdgToplevel()->surface()->sendEnter(output(0));}); ++ ++ QTRY_COMPARE(QGuiApplication::screens().size(), 1); ++ QScreen *primaryScreen = QGuiApplication::screens().first(); ++ QCOMPARE(window.screen(), primaryScreen); ++ ++ auto *out = exec([=] { ++ return add(); ++ }); ++ ++ // In Compositor Thread ++ connect(out, &Output::outputBound, this, [this](QtWaylandServer::wl_output::Resource *resource){ ++ auto surface = xdgToplevel()->surface(); ++ surface->sendLeave(output(0)); ++ surface->QtWaylandServer::wl_surface::send_enter(surface->resource()->handle, resource->handle); ++ }, Qt::DirectConnection); ++ ++ QTRY_COMPARE(QGuiApplication::screens().size(), 2); ++ QTRY_COMPARE(window.screen(), QGuiApplication::screens()[1]); ++ ++ exec([=] { remove(out); }); ++ m_config.autoConfigure = false; ++ m_config.autoEnter = true; ++} ++ + QCOMPOSITOR_TEST_MAIN(tst_xdgoutput) + #include "tst_xdgoutput.moc" +-- +2.40.0 + diff --git a/SOURCES/0053-Client-Manage-QMimeData-lifecycle.patch b/SOURCES/0053-Client-Manage-QMimeData-lifecycle.patch new file mode 100644 index 0000000..4941d36 --- /dev/null +++ b/SOURCES/0053-Client-Manage-QMimeData-lifecycle.patch @@ -0,0 +1,138 @@ +From 31eab11d3b6697a76cc7802d02e525c1152a42c2 Mon Sep 17 00:00:00 2001 +From: Tang Haixiang +Date: Thu, 22 Dec 2022 15:19:53 +0800 +Subject: [PATCH 53/55] Client: Manage QMimeData lifecycle + +QMimeData is created by user, it is not taken care of in qtwayland, +which will cause memory leak. + +It is now handled in qtwayland that when a new QMimeData is set, +the previous QMimeData is freed. + +Change-Id: Ic502021fe700c7ee10454d94f0d1868901809af7 +Reviewed-by: David Edmundson +Reviewed-by: Qt CI Bot +(cherry picked from commit 3af40c6c42703a65656fdd3322183abb2905e44d) +--- + src/client/qwaylandclipboard.cpp | 27 +++++++++++++++++++++------ + src/client/qwaylandclipboard_p.h | 1 + + src/client/qwaylanddatasource.cpp | 5 ----- + src/client/qwaylanddatasource_p.h | 2 -- + 4 files changed, 22 insertions(+), 13 deletions(-) + +diff --git a/src/client/qwaylandclipboard.cpp b/src/client/qwaylandclipboard.cpp +index 81f48e05..14561c77 100644 +--- a/src/client/qwaylandclipboard.cpp ++++ b/src/client/qwaylandclipboard.cpp +@@ -54,10 +54,15 @@ namespace QtWaylandClient { + QWaylandClipboard::QWaylandClipboard(QWaylandDisplay *display) + : mDisplay(display) + { ++ m_clientClipboard[QClipboard::Clipboard] = nullptr; ++ m_clientClipboard[QClipboard::Selection] = nullptr; + } + + QWaylandClipboard::~QWaylandClipboard() + { ++ if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection]) ++ delete m_clientClipboard[QClipboard::Clipboard]; ++ delete m_clientClipboard[QClipboard::Selection]; + } + + QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) +@@ -69,8 +74,8 @@ QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) + switch (mode) { + case QClipboard::Clipboard: + if (auto *dataDevice = seat->dataDevice()) { +- if (auto *source = dataDevice->selectionSource()) +- return source->mimeData(); ++ if (dataDevice->selectionSource()) ++ return m_clientClipboard[QClipboard::Clipboard]; + if (auto *offer = dataDevice->selectionOffer()) + return offer->mimeData(); + } +@@ -78,8 +83,8 @@ QMimeData *QWaylandClipboard::mimeData(QClipboard::Mode mode) + case QClipboard::Selection: + #if QT_CONFIG(wayland_client_primary_selection) + if (auto *selectionDevice = seat->primarySelectionDevice()) { +- if (auto *source = selectionDevice->selectionSource()) +- return source->mimeData(); ++ if (selectionDevice->selectionSource()) ++ return m_clientClipboard[QClipboard::Selection]; + if (auto *offer = selectionDevice->selectionOffer()) + return offer->mimeData(); + } +@@ -104,17 +109,27 @@ void QWaylandClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) + if (data && data->hasFormat(plain) && !data->hasFormat(utf8)) + data->setData(utf8, data->data(plain)); + ++ if (m_clientClipboard[mode]) { ++ if (m_clientClipboard[QClipboard::Clipboard] != m_clientClipboard[QClipboard::Selection]) ++ delete m_clientClipboard[mode]; ++ m_clientClipboard[mode] = nullptr; ++ } ++ ++ m_clientClipboard[mode] = data; ++ + switch (mode) { + case QClipboard::Clipboard: + if (auto *dataDevice = seat->dataDevice()) { +- dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), data) : nullptr); ++ dataDevice->setSelectionSource(data ? new QWaylandDataSource(mDisplay->dndSelectionHandler(), ++ m_clientClipboard[QClipboard::Clipboard]) : nullptr); + emitChanged(mode); + } + break; + case QClipboard::Selection: + #if QT_CONFIG(wayland_client_primary_selection) + if (auto *selectionDevice = seat->primarySelectionDevice()) { +- selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), data) : nullptr); ++ selectionDevice->setSelectionSource(data ? new QWaylandPrimarySelectionSourceV1(mDisplay->primarySelectionManager(), ++ m_clientClipboard[QClipboard::Selection]) : nullptr); + emitChanged(mode); + } + #endif +diff --git a/src/client/qwaylandclipboard_p.h b/src/client/qwaylandclipboard_p.h +index ce14e124..bb52683d 100644 +--- a/src/client/qwaylandclipboard_p.h ++++ b/src/client/qwaylandclipboard_p.h +@@ -80,6 +80,7 @@ public: + private: + QWaylandDisplay *mDisplay = nullptr; + QMimeData m_emptyData; ++ QMimeData *m_clientClipboard[2]; + }; + + } +diff --git a/src/client/qwaylanddatasource.cpp b/src/client/qwaylanddatasource.cpp +index 5599cbd4..e085152c 100644 +--- a/src/client/qwaylanddatasource.cpp ++++ b/src/client/qwaylanddatasource.cpp +@@ -71,11 +71,6 @@ QWaylandDataSource::~QWaylandDataSource() + destroy(); + } + +-QMimeData * QWaylandDataSource::mimeData() const +-{ +- return m_mime_data; +-} +- + void QWaylandDataSource::data_source_cancelled() + { + Q_EMIT cancelled(); +diff --git a/src/client/qwaylanddatasource_p.h b/src/client/qwaylanddatasource_p.h +index 96f07bc3..14d1542d 100644 +--- a/src/client/qwaylanddatasource_p.h ++++ b/src/client/qwaylanddatasource_p.h +@@ -74,8 +74,6 @@ public: + QWaylandDataSource(QWaylandDataDeviceManager *dataDeviceManager, QMimeData *mimeData); + ~QWaylandDataSource() override; + +- QMimeData *mimeData() const; +- + Q_SIGNALS: + void cancelled(); + void finished(); +-- +2.40.0 + diff --git a/SOURCES/0054-client-Do-not-cast-placeholder-screens-to-QWaylandSc.patch b/SOURCES/0054-client-Do-not-cast-placeholder-screens-to-QWaylandSc.patch new file mode 100644 index 0000000..2d7ba8a --- /dev/null +++ b/SOURCES/0054-client-Do-not-cast-placeholder-screens-to-QWaylandSc.patch @@ -0,0 +1,34 @@ +From 18c2bcec31f68aafd66d7ec503ec6511ca804d07 Mon Sep 17 00:00:00 2001 +From: Aleix Pol +Date: Mon, 6 Mar 2023 01:11:45 +0100 +Subject: [PATCH 54/55] client: Do not cast placeholder screens to + QWaylandScreen + +It's wrong to C-cast an object to a class that isn't theirs. Check if it +is a placeholder first. + +Pick-to: 5.15 6.2 6.5 +Change-Id: I45d3c423422ae6638a033fb0f4cfefc7cd4460f0 +Reviewed-by: Eskil Abrahamsen Blomfeldt +Reviewed-by: David Edmundson +(cherry picked from commit a53f022393a1276dbf8eccbae04cb0bd6cea0160) +--- + src/client/qwaylandnativeinterface.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/client/qwaylandnativeinterface.cpp b/src/client/qwaylandnativeinterface.cpp +index bf54a1a0..9763c312 100644 +--- a/src/client/qwaylandnativeinterface.cpp ++++ b/src/client/qwaylandnativeinterface.cpp +@@ -139,7 +139,7 @@ void *QWaylandNativeInterface::nativeResourceForScreen(const QByteArray &resourc + { + QByteArray lowerCaseResource = resourceString.toLower(); + +- if (lowerCaseResource == "output") ++ if (lowerCaseResource == "output" && !screen->handle()->isPlaceholder()) + return ((QWaylandScreen *) screen->handle())->output(); + + return nullptr; +-- +2.40.0 + diff --git a/SOURCES/0055-Client-Remove-flip-popup-constraints.patch b/SOURCES/0055-Client-Remove-flip-popup-constraints.patch new file mode 100644 index 0000000..a51fad7 --- /dev/null +++ b/SOURCES/0055-Client-Remove-flip-popup-constraints.patch @@ -0,0 +1,41 @@ +From c4c3fc69250c01cb35aaae5ea1ea2bcc8236dff0 Mon Sep 17 00:00:00 2001 +From: Vlad Zahorodnii +Date: Thu, 12 Jan 2023 14:49:25 +0200 +Subject: [PATCH 55/55] Client: Remove flip popup constraints + +xdg_positioner doesn't have good anchor rect and other needed +information so the compositor can properly flip popups. In some windows +I see that some popups are flipped in such a way that the popups look +"detached" from the parent window. + +With the information that QtWayland provides so far only slide +constraint adjustments can produce somewhat expected results. Although +there will be still some issues near screen edges. + +Pick-to: 6.5 6.4 6.2 5.15 +Task-number: QTBUG-87303 +Change-Id: I4021f497b78e62651fe606c4be21a387a92edd6c +Reviewed-by: Liang Qi +(cherry picked from commit d7a5dab0182cba19d7f59e542672aa3d1b2e859e) +--- + src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +index ad666129..822b385c 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +@@ -425,9 +425,7 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent) + positioner->set_gravity(QtWayland::xdg_positioner::gravity_bottom_right); + positioner->set_size(m_window->geometry().width(), m_window->geometry().height()); + positioner->set_constraint_adjustment(QtWayland::xdg_positioner::constraint_adjustment_slide_x +- | QtWayland::xdg_positioner::constraint_adjustment_slide_y +- | QtWayland::xdg_positioner::constraint_adjustment_flip_x +- | QtWayland::xdg_positioner::constraint_adjustment_flip_y); ++ | QtWayland::xdg_positioner::constraint_adjustment_slide_y); + m_popup = new Popup(this, parent, positioner); + positioner->destroy(); + +-- +2.40.0 + diff --git a/SOURCES/qtwayland-client-expose-toplevel-window-state.patch b/SOURCES/qtwayland-client-expose-toplevel-window-state.patch new file mode 100644 index 0000000..7823473 --- /dev/null +++ b/SOURCES/qtwayland-client-expose-toplevel-window-state.patch @@ -0,0 +1,166 @@ +From d533901938a996367d7b6f87b0214f5a17098aed Mon Sep 17 00:00:00 2001 +From: Jan Grulich +Date: Tue, 23 Mar 2021 16:03:22 +0100 +Subject: [PATCH] Client: expose toplevel window state + +QWaylandWindow has only basic information about window state, like if +it's active or maximized, but it has no information about tiling, which +can be useful for client-side decorations. We also need to bump version +of xdg-shell protocol we support, because additional states are not in +the version currently supported by QtWayland. It shouldn't be a problem +to increase the version as the new version adds just these additional +window states. + +Change-Id: I4c46516d9c7296c69ea51a022b3bdb4ca06bef8d +Reviewed-by: David Edmundson +--- + src/client/qwaylandwindow.cpp | 15 +++++++++++++++ + src/client/qwaylandwindow_p.h | 16 ++++++++++++++++ + .../xdg-shell/qwaylandxdgshell.cpp | 16 +++++++++++++++- + .../xdg-shell/qwaylandxdgshell_p.h | 3 ++- + 4 files changed, 48 insertions(+), 2 deletions(-) + +diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp +index ba881cb..a1e891d 100644 +--- a/src/client/qwaylandwindow.cpp ++++ b/src/client/qwaylandwindow.cpp +@@ -1089,6 +1089,16 @@ Qt::WindowStates QWaylandWindow::windowStates() const + return mLastReportedWindowStates; + } + ++QWaylandWindow::ToplevelWindowTilingStates QWaylandWindow::toplevelWindowTilingStates() const ++{ ++ return mLastReportedToplevelWindowTilingStates; ++} ++ ++void QWaylandWindow::handleToplevelWindowTilingStatesChanged(ToplevelWindowTilingStates states) ++{ ++ mLastReportedToplevelWindowTilingStates = states; ++} ++ + void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states) + { + createDecoration(); +diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h +index e068796..f4e5d3d 100644 +--- a/src/client/qwaylandwindow_p.h ++++ b/src/client/qwaylandwindow_p.h +@@ -95,6 +95,15 @@ public: + Vulkan + }; + ++ enum ToplevelWindowTilingState { ++ WindowNoState = 0, ++ WindowTiledLeft = 1, ++ WindowTiledRight = 2, ++ WindowTiledTop = 4, ++ WindowTiledBottom = 8 ++ }; ++ Q_DECLARE_FLAGS(ToplevelWindowTilingStates, ToplevelWindowTilingState) ++ + QWaylandWindow(QWindow *window, QWaylandDisplay *display); + ~QWaylandWindow() override; + +@@ -145,6 +154,9 @@ public: + void handleContentOrientationChange(Qt::ScreenOrientation orientation) override; + void setOrientationMask(Qt::ScreenOrientations mask); + ++ ToplevelWindowTilingStates toplevelWindowTilingStates() const; ++ void handleToplevelWindowTilingStatesChanged(ToplevelWindowTilingStates states); ++ + void setWindowState(Qt::WindowStates states) override; + void setWindowFlags(Qt::WindowFlags flags) override; + void handleWindowStatesChanged(Qt::WindowStates states); +@@ -257,6 +269,7 @@ protected: + QRegion mMask; + QRegion mOpaqueArea; + Qt::WindowStates mLastReportedWindowStates = Qt::WindowNoState; ++ ToplevelWindowTilingStates mLastReportedToplevelWindowTilingStates = WindowNoState; + + QWaylandShmBackingStore *mBackingStore = nullptr; + QWaylandBuffer *mQueuedBuffer = nullptr; +@@ -293,6 +306,8 @@ private: + friend class QWaylandSubSurface; + }; + ++Q_DECLARE_OPERATORS_FOR_FLAGS(QWaylandWindow::ToplevelWindowTilingStates) ++ + inline QIcon QWaylandWindow::windowIcon() const + { + return mWindowIcon; +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +index d7d0ddf..2c6e84b 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +@@ -88,6 +88,7 @@ void QWaylandXdgSurface::Toplevel::applyConfigure() + && !m_xdgSurface->m_window->display()->isKeyboardAvailable()) + m_xdgSurface->m_window->display()->handleWindowDeactivated(m_xdgSurface->m_window); + ++ m_xdgSurface->m_window->handleToplevelWindowTilingStatesChanged(m_toplevelStates); + m_xdgSurface->m_window->handleWindowStatesChanged(m_pending.states); + + if (m_pending.size.isEmpty()) { +@@ -120,6 +121,7 @@ void QWaylandXdgSurface::Toplevel::xdg_toplevel_configure(int32_t width, int32_t + size_t numStates = states->size / sizeof(uint32_t); + + m_pending.states = Qt::WindowNoState; ++ m_toplevelStates = QWaylandWindow::WindowNoState; + + for (size_t i = 0; i < numStates; i++) { + switch (xdgStates[i]) { +@@ -132,6 +134,18 @@ void QWaylandXdgSurface::Toplevel::xdg_toplevel_configure(int32_t width, int32_t + case XDG_TOPLEVEL_STATE_FULLSCREEN: + m_pending.states |= Qt::WindowFullScreen; + break; ++ case XDG_TOPLEVEL_STATE_TILED_LEFT: ++ m_toplevelStates |= QWaylandWindow::WindowTiledLeft; ++ break; ++ case XDG_TOPLEVEL_STATE_TILED_RIGHT: ++ m_toplevelStates |= QWaylandWindow::WindowTiledRight; ++ break; ++ case XDG_TOPLEVEL_STATE_TILED_TOP: ++ m_toplevelStates |= QWaylandWindow::WindowTiledTop; ++ break; ++ case XDG_TOPLEVEL_STATE_TILED_BOTTOM: ++ m_toplevelStates |= QWaylandWindow::WindowTiledBottom; ++ break; + default: + break; + } +@@ -451,7 +465,7 @@ void QWaylandXdgSurface::xdg_surface_configure(uint32_t serial) + } + + QWaylandXdgShell::QWaylandXdgShell(QWaylandDisplay *display, uint32_t id, uint32_t availableVersion) +- : QtWayland::xdg_wm_base(display->wl_registry(), id, qMin(availableVersion, 1u)) ++ : QtWayland::xdg_wm_base(display->wl_registry(), id, qMin(availableVersion, 2u)) + , m_display(display) + { + display->addRegistryListener(&QWaylandXdgShell::handleRegistryGlobal, this); +diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +index 0c98be3..d791213 100644 +--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h ++++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +@@ -58,6 +58,7 @@ + + #include + #include ++#include + + #include + #include +@@ -69,7 +70,6 @@ class QWindow; + namespace QtWaylandClient { + + class QWaylandDisplay; +-class QWaylandWindow; + class QWaylandInputDevice; + class QWaylandXdgShell; + +@@ -123,6 +123,7 @@ private: + QSize size = {0, 0}; + Qt::WindowStates states = Qt::WindowNoState; + } m_pending, m_applied; ++ QWaylandWindow::ToplevelWindowTilingStates m_toplevelStates = QWaylandWindow::WindowNoState; + QSize m_normalSize; + + QWaylandXdgSurface *m_xdgSurface = nullptr; diff --git a/SPECS/qt5-qtwayland.spec b/SPECS/qt5-qtwayland.spec new file mode 100644 index 0000000..3cb1d25 --- /dev/null +++ b/SPECS/qt5-qtwayland.spec @@ -0,0 +1,556 @@ +%global qt_module qtwayland + +%global build_tests 1 + +Summary: Qt5 - Wayland platform support and QtCompositor module +Name: qt5-%{qt_module} +Version: 5.15.9 +Release: 1%{?dist} + +License: LGPLv3 +Url: http://www.qt.io +%global majmin %(echo %{version} | cut -d. -f1-2) +Source0: https://download.qt.io/official_releases/qt/%{majmin}/%{version}/submodules/%{qt_module}-everywhere-opensource-src-%{version}.tar.xz + +## Upstream patches +## repo: https://invent.kde.org/qt/qt/qtwayland +## branch: kde/5.15 +## git format-patch v5.15.8-lts-lgpl +Patch1: 0001-Client-Announce-an-output-after-receiving-more-compl.patch +Patch2: 0002-Fix-issue-with-repeated-window-size-changes.patch +Patch3: 0003-Client-Connect-drags-being-accepted-to-updating-the-.patch +Patch4: 0004-Client-Disconnect-registry-listener-on-destruction.patch +Patch5: 0005-Client-Set-XdgShell-size-hints-before-the-first-comm.patch +Patch6: 0006-Fix-build.patch +Patch7: 0007-Fix-remove-listener.patch +Patch8: 0008-Hook-up-queryKeyboardModifers.patch +Patch9: 0009-Correctly-detect-if-image-format-is-supported-by-QIm.patch +Patch10: 0010-Client-Don-t-always-recreate-frame-callbacks.patch +Patch11: 0011-Client-Always-destroy-frame-callback-in-the-actual-c.patch +Patch12: 0012-Wayland-client-use-wl_keyboard-to-determine-active-s.patch +Patch13: 0013-Client-do-not-empty-clipboard-when-a-new-popup-windo.patch +Patch14: 0014-Client-Implement-DataDeviceV3.patch +Patch15: 0015-Client-Delay-deletion-of-QDrag-object-until-after-we.patch +Patch16: 0016-Client-Avoid-processing-of-events-when-showing-windo.patch +Patch17: 0017-Handle-registry_global-out-of-constructor.patch +Patch18: 0018-Connect-flushRequest-after-forceRoundTrip.patch +Patch19: 0019-Move-the-wayland-socket-polling-to-a-separate-event-.patch +Patch20: 0020-Client-Remove-mWaitingForUpdateDelivery.patch +Patch21: 0021-client-Simplify-round-trip-behavior.patch +Patch22: 0022-Client-Fix-opaque-region-setter.patch +Patch23: 0023-Use-proper-dependencies-in-compile-tests.patch +Patch24: 0024-Revert-Client-Remove-mWaitingForUpdateDelivery.patch +Patch25: 0025-Fix-race-condition-on-mWaitingForUpdateDelivery.patch +Patch26: 0026-use-poll-2-when-reading-from-clipboard.patch +Patch27: 0027-Reduce-memory-leakage.patch +Patch28: 0028-Fix-build-with-libcxx-missing-array-include.patch +Patch29: 0029-Only-close-popup-in-the-the-hierchary.patch +Patch30: 0030-Build-fixes-for-GCC-11.patch +Patch31: 0031-Check-pointer-for-null-before-use-in-ASSERT.patch +Patch32: 0032-Use-wl_surface.damage_buffer-on-the-client-side.patch +Patch33: 0033-Client-clear-focus-on-touch-cancel.patch +Patch34: 0034-Guard-mResizeDirty-by-the-correctMutex.patch +Patch35: 0035-client-Synthesize-enter-leave-event-for-popup-in-xdg.patch +Patch36: 0036-Fix-compile-tests.patch +Patch37: 0037-Use-CRLF-line-delimiter-for-text-uri-list-data.patch +Patch38: 0038-Avoid-calling-requestUpdate-from-wrong-thread.patch +Patch39: 0039-Call-finishDrag-in-QWaylandDataDevice-dragSourceCanc.patch +Patch40: 0040-Hold-surface-read-lock-throughout-QWaylandEglWindow-.patch +Patch41: 0041-Client-Ensure-that-wl_surface-lives-as-long-as-qtqui.patch +Patch42: 0042-Keep-toplevel-windows-in-the-top-left-corner-of-the-.patch +Patch43: 0043-Revert-Client-Ensure-that-wl_surface-lives-as-long-a.patch +Patch44: 0044-Client-Add-F_SEAL_SHRINK-seal-to-shm-backing-file.patch +Patch45: 0045-Client-Call-wl_output_release-upon-QWaylandScreen-de.patch +Patch46: 0046-Client-Bump-wl_output-version.patch +Patch47: 0047-Fix-frame-sync-related-to-unprotected-multithread-ac.patch +Patch48: 0048-Client-Handle-zwp_primary_selection_device_manager_v.patch +Patch49: 0049-Fixes-the-build-on-CentOS.patch +Patch50: 0050-client-Avoid-protocol-error-with-invalid-min-max-siz.patch +Patch51: 0051-Client-Fix-handling-of-Qt-BlankCursor.patch +Patch52: 0052-client-Force-a-roundtrip-when-an-XdgOutput-is-not-re.patch +Patch53: 0053-Client-Manage-QMimeData-lifecycle.patch +Patch54: 0054-client-Do-not-cast-placeholder-screens-to-QWaylandSc.patch +Patch55: 0055-Client-Remove-flip-popup-constraints.patch + +# Disable for now, there is a Qt bug making this broken +# Patch102: qtwayland-decoration-support-backports-from-qt6.patch +Patch103: qtwayland-client-expose-toplevel-window-state.patch + +# filter qml provides +%global __provides_exclude_from ^%{_qt5_archdatadir}/qml/.*\\.so$ + +BuildRequires: make +BuildRequires: qt5-qtbase-devel >= %{version} +BuildRequires: qt5-qtbase-static +BuildRequires: qt5-qtbase-private-devel +%{?_qt5:Requires: %{_qt5}%{?_isa} = %{_qt5_version}} +BuildRequires: qt5-qtdeclarative-devel + +BuildRequires: pkgconfig(xkbcommon) +BuildRequires: pkgconfig(wayland-scanner) +BuildRequires: pkgconfig(wayland-server) +BuildRequires: pkgconfig(wayland-client) +BuildRequires: pkgconfig(wayland-cursor) +BuildRequires: pkgconfig(wayland-egl) +BuildRequires: pkgconfig(egl) +BuildRequires: pkgconfig(gl) +BuildRequires: pkgconfig(xcomposite) +BuildRequires: pkgconfig(xrender) +BuildRequires: pkgconfig(libudev) +BuildRequires: pkgconfig(libinput) + +BuildRequires: libXext-devel + +%description +%{summary}. + +%package devel +Summary: Development files for %{name} +Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: qt5-qtbase-devel%{?_isa} +%description devel +%{summary}. + +%package examples +Summary: Programming examples for %{name} +Requires: %{name}%{?_isa} = %{version}-%{release} +%description examples +%{summary}. + +%if 0%{?build_tests} +%package tests +Summary: Unit tests for %{name} +Requires: %{name}%{?_isa} = %{version}-%{release} + +%description tests +%{summary}. +%endif + +%prep +%autosetup -n %{qt_module}-everywhere-src-%{version} -p1 + + +%build +%{qmake_qt5} + +%make_build + +%if 0%{?build_tests} +%qt5_build_tests +%endif + +%install +make install INSTALL_ROOT=%{buildroot} + +%if 0%{?build_tests} +%qt5_install_tests +%endif + +## .prl/.la file love +# nuke .prl reference(s) to %%buildroot, excessive (.la-like) libs +pushd %{buildroot}%{_qt5_libdir} +for prl_file in libQt5*.prl ; do + sed -i -e "/^QMAKE_PRL_BUILD_DIR/d" ${prl_file} + if [ -f "$(basename ${prl_file} .prl).so" ]; then + rm -fv "$(basename ${prl_file} .prl).la" + sed -i -e "/^QMAKE_PRL_LIBS/d" ${prl_file} + fi +done +popd + + +%ldconfig_scriptlets + +%files +%doc README +%license LICENSE.* +%{_qt5_libdir}/libQt5WaylandCompositor.so.5* +%{_qt5_libdir}/libQt5WaylandClient.so.5* +%{_qt5_plugindir}/wayland-decoration-client/ +%{_qt5_plugindir}/wayland-graphics-integration-server +%{_qt5_plugindir}/wayland-graphics-integration-client +%{_qt5_plugindir}/wayland-shell-integration +%{_qt5_plugindir}/platforms/libqwayland-egl.so +%{_qt5_plugindir}/platforms/libqwayland-generic.so +%{_qt5_plugindir}/platforms/libqwayland-xcomposite-egl.so +%{_qt5_plugindir}/platforms/libqwayland-xcomposite-glx.so +%{_qt5_qmldir}/QtWayland/ + +%files devel +%{_qt5_bindir}/qtwaylandscanner +%{_qt5_headerdir}/QtWaylandCompositor/ +%{_qt5_headerdir}/QtWaylandClient/ +%{_qt5_libdir}/libQt5WaylandCompositor.so +%{_qt5_libdir}/libQt5WaylandClient.so +%{_qt5_libdir}/libQt5WaylandCompositor.prl +%{_qt5_libdir}/libQt5WaylandClient.prl +%{_qt5_libdir}/cmake/Qt5WaylandCompositor/Qt5WaylandCompositorConfig*.cmake +%{_qt5_libdir}/pkgconfig/*.pc +%{_qt5_archdatadir}/mkspecs/modules/*.pri +%{_qt5_libdir}/cmake/Qt5WaylandCompositor/ +%{_qt5_libdir}/cmake/Qt5Gui/Qt5Gui_*.cmake +%{_qt5_libdir}/cmake/Qt5WaylandClient/ + +%files examples +%{_qt5_examplesdir}/wayland/ + +%if 0%{?build_tests} +%files tests +%{_qt5_libdir}/qt5/tests +%endif + +%changelog +* Fri Sep 22 2023 MSVSphere Packaging Team - 5.15.9-1 +- Rebuilt for MSVSphere 9.3 beta + +* Tue Apr 18 2023 Jan Grulich - 5.15.9-1 +- 5.15.9 + Resolves: bz#2175746 + +* Mon Mar 28 2022 Jan Grulich - 5.15.3-1 +- 5.15.3 + Resolves: bz#2061372 + +* Tue Feb 15 2022 Jan Grulich - 5.15.2-15 +- Sync with Fedora + Resolves: bz#2051384 + +* Mon Jan 24 2022 Jan Grulich - 5.15.2-14 +- Sync with Fedora + Resolves: bz#2044169 + +* Wed Dec 08 2021 Jan Grulich - 5.15.2-13 +- Sync with Fedora + Resolves: bz#2028778 + +* Thu Oct 14 2021 Jan Grulich - 5.15.2-12 +- Drop BR: tree + Resolves: bz#2014080 + +* Wed Oct 13 2021 Jan Grulich - 5.15.2-11 +- Sync with Fedora + Resolves: bz#2012714 + +* Mon Sep 20 2021 Jan Grulich - 5.15.2-10 +- Sync with Fedora + Resolves: bz#2003935 + +* Mon Aug 23 2021 Jan Grulich - 5.15.2-9 +- Client: expose toplevel window state (change from Qt6) + Resolves: bz#1968292 + +* Tue Aug 10 2021 Mohan Boddu - 5.15.2-8 +- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags + Related: rhbz#1991688 + +* Wed Jun 09 2021 Jan Grulich - 5.15.2-7 +- Add gating tests + Resolves: bz#1968477 + +* Mon May 03 2021 Jan Grulich - 5.15.2-6 +- Sync with Fedora + Resolves: bz#1951152 + +* Fri Apr 16 2021 Mohan Boddu - 5.15.2-5 +- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 + +* Wed Jan 27 2021 Fedora Release Engineering - 5.15.2-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Wed Jan 6 09:32:16 CET 2021 Jan Grulich - 5.15.2-3 +- Scanner: Avoid accessing dangling pointers in destroy_func() + +* Tue Nov 24 07:54:16 CET 2020 Jan Grulich - 5.15.2-2 +- Rebuild for qtbase with -no-reduce-relocations option + +* Fri Nov 20 09:30:47 CET 2020 Jan Grulich - 5.15.2-1 +- 5.15.2 + +* Thu Sep 10 2020 Jan Grulich - 5.15.1-1 +- 5.15.1 + +* Sat Aug 01 2020 Fedora Release Engineering - 5.14.2-6 +- Second attempt - Rebuilt for + https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Wed Jul 29 2020 Fedora Release Engineering - 5.14.2-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Mon Jul 27 2020 Jan Grulich - 5.14.2-4 +- Backport upstream patches + Resolves: bz#1860455 + +* Thu Apr 30 2020 Ivan Mironov - 5.14.2-3 +- Cherry-pick fix for clipboard related crash from v5.15.0 + +* Tue Apr 21 2020 Jan Grulich - 5.14.2-2 +- Fix bold font rendering + Resolves: bz#1823984 + +* Sat Apr 04 2020 Rex Dieter - 5.14.2-1 +- 5.14.2 + +* Thu Jan 30 2020 Fedora Release Engineering - 5.13.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Wed Dec 11 2019 Jan Grulich - 5.13.2-2 +- Add support for primary-selection-unstable-v1 protocol +- Fix inverse repeat rate implementation + +* Mon Dec 09 2019 Jan Grulich - 5.13.2-1 +- 5.13.2 + +* Fri Oct 18 2019 Jan Grulich - 5.12.5-2 +- Client: Fix 100ms freeze when applications do not swap after deliverUpdateRequest + +* Tue Sep 24 2019 Jan Grulich - 5.12.5-1 +- 5.12.5 + +* Tue Jul 30 2019 Jan Grulich - 5.12.3-6 +- Do not redraw decorations everytime + +* Fri Jul 26 2019 Fedora Release Engineering - 5.12.4-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Tue Jul 23 2019 Jan Grulich - 5.12.3-5 +- Use Gnome platform theme on Gnome Wayland sessions + Resolves: bz#1732129 + +* Thu Jul 11 2019 Jan Grulich - 5.12.4-4 +- Pull in upstream fixes +- Disable patch which is not needed anymore because of qtbase change + +* Tue Jul 02 2019 Jan Grulich - 5.12.4-3 +- Pull in upstream fixes + +* Thu Jun 27 2019 Jan Grulich - 5.12.4-2 +- Pull in upstream fixes + +* Fri Jun 14 2019 Jan Grulich - 5.12.4-1 +- 5.12.4 + +* Tue Jun 04 2019 Jan Grulich - 5.12.3-1 +- 5.12.3 + +* Fri May 17 2019 Rex Dieter - 5.12.1-4 +- rebuild again (#1711115) + +* Fri May 10 2019 Rex Dieter - 5.12.1-3 +- rebuild (qt5-qtbase) + +* Thu May 09 2019 Rex Dieter - 5.12.1-2 +- drop BR: pkgconfig(glesv2) + +* Fri Feb 15 2019 Rex Dieter - 5.12.1-1 +- 5.12.1 + +* Sat Feb 02 2019 Fedora Release Engineering - 5.11.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Fri Dec 07 2018 Rex Dieter - 5.11.3-1 +- 5.11.3 + +* Fri Sep 21 2018 Jan Grulich - 5.11.2-1 +- 5.11.2 + +* Sat Jul 14 2018 Fedora Release Engineering - 5.11.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Wed Jun 20 2018 Rex Dieter - 5.11.1-1 +- 5.11.1 + +* Sun May 27 2018 Rex Dieter - 5.11.0-1 +- 5.11.0 +- use %%make_build %%ldconfig_scriptlets + +* Tue Mar 13 2018 Jan Grulich - 5.10.1-2 +- Do not crash when opening dialogs + +* Wed Feb 14 2018 Jan Grulich - 5.10.1-1 +- 5.10.1 + +* Fri Feb 09 2018 Fedora Release Engineering - 5.10.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Wed Jan 10 2018 Jan Grulich - 5.10.0-2 +- Do not recreate hidden egl surfaces + QTBUG-65553 + +* Tue Dec 19 2017 Jan Grulich - 5.10.0-1 +- 5.10.0 + +* Thu Nov 23 2017 Jan Grulich - 5.9.3-1 +- 5.9.3 + +* Mon Oct 09 2017 Jan Grulich - 5.9.2-1 +- 5.9.2 + +* Thu Aug 03 2017 Fedora Release Engineering - 5.9.1-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild + +* Thu Jul 27 2017 Fedora Release Engineering - 5.9.1-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Wed Jul 19 2017 Rex Dieter - 5.9.1-1 +- 5.9.1 + +* Fri Jun 16 2017 Rex Dieter - 5.9.0-2 +- .spec cosmetics, Source URL, refer to qt5- builddeps directly + +* Wed May 31 2017 Helio Chissini de Castro - 5.9.0-1 +- Upstream official release + +* Fri May 26 2017 Helio Chissini de Castro - 5.9.0-0.1.rc +- Upstream Release Candidate retagged + +* Tue May 09 2017 Helio Chissini de Castro - 5.9.0-0.beta.3 +- Upstream beta 3 + +* Mon Jan 30 2017 Helio Chissini de Castro - 5.8.0-1 +- New upstream version + +* Mon Jan 02 2017 Rex Dieter - 5.7.1-3 +- filter qml provides, BR: qtbase-private-devel qtdeclarative explicitly + +* Sat Dec 10 2016 Rex Dieter - 5.7.1-2 +- drop BR: cmake (handled by qt5-rpm-macros now) +- 5.7.1 dec5 snapshot + +* Wed Nov 09 2016 Helio Chissini de Castro - 5.7.1-1 +- New upstream version + +* Mon Jul 04 2016 Helio Chissini de Castro - 5.7.0-2 +- Compiled with gcc + +* Wed Jun 15 2016 Helio Chissini de Castro - 5.7.0-1 +- Qt 5.7.0 release + +* Thu Jun 09 2016 Jan Grulich - 5.6.1-1 +- Update to 5.6.1 + +* Mon Mar 21 2016 Rex Dieter - 5.6.0-11 +- rebuild + +* Fri Mar 18 2016 Rex Dieter - 5.6.0-10 +- rebuild + +* Tue Mar 15 2016 Peter Robinson 5.6.0-9 +- Bump release to 9 so it's higher than the final RC + +* Mon Mar 14 2016 Helio Chissini de Castro - 5.6.0-1 +- 5.6.0 final release + +* Tue Feb 23 2016 Helio Chissini de Castro - 5.6.0-8.rc +- Update to final RC + +* Mon Feb 15 2016 Helio Chissini de Castro - 5.6.0-7 +- Update RC release + +* Thu Feb 04 2016 Fedora Release Engineering - 5.6.0-6.rc +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Mon Feb 01 2016 Rex Dieter 5.6.0-5.rc +- use %%qmake_qt5 consistently + +* Mon Dec 28 2015 Rex Dieter 5.6.0-4.rc +- BR: cmake, update source URL, use %%license + +* Mon Dec 21 2015 Helio Chissini de Castro - 5.6.0-3 +- Update to final rc release + +* Thu Dec 10 2015 Helio Chissini de Castro - 5.6.0-2 +- Official rc release + +* Tue Nov 03 2015 Helio Chissini de Castro - 5.6.0-0.1 +- Start to implement 5.6.0 rc + +* Thu Oct 15 2015 Helio Chissini de Castro - 5.5.1-2 +- Update to final release 5.5.1 + +* Tue Sep 29 2015 Helio Chissini de Castro - 5.5.1-1 +- Update to Qt 5.5.1 RC1 + +* Thu Jul 16 2015 Rex Dieter 5.5.0-3 +- tighten qtbase dep (#1233829) + +* Sun Jul 05 2015 Pier Luigi Fiorini - 5.5.0-2 +- Add xkbcommon to the devel package. + +* Wed Jul 1 2015 Helio Chissini de Castro 5.5.0-1 +- New final upstream release Qt 5.5.0 + +* Thu Jun 25 2015 Helio Chissini de Castro - 5.5.0-0.2.rc +- Update for official RC1 released packages + +* Wed Jun 17 2015 Daniel Vrátil - 5.5.0-0.1.rc +- Qt5 5.5.0 RC1 + +* Wed Jun 03 2015 Jan Grulich - 5.4.2-1 +- 5.4.2 + +* Sat May 02 2015 Kalev Lember - 5.4.1-2 +- Rebuilt for GCC 5 C++11 ABI change + +* Fri Feb 27 2015 Rex Dieter 5.4.1-1 +- 5.4.1 + +* Wed Dec 10 2014 Rex Dieter 5.4.0-1 +- 5.4.0 (final) + +* Fri Nov 28 2014 Rex Dieter 5.4.0-0.3.rc +- 5.4.0-rc + +* Mon Nov 03 2014 Rex Dieter 5.4.0-0.2.rc +- use %%qmake_qt5 macro + +* Mon Oct 20 2014 Rex Dieter 5.4.0-0.1.rc +- 5.4.0-rc + +* Wed Sep 24 2014 Lubomir Rintel - 5.4.0-0.alpha1 +- Switch from a Git snapshot to a pre-release tarball + +* Sun Aug 17 2014 Fedora Release Engineering - 5.3.0-0.3.20140723git02c499c +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Thu Jul 24 2014 Lubomir Rintel - 5.3.0-0.2.20140723git02c499c +- Update + +* Sun Jun 08 2014 Fedora Release Engineering - 5.3.0-0.2.20140529git98dca3b +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Tue May 27 2014 Lubomir Rintel - 5.3.0-0.1.20140529git98dca3b +- Update and rebuild for Qt 5.3 + +* Fri Feb 14 2014 Lubomir Rintel - 5.1.0-0.6.20140202git6d038fb +- A more recent snapshot +- Disable xcomposite compositor until it builds + +* Sat Jan 04 2014 Lubomir Rintel - 5.1.0-0.6.20131203git6b20dfe +- Enable QtQuick compositor + +* Sat Jan 04 2014 Lubomir Rintel - 5.1.0-0.5.20131203git6b20dfe +- A newer snapshot + +* Mon Nov 25 2013 Lubomir Rintel - 5.1.0-0.5.20131125git4f5985c +- Rebase to a later snapshot, drop our patches +- Add license texts + +* Sat Nov 23 2013 Lubomir Rintel - 5.1.0-0.5.20131120git8cd1a77 +- Rebuild with EGL backend + +* Fri Nov 22 2013 Lubomir Rintel - 5.1.0-0.4.20131120git8cd1a77 +- Rebase to a later snapshot, drop 5.2 ABI patch +- Enable nogl backend + +* Sun Nov 10 2013 Rex Dieter 5.1.0-0.4.20130826git3b0b90b +- rebuild (arm/qreal) + +* Thu Oct 24 2013 Lubomir Rintel - 5.1.0-0.3.20130826git3b0b90b +- Bulk sad and useless attempt at consistent SPEC file formatting + +* Sun Oct 06 2013 Lubomir Rintel - 5.1.0-0.2.20130826git3b0b90b +- Bump platform plugin ABI to 5.2 for Qt 5.2 aplha + +* Wed Sep 11 2013 Lubomir Rintel - 5.1.0-0.1.20130826git3b0b90b +- Initial packaging +- Adjustments from review (Rex Dieter, #1008529)