diff options
90 files changed, 1839 insertions, 838 deletions
diff --git a/.qmake.conf b/.qmake.conf index dc68d3884..097d8b948 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,3 @@ load(qt_build_config) -MODULE_VERSION = 5.11.0 +MODULE_VERSION = 5.12.0 @@ -70,8 +70,8 @@ the generic wl_shell or xdg_shell extensions that target desktop systems. Custom shell protocols are added through shell integration plugins. Which shell integration to use is determined by the QT_WAYLAND_SHELL_INTEGRATION -environment variable. If no shell is specified, the default is to first try -xdg-shell-v6 and then wl-shell. +environment variable. If no shell is specified, the default is to try shells in +the following order: xdg-shell-v6, wl-shell and then ivi-shell. Example Usage: diff --git a/examples/wayland/minimal-qml/main.qml b/examples/wayland/minimal-qml/main.qml index 0f0ca61bc..85c8c5783 100644 --- a/examples/wayland/minimal-qml/main.qml +++ b/examples/wayland/minimal-qml/main.qml @@ -67,6 +67,7 @@ WaylandCompositor { // resize/move, and forwarding of mouse and keyboard // events to the client process. ShellSurfaceItem { + autoCreatePopupItems: true shellSurface: modelData onSurfaceDestroyed: shellSurfaces.remove(index) } diff --git a/examples/wayland/pure-qml/qml/Chrome.qml b/examples/wayland/pure-qml/qml/Chrome.qml index e6fe97476..a63ee705c 100644 --- a/examples/wayland/pure-qml/qml/Chrome.qml +++ b/examples/wayland/pure-qml/qml/Chrome.qml @@ -52,15 +52,28 @@ import QtQuick 2.0 import QtWayland.Compositor 1.0 ShellSurfaceItem { - id: rootChrome + id: chrome property bool isChild: parent.shellSurface !== undefined + signal destroyAnimationFinished + + // If the client asks to show popups on this surface, automatically create child ShellSurfaceItems + autoCreatePopupItems: true + onSurfaceDestroyed: { bufferLocked = true; destroyAnimation.start(); } + transform: [ + Scale { + id: scaleTransform + origin.x: chrome.width / 2 + origin.y: chrome.height / 2 + } + ] + Connections { target: shellSurface @@ -76,17 +89,19 @@ ShellSurfaceItem { SequentialAnimation { id: destroyAnimation + ParallelAnimation { NumberAnimation { target: scaleTransform; property: "yScale"; to: 2/height; duration: 150 } NumberAnimation { target: scaleTransform; property: "xScale"; to: 0.4; duration: 150 } - NumberAnimation { target: rootChrome; property: "opacity"; to: rootChrome.isChild ? 0 : 1; duration: 150 } + NumberAnimation { target: chrome; property: "opacity"; to: chrome.isChild ? 0 : 1; duration: 150 } } NumberAnimation { target: scaleTransform; property: "xScale"; to: 0; duration: 150 } - ScriptAction { script: { rootChrome.destroy(); } } + ScriptAction { script: destroyAnimationFinished() } } SequentialAnimation { id: receivedFocusAnimation + ParallelAnimation { NumberAnimation { target: scaleTransform; property: "yScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad } NumberAnimation { target: scaleTransform; property: "xScale"; to: 1.02; duration: 100; easing.type: Easing.OutQuad } @@ -96,12 +111,4 @@ ShellSurfaceItem { NumberAnimation { target: scaleTransform; property: "xScale"; to: 1; duration: 100; easing.type: Easing.InOutQuad } } } - - transform: [ - Scale { - id:scaleTransform - origin.x: rootChrome.width / 2 - origin.y: rootChrome.height / 2 - } - ] } diff --git a/examples/wayland/pure-qml/qml/Keyboard.qml b/examples/wayland/pure-qml/qml/Keyboard.qml index e1f0a8b19..b2586ffd4 100644 --- a/examples/wayland/pure-qml/qml/Keyboard.qml +++ b/examples/wayland/pure-qml/qml/Keyboard.qml @@ -52,9 +52,8 @@ import QtQuick 2.5 import QtQuick.VirtualKeyboard 2.1 InputPanel { - id: inputPanel visible: active - y: active ? parent.height - inputPanel.height : parent.height + y: active ? parent.height - height : parent.height anchors.left: parent.left anchors.right: parent.right } diff --git a/examples/wayland/pure-qml/qml/Screen.qml b/examples/wayland/pure-qml/qml/Screen.qml index 562eb86c5..b156cb434 100644 --- a/examples/wayland/pure-qml/qml/Screen.qml +++ b/examples/wayland/pure-qml/qml/Screen.qml @@ -54,49 +54,69 @@ import QtWayland.Compositor 1.0 WaylandOutput { id: output - property alias surfaceArea: background - sizeFollowsWindow: true - window: Window { - id: screen - property QtObject output + property ListModel shellSurfaces: ListModel {} + property bool isNestedCompositor: Qt.platform.pluginName.startsWith("wayland") || Qt.platform.pluginName === "xcb" + + function handleShellSurface(shellSurface) { + shellSurfaces.append({shellSurface: shellSurface}); + } + + // During development, it can be useful to start the compositor inside X11 or + // another Wayland compositor. In such cases, set sizeFollowsWindow to true to + // enable resizing of the compositor window to be forwarded to the Wayland clients + // as the output (screen) changing resolution. Consider setting it to false if you + // are running the compositor using eglfs, linuxfb or similar QPA backends. + sizeFollowsWindow: output.isNestedCompositor + window: Window { width: 1024 height: 760 visible: true WaylandMouseTracker { id: mouseTracker + anchors.fill: parent - windowSystemCursorEnabled: true + // Set this to false to disable the outer mouse cursor when running nested + // compositors. Otherwise you would see two mouse cursors, one for each compositor. + windowSystemCursorEnabled: output.isNestedCompositor + Image { id: background + anchors.fill: parent fillMode: Image.Tile source: "qrc:/images/background.jpg" smooth: true + + Repeater { + model: output.shellSurfaces + // Chrome displays a shell surface on the screen (See Chrome.qml) + Chrome { + shellSurface: modelData + onDestroyAnimationFinished: output.shellSurfaces.remove(index) + } + } } + + // Virtual Keyboard Loader { anchors.fill: parent source: "Keyboard.qml" } + + // Draws the mouse cursor for a given Wayland seat WaylandCursorItem { - id: cursor inputEventsEnabled: false x: mouseTracker.mouseX y: mouseTracker.mouseY - seat: output.compositor.defaultSeat } } Shortcut { - sequence: "Meta+F" - onActivated: qtWindowManager.showIsFullScreen = !qtWindowManager.showIsFullScreen - } - - Shortcut { sequence: "Ctrl+Alt+Backspace" onActivated: Qt.quit() } diff --git a/examples/wayland/pure-qml/qml/main.qml b/examples/wayland/pure-qml/qml/main.qml index 77ed3ecd9..b188ebbb5 100644 --- a/examples/wayland/pure-qml/qml/main.qml +++ b/examples/wayland/pure-qml/qml/main.qml @@ -52,68 +52,21 @@ import QtQuick 2.0 import QtWayland.Compositor 1.1 WaylandCompositor { - id: comp + id: waylandCompositor - property var primarySurfacesArea: null - - Screen { - compositor: comp - } - - Component { - id: chromeComponent - Chrome { - } - } - - Component { - id: surfaceComponent - WaylandSurface { - } - } - - QtWindowManager { - id: qtWindowManager - onShowIsFullScreenChanged: console.debug("Show is fullscreen hint for Qt applications:", showIsFullScreen) - } - - WlShell { - onWlShellSurfaceCreated: { - chromeComponent.createObject(defaultOutput.surfaceArea, { "shellSurface": shellSurface } ); - } - } - - property variant viewsBySurface: ({}) - - XdgShellV5 { - onXdgSurfaceCreated: { - var item = chromeComponent.createObject(defaultOutput.surfaceArea, { "shellSurface": xdgSurface } ); - viewsBySurface[xdgSurface.surface] = item; - } - onXdgPopupCreated: { - var parentView = viewsBySurface[xdgPopup.parentSurface]; - var item = chromeComponent.createObject(parentView, { "shellSurface": xdgPopup } ); - viewsBySurface[xdgPopup.surface] = item; - } - } + Screen { id: screen; compositor: waylandCompositor } + // Shell surface extension. Needed to provide a window concept for Wayland clients. + // I.e. requests and events for maximization, minimization, resizing, closing etc. XdgShellV6 { - onToplevelCreated: { - var item = chromeComponent.createObject(defaultOutput.surfaceArea, { "shellSurface": xdgSurface } ); - viewsBySurface[xdgSurface.surface] = item; - } - onPopupCreated: { - var parentView = viewsBySurface[popup.parentXdgSurface.surface]; - var item = chromeComponent.createObject(parentView, { "shellSurface": xdgSurface } ); - viewsBySurface[xdgSurface.surface] = item; - } + onToplevelCreated: screen.handleShellSurface(xdgSurface) } - TextInputManager { + // Deprecated shell extension, still used by some clients + WlShell { + onWlShellSurfaceCreated: screen.handleShellSurface(shellSurface) } - onSurfaceRequested: { - var surface = surfaceComponent.createObject(comp, { } ); - surface.initialize(comp, client, id, version); - } + // Extension for Virtual keyboard support + TextInputManager {} } diff --git a/src/3rdparty/protocol/qt_attribution.json b/src/3rdparty/protocol/qt_attribution.json index f35dec4f3..94d77f609 100644 --- a/src/3rdparty/protocol/qt_attribution.json +++ b/src/3rdparty/protocol/qt_attribution.json @@ -41,7 +41,7 @@ Copyright (c) 2013 BMW Car IT GmbH" "Name": "Wayland XDG Shell Protocol", "QDocModule": "qtwaylandcompositor", "QtUsage": "Used in the Qt Wayland Compositor, and the Qt Wayland platform plugin.", - "Files": "xdg-shell.xml", + "Files": "xdg-shell-unstable-v5.xml xdg-shell-unstable-v6.xml", "Description": "The XDG-Shell protocol is an extended way to manage surfaces under Wayland compositors.", "Homepage": "https://wayland.freedesktop.org", diff --git a/src/3rdparty/protocol/xdg-shell.xml b/src/3rdparty/protocol/xdg-shell-unstable-v5.xml index f98e760d4..89c9d58c5 100644 --- a/src/3rdparty/protocol/xdg-shell.xml +++ b/src/3rdparty/protocol/xdg-shell-unstable-v5.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="UTF-8"?> -<protocol name="xdg_shell"> +<protocol name="xdg_shell_unstable_v5"> <copyright> Copyright © 2008-2013 Kristian Høgsberg diff --git a/src/client/client.pro b/src/client/client.pro index eae7ad52e..3b11902de 100644 --- a/src/client/client.pro +++ b/src/client/client.pro @@ -25,10 +25,9 @@ INCLUDEPATH += $$PWD/../shared WAYLANDCLIENTSOURCES += \ ../extensions/surface-extension.xml \ ../extensions/touch-extension.xml \ - ../extensions/qtkey-extension.xml \ + ../extensions/qt-key-unstable-v1.xml \ ../extensions/qt-windowmanager.xml \ ../3rdparty/protocol/text-input-unstable-v2.xml \ - ../3rdparty/protocol/xdg-shell.xml \ ../3rdparty/protocol/xdg-shell-unstable-v6.xml \ WAYLANDCLIENTSOURCES_SYSTEM += \ @@ -43,12 +42,6 @@ SOURCES += qwaylandintegration.cpp \ qwaylandscreen.cpp \ qwaylandshmwindow.cpp \ qwaylandshellsurface.cpp \ - qwaylandwlshellsurface.cpp \ - qwaylandwlshellintegration.cpp \ - qwaylandxdgshell.cpp \ - qwaylandxdgsurface.cpp \ - qwaylandxdgpopup.cpp \ - qwaylandxdgshellintegration.cpp \ qwaylandxdgshellv6.cpp \ qwaylandxdgshellv6integration.cpp \ qwaylandextendedsurface.cpp \ @@ -76,12 +69,6 @@ HEADERS += qwaylandintegration_p.h \ qwaylandbuffer_p.h \ qwaylandshmwindow_p.h \ qwaylandshellsurface_p.h \ - qwaylandwlshellsurface_p.h \ - qwaylandwlshellintegration_p.h \ - qwaylandxdgshell_p.h \ - qwaylandxdgsurface_p.h \ - qwaylandxdgpopup_p.h \ - qwaylandxdgshellintegration_p.h \ qwaylandxdgshellv6_p.h \ qwaylandxdgshellv6integration_p.h \ qwaylandextendedsurface_p.h \ diff --git a/src/client/qwaylandcursor.cpp b/src/client/qwaylandcursor.cpp index 21a6c0811..a356950c3 100644 --- a/src/client/qwaylandcursor.cpp +++ b/src/client/qwaylandcursor.cpp @@ -60,11 +60,14 @@ QWaylandCursor::QWaylandCursor(QWaylandScreen *screen) QByteArray cursorTheme = qgetenv("XCURSOR_THEME"); if (cursorTheme.isEmpty()) cursorTheme = QByteArray("default"); - QByteArray cursorSizeFromEnv = qgetenv("XCURSOR_SIZE"); - bool hasCursorSize = false; - int cursorSize = cursorSizeFromEnv.toInt(&hasCursorSize); - if (!hasCursorSize || cursorSize <= 0) + int cursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE"); + if (cursorSize <= 0) cursorSize = 32; + + // wl_surface.set_buffer_scale is not supported on earlier versions + if (mDisplay->compositorVersion() >= 3) + cursorSize *= screen->devicePixelRatio(); + mCursorTheme = wl_cursor_theme_load(cursorTheme, cursorSize, mDisplay->shm()->object()); if (!mCursorTheme) qDebug() << "Could not load theme" << cursorTheme; @@ -84,7 +87,7 @@ struct wl_cursor_image *QWaylandCursor::cursorImage(Qt::CursorShape newShape) /* Hide cursor */ if (newShape == Qt::BlankCursor) { - mDisplay->setCursor(nullptr, nullptr); + mDisplay->setCursor(nullptr, nullptr, 1); return nullptr; } @@ -125,12 +128,10 @@ QSharedPointer<QWaylandBuffer> QWaylandCursor::cursorBitmapImage(const QCursor * void QWaylandCursor::changeCursor(QCursor *cursor, QWindow *window) { - Q_UNUSED(window) - const Qt::CursorShape newShape = cursor ? cursor->shape() : Qt::ArrowCursor; if (newShape == Qt::BitmapCursor) { - mDisplay->setCursor(cursorBitmapImage(cursor), cursor->hotSpot()); + mDisplay->setCursor(cursorBitmapImage(cursor), cursor->hotSpot(), window->screen()->devicePixelRatio()); return; } @@ -140,7 +141,7 @@ void QWaylandCursor::changeCursor(QCursor *cursor, QWindow *window) } struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); - mDisplay->setCursor(buffer, image); + mDisplay->setCursor(buffer, image, window->screen()->devicePixelRatio()); } void QWaylandCursor::pointerEvent(const QMouseEvent &event) diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index a9fff410a..9cbb3524d 100644 --- a/src/client/qwaylanddisplay.cpp +++ b/src/client/qwaylanddisplay.cpp @@ -51,9 +51,6 @@ #include "qwaylanddatadevicemanager_p.h" #endif #include "qwaylandhardwareintegration_p.h" -#include "qwaylandxdgshell_p.h" -#include "qwaylandxdgsurface_p.h" -#include "qwaylandwlshellsurface_p.h" #include "qwaylandinputcontext_p.h" #include "qwaylandwindowmanagerintegration_p.h" @@ -66,7 +63,6 @@ #include "qwaylandqtkey_p.h" #include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> -#include <QtWaylandClient/private/qwayland-xdg-shell.h> #include <QtCore/QAbstractEventDispatcher> #include <QtGui/private/qguiapplication_p.h> @@ -241,7 +237,6 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin mScreens.append(screen); // We need to get the output events before creating surfaces forceRoundTrip(); - screen->init(); mWaylandIntegration->screenAdded(screen); } else if (interface == QStringLiteral("wl_compositor")) { mCompositorVersion = qMin((int)version, 3); @@ -261,7 +256,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin mSubCompositor.reset(new QtWayland::wl_subcompositor(registry, id, 1)); } else if (interface == QStringLiteral("qt_touch_extension")) { mTouchExtension.reset(new QWaylandTouchExtension(this, id)); - } else if (interface == QStringLiteral("qt_key_extension")) { + } else if (interface == QStringLiteral("zqt_key_v1")) { mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id)); } else if (interface == QStringLiteral("zwp_text_input_manager_v2")) { mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1)); @@ -477,23 +472,23 @@ QWaylandInputDevice *QWaylandDisplay::defaultInputDevice() const } #if QT_CONFIG(cursor) -void QWaylandDisplay::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image) +void QWaylandDisplay::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, 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, image); + inputDevice->setCursor(buffer, image, dpr); } } -void QWaylandDisplay::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot) +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); + inputDevice->setCursor(buffer, hotSpot, dpr); } } #endif // QT_CONFIG(cursor) diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h index a65ed320d..d39e561f6 100644 --- a/src/client/qwaylanddisplay_p.h +++ b/src/client/qwaylanddisplay_p.h @@ -63,7 +63,6 @@ #include <QtWaylandClient/private/qwayland-wayland.h> #include <QtWaylandClient/private/qtwaylandclientglobal_p.h> -#include <QtWaylandClient/private/qwayland-xdg-shell.h> #include <QtWaylandClient/private/qwaylandshm_p.h> struct wl_cursor_image; @@ -75,16 +74,13 @@ class QSocketNotifier; class QPlatformScreen; namespace QtWayland { - class qt_shell; - class qt_sub_surface_extension; class qt_surface_extension; class zwp_text_input_manager_v2; - class xdg_shell; } namespace QtWaylandClient { -Q_DECLARE_LOGGING_CATEGORY(lcQpaWayland); +Q_WAYLAND_CLIENT_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcQpaWayland); class QWaylandInputDevice; class QWaylandBuffer; @@ -95,10 +91,8 @@ class QWaylandDataDeviceManager; class QWaylandTouchExtension; class QWaylandQtKeyExtension; class QWaylandWindow; -class QWaylandEventThread; class QWaylandIntegration; class QWaylandHardwareIntegration; -class QWaylandXdgShell; class QWaylandShellSurface; typedef void (*RegistryListener)(void *data, @@ -127,8 +121,8 @@ public: QWaylandWindowManagerIntegration *windowManagerIntegration() const; #if QT_CONFIG(cursor) - void setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image); - void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot); + void setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, qreal dpr); + void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, qreal dpr); #endif struct wl_display *wl_display() const { return mDisplay; } struct ::wl_registry *wl_registry() { return object(); } diff --git a/src/client/qwaylanddnd.cpp b/src/client/qwaylanddnd.cpp index fe620506d..b01a9db36 100644 --- a/src/client/qwaylanddnd.cpp +++ b/src/client/qwaylanddnd.cpp @@ -77,15 +77,19 @@ void QWaylandDrag::cancel() m_display->currentInputDevice()->dataDevice()->cancelDrag(); } -void QWaylandDrag::move(const QPoint &globalPos) +void QWaylandDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) { Q_UNUSED(globalPos); + Q_UNUSED(b); + Q_UNUSED(mods); // Do nothing } -void QWaylandDrag::drop(const QPoint &globalPos) +void QWaylandDrag::drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) { Q_UNUSED(globalPos); + Q_UNUSED(b); + Q_UNUSED(mods); // Do nothing } diff --git a/src/client/qwaylanddnd_p.h b/src/client/qwaylanddnd_p.h index 04b39826c..474fe2ab1 100644 --- a/src/client/qwaylanddnd_p.h +++ b/src/client/qwaylanddnd_p.h @@ -78,8 +78,8 @@ public: protected: void startDrag() override; void cancel() override; - void move(const QPoint &globalPos) override; - void drop(const QPoint &globalPos) override; + void move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; + void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override; void endDrag() override; diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index 93344d13f..4835bd68e 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -371,7 +371,7 @@ void QWaylandInputDevice::setCursor(Qt::CursorShape newShape, QWaylandScreen *sc } struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); - setCursor(buffer, image); + setCursor(buffer, image, screen->devicePixelRatio()); } void QWaylandInputDevice::setCursor(const QCursor &cursor, QWaylandScreen *screen) @@ -381,20 +381,20 @@ void QWaylandInputDevice::setCursor(const QCursor &cursor, QWaylandScreen *scree mPointer->mCursorShape = cursor.shape(); if (cursor.shape() == Qt::BitmapCursor) { - setCursor(screen->waylandCursor()->cursorBitmapImage(&cursor), cursor.hotSpot()); + 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) +void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, struct wl_cursor_image *image, int bufferScale) { setCursor(buffer, image ? QPoint(image->hotspot_x, image->hotspot_y) : QPoint(), - image ? QSize(image->width, image->height) : QSize()); + image ? QSize(image->width, image->height) : QSize(), bufferScale); } -void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size) +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; @@ -417,14 +417,16 @@ void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, const QPoint &hotS 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) +void QWaylandInputDevice::setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot, int bufferScale) { - setCursor(buffer->buffer(), hotSpot, buffer->size()); + setCursor(buffer->buffer(), hotSpot, buffer->size(), bufferScale); mPixmapCursor = buffer; } #endif diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h index 48c1cf57f..4b12cc089 100644 --- a/src/client/qwaylandinputdevice_p.h +++ b/src/client/qwaylandinputdevice_p.h @@ -110,9 +110,9 @@ public: #if QT_CONFIG(cursor) void setCursor(const QCursor &cursor, QWaylandScreen *screen); - void setCursor(struct wl_buffer *buffer, struct ::wl_cursor_image *image); - void setCursor(struct wl_buffer *buffer, const QPoint &hotSpot, const QSize &size); - void setCursor(const QSharedPointer<QWaylandBuffer> &buffer, const QPoint &hotSpot); + 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); #endif void handleWindowDestroyed(QWaylandWindow *window); void handleEndDrag(); diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp index b804988b2..fbfdd57f4 100644 --- a/src/client/qwaylandintegration.cpp +++ b/src/client/qwaylandintegration.cpp @@ -77,8 +77,6 @@ #include "qwaylandshellintegration_p.h" #include "qwaylandshellintegrationfactory_p.h" -#include "qwaylandxdgshellintegration_p.h" -#include "qwaylandwlshellintegration_p.h" #include "qwaylandxdgshellv6integration_p.h" #include "qwaylandinputdeviceintegration_p.h" @@ -409,9 +407,9 @@ void QWaylandIntegration::initializeShellIntegration() } } - if (!mShellIntegration || !mShellIntegration->initialize(mDisplay.data())) { - mShellIntegration.reset(); - qWarning("Failed to load shell integration %s", qPrintable(targetKey)); + if (!mShellIntegration) { + qCWarning(lcQpaWayland) << "Loading shell integration failed."; + qCWarning(lcQpaWayland) << "Attempted to load the following shells" << preferredShells; } } @@ -443,15 +441,12 @@ void QWaylandIntegration::initializeInputDeviceIntegration() QWaylandShellIntegration *QWaylandIntegration::createShellIntegration(const QString &integrationName) { - if (integrationName == QLatin1Literal("wl-shell")) { - return QWaylandWlShellIntegration::create(mDisplay.data()); - } else if (integrationName == QLatin1Literal("xdg-shell-v5")) { - return QWaylandXdgShellIntegration::create(mDisplay.data()); - } else if (integrationName == QLatin1Literal("xdg-shell-v6")) { + if (integrationName == QLatin1Literal("xdg-shell-v6")) { return QWaylandXdgShellV6Integration::create(mDisplay.data()); } else if (QWaylandShellIntegrationFactory::keys().contains(integrationName)) { - return QWaylandShellIntegrationFactory::create(integrationName, QStringList()); + return QWaylandShellIntegrationFactory::create(integrationName, mDisplay.data()); } else { + qCWarning(lcQpaWayland) << "No shell integration named" << integrationName << "found"; return nullptr; } } diff --git a/src/client/qwaylandnativeinterface.cpp b/src/client/qwaylandnativeinterface.cpp index 9946c3235..76acb526b 100644 --- a/src/client/qwaylandnativeinterface.cpp +++ b/src/client/qwaylandnativeinterface.cpp @@ -40,13 +40,13 @@ #include "qwaylandnativeinterface_p.h" #include "qwaylanddisplay_p.h" #include "qwaylandwindow_p.h" +#include "qwaylandshellintegration_p.h" #include "qwaylandsubsurface_p.h" #include "qwaylandextendedsurface_p.h" #include "qwaylandintegration_p.h" #include "qwaylanddisplay_p.h" #include "qwaylandwindowmanagerintegration_p.h" #include "qwaylandscreen_p.h" -#include "qwaylandwlshellsurface_p.h" #include <QtGui/private/qguiapplication_p.h> #include <QtGui/QScreen> #include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h> @@ -91,18 +91,13 @@ void *QWaylandNativeInterface::nativeResourceForWindow(const QByteArray &resourc QWaylandWindow *w = static_cast<QWaylandWindow*>(window->handle()); return w ? w->object() : nullptr; } - if (lowerCaseResource == "wl_shell_surface") { - QWaylandWindow *w = static_cast<QWaylandWindow*>(window->handle()); - if (!w) - return nullptr; - QWaylandWlShellSurface *s = qobject_cast<QWaylandWlShellSurface *>(w->shellSurface()); - if (!s) - return nullptr; - return s->object(); - } + if (lowerCaseResource == "egldisplay" && m_integration->clientBufferIntegration()) return m_integration->clientBufferIntegration()->nativeResource(QWaylandClientBufferIntegration::EglDisplay); + if (auto shellIntegration = m_integration->shellIntegration()) + return shellIntegration->nativeResourceForWindow(resourceString, window); + return nullptr; } diff --git a/src/client/qwaylandqtkey.cpp b/src/client/qwaylandqtkey.cpp index ce0d2124e..a60185bd6 100644 --- a/src/client/qwaylandqtkey.cpp +++ b/src/client/qwaylandqtkey.cpp @@ -46,12 +46,12 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { QWaylandQtKeyExtension::QWaylandQtKeyExtension(QWaylandDisplay *display, uint32_t id) - : QtWayland::qt_key_extension(display->wl_registry(), id, 2) + : QtWayland::zqt_key_v1(display->wl_registry(), id, 1) , m_display(display) { } -void QWaylandQtKeyExtension::key_extension_qtkey(struct wl_surface *surface, +void QWaylandQtKeyExtension::zqt_key_v1_key(struct wl_surface *surface, uint32_t time, uint32_t type, uint32_t key, diff --git a/src/client/qwaylandqtkey_p.h b/src/client/qwaylandqtkey_p.h index 9091cf819..117c44a6d 100644 --- a/src/client/qwaylandqtkey_p.h +++ b/src/client/qwaylandqtkey_p.h @@ -54,7 +54,7 @@ #include <qpa/qwindowsysteminterface.h> #include <QtWaylandClient/qtwaylandclientglobal.h> -#include <QtWaylandClient/private/qwayland-qtkey-extension.h> +#include <QtWaylandClient/private/qwayland-qt-key-unstable-v1.h> QT_BEGIN_NAMESPACE @@ -62,7 +62,7 @@ namespace QtWaylandClient { class QWaylandDisplay; -class Q_WAYLAND_CLIENT_EXPORT QWaylandQtKeyExtension : public QtWayland::qt_key_extension +class Q_WAYLAND_CLIENT_EXPORT QWaylandQtKeyExtension : public QtWayland::zqt_key_v1 { public: QWaylandQtKeyExtension(QWaylandDisplay *display, uint32_t id); @@ -70,17 +70,17 @@ public: private: QWaylandDisplay *m_display = nullptr; - void key_extension_qtkey(struct wl_surface *surface, - uint32_t time, - uint32_t type, - uint32_t key, - uint32_t modifiers, - uint32_t nativeScanCode, - uint32_t nativeVirtualKey, - uint32_t nativeModifiers, - const QString &text, - uint32_t autorep, - uint32_t count) override; + void zqt_key_v1_key(struct wl_surface *surface, + uint32_t time, + uint32_t type, + uint32_t key, + uint32_t modifiers, + uint32_t nativeScanCode, + uint32_t nativeVirtualKey, + uint32_t nativeModifiers, + const QString &text, + uint32_t autorep, + uint32_t count) override; }; diff --git a/src/client/qwaylandscreen.cpp b/src/client/qwaylandscreen.cpp index 38d97a91d..37fe5f323 100644 --- a/src/client/qwaylandscreen.cpp +++ b/src/client/qwaylandscreen.cpp @@ -60,20 +60,6 @@ QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uin { } -QWaylandScreen::~QWaylandScreen() -{ -#if QT_CONFIG(cursor) - delete mWaylandCursor; -#endif -} - -void QWaylandScreen::init() -{ -#if QT_CONFIG(cursor) - mWaylandCursor = new QWaylandCursor(this); -#endif -} - QWaylandDisplay * QWaylandScreen::display() const { return mWaylandDisplay; @@ -165,11 +151,20 @@ qreal QWaylandScreen::refreshRate() const } #if QT_CONFIG(cursor) + QPlatformCursor *QWaylandScreen::cursor() const { - return mWaylandCursor; + return const_cast<QWaylandScreen *>(this)->waylandCursor(); +} + +QWaylandCursor *QWaylandScreen::waylandCursor() +{ + if (!mWaylandCursor) + mWaylandCursor.reset(new QWaylandCursor(this)); + return mWaylandCursor.data(); } -#endif + +#endif // QT_CONFIG(cursor) QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window) { diff --git a/src/client/qwaylandscreen_p.h b/src/client/qwaylandscreen_p.h index 9df55d603..39b72bc24 100644 --- a/src/client/qwaylandscreen_p.h +++ b/src/client/qwaylandscreen_p.h @@ -67,9 +67,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandScreen : public QPlatformScreen, QtWayland { public: QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id); - ~QWaylandScreen() override; - void init(); QWaylandDisplay *display() const; QString manufacturer() const override; @@ -95,7 +93,7 @@ public: #if QT_CONFIG(cursor) QPlatformCursor *cursor() const override; - QWaylandCursor *waylandCursor() const { return mWaylandCursor; } + QWaylandCursor *waylandCursor(); #endif uint32_t outputId() const { return m_outputId; } @@ -130,7 +128,7 @@ private: Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; #if QT_CONFIG(cursor) - QWaylandCursor *mWaylandCursor = nullptr; + QScopedPointer<QWaylandCursor> mWaylandCursor; #endif }; diff --git a/src/client/qwaylandshellsurface_p.h b/src/client/qwaylandshellsurface_p.h index fdc309a63..6b6bb9e84 100644 --- a/src/client/qwaylandshellsurface_p.h +++ b/src/client/qwaylandshellsurface_p.h @@ -96,12 +96,9 @@ public: inline QWaylandWindow *window() { return m_window; } virtual void setType(Qt::WindowType type, QWaylandWindow *transientParent) = 0; - -protected: - virtual void setMaximized() {} - virtual void setFullscreen() {} - virtual void setNormal() {} - virtual void setMinimized() {} + virtual void applyConfigure() {} + virtual void requestWindowStates(Qt::WindowStates states) {Q_UNUSED(states);} + virtual bool wantsDecorations() const { return false; } private: QWaylandWindow *m_window = nullptr; diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp index fdfd66688..ef49e8ea8 100644 --- a/src/client/qwaylandwindow.cpp +++ b/src/client/qwaylandwindow.cpp @@ -44,8 +44,6 @@ #include "qwaylandinputdevice_p.h" #include "qwaylandscreen_p.h" #include "qwaylandshellsurface_p.h" -#include "qwaylandwlshellsurface_p.h" -#include "qwaylandxdgsurface_p.h" #include "qwaylandsubsurface_p.h" #include "qwaylandabstractdecoration_p.h" #include "qwaylandwindowmanagerintegration_p.h" @@ -200,11 +198,8 @@ void QWaylandWindow::initWindow() else setGeometry_helper(window()->geometry()); setMask(window()->mask()); - // setWindowStateInternal is a no-op if the argument is equal to mState, - // but since we're creating the shellsurface only now we reset mState to - // make sure the state gets sent out to the compositor - mState = Qt::WindowNoState; - setWindowStateInternal(window()->windowStates()); + if (mShellSurface) + mShellSurface->requestWindowStates(window()->windowStates()); handleContentOrientationChange(window()->contentOrientation()); mFlags = window()->flags(); } @@ -333,7 +328,16 @@ void QWaylandWindow::setGeometry(const QRect &rect) sendExposeEvent(QRect(QPoint(), geometry().size())); } +void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) +{ + QMargins margins = frameMargins(); + int widthWithoutMargins = qMax(sizeWithMargins.width() - (margins.left()+margins.right()), 1); + int heightWithoutMargins = qMax(sizeWithMargins.height() - (margins.top()+margins.bottom()), 1); + QRect geometry(QPoint(), QSize(widthWithoutMargins, heightWithoutMargins)); + mOffset += offset; + setGeometry(geometry); +} void QWaylandWindow::sendExposeEvent(const QRect &rect) { @@ -421,46 +425,24 @@ void QWaylandWindow::setMask(const QRegion &mask) wl_surface::commit(); } -void QWaylandWindow::configure(uint32_t edges, int32_t width, int32_t height) +void QWaylandWindow::applyConfigureWhenPossible() { QMutexLocker resizeLocker(&mResizeLock); - mConfigure.edges |= edges; - mConfigure.width = width; - mConfigure.height = height; - - if (!mRequestResizeSent && !mConfigure.isEmpty()) { - mRequestResizeSent= true; - QMetaObject::invokeMethod(this, "requestResize", Qt::QueuedConnection); + if (!mWaitingToApplyConfigure) { + mWaitingToApplyConfigure = true; + QMetaObject::invokeMethod(this, "applyConfigure", Qt::QueuedConnection); } } -void QWaylandWindow::doResize() +void QWaylandWindow::doApplyConfigure() { - if (mConfigure.isEmpty()) { + if (!mWaitingToApplyConfigure) return; - } - - int widthWithoutMargins = qMax(mConfigure.width-(frameMargins().left() +frameMargins().right()),1); - int heightWithoutMargins = qMax(mConfigure.height-(frameMargins().top()+frameMargins().bottom()),1); - widthWithoutMargins = qMax(widthWithoutMargins, window()->minimumSize().width()); - heightWithoutMargins = qMax(heightWithoutMargins, window()->minimumSize().height()); - QRect geometry = QRect(0,0, widthWithoutMargins, heightWithoutMargins); - - int x = 0; - int y = 0; - QSize size = this->geometry().size(); - if (mConfigure.edges & WL_SHELL_SURFACE_RESIZE_LEFT) { - x = size.width() - geometry.width(); - } - if (mConfigure.edges & WL_SHELL_SURFACE_RESIZE_TOP) { - y = size.height() - geometry.height(); - } - mOffset += QPoint(x, y); - - setGeometry(geometry); + if (mShellSurface) + mShellSurface->applyConfigure(); - mConfigure.clear(); + mWaitingToApplyConfigure = false; } void QWaylandWindow::setCanResize(bool canResize) @@ -472,8 +454,8 @@ void QWaylandWindow::setCanResize(bool canResize) if (mResizeDirty) { QWindowSystemInterface::handleGeometryChange(window(), geometry()); } - if (!mConfigure.isEmpty()) { - doResize(); + if (mWaitingToApplyConfigure) { + doApplyConfigure(); sendExposeEvent(QRect(QPoint(), geometry().size())); } else if (mResizeDirty) { mResizeDirty = false; @@ -482,15 +464,13 @@ void QWaylandWindow::setCanResize(bool canResize) } } -void QWaylandWindow::requestResize() +void QWaylandWindow::applyConfigure() { QMutexLocker lock(&mResizeLock); - if (mCanResize || !mSentInitialResize) { - doResize(); - } + if (mCanResize || !mSentInitialResize) + doApplyConfigure(); - mRequestResizeSent = false; lock.unlock(); sendExposeEvent(QRect(QPoint(), geometry().size())); QWindowSystemInterface::flushWindowSystemEvents(); @@ -596,9 +576,8 @@ void QWaylandWindow::frameCallback(void *data, struct wl_callback *callback, uin self->mWaitingForFrameSync = false; if (self->mUpdateRequested) { - QWindowPrivate *w = QWindowPrivate::get(self->window()); self->mUpdateRequested = false; - w->deliverUpdateRequest(); + self->deliverUpdateRequest(); } } @@ -673,10 +652,10 @@ void QWaylandWindow::setOrientationMask(Qt::ScreenOrientations mask) mShellSurface->setContentOrientationMask(mask); } -void QWaylandWindow::setWindowState(Qt::WindowStates state) +void QWaylandWindow::setWindowState(Qt::WindowStates states) { - if (setWindowStateInternal(state)) - QWindowSystemInterface::flushWindowSystemEvents(); // Required for oldState to work on WindowStateChanged + if (mShellSurface) + mShellSurface->requestWindowStates(states); } void QWaylandWindow::setWindowFlags(Qt::WindowFlags flags) @@ -690,20 +669,6 @@ void QWaylandWindow::setWindowFlags(Qt::WindowFlags flags) bool QWaylandWindow::createDecoration() { - // so far only xdg-shell support this "unminimize" trick, may be moved elsewhere - if (mState & Qt::WindowMinimized) { - QWaylandXdgSurface *xdgSurface = qobject_cast<QWaylandXdgSurface *>(mShellSurface); - if ( xdgSurface ) { - Qt::WindowStates states; - if (xdgSurface->isFullscreen()) - states |= Qt::WindowFullScreen; - if (xdgSurface->isMaximized()) - states |= Qt::WindowMaximized; - - setWindowStateInternal(states); - } - } - if (!mDisplay->supportsWindowDecoration()) return false; @@ -720,12 +685,14 @@ bool QWaylandWindow::createDecoration() default: break; } - if (mFlags & Qt::FramelessWindowHint || isFullscreen()) + if (mFlags & Qt::FramelessWindowHint) decoration = false; if (mFlags & Qt::BypassWindowManagerHint) decoration = false; if (mSubSurfaceWindow) decoration = false; + if (mShellSurface && !mShellSurface->wantsDecorations()) + decoration = false; bool hadDecoration = mWindowDecoration; if (decoration && !decorationPluginFailed) { @@ -971,31 +938,11 @@ bool QWaylandWindow::setMouseGrabEnabled(bool grab) return true; } -bool QWaylandWindow::setWindowStateInternal(Qt::WindowStates state) +void QWaylandWindow::handleWindowStatesChanged(Qt::WindowStates states) { - if (mState == state) { - return false; - } - - // As of february 2013 QWindow::setWindowState sets the new state value after - // QPlatformWindow::setWindowState returns, so we cannot rely on QWindow::windowState - // here. We use then this mState variable. - mState = state; - - if (mShellSurface) { - createDecoration(); - if (state & Qt::WindowMaximized) - mShellSurface->setMaximized(); - if (state & Qt::WindowFullScreen) - mShellSurface->setFullscreen(); - if (state & Qt::WindowMinimized) - mShellSurface->setMinimized(); - if (!state) - mShellSurface->setNormal(); - } - - QWindowSystemInterface::handleWindowStateChanged(window(), mState); - return true; + createDecoration(); + QWindowSystemInterface::handleWindowStateChanged(window(), states, mLastReportedWindowStates); + mLastReportedWindowStates = states; } void QWaylandWindow::sendProperty(const QString &name, const QVariant &value) diff --git a/src/client/qwaylandwindow_p.h b/src/client/qwaylandwindow_p.h index 3324bf700..55f3a515f 100644 --- a/src/client/qwaylandwindow_p.h +++ b/src/client/qwaylandwindow_p.h @@ -77,23 +77,6 @@ class QWaylandScreen; class QWaylandShmBackingStore; class QWaylandPointerEvent; -class Q_WAYLAND_CLIENT_EXPORT QWaylandWindowConfigure -{ -public: - QWaylandWindowConfigure() - { } - - void clear() - { width = height = edges = 0; } - - bool isEmpty() const - { return !height || !width; } - - int width = 0; - int height = 0; - uint32_t edges = 0; -}; - class Q_WAYLAND_CLIENT_EXPORT QWaylandWindow : public QObject, public QPlatformWindow, public QtWayland::wl_surface { Q_OBJECT @@ -118,8 +101,9 @@ public: void setWindowIcon(const QIcon &icon) override; void setGeometry(const QRect &rect) override; + void resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset = {0, 0}); - void configure(uint32_t edges, int32_t width, int32_t height); + void applyConfigureWhenPossible(); //rename to possible? using QtWayland::wl_surface::attach; void attach(QWaylandBuffer *buffer, int x, int y); @@ -145,8 +129,9 @@ public: void handleContentOrientationChange(Qt::ScreenOrientation orientation) override; void setOrientationMask(Qt::ScreenOrientations mask); - void setWindowState(Qt::WindowStates state) override; + void setWindowState(Qt::WindowStates states) override; void setWindowFlags(Qt::WindowFlags flags) override; + void handleWindowStatesChanged(Qt::WindowStates states); void raise() override; void lower() override; @@ -170,9 +155,6 @@ public: bool createDecoration(); - inline bool isMaximized() const { return mState & Qt::WindowMaximized; } - inline bool isFullscreen() const { return mState & Qt::WindowFullScreen; } - #if QT_CONFIG(cursor) void setMouseCursor(QWaylandInputDevice *device, const QCursor &cursor); void restoreMouseCursor(QWaylandInputDevice *device); @@ -181,7 +163,7 @@ public: QWaylandWindow *transientParent() const; QMutex *resizeMutex() { return &mResizeLock; } - void doResize(); + void doApplyConfigure(); void setCanResize(bool canResize); bool setMouseGrabEnabled(bool grab) override; @@ -206,7 +188,7 @@ public: void requestUpdate() override; public slots: - void requestResize(); + void applyConfigure(); protected: void surface_enter(struct ::wl_output *output) override; @@ -228,8 +210,7 @@ protected: QWaitCondition mFrameSyncWait; QMutex mResizeLock; - QWaylandWindowConfigure mConfigure; - bool mRequestResizeSent = false; + bool mWaitingToApplyConfigure = false; bool mCanResize = true; bool mResizeDirty = false; bool mResizeAfterSwap; @@ -241,9 +222,9 @@ protected: QIcon mWindowIcon; - Qt::WindowStates mState = Qt::WindowNoState; Qt::WindowFlags mFlags; QRegion mMask; + Qt::WindowStates mLastReportedWindowStates = Qt::WindowNoState; QWaylandShmBackingStore *mBackingStore = nullptr; @@ -251,7 +232,6 @@ private slots: void handleScreenRemoved(QScreen *qScreen); private: - bool setWindowStateInternal(Qt::WindowStates flags); void setGeometry_helper(const QRect &rect); void initWindow(); void initializeWlSurface(); diff --git a/src/client/qwaylandxdgshellv6.cpp b/src/client/qwaylandxdgshellv6.cpp index a166a3bc9..beabddbbb 100644 --- a/src/client/qwaylandxdgshellv6.cpp +++ b/src/client/qwaylandxdgshellv6.cpp @@ -46,8 +46,6 @@ #include "qwaylandscreen_p.h" #include "qwaylandabstractdecoration_p.h" -#include <QtCore/QDebug> - QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -56,32 +54,65 @@ QWaylandXdgSurfaceV6::Toplevel::Toplevel(QWaylandXdgSurfaceV6 *xdgSurface) : QtWayland::zxdg_toplevel_v6(xdgSurface->get_toplevel()) , m_xdgSurface(xdgSurface) { + requestWindowStates(xdgSurface->window()->window()->windowStates()); } QWaylandXdgSurfaceV6::Toplevel::~Toplevel() { + if (m_applied.states & Qt::WindowActive) { + QWaylandWindow *window = m_xdgSurface->window(); + window->display()->handleWindowDeactivated(window); + } if (isInitialized()) destroy(); } void QWaylandXdgSurfaceV6::Toplevel::applyConfigure() { - //TODO: resize, activate etc - m_xdgSurface->m_window->configure(0, m_configureState.width, m_configureState.height); + if (!(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen))) + m_normalSize = m_xdgSurface->m_window->window()->frameGeometry().size(); + + if (m_pending.size.isEmpty() && !m_normalSize.isEmpty()) + m_pending.size = m_normalSize; + + if ((m_pending.states & Qt::WindowActive) && !(m_applied.states & Qt::WindowActive)) + m_xdgSurface->m_window->display()->handleWindowActivated(m_xdgSurface->m_window); + + if (!(m_pending.states & Qt::WindowActive) && (m_applied.states & Qt::WindowActive)) + m_xdgSurface->m_window->display()->handleWindowDeactivated(m_xdgSurface->m_window); + + // TODO: none of the other plugins send WindowActive either, but is it on purpose? + Qt::WindowStates statesWithoutActive = m_pending.states & ~Qt::WindowActive; + + m_xdgSurface->m_window->handleWindowStatesChanged(statesWithoutActive); + m_xdgSurface->m_window->resizeFromApplyConfigure(m_pending.size); + m_applied = m_pending; } void QWaylandXdgSurfaceV6::Toplevel::zxdg_toplevel_v6_configure(int32_t width, int32_t height, wl_array *states) { - m_configureState.width = width; - m_configureState.height = height; + m_pending.size = QSize(width, height); - uint32_t *state = reinterpret_cast<uint32_t *>(states->data); + auto *xdgStates = static_cast<uint32_t *>(states->data); size_t numStates = states->size / sizeof(uint32_t); - m_configureState.states.reserve(numStates); - m_configureState.states.clear(); - for (size_t i = 0; i < numStates; i++) - m_configureState.states << state[i]; + m_pending.states = Qt::WindowNoState; + + for (size_t i = 0; i < numStates; i++) { + switch (xdgStates[i]) { + case ZXDG_TOPLEVEL_V6_STATE_ACTIVATED: + m_pending.states |= Qt::WindowActive; + break; + case ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED: + m_pending.states |= Qt::WindowMaximized; + break; + case ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN: + m_pending.states |= Qt::WindowFullScreen; + break; + default: + break; + } + } } void QWaylandXdgSurfaceV6::Toplevel::zxdg_toplevel_v6_close() @@ -89,6 +120,32 @@ void QWaylandXdgSurfaceV6::Toplevel::zxdg_toplevel_v6_close() m_xdgSurface->m_window->window()->close(); } +void QWaylandXdgSurfaceV6::Toplevel::requestWindowStates(Qt::WindowStates states) +{ + // Re-send what's different from the applied state + Qt::WindowStates changedStates = m_applied.states ^ states; + + if (changedStates & Qt::WindowMaximized) { + if (states & Qt::WindowMaximized) + set_maximized(); + else + unset_maximized(); + } + + if (changedStates & Qt::WindowFullScreen) { + if (states & Qt::WindowFullScreen) + set_fullscreen(nullptr); + else + unset_fullscreen(); + } + + // Minimized state is not reported by the protocol, so always send it + if (states & Qt::WindowMinimized) { + set_minimized(); + m_xdgSurface->window()->handleWindowStatesChanged(states & ~Qt::WindowMinimized); + } +} + QWaylandXdgSurfaceV6::Popup::Popup(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdgSurfaceV6 *parent, QtWayland::zxdg_positioner_v6 *positioner) : zxdg_popup_v6(xdgSurface->get_popup(parent->object(), positioner->object())) @@ -104,7 +161,6 @@ QWaylandXdgSurfaceV6::Popup::~Popup() void QWaylandXdgSurfaceV6::Popup::applyConfigure() { - } void QWaylandXdgSurfaceV6::Popup::zxdg_popup_v6_popup_done() @@ -187,6 +243,34 @@ bool QWaylandXdgSurfaceV6::handleExpose(const QRegion ®ion) return false; } +void QWaylandXdgSurfaceV6::applyConfigure() +{ + Q_ASSERT(m_pendingConfigureSerial != 0); + + if (m_toplevel) + m_toplevel->applyConfigure(); + if (m_popup) + m_popup->applyConfigure(); + + m_configured = true; + ack_configure(m_pendingConfigureSerial); + + m_pendingConfigureSerial = 0; +} + +bool QWaylandXdgSurfaceV6::wantsDecorations() const +{ + return m_toplevel && !(m_toplevel->m_pending.states & Qt::WindowFullScreen); +} + +void QWaylandXdgSurfaceV6::requestWindowStates(Qt::WindowStates states) +{ + if (m_toplevel) + m_toplevel->requestWindowStates(states); + else + qCWarning(lcQpaWayland) << "Non-toplevel surfaces can't request window states"; +} + void QWaylandXdgSurfaceV6::setToplevel() { Q_ASSERT(!m_toplevel && !m_popup); @@ -220,21 +304,14 @@ void QWaylandXdgSurfaceV6::setPopup(QWaylandWindow *parent, QWaylandInputDevice void QWaylandXdgSurfaceV6::zxdg_surface_v6_configure(uint32_t serial) { - m_configured = true; - if (m_toplevel) - m_toplevel->applyConfigure(); - else if (m_popup) - m_popup->applyConfigure(); - + m_window->applyConfigureWhenPossible(); + m_pendingConfigureSerial = serial; if (!m_exposeRegion.isEmpty()) { QWindowSystemInterface::handleExposeEvent(m_window->window(), m_exposeRegion); m_exposeRegion = QRegion(); } - ack_configure(serial); } - - QWaylandXdgShellV6::QWaylandXdgShellV6(struct ::wl_registry *registry, uint32_t id, uint32_t availableVersion) : QtWayland::zxdg_shell_v6(registry, id, qMin(availableVersion, 1u)) { diff --git a/src/client/qwaylandxdgshellv6_p.h b/src/client/qwaylandxdgshellv6_p.h index b72d3d18a..d2448bc66 100644 --- a/src/client/qwaylandxdgshellv6_p.h +++ b/src/client/qwaylandxdgshellv6_p.h @@ -73,6 +73,7 @@ class QWaylandXdgShellV6; class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgSurfaceV6 : public QWaylandShellSurface, public QtWayland::zxdg_surface_v6 { + Q_OBJECT public: QWaylandXdgSurfaceV6(QWaylandXdgShellV6 *shell, ::zxdg_surface_v6 *surface, QWaylandWindow *window); ~QWaylandXdgSurfaceV6() override; @@ -85,8 +86,12 @@ public: void setType(Qt::WindowType type, QWaylandWindow *transientParent) override; bool handleExpose(const QRegion &) override; + bool handlesActiveState() const { return m_toplevel; } + void applyConfigure() override; + bool wantsDecorations() const override; protected: + void requestWindowStates(Qt::WindowStates states) override; void zxdg_surface_v6_configure(uint32_t serial) override; private: @@ -101,10 +106,12 @@ private: void zxdg_toplevel_v6_configure(int32_t width, int32_t height, wl_array *states) override; void zxdg_toplevel_v6_close() override; + void requestWindowStates(Qt::WindowStates states); struct { - int32_t width, height; - QVarLengthArray<uint32_t> states; - } m_configureState; + QSize size = {0, 0}; + Qt::WindowStates states = Qt::WindowNoState; + } m_pending, m_applied; + QSize m_normalSize; QWaylandXdgSurfaceV6 *m_xdgSurface = nullptr; }; @@ -129,6 +136,7 @@ private: Popup *m_popup = nullptr; bool m_configured = false; QRegion m_exposeRegion; + uint m_pendingConfigureSerial = 0; }; class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgShellV6 : public QtWayland::zxdg_shell_v6 diff --git a/src/client/qwaylandxdgshellv6integration.cpp b/src/client/qwaylandxdgshellv6integration.cpp index cb82354b6..0eb6b5929 100644 --- a/src/client/qwaylandxdgshellv6integration.cpp +++ b/src/client/qwaylandxdgshellv6integration.cpp @@ -41,9 +41,6 @@ #include <QtWaylandClient/private/qwaylandwindow_p.h> #include <QtWaylandClient/private/qwaylanddisplay_p.h> -#include <QtWaylandClient/private/qwaylandxdgsurface_p.h> -#include <QtWaylandClient/private/qwaylandxdgpopup_p.h> -#include <QtWaylandClient/private/qwaylandxdgshell_p.h> #include <QtWaylandClient/private/qwaylandxdgshellv6_p.h> QT_BEGIN_NAMESPACE @@ -62,9 +59,15 @@ QWaylandXdgShellV6Integration::QWaylandXdgShellV6Integration(QWaylandDisplay *di QWaylandXdgShellV6Integration *QWaylandXdgShellV6Integration::create(QWaylandDisplay *display) { - if (display->hasRegistryGlobal(QLatin1String("zxdg_shell_v6"))) - return new QWaylandXdgShellV6Integration(display); - return nullptr; + if (!display->hasRegistryGlobal(QLatin1String("zxdg_shell_v6"))) + return nullptr; + + QScopedPointer<QWaylandXdgShellV6Integration> integration; + integration.reset(new QWaylandXdgShellV6Integration(display)); + if (integration && !integration->initialize(display)) + return nullptr; + + return integration.take(); } bool QWaylandXdgShellV6Integration::initialize(QWaylandDisplay *display) @@ -78,6 +81,20 @@ QWaylandShellSurface *QWaylandXdgShellV6Integration::createShellSurface(QWayland return m_xdgShell->getXdgSurface(window); } +void QWaylandXdgShellV6Integration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) +{ + if (newFocus) { + auto *xdgSurface = qobject_cast<QWaylandXdgSurfaceV6 *>(newFocus->shellSurface()); + if (xdgSurface && !xdgSurface->handlesActiveState()) + m_display->handleWindowActivated(newFocus); + } + if (oldFocus && qobject_cast<QWaylandXdgSurfaceV6 *>(oldFocus->shellSurface())) { + auto *xdgSurface = qobject_cast<QWaylandXdgSurfaceV6 *>(oldFocus->shellSurface()); + if (xdgSurface && !xdgSurface->handlesActiveState()) + m_display->handleWindowDeactivated(oldFocus); + } +} + } QT_END_NAMESPACE diff --git a/src/client/qwaylandxdgshellv6integration_p.h b/src/client/qwaylandxdgshellv6integration_p.h index bdfd19723..66323a775 100644 --- a/src/client/qwaylandxdgshellv6integration_p.h +++ b/src/client/qwaylandxdgshellv6integration_p.h @@ -67,6 +67,7 @@ public: static QWaylandXdgShellV6Integration *create(QWaylandDisplay* display); bool initialize(QWaylandDisplay *display) override; QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; + void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; private: QWaylandXdgShellV6Integration(QWaylandDisplay *display); diff --git a/src/client/qwaylandxdgsurface.cpp b/src/client/qwaylandxdgsurface.cpp deleted file mode 100644 index 4dfc5e6da..000000000 --- a/src/client/qwaylandxdgsurface.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the config.tests of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or 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.GPL2 and 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-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwaylandxdgsurface_p.h" - -#include "qwaylanddisplay_p.h" -#include "qwaylandwindow_p.h" -#include "qwaylandinputdevice_p.h" -#include "qwaylandabstractdecoration_p.h" -#include "qwaylandscreen_p.h" -#include "qwaylandextendedsurface_p.h" -#include "qwaylandxdgshell_p.h" - - -QT_BEGIN_NAMESPACE - -namespace QtWaylandClient { - -QWaylandXdgSurface::QWaylandXdgSurface(QWaylandXdgShell *shell, QWaylandWindow *window) - : QWaylandShellSurface(window) - , QtWayland::xdg_surface(shell->get_xdg_surface(window->object())) - , m_window(window) - , m_shell(shell) -{ - if (window->display()->windowExtension()) - m_extendedWindow = new QWaylandExtendedSurface(window); -} - -QWaylandXdgSurface::~QWaylandXdgSurface() -{ - if (m_active) - window()->display()->handleWindowDeactivated(m_window); - - xdg_surface_destroy(object()); - delete m_extendedWindow; -} - -void QWaylandXdgSurface::resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) -{ - // May need some conversion if types get incompatibles, ATM they're identical - enum resize_edge const * const arg = reinterpret_cast<enum resize_edge const * const>(&edges); - resize(inputDevice, *arg); -} - -void QWaylandXdgSurface::resize(QWaylandInputDevice *inputDevice, enum resize_edge edges) -{ - resize(inputDevice->wl_seat(), - inputDevice->serial(), - edges); -} - -bool QWaylandXdgSurface::move(QWaylandInputDevice *inputDevice) -{ - move(inputDevice->wl_seat(), - inputDevice->serial()); - return true; -} - -void QWaylandXdgSurface::setMaximized() -{ - if (!m_maximized) - set_maximized(); -} - -void QWaylandXdgSurface::setFullscreen() -{ - if (!m_fullscreen) - set_fullscreen(nullptr); -} - -void QWaylandXdgSurface::setNormal() -{ - if (m_fullscreen || m_maximized || m_minimized) { - if (m_maximized) { - unset_maximized(); - } - if (m_fullscreen) { - unset_fullscreen(); - } - - m_fullscreen = m_maximized = m_minimized = false; - } -} - -void QWaylandXdgSurface::setMinimized() -{ - m_minimized = true; - set_minimized(); -} - -void QWaylandXdgSurface::updateTransientParent(QWaylandWindow *parent) -{ - if (!parent) - return; - auto parentXdgSurface = qobject_cast<QWaylandXdgSurface *>(parent->shellSurface()); - Q_ASSERT(parentXdgSurface); - set_parent(parentXdgSurface->object()); -} - -void QWaylandXdgSurface::setTitle(const QString & title) -{ - return QtWayland::xdg_surface::set_title(title); -} - -void QWaylandXdgSurface::setAppId(const QString & appId) -{ - return QtWayland::xdg_surface::set_app_id(appId); -} - -void QWaylandXdgSurface::raise() -{ - if (m_extendedWindow) - m_extendedWindow->raise(); -} - -void QWaylandXdgSurface::lower() -{ - if (m_extendedWindow) - m_extendedWindow->lower(); -} - -void QWaylandXdgSurface::setContentOrientationMask(Qt::ScreenOrientations orientation) -{ - if (m_extendedWindow) - m_extendedWindow->setContentOrientationMask(orientation); -} - -void QWaylandXdgSurface::setWindowFlags(Qt::WindowFlags flags) -{ - if (m_extendedWindow) - m_extendedWindow->setWindowFlags(flags); -} - -void QWaylandXdgSurface::sendProperty(const QString &name, const QVariant &value) -{ - if (m_extendedWindow) - m_extendedWindow->updateGenericProperty(name, value); -} - -void QWaylandXdgSurface::setType(Qt::WindowType type, QWaylandWindow *transientParent) -{ - Q_UNUSED(type) - if (transientParent) - updateTransientParent(transientParent); -} - -void QWaylandXdgSurface::xdg_surface_configure(int32_t width, int32_t height, struct wl_array *states,uint32_t serial) -{ - uint32_t *state = reinterpret_cast<uint32_t*>(states->data); - size_t numStates = states->size / sizeof(uint32_t); - bool aboutToMaximize = false; - bool aboutToFullScreen = false; - bool aboutToActivate = false; - - for (size_t i = 0; i < numStates; i++) { - switch (state[i]) { - case XDG_SURFACE_STATE_MAXIMIZED: - aboutToMaximize = ((width > 0) && (height > 0)); - break; - case XDG_SURFACE_STATE_FULLSCREEN: - aboutToFullScreen = true; - break; - case XDG_SURFACE_STATE_RESIZING: - m_normalSize = QSize(width, height); - break; - case XDG_SURFACE_STATE_ACTIVATED: - aboutToActivate = true; - break; - default: - break; - } - } - - if (!m_active && aboutToActivate) { - m_active = true; - window()->display()->handleWindowActivated(m_window); - } else if (m_active && !aboutToActivate) { - m_active = false; - window()->display()->handleWindowDeactivated(m_window); - } - - if (!m_fullscreen && aboutToFullScreen) { - if (!m_maximized) - m_normalSize = m_window->window()->frameGeometry().size(); - m_fullscreen = true; - m_window->window()->showFullScreen(); - } else if (m_fullscreen && !aboutToFullScreen) { - m_fullscreen = false; - if ( m_maximized ) { - m_window->window()->showMaximized(); - } else { - m_window->window()->showNormal(); - } - } else if (!m_maximized && aboutToMaximize) { - if (!m_fullscreen) - m_normalSize = m_window->window()->frameGeometry().size(); - m_maximized = true; - m_window->window()->showMaximized(); - } else if (m_maximized && !aboutToMaximize) { - m_maximized = false; - m_window->window()->showNormal(); - } - - if (width <= 0 || height <= 0) { - if (!m_normalSize.isEmpty()) - m_window->configure(0, m_normalSize.width(), m_normalSize.height()); - } else { - m_window->configure(0, width, height); - } - - ack_configure(serial); -} - -void QWaylandXdgSurface::xdg_surface_close() -{ -} - -} - -QT_END_NAMESPACE diff --git a/src/client/shellintegration/qwaylandshellintegration_p.h b/src/client/shellintegration/qwaylandshellintegration_p.h index 7f3a2cefc..ccad00481 100644 --- a/src/client/shellintegration/qwaylandshellintegration_p.h +++ b/src/client/shellintegration/qwaylandshellintegration_p.h @@ -79,6 +79,11 @@ public: if (oldFocus) m_display->handleWindowDeactivated(oldFocus); } + virtual void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) { + Q_UNUSED(resource); + Q_UNUSED(window); + return nullptr; + } protected: QWaylandDisplay *m_display = nullptr; diff --git a/src/client/shellintegration/qwaylandshellintegrationfactory.cpp b/src/client/shellintegration/qwaylandshellintegrationfactory.cpp index c5a505bbe..48fda14d4 100644 --- a/src/client/shellintegration/qwaylandshellintegrationfactory.cpp +++ b/src/client/shellintegration/qwaylandshellintegrationfactory.cpp @@ -78,19 +78,24 @@ QStringList QWaylandShellIntegrationFactory::keys(const QString &pluginPath) #endif } -QWaylandShellIntegration *QWaylandShellIntegrationFactory::create(const QString &name, const QStringList &args, const QString &pluginPath) +QWaylandShellIntegration *QWaylandShellIntegrationFactory::create(const QString &name, QWaylandDisplay *display, const QStringList &args, const QString &pluginPath) { #if QT_CONFIG(library) + QScopedPointer<QWaylandShellIntegration> integration; + // Try loading the plugin from platformPluginPath first: if (!pluginPath.isEmpty()) { QCoreApplication::addLibraryPath(pluginPath); - if (QWaylandShellIntegration *ret = qLoadPlugin<QWaylandShellIntegration, QWaylandShellIntegrationPlugin>(directLoader(), name, args)) - return ret; + integration.reset(qLoadPlugin<QWaylandShellIntegration, QWaylandShellIntegrationPlugin>(directLoader(), name, args)); } - if (QWaylandShellIntegration *ret = qLoadPlugin<QWaylandShellIntegration, QWaylandShellIntegrationPlugin>(loader(), name, args)) - return ret; + if (!integration) + integration.reset(qLoadPlugin<QWaylandShellIntegration, QWaylandShellIntegrationPlugin>(loader(), name, args)); #endif - return nullptr; + + if (integration && !integration->initialize(display)) + return nullptr; + + return integration.take(); } } diff --git a/src/client/shellintegration/qwaylandshellintegrationfactory_p.h b/src/client/shellintegration/qwaylandshellintegrationfactory_p.h index 3edb0a89d..515a18e1f 100644 --- a/src/client/shellintegration/qwaylandshellintegrationfactory_p.h +++ b/src/client/shellintegration/qwaylandshellintegrationfactory_p.h @@ -51,7 +51,10 @@ // We mean it. // +#include <QtWaylandClient/private/qwaylanddisplay_p.h> + #include <QtWaylandClient/qtwaylandclientglobal.h> + #include <QtCore/QStringList> QT_BEGIN_NAMESPACE @@ -64,7 +67,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandShellIntegrationFactory { public: static QStringList keys(const QString &pluginPath = QString()); - static QWaylandShellIntegration *create(const QString &name, const QStringList &args, const QString &pluginPath = QString()); + static QWaylandShellIntegration *create(const QString &name, QWaylandDisplay *display, const QStringList &args = QStringList(), const QString &pluginPath = QString()); }; } diff --git a/src/compositor/compositor_api/qwaylandkeyboard.cpp b/src/compositor/compositor_api/qwaylandkeyboard.cpp index 7ab8bff9e..c172bca85 100644 --- a/src/compositor/compositor_api/qwaylandkeyboard.cpp +++ b/src/compositor/compositor_api/qwaylandkeyboard.cpp @@ -54,6 +54,7 @@ #if QT_CONFIG(xkbcommon_evdev) #include <sys/mman.h> #include <sys/types.h> +#include <qwaylandxkb_p.h> #endif QT_BEGIN_NAMESPACE @@ -196,6 +197,32 @@ void QWaylandKeyboardPrivate::modifiers(uint32_t serial, uint32_t mods_depressed } } +#if QT_CONFIG(xkbcommon_evdev) +void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable() +{ + if (!scanCodesByQtKey.isEmpty() || !xkbState()) + return; + + if (xkb_keymap *keymap = xkb_state_get_keymap(xkb_state)) { + xkb_keymap_key_for_each(keymap, [](xkb_keymap *keymap, xkb_keycode_t keycode, void *d){ + auto *scanCodesByQtKey = static_cast<QMap<ScanCodeKey, uint>*>(d); + uint numLayouts = xkb_keymap_num_layouts_for_key(keymap, keycode); + for (uint layout = 0; layout < numLayouts; ++layout) { + const xkb_keysym_t *syms = nullptr; + xkb_keymap_key_get_syms_by_level(keymap, keycode, layout, 0, &syms); + if (!syms) + continue; + + Qt::KeyboardModifiers mods = {}; + int qtKey = QWaylandXkb::keysymToQtKey(syms[0], mods).first; + if (qtKey != 0) + scanCodesByQtKey->insert({layout, qtKey}, keycode); + } + }, &scanCodesByQtKey); + } +} +#endif + void QWaylandKeyboardPrivate::updateModifierState(uint code, uint32_t state) { #if QT_CONFIG(xkbcommon_evdev) @@ -355,6 +382,7 @@ void QWaylandKeyboardPrivate::createXKBKeymap() struct xkb_keymap *xkbKeymap = xkb_keymap_new_from_names(xkb_context, &rule_names, static_cast<xkb_keymap_compile_flags>(0)); if (xkbKeymap) { + scanCodesByQtKey.clear(); createXKBState(xkbKeymap); xkb_keymap_unref(xkbKeymap); } else { @@ -561,4 +589,17 @@ void QWaylandKeyboard::addClient(QWaylandClient *client, uint32_t id, uint32_t v d->add(client->client(), id, qMin<uint32_t>(QtWaylandServer::wl_keyboard::interfaceVersion(), version)); } +uint QWaylandKeyboard::toScanCode(int qtKey) const +{ + uint scanCode = 0; +#if QT_CONFIG(xkbcommon_evdev) + Q_D(const QWaylandKeyboard); + const_cast<QWaylandKeyboardPrivate *>(d)->maybeUpdateXkbScanCodeTable(); + scanCode = d->scanCodesByQtKey.value({d->group, qtKey}, 0); +#else + Q_UNUSED(qtKey); +#endif + return scanCode; +} + QT_END_NAMESPACE diff --git a/src/compositor/compositor_api/qwaylandkeyboard.h b/src/compositor/compositor_api/qwaylandkeyboard.h index 580f0c4ed..e5d5e086e 100644 --- a/src/compositor/compositor_api/qwaylandkeyboard.h +++ b/src/compositor/compositor_api/qwaylandkeyboard.h @@ -82,6 +82,8 @@ public: virtual void addClient(QWaylandClient *client, uint32_t id, uint32_t version); + uint toScanCode(int qtKey) const; + Q_SIGNALS: void focusChanged(QWaylandSurface *surface); void repeatRateChanged(quint32 repeatRate); diff --git a/src/compositor/compositor_api/qwaylandkeyboard_p.h b/src/compositor/compositor_api/qwaylandkeyboard_p.h index 6db312cfb..cd1f27956 100644 --- a/src/compositor/compositor_api/qwaylandkeyboard_p.h +++ b/src/compositor/compositor_api/qwaylandkeyboard_p.h @@ -89,6 +89,7 @@ public: #if QT_CONFIG(xkbcommon_evdev) struct xkb_state *xkbState() const { return xkb_state; } uint32_t xkbModsMask() const { return modsDepressed | modsLatched | modsLocked; } + void maybeUpdateXkbScanCodeTable(); #endif void keyEvent(uint code, uint32_t state); @@ -131,6 +132,8 @@ private: size_t keymap_size; int keymap_fd = -1; char *keymap_area = nullptr; + using ScanCodeKey = std::pair<uint,int>; // group/layout and QtKey + QMap<ScanCodeKey, uint> scanCodesByQtKey; struct xkb_context *xkb_context = nullptr; struct xkb_state *xkb_state = nullptr; #endif diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp index d4cc94e4b..f3e0ffa78 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.cpp +++ b/src/compositor/compositor_api/qwaylandquickitem.cpp @@ -1315,6 +1315,19 @@ void QWaylandQuickItem::raise() stackAfter(top); } +void QWaylandQuickItem::sendMouseMoveEvent(const QPointF &position, QWaylandSeat *seat) +{ + if (seat == nullptr) + seat = compositor()->defaultSeat(); + + if (!seat) { + qWarning() << "No seat, can't send mouse event"; + return; + } + + seat->sendMouseMoveEvent(view(), position); +} + /*! * \internal * diff --git a/src/compositor/compositor_api/qwaylandquickitem.h b/src/compositor/compositor_api/qwaylandquickitem.h index 047fdeb65..0aa863f5f 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.h +++ b/src/compositor/compositor_api/qwaylandquickitem.h @@ -151,6 +151,7 @@ public Q_SLOTS: void setPaintEnabled(bool paintEnabled); void raise(); void lower(); + void sendMouseMoveEvent(const QPointF &position, QWaylandSeat *seat = nullptr); private Q_SLOTS: void surfaceMappedChanged(); diff --git a/src/compositor/compositor_api/qwaylandseat.cpp b/src/compositor/compositor_api/qwaylandseat.cpp index c67e6020d..957f5ea83 100644 --- a/src/compositor/compositor_api/qwaylandseat.cpp +++ b/src/compositor/compositor_api/qwaylandseat.cpp @@ -440,10 +440,50 @@ void QWaylandSeat::sendFullKeyEvent(QKeyEvent *event) return; if (!d->keyboard.isNull() && !event->isAutoRepeat()) { + + uint scanCode = event->nativeScanCode(); + if (scanCode == 0) + scanCode = d->keyboard->toScanCode(event->key()); + + if (scanCode == 0) { + qWarning() << "Can't send Wayland key event: Unable to get a valid scan code"; + return; + } + if (event->type() == QEvent::KeyPress) - d->keyboard->sendKeyPressEvent(event->nativeScanCode()); + d->keyboard->sendKeyPressEvent(scanCode); else if (event->type() == QEvent::KeyRelease) - d->keyboard->sendKeyReleaseEvent(event->nativeScanCode()); + d->keyboard->sendKeyReleaseEvent(scanCode); + } +} + +/*! + * \qmlmethod void QtWaylandCompositor::WaylandSeat::sendKeyEvent(int qtKey, bool pressed) + * \since 5.12 + * + * Sends a key press or release to the keyboard device. + */ + +/*! + * Sends a key press or release to the keyboard device. + * + * \since 5.12 + */ +void QWaylandSeat::sendKeyEvent(int qtKey, bool pressed) +{ + Q_D(QWaylandSeat); + if (!keyboardFocus()) { + qWarning("Cannot send Wayland key event, no keyboard focus, fix the compositor"); + return; + } + + if (auto scanCode = d->keyboard->toScanCode(qtKey)) { + if (pressed) + d->keyboard->sendKeyPressEvent(scanCode); + else + d->keyboard->sendKeyReleaseEvent(scanCode); + } else { + qWarning() << "Can't send Wayland key event: Unable to get scan code for" << Qt::Key(qtKey); } } diff --git a/src/compositor/compositor_api/qwaylandseat.h b/src/compositor/compositor_api/qwaylandseat.h index f438b6639..6a4d6e176 100644 --- a/src/compositor/compositor_api/qwaylandseat.h +++ b/src/compositor/compositor_api/qwaylandseat.h @@ -97,6 +97,7 @@ public: void sendKeyReleaseEvent(uint code); void sendFullKeyEvent(QKeyEvent *event); + Q_INVOKABLE void sendKeyEvent(int qtKey, bool pressed); uint sendTouchPointEvent(QWaylandSurface *surface, int id, const QPointF &point, Qt::TouchPointState state); Q_INVOKABLE uint sendTouchPointPressed(QWaylandSurface *surface, int id, const QPointF &position); diff --git a/src/compositor/extensions/extensions.pri b/src/compositor/extensions/extensions.pri index 68e473e14..77c09aa23 100644 --- a/src/compositor/extensions/extensions.pri +++ b/src/compositor/extensions/extensions.pri @@ -3,10 +3,10 @@ CONFIG += generated_privates WAYLANDSERVERSOURCES += \ ../extensions/touch-extension.xml \ - ../extensions/qtkey-extension.xml \ + ../extensions/qt-key-unstable-v1.xml \ ../extensions/qt-windowmanager.xml \ ../3rdparty/protocol/text-input-unstable-v2.xml \ - ../3rdparty/protocol/xdg-shell.xml \ + ../3rdparty/protocol/xdg-shell-unstable-v5.xml \ ../3rdparty/protocol/xdg-shell-unstable-v6.xml \ ../3rdparty/protocol/ivi-application.xml \ diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp index 6b8845797..84e9c356d 100644 --- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp +++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.cpp @@ -45,6 +45,21 @@ QT_BEGIN_NAMESPACE +QWaylandQuickShellSurfaceItem *QWaylandQuickShellSurfaceItemPrivate::maybeCreateAutoPopup(QWaylandShellSurface* shellSurface) +{ + if (!m_autoCreatePopupItems) + return nullptr; + + Q_Q(QWaylandQuickShellSurfaceItem); + auto *popupItem = new QWaylandQuickShellSurfaceItem(q); + popupItem->setShellSurface(shellSurface); + popupItem->setAutoCreatePopupItems(true); + QObject::connect(popupItem, &QWaylandQuickShellSurfaceItem::surfaceDestroyed, [popupItem](){ + popupItem->deleteLater(); + }); + return popupItem; +} + /*! * \qmltype ShellSurfaceItem * \inherits WaylandQuickItem @@ -163,6 +178,36 @@ void QWaylandQuickShellSurfaceItem::setMoveItem(QQuickItem *moveItem) moveItemChanged(); } +/*! + * \qmlproperty bool QtWaylandCompositor::ShellSurfaceItem::autoCreatePopupItems + * + * This property holds whether ShellSurfaceItems for popups parented to the shell + * surface managed by this item should automatically be created. + */ + +/*! + * \property QWaylandQuickShellSurfaceItem::autoCreatePopupItems + * + * This property holds whether QWaylandQuickShellSurfaceItems for popups + * parented to the shell surface managed by this item should automatically be created. + */ +bool QWaylandQuickShellSurfaceItem::autoCreatePopupItems() +{ + Q_D(const QWaylandQuickShellSurfaceItem); + return d->m_autoCreatePopupItems; +} + +void QWaylandQuickShellSurfaceItem::setAutoCreatePopupItems(bool enabled) +{ + Q_D(QWaylandQuickShellSurfaceItem); + + if (enabled == d->m_autoCreatePopupItems) + return; + + d->m_autoCreatePopupItems = enabled; + emit autoCreatePopupItemsChanged(); +} + void QWaylandQuickShellSurfaceItem::mouseMoveEvent(QMouseEvent *event) { Q_D(QWaylandQuickShellSurfaceItem); diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem.h b/src/compositor/extensions/qwaylandquickshellsurfaceitem.h index e2c823651..d14fa3fce 100644 --- a/src/compositor/extensions/qwaylandquickshellsurfaceitem.h +++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem.h @@ -54,6 +54,7 @@ class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellSurfaceItem : public QWaylan Q_DECLARE_PRIVATE(QWaylandQuickShellSurfaceItem) Q_PROPERTY(QWaylandShellSurface *shellSurface READ shellSurface WRITE setShellSurface NOTIFY shellSurfaceChanged) Q_PROPERTY(QQuickItem *moveItem READ moveItem WRITE setMoveItem NOTIFY moveItemChanged) + Q_PROPERTY(bool autoCreatePopupItems READ autoCreatePopupItems WRITE setAutoCreatePopupItems NOTIFY autoCreatePopupItemsChanged) public: QWaylandQuickShellSurfaceItem(QQuickItem *parent = nullptr); ~QWaylandQuickShellSurfaceItem() override; @@ -64,9 +65,13 @@ public: QQuickItem *moveItem() const; void setMoveItem(QQuickItem *moveItem); + bool autoCreatePopupItems(); + void setAutoCreatePopupItems(bool enabled); + Q_SIGNALS: void shellSurfaceChanged(); void moveItemChanged(); + void autoCreatePopupItemsChanged(); protected: QWaylandQuickShellSurfaceItem(QWaylandQuickShellSurfaceItemPrivate &dd, QQuickItem *parent); diff --git a/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h b/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h index abb56221e..7a458381e 100644 --- a/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h +++ b/src/compositor/extensions/qwaylandquickshellsurfaceitem_p.h @@ -40,6 +40,7 @@ #ifndef QWAYLANDQUICKSHELLSURFACEITEM_P_H #define QWAYLANDQUICKSHELLSURFACEITEM_P_H +#include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem> #include <QtWaylandCompositor/private/qwaylandquickitem_p.h> #include <QtCore/QBasicTimer> @@ -60,15 +61,20 @@ QT_BEGIN_NAMESPACE class QWaylandQuickShellIntegration; class QWaylandShellSurface; +class QWaylandQuickShellSurfaceItem; class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellSurfaceItemPrivate : public QWaylandQuickItemPrivate { + Q_DECLARE_PUBLIC(QWaylandQuickShellSurfaceItem) public: - QWaylandQuickShellSurfaceItemPrivate() - {} + QWaylandQuickShellSurfaceItemPrivate() {} + QWaylandQuickShellSurfaceItem *maybeCreateAutoPopup(QWaylandShellSurface* shellSurface); + static QWaylandQuickShellSurfaceItemPrivate *get(QWaylandQuickShellSurfaceItem *item) { return item->d_func(); } + QWaylandQuickShellIntegration *m_shellIntegration = nullptr; QWaylandShellSurface *m_shellSurface = nullptr; QQuickItem *m_moveItem = nullptr; + bool m_autoCreatePopupItems = false; }; class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickShellIntegration : public QObject diff --git a/src/compositor/extensions/qwaylandxdgshellv5_p.h b/src/compositor/extensions/qwaylandxdgshellv5_p.h index b0b51698d..032441e8d 100644 --- a/src/compositor/extensions/qwaylandxdgshellv5_p.h +++ b/src/compositor/extensions/qwaylandxdgshellv5_p.h @@ -41,7 +41,7 @@ #define QWAYLANDXDGSHELLV5_P_H #include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h> -#include <QtWaylandCompositor/private/qwayland-server-xdg-shell.h> +#include <QtWaylandCompositor/private/qwayland-server-xdg-shell-unstable-v5.h> #include <QtWaylandCompositor/QWaylandXdgShellV5> diff --git a/src/compositor/extensions/qwaylandxdgshellv5integration.cpp b/src/compositor/extensions/qwaylandxdgshellv5integration.cpp index a349a25aa..ea04a33d2 100644 --- a/src/compositor/extensions/qwaylandxdgshellv5integration.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv5integration.cpp @@ -50,6 +50,12 @@ QT_BEGIN_NAMESPACE namespace QtWayland { +static void handlePopupCreated(QWaylandQuickShellSurfaceItem *parentItem, QWaylandXdgPopupV5 *popup) +{ + if (parentItem->surface() == popup->parentSurface()) + QWaylandQuickShellSurfaceItemPrivate::get(parentItem)->maybeCreateAutoPopup(popup); +} + XdgShellV5Integration::XdgShellV5Integration(QWaylandQuickShellSurfaceItem *item) : QWaylandQuickShellIntegration(item) , m_item(item) @@ -66,6 +72,9 @@ XdgShellV5Integration::XdgShellV5Integration(QWaylandQuickShellSurfaceItem *item connect(m_xdgSurface, &QWaylandXdgSurfaceV5::maximizedChanged, this, &XdgShellV5Integration::handleMaximizedChanged); connect(m_xdgSurface, &QWaylandXdgSurfaceV5::activatedChanged, this, &XdgShellV5Integration::handleActivatedChanged); connect(m_xdgSurface->surface(), &QWaylandSurface::sizeChanged, this, &XdgShellV5Integration::handleSurfaceSizeChanged); + connect(m_xdgSurface->shell(), &QWaylandXdgShellV5::xdgPopupCreated, this, [item](QWaylandXdgPopupV5 *popup){ + handlePopupCreated(item, popup); + }); } XdgShellV5Integration::~XdgShellV5Integration() @@ -213,6 +222,9 @@ XdgPopupV5Integration::XdgPopupV5Integration(QWaylandQuickShellSurfaceItem *item QWaylandQuickShellEventFilter::startFilter(client, [shell]() { shell->closeAllPopups(); }); connect(m_xdgPopup, &QWaylandXdgPopupV5::destroyed, this, &XdgPopupV5Integration::handlePopupDestroyed); + connect(m_xdgPopup->shell(), &QWaylandXdgShellV5::xdgPopupCreated, this, [item](QWaylandXdgPopupV5 *popup) { + handlePopupCreated(item, popup); + }); } XdgPopupV5Integration::~XdgPopupV5Integration() diff --git a/src/compositor/extensions/qwaylandxdgshellv6integration.cpp b/src/compositor/extensions/qwaylandxdgshellv6integration.cpp index 17ee10db7..68230937f 100644 --- a/src/compositor/extensions/qwaylandxdgshellv6integration.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv6integration.cpp @@ -44,6 +44,12 @@ QT_BEGIN_NAMESPACE namespace QtWayland { +static void handlePopupCreated(QWaylandQuickShellSurfaceItem *parentItem, QWaylandXdgPopupV6 *popup) +{ + if (parentItem->shellSurface() == popup->parentXdgSurface()) + QWaylandQuickShellSurfaceItemPrivate::get(parentItem)->maybeCreateAutoPopup(popup->xdgSurface()); +} + XdgToplevelV6Integration::XdgToplevelV6Integration(QWaylandQuickShellSurfaceItem *item) : QWaylandQuickShellIntegration(item) , m_item(item) @@ -54,12 +60,16 @@ XdgToplevelV6Integration::XdgToplevelV6Integration(QWaylandQuickShellSurfaceItem Q_ASSERT(m_toplevel); m_item->setSurface(m_xdgSurface->surface()); + connect(m_toplevel, &QWaylandXdgToplevelV6::startMove, this, &XdgToplevelV6Integration::handleStartMove); connect(m_toplevel, &QWaylandXdgToplevelV6::startResize, this, &XdgToplevelV6Integration::handleStartResize); connect(m_toplevel, &QWaylandXdgToplevelV6::setMaximized, this, &XdgToplevelV6Integration::handleSetMaximized); connect(m_toplevel, &QWaylandXdgToplevelV6::unsetMaximized, this, &XdgToplevelV6Integration::handleUnsetMaximized); connect(m_toplevel, &QWaylandXdgToplevelV6::maximizedChanged, this, &XdgToplevelV6Integration::handleMaximizedChanged); connect(m_toplevel, &QWaylandXdgToplevelV6::activatedChanged, this, &XdgToplevelV6Integration::handleActivatedChanged); + connect(m_xdgSurface->shell(), &QWaylandXdgShellV6::popupCreated, this, [item](QWaylandXdgPopupV6 *popup, QWaylandXdgSurfaceV6 *){ + handlePopupCreated(item, popup); + }); connect(m_xdgSurface->surface(), &QWaylandSurface::sizeChanged, this, &XdgToplevelV6Integration::handleSurfaceSizeChanged); } @@ -181,6 +191,9 @@ XdgPopupV6Integration::XdgPopupV6Integration(QWaylandQuickShellSurfaceItem *item handleGeometryChanged(); connect(m_popup, &QWaylandXdgPopupV6::configuredGeometryChanged, this, &XdgPopupV6Integration::handleGeometryChanged); + connect(m_xdgSurface->shell(), &QWaylandXdgShellV6::popupCreated, this, [item](QWaylandXdgPopupV6 *popup, QWaylandXdgSurfaceV6 *){ + handlePopupCreated(item, popup); + }); } void XdgPopupV6Integration::handleGeometryChanged() diff --git a/src/compositor/extensions/qwlqtkey.cpp b/src/compositor/extensions/qwlqtkey.cpp index 73d06ae6b..94a2b5a77 100644 --- a/src/compositor/extensions/qwlqtkey.cpp +++ b/src/compositor/extensions/qwlqtkey.cpp @@ -48,7 +48,7 @@ namespace QtWayland { QtKeyExtensionGlobal::QtKeyExtensionGlobal(QWaylandCompositor *compositor) : QWaylandCompositorExtensionTemplate(compositor) - , QtWaylandServer::qt_key_extension(compositor->display(), 2) + , QtWaylandServer::zqt_key_v1(compositor->display(), 1) , m_compositor(compositor) { } @@ -60,15 +60,15 @@ bool QtKeyExtensionGlobal::postQtKeyEvent(QKeyEvent *event, QWaylandSurface *sur Resource *target = surface ? resourceMap().value(surface->waylandClient()) : 0; if (target) { - send_qtkey(target->handle, - surface ? surface->resource() : nullptr, - time, event->type(), event->key(), event->modifiers(), - event->nativeScanCode(), - event->nativeVirtualKey(), - event->nativeModifiers(), - event->text(), - event->isAutoRepeat(), - event->count()); + send_key(target->handle, + surface ? surface->resource() : nullptr, + time, event->type(), event->key(), event->modifiers(), + event->nativeScanCode(), + event->nativeVirtualKey(), + event->nativeModifiers(), + event->text(), + event->isAutoRepeat(), + event->count()); return true; } diff --git a/src/compositor/extensions/qwlqtkey_p.h b/src/compositor/extensions/qwlqtkey_p.h index d3f573757..4944829ef 100644 --- a/src/compositor/extensions/qwlqtkey_p.h +++ b/src/compositor/extensions/qwlqtkey_p.h @@ -53,7 +53,7 @@ #include <QtWaylandCompositor/QWaylandCompositorExtensionTemplate> #include <QtWaylandCompositor/QWaylandCompositor> -#include <QtWaylandCompositor/private/qwayland-server-qtkey-extension.h> +#include <QtWaylandCompositor/private/qwayland-server-qt-key-unstable-v1.h> #include <wayland-util.h> @@ -64,7 +64,7 @@ class QKeyEvent; namespace QtWayland { -class QtKeyExtensionGlobal : public QWaylandCompositorExtensionTemplate<QtKeyExtensionGlobal>, public QtWaylandServer::qt_key_extension +class QtKeyExtensionGlobal : public QWaylandCompositorExtensionTemplate<QtKeyExtensionGlobal>, public QtWaylandServer::zqt_key_v1 { Q_OBJECT public: diff --git a/src/extensions/README.md b/src/extensions/README.md new file mode 100644 index 000000000..f75e0f236 --- /dev/null +++ b/src/extensions/README.md @@ -0,0 +1,18 @@ +# Internal Qt protocol extensions + +The protocol extensions in this folder are considered implementation details of +Qt. I.e. they may removed, renamed or changed without warning. + +## Suffixed protocols + +For protocols that have a version suffix, however, we will strive to not break +backwards compatibility without bumping the suffix (renaming the protocol). +E.g.: If your client sees a `zqt_key_v1` global, it can safely bind to it: +the key event will always take the same number of arguments, regardless of +compositor version. + +This is important also within a Qt-only scope if there are multiple versions of +Qt on the system. Consider for instance an application statically linked to Qt +(such as Qt Creator) running against a Qt compositor installed by the distro). +In such cases we don't want the compositor and client to disagree on the +protocol definition. diff --git a/src/extensions/qtkey-extension.xml b/src/extensions/qt-key-unstable-v1.xml index 34cf24797..0f8d8284c 100644 --- a/src/extensions/qtkey-extension.xml +++ b/src/extensions/qt-key-unstable-v1.xml @@ -1,7 +1,7 @@ -<protocol name="qtkey_extension"> +<protocol name="qt_key_unstable_v1"> <copyright> - Copyright (C) 2015 The Qt Company Ltd. + Copyright (C) 2018 The Qt Company Ltd. Contact: http://www.qt.io/licensing/ This file is part of the plugins of the Qt Toolkit. @@ -38,26 +38,36 @@ $QT_END_LICENSE$ </copyright> - <interface name="qt_key_extension" version="2"> - <event name="qtkey"> - <description summary="qtkey event"> - Serialized QKeyEvent - </description> - - <arg name="surface" type="object" interface="wl_surface"/> - <arg name="time" type="uint"/> - <arg name="type" type="uint"/> - <arg name="key" type="uint"/> - <arg name="modifiers" type="uint"/> - <arg name="nativeScanCode" type="uint"/> - <arg name="nativeVirtualKey" type="uint"/> - <arg name="nativeModifiers" type="uint"/> - <arg name="text" type="string"/> - <arg name="autorepeat" type="uint"/> - <arg name="count" type="uint"/> - </event> + <interface name="zqt_key_v1" version="1"> + <description summary="send serialized QKeyEvents"> + This protocol is independent of the Wayland seat concept. Its + intention is to let a QKeyEvent be transferred from a Qt compositor + to a Qt client without losing or adding false information by going + through wl_seat and wl_keyboard. - <request name="dummy"> - </request> + Note: This protocol is considered private to Qt. We will do our + best to bump version numbers when we make backwards compatible + changes, bump the protocol name and interface suffixes when we make + backwards incompatible changes, but we provide no guarantees. We + may also remove the protocol without warning. Implement this at + your own risk. + </description> + <event name="key"> + <description summary="send key event"> + The key event notifies the client that a QKeyEvent has occurred on + the server side. + </description> + <arg name="surface" type="object" interface="wl_surface"/> + <arg name="time" type="uint"/> + <arg name="type" type="uint"/> + <arg name="key" type="uint"/> + <arg name="modifiers" type="uint"/> + <arg name="nativeScanCode" type="uint"/> + <arg name="nativeVirtualKey" type="uint"/> + <arg name="nativeModifiers" type="uint"/> + <arg name="text" type="string"/> + <arg name="autorepeat" type="uint"/> + <arg name="count" type="uint"/> + </event> </interface> </protocol> diff --git a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.cpp b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.cpp index 4478b9fdf..cd0f285cd 100644 --- a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.cpp +++ b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.cpp @@ -124,6 +124,33 @@ EGLDisplay QWaylandBrcmEglIntegration::eglDisplay() const return m_eglDisplay; } +void *QWaylandBrcmEglIntegration::nativeResource(NativeResource resource) +{ + switch (resource) { + case EglDisplay: + return m_eglDisplay; + default: + break; + } + return nullptr; +} + +void *QWaylandBrcmEglIntegration::nativeResourceForContext(NativeResource resource, QPlatformOpenGLContext *context) +{ + Q_ASSERT(context); + switch (resource) { + case EglConfig: + return static_cast<QWaylandBrcmGLContext *>(context)->eglConfig(); + case EglContext: + return static_cast<QWaylandBrcmGLContext *>(context)->eglContext(); + case EglDisplay: + return m_eglDisplay; + default: + break; + } + return nullptr; +} + } QT_END_NAMESPACE diff --git a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h index 651400d87..5e8a3bf46 100644 --- a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h +++ b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h @@ -82,6 +82,9 @@ public: PFNEGLCREATEGLOBALIMAGEBRCMPROC eglCreateGlobalImageBRCM; PFNEGLDESTROYGLOBALIMAGEBRCMPROC eglDestroyGlobalImageBRCM; + void *nativeResource(NativeResource resource) override; + void *nativeResourceForContext(NativeResource resource, QPlatformOpenGLContext *context) override; + private: static void wlDisplayHandleGlobal(void *data, struct wl_registry *registry, uint32_t id, const QString &interface, uint32_t version); diff --git a/src/plugins/decorations/bradient/main.cpp b/src/plugins/decorations/bradient/main.cpp index 9a7e91e6f..d3627d2f7 100644 --- a/src/plugins/decorations/bradient/main.cpp +++ b/src/plugins/decorations/bradient/main.cpp @@ -270,8 +270,7 @@ void QWaylandBradientDecoration::paint(QPaintDevice *device) p.drawPixmap(closeButtonRect(), closePixmap, closePixmap.rect()); // Maximize button - QPixmap maximizePixmap(waylandWindow()->isMaximized() - ? qt_normalizeup_xpm : qt_maximize_xpm); + QPixmap maximizePixmap((window()->windowStates() & Qt::WindowMaximized) ? qt_normalizeup_xpm : qt_maximize_xpm); p.drawPixmap(maximizeButtonRect(), maximizePixmap, maximizePixmap.rect()); // Minimize button @@ -356,7 +355,7 @@ bool QWaylandBradientDecoration::handleMouse(QWaylandInputDevice *inputDevice, c QWindowSystemInterface::handleCloseEvent(window()); } else if (maximizeButtonRect().contains(local)) { if (clickButton(b, Maximize)) - window()->setWindowState(waylandWindow()->isMaximized() ? Qt::WindowNoState : Qt::WindowMaximized); + window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized); } else if (minimizeButtonRect().contains(local)) { if (clickButton(b, Minimize)) window()->setWindowState(Qt::WindowMinimized); @@ -390,7 +389,7 @@ bool QWaylandBradientDecoration::handleTouch(QWaylandInputDevice *inputDevice, c if (closeButtonRect().contains(local)) QWindowSystemInterface::handleCloseEvent(window()); else if (maximizeButtonRect().contains(local)) - window()->setWindowState(waylandWindow()->isMaximized() ? Qt::WindowNoState : Qt::WindowMaximized); + window()->setWindowStates(window()->windowStates() ^ Qt::WindowMaximized); else if (minimizeButtonRect().contains(local)) window()->setWindowState(Qt::WindowMinimized); else if (local.y() <= margins().top()) diff --git a/src/plugins/shellintegration/ivi-shell/qwaylandivisurface.cpp b/src/plugins/shellintegration/ivi-shell/qwaylandivisurface.cpp index ec529b124..871709cdf 100644 --- a/src/plugins/shellintegration/ivi-shell/qwaylandivisurface.cpp +++ b/src/plugins/shellintegration/ivi-shell/qwaylandivisurface.cpp @@ -82,6 +82,11 @@ void QWaylandIviSurface::setType(Qt::WindowType type, QWaylandWindow *transientP Q_UNUSED(transientParent) } +void QWaylandIviSurface::applyConfigure() +{ + m_window->resizeFromApplyConfigure(m_pendingSize); +} + void QWaylandIviSurface::createExtendedSurface(QWaylandWindow *window) { if (window->display()->windowExtension()) @@ -90,7 +95,8 @@ void QWaylandIviSurface::createExtendedSurface(QWaylandWindow *window) void QWaylandIviSurface::ivi_surface_configure(int32_t width, int32_t height) { - this->m_window->configure(0, width, height); + m_pendingSize = {width, height}; + m_window->applyConfigureWhenPossible(); } void QWaylandIviSurface::ivi_controller_surface_visibility(int32_t visibility) diff --git a/src/plugins/shellintegration/ivi-shell/qwaylandivisurface_p.h b/src/plugins/shellintegration/ivi-shell/qwaylandivisurface_p.h index ff943060b..6ec28e758 100644 --- a/src/plugins/shellintegration/ivi-shell/qwaylandivisurface_p.h +++ b/src/plugins/shellintegration/ivi-shell/qwaylandivisurface_p.h @@ -63,6 +63,7 @@ public: ~QWaylandIviSurface() override; void setType(Qt::WindowType type, QWaylandWindow *transientParent) override; + void applyConfigure() override; private: void createExtendedSurface(QWaylandWindow *window); @@ -71,6 +72,7 @@ private: QWaylandWindow *m_window = nullptr; QWaylandExtendedSurface *m_extendedWindow = nullptr; + QSize m_pendingSize = {0, 0}; }; } diff --git a/src/plugins/shellintegration/shellintegration.pro b/src/plugins/shellintegration/shellintegration.pro index 9867d2b40..12a900db2 100644 --- a/src/plugins/shellintegration/shellintegration.pro +++ b/src/plugins/shellintegration/shellintegration.pro @@ -1,3 +1,7 @@ TEMPLATE = subdirs -SUBDIRS += ivi-shell +SUBDIRS += \ + ivi-shell \ + xdg-shell-v5 \ + wl-shell \ + diff --git a/src/plugins/shellintegration/wl-shell/main.cpp b/src/plugins/shellintegration/wl-shell/main.cpp new file mode 100644 index 000000000..9b033b5f3 --- /dev/null +++ b/src/plugins/shellintegration/wl-shell/main.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2017 ITAGE Corporation, author: <yusuke.binsaki@itage.co.jp> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandwlshellintegration_p.h" + +#include <QtWaylandClient/private/qwaylandshellintegrationplugin_p.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandWlShellIntegrationPlugin : public QWaylandShellIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QWaylandShellIntegrationFactoryInterface_iid FILE "wl-shell.json") + +public: + QWaylandShellIntegration *create(const QString &key, const QStringList ¶mList) override; +}; + +QWaylandShellIntegration *QWaylandWlShellIntegrationPlugin::create(const QString &key, const QStringList ¶mList) +{ + Q_UNUSED(key); + Q_UNUSED(paramList); + return new QWaylandWlShellIntegration(); +} + +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/client/qwaylandwlshellintegration.cpp b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp index 81c46be29..be156dad2 100644 --- a/src/client/qwaylandwlshellintegration.cpp +++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration.cpp @@ -38,23 +38,16 @@ ****************************************************************************/ #include "qwaylandwlshellintegration_p.h" +#include "qwaylandwlshellsurface_p.h" #include <QtWaylandClient/private/qwaylandwindow_p.h> #include <QtWaylandClient/private/qwaylanddisplay_p.h> -#include <QtWaylandClient/private/qwaylandwlshellsurface_p.h> QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandWlShellIntegration *QWaylandWlShellIntegration::create(QWaylandDisplay *display) -{ - if (display->hasRegistryGlobal(QLatin1String("wl_shell"))) - return new QWaylandWlShellIntegration(display); - return nullptr; -} - -QWaylandWlShellIntegration::QWaylandWlShellIntegration(QWaylandDisplay *display) +bool QWaylandWlShellIntegration::initialize(QWaylandDisplay *display) { Q_FOREACH (QWaylandDisplay::RegistryGlobal global, display->globals()) { if (global.interface == QLatin1String("wl_shell")) { @@ -62,19 +55,33 @@ QWaylandWlShellIntegration::QWaylandWlShellIntegration(QWaylandDisplay *display) break; } } -} -bool QWaylandWlShellIntegration::initialize(QWaylandDisplay *display) -{ - QWaylandShellIntegration::initialize(display); - return m_wlShell != nullptr; -}; + if (!m_wlShell) { + qCDebug(lcQpaWayland) << "Couldn't find global wl_shell"; + return false; + } + + return QWaylandShellIntegration::initialize(display); +} QWaylandShellSurface *QWaylandWlShellIntegration::createShellSurface(QWaylandWindow *window) { return new QWaylandWlShellSurface(m_wlShell->get_shell_surface(window->object()), window); } +void *QWaylandWlShellIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window) +{ + QByteArray lowerCaseResource = resource.toLower(); + if (lowerCaseResource == "wl_shell_surface") { + if (auto waylandWindow = static_cast<QWaylandWindow *>(window->handle())) { + if (auto shellSurface = qobject_cast<QWaylandWlShellSurface *>(waylandWindow->shellSurface())) { + return shellSurface->object(); + } + } + } + return nullptr; } +} // namespace QtWaylandClient + QT_END_NAMESPACE diff --git a/src/client/qwaylandwlshellintegration_p.h b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h index 7a6b53524..80a7507d4 100644 --- a/src/client/qwaylandwlshellintegration_p.h +++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h @@ -63,13 +63,12 @@ namespace QtWaylandClient { class Q_WAYLAND_CLIENT_EXPORT QWaylandWlShellIntegration : public QWaylandShellIntegration { public: - static QWaylandWlShellIntegration *create(QWaylandDisplay* display); + QWaylandWlShellIntegration() {} bool initialize(QWaylandDisplay *) override; QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; + void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override; private: - QWaylandWlShellIntegration(QWaylandDisplay* display); - QtWayland::wl_shell *m_wlShell = nullptr; }; diff --git a/src/client/qwaylandwlshellsurface.cpp b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp index 098eddcbc..9f2bcc00c 100644 --- a/src/client/qwaylandwlshellsurface.cpp +++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp @@ -39,12 +39,12 @@ #include "qwaylandwlshellsurface_p.h" -#include "qwaylanddisplay_p.h" -#include "qwaylandwindow_p.h" -#include "qwaylandinputdevice_p.h" -#include "qwaylandabstractdecoration_p.h" -#include "qwaylandscreen_p.h" -#include "qwaylandextendedsurface_p.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/private/qwaylandwindow_p.h> +#include <QtWaylandClient/private/qwaylandinputdevice_p.h> +#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h> +#include <QtWaylandClient/private/qwaylandscreen_p.h> +#include <QtWaylandClient/private/qwaylandextendedsurface_p.h> #include <QtCore/QDebug> @@ -121,35 +121,70 @@ void QWaylandWlShellSurface::sendProperty(const QString &name, const QVariant &v m_extendedWindow->updateGenericProperty(name, value); } -void QWaylandWlShellSurface::setMaximized() +void QWaylandWlShellSurface::applyConfigure() { - m_maximized = true; - m_size = m_window->window()->geometry().size(); - set_maximized(nullptr); + if ((m_pending.states & (Qt::WindowMaximized|Qt::WindowFullScreen)) + && !(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen))) { + m_normalSize = m_window->window()->frameGeometry().size(); + } + + if (m_pending.states != m_applied.states) + m_window->handleWindowStatesChanged(m_pending.states); + + if (!m_pending.size.isEmpty()) { + int x = 0; + int y = 0; + if (m_pending.edges & resize_left) + x = m_applied.size.width() - m_pending.size.width(); + if (m_pending.edges & resize_top) + y = m_applied.size.height() - m_pending.size.height(); + QPoint offset(x, y); + m_window->resizeFromApplyConfigure(m_pending.size, offset); + } else if (m_pending.size.isValid() && !m_normalSize.isEmpty()) { + m_window->resizeFromApplyConfigure(m_normalSize); + } + + m_applied = m_pending; } -void QWaylandWlShellSurface::setFullscreen() +bool QWaylandWlShellSurface::wantsDecorations() const { - m_fullscreen = true; - m_size = m_window->window()->geometry().size(); - set_fullscreen(WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, nullptr); + return !(m_pending.states & Qt::WindowFullScreen); } -void QWaylandWlShellSurface::setNormal() +void QWaylandWlShellSurface::requestWindowStates(Qt::WindowStates states) { - if (m_fullscreen || m_maximized) { - m_fullscreen = m_maximized = false; - setTopLevel(); - QMargins m = m_window->frameMargins(); - m_window->configure(0, m_size.width() + m.left() + m.right(), m_size.height() + m.top() + m.bottom()); + // On wl-shell the client is in charge of states, so diff from the pending state + Qt::WindowStates changedStates = m_pending.states ^ states; + Qt::WindowStates addedStates = changedStates & states; + + if (addedStates & Qt::WindowMinimized) + qCWarning(lcQpaWayland) << "Minimizing is not supported on wl-shell. Consider using xdg-shell instead."; + + if (addedStates & Qt::WindowMaximized) { + set_maximized(nullptr); + m_window->applyConfigureWhenPossible(); } -} -void QWaylandWlShellSurface::setMinimized() -{ - qCWarning(lcQpaWayland) << "Minimization is not supported on wl-shell. Consider using xdg-shell instead."; + if (addedStates & Qt::WindowFullScreen) { + set_fullscreen(WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, nullptr); + m_window->applyConfigureWhenPossible(); + } + + bool isNormal = ~states & (Qt::WindowMaximized | Qt::WindowFullScreen); + if (isNormal && (changedStates & (Qt::WindowMaximized | Qt::WindowFullScreen))) { + setTopLevel(); // set normal window + // There's usually no configure event after this, so just clear the rest of the pending + // configure here and queue the applyConfigure call + m_pending.size = {0, 0}; + m_pending.edges = resize_none; + m_window->applyConfigureWhenPossible(); + } + + m_pending.states = states & ~Qt::WindowMinimized; } + void QWaylandWlShellSurface::setTopLevel() { set_toplevel(); @@ -194,11 +229,11 @@ void QWaylandWlShellSurface::setPopup(QWaylandWindow *parent, QWaylandInputDevic { QWaylandWindow *parent_wayland_window = parent; if (!parent_wayland_window) { - qWarning("setPopup called without parent window"); + qCWarning(lcQpaWayland) << "setPopup called without a parent window"; return; } if (!device) { - qWarning("setPopup called without input device"); + qCWarning(lcQpaWayland) << "setPopup called without an input device"; return; } @@ -230,11 +265,13 @@ void QWaylandWlShellSurface::shell_surface_ping(uint32_t serial) pong(serial); } -void QWaylandWlShellSurface::shell_surface_configure(uint32_t edges, - int32_t width, - int32_t height) +void QWaylandWlShellSurface::shell_surface_configure(uint32_t edges, int32_t width, int32_t height) { - m_window->configure(edges, width, height); + m_pending.size = QSize(width, height); + m_pending.edges = static_cast<enum resize>(edges); + if (m_pending.edges && !m_pending.size.isEmpty()) + m_normalSize = m_pending.size; + m_window->applyConfigureWhenPossible(); } void QWaylandWlShellSurface::shell_surface_popup_done() diff --git a/src/client/qwaylandwlshellsurface_p.h b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h index 497ec6043..ca81dd685 100644 --- a/src/client/qwaylandwlshellsurface_p.h +++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h @@ -93,21 +93,26 @@ public: void sendProperty(const QString &name, const QVariant &value) override; void setType(Qt::WindowType type, QWaylandWindow *transientParent) override; + void applyConfigure() override; + bool wantsDecorations() const override; -private: - void setMaximized() override; - void setFullscreen() override; - void setNormal() override; - void setMinimized() override; +protected: + void requestWindowStates(Qt::WindowStates states) override; +private: void setTopLevel(); void updateTransientParent(QWindow *parent); void setPopup(QWaylandWindow *parent, QWaylandInputDevice *device, uint serial); QWaylandWindow *m_window = nullptr; - bool m_maximized = false; - bool m_fullscreen = false; - QSize m_size; + struct { + Qt::WindowStates states = Qt::WindowNoState; + QSize size; + enum resize edges = resize_none; + } m_applied, m_pending; + QSize m_normalSize; + // There's really no need to have pending and applied state on wl-shell, but we do it just to + // keep the different shell implementations more similar. QWaylandExtendedSurface *m_extendedWindow = nullptr; void shell_surface_ping(uint32_t serial) override; diff --git a/src/plugins/shellintegration/wl-shell/wl-shell.json b/src/plugins/shellintegration/wl-shell/wl-shell.json new file mode 100644 index 000000000..1859bb58a --- /dev/null +++ b/src/plugins/shellintegration/wl-shell/wl-shell.json @@ -0,0 +1,3 @@ +{ + "Keys":[ "wl-shell" ] +} diff --git a/src/plugins/shellintegration/wl-shell/wl-shell.pro b/src/plugins/shellintegration/wl-shell/wl-shell.pro new file mode 100644 index 000000000..211dc876f --- /dev/null +++ b/src/plugins/shellintegration/wl-shell/wl-shell.pro @@ -0,0 +1,23 @@ +QT += gui-private waylandclient-private +CONFIG += wayland-scanner + +QMAKE_USE += wayland-client + +WAYLANDCLIENTSOURCES += \ + ../../../3rdparty/protocol/wayland.xml + +HEADERS += \ + qwaylandwlshellintegration_p.h \ + qwaylandwlshellsurface_p.h \ + +SOURCES += \ + main.cpp \ + qwaylandwlshellintegration.cpp \ + qwaylandwlshellsurface.cpp \ + +OTHER_FILES += \ + wl-shell.json + +PLUGIN_TYPE = wayland-shell-integration +PLUGIN_CLASS_NAME = QWaylandWlShellIntegrationPlugin +load(qt_plugin) diff --git a/src/plugins/shellintegration/xdg-shell-v5/main.cpp b/src/plugins/shellintegration/xdg-shell-v5/main.cpp new file mode 100644 index 000000000..d365f682d --- /dev/null +++ b/src/plugins/shellintegration/xdg-shell-v5/main.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2017 ITAGE Corporation, author: <yusuke.binsaki@itage.co.jp> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandxdgshellv5integration_p.h" + +#include <QtWaylandClient/private/qwaylandshellintegrationplugin_p.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandXdgShellV5IntegrationPlugin : public QWaylandShellIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QWaylandShellIntegrationFactoryInterface_iid FILE "xdg-shell-v5.json") + +public: + QWaylandShellIntegration *create(const QString &key, const QStringList ¶mList) override; +}; + +QWaylandShellIntegration *QWaylandXdgShellV5IntegrationPlugin::create(const QString &key, const QStringList ¶mList) +{ + Q_UNUSED(key); + Q_UNUSED(paramList); + return new QWaylandXdgShellV5Integration(); +} + +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/client/qwaylandxdgpopup.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp index 46a12a828..542b1628d 100644 --- a/src/client/qwaylandxdgpopup.cpp +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5.cpp @@ -37,17 +37,17 @@ ** ****************************************************************************/ -#include "qwaylandxdgpopup_p.h" +#include "qwaylandxdgpopupv5_p.h" -#include "qwaylandwindow_p.h" -#include "qwaylanddisplay_p.h" -#include "qwaylandextendedsurface_p.h" +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/private/qwaylandwindow_p.h> +#include <QtWaylandClient/private/qwaylandextendedsurface_p.h> QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandXdgPopup::QWaylandXdgPopup(struct ::xdg_popup *popup, QWaylandWindow *window) +QWaylandXdgPopupV5::QWaylandXdgPopupV5(struct ::xdg_popup *popup, QWaylandWindow *window) : QWaylandShellSurface(window) , QtWayland::xdg_popup(popup) , m_window(window) @@ -56,19 +56,19 @@ QWaylandXdgPopup::QWaylandXdgPopup(struct ::xdg_popup *popup, QWaylandWindow *wi m_extendedWindow = new QWaylandExtendedSurface(window); } -QWaylandXdgPopup::~QWaylandXdgPopup() +QWaylandXdgPopupV5::~QWaylandXdgPopupV5() { xdg_popup_destroy(object()); delete m_extendedWindow; } -void QWaylandXdgPopup::setType(Qt::WindowType type, QWaylandWindow *transientParent) +void QWaylandXdgPopupV5::setType(Qt::WindowType type, QWaylandWindow *transientParent) { Q_UNUSED(type); Q_UNUSED(transientParent); } -void QWaylandXdgPopup::xdg_popup_popup_done() +void QWaylandXdgPopupV5::xdg_popup_popup_done() { m_window->window()->close(); } diff --git a/src/client/qwaylandxdgpopup_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h index b027c05dc..638b71534 100644 --- a/src/client/qwaylandxdgpopup_p.h +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QWAYLANDXDGPOPUP_P_H -#define QWAYLANDXDGPOPUP_P_H +#ifndef QWAYLANDXDGPOPUPV5_P_H +#define QWAYLANDXDGPOPUPV5_P_H // // W A R N I N G @@ -51,10 +51,11 @@ // We mean it. // +#include "qwayland-xdg-shell-unstable-v5.h" + #include <wayland-client.h> #include <QtWaylandClient/qtwaylandclientglobal.h> -#include <QtWaylandClient/private/qwayland-xdg-shell.h> #include <QtWaylandClient/private/qwaylandshellsurface_p.h> QT_BEGIN_NAMESPACE @@ -66,13 +67,13 @@ namespace QtWaylandClient { class QWaylandWindow; class QWaylandExtendedSurface; -class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgPopup : public QWaylandShellSurface +class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgPopupV5 : public QWaylandShellSurface , public QtWayland::xdg_popup { Q_OBJECT public: - QWaylandXdgPopup(struct ::xdg_popup *popup, QWaylandWindow *window); - ~QWaylandXdgPopup() override; + QWaylandXdgPopupV5(struct ::xdg_popup *popup, QWaylandWindow *window); + ~QWaylandXdgPopupV5() override; void setType(Qt::WindowType type, QWaylandWindow *transientParent) override; @@ -88,4 +89,4 @@ QT_END_NAMESPACE } -#endif // QWAYLANDXDGPOPUP_P_H +#endif // QWAYLANDXDGPOPUPV5_P_H diff --git a/src/client/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp index 9a34e72dd..6a693578c 100644 --- a/src/client/qwaylandxdgshell.cpp +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5.cpp @@ -37,43 +37,43 @@ ** ****************************************************************************/ -#include "qwaylandxdgshell_p.h" - -#include "qwaylanddisplay_p.h" -#include "qwaylandwindow_p.h" -#include "qwaylandinputdevice_p.h" -#include "qwaylandscreen_p.h" -#include "qwaylandxdgpopup_p.h" -#include "qwaylandxdgsurface_p.h" +#include "qwaylandxdgshellv5_p.h" +#include "qwaylandxdgpopupv5_p.h" +#include "qwaylandxdgsurfacev5_p.h" #include <QtCore/QDebug> +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/private/qwaylandwindow_p.h> +#include <QtWaylandClient/private/qwaylandinputdevice_p.h> +#include <QtWaylandClient/private/qwaylandscreen_p.h> + QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandXdgShell::QWaylandXdgShell(struct ::xdg_shell *shell) +QWaylandXdgShellV5::QWaylandXdgShellV5(struct ::xdg_shell *shell) : QtWayland::xdg_shell(shell) { } -QWaylandXdgShell::QWaylandXdgShell(struct ::wl_registry *registry, uint32_t id) +QWaylandXdgShellV5::QWaylandXdgShellV5(struct ::wl_registry *registry, uint32_t id) : QtWayland::xdg_shell(registry, id, 1) { use_unstable_version(QtWayland::xdg_shell::version_current); } -QWaylandXdgShell::~QWaylandXdgShell() +QWaylandXdgShellV5::~QWaylandXdgShellV5() { xdg_shell_destroy(object()); } -QWaylandXdgSurface *QWaylandXdgShell::createXdgSurface(QWaylandWindow *window) +QWaylandXdgSurfaceV5 *QWaylandXdgShellV5::createXdgSurface(QWaylandWindow *window) { - return new QWaylandXdgSurface(this, window); + return new QWaylandXdgSurfaceV5(this, window); } -QWaylandXdgPopup *QWaylandXdgShell::createXdgPopup(QWaylandWindow *window, QWaylandInputDevice *inputDevice) +QWaylandXdgPopupV5 *QWaylandXdgShellV5::createXdgPopup(QWaylandWindow *window, QWaylandInputDevice *inputDevice) { QWaylandWindow *parentWindow = m_popups.empty() ? window->transientParent() : m_popups.last(); ::wl_surface *parentSurface = parentWindow->object(); @@ -86,9 +86,9 @@ QWaylandXdgPopup *QWaylandXdgShell::createXdgPopup(QWaylandWindow *window, QWayl int x = position.x() + parentWindow->frameMargins().left(); int y = position.y() + parentWindow->frameMargins().top(); - auto popup = new QWaylandXdgPopup(get_xdg_popup(window->object(), parentSurface, seat, m_popupSerial, x, y), window); + auto popup = new QWaylandXdgPopupV5(get_xdg_popup(window->object(), parentSurface, seat, m_popupSerial, x, y), window); m_popups.append(window); - QObject::connect(popup, &QWaylandXdgPopup::destroyed, [this, window](){ + QObject::connect(popup, &QWaylandXdgPopupV5::destroyed, [this, window](){ m_popups.removeOne(window); if (m_popups.empty()) m_popupSerial = 0; @@ -96,7 +96,7 @@ QWaylandXdgPopup *QWaylandXdgShell::createXdgPopup(QWaylandWindow *window, QWayl return popup; } -void QWaylandXdgShell::xdg_shell_ping(uint32_t serial) +void QWaylandXdgShellV5::xdg_shell_ping(uint32_t serial) { pong(serial); } diff --git a/src/client/qwaylandxdgshell_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h index 761f25218..ec45bb32c 100644 --- a/src/client/qwaylandxdgshell_p.h +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QWAYLANDXDGSHELL_H -#define QWAYLANDXDGSHELL_H +#ifndef QWAYLANDXDGSHELLV5_H +#define QWAYLANDXDGSHELLV5_H // // W A R N I N G @@ -51,12 +51,13 @@ // We mean it. // +#include "qwayland-xdg-shell-unstable-v5.h" + #include <QtCore/QSize> #include <QtCore/QVector> #include <wayland-client.h> -#include <QtWaylandClient/private/qwayland-xdg-shell.h> #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtWaylandClient/private/qwaylandshellsurface_p.h> @@ -68,18 +69,18 @@ namespace QtWaylandClient { class QWaylandWindow; class QWaylandInputDevice; -class QWaylandXdgSurface; -class QWaylandXdgPopup; +class QWaylandXdgSurfaceV5; +class QWaylandXdgPopupV5; -class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgShell : public QtWayland::xdg_shell +class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgShellV5 : public QtWayland::xdg_shell { public: - QWaylandXdgShell(struct ::xdg_shell *shell); - QWaylandXdgShell(struct ::wl_registry *registry, uint32_t id); - ~QWaylandXdgShell() override; + QWaylandXdgShellV5(struct ::xdg_shell *shell); + QWaylandXdgShellV5(struct ::wl_registry *registry, uint32_t id); + ~QWaylandXdgShellV5() override; - QWaylandXdgSurface *createXdgSurface(QWaylandWindow *window); - QWaylandXdgPopup *createXdgPopup(QWaylandWindow *window, QWaylandInputDevice *inputDevice); + QWaylandXdgSurfaceV5 *createXdgSurface(QWaylandWindow *window); + QWaylandXdgPopupV5 *createXdgPopup(QWaylandWindow *window, QWaylandInputDevice *inputDevice); private: void xdg_shell_ping(uint32_t serial) override; @@ -92,4 +93,4 @@ QT_END_NAMESPACE } -#endif // QWAYLANDXDGSHELL_H +#endif // QWAYLANDXDGSHELLV5_H diff --git a/src/client/qwaylandxdgshellintegration.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp index ee72c2d52..47d748b1c 100644 --- a/src/client/qwaylandxdgshellintegration.cpp +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration.cpp @@ -37,42 +37,36 @@ ** ****************************************************************************/ -#include "qwaylandxdgshellintegration_p.h" +#include "qwaylandxdgshellv5integration_p.h" +#include "qwaylandxdgsurfacev5_p.h" +#include "qwaylandxdgpopupv5_p.h" +#include "qwaylandxdgshellv5_p.h" #include <QtWaylandClient/private/qwaylandwindow_p.h> #include <QtWaylandClient/private/qwaylanddisplay_p.h> -#include <QtWaylandClient/private/qwaylandxdgsurface_p.h> -#include <QtWaylandClient/private/qwaylandxdgpopup_p.h> -#include <QtWaylandClient/private/qwaylandxdgshell_p.h> QT_BEGIN_NAMESPACE namespace QtWaylandClient { -QWaylandXdgShellIntegration *QWaylandXdgShellIntegration::create(QWaylandDisplay *display) -{ - if (display->hasRegistryGlobal(QLatin1String("xdg_shell"))) - return new QWaylandXdgShellIntegration(display); - return nullptr; -} - -QWaylandXdgShellIntegration::QWaylandXdgShellIntegration(QWaylandDisplay *display) +bool QWaylandXdgShellV5Integration::initialize(QWaylandDisplay *display) { Q_FOREACH (QWaylandDisplay::RegistryGlobal global, display->globals()) { if (global.interface == QLatin1String("xdg_shell")) { - m_xdgShell = new QWaylandXdgShell(display->wl_registry(), global.id); + m_xdgShell.reset(new QWaylandXdgShellV5(display->wl_registry(), global.id)); break; } } -} -bool QWaylandXdgShellIntegration::initialize(QWaylandDisplay *display) -{ - QWaylandShellIntegration::initialize(display); - return m_xdgShell != nullptr; + if (!m_xdgShell) { + qWarning() << "Couldn't find global xdg_shell for xdg-shell unstable v5"; + return false; + } + + return QWaylandShellIntegration::initialize(display); } -QWaylandShellSurface *QWaylandXdgShellIntegration::createShellSurface(QWaylandWindow *window) +QWaylandShellSurface *QWaylandXdgShellV5Integration::createShellSurface(QWaylandWindow *window) { QWaylandInputDevice *inputDevice = window->display()->lastInputDevice(); if (window->window()->type() == Qt::WindowType::Popup && inputDevice) @@ -81,10 +75,10 @@ QWaylandShellSurface *QWaylandXdgShellIntegration::createShellSurface(QWaylandWi return m_xdgShell->createXdgSurface(window); } -void QWaylandXdgShellIntegration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) { - if (newFocus && qobject_cast<QWaylandXdgPopup *>(newFocus->shellSurface())) +void QWaylandXdgShellV5Integration::handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) { + if (newFocus && qobject_cast<QWaylandXdgPopupV5 *>(newFocus->shellSurface())) m_display->handleWindowActivated(newFocus); - if (oldFocus && qobject_cast<QWaylandXdgPopup *>(oldFocus->shellSurface())) + if (oldFocus && qobject_cast<QWaylandXdgPopupV5 *>(oldFocus->shellSurface())) m_display->handleWindowDeactivated(oldFocus); } diff --git a/src/client/qwaylandxdgshellintegration_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h index ed307a5de..ce6bdb9ee 100644 --- a/src/client/qwaylandxdgshellintegration_p.h +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5integration_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QWAYLANDXDGSHELLINTEGRATION_P_H -#define QWAYLANDXDGSHELLINTEGRATION_P_H +#ifndef QWAYLANDXDGSHELLV5INTEGRATION_P_H +#define QWAYLANDXDGSHELLV5INTEGRATION_P_H // // W A R N I N G @@ -51,7 +51,7 @@ // We mean it. // -#include <wayland-client.h> +#include "qwaylandxdgshellv5_p.h" #include <QtWaylandClient/private/qwaylandshellintegration_p.h> @@ -59,24 +59,22 @@ QT_BEGIN_NAMESPACE namespace QtWaylandClient { -class QWaylandXdgShell; +class QWaylandXdgShellV5; -class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgShellIntegration : public QWaylandShellIntegration +class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgShellV5Integration : public QWaylandShellIntegration { public: - static QWaylandXdgShellIntegration *create(QWaylandDisplay* display); + QWaylandXdgShellV5Integration() {} bool initialize(QWaylandDisplay *display) override; QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; void handleKeyboardFocusChanged(QWaylandWindow *newFocus, QWaylandWindow *oldFocus) override; private: - QWaylandXdgShellIntegration(QWaylandDisplay *display); - - QWaylandXdgShell *m_xdgShell = nullptr; + QScopedPointer<QWaylandXdgShellV5> m_xdgShell; }; } QT_END_NAMESPACE -#endif // QWAYLANDXDGSHELLINTEGRATION_P_H +#endif // QWAYLANDXDGSHELLV5INTEGRATION_P_H diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp new file mode 100644 index 000000000..315dc6231 --- /dev/null +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the config.tests of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or 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.GPL2 and 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandxdgsurfacev5_p.h" +#include "qwaylandxdgshellv5_p.h" + +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/private/qwaylanddisplay_p.h> +#include <QtWaylandClient/private/qwaylandwindow_p.h> +#include <QtWaylandClient/private/qwaylandinputdevice_p.h> +#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h> +#include <QtWaylandClient/private/qwaylandscreen_p.h> +#include <QtWaylandClient/private/qwaylandextendedsurface_p.h> + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandXdgSurfaceV5::QWaylandXdgSurfaceV5(QWaylandXdgShellV5 *shell, QWaylandWindow *window) + : QWaylandShellSurface(window) + , QtWayland::xdg_surface(shell->get_xdg_surface(window->object())) + , m_window(window) + , m_shell(shell) +{ + if (window->display()->windowExtension()) + m_extendedWindow = new QWaylandExtendedSurface(window); +} + +QWaylandXdgSurfaceV5::~QWaylandXdgSurfaceV5() +{ + if (m_acked.states & Qt::WindowActive) + window()->display()->handleWindowDeactivated(m_window); + + xdg_surface_destroy(object()); + delete m_extendedWindow; +} + +void QWaylandXdgSurfaceV5::resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) +{ + // May need some conversion if types get incompatibles, ATM they're identical + enum resize_edge const * const arg = reinterpret_cast<enum resize_edge const * const>(&edges); + resize(inputDevice, *arg); +} + +void QWaylandXdgSurfaceV5::resize(QWaylandInputDevice *inputDevice, enum resize_edge edges) +{ + resize(inputDevice->wl_seat(), + inputDevice->serial(), + edges); +} + +bool QWaylandXdgSurfaceV5::move(QWaylandInputDevice *inputDevice) +{ + move(inputDevice->wl_seat(), + inputDevice->serial()); + return true; +} + +void QWaylandXdgSurfaceV5::updateTransientParent(QWaylandWindow *parent) +{ + if (!parent) + return; + auto parentXdgSurface = qobject_cast<QWaylandXdgSurfaceV5 *>(parent->shellSurface()); + Q_ASSERT(parentXdgSurface); + set_parent(parentXdgSurface->object()); +} + +void QWaylandXdgSurfaceV5::setTitle(const QString & title) +{ + return QtWayland::xdg_surface::set_title(title); +} + +void QWaylandXdgSurfaceV5::setAppId(const QString & appId) +{ + return QtWayland::xdg_surface::set_app_id(appId); +} + +void QWaylandXdgSurfaceV5::raise() +{ + if (m_extendedWindow) + m_extendedWindow->raise(); +} + +void QWaylandXdgSurfaceV5::lower() +{ + if (m_extendedWindow) + m_extendedWindow->lower(); +} + +void QWaylandXdgSurfaceV5::setContentOrientationMask(Qt::ScreenOrientations orientation) +{ + if (m_extendedWindow) + m_extendedWindow->setContentOrientationMask(orientation); +} + +void QWaylandXdgSurfaceV5::setWindowFlags(Qt::WindowFlags flags) +{ + if (m_extendedWindow) + m_extendedWindow->setWindowFlags(flags); +} + +void QWaylandXdgSurfaceV5::sendProperty(const QString &name, const QVariant &value) +{ + if (m_extendedWindow) + m_extendedWindow->updateGenericProperty(name, value); +} + +void QWaylandXdgSurfaceV5::setType(Qt::WindowType type, QWaylandWindow *transientParent) +{ + Q_UNUSED(type) + if (transientParent) + updateTransientParent(transientParent); +} + +void QWaylandXdgSurfaceV5::applyConfigure() +{ + if (m_pending.isResizing) + m_normalSize = m_pending.size; + else if (!(m_acked.states & (Qt::WindowMaximized|Qt::WindowFullScreen))) + m_normalSize = m_window->window()->frameGeometry().size(); + + if ((m_pending.states & Qt::WindowActive) && !(m_acked.states & Qt::WindowActive)) + m_window->display()->handleWindowActivated(m_window); + + if (!(m_pending.states & Qt::WindowActive) && (m_acked.states & Qt::WindowActive)) + m_window->display()->handleWindowDeactivated(m_window); + + // TODO: none of the other plugins send WindowActive either, but is it on purpose? + Qt::WindowStates statesWithoutActive = m_pending.states & ~Qt::WindowActive; + + m_window->handleWindowStatesChanged(statesWithoutActive); + if (!m_pending.size.isEmpty()) + m_window->resizeFromApplyConfigure(m_pending.size); + else if (!m_normalSize.isEmpty()) + m_window->resizeFromApplyConfigure(m_normalSize); + ack_configure(m_pending.serial); + m_acked = m_pending; +} + +void QWaylandXdgSurfaceV5::requestWindowStates(Qt::WindowStates states) +{ + Qt::WindowStates changedStates = m_acked.states ^ states; + + if (changedStates & Qt::WindowMaximized) { + if (states & Qt::WindowMaximized) + set_maximized(); + else + unset_maximized(); + } + + if (changedStates & Qt::WindowFullScreen) { + if (states & Qt::WindowFullScreen) + set_fullscreen(nullptr); + else + unset_fullscreen(); + } + + // Minimized state is not reported by the protocol, so always send it + if (states & Qt::WindowMinimized) { + set_minimized(); + window()->handleWindowStatesChanged(states & ~Qt::WindowMinimized); + } +} + +bool QWaylandXdgSurfaceV5::wantsDecorations() const +{ + return !(m_pending.states & Qt::WindowFullScreen); +} + +void QWaylandXdgSurfaceV5::xdg_surface_configure(int32_t width, int32_t height, struct wl_array *states,uint32_t serial) +{ + uint32_t *xdgStates = reinterpret_cast<uint32_t*>(states->data); + size_t numStates = states->size / sizeof(uint32_t); + m_pending.serial = serial; + m_pending.size = QSize(width, height); + m_pending.isResizing = false; + m_pending.states = Qt::WindowNoState; + for (size_t i = 0; i < numStates; i++) { + switch (xdgStates[i]) { + case XDG_SURFACE_STATE_MAXIMIZED: + m_pending.states |= Qt::WindowMaximized; + break; + case XDG_SURFACE_STATE_FULLSCREEN: + m_pending.states |= Qt::WindowFullScreen; + break; + case XDG_SURFACE_STATE_RESIZING: + m_pending.isResizing = true; + break; + case XDG_SURFACE_STATE_ACTIVATED: + m_pending.states |= Qt::WindowActive; + break; + default: + break; + } + } + m_window->applyConfigureWhenPossible(); +} + +void QWaylandXdgSurfaceV5::xdg_surface_close() +{ +} + +} + +QT_END_NAMESPACE diff --git a/src/client/qwaylandxdgsurface_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h index b8dd93f47..928395779 100644 --- a/src/client/qwaylandxdgsurface_p.h +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QWAYLANDXDGSURFACE_H -#define QWAYLANDXDGSURFACE_H +#ifndef QWAYLANDXDGSURFACEV5_P_H +#define QWAYLANDXDGSURFACEV5_P_H // // W A R N I N G @@ -51,15 +51,16 @@ // We mean it. // +#include "qwayland-xdg-shell-unstable-v5.h" + +#include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtWaylandClient/private/qwaylandshellsurface_p.h> + #include <QtCore/QSize> #include <QtCore/QMargins> #include <wayland-client.h> -#include <QtWaylandClient/qtwaylandclientglobal.h> -#include <QtWaylandClient/private/qwayland-xdg-shell.h> -#include <QtWaylandClient/private/qwaylandshellsurface_p.h> - QT_BEGIN_NAMESPACE class QWindow; @@ -69,15 +70,15 @@ namespace QtWaylandClient { class QWaylandWindow; class QWaylandInputDevice; class QWaylandExtendedSurface; -class QWaylandXdgShell; +class QWaylandXdgShellV5; -class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgSurface : public QWaylandShellSurface +class Q_WAYLAND_CLIENT_EXPORT QWaylandXdgSurfaceV5 : public QWaylandShellSurface , public QtWayland::xdg_surface { Q_OBJECT public: - QWaylandXdgSurface(QWaylandXdgShell *shell, QWaylandWindow *window); - ~QWaylandXdgSurface() override; + QWaylandXdgSurfaceV5(QWaylandXdgShellV5 *shell, QWaylandWindow *window); + ~QWaylandXdgSurfaceV5() override; using QtWayland::xdg_surface::resize; void resize(QWaylandInputDevice *inputDevice, enum resize_edge edges); @@ -96,26 +97,23 @@ public: void setWindowFlags(Qt::WindowFlags flags) override; void sendProperty(const QString &name, const QVariant &value) override; - bool isFullscreen() const { return m_fullscreen; } - bool isMaximized() const { return m_maximized; } - void setType(Qt::WindowType type, QWaylandWindow *transientParent) override; + void applyConfigure() override; + void requestWindowStates(Qt::WindowStates states) override; + bool wantsDecorations() const override; private: - void setMaximized() override; - void setFullscreen() override; - void setNormal() override; - void setMinimized() override; - void updateTransientParent(QWaylandWindow *parent); private: QWaylandWindow *m_window = nullptr; - QWaylandXdgShell* m_shell = nullptr; - bool m_maximized = false; - bool m_minimized = false; - bool m_fullscreen = false; - bool m_active = false; + QWaylandXdgShellV5* m_shell = nullptr; + struct { + Qt::WindowStates states = Qt::WindowNoState; + bool isResizing = false; + QSize size = {0, 0}; + uint serial = 0; + } m_acked, m_pending; QSize m_normalSize; QMargins m_margins; QWaylandExtendedSurface *m_extendedWindow = nullptr; @@ -133,4 +131,4 @@ QT_END_NAMESPACE } -#endif // QWAYLANDXDGSURFACE_H +#endif // QWAYLANDXDGSURFACEV5_P_H diff --git a/src/plugins/shellintegration/xdg-shell-v5/xdg-shell-v5.json b/src/plugins/shellintegration/xdg-shell-v5/xdg-shell-v5.json new file mode 100644 index 000000000..ba1ed4489 --- /dev/null +++ b/src/plugins/shellintegration/xdg-shell-v5/xdg-shell-v5.json @@ -0,0 +1,3 @@ +{ + "Keys":[ "xdg-shell-v5" ] +} diff --git a/src/plugins/shellintegration/xdg-shell-v5/xdg-shell-v5.pro b/src/plugins/shellintegration/xdg-shell-v5/xdg-shell-v5.pro new file mode 100644 index 000000000..0e97832e4 --- /dev/null +++ b/src/plugins/shellintegration/xdg-shell-v5/xdg-shell-v5.pro @@ -0,0 +1,30 @@ +QT += gui-private waylandclient-private +CONFIG += wayland-scanner + +QMAKE_USE += wayland-client + +qtConfig(xkbcommon-evdev): \ + QMAKE_USE += xkbcommon_evdev + +WAYLANDCLIENTSOURCES += \ + ../../../3rdparty/protocol/xdg-shell-unstable-v5.xml + +HEADERS += \ + qwaylandxdgpopupv5_p.h \ + qwaylandxdgshellv5_p.h \ + qwaylandxdgshellv5integration_p.h \ + qwaylandxdgsurfacev5_p.h \ + +SOURCES += \ + main.cpp \ + qwaylandxdgpopupv5.cpp \ + qwaylandxdgshellv5.cpp \ + qwaylandxdgshellv5integration.cpp \ + qwaylandxdgsurfacev5.cpp \ + +OTHER_FILES += \ + xdg-shell-v5.json + +PLUGIN_TYPE = wayland-shell-integration +PLUGIN_CLASS_NAME = QWaylandXdgShellV5IntegrationPlugin +load(qt_plugin) diff --git a/tests/auto/client/client/tst_client.cpp b/tests/auto/client/client/tst_client.cpp index 978a9d5e6..21f748ecd 100644 --- a/tests/auto/client/client/tst_client.cpp +++ b/tests/auto/client/client/tst_client.cpp @@ -172,6 +172,7 @@ private slots: void windowScreens(); void removePrimaryScreen(); void createDestroyWindow(); + void activeWindowFollowsKeyboardFocus(); void events(); void backingStore(); void touchDrag(); @@ -307,7 +308,7 @@ void tst_WaylandClient::createDestroyWindow() QTRY_VERIFY(!compositor->surface()); } -void tst_WaylandClient::events() +void tst_WaylandClient::activeWindowFollowsKeyboardFocus() { TestWindow window; window.show(); @@ -318,6 +319,9 @@ void tst_WaylandClient::events() QTRY_VERIFY(window.isExposed()); + if (compositor->xdgToplevelV6()) + QSKIP("On xdg-shell v6 focus is handled by configure events"); + QCOMPARE(window.focusInEventCount, 0); compositor->setKeyboardFocus(surface); QTRY_COMPARE(window.focusInEventCount, 1); @@ -327,9 +331,21 @@ void tst_WaylandClient::events() compositor->setKeyboardFocus(QSharedPointer<MockSurface>(nullptr)); QTRY_COMPARE(window.focusOutEventCount, 1); QTRY_COMPARE(QGuiApplication::focusWindow(), static_cast<QWindow *>(nullptr)); +} + +void tst_WaylandClient::events() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockSurface> surface; + QTRY_VERIFY(surface = compositor->surface()); + compositor->sendShellSurfaceConfigure(surface); + + QTRY_VERIFY(window.isExposed()); compositor->setKeyboardFocus(surface); - QTRY_COMPARE(window.focusInEventCount, 2); + QTRY_COMPARE(window.focusInEventCount, 1); QTRY_COMPARE(QGuiApplication::focusWindow(), &window); uint keyCode = 80; // arbitrarily chosen diff --git a/tests/auto/client/shared/mockcompositor.cpp b/tests/auto/client/shared/mockcompositor.cpp index 9ef08ad95..11b40d9fc 100644 --- a/tests/auto/client/shared/mockcompositor.cpp +++ b/tests/auto/client/shared/mockcompositor.cpp @@ -236,11 +236,14 @@ void MockCompositor::sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface processCommand(command); } -void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size) +void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size, const QVector<uint> &states) { Command command = makeCommand(Impl::Compositor::sendXdgToplevelV6Configure, m_compositor); command.parameters << QVariant::fromValue(toplevel); command.parameters << QVariant::fromValue(size); + auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()), + states.size() * static_cast<int>(sizeof(uint))); + command.parameters << statesBytes; processCommand(command); } diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h index 34c20943a..d3568c165 100644 --- a/tests/auto/client/shared/mockcompositor.h +++ b/tests/auto/client/shared/mockcompositor.h @@ -175,12 +175,21 @@ private: Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>) -class MockXdgToplevelV6 +class MockXdgToplevelV6 : public QObject { + Q_OBJECT public: Impl::XdgToplevelV6 *handle() const { return m_toplevel; } void sendConfigure(const QSharedPointer<MockXdgToplevelV6> toplevel); + +signals: + uint setMinimizedRequested(); + uint setMaximizedRequested(); + uint unsetMaximizedRequested(); + uint setFullscreenRequested(); + uint unsetFullscreenRequested(); + private: MockXdgToplevelV6(Impl::XdgToplevelV6 *toplevel) : m_toplevel(toplevel) {} friend class Impl::Compositor; @@ -234,7 +243,8 @@ public: void sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output); void sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size = QSize(0, 0)); void sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size); - void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0)); + void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0), + const QVector<uint> &states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }); void waitForStartDrag(); QSharedPointer<MockSurface> surface(); diff --git a/tests/auto/client/shared/mocksurface.cpp b/tests/auto/client/shared/mocksurface.cpp index 81a865f11..82ce37acb 100644 --- a/tests/auto/client/shared/mocksurface.cpp +++ b/tests/auto/client/shared/mocksurface.cpp @@ -70,7 +70,10 @@ void Compositor::sendShellSurfaceConfigure(void *data, const QList<QVariant> &pa QSize size = parameters.at(1).toSize(); Q_ASSERT(size.isValid()); if (auto toplevel = surface->xdgToplevelV6()) { - toplevel->send_configure(size.width(), size.height(), QByteArray()); + QVector<uint> states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }; + auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()), + states.size() * static_cast<int>(sizeof(uint))); + toplevel->send_configure(size.width(), size.height(), statesBytes); toplevel->xdgSurface()->send_configure(compositor->nextSerial()); } else if (auto wlShellSurface = surface->wlShellSurface()) { const uint edges = 0; diff --git a/tests/auto/client/shared/mockxdgshellv6.cpp b/tests/auto/client/shared/mockxdgshellv6.cpp index 39e03296d..6f6f0b905 100644 --- a/tests/auto/client/shared/mockxdgshellv6.cpp +++ b/tests/auto/client/shared/mockxdgshellv6.cpp @@ -39,8 +39,8 @@ void Compositor::sendXdgToplevelV6Configure(void *data, const QList<QVariant> &p Q_ASSERT(toplevel && toplevel->resource()); QSize size = parameters.at(1).toSize(); Q_ASSERT(size.isValid()); - QByteArray states; - toplevel->send_configure(size.width(), size.height(), states); + auto statesBytes = parameters.at(2).toByteArray(); + toplevel->send_configure(size.width(), size.height(), statesBytes); toplevel->xdgSurface()->send_configure(compositor->nextSerial()); } @@ -87,6 +87,37 @@ void XdgToplevelV6::zxdg_toplevel_v6_destroy(QtWaylandServer::zxdg_toplevel_v6:: wl_resource_destroy(resource->handle); } +void XdgToplevelV6::zxdg_toplevel_v6_set_minimized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + emit m_mockToplevel->setMinimizedRequested(); +} + +void XdgToplevelV6::zxdg_toplevel_v6_set_maximized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + emit m_mockToplevel->setMaximizedRequested(); +} + +void XdgToplevelV6::zxdg_toplevel_v6_unset_maximized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + emit m_mockToplevel->unsetMaximizedRequested(); +} + +void XdgToplevelV6::zxdg_toplevel_v6_set_fullscreen(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, wl_resource *output) +{ + Q_UNUSED(resource); + Q_UNUSED(output); + emit m_mockToplevel->setFullscreenRequested(); +} + +void XdgToplevelV6::zxdg_toplevel_v6_unset_fullscreen(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + emit m_mockToplevel->unsetFullscreenRequested(); +} + void Impl::XdgShellV6::zxdg_shell_v6_get_xdg_surface(QtWaylandServer::zxdg_shell_v6::Resource *resource, uint32_t id, wl_resource *surface) { new XdgSurfaceV6(this, Surface::fromResource(surface), resource->client(), id); diff --git a/tests/auto/client/shared/mockxdgshellv6.h b/tests/auto/client/shared/mockxdgshellv6.h index 92b808ba8..faadb785a 100644 --- a/tests/auto/client/shared/mockxdgshellv6.h +++ b/tests/auto/client/shared/mockxdgshellv6.h @@ -74,6 +74,11 @@ public: protected: void zxdg_toplevel_v6_destroy_resource(Resource *) override { delete this; } void zxdg_toplevel_v6_destroy(Resource *resource) override; + void zxdg_toplevel_v6_set_minimized(Resource *resource) override; + void zxdg_toplevel_v6_set_maximized(Resource *resource) override; + void zxdg_toplevel_v6_unset_maximized(Resource *resource) override; + void zxdg_toplevel_v6_set_fullscreen(Resource *resource, struct ::wl_resource *output) override; + void zxdg_toplevel_v6_unset_fullscreen(Resource *resource) override; private: XdgSurfaceV6 *m_xdgSurface = nullptr; diff --git a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp index 364cd1099..0f72f58a9 100644 --- a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp +++ b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp @@ -42,6 +42,7 @@ static const QSize screenSize(1600, 1200); class TestWindow : public QWindow { + Q_OBJECT public: TestWindow() { @@ -49,6 +50,16 @@ public: setGeometry(0, 0, 32, 32); create(); } + + bool event(QEvent *event) override + { + if (event->type() == QEvent::WindowStateChange) + emit windowStateChangeEventReceived(static_cast<QWindowStateChangeEvent *>(event)->oldState()); + return QWindow::event(event); + } + +signals: + void windowStateChangeEventReceived(uint oldState); }; class tst_WaylandClientXdgShellV6 : public QObject @@ -58,6 +69,7 @@ public: tst_WaylandClientXdgShellV6(MockCompositor *c) : m_compositor(c) { + qRegisterMetaType<Qt::WindowState>(); QSocketNotifier *notifier = new QSocketNotifier(m_compositor->waylandFileDescriptor(), QSocketNotifier::Read, this); connect(notifier, &QSocketNotifier::activated, this, &tst_WaylandClientXdgShellV6::processWaylandEvents); // connect to the event dispatcher to make sure to flush out the outgoing message queue @@ -82,6 +94,11 @@ public slots: private slots: void createDestroyWindow(); void configure(); + void showMinimized(); + void setMinimized(); + void unsetMaximized(); + void focusWindowFollowsConfigure(); + void windowStateChangedEvents(); private: MockCompositor *m_compositor = nullptr; @@ -118,10 +135,182 @@ void tst_WaylandClientXdgShellV6::configure() QSharedPointer<MockXdgToplevelV6> toplevel; QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + const QSize newSize(123, 456); m_compositor->sendXdgToplevelV6Configure(toplevel, newSize); QTRY_VERIFY(window.isExposed()); + QTRY_COMPARE(window.visibility(), QWindow::Windowed); + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), newSize)); + + m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED }); + QTRY_COMPARE(window.visibility(), QWindow::Maximized); + QTRY_COMPARE(window.windowStates(), Qt::WindowMaximized); + QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), screenSize)); + + m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN }); + QTRY_COMPARE(window.visibility(), QWindow::FullScreen); + QTRY_COMPARE(window.windowStates(), Qt::WindowFullScreen); + QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), screenSize)); + + //The window should remember it's original size + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }); + QTRY_COMPARE(window.visibility(), QWindow::Windowed); + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); + QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), newSize)); +} + +void tst_WaylandClientXdgShellV6::showMinimized() +{ + // On xdg-shell v6 there's really no way for the compositor to tell the window if it's minimized + // There are wl_surface.enter events and so on, but there's really no way to differentiate + // between a window preview and an unminimized window. + TestWindow window; + window.showMinimized(); + QCOMPARE(window.windowStates(), Qt::WindowMinimized); // should return minimized until + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); // rejected by handleWindowStateChanged +} + +void tst_WaylandClientXdgShellV6::setMinimized() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockXdgToplevelV6> toplevel; + QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + + m_compositor->sendXdgToplevelV6Configure(toplevel); + QTRY_COMPARE(window.visibility(), QWindow::Windowed); + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); + + QSignalSpy setMinimizedSpy(toplevel.data(), SIGNAL(setMinimizedRequested())); + QSignalSpy windowStateChangeSpy(&window, SIGNAL(windowStateChangeEventReceived(uint))); + + window.setVisibility(QWindow::Minimized); + QCOMPARE(window.visibility(), QWindow::Minimized); + QCOMPARE(window.windowStates(), Qt::WindowMinimized); + QTRY_COMPARE(setMinimizedSpy.count(), 1); + { + QTRY_VERIFY(windowStateChangeSpy.count() > 0); + Qt::WindowStates oldStates(windowStateChangeSpy.takeFirst().at(0).toUInt()); + QCOMPARE(oldStates, Qt::WindowNoState); + } + + // In the meantime the compositor may minimize, do nothing or reshow the window without + // telling us. + + QTRY_COMPARE(window.visibility(), QWindow::Windowed); // verify that we don't know anything + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); + { + QTRY_COMPARE(windowStateChangeSpy.count(), 1); + Qt::WindowStates oldStates(windowStateChangeSpy.takeFirst().at(0).toUInt()); + QCOMPARE(oldStates, Qt::WindowNoState); // because the window never was minimized + } + + // Setting visibility again should send another set_minimized request + window.setVisibility(QWindow::Minimized); + QTRY_COMPARE(setMinimizedSpy.count(), 2); +} + +void tst_WaylandClientXdgShellV6::unsetMaximized() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockXdgToplevelV6> toplevel; + QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + + QSignalSpy unsetMaximizedSpy(toplevel.data(), SIGNAL(unsetMaximizedRequested())); + + QSignalSpy windowStateChangedSpy(&window, SIGNAL(windowStateChanged(Qt::WindowState))); + + m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED }); + + QTRY_COMPARE(windowStateChangedSpy.count(), 1); + QCOMPARE(windowStateChangedSpy.takeFirst().at(0).toUInt(), Qt::WindowMaximized); + + window.setWindowStates(Qt::WindowNoState); + + QTRY_COMPARE(unsetMaximizedSpy.count(), 1); + QTRY_COMPARE(windowStateChangedSpy.count(), 1); + QCOMPARE(windowStateChangedSpy.takeFirst().at(0).toUInt(), Qt::WindowNoState); + + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), {}); + + QTRY_COMPARE(windowStateChangedSpy.count(), 1); + QCOMPARE(windowStateChangedSpy.takeFirst().at(0).toUInt(), Qt::WindowNoState); +} + +void tst_WaylandClientXdgShellV6::focusWindowFollowsConfigure() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockXdgToplevelV6> toplevel; + QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + QTRY_VERIFY(!window.isActive()); + + QSignalSpy windowStateChangeSpy(&window, SIGNAL(windowStateChangeEventReceived(uint))); + + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }); + QTRY_VERIFY(window.isActive()); + + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), {}); + QTRY_VERIFY(!window.isActive()); +} + +void tst_WaylandClientXdgShellV6::windowStateChangedEvents() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockXdgToplevelV6> toplevel; + QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + + QSignalSpy eventSpy(&window, SIGNAL(windowStateChangeEventReceived(uint))); + QSignalSpy signalSpy(&window, SIGNAL(windowStateChanged(Qt::WindowState))); + + m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED }); + + QTRY_COMPARE(window.windowStates(), Qt::WindowMaximized); + QTRY_COMPARE(window.windowState(), Qt::WindowMaximized); + { + QTRY_COMPARE(eventSpy.count(), 1); + Qt::WindowStates oldStates(eventSpy.takeFirst().at(0).toUInt()); + QCOMPARE(oldStates, Qt::WindowNoState); + + QTRY_COMPARE(signalSpy.count(), 1); + uint newState = signalSpy.takeFirst().at(0).toUInt(); + QCOMPARE(newState, Qt::WindowMaximized); + } + + m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN }); + + QTRY_COMPARE(window.windowStates(), Qt::WindowFullScreen); + QTRY_COMPARE(window.windowState(), Qt::WindowFullScreen); + { + QTRY_COMPARE(eventSpy.count(), 1); + Qt::WindowStates oldStates(eventSpy.takeFirst().at(0).toUInt()); + QCOMPARE(oldStates, Qt::WindowMaximized); + + QTRY_COMPARE(signalSpy.count(), 1); + uint newState = signalSpy.takeFirst().at(0).toUInt(); + QCOMPARE(newState, Qt::WindowFullScreen); + } + + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), {}); + + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); + QTRY_COMPARE(window.windowState(), Qt::WindowNoState); + { + QTRY_COMPARE(eventSpy.count(), 1); + Qt::WindowStates oldStates(eventSpy.takeFirst().at(0).toUInt()); + QCOMPARE(oldStates, Qt::WindowFullScreen); + + QTRY_COMPARE(signalSpy.count(), 1); + uint newState = signalSpy.takeFirst().at(0).toUInt(); + QCOMPARE(newState, Qt::WindowNoState); + } } int main(int argc, char **argv) diff --git a/tests/auto/compositor/compositor/compositor.pro b/tests/auto/compositor/compositor/compositor.pro index 2919fa4bb..d69db4ca5 100644 --- a/tests/auto/compositor/compositor/compositor.pro +++ b/tests/auto/compositor/compositor/compositor.pro @@ -11,7 +11,7 @@ qtConfig(xkbcommon-evdev): \ QMAKE_USE += xkbcommon_evdev WAYLANDCLIENTSOURCES += \ - ../../../../src/3rdparty/protocol/xdg-shell.xml \ + ../../../../src/3rdparty/protocol/xdg-shell-unstable-v5.xml \ ../../../../src/3rdparty/protocol/ivi-application.xml \ SOURCES += \ @@ -21,6 +21,7 @@ SOURCES += \ mockclient.cpp \ mockseat.cpp \ testseat.cpp \ + mockkeyboard.cpp \ mockpointer.cpp HEADERS += \ @@ -29,4 +30,5 @@ HEADERS += \ mockclient.h \ mockseat.h \ testseat.h \ + mockkeyboard.h \ mockpointer.h diff --git a/tests/auto/compositor/compositor/mockclient.h b/tests/auto/compositor/compositor/mockclient.h index 6d5bf31df..6bfb652ed 100644 --- a/tests/auto/compositor/compositor/mockclient.h +++ b/tests/auto/compositor/compositor/mockclient.h @@ -27,7 +27,7 @@ ****************************************************************************/ #include <wayland-client.h> -#include <wayland-xdg-shell-client-protocol.h> +#include <qwayland-xdg-shell-unstable-v5.h> #include <wayland-ivi-application-client-protocol.h> #include <QObject> diff --git a/tests/auto/compositor/compositor/mockkeyboard.cpp b/tests/auto/compositor/compositor/mockkeyboard.cpp new file mode 100644 index 000000000..e5f5f8d36 --- /dev/null +++ b/tests/auto/compositor/compositor/mockkeyboard.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "mockkeyboard.h" + +void keyboardKeymap(void *keyboard, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) +{ + Q_UNUSED(keyboard); + Q_UNUSED(wl_keyboard); + Q_UNUSED(format); + Q_UNUSED(fd); + Q_UNUSED(size); +} + +void keyboardEnter(void *keyboard, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) +{ + Q_UNUSED(wl_keyboard); + Q_UNUSED(serial); + Q_UNUSED(surface); + Q_UNUSED(keys); + + static_cast<MockKeyboard *>(keyboard)->m_enteredSurface = surface; +} + +void keyboardLeave(void *keyboard, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) +{ + Q_UNUSED(wl_keyboard); + Q_UNUSED(serial); + Q_UNUSED(surface); + + static_cast<MockKeyboard *>(keyboard)->m_enteredSurface = nullptr; +} + +void keyboardKey(void *keyboard, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) +{ + Q_UNUSED(keyboard); + Q_UNUSED(wl_keyboard); + Q_UNUSED(serial); + Q_UNUSED(time); + Q_UNUSED(key); + Q_UNUSED(state); + auto kb = static_cast<MockKeyboard *>(keyboard); + kb->m_lastKeyCode = key; + kb->m_lastKeyState = state; +} + +void keyboardModifiers(void *keyboard, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) +{ + Q_UNUSED(keyboard); + Q_UNUSED(wl_keyboard); + Q_UNUSED(serial); + Q_UNUSED(mods_depressed); + Q_UNUSED(mods_latched); + Q_UNUSED(mods_locked); + auto kb = static_cast<MockKeyboard *>(keyboard); + kb->m_group = group; +} + +static const struct wl_keyboard_listener keyboardListener = { + keyboardKeymap, + keyboardEnter, + keyboardLeave, + keyboardKey, + keyboardModifiers +}; + +MockKeyboard::MockKeyboard(wl_seat *seat) + : m_keyboard(wl_seat_get_keyboard(seat)) +{ + wl_keyboard_add_listener(m_keyboard, &keyboardListener, this); +} + +MockKeyboard::~MockKeyboard() +{ + wl_keyboard_destroy(m_keyboard); +} diff --git a/tests/auto/compositor/compositor/mockkeyboard.h b/tests/auto/compositor/compositor/mockkeyboard.h new file mode 100644 index 000000000..1090db597 --- /dev/null +++ b/tests/auto/compositor/compositor/mockkeyboard.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 MOCKKEYBOARD_H +#define MOCKKEYBOARD_H + +#include <QObject> +#include <wayland-client.h> + +class MockKeyboard : public QObject +{ + Q_OBJECT + +public: + explicit MockKeyboard(wl_seat *seat); + ~MockKeyboard() override; + + wl_keyboard *m_keyboard = nullptr; + wl_surface *m_enteredSurface = nullptr; + uint m_lastKeyCode = 0; + uint m_lastKeyState = 0; + uint m_group = 0; +}; + +#endif // MOCKKEYBOARD_H diff --git a/tests/auto/compositor/compositor/mockseat.cpp b/tests/auto/compositor/compositor/mockseat.cpp index 052c2f90b..ce873c129 100644 --- a/tests/auto/compositor/compositor/mockseat.cpp +++ b/tests/auto/compositor/compositor/mockseat.cpp @@ -31,14 +31,11 @@ MockSeat::MockSeat(wl_seat *seat) : m_seat(seat) , m_pointer(new MockPointer(seat)) + , m_keyboard(new MockKeyboard(seat)) { - // Bind to the keyboard interface so that the compositor has - // the right resource associations - m_keyboard = wl_seat_get_keyboard(seat); } MockSeat::~MockSeat() { - wl_keyboard_destroy(m_keyboard); wl_seat_destroy(m_seat); } diff --git a/tests/auto/compositor/compositor/mockseat.h b/tests/auto/compositor/compositor/mockseat.h index 73d289281..f8c103ed4 100644 --- a/tests/auto/compositor/compositor/mockseat.h +++ b/tests/auto/compositor/compositor/mockseat.h @@ -29,6 +29,7 @@ #define MOCKSEAT #include "mockpointer.h" +#include "mockkeyboard.h" #include <QObject> #include <wayland-client.h> @@ -41,12 +42,13 @@ public: MockSeat(wl_seat *seat); ~MockSeat() override; MockPointer *pointer() const { return m_pointer.data(); } + MockKeyboard *keyboard() const { return m_keyboard.data(); } wl_seat *m_seat = nullptr; - wl_keyboard *m_keyboard = nullptr; private: QScopedPointer<MockPointer> m_pointer; + QScopedPointer<MockKeyboard> m_keyboard; }; #endif diff --git a/tests/auto/compositor/compositor/tst_compositor.cpp b/tests/auto/compositor/compositor/tst_compositor.cpp index ae5ea927d..e3c4daccc 100644 --- a/tests/auto/compositor/compositor/tst_compositor.cpp +++ b/tests/auto/compositor/compositor/tst_compositor.cpp @@ -40,11 +40,13 @@ #include <QtGui/QScreen> #include <QtWaylandCompositor/QWaylandXdgShellV5> #include <QtWaylandCompositor/private/qwaylandxdgshellv6_p.h> +#include <QtWaylandCompositor/private/qwaylandkeyboard_p.h> #include <QtWaylandCompositor/QWaylandIviApplication> #include <QtWaylandCompositor/QWaylandIviSurface> #include <QtWaylandCompositor/QWaylandSurface> #include <QtWaylandCompositor/QWaylandResource> -#include <qwayland-xdg-shell.h> +#include <QtWaylandCompositor/QWaylandKeymap> +#include <qwayland-xdg-shell-unstable-v5.h> #include <qwayland-ivi-application.h> #include <QtTest/QtTest> @@ -56,6 +58,11 @@ class tst_WaylandCompositor : public QObject private slots: void init(); void seatCapabilities(); +#if QT_CONFIG(xkbcommon_evdev) + void simpleKeyboard(); + void keyboardKeymaps(); + void keyboardLayoutSwitching(); +#endif void keyboardGrab(); void seatCreation(); void seatKeyboardFocus(); @@ -160,6 +167,121 @@ void tst_WaylandCompositor::multipleClients() QTRY_COMPARE(compositor.surfaces.size(), 0); } +#if QT_CONFIG(xkbcommon_evdev) + +void tst_WaylandCompositor::simpleKeyboard() +{ + TestCompositor compositor; + compositor.create(); + + QWaylandSeat* seat = compositor.defaultSeat(); + seat->keymap()->setLayout("us"); + + MockClient client; + + QTRY_COMPARE(client.m_seats.size(), 1); + MockKeyboard *mockKeyboard = client.m_seats.at(0)->keyboard(); + + wl_surface *mockSurface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + seat->setKeyboardFocus(compositor.surfaces.at(0)); + + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_enteredSurface, mockSurface); + + seat->sendKeyEvent(Qt::Key_A, true); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyState, 1u); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 30u); // 30 is the scan code for A on us keyboard layouts + + seat->sendKeyEvent(Qt::Key_A, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyState, 0u); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 30u); + + seat->sendKeyEvent(Qt::Key_Super_L, true); + seat->sendKeyEvent(Qt::Key_Super_L, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 125u); +} + +void tst_WaylandCompositor::keyboardKeymaps() +{ + TestCompositor compositor; + compositor.create(); + QWaylandSeat* seat = compositor.defaultSeat(); + MockClient client; + QTRY_COMPARE(client.m_seats.size(), 1); + MockKeyboard *mockKeyboard = client.m_seats.at(0)->keyboard(); + client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + seat->setKeyboardFocus(compositor.surfaces.at(0)); + + seat->keymap()->setLayout("us"); + + seat->sendKeyEvent(Qt::Key_Y, true); + seat->sendKeyEvent(Qt::Key_Y, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 21u); + + seat->sendKeyEvent(Qt::Key_Z, true); + seat->sendKeyEvent(Qt::Key_Z, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 44u); + + seat->keymap()->setLayout("de"); // In the German layout y and z have changed places + + seat->sendKeyEvent(Qt::Key_Y, true); + seat->sendKeyEvent(Qt::Key_Y, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 44u); + + seat->sendKeyEvent(Qt::Key_Z, true); + seat->sendKeyEvent(Qt::Key_Z, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 21u); +} + +void tst_WaylandCompositor::keyboardLayoutSwitching() +{ + TestCompositor compositor; + compositor.create(); + QWaylandSeat* seat = compositor.defaultSeat(); + MockClient client; + QTRY_COMPARE(client.m_seats.size(), 1); + MockKeyboard *mockKeyboard = client.m_seats.at(0)->keyboard(); + client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + seat->setKeyboardFocus(compositor.surfaces.at(0)); + + seat->keymap()->setLayout("us,de"); + seat->keymap()->setOptions("grp:lalt_toggle"); //toggle keyboard layout with left alt + + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_group, 0u); + + seat->sendKeyEvent(Qt::Key_Y, true); + seat->sendKeyEvent(Qt::Key_Y, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 21u); + + // It's not currently possible to switch layouts programmatically with the public APIs + // We will just fake it with the private APIs here. + auto keyboardPrivate = QWaylandKeyboardPrivate::get(seat->keyboard()); + const uint leftAltCode = 64; + keyboardPrivate->updateModifierState(leftAltCode, WL_KEYBOARD_KEY_STATE_PRESSED); + keyboardPrivate->updateModifierState(leftAltCode, WL_KEYBOARD_KEY_STATE_RELEASED); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_group, 1u); + + seat->sendKeyEvent(Qt::Key_Y, true); + seat->sendKeyEvent(Qt::Key_Y, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 44u); +} + +#endif // QT_CONFIG(xkbcommon_evdev) + void tst_WaylandCompositor::keyboardGrab() { TestCompositor compositor; @@ -450,13 +572,24 @@ void tst_WaylandCompositor::seatKeyboardFocus() // Create client after all the input devices have been set up as the mock client // does not dynamically listen to new seats MockClient client; + + QTRY_COMPARE(client.m_seats.size(), 1); + MockKeyboard *mockKeyboard = client.m_seats.first()->keyboard(); + QVERIFY(mockKeyboard); + QCOMPARE(mockKeyboard->m_enteredSurface, nullptr); + wl_surface *surface = client.createSurface(); QTRY_COMPARE(compositor.surfaces.size(), 1); QWaylandSurface *waylandSurface = compositor.surfaces.at(0); - QWaylandSeat* dev = compositor.defaultSeat(); - dev->setKeyboardFocus(waylandSurface); - QTRY_COMPARE(compositor.defaultSeat()->keyboardFocus(), waylandSurface); + QWaylandSeat* seat = compositor.defaultSeat(); + QVERIFY(seat->setKeyboardFocus(waylandSurface)); + QCOMPARE(compositor.defaultSeat()->keyboardFocus(), waylandSurface); + + compositor.flushClients(); + + qDebug() << mockKeyboard->m_enteredSurface; + QTRY_COMPARE(mockKeyboard->m_enteredSurface, surface); wl_surface_destroy(surface); QTRY_VERIFY(compositor.surfaces.size() == 0); |