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