/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin 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 . *********************************************************************/ #include "logind.h" #include #include #include #include #include #include #include #include #include #include "utils.h" namespace KWin { const static QString s_login1Service = QStringLiteral("org.freedesktop.login1"); const static QString s_login1Path = QStringLiteral("/org/freedesktop/login1"); const static QString s_login1ManagerInterface = QStringLiteral("org.freedesktop.login1.Manager"); const static QString s_login1SessionInterface = QStringLiteral("org.freedesktop.login1.Session"); const static QString s_dbusPropertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); LogindIntegration *LogindIntegration::s_self = nullptr; LogindIntegration *LogindIntegration::create(QObject *parent) { Q_ASSERT(!s_self); s_self = new LogindIntegration(parent); return s_self; } LogindIntegration::LogindIntegration(const QDBusConnection &connection, QObject *parent) : QObject(parent) , m_bus(connection) , m_logindServiceWatcher(new QDBusServiceWatcher(s_login1Service, m_bus, QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration, this)) , m_connected(false) , m_sessionControl(false) , m_sessionActive(false) { connect(m_logindServiceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &LogindIntegration::logindServiceRegistered); connect(m_logindServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this]() { m_connected = false; emit connectedChanged(); } ); // check whether the logind service is registered QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), QStringLiteral("/"), QStringLiteral("org.freedesktop.DBus"), QStringLiteral("ListNames")); QDBusPendingReply async = m_bus.asyncCall(message); QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this); connect(callWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { QDBusPendingReply reply = *self; self->deleteLater(); if (!reply.isValid()) { return; } if (reply.value().contains(s_login1Service)) { logindServiceRegistered(); } } ); } LogindIntegration::LogindIntegration(QObject *parent) : LogindIntegration(QDBusConnection::systemBus(), parent) { } LogindIntegration::~LogindIntegration() { s_self = nullptr; } void LogindIntegration::logindServiceRegistered() { // get the current session QDBusMessage message = QDBusMessage::createMethodCall(s_login1Service, s_login1Path, s_login1ManagerInterface, QStringLiteral("GetSessionByPID")); message.setArguments(QVariantList() << (quint32) QCoreApplication::applicationPid()); QDBusPendingReply session = m_bus.asyncCall(message); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(session, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { QDBusPendingReply reply = *self; self->deleteLater(); if (m_connected) { return; } if (!reply.isValid()) { qCDebug(KWIN_CORE) << "The session is not registered with logind" << reply.error().message(); return; } m_sessionPath = reply.value().path(); qCDebug(KWIN_CORE) << "Session path:" << m_sessionPath; m_connected = true; connectSessionPropertiesChanged(); getSessionActive(); emit connectedChanged(); } ); } void LogindIntegration::connectSessionPropertiesChanged() { m_bus.connect(s_login1Service, m_sessionPath, s_dbusPropertiesInterface, QStringLiteral("PropertiesChanged"), this, SLOT(getSessionActive())); } void LogindIntegration::getSessionActive() { if (!m_connected || m_sessionPath.isEmpty()) { return; } QDBusMessage message = QDBusMessage::createMethodCall(s_login1Service, m_sessionPath, s_dbusPropertiesInterface, QStringLiteral("Get")); message.setArguments(QVariantList({s_login1SessionInterface, QStringLiteral("Active")})); QDBusPendingReply reply = m_bus.asyncCall(message); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { QDBusPendingReply reply = *self; self->deleteLater(); if (!reply.isValid()) { qCDebug(KWIN_CORE) << "Failed to get Active Property of logind session:" << reply.error().message(); return; } const bool active = reply.value().toBool(); if (m_sessionActive != active) { m_sessionActive = active; emit sessionActiveChanged(m_sessionActive); } } ); } void LogindIntegration::takeControl() { if (!m_connected || m_sessionPath.isEmpty() || m_sessionControl) { return; } QDBusMessage message = QDBusMessage::createMethodCall(s_login1Service, m_sessionPath, s_login1SessionInterface, QStringLiteral("TakeControl")); message.setArguments(QVariantList({QVariant(false)})); QDBusPendingReply session = m_bus.asyncCall(message); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(session, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *self) { QDBusPendingReply reply = *self; self->deleteLater(); if (!reply.isValid()) { qCDebug(KWIN_CORE) << "Failed to get session control" << reply.error().message(); emit hasSessionControlChanged(false); return; } qCDebug(KWIN_CORE) << "Gained session control"; m_sessionControl = true; emit hasSessionControlChanged(true); } ); } void LogindIntegration::releaseControl() { if (!m_connected || m_sessionPath.isEmpty() || !m_sessionControl) { return; } QDBusMessage message = QDBusMessage::createMethodCall(s_login1Service, m_sessionPath, s_login1SessionInterface, QStringLiteral("ReleaseControl")); m_bus.asyncCall(message); m_sessionControl = false; emit hasSessionControlChanged(false); } int LogindIntegration::takeDevice(const char *path) { struct stat st; if (stat(path, &st) < 0) { qCDebug(KWIN_CORE) << "Could not stat the path"; return -1; } QDBusMessage message = QDBusMessage::createMethodCall(s_login1Service, m_sessionPath, s_login1SessionInterface, QStringLiteral("TakeDevice")); message.setArguments(QVariantList({QVariant(major(st.st_rdev)), QVariant(minor(st.st_rdev))})); // intended to be a blocking call QDBusMessage reply = m_bus.call(message); if (reply.type() == QDBusMessage::ErrorMessage) { qCDebug(KWIN_CORE) << "Could not take device" << path << ", cause: " << reply.errorMessage(); return -1; } return dup(reply.arguments().first().value().fileDescriptor()); } void LogindIntegration::releaseDevice(int fd) { struct stat st; if (fstat(fd, &st) < 0) { qCDebug(KWIN_CORE) << "Could not stat the file descriptor"; return; } QDBusMessage message = QDBusMessage::createMethodCall(s_login1Service, m_sessionPath, s_login1SessionInterface, QStringLiteral("ReleaseDevice")); message.setArguments(QVariantList({QVariant(major(st.st_rdev)), QVariant(minor(st.st_rdev))})); m_bus.asyncCall(message); } } // namespace