Sync with Fedora

Resolves: bz#2044169
This commit is contained in:
Jan Grulich 2022-01-24 12:52:52 +01:00
parent b398e27a3e
commit 3aef25cacc
9 changed files with 1395 additions and 1 deletions

View File

@ -0,0 +1,29 @@
From eb422ab5e07498a7a8d086f6a942ee35ab3c9776 Mon Sep 17 00:00:00 2001
From: Aleix Pol <aleixpol@kde.org>
Date: Thu, 9 Dec 2021 17:35:24 +0100
Subject: [PATCH 37/41] Fix backport, context destruction was omitted
When cherry-picking for 9df11e79b46c77d8c83f765b2a8e85b639fd55a2, this
line got removed when rebasing the patch, which created a leak.
BUG: 442844
Change-Id: Id00e8b194dcd910c5f01ce9d2cc318f96a4129a2
---
src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
index 95d1049c..683fe123 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
@@ -407,6 +407,7 @@ QWaylandGLContext::~QWaylandGLContext()
{
delete m_blitter;
m_blitter = nullptr;
+ eglDestroyContext(m_eglDisplay, m_context);
if (m_decorationsContext != EGL_NO_CONTEXT)
eglDestroyContext(m_eglDisplay, m_decorationsContext);
}
--
2.34.1

View File

