diff options
Diffstat (limited to 'tests/auto/client/xdgshell/tst_xdgshell.cpp')
-rw-r--r-- | tests/auto/client/xdgshell/tst_xdgshell.cpp | 455 |
1 files changed, 371 insertions, 84 deletions
diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp index ac5c24988..1dc57e280 100644 --- a/tests/auto/client/xdgshell/tst_xdgshell.cpp +++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp @@ -1,36 +1,12 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// Copyright (C) 2023 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "mockcompositor.h" #include <QtGui/QRasterWindow> -#include <QtGui/QOpenGLWindow> #include <QtGui/qpa/qplatformnativeinterface.h> #include <QtWaylandClient/private/wayland-wayland-client-protocol.h> +#include <QtWaylandClient/private/qwaylandwindow_p.h> using namespace MockCompositor; @@ -38,23 +14,36 @@ class tst_xdgshell : public QObject, private DefaultCompositor { Q_OBJECT private slots: + void init(); void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); } void showMinimized(); void basicConfigure(); void configureSize(); void configureStates(); + void configureBounds(); void popup(); void tooltipOnPopup(); + void tooltipAndSiblingPopup(); void switchPopups(); + void hidePopupParent(); void pongs(); + void minMaxSize_data(); void minMaxSize(); void windowGeometry(); void foreignSurface(); + void nativeResources(); + void suspended(); + void initiallySuspended(); + void modality(); }; +void tst_xdgshell::init() +{ + setenv("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1", 1); +} + void tst_xdgshell::showMinimized() { - QSKIP("TODO: This currently fails, needs a fix"); // On xdg-shell 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. @@ -76,13 +65,13 @@ void tst_xdgshell::basicConfigure() window.show(); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); - QSignalSpy configureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted); + QSignalSpy configureSpy(exec([&] { return xdgSurface(); }), &XdgSurface::configureCommitted); QTRY_VERIFY(window.isVisible()); // The window should not be exposed before the first xdg_surface configure event QTRY_VERIFY(!window.isExposed()); - exec([=] { + exec([&] { xdgToplevel()->sendConfigure({0, 0}, {}); // Let the window decide the size }); @@ -90,9 +79,9 @@ void tst_xdgshell::basicConfigure() QTRY_VERIFY(!window.isExposed()); //Window should not be exposed before the first configure event QVERIFY(configureSpy.isEmpty()); - const uint serial = exec([=] { return nextSerial(); }); + const uint serial = exec([&] { return nextSerial(); }); - exec([=] { + exec([&] { xdgSurface()->sendConfigure(serial); }); @@ -100,7 +89,7 @@ void tst_xdgshell::basicConfigure() QTRY_VERIFY(window.isExposed()); // The client is now going to ack the configure - QTRY_COMPARE(configureSpy.count(), 1); + QTRY_COMPARE(configureSpy.size(), 1); QCOMPARE(configureSpy.takeFirst().at(0).toUInt(), serial); // And attach a buffer @@ -118,17 +107,17 @@ void tst_xdgshell::configureSize() window.show(); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); - QSignalSpy configureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted); + QSignalSpy configureSpy(exec([&] { return xdgSurface(); }), &XdgSurface::configureCommitted); const QSize configureSize(60, 40); - exec([=] { + exec([&] { xdgToplevel()->sendCompleteConfigure(configureSize); }); - QTRY_COMPARE(configureSpy.count(), 1); + QTRY_COMPARE(configureSpy.size(), 1); - exec([=] { + exec([&] { Buffer *buffer = xdgToplevel()->surface()->m_committed.buffer; QVERIFY(buffer); QCOMPARE(buffer->size(), configureSize); @@ -137,13 +126,14 @@ void tst_xdgshell::configureSize() void tst_xdgshell::configureStates() { + QVERIFY(qputenv("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT", "0")); QRasterWindow window; window.resize(64, 48); window.show(); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); const QSize windowedSize(320, 240); - const uint windowedSerial = exec([=] { + const uint windowedSerial = exec([&] { return xdgToplevel()->sendCompleteConfigure(windowedSize, { XdgToplevel::state_activated }); }); QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, windowedSerial); @@ -153,12 +143,15 @@ void tst_xdgshell::configureStates() // Toplevel windows don't know their position on xdg-shell // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled -// QEXPECT_FAIL("", "configure has already been acked, we shouldn't have to wait for isActive", Continue); -// QVERIFY(window.isActive()); - QTRY_VERIFY(window.isActive()); // Just make sure it eventually get's set correctly + // window.windowstate() is driven by keyboard focus, however for decorations we want to follow + // XDGShell this is internal to QtWayland so it is queried directly + auto waylandWindow = static_cast<QtWaylandClient::QWaylandWindow *>(window.handle()); + Q_ASSERT(waylandWindow); + QTRY_VERIFY(waylandWindow->windowStates().testFlag( + Qt::WindowActive)); // Just make sure it eventually get's set correctly const QSize screenSize(640, 480); - const uint maximizedSerial = exec([=] { + const uint maximizedSerial = exec([&] { return xdgToplevel()->sendCompleteConfigure(screenSize, { XdgToplevel::state_activated, XdgToplevel::state_maximized }); }); QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, maximizedSerial); @@ -167,7 +160,7 @@ void tst_xdgshell::configureStates() QCOMPARE(window.frameGeometry().size(), screenSize); // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled - const uint fullscreenSerial = exec([=] { + const uint fullscreenSerial = exec([&] { return xdgToplevel()->sendCompleteConfigure(screenSize, { XdgToplevel::state_activated, XdgToplevel::state_fullscreen }); }); QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, fullscreenSerial); @@ -177,7 +170,7 @@ void tst_xdgshell::configureStates() // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled // The window should remember its original size - const uint restoreSerial = exec([=] { + const uint restoreSerial = exec([&] { return xdgToplevel()->sendCompleteConfigure({0, 0}, { XdgToplevel::state_activated }); }); QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, restoreSerial); @@ -185,6 +178,31 @@ void tst_xdgshell::configureStates() QCOMPARE(window.windowStates(), Qt::WindowNoState); QCOMPARE(window.frameGeometry().size(), windowedSize); // QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled + QVERIFY(qunsetenv("QT_WAYLAND_FRAME_CALLBACK_TIMEOUT")); +} + +void tst_xdgshell::configureBounds() +{ + QRasterWindow window; + window.resize(1280, 1024); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + + // Take xdg_toplevel.configure_bounds into account only if the configure event has 0x0 size. + const uint serial1 = exec([&] { + xdgToplevel()->sendConfigureBounds(QSize(800, 600)); + return xdgToplevel()->sendCompleteConfigure(QSize(0, 0), { XdgToplevel::state_activated }); + }); + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, serial1); + QCOMPARE(window.frameGeometry().size(), QSize(800, 600)); + + // Window size in xdg_toplevel configure events takes precedence over the configure bounds. + const uint serial2 = exec([&] { + xdgToplevel()->sendConfigureBounds(QSize(800, 600)); + return xdgToplevel()->sendCompleteConfigure(QSize(1600, 900), { XdgToplevel::state_activated }); + }); + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, serial2); + QCOMPARE(window.frameGeometry().size(), QSize(1600, 900)); } void tst_xdgshell::popup() @@ -207,11 +225,11 @@ void tst_xdgshell::popup() window.show(); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); - QSignalSpy toplevelConfigureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted); - exec([=] { xdgToplevel()->sendCompleteConfigure(); }); - QTRY_COMPARE(toplevelConfigureSpy.count(), 1); + QSignalSpy toplevelConfigureSpy(exec([&] { return xdgSurface(); }), &XdgSurface::configureCommitted); + exec([&] { xdgToplevel()->sendCompleteConfigure(); }); + QTRY_COMPARE(toplevelConfigureSpy.size(), 1); - uint clickSerial = exec([=] { + uint clickSerial = exec([&] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); auto *c = client(); @@ -219,27 +237,27 @@ void tst_xdgshell::popup() p->sendFrame(c); uint serial = p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); p->sendButton(c, BTN_LEFT, Pointer::button_state_released); - return serial; p->sendFrame(c); + return serial; }); QTRY_VERIFY(window.m_popup); QCOMPOSITOR_TRY_VERIFY(xdgPopup()); - QSignalSpy popupConfigureSpy(exec([=] { return xdgPopup()->m_xdgSurface; }), &XdgSurface::configureCommitted); + QSignalSpy popupConfigureSpy(exec([&] { return xdgPopup()->m_xdgSurface; }), &XdgSurface::configureCommitted); QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed); QCOMPOSITOR_TRY_COMPARE(xdgPopup()->m_grabSerial, clickSerial); QRasterWindow *popup = window.m_popup.get(); QVERIFY(!popup->isExposed()); // wait for configure - //TODO: Verify it works with a different configure window geometry - exec([=] { xdgPopup()->sendConfigure(QRect(100, 100, 100, 100)); }); + QRect rect1 = QRect(100, 100, 100, 100); + exec([&] { xdgPopup()->sendConfigure(rect1); }); // Nothing should happen before the *xdg_surface* configure QTRY_VERIFY(!popup->isExposed()); // Popup shouldn't be exposed before the first configure event QVERIFY(popupConfigureSpy.isEmpty()); - const uint configureSerial = exec([=] { + const uint configureSerial = exec([&] { return xdgPopup()->m_xdgSurface->sendConfigure(); }); @@ -247,8 +265,20 @@ void tst_xdgshell::popup() QTRY_VERIFY(popup->isExposed()); // The client is now going to ack the configure - QTRY_COMPARE(popupConfigureSpy.count(), 1); + QTRY_COMPARE(popupConfigureSpy.size(), 1); QCOMPARE(popupConfigureSpy.takeFirst().at(0).toUInt(), configureSerial); + QCOMPARE(popup->geometry(), rect1); + + QRect rect2 = QRect(50, 50, 150, 150); + exec([&] { xdgPopup()->sendConfigure(rect2); }); + + const uint configureSerial2 = exec([&] { + return xdgPopup()->m_xdgSurface->sendConfigure(); + }); + + QTRY_COMPARE(popupConfigureSpy.size(), 1); + QCOMPARE(popupConfigureSpy.takeFirst().at(0).toUInt(), configureSerial2); + QCOMPARE(popup->geometry(), rect2); // And attach a buffer exec([&] { @@ -293,10 +323,10 @@ void tst_xdgshell::tooltipOnPopup() window.show(); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); - exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + exec([&] { xdgToplevel()->sendCompleteConfigure(); }); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial); - exec([=] { + exec([&] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); auto *c = client(); @@ -310,11 +340,11 @@ void tst_xdgshell::tooltipOnPopup() }); QCOMPOSITOR_TRY_VERIFY(xdgPopup()); - exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + exec([&] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial); QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed); - exec([=] { + exec([&] { auto *surface = xdgPopup()->surface(); auto *p = pointer(); auto *c = client(); @@ -326,7 +356,7 @@ void tst_xdgshell::tooltipOnPopup() }); QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)); - exec([=] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + exec([&] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_xdgSurface->m_committedConfigureSerial); QCOMPOSITOR_TRY_VERIFY(!xdgPopup(1)->m_grabbed); @@ -339,6 +369,92 @@ void tst_xdgshell::tooltipOnPopup() QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); } +void tst_xdgshell::tooltipAndSiblingPopup() +{ + class ToolTip : public QRasterWindow { + public: + explicit ToolTip(QWindow *parent) { + setTransientParent(parent); + setFlags(Qt::ToolTip); + resize(100, 100); + show(); + } + void mousePressEvent(QMouseEvent *event) override { + QRasterWindow::mousePressEvent(event); + m_popup = new QRasterWindow; + m_popup->setTransientParent(transientParent()); + m_popup->setFlags(Qt::Popup); + m_popup->resize(100, 100); + m_popup->show(); + } + + QRasterWindow *m_popup = nullptr; + }; + + class Window : public QRasterWindow { + public: + void mousePressEvent(QMouseEvent *event) override { + QRasterWindow::mousePressEvent(event); + m_tooltip = new ToolTip(this); + } + ToolTip *m_tooltip = nullptr; + }; + + Window window; + window.resize(200, 200); + window.show(); + + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + exec([&] { xdgToplevel()->sendCompleteConfigure(); }); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial); + + exec([&] { + auto *surface = xdgToplevel()->surface(); + auto *p = pointer(); + auto *c = client(); + p->sendEnter(surface, {100, 100}); + p->sendFrame(c); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); + p->sendFrame(c); + p->sendLeave(surface); + p->sendFrame(c); + }); + + QCOMPOSITOR_TRY_VERIFY(xdgPopup()); + exec([&] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial); + QCOMPOSITOR_TRY_VERIFY(!xdgPopup()->m_grabbed); + + exec([&] { + auto *surface = xdgPopup()->surface(); + auto *p = pointer(); + auto *c = client(); + p->sendEnter(surface, {100, 100}); + p->sendFrame(c); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); + p->sendFrame(c); + }); + + QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)); + exec([&] { xdgPopup(1)->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_xdgSurface->m_committedConfigureSerial); + QCOMPOSITOR_TRY_VERIFY(xdgPopup(1)->m_grabbed); + + // Close the middle tooltip (it should not close the sibling popup) + window.m_tooltip->close(); + + QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr); + // Verify the remaining xdg surface is a grab popup.. + QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)); + QCOMPOSITOR_TRY_VERIFY(xdgPopup(0)->m_grabbed); + + window.m_tooltip->m_popup->close(); + QCOMPOSITOR_TRY_COMPARE(xdgPopup(1), nullptr); + QCOMPOSITOR_TRY_COMPARE(xdgPopup(0), nullptr); +} + // QTBUG-65680 void tst_xdgshell::switchPopups() { @@ -369,7 +485,7 @@ void tst_xdgshell::switchPopups() m_popups << new Popup(this); } ~Window() override { qDeleteAll(m_popups); } - QVector<Popup *> m_popups; + QList<Popup *> m_popups; }; Window window; @@ -377,10 +493,10 @@ void tst_xdgshell::switchPopups() window.show(); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); - exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + exec([&] { xdgToplevel()->sendCompleteConfigure(); }); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial); - exec([=] { + exec([&] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); auto *c = client(); @@ -394,13 +510,13 @@ void tst_xdgshell::switchPopups() }); QCOMPOSITOR_TRY_VERIFY(xdgPopup()); - exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + exec([&] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial); QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed); - QSignalSpy firstDestroyed(exec([=] { return xdgPopup(); }), &XdgPopup::destroyRequested); + QSignalSpy firstDestroyed(exec([&] { return xdgPopup(); }), &XdgPopup::destroyRequested); - exec([=] { + exec([&] { auto *surface = xdgToplevel()->surface(); auto *p = pointer(); auto *c = client(); @@ -425,10 +541,54 @@ void tst_xdgshell::switchPopups() QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_parentXdgSurface == xdgToplevel()->m_xdgSurface); // For good measure just check that configuring works as usual - exec([=] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); + exec([&] { xdgPopup()->sendCompleteConfigure(QRect(100, 100, 100, 100)); }); QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial); } +void tst_xdgshell::hidePopupParent() +{ + class Window : public QRasterWindow { + public: + void mousePressEvent(QMouseEvent *event) override + { + QRasterWindow::mousePressEvent(event); + m_popup.reset(new QRasterWindow); + m_popup->setTransientParent(this); + m_popup->setFlags(Qt::Popup); + m_popup->resize(100, 100); + m_popup->show(); + } + QScopedPointer<QRasterWindow> m_popup; + }; + Window window; + window.resize(200, 200); + window.show(); + + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + exec([&] { xdgToplevel()->sendCompleteConfigure(); }); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial); + + exec([&] { + auto *surface = xdgToplevel()->surface(); + auto *p = pointer(); + auto *c = client(); + p->sendEnter(surface, {100, 100}); + p->sendFrame(c); + p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(c, BTN_LEFT, Pointer::button_state_released); + p->sendFrame(c); + }); + QCOMPOSITOR_TRY_VERIFY(xdgPopup()); + exec([&] { + xdgPopup()->sendConfigure(QRect(100, 100, 100, 100)); + xdgPopup()->m_xdgSurface->sendConfigure(); + }); + QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_xdgSurface->m_committedConfigureSerial); + + window.hide(); + QCOMPOSITOR_TRY_VERIFY(!xdgToplevel()); +} + void tst_xdgshell::pongs() { // Create and show a window to trigger shell integration initialzation, @@ -440,36 +600,80 @@ void tst_xdgshell::pongs() // Verify that the client has bound to the global QCOMPOSITOR_TRY_COMPARE(get<XdgWmBase>()->resourceMap().size(), 1); - QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong); - const uint serial = exec([=] { return nextSerial(); }); - exec([=] { + QSignalSpy pongSpy(exec([&] { return get<XdgWmBase>(); }), &XdgWmBase::pong); + const uint serial = exec([&] { return nextSerial(); }); + exec([&] { auto *base = get<XdgWmBase>(); wl_resource *resource = base->resourceMap().first()->handle; base->send_ping(resource, serial); }); - QTRY_COMPARE(pongSpy.count(), 1); + QTRY_COMPARE(pongSpy.size(), 1); QCOMPARE(pongSpy.first().at(0).toUInt(), serial); } +void tst_xdgshell::minMaxSize_data() +{ + QTest::addColumn<QSize>("initialMinSize"); + QTest::addColumn<QSize>("initialMaxSize"); + QTest::addColumn<QSize>("nextMinSize"); + QTest::addColumn<QSize>("nextMaxSize"); + QTest::addColumn<QSize>("expectedInitialMinSize"); + QTest::addColumn<QSize>("expectedInitialMaxSize"); + QTest::addColumn<QSize>("expectedNextMinSize"); + QTest::addColumn<QSize>("expectedNextMaxSize"); + + QTest::newRow("onlyMinSize") << QSize(50, 60) << QSize() << QSize(500, 600) << QSize() + << QSize(50, 60) << QSize(0, 0) << QSize(500, 600) << QSize(0, 0); + + QTest::newRow("onlyMaxSize") << QSize() << QSize(70, 80) << QSize() << QSize(700, 800) + << QSize(0,0 ) << QSize(70, 80) << QSize(0, 0) << QSize(700, 800); + + QTest::newRow("maxIsSentAsZero") << QSize() << QSize(QWINDOWSIZE_MAX, QWINDOWSIZE_MAX) << QSize() << QSize() + << QSize(0,0 ) << QSize(0, 0) << QSize(0, 0) << QSize(0, 0); + + + QTest::newRow("fullHints") << QSize(50, 60) << QSize(700, 800) << QSize(500, 600) << QSize(710, 810) + << QSize(50, 60) << QSize(700, 800) << QSize(500, 600) << QSize(710, 810); + + // setting a minimum above the maximum is not allowed, we should no-op + QTest::newRow("invalidResize") << QSize(50, 60) << QSize(100, 100) << QSize(500, 600) << QSize(100, 100) + << QSize(50, 60) << QSize(100, 100) << QSize(50, 60) << QSize(100, 100);} + void tst_xdgshell::minMaxSize() { + QFETCH(QSize, initialMinSize); + QFETCH(QSize, initialMaxSize); + + QFETCH(QSize, expectedInitialMinSize); + QFETCH(QSize, expectedInitialMaxSize); + QRasterWindow window; - window.setMinimumSize(QSize(100, 100)); - window.setMaximumSize(QSize(1000, 1000)); - window.resize(400, 320); + if (initialMinSize.isValid()) + window.setMinimumSize(initialMinSize); + if (initialMaxSize.isValid()) + window.setMaximumSize(initialMaxSize); window.show(); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); - exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + exec([&] { xdgToplevel()->sendCompleteConfigure(); }); - QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(100, 100)); - QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(1000, 1000)); + // we don't roundtrip with our configuration the initial commit should be correct - window.setMaximumSize(QSize(500, 400)); - QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, QSize(500, 400)); + QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, expectedInitialMinSize); + QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, expectedInitialMaxSize); - window.setMinimumSize(QSize(50, 40)); - QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, QSize(50, 40)); + QFETCH(QSize, nextMinSize); + QFETCH(QSize, expectedNextMinSize); + window.setMinimumSize(nextMinSize); + window.update(); + QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.minSize, expectedNextMinSize); + + QFETCH(QSize, nextMaxSize); + QFETCH(QSize, expectedNextMaxSize); + + window.setMaximumSize(nextMaxSize); + window.update(); + QCOMPOSITOR_TRY_COMPARE(xdgToplevel()->m_committed.maxSize, expectedNextMaxSize); } void tst_xdgshell::windowGeometry() @@ -479,7 +683,7 @@ void tst_xdgshell::windowGeometry() window.show(); QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); - exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + exec([&] { xdgToplevel()->sendCompleteConfigure(); }); QSize marginsSize; marginsSize.setWidth(window.frameMargins().left() + window.frameMargins().right()); @@ -511,12 +715,95 @@ void tst_xdgshell::foreignSurface() // Just do something to make sure we don't destroy the surface before // the pointer events above are handled. - QSignalSpy spy(exec([=] { return surface(newSurfaceIndex); }), &Surface::commit); + QSignalSpy spy(exec([&] { return surface(newSurfaceIndex); }), &Surface::commit); wl_surface_commit(foreignSurface); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); wl_surface_destroy(foreignSurface); } +void tst_xdgshell::nativeResources() +{ + QRasterWindow window; + window.resize(400, 320); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + + auto *ni = QGuiApplication::platformNativeInterface(); + auto *xdg_surface_proxy = static_cast<::wl_proxy *>(ni->nativeResourceForWindow("xdg_surface", &window)); + QCOMPARE(wl_proxy_get_class(xdg_surface_proxy), "xdg_surface"); + + auto *xdg_toplevel_proxy = static_cast<::wl_proxy *>(ni->nativeResourceForWindow("xdg_toplevel", &window)); + QCOMPARE(wl_proxy_get_class(xdg_toplevel_proxy), "xdg_toplevel"); + + auto *xdg_popup_proxy = static_cast<::wl_proxy *>(ni->nativeResourceForWindow("xdg_popup", &window)); + QCOMPARE(xdg_popup_proxy, nullptr); +} + +void tst_xdgshell::suspended() +{ + QRasterWindow window; + window.resize(400, 320); + window.show(); + QVERIFY(!window.isExposed()); // not exposed until we're configured + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + + exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()->m_xdgSurface->m_committedConfigureSerial); + QTRY_VERIFY(window.isExposed()); + + exec([=] { xdgToplevel()->sendCompleteConfigure(QSize(), {XdgToplevel::state_suspended}); }); + QTRY_VERIFY(!window.isExposed()); + + exec([=] { xdgToplevel()->sendCompleteConfigure(QSize(), {}); }); + QTRY_VERIFY(window.isExposed()); +} + +void tst_xdgshell::initiallySuspended() +{ + QRasterWindow window; + window.resize(400, 320); + window.show(); + QVERIFY(!window.isExposed()); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + exec([=] { xdgToplevel()->sendCompleteConfigure(QSize(), {XdgToplevel::state_suspended}); }); + QVERIFY(!window.isExposed()); +} + +void tst_xdgshell::modality() +{ + QRasterWindow parent; + parent.resize(400, 320); + parent.show(); + + QRasterWindow child; + child.resize(400, 320); + child.setTransientParent(&parent); + child.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel(1)); + QCOMPOSITOR_VERIFY(!xdgDialog()); + + child.hide(); + child.setModality(Qt::WindowModal); + child.show(); + QCOMPOSITOR_TRY_VERIFY(xdgDialog()); + QCOMPOSITOR_VERIFY(xdgDialog()->modal); + + child.hide(); + QCOMPOSITOR_TRY_VERIFY(!xdgDialog()); + + child.setModality(Qt::ApplicationModal); + child.show(); + QCOMPOSITOR_TRY_VERIFY(xdgDialog()); + QCOMPOSITOR_VERIFY(xdgDialog()->modal); + + child.hide(); + QCOMPOSITOR_TRY_VERIFY(!xdgDialog()); + + child.show(); + child.setModality(Qt::NonModal); + QCOMPOSITOR_TRY_VERIFY(!xdgDialog()); +} + QCOMPOSITOR_TEST_MAIN(tst_xdgshell) #include "tst_xdgshell.moc" |