File indexing completed on 2025-03-23 13:48:02
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 "virtualdesktops.h" 0015 #include "wayland_server.h" 0016 #include "window.h" 0017 #include "workspace.h" 0018 #include <KWayland/Client/compositor.h> 0019 #include <KWayland/Client/connection_thread.h> 0020 #include <KWayland/Client/event_queue.h> 0021 #include <KWayland/Client/plasmashell.h> 0022 #include <KWayland/Client/registry.h> 0023 #include <KWayland/Client/shm_pool.h> 0024 #include <KWayland/Client/surface.h> 0025 0026 using namespace KWin; 0027 0028 Q_DECLARE_METATYPE(KWin::Layer) 0029 0030 static const QString s_socketName = QStringLiteral("wayland_test_kwin_plasma_surface-0"); 0031 0032 class PlasmaSurfaceTest : public QObject 0033 { 0034 Q_OBJECT 0035 private Q_SLOTS: 0036 void initTestCase(); 0037 void init(); 0038 void cleanup(); 0039 0040 void testRoleOnAllDesktops_data(); 0041 void testRoleOnAllDesktops(); 0042 void testAcceptsFocus_data(); 0043 void testAcceptsFocus(); 0044 0045 void testPanelWindowsCanCover_data(); 0046 void testPanelWindowsCanCover(); 0047 void testOSDPlacement(); 0048 void testOSDPlacementManualPosition(); 0049 void testPanelTypeHasStrut_data(); 0050 void testPanelTypeHasStrut(); 0051 void testPanelActivate_data(); 0052 void testPanelActivate(); 0053 0054 private: 0055 KWayland::Client::Compositor *m_compositor = nullptr; 0056 KWayland::Client::PlasmaShell *m_plasmaShell = nullptr; 0057 }; 0058 0059 void PlasmaSurfaceTest::initTestCase() 0060 { 0061 qRegisterMetaType<KWin::Window *>(); 0062 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); 0063 QVERIFY(waylandServer()->init(s_socketName)); 0064 QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024))); 0065 0066 kwinApp()->start(); 0067 QVERIFY(applicationStartedSpy.wait()); 0068 } 0069 0070 void PlasmaSurfaceTest::init() 0071 { 0072 QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::PlasmaShell)); 0073 m_compositor = Test::waylandCompositor(); 0074 m_plasmaShell = Test::waylandPlasmaShell(); 0075 0076 KWin::Cursors::self()->mouse()->setPos(640, 512); 0077 } 0078 0079 void PlasmaSurfaceTest::cleanup() 0080 { 0081 Test::destroyWaylandConnection(); 0082 } 0083 0084 void PlasmaSurfaceTest::testRoleOnAllDesktops_data() 0085 { 0086 QTest::addColumn<KWayland::Client::PlasmaShellSurface::Role>("role"); 0087 QTest::addColumn<bool>("expectedOnAllDesktops"); 0088 0089 QTest::newRow("Desktop") << KWayland::Client::PlasmaShellSurface::Role::Desktop << true; 0090 QTest::newRow("Panel") << KWayland::Client::PlasmaShellSurface::Role::Panel << true; 0091 QTest::newRow("OSD") << KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay << true; 0092 QTest::newRow("Normal") << KWayland::Client::PlasmaShellSurface::Role::Normal << false; 0093 QTest::newRow("Notification") << KWayland::Client::PlasmaShellSurface::Role::Notification << true; 0094 QTest::newRow("ToolTip") << KWayland::Client::PlasmaShellSurface::Role::ToolTip << true; 0095 QTest::newRow("CriticalNotification") << KWayland::Client::PlasmaShellSurface::Role::CriticalNotification << true; 0096 QTest::newRow("AppletPopup") << KWayland::Client::PlasmaShellSurface::Role::AppletPopup << true; 0097 } 0098 0099 void PlasmaSurfaceTest::testRoleOnAllDesktops() 0100 { 0101 // this test verifies that a XdgShellClient is set on all desktops when the role changes 0102 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0103 QVERIFY(surface != nullptr); 0104 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0105 QVERIFY(shellSurface != nullptr); 0106 std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get())); 0107 QVERIFY(plasmaSurface != nullptr); 0108 0109 // now render to map the window 0110 Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0111 QVERIFY(window); 0112 QCOMPARE(workspace()->activeWindow(), window); 0113 0114 // currently the role is not yet set, so the window should not be on all desktops 0115 QCOMPARE(window->isOnAllDesktops(), false); 0116 0117 // now let's try to change that 0118 QSignalSpy onAllDesktopsSpy(window, &Window::desktopChanged); 0119 QFETCH(KWayland::Client::PlasmaShellSurface::Role, role); 0120 plasmaSurface->setRole(role); 0121 QFETCH(bool, expectedOnAllDesktops); 0122 QCOMPARE(onAllDesktopsSpy.wait(), expectedOnAllDesktops); 0123 QCOMPARE(window->isOnAllDesktops(), expectedOnAllDesktops); 0124 0125 // let's create a second window where we init a little bit different 0126 // first creating the PlasmaSurface then the Shell Surface 0127 std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface()); 0128 QVERIFY(surface2 != nullptr); 0129 std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface2(m_plasmaShell->createSurface(surface2.get())); 0130 QVERIFY(plasmaSurface2 != nullptr); 0131 plasmaSurface2->setRole(role); 0132 std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get())); 0133 QVERIFY(shellSurface2 != nullptr); 0134 auto c2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue); 0135 QVERIFY(c2); 0136 QVERIFY(window != c2); 0137 0138 QCOMPARE(c2->isOnAllDesktops(), expectedOnAllDesktops); 0139 } 0140 0141 void PlasmaSurfaceTest::testAcceptsFocus_data() 0142 { 0143 QTest::addColumn<KWayland::Client::PlasmaShellSurface::Role>("role"); 0144 QTest::addColumn<bool>("wantsInput"); 0145 QTest::addColumn<bool>("active"); 0146 0147 QTest::newRow("Desktop") << KWayland::Client::PlasmaShellSurface::Role::Desktop << true << true; 0148 QTest::newRow("Panel") << KWayland::Client::PlasmaShellSurface::Role::Panel << true << false; 0149 QTest::newRow("OSD") << KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay << false << false; 0150 QTest::newRow("Normal") << KWayland::Client::PlasmaShellSurface::Role::Normal << true << true; 0151 QTest::newRow("Notification") << KWayland::Client::PlasmaShellSurface::Role::Notification << false << false; 0152 QTest::newRow("ToolTip") << KWayland::Client::PlasmaShellSurface::Role::ToolTip << false << false; 0153 QTest::newRow("CriticalNotification") << KWayland::Client::PlasmaShellSurface::Role::CriticalNotification << false << false; 0154 QTest::newRow("AppletPopup") << KWayland::Client::PlasmaShellSurface::Role::AppletPopup << true << true; 0155 } 0156 0157 void PlasmaSurfaceTest::testAcceptsFocus() 0158 { 0159 // this test verifies that some surface roles don't get focus 0160 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0161 QVERIFY(surface != nullptr); 0162 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0163 QVERIFY(shellSurface != nullptr); 0164 std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get())); 0165 QVERIFY(plasmaSurface != nullptr); 0166 QFETCH(KWayland::Client::PlasmaShellSurface::Role, role); 0167 plasmaSurface->setRole(role); 0168 0169 // now render to map the window 0170 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0171 0172 QVERIFY(window); 0173 QTEST(window->wantsInput(), "wantsInput"); 0174 QTEST(window->isActive(), "active"); 0175 } 0176 0177 void PlasmaSurfaceTest::testOSDPlacement() 0178 { 0179 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0180 QVERIFY(surface != nullptr); 0181 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0182 QVERIFY(shellSurface != nullptr); 0183 std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get())); 0184 QVERIFY(plasmaSurface != nullptr); 0185 plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay); 0186 0187 // now render and map the window 0188 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0189 0190 QVERIFY(window); 0191 QCOMPARE(window->windowType(), NET::OnScreenDisplay); 0192 QVERIFY(window->isOnScreenDisplay()); 0193 QCOMPARE(window->frameGeometry(), QRect(1280 / 2 - 100 / 2, 2 * 1024 / 3 - 50 / 2, 100, 50)); 0194 0195 // change the screen size 0196 const QVector<QRect> geometries{QRect(0, 0, 1280, 1024), QRect(1280, 0, 1280, 1024)}; 0197 QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", 0198 Qt::DirectConnection, 0199 Q_ARG(QVector<QRect>, geometries)); 0200 const auto outputs = workspace()->outputs(); 0201 QCOMPARE(outputs.count(), 2); 0202 QCOMPARE(outputs[0]->geometry(), geometries[0]); 0203 QCOMPARE(outputs[1]->geometry(), geometries[1]); 0204 0205 QCOMPARE(window->frameGeometry(), QRect(1280 / 2 - 100 / 2, 2 * 1024 / 3 - 50 / 2, 100, 50)); 0206 0207 // change size of window 0208 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); 0209 Test::render(surface.get(), QSize(200, 100), Qt::red); 0210 QVERIFY(frameGeometryChangedSpy.wait()); 0211 QCOMPARE(window->frameGeometry(), QRect(1280 / 2 - 200 / 2, 2 * 1024 / 3 - 100 / 2, 200, 100)); 0212 } 0213 0214 void PlasmaSurfaceTest::testOSDPlacementManualPosition() 0215 { 0216 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0217 QVERIFY(surface != nullptr); 0218 std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get())); 0219 QVERIFY(plasmaSurface != nullptr); 0220 plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay); 0221 0222 plasmaSurface->setPosition(QPoint(50, 70)); 0223 0224 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0225 QVERIFY(shellSurface != nullptr); 0226 0227 // now render and map the window 0228 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0229 0230 QVERIFY(window); 0231 QVERIFY(!window->isPlaceable()); 0232 QCOMPARE(window->windowType(), NET::OnScreenDisplay); 0233 QVERIFY(window->isOnScreenDisplay()); 0234 QCOMPARE(window->frameGeometry(), QRect(50, 70, 100, 50)); 0235 } 0236 0237 void PlasmaSurfaceTest::testPanelTypeHasStrut_data() 0238 { 0239 QTest::addColumn<KWayland::Client::PlasmaShellSurface::PanelBehavior>("panelBehavior"); 0240 QTest::addColumn<bool>("expectedStrut"); 0241 QTest::addColumn<QRectF>("expectedMaxArea"); 0242 QTest::addColumn<KWin::Layer>("expectedLayer"); 0243 0244 QTest::newRow("always visible - xdgWmBase") << KWayland::Client::PlasmaShellSurface::PanelBehavior::AlwaysVisible << true << QRectF(0, 50, 1280, 974) << KWin::DockLayer; 0245 QTest::newRow("autohide - xdgWmBase") << KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide << false << QRectF(0, 0, 1280, 1024) << KWin::AboveLayer; 0246 QTest::newRow("windows can cover - xdgWmBase") << KWayland::Client::PlasmaShellSurface::PanelBehavior::WindowsCanCover << false << QRectF(0, 0, 1280, 1024) << KWin::NormalLayer; 0247 QTest::newRow("windows go below - xdgWmBase") << KWayland::Client::PlasmaShellSurface::PanelBehavior::WindowsGoBelow << false << QRectF(0, 0, 1280, 1024) << KWin::AboveLayer; 0248 } 0249 0250 void PlasmaSurfaceTest::testPanelTypeHasStrut() 0251 { 0252 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0253 QVERIFY(surface != nullptr); 0254 std::unique_ptr<QObject> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0255 QVERIFY(shellSurface != nullptr); 0256 std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get())); 0257 QVERIFY(plasmaSurface != nullptr); 0258 plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel); 0259 plasmaSurface->setPosition(QPoint(0, 0)); 0260 QFETCH(KWayland::Client::PlasmaShellSurface::PanelBehavior, panelBehavior); 0261 plasmaSurface->setPanelBehavior(panelBehavior); 0262 0263 // now render and map the window 0264 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0265 0266 // the panel is on the first output and the current desktop 0267 Output *output = workspace()->outputs().constFirst(); 0268 VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop(); 0269 0270 QVERIFY(window); 0271 QCOMPARE(window->windowType(), NET::Dock); 0272 QVERIFY(window->isDock()); 0273 QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50)); 0274 QTEST(window->hasStrut(), "expectedStrut"); 0275 QTEST(workspace()->clientArea(MaximizeArea, output, desktop), "expectedMaxArea"); 0276 QTEST(window->layer(), "expectedLayer"); 0277 } 0278 0279 void PlasmaSurfaceTest::testPanelWindowsCanCover_data() 0280 { 0281 QTest::addColumn<QRect>("panelGeometry"); 0282 QTest::addColumn<QRect>("windowGeometry"); 0283 QTest::addColumn<QPoint>("triggerPoint"); 0284 0285 QTest::newRow("top-full-edge") << QRect(0, 0, 1280, 30) << QRect(0, 0, 200, 300) << QPoint(100, 0); 0286 QTest::newRow("top-left-edge") << QRect(0, 0, 1000, 30) << QRect(0, 0, 200, 300) << QPoint(100, 0); 0287 QTest::newRow("top-right-edge") << QRect(280, 0, 1000, 30) << QRect(1000, 0, 200, 300) << QPoint(1000, 0); 0288 QTest::newRow("bottom-full-edge") << QRect(0, 994, 1280, 30) << QRect(0, 724, 200, 300) << QPoint(100, 1023); 0289 QTest::newRow("bottom-left-edge") << QRect(0, 994, 1000, 30) << QRect(0, 724, 200, 300) << QPoint(100, 1023); 0290 QTest::newRow("bottom-right-edge") << QRect(280, 994, 1000, 30) << QRect(1000, 724, 200, 300) << QPoint(1000, 1023); 0291 QTest::newRow("left-full-edge") << QRect(0, 0, 30, 1024) << QRect(0, 0, 200, 300) << QPoint(0, 100); 0292 QTest::newRow("left-top-edge") << QRect(0, 0, 30, 800) << QRect(0, 0, 200, 300) << QPoint(0, 100); 0293 QTest::newRow("left-bottom-edge") << QRect(0, 200, 30, 824) << QRect(0, 0, 200, 300) << QPoint(0, 250); 0294 QTest::newRow("right-full-edge") << QRect(1250, 0, 30, 1024) << QRect(1080, 0, 200, 300) << QPoint(1279, 100); 0295 QTest::newRow("right-top-edge") << QRect(1250, 0, 30, 800) << QRect(1080, 0, 200, 300) << QPoint(1279, 100); 0296 QTest::newRow("right-bottom-edge") << QRect(1250, 200, 30, 824) << QRect(1080, 0, 200, 300) << QPoint(1279, 250); 0297 } 0298 0299 void PlasmaSurfaceTest::testPanelWindowsCanCover() 0300 { 0301 // this test verifies the behavior of a panel with windows can cover 0302 // triggering the screen edge should raise the panel. 0303 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0304 QVERIFY(surface != nullptr); 0305 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0306 QVERIFY(shellSurface != nullptr); 0307 std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get())); 0308 QVERIFY(plasmaSurface != nullptr); 0309 plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel); 0310 QFETCH(QRect, panelGeometry); 0311 plasmaSurface->setPosition(panelGeometry.topLeft()); 0312 plasmaSurface->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::WindowsCanCover); 0313 0314 // now render and map the window 0315 auto panel = Test::renderAndWaitForShown(surface.get(), panelGeometry.size(), Qt::blue); 0316 0317 // the panel is on the first output and the current desktop 0318 Output *output = workspace()->outputs().constFirst(); 0319 VirtualDesktop *desktop = VirtualDesktopManager::self()->currentDesktop(); 0320 0321 QVERIFY(panel); 0322 QCOMPARE(panel->windowType(), NET::Dock); 0323 QVERIFY(panel->isDock()); 0324 QCOMPARE(panel->frameGeometry(), panelGeometry); 0325 QCOMPARE(panel->hasStrut(), false); 0326 QCOMPARE(workspace()->clientArea(MaximizeArea, output, desktop), QRect(0, 0, 1280, 1024)); 0327 QCOMPARE(panel->layer(), KWin::NormalLayer); 0328 0329 // create a Window 0330 std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface()); 0331 QVERIFY(surface2 != nullptr); 0332 std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get())); 0333 QVERIFY(shellSurface2 != nullptr); 0334 0335 QFETCH(QRect, windowGeometry); 0336 auto window = Test::renderAndWaitForShown(surface2.get(), windowGeometry.size(), Qt::red); 0337 0338 QVERIFY(window); 0339 QCOMPARE(window->windowType(), NET::Normal); 0340 QVERIFY(window->isActive()); 0341 QCOMPARE(window->layer(), KWin::NormalLayer); 0342 window->move(windowGeometry.topLeft()); 0343 QCOMPARE(window->frameGeometry(), windowGeometry); 0344 0345 auto stackingOrder = workspace()->stackingOrder(); 0346 QCOMPARE(stackingOrder.count(), 2); 0347 QCOMPARE(stackingOrder.first(), panel); 0348 QCOMPARE(stackingOrder.last(), window); 0349 0350 QSignalSpy stackingOrderChangedSpy(workspace(), &Workspace::stackingOrderChanged); 0351 // trigger screenedge 0352 QFETCH(QPoint, triggerPoint); 0353 KWin::Cursors::self()->mouse()->setPos(triggerPoint); 0354 QVERIFY(stackingOrderChangedSpy.wait()); 0355 QCOMPARE(stackingOrderChangedSpy.count(), 1); 0356 stackingOrder = workspace()->stackingOrder(); 0357 QCOMPARE(stackingOrder.count(), 2); 0358 QCOMPARE(stackingOrder.first(), window); 0359 QCOMPARE(stackingOrder.last(), panel); 0360 } 0361 0362 void PlasmaSurfaceTest::testPanelActivate_data() 0363 { 0364 QTest::addColumn<bool>("wantsFocus"); 0365 QTest::addColumn<bool>("active"); 0366 0367 QTest::newRow("no focus") << false << false; 0368 QTest::newRow("focus") << true << true; 0369 } 0370 0371 void PlasmaSurfaceTest::testPanelActivate() 0372 { 0373 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0374 QVERIFY(surface != nullptr); 0375 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0376 QVERIFY(shellSurface != nullptr); 0377 std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get())); 0378 QVERIFY(plasmaSurface != nullptr); 0379 plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel); 0380 QFETCH(bool, wantsFocus); 0381 plasmaSurface->setPanelTakesFocus(wantsFocus); 0382 0383 auto panel = Test::renderAndWaitForShown(surface.get(), QSize(100, 200), Qt::blue); 0384 0385 QVERIFY(panel); 0386 QCOMPARE(panel->windowType(), NET::Dock); 0387 QVERIFY(panel->isDock()); 0388 QFETCH(bool, active); 0389 QCOMPARE(panel->dockWantsInput(), active); 0390 QCOMPARE(panel->isActive(), active); 0391 } 0392 0393 WAYLANDTEST_MAIN(PlasmaSurfaceTest) 0394 #include "plasma_surface_test.moc"