diff options
author | Paul Olav Tvete <paul.tvete@qt.io> | 2019-03-21 11:42:15 +0100 |
---|---|---|
committer | Paul Olav Tvete <paul.tvete@qt.io> | 2019-03-21 11:57:15 +0100 |
commit | 77e8ee63dc9ad0a4c139f35f8cf078d1a5bd315c (patch) | |
tree | 499f94df98fc25bca2982fba4dd67457f42bd6b9 /src | |
parent | df3a1761af2f20d59ae09a7adaa2f5b959047687 (diff) | |
parent | c0905957be0d7db90c9d9f05069a259575753dfe (diff) | |
download | qtwayland-77e8ee63dc9ad0a4c139f35f8cf078d1a5bd315c.tar.gz |
Merge remote-tracking branch 'qt/5.13' into dev
Change-Id: I3dc204fcaa71c01a80b0c622443012eb07964431
Diffstat (limited to 'src')
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); |