File indexing completed on 2024-09-08 03:42:17
0001 /* 0002 SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "kwindowinfo.h" 0008 #include "kwindowsystem.h" 0009 #include "kx11extras.h" 0010 #include "nettesthelper.h" 0011 #include "netwm.h" 0012 0013 #include <QSignalSpy> 0014 #include <QWidget> 0015 #include <private/qtx11extras_p.h> 0016 0017 #include <qtest_widgets.h> 0018 Q_DECLARE_METATYPE(WId) 0019 Q_DECLARE_METATYPE(NET::Properties) 0020 Q_DECLARE_METATYPE(NET::Properties2) 0021 Q_DECLARE_METATYPE(const unsigned long *) 0022 0023 class KWindowSystemX11Test : public QObject 0024 { 0025 Q_OBJECT 0026 private Q_SLOTS: 0027 void initTestCase(); 0028 // needs to be first test, would fail if run after others (X11) 0029 void testActiveWindowChanged(); 0030 void testWindowAdded(); 0031 void testWindowRemoved(); 0032 void testDesktopChanged(); 0033 void testNumberOfDesktopsChanged(); 0034 void testDesktopNamesChanged(); 0035 void testShowingDesktopChanged(); 0036 void testSetShowingDesktop(); 0037 void testWorkAreaChanged(); 0038 void testWindowTitleChanged(); 0039 void testMinimizeWindow(); 0040 void testPlatformX11(); 0041 }; 0042 0043 void KWindowSystemX11Test::initTestCase() 0044 { 0045 QCoreApplication::setAttribute(Qt::AA_ForceRasterWidgets); 0046 } 0047 0048 void KWindowSystemX11Test::testActiveWindowChanged() 0049 { 0050 qRegisterMetaType<WId>("WId"); 0051 QSignalSpy spy(KX11Extras::self(), &KX11Extras::activeWindowChanged); 0052 0053 std::unique_ptr<QWidget> widget(new QWidget); 0054 widget->show(); 0055 0056 QVERIFY(spy.wait()); 0057 QCOMPARE(spy.count(), 1); 0058 QCOMPARE(spy.first().at(0).toULongLong(), widget->winId()); 0059 QCOMPARE(KX11Extras::activeWindow(), widget->winId()); 0060 } 0061 0062 void KWindowSystemX11Test::testWindowAdded() 0063 { 0064 qRegisterMetaType<WId>("WId"); 0065 QSignalSpy spy(KX11Extras::self(), &KX11Extras::windowAdded); 0066 QSignalSpy stackingOrderSpy(KX11Extras::self(), &KX11Extras::stackingOrderChanged); 0067 std::unique_ptr<QWidget> widget(new QWidget); 0068 widget->show(); 0069 QVERIFY(QTest::qWaitForWindowExposed(widget.get())); 0070 QVERIFY(spy.count() > 0); 0071 bool hasWId = false; 0072 for (auto it = spy.constBegin(); it != spy.constEnd(); ++it) { 0073 if ((*it).isEmpty()) { 0074 continue; 0075 } 0076 QCOMPARE((*it).count(), 1); 0077 hasWId = (*it).at(0).toULongLong() == widget->winId(); 0078 if (hasWId) { 0079 break; 0080 } 0081 } 0082 QVERIFY(hasWId); 0083 QVERIFY(KX11Extras::hasWId(widget->winId())); 0084 QVERIFY(!stackingOrderSpy.isEmpty()); 0085 } 0086 0087 void KWindowSystemX11Test::testWindowRemoved() 0088 { 0089 qRegisterMetaType<WId>("WId"); 0090 std::unique_ptr<QWidget> widget(new QWidget); 0091 widget->show(); 0092 QVERIFY(QTest::qWaitForWindowExposed(widget.get())); 0093 QVERIFY(KX11Extras::hasWId(widget->winId())); 0094 0095 QSignalSpy spy(KX11Extras::self(), &KX11Extras::windowRemoved); 0096 widget->hide(); 0097 spy.wait(1000); 0098 QCOMPARE(spy.first().at(0).toULongLong(), widget->winId()); 0099 QVERIFY(!KX11Extras::hasWId(widget->winId())); 0100 } 0101 0102 void KWindowSystemX11Test::testDesktopChanged() 0103 { 0104 // This test requires a running NETWM-compliant window manager 0105 if (KX11Extras::numberOfDesktops() == 1) { 0106 QSKIP("At least two virtual desktops are required to test desktop changed"); 0107 } 0108 const int current = KX11Extras::currentDesktop(); 0109 0110 QSignalSpy spy(KX11Extras::self(), &KX11Extras::currentDesktopChanged); 0111 int newDesktop = current + 1; 0112 if (newDesktop > KX11Extras::numberOfDesktops()) { 0113 newDesktop = 1; 0114 } 0115 KX11Extras::setCurrentDesktop(newDesktop); 0116 QVERIFY(spy.wait()); 0117 QCOMPARE(KX11Extras::currentDesktop(), newDesktop); 0118 QCOMPARE(spy.count(), 1); 0119 QCOMPARE(spy.first().at(0).toInt(), newDesktop); 0120 spy.clear(); 0121 0122 // setting to current desktop should not change anything 0123 KX11Extras::setCurrentDesktop(newDesktop); 0124 0125 // set back for clean state 0126 KX11Extras::setCurrentDesktop(current); 0127 QVERIFY(spy.wait()); 0128 QCOMPARE(KX11Extras::currentDesktop(), current); 0129 QCOMPARE(spy.count(), 1); 0130 QCOMPARE(spy.first().at(0).toInt(), current); 0131 } 0132 0133 void KWindowSystemX11Test::testNumberOfDesktopsChanged() 0134 { 0135 // This test requires a running NETWM-compliant window manager 0136 const int oldNumber = KX11Extras::numberOfDesktops(); 0137 QSignalSpy spy(KX11Extras::self(), &KX11Extras::numberOfDesktopsChanged); 0138 0139 // KWin has arbitrary max number of 20 desktops, so don't fail the test if we use +1 0140 const int newNumber = oldNumber < 20 ? oldNumber + 1 : oldNumber - 1; 0141 0142 NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops, NET::Properties2()); 0143 info.setNumberOfDesktops(newNumber); 0144 0145 QVERIFY(spy.wait()); 0146 QCOMPARE(KX11Extras::numberOfDesktops(), newNumber); 0147 QCOMPARE(spy.count(), 1); 0148 QCOMPARE(spy.first().at(0).toInt(), newNumber); 0149 spy.clear(); 0150 0151 // setting to same number should not change 0152 info.setNumberOfDesktops(newNumber); 0153 0154 // set back for clean state 0155 info.setNumberOfDesktops(oldNumber); 0156 QVERIFY(spy.wait()); 0157 QCOMPARE(KX11Extras::numberOfDesktops(), oldNumber); 0158 QCOMPARE(spy.count(), 1); 0159 QCOMPARE(spy.first().at(0).toInt(), oldNumber); 0160 } 0161 0162 void KWindowSystemX11Test::testDesktopNamesChanged() 0163 { 0164 // This test requires a running NETWM-compliant window manager 0165 const QString origName = KX11Extras::desktopName(KX11Extras::currentDesktop()); 0166 QSignalSpy spy(KX11Extras::self(), &KX11Extras::desktopNamesChanged); 0167 0168 const QString testName = QStringLiteral("testFooBar"); 0169 0170 KX11Extras::setDesktopName(KX11Extras::currentDesktop(), testName); 0171 QVERIFY(spy.wait()); 0172 QCOMPARE(KX11Extras::desktopName(KX11Extras::currentDesktop()), testName); 0173 QCOMPARE(spy.count(), 1); 0174 spy.clear(); 0175 0176 QX11Info::setAppTime(QX11Info::getTimestamp()); 0177 0178 // setting back to clean state 0179 KX11Extras::setDesktopName(KX11Extras::currentDesktop(), origName); 0180 QVERIFY(spy.wait()); 0181 QCOMPARE(KX11Extras::desktopName(KX11Extras::currentDesktop()), origName); 0182 QCOMPARE(spy.count(), 1); 0183 } 0184 0185 void KWindowSystemX11Test::testShowingDesktopChanged() 0186 { 0187 QX11Info::setAppTime(QX11Info::getTimestamp()); 0188 const bool showingDesktop = KWindowSystem::showingDesktop(); 0189 0190 QSignalSpy spy(KWindowSystem::self(), &KWindowSystem::showingDesktopChanged); 0191 0192 NETRootInfo info(QX11Info::connection(), NET::Properties(), NET::WM2ShowingDesktop); 0193 info.setShowingDesktop(!showingDesktop); 0194 0195 QVERIFY(spy.wait()); 0196 QCOMPARE(spy.count(), 1); 0197 QCOMPARE(spy.first().at(0).toBool(), !showingDesktop); 0198 QCOMPARE(KWindowSystem::showingDesktop(), !showingDesktop); 0199 spy.clear(); 0200 0201 QX11Info::setAppTime(QX11Info::getTimestamp()); 0202 0203 // setting again should not change 0204 info.setShowingDesktop(!showingDesktop); 0205 0206 // setting back to clean state 0207 info.setShowingDesktop(showingDesktop); 0208 QVERIFY(spy.wait(100)); 0209 QCOMPARE(spy.count(), 1); 0210 QCOMPARE(spy.first().at(0).toBool(), showingDesktop); 0211 QCOMPARE(KWindowSystem::showingDesktop(), showingDesktop); 0212 } 0213 0214 void KWindowSystemX11Test::testSetShowingDesktop() 0215 { 0216 QSignalSpy spy(KWindowSystem::self(), &KWindowSystem::showingDesktopChanged); 0217 const bool showingDesktop = KWindowSystem::showingDesktop(); 0218 0219 // setting the same state shouldn't change it 0220 QX11Info::setAppTime(QX11Info::getTimestamp()); 0221 KWindowSystem::setShowingDesktop(showingDesktop); 0222 QCOMPARE(spy.wait(), false); // spy.wait() waits for 5s 0223 QCOMPARE(KWindowSystem::showingDesktop(), showingDesktop); 0224 spy.clear(); 0225 0226 // set opposite state 0227 QX11Info::setAppTime(QX11Info::getTimestamp()); 0228 KWindowSystem::setShowingDesktop(!showingDesktop); 0229 QVERIFY(spy.wait()); 0230 QCOMPARE(KWindowSystem::showingDesktop(), !showingDesktop); 0231 spy.clear(); 0232 0233 // setting back to clean state 0234 QX11Info::setAppTime(QX11Info::getTimestamp()); 0235 KWindowSystem::setShowingDesktop(showingDesktop); 0236 QVERIFY(spy.wait()); 0237 QCOMPARE(KWindowSystem::showingDesktop(), showingDesktop); 0238 spy.clear(); 0239 } 0240 0241 void KWindowSystemX11Test::testWorkAreaChanged() 0242 { 0243 // if there are multiple screens this test can fail as workarea is not multi screen aware 0244 QSignalSpy spy(KX11Extras::self(), &KX11Extras::workAreaChanged); 0245 QSignalSpy strutSpy(KX11Extras::self(), &KX11Extras::strutChanged); 0246 0247 QWidget widget; 0248 widget.setGeometry(0, 0, 100, 10); 0249 widget.show(); 0250 0251 KX11Extras::setExtendedStrut(widget.winId(), 10, 0, 10, 0, 0, 0, 100, 0, 100, 0, 0, 0); 0252 QVERIFY(spy.wait()); 0253 QVERIFY(!spy.isEmpty()); 0254 QVERIFY(!strutSpy.isEmpty()); 0255 } 0256 0257 void KWindowSystemX11Test::testWindowTitleChanged() 0258 { 0259 qRegisterMetaType<WId>("WId"); 0260 qRegisterMetaType<NET::Properties>("NET::Properties"); 0261 qRegisterMetaType<NET::Properties2>("NET::Properties2"); 0262 qRegisterMetaType<const unsigned long *>("const ulong*"); 0263 QWidget widget; 0264 widget.setWindowTitle(QStringLiteral("foo")); 0265 widget.show(); 0266 QVERIFY(QTest::qWaitForWindowExposed(&widget)); 0267 0268 // wait till the window is mapped, etc. 0269 QTest::qWait(200); 0270 0271 QSignalSpy propertiesChangedSpy(KX11Extras::self(), &KX11Extras::windowChanged); 0272 QVERIFY(propertiesChangedSpy.isValid()); 0273 0274 widget.setWindowTitle(QStringLiteral("bar")); 0275 QX11Info::setAppTime(QX11Info::getTimestamp()); 0276 0277 int counter = 0; 0278 bool gotWMName = false; 0279 while (propertiesChangedSpy.wait() && counter < 10) { 0280 for (auto it = propertiesChangedSpy.constBegin(); it != propertiesChangedSpy.constEnd(); ++it) { 0281 if ((*it).isEmpty()) { 0282 continue; 0283 } 0284 if ((*it).at(0).toULongLong() == widget.winId()) { 0285 NET::Properties props = (*it).at(1).value<NET::Properties>(); 0286 if (props.testFlag(NET::WMName)) { 0287 gotWMName = true; 0288 } 0289 } 0290 } 0291 if (gotWMName) { 0292 break; 0293 } 0294 propertiesChangedSpy.clear(); 0295 counter++; 0296 } 0297 QVERIFY(gotWMName); 0298 0299 // now let's verify the info in KWindowInfo 0300 // we wait a little bit more as openbox is updating the visible name 0301 QTest::qWait(500); 0302 KWindowInfo info(widget.winId(), NET::WMName | NET::WMVisibleName | NET::WMVisibleIconName | NET::WMIconName, NET::Properties2()); 0303 QVERIFY(info.valid()); 0304 const QString expectedName = QStringLiteral("bar"); 0305 QCOMPARE(info.name(), expectedName); 0306 QCOMPARE(info.visibleName(), expectedName); 0307 QCOMPARE(info.visibleIconName(), expectedName); 0308 QCOMPARE(info.iconName(), expectedName); 0309 } 0310 0311 void KWindowSystemX11Test::testMinimizeWindow() 0312 { 0313 NETRootInfo rootInfo(QX11Info::connection(), NET::Supported | NET::SupportingWMCheck); 0314 if (qstrcmp(rootInfo.wmName(), "Openbox") != 0 && qstrcmp(rootInfo.wmName(), "KWin") != 0) { 0315 QSKIP("Test minimize window might not be supported on the used window manager."); 0316 } 0317 QWidget widget; 0318 widget.show(); 0319 QVERIFY(QTest::qWaitForWindowExposed(&widget)); 0320 0321 KWindowInfo info(widget.winId(), NET::WMState | NET::XAWMState); 0322 QVERIFY(!info.isMinimized()); 0323 0324 KX11Extras::minimizeWindow(widget.winId()); 0325 // create a roundtrip, updating minimized state is done by the window manager and wait a short time 0326 QX11Info::setAppTime(QX11Info::getTimestamp()); 0327 QTest::qWait(200); 0328 0329 KWindowInfo info2(widget.winId(), NET::WMState | NET::XAWMState); 0330 QVERIFY(info2.isMinimized()); 0331 0332 KX11Extras::unminimizeWindow(widget.winId()); 0333 // create a roundtrip, updating minimized state is done by the window manager and wait a short time 0334 QX11Info::setAppTime(QX11Info::getTimestamp()); 0335 QTest::qWait(200); 0336 0337 KWindowInfo info3(widget.winId(), NET::WMState | NET::XAWMState); 0338 QVERIFY(!info3.isMinimized()); 0339 } 0340 0341 void KWindowSystemX11Test::testPlatformX11() 0342 { 0343 QCOMPARE(KWindowSystem::platform(), KWindowSystem::Platform::X11); 0344 QCOMPARE(KWindowSystem::isPlatformX11(), true); 0345 QCOMPARE(KWindowSystem::isPlatformWayland(), false); 0346 } 0347 0348 QTEST_MAIN(KWindowSystemX11Test) 0349 0350 #include "kwindowsystemx11test.moc"