qt5-qtwayland package is retired on branch c10s for BAKERY-412

This commit is contained in:
Troy Dawson 2024-03-21 19:39:08 +00:00
parent 2ff721c9b7
commit 39378799ad
21 changed files with 1 additions and 2442 deletions

21
.gitignore vendored
View File

@ -1,21 +0,0 @@
/qtwayland-everywhere-src-5.11.0.tar.xz
/qtwayland-everywhere-src-5.11.1.tar.xz
/qtwayland-everywhere-src-5.11.2.tar.xz
/qtwayland-everywhere-src-5.11.3.tar.xz
/qtwayland-everywhere-src-5.12.1.tar.xz
/qtwayland-everywhere-src-5.12.3.tar.xz
/qtwayland-everywhere-src-5.12.4.tar.xz
/qtwayland-everywhere-src-5.12.5.tar.xz
/qtwayland-everywhere-src-5.13.2.tar.xz
/qtwayland-everywhere-src-5.14.2.tar.xz
/qtwayland-everywhere-src-5.15.1.tar.xz
/qtwayland-everywhere-src-5.15.2.tar.xz
/qtwayland-everywhere-opensource-src-5.15.3.tar.xz
/qtwayland-everywhere-opensource-src-5.15.4.tar.xz
/qtwayland-everywhere-opensource-src-5.15.5.tar.xz
/qtwayland-everywhere-opensource-src-5.15.6.tar.xz
/qtwayland-everywhere-opensource-src-5.15.7.tar.xz
/qtwayland-everywhere-opensource-src-5.15.8.tar.xz
/qtwayland-everywhere-opensource-src-5.15.9.tar.xz
/qtwayland-everywhere-opensource-src-5.15.10.tar.xz
/qtwayland-everywhere-opensource-src-5.15.11.tar.xz

View File

@ -1,146 +0,0 @@
From ec085b69839e85b4543afb2009d519da21ffd782 Mon Sep 17 00:00:00 2001
From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
Date: Wed, 5 May 2021 20:49:26 +0300
Subject: [PATCH 01/59] 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 <davidedmundson@kde.org>
(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.41.0

View File

@ -1,58 +0,0 @@
From 09b0405d8dd413c34ab03b3c9a288f3e11765ec1 Mon Sep 17 00:00:00 2001
From: Jaeyoon Jung <jaeyoon.jung@lge.com>
Date: Mon, 15 Feb 2021 08:31:06 +0900
Subject: [PATCH 02/59] 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 <eskil.abrahamsen-blomfeldt@qt.io>
(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 e00c28c3..64f7caeb 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, &current_width, &current_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 2fccbcea..ad1e5ee9 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h
@@ -85,6 +85,7 @@ private:
mutable QOpenGLFramebufferObject *m_contentFBO = nullptr;
QSurfaceFormat m_format;
+ QSize m_requestedSize;
};
}
--
2.41.0

View File

