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"