File indexing completed on 2025-03-23 13:48:12
0001 /* 0002 SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kwin_wayland_test.h" 0008 0009 #include "composite.h" 0010 #include "core/output.h" 0011 #include "core/outputbackend.h" 0012 #include "main.h" 0013 #include "scene/workspacescene.h" 0014 #include "unmanaged.h" 0015 #include "wayland_server.h" 0016 #include "workspace.h" 0017 #include "x11window.h" 0018 #include "xwayland/xwayland.h" 0019 #include "xwayland/xwaylandlauncher.h" 0020 0021 #include <xcb/xcb_icccm.h> 0022 0023 namespace KWin 0024 { 0025 0026 struct XcbConnectionDeleter 0027 { 0028 void operator()(xcb_connection_t *pointer) 0029 { 0030 xcb_disconnect(pointer); 0031 } 0032 }; 0033 0034 static const QString s_socketName = QStringLiteral("wayland_test_kwin_xwayland_server_crash-0"); 0035 0036 class XwaylandServerCrashTest : public QObject 0037 { 0038 Q_OBJECT 0039 0040 private Q_SLOTS: 0041 void initTestCase(); 0042 void testCrash(); 0043 }; 0044 0045 void XwaylandServerCrashTest::initTestCase() 0046 { 0047 qRegisterMetaType<Unmanaged *>(); 0048 qRegisterMetaType<X11Window *>(); 0049 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); 0050 QVERIFY(waylandServer()->init(s_socketName)); 0051 QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024))); 0052 0053 KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); 0054 KConfigGroup xwaylandGroup = config->group("Xwayland"); 0055 xwaylandGroup.writeEntry(QStringLiteral("XwaylandCrashPolicy"), QStringLiteral("Stop")); 0056 xwaylandGroup.sync(); 0057 kwinApp()->setConfig(config); 0058 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 } 0066 0067 void XwaylandServerCrashTest::testCrash() 0068 { 0069 // This test verifies that all connected X11 clients get destroyed when Xwayland crashes. 0070 0071 // Create a normal window. 0072 std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr)); 0073 QVERIFY(!xcb_connection_has_error(c.get())); 0074 const QRect windowGeometry(0, 0, 100, 200); 0075 xcb_window_t windowId1 = xcb_generate_id(c.get()); 0076 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId1, rootWindow(), 0077 windowGeometry.x(), 0078 windowGeometry.y(), 0079 windowGeometry.width(), 0080 windowGeometry.height(), 0081 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0082 xcb_size_hints_t hints; 0083 memset(&hints, 0, sizeof(hints)); 0084 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0085 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0086 xcb_icccm_size_hints_set_min_size(&hints, windowGeometry.width(), windowGeometry.height()); 0087 xcb_icccm_set_wm_normal_hints(c.get(), windowId1, &hints); 0088 xcb_map_window(c.get(), windowId1); 0089 xcb_flush(c.get()); 0090 0091 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0092 QVERIFY(windowCreatedSpy.wait()); 0093 QPointer<X11Window> window = windowCreatedSpy.last().first().value<X11Window *>(); 0094 QVERIFY(window); 0095 QVERIFY(window->isDecorated()); 0096 0097 // Create an override-redirect window. 0098 xcb_window_t windowId2 = xcb_generate_id(c.get()); 0099 const uint32_t values[] = {true}; 0100 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId2, rootWindow(), 0101 windowGeometry.x(), windowGeometry.y(), 0102 windowGeometry.width(), windowGeometry.height(), 0, 0103 XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0104 XCB_CW_OVERRIDE_REDIRECT, values); 0105 xcb_map_window(c.get(), windowId2); 0106 xcb_flush(c.get()); 0107 0108 QSignalSpy unmanagedAddedSpy(workspace(), &Workspace::unmanagedAdded); 0109 QVERIFY(unmanagedAddedSpy.wait()); 0110 QPointer<Unmanaged> unmanaged = unmanagedAddedSpy.last().first().value<Unmanaged *>(); 0111 QVERIFY(unmanaged); 0112 0113 // Let's pretend that the Xwayland process has crashed. 0114 QSignalSpy x11ConnectionChangedSpy(kwinApp(), &Application::x11ConnectionChanged); 0115 Xwl::Xwayland *xwayland = static_cast<Xwl::Xwayland *>(kwinApp()->xwayland()); 0116 xwayland->xwaylandLauncher()->process()->terminate(); 0117 QVERIFY(x11ConnectionChangedSpy.wait()); 0118 0119 // When Xwayland crashes, the compositor should tear down the XCB connection and destroy 0120 // all connected X11 clients. 0121 QTRY_VERIFY(!window); 0122 QTRY_VERIFY(!unmanaged); 0123 QCOMPARE(kwinApp()->x11Connection(), nullptr); 0124 QCOMPARE(kwinApp()->x11RootWindow(), XCB_WINDOW_NONE); 0125 0126 // Render a frame to ensure that the compositor doesn't crash. 0127 Compositor::self()->scene()->addRepaintFull(); 0128 QSignalSpy frameRenderedSpy(Compositor::self()->scene(), &WorkspaceScene::frameRendered); 0129 QVERIFY(frameRenderedSpy.wait()); 0130 } 0131 0132 } // namespace KWin 0133 0134 WAYLANDTEST_MAIN(KWin::XwaylandServerCrashTest) 0135 #include "xwaylandserver_crash_test.moc"