File indexing completed on 2024-05-05 17:35:57

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 "deleted.h"
0015 #include "screenedge.h"
0016 #include "wayland_server.h"
0017 #include "workspace.h"
0018 #include "x11window.h"
0019 #include <kwineffects.h>
0020 
0021 #include <netwm.h>
0022 #include <xcb/xcb_icccm.h>
0023 
0024 namespace KWin
0025 {
0026 
0027 static const QString s_socketName = QStringLiteral("wayland_test_kwin_screenedge_client_show-0");
0028 
0029 class ScreenEdgeClientShowTest : public QObject
0030 {
0031     Q_OBJECT
0032 private Q_SLOTS:
0033     void initTestCase();
0034     void init();
0035     void testScreenEdgeShowHideX11_data();
0036     void testScreenEdgeShowHideX11();
0037     void testScreenEdgeShowX11Touch_data();
0038     void testScreenEdgeShowX11Touch();
0039 };
0040 
0041 void ScreenEdgeClientShowTest::initTestCase()
0042 {
0043     qRegisterMetaType<KWin::Window *>();
0044     qRegisterMetaType<KWin::Deleted *>();
0045     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0046     QVERIFY(waylandServer()->init(s_socketName));
0047     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024)));
0048 
0049     // set custom config which disable touch edge
0050     KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0051     KConfigGroup group = config->group("TabBox");
0052     group.writeEntry(QStringLiteral("TouchBorderActivate"), "9");
0053     group.sync();
0054 
0055     kwinApp()->setConfig(config);
0056 
0057     kwinApp()->start();
0058     QVERIFY(applicationStartedSpy.wait());
0059     const auto outputs = workspace()->outputs();
0060     QCOMPARE(outputs.count(), 2);
0061     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0062     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0063     setenv("QT_QPA_PLATFORM", "wayland", true);
0064 }
0065 
0066 void ScreenEdgeClientShowTest::init()
0067 {
0068     workspace()->setActiveOutput(QPoint(640, 512));
0069     Cursors::self()->mouse()->setPos(QPoint(640, 512));
0070     QVERIFY(waylandServer()->windows().isEmpty());
0071 }
0072 
0073 struct XcbConnectionDeleter
0074 {
0075     void operator()(xcb_connection_t *pointer)
0076     {
0077         xcb_disconnect(pointer);
0078     }
0079 };
0080 
0081 void ScreenEdgeClientShowTest::testScreenEdgeShowHideX11_data()
0082 {
0083     QTest::addColumn<QRect>("windowGeometry");
0084     QTest::addColumn<QRect>("resizedWindowGeometry");
0085     QTest::addColumn<quint32>("location");
0086     QTest::addColumn<QPoint>("triggerPos");
0087 
0088     QTest::newRow("bottom/left") << QRect(50, 1004, 1180, 20) << QRect(150, 1004, 1000, 20) << 2u << QPoint(100, 1023);
0089     QTest::newRow("bottom/right") << QRect(1330, 1004, 1180, 20) << QRect(1410, 1004, 1000, 20) << 2u << QPoint(1400, 1023);
0090     QTest::newRow("top/left") << QRect(50, 0, 1180, 20) << QRect(150, 0, 1000, 20) << 0u << QPoint(100, 0);
0091     QTest::newRow("top/right") << QRect(1330, 0, 1180, 20) << QRect(1410, 0, 1000, 20) << 0u << QPoint(1400, 0);
0092     QTest::newRow("left") << QRect(0, 10, 20, 1000) << QRect(0, 70, 20, 800) << 3u << QPoint(0, 50);
0093     QTest::newRow("right") << QRect(2540, 10, 20, 1000) << QRect(2540, 70, 20, 800) << 1u << QPoint(2559, 60);
0094 }
0095 
0096 void ScreenEdgeClientShowTest::testScreenEdgeShowHideX11()
0097 {
0098     // this test creates a window which borders the screen and sets the screenedge show hint
0099     // that should trigger a show of the window whenever the cursor is pushed against the screen edge
0100 
0101     // create the test window
0102     std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr));
0103     QVERIFY(!xcb_connection_has_error(c.get()));
0104     // atom for the screenedge show hide functionality
0105     Xcb::Atom atom(QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW"), false, c.get());
0106 
0107     xcb_window_t windowId = xcb_generate_id(c.get());
0108     QFETCH(QRect, windowGeometry);
0109     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0110                       windowGeometry.x(),
0111                       windowGeometry.y(),
0112                       windowGeometry.width(),
0113                       windowGeometry.height(),
0114                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0115     xcb_size_hints_t hints;
0116     memset(&hints, 0, sizeof(hints));
0117     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0118     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0119     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0120     NETWinInfo info(c.get(), windowId, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties);
0121     info.setWindowType(NET::Dock);
0122     xcb_map_window(c.get(), windowId);
0123     xcb_flush(c.get());
0124 
0125     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0126     QVERIFY(windowCreatedSpy.wait());
0127     X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
0128     QVERIFY(window);
0129     QVERIFY(!window->isDecorated());
0130     QCOMPARE(window->frameGeometry(), windowGeometry);
0131     QVERIFY(!window->hasStrut());
0132     QVERIFY(!window->isHiddenInternal());
0133 
0134     QSignalSpy effectsWindowAdded(effects, &EffectsHandler::windowAdded);
0135     QVERIFY(effectsWindowAdded.wait());
0136 
0137     // now try to hide
0138     QFETCH(quint32, location);
0139     xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atom, XCB_ATOM_CARDINAL, 32, 1, &location);
0140     xcb_flush(c.get());
0141 
0142     QSignalSpy effectsWindowHiddenSpy(effects, &EffectsHandler::windowHidden);
0143     QSignalSpy clientHiddenSpy(window, &X11Window::windowHidden);
0144     QVERIFY(clientHiddenSpy.wait());
0145     QVERIFY(window->isHiddenInternal());
0146     QCOMPARE(effectsWindowHiddenSpy.count(), 1);
0147 
0148     // now trigger the edge
0149     QSignalSpy effectsWindowShownSpy(effects, &EffectsHandler::windowShown);
0150     QFETCH(QPoint, triggerPos);
0151     Cursors::self()->mouse()->setPos(triggerPos);
0152     QVERIFY(!window->isHiddenInternal());
0153     QCOMPARE(effectsWindowShownSpy.count(), 1);
0154 
0155     // go into event loop to trigger xcb_flush
0156     QTest::qWait(1);
0157 
0158     // hide window again
0159     Cursors::self()->mouse()->setPos(QPoint(640, 512));
0160     xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atom, XCB_ATOM_CARDINAL, 32, 1, &location);
0161     xcb_flush(c.get());
0162     QVERIFY(clientHiddenSpy.wait());
0163     QVERIFY(window->isHiddenInternal());
0164     QFETCH(QRect, resizedWindowGeometry);
0165     // resizewhile hidden
0166     window->moveResize(resizedWindowGeometry);
0167     // triggerPos shouldn't be valid anymore
0168     Cursors::self()->mouse()->setPos(triggerPos);
0169     QVERIFY(window->isHiddenInternal());
0170 
0171     // destroy window again
0172     QSignalSpy windowClosedSpy(window, &X11Window::windowClosed);
0173     xcb_unmap_window(c.get(), windowId);
0174     xcb_destroy_window(c.get(), windowId);
0175     xcb_flush(c.get());
0176     QVERIFY(windowClosedSpy.wait());
0177 }
0178 
0179 void ScreenEdgeClientShowTest::testScreenEdgeShowX11Touch_data()
0180 {
0181     QTest::addColumn<QRect>("windowGeometry");
0182     QTest::addColumn<quint32>("location");
0183     QTest::addColumn<QPoint>("touchDownPos");
0184     QTest::addColumn<QPoint>("targetPos");
0185 
0186     QTest::newRow("bottom/left") << QRect(50, 1004, 1180, 20) << 2u << QPoint(100, 1023) << QPoint(100, 540);
0187     QTest::newRow("bottom/right") << QRect(1330, 1004, 1180, 20) << 2u << QPoint(1400, 1023) << QPoint(1400, 520);
0188     QTest::newRow("top/left") << QRect(50, 0, 1180, 20) << 0u << QPoint(100, 0) << QPoint(100, 350);
0189     QTest::newRow("top/right") << QRect(1330, 0, 1180, 20) << 0u << QPoint(1400, 0) << QPoint(1400, 400);
0190     QTest::newRow("left") << QRect(0, 10, 20, 1000) << 3u << QPoint(0, 50) << QPoint(400, 50);
0191     QTest::newRow("right") << QRect(2540, 10, 20, 1000) << 1u << QPoint(2559, 60) << QPoint(2200, 60);
0192 }
0193 
0194 void ScreenEdgeClientShowTest::testScreenEdgeShowX11Touch()
0195 {
0196     // this test creates a window which borders the screen and sets the screenedge show hint
0197     // that should trigger a show of the window whenever the touch screen swipe gesture is triggered
0198 
0199     // create the test window
0200     std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr));
0201     QVERIFY(!xcb_connection_has_error(c.get()));
0202     // atom for the screenedge show hide functionality
0203     Xcb::Atom atom(QByteArrayLiteral("_KDE_NET_WM_SCREEN_EDGE_SHOW"), false, c.get());
0204 
0205     xcb_window_t windowId = xcb_generate_id(c.get());
0206     QFETCH(QRect, windowGeometry);
0207     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0208                       windowGeometry.x(),
0209                       windowGeometry.y(),
0210                       windowGeometry.width(),
0211                       windowGeometry.height(),
0212                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0213     xcb_size_hints_t hints;
0214     memset(&hints, 0, sizeof(hints));
0215     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0216     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0217     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0218     NETWinInfo info(c.get(), windowId, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties);
0219     info.setWindowType(NET::Dock);
0220     xcb_map_window(c.get(), windowId);
0221     xcb_flush(c.get());
0222 
0223     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0224     QVERIFY(windowCreatedSpy.wait());
0225     X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
0226     QVERIFY(window);
0227     QVERIFY(!window->isDecorated());
0228     QCOMPARE(window->frameGeometry(), windowGeometry);
0229     QVERIFY(!window->hasStrut());
0230     QVERIFY(!window->isHiddenInternal());
0231 
0232     QSignalSpy effectsWindowAdded(effects, &EffectsHandler::windowAdded);
0233     QVERIFY(effectsWindowAdded.wait());
0234 
0235     // now try to hide
0236     QFETCH(quint32, location);
0237     xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atom, XCB_ATOM_CARDINAL, 32, 1, &location);
0238     xcb_flush(c.get());
0239 
0240     QSignalSpy effectsWindowHiddenSpy(effects, &EffectsHandler::windowHidden);
0241     QSignalSpy clientHiddenSpy(window, &X11Window::windowHidden);
0242     QVERIFY(clientHiddenSpy.wait());
0243     QVERIFY(window->isHiddenInternal());
0244     QCOMPARE(effectsWindowHiddenSpy.count(), 1);
0245 
0246     // now trigger the edge
0247     QSignalSpy effectsWindowShownSpy(effects, &EffectsHandler::windowShown);
0248     quint32 timestamp = 0;
0249     QFETCH(QPoint, touchDownPos);
0250     QFETCH(QPoint, targetPos);
0251     Test::touchDown(0, touchDownPos, timestamp++);
0252     Test::touchMotion(0, targetPos, timestamp++);
0253     Test::touchUp(0, timestamp++);
0254     QVERIFY(effectsWindowShownSpy.wait());
0255     QVERIFY(!window->isHiddenInternal());
0256     QCOMPARE(effectsWindowShownSpy.count(), 1);
0257 
0258     // destroy window again
0259     QSignalSpy windowClosedSpy(window, &X11Window::windowClosed);
0260     xcb_unmap_window(c.get(), windowId);
0261     xcb_destroy_window(c.get(), windowId);
0262     xcb_flush(c.get());
0263     QVERIFY(windowClosedSpy.wait());
0264 }
0265 
0266 }
0267 
0268 WAYLANDTEST_MAIN(KWin::ScreenEdgeClientShowTest)
0269 #include "screenedge_client_show_test.moc"