File indexing completed on 2024-04-21 13:03:31
0001 /* 0002 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0003 SPDX-FileCopyrightText: 2015 Bhushan Shah <bhush94@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 // own 0008 #include "../x11locker.h" 0009 // Qt 0010 #include <QWindow> 0011 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0012 #include <private/qtx11extras_p.h> 0013 #else 0014 #include <QX11Info> 0015 #endif 0016 #include <QtTest> 0017 // xcb 0018 #include <xcb/xcb.h> 0019 0020 template<typename T> 0021 using ScopedCPointer = QScopedPointer<T, QScopedPointerPodDeleter>; 0022 0023 class LockWindowTest : public QObject 0024 { 0025 Q_OBJECT 0026 private Q_SLOTS: 0027 void initTestCase(); 0028 void testBlankScreen(); 0029 void testEmergencyShow(); 0030 }; 0031 0032 xcb_screen_t *defaultScreen() 0033 { 0034 int screen = QX11Info::appScreen(); 0035 for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(QX11Info::connection())); it.rem; --screen, xcb_screen_next(&it)) { 0036 if (screen == 0) { 0037 return it.data; 0038 } 0039 } 0040 return nullptr; 0041 } 0042 0043 bool isColored(const QColor color, const int x, const int y, const int width, const int height) 0044 { 0045 xcb_connection_t *c = QX11Info::connection(); 0046 const auto cookie = xcb_get_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, QX11Info::appRootWindow(), x, y, width, height, ~0); 0047 ScopedCPointer<xcb_get_image_reply_t> xImage(xcb_get_image_reply(c, cookie, nullptr)); 0048 if (xImage.isNull()) { 0049 return false; 0050 } 0051 0052 // this operates on the assumption that X server default depth matches Qt's image format 0053 QImage image(xcb_get_image_data(xImage.data()), width, height, xcb_get_image_data_length(xImage.data()) / height, QImage::Format_ARGB32_Premultiplied); 0054 0055 for (int i = 0; i < image.width(); i++) { 0056 for (int j = 0; j < image.height(); j++) { 0057 if (QColor(image.pixel(i, j)) != color) { 0058 return false; 0059 } 0060 } 0061 } 0062 return true; 0063 } 0064 0065 bool isBlack() 0066 { 0067 xcb_screen_t *screen = defaultScreen(); 0068 const int width = screen->width_in_pixels; 0069 const int height = screen->height_in_pixels; 0070 0071 return isColored(Qt::black, 0, 0, width, height); 0072 } 0073 0074 xcb_atom_t screenLockerAtom() 0075 { 0076 const QByteArray atomName = QByteArrayLiteral("_KDE_SCREEN_LOCKER"); 0077 xcb_connection_t *c = QX11Info::connection(); 0078 const auto cookie = xcb_intern_atom(c, false, atomName.length(), atomName.constData()); 0079 ScopedCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, cookie, nullptr)); 0080 if (atom.isNull()) { 0081 return XCB_ATOM_NONE; 0082 } 0083 return atom->atom; 0084 } 0085 0086 void LockWindowTest::initTestCase() 0087 { 0088 QCoreApplication::setAttribute(Qt::AA_ForceRasterWidgets); 0089 } 0090 0091 void LockWindowTest::testBlankScreen() 0092 { 0093 // create and show a dummy window to ensure the background doesn't start as black 0094 QWidget dummy; 0095 dummy.setWindowFlags(Qt::X11BypassWindowManagerHint); 0096 QPalette p; 0097 p.setColor(QPalette::Window, Qt::red); 0098 dummy.setAutoFillBackground(true); 0099 dummy.setPalette(p); 0100 dummy.setGeometry(0, 0, 100, 100); 0101 dummy.show(); 0102 xcb_flush(QX11Info::connection()); 0103 0104 // Lets wait till it gets shown 0105 QTest::qWait(1000); 0106 0107 // Verify that red window is shown 0108 QVERIFY(isColored(Qt::red, 0, 0, 100, 100)); 0109 0110 ScreenLocker::X11Locker lockWindow; 0111 lockWindow.showLockWindow(); 0112 0113 // the screen used to be blanked once the first lock window gets mapped, so let's create one 0114 QWindow fakeWindow; 0115 fakeWindow.setFlags(Qt::X11BypassWindowManagerHint); 0116 // it's on purpose outside the visual area 0117 fakeWindow.setGeometry(-1, -1, 1, 1); 0118 fakeWindow.create(); 0119 xcb_atom_t atom = screenLockerAtom(); 0120 QVERIFY(atom != XCB_ATOM_NONE); 0121 xcb_connection_t *c = QX11Info::connection(); 0122 xcb_change_property(c, XCB_PROP_MODE_REPLACE, fakeWindow.winId(), atom, atom, 32, 0, nullptr); 0123 xcb_flush(c); 0124 fakeWindow.show(); 0125 0126 // give it time to be shown 0127 QTest::qWait(1000); 0128 0129 // now lets try to get a screen grab and verify it's not yet black 0130 QVERIFY(!isBlack()); 0131 0132 // nowadays we need to pass the window id to the lock window 0133 lockWindow.addAllowedWindow(fakeWindow.winId()); 0134 0135 // give it time to be shown 0136 QTest::qWait(1000); 0137 0138 // now lets try to get a screen grab and verify it's black 0139 QVERIFY(isBlack()); 0140 dummy.hide(); 0141 0142 // destroying the fakeWindow should not remove the blanked screen 0143 fakeWindow.destroy(); 0144 QTest::qWait(1000); 0145 QVERIFY(isBlack()); 0146 0147 // let's create another window and try to raise it above the lockWindow 0148 // using a QWidget to get proper content which won't be black 0149 QWidget widgetWindow; 0150 widgetWindow.setGeometry(10, 10, 100, 100); 0151 QPalette p1; 0152 p1.setColor(QPalette::Window, Qt::blue); 0153 widgetWindow.setAutoFillBackground(true); 0154 widgetWindow.setPalette(p1); 0155 widgetWindow.show(); 0156 const uint32_t values[] = {XCB_STACK_MODE_ABOVE}; 0157 xcb_configure_window(c, widgetWindow.winId(), XCB_CONFIG_WINDOW_STACK_MODE, values); 0158 xcb_flush(c); 0159 QTest::qWait(1000); 0160 QVERIFY(isBlack()); 0161 0162 lockWindow.hideLockWindow(); 0163 } 0164 0165 void LockWindowTest::testEmergencyShow() 0166 { 0167 QWidget dummy; 0168 dummy.setWindowFlags(Qt::X11BypassWindowManagerHint); 0169 QPalette p; 0170 p.setColor(QPalette::Window, Qt::red); 0171 dummy.setAutoFillBackground(true); 0172 dummy.setPalette(p); 0173 dummy.setGeometry(0, 0, 100, 100); 0174 dummy.show(); 0175 xcb_flush(QX11Info::connection()); 0176 0177 // Lets wait till it gets shown 0178 QTest::qWait(1000); 0179 0180 // Verify that red window is shown 0181 QVERIFY(isColored(Qt::red, 0, 0, 100, 100)); 0182 0183 qputenv("KSLD_TESTMODE", QByteArrayLiteral("true")); 0184 0185 ScreenLocker::X11Locker lockWindow; 0186 lockWindow.showLockWindow(); 0187 0188 QTest::qWait(1000); 0189 0190 // the screen used to be blanked once the first lock window gets mapped, so let's create one 0191 QWindow fakeWindow; 0192 fakeWindow.setFlags(Qt::X11BypassWindowManagerHint); 0193 // it's on purpose outside the visual area 0194 fakeWindow.setGeometry(-1, -1, 1, 1); 0195 fakeWindow.create(); 0196 xcb_atom_t atom = screenLockerAtom(); 0197 QVERIFY(atom != XCB_ATOM_NONE); 0198 xcb_connection_t *c = QX11Info::connection(); 0199 xcb_change_property(c, XCB_PROP_MODE_REPLACE, fakeWindow.winId(), atom, atom, 32, 0, nullptr); 0200 xcb_flush(c); 0201 fakeWindow.show(); 0202 0203 // nowadays we need to pass the window id to the lock window 0204 lockWindow.addAllowedWindow(fakeWindow.winId()); 0205 0206 QTest::qWait(1000); 0207 0208 lockWindow.emergencyShow(); 0209 0210 QTest::qWait(1000); 0211 xcb_flush(c); 0212 0213 xcb_screen_t *screen = defaultScreen(); 0214 const int width = screen->width_in_pixels; 0215 const int height = screen->height_in_pixels; 0216 0217 const auto cookie = xcb_get_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, QX11Info::appRootWindow(), 0, 0, width, height, ~0); 0218 ScopedCPointer<xcb_get_image_reply_t> xImage(xcb_get_image_reply(c, cookie, nullptr)); 0219 0220 QVERIFY(!xImage.isNull()); 0221 0222 // this operates on the assumption that X server default depth matches Qt's image format 0223 QImage image(xcb_get_image_data(xImage.data()), width, height, xcb_get_image_data_length(xImage.data()) / height, QImage::Format_ARGB32_Premultiplied); 0224 0225 bool isColored = false; 0226 int blackPixelCount = 0; 0227 int whitePixelCount = 0; 0228 0229 // This verifies that, 0230 // - there is at least one white and one black pixel 0231 // - there is no other colored pixel 0232 QColor color; 0233 for (int i = 0; i < image.width(); i++) { 0234 for (int j = 0; j < image.height(); j++) { 0235 color = QColor(image.pixel(i, j)); 0236 if (color == Qt::black) { 0237 blackPixelCount++; 0238 } else if (color == Qt::white) { 0239 whitePixelCount++; 0240 } else { 0241 isColored = true; 0242 break; 0243 } 0244 } 0245 } 0246 0247 QVERIFY(!isColored); 0248 QVERIFY(blackPixelCount > 0); 0249 QVERIFY(whitePixelCount > 0); 0250 0251 lockWindow.hideLockWindow(); 0252 } 0253 0254 QTEST_MAIN(LockWindowTest) 0255 #include "x11lockertest.moc"