File indexing completed on 2024-11-10 04:56:09
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 "atoms.h" 0012 #include "core/output.h" 0013 #include "pointer_input.h" 0014 #include "rules.h" 0015 #include "wayland_server.h" 0016 #include "workspace.h" 0017 #include "x11window.h" 0018 0019 #include <netwm.h> 0020 #include <xcb/xcb_icccm.h> 0021 0022 namespace KWin 0023 { 0024 0025 static const QString s_socketName = QStringLiteral("wayland_test_kwin_window_rules-0"); 0026 0027 class WindowRuleTest : public QObject 0028 { 0029 Q_OBJECT 0030 private Q_SLOTS: 0031 void initTestCase(); 0032 void init(); 0033 void cleanup(); 0034 void testApplyInitialMaximizeVert(); 0035 void testWindowClassChange(); 0036 }; 0037 0038 void WindowRuleTest::initTestCase() 0039 { 0040 qRegisterMetaType<KWin::Window *>(); 0041 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); 0042 QVERIFY(waylandServer()->init(s_socketName)); 0043 Test::setOutputConfig({ 0044 QRect(0, 0, 1280, 1024), 0045 QRect(1280, 0, 1280, 1024), 0046 }); 0047 0048 kwinApp()->start(); 0049 QVERIFY(applicationStartedSpy.wait()); 0050 const auto outputs = workspace()->outputs(); 0051 QCOMPARE(outputs.count(), 2); 0052 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024)); 0053 QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024)); 0054 setenv("QT_QPA_PLATFORM", "wayland", true); 0055 } 0056 0057 void WindowRuleTest::init() 0058 { 0059 workspace()->setActiveOutput(QPoint(640, 512)); 0060 input()->pointer()->warp(QPoint(640, 512)); 0061 QVERIFY(waylandServer()->windows().isEmpty()); 0062 } 0063 0064 void WindowRuleTest::cleanup() 0065 { 0066 // discards old rules 0067 workspace()->rulebook()->load(); 0068 } 0069 0070 struct XcbConnectionDeleter 0071 { 0072 void operator()(xcb_connection_t *pointer) 0073 { 0074 xcb_disconnect(pointer); 0075 } 0076 }; 0077 0078 void WindowRuleTest::testApplyInitialMaximizeVert() 0079 { 0080 // this test creates the situation of BUG 367554: creates a window and initial apply maximize vertical 0081 // the window is matched by class and role 0082 // load the rule 0083 workspace()->rulebook()->setConfig(KSharedConfig::openConfig(QFINDTESTDATA("./data/rules/maximize-vert-apply-initial"), KConfig::SimpleConfig)); 0084 workspace()->slotReconfigure(); 0085 0086 // create the test window 0087 Test::XcbConnectionPtr c = Test::createX11Connection(); 0088 QVERIFY(!xcb_connection_has_error(c.get())); 0089 0090 xcb_window_t windowId = xcb_generate_id(c.get()); 0091 const QRect windowGeometry = QRect(0, 0, 10, 20); 0092 const uint32_t values[] = { 0093 XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW}; 0094 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0095 windowGeometry.x(), 0096 windowGeometry.y(), 0097 windowGeometry.width(), 0098 windowGeometry.height(), 0099 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values); 0100 xcb_size_hints_t hints; 0101 memset(&hints, 0, sizeof(hints)); 0102 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0103 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0104 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0105 xcb_icccm_set_wm_class(c.get(), windowId, 9, "kpat\0kpat"); 0106 0107 const QByteArray role = QByteArrayLiteral("mainwindow"); 0108 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->wm_window_role, XCB_ATOM_STRING, 8, role.length(), role.constData()); 0109 0110 NETWinInfo info(c.get(), windowId, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties); 0111 info.setWindowType(NET::Normal); 0112 xcb_map_window(c.get(), windowId); 0113 xcb_flush(c.get()); 0114 0115 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0116 QVERIFY(windowCreatedSpy.wait()); 0117 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>(); 0118 QVERIFY(window); 0119 QVERIFY(window->isDecorated()); 0120 QVERIFY(!window->hasStrut()); 0121 QVERIFY(!window->readyForPainting()); 0122 QMetaObject::invokeMethod(window, "setReadyForPainting"); 0123 QVERIFY(window->readyForPainting()); 0124 QVERIFY(Test::waitForWaylandSurface(window)); 0125 QCOMPARE(window->maximizeMode(), MaximizeVertical); 0126 0127 // destroy window again 0128 QSignalSpy windowClosedSpy(window, &X11Window::closed); 0129 xcb_unmap_window(c.get(), windowId); 0130 xcb_destroy_window(c.get(), windowId); 0131 xcb_flush(c.get()); 0132 QVERIFY(windowClosedSpy.wait()); 0133 } 0134 0135 void WindowRuleTest::testWindowClassChange() 0136 { 0137 KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); 0138 config->group(QStringLiteral("General")).writeEntry("count", 1); 0139 0140 auto group = config->group(QStringLiteral("1")); 0141 group.writeEntry("above", true); 0142 group.writeEntry("aboverule", 2); 0143 group.writeEntry("wmclass", "org.kde.foo"); 0144 group.writeEntry("wmclasscomplete", false); 0145 group.writeEntry("wmclassmatch", 1); 0146 group.sync(); 0147 0148 workspace()->rulebook()->setConfig(config); 0149 workspace()->slotReconfigure(); 0150 0151 // create the test window 0152 Test::XcbConnectionPtr c = Test::createX11Connection(); 0153 QVERIFY(!xcb_connection_has_error(c.get())); 0154 0155 xcb_window_t windowId = xcb_generate_id(c.get()); 0156 const QRect windowGeometry = QRect(0, 0, 10, 20); 0157 const uint32_t values[] = { 0158 XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW}; 0159 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0160 windowGeometry.x(), 0161 windowGeometry.y(), 0162 windowGeometry.width(), 0163 windowGeometry.height(), 0164 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values); 0165 xcb_size_hints_t hints; 0166 memset(&hints, 0, sizeof(hints)); 0167 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y()); 0168 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height()); 0169 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0170 xcb_icccm_set_wm_class(c.get(), windowId, 23, "org.kde.bar\0org.kde.bar"); 0171 0172 NETWinInfo info(c.get(), windowId, rootWindow(), NET::WMAllProperties, NET::WM2AllProperties); 0173 info.setWindowType(NET::Normal); 0174 xcb_map_window(c.get(), windowId); 0175 xcb_flush(c.get()); 0176 0177 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0178 QVERIFY(windowCreatedSpy.wait()); 0179 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>(); 0180 QVERIFY(window); 0181 QVERIFY(window->isDecorated()); 0182 QVERIFY(!window->hasStrut()); 0183 QVERIFY(!window->readyForPainting()); 0184 QMetaObject::invokeMethod(window, "setReadyForPainting"); 0185 QVERIFY(window->readyForPainting()); 0186 QVERIFY(Test::waitForWaylandSurface(window)); 0187 QCOMPARE(window->keepAbove(), false); 0188 0189 // now change class 0190 QSignalSpy windowClassChangedSpy{window, &X11Window::windowClassChanged}; 0191 xcb_icccm_set_wm_class(c.get(), windowId, 23, "org.kde.foo\0org.kde.foo"); 0192 xcb_flush(c.get()); 0193 QVERIFY(windowClassChangedSpy.wait()); 0194 QCOMPARE(window->keepAbove(), true); 0195 0196 // destroy window 0197 QSignalSpy windowClosedSpy(window, &X11Window::closed); 0198 xcb_unmap_window(c.get(), windowId); 0199 xcb_destroy_window(c.get(), windowId); 0200 xcb_flush(c.get()); 0201 QVERIFY(windowClosedSpy.wait()); 0202 } 0203 0204 } 0205 0206 WAYLANDTEST_MAIN(KWin::WindowRuleTest) 0207 #include "window_rules_test.moc"