From a7bb7210ac76a397b4aec8d8f32d902dc932d855 Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Mon, 18 Oct 2021 16:41:12 +0200 Subject: [PATCH] Introduce new qt-shell and an API for custom shells Adds a new API for writing custom shell extensions. This API is supported, but semi-public. Binary compatibility is not guaranteed. Also adds qt-shell, a new shell that maps directly to the QWindow API, and provides functionality that Qt provides on other window systems, such as absolute window positions and window activation. This shell is not intended for use on the desktop. This is a squashed commit of a development branch consisting of approximately 60 changes. Contributors: Eskil Abrahamsen Blomfeldt Paul Olav Tvete Task-number: QTBUG-94330 Task-number: QTBUG-91542 Change-Id: I419b6bd8179fe03e4da47d328c7ff4b4795b8a91 Reviewed-by: Eskil Abrahamsen Blomfeldt Reviewed-by: David Edmundson Reviewed-by: Paul Olav Tvete --- src/plugins/platforms/wayland/CMakeLists.txt | 3 +- src/plugins/platforms/wayland/configure.cmake | 5 ++ .../plugins/shellintegration/CMakeLists.txt | 4 ++ .../qwaylandfullscreenshellv1integration.cpp | 21 ++++--- .../qwaylandfullscreenshellv1integration.h | 3 +- .../wl-shell/qwaylandwlshellintegration.cpp | 19 +++--- .../wl-shell/qwaylandwlshellintegration_p.h | 2 +- .../xdg-shell/qwaylandxdgshellintegration.cpp | 20 +++---- .../xdg-shell/qwaylandxdgshellintegration_p.h | 2 +- .../platforms/wayland/qtwaylandclientglobal.h | 11 ---- .../platforms/wayland/qwaylanddisplay_p.h | 1 + .../platforms/wayland/qwaylandintegration.cpp | 16 +++++ .../wayland/qwaylandshellsurface.cpp | 44 ++++++++++++++ .../wayland/qwaylandshellsurface_p.h | 25 ++++++-- .../platforms/wayland/qwaylandwindow.cpp | 60 +++++++++++++++---- .../platforms/wayland/qwaylandwindow_p.h | 3 + .../qwaylandclientshellapi_p.h | 60 +++++++++++++++++++ .../qwaylandshellintegration.cpp | 57 ++++++++++++++++++ .../qwaylandshellintegration_p.h | 57 +++++++++++++++++- 19 files changed, 350 insertions(+), 63 deletions(-) create mode 100644 src/plugins/platforms/wayland/shellintegration/qwaylandclientshellapi_p.h create mode 100644 src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration.cpp diff --git a/src/plugins/platforms/wayland/CMakeLists.txt b/src/plugins/platforms/wayland/CMakeLists.txt index c7264d6774f..696a8b3d284 100644 --- a/src/plugins/platforms/wayland/CMakeLists.txt +++ b/src/plugins/platforms/wayland/CMakeLists.txt @@ -48,7 +48,8 @@ qt_internal_add_module(WaylandClient qwaylandtouch.cpp qwaylandtouch_p.h qwaylandwindow.cpp qwaylandwindow_p.h qwaylandwindowmanagerintegration.cpp qwaylandwindowmanagerintegration_p.h - shellintegration/qwaylandshellintegration_p.h + shellintegration/qwaylandclientshellapi_p.h + shellintegration/qwaylandshellintegration_p.h shellintegration/qwaylandshellintegration.cpp shellintegration/qwaylandshellintegrationfactory.cpp shellintegration/qwaylandshellintegrationfactory_p.h shellintegration/qwaylandshellintegrationplugin.cpp shellintegration/qwaylandshellintegrationplugin_p.h INCLUDE_DIRECTORIES diff --git a/src/plugins/platforms/wayland/configure.cmake b/src/plugins/platforms/wayland/configure.cmake index e0d0205b8a4..a8184fc1c38 100644 --- a/src/plugins/platforms/wayland/configure.cmake +++ b/src/plugins/platforms/wayland/configure.cmake @@ -37,6 +37,10 @@ qt_feature("wayland-client-xdg-shell" PRIVATE LABEL "xdg-shell" CONDITION QT_FEATURE_wayland_client ) +qt_feature("wayland-client-qt-shell" PRIVATE + LABEL "qt-shell" + CONDITION QT_FEATURE_wayland_client +) qt_feature("egl-extension-platform-wayland" PRIVATE LABEL "EGL wayland platform extension" CONDITION QT_FEATURE_wayland_client AND QT_FEATURE_opengl AND QT_FEATURE_egl AND TEST_egl_1_5_wayland @@ -45,4 +49,5 @@ qt_configure_add_summary_section(NAME "Qt Wayland Client Shell Integrations") qt_configure_add_summary_entry(ARGS "wayland-client-xdg-shell") qt_configure_add_summary_entry(ARGS "wayland-client-ivi-shell") qt_configure_add_summary_entry(ARGS "wayland-client-wl-shell") +qt_configure_add_summary_entry(ARGS "wayland-client-qt-shell") qt_configure_end_summary_section() # end of "Qt Wayland Client Shell Integrations" section diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/CMakeLists.txt b/src/plugins/platforms/wayland/plugins/shellintegration/CMakeLists.txt index fef15fe798e..eefa0227d4a 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/CMakeLists.txt +++ b/src/plugins/platforms/wayland/plugins/shellintegration/CMakeLists.txt @@ -12,3 +12,7 @@ endif() if(QT_FEATURE_wayland_client_xdg_shell) add_subdirectory(xdg-shell) endif() + +if(QT_FEATURE_wayland_client_qt_shell) + add_subdirectory(qt-shell) +endif() diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp index 0cd2cb1e893..033cbf6ec02 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp @@ -44,23 +44,22 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -bool QWaylandFullScreenShellV1Integration::initialize(QWaylandDisplay *display) +bool QWaylandFullScreenShellV1Integration::initialize() { - for (const QWaylandDisplay::RegistryGlobal &global : display->globals()) { - if (global.interface == QLatin1String("zwp_fullscreen_shell_v1") && !m_shell) { - m_shell.reset(new QtWayland::zwp_fullscreen_shell_v1(display->wl_registry(), global.id, global.version)); - break; - } - } - + if (m_shell) + return true; + wl_registry *registry; + uint32_t id; + uint32_t version; + bool found = findGlobal(QLatin1String("zwp_fullscreen_shell_v1"), ®istry, &id, &version); + if (found) + m_shell.reset(new QtWayland::zwp_fullscreen_shell_v1(registry, id, version)); if (!m_shell) { qCDebug(lcQpaWayland) << "Couldn't find global zwp_fullscreen_shell_v1 for fullscreen-shell"; return false; } - - return QWaylandShellIntegration::initialize(display); + return true; } - QWaylandShellSurface *QWaylandFullScreenShellV1Integration::createShellSurface(QWaylandWindow *window) { return new QWaylandFullScreenShellV1Surface(m_shell.data(), window); diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h b/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h index 131f9e72097..da99f6c4e0e 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h +++ b/src/plugins/platforms/wayland/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h @@ -43,6 +43,7 @@ #include #include #include +#include #include "qwayland-fullscreen-shell-unstable-v1.h" @@ -53,7 +54,7 @@ namespace QtWaylandClient { class Q_WAYLAND_CLIENT_EXPORT QWaylandFullScreenShellV1Integration : public QWaylandShellIntegration { public: - bool initialize(QWaylandDisplay *display) override; + bool initialize() override; QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; private: diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp index 354ee19b886..7353cb1f968 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp @@ -47,15 +47,16 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -bool QWaylandWlShellIntegration::initialize(QWaylandDisplay *display) +bool QWaylandWlShellIntegration::initialize() { - const auto globals = display->globals(); - for (QWaylandDisplay::RegistryGlobal global : globals) { - if (global.interface == QLatin1String("wl_shell")) { - m_wlShell = new QtWayland::wl_shell(display->wl_registry(), global.id, 1); - break; - } - } + if (m_wlShell) + return true; + wl_registry *registry; + uint32_t id; + uint32_t version; + bool found = findGlobal(QLatin1String("wl_shell"), ®istry, &id, &version); + if (found) + m_wlShell = new QtWayland::wl_shell(registry, id, 1); if (!m_wlShell) { qCDebug(lcQpaWayland) << "Couldn't find global wl_shell"; @@ -66,7 +67,7 @@ bool QWaylandWlShellIntegration::initialize(QWaylandDisplay *display) << "\"xdg-shell\" if supported by the compositor" << "by setting the environment variable QT_WAYLAND_SHELL_INTEGRATION"; - return QWaylandShellIntegration::initialize(display); + return true; } QWaylandShellSurface *QWaylandWlShellIntegration::createShellSurface(QWaylandWindow *window) diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h b/src/plugins/platforms/wayland/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h index 3d76cc31073..47815d54a18 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h +++ b/src/plugins/platforms/wayland/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h @@ -63,7 +63,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandWlShellIntegration : public QWaylandShellI { public: QWaylandWlShellIntegration() {} - bool initialize(QWaylandDisplay *) override; + bool initialize() override; QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override; diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp index fcdd435c6b7..8390d76f68e 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration.cpp @@ -47,21 +47,21 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -bool QWaylandXdgShellIntegration::initialize(QWaylandDisplay *display) +bool QWaylandXdgShellIntegration::initialize() { - for (QWaylandDisplay::RegistryGlobal global : display->globals()) { - if (global.interface == QLatin1String("xdg_wm_base")) { - m_xdgShell.reset(new QWaylandXdgShell(display, global.id, global.version)); - break; - } - } - + if (m_xdgShell) + return true; + wl_registry *registry; + uint32_t id; + uint32_t version; + bool found = findGlobal(QLatin1String("xdg_wm_base"), ®istry, &id, &version); + if (found) + m_xdgShell.reset(new QWaylandXdgShell(m_display, id, version)); if (!m_xdgShell) { qCDebug(lcQpaWayland) << "Couldn't find global xdg_wm_base for xdg-shell stable"; return false; } - - return QWaylandShellIntegration::initialize(display); + return true; } QWaylandShellSurface *QWaylandXdgShellIntegration::createShellSurface(QWaylandWindow *window) diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h index fced9eb07c2..cd54dd489d3 100644 --- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h +++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshellintegration_p.h @@ -63,7 +63,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgShellIntegration : public QWaylandShell { public: QWaylandXdgShellIntegration() {} - bool initialize(QWaylandDisplay *display) override; + bool initialize() override; QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override; diff --git a/src/plugins/platforms/wayland/qtwaylandclientglobal.h b/src/plugins/platforms/wayland/qtwaylandclientglobal.h index 5f474f378b9..42f8ec8aac4 100644 --- a/src/plugins/platforms/wayland/qtwaylandclientglobal.h +++ b/src/plugins/platforms/wayland/qtwaylandclientglobal.h @@ -40,17 +40,6 @@ #ifndef QWAYLANDCLIENTGLOBAL_H #define QWAYLANDCLIENTGLOBAL_H -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - #include #include diff --git a/src/plugins/platforms/wayland/qwaylanddisplay_p.h b/src/plugins/platforms/wayland/qwaylanddisplay_p.h index 8be911188cf..cb345d7a049 100644 --- a/src/plugins/platforms/wayland/qwaylanddisplay_p.h +++ b/src/plugins/platforms/wayland/qwaylanddisplay_p.h @@ -217,6 +217,7 @@ public: bool isKeyboardAvailable() const; void initEventThread(); + public slots: void blockingReadEvents(); void flushRequests(); diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp index 73e4222fffb..274ae9a9df7 100644 --- a/src/plugins/platforms/wayland/qwaylandintegration.cpp +++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp @@ -83,12 +83,14 @@ #include "qwaylandserverbufferintegration_p.h" #include "qwaylandserverbufferintegrationfactory_p.h" +#include "qwaylandshellsurface_p.h" #include "qwaylandshellintegration_p.h" #include "qwaylandshellintegrationfactory_p.h" #include "qwaylandinputdeviceintegration_p.h" #include "qwaylandinputdeviceintegrationfactory_p.h" +#include "qwaylandwindow_p.h" #if QT_CONFIG(accessibility_atspi_bridge) #include @@ -119,6 +121,19 @@ QWaylandIntegration::QWaylandIntegration() mFailed = true; return; } + + // ### Not ideal... + // We don't want to use QPlatformWindow::requestActivate here, since that gives a warning + // for most shells. Also, we don't want to put this into the specific shells that can use + // it, since we want to support more than one shell in one client. + // In addition, this will send a new requestActivate when the focus object changes, even if + // the focus window stays the same. + QObject::connect(qApp, &QGuiApplication::focusObjectChanged, qApp, [](){ + QWindow *fw = QGuiApplication::focusWindow(); + auto *w = fw ? static_cast(fw->handle()) : nullptr; + if (w && w->shellSurface()) + w->shellSurface()->requestActivate(); + }); } QWaylandIntegration::~QWaylandIntegration() @@ -435,6 +450,7 @@ void QWaylandIntegration::initializeShellIntegration() } else { preferredShells << QLatin1String("xdg-shell"); preferredShells << QLatin1String("wl-shell") << QLatin1String("ivi-shell"); + preferredShells << QLatin1String("qt-shell"); } for (const QString &preferredShell : qAsConst(preferredShells)) { diff --git a/src/plugins/platforms/wayland/qwaylandshellsurface.cpp b/src/plugins/platforms/wayland/qwaylandshellsurface.cpp index 1dfdfd5e007..81e05a444e8 100644 --- a/src/plugins/platforms/wayland/qwaylandshellsurface.cpp +++ b/src/plugins/platforms/wayland/qwaylandshellsurface.cpp @@ -40,6 +40,7 @@ #include "qwaylandshellsurface_p.h" #include "qwaylandwindow_p.h" #include "qwaylandextendedsurface_p.h" +#include "qwaylandinputdevice_p.h" QT_BEGIN_NAMESPACE @@ -61,6 +62,49 @@ void QWaylandShellSurface::sendProperty(const QString &name, const QVariant &val Q_UNUSED(value); } +QPlatformWindow *QWaylandShellSurface::platformWindow() +{ + return m_window; +} + +wl_surface *QWaylandShellSurface::wlSurface() +{ + return m_window ? m_window->wlSurface() : nullptr; +} + +void QWaylandShellSurface::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) +{ + m_window->resizeFromApplyConfigure(sizeWithMargins, offset); +} + +void QWaylandShellSurface::repositionFromApplyConfigure(const QPoint &position) +{ + m_window->repositionFromApplyConfigure(position); +} + +void QWaylandShellSurface::setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins) +{ + m_window->setGeometryFromApplyConfigure(globalPosition, sizeWithMargins); +} + +void QWaylandShellSurface::applyConfigureWhenPossible() +{ + m_window->applyConfigureWhenPossible(); +} + +void QWaylandShellSurface::handleActivationChanged(bool activated) +{ + if (activated) + m_window->display()->handleWindowActivated(m_window); + else + m_window->display()->handleWindowDeactivated(m_window); +} + +uint32_t QWaylandShellSurface::getSerial(QWaylandInputDevice *inputDevice) +{ + return inputDevice->serial(); +} + } QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/qwaylandshellsurface_p.h b/src/plugins/platforms/wayland/qwaylandshellsurface_p.h index 080d13851bc..cdf1abdbc71 100644 --- a/src/plugins/platforms/wayland/qwaylandshellsurface_p.h +++ b/src/plugins/platforms/wayland/qwaylandshellsurface_p.h @@ -53,14 +53,16 @@ #include #include - -#include +#include #include +struct wl_surface; + QT_BEGIN_NAMESPACE class QVariant; class QWindow; +class QPlatformWindow; namespace QtWaylandClient { @@ -90,15 +92,30 @@ public: virtual void sendProperty(const QString &name, const QVariant &value); - inline QWaylandWindow *window() { return m_window; } - virtual void applyConfigure() {} virtual void requestWindowStates(Qt::WindowStates states) {Q_UNUSED(states);} virtual bool wantsDecorations() const { return false; } + virtual QMargins serverSideFrameMargins() const { return QMargins(); } virtual void propagateSizeHints() {} virtual void setWindowGeometry(const QRect &rect) { Q_UNUSED(rect); } + virtual void setWindowPosition(const QPoint &position) { Q_UNUSED(position); } + + virtual bool requestActivate() { return false; } + + inline QWaylandWindow *window() { return m_window; } + QPlatformWindow *platformWindow(); + struct wl_surface *wlSurface(); + +protected: + void resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset = {0, 0}); + void repositionFromApplyConfigure(const QPoint &position); + void setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins); + void applyConfigureWhenPossible(); + void handleActivationChanged(bool activated); + + static uint32_t getSerial(QWaylandInputDevice *inputDevice); private: QWaylandWindow *m_window = nullptr; diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index 0cc185522b2..635c92a085e 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -342,7 +342,7 @@ void QWaylandWindow::setGeometry_helper(const QRect &rect) qBound(minimum.height(), rect.height(), maximum.height()))); if (mSubSurfaceWindow) { - QMargins m = QPlatformWindow::parent()->frameMargins(); + QMargins m = static_cast(QPlatformWindow::parent())->clientSideMargins(); mSubSurfaceWindow->set_position(rect.x() + m.left(), rect.y() + m.top()); QWaylandWindow *parentWindow = mSubSurfaceWindow->parent(); @@ -372,16 +372,45 @@ void QWaylandWindow::setGeometry(const QRect &rect) if (isExposed() && !mInResizeFromApplyConfigure && exposeGeometry != mLastExposeGeometry) sendExposeEvent(exposeGeometry); - if (mShellSurface && isExposed()) + if (mShellSurface && isExposed()) { mShellSurface->setWindowGeometry(windowContentGeometry()); + if (!qt_window_private(window())->positionAutomatic) + mShellSurface->setWindowPosition(windowGeometry().topLeft()); + } if (isOpaque() && mMask.isEmpty()) setOpaqueArea(rect); } +void QWaylandWindow::setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins) +{ + QMargins margins = clientSideMargins(); + + QPoint positionWithoutMargins = globalPosition + QPoint(margins.left(), margins.top()); + int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left() + margins.right()), 1); + int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top() + margins.bottom()), 1); + + QRect geometry(positionWithoutMargins, QSize(widthWithoutMargins, heightWithoutMargins)); + + mInResizeFromApplyConfigure = true; + setGeometry(geometry); + mInResizeFromApplyConfigure = false; +} + +void QWaylandWindow::repositionFromApplyConfigure(const QPoint &globalPosition) +{ + QMargins margins = clientSideMargins(); + QPoint positionWithoutMargins = globalPosition + QPoint(margins.left(), margins.top()); + + QRect geometry(positionWithoutMargins, windowGeometry().size()); + mInResizeFromApplyConfigure = true; + setGeometry(geometry); + mInResizeFromApplyConfigure = false; +} + void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) { - QMargins margins = frameMargins(); + QMargins margins = clientSideMargins(); // Exclude shadows from margins once they are excluded from window geometry // 1) First resizeFromApplyConfigure() call will have sizeWithMargins equal to surfaceSize() @@ -394,7 +423,8 @@ void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, cons int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left() + margins.right()), 1); int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top() + margins.bottom()), 1); - QRect geometry(windowGeometry().topLeft(), QSize(widthWithoutMargins, heightWithoutMargins)); + QRect geometry(windowGeometry().topLeft() + QPoint(margins.left(), margins.top()), + QSize(widthWithoutMargins, heightWithoutMargins)); mOffset += offset; mInResizeFromApplyConfigure = true; @@ -411,7 +441,6 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect) mLastExposeGeometry = rect; } - static QList> activePopups; void QWaylandWindow::closePopups(QWaylandWindow *parent) @@ -719,7 +748,15 @@ QMargins QWaylandWindow::frameMargins() const { if (mWindowDecoration) return mWindowDecoration->margins(); - return QPlatformWindow::frameMargins(); + else if (mShellSurface) + return mShellSurface->serverSideFrameMargins(); + else + return QPlatformWindow::frameMargins(); +} + +QMargins QWaylandWindow::clientSideMargins() const +{ + return mWindowDecoration ? mWindowDecoration->margins() : QMargins{}; } /*! @@ -727,7 +764,7 @@ QMargins QWaylandWindow::frameMargins() const */ QSize QWaylandWindow::surfaceSize() const { - return geometry().marginsAdded(frameMargins()).size(); + return geometry().marginsAdded(clientSideMargins()).size(); } /*! @@ -753,7 +790,7 @@ QRect QWaylandWindow::windowContentGeometry() const */ QPointF QWaylandWindow::mapFromWlSurface(const QPointF &surfacePosition) const { - const QMargins margins = frameMargins(); + const QMargins margins = clientSideMargins(); return QPointF(surfacePosition.x() - margins.left(), surfacePosition.y() - margins.top()); } @@ -980,7 +1017,7 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan #if QT_CONFIG(cursor) if (e.type == QEvent::Enter) { - QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins()); + QRect contentGeometry = windowContentGeometry().marginsRemoved(clientSideMargins()); if (contentGeometry.contains(e.local.toPoint())) restoreMouseCursor(inputDevice); } @@ -1213,7 +1250,8 @@ void QWaylandWindow::restoreMouseCursor(QWaylandInputDevice *device) void QWaylandWindow::requestActivateWindow() { - qCWarning(lcQpaWayland) << "Wayland does not support QWindow::requestActivate()"; + if (mShellSurface == nullptr || !mShellSurface->requestActivate()) + qCWarning(lcQpaWayland) << "Wayland does not support QWindow::requestActivate()"; } bool QWaylandWindow::isExposed() const @@ -1448,7 +1486,7 @@ bool QWaylandWindow::isOpaque() const void QWaylandWindow::setOpaqueArea(const QRegion &opaqueArea) { - const QRegion translatedOpaqueArea = opaqueArea.translated(frameMargins().left(), frameMargins().top()); + const QRegion translatedOpaqueArea = opaqueArea.translated(clientSideMargins().left(), clientSideMargins().top()); if (translatedOpaqueArea == mOpaqueArea || !mSurface) return; diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 17ed9fdca13..f2dbe337fc7 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -123,6 +123,8 @@ public: void setGeometry(const QRect &rect) override; void resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset = {0, 0}); + void repositionFromApplyConfigure(const QPoint &position); + void setGeometryFromApplyConfigure(const QPoint &globalPosition, const QSize &sizeWithMargins); void applyConfigureWhenPossible(); //rename to possible? @@ -239,6 +241,7 @@ protected: virtual void doHandleFrameCallback(); virtual QRect defaultGeometry() const; void sendExposeEvent(const QRect &rect); + QMargins clientSideMargins() const; QWaylandDisplay *mDisplay = nullptr; QScopedPointer mSurface; diff --git a/src/plugins/platforms/wayland/shellintegration/qwaylandclientshellapi_p.h b/src/plugins/platforms/wayland/shellintegration/qwaylandclientshellapi_p.h new file mode 100644 index 00000000000..a8863e7d3ed --- /dev/null +++ b/src/plugins/platforms/wayland/shellintegration/qwaylandclientshellapi_p.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandClient module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDCLIENTSHELLAPI_P_H +#define QWAYLANDCLIENTSHELLAPI_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// N O T E +// ------- +// This file provides a supported API for creating client-side shell +// extensions. Source compatibility will be preserved, but we may break +// forward and backward binary compatibility, even in patch releases. +// +// The supported API contains these classes: +// +// QtWaylandClient::QWaylandShellSurface +// QtWaylandClient::QWaylandShellIntegration +// QtWaylandClient::QWaylandShellIntegrationPlugin + +#include "QtWaylandClient/private/qwaylandshellsurface_p.h" +#include "QtWaylandClient/private/qwaylandshellintegration_p.h" +#include "QtWaylandClient/private/qwaylandshellintegrationplugin_p.h" + +#endif // QWAYLANDCLIENTSHELLAPI_P_H diff --git a/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration.cpp b/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration.cpp new file mode 100644 index 00000000000..7bc37792f8c --- /dev/null +++ b/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandClient module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qwaylandshellintegration_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +wl_surface *QWaylandShellIntegration::wlSurfaceForWindow(QWaylandWindow *window) +{ + return window->wlSurface(); +} + +bool QWaylandShellIntegration::findGlobal(const QString &interface, wl_registry **registry, uint32_t *id, uint32_t *version) +{ + for (QWaylandDisplay::RegistryGlobal &global : m_display->globals()) { + if (global.interface == interface) { + *registry = m_display->wl_registry(); + *id = global.id; + *version = global.version; + return true; + } + } + return false; +} + +} + +QT_END_NAMESPACE diff --git a/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration_p.h b/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration_p.h index b308ffe25ee..4de19e7860c 100644 --- a/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration_p.h +++ b/src/plugins/platforms/wayland/shellintegration/qwaylandshellintegration_p.h @@ -52,10 +52,19 @@ // #include -#include +#include + + + +#include + +struct wl_surface; +struct wl_registry; QT_BEGIN_NAMESPACE +class QWindow; + namespace QtWaylandClient { class QWaylandWindow; @@ -68,9 +77,12 @@ public: QWaylandShellIntegration() {} virtual ~QWaylandShellIntegration() {} - virtual bool initialize(QWaylandDisplay *display) { + bool initialize(QWaylandDisplay *display) { m_display = display; - return true; + return initialize(); + } + virtual bool initialize() { + return false; } virtual QWaylandShellSurface *createShellSurface(QWaylandWindow *window) = 0; virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) { @@ -79,10 +91,49 @@ public: return nullptr; } + static wl_surface *wlSurfaceForWindow(QWaylandWindow *window); + bool findGlobal(const QString &interface, wl_registry **registry, uint32_t *id, uint32_t *version); + protected: QWaylandDisplay *m_display = nullptr; }; +template +class Q_WAYLAND_CLIENT_EXPORT QWaylandShellIntegrationTemplate : public QWaylandShellIntegration, public QWaylandClientExtension +{ +public: + QWaylandShellIntegrationTemplate(const int ver) : + QWaylandClientExtension(ver) + { + } + + bool initialize() override + { + QWaylandClientExtension::initialize(); + return isActive(); + } + + const struct wl_interface *extensionInterface() const override + { + return T::interface(); + } + + void bind(struct ::wl_registry *registry, int id, int ver) override + { + T* instance = static_cast(this); + // Make sure lowest version is used of the supplied version from the + // developer and the version specified in the protocol and also the + // compositor version. + if (this->version() > T::interface()->version) { + qWarning("Supplied protocol version to QWaylandClientExtensionTemplate is higher than the version of the protocol, using protocol version instead."); + } + int minVersion = qMin(ver, qMin(T::interface()->version, this->version())); + setVersion(minVersion); + instance->init(registry, id, minVersion); + } +}; + + } QT_END_NAMESPACE