3119 lines
120 KiB
Diff
3119 lines
120 KiB
Diff
diff --git a/src/client/configure.json b/src/client/configure.json
|
|
index 73f233622..6247f85e6 100644
|
|
--- a/src/client/configure.json
|
|
+++ b/src/client/configure.json
|
|
@@ -167,7 +167,8 @@
|
|
"exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;",
|
|
"return 0;"
|
|
]
|
|
- }
|
|
+ },
|
|
+ "use": "wayland-client"
|
|
},
|
|
"egl_1_5-wayland": {
|
|
"label": "EGL 1.5 with Wayland Platform",
|
|
@@ -182,7 +183,7 @@
|
|
"eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_EXT, (struct wl_display *)(nullptr), nullptr);"
|
|
]
|
|
},
|
|
- "use": "egl"
|
|
+ "use": "egl wayland-client"
|
|
}
|
|
},
|
|
|
|
diff --git a/src/client/global/qwaylandclientextension.cpp b/src/client/global/qwaylandclientextension.cpp
|
|
index 966096a88..36609c08f 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)
|
|
@@ -88,6 +91,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 98272e571..5bd283985 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;
|
|
diff --git a/src/client/global/qwaylandclientextension_p.h b/src/client/global/qwaylandclientextension_p.h
|
|
index 69cc46a0a..9091efbe5 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
|
|
diff --git a/src/client/qwaylandabstractdecoration.cpp b/src/client/qwaylandabstractdecoration.cpp
|
|
index b628930df..d15a7f9fe 100644
|
|
--- a/src/client/qwaylandabstractdecoration.cpp
|
|
+++ b/src/client/qwaylandabstractdecoration.cpp
|
|
@@ -122,7 +122,7 @@ const QImage &QWaylandAbstractDecoration::contentImage()
|
|
if (d->m_isDirty) {
|
|
// Update the decoration backingstore
|
|
|
|
- const int bufferScale = waylandWindow()->scale();
|
|
+ const qreal bufferScale = waylandWindow()->scale();
|
|
const QSize imageSize = waylandWindow()->surfaceSize() * bufferScale;
|
|
d->m_decorationContentImage = QImage(imageSize, QImage::Format_ARGB32_Premultiplied);
|
|
// Only scale by buffer scale, not QT_SCALE_FACTOR etc.
|
|
diff --git a/src/client/qwaylandcursor.cpp b/src/client/qwaylandcursor.cpp
|
|
index e4eca9d4e..ba76ba2d0 100644
|
|
--- a/src/client/qwaylandcursor.cpp
|
|
+++ b/src/client/qwaylandcursor.cpp
|
|
@@ -44,6 +44,7 @@
|
|
#include "qwaylandshmbackingstore_p.h"
|
|
|
|
#include <QtGui/QImageReader>
|
|
+#include <QBitmap>
|
|
#include <QDebug>
|
|
|
|
#include <wayland-cursor.h>
|
|
@@ -250,7 +251,27 @@ QWaylandCursor::QWaylandCursor(QWaylandDisplay *display)
|
|
QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor)
|
|
{
|
|
Q_ASSERT(cursor->shape() == Qt::BitmapCursor);
|
|
- const QImage &img = cursor->pixmap().toImage();
|
|
+
|
|
+ const QBitmap mask = cursor->mask(Qt::ReturnByValue);
|
|
+ QImage img;
|
|
+ if (cursor->pixmap().isNull())
|
|
+ img = cursor->bitmap(Qt::ReturnByValue).toImage();
|
|
+ else
|
|
+ img = cursor->pixmap().toImage();
|
|
+
|
|
+ // convert to supported format if necessary
|
|
+ if (!display->shm()->formatSupported(img.format())) {
|
|
+ if (mask.isNull()) {
|
|
+ img.convertTo(QImage::Format_RGB32);
|
|
+ } else {
|
|
+ // preserve mask
|
|
+ img.convertTo(QImage::Format_ARGB32);
|
|
+ QPixmap pixmap = QPixmap::fromImage(img);
|
|
+ pixmap.setMask(mask);
|
|
+ img = pixmap.toImage();
|
|
+ }
|
|
+ }
|
|
+
|
|
QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(display, img.size(), img.format()));
|
|
memcpy(buffer->image()->bits(), img.bits(), size_t(img.sizeInBytes()));
|
|
return buffer;
|
|
diff --git a/src/client/qwaylanddatadevice.cpp b/src/client/qwaylanddatadevice.cpp
|
|
index 4d2459d1c..c57f1a499 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();
|
|
@@ -129,7 +131,31 @@ bool QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
|
|
mimeData->setData("application/x-qt-avoid-empty-placeholder", QByteArray("1"));
|
|
|
|
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::dndResponseUpdated, this, [this](bool accepted, Qt::DropAction action) {
|
|
+ auto drag = static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag());
|
|
+ if (!drag->currentDrag()) {
|
|
+ return;
|
|
+ }
|
|
+ // 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;
|
|
@@ -158,7 +184,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;
|
|
}
|
|
@@ -168,7 +194,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();
|
|
}
|
|
}
|
|
|
|
@@ -192,7 +222,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,
|
|
@@ -203,11 +233,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()
|
|
@@ -241,10 +267,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());
|
|
|
|
@@ -252,11 +278,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)
|
|
|
|
@@ -283,14 +305,10 @@ void QWaylandDataDevice::selectionSourceCancelled()
|
|
#if QT_CONFIG(draganddrop)
|
|
void QWaylandDataDevice::dragSourceCancelled()
|
|
{
|
|
+ static_cast<QWaylandDrag *>(QGuiApplicationPrivate::platformIntegration()->drag())->finishDrag();
|
|
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));
|
|
@@ -303,6 +321,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 16c3ad28e..801dcc2c1 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 35d67307f..6dc4f77f5 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 bd05c0fbb..510d9be48 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 2297e8a16..0241a1dfc 100644
|
|
--- a/src/client/qwaylanddataoffer.cpp
|
|
+++ b/src/client/qwaylanddataoffer.cpp
|
|
@@ -56,6 +56,11 @@ static QString utf8Text()
|
|
return QStringLiteral("text/plain;charset=utf-8");
|
|
}
|
|
|
|
+static QString portalFileTransfer()
|
|
+{
|
|
+ return QStringLiteral("application/vnd.portal.filetransfer");
|
|
+}
|
|
+
|
|
QWaylandDataOffer::QWaylandDataOffer(QWaylandDisplay *display, struct ::wl_data_offer *offer)
|
|
: QtWayland::wl_data_offer(offer)
|
|
, m_display(display)
|
|
@@ -82,6 +87,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 +107,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)
|
|
{
|
|
@@ -157,23 +187,26 @@ QVariant QWaylandMimeData::retrieveData_sys(const QString &mimeType, QVariant::T
|
|
}
|
|
|
|
close(pipefd[0]);
|
|
- m_data.insert(mimeType, content);
|
|
+ if (mimeType != portalFileTransfer())
|
|
+ m_data.insert(mimeType, content);
|
|
+
|
|
return content;
|
|
}
|
|
|
|
int QWaylandMimeData::readData(int fd, QByteArray &data) const
|
|
{
|
|
- fd_set readset;
|
|
- FD_ZERO(&readset);
|
|
- FD_SET(fd, &readset);
|
|
- struct timeval timeout;
|
|
+ struct pollfd readset;
|
|
+ readset.fd = fd;
|
|
+ readset.events = POLLIN;
|
|
+ struct timespec timeout;
|
|
timeout.tv_sec = 1;
|
|
- timeout.tv_usec = 0;
|
|
+ timeout.tv_nsec = 0;
|
|
+
|
|
|
|
Q_FOREVER {
|
|
- int ready = select(FD_SETSIZE, &readset, nullptr, nullptr, &timeout);
|
|
+ int ready = qt_safe_poll(&readset, 1, &timeout);
|
|
if (ready < 0) {
|
|
- qWarning() << "QWaylandDataOffer: select() failed";
|
|
+ qWarning() << "QWaylandDataOffer: qt_safe_poll() failed";
|
|
return -1;
|
|
} else if (ready == 0) {
|
|
qWarning("QWaylandDataOffer: timeout reading from pipe");
|
|
diff --git a/src/client/qwaylanddataoffer_p.h b/src/client/qwaylanddataoffer_p.h
|
|
index 9cf1483ca..6f6673984 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 c86c14162..321170a66 100644
|
|
--- a/src/client/qwaylanddatasource.cpp
|
|
+++ b/src/client/qwaylanddatasource.cpp
|
|
@@ -105,7 +105,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 520b31656..089c54853 100644
|
|
--- a/src/client/qwaylanddatasource_p.h
|
|
+++ b/src/client/qwaylanddatasource_p.h
|
|
@@ -75,16 +75,24 @@ public:
|
|
~QWaylandDataSource() override;
|
|
|
|
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 8a6d5db1d..737b539de 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)
|
|
@@ -160,17 +353,16 @@ 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)
|
|
{
|
|
+ if (m_eventThread)
|
|
+ m_eventThread->stop();
|
|
+
|
|
+ if (m_frameEventQueueThread)
|
|
+ m_frameEventQueueThread->stop();
|
|
+
|
|
if (mSyncCallback)
|
|
wl_callback_destroy(mSyncCallback);
|
|
|
|
@@ -187,10 +379,26 @@ QWaylandDisplay::~QWaylandDisplay(void)
|
|
#if QT_CONFIG(cursor)
|
|
qDeleteAll(mCursorThemes);
|
|
#endif
|
|
+
|
|
+ if (m_frameEventQueue)
|
|
+ wl_event_queue_destroy(m_frameEventQueue);
|
|
+
|
|
if (mDisplay)
|
|
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)
|
|
@@ -205,98 +413,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
|
|
@@ -347,7 +494,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
|
|
if (interface == QStringLiteral("wl_output")) {
|
|
mWaitingScreens << new QWaylandScreen(this, version, id);
|
|
} else if (interface == QStringLiteral("wl_compositor")) {
|
|
- mCompositorVersion = qMin((int)version, 3);
|
|
+ mCompositorVersion = qMin((int)version, 4);
|
|
mCompositor.init(registry, id, mCompositorVersion);
|
|
} else if (interface == QStringLiteral("wl_shm")) {
|
|
mShm.reset(new QWaylandShm(this, version, id));
|
|
@@ -356,7 +503,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));
|
|
@@ -373,6 +520,8 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
|
|
#if QT_CONFIG(wayland_client_primary_selection)
|
|
} else if (interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) {
|
|
mPrimarySelectionManager.reset(new QWaylandPrimarySelectionDeviceManagerV1(this, id, 1));
|
|
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
|
|
+ inputDevice->setPrimarySelectionDevice(mPrimarySelectionManager->createDevice(inputDevice));
|
|
#endif
|
|
} else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) {
|
|
mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
|
|
@@ -431,6 +580,13 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
|
|
inputDevice->setTextInput(nullptr);
|
|
mWaylandIntegration->reconfigureInputContext();
|
|
}
|
|
+#if QT_CONFIG(wayland_client_primary_selection)
|
|
+ if (global.interface == QStringLiteral("zwp_primary_selection_device_manager_v1")) {
|
|
+ mPrimarySelectionManager.reset();
|
|
+ for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
|
|
+ inputDevice->setPrimarySelectionDevice(nullptr);
|
|
+ }
|
|
+#endif
|
|
mGlobals.removeAt(i);
|
|
break;
|
|
}
|
|
@@ -456,9 +612,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()
|
|
@@ -471,50 +628,9 @@ uint32_t QWaylandDisplay::currentTimeMillisec()
|
|
return 0;
|
|
}
|
|
|
|
-static void
|
|
-sync_callback(void *data, struct wl_callback *callback, uint32_t serial)
|
|
-{
|
|
- Q_UNUSED(serial)
|
|
- bool *done = static_cast<bool *>(data);
|
|
-
|
|
- *done = true;
|
|
-
|
|
- // If the wl_callback done event is received after the condition check in the while loop in
|
|
- // forceRoundTrip(), but before the call to processEvents, the call to processEvents may block
|
|
- // forever if no more events are posted (eventhough the callback is handled in response to the
|
|
- // aboutToBlock signal). Hence, we wake up the event dispatcher so forceRoundTrip may return.
|
|
- // (QTBUG-64696)
|
|
- if (auto *dispatcher = QThread::currentThread()->eventDispatcher())
|
|
- dispatcher->wakeUp();
|
|
-
|
|
- wl_callback_destroy(callback);
|
|
-}
|
|
-
|
|
-static const struct wl_callback_listener sync_listener = {
|
|
- sync_callback
|
|
-};
|
|
-
|
|
void QWaylandDisplay::forceRoundTrip()
|
|
{
|
|
- // wl_display_roundtrip() works on the main queue only,
|
|
- // but we use a separate one, so basically reimplement it here
|
|
- int ret = 0;
|
|
- bool done = false;
|
|
- wl_callback *callback = wl_display_sync(mDisplay);
|
|
- wl_callback_add_listener(callback, &sync_listener, &done);
|
|
- flushRequests();
|
|
- if (QThread::currentThread()->eventDispatcher()) {
|
|
- while (!done && ret >= 0) {
|
|
- QThread::currentThread()->eventDispatcher()->processEvents(QEventLoop::WaitForMoreEvents);
|
|
- ret = wl_display_dispatch_pending(mDisplay);
|
|
- }
|
|
- } else {
|
|
- while (!done && ret >= 0)
|
|
- ret = wl_display_dispatch(mDisplay);
|
|
- }
|
|
-
|
|
- if (ret == -1 && !done)
|
|
- wl_callback_destroy(callback);
|
|
+ wl_display_roundtrip(mDisplay);
|
|
}
|
|
|
|
bool QWaylandDisplay::supportsWindowDecoration() const
|
|
@@ -578,14 +694,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;
|
|
}
|
|
@@ -604,6 +716,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 = {
|
|
@@ -630,6 +755,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()
|
|
@@ -656,6 +788,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 1bad8b678..cf91b9245 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,15 +123,11 @@ 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;
|
|
|
|
+ void initialize();
|
|
+
|
|
#if QT_CONFIG(xkbcommon)
|
|
struct xkb_context *xkbContext() const { return mXkbContext.get(); }
|
|
#endif
|
|
@@ -214,11 +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();
|
|
@@ -241,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;
|
|
@@ -279,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/qwaylanddnd.cpp b/src/client/qwaylanddnd.cpp
|
|
index 6535aa16b..7c53f5faa 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
|
|
@@ -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)
|
|
@@ -103,33 +106,41 @@ 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);
|
|
+
|
|
+ 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 474fe2ab1..46f629ace 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;
|
|
@@ -82,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;
|
|
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
|
|
index 346c438c6..ab978d3f4 100644
|
|
--- a/src/client/qwaylandinputdevice.cpp
|
|
+++ b/src/client/qwaylandinputdevice.cpp
|
|
@@ -310,8 +310,7 @@ void QWaylandInputDevice::Pointer::updateCursor()
|
|
auto shape = seat()->mCursor.shape;
|
|
|
|
if (shape == Qt::BlankCursor) {
|
|
- if (mCursor.surface)
|
|
- mCursor.surface->hide();
|
|
+ getOrCreateCursorSurface()->hide();
|
|
return;
|
|
}
|
|
|
|
@@ -1304,14 +1303,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();
|
|
}
|
|
@@ -1400,6 +1391,7 @@ void QWaylandInputDevice::Touch::touch_cancel()
|
|
if (touchExt)
|
|
touchExt->touchCanceled();
|
|
|
|
+ mFocus = nullptr;
|
|
QWindowSystemInterface::handleTouchCancelEvent(nullptr, mParent->mTouchDevice);
|
|
}
|
|
|
|
diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp
|
|
index d257e2e33..548616005 100644
|
|
--- a/src/client/qwaylandintegration.cpp
|
|
+++ b/src/client/qwaylandintegration.cpp
|
|
@@ -125,6 +125,9 @@ QWaylandIntegration::QWaylandIntegration()
|
|
#endif
|
|
|
|
reconfigureInputContext();
|
|
+
|
|
+ QWaylandWindow::fixedToplevelPositions =
|
|
+ !qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_FIXED_POSITIONS");
|
|
}
|
|
|
|
QWaylandIntegration::~QWaylandIntegration()
|
|
@@ -192,14 +195,18 @@ QAbstractEventDispatcher *QWaylandIntegration::createEventDispatcher() const
|
|
|
|
void QWaylandIntegration::initialize()
|
|
{
|
|
+ mDisplay->initEventThread();
|
|
+
|
|
+ // 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()));
|
|
|
|
- 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()));
|
|
-
|
|
// Qt does not support running with no screens
|
|
mDisplay->ensureScreen();
|
|
}
|
|
@@ -262,6 +269,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 ff70ae25d..73b806588 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;
|
|
diff --git a/src/client/qwaylandprimaryselectionv1.cpp b/src/client/qwaylandprimaryselectionv1.cpp
|
|
index 7805dd734..dac532b20 100644
|
|
--- a/src/client/qwaylandprimaryselectionv1.cpp
|
|
+++ b/src/client/qwaylandprimaryselectionv1.cpp
|
|
@@ -54,11 +54,6 @@ QWaylandPrimarySelectionDeviceManagerV1::QWaylandPrimarySelectionDeviceManagerV1
|
|
: zwp_primary_selection_device_manager_v1(display->wl_registry(), id, qMin(version, uint(1)))
|
|
, m_display(display)
|
|
{
|
|
- // Create devices for all seats.
|
|
- // This only works if we get the global before all devices
|
|
- const auto seats = m_display->inputDevices();
|
|
- for (auto *seat : seats)
|
|
- seat->setPrimarySelectionDevice(createDevice(seat));
|
|
}
|
|
|
|
QWaylandPrimarySelectionDeviceV1 *QWaylandPrimarySelectionDeviceManagerV1::createDevice(QWaylandInputDevice *seat)
|
|
diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp
|
|
index 6cb337de3..5537dafd0 100644
|
|
--- a/src/client/qwaylandscreen.cpp
|
|
+++ b/src/client/qwaylandscreen.cpp
|
|
@@ -60,7 +60,7 @@ QWaylandXdgOutputManagerV1::QWaylandXdgOutputManagerV1(QWaylandDisplay* display,
|
|
}
|
|
|
|
QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id)
|
|
- : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 2))
|
|
+ : QtWayland::wl_output(waylandDisplay->wl_registry(), id, qMin(version, 3))
|
|
, m_outputId(id)
|
|
, mWaylandDisplay(waylandDisplay)
|
|
, mOutputName(QStringLiteral("Screen%1").arg(id))
|
|
@@ -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();
|
|
}
|
|
}
|
|
@@ -81,16 +81,29 @@ QWaylandScreen::~QWaylandScreen()
|
|
{
|
|
if (zxdg_output_v1::isInitialized())
|
|
zxdg_output_v1::destroy();
|
|
+ if (wl_output::isInitialized() && wl_output_get_version(wl_output::object()) >= WL_OUTPUT_RELEASE_SINCE_VERSION)
|
|
+ wl_output::release();
|
|
+}
|
|
+
|
|
+uint QWaylandScreen::requiredEvents() const
|
|
+{
|
|
+ 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 +289,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 +351,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 +360,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 df1c94f24..050cfdc06 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)
|
|
diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp
|
|
index dc7ff6708..145f933b7 100644
|
|
--- a/src/client/qwaylandshmbackingstore.cpp
|
|
+++ b/src/client/qwaylandshmbackingstore.cpp
|
|
@@ -52,6 +52,7 @@
|
|
|
|
#include <QtWaylandClient/private/wayland-wayland-client-protocol.h>
|
|
|
|
+#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
|
|
@@ -61,6 +62,9 @@
|
|
# ifndef MFD_CLOEXEC
|
|
# define MFD_CLOEXEC 0x0001U
|
|
# endif
|
|
+# ifndef MFD_ALLOW_SEALING
|
|
+# define MFD_ALLOW_SEALING 0x0002U
|
|
+# endif
|
|
#endif
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
@@ -68,14 +72,16 @@ QT_BEGIN_NAMESPACE
|
|
namespace QtWaylandClient {
|
|
|
|
QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display,
|
|
- const QSize &size, QImage::Format format, int scale)
|
|
+ const QSize &size, QImage::Format format, qreal scale)
|
|
{
|
|
int stride = size.width() * 4;
|
|
int alloc = stride * size.height();
|
|
int fd = -1;
|
|
|
|
-#ifdef SYS_memfd_create
|
|
- fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC);
|
|
+#if defined(SYS_memfd_create) && defined(F_SEAL_SEAL)
|
|
+ fd = syscall(SYS_memfd_create, "wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING);
|
|
+ if (fd >= 0)
|
|
+ fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);
|
|
#endif
|
|
|
|
QScopedPointer<QFile> filePointer;
|
|
@@ -108,7 +114,7 @@ QWaylandShmBuffer::QWaylandShmBuffer(QWaylandDisplay *display,
|
|
QWaylandShm* shm = display->shm();
|
|
wl_shm_format wl_format = shm->formatFrom(format);
|
|
mImage = QImage(data, size.width(), size.height(), stride, format);
|
|
- mImage.setDevicePixelRatio(qreal(scale));
|
|
+ mImage.setDevicePixelRatio(scale);
|
|
|
|
mShmPool = wl_shm_create_pool(shm->object(), fd, alloc);
|
|
init(wl_shm_pool_create_buffer(mShmPool,0, size.width(), size.height(),
|
|
@@ -180,8 +186,6 @@ void QWaylandShmBackingStore::beginPaint(const QRegion ®ion)
|
|
mPainting = true;
|
|
ensureSize();
|
|
|
|
- waylandWindow()->setCanResize(false);
|
|
-
|
|
if (mBackBuffer->image()->hasAlphaChannel()) {
|
|
QPainter p(paintDevice());
|
|
p.setCompositionMode(QPainter::CompositionMode_Source);
|
|
@@ -196,7 +200,6 @@ void QWaylandShmBackingStore::endPaint()
|
|
mPainting = false;
|
|
if (mPendingFlush)
|
|
flush(window(), mPendingRegion, QPoint());
|
|
- waylandWindow()->setCanResize(true);
|
|
}
|
|
|
|
void QWaylandShmBackingStore::ensureSize()
|
|
@@ -271,7 +274,7 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size)
|
|
void QWaylandShmBackingStore::resize(const QSize &size)
|
|
{
|
|
QMargins margins = windowDecorationMargins();
|
|
- int scale = waylandWindow()->scale();
|
|
+ qreal scale = waylandWindow()->scale();
|
|
QSize sizeWithMargins = (size + QSize(margins.left()+margins.right(),margins.top()+margins.bottom())) * scale;
|
|
|
|
// We look for a free buffer to draw into. If the buffer is not the last buffer we used,
|
|
diff --git a/src/client/qwaylandshmbackingstore_p.h b/src/client/qwaylandshmbackingstore_p.h
|
|
index e01632daf..f3fae4380 100644
|
|
--- a/src/client/qwaylandshmbackingstore_p.h
|
|
+++ b/src/client/qwaylandshmbackingstore_p.h
|
|
@@ -71,7 +71,7 @@ class QWaylandWindow;
|
|
class Q_WAYLAND_CLIENT_EXPORT QWaylandShmBuffer : public QWaylandBuffer {
|
|
public:
|
|
QWaylandShmBuffer(QWaylandDisplay *display,
|
|
- const QSize &size, QImage::Format format, int scale = 1);
|
|
+ const QSize &size, QImage::Format format, qreal scale = 1);
|
|
~QWaylandShmBuffer() override;
|
|
QSize size() const override { return mImage.size(); }
|
|
int scale() const override { return int(mImage.devicePixelRatio()); }
|
|
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
|
|
index d57094a71..7a9bccc16 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,9 +94,6 @@ QWaylandWindow::QWaylandWindow(QWindow *window, QWaylandDisplay *display)
|
|
|
|
QWaylandWindow::~QWaylandWindow()
|
|
{
|
|
- mDisplay->destroyFrameQueue(mFrameQueue);
|
|
- mDisplay->handleWindowDestroyed(this);
|
|
-
|
|
delete mWindowDecoration;
|
|
|
|
if (mSurface)
|
|
@@ -189,7 +185,7 @@ void QWaylandWindow::initWindow()
|
|
// typically be integer 1 (normal-dpi) or 2 (high-dpi). Call set_buffer_scale()
|
|
// to inform the compositor that high-resolution buffers will be provided.
|
|
if (mDisplay->compositorVersion() >= 3)
|
|
- mSurface->set_buffer_scale(scale());
|
|
+ mSurface->set_buffer_scale(mScale);
|
|
|
|
if (QScreen *s = window()->screen())
|
|
setOrientationMask(s->orientationUpdateMask());
|
|
@@ -204,6 +200,8 @@ void QWaylandWindow::initWindow()
|
|
mShellSurface->requestWindowStates(window()->windowStates());
|
|
handleContentOrientationChange(window()->contentOrientation());
|
|
mFlags = window()->flags();
|
|
+
|
|
+ mSurface->commit();
|
|
}
|
|
|
|
void QWaylandWindow::initializeWlSurface()
|
|
@@ -243,6 +241,7 @@ bool QWaylandWindow::shouldCreateSubSurface() const
|
|
|
|
void QWaylandWindow::reset()
|
|
{
|
|
+ closeChildPopups();
|
|
delete mShellSurface;
|
|
mShellSurface = nullptr;
|
|
delete mSubSurfaceWindow;
|
|
@@ -255,17 +254,22 @@ void QWaylandWindow::reset()
|
|
mSurface.reset();
|
|
}
|
|
|
|
- if (mFrameCallback) {
|
|
- wl_callback_destroy(mFrameCallback);
|
|
- mFrameCallback = nullptr;
|
|
- }
|
|
+ {
|
|
+ QMutexLocker lock(&mFrameSyncMutex);
|
|
+ if (mFrameCallback) {
|
|
+ wl_callback_destroy(mFrameCallback);
|
|
+ mFrameCallback = nullptr;
|
|
+ }
|
|
|
|
- mFrameCallbackElapsedTimer.invalidate();
|
|
- mWaitingForFrameCallback = false;
|
|
+ mFrameCallbackElapsedTimer.invalidate();
|
|
+ mWaitingForFrameCallback = false;
|
|
+ }
|
|
mFrameCallbackTimedOut = false;
|
|
|
|
mMask = QRegion();
|
|
mQueuedBuffer = nullptr;
|
|
+
|
|
+ mDisplay->handleWindowDestroyed(this);
|
|
}
|
|
|
|
QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface)
|
|
@@ -351,19 +355,25 @@ void QWaylandWindow::setGeometry_helper(const QRect &rect)
|
|
}
|
|
}
|
|
|
|
-void QWaylandWindow::setGeometry(const QRect &rect)
|
|
+void QWaylandWindow::setGeometry(const QRect &r)
|
|
{
|
|
+ auto rect = r;
|
|
+ if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup
|
|
+ && window()->type() != Qt::ToolTip) {
|
|
+ rect.moveTo(screen()->geometry().topLeft());
|
|
+ }
|
|
setGeometry_helper(rect);
|
|
|
|
if (window()->isVisible() && rect.isValid()) {
|
|
if (mWindowDecoration)
|
|
mWindowDecoration->update();
|
|
|
|
- if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize)
|
|
+ if (mResizeAfterSwap && windowType() == Egl && mSentInitialResize) {
|
|
+ QMutexLocker lock(&mResizeLock);
|
|
mResizeDirty = true;
|
|
- else
|
|
+ } else {
|
|
QWindowSystemInterface::handleGeometryChange(window(), geometry());
|
|
-
|
|
+ }
|
|
mSentInitialResize = true;
|
|
}
|
|
QRect exposeGeometry(QPoint(), geometry().size());
|
|
@@ -374,7 +384,7 @@ void QWaylandWindow::setGeometry(const QRect &rect)
|
|
mShellSurface->setWindowGeometry(windowContentGeometry());
|
|
|
|
if (isOpaque() && mMask.isEmpty())
|
|
- setOpaqueArea(rect);
|
|
+ setOpaqueArea(QRect(QPoint(0, 0), rect.size()));
|
|
}
|
|
|
|
void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset)
|
|
@@ -399,21 +409,6 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect)
|
|
mLastExposeGeometry = rect;
|
|
}
|
|
|
|
-
|
|
-static QVector<QPointer<QWaylandWindow>> activePopups;
|
|
-
|
|
-void QWaylandWindow::closePopups(QWaylandWindow *parent)
|
|
-{
|
|
- while (!activePopups.isEmpty()) {
|
|
- auto popup = activePopups.takeLast();
|
|
- if (popup.isNull())
|
|
- continue;
|
|
- if (popup.data() == parent)
|
|
- return;
|
|
- popup->reset();
|
|
- }
|
|
-}
|
|
-
|
|
QPlatformScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
|
|
{
|
|
QReadLocker lock(&mSurfaceLock);
|
|
@@ -433,10 +428,7 @@ void QWaylandWindow::setVisible(bool visible)
|
|
lastVisible = visible;
|
|
|
|
if (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
|
|
@@ -444,7 +436,6 @@ void QWaylandWindow::setVisible(bool visible)
|
|
// QWaylandShmBackingStore::beginPaint().
|
|
} else {
|
|
sendExposeEvent(QRect());
|
|
- closePopups(this);
|
|
reset();
|
|
}
|
|
}
|
|
@@ -487,8 +478,6 @@ void QWaylandWindow::setMask(const QRegion &mask)
|
|
if (isOpaque())
|
|
setOpaqueArea(mMask);
|
|
}
|
|
-
|
|
- mSurface->commit();
|
|
}
|
|
|
|
void QWaylandWindow::applyConfigureWhenPossible()
|
|
@@ -556,12 +545,12 @@ void QWaylandWindow::sendRecursiveExposeEvent()
|
|
|
|
void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
|
|
{
|
|
- Q_ASSERT(!buffer->committed());
|
|
QReadLocker locker(&mSurfaceLock);
|
|
if (mSurface == nullptr)
|
|
return;
|
|
|
|
if (buffer) {
|
|
+ Q_ASSERT(!buffer->committed());
|
|
handleUpdate();
|
|
buffer->setBusy();
|
|
|
|
@@ -583,7 +572,16 @@ void QWaylandWindow::damage(const QRect &rect)
|
|
if (mSurface == nullptr)
|
|
return;
|
|
|
|
- mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
|
|
+ const qreal s = scale();
|
|
+ if (mDisplay->compositorVersion() >= 4) {
|
|
+ const QRect bufferRect =
|
|
+ QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
|
|
+ .toAlignedRect();
|
|
+ mSurface->damage_buffer(bufferRect.x(), bufferRect.y(), bufferRect.width(),
|
|
+ bufferRect.height());
|
|
+ } else {
|
|
+ mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
|
|
+ }
|
|
}
|
|
|
|
void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage)
|
|
@@ -619,8 +617,19 @@ void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage)
|
|
return;
|
|
|
|
attachOffset(buffer);
|
|
- for (const QRect &rect: damage)
|
|
- mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
|
|
+ if (mDisplay->compositorVersion() >= 4) {
|
|
+ const qreal s = scale();
|
|
+ for (const QRect &rect : damage) {
|
|
+ const QRect bufferRect =
|
|
+ QRectF(s * rect.x(), s * rect.y(), s * rect.width(), s * rect.height())
|
|
+ .toAlignedRect();
|
|
+ mSurface->damage_buffer(bufferRect.x(), bufferRect.y(), bufferRect.width(),
|
|
+ bufferRect.height());
|
|
+ }
|
|
+ } else {
|
|
+ for (const QRect &rect: damage)
|
|
+ mSurface->damage(rect.x(), rect.y(), rect.width(), rect.height());
|
|
+ }
|
|
Q_ASSERT(!buffer->committed());
|
|
buffer->setCommitted();
|
|
mSurface->commit();
|
|
@@ -635,42 +644,53 @@ 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);
|
|
- window->handleFrameCallback();
|
|
+ window->handleFrameCallback(callback);
|
|
}
|
|
};
|
|
|
|
-void QWaylandWindow::handleFrameCallback()
|
|
+void QWaylandWindow::handleFrameCallback(wl_callback* callback)
|
|
{
|
|
+ QMutexLocker locker(&mFrameSyncMutex);
|
|
+ if (!mFrameCallback) {
|
|
+ // This means the callback is already unset by QWaylandWindow::reset.
|
|
+ // The wl_callback object will be destroyed there too.
|
|
+ return;
|
|
+ }
|
|
+ Q_ASSERT(callback == mFrameCallback);
|
|
+ wl_callback_destroy(callback);
|
|
+ mFrameCallback = nullptr;
|
|
+
|
|
mWaitingForFrameCallback = false;
|
|
mFrameCallbackElapsedTimer.invalidate();
|
|
|
|
// The rest can wait until we can run it on the correct thread
|
|
- if (!mWaitingForUpdateDelivery) {
|
|
- auto doHandleExpose = [this]() {
|
|
- bool wasExposed = isExposed();
|
|
- mFrameCallbackTimedOut = false;
|
|
- if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed?
|
|
- sendExposeEvent(QRect(QPoint(), geometry().size()));
|
|
- if (wasExposed && hasPendingUpdateRequest())
|
|
- deliverUpdateRequest();
|
|
-
|
|
- mWaitingForUpdateDelivery = false;
|
|
- };
|
|
+ auto doHandleExpose = [this]() {
|
|
+ mWaitingForUpdateDelivery.storeRelease(false);
|
|
+ bool wasExposed = isExposed();
|
|
+ mFrameCallbackTimedOut = false;
|
|
+ if (!wasExposed && isExposed()) // Did setting mFrameCallbackTimedOut make the window exposed?
|
|
+ sendExposeEvent(QRect(QPoint(), geometry().size()));
|
|
+ if (wasExposed && hasPendingUpdateRequest())
|
|
+ deliverUpdateRequest();
|
|
+ };
|
|
|
|
+ if (mWaitingForUpdateDelivery.testAndSetAcquire(false, true)) {
|
|
// Queued connection, to make sure we don't call handleUpdate() from inside waitForFrameSync()
|
|
// in the single-threaded case.
|
|
- mWaitingForUpdateDelivery = true;
|
|
QMetaObject::invokeMethod(this, doHandleExpose, Qt::QueuedConnection);
|
|
}
|
|
+
|
|
+ mFrameSyncWait.notify_all();
|
|
}
|
|
|
|
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";
|
|
@@ -772,8 +792,6 @@ void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orient
|
|
Q_UNREACHABLE();
|
|
}
|
|
mSurface->set_buffer_transform(transform);
|
|
- // set_buffer_transform is double buffered, we need to commit.
|
|
- mSurface->commit();
|
|
}
|
|
|
|
void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask)
|
|
@@ -1032,8 +1050,17 @@ void QWaylandWindow::handleScreensChanged()
|
|
if (newScreen == mLastReportedScreen)
|
|
return;
|
|
|
|
+ if (!newScreen->isPlaceholder() && !newScreen->QPlatformScreen::screen())
|
|
+ mDisplay->forceRoundTrip();
|
|
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
|
|
mLastReportedScreen = newScreen;
|
|
+ if (fixedToplevelPositions && !QPlatformWindow::parent() && window()->type() != Qt::Popup
|
|
+ && window()->type() != Qt::ToolTip
|
|
+ && geometry().topLeft() != newScreen->geometry().topLeft()) {
|
|
+ auto geometry = this->geometry();
|
|
+ geometry.moveTo(newScreen->geometry().topLeft());
|
|
+ setGeometry(geometry);
|
|
+ }
|
|
|
|
int scale = newScreen->isPlaceholder() ? 1 : static_cast<QWaylandScreen *>(newScreen)->scale();
|
|
if (scale != mScale) {
|
|
@@ -1087,14 +1114,14 @@ bool QWaylandWindow::isActive() const
|
|
return mDisplay->isWindowActivated(this);
|
|
}
|
|
|
|
-int QWaylandWindow::scale() const
|
|
+qreal QWaylandWindow::scale() const
|
|
{
|
|
- return mScale;
|
|
+ return devicePixelRatio();
|
|
}
|
|
|
|
qreal QWaylandWindow::devicePixelRatio() const
|
|
{
|
|
- return mScale;
|
|
+ return qreal(mScale);
|
|
}
|
|
|
|
bool QWaylandWindow::setMouseGrabEnabled(bool grab)
|
|
@@ -1108,10 +1135,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;
|
|
}
|
|
|
|
@@ -1153,19 +1188,24 @@ void QWaylandWindow::timerEvent(QTimerEvent *event)
|
|
if (event->timerId() != mFrameCallbackCheckIntervalTimerId)
|
|
return;
|
|
|
|
- bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout);
|
|
- if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) {
|
|
- killTimer(mFrameCallbackCheckIntervalTimerId);
|
|
- mFrameCallbackCheckIntervalTimerId = -1;
|
|
- }
|
|
- if (mFrameCallbackElapsedTimer.isValid() && callbackTimerExpired) {
|
|
- mFrameCallbackElapsedTimer.invalidate();
|
|
+ {
|
|
+ QMutexLocker lock(&mFrameSyncMutex);
|
|
|
|
- qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
|
|
- mFrameCallbackTimedOut = true;
|
|
- mWaitingForUpdate = false;
|
|
- sendExposeEvent(QRect());
|
|
+ bool callbackTimerExpired = mFrameCallbackElapsedTimer.hasExpired(mFrameCallbackTimeout);
|
|
+ if (!mFrameCallbackElapsedTimer.isValid() || callbackTimerExpired ) {
|
|
+ killTimer(mFrameCallbackCheckIntervalTimerId);
|
|
+ mFrameCallbackCheckIntervalTimerId = -1;
|
|
+ }
|
|
+ if (!mFrameCallbackElapsedTimer.isValid() || !callbackTimerExpired) {
|
|
+ return;
|
|
+ }
|
|
+ mFrameCallbackElapsedTimer.invalidate();
|
|
}
|
|
+
|
|
+ qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
|
|
+ mFrameCallbackTimedOut = true;
|
|
+ mWaitingForUpdate = false;
|
|
+ sendExposeEvent(QRect());
|
|
}
|
|
|
|
void QWaylandWindow::requestUpdate()
|
|
@@ -1174,8 +1214,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
|
|
@@ -1188,7 +1231,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);
|
|
}
|
|
@@ -1199,19 +1247,18 @@ void QWaylandWindow::requestUpdate()
|
|
void QWaylandWindow::handleUpdate()
|
|
{
|
|
qCDebug(lcWaylandBackingstore) << "handleUpdate" << QThread::currentThread();
|
|
+
|
|
// TODO: Should sync subsurfaces avoid requesting frame callbacks?
|
|
QReadLocker lock(&mSurfaceLock);
|
|
if (!mSurface)
|
|
return;
|
|
|
|
- if (mFrameCallback) {
|
|
- wl_callback_destroy(mFrameCallback);
|
|
- mFrameCallback = nullptr;
|
|
- }
|
|
+ QMutexLocker locker(&mFrameSyncMutex);
|
|
+ if (mWaitingForFrameCallback)
|
|
+ return;
|
|
|
|
- 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);
|
|
+ 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);
|
|
@@ -1221,6 +1268,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);
|
|
@@ -1281,6 +1330,20 @@ void QWaylandWindow::setOpaqueArea(const QRegion &opaqueArea)
|
|
wl_region_destroy(region);
|
|
}
|
|
|
|
+void QWaylandWindow::addChildPopup(QWaylandWindow *surface) {
|
|
+ mChildPopups.append(surface);
|
|
+}
|
|
+
|
|
+void QWaylandWindow::removeChildPopup(QWaylandWindow *surface) {
|
|
+ mChildPopups.removeAll(surface);
|
|
+}
|
|
+
|
|
+void QWaylandWindow::closeChildPopups() {
|
|
+ while (!mChildPopups.isEmpty()) {
|
|
+ auto popup = mChildPopups.takeLast();
|
|
+ popup->reset();
|
|
+ }
|
|
+}
|
|
}
|
|
|
|
QT_END_NAMESPACE
|
|
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
|
|
index 01337cff1..741f9e5cc 100644
|
|
--- a/src/client/qwaylandwindow_p.h
|
|
+++ b/src/client/qwaylandwindow_p.h
|
|
@@ -98,6 +98,9 @@ public:
|
|
QWaylandWindow(QWindow *window, QWaylandDisplay *display);
|
|
~QWaylandWindow() override;
|
|
|
|
+ // Keep Toplevels position on the top left corner of their screen
|
|
+ static inline bool fixedToplevelPositions = true;
|
|
+
|
|
virtual WindowType windowType() const = 0;
|
|
virtual void ensureSize();
|
|
WId winId() const override;
|
|
@@ -148,13 +151,14 @@ 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;
|
|
|
|
void setMask(const QRegion ®ion) override;
|
|
|
|
- int scale() const;
|
|
+ qreal scale() const;
|
|
qreal devicePixelRatio() const override;
|
|
|
|
void requestActivateWindow() override;
|
|
@@ -206,6 +210,10 @@ public:
|
|
void handleUpdate();
|
|
void deliverUpdateRequest() override;
|
|
|
|
+ void addChildPopup(QWaylandWindow* child);
|
|
+ void removeChildPopup(QWaylandWindow* child);
|
|
+ void closeChildPopups();
|
|
+
|
|
public slots:
|
|
void applyConfigure();
|
|
|
|
@@ -215,7 +223,11 @@ signals:
|
|
|
|
protected:
|
|
QWaylandDisplay *mDisplay = nullptr;
|
|
+
|
|
+ // mSurface can be written by the main thread. Other threads should claim a read lock for access
|
|
+ mutable QReadWriteLock mSurfaceLock;
|
|
QScopedPointer<QWaylandSurface> mSurface;
|
|
+
|
|
QWaylandShellSurface *mShellSurface = nullptr;
|
|
QWaylandSubSurface *mSubSurfaceWindow = nullptr;
|
|
QVector<QWaylandSubSurface *> mChildren;
|
|
@@ -225,13 +237,14 @@ protected:
|
|
Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton;
|
|
|
|
WId mWindowId;
|
|
- bool mWaitingForFrameCallback = false;
|
|
bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out
|
|
- bool mWaitingForUpdateDelivery = false;
|
|
int mFrameCallbackCheckIntervalTimerId = -1;
|
|
- QElapsedTimer mFrameCallbackElapsedTimer;
|
|
- struct ::wl_callback *mFrameCallback = nullptr;
|
|
- QWaylandDisplay::FrameQueue mFrameQueue;
|
|
+ QAtomicInt mWaitingForUpdateDelivery = false;
|
|
+
|
|
+ bool mWaitingForFrameCallback = false; // Protected by mFrameSyncMutex
|
|
+ QElapsedTimer mFrameCallbackElapsedTimer; // Protected by mFrameSyncMutex
|
|
+ struct ::wl_callback *mFrameCallback = nullptr; // Protected by mFrameSyncMutex
|
|
+ QMutex mFrameSyncMutex;
|
|
QWaitCondition mFrameSyncWait;
|
|
|
|
// True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer
|
|
@@ -261,6 +274,8 @@ protected:
|
|
QWaylandBuffer *mQueuedBuffer = nullptr;
|
|
QRegion mQueuedBufferDamage;
|
|
|
|
+ QList<QPointer<QWaylandWindow>> mChildPopups;
|
|
+
|
|
private:
|
|
void setGeometry_helper(const QRect &rect);
|
|
void initWindow();
|
|
@@ -283,12 +298,10 @@ private:
|
|
QRect mLastExposeGeometry;
|
|
|
|
static const wl_callback_listener callbackListener;
|
|
- void handleFrameCallback();
|
|
+ void handleFrameCallback(struct ::wl_callback* callback);
|
|
|
|
static QWaylandWindow *mMouseGrab;
|
|
|
|
- mutable QReadWriteLock mSurfaceLock;
|
|
-
|
|
friend class QWaylandSubSurface;
|
|
};
|
|
|
|
diff --git a/src/client/shellintegration/qwaylandshellintegration_p.h b/src/client/shellintegration/qwaylandshellintegration_p.h
|
|
index ccad00481..4cc9b3b84 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/compositor/configure.json b/src/compositor/configure.json
|
|
index c5b0f03ed..031e4cc3a 100644
|
|
--- a/src/compositor/configure.json
|
|
+++ b/src/compositor/configure.json
|
|
@@ -7,6 +7,31 @@
|
|
"testDir": "../../config.tests",
|
|
|
|
"libraries": {
|
|
+ "wayland-client": {
|
|
+ "label": "Wayland client library",
|
|
+ "headers": "wayland-version.h",
|
|
+ "test": {
|
|
+ "main": [
|
|
+ "#if WAYLAND_VERSION_MAJOR < 1",
|
|
+ "# error Wayland 1.8.0 or higher required",
|
|
+ "#endif",
|
|
+ "#if WAYLAND_VERSION_MAJOR == 1",
|
|
+ "# if WAYLAND_VERSION_MINOR < 8",
|
|
+ "# error Wayland 1.8.0 or higher required",
|
|
+ "# endif",
|
|
+ "# if WAYLAND_VERSION_MINOR == 8",
|
|
+ "# if WAYLAND_VERSION_MICRO < 0",
|
|
+ "# error Wayland 1.8.0 or higher required",
|
|
+ "# endif",
|
|
+ "# endif",
|
|
+ "#endif"
|
|
+ ]
|
|
+ },
|
|
+ "sources": [
|
|
+ { "type": "pkgConfig", "args": "wayland-client" },
|
|
+ "-lwayland-client"
|
|
+ ]
|
|
+ },
|
|
"wayland-server": {
|
|
"label": "wayland-server",
|
|
"headers": "wayland-version.h",
|
|
@@ -193,7 +218,8 @@
|
|
"exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR;",
|
|
"return 0;"
|
|
]
|
|
- }
|
|
+ },
|
|
+ "use": "wayland-client"
|
|
}
|
|
},
|
|
|
|
diff --git a/src/compositor/wayland_wrapper/qwldatadevice.cpp b/src/compositor/wayland_wrapper/qwldatadevice.cpp
|
|
index a3a795f9b..f301678e2 100644
|
|
--- a/src/compositor/wayland_wrapper/qwldatadevice.cpp
|
|
+++ b/src/compositor/wayland_wrapper/qwldatadevice.cpp
|
|
@@ -76,6 +76,9 @@ void DataDevice::sourceDestroyed(DataSource *source)
|
|
{
|
|
if (m_selectionSource == source)
|
|
m_selectionSource = nullptr;
|
|
+
|
|
+ if (m_dragDataSource == source)
|
|
+ m_dragDataSource = nullptr;
|
|
}
|
|
|
|
#if QT_CONFIG(draganddrop)
|
|
@@ -105,9 +108,11 @@ void DataDevice::setDragFocus(QWaylandSurface *focus, const QPointF &localPositi
|
|
if (m_dragDataSource && !offer)
|
|
return;
|
|
|
|
- send_enter(resource->handle, serial, focus->resource(),
|
|
- wl_fixed_from_double(localPosition.x()), wl_fixed_from_double(localPosition.y()),
|
|
- offer->resource()->handle);
|
|
+ if (offer) {
|
|
+ send_enter(resource->handle, serial, focus->resource(),
|
|
+ wl_fixed_from_double(localPosition.x()), wl_fixed_from_double(localPosition.y()),
|
|
+ offer->resource()->handle);
|
|
+ }
|
|
|
|
m_dragFocus = focus;
|
|
m_dragFocusResource = resource;
|
|
@@ -139,7 +144,7 @@ void DataDevice::drop()
|
|
if (m_dragFocusResource) {
|
|
send_drop(m_dragFocusResource->handle);
|
|
setDragFocus(nullptr, QPoint());
|
|
- } else {
|
|
+ } else if (m_dragDataSource) {
|
|
m_dragDataSource->cancel();
|
|
}
|
|
m_dragOrigin = nullptr;
|
|
@@ -155,6 +160,8 @@ void DataDevice::data_device_start_drag(Resource *resource, struct ::wl_resource
|
|
{
|
|
m_dragClient = resource->client();
|
|
m_dragDataSource = source ? DataSource::fromResource(source) : nullptr;
|
|
+ if (m_dragDataSource)
|
|
+ m_dragDataSource->setDevice(this);
|
|
m_dragOrigin = QWaylandSurface::fromResource(origin);
|
|
QWaylandDrag *drag = m_seat->drag();
|
|
setDragIcon(icon ? QWaylandSurface::fromResource(icon) : nullptr);
|
|
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp
|
|
index e00c28c3b..95e8c666c 100644
|
|
--- a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp
|
|
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.cpp
|
|
@@ -40,6 +40,7 @@
|
|
#include "qwaylandeglwindow.h"
|
|
|
|
#include <QtWaylandClient/private/qwaylandscreen_p.h>
|
|
+#include <QtWaylandClient/private/qwaylandsurface_p.h>
|
|
#include "qwaylandglcontext.h"
|
|
|
|
#include <QtEglSupport/private/qeglconvenience_p.h>
|
|
@@ -115,21 +116,25 @@ void QWaylandEglWindow::updateSurface(bool create)
|
|
}
|
|
mOffset = QPoint();
|
|
} else {
|
|
+ QReadLocker locker(&mSurfaceLock);
|
|
if (m_waylandEglWindow) {
|
|
- int current_width, current_height;
|
|
+ int current_width = 0;
|
|
+ int current_height = 0;
|
|
static bool disableResizeCheck = qgetenv("QT_WAYLAND_DISABLE_RESIZECHECK").toInt();
|
|
|
|
if (!disableResizeCheck) {
|
|
wl_egl_window_get_attached_size(m_waylandEglWindow, ¤t_width, ¤t_height);
|
|
}
|
|
- if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height())) {
|
|
+ if (disableResizeCheck || (current_width != sizeWithMargins.width() || current_height != sizeWithMargins.height()) || m_requestedSize != sizeWithMargins) {
|
|
wl_egl_window_resize(m_waylandEglWindow, sizeWithMargins.width(), sizeWithMargins.height(), mOffset.x(), mOffset.y());
|
|
+ m_requestedSize = sizeWithMargins;
|
|
mOffset = QPoint();
|
|
|
|
m_resize = true;
|
|
}
|
|
- } else if (create && wlSurface()) {
|
|
- m_waylandEglWindow = wl_egl_window_create(wlSurface(), sizeWithMargins.width(), sizeWithMargins.height());
|
|
+ } else if (create && mSurface) {
|
|
+ m_waylandEglWindow = wl_egl_window_create(mSurface->object(), 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 2fccbcea5..ad1e5ee9f 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;
|
|
};
|
|
|
|
}
|
|
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
|
|
index c1f45fa69..bbc634445 100644
|
|
--- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
|
|
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
|
|
@@ -195,7 +195,7 @@ public:
|
|
QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context());
|
|
|
|
QSize surfaceSize = window->surfaceSize();
|
|
- int scale = window->scale() ;
|
|
+ qreal scale = window->scale() ;
|
|
glViewport(0, 0, surfaceSize.width() * scale, surfaceSize.height() * scale);
|
|
|
|
//Draw Decoration
|
|
diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp
|
|
index 85d25e3c0..60bdd4911 100644
|
|
--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp
|
|
+++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp
|
|
@@ -47,18 +47,21 @@ QT_BEGIN_NAMESPACE
|
|
|
|
namespace QtWaylandClient {
|
|
|
|
-QWaylandXdgPopupV5::QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow *window)
|
|
+QWaylandXdgPopupV5::QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow* parent, QWaylandWindow *window)
|
|
: QWaylandShellSurface(window)
|
|
, QtWayland::xdg_popup_v5(popup)
|
|
+ , m_parent(parent)
|
|
, m_window(window)
|
|
{
|
|
if (window->display()->windowExtension())
|
|
m_extendedWindow = new QWaylandExtendedSurface(window);
|
|
+ m_parent->addChildPopup(m_window);
|
|
}
|
|
|
|
QWaylandXdgPopupV5::~QWaylandXdgPopupV5()
|
|
{
|
|
xdg_popup_destroy(object());
|
|
+ m_parent->removeChildPopup(m_window);
|
|
delete m_extendedWindow;
|
|
}
|
|
|
|
diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h
|
|
index 7494f6a67..d85f130bc 100644
|
|
--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h
|
|
+++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h
|
|
@@ -70,7 +70,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgPopupV5 : public QWaylandShellSurface
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
- QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow *window);
|
|
+ QWaylandXdgPopupV5(struct ::xdg_popup_v5 *popup, QWaylandWindow* parent, QWaylandWindow *window);
|
|
~QWaylandXdgPopupV5() override;
|
|
|
|
protected:
|
|
@@ -78,6 +78,7 @@ protected:
|
|
|
|
private:
|
|
QWaylandExtendedSurface *m_extendedWindow = nullptr;
|
|
+ QWaylandWindow *m_parent = nullptr;
|
|
QWaylandWindow *m_window = nullptr;
|
|
};
|
|
|
|
diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp
|
|
index 7e242c4a5..def8452a1 100644
|
|
--- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp
|
|
+++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp
|
|
@@ -84,7 +84,7 @@ QWaylandXdgPopupV5 *QWaylandXdgShellV5::createXdgPopup(QWaylandWindow *window, Q
|
|
int x = position.x() + parentWindow->frameMargins().left();
|
|
int y = position.y() + parentWindow->frameMargins().top();
|
|
|
|
- auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->wlSurface(), parentSurface, seat, m_popupSerial, x, y), window);
|
|
+ auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->wlSurface(), parentSurface, seat, m_popupSerial, x, y), parentWindow, window);
|
|
m_popups.append(window);
|
|
QObject::connect(popup, &QWaylandXdgPopupV5::destroyed, [this, window](){
|
|
m_popups.removeOne(window);
|
|
diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp
|
|
index 4e25949fa..cfc609394 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 ce6bdb9ee..aed886707 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/qwaylandxdgshellv6.cpp b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
|
|
index 8c371661f..151c78e38 100644
|
|
--- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
|
|
+++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
|
|
@@ -174,6 +174,7 @@ QWaylandXdgSurfaceV6::Popup::Popup(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdg
|
|
, m_xdgSurface(xdgSurface)
|
|
, m_parent(parent)
|
|
{
|
|
+ m_parent->window()->addChildPopup(m_xdgSurface->window());
|
|
}
|
|
|
|
QWaylandXdgSurfaceV6::Popup::~Popup()
|
|
@@ -181,6 +182,8 @@ QWaylandXdgSurfaceV6::Popup::~Popup()
|
|
if (isInitialized())
|
|
destroy();
|
|
|
|
+ m_parent->window()->removeChildPopup(m_xdgSurface->window());
|
|
+
|
|
if (m_grabbing) {
|
|
auto *shell = m_xdgSurface->m_shell;
|
|
Q_ASSERT(shell->m_topmostGrabbingPopup == this);
|
|
diff --git a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6integration.cpp
|
|
index 031643165..e8da8ba12 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 261f8cbb4..c1bcd5c68 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 49e9d9538..9c6cbb81f 100644
|
|
--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
|
|
+++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
|
|
@@ -44,6 +44,7 @@
|
|
#include <QtWaylandClient/private/qwaylandwindow_p.h>
|
|
#include <QtWaylandClient/private/qwaylandinputdevice_p.h>
|
|
#include <QtWaylandClient/private/qwaylandscreen_p.h>
|
|
+#include <QtWaylandClient/private/qwaylandcursor_p.h>
|
|
#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h>
|
|
|
|
#include <QtGui/private/qwindow_p.h>
|
|
@@ -67,11 +68,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 +81,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
|
|
@@ -105,8 +100,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;
|
|
}
|
|
@@ -203,12 +196,17 @@ QtWayland::xdg_toplevel::resize_edge QWaylandXdgSurface::Toplevel::convertToResi
|
|
| ((edges & Qt::RightEdge) ? resize_edge_right : 0));
|
|
}
|
|
|
|
-QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent,
|
|
+QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent,
|
|
QtWayland::xdg_positioner *positioner)
|
|
- : xdg_popup(xdgSurface->get_popup(parent->object(), positioner->object()))
|
|
- , m_xdgSurface(xdgSurface)
|
|
+ : m_xdgSurface(xdgSurface)
|
|
+ , m_parentXdgSurface(qobject_cast<QWaylandXdgSurface *>(parent->shellSurface()))
|
|
, m_parent(parent)
|
|
{
|
|
+
|
|
+ init(xdgSurface->get_popup(m_parentXdgSurface ? m_parentXdgSurface->object() : nullptr, positioner->object()));
|
|
+ if (m_parent) {
|
|
+ m_parent->addChildPopup(m_xdgSurface->window());
|
|
+ }
|
|
}
|
|
|
|
QWaylandXdgSurface::Popup::~Popup()
|
|
@@ -216,10 +214,14 @@ QWaylandXdgSurface::Popup::~Popup()
|
|
if (isInitialized())
|
|
destroy();
|
|
|
|
+ if (m_parent) {
|
|
+ m_parent->removeChildPopup(m_xdgSurface->window());
|
|
+ }
|
|
+
|
|
if (m_grabbing) {
|
|
auto *shell = m_xdgSurface->m_shell;
|
|
Q_ASSERT(shell->m_topmostGrabbingPopup == this);
|
|
- shell->m_topmostGrabbingPopup = m_parent->m_popup;
|
|
+ shell->m_topmostGrabbingPopup = m_parentXdgSurface ? m_parentXdgSurface->m_popup : nullptr;
|
|
m_grabbing = false;
|
|
|
|
// Synthesize Qt enter/leave events for popup
|
|
@@ -228,8 +230,10 @@ QWaylandXdgSurface::Popup::~Popup()
|
|
leave = m_xdgSurface->window()->window();
|
|
QWindowSystemInterface::handleLeaveEvent(leave);
|
|
|
|
- if (QWindow *enter = QGuiApplication::topLevelAt(QCursor::pos()))
|
|
- QWindowSystemInterface::handleEnterEvent(enter, enter->mapFromGlobal(QCursor::pos()), QCursor::pos());
|
|
+ if (QWindow *enter = QGuiApplication::topLevelAt(QCursor::pos())) {
|
|
+ const auto pos = m_xdgSurface->window()->display()->waylandCursor()->pos();
|
|
+ QWindowSystemInterface::handleEnterEvent(enter, enter->handle()->mapFromGlobal(pos), pos);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
@@ -267,6 +271,7 @@ QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *s
|
|
m_toplevel->set_parent(parentXdgSurface->m_toplevel->object());
|
|
}
|
|
}
|
|
+ setSizeHints();
|
|
}
|
|
|
|
QWaylandXdgSurface::~QWaylandXdgSurface()
|
|
@@ -365,9 +370,6 @@ bool QWaylandXdgSurface::wantsDecorations() const
|
|
void QWaylandXdgSurface::propagateSizeHints()
|
|
{
|
|
setSizeHints();
|
|
-
|
|
- if (m_toplevel && m_window)
|
|
- m_window->commit();
|
|
}
|
|
|
|
void QWaylandXdgSurface::setWindowGeometry(const QRect &rect)
|
|
@@ -382,10 +384,10 @@ void QWaylandXdgSurface::setSizeHints()
|
|
const int minHeight = qMax(0, m_window->windowMinimumSize().height());
|
|
m_toplevel->set_min_size(minWidth, minHeight);
|
|
|
|
- int maxWidth = qMax(0, m_window->windowMaximumSize().width());
|
|
+ int maxWidth = qMax(minWidth, m_window->windowMaximumSize().width());
|
|
if (maxWidth == QWINDOWSIZE_MAX)
|
|
maxWidth = 0;
|
|
- int maxHeight = qMax(0, m_window->windowMaximumSize().height());
|
|
+ int maxHeight = qMax(minHeight, m_window->windowMaximumSize().height());
|
|
if (maxHeight == QWINDOWSIZE_MAX)
|
|
maxHeight = 0;
|
|
m_toplevel->set_max_size(maxWidth, maxHeight);
|
|
@@ -410,8 +412,6 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent)
|
|
{
|
|
Q_ASSERT(!m_toplevel && !m_popup);
|
|
|
|
- auto parentXdgSurface = static_cast<QWaylandXdgSurface *>(parent->shellSurface());
|
|
-
|
|
auto positioner = new QtWayland::xdg_positioner(m_shell->create_positioner());
|
|
// set_popup expects a position relative to the parent
|
|
QPoint transientPos = m_window->geometry().topLeft(); // this is absolute
|
|
@@ -426,8 +426,9 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent)
|
|
positioner->set_size(m_window->geometry().width(), m_window->geometry().height());
|
|
positioner->set_constraint_adjustment(QtWayland::xdg_positioner::constraint_adjustment_slide_x
|
|
| QtWayland::xdg_positioner::constraint_adjustment_slide_y);
|
|
- m_popup = new Popup(this, parentXdgSurface, positioner);
|
|
+ m_popup = new Popup(this, parent, positioner);
|
|
positioner->destroy();
|
|
+
|
|
delete positioner;
|
|
}
|
|
|
|
@@ -464,8 +465,10 @@ void QWaylandXdgSurface::setGrabPopup(QWaylandWindow *parent, QWaylandInputDevic
|
|
if (m_popup && m_popup->m_xdgSurface && m_popup->m_xdgSurface->window())
|
|
enter = m_popup->m_xdgSurface->window()->window();
|
|
|
|
- if (enter)
|
|
- QWindowSystemInterface::handleEnterEvent(enter, enter->mapFromGlobal(QCursor::pos()), QCursor::pos());
|
|
+ if (enter) {
|
|
+ const auto pos = m_popup->m_xdgSurface->window()->display()->waylandCursor()->pos();
|
|
+ QWindowSystemInterface::handleEnterEvent(enter, enter->handle()->mapFromGlobal(pos), pos);
|
|
+ }
|
|
}
|
|
|
|
void QWaylandXdgSurface::xdg_surface_configure(uint32_t serial)
|
|
diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h
|
|
index 96785205f..4b518f0a1 100644
|
|
--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h
|
|
+++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h
|
|
@@ -131,14 +131,15 @@ private:
|
|
|
|
class Popup : public QtWayland::xdg_popup {
|
|
public:
|
|
- Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent, QtWayland::xdg_positioner *positioner);
|
|
+ Popup(QWaylandXdgSurface *xdgSurface, QWaylandWindow *parent, QtWayland::xdg_positioner *positioner);
|
|
~Popup() override;
|
|
|
|
void grab(QWaylandInputDevice *seat, uint serial);
|
|
void xdg_popup_popup_done() override;
|
|
|
|
QWaylandXdgSurface *m_xdgSurface = nullptr;
|
|
- QWaylandXdgSurface *m_parent = nullptr;
|
|
+ QWaylandXdgSurface *m_parentXdgSurface = nullptr;
|
|
+ QWaylandWindow *m_parent = nullptr;
|
|
bool m_grabbing = false;
|
|
};
|
|
|
|
diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp
|
|
index 8769d9716..da0dd6a72 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 b6caa6c95..2f929f98a 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/src/shared/qwaylandmimehelper.cpp b/src/shared/qwaylandmimehelper.cpp
|
|
index c5266ab3e..e2fe19282 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;
|
|
}
|
|
diff --git a/tests/auto/client/datadevicev1/tst_datadevicev1.cpp b/tests/auto/client/datadevicev1/tst_datadevicev1.cpp
|
|
index 1568b3b96..067410d08 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:
|
|
diff --git a/tests/auto/client/seatv5/tst_seatv5.cpp b/tests/auto/client/seatv5/tst_seatv5.cpp
|
|
index 9312c2e5d..2ea382f1c 100644
|
|
--- a/tests/auto/client/seatv5/tst_seatv5.cpp
|
|
+++ b/tests/auto/client/seatv5/tst_seatv5.cpp
|
|
@@ -73,6 +73,7 @@ private slots:
|
|
void multiTouch();
|
|
void multiTouchUpAndMotionFrame();
|
|
void tapAndMoveInSameFrame();
|
|
+ void cancelTouch();
|
|
};
|
|
|
|
void tst_seatv5::bindsToSeat()
|
|
@@ -646,5 +647,34 @@ void tst_seatv5::tapAndMoveInSameFrame()
|
|
QTRY_COMPARE(window.m_events.last().touchPoints.first().state(), Qt::TouchPointState::TouchPointReleased);
|
|
}
|
|
|
|
+void tst_seatv5::cancelTouch()
|
|
+{
|
|
+ TouchWindow window;
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
|
+
|
|
+ exec([=] {
|
|
+ auto *t = touch();
|
|
+ auto *c = client();
|
|
+ t->sendDown(xdgToplevel()->surface(), {32, 32}, 1);
|
|
+ t->sendFrame(c);
|
|
+ t->sendCancel(c);
|
|
+ t->sendFrame(c);
|
|
+ });
|
|
+
|
|
+ QTRY_VERIFY(!window.m_events.empty());
|
|
+ {
|
|
+ auto e = window.m_events.takeFirst();
|
|
+ QCOMPARE(e.type, QEvent::TouchBegin);
|
|
+ QCOMPARE(e.touchPointStates, Qt::TouchPointPressed);
|
|
+ QCOMPARE(e.touchPoints.length(), 1);
|
|
+ QCOMPARE(e.touchPoints.first().pos(), QPointF(32-window.frameMargins().left(), 32-window.frameMargins().top()));
|
|
+ }
|
|
+ {
|
|
+ auto e = window.m_events.takeFirst();
|
|
+ QCOMPARE(e.type, QEvent::TouchCancel);
|
|
+ QCOMPARE(e.touchPoints.length(), 0);
|
|
+ }
|
|
+}
|
|
+
|
|
QCOMPOSITOR_TEST_MAIN(tst_seatv5)
|
|
#include "tst_seatv5.moc"
|
|
diff --git a/tests/auto/client/shared/coreprotocol.cpp b/tests/auto/client/shared/coreprotocol.cpp
|
|
index 0d9885216..53e122910 100644
|
|
--- a/tests/auto/client/shared/coreprotocol.cpp
|
|
+++ b/tests/auto/client/shared/coreprotocol.cpp
|
|
@@ -185,6 +185,8 @@ void Output::output_bind_resource(QtWaylandServer::wl_output::Resource *resource
|
|
|
|
if (m_version >= WL_OUTPUT_DONE_SINCE_VERSION)
|
|
wl_output::send_done(resource->handle);
|
|
+
|
|
+ Q_EMIT outputBound(resource);
|
|
}
|
|
|
|
// Seat stuff
|
|
@@ -451,6 +453,13 @@ void Touch::sendFrame(wl_client *client)
|
|
send_frame(r->handle);
|
|
}
|
|
|
|
+void Touch::sendCancel(wl_client *client)
|
|
+{
|
|
+ const auto touchResources = resourceMap().values(client);
|
|
+ for (auto *r : touchResources)
|
|
+ send_cancel(r->handle);
|
|
+}
|
|
+
|
|
uint Keyboard::sendEnter(Surface *surface)
|
|
{
|
|
auto serial = m_seat->m_compositor->nextSerial();
|
|
diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h
|
|
index a1af137a1..00c439e18 100644
|
|
--- a/tests/auto/client/shared/coreprotocol.h
|
|
+++ b/tests/auto/client/shared/coreprotocol.h
|
|
@@ -158,7 +158,7 @@ class WlCompositor : public Global, public QtWaylandServer::wl_compositor
|
|
{
|
|
Q_OBJECT
|
|
public:
|
|
- explicit WlCompositor(CoreCompositor *compositor, int version = 3)
|
|
+ explicit WlCompositor(CoreCompositor *compositor, int version = 4)
|
|
: QtWaylandServer::wl_compositor(compositor->m_display, version)
|
|
, m_compositor(compositor)
|
|
{}
|
|
@@ -273,6 +273,9 @@ public:
|
|
OutputData m_data;
|
|
int m_version = 1; // TODO: remove on libwayland upgrade
|
|
|
|
+Q_SIGNALS:
|
|
+ void outputBound(Resource *resource);
|
|
+
|
|
protected:
|
|
void output_bind_resource(Resource *resource) override;
|
|
};
|
|
@@ -364,6 +367,7 @@ public:
|
|
uint sendUp(wl_client *client, int id);
|
|
void sendMotion(wl_client *client, const QPointF &position, int id);
|
|
void sendFrame(wl_client *client);
|
|
+ void sendCancel(wl_client *client);
|
|
|
|
Seat *m_seat = nullptr;
|
|
};
|
|
diff --git a/tests/auto/client/shared_old/mockcompositor.cpp b/tests/auto/client/shared_old/mockcompositor.cpp
|
|
index a415cbf56..b1d3d07d5 100644
|
|
--- a/tests/auto/client/shared_old/mockcompositor.cpp
|
|
+++ b/tests/auto/client/shared_old/mockcompositor.cpp
|
|
@@ -342,7 +342,7 @@ Compositor::Compositor(MockCompositor *mockCompositor)
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
- wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor);
|
|
+ wl_global_create(m_display, &wl_compositor_interface, 4, this, bindCompositor);
|
|
|
|
m_data_device_manager.reset(new DataDeviceManager(this, m_display));
|
|
|
|
diff --git a/tests/auto/client/shared_old/mocksurface.cpp b/tests/auto/client/shared_old/mocksurface.cpp
|
|
index e9df5f907..c3246e4a8 100644
|
|
--- a/tests/auto/client/shared_old/mocksurface.cpp
|
|
+++ b/tests/auto/client/shared_old/mocksurface.cpp
|
|
@@ -125,6 +125,16 @@ void Surface::surface_damage(Resource *resource,
|
|
Q_UNUSED(height);
|
|
}
|
|
|
|
+void Surface::surface_damage_buffer(Resource *resource,
|
|
+ int32_t x, int32_t y, int32_t width, int32_t height)
|
|
+{
|
|
+ Q_UNUSED(resource);
|
|
+ Q_UNUSED(x);
|
|
+ Q_UNUSED(y);
|
|
+ Q_UNUSED(width);
|
|
+ Q_UNUSED(height);
|
|
+}
|
|
+
|
|
void Surface::surface_frame(Resource *resource,
|
|
uint32_t callback)
|
|
{
|
|
diff --git a/tests/auto/client/shared_old/mocksurface.h b/tests/auto/client/shared_old/mocksurface.h
|
|
index 949dc23dd..d176837e7 100644
|
|
--- a/tests/auto/client/shared_old/mocksurface.h
|
|
+++ b/tests/auto/client/shared_old/mocksurface.h
|
|
@@ -65,6 +65,8 @@ protected:
|
|
struct wl_resource *buffer, int x, int y) override;
|
|
void surface_damage(Resource *resource,
|
|
int32_t x, int32_t y, int32_t width, int32_t height) override;
|
|
+ void surface_damage_buffer(Resource *resource,
|
|
+ int32_t x, int32_t y, int32_t width, int32_t height) override;
|
|
void surface_frame(Resource *resource,
|
|
uint32_t callback) override;
|
|
void surface_commit(Resource *resource) override;
|
|
diff --git a/tests/auto/client/xdgoutput/tst_xdgoutput.cpp b/tests/auto/client/xdgoutput/tst_xdgoutput.cpp
|
|
index 20f762e06..2a0cad1de 100644
|
|
--- a/tests/auto/client/xdgoutput/tst_xdgoutput.cpp
|
|
+++ b/tests/auto/client/xdgoutput/tst_xdgoutput.cpp
|
|
@@ -55,6 +55,7 @@ private slots:
|
|
void primaryScreen();
|
|
void overrideGeometry();
|
|
void changeGeometry();
|
|
+ void outputCreateEnterRace();
|
|
};
|
|
|
|
void tst_xdgoutput::cleanup()
|
|
@@ -134,5 +135,39 @@ void tst_xdgoutput::changeGeometry()
|
|
exec([&] { remove(output(1)); });
|
|
}
|
|
|
|
+void tst_xdgoutput::outputCreateEnterRace()
|
|
+{
|
|
+ m_config.autoConfigure = true;
|
|
+ m_config.autoEnter = false;
|
|
+ QRasterWindow window;
|
|
+ QSignalSpy screenChanged(&window, &QWindow::screenChanged);
|
|
+ window.resize(400, 320);
|
|
+ window.show();
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial);
|
|
+ exec([=] { xdgToplevel()->surface()->sendEnter(output(0));});
|
|
+
|
|
+ QTRY_COMPARE(QGuiApplication::screens().size(), 1);
|
|
+ QScreen *primaryScreen = QGuiApplication::screens().first();
|
|
+ QCOMPARE(window.screen(), primaryScreen);
|
|
+
|
|
+ auto *out = exec([=] {
|
|
+ return add<Output>();
|
|
+ });
|
|
+
|
|
+ // In Compositor Thread
|
|
+ connect(out, &Output::outputBound, this, [this](QtWaylandServer::wl_output::Resource *resource){
|
|
+ auto surface = xdgToplevel()->surface();
|
|
+ surface->sendLeave(output(0));
|
|
+ surface->QtWaylandServer::wl_surface::send_enter(surface->resource()->handle, resource->handle);
|
|
+ }, Qt::DirectConnection);
|
|
+
|
|
+ QTRY_COMPARE(QGuiApplication::screens().size(), 2);
|
|
+ QTRY_COMPARE(window.screen(), QGuiApplication::screens()[1]);
|
|
+
|
|
+ exec([=] { remove(out); });
|
|
+ m_config.autoConfigure = false;
|
|
+ m_config.autoEnter = true;
|
|
+}
|
|
+
|
|
QCOMPOSITOR_TEST_MAIN(tst_xdgoutput)
|
|
#include "tst_xdgoutput.moc"
|
|
diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp
|
|
index 1c23728b2..f2181fd69 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;
|
|
|
|
@@ -45,6 +46,7 @@ private slots:
|
|
void configureStates();
|
|
void popup();
|
|
void tooltipOnPopup();
|
|
+ void tooltipAndSiblingPopup();
|
|
void switchPopups();
|
|
void hidePopupParent();
|
|
void pongs();
|
|
@@ -138,6 +140,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();
|
|
@@ -154,9 +157,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([&] {
|
|
@@ -186,6 +192,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()
|
|
@@ -340,6 +347,92 @@ void tst_xdgshell::tooltipOnPopup()
|
|
QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr);
|
|
}
|
|
|
|
+void tst_xdgshell::tooltipAndSiblingPopup()
|
|
+{
|
|
+ class ToolTip : public QRasterWindow {
|
|
+ public:
|
|
+ explicit ToolTip(QWindow *parent) {
|
|
+ setTransientParent(parent);
|
|
+ setFlags(Qt::ToolTip);
|
|
+ resize(100, 100);
|
|
+ show();
|
|
+ }
|
|
+ void mousePressEvent(QMouseEvent *event) override {
|
|
+ QRasterWindow::mousePressEvent(event);
|
|
+ m_popup = new QRasterWindow;
|
|
+ m_popup->setTransientParent(transientParent());
|
|
+ m_popup->setFlags(Qt::Popup);
|
|
+ m_popup->resize(100, 100);
|
|
+ m_popup->show();
|
|
+ }
|
|
+
|
|
+ QRasterWindow *m_popup = nullptr;
|
|
+ };
|
|
+
|
|
+ class Window : public QRasterWindow {
|
|
+ public:
|
|
+ void mousePressEvent(QMouseEvent *event) override {
|
|
+ QRasterWindow::mousePressEvent(event);
|
|
+ m_tooltip = new ToolTip(this);
|
|
+ }
|
|
+ ToolTip *m_tooltip = nullptr;
|
|
+ };
|
|
+
|
|
+ Window window;
|
|
+ window.resize(200, 200);
|
|
+ window.show();
|
|
+
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
|
|
+ exec([=] { xdgToplevel()->sendCompleteConfigure(); });
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial);
|
|
+
|
|
+ exec([=] {
|
|
+ auto *surface = xdgToplevel()->surface();
|
|
+ auto *p = pointer();
|
|
+ auto *c = client();
|
|
+ p->sendEnter(surface, {100, 100});
|
|
+ p->sendFrame(c);
|
|
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
|
|
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
|
|
+ p->sendFrame(c);
|
|
+ p->sendLeave(surface);
|
|
+ p->sendFrame(c);
|
|
+ });
|
|
+
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup());
|
|
+ exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial);
|
|
+ QCOMPOSITOR_TRY_VERIFY(!xdgPopup()->m_grabbed);
|
|
+
|
|
+ exec([=] {
|
|
+ auto *surface = xdgPopup()->surface();
|
|
+ auto *p = pointer();
|
|
+ auto *c = client();
|
|
+ p->sendEnter(surface, {100, 100});
|
|
+ p->sendFrame(c);
|
|
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);
|
|
+ p->sendButton(client(), BTN_LEFT, Pointer::button_state_released);
|
|
+ p->sendFrame(c);
|
|
+ });
|
|
+
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1));
|
|
+ exec([=] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); });
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_xdgSurface->m_committedConfigureSerial);
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_grabbed);
|
|
+
|
|
+ // Close the middle tooltip (it should not close the sibling popup)
|
|
+ window.m_tooltip->close();
|
|
+
|
|
+ QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr);
|
|
+ // Verify the remaining xdg surface is a grab popup..
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup(0));
|
|
+ QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)->m_grabbed);
|
|
+
|
|
+ window.m_tooltip->m_popup->close();
|
|
+ QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr);
|
|
+ QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr);
|
|
+}
|
|
+
|
|
// QTBUG-65680
|
|
void tst_xdgshell::switchPopups()
|
|
{
|
|
@@ -505,15 +598,17 @@ 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));
|
|
|
|
window.setMaximumSize(QSize(500, 400));
|
|
+ window.update();
|
|
QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(500, 400));
|
|
|
|
window.setMinimumSize(QSize(50, 40));
|
|
+ window.update();
|
|
QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(50, 40));
|
|
}
|
|
|