[screenlocker] Split generic parts of X11Locker into AbstractLocker

This commit introduces AbstractLocker class, which can be subclassed to
provide platform specific implementation of LockScreen, for example
X11Locker and WaylandLocker.

REVIEW: 125802
wilder-5.14
Bhushan Shah 11 years ago
parent d9328ef3c4
commit 5a0ab38613
  1. 1
      ksmserver/screenlocker/CMakeLists.txt
  2. 10
      ksmserver/screenlocker/autotests/CMakeLists.txt
  3. 171
      ksmserver/screenlocker/autotests/lockwindowtest.cpp
  4. 6
      ksmserver/screenlocker/ksldapp.cpp
  5. 4
      ksmserver/screenlocker/ksldapp.h
  6. 53
      ksmserver/screenlocker/lockwindow.cpp
  7. 48
      ksmserver/screenlocker/lockwindow.h

@ -16,6 +16,7 @@ set(ksmserver_xml ${PROJECT_SOURCE_DIR}/ksmserver/org.kde.KSMServerInterface.xm
set(powerdevilpolicyagent_xml ${KDE4_DBUS_INTERFACES_DIR}/kf5_org.kde.Solid.PowerManagement.PolicyAgent.xml)
set(ksld_SRCS
abstractlocker.cpp
ksldapp.cpp
interface.cpp
globalaccel.cpp

@ -39,9 +39,9 @@ ecm_mark_as_test(pointerGrabber)
#######################################
# LockWindowTest
#######################################
set(lockWindowTest_SRCS lockwindowtest.cpp ../lockwindow.cpp ../globalaccel.cpp)
add_executable(lockWindowTest ${lockWindowTest_SRCS})
target_link_libraries(lockWindowTest
set(x11LockerTest_SRCS x11lockertest.cpp ../lockwindow.cpp ../globalaccel.cpp ../abstractlocker.cpp)
add_executable(x11LockerTest ${x11LockerTest_SRCS})
target_link_libraries(x11LockerTest
KF5::GlobalAccel
KF5::I18n
KF5::WindowSystem
@ -52,5 +52,5 @@ target_link_libraries(lockWindowTest
XCB::XCB
XCB::KEYSYMS
)
add_test(ksmserver-lockWindowTest lockWindowTest)
ecm_mark_as_test(lockWindowTest)
add_test(ksmserver-x11LockerTest x11LockerTest)
ecm_mark_as_test(x11LockerTest)

@ -1,171 +0,0 @@
/********************************************************************
KSld - the KDE Screenlocker Daemon
This file is part of the KDE project.
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
// own
#include "../lockwindow.h"
// Qt
#include <QtTest/QtTest>
#include <QWindow>
#include <QX11Info>
// xcb
#include <xcb/xcb.h>
template <typename T> using ScopedCPointer = QScopedPointer<T, QScopedPointerPodDeleter>;
class LockWindowTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testBlankScreen();
};
xcb_screen_t *defaultScreen()
{
int screen = QX11Info::appScreen();
for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(QX11Info::connection()));
it.rem;
--screen, xcb_screen_next(&it)) {
if (screen == 0) {
return it.data;
}
}
return nullptr;
}
bool isColored(const QColor color, const int x, const int y, const int width, const int height)
{
xcb_connection_t *c = QX11Info::connection();
const auto cookie = xcb_get_image(c, XCB_IMAGE_FORMAT_Z_PIXMAP, QX11Info::appRootWindow(),
x, y, width, height, ~0);
ScopedCPointer<xcb_get_image_reply_t> xImage(xcb_get_image_reply(c, cookie, nullptr));
if (xImage.isNull()) {
return false;
}
// this operates on the assumption that X server default depth matches Qt's image format
QImage image(xcb_get_image_data(xImage.data()), width, height,
xcb_get_image_data_length(xImage.data()) / height, QImage::Format_ARGB32_Premultiplied);
for (int i = 0; i < image.width(); i++) {
for (int j = 0; j < image.height(); j++) {
if (QColor(image.pixel(i, j)) != color) {
return false;
}
}
}
return true;
}
bool isBlack()
{
xcb_screen_t *screen = defaultScreen();
const int width = screen->width_in_pixels;
const int height = screen->height_in_pixels;
return isColored(Qt::black, 0, 0, width, height);
}
xcb_atom_t screenLockerAtom()
{
const QByteArray atomName = QByteArrayLiteral("_KDE_SCREEN_LOCKER");
xcb_connection_t *c = QX11Info::connection();
const auto cookie = xcb_intern_atom(c, false, atomName.length(), atomName.constData());
ScopedCPointer<xcb_intern_atom_reply_t> atom(xcb_intern_atom_reply(c, cookie, nullptr));
if (atom.isNull()) {
return XCB_ATOM_NONE;
}
return atom->atom;
}
void LockWindowTest::testBlankScreen()
{
// create and show a dummy window to ensure the background doesn't start as black
QWidget dummy;
dummy.setWindowFlags(Qt::X11BypassWindowManagerHint);
QPalette p;
p.setColor(QPalette::Background, Qt::red);
dummy.setAutoFillBackground(true);
dummy.setPalette(p);
dummy.setGeometry(0, 0, 100, 100);
dummy.show();
xcb_flush(QX11Info::connection());
// Lets wait till it gets shown
QTest::qWait(1000);
// Verify that red window is shown
QVERIFY(isColored(Qt::red, 0, 0, 100, 100));
ScreenLocker::X11Locker lockWindow;
lockWindow.showLockWindow();
// the screen used to be blanked once the first lock window gets mapped, so let's create one
QWindow fakeWindow;
fakeWindow.setFlags(Qt::X11BypassWindowManagerHint);
// it's on purpose outside the visual area
fakeWindow.setGeometry(-1, -1, 1, 1);
fakeWindow.create();
xcb_atom_t atom = screenLockerAtom();
QVERIFY(atom != XCB_ATOM_NONE);
xcb_connection_t *c = QX11Info::connection();
xcb_change_property(c, XCB_PROP_MODE_REPLACE, fakeWindow.winId(), atom, atom, 32, 0, nullptr);
xcb_flush(c);
fakeWindow.show();
// give it time to be shown
QTest::qWait(1000);
// now lets try to get a screen grab and verify it's not yet black
QVERIFY(!isBlack());
// nowadays we need to pass the window id to the lock window
lockWindow.addAllowedWindow(fakeWindow.winId());
// give it time to be shown
QTest::qWait(1000);
// now lets try to get a screen grab and verify it's black
QVERIFY(isBlack());
dummy.hide();
// destorying the fakeWindow should not remove the blanked screen
fakeWindow.destroy();
QTest::qWait(1000);
QVERIFY(isBlack());
// let's create another window and try to raise it above the lockWindow
// using a QWidget to get proper content which won't be black
QWidget widgetWindow;
widgetWindow.setGeometry(10, 10, 100, 100);
QPalette p1;
p1.setColor(QPalette::Background, Qt::blue);
widgetWindow.setAutoFillBackground(true);
widgetWindow.setPalette(p1);
widgetWindow.show();
const uint32_t values[] = { XCB_STACK_MODE_ABOVE };
xcb_configure_window(c, widgetWindow.winId(), XCB_CONFIG_WINDOW_STACK_MODE, values);
xcb_flush(c);
QTest::qWait(1000);
QVERIFY(isBlack());
lockWindow.hideLockWindow();
}
QTEST_MAIN(LockWindowTest)
#include "lockwindowtest.moc"

@ -562,7 +562,7 @@ void KSldApp::showLockWindow()
if (!m_lockWindow) {
m_lockWindow = new X11Locker();
m_lockWindow->setGlobalAccel(m_globalAccel);
connect(m_lockWindow, &X11Locker::userActivity, this,
connect(m_lockWindow, &AbstractLocker::userActivity, this,
[this]() {
if (isGraceTime()) {
unlock();
@ -570,14 +570,14 @@ void KSldApp::showLockWindow()
},
Qt::QueuedConnection
);
connect(m_lockWindow, &X11Locker::lockWindowShown, this,
connect(m_lockWindow, &AbstractLocker::lockWindowShown, this,
[this] {
m_lockState = Locked;
m_lockedTimer.restart();
emit locked();
}, Qt::QueuedConnection
);
connect(m_waylandServer, &WaylandServer::x11WindowAdded, m_lockWindow, &X11Locker::addAllowedWindow);
connect(m_waylandServer, &WaylandServer::x11WindowAdded, m_lockWindow, &AbstractLocker::addAllowedWindow);
}
m_lockWindow->showLockWindow();
XSync(QX11Info::display(), False);

@ -41,7 +41,7 @@ enum class EstablishLock {
Delayed
};
class X11Locker;
class AbstractLocker;
class WaylandServer;
class KSldApp : public QObject
@ -108,7 +108,7 @@ private:
KActionCollection *m_actionCollection;
LockState m_lockState;
QProcess *m_lockProcess;
X11Locker *m_lockWindow;
AbstractLocker *m_lockWindow;
WaylandServer *m_waylandServer;
/**
* Timer to measure how long the screen is locked.

@ -23,6 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "lockwindow.h"
#include "globalaccel.h"
#include "abstractlocker.h"
// KDE
#include <KLocalizedString>
// Qt
@ -47,50 +48,9 @@ static Atom gXA_SCREENSAVER_VERSION;
namespace ScreenLocker
{
BackgroundWindow::BackgroundWindow(X11Locker *lock)
: QRasterWindow()
, m_lock(lock)
{
setFlags(Qt::X11BypassWindowManagerHint);
}
BackgroundWindow::~BackgroundWindow() = default;
void BackgroundWindow::paintEvent(QPaintEvent* )
{
QPainter p(this);
p.fillRect(0, 0, width(), height(), Qt::black);
if (m_greeterFailure) {
auto text = ki18n("The screen locker is broken and unlocking is not possible anymore.\n"
"In order to unlock switch to a virtual terminal (e.g. Ctrl+Alt+F2),\n"
"log in and execute the command:\n\n"
"loginctl unlock-sessions\n\n"
"Afterwards switch back to the running session (Ctrl+Alt+F%1).");
text = text.subs(QString::fromLocal8Bit(qgetenv("XDG_VTNR")));
p.setPen(Qt::white);
QFont f = p.font();
f.setBold(true);
f.setPointSize(24);
p.setFont(f);
const auto screens = QGuiApplication::screens();
for (auto s : screens) {
p.drawText(s->geometry(), Qt::AlignVCenter | Qt::AlignHCenter, text.toString());
}
}
m_lock->stayOnTop();
}
void BackgroundWindow::emergencyShow()
{
m_greeterFailure = true;
update();
show();
}
X11Locker::X11Locker()
: QObject()
: AbstractLocker()
, QAbstractNativeEventFilter()
, m_background(new BackgroundWindow(this))
{
initialize();
}
@ -100,11 +60,6 @@ X11Locker::~X11Locker()
qApp->removeNativeEventFilter(this);
}
void X11Locker::emergencyShow()
{
m_background->emergencyShow();
}
void X11Locker::initialize()
{
qApp->installNativeEventFilter(this);
@ -317,8 +272,8 @@ bool X11Locker::nativeEventFilter(const QByteArray &eventType, void *message, lo
}
xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t*>(message);
const uint8_t responseType = event->response_type & ~0x80;
if (m_globalAccel && responseType == XCB_KEY_PRESS) {
if (m_globalAccel->checkKeyPress(reinterpret_cast<xcb_key_press_event_t*>(event))) {
if (globalAccel() && responseType == XCB_KEY_PRESS) {
if (globalAccel()->checkKeyPress(reinterpret_cast<xcb_key_press_event_t*>(event))) {
emit userActivity();
return true;
}

@ -23,61 +23,34 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef SCREENLOCKER_LOCKWINDOW_H
#define SCREENLOCKER_LOCKWINDOW_H
#include "abstractlocker.h"
#include <QAbstractNativeEventFilter>
#include <QRasterWindow>
#include <X11/Xlib.h>
#include <fixx11h.h>
class QTimer;
class GlobalAccel;
namespace ScreenLocker
{
class X11Locker;
class AbstractLocker;
class BackgroundWindow : public QRasterWindow
{
Q_OBJECT
public:
explicit BackgroundWindow(X11Locker *lock);
virtual ~BackgroundWindow();
void emergencyShow();
protected:
void paintEvent(QPaintEvent *) override;
private:
X11Locker *m_lock;
bool m_greeterFailure = false;
};
class X11Locker : public QObject, public QAbstractNativeEventFilter
class X11Locker : public AbstractLocker, public QAbstractNativeEventFilter
{
Q_OBJECT
public:
X11Locker();
virtual ~X11Locker();
void showLockWindow();
void hideLockWindow();
void showLockWindow() override;
void hideLockWindow() override;
void addAllowedWindow(quint32 window);
void setGlobalAccel(GlobalAccel *ga) {
m_globalAccel = ga;
}
void emergencyShow();
void addAllowedWindow(quint32 window) override;
virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override;
Q_SIGNALS:
void userActivity();
void lockWindowShown();
private Q_SLOTS:
void updateGeo();
@ -87,7 +60,7 @@ private:
void setVRoot(Window win, Window vr);
void removeVRoot(Window win);
int findWindowInfo(Window w);
void stayOnTop();
void stayOnTop() override;
struct WindowInfo
{
Window window;
@ -96,9 +69,6 @@ private:
QList<WindowInfo> m_windowInfo;
QList<WId> m_lockWindows;
QList<quint32> m_allowedWindows;
GlobalAccel *m_globalAccel = nullptr;
QScopedPointer<BackgroundWindow> m_background;
friend class BackgroundWindow;
};
}

Loading…
Cancel
Save