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"