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

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 "pointer_input.h"
0013 #include "virtualdesktops.h"
0014 #include "wayland_server.h"
0015 #include "window.h"
0016 #include "workspace.h"
0017 
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     void testOSDPlacement();
0045     void testOSDPlacementManualPosition();
0046     void testPanelActivate_data();
0047     void testPanelActivate();
0048     void testMovable_data();
0049     void testMovable();
0050 
0051 private:
0052     KWayland::Client::Compositor *m_compositor = nullptr;
0053     KWayland::Client::PlasmaShell *m_plasmaShell = nullptr;
0054 };
0055 
0056 void PlasmaSurfaceTest::initTestCase()
0057 {
0058     qRegisterMetaType<KWin::Window *>();
0059     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0060     QVERIFY(waylandServer()->init(s_socketName));
0061     Test::setOutputConfig({QRect(0, 0, 1280, 1024)});
0062 
0063     kwinApp()->start();
0064     QVERIFY(applicationStartedSpy.wait());
0065 }
0066 
0067 void PlasmaSurfaceTest::init()
0068 {
0069     QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::PlasmaShell));
0070     m_compositor = Test::waylandCompositor();
0071     m_plasmaShell = Test::waylandPlasmaShell();
0072 
0073     KWin::input()->pointer()->warp(QPointF(640, 512));
0074 }
0075 
0076 void PlasmaSurfaceTest::cleanup()
0077 {
0078     Test::destroyWaylandConnection();
0079 }
0080 
0081 void PlasmaSurfaceTest::testRoleOnAllDesktops_data()
0082 {
0083     QTest::addColumn<KWayland::Client::PlasmaShellSurface::Role>("role");
0084     QTest::addColumn<bool>("expectedOnAllDesktops");
0085 
0086     QTest::newRow("Desktop") << KWayland::Client::PlasmaShellSurface::Role::Desktop << true;
0087     QTest::newRow("Panel") << KWayland::Client::PlasmaShellSurface::Role::Panel << true;
0088     QTest::newRow("OSD") << KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay << true;
0089     QTest::newRow("Normal") << KWayland::Client::PlasmaShellSurface::Role::Normal << false;
0090     QTest::newRow("Notification") << KWayland::Client::PlasmaShellSurface::Role::Notification << true;
0091     QTest::newRow("ToolTip") << KWayland::Client::PlasmaShellSurface::Role::ToolTip << true;
0092     QTest::newRow("CriticalNotification") << KWayland::Client::PlasmaShellSurface::Role::CriticalNotification << true;
0093     QTest::newRow("AppletPopup") << KWayland::Client::PlasmaShellSurface::Role::AppletPopup << true;
0094 }
0095 
0096 void PlasmaSurfaceTest::testRoleOnAllDesktops()
0097 {
0098     // this test verifies that a XdgShellClient is set on all desktops when the role changes
0099     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0100     QVERIFY(surface != nullptr);
0101     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0102     QVERIFY(shellSurface != nullptr);
0103     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get()));
0104     QVERIFY(plasmaSurface != nullptr);
0105 
0106     // now render to map the window
0107     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0108     QVERIFY(window);
0109     QCOMPARE(workspace()->activeWindow(), window);
0110 
0111     // currently the role is not yet set, so the window should not be on all desktops
0112     QCOMPARE(window->isOnAllDesktops(), false);
0113 
0114     // now let's try to change that
0115     QSignalSpy onAllDesktopsSpy(window, &Window::desktopsChanged);
0116     QFETCH(KWayland::Client::PlasmaShellSurface::Role, role);
0117     plasmaSurface->setRole(role);
0118     QFETCH(bool, expectedOnAllDesktops);
0119     QCOMPARE(onAllDesktopsSpy.wait(), expectedOnAllDesktops);
0120     QCOMPARE(window->isOnAllDesktops(), expectedOnAllDesktops);
0121 
0122     // let's create a second window where we init a little bit different
0123     // first creating the PlasmaSurface then the Shell Surface
0124     std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
0125     QVERIFY(surface2 != nullptr);
0126     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface2(m_plasmaShell->createSurface(surface2.get()));
0127     QVERIFY(plasmaSurface2 != nullptr);
0128     plasmaSurface2->setRole(role);
0129     std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
0130     QVERIFY(shellSurface2 != nullptr);
0131     auto c2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue);
0132     QVERIFY(c2);
0133     QVERIFY(window != c2);
0134 
0135     QCOMPARE(c2->isOnAllDesktops(), expectedOnAllDesktops);
0136 }
0137 
0138 void PlasmaSurfaceTest::testAcceptsFocus_data()
0139 {
0140     QTest::addColumn<KWayland::Client::PlasmaShellSurface::Role>("role");
0141     QTest::addColumn<bool>("wantsInput");
0142     QTest::addColumn<bool>("active");
0143 
0144     QTest::newRow("Desktop") << KWayland::Client::PlasmaShellSurface::Role::Desktop << true << true;
0145     QTest::newRow("Panel") << KWayland::Client::PlasmaShellSurface::Role::Panel << true << false;
0146     QTest::newRow("OSD") << KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay << false << false;
0147     QTest::newRow("Normal") << KWayland::Client::PlasmaShellSurface::Role::Normal << true << true;
0148     QTest::newRow("Notification") << KWayland::Client::PlasmaShellSurface::Role::Notification << false << false;
0149     QTest::newRow("ToolTip") << KWayland::Client::PlasmaShellSurface::Role::ToolTip << false << false;
0150     QTest::newRow("CriticalNotification") << KWayland::Client::PlasmaShellSurface::Role::CriticalNotification << false << false;
0151     QTest::newRow("AppletPopup") << KWayland::Client::PlasmaShellSurface::Role::AppletPopup << true << true;
0152 }
0153 
0154 void PlasmaSurfaceTest::testAcceptsFocus()
0155 {
0156     // this test verifies that some surface roles don't get focus
0157     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0158     QVERIFY(surface != nullptr);
0159     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0160     QVERIFY(shellSurface != nullptr);
0161     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get()));
0162     QVERIFY(plasmaSurface != nullptr);
0163     QFETCH(KWayland::Client::PlasmaShellSurface::Role, role);
0164     plasmaSurface->setRole(role);
0165 
0166     // now render to map the window
0167     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0168 
0169     QVERIFY(window);
0170     QTEST(window->wantsInput(), "wantsInput");
0171     QTEST(window->isActive(), "active");
0172 }
0173 
0174 void PlasmaSurfaceTest::testOSDPlacement()
0175 {
0176     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0177     QVERIFY(surface != nullptr);
0178     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0179     QVERIFY(shellSurface != nullptr);
0180     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get()));
0181     QVERIFY(plasmaSurface != nullptr);
0182     plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay);
0183 
0184     // now render and map the window
0185     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0186 
0187     QVERIFY(window);
0188     QCOMPARE(window->windowType(), NET::OnScreenDisplay);
0189     QVERIFY(window->isOnScreenDisplay());
0190     QCOMPARE(window->frameGeometry(), QRect(1280 / 2 - 100 / 2, 2 * 1024 / 3 - 50 / 2, 100, 50));
0191 
0192     // change the screen size
0193     const QList<QRect> geometries{QRect(0, 0, 1280, 1024), QRect(1280, 0, 1280, 1024)};
0194     Test::setOutputConfig(geometries);
0195     const auto outputs = workspace()->outputs();
0196     QCOMPARE(outputs.count(), 2);
0197     QCOMPARE(outputs[0]->geometry(), geometries[0]);
0198     QCOMPARE(outputs[1]->geometry(), geometries[1]);
0199 
0200     QCOMPARE(window->frameGeometry(), QRect(1280 / 2 - 100 / 2, 2 * 1024 / 3 - 50 / 2, 100, 50));
0201 
0202     // change size of window
0203     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0204     Test::render(surface.get(), QSize(200, 100), Qt::red);
0205     QVERIFY(frameGeometryChangedSpy.wait());
0206     QCOMPARE(window->frameGeometry(), QRect(1280 / 2 - 200 / 2, 2 * 1024 / 3 - 100 / 2, 200, 100));
0207 }
0208 
0209 void PlasmaSurfaceTest::testOSDPlacementManualPosition()
0210 {
0211     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0212     QVERIFY(surface != nullptr);
0213     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get()));
0214     QVERIFY(plasmaSurface != nullptr);
0215     plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay);
0216 
0217     plasmaSurface->setPosition(QPoint(50, 70));
0218 
0219     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0220     QVERIFY(shellSurface != nullptr);
0221 
0222     // now render and map the window
0223     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0224 
0225     QVERIFY(window);
0226     QVERIFY(!window->isPlaceable());
0227     QCOMPARE(window->windowType(), NET::OnScreenDisplay);
0228     QVERIFY(window->isOnScreenDisplay());
0229     QCOMPARE(window->frameGeometry(), QRect(50, 70, 100, 50));
0230 }
0231 
0232 void PlasmaSurfaceTest::testPanelActivate_data()
0233 {
0234     QTest::addColumn<bool>("wantsFocus");
0235     QTest::addColumn<bool>("active");
0236 
0237     QTest::newRow("no focus") << false << false;
0238     QTest::newRow("focus") << true << true;
0239 }
0240 
0241 void PlasmaSurfaceTest::testPanelActivate()
0242 {
0243     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0244     QVERIFY(surface != nullptr);
0245     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0246     QVERIFY(shellSurface != nullptr);
0247     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(m_plasmaShell->createSurface(surface.get()));
0248     QVERIFY(plasmaSurface != nullptr);
0249     plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel);
0250     QFETCH(bool, wantsFocus);
0251     plasmaSurface->setPanelTakesFocus(wantsFocus);
0252 
0253     auto panel = Test::renderAndWaitForShown(surface.get(), QSize(100, 200), Qt::blue);
0254 
0255     QVERIFY(panel);
0256     QCOMPARE(panel->windowType(), NET::Dock);
0257     QVERIFY(panel->isDock());
0258     QFETCH(bool, active);
0259     QCOMPARE(panel->dockWantsInput(), active);
0260     QCOMPARE(panel->isActive(), active);
0261 }
0262 
0263 void PlasmaSurfaceTest::testMovable_data()
0264 {
0265     QTest::addColumn<KWayland::Client::PlasmaShellSurface::Role>("role");
0266     QTest::addColumn<bool>("movable");
0267     QTest::addColumn<bool>("movableAcrossScreens");
0268     QTest::addColumn<bool>("resizable");
0269 
0270     QTest::newRow("normal") << KWayland::Client::PlasmaShellSurface::Role::Normal << true << true << true;
0271     QTest::newRow("desktop") << KWayland::Client::PlasmaShellSurface::Role::Desktop << false << false << false;
0272     QTest::newRow("panel") << KWayland::Client::PlasmaShellSurface::Role::Panel << false << false << false;
0273     QTest::newRow("osd") << KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay << false << false << false;
0274 }
0275 
0276 void PlasmaSurfaceTest::testMovable()
0277 {
0278     // this test verifies that certain window types from PlasmaShellSurface are not moveable or resizable
0279     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0280     QVERIFY(surface != nullptr);
0281 
0282     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0283     QVERIFY(shellSurface != nullptr);
0284     // and a PlasmaShellSurface
0285     std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(Test::waylandPlasmaShell()->createSurface(surface.get()));
0286     QVERIFY(plasmaSurface != nullptr);
0287     QFETCH(KWayland::Client::PlasmaShellSurface::Role, role);
0288     plasmaSurface->setRole(role);
0289     // let's render
0290     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0291 
0292     QVERIFY(window);
0293     QTEST(window->isMovable(), "movable");
0294     QTEST(window->isMovableAcrossScreens(), "movableAcrossScreens");
0295     QTEST(window->isResizable(), "resizable");
0296     surface.reset();
0297     QVERIFY(Test::waitForWindowClosed(window));
0298 }
0299 
0300 WAYLANDTEST_MAIN(PlasmaSurfaceTest)
0301 #include "plasma_surface_test.moc"