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