summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Olav Tvete <paul.tvete@qt.io>2019-03-21 11:42:15 +0100
committerPaul Olav Tvete <paul.tvete@qt.io>2019-03-21 11:57:15 +0100
commit77e8ee63dc9ad0a4c139f35f8cf078d1a5bd315c (patch)
tree499f94df98fc25bca2982fba4dd67457f42bd6b9 /src
parentdf3a1761af2f20d59ae09a7adaa2f5b959047687 (diff)
parentc0905957be0d7db90c9d9f05069a259575753dfe (diff)
downloadqtwayland-77e8ee63dc9ad0a4c139f35f8cf078d1a5bd315c.tar.gz
Merge remote-tracking branch 'qt/5.13' into dev
Change-Id: I3dc204fcaa71c01a80b0c622443012eb07964431
Diffstat (limited to 'src')
-rw-r--r--src/client/configure.json1
-rw-r--r--src/client/qwaylandabstractdecoration.cpp15
-rw-r--r--src/client/qwaylandcursor.cpp67
-rw-r--r--src/client/qwaylandcursor_p.h6
-rw-r--r--src/client/qwaylanddisplay.cpp41
-rw-r--r--src/client/qwaylanddisplay_p.h9
-rw-r--r--src/client/qwaylandinputdevice.cpp385
-rw-r--r--src/client/qwaylandinputdevice_p.h60
-rw-r--r--src/client/qwaylandscreen.cpp11
-rw-r--r--src/client/qwaylandscreen_p.h1
-rw-r--r--src/client/qwaylandwindow.cpp35
-rw-r--r--src/client/qwaylandwindow_p.h4
-rw-r--r--src/compositor/compositor_api/qwaylandcompositor.cpp7
-rw-r--r--src/compositor/compositor_api/qwaylandkeyboard.cpp2
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem.cpp30
-rw-r--r--src/compositor/compositor_api/qwaylandquickitem.h2
-rw-r--r--src/compositor/compositor_api/qwaylandsurface.cpp11
-rw-r--r--src/compositor/compositor_api/qwaylandsurface.h11
-rw-r--r--src/compositor/configure.json1
-rw-r--r--src/compositor/extensions/qwaylandiviapplication.cpp4
-rw-r--r--src/compositor/extensions/qwaylandxdgdecorationv1.cpp4
-rw-r--r--src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp1
-rw-r--r--src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp8
-rw-r--r--src/imports/compositor/compositor.pro3
-rw-r--r--src/imports/compositor/plugins.qmltypes43
-rw-r--r--src/imports/compositor/qwaylandquickcompositorplugin.cpp2
-rw-r--r--src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp9
-rw-r--r--src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp9
28 files changed, 502 insertions, 280 deletions
diff --git a/src/client/configure.json b/src/client/configure.json
index 4f1a297e..2ec87eb4 100644
--- a/src/client/configure.json
+++ b/src/client/configure.json
@@ -1,5 +1,6 @@
{
"module": "waylandclient",
+ "condition": "module.gui",
"depends": [
"gui-private"
],
diff --git a/src/client/qwaylandabstractdecoration.cpp b/src/client/qwaylandabstractdecoration.cpp
index 0f00b989..87dd6cea 100644
--- a/src/client/qwaylandabstractdecoration.cpp
+++ b/src/client/qwaylandabstractdecoration.cpp
@@ -100,14 +100,19 @@ void QWaylandAbstractDecoration::setWaylandWindow(QWaylandWindow *window)
d->m_wayland_window = window;
}
-// \a size is without margins
+// Creates regions like this on the outside of a rectangle with inner size \a size
+// -----
+// | |
+// -----
+// I.e. the top and bottom extends into the corners
static QRegion marginsRegion(const QSize &size, const QMargins &margins)
{
QRegion r;
- r += QRect(0, 0, size.width(), margins.top()); // top
- r += QRect(0, size.height()+margins.top(), size.width(), margins.bottom()); //bottom
- r += QRect(0, 0, margins.left(), size.height()); //left
- r += QRect(size.width()+margins.left(), 0, margins.right(), size.height()); // right
+ const int widthWithMargins = margins.left() + size.width() + margins.right();
+ r += QRect(0, 0, widthWithMargins, margins.top()); // top
+ r += QRect(0, size.height()+margins.top(), widthWithMargins, margins.bottom()); //bottom
+ r += QRect(0, margins.top(), margins.left(), size.height()); //left
+ r += QRect(size.width()+margins.left(), margins.top(), margins.right(), size.height()); // right
return r;
}
diff --git a/src/client/qwaylandcursor.cpp b/src/client/qwaylandcursor.cpp
index 6947e97f..8b2ed036 100644
--- a/src/client/qwaylandcursor.cpp
+++ b/src/client/qwaylandcursor.cpp
@@ -41,7 +41,6 @@
#include "qwaylanddisplay_p.h"
#include "qwaylandinputdevice_p.h"
-#include "qwaylandscreen_p.h"
#include "qwaylandshmbackingstore_p.h"
#include <QtGui/QImageReader>
@@ -53,12 +52,6 @@ QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
-QWaylandCursorTheme *QWaylandCursorTheme::create(QWaylandShm *shm, int size)
-{
- static QString themeName = qEnvironmentVariable("XCURSOR_THEME", QStringLiteral("default"));
- return create(shm, size, themeName);
-}
-
QWaylandCursorTheme *QWaylandCursorTheme::create(QWaylandShm *shm, int size, const QString &themeName)
{
QByteArray nameBytes = themeName.toLocal8Bit();
@@ -244,56 +237,32 @@ struct wl_cursor_image *QWaylandCursorTheme::cursorImage(Qt::CursorShape shape)
return image;
}
-QWaylandCursor::QWaylandCursor(QWaylandScreen *screen)
- : mDisplay(screen->display())
- , mCursorTheme(mDisplay->loadCursorTheme(screen->devicePixelRatio()))
+QWaylandCursor::QWaylandCursor(QWaylandDisplay *display)
+ : mDisplay(display)
{
}
-QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapImage(const QCursor *cursor)
+QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor)
{
- if (cursor->shape() != Qt::BitmapCursor)
- return QSharedPointer<QWaylandShmBuffer>();
-
+ Q_ASSERT(cursor->shape() == Qt::BitmapCursor);
const QImage &img = cursor->pixmap().toImage();
- QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(mDisplay, img.size(), img.format()));
- memcpy(buffer->image()->bits(), img.bits(), img.sizeInBytes());
- return buffer;
-}
-
-struct wl_cursor_image *QWaylandCursor::cursorImage(Qt::CursorShape shape)
-{
- if (!mCursorTheme)
- return nullptr;
- return mCursorTheme->cursorImage(shape);
+ QSharedPointer<QWaylandShmBuffer> buffer(new QWaylandShmBuffer(display, img.size(), img.format()));
+ memcpy(buffer->image()->bits(), img.bits(), size_t(img.sizeInBytes()));
+ return std::move(buffer);
}
void QWaylandCursor::changeCursor(QCursor *cursor, QWindow *window)
{
- const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor;
-
- if (newShape == Qt::BlankCursor) {
- mDisplay->setCursor(nullptr, nullptr, 1);
- return;
- }
-
- if (newShape == Qt::BitmapCursor) {
- mDisplay->setCursor(cursorBitmapImage(cursor), cursor->hotSpot(), window->screen()->devicePixelRatio());
- return;
- }
-
- if (!mCursorTheme) {
- qCWarning(lcQpaWayland) << "Can't set cursor from shape with no cursor theme";
- return;
- }
-
- if (struct ::wl_cursor_image *image = mCursorTheme->cursorImage(newShape)) {
- struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
- mDisplay->setCursor(buffer, image, window->screen()->devicePixelRatio());
- return;
- }
-
- qCWarning(lcQpaWayland) << "Unable to change to cursor" << cursor;
+ Q_UNUSED(window);
+ // Create the buffer here so we don't have to create one per input device
+ QSharedPointer<QWaylandBuffer> bitmapBuffer;
+ if (cursor && cursor->shape() == Qt::BitmapCursor)
+ bitmapBuffer = cursorBitmapBuffer(mDisplay, cursor);
+
+ int fallbackOutputScale = int(window->devicePixelRatio());
+ const auto seats = mDisplay->inputDevices();
+ for (auto *seat : seats)
+ seat->setCursor(cursor, bitmapBuffer, fallbackOutputScale);
}
void QWaylandCursor::pointerEvent(const QMouseEvent &event)
@@ -312,6 +281,6 @@ void QWaylandCursor::setPos(const QPoint &pos)
qCWarning(lcQpaWayland) << "Setting cursor position is not possible on wayland";
}
-}
+} // namespace QtWaylandClient
QT_END_NAMESPACE
diff --git a/src/client/qwaylandcursor_p.h b/src/client/qwaylandcursor_p.h
index 71f9cd1b..6c48fb62 100644
--- a/src/client/qwaylandcursor_p.h
+++ b/src/client/qwaylandcursor_p.h
@@ -73,7 +73,6 @@ class QWaylandShm;
class Q_WAYLAND_CLIENT_EXPORT QWaylandCursorTheme
{
public:
- static QWaylandCursorTheme *create(QWaylandShm *shm, int size);
static QWaylandCursorTheme *create(QWaylandShm *shm, int size, const QString &themeName);
~QWaylandCursorTheme();
struct wl_cursor_image *cursorImage(Qt::CursorShape shape);
@@ -122,19 +121,18 @@ private:
class Q_WAYLAND_CLIENT_EXPORT QWaylandCursor : public QPlatformCursor
{
public:
- QWaylandCursor(QWaylandScreen *screen);
+ explicit QWaylandCursor(QWaylandDisplay *display);
void changeCursor(QCursor *cursor, QWindow *window) override;
void pointerEvent(const QMouseEvent &event) override;
QPoint pos() const override;
void setPos(const QPoint &pos) override;
- QSharedPointer<QWaylandBuffer> cursorBitmapImage(const QCursor *cursor);
+ static QSharedPointer<QWaylandBuffer> cursorBitmapBuffer(QWaylandDisplay *display, const QCursor *cursor);
struct wl_cursor_image *cursorImage(Qt::CursorShape shape);
private:
QWaylandDisplay *mDisplay = nullptr;
- QWaylandCursorTheme *mCursorTheme = nullptr;
QPoint mLastPos;
};
diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp
index 77db05f1..1063fd59 100644
--- a/src/client/qwaylanddisplay.cpp
+++ b/src/client/qwaylanddisplay.cpp
@@ -50,6 +50,7 @@
#endif
#if QT_CONFIG(wayland_datadevice)
#include "qwaylanddatadevicemanager_p.h"
+#include "qwaylanddatadevice_p.h"
#endif
#if QT_CONFIG(cursor)
#include <wayland-cursor.h>
@@ -160,7 +161,7 @@ QWaylandDisplay::~QWaylandDisplay(void)
delete mDndSelectionHandler.take();
#endif
#if QT_CONFIG(cursor)
- qDeleteAll(mCursorThemesBySize);
+ qDeleteAll(mCursorThemes);
#endif
if (mDisplay)
wl_display_disconnect(mDisplay);
@@ -505,40 +506,20 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const
#if QT_CONFIG(cursor)
-void QWaylandDisplay::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, qreal dpr)
+QWaylandCursor *QWaylandDisplay::waylandCursor()
{
- /* Qt doesn't tell us which input device we should set the cursor
- * for, so set it for all devices. */
- for (int i = 0; i < mInputDevices.count(); i++) {
- QWaylandInputDevice *inputDevice = mInputDevices.at(i);
- inputDevice->setCursor(buffer, image, dpr);
- }
-}
-
-void QWaylandDisplay::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, qreal dpr)
-{
- /* Qt doesn't tell us which input device we should set the cursor
- * for, so set it for all devices. */
- for (int i = 0; i < mInputDevices.count(); i++) {
- QWaylandInputDevice *inputDevice = mInputDevices.at(i);
- inputDevice->setCursor(buffer, hotSpot, dpr);
- }
+ if (!mCursor)
+ mCursor.reset(new QWaylandCursor(this));
+ return mCursor.data();
}
-QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(qreal devicePixelRatio)
+QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(const QString &name, int pixelSize)
{
- constexpr int defaultCursorSize = 32;
- static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
- int cursorSize = xCursorSize > 0 ? xCursorSize : defaultCursorSize;
-
- if (compositorVersion() >= 3) // set_buffer_scale is not supported on earlier versions
- cursorSize *= devicePixelRatio;
-
- if (auto *theme = mCursorThemesBySize.value(cursorSize, nullptr))
+ if (auto *theme = mCursorThemes.value({name, pixelSize}, nullptr))
return theme;
- if (auto *theme = QWaylandCursorTheme::create(shm(), cursorSize)) {
- mCursorThemesBySize[cursorSize] = theme;
+ if (auto *theme = QWaylandCursorTheme::create(shm(), pixelSize, name)) {
+ mCursorThemes[{name, pixelSize}] = theme;
return theme;
}
@@ -547,6 +528,6 @@ QWaylandCursorTheme *QWaylandDisplay::loadCursorTheme(qreal devicePixelRatio)
#endif // QT_CONFIG(cursor)
-}
+} // namespace QtWaylandClient
QT_END_NAMESPACE
diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h
index 41ad7025..4a98b935 100644
--- a/src/client/qwaylanddisplay_p.h
+++ b/src/client/qwaylanddisplay_p.h
@@ -93,6 +93,7 @@ class QWaylandWindow;
class QWaylandIntegration;
class QWaylandHardwareIntegration;
class QWaylandShellSurface;
+class QWaylandCursor;
class QWaylandCursorTheme;
typedef void (*RegistryListener)(void *data,
@@ -121,9 +122,8 @@ public:
QWaylandWindowManagerIntegration *windowManagerIntegration() const;
#if QT_CONFIG(cursor)
- void setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, qreal dpr);
- void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, qreal dpr);
- QWaylandCursorTheme *loadCursorTheme(qreal devicePixelRatio);
+ QWaylandCursor *waylandCursor();
+ QWaylandCursorTheme *loadCursorTheme(const QString &name, int pixelSize);
#endif
struct wl_display *wl_display() const { return mDisplay; }
struct ::wl_registry *wl_registry() { return object(); }
@@ -210,7 +210,8 @@ private:
QList<Listener> mRegistryListeners;
QWaylandIntegration *mWaylandIntegration = nullptr;
#if QT_CONFIG(cursor)
- QMap<int, QWaylandCursorTheme *> mCursorThemesBySize;
+ QMap<std::pair<QString, int>, QWaylandCursorTheme *> mCursorThemes; // theme name and size
+ QScopedPointer<QWaylandCursor> mCursor;
#endif
#if QT_CONFIG(wayland_datadevice)
QScopedPointer<QWaylandDataDeviceManager> mDndSelectionHandler;
diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp
index 23a84e4b..b01f5ea0 100644
--- a/src/client/qwaylandinputdevice.cpp
+++ b/src/client/qwaylandinputdevice.cpp
@@ -170,13 +170,13 @@ QWaylandInputDevice::Keyboard::~Keyboard()
wl_keyboard_destroy(object());
}
-void QWaylandInputDevice::Keyboard::stopRepeat()
+QWaylandWindow *QWaylandInputDevice::Keyboard::focusWindow() const
{
- mRepeatTimer.stop();
+ return mFocus ? QWaylandWindow::fromWlSurface(mFocus) : nullptr;
}
-QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *p)
- : mParent(p)
+QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *seat)
+ : mParent(seat)
{
}
@@ -188,6 +188,191 @@ QWaylandInputDevice::Pointer::~Pointer()
wl_pointer_destroy(object());
}
+#if QT_CONFIG(cursor)
+
+class CursorSurface : public QObject, public QtWayland::wl_surface
+{
+public:
+ explicit CursorSurface(QWaylandInputDevice::Pointer *pointer, QWaylandDisplay *display)
+ : m_pointer(pointer)
+ {
+ init(display->createSurface(this));
+ //TODO: When we upgrade to libwayland 1.10, use wl_surface_get_version instead.
+ m_version = display->compositorVersion();
+ connect(qApp, &QGuiApplication::screenRemoved, this, [this](QScreen *screen) {
+ int oldScale = outputScale();
+ if (!m_screens.removeOne(static_cast<QWaylandScreen *>(screen->handle())))
+ return;
+
+ if (outputScale() != oldScale)
+ m_pointer->updateCursor();
+ });
+ }
+
+ void hide()
+ {
+ uint serial = m_pointer->mEnterSerial;
+ Q_ASSERT(serial);
+ m_pointer->set_cursor(serial, nullptr, 0, 0);
+ m_setSerial = 0;
+ }
+
+ // Size and hotspot are in surface coordinates
+ void update(wl_buffer *buffer, const QPoint &hotspot, const QSize &size, int bufferScale)
+ {
+ // Calling code needs to ensure buffer scale is supported if != 1
+ Q_ASSERT(bufferScale == 1 || m_version >= 3);
+
+ auto enterSerial = m_pointer->mEnterSerial;
+ if (m_setSerial < enterSerial || m_hotspot != hotspot) {
+ m_pointer->set_cursor(m_pointer->mEnterSerial, object(), hotspot.x(), hotspot.y());
+ m_setSerial = enterSerial;
+ m_hotspot = hotspot;
+ }
+
+ if (m_version >= 3)
+ set_buffer_scale(bufferScale);
+
+ attach(buffer, 0, 0);
+ damage(0, 0, size.width(), size.height());
+ commit();
+ }
+
+ int outputScale() const
+ {
+ int scale = 0;
+ for (auto *screen : m_screens)
+ scale = qMax(scale, screen->scale());
+ return scale;
+ }
+
+protected:
+ void surface_enter(struct ::wl_output *output) override
+ {
+ int oldScale = outputScale();
+ auto *screen = QWaylandScreen::fromWlOutput(output);
+ if (m_screens.contains(screen))
+ return;
+
+ m_screens.append(screen);
+
+ if (outputScale() != oldScale)
+ m_pointer->updateCursor();
+ }
+
+ void surface_leave(struct ::wl_output *output) override
+ {
+ int oldScale = outputScale();
+ auto *screen = QWaylandScreen::fromWlOutput(output);
+
+ if (!m_screens.removeOne(screen))
+ return;
+
+ if (outputScale() != oldScale)
+ m_pointer->updateCursor();
+ }
+
+private:
+ QWaylandInputDevice::Pointer *m_pointer = nullptr;
+ uint m_version = 0;
+ uint m_setSerial = 0;
+ QPoint m_hotspot;
+ QVector<QWaylandScreen *> m_screens;
+};
+
+QString QWaylandInputDevice::Pointer::cursorThemeName() const
+{
+ static QString themeName = qEnvironmentVariable("XCURSOR_THEME", QStringLiteral("default"));
+ return themeName;
+}
+
+int QWaylandInputDevice::Pointer::cursorSize() const
+{
+ constexpr int defaultCursorSize = 32;
+ static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
+ return xCursorSize > 0 ? xCursorSize : defaultCursorSize;
+}
+
+int QWaylandInputDevice::Pointer::idealCursorScale() const
+{
+ // set_buffer_scale is not supported on earlier versions
+ if (seat()->mQDisplay->compositorVersion() < 3)
+ return 1;
+
+ if (auto *s = mCursor.surface.data()) {
+ if (s->outputScale() > 0)
+ return s->outputScale();
+ }
+
+ return seat()->mCursor.fallbackOutputScale;
+}
+
+void QWaylandInputDevice::Pointer::updateCursorTheme()
+{
+ int scale = idealCursorScale();
+ int pixelSize = cursorSize() * scale;
+ auto *display = seat()->mQDisplay;
+ mCursor.theme = display->loadCursorTheme(cursorThemeName(), pixelSize);
+ if (auto *arrow = mCursor.theme->cursorImage(Qt::ArrowCursor)) {
+ int arrowPixelSize = qMax(arrow->width, arrow->height); // Not all cursor themes are square
+ while (scale > 1 && arrowPixelSize / scale < cursorSize())
+ --scale;
+ } else {
+ qWarning(lcQpaWayland) << "Cursor theme does not support the arrow cursor";
+ }
+ mCursor.themeBufferScale = scale;
+}
+
+void QWaylandInputDevice::Pointer::updateCursor()
+{
+ if (mEnterSerial == 0)
+ return;
+
+ auto shape = seat()->mCursor.shape;
+
+ if (shape == Qt::BlankCursor) {
+ if (mCursor.surface)
+ mCursor.surface->hide();
+ return;
+ }
+
+ if (shape == Qt::BitmapCursor) {
+ auto buffer = seat()->mCursor.bitmapBuffer;
+ if (!buffer) {
+ qCWarning(lcQpaWayland) << "No buffer for bitmap cursor, can't set cursor";
+ return;
+ }
+ auto hotspot = seat()->mCursor.hotspot;
+ int bufferScale = seat()->mCursor.bitmapScale;
+ getOrCreateCursorSurface()->update(buffer->buffer(), hotspot, buffer->size(), bufferScale);
+ return;
+ }
+
+ if (!mCursor.theme || idealCursorScale() != mCursor.themeBufferScale)
+ updateCursorTheme();
+
+ // Set from shape using theme
+ if (struct ::wl_cursor_image *image = mCursor.theme->cursorImage(shape)) {
+ struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
+ int bufferScale = mCursor.themeBufferScale;
+ QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale;
+ QSize size = QSize(image->width, image->height) / bufferScale;
+ getOrCreateCursorSurface()->update(buffer, hotspot, size, bufferScale);
+ return;
+ }
+
+ qCWarning(lcQpaWayland) << "Unable to change to cursor" << shape;
+}
+
+CursorSurface *QWaylandInputDevice::Pointer::getOrCreateCursorSurface()
+{
+ if (!mCursor.surface)
+ mCursor.surface.reset(new CursorSurface(this, seat()->mQDisplay));
+ return mCursor.surface.get();
+}
+
+#endif // QT_CONFIG(cursor)
+
QWaylandInputDevice::Touch::Touch(QWaylandInputDevice *p)
: mParent(p)
{
@@ -276,12 +461,6 @@ QWaylandInputDevice::Touch *QWaylandInputDevice::createTouch(QWaylandInputDevice
return new Touch(device);
}
-void QWaylandInputDevice::handleWindowDestroyed(QWaylandWindow *window)
-{
- if (mKeyboard && window == mKeyboard->mFocus)
- mKeyboard->stopRepeat();
-}
-
void QWaylandInputDevice::handleEndDrag()
{
if (mTouch)
@@ -325,7 +504,7 @@ QWaylandWindow *QWaylandInputDevice::pointerFocus() const
QWaylandWindow *QWaylandInputDevice::keyboardFocus() const
{
- return mKeyboard ? mKeyboard->mFocus : nullptr;
+ return mKeyboard ? mKeyboard->focusWindow() : nullptr;
}
QWaylandWindow *QWaylandInputDevice::touchFocus() const
@@ -361,87 +540,33 @@ Qt::KeyboardModifiers QWaylandInputDevice::Keyboard::modifiers() const
}
#if QT_CONFIG(cursor)
-uint32_t QWaylandInputDevice::cursorSerial() const
-{
- if (mPointer)
- return mPointer->mCursorSerial;
- return 0;
-}
-
-void QWaylandInputDevice::setCursor(Qt::CursorShape newShape, QWaylandScreen *screen)
-{
- struct wl_cursor_image *image = screen->waylandCursor()->cursorImage(newShape);
- if (!image) {
- return;
+void QWaylandInputDevice::setCursor(const QCursor *cursor, const QSharedPointer<QWaylandBuffer> &cachedBuffer, int fallbackOutputScale)
+{
+ CursorState oldCursor = mCursor;
+ mCursor = CursorState(); // Clear any previous state
+ mCursor.shape = cursor ? cursor->shape() : Qt::ArrowCursor;
+ mCursor.hotspot = cursor ? cursor->hotSpot() : QPoint();
+ mCursor.fallbackOutputScale = fallbackOutputScale;
+
+ if (mCursor.shape == Qt::BitmapCursor) {
+ mCursor.bitmapBuffer = cachedBuffer ? cachedBuffer : QWaylandCursor::cursorBitmapBuffer(mQDisplay, cursor);
+ qreal dpr = cursor->pixmap().devicePixelRatio();
+ mCursor.bitmapScale = int(dpr); // Wayland doesn't support fractional buffer scale
+ // If there was a fractional part of the dpr, we need to scale the hotspot accordingly
+ if (mCursor.bitmapScale < dpr)
+ mCursor.hotspot *= dpr / mCursor.bitmapScale;
}
- struct wl_buffer *buffer = wl_cursor_image_get_buffer(image);
- setCursor(buffer, image, screen->devicePixelRatio());
-}
-
-void QWaylandInputDevice::setCursor(const QCursor &cursor, QWaylandScreen *screen)
-{
- if (mPointer->mCursorSerial >= mPointer->mEnterSerial && (cursor.shape() != Qt::BitmapCursor && cursor.shape() == mPointer->mCursorShape))
+ // Return early if setCursor was called redundantly (mostly happens from decorations)
+ if (mCursor.shape != Qt::BitmapCursor
+ && mCursor.shape == oldCursor.shape
+ && mCursor.hotspot == oldCursor.hotspot
+ && mCursor.fallbackOutputScale == oldCursor.fallbackOutputScale) {
return;
-
- mPointer->mCursorShape = cursor.shape();
- if (cursor.shape() == Qt::BitmapCursor) {
- setCursor(screen->waylandCursor()->cursorBitmapImage(&cursor), cursor.hotSpot(), screen->devicePixelRatio());
- return;
- }
- setCursor(cursor.shape(), screen);
-}
-
-void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, int bufferScale)
-{
- if (image) {
- // Convert from pixel coordinates to surface coordinates
- QPoint hotspot = QPoint(image->hotspot_x, image->hotspot_y) / bufferScale;
- QSize size = QSize(image->width, image->height) / bufferScale;
- setCursor(buffer, hotspot, size, bufferScale);
- } else {
- setCursor(buffer, QPoint(), QSize(), bufferScale);
}
-}
-
-// size and hotspot are in surface coordinates
-void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size, int bufferScale)
-{
- if (mCaps & WL_SEAT_CAPABILITY_POINTER) {
- bool force = mPointer->mEnterSerial > mPointer->mCursorSerial;
-
- if (!force && mPointer->mCursorBuffer == buffer)
- return;
-
- mPixmapCursor.clear();
- mPointer->mCursorSerial = mPointer->mEnterSerial;
- mPointer->mCursorBuffer = buffer;
-
- /* Hide cursor */
- if (!buffer)
- {
- mPointer->set_cursor(mPointer->mEnterSerial, nullptr, 0, 0);
- return;
- }
-
- if (!pointerSurface)
- pointerSurface = mQDisplay->createSurface(this);
-
- mPointer->set_cursor(mPointer->mEnterSerial, pointerSurface,
- hotSpot.x(), hotSpot.y());
- wl_surface_attach(pointerSurface, buffer, 0, 0);
- if (mQDisplay->compositorVersion() >= 3)
- wl_surface_set_buffer_scale(pointerSurface, bufferScale);
- wl_surface_damage(pointerSurface, 0, 0, size.width(), size.height());
- wl_surface_commit(pointerSurface);
- }
-}
-
-void QWaylandInputDevice::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, int bufferScale)
-{
- setCursor(buffer->buffer(), hotSpot, buffer->size(), bufferScale);
- mPixmapCursor = buffer;
+ if (mPointer)
+ mPointer->updateCursor();
}
#endif
@@ -461,7 +586,16 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
return;
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
+
+ if (mFocus) {
+ qWarning(lcQpaWayland) << "The compositor sent a wl_pointer.enter event before sending a"
+ << "leave event first, this is not allowed by the wayland protocol"
+ << "attempting to work around it by invalidating the current focus";
+ invalidateFocus();
+ }
mFocus = window;
+ connect(mFocus, &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
+
mSurfacePos = QPointF(wl_fixed_to_double(sx), wl_fixed_to_double(sy));
mGlobalPos = window->window()->mapToGlobal(mSurfacePos.toPoint());
@@ -470,7 +604,7 @@ void QWaylandInputDevice::Pointer::pointer_enter(uint32_t serial, struct wl_surf
#if QT_CONFIG(cursor)
// Depends on mEnterSerial being updated
- window->window()->setCursor(window->window()->cursor());
+ updateCursor();
#endif
QWaylandWindow *grab = QWaylandWindow::mouseGrab();
@@ -498,7 +632,8 @@ void QWaylandInputDevice::Pointer::pointer_leave(uint32_t time, struct wl_surfac
QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
setFrameEvent(new LeaveEvent(window, mSurfacePos, mGlobalPos));
}
- mFocus = nullptr;
+
+ invalidateFocus();
mButtons = Qt::NoButton;
mParent->mTime = time;
@@ -627,6 +762,13 @@ void QWaylandInputDevice::Pointer::pointer_button(uint32_t serial, uint32_t time
setFrameEvent(new ReleaseEvent(window, time, pos, global, mButtons, mParent->modifiers()));
}
+void QWaylandInputDevice::Pointer::invalidateFocus()
+{
+ disconnect(mFocus, &QWaylandWindow::wlSurfaceDestroyed, this, &Pointer::handleFocusDestroyed);
+ mFocus = nullptr;
+ mEnterSerial = 0;
+}
+
void QWaylandInputDevice::Pointer::releaseButtons()
{
mButtons = Qt::NoButton;
@@ -932,12 +1074,19 @@ void QWaylandInputDevice::Keyboard::keyboard_enter(uint32_t time, struct wl_surf
Q_UNUSED(time);
Q_UNUSED(keys);
- if (!surface)
+ if (!surface) {
+ // Ignoring wl_keyboard.enter event with null surface. This is either a compositor bug,
+ // or it's a race with a wl_surface.destroy request. In either case, ignore the event.
return;
+ }
+ if (mFocus) {
+ qCWarning(lcQpaWayland()) << "Unexpected wl_keyboard.enter event. Keyboard already has focus";
+ disconnect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed);
+ }
- QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
- mFocus = window;
+ mFocus = surface;
+ connect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed);
mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
}
@@ -945,18 +1094,20 @@ void QWaylandInputDevice::Keyboard::keyboard_enter(uint32_t time, struct wl_surf
void QWaylandInputDevice::Keyboard::keyboard_leave(uint32_t time, struct wl_surface *surface)
{
Q_UNUSED(time);
- Q_UNUSED(surface);
- if (surface) {
- QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface);
- window->unfocus();
+ if (!surface) {
+ // Either a compositor bug, or a race condition with wl_surface.destroy, ignore the event.
+ return;
}
- mFocus = nullptr;
-
- mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
-
- mRepeatTimer.stop();
+ if (surface != mFocus) {
+ qCWarning(lcQpaWayland) << "Ignoring unexpected wl_keyboard.leave event."
+ << "wl_surface argument does not match the current focus"
+ << "This is most likely a compositor bug";
+ return;
+ }
+ disconnect(focusWindow(), &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed);
+ handleFocusLost();
}
static void sendKey(QWindow *tlw, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
@@ -981,7 +1132,7 @@ static void sendKey(QWindow *tlw, ulong timestamp, QEvent::Type type, int key, Q
void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
- QWaylandWindow *window = mFocus;
+ auto *window = focusWindow();
if (!window) {
// We destroyed the keyboard focus surface, but the server didn't get the message yet...
// or the server didn't send an enter event first. In either case, ignore the event.
@@ -1061,14 +1212,15 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time,
void QWaylandInputDevice::Keyboard::repeatKey()
{
- if (!mFocus) {
+ auto *window = focusWindow();
+ if (!window) {
// We destroyed the keyboard focus surface, but the server didn't get the message yet...
// or the server didn't send an enter event first.
return;
}
mRepeatTimer.setInterval(mRepeatRate);
- sendKey(mFocus->window(), mRepeatTime, QEvent::KeyRelease, mRepeatKey, modifiers(), mRepeatCode,
+ sendKey(window->window(), mRepeatTime, QEvent::KeyRelease, mRepeatKey, modifiers(), mRepeatCode,
#if QT_CONFIG(xkbcommon)
mRepeatSym, mNativeModifiers,
#else
@@ -1076,7 +1228,7 @@ void QWaylandInputDevice::Keyboard::repeatKey()
#endif
mRepeatText, true);
- sendKey(mFocus->window(), mRepeatTime, QEvent::KeyPress, mRepeatKey, modifiers(), mRepeatCode,
+ sendKey(window->window(), mRepeatTime, QEvent::KeyPress, mRepeatKey, modifiers(), mRepeatCode,
#if QT_CONFIG(xkbcommon)
mRepeatSym, mNativeModifiers,
#else
@@ -1085,6 +1237,27 @@ void QWaylandInputDevice::Keyboard::repeatKey()
mRepeatText, true);
}
+void QWaylandInputDevice::Keyboard::handleFocusDestroyed()
+{
+ // The signal is emitted by QWaylandWindow, which is not necessarily destroyed along with the
+ // surface, so we still need to disconnect the signal
+ auto *window = qobject_cast<QWaylandWindow *>(sender());
+ disconnect(window, &QWaylandWindow::wlSurfaceDestroyed, this, &Keyboard::handleFocusDestroyed);
+ Q_ASSERT(window->wlSurface() == mFocus);
+ handleFocusLost();
+}
+
+void QWaylandInputDevice::Keyboard::handleFocusLost()
+{
+ mFocus = nullptr;
+#if QT_CONFIG(clipboard)
+ if (auto *dataDevice = mParent->dataDevice())
+ dataDevice->invalidateSelectionOffer();
+#endif
+ mParent->mQDisplay->handleKeyboardFocusChanged(mParent);
+ mRepeatTimer.stop();
+}
+
void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial,
uint32_t mods_depressed,
uint32_t mods_latched,
@@ -1186,7 +1359,7 @@ void QWaylandInputDevice::handleTouchPoint(int id, double x, double y, Qt::Touch
if (!win && mPointer)
win = mPointer->mFocus;
if (!win && mKeyboard)
- win = mKeyboard->mFocus;
+ win = mKeyboard->focusWindow();
if (!win || !win->window())
return;
diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h
index 0da73e5d..e87de6ae 100644
--- a/src/client/qwaylandinputdevice_p.h
+++ b/src/client/qwaylandinputdevice_p.h
@@ -88,6 +88,10 @@ class QWaylandWindow;
class QWaylandDisplay;
class QWaylandDataDevice;
class QWaylandTextInput;
+#if QT_CONFIG(cursor)
+class QWaylandCursorTheme;
+class CursorSurface;
+#endif
class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice
: public QObject
@@ -107,12 +111,8 @@ public:
struct ::wl_seat *wl_seat() { return QtWayland::wl_seat::object(); }
#if QT_CONFIG(cursor)
- void setCursor(const QCursor &cursor, QWaylandScreen *screen);
- void setCursor(struct wl_buffer *buffer, struct ::wl_cursor_image *image, int bufferScale);
- void setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size, int bufferScale);
- void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, int bufferScale);
+ void setCursor(const QCursor *cursor, const QSharedPointer<QWaylandBuffer> &cachedBuffer = {}, int fallbackOutputScale = 1);
#endif
- void handleWindowDestroyed(QWaylandWindow *window);
void handleEndDrag();
#if QT_CONFIG(wayland_datadevice)
@@ -134,22 +134,27 @@ public:
Qt::KeyboardModifiers modifiers() const;
uint32_t serial() const;
- uint32_t cursorSerial() const;
virtual Keyboard *createKeyboard(QWaylandInputDevice *device);
virtual Pointer *createPointer(QWaylandInputDevice *device);
virtual Touch *createTouch(QWaylandInputDevice *device);
private:
- void setCursor(Qt::CursorShape cursor, QWaylandScreen *screen);
-
QWaylandDisplay *mQDisplay = nullptr;
struct wl_display *mDisplay = nullptr;
int mVersion;
uint32_t mCaps = 0;
- struct wl_surface *pointerSurface = nullptr;
+#if QT_CONFIG(cursor)
+ struct CursorState {
+ QSharedPointer<QWaylandBuffer> bitmapBuffer; // not used with shape cursors
+ int bitmapScale = 1;
+ Qt::CursorShape shape = Qt::ArrowCursor;
+ int fallbackOutputScale = 1;
+ QPoint hotspot;
+ } mCursor;
+#endif
#if QT_CONFIG(wayland_datadevice)
QWaylandDataDevice *mDataDevice = nullptr;
@@ -169,8 +174,6 @@ private:
QTouchDevice *mTouchDevice = nullptr;
- QSharedPointer<QWaylandBuffer> mPixmapCursor;
-
friend class QWaylandTouchExtension;
friend class QWaylandQtKeyExtension;
};
@@ -189,7 +192,7 @@ public:
Keyboard(QWaylandInputDevice *p);
~Keyboard() override;
- void stopRepeat();
+ QWaylandWindow *focusWindow() const;
void keyboard_keymap(uint32_t format,
int32_t fd,
@@ -209,7 +212,7 @@ public:
void keyboard_repeat_info(int32_t rate, int32_t delay) override;
QWaylandInputDevice *mParent = nullptr;
- QPointer<QWaylandWindow> mFocus;
+ ::wl_surface *mFocus = nullptr;
#if QT_CONFIG(xkbcommon)
xkb_context *mXkbContext = nullptr;
xkb_keymap *mXkbMap = nullptr;
@@ -234,6 +237,8 @@ public:
private slots:
void repeatKey();
+ void handleFocusDestroyed();
+ void handleFocusLost();
private:
#if QT_CONFIG(xkbcommon)
@@ -245,13 +250,23 @@ private:
};
-class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QtWayland::wl_pointer
+class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QObject, public QtWayland::wl_pointer
{
-
+ Q_OBJECT
public:
- Pointer(QWaylandInputDevice *p);
+ explicit Pointer(QWaylandInputDevice *seat);
~Pointer() override;
+#if QT_CONFIG(cursor)
+ QString cursorThemeName() const;
+ int cursorSize() const; // in surface coordinates
+ int idealCursorScale() const;
+ void updateCursorTheme();
+ void updateCursor();
+ CursorSurface *getOrCreateCursorSurface();
+#endif
+ QWaylandInputDevice *seat() const { return mParent; }
+protected:
void pointer_enter(uint32_t serial, struct wl_surface *surface,
wl_fixed_t sx, wl_fixed_t sy) override;
void pointer_leave(uint32_t time, struct wl_surface *surface) override;
@@ -267,13 +282,24 @@ public:
void pointer_axis_discrete(uint32_t axis, int32_t value) override;
void pointer_frame() override;
+private slots:
+ void handleFocusDestroyed() { invalidateFocus(); }
+
+private:
+ void invalidateFocus();
+
+public:
void releaseButtons();
QWaylandInputDevice *mParent = nullptr;
QPointer<QWaylandWindow> mFocus;
uint32_t mEnterSerial = 0;
#if QT_CONFIG(cursor)
- uint32_t mCursorSerial = 0;
+ struct {
+ QWaylandCursorTheme *theme = nullptr;
+ int themeBufferScale = 0;
+ QScopedPointer<CursorSurface> surface;
+ } mCursor;
#endif
QPointF mSurfacePos;
QPointF mGlobalPos;
diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp
index a6caae0d..b2e3ce81 100644
--- a/src/client/qwaylandscreen.cpp
+++ b/src/client/qwaylandscreen.cpp
@@ -176,19 +176,10 @@ qreal QWaylandScreen::refreshRate() const
}
#if QT_CONFIG(cursor)
-
QPlatformCursor *QWaylandScreen::cursor() const
{
- return const_cast<QWaylandScreen *>(this)->waylandCursor();
+ return mWaylandDisplay->waylandCursor();
}
-
-QWaylandCursor *QWaylandScreen::waylandCursor()
-{
- if (!mWaylandCursor)
- mWaylandCursor.reset(new QWaylandCursor(this));
- return mWaylandCursor.data();
-}
-
#endif // QT_CONFIG(cursor)
QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window)
diff --git a/src/client/qwaylandscreen_p.h b/src/client/qwaylandscreen_p.h
index 36009cce..4ef58c0c 100644
--- a/src/client/qwaylandscreen_p.h
+++ b/src/client/qwaylandscreen_p.h
@@ -98,7 +98,6 @@ public:
#if QT_CONFIG(cursor)
QPlatformCursor *cursor() const override;
- QWaylandCursor *waylandCursor();
#endif
uint32_t outputId() const { return m_outputId; }
diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp
index abbea629..f4dcae81 100644
--- a/src/client/qwaylandwindow.cpp
+++ b/src/client/qwaylandwindow.cpp
@@ -51,11 +51,6 @@
#include "qwaylanddecorationfactory_p.h"
#include "qwaylandshmbackingstore_p.h"
-#if QT_CONFIG(wayland_datadevice)
-#include "qwaylanddatadevice_p.h"
-#endif
-
-
#include <QtCore/QFileInfo>
#include <QtCore/QPointer>
#include <QtCore/QRegularExpression>
@@ -95,10 +90,6 @@ QWaylandWindow::~QWaylandWindow()
if (isInitialized())
reset(false);
- QList<QWaylandInputDevice *> inputDevices = mDisplay->inputDevices();
- for (int i = 0; i < inputDevices.size(); ++i)
- inputDevices.at(i)->handleWindowDestroyed(this);
-
const QWindow *parent = window();
foreach (QWindow *w, QGuiApplication::topLevelWindows()) {
if (w->transientParent() == parent)
@@ -236,8 +227,10 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
mShellSurface = nullptr;
delete mSubSurfaceWindow;
mSubSurfaceWindow = nullptr;
- if (isInitialized())
+ if (isInitialized()) {
+ emit wlSurfaceDestroyed();
destroy();
+ }
mScreens.clear();
if (mFrameCallback) {
@@ -396,14 +389,9 @@ void QWaylandWindow::setVisible(bool visible)
// QWaylandShmBackingStore::beginPaint().
} else {
sendExposeEvent(QRect());
- // when flushing the event queue, it could contain a close event, in which
- // case 'this' will be deleted. When that happens, we must abort right away.
- QPointer<QWaylandWindow> deleteGuard(this);
- QWindowSystemInterface::flushWindowSystemEvents();
- if (!deleteGuard.isNull() && window()->type() == Qt::Popup)
+ if (window()->type() == Qt::Popup)
closePopups(this);
- if (!deleteGuard.isNull())
- reset();
+ reset();
}
}
@@ -970,7 +958,8 @@ void QWaylandWindow::handleScreenChanged()
#if QT_CONFIG(cursor)
void QWaylandWindow::setMouseCursor(QWaylandInputDevice *device, const QCursor &cursor)
{
- device->setCursor(cursor, waylandScreen());
+ int fallbackBufferScale = int(devicePixelRatio());
+ device->setCursor(&cursor, {}, fallbackBufferScale);
}
void QWaylandWindow::restoreMouseCursor(QWaylandInputDevice *device)
@@ -984,16 +973,6 @@ void QWaylandWindow::requestActivateWindow()
qCWarning(lcQpaWayland) << "Wayland does not support QWindow::requestActivate()";
}
-void QWaylandWindow::unfocus()
-{
-#if QT_CONFIG(clipboard)
- QWaylandInputDevice *inputDevice = mDisplay->currentInputDevice();
- if (inputDevice && inputDevice->dataDevice()) {
- inputDevice->dataDevice()->invalidateSelectionOffer();
- }
-#endif
-}
-
bool QWaylandWindow::isExposed() const
{
if (mShellSurface)
diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h
index 7791c5e4..43eaba64 100644
--- a/src/client/qwaylandwindow_p.h
+++ b/src/client/qwaylandwindow_p.h
@@ -154,7 +154,6 @@ public:
void requestActivateWindow() override;
bool isExposed() const override;
bool isActive() const override;
- void unfocus();
QWaylandAbstractDecoration *decoration() const;
@@ -200,6 +199,9 @@ public:
public slots:
void applyConfigure();
+signals:
+ void wlSurfaceDestroyed();
+
protected:
void surface_enter(struct ::wl_output *output) override;
void surface_leave(struct ::wl_output *output) override;
diff --git a/src/compositor/compositor_api/qwaylandcompositor.cpp b/src/compositor/compositor_api/qwaylandcompositor.cpp
index 173b50ce..05323543 100644
--- a/src/compositor/compositor_api/qwaylandcompositor.cpp
+++ b/src/compositor/compositor_api/qwaylandcompositor.cpp
@@ -243,6 +243,9 @@ QWaylandCompositorPrivate::~QWaylandCompositorPrivate()
delete data_device_manager;
#endif
+ // Some client buffer integrations need to clean up before the destroying the wl_display
+ client_buffer_integration.reset();
+
wl_display_destroy(display);
}
@@ -583,7 +586,7 @@ QByteArray QWaylandCompositor::socketName() const
* \qmlmethod QtWaylandCompositor::WaylandCompositor::addSocketDescriptor(fd)
* \since 5.12
*
- * Listen for client connections on a file descriptor referring to a
+ * Listen for client connections on a file descriptor, \a fd, referring to a
* server socket already bound and listening.
*
* Does not take ownership of the file descriptor; it must be closed
@@ -595,7 +598,7 @@ QByteArray QWaylandCompositor::socketName() const
*/
/*!
- * Listen for client connections on a file descriptor referring to a
+ * Listen for client connections on a file descriptor, \a fd, referring to a
* server socket already bound and listening.
*
* Does not take ownership of the file descriptor; it must be closed
diff --git a/src/compositor/compositor_api/qwaylandkeyboard.cpp b/src/compositor/compositor_api/qwaylandkeyboard.cpp
index 68d855a6..5f3bd3d4 100644
--- a/src/compositor/compositor_api/qwaylandkeyboard.cpp
+++ b/src/compositor/compositor_api/qwaylandkeyboard.cpp
@@ -486,7 +486,7 @@ QWaylandClient *QWaylandKeyboard::focusClient() const
/*!
* Sends the current key modifiers to \a client with the given \a serial.
*/
-void QWaylandKeyboard::sendKeyModifiers(QWaylandClient *client, uint serial)
+void QWaylandKeyboard::sendKeyModifiers(QWaylandClient *client, uint32_t serial)
{
Q_D(QWaylandKeyboard);
QtWaylandServer::wl_keyboard::Resource *resource = d->resourceMap().value(client->client());
diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp
index 6e41c7c0..fb73760b 100644
--- a/src/compositor/compositor_api/qwaylandquickitem.cpp
+++ b/src/compositor/compositor_api/qwaylandquickitem.cpp
@@ -295,7 +295,8 @@ public:
}
auto texture = buffer.toOpenGLTexture();
- m_sgTex = surfaceItem->window()->createTextureFromId(texture->textureId() , QSize(surfaceItem->width(), surfaceItem->height()), opt);
+ auto size = surface->bufferSize();
+ m_sgTex = surfaceItem->window()->createTextureFromId(texture->textureId(), size, opt);
}
}
emit textureChanged();
@@ -825,6 +826,15 @@ void QWaylandQuickItem::setOutput(QWaylandOutput *output)
}
/*!
+ * \qmlproperty bool QtWaylandCompositor::WaylandQuickItem::bufferLocked
+ *
+ * This property holds whether the item's buffer is currently locked. As long as
+ * the buffer is locked, it will not be released and returned to the client.
+ *
+ * The default is false.
+ */
+
+/*!
* \property QWaylandQuickItem::bufferLocked
*
* This property holds whether the item's buffer is currently locked. As long as
@@ -1058,6 +1068,14 @@ bool QWaylandQuickItem::inputRegionContains(const QPointF &localPosition)
}
/*!
+ * \qmlmethod point WaylandQuickItem::mapToSurface(point point)
+ *
+ * Maps the given \a point in this item's coordinate system to the equivalent
+ * point within the Wayland surface's coordinate system, and returns the mapped
+ * coordinate.
+ */
+
+/*!
* Maps the given \a point in this item's coordinate system to the equivalent
* point within the Wayland surface's coordinate system, and returns the mapped
* coordinate.
@@ -1075,9 +1093,19 @@ QPointF QWaylandQuickItem::mapToSurface(const QPointF &point) const
}
/*!
+ * \qmlmethod point WaylandQuickItem::mapFromSurface(point point)
+ * \since 5.13
+ *
* Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent
* point within this item's coordinate system, and returns the mapped coordinate.
*/
+
+/*!
+ * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent
+ * point within this item's coordinate system, and returns the mapped coordinate.
+ *
+ * \since 5.13
+ */
QPointF QWaylandQuickItem::mapFromSurface(const QPointF &point) const
{
Q_D(const QWaylandQuickItem);
diff --git a/src/compositor/compositor_api/qwaylandquickitem.h b/src/compositor/compositor_api/qwaylandquickitem.h
index 269e5556..2aa8e1b7 100644
--- a/src/compositor/compositor_api/qwaylandquickitem.h
+++ b/src/compositor/compositor_api/qwaylandquickitem.h
@@ -102,7 +102,7 @@ public:
bool inputRegionContains(const QPointF &localPosition) const;
bool inputRegionContains(const QPointF &localPosition);
Q_INVOKABLE QPointF mapToSurface(const QPointF &point) const;
- Q_INVOKABLE QPointF mapFromSurface(const QPointF &point) const;
+ Q_REVISION(13) Q_INVOKABLE QPointF mapFromSurface(const QPointF &point) const;
bool sizeFollowsSurface() const;
void setSizeFollowsSurface(bool sizeFollowsSurface);
diff --git a/src/compositor/compositor_api/qwaylandsurface.cpp b/src/compositor/compositor_api/qwaylandsurface.cpp
index cc61fb17..826cd3fd 100644
--- a/src/compositor/compositor_api/qwaylandsurface.cpp
+++ b/src/compositor/compositor_api/qwaylandsurface.cpp
@@ -481,6 +481,7 @@ bool QWaylandSurface::hasContent() const
/*!
* \qmlproperty rect QtWaylandCompositor::WaylandSurface::sourceGeometry
+ * \since 5.13
*
* This property describes the portion of the attached Wayland buffer that should
* be drawn on the screen. The coordinates are from the corner of the buffer and are
@@ -493,6 +494,7 @@ bool QWaylandSurface::hasContent() const
/*!
* \property QWaylandSurface::sourceGeometry
+ * \since 5.13
*
* This property describes the portion of the attached QWaylandBuffer that should
* be drawn on the screen. The coordinates are from the corner of the buffer and are
@@ -510,6 +512,7 @@ QRectF QWaylandSurface::sourceGeometry() const
/*!
* \qmlproperty size QtWaylandCompositor::WaylandSurface::destinationSize
+ * \since 5.13
*
* This property holds the size of this WaylandSurface in surface coordinates.
*
@@ -519,6 +522,7 @@ QRectF QWaylandSurface::sourceGeometry() const
/*!
* \property QWaylandSurface::destinationSize
+ * \since 5.13
*
* This property holds the size of this WaylandSurface in surface coordinates.
*
@@ -843,7 +847,7 @@ QList<QWaylandView *> QWaylandSurface::views() const
}
/*!
- * Returns the QWaylandSurface corresponding to the Wayland resource \a res.
+ * Returns the QWaylandSurface corresponding to the Wayland resource \a resource.
*/
QWaylandSurface *QWaylandSurface::fromResource(::wl_resource *resource)
{
@@ -862,11 +866,12 @@ struct wl_resource *QWaylandSurface::resource() const
}
/*!
- * Sets a \a role on the surface. A role defines how a surface will be mapped on screen, without a role
- * a surface is supposed to be hidden. Only one role at all times can be set on a surface. Although
+ * Sets a \a role on the surface. A role defines how a surface will be mapped on screen; without a role
+ * a surface is supposed to be hidden. Only one role can be set on a surface, at all times. Although
* setting the same role many times is allowed, attempting to change the role of a surface will trigger
* a protocol error to the \a errorResource and send an \a errorCode to the client.
*
+ * Returns true if a role can be assigned; false otherwise.
*/
bool QWaylandSurface::setRole(QWaylandSurfaceRole *role, wl_resource *errorResource, uint32_t errorCode)
{
diff --git a/src/compositor/compositor_api/qwaylandsurface.h b/src/compositor/compositor_api/qwaylandsurface.h
index 9bf84290..667f911c 100644
--- a/src/compositor/compositor_api/qwaylandsurface.h
+++ b/src/compositor/compositor_api/qwaylandsurface.h
@@ -81,8 +81,9 @@ class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandSurface : public QWaylandObject
Q_OBJECT
Q_DECLARE_PRIVATE(QWaylandSurface)
Q_PROPERTY(QWaylandClient *client READ client CONSTANT)
- Q_PROPERTY(QRectF sourceGeometry READ sourceGeometry NOTIFY sourceGeometryChanged)
- Q_PROPERTY(QSize destinationSize READ destinationSize NOTIFY destinationSizeChanged)
+ Q_PROPERTY(QRectF sourceGeometry READ sourceGeometry NOTIFY sourceGeometryChanged REVISION 13)
+ Q_PROPERTY(QSize destinationSize READ destinationSize NOTIFY destinationSizeChanged REVISION 13)
+ Q_PROPERTY(QSize bufferSize READ bufferSize NOTIFY bufferSizeChanged REVISION 13)
#if QT_DEPRECATED_SINCE(5, 13)
Q_PROPERTY(QSize size READ size NOTIFY sizeChanged) // Qt 6: Remove
#endif
@@ -164,12 +165,12 @@ Q_SIGNALS:
void damaged(const QRegion &rect);
void parentChanged(QWaylandSurface *newParent, QWaylandSurface *oldParent);
void childAdded(QWaylandSurface *child);
- void sourceGeometryChanged();
- void destinationSizeChanged();
+ Q_REVISION(13) void sourceGeometryChanged();
+ Q_REVISION(13) void destinationSizeChanged();
#if QT_DEPRECATED_SINCE(5, 13)
QT_DEPRECATED void sizeChanged();
#endif
- void bufferSizeChanged();
+ Q_REVISION(13) void bufferSizeChanged();
void bufferScaleChanged();
void offsetForNextFrame(const QPoint &offset);
void contentOrientationChanged();
diff --git a/src/compositor/configure.json b/src/compositor/configure.json
index ca1493af..db80543f 100644
--- a/src/compositor/configure.json
+++ b/src/compositor/configure.json
@@ -1,5 +1,6 @@
{
"module": "waylandcompositor",
+ "condition": "module.gui",
"depends": [
"gui-private"
],
diff --git a/src/compositor/extensions/qwaylandiviapplication.cpp b/src/compositor/extensions/qwaylandiviapplication.cpp
index 57b1627b..4ac5c273 100644
--- a/src/compositor/extensions/qwaylandiviapplication.cpp
+++ b/src/compositor/extensions/qwaylandiviapplication.cpp
@@ -140,7 +140,7 @@ QByteArray QWaylandIviApplication::interfaceName()
* \qmlsignal void QtWaylandCompositor::IviApplication::iviSurfaceRequested(WaylandSurface surface, int iviId, WaylandResource resource)
*
* This signal is emitted when the client has requested an \c ivi_surface to be associated
- * with \a surface, which is identified by \a id. The handler for this signal is
+ * with \a surface, which is identified by \a iviId. The handler for this signal is
* expected to create the ivi surface and initialize it within the scope of the
* signal emission. If no ivi surface is created, a default one will be created instead.
*/
@@ -149,7 +149,7 @@ QByteArray QWaylandIviApplication::interfaceName()
* \fn void QWaylandIviApplication::iviSurfaceRequested(QWaylandSurface *surface, uint iviId, const QWaylandResource &resource)
*
* This signal is emitted when the client has requested an \c ivi_surface to be associated
- * with \a surface, which is identified by \a id. The handler for this signal is
+ * with \a surface, which is identified by \a iviId. The handler for this signal is
* expected to create the ivi surface and initialize it within the scope of the
* signal emission. If no ivi surface is created, a default one will be created instead.
*/
diff --git a/src/compositor/extensions/qwaylandxdgdecorationv1.cpp b/src/compositor/extensions/qwaylandxdgdecorationv1.cpp
index 1abd5e3f..89c56acd 100644
--- a/src/compositor/extensions/qwaylandxdgdecorationv1.cpp
+++ b/src/compositor/extensions/qwaylandxdgdecorationv1.cpp
@@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE
\qmltype XdgDecorationManagerV1
\inqmlmodule QtWayland.Compositor
\since 5.12
- \brief Provides an extension for negotiation of server-side and client-side window decorations
+ \brief Provides an extension for negotiation of server-side and client-side window decorations.
The XdgDecorationManagerV1 extension provides a way for a compositor to announce support for
server-side window decorations, and for xdg-shell clients to communicate whether they prefer
@@ -82,7 +82,7 @@ QT_BEGIN_NAMESPACE
\class QWaylandXdgDecorationManagerV1
\inmodule QtWaylandCompositor
\since 5.12
- \brief Provides an extension for negotiation of server-side and client-side window decorations
+ \brief Provides an extension for negotiation of server-side and client-side window decorations.
The QWaylandXdgDecorationManagerV1 extension provides a way for a compositor to announce support
for server-side window decorations, and for xdg-shell clients to communicate whether they prefer
diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
index 6455b6fa..13b78241 100644
--- a/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
+++ b/src/hardwareintegration/client/wayland-egl/qwaylandglcontext.cpp
@@ -145,6 +145,7 @@ public:
}
void blit(QWaylandEglWindow *window)
{
+ Q_ASSERT(window->wlSurface());
QOpenGLTextureCache *cache = QOpenGLTextureCache::cacheForContext(m_context->context());
QSize surfaceSize = window->surfaceSize();
diff --git a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp
index 2cadf850..88dab2ab 100644
--- a/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp
+++ b/src/hardwareintegration/compositor/wayland-egl/waylandeglclientbufferintegration.cpp
@@ -186,6 +186,7 @@ public:
EGLDisplay egl_display = EGL_NO_DISPLAY;
bool valid = false;
bool display_bound = false;
+ ::wl_display *wlDisplay = nullptr;
QOffscreenSurface *offscreenSurface = nullptr;
QOpenGLContext *localContext = nullptr;
QVector<QOpenGLTexture *> orphanedTextures;
@@ -394,6 +395,12 @@ WaylandEglClientBufferIntegration::WaylandEglClientBufferIntegration()
WaylandEglClientBufferIntegration::~WaylandEglClientBufferIntegration()
{
WaylandEglClientBufferIntegrationPrivate::shuttingDown = true;
+ Q_D(WaylandEglClientBufferIntegration);
+ if (d->egl_unbind_wayland_display && d->display_bound) {
+ Q_ASSERT(d->wlDisplay);
+ if (!d->egl_unbind_wayland_display(d->egl_display, d->wlDisplay))
+ qWarning() << "Qt Wayland Compositor: eglUnbindWaylandDisplayWL failed";
+ }
}
void WaylandEglClientBufferIntegration::initializeHardware(struct wl_display *display)
@@ -450,6 +457,7 @@ void WaylandEglClientBufferIntegration::initializeHardware(struct wl_display *di
qWarning("QtCompositor: Could not bind Wayland display. Ignoring.");
}
}
+ d->wlDisplay = display;
}
d->funcs = new QEGLStreamConvenience;
diff --git a/src/imports/compositor/compositor.pro b/src/imports/compositor/compositor.pro
index 50b26d4d..f5c7567a 100644
--- a/src/imports/compositor/compositor.pro
+++ b/src/imports/compositor/compositor.pro
@@ -1,7 +1,7 @@
CXX_MODULE = qml
TARGET = qwaylandcompositorplugin
TARGETPATH = QtWayland/Compositor
-IMPORT_VERSION = 1.3
+IMPORT_VERSION = 1.$$QT_MINOR_VERSION
HEADERS += \
qwaylandmousetracker_p.h
@@ -43,4 +43,5 @@ CONFIG(debug, debug|release): QML_FILES += $$COMPOSITOR_QML_FILES
QT += quick-private qml-private gui-private core-private waylandcompositor waylandcompositor-private
+QMAKE_QMLPLUGINDUMP_FLAGS = -defaultplatform
load(qml_plugin)
diff --git a/src/imports/compositor/plugins.qmltypes b/src/imports/compositor/plugins.qmltypes
index a0ff2eb7..07a306e5 100644
--- a/src/imports/compositor/plugins.qmltypes
+++ b/src/imports/compositor/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtWayland.Compositor 1.3'
+// 'qmlplugindump -nonrelocatable -defaultplatform QtWayland.Compositor 1.13'
Module {
dependencies: ["QtQuick 2.0", "QtQuick.Window 2.11"]
@@ -280,8 +280,11 @@ Module {
name: "QWaylandQuickItem"
defaultProperty: "data"
prototype: "QQuickItem"
- exports: ["QtWayland.Compositor/WaylandQuickItem 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtWayland.Compositor/WaylandQuickItem 1.0",
+ "QtWayland.Compositor/WaylandQuickItem 1.13"
+ ]
+ exportMetaObjectRevisions: [0, 13]
Property { name: "compositor"; type: "QWaylandCompositor"; isReadonly: true; isPointer: true }
Property { name: "surface"; type: "QWaylandSurface"; isPointer: true }
Property { name: "paintEnabled"; type: "bool" }
@@ -326,6 +329,12 @@ Module {
Parameter { name: "point"; type: "QPointF" }
}
Method {
+ name: "mapFromSurface"
+ revision: 13
+ type: "QPointF"
+ Parameter { name: "point"; type: "QPointF" }
+ }
+ Method {
name: "inputMethodQuery"
type: "QVariant"
Parameter { name: "query"; type: "Qt::InputMethodQuery" }
@@ -357,8 +366,11 @@ Module {
name: "QWaylandQuickSurface"
defaultProperty: "data"
prototype: "QWaylandSurface"
- exports: ["QtWayland.Compositor/WaylandSurface 1.0"]
- exportMetaObjectRevisions: [0]
+ exports: [
+ "QtWayland.Compositor/WaylandSurface 1.0",
+ "QtWayland.Compositor/WaylandSurface 1.13"
+ ]
+ exportMetaObjectRevisions: [0, 13]
Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
Property { name: "useTextureAlpha"; type: "bool" }
Property { name: "clientRenderingEnabled"; type: "bool" }
@@ -474,6 +486,9 @@ Module {
}
}
Property { name: "client"; type: "QWaylandClient"; isReadonly: true; isPointer: true }
+ Property { name: "sourceGeometry"; revision: 13; type: "QRectF"; isReadonly: true }
+ Property { name: "destinationSize"; revision: 13; type: "QSize"; isReadonly: true }
+ Property { name: "bufferSize"; revision: 13; type: "QSize"; isReadonly: true }
Property { name: "size"; type: "QSize"; isReadonly: true }
Property { name: "bufferScale"; type: "int"; isReadonly: true }
Property { name: "contentOrientation"; type: "Qt::ScreenOrientation"; isReadonly: true }
@@ -493,6 +508,9 @@ Module {
name: "childAdded"
Parameter { name: "child"; type: "QWaylandSurface"; isPointer: true }
}
+ Signal { name: "sourceGeometryChanged"; revision: 13 }
+ Signal { name: "destinationSizeChanged"; revision: 13 }
+ Signal { name: "bufferSizeChanged"; revision: 13 }
Signal {
name: "offsetForNextFrame"
Parameter { name: "offset"; type: "QPoint" }
@@ -541,6 +559,15 @@ Module {
exportMetaObjectRevisions: [0]
Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
}
+ Component { name: "QWaylandWlScaler"; prototype: "QWaylandCompositorExtension" }
+ Component {
+ name: "QWaylandWlScalerQuickExtension"
+ defaultProperty: "data"
+ prototype: "QWaylandWlScaler"
+ exports: ["QtWayland.Compositor/WlScaler 1.13"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
+ }
Component {
name: "QWaylandWlShell"
prototype: "QWaylandShell"
@@ -1262,6 +1289,12 @@ Module {
Parameter { name: "point"; type: "QPointF" }
}
Method {
+ name: "mapFromSurface"
+ revision: 13
+ type: "QPointF"
+ Parameter { name: "point"; type: "QPointF" }
+ }
+ Method {
name: "inputMethodQuery"
type: "QVariant"
Parameter { name: "query"; type: "Qt::InputMethodQuery" }
diff --git a/src/imports/compositor/qwaylandquickcompositorplugin.cpp b/src/imports/compositor/qwaylandquickcompositorplugin.cpp
index d0a38c58..df7536ed 100644
--- a/src/imports/compositor/qwaylandquickcompositorplugin.cpp
+++ b/src/imports/compositor/qwaylandquickcompositorplugin.cpp
@@ -132,10 +132,12 @@ public:
{
qmlRegisterType<QWaylandQuickCompositorQuickExtensionContainer>(uri, 1, 0, "WaylandCompositor");
qmlRegisterType<QWaylandQuickItem>(uri, 1, 0, "WaylandQuickItem");
+ qmlRegisterType<QWaylandQuickItem, 13>(uri, 1, 13, "WaylandQuickItem");
qmlRegisterType<QWaylandQuickHardwareLayer>(uri, 1, 2, "WaylandHardwareLayer");
qmlRegisterType<QWaylandMouseTracker>(uri, 1, 0, "WaylandMouseTracker");
qmlRegisterType<QWaylandQuickOutput>(uri, 1, 0, "WaylandOutput");
qmlRegisterType<QWaylandQuickSurface>(uri, 1, 0, "WaylandSurface");
+ qmlRegisterType<QWaylandQuickSurface, 13>(uri, 1, 13, "WaylandSurface");
qmlRegisterType<QWaylandKeymap>(uri, 1, 0, "WaylandKeymap");
qmlRegisterUncreatableType<QWaylandCompositorExtension>(uri, 1, 0, "WaylandExtension", QObject::tr("Cannot create instance of WaylandExtension"));
diff --git a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
index 9e2c8e6d..e81aa903 100644
--- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
+++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp
@@ -380,7 +380,14 @@ void QWaylandXdgSurfaceV6::setGrabPopup(QWaylandWindow *parent, QWaylandInputDev
auto *top = m_shell->m_topmostGrabbingPopup;
if (top && top->m_xdgSurface != parentXdgSurface) {
- qCWarning(lcQpaWayland) << "setGrabPopup called for a surface that was not the topmost popup, positions might be off.";
+ qCWarning(lcQpaWayland) << "setGrabPopup called with a parent," << parentXdgSurface
+ << "which does not match the current topmost grabbing popup,"
+ << top->m_xdgSurface << "According to the xdg-shell-v6 protocol, this"
+ << "is not allowed. The wayland QPA plugin is currently handling"
+ << "it by setting the parent to the topmost grabbing popup."
+ << "Note, however, that this may cause positioning errors and"
+ << "popups closing unxpectedly because xdg-shell-v6 mandate that child"
+ << "popups close before parents";
parent = top->m_xdgSurface->m_window;
}
setPopup(parent);
diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
index acce08b5..e4543dba 100644
--- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
+++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
@@ -414,7 +414,14 @@ void QWaylandXdgSurface::setGrabPopup(QWaylandWindow *parent, QWaylandInputDevic
auto *top = m_shell->m_topmostGrabbingPopup;
if (top && top->m_xdgSurface != parentXdgSurface) {
- qCWarning(lcQpaWayland) << "setGrabPopup called for a surface that was not the topmost popup, positions might be off.";
+ qCWarning(lcQpaWayland) << "setGrabPopup called with a parent," << parentXdgSurface
+ << "which does not match the current topmost grabbing popup,"
+ << top->m_xdgSurface << "According to the xdg-shell protocol, this"
+ << "is not allowed. The wayland QPA plugin is currently handling"
+ << "it by setting the parent to the topmost grabbing popup."
+ << "Note, however, that this may cause positioning errors and"
+ << "popups closing unxpectedly because xdg-shell mandate that child"
+ << "popups close before parents";
parent = top->m_xdgSurface->m_window;
}
setPopup(parent);