diff --git a/CMakeLists.txt b/CMakeLists.txt index 368b012565..0969bc1148 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -317,14 +317,6 @@ set_package_properties(QAccessibilityClient PROPERTIES ) set(HAVE_ACCESSIBILITY ${QAccessibilityClient_FOUND}) -if(CMAKE_SYSTEM MATCHES "FreeBSD") - find_package(epoll) - set_package_properties(epoll PROPERTIES DESCRIPTION "I/O event notification facility" - TYPE REQUIRED - PURPOSE "Needed for running kwin_wayland" - ) -endif() - include(ECMQMLModules) ecm_find_qmlmodule(QtQuick 2.3) ecm_find_qmlmodule(QtQuick.Controls 1.2) diff --git a/autotests/integration/kwin_wayland_test.cpp b/autotests/integration/kwin_wayland_test.cpp index 50a603115e..284cbd643d 100644 --- a/autotests/integration/kwin_wayland_test.cpp +++ b/autotests/integration/kwin_wayland_test.cpp @@ -128,27 +128,21 @@ void WaylandTestApplication::performStartup() // first load options - done internally by a different thread createOptions(); - createSession(); + if (!platform()->initialize()) { + std::exit(1); + } createColorManager(); waylandServer()->createInternalConnection(); // try creating the Wayland Backend createInput(); - createBackend(); createPlugins(); -} -void WaylandTestApplication::createBackend() -{ - Platform *platform = kwinApp()->platform(); - connect(platform, &Platform::screensQueried, this, &WaylandTestApplication::continueStartupWithScreens); - connect(platform, &Platform::initFailed, this, - [] () { - std::cerr << "FATAL ERROR: backend failed to initialize, exiting now" << std::endl; - ::exit(1); - } - ); - platform->init(); + if (!platform()->enabledOutputs().isEmpty()) { + continueStartupWithScreens(); + } else { + connect(platform(), &Platform::screensQueried, this, &WaylandTestApplication::continueStartupWithScreens); + } } void WaylandTestApplication::continueStartupWithScreens() diff --git a/autotests/integration/kwin_wayland_test.h b/autotests/integration/kwin_wayland_test.h index fd28ce8e45..6a62de8f62 100644 --- a/autotests/integration/kwin_wayland_test.h +++ b/autotests/integration/kwin_wayland_test.h @@ -76,7 +76,6 @@ protected: void performStartup() override; private: - void createBackend(); void continueStartupWithScreens(); void continueStartupWithScene(); void finalizeStartup(); diff --git a/cmake/modules/Findepoll.cmake b/cmake/modules/Findepoll.cmake deleted file mode 100644 index 80933ce169..0000000000 --- a/cmake/modules/Findepoll.cmake +++ /dev/null @@ -1,59 +0,0 @@ -#.rest: -# FindEpoll -# -------------- -# -# Try to find epoll or epoll-shim on this system. This finds: -# - some shim on Unix like systems (FreeBSD), or -# - the kernel's epoll on Linux systems. -# -# This will define the following variables: -# -# ``epoll_FOUND`` -# True if epoll is available -# ``epoll_LIBRARIES`` -# This has to be passed to target_link_libraries() -# ``epoll_INCLUDE_DIRS`` -# This has to be passed to target_include_directories() -# -# On Linux, the libraries and include directories are empty, -# even though epoll_FOUND may be set to TRUE. This is because -# no special includes or libraries are needed. On other systems -# these may be needed to use epoll. - -#============================================================================= -# SPDX-FileCopyrightText: 2019 Tobias C. Berner -# -# SPDX-License-Identifier: BSD-2-Clause -#============================================================================= - -find_path(epoll_INCLUDE_DIRS sys/epoll.h PATH_SUFFIXES libepoll-shim) - -if(epoll_INCLUDE_DIRS) - # On Linux there is no library to link against, on the BSDs there is. - # On the BSD's, epoll is implemented through a library, libepoll-shim. - if( CMAKE_SYSTEM_NAME MATCHES "Linux") - set(epoll_FOUND TRUE) - set(epoll_LIBRARIES "") - set(epoll_INCLUDE_DIRS "") - else() - find_library(epoll_LIBRARIES NAMES epoll-shim) - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(epoll - FOUND_VAR - epoll_FOUND - REQUIRED_VARS - epoll_LIBRARIES - epoll_INCLUDE_DIRS - ) - mark_as_advanced(epoll_LIBRARIES epoll_INCLUDE_DIRS) - include(FeatureSummary) - set_package_properties(epoll PROPERTIES - URL "https://github.com/FreeBSDDesktop/epoll-shim" - DESCRIPTION "small epoll implementation using kqueue" - ) - endif() -else() - set(epoll_FOUND FALSE) -endif() - -mark_as_advanced(epoll_LIBRARIES epoll_INCLUDE_DIRS) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ed827187ba..8bc3797231 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -73,7 +73,6 @@ set(kwin_SRCS libinput/events.cpp libinput/libinput_logging.cpp linux_dmabuf.cpp - logind.cpp main.cpp modifier_only_shortcuts.cpp moving_client_x11_filter.cpp @@ -106,6 +105,11 @@ set(kwin_SRCS scripting/scripting_model.cpp scripting/scriptingutils.cpp scripting/workspace_wrapper.cpp + session.cpp + session_consolekit.cpp + session_direct.h + session_logind.cpp + session_noop.cpp shadow.cpp sm.cpp subsurfacemonitor.cpp @@ -141,24 +145,6 @@ set(kwin_SRCS qt5_add_dbus_adaptor(kwin_SRCS scripting/org.kde.kwin.Script.xml scripting/scripting.h KWin::AbstractScript) -if (HAVE_LINUX_VT_H) - set(kwin_SRCS ${kwin_SRCS} - virtual_terminal.cpp - ) - set(KWIN_TTY_PREFIX "/dev/tty") -endif() - -if(CMAKE_SYSTEM MATCHES "FreeBSD") - # We know it has epoll, so supports VT as well - set(kwin_SRCS ${kwin_SRCS} - virtual_terminal.cpp - ) - set(KWIN_TTY_PREFIX "/dev/ttyv") -endif() -if(KWIN_TTY_PREFIX) - set_source_files_properties(virtual_terminal.cpp PROPERTIES COMPILE_DEFINITIONS KWIN_TTY_PREFIX="${KWIN_TTY_PREFIX}") -endif() - kconfig_add_kcfg_files(kwin_SRCS settings.kcfgc) kconfig_add_kcfg_files(kwin_SRCS rulesettings.kcfgc) kconfig_add_kcfg_files(kwin_SRCS rulebooksettingsbase.kcfgc) @@ -238,6 +224,12 @@ target_link_libraries(kwin Threads::Threads ) +if (CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_sources(kwin PRIVATE session_direct_linux.cpp) +elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + target_sources(kwin PRIVATE session_direct_freebsd.cpp) +endif() + if (KWIN_BUILD_CMS) target_sources(kwin PRIVATE colordevice.cpp @@ -270,12 +262,6 @@ qt5_generate_dbus_interface(virtualkeyboard_dbus.h org.kde.kwin.VirtualKeyboard. generate_export_header(kwin EXPORT_FILE_NAME kwin_export.h) -if(CMAKE_SYSTEM MATCHES "FreeBSD") - # epoll is required, includes live under ${LOCALBASE}, separate library - target_include_directories(kwin PUBLIC ${epoll_INCLUDE_DIRS}) - target_link_libraries(kwin ${epoll_LIBRARIES}) -endif() - add_executable(kwin_x11 main_x11.cpp) target_link_libraries(kwin_x11 kwin KF5::Crash Qt::X11Extras) diff --git a/src/colormanager.cpp b/src/colormanager.cpp index 73db968bf2..5cfcc1cf1b 100644 --- a/src/colormanager.cpp +++ b/src/colormanager.cpp @@ -7,9 +7,9 @@ #include "colormanager.h" #include "abstract_output.h" #include "colordevice.h" -#include "logind.h" #include "main.h" #include "platform.h" +#include "session.h" #include "utils.h" namespace KWin @@ -27,17 +27,17 @@ ColorManager::ColorManager(QObject *parent) : QObject(parent) , d(new ColorManagerPrivate) { - const QVector outputs = kwinApp()->platform()->enabledOutputs(); + Platform *platform = kwinApp()->platform(); + Session *session = platform->session(); + + const QVector outputs = platform->enabledOutputs(); for (AbstractOutput *output : outputs) { handleOutputEnabled(output); } - connect(kwinApp()->platform(), &Platform::outputEnabled, - this, &ColorManager::handleOutputEnabled); - connect(kwinApp()->platform(), &Platform::outputDisabled, - this, &ColorManager::handleOutputDisabled); - connect(LogindIntegration::self(), &LogindIntegration::sessionActiveChanged, - this, &ColorManager::handleSessionActiveChanged); + connect(platform, &Platform::outputEnabled, this, &ColorManager::handleOutputEnabled); + connect(platform, &Platform::outputDisabled, this, &ColorManager::handleOutputDisabled); + connect(session, &Session::activeChanged, this, &ColorManager::handleSessionActiveChanged); } ColorManager::~ColorManager() diff --git a/src/input.cpp b/src/input.cpp index 2287c33811..bd5ffc9fec 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -15,9 +15,9 @@ #include "input_event.h" #include "input_event_spy.h" #include "keyboard_input.h" -#include "logind.h" #include "main.h" #include "pointer_input.h" +#include "session.h" #include "tablet_input.h" #include "touch_hide_cursor_spy.h" #include "touch_input.h" @@ -252,7 +252,7 @@ public: if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) { const xkb_keysym_t keysym = event->nativeVirtualKey(); if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) { - LogindIntegration::self()->switchVirtualTerminal(keysym - XKB_KEY_XF86Switch_VT_1 + 1); + kwinApp()->platform()->session()->switchTo(keysym - XKB_KEY_XF86Switch_VT_1 + 1); return true; } } @@ -2012,23 +2012,7 @@ InputRedirection::InputRedirection(QObject *parent) qRegisterMetaType(); qRegisterMetaType(); if (Application::usesLibinput()) { - if (LogindIntegration::self()->hasSessionControl()) { - setupLibInput(); - } else { - LibInput::Connection::createThread(); - if (LogindIntegration::self()->isConnected()) { - LogindIntegration::self()->takeControl(); - } else { - connect(LogindIntegration::self(), &LogindIntegration::connectedChanged, LogindIntegration::self(), &LogindIntegration::takeControl); - } - connect(LogindIntegration::self(), &LogindIntegration::hasSessionControlChanged, this, - [this] (bool sessionControl) { - if (sessionControl) { - setupLibInput(); - } - } - ); - } + setupLibInput(); } connect(kwinApp(), &Application::workspaceCreated, this, &InputRedirection::setupWorkspace); } @@ -2188,13 +2172,15 @@ void InputRedirection::setupWorkspace() m_touch->init(); m_tablet->init(); } + setupTouchpadShortcuts(); setupInputFilters(); } void InputRedirection::setupInputFilters() { const bool hasGlobalShortcutSupport = !waylandServer() || waylandServer()->hasGlobalShortcutSupport(); - if (LogindIntegration::self()->hasSessionControl() && hasGlobalShortcutSupport) { + if ((kwinApp()->platform()->session()->capabilities() & Session::Capability::SwitchTerminal) + && hasGlobalShortcutSupport) { installInputEventFilter(new VirtualTerminalFilter); } if (waylandServer()) { @@ -2381,21 +2367,17 @@ void InputRedirection::setupLibInput() } ); } - connect(LogindIntegration::self(), &LogindIntegration::sessionActiveChanged, m_libInput, - [this] (bool active) { - if (!active) { - m_libInput->deactivate(); - } + connect(kwinApp()->platform()->session(), &Session::activeChanged, m_libInput, [this](bool active) { + if (!active) { + m_libInput->deactivate(); } - ); + }); m_inputConfigWatcher = KConfigWatcher::create(InputConfig::self()->inputConfig()); connect(m_inputConfigWatcher.data(), &KConfigWatcher::configChanged, this, &InputRedirection::handleInputConfigChanged); reconfigure(); } - - setupTouchpadShortcuts(); } void InputRedirection::setupTouchpadShortcuts() diff --git a/src/libinput/connection.cpp b/src/libinput/connection.cpp index 6a7b5c3e5f..279f7cc451 100644 --- a/src/libinput/connection.cpp +++ b/src/libinput/connection.cpp @@ -22,7 +22,7 @@ #endif #include "input_event.h" -#include "logind.h" +#include "session.h" #include "udev.h" #include "libinput_logging.h" @@ -133,8 +133,9 @@ Connection *Connection::create(QObject *parent) s_context = nullptr; return nullptr; } - if (!s_context->assignSeat(LogindIntegration::self()->seat().toUtf8().constData())) { - qCWarning(KWIN_LIBINPUT) << "Failed to assign seat" << LogindIntegration::self()->seat(); + const QString seat = kwinApp()->platform()->session()->seat(); + if (!s_context->assignSeat(seat.toUtf8().constData())) { + qCWarning(KWIN_LIBINPUT) << "Failed to assign seat" << seat; delete s_context; s_context = nullptr; return nullptr; @@ -194,20 +195,17 @@ void Connection::doSetup() m_notifier = new QSocketNotifier(m_input->fileDescriptor(), QSocketNotifier::Read, this); connect(m_notifier, &QSocketNotifier::activated, this, &Connection::handleEvent); - LogindIntegration *logind = LogindIntegration::self(); - connect(logind, &LogindIntegration::sessionActiveChanged, this, - [this](bool active) { - if (active) { - if (!m_input->isSuspended()) { - return; - } - m_input->resume(); - wasSuspended = true; - } else { - deactivate(); + connect(kwinApp()->platform()->session(), &Session::activeChanged, this, [this](bool active) { + if (active) { + if (!m_input->isSuspended()) { + return; } + m_input->resume(); + wasSuspended = true; + } else { + deactivate(); } - ); + }); handleEvent(); } diff --git a/src/libinput/context.cpp b/src/libinput/context.cpp index 9fbaa5438c..ec31494f14 100644 --- a/src/libinput/context.cpp +++ b/src/libinput/context.cpp @@ -10,7 +10,9 @@ #include "events.h" #include "libinput_logging.h" -#include "logind.h" +#include "main.h" +#include "platform.h" +#include "session.h" #include "udev.h" #include @@ -97,9 +99,7 @@ void Context::closeRestrictedCallBack(int fd, void *user_data) int Context::openRestricted(const char *path, int flags) { - LogindIntegration *logind = LogindIntegration::self(); - Q_ASSERT(logind); - int fd = logind->takeDevice(path); + int fd = kwinApp()->platform()->session()->openRestricted(path); if (fd < 0) { // failed return fd; @@ -143,9 +143,7 @@ int Context::openRestricted(const char *path, int flags) void Context::closeRestricted(int fd) { - LogindIntegration *logind = LogindIntegration::self(); - Q_ASSERT(logind); - logind->releaseDevice(fd); + kwinApp()->platform()->session()->closeRestricted(fd); } Event *Context::event() diff --git a/src/logind.cpp b/src/logind.cpp deleted file mode 100644 index cf5266b68b..0000000000 --- a/src/logind.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2014 Martin Gräßlin - - SPDX-License-Identifier: GPL-2.0-or-later -*/ -#include "logind.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#if HAVE_SYS_SYSMACROS_H -#include -#endif -#ifndef major -#include -#endif -#include -#include -#include "utils.h" - -struct DBusLogindSeat { - QString name; - QDBusObjectPath path; -}; - -QDBusArgument &operator<<(QDBusArgument &argument, const DBusLogindSeat &seat) -{ - argument.beginStructure(); - argument << seat.name << seat.path ; - argument.endStructure(); - return argument; -} - -const QDBusArgument &operator>>(const QDBusArgument &argument, DBusLogindSeat &seat) -{ - argument.beginStructure(); - argument >> seat.name >> seat.path; - argument.endStructure(); - return argument; -} - -Q_DECLARE_METATYPE(DBusLogindSeat) - -namespace KWin -{ - -const static QString s_login1Name = QStringLiteral("logind"); -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_login1SeatInterface = QStringLiteral("org.freedesktop.login1.Seat"); -const static QString s_login1SessionInterface = QStringLiteral("org.freedesktop.login1.Session"); -const static QString s_login1ActiveProperty = QStringLiteral("Active"); - -const static QString s_ck2Name = QStringLiteral("ConsoleKit"); -const static QString s_ck2Service = QStringLiteral("org.freedesktop.ConsoleKit"); -const static QString s_ck2Path = QStringLiteral("/org/freedesktop/ConsoleKit/Manager"); -const static QString s_ck2ManagerInterface = QStringLiteral("org.freedesktop.ConsoleKit.Manager"); -const static QString s_ck2SeatInterface = QStringLiteral("org.freedesktop.ConsoleKit.Seat"); -const static QString s_ck2SessionInterface = QStringLiteral("org.freedesktop.ConsoleKit.Session"); -const static QString s_ck2ActiveProperty = QStringLiteral("active"); - -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_connected(false) - , m_sessionControl(false) - , m_sessionActive(false) -{ - // 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)) { - setupSessionController(SessionControllerLogind); - } else if (reply.value().contains(s_ck2Service)) { - setupSessionController(SessionControllerConsoleKit); - } - - } - ); -} - -LogindIntegration::LogindIntegration(QObject *parent) - : LogindIntegration(QDBusConnection::systemBus(), parent) -{ -} - -LogindIntegration::~LogindIntegration() -{ - s_self = nullptr; -} - -void LogindIntegration::setupSessionController(SessionController controller) -{ - if (controller == SessionControllerLogind) { - // We have the logind serivce, set it up and use it - m_sessionControllerName = s_login1Name; - m_sessionControllerService = s_login1Service; - m_sessionControllerPath = s_login1Path; - m_sessionControllerManagerInterface = s_login1ManagerInterface; - m_sessionControllerSeatInterface = s_login1SeatInterface; - m_sessionControllerSessionInterface = s_login1SessionInterface; - m_sessionControllerActiveProperty = s_login1ActiveProperty; - m_logindServiceWatcher = new QDBusServiceWatcher(m_sessionControllerService, - m_bus, - QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration, - this); - connect(m_logindServiceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &LogindIntegration::logindServiceRegistered); - connect(m_logindServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, - [this]() { - m_connected = false; - emit connectedChanged(); - } - ); - logindServiceRegistered(); - } else if (controller == SessionControllerConsoleKit) { - // We have the ConsoleKit serivce, set it up and use it - m_sessionControllerName = s_ck2Name; - m_sessionControllerService = s_ck2Service; - m_sessionControllerPath = s_ck2Path; - m_sessionControllerManagerInterface = s_ck2ManagerInterface; - m_sessionControllerSeatInterface = s_ck2SeatInterface; - m_sessionControllerSessionInterface = s_ck2SessionInterface; - m_sessionControllerActiveProperty = s_ck2ActiveProperty; - m_logindServiceWatcher = new QDBusServiceWatcher(m_sessionControllerService, - m_bus, - QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration, - this); - connect(m_logindServiceWatcher, &QDBusServiceWatcher::serviceRegistered, this, &LogindIntegration::logindServiceRegistered); - connect(m_logindServiceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, - [this]() { - m_connected = false; - emit connectedChanged(); - } - ); - logindServiceRegistered(); - } -} - -void LogindIntegration::logindServiceRegistered() -{ - const QByteArray sessionId = qgetenv("XDG_SESSION_ID"); - QString methodName; - QVariantList args; - if (sessionId.isEmpty()) { - methodName = QStringLiteral("GetSessionByPID"); - args << (quint32) QCoreApplication::applicationPid(); - } else { - methodName = QStringLiteral("GetSession"); - args << QString::fromLocal8Bit(sessionId); - } - // get the current session - QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService, - m_sessionControllerPath, - m_sessionControllerManagerInterface, - methodName); - message.setArguments(args); - 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 " << m_sessionControllerName << " " << reply.error().message(); - return; - } - m_sessionPath = reply.value().path(); - qCDebug(KWIN_CORE) << "Session path:" << m_sessionPath; - m_connected = true; - connectSessionPropertiesChanged(); - // activate the session, in case we are not on it - QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService, - m_sessionPath, - m_sessionControllerSessionInterface, - QStringLiteral("Activate")); - // blocking on purpose - m_bus.call(message); - getSeat(); - getSessionActive(); - getVirtualTerminal(); - - emit connectedChanged(); - } - ); - - m_bus.connect(m_sessionControllerService, - m_sessionControllerPath, - m_sessionControllerManagerInterface, - QStringLiteral("PrepareForSleep"), - this, - SIGNAL(prepareForSleep(bool))); -} - -void LogindIntegration::connectSessionPropertiesChanged() -{ - m_bus.connect(m_sessionControllerService, - m_sessionPath, - s_dbusPropertiesInterface, - QStringLiteral("PropertiesChanged"), - this, - SLOT(getSessionActive())); - m_bus.connect(m_sessionControllerService, - m_sessionPath, - s_dbusPropertiesInterface, - QStringLiteral("PropertiesChanged"), - this, - SLOT(getVirtualTerminal())); -} - -void LogindIntegration::getSessionActive() -{ - if (!m_connected || m_sessionPath.isEmpty()) { - return; - } - QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService, - m_sessionPath, - s_dbusPropertiesInterface, - QStringLiteral("Get")); - message.setArguments(QVariantList({m_sessionControllerSessionInterface, m_sessionControllerActiveProperty})); - 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 " << m_sessionControllerName << " session:" << reply.error().message(); - return; - } - const bool active = reply.value().toBool(); - if (m_sessionActive != active) { - m_sessionActive = active; - emit sessionActiveChanged(m_sessionActive); - } - } - ); -} - -void LogindIntegration::getVirtualTerminal() -{ - if (!m_connected || m_sessionPath.isEmpty()) { - return; - } - QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService, - m_sessionPath, - s_dbusPropertiesInterface, - QStringLiteral("Get")); - message.setArguments(QVariantList({m_sessionControllerSessionInterface, QStringLiteral("VTNr")})); - 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 VTNr Property of " << m_sessionControllerName << " session:" << reply.error().message(); - return; - } - const int vt = reply.value().toUInt(); - if (m_vt != (int)vt) { - m_vt = vt; - emit virtualTerminalChanged(m_vt); - } - } - ); -} - -void LogindIntegration::takeControl() -{ - if (!m_connected || m_sessionPath.isEmpty() || m_sessionControl) { - return; - } - static bool s_recursionCheck = false; - if (s_recursionCheck) { - return; - } - s_recursionCheck = true; - - QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService, - m_sessionPath, - m_sessionControllerSessionInterface, - 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) { - s_recursionCheck = false; - 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); - m_bus.connect(m_sessionControllerService, m_sessionPath, - m_sessionControllerSessionInterface, QStringLiteral("PauseDevice"), - this, SLOT(pauseDevice(uint,uint,QString))); - } - ); -} - -void LogindIntegration::releaseControl() -{ - if (!m_connected || m_sessionPath.isEmpty() || !m_sessionControl) { - return; - } - - QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService, - m_sessionPath, - m_sessionControllerSessionInterface, - 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(m_sessionControllerService, - m_sessionPath, - m_sessionControllerSessionInterface, - 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; - } - - // The dup syscall removes the CLOEXEC flag as a side-effect. So use fcntl's F_DUPFD_CLOEXEC cmd. - return fcntl(reply.arguments().first().value().fileDescriptor(), F_DUPFD_CLOEXEC, 0); -} - -void LogindIntegration::releaseDevice(int fd) -{ - struct stat st; - if (fstat(fd, &st) < 0) { - qCDebug(KWIN_CORE) << "Could not stat the file descriptor"; - } else { - QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService, - m_sessionPath, - m_sessionControllerSessionInterface, - QStringLiteral("ReleaseDevice")); - message.setArguments(QVariantList({QVariant(major(st.st_rdev)), QVariant(minor(st.st_rdev))})); - m_bus.asyncCall(message); - } - close(fd); -} - -void LogindIntegration::pauseDevice(uint devMajor, uint devMinor, const QString &type) -{ - if (QString::compare(type, QStringLiteral("pause"), Qt::CaseInsensitive) == 0) { - // unconditionally call complete - QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService, m_sessionPath, m_sessionControllerSessionInterface, QStringLiteral("PauseDeviceComplete")); - message.setArguments(QVariantList({QVariant(devMajor), QVariant(devMinor)})); - m_bus.asyncCall(message); - } -} - -void LogindIntegration::getSeat() -{ - if (m_sessionPath.isEmpty()) { - return; - } - qDBusRegisterMetaType(); - QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService, - m_sessionPath, - s_dbusPropertiesInterface, - QStringLiteral("Get")); - message.setArguments(QVariantList({m_sessionControllerSessionInterface, QStringLiteral("Seat")})); - 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 Seat Property of " << m_sessionControllerName << " session:" << reply.error().message(); - return; - } - DBusLogindSeat seat = qdbus_cast(reply.value().value()); - const QString seatPath = seat.path.path(); - qCDebug(KWIN_CORE) << m_sessionControllerName << " seat:" << seat.name << "/" << seatPath; - m_seatPath = seatPath; - m_seatName = seat.name; - } - ); -} - -void LogindIntegration::switchVirtualTerminal(quint32 vtNr) -{ - if (!m_connected || m_seatPath.isEmpty()) { - return; - } - QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService, - m_seatPath, - m_sessionControllerSeatInterface, - QStringLiteral("SwitchTo")); - message.setArguments(QVariantList{vtNr}); - m_bus.asyncCall(message); -} - -} // namespace diff --git a/src/logind.h b/src/logind.h deleted file mode 100644 index 9cda18e69f..0000000000 --- a/src/logind.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2014 Martin Gräßlin - - SPDX-License-Identifier: GPL-2.0-or-later -*/ -#ifndef KWIN_LOGIND_H -#define KWIN_LOGIND_H - -#include - -#include -#include - -class QDBusServiceWatcher; - -namespace KWin -{ - -class KWIN_EXPORT LogindIntegration : public QObject -{ - Q_OBJECT -public: - ~LogindIntegration() override; - - bool isConnected() const { - return m_connected; - } - bool hasSessionControl() const { - return m_sessionControl; - } - bool isActiveSession() const { - return m_sessionActive; - } - int vt() const { - return m_vt; - } - void switchVirtualTerminal(quint32 vtNr); - - void takeControl(); - void releaseControl(); - - int takeDevice(const char *path); - void releaseDevice(int fd); - - const QString seat() const { - return m_seatName; - } - -Q_SIGNALS: - void connectedChanged(); - void hasSessionControlChanged(bool); - void sessionActiveChanged(bool); - void virtualTerminalChanged(int); - void prepareForSleep(bool prepare); - -private Q_SLOTS: - void getSessionActive(); - void getVirtualTerminal(); - void pauseDevice(uint major, uint minor, const QString &type); - -private: - friend class LogindTest; - /** - * The DBusConnection argument is needed for the unit test. Logind uses the system bus - * on which the unit test's fake logind cannot register to. Thus the unit test need to - * be able to do everything over the session bus. This ctor allows the LogindTest to - * create a LogindIntegration which listens on the session bus. - */ - explicit LogindIntegration(const QDBusConnection &connection, QObject *parent = nullptr); - void logindServiceRegistered(); - void connectSessionPropertiesChanged(); - enum SessionController { - SessionControllerLogind, - SessionControllerConsoleKit, - }; - void setupSessionController(SessionController controller); - void getSeat(); - QDBusConnection m_bus; - QDBusServiceWatcher *m_logindServiceWatcher; - bool m_connected; - QString m_sessionPath; - bool m_sessionControl; - bool m_sessionActive; - int m_vt = -1; - QString m_seatName = QStringLiteral("seat0"); - QString m_seatPath; - QString m_sessionControllerName; - QString m_sessionControllerService; - QString m_sessionControllerPath; - QString m_sessionControllerManagerInterface; - QString m_sessionControllerSeatInterface; - QString m_sessionControllerSessionInterface; - QString m_sessionControllerActiveProperty; - KWIN_SINGLETON(LogindIntegration) -}; - -} - -#endif diff --git a/src/main.cpp b/src/main.cpp index 069547bd6c..c5870356ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,7 +18,6 @@ #include "composite.h" #include "cursor.h" #include "input.h" -#include "logind.h" #include "options.h" #include "pluginmanager.h" #include "screens.h" @@ -282,11 +281,6 @@ void Application::createWorkspace() emit workspaceCreated(); } -void Application::createSession() -{ - LogindIntegration::create(this); -} - void Application::createInput() { ScreenLockerWatcher::create(this); diff --git a/src/main.h b/src/main.h index 77a66b0beb..606b0669f6 100644 --- a/src/main.h +++ b/src/main.h @@ -225,7 +225,6 @@ protected: void notifyKSplash(); void notifyStarted(); - void createSession(); void createInput(); void createWorkspace(); void createAtoms(); diff --git a/src/main_wayland.cpp b/src/main_wayland.cpp index e31108e048..494c52bc7a 100644 --- a/src/main_wayland.cpp +++ b/src/main_wayland.cpp @@ -155,7 +155,11 @@ void ApplicationWayland::performStartup() } // first load options - done internally by a different thread createOptions(); - createSession(); + + if (!platform()->initialize()) { + std::exit(1); + } + createColorManager(); waylandServer()->createInternalConnection(); @@ -165,21 +169,14 @@ void ApplicationWayland::performStartup() gainRealTime(RealTimeFlags::ResetOnFork); InputMethod::create(this); - createBackend(); TabletModeManager::create(this); createPlugins(); -} -void ApplicationWayland::createBackend() -{ - connect(platform(), &Platform::screensQueried, this, &ApplicationWayland::continueStartupWithScreens); - connect(platform(), &Platform::initFailed, this, - [] () { - std::cerr << "FATAL ERROR: backend failed to initialize, exiting now" << std::endl; - QCoreApplication::exit(1); - } - ); - platform()->init(); + if (!platform()->enabledOutputs().isEmpty()) { + continueStartupWithScreens(); + } else { + connect(platform(), &Platform::screensQueried, this, &ApplicationWayland::continueStartupWithScreens); + } } void ApplicationWayland::continueStartupWithScreens() diff --git a/src/main_wayland.h b/src/main_wayland.h index 18d7fad584..ea5fceb391 100644 --- a/src/main_wayland.h +++ b/src/main_wayland.h @@ -51,7 +51,6 @@ protected: void performStartup() override; private: - void createBackend(); void continueStartupWithScreens(); void continueStartupWithScene(); void finalizeStartup(); diff --git a/src/main_x11.cpp b/src/main_x11.cpp index 6484e8f887..7e4268b6dd 100644 --- a/src/main_x11.cpp +++ b/src/main_x11.cpp @@ -230,7 +230,11 @@ void ApplicationX11::performStartup() installNativeX11EventFilter(); // first load options - done internally by a different thread createOptions(); - createSession(); + + if (!platform()->initialize()) { + std::exit(1); + } + createColorManager(); // Check whether another windowmanager is running @@ -247,15 +251,13 @@ void ApplicationX11::performStartup() } createInput(); + createWorkspace(); + createPlugins(); - connect(platform(), &Platform::screensQueried, this, &ApplicationX11::continueStartupWithScreens); - connect(platform(), &Platform::initFailed, this, - [] () { - std::cerr << "FATAL ERROR: backend failed to initialize, exiting now" << std::endl; - ::exit(1); - } - ); - platform()->init(); + Xcb::sync(); // Trigger possible errors, there's still a chance to abort + + notifyKSplash(); + notifyStarted(); }); // we need to do an XSync here, otherwise the QPA might crash us later on Xcb::sync(); @@ -264,19 +266,6 @@ void ApplicationX11::performStartup() createAtoms(); } -void ApplicationX11::continueStartupWithScreens() -{ - disconnect(platform(), &Platform::screensQueried, this, &ApplicationX11::continueStartupWithScreens); - - createWorkspace(); - createPlugins(); - - Xcb::sync(); // Trigger possible errors, there's still a chance to abort - - notifyKSplash(); - notifyStarted(); -} - bool ApplicationX11::notify(QObject* o, QEvent* e) { if (e->spontaneous() && Workspace::self()->workspaceEvent(e)) diff --git a/src/main_x11.h b/src/main_x11.h index e53053d585..a360b87d58 100644 --- a/src/main_x11.h +++ b/src/main_x11.h @@ -30,7 +30,6 @@ protected: private Q_SLOTS: void lostSelection(); - void continueStartupWithScreens(); private: void crashChecking(); diff --git a/src/platform.h b/src/platform.h index 8f8ecd9a02..712d443503 100644 --- a/src/platform.h +++ b/src/platform.h @@ -39,6 +39,7 @@ class QPainterBackend; class RenderLoop; class Scene; class ScreenEdges; +class Session; class Toplevel; #ifdef KWIN_HAVE_XRENDER_COMPOSITING class XRenderBackend; @@ -67,7 +68,8 @@ class KWIN_EXPORT Platform : public QObject public: ~Platform() override; - virtual void init() = 0; + virtual Session *session() const = 0; + virtual bool initialize() = 0; virtual OpenGLBackend *createOpenGLBackend(); virtual QPainterBackend *createQPainterBackend(); #ifdef KWIN_HAVE_XRENDER_COMPOSITING @@ -501,7 +503,6 @@ public Q_SLOTS: Q_SIGNALS: void screensQueried(); - void initFailed(); void readyChanged(bool); /** * This signal is emitted when an output has been connected. The @a output is not ready diff --git a/src/plugins/nightcolor/nightcolormanager.cpp b/src/plugins/nightcolor/nightcolormanager.cpp index 53d3a3e28c..1a93af562e 100644 --- a/src/plugins/nightcolor/nightcolormanager.cpp +++ b/src/plugins/nightcolor/nightcolormanager.cpp @@ -17,8 +17,8 @@ #include #include +#include #include -#include #include #include @@ -111,15 +111,13 @@ void NightColorManager::init() connect(ColorManager::self(), &ColorManager::deviceAdded, this, &NightColorManager::hardReset); - connect(LogindIntegration::self(), &LogindIntegration::sessionActiveChanged, this, - [this](bool active) { - if (active) { - hardReset(); - } else { - cancelAllTimers(); - } - } - ); + connect(kwinApp()->platform()->session(), &Session::activeChanged, this, [this](bool active) { + if (active) { + hardReset(); + } else { + cancelAllTimers(); + } + }); connect(m_skewNotifier, &ClockSkewNotifier::clockSkewed, this, [this]() { // check if we're resuming from suspend - in this case do a hard reset diff --git a/src/plugins/platforms/drm/drm_backend.cpp b/src/plugins/platforms/drm/drm_backend.cpp index 3f764c73ae..5a6000952e 100644 --- a/src/plugins/platforms/drm/drm_backend.cpp +++ b/src/plugins/platforms/drm/drm_backend.cpp @@ -14,10 +14,10 @@ #include "composite.h" #include "cursor.h" #include "logging.h" -#include "logind.h" #include "main.h" #include "renderloop_p.h" #include "scene_qpainter_drm_backend.h" +#include "session.h" #include "udev.h" #include "wayland_server.h" #if HAVE_GBM @@ -64,6 +64,7 @@ DrmBackend::DrmBackend(QObject *parent) : Platform(parent) , m_udev(new Udev) , m_udevMonitor(m_udev->monitor()) + , m_session(Session::create(this)) , m_dpmsFilter() { setSupportsGammaControl(true); @@ -76,27 +77,9 @@ DrmBackend::~DrmBackend() qDeleteAll(m_gpus); } -void DrmBackend::init() +Session *DrmBackend::session() const { - LogindIntegration *logind = LogindIntegration::self(); - auto takeControl = [logind, this]() { - if (logind->hasSessionControl()) { - openDrm(); - } else { - logind->takeControl(); - connect(logind, &LogindIntegration::hasSessionControlChanged, this, &DrmBackend::openDrm); - } - }; - if (logind->isConnected()) { - takeControl(); - } else { - connect(logind, &LogindIntegration::connectedChanged, this, takeControl); - } - connect(logind, &LogindIntegration::prepareForSleep, this, [this] (bool active) { - if (!active) { - turnOutputsOn(); - } - }); + return m_session; } void DrmBackend::prepareShutdown() @@ -246,29 +229,30 @@ void DrmBackend::pageFlipHandler(int fd, unsigned int frame, unsigned int sec, u renderLoopPrivate->notifyFrameCompleted(timestamp); } -void DrmBackend::openDrm() +bool DrmBackend::initialize() { - connect(LogindIntegration::self(), &LogindIntegration::sessionActiveChanged, this, &DrmBackend::activate); + connect(session(), &Session::activeChanged, this, &DrmBackend::activate); + connect(session(), &Session::awoke, this, &DrmBackend::turnOutputsOn); std::vector devices = m_udev->listGPUs(); if (devices.size() == 0) { qCWarning(KWIN_DRM) << "Did not find a GPU"; - return; + return false; } for (unsigned int gpu_index = 0; gpu_index < devices.size(); gpu_index++) { auto device = std::move(devices.at(gpu_index)); auto devNode = QByteArray(device->devNode()); - int fd = LogindIntegration::self()->takeDevice(devNode.constData()); + int fd = session()->openRestricted(devNode.constData()); if (fd < 0) { qCWarning(KWIN_DRM) << "failed to open drm device at" << devNode; - return; + return false; } // try to make a simple drm get resource call, if it fails it is not useful for us drmModeRes *resources = drmModeGetResources(fd); if (!resources) { qCDebug(KWIN_DRM) << "Skipping KMS incapable drm device node at" << devNode; - LogindIntegration::self()->releaseDevice(fd); + session()->closeRestricted(fd); continue; } drmModeFreeResources(resources); @@ -295,7 +279,7 @@ void DrmBackend::openDrm() initCursor(); if (!updateOutputs()) - return; + return false; if (m_outputs.isEmpty()) { qCDebug(KWIN_DRM) << "No connected outputs found on startup."; @@ -334,6 +318,7 @@ void DrmBackend::openDrm() } } setReady(true); + return true; } void DrmBackend::addOutput(DrmOutput *o) diff --git a/src/plugins/platforms/drm/drm_backend.h b/src/plugins/platforms/drm/drm_backend.h index 24d20c0aa8..19f72986d8 100644 --- a/src/plugins/platforms/drm/drm_backend.h +++ b/src/plugins/platforms/drm/drm_backend.h @@ -57,8 +57,8 @@ public: QPainterBackend *createQPainterBackend() override; OpenGLBackend* createOpenGLBackend() override; DmaBufTexture *createDmaBufTexture(const QSize &size) override; - - void init() override; + Session *session() const override; + bool initialize() override; void prepareShutdown() override; Outputs outputs() const override; @@ -92,7 +92,6 @@ private: void addOutput(DrmOutput* output); void removeOutput(DrmOutput* output); static void pageFlipHandler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void *data); - void openDrm(); void activate(bool active); void reactivate(); void deactivate(); @@ -107,7 +106,7 @@ private: void updateOutputsEnabled(); QScopedPointer m_udev; QScopedPointer m_udevMonitor; - + Session *m_session = nullptr; // active output pipelines (planes + crtc + encoder + connector) QVector m_outputs; // active and enabled pipelines (above + wl_output) diff --git a/src/plugins/platforms/drm/drm_gpu.cpp b/src/plugins/platforms/drm/drm_gpu.cpp index ab6019e182..c8ada81a47 100644 --- a/src/plugins/platforms/drm/drm_gpu.cpp +++ b/src/plugins/platforms/drm/drm_gpu.cpp @@ -15,7 +15,7 @@ #include "drm_object_crtc.h" #include "abstract_egl_backend.h" #include "logging.h" -#include "logind.h" +#include "session.h" #if HAVE_GBM #include "egl_gbm_backend.h" @@ -81,7 +81,7 @@ DrmGpu::~DrmGpu() qDeleteAll(m_connectors); qDeleteAll(m_planes); delete m_socketNotifier; - LogindIntegration::self()->releaseDevice(m_fd); + m_backend->session()->closeRestricted(m_fd); } clockid_t DrmGpu::presentationClock() const @@ -319,7 +319,7 @@ DrmPlane *DrmGpu::getCompatiblePlane(DrmPlane::TypeIndex typeIndex, DrmCrtc *crt void DrmGpu::dispatchEvents() { - if (!LogindIntegration::self()->isActiveSession()) { + if (!m_backend->session()->isActive()) { return; } drmEventContext context = {}; diff --git a/src/plugins/platforms/drm/drm_output.cpp b/src/plugins/platforms/drm/drm_output.cpp index 1fef950819..c3fd521c17 100644 --- a/src/plugins/platforms/drm/drm_output.cpp +++ b/src/plugins/platforms/drm/drm_output.cpp @@ -13,11 +13,11 @@ #include "composite.h" #include "cursor.h" -#include "logind.h" #include "logging.h" #include "main.h" -#include "screens.h" #include "renderloop.h" +#include "screens.h" +#include "session.h" #include "wayland_server.h" // KWayland #include @@ -744,8 +744,8 @@ bool DrmOutput::dpmsAtomicOff() bool DrmOutput::presentAtomically(DrmBuffer *buffer) { - if (!LogindIntegration::self()->isActiveSession()) { - qCWarning(KWIN_DRM) << "Logind session not active."; + if (!m_backend->session()->isActive()) { + qCWarning(KWIN_DRM) << "Refusing to present output because session is inactive"; return false; } @@ -815,7 +815,7 @@ bool DrmOutput::presentLegacy(DrmBuffer *buffer) if (m_crtc->next()) { return false; } - if (!LogindIntegration::self()->isActiveSession()) { + if (!m_backend->session()->isActive()) { m_crtc->setNext(buffer); return false; } diff --git a/src/plugins/platforms/drm/egl_stream_backend.cpp b/src/plugins/platforms/drm/egl_stream_backend.cpp index 76c520df22..e72b18019e 100644 --- a/src/plugins/platforms/drm/egl_stream_backend.cpp +++ b/src/plugins/platforms/drm/egl_stream_backend.cpp @@ -13,7 +13,6 @@ #include "drm_object_crtc.h" #include "drm_object_plane.h" #include "logging.h" -#include "logind.h" #include "options.h" #include "renderloop_p.h" #include "scene.h" diff --git a/src/plugins/platforms/fbdev/fb_backend.cpp b/src/plugins/platforms/fbdev/fb_backend.cpp index fac99e1dcf..051b2a4c47 100644 --- a/src/plugins/platforms/fbdev/fb_backend.cpp +++ b/src/plugins/platforms/fbdev/fb_backend.cpp @@ -11,11 +11,12 @@ #include "composite.h" #include "logging.h" -#include "logind.h" +#include "main.h" +#include "platform.h" #include "renderloop_p.h" #include "scene_qpainter_fb_backend.h" +#include "session.h" #include "softwarevsyncmonitor.h" -#include "virtual_terminal.h" #include "udev.h" // system #include @@ -80,6 +81,7 @@ void FramebufferOutput::vblank(std::chrono::nanoseconds timestamp) FramebufferBackend::FramebufferBackend(QObject *parent) : Platform(parent) + , m_session(Session::create(this)) { setPerScreenRenderingEnabled(true); } @@ -97,24 +99,37 @@ QPainterBackend *FramebufferBackend::createQPainterBackend() return new FramebufferQPainterBackend(this); } -void FramebufferBackend::init() +Session *FramebufferBackend::session() const +{ + return m_session; +} + +bool FramebufferBackend::initialize() { setSoftwareCursorForced(true); - LogindIntegration *logind = LogindIntegration::self(); - auto takeControl = [logind, this]() { - if (logind->hasSessionControl()) { - openFrameBuffer(); - } else { - logind->takeControl(); - connect(logind, &LogindIntegration::hasSessionControlChanged, this, &FramebufferBackend::openFrameBuffer); - } - }; - if (logind->isConnected()) { - takeControl(); - } else { - connect(logind, &LogindIntegration::connectedChanged, this, takeControl); + + QString framebufferDevice = deviceIdentifier().constData(); + if (framebufferDevice.isEmpty()) { + framebufferDevice = QString(Udev().listFramebuffers().at(0)->devNode()); } - VirtualTerminal::create(this); + int fd = open(framebufferDevice.toUtf8().constData(), O_RDWR | O_CLOEXEC); + qCDebug(KWIN_FB) << "Using frame buffer device:" << framebufferDevice; + if (fd < 0) { + qCWarning(KWIN_FB) << "failed to open frame buffer device:" << framebufferDevice; + return false; + } + m_fd = fd; + if (!handleScreenInfo()) { + qCWarning(KWIN_FB) << "failed to handle framebuffer information"; + return false; + } + initImageFormat(); + if (m_imageFormat == QImage::Format_Invalid) { + return false; + } + setReady(true); + emit screensQueried(); + return true; } int FramebufferBackend::fileDescriptor() const @@ -122,39 +137,6 @@ int FramebufferBackend::fileDescriptor() const return m_fd; } -void FramebufferBackend::openFrameBuffer() -{ - VirtualTerminal::self()->init(); - QString framebufferDevice = deviceIdentifier().constData(); - if (framebufferDevice.isEmpty()) { - framebufferDevice = QString(Udev().listFramebuffers().at(0)->devNode()); - } - int fd = LogindIntegration::self()->takeDevice(framebufferDevice.toUtf8().constData()); - qCDebug(KWIN_FB) << "Using frame buffer device:" << framebufferDevice; - if (fd < 0) { - qCWarning(KWIN_FB) << "Failed to open frame buffer device:" << framebufferDevice << "through logind, trying without"; - } - fd = open(framebufferDevice.toUtf8().constData(), O_RDWR | O_CLOEXEC); - if (fd < 0) { - qCWarning(KWIN_FB) << "failed to open frame buffer device:" << framebufferDevice; - emit initFailed(); - return; - } - m_fd = fd; - if (!handleScreenInfo()) { - qCWarning(KWIN_FB) << "failed to handle framebuffer information"; - emit initFailed(); - return; - } - initImageFormat(); - if (m_imageFormat == QImage::Format_Invalid) { - emit initFailed(); - return; - } - setReady(true); - emit screensQueried(); -} - bool FramebufferBackend::handleScreenInfo() { if (m_fd < 0) { diff --git a/src/plugins/platforms/fbdev/fb_backend.h b/src/plugins/platforms/fbdev/fb_backend.h index e6d586d07c..7f821b12c6 100644 --- a/src/plugins/platforms/fbdev/fb_backend.h +++ b/src/plugins/platforms/fbdev/fb_backend.h @@ -53,8 +53,8 @@ public: QPainterBackend *createQPainterBackend() override; QSize screenSize() const override; - - void init() override; + bool initialize() override; + Session *session() const override; int fileDescriptor() const; bool isValid() const { @@ -91,12 +91,11 @@ public: } private: - void openFrameBuffer(); bool handleScreenInfo(); void initImageFormat(); QVector m_outputs; - + Session *m_session; QByteArray m_id; struct Color { quint32 offset; diff --git a/src/plugins/platforms/fbdev/scene_qpainter_fb_backend.cpp b/src/plugins/platforms/fbdev/scene_qpainter_fb_backend.cpp index f40ef43695..f68194d1fc 100644 --- a/src/plugins/platforms/fbdev/scene_qpainter_fb_backend.cpp +++ b/src/plugins/platforms/fbdev/scene_qpainter_fb_backend.cpp @@ -9,10 +9,11 @@ #include "scene_qpainter_fb_backend.h" #include "fb_backend.h" #include "composite.h" -#include "logind.h" #include "cursor.h" +#include "main.h" +#include "platform.h" #include "renderloop.h" -#include "virtual_terminal.h" +#include "session.h" #include "vsyncmonitor.h" // Qt #include @@ -35,7 +36,7 @@ FramebufferQPainterBackend::FramebufferQPainterBackend(FramebufferBackend *backe m_backend->bytesPerLine(), m_backend->imageFormat()); m_backBuffer.fill(Qt::black); - connect(VirtualTerminal::self(), &VirtualTerminal::activeChanged, this, [this](bool active) { + connect(kwinApp()->platform()->session(), &Session::activeChanged, this, [this](bool active) { if (active) { reactivate(); } else { @@ -87,7 +88,7 @@ void FramebufferQPainterBackend::endFrame(int screenId, int mask, const QRegion Q_UNUSED(mask) Q_UNUSED(damage) - if (!LogindIntegration::self()->isActiveSession()) { + if (!kwinApp()->platform()->session()->isActive()) { return; } m_needsFullRepaint = false; diff --git a/src/plugins/platforms/virtual/virtual_backend.cpp b/src/plugins/platforms/virtual/virtual_backend.cpp index 1def00f39b..823bd6b2fa 100644 --- a/src/plugins/platforms/virtual/virtual_backend.cpp +++ b/src/plugins/platforms/virtual/virtual_backend.cpp @@ -9,6 +9,7 @@ #include "virtual_backend.h" #include "virtual_output.h" #include "scene_qpainter_virtual_backend.h" +#include "session.h" #include "wayland_server.h" #include "egl_gbm_backend.h" // Qt @@ -25,6 +26,7 @@ namespace KWin VirtualBackend::VirtualBackend(QObject *parent) : Platform(parent) + , m_session(Session::create(Session::Type::Noop, this)) { if (qEnvironmentVariableIsSet("KWIN_WAYLAND_VIRTUAL_SCREENSHOTS")) { m_screenshotDir.reset(new QTemporaryDir); @@ -49,7 +51,12 @@ VirtualBackend::~VirtualBackend() } } -void VirtualBackend::init() +Session *VirtualBackend::session() const +{ + return m_session; +} + +bool VirtualBackend::initialize() { /* * Some tests currently expect one output present at start, @@ -73,6 +80,7 @@ void VirtualBackend::init() waylandServer()->seat()->setHasTouch(true); emit screensQueried(); + return true; } QString VirtualBackend::screenshotDirPath() const diff --git a/src/plugins/platforms/virtual/virtual_backend.h b/src/plugins/platforms/virtual/virtual_backend.h index 49012e1f26..330bdbe808 100644 --- a/src/plugins/platforms/virtual/virtual_backend.h +++ b/src/plugins/platforms/virtual/virtual_backend.h @@ -30,7 +30,9 @@ class KWIN_EXPORT VirtualBackend : public Platform public: VirtualBackend(QObject *parent = nullptr); ~VirtualBackend() override; - void init() override; + + Session *session() const override; + bool initialize() override; bool saveFrames() const { return !m_screenshotDir.isNull(); @@ -63,6 +65,7 @@ private: QVector m_outputs; QVector m_outputsEnabled; QScopedPointer m_screenshotDir; + Session *m_session; }; } diff --git a/src/plugins/platforms/wayland/wayland_backend.cpp b/src/plugins/platforms/wayland/wayland_backend.cpp index 575fe8f64c..5a99d43dde 100644 --- a/src/plugins/platforms/wayland/wayland_backend.cpp +++ b/src/plugins/platforms/wayland/wayland_backend.cpp @@ -20,6 +20,7 @@ #include "logging.h" #include "renderloop_p.h" #include "scene_qpainter_wayland_backend.h" +#include "session.h" #include "wayland_output.h" #include "composite.h" @@ -438,6 +439,7 @@ void WaylandSeat::setupPointerGestures() WaylandBackend::WaylandBackend(QObject *parent) : Platform(parent) + , m_session(Session::create(Session::Type::Noop, this)) , m_display(nullptr) , m_eventQueue(new EventQueue(this)) , m_registry(new Registry(this)) @@ -449,7 +451,7 @@ WaylandBackend::WaylandBackend(QObject *parent) { setPerScreenRenderingEnabled(true); supportsOutputChanges(); - connect(this, &WaylandBackend::connectionFailed, this, &WaylandBackend::initFailed); + connect(this, &WaylandBackend::connectionFailed, qApp, &QCoreApplication::quit); #if HAVE_GBM && HAVE_WAYLAND_EGL @@ -497,7 +499,7 @@ WaylandBackend::~WaylandBackend() qCDebug(KWIN_WAYLAND_BACKEND) << "Destroyed Wayland display"; } -void WaylandBackend::init() +bool WaylandBackend::initialize() { connect(m_registry, &Registry::compositorAnnounced, this, [this](quint32 name, quint32 version) { if (version < 4) { @@ -594,6 +596,12 @@ void WaylandBackend::init() m_waylandCursor->init(); }); initConnection(); + return true; +} + +Session *WaylandBackend::session() const +{ + return m_session; } void WaylandBackend::relativeMotionHandler(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 timestamp) diff --git a/src/plugins/platforms/wayland/wayland_backend.h b/src/plugins/platforms/wayland/wayland_backend.h index 2b8b7d71cf..f681e5923c 100644 --- a/src/plugins/platforms/wayland/wayland_backend.h +++ b/src/plugins/platforms/wayland/wayland_backend.h @@ -165,7 +165,8 @@ class KWIN_EXPORT WaylandBackend : public Platform public: explicit WaylandBackend(QObject *parent = nullptr); ~WaylandBackend() override; - void init() override; + bool initialize() override; + Session *session() const override; wl_display *display(); KWayland::Client::Compositor *compositor(); KWayland::Client::SubCompositor *subCompositor(); @@ -214,6 +215,7 @@ private: void updateScreenSize(WaylandOutput *output); void relativeMotionHandler(const QSizeF &delta, const QSizeF &deltaNonAccelerated, quint64 timestamp); + Session *m_session; wl_display *m_display; KWayland::Client::EventQueue *m_eventQueue; KWayland::Client::Registry *m_registry; diff --git a/src/plugins/platforms/x11/standalone/x11_platform.cpp b/src/plugins/platforms/x11/standalone/x11_platform.cpp index 9a54a8bd66..f6eb1ba9e0 100644 --- a/src/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/src/plugins/platforms/x11/standalone/x11_platform.cpp @@ -10,6 +10,7 @@ #include "x11cursor.h" #include "x11placeholderoutput.h" #include "edge.h" +#include "session.h" #include "windowselector.h" #ifdef KWIN_HAVE_XRENDER_COMPOSITING #include "x11xrenderbackend.h" @@ -93,6 +94,7 @@ bool XrandrEventFilter::event(xcb_generic_event_t *event) X11StandalonePlatform::X11StandalonePlatform(QObject *parent) : Platform(parent) + , m_session(Session::create(Session::Type::Noop, this)) , m_updateOutputsTimer(new QTimer(this)) , m_x11Display(QX11Info::display()) , m_renderLoop(new RenderLoop(this)) @@ -132,11 +134,10 @@ X11StandalonePlatform::~X11StandalonePlatform() } } -void X11StandalonePlatform::init() +bool X11StandalonePlatform::initialize() { if (!QX11Info::isPlatformX11()) { - emit initFailed(); - return; + return false; } XRenderUtils::init(kwinApp()->x11Connection(), kwinApp()->x11RootWindow()); setReady(true); @@ -145,6 +146,12 @@ void X11StandalonePlatform::init() if (Xcb::Extensions::self()->isRandrAvailable()) { m_randrEventFilter.reset(new XrandrEventFilter(this)); } + return true; +} + +Session *X11StandalonePlatform::session() const +{ + return m_session; } OpenGLBackend *X11StandalonePlatform::createOpenGLBackend() diff --git a/src/plugins/platforms/x11/standalone/x11_platform.h b/src/plugins/platforms/x11/standalone/x11_platform.h index 37f1c59a3b..8b1b33265c 100644 --- a/src/plugins/platforms/x11/standalone/x11_platform.h +++ b/src/plugins/platforms/x11/standalone/x11_platform.h @@ -34,7 +34,8 @@ class KWIN_EXPORT X11StandalonePlatform : public Platform public: X11StandalonePlatform(QObject *parent = nullptr); ~X11StandalonePlatform() override; - void init() override; + bool initialize() override; + Session *session() const override; OpenGLBackend *createOpenGLBackend() override; #ifdef KWIN_HAVE_XRENDER_COMPOSITING @@ -91,6 +92,7 @@ private: void doUpdateOutputs(); void updateRefreshRate(); + Session *m_session; XInputIntegration *m_xinputIntegration = nullptr; QThread *m_openGLFreezeProtectionThread = nullptr; QTimer *m_openGLFreezeProtection = nullptr; diff --git a/src/plugins/platforms/x11/windowed/x11windowed_backend.cpp b/src/plugins/platforms/x11/windowed/x11windowed_backend.cpp index eeef829de9..c907ad8c19 100644 --- a/src/plugins/platforms/x11/windowed/x11windowed_backend.cpp +++ b/src/plugins/platforms/x11/windowed/x11windowed_backend.cpp @@ -14,6 +14,7 @@ #include "xcbutils.h" #include "egl_x11_backend.h" #include "screens.h" +#include "session.h" #include #include #include @@ -43,6 +44,7 @@ namespace KWin X11WindowedBackend::X11WindowedBackend(QObject *parent) : Platform(parent) + , m_session(Session::create(Session::Type::Noop, this)) { setSupportsPointerWarping(true); setPerScreenRenderingEnabled(true); @@ -64,7 +66,7 @@ X11WindowedBackend::~X11WindowedBackend() } } -void X11WindowedBackend::init() +bool X11WindowedBackend::initialize() { int screen = 0; xcb_connection_t *c = nullptr; @@ -102,11 +104,17 @@ void X11WindowedBackend::init() waylandServer()->seat()->setHasTouch(true); } emit screensQueried(); + return true; } else { - emit initFailed(); + return false; } } +Session *X11WindowedBackend::session() const +{ + return m_session; +} + void X11WindowedBackend::initXInput() { #if HAVE_X11_XINPUT diff --git a/src/plugins/platforms/x11/windowed/x11windowed_backend.h b/src/plugins/platforms/x11/windowed/x11windowed_backend.h index d830d020bf..804597a1a9 100644 --- a/src/plugins/platforms/x11/windowed/x11windowed_backend.h +++ b/src/plugins/platforms/x11/windowed/x11windowed_backend.h @@ -35,7 +35,8 @@ class KWIN_EXPORT X11WindowedBackend : public Platform public: X11WindowedBackend(QObject *parent = nullptr); ~X11WindowedBackend() override; - void init() override; + bool initialize() override; + Session *session() const override; xcb_connection_t *connection() const { return m_connection; @@ -89,6 +90,7 @@ private: void initXInput(); X11WindowedOutput *findOutput(xcb_window_t window) const; + Session *m_session; xcb_connection_t *m_connection = nullptr; xcb_screen_t *m_screen = nullptr; xcb_key_symbols_t *m_keySymbols = nullptr; diff --git a/src/session.cpp b/src/session.cpp new file mode 100644 index 0000000000..bd01e6fac8 --- /dev/null +++ b/src/session.cpp @@ -0,0 +1,52 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "session.h" +#include "session_consolekit.h" +#include "session_direct.h" +#include "session_logind.h" +#include "session_noop.h" + +namespace KWin +{ + +static const struct { + Session::Type type; + std::function createFunc; +} s_availableSessions[] = { + { Session::Type::Logind, &LogindSession::create }, + { Session::Type::ConsoleKit, &ConsoleKitSession::create }, + { Session::Type::Direct, &DirectSession::create }, + { Session::Type::Noop, &NoopSession::create }, +}; + +Session::Session(QObject *parent) + : QObject(parent) +{ +} + +Session *Session::create(QObject *parent) +{ + for (const auto &sessionInfo : s_availableSessions) { + Session *session = sessionInfo.createFunc(parent); + if (session) { + return session; + } + } + return nullptr; +} + +Session *Session::create(Type type, QObject *parent) +{ + for (const auto &sessionInfo : s_availableSessions) { + if (sessionInfo.type == type) { + return sessionInfo.createFunc(parent); + } + } + return nullptr; +} + +} // namespace KWin diff --git a/src/session.h b/src/session.h new file mode 100644 index 0000000000..c1de8b903b --- /dev/null +++ b/src/session.h @@ -0,0 +1,103 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include + +#include +#include + +namespace KWin +{ + +/** + * The Session class represents the session controlled by the compositor. + * + * The Session class provides information about the virtual terminal where the compositor + * is running and a way to open files that require special privileges, e.g. DRM devices or + * input devices. + */ +class KWIN_EXPORT Session : public QObject +{ + Q_OBJECT + +public: + /** + * This enum type is used to specify the type of the session. + */ + enum class Type { + Direct, + Noop, + ConsoleKit, + Logind, + }; + + /** + * This enum type is used to specify optional capabilities of the session. + */ + enum class Capability : uint { + SwitchTerminal = 0x1, + }; + Q_DECLARE_FLAGS(Capabilities, Capability) + + static Session *create(QObject *parent = nullptr); + static Session *create(Type type, QObject *parent = nullptr); + + /** + * Returns @c true if the session is active; otherwise returns @c false. + */ + virtual bool isActive() const = 0; + + /** + * Returns the capabilities supported by the session. + */ + virtual Capabilities capabilities() const = 0; + + /** + * Returns the seat name for the Session. + */ + virtual QString seat() const = 0; + + /** + * Returns the terminal controlled by the Session. + */ + virtual uint terminal() const = 0; + + /** + * Opens the file with the specified @a fileName. Returns the file descriptor + * of the file or @a -1 if an error has occurred. + */ + virtual int openRestricted(const QString &fileName) = 0; + + /** + * Closes a file that has been opened using the openRestricted() function. + */ + virtual void closeRestricted(int fileDescriptor) = 0; + + /** + * Switches to the specified virtual @a terminal. This function does nothing if the + * Capability::SwitchTerminal capability is unsupported. + */ + virtual void switchTo(uint terminal) = 0; + +Q_SIGNALS: + /** + * This signal is emitted when the session is resuming from suspend. + */ + void awoke(); + /** + * This signal is emitted when the active state of the session has changed. + */ + void activeChanged(bool active); + +protected: + explicit Session(QObject *parent = nullptr); +}; + +} // namespace KWin + +Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::Session::Capabilities) diff --git a/src/session_consolekit.cpp b/src/session_consolekit.cpp new file mode 100644 index 0000000000..d6c213788a --- /dev/null +++ b/src/session_consolekit.cpp @@ -0,0 +1,334 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "session_consolekit.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if HAVE_SYS_SYSMACROS_H +#include +#endif + +// Note that ConsoleKit's session api is not fully compatible with logind's session api. + +struct DBusConsoleKitSeat +{ + QString id; + QDBusObjectPath path; +}; + +QDBusArgument &operator<<(QDBusArgument &argument, const DBusConsoleKitSeat &seat) +{ + argument.beginStructure(); + argument << seat.id << seat.path; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, DBusConsoleKitSeat &seat) +{ + argument.beginStructure(); + argument >> seat.id >> seat.path; + argument.endStructure(); + return argument; +} + +Q_DECLARE_METATYPE(DBusConsoleKitSeat) + +namespace KWin +{ + +static const QString s_serviceName = QStringLiteral("org.freedesktop.ConsoleKit"); +static const QString s_propertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); +static const QString s_sessionInterface = QStringLiteral("org.freedesktop.ConsoleKit.Session"); +static const QString s_seatInterface = QStringLiteral("org.freedesktop.ConsoleKit.Seat"); +static const QString s_managerInterface = QStringLiteral("org.freedesktop.ConsoleKit.Manager"); +static const QString s_managerPath = QStringLiteral("/org/freedesktop/ConsoleKit/Manager"); + +static QString findProcessSessionPath() +{ + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, s_managerPath, + s_managerInterface, + QStringLiteral("GetSessionByPID")); + message.setArguments({ uint32_t(QCoreApplication::applicationPid()) }); + + const QDBusMessage reply = QDBusConnection::systemBus().call(message); + if (reply.type() == QDBusMessage::ErrorMessage) { + return QString(); + } + + return reply.arguments().constFirst().value().path(); +} + +static bool takeControl(const QString &sessionPath) +{ + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath, + s_sessionInterface, + QStringLiteral("TakeControl")); + message.setArguments({ false }); + + const QDBusMessage reply = QDBusConnection::systemBus().call(message); + + return reply.type() != QDBusMessage::ErrorMessage; +} + +static void releaseControl(const QString &sessionPath) +{ + const QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath, + s_sessionInterface, + QStringLiteral("ReleaseControl")); + + QDBusConnection::systemBus().asyncCall(message); +} + +static bool activate(const QString &sessionPath) +{ + const QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath, + s_sessionInterface, + QStringLiteral("Activate")); + + const QDBusMessage reply = QDBusConnection::systemBus().call(message); + + return reply.type() != QDBusMessage::ErrorMessage; +} + +ConsoleKitSession *ConsoleKitSession::create(QObject *parent) +{ + if (!QDBusConnection::systemBus().interface()->isServiceRegistered(s_serviceName)) { + return nullptr; + } + + const QString sessionPath = findProcessSessionPath(); + if (sessionPath.isEmpty()) { + qCWarning(KWIN_CORE) << "Could not determine the active graphical session"; + return nullptr; + } + + if (!activate(sessionPath)) { + qCWarning(KWIN_CORE, "Failed to activate %s session. Maybe another compositor is running?", + qPrintable(sessionPath)); + return nullptr; + } + + if (!takeControl(sessionPath)) { + qCWarning(KWIN_CORE, "Failed to take control of %s session. Maybe another compositor is running?", + qPrintable(sessionPath)); + return nullptr; + } + + ConsoleKitSession *session = new ConsoleKitSession(sessionPath, parent); + if (session->initialize()) { + return session; + } + + delete session; + return nullptr; +} + +bool ConsoleKitSession::isActive() const +{ + return m_isActive; +} + +ConsoleKitSession::Capabilities ConsoleKitSession::capabilities() const +{ + return Capability::SwitchTerminal; +} + +QString ConsoleKitSession::seat() const +{ + return m_seatId; +} + +uint ConsoleKitSession::terminal() const +{ + return m_terminal; +} + +int ConsoleKitSession::openRestricted(const QString &fileName) +{ + struct stat st; + if (stat(fileName.toUtf8(), &st) < 0) { + return -1; + } + + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_sessionInterface, + QStringLiteral("TakeDevice")); + message.setArguments({ major(st.st_rdev), minor(st.st_rdev) }); + + const QDBusMessage reply = QDBusConnection::systemBus().call(message); + if (reply.type() == QDBusMessage::ErrorMessage) { + return -1; + } + + const QDBusUnixFileDescriptor descriptor = reply.arguments().constFirst().value(); + if (!descriptor.isValid()) { + return -1; + } + + return fcntl(descriptor.fileDescriptor(), F_DUPFD_CLOEXEC, 0); +} + +void ConsoleKitSession::closeRestricted(int fileDescriptor) +{ + struct stat st; + if (fstat(fileDescriptor, &st) < 0) { + close(fileDescriptor); + return; + } + + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_sessionInterface, + QStringLiteral("ReleaseDevice")); + message.setArguments({ major(st.st_rdev), minor(st.st_rdev) }); + + QDBusConnection::systemBus().asyncCall(message); + + close(fileDescriptor); +} + +void ConsoleKitSession::switchTo(uint terminal) +{ + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_seatPath, + s_seatInterface, + QStringLiteral("SwitchTo")); + message.setArguments({ terminal }); + + QDBusConnection::systemBus().asyncCall(message); +} + +ConsoleKitSession::ConsoleKitSession(const QString &sessionPath, QObject *parent) + : Session(parent) + , m_sessionPath(sessionPath) +{ + qDBusRegisterMetaType(); +} + +ConsoleKitSession::~ConsoleKitSession() +{ + releaseControl(m_sessionPath); +} + +bool ConsoleKitSession::initialize() +{ + QDBusMessage activeMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_propertiesInterface, + QStringLiteral("Get")); + activeMessage.setArguments({ s_sessionInterface, QStringLiteral("active") }); + + QDBusMessage seatMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_propertiesInterface, + QStringLiteral("Get")); + seatMessage.setArguments({ s_sessionInterface, QStringLiteral("Seat") }); + + QDBusMessage terminalMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_propertiesInterface, + QStringLiteral("Get")); + terminalMessage.setArguments({ s_sessionInterface, QStringLiteral("VTNr") }); + + QDBusPendingReply activeReply = + QDBusConnection::systemBus().asyncCall(activeMessage); + QDBusPendingReply terminalReply = + QDBusConnection::systemBus().asyncCall(terminalMessage); + QDBusPendingReply seatReply = + QDBusConnection::systemBus().asyncCall(seatMessage); + + // We must wait until all replies have been received because the drm backend needs a + // valid seat name to properly select gpu devices, this also simplifies startup code. + activeReply.waitForFinished(); + terminalReply.waitForFinished(); + seatReply.waitForFinished(); + + if (activeReply.isError()) { + qCWarning(KWIN_CORE) << "Failed to query active session property:" << activeReply.error(); + return false; + } + if (terminalReply.isError()) { + qCWarning(KWIN_CORE) << "Failed to query VTNr session property:" << terminalReply.error(); + return false; + } + if (seatReply.isError()) { + qCWarning(KWIN_CORE) << "Failed to query Seat session property:" << seatReply.error(); + return false; + } + + m_isActive = activeReply.value().toBool(); + m_terminal = terminalReply.value().toUInt(); + + const DBusConsoleKitSeat seat = qdbus_cast(seatReply.value().value()); + m_seatId = seat.id; + m_seatPath = seat.path.path(); + + QDBusConnection::systemBus().connect(s_serviceName, s_managerPath, s_managerInterface, + QStringLiteral("PrepareForSleep"), + this, + SLOT(handlePrepareForSleep(bool))); + + QDBusConnection::systemBus().connect(s_serviceName, m_sessionPath, s_sessionInterface, + QStringLiteral("PauseDevice"), + this, + SLOT(handlePauseDevice(uint, uint, QString))); + + QDBusConnection::systemBus().connect(s_serviceName, m_sessionPath, s_propertiesInterface, + QStringLiteral("PropertiesChanged"), + this, + SLOT(handlePropertiesChanged(QString, QVariantMap))); + + return true; +} + +void ConsoleKitSession::updateActive(bool active) +{ + if (m_isActive != active) { + m_isActive = active; + emit activeChanged(active); + } +} + +void ConsoleKitSession::handlePauseDevice(uint major, uint minor, const QString &type) +{ + if (type == QLatin1String("pause")) { + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_sessionInterface, + QStringLiteral("PauseDeviceComplete")); + message.setArguments({ major, minor }); + + QDBusConnection::systemBus().asyncCall(message); + } +} + +void ConsoleKitSession::handlePropertiesChanged(const QString &interfaceName, const QVariantMap &properties) +{ + if (interfaceName == s_sessionInterface) { + const QVariant active = properties.value(QStringLiteral("active")); + if (active.isValid()) { + updateActive(active.toBool()); + } + } +} + +void ConsoleKitSession::handlePrepareForSleep(bool sleep) +{ + if (!sleep) { + emit awoke(); + } +} + +} // namespace KWin diff --git a/src/session_consolekit.h b/src/session_consolekit.h new file mode 100644 index 0000000000..e6cbdd29b2 --- /dev/null +++ b/src/session_consolekit.h @@ -0,0 +1,48 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "session.h" + +namespace KWin +{ + +class ConsoleKitSession : public Session +{ + Q_OBJECT + +public: + static ConsoleKitSession *create(QObject *parent = nullptr); + ~ConsoleKitSession() override; + + bool isActive() const override; + Capabilities capabilities() const override; + QString seat() const override; + uint terminal() const override; + int openRestricted(const QString &fileName) override; + void closeRestricted(int fileDescriptor) override; + void switchTo(uint terminal) override; + +private Q_SLOTS: + void handlePauseDevice(uint major, uint minor, const QString &type); + void handlePropertiesChanged(const QString &interfaceName, const QVariantMap &properties); + void handlePrepareForSleep(bool sleep); + +private: + explicit ConsoleKitSession(const QString &sessionPath, QObject *parent = nullptr); + + bool initialize(); + void updateActive(bool active); + + QString m_sessionPath; + QString m_seatId; + QString m_seatPath; + uint m_terminal = 0; + bool m_isActive = false; +}; + +} // namespace KWin diff --git a/src/session_direct.h b/src/session_direct.h new file mode 100644 index 0000000000..890765dc64 --- /dev/null +++ b/src/session_direct.h @@ -0,0 +1,57 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "session.h" + +#include + +#include + +namespace KWin +{ + +struct DirectSessionDevice +{ + int fd; + dev_t id; +}; + +class DirectSession : public Session +{ + Q_OBJECT + +public: + static DirectSession *create(QObject *parent = nullptr); + ~DirectSession() override; + + bool isActive() const override; + Capabilities capabilities() const override; + QString seat() const override; + uint terminal() const override; + int openRestricted(const QString &fileName) override; + void closeRestricted(int fileDescriptor) override; + void switchTo(uint terminal) override; + +private: + explicit DirectSession(QObject *parent = nullptr); + + bool setupTerminal(); + void restoreTerminal(); + void updateActive(bool active); + void processSignals(); + + QSocketNotifier *m_signalNotifier = nullptr; + QString m_seat; + QVector m_devices; + uint m_terminal = 0; + int m_ttyFd = -1; + int m_keyboardMode = 0; + bool m_isActive = false; +}; + +} // namespace KWin diff --git a/src/session_direct_freebsd.cpp b/src/session_direct_freebsd.cpp new file mode 100644 index 0000000000..2bc316ffc2 --- /dev/null +++ b/src/session_direct_freebsd.cpp @@ -0,0 +1,331 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "session_direct.h" +#include "utils.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef DRM_IOCTL_SET_MASTER +#define DRM_IOCTL_SET_MASTER _IO('d', 0x1e) +#endif +#ifndef DRM_IOCTL_DROP_MASTER +#define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f) +#endif + +#define DRM_MAJOR 226 + +#ifndef KDSKBMUTE +#define KDSKBMUTE 0x4B51 +#endif + +namespace KWin +{ + +DirectSession *DirectSession::create(QObject *parent) +{ + DirectSession *session = new DirectSession(parent); + if (session->setupTerminal()) { + return session; + } + + delete session; + return nullptr; +} + +DirectSession::DirectSession(QObject *parent) + : Session(parent) +{ + const QString seat = qEnvironmentVariable("XDG_SEAT"); + if (!seat.isEmpty()) { + m_seat = seat; + } else { + m_seat = QStringLiteral("seat0"); + } +} + +DirectSession::~DirectSession() +{ + if (m_ttyFd == -1) { + return; + } + restoreTerminal(); + close(m_ttyFd); + close(m_signalNotifier->socket()); +} + +bool DirectSession::setupTerminal() +{ + if (m_seat != QStringLiteral("seat0")) { + qCDebug(KWIN_CORE) << "Skipping VT initialization"; + return true; + } + + if (ioctl(0, VT_OPENQRY, &m_terminal)) { + qCWarning(KWIN_CORE, "Failed to get unused VT: %s", strerror(errno)); + return false; + } + + const QByteArray ttyPath = "/dev/ttyv" + QByteArray::number(m_terminal - 1); + + int fd = open(ttyPath.constData(), O_RDWR | O_NOCTTY | O_CLOEXEC); + if (fd == -1) { + qCWarning(KWIN_CORE, "Cannot open %s: %s", ttyPath.constData(), strerror(errno)); + return false; + } + auto ttyCleanup = qScopeGuard([&fd]() { close(fd); }); + + int kdMode; + if (ioctl(fd, KDGETMODE, &kdMode)) { + qCWarning(KWIN_CORE, "Failed to get the keyboard mode: %s", strerror(errno)); + return false; + } + if (kdMode != KD_TEXT) { + qCWarning(KWIN_CORE) << "tty is already in graphics mode"; + } + + ioctl(fd, VT_ACTIVATE, m_terminal); + ioctl(fd, VT_WAITACTIVE, m_terminal); + + if (ioctl(fd, KDGKBMODE, &m_keyboardMode)) { + qCWarning(KWIN_CORE, "Failed to read keyboard mode: %s", strerror(errno)); + return false; + } + + if (ioctl(fd, KDSKBMUTE, 1) && ioctl(fd, KDSKBMODE, K_CODE)) { + qCWarning(KWIN_CORE, "Failed to set K_CODE keyboard mode: %s", strerror(errno)); + return false; + } + + if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) { + qCWarning(KWIN_CORE, "Failed to set graphics mode on tty: %s", strerror(errno)); + return false; + } + + vt_mode virtualTerminalMode = {}; + virtualTerminalMode.mode = VT_PROCESS; + virtualTerminalMode.relsig = SIGUSR1; + virtualTerminalMode.acqsig = SIGUSR2; + virtualTerminalMode.frsig = SIGUSR1; // unused, but still needs to be set + + if (ioctl(fd, VT_SETMODE, &virtualTerminalMode)) { + qCWarning(KWIN_CORE, "Failed to take control of vt: %s", strerror(errno)); + return false; + } + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + sigaddset(&mask, SIGUSR2); + + if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) { + qCWarning(KWIN_CORE) << "Failed to block acquire and release tty signals"; + return false; + } + + const int queueFd = kqueue(); + if (queueFd < 0) { + qCWarning(KWIN_CORE, "Failed to create a signal queue: %s", strerror(errno)); + return false; + } + auto queueCleanup = qScopeGuard([&queueFd]() { close(queueFd); }); + + struct kevent signalEvents[2]; + EV_SET(&signalEvents[0], SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, nullptr); + EV_SET(&signalEvents[1], SIGUSR2, EVFILT_SIGNAL, EV_ADD, 0, 0, nullptr); + if (kevent(queueFd, signalEvents, 2, nullptr, 0, nullptr) == -1) { + qCWarning(KWIN_CORE, "Failed to add signals to kqueue: %s", strerror(errno)); + return false; + } + for (const struct kevent &event : signalEvents) { + if (event.flags & EV_ERROR) { + qCWarning(KWIN_CORE, "Failed to add %s to kqueue: %s", + strsignal(event.ident), strerror(event.data)); + return false; + } + } + fcntl(queueFd, F_SETFD, FD_CLOEXEC); + + m_signalNotifier = new QSocketNotifier(queueFd, QSocketNotifier::Read, this); + connect(m_signalNotifier, &QSocketNotifier::activated, + this, &DirectSession::processSignals); + + m_isActive = true; + m_ttyFd = fd; + + queueCleanup.dismiss(); + ttyCleanup.dismiss(); + + return true; +} + +void DirectSession::restoreTerminal() +{ + vt_mode virtualTerminalMode = {}; + + if (ioctl(m_ttyFd, KDSKBMUTE, 0) && ioctl(m_ttyFd, KDSKBMODE, m_keyboardMode)) { + qCWarning(KWIN_CORE, "Failed to restore keyboard mode: %s", strerror(errno)); + } + + if (ioctl(m_ttyFd, KDSETMODE, KD_TEXT)) { + qCWarning(KWIN_CORE, "Failed to set KD_TEXT mode on tty: %s", strerror(errno)); + } + + virtualTerminalMode.mode = VT_AUTO; + if (ioctl(m_ttyFd, VT_SETMODE, &virtualTerminalMode)) { + qCWarning(KWIN_CORE, "Failed to reset VT handling: %s", strerror(errno)); + } +} + +bool DirectSession::isActive() const +{ + return m_isActive; +} + +DirectSession::Capabilities DirectSession::capabilities() const +{ + return Capability::SwitchTerminal; +} + +QString DirectSession::seat() const +{ + return m_seat; +} + +uint DirectSession::terminal() const +{ + return m_terminal; +} + +static void drmSetMasterInternal(int fd) +{ + if (ioctl(fd, DRM_IOCTL_SET_MASTER, 0) == -1) { + qCWarning(KWIN_CORE) << "ioctl(DRM_IOCTL_SET_MASTER) failed:" << strerror(errno); + } +} + +static void drmDropMasterInternal(int fd) +{ + if (ioctl(fd, DRM_IOCTL_DROP_MASTER, 0) == -1) { + qCWarning(KWIN_CORE) << "ioctl(DRM_IOCTL_DROP_MASTER) failed:" << strerror(errno); + } +} + +int DirectSession::openRestricted(const QString &fileName) +{ + const int fd = open(fileName.toUtf8().constData(), + O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); + if (fd == -1) { + qCWarning(KWIN_CORE) << "open() failed:" << strerror(errno); + return -1; + } + + struct stat buf; + if (fstat(fd, &buf) == -1) { + close(fd); + qCWarning(KWIN_CORE) << "fstat() failed:" << strerror(errno); + return -1; + } + + DirectSessionDevice device; + device.fd = fd; + device.id = buf.st_rdev; + m_devices.append(device); + + if (major(device.id) == DRM_MAJOR) { + drmSetMasterInternal(device.fd); + } + + return fd; +} + +void DirectSession::closeRestricted(int fileDescriptor) +{ + auto it = std::find_if(m_devices.begin(), m_devices.end(), [&](const DirectSessionDevice &device) { + return device.fd == fileDescriptor; + }); + if (it == m_devices.end()) { + close(fileDescriptor); + return; + } + + const DirectSessionDevice device = *it; + m_devices.erase(it); + + if (major(device.id) == DRM_MAJOR) { + drmDropMasterInternal(device.fd); + } + close(fileDescriptor); +} + +void DirectSession::switchTo(uint terminal) +{ + if (m_seat == QStringLiteral("seat0")) { + ioctl(m_ttyFd, VT_ACTIVATE, terminal); + } +} + +void DirectSession::updateActive(bool active) +{ + if (m_isActive != active) { + m_isActive = active; + emit activeChanged(active); + } +} + +void DirectSession::processSignals() +{ + const int queueFd = m_signalNotifier->socket(); + + while (true) { + struct timespec ts = { 0, 0 }; + struct kevent kev; + const int eventCount = kevent(queueFd, nullptr, 0, &kev, 1, &ts); + + if (eventCount < 0) { + qCWarning(KWIN_CORE, "kevent() failed: %s", strerror(errno)); + return; + } else if (eventCount == 0) { + break; + } + + switch (kev.ident) { + case SIGUSR1: + qCDebug(KWIN_CORE) << "Releasing virtual terminal" << m_terminal; + updateActive(false); + for (const DirectSessionDevice &device : qAsConst(m_devices)) { + if (major(device.id) == DRM_MAJOR) { + drmDropMasterInternal(device.fd); + } + } + ioctl(m_ttyFd, VT_RELDISP, 1); + break; + case SIGUSR2: + qCDebug(KWIN_CORE) << "Acquiring virtual terminal" << m_terminal; + ioctl(m_ttyFd, VT_RELDISP, VT_ACKACQ); + for (const DirectSessionDevice &device : qAsConst(m_devices)) { + if (major(device.id) == DRM_MAJOR) { + drmSetMasterInternal(device.fd); + } + } + updateActive(true); + break; + } + } +} + +} // namespace KWin diff --git a/src/session_direct_linux.cpp b/src/session_direct_linux.cpp new file mode 100644 index 0000000000..79e43a1607 --- /dev/null +++ b/src/session_direct_linux.cpp @@ -0,0 +1,313 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "session_direct.h" +#include "utils.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if HAVE_SYS_SYSMACROS_H +#include +#endif + +#ifndef DRM_IOCTL_SET_MASTER +#define DRM_IOCTL_SET_MASTER _IO('d', 0x1e) +#endif +#ifndef DRM_IOCTL_DROP_MASTER +#define DRM_IOCTL_DROP_MASTER _IO('d', 0x1f) +#endif + +#define DRM_MAJOR 226 + +#ifndef KDSKBMUTE +#define KDSKBMUTE 0x4B51 +#endif + +namespace KWin +{ + +DirectSession *DirectSession::create(QObject *parent) +{ + DirectSession *session = new DirectSession(parent); + if (session->setupTerminal()) { + return session; + } + + delete session; + return nullptr; +} + +DirectSession::DirectSession(QObject *parent) + : Session(parent) +{ + const QString seat = qEnvironmentVariable("XDG_SEAT"); + if (!seat.isEmpty()) { + m_seat = seat; + } else { + m_seat = QStringLiteral("seat0"); + } +} + +DirectSession::~DirectSession() +{ + if (m_ttyFd == -1) { + return; + } + restoreTerminal(); + close(m_ttyFd); + close(m_signalNotifier->socket()); +} + +bool DirectSession::setupTerminal() +{ + if (m_seat != QStringLiteral("seat0")) { + qCDebug(KWIN_CORE) << "Skipping VT initialization"; + return true; + } + + const char *ttyPath = "/dev/tty"; + + int fd = open(ttyPath, O_RDWR | O_CLOEXEC); + if (fd == -1) { + qCWarning(KWIN_CORE, "Cannot open %s: %s", ttyPath, strerror(errno)); + return false; + } + auto cleanup = qScopeGuard([&fd]() { close(fd); }); + + vt_stat virtualTerminalStat; + if (ioctl(fd, VT_GETSTATE, &virtualTerminalStat)) { + qCWarning(KWIN_CORE) << "Failed to get current tty number"; + return false; + } + + m_terminal = virtualTerminalStat.v_active; + + int kdMode; + if (ioctl(fd, KDGETMODE, &kdMode)) { + qCWarning(KWIN_CORE, "Failed to get the keyboard mode: %s", strerror(errno)); + return false; + } + if (kdMode != KD_TEXT) { + qCWarning(KWIN_CORE) << "tty is already in graphics mode"; + } + + ioctl(fd, VT_ACTIVATE, m_terminal); + ioctl(fd, VT_WAITACTIVE, m_terminal); + + if (ioctl(fd, KDGKBMODE, &m_keyboardMode)) { + qCWarning(KWIN_CORE, "Failed to read keyboard mode: %s", strerror(errno)); + return false; + } + + if (ioctl(fd, KDSKBMUTE, 1) && ioctl(fd, KDSKBMODE, K_OFF)) { + qCWarning(KWIN_CORE, "Failed to set K_OFF keyboard mode: %s", strerror(errno)); + return false; + } + + if (ioctl(fd, KDSETMODE, KD_GRAPHICS)) { + qCWarning(KWIN_CORE, "Failed to set graphics mode on tty: %s", strerror(errno)); + return false; + } + + vt_mode virtualTerminalMode = {}; + virtualTerminalMode.mode = VT_PROCESS; + virtualTerminalMode.relsig = SIGUSR1; + virtualTerminalMode.acqsig = SIGUSR2; + + if (ioctl(fd, VT_SETMODE, &virtualTerminalMode)) { + qCWarning(KWIN_CORE, "Failed to take control of vt: %s", strerror(errno)); + return false; + } + + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + sigaddset(&mask, SIGUSR2); + + if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) { + qCWarning(KWIN_CORE) << "Failed to block acquire and release tty signals"; + return false; + } + + const int signalFd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); + if (signalFd == -1) { + qCWarning(KWIN_CORE, "Failed to create signalfd for vt handler"); + return false; + } + m_signalNotifier = new QSocketNotifier(signalFd, QSocketNotifier::Read, this); + connect(m_signalNotifier, &QSocketNotifier::activated, + this, &DirectSession::processSignals); + + m_isActive = true; + m_ttyFd = fd; + cleanup.dismiss(); + + return true; +} + +void DirectSession::restoreTerminal() +{ + vt_mode virtualTerminalMode = {}; + + if (ioctl(m_ttyFd, KDSKBMUTE, 0) && ioctl(m_ttyFd, KDSKBMODE, m_keyboardMode)) { + qCWarning(KWIN_CORE, "Failed to restore keyboard mode: %s", strerror(errno)); + } + + if (ioctl(m_ttyFd, KDSETMODE, KD_TEXT)) { + qCWarning(KWIN_CORE, "Failed to set KD_TEXT mode on tty: %s", strerror(errno)); + } + + virtualTerminalMode.mode = VT_AUTO; + if (ioctl(m_ttyFd, VT_SETMODE, &virtualTerminalMode)) { + qCWarning(KWIN_CORE, "Failed to reset VT handling: %s", strerror(errno)); + } +} + +bool DirectSession::isActive() const +{ + return m_isActive; +} + +DirectSession::Capabilities DirectSession::capabilities() const +{ + return Capability::SwitchTerminal; +} + +QString DirectSession::seat() const +{ + return m_seat; +} + +uint DirectSession::terminal() const +{ + return m_terminal; +} + +static void drmSetMasterInternal(int fd) +{ + if (ioctl(fd, DRM_IOCTL_SET_MASTER, 0) == -1) { + qCWarning(KWIN_CORE) << "ioctl(DRM_IOCTL_SET_MASTER) failed:" << strerror(errno); + } +} + +static void drmDropMasterInternal(int fd) +{ + if (ioctl(fd, DRM_IOCTL_DROP_MASTER, 0) == -1) { + qCWarning(KWIN_CORE) << "ioctl(DRM_IOCTL_DROP_MASTER) failed:" << strerror(errno); + } +} + +int DirectSession::openRestricted(const QString &fileName) +{ + const int fd = open(fileName.toUtf8().constData(), + O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); + if (fd == -1) { + qCWarning(KWIN_CORE) << "fstat() failed:" << strerror(errno); + return -1; + } + + struct stat buf; + if (fstat(fd, &buf) == -1) { + close(fd); + qCWarning(KWIN_CORE) << "fstat() failed:" << strerror(errno); + return -1; + } + + DirectSessionDevice device; + device.fd = fd; + device.id = buf.st_rdev; + m_devices.append(device); + + if (major(device.id) == DRM_MAJOR) { + drmSetMasterInternal(device.fd); + } + + return fd; +} + +void DirectSession::closeRestricted(int fileDescriptor) +{ + auto it = std::find_if(m_devices.begin(), m_devices.end(), [&](const DirectSessionDevice &device) { + return device.fd == fileDescriptor; + }); + if (it == m_devices.end()) { + close(fileDescriptor); + return; + } + + const DirectSessionDevice device = *it; + m_devices.erase(it); + + if (major(device.id) == DRM_MAJOR) { + drmDropMasterInternal(device.fd); + } + close(fileDescriptor); +} + +void DirectSession::switchTo(uint terminal) +{ + if (m_seat == QStringLiteral("seat0")) { + ioctl(m_ttyFd, VT_ACTIVATE, terminal); + } +} + +void DirectSession::updateActive(bool active) +{ + if (m_isActive != active) { + m_isActive = active; + emit activeChanged(active); + } +} + +void DirectSession::processSignals() +{ + const int signalFd = m_signalNotifier->socket(); + + while (true) { + signalfd_siginfo info; + + const ssize_t readCount = read(signalFd, &info, sizeof(info)); + if (readCount != sizeof(info)) { + break; + } + + switch (info.ssi_signo) { + case SIGUSR1: + qCDebug(KWIN_CORE) << "Releasing virtual terminal" << m_terminal; + updateActive(false); + for (const DirectSessionDevice &device : qAsConst(m_devices)) { + if (major(device.id) == DRM_MAJOR) { + drmDropMasterInternal(device.fd); + } + } + ioctl(m_ttyFd, VT_RELDISP, 1); + break; + case SIGUSR2: + qCDebug(KWIN_CORE) << "Acquiring virtual terminal" << m_terminal; + ioctl(m_ttyFd, VT_RELDISP, VT_ACKACQ); + for (const DirectSessionDevice &device : qAsConst(m_devices)) { + if (major(device.id) == DRM_MAJOR) { + drmSetMasterInternal(device.fd); + } + } + updateActive(true); + break; + } + } +} + +} // namespace KWin diff --git a/src/session_logind.cpp b/src/session_logind.cpp new file mode 100644 index 0000000000..81786f0377 --- /dev/null +++ b/src/session_logind.cpp @@ -0,0 +1,332 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "session_logind.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if HAVE_SYS_SYSMACROS_H +#include +#endif + +struct DBusLogindSeat +{ + QString id; + QDBusObjectPath path; +}; + +QDBusArgument &operator<<(QDBusArgument &argument, const DBusLogindSeat &seat) +{ + argument.beginStructure(); + argument << seat.id << seat.path; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, DBusLogindSeat &seat) +{ + argument.beginStructure(); + argument >> seat.id >> seat.path; + argument.endStructure(); + return argument; +} + +Q_DECLARE_METATYPE(DBusLogindSeat) + +namespace KWin +{ + +static const QString s_serviceName = QStringLiteral("org.freedesktop.login1"); +static const QString s_propertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); +static const QString s_sessionInterface = QStringLiteral("org.freedesktop.login1.Session"); +static const QString s_seatInterface = QStringLiteral("org.freedesktop.login1.Seat"); +static const QString s_managerInterface = QStringLiteral("org.freedesktop.login1.Manager"); +static const QString s_managerPath = QStringLiteral("/org/freedesktop/login1"); + +static QString findProcessSessionPath() +{ + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, s_managerPath, + s_managerInterface, + QStringLiteral("GetSessionByPID")); + message.setArguments({ uint32_t(QCoreApplication::applicationPid()) }); + + const QDBusMessage reply = QDBusConnection::systemBus().call(message); + if (reply.type() == QDBusMessage::ErrorMessage) { + return QString(); + } + + return reply.arguments().constFirst().value().path(); +} + +static bool takeControl(const QString &sessionPath) +{ + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath, + s_sessionInterface, + QStringLiteral("TakeControl")); + message.setArguments({ false }); + + const QDBusMessage reply = QDBusConnection::systemBus().call(message); + + return reply.type() != QDBusMessage::ErrorMessage; +} + +static void releaseControl(const QString &sessionPath) +{ + const QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath, + s_sessionInterface, + QStringLiteral("ReleaseControl")); + + QDBusConnection::systemBus().asyncCall(message); +} + +static bool activate(const QString &sessionPath) +{ + const QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, sessionPath, + s_sessionInterface, + QStringLiteral("Activate")); + + const QDBusMessage reply = QDBusConnection::systemBus().call(message); + + return reply.type() != QDBusMessage::ErrorMessage; +} + +LogindSession *LogindSession::create(QObject *parent) +{ + if (!QDBusConnection::systemBus().interface()->isServiceRegistered(s_serviceName)) { + return nullptr; + } + + const QString sessionPath = findProcessSessionPath(); + if (sessionPath.isEmpty()) { + qCWarning(KWIN_CORE) << "Could not determine the active graphical session"; + return nullptr; + } + + if (!activate(sessionPath)) { + qCWarning(KWIN_CORE, "Failed to activate %s session. Maybe another compositor is running?", + qPrintable(sessionPath)); + return nullptr; + } + + if (!takeControl(sessionPath)) { + qCWarning(KWIN_CORE, "Failed to take control of %s session. Maybe another compositor is running?", + qPrintable(sessionPath)); + return nullptr; + } + + LogindSession *session = new LogindSession(sessionPath, parent); + if (session->initialize()) { + return session; + } + + delete session; + return nullptr; +} + +bool LogindSession::isActive() const +{ + return m_isActive; +} + +LogindSession::Capabilities LogindSession::capabilities() const +{ + return Capability::SwitchTerminal; +} + +QString LogindSession::seat() const +{ + return m_seatId; +} + +uint LogindSession::terminal() const +{ + return m_terminal; +} + +int LogindSession::openRestricted(const QString &fileName) +{ + struct stat st; + if (stat(fileName.toUtf8(), &st) < 0) { + return -1; + } + + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_sessionInterface, + QStringLiteral("TakeDevice")); + message.setArguments({ major(st.st_rdev), minor(st.st_rdev) }); + + const QDBusMessage reply = QDBusConnection::systemBus().call(message); + if (reply.type() == QDBusMessage::ErrorMessage) { + return -1; + } + + const QDBusUnixFileDescriptor descriptor = reply.arguments().constFirst().value(); + if (!descriptor.isValid()) { + return -1; + } + + return fcntl(descriptor.fileDescriptor(), F_DUPFD_CLOEXEC, 0); +} + +void LogindSession::closeRestricted(int fileDescriptor) +{ + struct stat st; + if (fstat(fileDescriptor, &st) < 0) { + close(fileDescriptor); + return; + } + + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_sessionInterface, + QStringLiteral("ReleaseDevice")); + message.setArguments({ major(st.st_rdev), minor(st.st_rdev) }); + + QDBusConnection::systemBus().asyncCall(message); + + close(fileDescriptor); +} + +void LogindSession::switchTo(uint terminal) +{ + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_seatPath, + s_seatInterface, + QStringLiteral("SwitchTo")); + message.setArguments({ terminal }); + + QDBusConnection::systemBus().asyncCall(message); +} + +LogindSession::LogindSession(const QString &sessionPath, QObject *parent) + : Session(parent) + , m_sessionPath(sessionPath) +{ + qDBusRegisterMetaType(); +} + +LogindSession::~LogindSession() +{ + releaseControl(m_sessionPath); +} + +bool LogindSession::initialize() +{ + QDBusMessage activeMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_propertiesInterface, + QStringLiteral("Get")); + activeMessage.setArguments({ s_sessionInterface, QStringLiteral("Active") }); + + QDBusMessage seatMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_propertiesInterface, + QStringLiteral("Get")); + seatMessage.setArguments({ s_sessionInterface, QStringLiteral("Seat") }); + + QDBusMessage terminalMessage = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_propertiesInterface, + QStringLiteral("Get")); + terminalMessage.setArguments({ s_sessionInterface, QStringLiteral("VTNr") }); + + QDBusPendingReply activeReply = + QDBusConnection::systemBus().asyncCall(activeMessage); + QDBusPendingReply terminalReply = + QDBusConnection::systemBus().asyncCall(terminalMessage); + QDBusPendingReply seatReply = + QDBusConnection::systemBus().asyncCall(seatMessage); + + // We must wait until all replies have been received because the drm backend needs a + // valid seat name to properly select gpu devices, this also simplifies startup code. + activeReply.waitForFinished(); + terminalReply.waitForFinished(); + seatReply.waitForFinished(); + + if (activeReply.isError()) { + qCWarning(KWIN_CORE) << "Failed to query Active session property:" << activeReply.error(); + return false; + } + if (terminalReply.isError()) { + qCWarning(KWIN_CORE) << "Failed to query VTNr session property:" << terminalReply.error(); + return false; + } + if (seatReply.isError()) { + qCWarning(KWIN_CORE) << "Failed to query Seat session property:" << seatReply.error(); + return false; + } + + m_isActive = activeReply.value().toBool(); + m_terminal = terminalReply.value().toUInt(); + + const DBusLogindSeat seat = qdbus_cast(seatReply.value().value()); + m_seatId = seat.id; + m_seatPath = seat.path.path(); + + QDBusConnection::systemBus().connect(s_serviceName, s_managerPath, s_managerInterface, + QStringLiteral("PrepareForSleep"), + this, + SLOT(handlePrepareForSleep(bool))); + + QDBusConnection::systemBus().connect(s_serviceName, m_sessionPath, s_sessionInterface, + QStringLiteral("PauseDevice"), + this, + SLOT(handlePauseDevice(uint, uint, QString))); + + QDBusConnection::systemBus().connect(s_serviceName, m_sessionPath, s_propertiesInterface, + QStringLiteral("PropertiesChanged"), + this, + SLOT(handlePropertiesChanged(QString, QVariantMap))); + + return true; +} + +void LogindSession::updateActive(bool active) +{ + if (m_isActive != active) { + m_isActive = active; + emit activeChanged(active); + } +} + +void LogindSession::handlePauseDevice(uint major, uint minor, const QString &type) +{ + if (type == QLatin1String("pause")) { + QDBusMessage message = QDBusMessage::createMethodCall(s_serviceName, m_sessionPath, + s_sessionInterface, + QStringLiteral("PauseDeviceComplete")); + message.setArguments({ major, minor }); + + QDBusConnection::systemBus().asyncCall(message); + } +} + +void LogindSession::handlePropertiesChanged(const QString &interfaceName, const QVariantMap &properties) +{ + if (interfaceName == s_sessionInterface) { + const QVariant active = properties.value(QStringLiteral("Active")); + if (active.isValid()) { + updateActive(active.toBool()); + } + } +} + +void LogindSession::handlePrepareForSleep(bool sleep) +{ + if (!sleep) { + emit awoke(); + } +} + +} // namespace KWin diff --git a/src/session_logind.h b/src/session_logind.h new file mode 100644 index 0000000000..3730498370 --- /dev/null +++ b/src/session_logind.h @@ -0,0 +1,48 @@ +/* + SPDX-FileCopyrightText: 2020 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "session.h" + +namespace KWin +{ + +class LogindSession : public Session +{ + Q_OBJECT + +public: + static LogindSession *create(QObject *parent = nullptr); + ~LogindSession() override; + + bool isActive() const override; + Capabilities capabilities() const override; + QString seat() const override; + uint terminal() const override; + int openRestricted(const QString &fileName) override; + void closeRestricted(int fileDescriptor) override; + void switchTo(uint terminal) override; + +private Q_SLOTS: + void handlePauseDevice(uint major, uint minor, const QString &type); + void handlePropertiesChanged(const QString &interfaceName, const QVariantMap &properties); + void handlePrepareForSleep(bool sleep); + +private: + explicit LogindSession(const QString &sessionPath, QObject *parent = nullptr); + + bool initialize(); + void updateActive(bool active); + + QString m_sessionPath; + QString m_seatId; + QString m_seatPath; + uint m_terminal = 0; + bool m_isActive = false; +}; + +} // namespace KWin diff --git a/src/session_noop.cpp b/src/session_noop.cpp new file mode 100644 index 0000000000..bd6db8ec02 --- /dev/null +++ b/src/session_noop.cpp @@ -0,0 +1,62 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "session_noop.h" + +namespace KWin +{ + +NoopSession *NoopSession::create(QObject *parent) +{ + return new NoopSession(parent); +} + +NoopSession::NoopSession(QObject *parent) + : Session(parent) +{ +} + +NoopSession::~NoopSession() +{ +} + +bool NoopSession::isActive() const +{ + return true; +} + +NoopSession::Capabilities NoopSession::capabilities() const +{ + return Capabilities(); +} + +QString NoopSession::seat() const +{ + return QStringLiteral("seat0"); +} + +uint NoopSession::terminal() const +{ + return 0; +} + +int NoopSession::openRestricted(const QString &fileName) +{ + Q_UNUSED(fileName) + return -1; +} + +void NoopSession::closeRestricted(int fileDescriptor) +{ + Q_UNUSED(fileDescriptor) +} + +void NoopSession::switchTo(uint terminal) +{ + Q_UNUSED(terminal) +} + +} // namespace KWin diff --git a/src/session_noop.h b/src/session_noop.h new file mode 100644 index 0000000000..397e21e085 --- /dev/null +++ b/src/session_noop.h @@ -0,0 +1,34 @@ +/* + SPDX-FileCopyrightText: 2021 Vlad Zahorodnii + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include "session.h" + +namespace KWin +{ + +class NoopSession : public Session +{ + Q_OBJECT + +public: + static NoopSession *create(QObject *parent = nullptr); + ~NoopSession() override; + + bool isActive() const override; + Capabilities capabilities() const override; + QString seat() const override; + uint terminal() const override; + int openRestricted(const QString &fileName) override; + void closeRestricted(int fileDescriptor) override; + void switchTo(uint terminal) override; + +private: + explicit NoopSession(QObject *parent = nullptr); +}; + +} // namespace KWin diff --git a/src/udev.cpp b/src/udev.cpp index 2c90c99a4e..e0f67171f8 100644 --- a/src/udev.cpp +++ b/src/udev.cpp @@ -7,7 +7,9 @@ SPDX-License-Identifier: GPL-2.0-or-later */ #include "udev.h" -#include "logind.h" +#include "main.h" +#include "platform.h" +#include "session.h" // Qt #include #include @@ -111,7 +113,7 @@ std::vector UdevEnumerate::find() if (deviceSeat.isEmpty()) { deviceSeat = defaultSeat; } - if (deviceSeat != LogindIntegration::self()->seat()) { + if (deviceSeat != kwinApp()->platform()->session()->seat()) { continue; } vect.push_back(std::move(device)); diff --git a/src/virtual_terminal.cpp b/src/virtual_terminal.cpp deleted file mode 100644 index 0f71c0f129..0000000000 --- a/src/virtual_terminal.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2015 Martin Gräßlin - - SPDX-License-Identifier: GPL-2.0-or-later -*/ -#include "virtual_terminal.h" -// kwin -#include "logind.h" -#include "main.h" -#include "utils.h" -// Qt -#include -#include -// linux -#ifdef Q_OS_LINUX -#include -#include -#include -#include -#endif -#ifdef Q_OS_FREEBSD -#include -#endif -// system -#include -#include -#include -#include -#include -// c++ -#include - -#define RELEASE_SIGNAL SIGUSR1 -#define ACQUISITION_SIGNAL SIGUSR2 - -namespace KWin -{ - -KWIN_SINGLETON_FACTORY(VirtualTerminal) - -VirtualTerminal::VirtualTerminal(QObject *parent) - : QObject(parent) -{ -} - -void VirtualTerminal::init() -{ - auto logind = LogindIntegration::self(); - if (logind->vt() != -1) { - setup(logind->vt()); - } - connect(logind, &LogindIntegration::virtualTerminalChanged, this, &VirtualTerminal::setup); - if (logind->isConnected()) { - logind->takeControl(); - } else { - connect(logind, &LogindIntegration::connectedChanged, logind, &LogindIntegration::takeControl); - } -} - -VirtualTerminal::~VirtualTerminal() -{ - s_self = nullptr; - closeFd(); -} - -static bool isTty(int fd) -{ - if (fd < 0) { - return false; - } - struct stat st; - if (fstat(fd, &st) == -1) { - return false; - } -#ifdef Q_OS_LINUX - // Not a TTY device or weird vt number, skip it - if (major(st.st_rdev) != TTY_MAJOR || minor (st.st_rdev) <= 0 || minor(st.st_rdev) >= 64) { - return false; - } -#endif - // FreeBSD doesn't have a notion of major device number, so nothing - // to check. isatty() might not do the trick. - return true; -} - -void VirtualTerminal::setup(int vtNr) -{ - if (m_vt != -1) { - return; - } - if (vtNr == -1) { - // error condition - return; - } - QString ttyName = QStringLiteral(KWIN_TTY_PREFIX "%1").arg(vtNr); - - m_vt = open(ttyName.toUtf8().constData(), O_RDWR|O_CLOEXEC|O_NONBLOCK); - if (m_vt < 0) { - qCWarning(KWIN_CORE) << "Failed to open tty" << vtNr; - return; - } - if (!isTty(m_vt)) { - qCWarning(KWIN_CORE) << vtNr << " is no tty"; - closeFd(); - return; - } - if (ioctl(m_vt, KDSETMODE, KD_GRAPHICS) < 0) { - qCWarning(KWIN_CORE()) << "Failed to set tty " << vtNr << " in graphics mode"; - closeFd(); - return; - } - if (!createSignalHandler()) { - qCWarning(KWIN_CORE) << "Failed to create signalfd"; - closeFd(); - return; - } - vt_mode mode = { - VT_PROCESS, - 0, - RELEASE_SIGNAL, - ACQUISITION_SIGNAL, - 0 - }; - if (ioctl(m_vt, VT_SETMODE, &mode) < 0) { - qCWarning(KWIN_CORE) << "Failed to take over virtual terminal"; - closeFd(); - return; - } - m_vtNumber = vtNr; - setActive(true); - emit kwinApp()->virtualTerminalCreated(); -} - -void VirtualTerminal::closeFd() -{ - if (m_vt < 0) { - return; - } - if (m_notifier) { - const int sfd = m_notifier->socket(); - delete m_notifier; - m_notifier = nullptr; - close(sfd); - } - close(m_vt); - m_vt = -1; -} - -bool VirtualTerminal::createSignalHandler() -{ - if (m_notifier) { - return false; - } - sigset_t mask; - sigemptyset(&mask); - sigaddset(&mask, RELEASE_SIGNAL); - sigaddset(&mask, ACQUISITION_SIGNAL); - pthread_sigmask(SIG_BLOCK, &mask, nullptr); - - const int fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); - if (fd < 0) { - return false; - } - m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); - connect(m_notifier, &QSocketNotifier::activated, this, - [this] { - if (m_vt < 0) { - return; - } - while (true) { - signalfd_siginfo sigInfo; - if (read(m_notifier->socket(), &sigInfo, sizeof(sigInfo)) != sizeof(sigInfo)) { - break; - } - switch (sigInfo.ssi_signo) { - case RELEASE_SIGNAL: - setActive(false); - ioctl(m_vt, VT_RELDISP, 1); - break; - case ACQUISITION_SIGNAL: - ioctl(m_vt, VT_RELDISP, VT_ACKACQ); - setActive(true); - break; - } - } - } - ); - return true; -} - -void VirtualTerminal::activate(int vt) -{ - if (m_vt < 0) { - return; - } - if (vt == m_vtNumber) { - return; - } - ioctl(m_vt, VT_ACTIVATE, vt); - setActive(false); -} - -void VirtualTerminal::setActive(bool active) -{ - if (m_active == active) { - return; - } - m_active = active; - emit activeChanged(m_active); -} - -} diff --git a/src/virtual_terminal.h b/src/virtual_terminal.h deleted file mode 100644 index 6d4d7736a1..0000000000 --- a/src/virtual_terminal.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2015 Martin Gräßlin - - SPDX-License-Identifier: GPL-2.0-or-later -*/ -#ifndef KWIN_VIRTUAL_TERMINAL_H -#define KWIN_VIRTUAL_TERMINAL_H -#include - -#include - -class QSocketNotifier; - -namespace KWin -{ - -class KWIN_EXPORT VirtualTerminal : public QObject -{ - Q_OBJECT -public: - ~VirtualTerminal() override; - - void init(); - void activate(int vt); - bool isActive() const { - return m_active; - } - -Q_SIGNALS: - void activeChanged(bool); - -private: - void setup(int vtNr); - void closeFd(); - bool createSignalHandler(); - void setActive(bool active); - int m_vt = -1; - QSocketNotifier *m_notifier = nullptr; - bool m_active = false; - int m_vtNumber = 0; - - KWIN_SINGLETON(VirtualTerminal) -}; - -} - -#endif