/* BEGIN_COMMON_COPYRIGHT_HEADER * (c)LGPL2+ * * LXDE-Qt - a lightweight, Qt based, desktop toolset * http://razor-qt.org * * Copyright (C) 2013 Alec Moskvin * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * This library 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 * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * END_COMMON_COPYRIGHT_HEADER */ #include #include #include #include #include #include #include "screensaveradaptor.h" #include "idlenesswatcher.h" #include "x11helper.h" /* lockers: * * xlock(more) * i3lock -n * slock * alock * xtrlock */ IdlenessWatcher::IdlenessWatcher(QObject* parent): Watcher(parent), mPSettings(), mErrorNotification(tr("LxQt Idleness watcher failed to start")), mDBusWatcher(this), mInhibitorCookie(0), mIsLocked(false) { qDebug() << "Starting idlenesswatcher"; mConn = X11Helper::connection(); xcb_prefetch_extension_data(mConn, &xcb_screensaver_id); xcb_prefetch_extension_data(mConn, &xcb_dpms_id); xcb_screensaver_query_version_cookie_t verCookie = xcb_screensaver_query_version_unchecked(mConn, XCB_SCREENSAVER_MAJOR_VERSION, XCB_SCREENSAVER_MINOR_VERSION); xcb_dpms_get_version_cookie_t dpmsVerCookie = xcb_dpms_get_version_unchecked(mConn, XCB_DPMS_MAJOR_VERSION, XCB_DPMS_MINOR_VERSION); // Note that XCB is asynchronous, so we want to make requests ASAP and get the responses as late as possible. mScreen = screenOfDisplay(mConn, 0); mErrorNotification.setUrgencyHint(LxQt::Notification::UrgencyCritical); mErrorNotification.setIcon("object-unlocked"); mErrorNotification.setTimeout(0); new ScreenSaverAdaptor(this); QDBusConnection sessionBus = QDBusConnection::sessionBus(); if (!sessionBus.registerService("org.freedesktop.ScreenSaver") || !sessionBus.registerObject("/ScreenSaver", this)) { mErrorNotification.setBody(tr("D-Bus interface org.freedesktop.ScreenSaver is already registered")); mErrorNotification.update(); qWarning() << "ERROR: D-Bus interface org.freedesktop.ScreenSaver is already registered"; } mDBusWatcher.setConnection(QDBusConnection::sessionBus()); mDBusWatcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration); connect(&mTimer, SIGNAL(timeout()), SLOT(idleTimeout())); connect(&mPSettings, SIGNAL(settingsChanged()), SLOT(restartTimer())); connect(this, SIGNAL(done()), this, SLOT(restartTimer())); connect(&mDBusWatcher, SIGNAL(serviceUnregistered(QString)), SLOT(serviceUnregistered(QString))); connect(&mLockProcess, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(screenUnlocked(int,QProcess::ExitStatus))); connect(&mErrorNotification, SIGNAL(actionActivated(int)), SLOT(notificationAction(int))); // Get XCB responses ... const xcb_query_extension_reply_t* extReply = xcb_get_extension_data(mConn, &xcb_screensaver_id); const xcb_query_extension_reply_t* dpmsExtReply = xcb_get_extension_data(mConn, &xcb_dpms_id); xcb_screensaver_query_version_reply_t* verReply = xcb_screensaver_query_version_reply(mConn, verCookie, NULL); xcb_dpms_get_version_reply_t* dpmsVerReply = xcb_dpms_get_version_reply(mConn, dpmsVerCookie, NULL); if (mScreen && extReply && extReply->present && dpmsExtReply && dpmsExtReply->present && verReply && dpmsVerReply && verReply->server_major_version == XCB_SCREENSAVER_MAJOR_VERSION && verReply->server_minor_version >= XCB_SCREENSAVER_MINOR_VERSION //&& dpmsVerReply->server_major_version == XCB_DPMS_MAJOR_VERSION //&& dpmsVerReply->server_minor_version >= XCB_DPMS_MINOR_VERSION ) { free(verReply); free(dpmsVerReply); } else { mErrorNotification.setBody(tr("The X11 Screensaver extension is not usable")); mErrorNotification.update(); if (verReply) free(verReply); qCritical() << "ERROR: Can't use the X11 Screensaver Extension!"; } mErrorNotification.setActions(QStringList(tr("Configure..."))); qDebug() << "LxQt Screenlocker started."; qDebug() << "timeout:" << getMaxIdleTimeoutMs() << "ms, lock command:" << mLockCommand; restartTimer(); } xcb_screen_t* IdlenessWatcher::screenOfDisplay(xcb_connection_t* conn, int screen) { xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(conn)); for (; iter.rem; --screen, xcb_screen_next(&iter)) if (screen == 0) return iter.data; return NULL; } uint IdlenessWatcher::getIdleTimeMs() { xcb_screensaver_query_info_cookie_t infoCookie = xcb_screensaver_query_info_unchecked(mConn, mScreen->root); xcb_screensaver_query_info_reply_t* infoReply = xcb_screensaver_query_info_reply(mConn, infoCookie, NULL); if (!infoReply) { qWarning() << "Bad reply from X11 Screensaver"; return 0; } uint msSinceUserInput = infoReply->ms_since_user_input; free(infoReply); return msSinceUserInput; } uint IdlenessWatcher::getMaxIdleTimeoutMs() { return 1000 * mPSettings.getIdlenessTimeSecs(); } void IdlenessWatcher::idleTimeout() { uint msSinceUserInput = getIdleTimeMs(); if (msSinceUserInput >= getMaxIdleTimeoutMs()) { mTimer.stop(); doAction(mPSettings.getIdlenessAction()); } else mTimer.start(getMaxIdleTimeoutMs() - msSinceUserInput); } void IdlenessWatcher::restartTimer() { qDebug() << ">>> Timer Restarted, waiting: " << getMaxIdleTimeoutMs() << "msecs"; mTimer.start(getMaxIdleTimeoutMs()); } void IdlenessWatcher::screenUnlocked(int exitCode, QProcess::ExitStatus exitStatus) { mIsLocked = false; emit ActiveChanged(false); if (exitCode == 0) { restartTimer(); } else { mErrorNotification.setSummary(tr("ERROR: Screen unlocked")); if (exitStatus == QProcess::NormalExit) mErrorNotification.setBody(tr("Locking program \"%1\" exited with error code %2").arg(mLockCommand).arg(exitCode)); else mErrorNotification.setBody(tr("Locking program \"%1\" crashed with error code %2").arg(mLockCommand).arg(exitCode)); mErrorNotification.update(); mTimer.stop(); connect(&mErrorNotification, SIGNAL(notificationClosed(CloseReason)), SLOT(idleTimeout())); } } void IdlenessWatcher::notificationAction(int num) { switch (num) { case 0: // "Configure" QProcess::startDetached("lxqt-config-screenlocker"); } } void IdlenessWatcher::serviceUnregistered(const QString& service) { for (QMutableMapIterator iter(mInhibitors); iter.hasNext();) { if (iter.next().value() == service) { qDebug() << "Service unregistered:" << iter.value(); mDBusWatcher.removeWatchedService(iter.value()); iter.remove(); } } if (mInhibitors.isEmpty()) restartTimer(); } /* ---------- D-Bus methods ---------- */ void IdlenessWatcher::Lock() { // lockScreen(); } uint IdlenessWatcher::GetSessionIdleTime() { return getIdleTimeMs() / 1000; } uint IdlenessWatcher::GetActiveTime() { if (!mIsLocked) return 0; return mLockTime.secsTo(QDateTime::currentDateTime()); } bool IdlenessWatcher::GetActive() { return mIsLocked; } bool IdlenessWatcher::SetActive(bool activate) { //if (!activate) return false; //return lockScreen(); } void IdlenessWatcher::SimulateUserActivity() { restartTimer(); } uint IdlenessWatcher::Inhibit(const QString& applicationName, const QString& reasonForInhibit) { mInhibitorCookie++; QString service(this->message().service()); qDebug() << "*** Inhibit by" << applicationName << ":" << reasonForInhibit << ". Service:" << service << "->" << mInhibitorCookie; mDBusWatcher.addWatchedService(service); qDebug() << mDBusWatcher.watchedServices(); mInhibitors.insert(mInhibitorCookie, service); mTimer.stop(); return mInhibitorCookie; } void IdlenessWatcher::UnInhibit(uint cookie) { qDebug() << "*** Uninhibit" << cookie; mDBusWatcher.removeWatchedService(mInhibitors.value(cookie)); mInhibitors.remove(cookie); if (mInhibitors.isEmpty()) restartTimer(); } uint IdlenessWatcher::Throttle(const QString& applicationName, const QString& reasonForThrottle) { Q_UNUSED(applicationName); Q_UNUSED(reasonForThrottle); return 0; } void IdlenessWatcher::UnThrottle(uint cookie) { Q_UNUSED(cookie); }