@ -0,0 +1,29 @@
From e0646f531e1e73a90a93faaa45d933ae40769985 Mon Sep 17 00:00:00 2001
From: Weng Xuetian <wengxt@gmail.com>
Date: Sat, 18 Dec 2021 23:42:49 -0800
Subject: [PATCH 38/41] Set preedit cursor when cursor equals to 0
Pick-to: 6.3 6.2 5.15
Change-Id: I832fbb22d973b36ac4ab51570fc53bc2e4c3ed58
Reviewed-by: Liang Qi <liang.qi@qt.io>
(cherry picked from commit 719a55be13bdadfa659a732755f280e276a894bd)
---
src/shared/qwaylandinputmethodeventbuilder.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/shared/qwaylandinputmethodeventbuilder.cpp b/src/shared/qwaylandinputmethodeventbuilder.cpp
index 526d0ef4..25be2509 100644
--- a/src/shared/qwaylandinputmethodeventbuilder.cpp
+++ b/src/shared/qwaylandinputmethodeventbuilder.cpp
@@ -151,7 +151,7 @@ QInputMethodEvent QWaylandInputMethodEventBuilder::buildPreedit(const QString &t
{
QList<QInputMethodEvent::Attribute> attributes;
- if (m_preeditCursor < 0) {
+ if (m_preeditCursor <= 0) {
attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
} else if (m_preeditCursor > 0) {
attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, indexFromWayland(text, m_preeditCursor), 1, QVariant()));
--
2.34.1

View File

@ -0,0 +1,513 @@
From 2044603ebb5ae70c785d50968ac620b842c2b14e Mon Sep 17 00:00:00 2001
From: David Edmundson <davidedmundson@kde.org>
Date: Tue, 16 Feb 2021 09:51:47 +0000
Subject: [PATCH 39/41] 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 bbd2d568..fbb5aa91 100644
--- a/src/client/qwaylanddatadevice.cpp
+++ b/src/client/qwaylanddatadevice.cpp
@@ -72,6 +72,8 @@ QWaylandDataDevice::QWaylandDataDevice(QWaylandDataDeviceManager *manager, QWayl
QWaylandDataDevice::~QWaylandDataDevice()
{
+ if (wl_data_device_get_version(object()) >= WL_DATA_DEVICE_RELEASE_SINCE_VERSION)
+ release();
}
QWaylandDataOffer *QWaylandDataDevice::selectionOffer() const
@@ -110,7 +112,7 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const
return m_dragOffer.data();
}
-bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
+bool QWaylandDataDevice::startDrag(QMimeData *mimeData, Qt::DropActions supportedActions, QWaylandWindow *icon)
{
auto *seat = m_display->currentInputDevice();
auto *origin = seat->pointerFocus();
@@ -123,8 +125,28 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
}
m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData));
+
+ if (wl_data_device_get_version(object()) >= 3)
+ m_dragSource->set_actions(dropActionsToWl(supportedActions));
+
connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled);
- connect(m_dragSource.data(), &QWaylandDataSource::targetChanged, this, &QWaylandDataDevice::dragSourceTargetChanged);
+ connect(m_dragSource.data(), &QWaylandDataSource::dndResponseUpdated, this, [this](bool accepted, Qt::DropAction action) {
+ auto drag = static_cast<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 f45122fb..5599cbd4 100644
--- a/src/client/qwaylanddatasource.cpp
+++ b/src/client/qwaylanddatasource.cpp
@@ -101,7 +101,32 @@ void QWaylandDataSource::data_source_send(const QString &mime_type, int32_t fd)
void QWaylandDataSource::data_source_target(const QString &mime_type)
{
- Q_EMIT targetChanged(mime_type);
+ m_accepted = !mime_type.isEmpty();
+ Q_EMIT dndResponseUpdated(m_accepted, m_dropAction);
+}
+
+void QWaylandDataSource::data_source_action(uint32_t action)
+{
+ Qt::DropAction qtAction = Qt::IgnoreAction;
+
+ if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
+ qtAction = Qt::MoveAction;
+ else if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
+ qtAction = Qt::CopyAction;
+
+ m_dropAction = qtAction;
+ Q_EMIT dndResponseUpdated(m_accepted, m_dropAction);
+}
+
+void QWaylandDataSource::data_source_dnd_finished()
+{
+ Q_EMIT finished();
+}
+
+void QWaylandDataSource::data_source_dnd_drop_performed()
+{
+
+ Q_EMIT dndDropped(m_accepted, m_dropAction);
}
}
diff --git a/src/client/qwaylanddatasource_p.h b/src/client/qwaylanddatasource_p.h
index 25afff79..96f07bc3 100644
--- a/src/client/qwaylanddatasource_p.h
+++ b/src/client/qwaylanddatasource_p.h
@@ -77,17 +77,25 @@ public:
QMimeData *mimeData() const;
Q_SIGNALS:
- void targetChanged(const QString &mime_type);
void cancelled();
+ void finished();
+
+ void dndResponseUpdated(bool accepted, Qt::DropAction action);
+ void dndDropped(bool accepted, Qt::DropAction action);
protected:
void data_source_cancelled() override;
void data_source_send(const QString &mime_type, int32_t fd) override;
void data_source_target(const QString &mime_type) override;
+ void data_source_dnd_drop_performed() override;
+ void data_source_dnd_finished() override;
+ void data_source_action(uint32_t action) override;
private:
QWaylandDisplay *m_display = nullptr;
QMimeData *m_mime_data = nullptr;
+ bool m_accepted = false;
+ Qt::DropAction m_dropAction = Qt::IgnoreAction;
};
}
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index 9f595af3..ea344c61 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -354,7 +354,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.34.1

View File

@ -0,0 +1,67 @@
From 0b15df7e9e26a4edfc2277eb3ec7b3d5c58a5dcd Mon Sep 17 00:00:00 2001
From: Arjen Hiemstra <ahiemstra@heimr.nl>
Date: Thu, 18 Nov 2021 13:05:30 +0100
Subject: [PATCH 40/44] 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.34.1

View File

@ -0,0 +1,38 @@
From 867540b9d913760a847ff67c8694d817c821f2c2 Mon Sep 17 00:00:00 2001
From: David Edmundson <davidedmundson@kde.org>
Date: Sun, 14 Nov 2021 13:54:19 +0000
Subject: [PATCH 41/44] 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 ba881cb3..1597f67e 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -436,7 +436,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.34.1

View File

