File indexing completed on 2024-05-12 05:30:32

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 "wayland_server.h"
0014 #include "workspace.h"
0015 #include "x11window.h"
0016 
0017 #include <KDecoration2/Decoration>
0018 
0019 #include <QQuickItem>
0020 
0021 #include <linux/input.h>
0022 
0023 namespace KWin
0024 {
0025 
0026 static const QString s_socketName = QStringLiteral("wayland_test_kwin_dont_crash_aurorae_destroy_deco-0");
0027 
0028 class DontCrashAuroraeDestroyDecoTest : public QObject
0029 {
0030     Q_OBJECT
0031 private Q_SLOTS:
0032     void initTestCase();
0033     void init();
0034     void testBorderlessMaximizedWindows();
0035 };
0036 
0037 void DontCrashAuroraeDestroyDecoTest::initTestCase()
0038 {
0039     if (!Test::renderNodeAvailable()) {
0040         QSKIP("no render node available");
0041         return;
0042     }
0043     qputenv("XDG_DATA_DIRS", QCoreApplication::applicationDirPath().toUtf8());
0044     qRegisterMetaType<KWin::Window *>();
0045     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0046     QVERIFY(waylandServer()->init(s_socketName));
0047     Test::setOutputConfig({
0048         QRect(0, 0, 1280, 1024),
0049         QRect(1280, 0, 1280, 1024),
0050     });
0051 
0052     KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0053     config->group(QStringLiteral("org.kde.kdecoration2")).writeEntry("library", "org.kde.kwin.aurorae");
0054     config->sync();
0055     kwinApp()->setConfig(config);
0056 
0057     // this test needs to enforce OpenGL compositing to get into the crashy condition
0058     qputenv("KWIN_COMPOSE", QByteArrayLiteral("O2"));
0059     kwinApp()->start();
0060     QVERIFY(applicationStartedSpy.wait());
0061     const auto outputs = workspace()->outputs();
0062     QCOMPARE(outputs.count(), 2);
0063     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0064     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0065     setenv("QT_QPA_PLATFORM", "wayland", true);
0066 }
0067 
0068 void DontCrashAuroraeDestroyDecoTest::init()
0069 {
0070     workspace()->setActiveOutput(QPoint(640, 512));
0071     input()->pointer()->warp(QPoint(640, 512));
0072 }
0073 
0074 void DontCrashAuroraeDestroyDecoTest::testBorderlessMaximizedWindows()
0075 {
0076     // this test verifies that Aurorae doesn't crash when clicking the maximize button
0077     // with kwin config option BorderlessMaximizedWindows
0078     // see BUG 362772
0079 
0080     // first adjust the config
0081     KConfigGroup group = kwinApp()->config()->group(QStringLiteral("Windows"));
0082     group.writeEntry("BorderlessMaximizedWindows", true);
0083     group.sync();
0084     workspace()->slotReconfigure();
0085     QCOMPARE(options->borderlessMaximizedWindows(), true);
0086 
0087     // create an xcb window
0088     Test::XcbConnectionPtr connection = Test::createX11Connection();
0089     auto c = connection.get();
0090     QVERIFY(!xcb_connection_has_error(c));
0091 
0092     xcb_window_t windowId = xcb_generate_id(c);
0093     xcb_create_window(c, XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0, 0, 100, 200, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0094     xcb_map_window(c, windowId);
0095     xcb_flush(c);
0096 
0097     // we should get a window for it
0098     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0099     QVERIFY(windowCreatedSpy.wait());
0100     X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
0101     QVERIFY(window);
0102     QCOMPARE(window->window(), windowId);
0103     QVERIFY(window->isDecorated());
0104     QCOMPARE(window->maximizeMode(), MaximizeRestore);
0105     QCOMPARE(window->noBorder(), false);
0106     // verify that the deco is Aurorae
0107     QCOMPARE(qstrcmp(window->decoration()->metaObject()->className(), "Aurorae::Decoration"), 0);
0108     // find the maximize button
0109     QQuickItem *item = window->decoration()->property("item").value<QQuickItem *>()->findChild<QQuickItem *>("maximizeButton");
0110     QVERIFY(item);
0111     const QPointF scenePoint = item->mapToScene(QPoint(0, 0));
0112 
0113     // mark the window as ready for painting, otherwise it doesn't get input events
0114     QMetaObject::invokeMethod(window, "setReadyForPainting");
0115     QVERIFY(window->readyForPainting());
0116 
0117     // simulate click on maximize button
0118     QSignalSpy maximizedStateChangedSpy(window, &Window::maximizedChanged);
0119     quint32 timestamp = 1;
0120     Test::pointerMotion(window->frameGeometry().topLeft() + scenePoint.toPoint(), timestamp++);
0121     Test::pointerButtonPressed(BTN_LEFT, timestamp++);
0122     Test::pointerButtonReleased(BTN_LEFT, timestamp++);
0123     QVERIFY(maximizedStateChangedSpy.wait());
0124     QCOMPARE(window->maximizeMode(), MaximizeFull);
0125     QCOMPARE(window->noBorder(), true);
0126 
0127     // and destroy the window again
0128     xcb_unmap_window(c, windowId);
0129     xcb_destroy_window(c, windowId);
0130     xcb_flush(c);
0131 
0132     QSignalSpy windowClosedSpy(window, &X11Window::closed);
0133     QVERIFY(windowClosedSpy.wait());
0134 }
0135 
0136 }
0137 
0138 WAYLANDTEST_MAIN(KWin::DontCrashAuroraeDestroyDecoTest)
0139 #include "dont_crash_aurorae_destroy_deco.moc"