@ -1,39 +0,0 @@
From 1cdb21c2e739aabb6793cf90029e76921b67d975 Mon Sep 17 00:00:00 2001
From: David Edmundson <davidedmundson@kde.org>
Date: Tue, 9 Feb 2021 16:09:21 +0000
Subject: [PATCH 03/59] 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 <vlad.zahorodnii@kde.org>
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
(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 1e2db786..0124b890 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.41.0

View File

@ -1,49 +0,0 @@
From 4bce11a39578ed43d7efd61e2140704e84386cb9 Mon Sep 17 00:00:00 2001
From: David Edmundson <davidedmundson@kde.org>
Date: Fri, 14 May 2021 13:23:24 +0100
Subject: [PATCH 04/59] 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 966096a8..2dc61b77 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.41.0

View File

@ -1,58 +0,0 @@
From 668ebf5792f1eb0910245465e5407fcab86ce98b Mon Sep 17 00:00:00 2001
From: David Edmundson <davidedmundson@kde.org>
Date: Mon, 3 May 2021 23:01:53 +0100
Subject: [PATCH 05/59] 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 <vlad.zahorodnii@kde.org>
Reviewed-by: Aleix Pol Gonzalez <aleixpol@kde.org>
(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 94ea573e..699a75eb 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;
}
@@ -267,6 +265,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.41.0

View File

@ -1,46 +0,0 @@
From bc07f9d8083f9f731fefb0f1180a18d2172eb57d Mon Sep 17 00:00:00 2001
From: David Edmundson <davidedmundson@kde.org>
Date: Mon, 14 Jun 2021 12:45:37 +0100
Subject: [PATCH 06/59] 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 2dc61b77..36609c08 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.41.0

View File

@ -1,33 +0,0 @@
From cfbb11da7f7ecbf6372967f01015d269d832dccf Mon Sep 17 00:00:00 2001
From: Zhang Liang <zhanglianga@uniontech.com>
Date: Mon, 1 Feb 2021 19:29:43 +0800
Subject: [PATCH 07/59] Fix: remove listener
Add the operation for removing the listener form listener list
Change-Id: Ief2ff1303b607eee499543303fe80e51f8f10cc5
Reviewed-by: David Edmundson <davidedmundson@kde.org>
(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 8a6d5db1..16f8ca1a 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.41.0

View File

@ -1,55 +0,0 @@
From 25218285ed45d32a93d10e354975ccbbd98cdb77 Mon Sep 17 00:00:00 2001
From: David Redondo <qt@david-redondo.de>
Date: Wed, 26 May 2021 14:49:40 +0200
Subject: [PATCH 08/59] 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 <mart@kde.org>
Reviewed-by: David Edmundson <davidedmundson@kde.org>
(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<int> 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<int> possibleKeys(const QKeyEvent *event) const override;
QStringList themeNames() const override;
--
2.41.0

View File

@ -1,68 +0,0 @@
From d9d5f69f5619dbaa40ca9b97981721467424947b Mon Sep 17 00:00:00 2001
From: Jan Blackquill <uhhadd@gmail.com>
Date: Tue, 24 Aug 2021 14:36:34 -0400
Subject: [PATCH 09/59] 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 <davidedmundson@kde.org>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
(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 c5266ab3..e2fe1928 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.41.0

View File

@ -1,77 +0,0 @@
From d2992d985306a8bec6298c5238a85ade1ae0c611 Mon Sep 17 00:00:00 2001
From: Georges Basile Stavracas Neto <gbsneto@gnome.org>
Date: Thu, 27 May 2021 19:55:04 -0300
Subject: [PATCH 10/59] 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 <qt_ci_bot@qt-project.org>
Reviewed-by: Georges Basile Stavracas Neto <gbsneto@gnome.org>
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
(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 5a390434..2dce02f0 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -1196,6 +1196,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.41.0

View File

@ -1,58 +0,0 @@
From cc3fbf3c1d36d5d997082927f895a88ea6d17474 Mon Sep 17 00:00:00 2001
From: Georges Basile Stavracas Neto <gbsneto@gnome.org>
Date: Thu, 27 May 2021 20:02:53 -0300
Subject: [PATCH 11/59] 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 <eskil.abrahamsen-blomfeldt@qt.io>
(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 2dce02f0..c532b44a 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<QWaylandWindow*>(data);
+
+ Q_ASSERT(callback == window->mFrameCallback);
+ wl_callback_destroy(callback);
+ window->mFrameCallback = nullptr;
+
window->handleFrameCallback();
}
};
@@ -1205,11 +1209,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<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(wrappedSurface), mFrameQueue.queue);
--
2.41.0

View File

@ -1,341 +0,0 @@
From 2a68cb54a8a07a59e140fe8acd966ca6bb485afd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=C3=A9ven=20Car?= <meven.car@enioka.com>
Date: Wed, 18 Aug 2021 18:28:20 +0200
Subject: [PATCH 12/59] 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 <eskil.abrahamsen-blomfeldt@qt.io>
---
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 16f8ca1a..d1ca0274 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<bool()> 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 c532b44a..fdffe584 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)
@@ -1109,10 +1110,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<QWaylandXdgPopupV5 *>(newFocus->shellSurface()))
- m_display->handleWindowActivated(newFocus);
- if (oldFocus && qobject_cast<QWaylandXdgPopupV5 *>(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<QWaylandXdgShellV5> 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<QWaylandXdgSurfaceV6 *>(newFocus->shellSurface());
- if (xdgSurface && !xdgSurface->handlesActiveState())
- m_display->handleWindowActivated(newFocus);
- }
- if (oldFocus && qobject_cast<QWaylandXdgSurfaceV6 *>(oldFocus->shellSurface())) {
- auto *xdgSurface = qobject_cast<QWaylandXdgSurfaceV6 *>(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<QWaylandXdgShellV6> m_xdgShell;
diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
index 699a75eb..888bab77 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<QWaylandXdgSurface *>(newFocus->shellSurface());
- if (xdgSurface && !xdgSurface->handlesActiveState())
- m_display->handleWindowActivated(newFocus);
- }
- if (oldFocus && qobject_cast<QWaylandXdgSurface *>(oldFocus->shellSurface())) {
- auto *xdgSurface = qobject_cast<QWaylandXdgSurface *>(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<QWaylandXdgShell> 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 <QtGui/QOpenGLWindow>
#include <QtGui/qpa/qplatformnativeinterface.h>
#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
+#include <QtWaylandClient/private/qwaylandwindow_p.h>
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<QtWaylandClient::QWaylandWindow *>(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.41.0

View File

@ -1,68 +0,0 @@
From 888f3c6eca8e807a3ca870067e080349a76a5703 Mon Sep 17 00:00:00 2001
From: Jan Grulich <jgrulich@redhat.com>
Date: Fri, 16 Jul 2021 13:00:03 +0200
Subject: [PATCH 13/59] 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 <qt_ci_bot@qt-project.org>
Reviewed-by: David Edmundson <davidedmundson@kde.org>
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 d1ca0274..7560bf41 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 08e8adb8..e1111124 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.41.0

View File

@ -1,512 +0,0 @@
From 23c698917e0fdac0d9519334b7c375cf226e36c7 Mon Sep 17 00:00:00 2001
From: David Edmundson <davidedmundson@kde.org>
Date: Tue, 16 Feb 2021 09:51:47 +0000
Subject: [PATCH 14/59] 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 <eskil.abrahamsen-blomfeldt@qt.io>
(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 0124b890..d154c695 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<QWaylandDrag *>(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<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->setDropResponse(response);
+ });
+ connect(m_dragSource.data(), &QWaylandDataSource::finished, this, []() {
+ static_cast<QWaylandDrag *>(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<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag(response);
+ auto drag = static_cast<QWaylandDrag *>(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<QWaylandDrag *>(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<QWaylandDrag *>(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<QWaylandDrag *>(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<QWaylandMimeData> m_mimeData;
+ Qt::DropActions m_supportedActions;
};
diff --git a/src/client/qwaylanddatasource.cpp b/src/client/qwaylanddatasource.cpp
index 9b17cdfa..9610366d 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 3003da1b..b6a9e06b 100644
--- a/src/client/qwaylanddatasource_p.h
+++ b/src/client/qwaylanddatasource_p.h
@@ -77,16 +77,24 @@ 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:
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 7560bf41..7f28d01c 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<QWaylandWindow *>(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.41.0

View File

@ -1,67 +0,0 @@
From bfc5a684b3f8aa38f28267513872376e04bda8b6 Mon Sep 17 00:00:00 2001
From: Arjen Hiemstra <ahiemstra@heimr.nl>
Date: Thu, 18 Nov 2021 13:05:30 +0100
Subject: [PATCH 15/59] 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 <davidedmundson@kde.org>
(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.41.0

View File

@ -1,38 +0,0 @@
From 4d4eb8e543f688c9d5c9e1083f5e46ddf42b9786 Mon Sep 17 00:00:00 2001
From: David Edmundson <davidedmundson@kde.org>
Date: Sun, 14 Nov 2021 13:54:19 +0000
Subject: [PATCH 16/59] 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 <qt_ci_bot@qt-project.org>
Reviewed-by: Aleix Pol Gonzalez <aleixpol@kde.org>
(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 fdffe584..83a394c4 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.41.0

View File

@ -1,85 +0,0 @@
From 359bc440082f7876a51c12a0db4e9399006726f2 Mon Sep 17 00:00:00 2001
From: Elvis Lee <kwangwoong.lee@lge.com>
Date: Thu, 18 Feb 2021 15:45:49 +0900
Subject: [PATCH 17/59] 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 <eskil.abrahamsen-blomfeldt@qt.io>
(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 7f28d01c..c2482030 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.41.0

View File

@ -1,47 +0,0 @@
From 3430f8812afa51b5b6fb139361b45fbfe6cff5d0 Mon Sep 17 00:00:00 2001
From: Elvis Lee <kwangwoong.lee@lge.com>
Date: Wed, 17 Mar 2021 16:31:10 +0900
Subject: [PATCH 18/59] 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 <eskil.abrahamsen-blomfeldt@qt.io>
(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.41.0

View File

@ -1,576 +0,0 @@
From 1fe2895d2e3ccae0ae0abb2760e1714ac296c3ea Mon Sep 17 00:00:00 2001
From: Adrien Faveraux <af@brain-networks.fr>
Date: Fri, 26 Nov 2021 09:18:58 +0100
Subject: [PATCH 19/59] 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 <ratchanan@ubports.com>
Task-number: QTBUG-66075
Change-Id: Ibb33ad7f4193b866d1b8d7a0405a94d59dcad5eb
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
(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 c2482030..c38f6f82 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -87,10 +87,203 @@
#include <errno.h>
+#include <tuple> // 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<bool> 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<bool ()> 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,6 +816,8 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p
} // namespace QtWaylandClient
+#include "qwaylanddisplay.moc"
+
QT_END_NAMESPACE
#include "moc_qwaylanddisplay_p.cpp"
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<bool()> 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<EventThread> m_eventThread;
+ wl_event_queue *m_frameEventQueue = nullptr;
+ QScopedPointer<EventThread> m_frameEventQueueThread;
QtWayland::wl_compositor mCompositor;
QScopedPointer<QWaylandShm> mShm;
QList<QWaylandScreen *> mWaitingScreens;
@@ -282,11 +279,9 @@ private:
QWaylandInputDevice *mLastInputDevice = nullptr;
QPointer<QWaylandWindow> mLastInputWindow;
QPointer<QWaylandWindow> mLastKeyboardFocus;
- QVector<QWaylandWindow *> mActiveWindows;
- QVector<FrameQueue> mExternalQueues;
+ QList<QWaylandWindow *> 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 83a394c4..3d9741d2 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";
@@ -1183,8 +1186,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
@@ -1197,7 +1203,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);
}
@@ -1217,9 +1228,10 @@ void QWaylandWindow::handleUpdate()
if (!mSurface)
return;
- QMutexLocker locker(mFrameQueue.mutex);
+ QMutexLocker locker(&mFrameSyncMutex);
+
struct ::wl_surface *wrappedSurface = reinterpret_cast<struct ::wl_surface *>(wl_proxy_create_wrapper(mSurface->object()));
- wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(wrappedSurface), mFrameQueue.queue);
+ wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(wrappedSurface), mDisplay->frameEventQueue());
mFrameCallback = wl_surface_frame(wrappedSurface);
wl_proxy_wrapper_destroy(wrappedSurface);
wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
@@ -1229,6 +1241,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.41.0

1
dead.package Normal file
View File

@ -0,0 +1 @@
qt5-qtwayland package is retired on branch c10s for BAKERY-412