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

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