Warning, file /frameworks/kwindowsystem/autotests/kwindoweffectstest.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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 <QSignalSpy> 0008 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0009 #include <private/qtx11extras_p.h> 0010 #else 0011 #include <QX11Info> 0012 #endif 0013 0014 #include <kmanagerselection.h> 0015 #include <kwindoweffects.h> 0016 #include <kwindowsystem.h> 0017 #include <kx11extras.h> 0018 #include <netwm.h> 0019 #include <qtest_widgets.h> 0020 #include <xcb/xcb.h> 0021 0022 #include "cptr_p.h" 0023 0024 Q_DECLARE_METATYPE(KWindowEffects::SlideFromLocation) 0025 Q_DECLARE_METATYPE(KWindowEffects::Effect) 0026 0027 class KWindowEffectsTest : public QObject 0028 { 0029 Q_OBJECT 0030 private Q_SLOTS: 0031 void initTestCase(); 0032 void testSlideWindow_data(); 0033 void testSlideWindow(); 0034 void testSlideWindowRemove(); 0035 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0036 void testPresentWindows_data(); 0037 void testPresentWindows(); 0038 void testPresentWindowsEmptyGroup(); 0039 void testPresentWindowsGroup_data(); 0040 void testPresentWindowsGroup(); 0041 void testHighlightWindows_data(); 0042 void testHighlightWindows(); 0043 void testHighlightWindowsEmpty(); 0044 #endif 0045 void testBlur_data(); 0046 void testBlur(); 0047 void testBlurDisable(); 0048 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 67) 0049 void testMarkAsDashboard(); 0050 #endif 0051 void testEffectAvailable_data(); 0052 void testEffectAvailable(); 0053 0054 private: 0055 int32_t locationToValue(KWindowEffects::SlideFromLocation location) const; 0056 void performSlideWindowTest(xcb_window_t window, int offset, KWindowEffects::SlideFromLocation location) const; 0057 void performSlideWindowRemoveTest(xcb_window_t window); 0058 void performWindowsOnPropertyTest(xcb_atom_t atom, const QList<WId> &windows); 0059 void performAtomIsRemoveTest(xcb_window_t window, xcb_atom_t atom); 0060 void getHelperAtom(const QByteArray &name, xcb_atom_t *atom) const; 0061 xcb_atom_t m_slide; 0062 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0063 xcb_atom_t m_presentWindows; 0064 xcb_atom_t m_presentWindowsGroup; 0065 xcb_atom_t m_highlightWindows; 0066 #endif 0067 xcb_atom_t m_thumbnails; 0068 xcb_atom_t m_blur; 0069 std::unique_ptr<QWindow> m_window; 0070 std::unique_ptr<QWidget> m_widget; 0071 }; 0072 0073 void KWindowEffectsTest::initTestCase() 0074 { 0075 m_window.reset(new QWindow()); 0076 QVERIFY(m_window->winId() != XCB_WINDOW_NONE); 0077 m_widget.reset(new QWidget()); 0078 m_widget->show(); 0079 QVERIFY(m_widget->effectiveWinId() != XCB_WINDOW_NONE); 0080 0081 getHelperAtom(QByteArrayLiteral("_KDE_SLIDE"), &m_slide); 0082 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0083 getHelperAtom(QByteArrayLiteral("_KDE_PRESENT_WINDOWS_DESKTOP"), &m_presentWindows); 0084 getHelperAtom(QByteArrayLiteral("_KDE_PRESENT_WINDOWS_GROUP"), &m_presentWindowsGroup); 0085 getHelperAtom(QByteArrayLiteral("_KDE_WINDOW_HIGHLIGHT"), &m_highlightWindows); 0086 #endif 0087 getHelperAtom(QByteArrayLiteral("_KDE_WINDOW_PREVIEW"), &m_thumbnails); 0088 getHelperAtom(QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION"), &m_blur); 0089 } 0090 0091 void KWindowEffectsTest::getHelperAtom(const QByteArray &name, xcb_atom_t *atom) const 0092 { 0093 xcb_connection_t *c = QX11Info::connection(); 0094 xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, name.length(), name.constData()); 0095 0096 UniqueCPointer<xcb_intern_atom_reply_t> reply(xcb_intern_atom_reply(c, atomCookie, nullptr)); 0097 QVERIFY(reply); 0098 *atom = reply->atom; 0099 } 0100 0101 void KWindowEffectsTest::testSlideWindow_data() 0102 { 0103 QTest::addColumn<int>("offset"); 0104 QTest::addColumn<KWindowEffects::SlideFromLocation>("location"); 0105 0106 QTest::newRow("Left") << 10 << KWindowEffects::LeftEdge; 0107 QTest::newRow("Right") << 20 << KWindowEffects::RightEdge; 0108 QTest::newRow("Top") << 0 << KWindowEffects::TopEdge; 0109 QTest::newRow("Bottom") << -1 << KWindowEffects::BottomEdge; 0110 } 0111 0112 void KWindowEffectsTest::testSlideWindow() 0113 { 0114 QFETCH(int, offset); 0115 QFETCH(KWindowEffects::SlideFromLocation, location); 0116 0117 KWindowEffects::slideWindow(m_window.get(), location, offset); 0118 performSlideWindowTest(m_window->winId(), offset, location); 0119 } 0120 0121 void KWindowEffectsTest::testSlideWindowRemove() 0122 { 0123 xcb_window_t window = m_window->winId(); 0124 // first install the atom 0125 KWindowEffects::slideWindow(m_window.get(), KWindowEffects::TopEdge, 0); 0126 performSlideWindowTest(window, 0, KWindowEffects::TopEdge); 0127 0128 // now delete it 0129 KWindowEffects::slideWindow(m_window.get(), KWindowEffects::NoEdge, 0); 0130 performSlideWindowRemoveTest(window); 0131 } 0132 0133 void KWindowEffectsTest::performSlideWindowTest(xcb_window_t window, int offset, KWindowEffects::SlideFromLocation location) const 0134 { 0135 xcb_connection_t *c = QX11Info::connection(); 0136 xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(c, false, window, m_slide, m_slide, 0, 100); 0137 UniqueCPointer<xcb_get_property_reply_t> reply(xcb_get_property_reply(c, cookie, nullptr)); 0138 QVERIFY(reply); 0139 QCOMPARE(reply->format, uint8_t(32)); 0140 QCOMPARE(reply->value_len, uint32_t(2)); 0141 QCOMPARE(reply->type, m_slide); 0142 int32_t *data = static_cast<int32_t *>(xcb_get_property_value(reply.get())); 0143 QCOMPARE(data[0], offset); 0144 QCOMPARE(data[1], locationToValue(location)); 0145 } 0146 0147 void KWindowEffectsTest::performSlideWindowRemoveTest(xcb_window_t window) 0148 { 0149 performAtomIsRemoveTest(window, m_slide); 0150 } 0151 0152 void KWindowEffectsTest::performAtomIsRemoveTest(xcb_window_t window, xcb_atom_t atom) 0153 { 0154 xcb_connection_t *c = QX11Info::connection(); 0155 xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(c, false, window, atom, atom, 0, 100); 0156 UniqueCPointer<xcb_get_property_reply_t> reply(xcb_get_property_reply(c, cookie, nullptr)); 0157 QVERIFY(reply); 0158 QCOMPARE(reply->type, xcb_atom_t(XCB_ATOM_NONE)); 0159 } 0160 0161 int32_t KWindowEffectsTest::locationToValue(KWindowEffects::SlideFromLocation location) const 0162 { 0163 switch (location) { 0164 case KWindowEffects::LeftEdge: 0165 return 0; 0166 case KWindowEffects::TopEdge: 0167 return 1; 0168 case KWindowEffects::RightEdge: 0169 return 2; 0170 case KWindowEffects::BottomEdge: 0171 return 3; 0172 default: 0173 return -1; 0174 } 0175 } 0176 0177 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0178 void KWindowEffectsTest::testPresentWindows_data() 0179 { 0180 QTest::addColumn<int>("desktop"); 0181 0182 QTest::newRow("all desktops") << -1; 0183 QTest::newRow("1") << 1; 0184 QTest::newRow("2") << 2; 0185 QTest::newRow("3") << 3; 0186 QTest::newRow("4") << 4; 0187 } 0188 #endif 0189 0190 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0191 void KWindowEffectsTest::testPresentWindows() 0192 { 0193 QFETCH(int, desktop); 0194 0195 KWindowEffects::presentWindows(m_window->winId(), desktop); 0196 0197 xcb_connection_t *c = QX11Info::connection(); 0198 xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(c, false, m_window->winId(), m_presentWindows, m_presentWindows, 0, 100); 0199 UniqueCPointer<xcb_get_property_reply_t> reply(xcb_get_property_reply(c, cookie, nullptr)); 0200 QVERIFY(reply); 0201 QCOMPARE(reply->format, uint8_t(32)); 0202 QCOMPARE(reply->value_len, uint32_t(1)); 0203 QCOMPARE(reply->type, m_presentWindows); 0204 int32_t *data = static_cast<int32_t *>(xcb_get_property_value(reply.get())); 0205 QCOMPARE(data[0], desktop); 0206 } 0207 #endif 0208 0209 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0210 void KWindowEffectsTest::testPresentWindowsEmptyGroup() 0211 { 0212 KWindowEffects::presentWindows(m_window->winId(), QList<WId>()); 0213 0214 xcb_connection_t *c = QX11Info::connection(); 0215 xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(c, false, m_window->winId(), m_presentWindowsGroup, m_presentWindowsGroup, 0, 100); 0216 UniqueCPointer<xcb_get_property_reply_t> reply(xcb_get_property_reply(c, cookie, nullptr)); 0217 QVERIFY(reply); 0218 QCOMPARE(reply->type, xcb_atom_t(XCB_ATOM_NONE)); 0219 } 0220 #endif 0221 0222 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0223 void KWindowEffectsTest::testPresentWindowsGroup_data() 0224 { 0225 QTest::addColumn<QList<WId>>("windows"); 0226 0227 QTest::newRow("one") << (QList<WId>() << m_window->winId()); 0228 QTest::newRow("two") << (QList<WId>() << m_window->winId() << m_widget->effectiveWinId()); 0229 } 0230 #endif 0231 0232 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0233 void KWindowEffectsTest::testPresentWindowsGroup() 0234 { 0235 QFETCH(QList<WId>, windows); 0236 KWindowEffects::presentWindows(m_window->winId(), windows); 0237 performWindowsOnPropertyTest(m_presentWindowsGroup, windows); 0238 } 0239 #endif 0240 0241 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0242 void KWindowEffectsTest::testHighlightWindows_data() 0243 { 0244 QTest::addColumn<QList<WId>>("windows"); 0245 0246 QTest::newRow("one") << (QList<WId>() << m_window->winId()); 0247 QTest::newRow("two") << (QList<WId>() << m_window->winId() << m_widget->effectiveWinId()); 0248 } 0249 #endif 0250 0251 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0252 void KWindowEffectsTest::testHighlightWindows() 0253 { 0254 QFETCH(QList<WId>, windows); 0255 KWindowEffects::highlightWindows(m_window->winId(), windows); 0256 performWindowsOnPropertyTest(m_highlightWindows, windows); 0257 } 0258 #endif 0259 0260 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0261 void KWindowEffectsTest::testHighlightWindowsEmpty() 0262 { 0263 // ensure it's empty 0264 KWindowEffects::highlightWindows(m_window->winId(), QList<WId>()); 0265 performAtomIsRemoveTest(m_window->winId(), m_highlightWindows); 0266 0267 // install some windows on the atom 0268 QList<WId> windows; 0269 windows.append(m_window->winId()); 0270 windows.append(m_widget->effectiveWinId()); 0271 KWindowEffects::highlightWindows(m_window->winId(), windows); 0272 performWindowsOnPropertyTest(m_highlightWindows, windows); 0273 0274 // and remove it again 0275 KWindowEffects::highlightWindows(m_window->winId(), QList<WId>()); 0276 performAtomIsRemoveTest(m_window->winId(), m_highlightWindows); 0277 } 0278 #endif 0279 0280 void KWindowEffectsTest::performWindowsOnPropertyTest(xcb_atom_t atom, const QList<WId> &windows) 0281 { 0282 xcb_connection_t *c = QX11Info::connection(); 0283 xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(c, false, m_window->winId(), atom, atom, 0, 100); 0284 UniqueCPointer<xcb_get_property_reply_t> reply(xcb_get_property_reply(c, cookie, nullptr)); 0285 QVERIFY(reply); 0286 QCOMPARE(reply->type, atom); 0287 QCOMPARE(reply->format, uint8_t(32)); 0288 QCOMPARE(reply->value_len, uint32_t(windows.size())); 0289 int32_t *data = static_cast<int32_t *>(xcb_get_property_value(reply.get())); 0290 for (int i = 0; i < windows.size(); ++i) { 0291 QCOMPARE(data[i], int32_t(windows.at(i))); 0292 } 0293 } 0294 0295 void KWindowEffectsTest::testBlur_data() 0296 { 0297 QTest::addColumn<QRegion>("blur"); 0298 0299 QRegion region(0, 0, 10, 10); 0300 QTest::newRow("one rect") << region; 0301 region = region.united(QRect(20, 20, 5, 5)); 0302 QTest::newRow("two rects") << region; 0303 region = region.united(QRect(100, 100, 20, 20)); 0304 QTest::newRow("three rects") << region; 0305 QTest::newRow("empty") << QRegion(); 0306 } 0307 0308 void KWindowEffectsTest::testBlur() 0309 { 0310 QFETCH(QRegion, blur); 0311 0312 KWindowEffects::enableBlurBehind(m_window.get(), true, blur); 0313 xcb_connection_t *c = QX11Info::connection(); 0314 xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(c, false, m_window->winId(), m_blur, XCB_ATOM_CARDINAL, 0, 100); 0315 UniqueCPointer<xcb_get_property_reply_t> reply(xcb_get_property_reply(c, cookie, nullptr)); 0316 QVERIFY(reply); 0317 QCOMPARE(reply->type, xcb_atom_t(XCB_ATOM_CARDINAL)); 0318 QCOMPARE(reply->format, uint8_t(32)); 0319 QCOMPARE(reply->value_len, uint32_t(blur.rectCount() * 4)); 0320 uint32_t *data = static_cast<uint32_t *>(xcb_get_property_value(reply.get())); 0321 int dataOffset = 0; 0322 for (const QRect &rect : blur) { 0323 QCOMPARE(data[dataOffset++], uint32_t(rect.x())); 0324 QCOMPARE(data[dataOffset++], uint32_t(rect.y())); 0325 QCOMPARE(data[dataOffset++], uint32_t(rect.width())); 0326 QCOMPARE(data[dataOffset++], uint32_t(rect.height())); 0327 } 0328 } 0329 0330 void KWindowEffectsTest::testBlurDisable() 0331 { 0332 KWindowEffects::enableBlurBehind(m_window.get(), false); 0333 performAtomIsRemoveTest(m_window->winId(), m_blur); 0334 0335 KWindowEffects::enableBlurBehind(m_window.get(), true); 0336 // verify that it got added 0337 xcb_connection_t *c = QX11Info::connection(); 0338 xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(c, false, m_window->winId(), m_blur, XCB_ATOM_CARDINAL, 0, 100); 0339 UniqueCPointer<xcb_get_property_reply_t> reply(xcb_get_property_reply(c, cookie, nullptr)); 0340 QVERIFY(reply); 0341 QCOMPARE(reply->type, xcb_atom_t(XCB_ATOM_CARDINAL)); 0342 0343 // and disable 0344 KWindowEffects::enableBlurBehind(m_window.get(), false); 0345 performAtomIsRemoveTest(m_window->winId(), m_blur); 0346 } 0347 0348 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 67) 0349 void KWindowEffectsTest::testMarkAsDashboard() 0350 { 0351 const QByteArray className = QByteArrayLiteral("dashboard"); 0352 // should not yet be set 0353 xcb_connection_t *c = QX11Info::connection(); 0354 xcb_get_property_cookie_t cookie = xcb_get_property_unchecked(c, false, m_window->winId(), XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 100); 0355 UniqueCPointer<xcb_get_property_reply_t> reply(xcb_get_property_reply(c, cookie, nullptr)); 0356 QVERIFY(reply); 0357 QCOMPARE(reply->type, xcb_atom_t(XCB_ATOM_STRING)); 0358 QCOMPARE(reply->format, uint8_t(8)); 0359 char *data = static_cast<char *>(xcb_get_property_value(reply.get())); 0360 QVERIFY(QByteArray(data) != className); 0361 0362 // now mark as dashboard 0363 KWindowEffects::markAsDashboard(m_window->winId()); 0364 cookie = xcb_get_property_unchecked(c, false, m_window->winId(), XCB_ATOM_WM_CLASS, XCB_ATOM_STRING, 0, 100); 0365 reply.reset(xcb_get_property_reply(c, cookie, nullptr)); 0366 QVERIFY(reply); 0367 QCOMPARE(reply->type, xcb_atom_t(XCB_ATOM_STRING)); 0368 QCOMPARE(reply->format, uint8_t(8)); 0369 QCOMPARE(reply->value_len, uint32_t(19)); 0370 data = static_cast<char *>(xcb_get_property_value(reply.get())); 0371 QCOMPARE(QByteArray(data), className); 0372 data = data + 10; 0373 QCOMPARE(QByteArray(data), className); 0374 } 0375 #endif 0376 0377 void KWindowEffectsTest::testEffectAvailable_data() 0378 { 0379 QTest::addColumn<KWindowEffects::Effect>("effect"); 0380 QTest::addColumn<QByteArray>("propertyName"); 0381 0382 QTest::newRow("slide") << KWindowEffects::Slide << QByteArrayLiteral("_KDE_SLIDE"); 0383 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 82) 0384 QTest::newRow("PresentWindows") << KWindowEffects::PresentWindows << QByteArrayLiteral("_KDE_PRESENT_WINDOWS_DESKTOP"); 0385 QTest::newRow("PresentWindowsGroup") << KWindowEffects::PresentWindowsGroup << QByteArrayLiteral("_KDE_PRESENT_WINDOWS_GROUP"); 0386 QTest::newRow("HighlightWindows") << KWindowEffects::HighlightWindows << QByteArrayLiteral("_KDE_WINDOW_HIGHLIGHT"); 0387 #endif 0388 QTest::newRow("BlurBehind") << KWindowEffects::BlurBehind << QByteArrayLiteral("_KDE_NET_WM_BLUR_BEHIND_REGION"); 0389 #if KWINDOWSYSTEM_BUILD_DEPRECATED_SINCE(5, 67) 0390 QTest::newRow("Dashboard") << KWindowEffects::Dashboard << QByteArrayLiteral("_WM_EFFECT_KDE_DASHBOARD"); 0391 #endif 0392 QTest::newRow("BackgroundContrast") << KWindowEffects::BackgroundContrast << QByteArrayLiteral("_KDE_NET_WM_BACKGROUND_CONTRAST_REGION"); 0393 } 0394 0395 void KWindowEffectsTest::testEffectAvailable() 0396 { 0397 NETRootInfo rootInfo(QX11Info::connection(), NET::Supported | NET::SupportingWMCheck); 0398 if (qstrcmp(rootInfo.wmName(), "KWin") == 0) { 0399 QSKIP("KWin running, we don't want to interact with the running system"); 0400 } 0401 // this test verifies whether an effect is available 0402 QFETCH(KWindowEffects::Effect, effect); 0403 // without a compositing manager it's not available 0404 // try-verify as there still might be the selection claimed from previous data run 0405 QTRY_VERIFY(!KX11Extras::compositingActive()); 0406 QVERIFY(!KWindowEffects::isEffectAvailable(effect)); 0407 0408 // fake the compositor 0409 QSignalSpy compositingChangedSpy(KX11Extras::self(), &KX11Extras::compositingChanged); 0410 QVERIFY(compositingChangedSpy.isValid()); 0411 KSelectionOwner compositorSelection("_NET_WM_CM_S0"); 0412 QSignalSpy claimedSpy(&compositorSelection, &KSelectionOwner::claimedOwnership); 0413 QVERIFY(claimedSpy.isValid()); 0414 compositorSelection.claim(true); 0415 QVERIFY(claimedSpy.wait()); 0416 QCOMPARE(compositingChangedSpy.count(), 1); 0417 QCOMPARE(compositingChangedSpy.first().first().toBool(), true); 0418 QVERIFY(KX11Extras::compositingActive()); 0419 0420 // but not yet available 0421 QVERIFY(!KWindowEffects::isEffectAvailable(effect)); 0422 0423 // set the atom 0424 QFETCH(QByteArray, propertyName); 0425 xcb_connection_t *c = QX11Info::connection(); 0426 xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom_unchecked(c, false, propertyName.length(), propertyName.constData()); 0427 UniqueCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, atomCookie, nullptr)); 0428 QVERIFY(atom); 0429 unsigned char dummy = 0; 0430 xcb_change_property(c, XCB_PROP_MODE_REPLACE, QX11Info::appRootWindow(), atom->atom, atom->atom, 8, 1, &dummy); 0431 xcb_flush(c); 0432 0433 // now the effect should be available 0434 QVERIFY(KWindowEffects::isEffectAvailable(effect)); 0435 0436 // delete the property again 0437 xcb_delete_property(c, QX11Info::appRootWindow(), atom->atom); 0438 xcb_flush(c); 0439 // which means it's no longer available 0440 QVERIFY(!KWindowEffects::isEffectAvailable(effect)); 0441 0442 // remove compositing selection 0443 compositorSelection.release(); 0444 QVERIFY(compositingChangedSpy.wait()); 0445 QCOMPARE(compositingChangedSpy.count(), 2); 0446 QCOMPARE(compositingChangedSpy.last().first().toBool(), false); 0447 QVERIFY(!KX11Extras::compositingActive()); 0448 } 0449 0450 QTEST_MAIN(KWindowEffectsTest) 0451 0452 #include "kwindoweffectstest.moc"