File indexing completed on 2024-04-21 16:13:17

0001 /*
0002     SPDX-FileCopyrightText: 1999 Martin R. Jones <mjones@kde.org>
0003     SPDX-FileCopyrightText: 2003 Oswald Buddenhagen <ossi@kde.org>
0004     SPDX-FileCopyrightText: 2008 Chani Armitage <chanika@gmail.com>
0005     SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007 SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "ksldapp.h"
0010 #include "globalaccel.h"
0011 #include "interface.h"
0012 #include "kscreensaversettings.h"
0013 #include "logind.h"
0014 #include "powermanagement_inhibition.h"
0015 #include "waylandlocker.h"
0016 #include "x11locker.h"
0017 
0018 #include "kscreenlocker_logging.h"
0019 #include <config-kscreenlocker.h>
0020 
0021 #include "config-unix.h"
0022 #include "waylandserver.h"
0023 // KDE
0024 #include <KAuthorized>
0025 #include <KGlobalAccel>
0026 #include <KIdleTime>
0027 #include <KLibexec>
0028 #include <KLocalizedString>
0029 #include <KNotification>
0030 
0031 // Qt
0032 #include <QAction>
0033 #include <QFile>
0034 #include <QKeyEvent>
0035 #include <QProcess>
0036 #include <QTimer>
0037 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0038 #include <private/qtx11extras_p.h>
0039 #else
0040 #include <QX11Info>
0041 #endif
0042 // X11
0043 #include <X11/Xlib.h>
0044 #include <xcb/xcb.h>
0045 #if X11_Xinput_FOUND
0046 #include <X11/extensions/XInput2.h>
0047 #endif
0048 // other
0049 #include <signal.h>
0050 #include <unistd.h>
0051 
0052 #include <sys/socket.h>
0053 #include <sys/types.h>
0054 
0055 namespace ScreenLocker
0056 {
0057 static const QString s_qtQuickBackend = QStringLiteral("QT_QUICK_BACKEND");
0058 
0059 static KSldApp *s_instance = nullptr;
0060 
0061 KSldApp *KSldApp::self()
0062 {
0063     if (!s_instance) {
0064         s_instance = new KSldApp();
0065     }
0066 
0067     return s_instance;
0068 }
0069 
0070 KSldApp::KSldApp(QObject *parent)
0071     : QObject(parent)
0072     , m_lockState(Unlocked)
0073     , m_lockProcess(nullptr)
0074     , m_lockWindow(nullptr)
0075     , m_waylandServer(new WaylandServer(this))
0076     , m_lockedTimer(QElapsedTimer())
0077     , m_idleId(0)
0078     , m_lockGrace(0)
0079     , m_inGraceTime(false)
0080     , m_graceTimer(new QTimer(this))
0081     , m_inhibitCounter(0)
0082     , m_logind(nullptr)
0083     , m_greeterEnv(QProcessEnvironment::systemEnvironment())
0084     , m_powerManagementInhibition(new PowerManagementInhibition(this))
0085 {
0086     m_isX11 = QX11Info::isPlatformX11();
0087     m_isWayland = QCoreApplication::instance()->property("platformName").toString().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive);
0088 }
0089 
0090 KSldApp::~KSldApp()
0091 {
0092 }
0093 
0094 static int s_XTimeout;
0095 static int s_XInterval;
0096 static int s_XBlanking;
0097 static int s_XExposures;
0098 
0099 void KSldApp::cleanUp()
0100 {
0101     if (m_lockProcess && m_lockProcess->state() != QProcess::NotRunning) {
0102         m_lockProcess->terminate();
0103     }
0104     delete m_lockProcess;
0105     delete m_lockWindow;
0106 
0107     // Restore X screensaver parameters
0108     XSetScreenSaver(QX11Info::display(), s_XTimeout, s_XInterval, s_XBlanking, s_XExposures);
0109 }
0110 
0111 static bool s_graceTimeKill = false;
0112 static bool s_logindExit = false;
0113 
0114 static bool hasXInput()
0115 {
0116 #if X11_Xinput_FOUND
0117     Display *dpy = QX11Info::display();
0118     int xi_opcode, event, error;
0119     // init XInput extension
0120     if (!XQueryExtension(dpy, "XInputExtension", &xi_opcode, &event, &error)) {
0121         return false;
0122     }
0123 
0124     // verify that the XInput extension is at at least version 2.0
0125     int major = 2, minor = 0;
0126     int result = XIQueryVersion(dpy, &major, &minor);
0127     if (result == BadImplementation) {
0128         // Xinput 2.2 returns BadImplementation if checked against 2.0
0129         major = 2;
0130         minor = 2;
0131         return XIQueryVersion(dpy, &major, &minor) == Success;
0132     }
0133     return result == Success;
0134 #else
0135     return false;
0136 #endif
0137 }
0138 
0139 void KSldApp::initializeX11()
0140 {
0141     m_hasXInput2 = hasXInput();
0142     // Save X screensaver parameters
0143     XGetScreenSaver(QX11Info::display(), &s_XTimeout, &s_XInterval, &s_XBlanking, &s_XExposures);
0144     // And disable it. The internal X screensaver is not used at all, but we use its
0145     // internal idle timer (and it is also used by DPMS support in X). This timer must not
0146     // be altered by this code, since e.g. resetting the counter after activating our
0147     // screensaver would prevent DPMS from activating. We use the timer merely to detect
0148     // user activity.
0149     XSetScreenSaver(QX11Info::display(), 0, s_XInterval, s_XBlanking, s_XExposures);
0150 }
0151 
0152 void KSldApp::initialize()
0153 {
0154     if (m_isX11) {
0155         initializeX11();
0156     }
0157 
0158     // Global keys
0159     if (KAuthorized::authorizeAction(QStringLiteral("lock_screen"))) {
0160         qCDebug(KSCREENLOCKER) << "Configuring Lock Action";
0161         QAction *a = new QAction(this);
0162         a->setObjectName(QStringLiteral("Lock Session"));
0163         a->setProperty("componentName", QStringLiteral("ksmserver"));
0164         a->setText(i18n("Lock Session"));
0165         KGlobalAccel::self()->setGlobalShortcut(a, KScreenSaverSettings::defaultShortcuts());
0166         connect(a, &QAction::triggered, this, [this]() {
0167             lock(EstablishLock::Immediate);
0168         });
0169     }
0170 
0171     // idle support
0172     auto idleTimeSignal = static_cast<void (KIdleTime::*)(int, int)>(&KIdleTime::timeoutReached);
0173     connect(KIdleTime::instance(), idleTimeSignal, this, [this](int identifier) {
0174         if (identifier != m_idleId) {
0175             // not our identifier
0176             return;
0177         }
0178         if (lockState() != Unlocked) {
0179             return;
0180         }
0181         if (m_inhibitCounter // either we got a direct inhibit request thru our outdated o.f.Screensaver iface ...
0182             || isFdoPowerInhibited()) { // ... or the newer one at o.f.PowerManagement.Inhibit
0183             // there is at least one process blocking the auto lock of screen locker
0184             return;
0185         }
0186         if (m_lockGrace) { // short-circuit if grace time is zero
0187             m_inGraceTime = true;
0188         } else if (m_lockGrace == -1) {
0189             m_inGraceTime = true; // if no timeout configured, grace time lasts forever
0190         }
0191 
0192         lock(EstablishLock::Delayed);
0193     });
0194 
0195     m_lockProcess = new QProcess();
0196     m_lockProcess->setProcessChannelMode(QProcess::ForwardedErrorChannel);
0197     m_lockProcess->setReadChannel(QProcess::StandardOutput);
0198     auto finishedSignal = static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished);
0199     connect(m_lockProcess, finishedSignal, this, [this](int exitCode, QProcess::ExitStatus exitStatus) {
0200         qCDebug(KSCREENLOCKER) << "Greeter process exitted with status:" << exitStatus << "exit code:" << exitCode;
0201 
0202         const bool regularExit = !exitCode && exitStatus == QProcess::NormalExit;
0203         if (regularExit || s_graceTimeKill || s_logindExit) {
0204             // unlock process finished successfully - we can remove the lock grab
0205 
0206             if (regularExit) {
0207                 qCDebug(KSCREENLOCKER) << "Unlocking now on regular exit.";
0208             } else if (s_graceTimeKill) {
0209                 qCDebug(KSCREENLOCKER) << "Unlocking anyway due to grace time.";
0210             } else {
0211                 Q_ASSERT(s_logindExit);
0212                 qCDebug(KSCREENLOCKER) << "Unlocking anyway since forced through logind.";
0213             }
0214 
0215             s_graceTimeKill = false;
0216             s_logindExit = false;
0217             doUnlock();
0218             return;
0219         }
0220 
0221         qCWarning(KSCREENLOCKER) << "Greeter process exit unregular. Restarting lock.";
0222 
0223         m_greeterCrashedCounter++;
0224         if (m_greeterCrashedCounter < 4) {
0225             // Perhaps it crashed due to a graphics driver issue, force software rendering now
0226             qCDebug(KSCREENLOCKER, "Trying to lock again with software rendering (%d/4).", m_greeterCrashedCounter);
0227             setForceSoftwareRendering(true);
0228             startLockProcess(EstablishLock::Immediate);
0229         } else if (m_lockWindow) {
0230             qCWarning(KSCREENLOCKER) << "Everything else failed. Need to put Greeter in emergency mode.";
0231             m_lockWindow->emergencyShow();
0232         } else {
0233             qCCritical(KSCREENLOCKER) << "Greeter process exitted and we could in no way recover from that!";
0234         }
0235     });
0236     connect(m_lockProcess, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
0237         if (error == QProcess::FailedToStart) {
0238             qCDebug(KSCREENLOCKER) << "Greeter Process  failed to start. Trying to directly unlock again.";
0239             doUnlock();
0240             m_waylandServer->stop();
0241             qCCritical(KSCREENLOCKER) << "Greeter Process not available";
0242         } else {
0243             qCWarning(KSCREENLOCKER) << "Greeter Process encountered an unhandled error:" << error;
0244         }
0245     });
0246     m_lockedTimer.invalidate();
0247     m_graceTimer->setSingleShot(true);
0248     connect(m_graceTimer, &QTimer::timeout, this, &KSldApp::endGraceTime);
0249     // create our D-Bus interface
0250     new Interface(this);
0251 
0252     // connect to logind
0253     m_logind = new LogindIntegration(this);
0254     connect(m_logind, &LogindIntegration::requestLock, this, [this]() {
0255         lock(EstablishLock::Immediate);
0256     });
0257     connect(m_logind, &LogindIntegration::requestUnlock, this, [this]() {
0258         if (lockState() == Locked || lockState() == AcquiringLock) {
0259             if (m_lockProcess->state() != QProcess::NotRunning) {
0260                 s_logindExit = true;
0261                 m_lockProcess->terminate();
0262             } else {
0263                 doUnlock();
0264             }
0265         }
0266     });
0267     connect(m_logind, &LogindIntegration::prepareForSleep, this, [this](bool goingToSleep) {
0268         if (!goingToSleep) {
0269             // not interested in doing anything on wakeup
0270             return;
0271         }
0272         if (KScreenSaverSettings::lockOnResume()) {
0273             lock(EstablishLock::Immediate);
0274         }
0275     });
0276     connect(m_logind, &LogindIntegration::inhibited, this, [this]() {
0277         // if we are already locked, we immediately remove the inhibition lock
0278         if (m_lockState == KSldApp::Locked) {
0279             m_logind->uninhibit();
0280         }
0281     });
0282     connect(m_logind, &LogindIntegration::connectedChanged, this, [this]() {
0283         if (!m_logind->isConnected()) {
0284             return;
0285         }
0286         if (m_lockState == ScreenLocker::KSldApp::Unlocked && KScreenSaverSettings::lockOnResume()) {
0287             m_logind->inhibit();
0288         }
0289         if (m_logind->isLocked()) {
0290             lock(EstablishLock::Immediate);
0291         }
0292     });
0293     connect(this, &KSldApp::locked, this, [this]() {
0294         m_logind->uninhibit();
0295         m_logind->setLocked(true);
0296         if (m_lockGrace > 0 && m_inGraceTime) {
0297             m_graceTimer->start(m_lockGrace);
0298         }
0299     });
0300     connect(this, &KSldApp::unlocked, this, [this]() {
0301         m_logind->setLocked(false);
0302         if (KScreenSaverSettings::lockOnResume()) {
0303             m_logind->inhibit();
0304         }
0305     });
0306 
0307     m_globalAccel = new GlobalAccel(this);
0308     connect(this, &KSldApp::locked, m_globalAccel, &GlobalAccel::prepare);
0309     connect(this, &KSldApp::unlocked, m_globalAccel, &GlobalAccel::release);
0310 
0311     // fallback for non-logind systems:
0312     // connect to signal emitted by Solid. This is emitted unconditionally also on logind enabled systems
0313     // ksld ignores it in case logind is used
0314     QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.Solid.PowerManagement"),
0315                                           QStringLiteral("/org/kde/Solid/PowerManagement/Actions/SuspendSession"),
0316                                           QStringLiteral("org.kde.Solid.PowerManagement.Actions.SuspendSession"),
0317                                           QStringLiteral("aboutToSuspend"),
0318                                           this,
0319                                           SLOT(solidSuspend()));
0320 
0321     configure();
0322 
0323     if (m_logind->isLocked()) {
0324         lock(EstablishLock::Immediate);
0325     }
0326     if (KScreenSaverSettings::lockOnStart()) {
0327         lock(EstablishLock::Immediate);
0328     }
0329 }
0330 
0331 void KSldApp::configure()
0332 {
0333     KScreenSaverSettings::self()->load();
0334     // idle support
0335     if (m_idleId) {
0336         KIdleTime::instance()->removeIdleTimeout(m_idleId);
0337         m_idleId = 0;
0338     }
0339     const int timeout = KScreenSaverSettings::timeout();
0340     // screen saver enabled means there is an auto lock timer
0341     // timeout > 0 is for backwards compatibility with old configs
0342     if (KScreenSaverSettings::autolock() && timeout > 0) {
0343         // timeout stored in minutes
0344         m_idleId = KIdleTime::instance()->addIdleTimeout(timeout * 1000 * 60);
0345     }
0346     if (KScreenSaverSettings::lock()) {
0347         // lockGrace is stored in seconds
0348         m_lockGrace = KScreenSaverSettings::lockGrace() * 1000;
0349     } else {
0350         m_lockGrace = -1;
0351     }
0352     if (m_logind && m_logind->isConnected()) {
0353         if (KScreenSaverSettings::lockOnResume() && !m_logind->isInhibited()) {
0354             m_logind->inhibit();
0355         } else if (!KScreenSaverSettings::lockOnResume() && m_logind->isInhibited()) {
0356             m_logind->uninhibit();
0357         }
0358     }
0359 }
0360 
0361 void KSldApp::lock(EstablishLock establishLock, int attemptCount)
0362 {
0363     if (lockState() != Unlocked) {
0364         // already locked or acquiring lock, no need to lock again
0365         // but make sure it's really locked
0366         endGraceTime();
0367         if (establishLock == EstablishLock::Immediate) {
0368             // signal the greeter to switch to immediateLock mode
0369             kill(m_lockProcess->processId(), SIGUSR1);
0370         }
0371         return;
0372     }
0373 
0374     if (attemptCount == 0) {
0375         Q_EMIT aboutToLock();
0376     }
0377 
0378     qCDebug(KSCREENLOCKER) << "lock called";
0379     if (!establishGrab()) {
0380         if (attemptCount < 3) {
0381             qCWarning(KSCREENLOCKER) << "Could not establish screen lock. Trying again in 10ms";
0382             QTimer::singleShot(10, this, [=]() {
0383                 lock(establishLock, attemptCount + 1);
0384             });
0385         } else {
0386             qCCritical(KSCREENLOCKER) << "Could not establish screen lock";
0387         }
0388         return;
0389     }
0390 
0391     KNotification::event(QStringLiteral("locked"), i18n("Screen locked"), QPixmap(), nullptr, KNotification::CloseOnTimeout, QStringLiteral("ksmserver"));
0392 
0393     // blank the screen
0394     showLockWindow();
0395 
0396     m_lockState = AcquiringLock;
0397 
0398     setForceSoftwareRendering(false);
0399     // start unlock screen process
0400     startLockProcess(establishLock);
0401     Q_EMIT lockStateChanged();
0402 }
0403 
0404 /*
0405  * Forward declarations:
0406  * Only called from KSldApp::establishGrab(). Using from somewhere else is incorrect usage!
0407  **/
0408 static bool grabKeyboard();
0409 static bool grabMouse();
0410 
0411 class XServerGrabber
0412 {
0413 public:
0414     XServerGrabber()
0415     {
0416         xcb_grab_server(QX11Info::connection());
0417     }
0418     ~XServerGrabber()
0419     {
0420         xcb_ungrab_server(QX11Info::connection());
0421         xcb_flush(QX11Info::connection());
0422     }
0423 };
0424 
0425 bool KSldApp::establishGrab()
0426 {
0427     if (m_isWayland) {
0428         return m_waylandFd >= 0;
0429     }
0430     if (!m_isX11) {
0431         return true;
0432     }
0433     XSync(QX11Info::display(), False);
0434     XServerGrabber serverGrabber;
0435     if (!grabKeyboard()) {
0436         return false;
0437     }
0438 
0439     if (!grabMouse()) {
0440         XUngrabKeyboard(QX11Info::display(), CurrentTime);
0441         XFlush(QX11Info::display());
0442         return false;
0443     }
0444 
0445 #if X11_Xinput_FOUND
0446     if (m_hasXInput2) {
0447         // get all devices
0448         Display *dpy = QX11Info::display();
0449         int numMasters;
0450         XIDeviceInfo *masters = XIQueryDevice(dpy, XIAllMasterDevices, &numMasters);
0451         bool success = true;
0452         for (int i = 0; i < numMasters; ++i) {
0453             // ignoring core pointer and core keyboard as we already grabbed them
0454             if (qstrcmp(masters[i].name, "Virtual core pointer") == 0) {
0455                 continue;
0456             }
0457             if (qstrcmp(masters[i].name, "Virtual core keyboard") == 0) {
0458                 continue;
0459             }
0460             XIEventMask mask;
0461             uchar bitmask[] = {0, 0};
0462             mask.deviceid = masters[i].deviceid;
0463             mask.mask = bitmask;
0464             mask.mask_len = sizeof(bitmask);
0465             XISetMask(bitmask, XI_ButtonPress);
0466             XISetMask(bitmask, XI_ButtonRelease);
0467             XISetMask(bitmask, XI_Motion);
0468             XISetMask(bitmask, XI_Enter);
0469             XISetMask(bitmask, XI_Leave);
0470             const int result = XIGrabDevice(dpy,
0471                                             masters[i].deviceid,
0472                                             QX11Info::appRootWindow(),
0473                                             XCB_TIME_CURRENT_TIME,
0474                                             XCB_CURSOR_NONE,
0475                                             XIGrabModeAsync,
0476                                             XIGrabModeAsync,
0477                                             True,
0478                                             &mask);
0479             if (result != XIGrabSuccess) {
0480                 success = false;
0481                 break;
0482             }
0483         }
0484         if (!success) {
0485             // ungrab all devices again
0486             for (int i = 0; i < numMasters; ++i) {
0487                 XIUngrabDevice(dpy, masters[i].deviceid, XCB_TIME_CURRENT_TIME);
0488             }
0489             xcb_connection_t *c = QX11Info::connection();
0490             xcb_ungrab_keyboard(c, XCB_CURRENT_TIME);
0491             xcb_ungrab_pointer(c, XCB_CURRENT_TIME);
0492         }
0493         XIFreeDeviceInfo(masters);
0494         XFlush(dpy);
0495         return success;
0496     }
0497 #endif
0498 
0499     return true;
0500 }
0501 
0502 static bool grabKeyboard()
0503 {
0504     int rv = XGrabKeyboard(QX11Info::display(), QX11Info::appRootWindow(), True, GrabModeAsync, GrabModeAsync, CurrentTime);
0505 
0506     return (rv == GrabSuccess);
0507 }
0508 
0509 static bool grabMouse()
0510 {
0511 #define GRABEVENTS ButtonPressMask | ButtonReleaseMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask
0512     int rv = XGrabPointer(QX11Info::display(), QX11Info::appRootWindow(), True, GRABEVENTS, GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
0513 #undef GRABEVENTS
0514 
0515     return (rv == GrabSuccess);
0516 }
0517 
0518 void KSldApp::doUnlock()
0519 {
0520     qCDebug(KSCREENLOCKER) << "Grab Released";
0521     if (m_isX11) {
0522         xcb_connection_t *c = QX11Info::connection();
0523         xcb_ungrab_keyboard(c, XCB_CURRENT_TIME);
0524         xcb_ungrab_pointer(c, XCB_CURRENT_TIME);
0525         xcb_flush(c);
0526 #if X11_Xinput_FOUND
0527         if (m_hasXInput2) {
0528             // get all devices
0529             Display *dpy = QX11Info::display();
0530             int numMasters;
0531             XIDeviceInfo *masters = XIQueryDevice(dpy, XIAllMasterDevices, &numMasters);
0532             // ungrab all devices again
0533             for (int i = 0; i < numMasters; ++i) {
0534                 XIUngrabDevice(dpy, masters[i].deviceid, XCB_TIME_CURRENT_TIME);
0535             }
0536             XIFreeDeviceInfo(masters);
0537             XFlush(dpy);
0538         }
0539 #endif
0540     }
0541     hideLockWindow();
0542     // delete the window again, to get rid of event filter
0543     delete m_lockWindow;
0544     m_lockWindow = nullptr;
0545     m_lockState = Unlocked;
0546     m_lockedTimer.invalidate();
0547     m_greeterCrashedCounter = 0;
0548     endGraceTime();
0549     m_waylandServer->stop();
0550     KNotification::event(QStringLiteral("unlocked"), i18n("Screen unlocked"), QPixmap(), nullptr, KNotification::CloseOnTimeout, QStringLiteral("ksmserver"));
0551     Q_EMIT unlocked();
0552     Q_EMIT lockStateChanged();
0553 }
0554 
0555 bool KSldApp::isFdoPowerInhibited() const
0556 {
0557     return m_powerManagementInhibition->isInhibited();
0558 }
0559 
0560 void KSldApp::setWaylandFd(int fd)
0561 {
0562     m_waylandFd = fd;
0563 }
0564 
0565 void KSldApp::startLockProcess(EstablishLock establishLock)
0566 {
0567     QProcessEnvironment env = m_greeterEnv;
0568 
0569     if (m_isWayland && m_waylandFd >= 0) {
0570         int socket = dup(m_waylandFd);
0571         if (socket >= 0) {
0572             env.insert(QStringLiteral("WAYLAND_SOCKET"), QString::number(socket));
0573         }
0574     }
0575 
0576     QStringList args;
0577     if (establishLock == EstablishLock::Immediate) {
0578         args << QStringLiteral("--immediateLock");
0579     }
0580     if (establishLock == EstablishLock::DefaultToSwitchUser) {
0581         args << QStringLiteral("--immediateLock");
0582         args << QStringLiteral("--switchuser");
0583     }
0584 
0585     if (m_lockGrace > 0) {
0586         args << QStringLiteral("--graceTime");
0587         args << QString::number(m_lockGrace);
0588     }
0589     if (m_lockGrace == -1) {
0590         args << QStringLiteral("--nolock");
0591     }
0592     if (m_forceSoftwareRendering) {
0593         env.insert(s_qtQuickBackend, QStringLiteral("software"));
0594     }
0595 
0596     // start the Wayland server
0597     int fd = m_waylandServer->start();
0598     if (fd == -1) {
0599         qCWarning(KSCREENLOCKER) << "Could not start the Wayland server.";
0600         Q_EMIT m_lockProcess->errorOccurred(QProcess::FailedToStart);
0601         return;
0602     }
0603 
0604     args << QStringLiteral("--ksldfd");
0605     args << QString::number(fd);
0606 
0607     auto greeterPath = KLibexec::path(QStringLiteral(KSCREENLOCKER_GREET_BIN_REL));
0608     if (!QFile::exists(greeterPath)) {
0609         greeterPath = KSCREENLOCKER_GREET_BIN_ABS;
0610     }
0611 
0612     m_lockProcess->setProcessEnvironment(env);
0613     m_lockProcess->start(greeterPath, args);
0614     close(fd);
0615 }
0616 
0617 void KSldApp::userActivity()
0618 {
0619     if (isGraceTime()) {
0620         unlock();
0621     }
0622     if (m_lockWindow) {
0623         m_lockWindow->userActivity();
0624     }
0625 }
0626 
0627 void KSldApp::showLockWindow()
0628 {
0629     if (!m_lockWindow) {
0630         if (m_isX11) {
0631             m_lockWindow = new X11Locker(this);
0632 
0633             connect(
0634                 m_lockWindow,
0635                 &AbstractLocker::userActivity,
0636                 m_lockWindow,
0637                 [this]() {
0638                     if (isGraceTime()) {
0639                         unlock();
0640                     }
0641                 },
0642                 Qt::QueuedConnection);
0643         }
0644 
0645         if (m_isWayland) {
0646             m_lockWindow = new WaylandLocker(this);
0647         }
0648         if (!m_lockWindow) {
0649             return;
0650         }
0651         m_lockWindow->setGlobalAccel(m_globalAccel);
0652 
0653         connect(m_lockWindow, &AbstractLocker::lockWindowShown, this, &KSldApp::lockScreenShown);
0654 
0655         connect(m_waylandServer, &WaylandServer::x11WindowAdded, m_lockWindow, &AbstractLocker::addAllowedWindow);
0656     }
0657     m_lockWindow->showLockWindow();
0658     if (m_isX11) {
0659         XSync(QX11Info::display(), False);
0660     }
0661 }
0662 
0663 void KSldApp::hideLockWindow()
0664 {
0665     if (!m_lockWindow) {
0666         return;
0667     }
0668     m_lockWindow->hideLockWindow();
0669 }
0670 
0671 uint KSldApp::activeTime() const
0672 {
0673     if (m_lockedTimer.isValid()) {
0674         return m_lockedTimer.elapsed();
0675     }
0676     return 0;
0677 }
0678 
0679 bool KSldApp::isGraceTime() const
0680 {
0681     return m_inGraceTime;
0682 }
0683 
0684 void KSldApp::endGraceTime()
0685 {
0686     m_graceTimer->stop();
0687     m_inGraceTime = false;
0688 }
0689 
0690 void KSldApp::unlock()
0691 {
0692     if (!isGraceTime()) {
0693         return;
0694     }
0695     s_graceTimeKill = true;
0696     m_lockProcess->terminate();
0697 }
0698 
0699 void KSldApp::inhibit()
0700 {
0701     ++m_inhibitCounter;
0702 }
0703 
0704 void KSldApp::uninhibit()
0705 {
0706     --m_inhibitCounter;
0707 }
0708 
0709 void KSldApp::solidSuspend()
0710 {
0711     // ignore in case that we use logind
0712     if (m_logind && m_logind->isConnected()) {
0713         return;
0714     }
0715     if (KScreenSaverSettings::lockOnResume()) {
0716         lock(EstablishLock::Immediate);
0717     }
0718 }
0719 
0720 void KSldApp::lockScreenShown()
0721 {
0722     if (m_lockState == Locked) {
0723         return;
0724     }
0725     m_lockState = Locked;
0726     m_lockedTimer.restart();
0727     Q_EMIT locked();
0728     Q_EMIT lockStateChanged();
0729 }
0730 
0731 void KSldApp::setGreeterEnvironment(const QProcessEnvironment &env)
0732 {
0733     m_greeterEnv = env;
0734     if (m_isWayland) {
0735         m_greeterEnv.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland"));
0736     }
0737 }
0738 
0739 bool KSldApp::event(QEvent *event)
0740 {
0741     if (event->type() == QEvent::KeyPress && m_globalAccel) {
0742         if (m_globalAccel->keyEvent(static_cast<QKeyEvent *>(event))) {
0743             event->setAccepted(true);
0744         }
0745     }
0746     return false;
0747 }
0748 
0749 } // namespace