File indexing completed on 2024-11-10 04:56:01

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "kwin_wayland_test.h"
0010 
0011 #include "core/output.h"
0012 #include "decorations/decorationbridge.h"
0013 #include "decorations/settings.h"
0014 #include "pointer_input.h"
0015 #include "wayland_server.h"
0016 #include "window.h"
0017 #include "workspace.h"
0018 
0019 #include <KWayland/Client/compositor.h>
0020 #include <KWayland/Client/shm_pool.h>
0021 #include <KWayland/Client/surface.h>
0022 
0023 #include <KDecoration2/DecoratedClient>
0024 #include <KDecoration2/Decoration>
0025 #include <KDecoration2/DecorationSettings>
0026 
0027 #include <QSignalSpy>
0028 
0029 using namespace KWin;
0030 
0031 static const QString s_socketName = QStringLiteral("wayland_test_kwin_maximized-0");
0032 
0033 class TestMaximized : public QObject
0034 {
0035     Q_OBJECT
0036 private Q_SLOTS:
0037     void initTestCase();
0038     void init();
0039     void cleanup();
0040 
0041     void testMaximizedPassedToDeco();
0042     void testInitiallyMaximizedBorderless();
0043     void testBorderlessMaximizedWindow();
0044     void testMaximizedGainFocusAndBeActivated();
0045 };
0046 
0047 void TestMaximized::initTestCase()
0048 {
0049     qRegisterMetaType<KWin::Window *>();
0050     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0051     QVERIFY(waylandServer()->init(s_socketName));
0052     Test::setOutputConfig({
0053         QRect(0, 0, 1280, 1024),
0054         QRect(1280, 0, 1280, 1024),
0055     });
0056 
0057     kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
0058 
0059     kwinApp()->start();
0060     QVERIFY(applicationStartedSpy.wait());
0061     const auto outputs = workspace()->outputs();
0062     QCOMPARE(outputs.count(), 2);
0063     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0064     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0065 }
0066 
0067 void TestMaximized::init()
0068 {
0069     QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::XdgDecorationV1));
0070 
0071     workspace()->setActiveOutput(QPoint(640, 512));
0072     KWin::input()->pointer()->warp(QPoint(640, 512));
0073 }
0074 
0075 void TestMaximized::cleanup()
0076 {
0077     Test::destroyWaylandConnection();
0078 
0079     // adjust config
0080     auto group = kwinApp()->config()->group(QStringLiteral("Windows"));
0081     group.writeEntry("BorderlessMaximizedWindows", false);
0082     group.sync();
0083     Workspace::self()->slotReconfigure();
0084     QCOMPARE(options->borderlessMaximizedWindows(), false);
0085 }
0086 
0087 void TestMaximized::testMaximizedPassedToDeco()
0088 {
0089     // this test verifies that when a XdgShellClient gets maximized the Decoration receives the signal
0090 
0091     // Create the test window.
0092     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0093     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0094     std::unique_ptr<Test::XdgToplevelDecorationV1> xdgDecoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
0095 
0096     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0097     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0098     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0099     QVERIFY(surfaceConfigureRequestedSpy.wait());
0100 
0101     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0102     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0103     QVERIFY(window);
0104     QVERIFY(window->isDecorated());
0105 
0106     auto decoration = window->decoration();
0107     QVERIFY(decoration);
0108     QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
0109 
0110     // Wait for configure event that signals the window is active now.
0111     QVERIFY(surfaceConfigureRequestedSpy.wait());
0112     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
0113 
0114     // When there are no borders, there is no change to them when maximizing.
0115     // TODO: we should test both cases with fixed fake decoration for autotests.
0116     const bool hasBorders = Workspace::self()->decorationBridge()->settings()->borderSize() != KDecoration2::BorderSize::None;
0117 
0118     // now maximize
0119     QSignalSpy bordersChangedSpy(decoration, &KDecoration2::Decoration::bordersChanged);
0120     QSignalSpy maximizedChangedSpy(decoration->client(), &KDecoration2::DecoratedClient::maximizedChanged);
0121     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0122 
0123     workspace()->slotWindowMaximize();
0124     QVERIFY(surfaceConfigureRequestedSpy.wait());
0125     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
0126     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024 - decoration->borderTop()));
0127     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0128     Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red);
0129     QVERIFY(frameGeometryChangedSpy.wait());
0130 
0131     // If no borders, there is only the initial geometry shape change, but none through border resizing.
0132     QCOMPARE(frameGeometryChangedSpy.count(), hasBorders ? 2 : 1);
0133     QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeFull);
0134     QCOMPARE(maximizedChangedSpy.count(), 1);
0135     QCOMPARE(maximizedChangedSpy.last().first().toBool(), true);
0136     QCOMPARE(bordersChangedSpy.count(), hasBorders ? 1 : 0);
0137     QCOMPARE(decoration->borderLeft(), 0);
0138     QCOMPARE(decoration->borderBottom(), 0);
0139     QCOMPARE(decoration->borderRight(), 0);
0140     QVERIFY(decoration->borderTop() != 0);
0141 
0142     // now unmaximize again
0143     workspace()->slotWindowMaximize();
0144     QVERIFY(surfaceConfigureRequestedSpy.wait());
0145     QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
0146     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50));
0147 
0148     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0149     Test::render(surface.get(), QSize(100, 50), Qt::red);
0150     QVERIFY(frameGeometryChangedSpy.wait());
0151     QCOMPARE(frameGeometryChangedSpy.count(), hasBorders ? 4 : 2);
0152     QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
0153     QCOMPARE(maximizedChangedSpy.count(), 2);
0154     QCOMPARE(maximizedChangedSpy.last().first().toBool(), false);
0155     QCOMPARE(bordersChangedSpy.count(), hasBorders ? 2 : 0);
0156     QVERIFY(decoration->borderTop() != 0);
0157     QVERIFY(decoration->borderLeft() != !hasBorders);
0158     QVERIFY(decoration->borderRight() != !hasBorders);
0159     QVERIFY(decoration->borderBottom() != !hasBorders);
0160 
0161     // Destroy the test window.
0162     shellSurface.reset();
0163     QVERIFY(Test::waitForWindowClosed(window));
0164 }
0165 
0166 void TestMaximized::testInitiallyMaximizedBorderless()
0167 {
0168     // This test verifies that a window created as maximized, will be maximized and without Border with BorderlessMaximizedWindows
0169 
0170     // adjust config
0171     auto group = kwinApp()->config()->group(QStringLiteral("Windows"));
0172     group.writeEntry("BorderlessMaximizedWindows", true);
0173     group.sync();
0174     Workspace::self()->slotReconfigure();
0175     QCOMPARE(options->borderlessMaximizedWindows(), true);
0176 
0177     // Create the test window.
0178     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0179     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0180     std::unique_ptr<Test::XdgToplevelDecorationV1> decoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
0181 
0182     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0183     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0184     shellSurface->set_maximized();
0185     QSignalSpy decorationConfigureRequestedSpy(decoration.get(), &Test::XdgToplevelDecorationV1::configureRequested);
0186     decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
0187     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0188 
0189     // Wait for the initial configure event.
0190     Test::XdgToplevel::States states;
0191     QVERIFY(surfaceConfigureRequestedSpy.wait());
0192     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
0193     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
0194     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0195     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
0196     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
0197 
0198     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0199     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(1280, 1024), Qt::blue);
0200     QVERIFY(window);
0201     QVERIFY(!window->isDecorated());
0202     QVERIFY(window->isActive());
0203     QVERIFY(window->isMaximizable());
0204     QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeFull);
0205     QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
0206     QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
0207     QCOMPARE(decorationConfigureRequestedSpy.last().at(0).value<Test::XdgToplevelDecorationV1::mode>(),
0208              Test::XdgToplevelDecorationV1::mode_server_side);
0209 
0210     // Destroy the window.
0211     shellSurface.reset();
0212     surface.reset();
0213     QVERIFY(Test::waitForWindowClosed(window));
0214 }
0215 void TestMaximized::testBorderlessMaximizedWindow()
0216 {
0217     // This test verifies that a maximized window looses it's server-side
0218     // decoration when the borderless maximized option is on.
0219 
0220     // Enable the borderless maximized windows option.
0221     auto group = kwinApp()->config()->group(QStringLiteral("Windows"));
0222     group.writeEntry("BorderlessMaximizedWindows", true);
0223     group.sync();
0224     Workspace::self()->slotReconfigure();
0225     QCOMPARE(options->borderlessMaximizedWindows(), true);
0226 
0227     // Create the test window.
0228     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0229     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0230     std::unique_ptr<Test::XdgToplevelDecorationV1> decoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
0231 
0232     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0233     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0234     QSignalSpy decorationConfigureRequestedSpy(decoration.get(), &Test::XdgToplevelDecorationV1::configureRequested);
0235     decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
0236     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0237 
0238     // Wait for the initial configure event.
0239     Test::XdgToplevel::States states;
0240     QVERIFY(surfaceConfigureRequestedSpy.wait());
0241     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
0242     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(0, 0));
0243     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0244     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
0245     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
0246 
0247     // Map the window.
0248     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0249     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0250     QVERIFY(window);
0251     QVERIFY(window->isActive());
0252     QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
0253     QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
0254     QCOMPARE(window->isDecorated(), true);
0255 
0256     // We should receive a configure event when the window becomes active.
0257     QVERIFY(surfaceConfigureRequestedSpy.wait());
0258     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
0259     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0260     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0261     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
0262 
0263     // Maximize the window.
0264     const QRectF maximizeRestoreGeometry = window->frameGeometry();
0265     workspace()->slotWindowMaximize();
0266     QVERIFY(surfaceConfigureRequestedSpy.wait());
0267     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
0268     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
0269     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0270     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0271     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
0272 
0273     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0274     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0275     Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
0276     QVERIFY(frameGeometryChangedSpy.wait());
0277     QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
0278     QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeFull);
0279     QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
0280     QCOMPARE(window->isDecorated(), false);
0281 
0282     // Restore the window.
0283     workspace()->slotWindowMaximize();
0284     QVERIFY(surfaceConfigureRequestedSpy.wait());
0285     QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
0286     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50));
0287     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0288     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0289     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
0290 
0291     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0292     Test::render(surface.get(), QSize(100, 50), Qt::red);
0293     QVERIFY(frameGeometryChangedSpy.wait());
0294     QCOMPARE(window->frameGeometry(), maximizeRestoreGeometry);
0295     QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
0296     QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
0297     QCOMPARE(window->isDecorated(), true);
0298 
0299     // Destroy the window.
0300     shellSurface.reset();
0301     QVERIFY(Test::waitForWindowClosed(window));
0302 }
0303 
0304 void TestMaximized::testMaximizedGainFocusAndBeActivated()
0305 {
0306     // This test verifies that a window will be raised and gain focus  when it's maximized
0307     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0308     std::unique_ptr<Test::XdgToplevel> xdgShellSurface(Test::createXdgToplevelSurface(surface.get()));
0309     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0310     std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
0311     std::unique_ptr<Test::XdgToplevel> xdgShellSurface2(Test::createXdgToplevelSurface(surface2.get()));
0312     auto window2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue);
0313 
0314     QVERIFY(!window->isActive());
0315     QVERIFY(window2->isActive());
0316     QCOMPARE(workspace()->stackingOrder(), (QList<Window *>{window, window2}));
0317 
0318     workspace()->performWindowOperation(window, Options::MaximizeOp);
0319 
0320     QVERIFY(window->isActive());
0321     QVERIFY(!window2->isActive());
0322     QCOMPARE(workspace()->stackingOrder(), (QList<Window *>{window2, window}));
0323 
0324     xdgShellSurface.reset();
0325     QVERIFY(Test::waitForWindowClosed(window));
0326     xdgShellSurface2.reset();
0327     QVERIFY(Test::waitForWindowClosed(window2));
0328 }
0329 
0330 WAYLANDTEST_MAIN(TestMaximized)
0331 #include "maximize_test.moc"