@ -0,0 +1,85 @@
From bc5a8d24f63181a36759723a4d1b39b59b3b53e6 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 42/44] 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 ea344c61..0f75cb7e 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -158,13 +158,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)
@@ -189,6 +182,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 09a1736a..d9c8849f 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -129,6 +129,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 e5e7dd42..f5632982 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.34.1

View File

@ -0,0 +1,47 @@
From 98dc33fdc66f15a0e69f3024fcef046f11adc58c 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 43/44] 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 f5632982..3a6fa651 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.34.1

View File

@ -0,0 +1,574 @@
From 4644d51f4b52e83fc1b4d02b380d80d9d57e76fa 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 44/44] 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 0f75cb7e..a7ce280a 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -85,10 +85,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)
@@ -162,6 +355,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);
@@ -208,98 +407,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
@@ -674,4 +812,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int p
} // namespace QtWaylandClient
+#include "qwaylanddisplay.moc"
+
QT_END_NAMESPACE
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index d9c8849f..42bc661d 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -109,6 +109,7 @@ class QWaylandSurface;
class QWaylandShellIntegration;
class QWaylandCursor;
class QWaylandCursorTheme;
+class EventThread;
typedef void (*RegistryListener)(void *data,
struct wl_registry *registry,
@@ -120,12 +121,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;
@@ -212,12 +207,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();
@@ -240,6 +234,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;
@@ -276,11 +273,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 3a6fa651..3b876047 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 1597f67e..7de19a74 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)
@@ -635,6 +632,8 @@ const wl_callback_listener QWaylandWindow::callbackListener = {
void QWaylandWindow::handleFrameCallback()
{
+ QMutexLocker locker(&mFrameSyncMutex);
+
mWaitingForFrameCallback = false;
mFrameCallbackElapsedTimer.invalidate();
@@ -656,12 +655,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";
@@ -1157,8 +1160,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
@@ -1171,7 +1177,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);
}
@@ -1191,9 +1202,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);
@@ -1203,6 +1215,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 e0687962..d45980a8 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.34.1

View File

@ -5,7 +5,7 @@
Summary: Qt5 - Wayland platform support and QtCompositor module
Name: qt5-%{qt_module}
Version: 5.15.2
Release: 13%{?dist}
Release: 14%{?dist}
License: LGPLv3
Url: http://www.qt.io
@ -53,6 +53,14 @@ Patch33: 0033-Client-Always-destroy-frame-callback-in-the-actual-c.patch
Patch34: 0034-Fix-the-logic-for-decoding-modifiers-map-in-Wayland-.patch
Patch35: 0035-Wayland-client-use-wl_keyboard-to-determine-active-s.patch
Patch36: 0036-Client-do-not-empty-clipboard-when-a-new-popup-windo.patch
Patch37: 0037-Fix-backport-context-destruction-was-omitted.patch
Patch38: 0038-Set-preedit-cursor-when-cursor-equals-to-0.patch
Patch39: 0039-Client-Implement-DataDeviceV3.patch
Patch40: 0040-Client-Delay-deletion-of-QDrag-object-until-after-we.patch
Patch41: 0041-Client-Avoid-processing-of-events-when-showing-windo.patch
Patch42: 0042-Handle-registry_global-out-of-constructor.patch
Patch43: 0043-Connect-flushRequest-after-forceRoundTrip.patch
Patch44: 0044-Move-the-wayland-socket-polling-to-a-separate-event-.patch
# Disable for now, there is a Qt bug making this broken
# Patch52: qtwayland-decoration-support-backports-from-qt6.patch
@ -182,6 +190,10 @@ popd
%endif
%changelog
* Mon Jan 24 2022 Jan Grulich <jgrulich@redhat.com> - 5.15.2-14
- Sync with Fedora
Resolves: bz#2044169
* Wed Dec 08 2021 Jan Grulich <jgrulich@redhat.com> - 5.15.2-13
- Sync with Fedora
Resolves: bz#2028778