Warning, file /plasma/kwin/autotests/integration/screenedges_test.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0006     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "kwin_wayland_test.h"
0012 
0013 #include "core/outputbackend.h"
0014 #include "cursor.h"
0015 #include "effectloader.h"
0016 #include "main.h"
0017 #include "screenedge.h"
0018 #include "wayland_server.h"
0019 #include "window.h"
0020 #include "workspace.h"
0021 
0022 #include <KConfigGroup>
0023 #include <KWayland/Client/surface.h>
0024 
0025 #include <QAction>
0026 
0027 Q_DECLARE_METATYPE(KWin::ElectricBorder)
0028 
0029 namespace KWin
0030 {
0031 
0032 static const QString s_socketName = QStringLiteral("wayland_test_kwin_screen-edges-0");
0033 
0034 class TestObject : public QObject
0035 {
0036     Q_OBJECT
0037 
0038 public Q_SLOTS:
0039     bool callback(ElectricBorder border)
0040     {
0041         Q_EMIT gotCallback(border);
0042         return true;
0043     }
0044 
0045 Q_SIGNALS:
0046     void gotCallback(KWin::ElectricBorder);
0047 };
0048 
0049 class ScreenEdgesTest : public QObject
0050 {
0051     Q_OBJECT
0052 
0053 private Q_SLOTS:
0054     void initTestCase();
0055     void init();
0056     void cleanup();
0057     void testTouchCallback_data();
0058     void testTouchCallback();
0059     void testPushBack_data();
0060     void testPushBack();
0061     void testClientEdge_data();
0062     void testClientEdge();
0063     void testObjectEdge_data();
0064     void testObjectEdge();
0065 };
0066 
0067 void ScreenEdgesTest::initTestCase()
0068 {
0069     qRegisterMetaType<KWin::Window *>();
0070     qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
0071 
0072     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0073     QVERIFY(waylandServer()->init(s_socketName));
0074     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024)));
0075 
0076     // Disable effects, in particular present windows, which reserves a screen edge.
0077     auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0078     KConfigGroup plugins(config, QStringLiteral("Plugins"));
0079     const auto builtinNames = EffectLoader().listOfKnownEffects();
0080     for (const QString &name : builtinNames) {
0081         plugins.writeEntry(name + QStringLiteral("Enabled"), false);
0082     }
0083 
0084     config->sync();
0085     kwinApp()->setConfig(config);
0086 
0087     kwinApp()->start();
0088     QVERIFY(applicationStartedSpy.wait());
0089 }
0090 
0091 void ScreenEdgesTest::init()
0092 {
0093     workspace()->screenEdges()->recreateEdges();
0094     Workspace::self()->setActiveOutput(QPoint(640, 512));
0095     KWin::Cursors::self()->mouse()->setPos(QPoint(640, 512));
0096 
0097     QVERIFY(Test::setupWaylandConnection());
0098 }
0099 
0100 void ScreenEdgesTest::cleanup()
0101 {
0102     Test::destroyWaylandConnection();
0103 }
0104 
0105 void ScreenEdgesTest::testTouchCallback_data()
0106 {
0107     QTest::addColumn<KWin::ElectricBorder>("border");
0108     QTest::addColumn<QPointF>("startPos");
0109     QTest::addColumn<QPointF>("delta");
0110 
0111     QTest::newRow("left") << ElectricLeft << QPointF(0, 50) << QPointF(256, 20);
0112     QTest::newRow("top") << ElectricTop << QPointF(50, 0) << QPointF(20, 250);
0113     QTest::newRow("right") << ElectricRight << QPointF(1279, 50) << QPointF(-256, 0);
0114     QTest::newRow("bottom") << ElectricBottom << QPointF(50, 1023) << QPointF(0, -205);
0115 }
0116 
0117 void ScreenEdgesTest::testTouchCallback()
0118 {
0119     // This test verifies that touch screen edges trigger associated callbacks.
0120 
0121     auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0122     auto group = config->group("TouchEdges");
0123     group.writeEntry("Top", "none");
0124     group.writeEntry("Left", "none");
0125     group.writeEntry("Bottom", "none");
0126     group.writeEntry("Right", "none");
0127     config->sync();
0128 
0129     auto s = workspace()->screenEdges();
0130     s->setConfig(config);
0131     s->reconfigure();
0132 
0133     // none of our actions should be reserved
0134     const auto &edges = s->edges();
0135     QCOMPARE(edges.size(), 8);
0136     for (auto &edge : edges) {
0137         QCOMPARE(edge->isReserved(), false);
0138         QCOMPARE(edge->activatesForPointer(), false);
0139         QCOMPARE(edge->activatesForTouchGesture(), false);
0140     }
0141 
0142     // let's reserve an action
0143     QAction action;
0144     QSignalSpy actionTriggeredSpy(&action, &QAction::triggered);
0145 
0146     // reserve on edge
0147     QFETCH(KWin::ElectricBorder, border);
0148     s->reserveTouch(border, &action);
0149     for (auto &edge : edges) {
0150         QCOMPARE(edge->isReserved(), edge->border() == border);
0151         QCOMPARE(edge->activatesForPointer(), false);
0152         QCOMPARE(edge->activatesForTouchGesture(), edge->border() == border);
0153     }
0154 
0155     quint32 timestamp = 0;
0156 
0157     // press the finger
0158     QFETCH(QPointF, startPos);
0159     Test::touchDown(1, startPos, timestamp++);
0160     QVERIFY(actionTriggeredSpy.isEmpty());
0161 
0162     // move the finger
0163     QFETCH(QPointF, delta);
0164     Test::touchMotion(1, startPos + delta, timestamp++);
0165     QVERIFY(actionTriggeredSpy.isEmpty());
0166 
0167     // release the finger
0168     Test::touchUp(1, timestamp++);
0169     QVERIFY(actionTriggeredSpy.wait());
0170     QCOMPARE(actionTriggeredSpy.count(), 1);
0171 
0172     // unreserve again
0173     s->unreserveTouch(border, &action);
0174     for (auto &edge : edges) {
0175         QCOMPARE(edge->isReserved(), false);
0176         QCOMPARE(edge->activatesForPointer(), false);
0177         QCOMPARE(edge->activatesForTouchGesture(), false);
0178     }
0179 
0180     // reserve another action
0181     std::unique_ptr<QAction> action2(new QAction);
0182     s->reserveTouch(border, action2.get());
0183     for (auto &edge : edges) {
0184         QCOMPARE(edge->isReserved(), edge->border() == border);
0185         QCOMPARE(edge->activatesForPointer(), false);
0186         QCOMPARE(edge->activatesForTouchGesture(), edge->border() == border);
0187     }
0188 
0189     // and unreserve by destroying
0190     action2.reset();
0191     for (auto &edge : edges) {
0192         QCOMPARE(edge->isReserved(), false);
0193         QCOMPARE(edge->activatesForPointer(), false);
0194         QCOMPARE(edge->activatesForTouchGesture(), false);
0195     }
0196 }
0197 
0198 void ScreenEdgesTest::testPushBack_data()
0199 {
0200     QTest::addColumn<KWin::ElectricBorder>("border");
0201     QTest::addColumn<int>("pushback");
0202     QTest::addColumn<QPoint>("trigger");
0203     QTest::addColumn<QPoint>("expected");
0204 
0205     QTest::newRow("top-left-3") << ElectricTopLeft << 3 << QPoint(0, 0) << QPoint(3, 3);
0206     QTest::newRow("top-5") << ElectricTop << 5 << QPoint(50, 0) << QPoint(50, 5);
0207     QTest::newRow("top-right-2") << ElectricTopRight << 2 << QPoint(1279, 0) << QPoint(1277, 2);
0208     QTest::newRow("right-10") << ElectricRight << 10 << QPoint(1279, 50) << QPoint(1269, 50);
0209     QTest::newRow("bottom-right-5") << ElectricBottomRight << 5 << QPoint(1279, 1023) << QPoint(1274, 1018);
0210     QTest::newRow("bottom-10") << ElectricBottom << 10 << QPoint(50, 1023) << QPoint(50, 1013);
0211     QTest::newRow("bottom-left-3") << ElectricBottomLeft << 3 << QPoint(0, 1023) << QPoint(3, 1020);
0212     QTest::newRow("left-10") << ElectricLeft << 10 << QPoint(0, 50) << QPoint(10, 50);
0213     QTest::newRow("invalid") << ElectricLeft << 10 << QPoint(50, 0) << QPoint(50, 0);
0214 }
0215 
0216 void ScreenEdgesTest::testPushBack()
0217 {
0218     // This test verifies that the pointer will be pushed back if it approached a screen edge.
0219 
0220     QFETCH(int, pushback);
0221     auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0222     config->group("Windows").writeEntry("ElectricBorderPushbackPixels", pushback);
0223     config->sync();
0224 
0225     auto s = workspace()->screenEdges();
0226     s->setConfig(config);
0227     s->reconfigure();
0228 
0229     TestObject callback;
0230     QSignalSpy spy(&callback, &TestObject::gotCallback);
0231 
0232     QFETCH(ElectricBorder, border);
0233     s->reserve(border, &callback, "callback");
0234 
0235     QFETCH(QPoint, trigger);
0236     Test::pointerMotion(trigger, 0);
0237     QVERIFY(spy.isEmpty());
0238     QTEST(Cursors::self()->mouse()->pos(), "expected");
0239 }
0240 
0241 void ScreenEdgesTest::testClientEdge_data()
0242 {
0243     QTest::addColumn<ElectricBorder>("border");
0244     QTest::addColumn<QRect>("geometry");
0245     QTest::addColumn<QPointF>("triggerPoint");
0246 
0247     QTest::newRow("top") << ElectricTop << QRect(540, 0, 200, 5) << QPointF(640, 0);
0248     QTest::newRow("right") << ElectricRight << QRect(1275, 412, 5, 200) << QPointF(1279, 512);
0249     QTest::newRow("bottom") << ElectricBottom << QRect(540, 1019, 200, 5) << QPointF(640, 1023);
0250     QTest::newRow("left") << ElectricLeft << QRect(0, 412, 5, 200) << QPointF(0, 512);
0251 }
0252 
0253 void ScreenEdgesTest::testClientEdge()
0254 {
0255     // This test verifies that a window will be shown when its screen edge is activated.
0256     QFETCH(QRect, geometry);
0257 
0258     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0259     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0260     Window *window = Test::renderAndWaitForShown(surface.get(), geometry.size(), Qt::red);
0261     QVERIFY(window);
0262     QVERIFY(window->isActive());
0263     window->move(geometry.topLeft());
0264 
0265     // Reserve an electric border.
0266     QFETCH(ElectricBorder, border);
0267     workspace()->screenEdges()->reserve(window, border);
0268 
0269     // Hide the window.
0270     window->hideClient();
0271     QVERIFY(window->isHiddenInternal());
0272 
0273     // Trigger the screen edge.
0274     QFETCH(QPointF, triggerPoint);
0275     quint32 timestamp = 0;
0276     Test::pointerMotion(triggerPoint, timestamp);
0277     QVERIFY(window->isHiddenInternal());
0278 
0279     timestamp += 150 + 1;
0280     Test::pointerMotion(triggerPoint, timestamp);
0281     QTRY_VERIFY(!window->isHiddenInternal());
0282 }
0283 
0284 void ScreenEdgesTest::testObjectEdge_data()
0285 {
0286     QTest::addColumn<ElectricBorder>("border");
0287     QTest::addColumn<QPointF>("triggerPoint");
0288     QTest::addColumn<QPointF>("delta");
0289 
0290     QTest::newRow("top") << ElectricTop << QPointF(640, 0) << QPointF(0, 50);
0291     QTest::newRow("right") << ElectricRight << QPointF(1279, 512) << QPointF(-50, 0);
0292     QTest::newRow("bottom") << ElectricBottom << QPointF(640, 1023) << QPointF(0, -50);
0293     QTest::newRow("left") << ElectricLeft << QPointF(0, 512) << QPointF(50, 0);
0294 }
0295 
0296 void ScreenEdgesTest::testObjectEdge()
0297 {
0298     // This test verifies that a screen edge reserved by a script or any QObject is activated.
0299 
0300     TestObject callback;
0301     QSignalSpy spy(&callback, &TestObject::gotCallback);
0302 
0303     // Reserve a screen edge border.
0304     QFETCH(ElectricBorder, border);
0305     workspace()->screenEdges()->reserve(border, &callback, "callback");
0306 
0307     QFETCH(QPointF, triggerPoint);
0308     QFETCH(QPointF, delta);
0309 
0310     // doesn't trigger as the edge was not triggered yet
0311     qint64 timestamp = 0;
0312     Test::pointerMotion(triggerPoint + delta, timestamp);
0313     QVERIFY(spy.isEmpty());
0314 
0315     // test doesn't trigger due to too much offset
0316     timestamp += 160;
0317     Test::pointerMotion(triggerPoint, timestamp);
0318     QVERIFY(spy.isEmpty());
0319 
0320     // doesn't activate as we are waiting too short
0321     timestamp += 50;
0322     Test::pointerMotion(triggerPoint, timestamp);
0323     QVERIFY(spy.isEmpty());
0324 
0325     // and this one triggers
0326     timestamp += 110;
0327     Test::pointerMotion(triggerPoint, timestamp);
0328     QVERIFY(!spy.isEmpty());
0329 
0330     // now let's try to trigger again
0331     timestamp += 351;
0332     Test::pointerMotion(triggerPoint, timestamp);
0333     QCOMPARE(spy.count(), 1);
0334 
0335     // it's still under the reactivation
0336     timestamp += 50;
0337     Test::pointerMotion(triggerPoint, timestamp);
0338     QCOMPARE(spy.count(), 1);
0339 
0340     // now it should trigger again
0341     timestamp += 250;
0342     Test::pointerMotion(triggerPoint, timestamp);
0343     QCOMPARE(spy.count(), 2);
0344 }
0345 
0346 } // namespace KWin
0347 
0348 WAYLANDTEST_MAIN(KWin::ScreenEdgesTest)
0349 #include "screenedges_test.moc"