Refactor session code

At the moment, the session code is far from being extensible. If we
decide to add support for libseatd, it will be a challenging task with
the current design of session management code. The goal of this
refactoring is to fix that.

Another motivation behind this change is to prepare session related code
for upstreaming to kwayland-server where it belongs.
This commit is contained in:
Vlad Zahorodnii 2021-01-30 15:22:07 +02:00
parent aad767f91f
commit ade861d6de
49 changed files with 1921 additions and 1152 deletions

View file

@ -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)

View file

@ -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()

View file

@ -76,7 +76,6 @@ protected:
void performStartup() override;
private:
void createBackend();
void continueStartupWithScreens();
void continueStartupWithScene();
void finalizeStartup();

View file

@ -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 <tcberner@FreeBSD.org>
#
# 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)

View file

@ -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)

View file

@ -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<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
Platform *platform = kwinApp()->platform();
Session *session = platform->session();
const QVector<AbstractOutput *> 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()

View file

@ -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<KWin::InputRedirection::PointerButtonState>();
qRegisterMetaType<KWin::InputRedirection::PointerAxis>();
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()

View file

@ -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();
}

View file

@ -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 <fcntl.h>
@ -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()

View file

@ -1,451 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "logind.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDBusConnectionInterface>
#include <QDBusPendingCallWatcher>
#include <QDBusServiceWatcher>
#include <QDBusUnixFileDescriptor>
#include <QDBusMetaType>
#include <sys/stat.h>
#include <config-kwin.h>
#if HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#endif
#ifndef major
#include <sys/types.h>
#endif
#include <fcntl.h>
#include <unistd.h>
#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<QStringList> async = m_bus.asyncCall(message);
QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
connect(callWatcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *self) {
QDBusPendingReply<QStringList> 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<QDBusObjectPath> session = m_bus.asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(session, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *self) {
QDBusPendingReply<QDBusObjectPath> 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<QVariant> reply = m_bus.asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *self) {
QDBusPendingReply<QVariant> 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<QVariant> reply = m_bus.asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *self) {
QDBusPendingReply<QVariant> 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<void> session = m_bus.asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(session, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *self) {
s_recursionCheck = false;
QDBusPendingReply<void> 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<QDBusUnixFileDescriptor>().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<DBusLogindSeat>();
QDBusMessage message = QDBusMessage::createMethodCall(m_sessionControllerService,
m_sessionPath,
s_dbusPropertiesInterface,
QStringLiteral("Get"));
message.setArguments(QVariantList({m_sessionControllerSessionInterface, QStringLiteral("Seat")}));
QDBusPendingReply<QVariant> reply = m_bus.asyncCall(message);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *self) {
QDBusPendingReply<QVariant> 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<DBusLogindSeat>(reply.value().value<QDBusArgument>());
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

View file

@ -1,102 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KWIN_LOGIND_H
#define KWIN_LOGIND_H
#include <kwinglobals.h>
#include <QDBusConnection>
#include <QObject>
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

View file

@ -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);

View file

@ -225,7 +225,6 @@ protected:
void notifyKSplash();
void notifyStarted();
void createSession();
void createInput();
void createWorkspace();
void createAtoms();

View file

@ -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()

View file

@ -51,7 +51,6 @@ protected:
void performStartup() override;
private:
void createBackend();
void continueStartupWithScreens();
void continueStartupWithScene();
void finalizeStartup();

View file

@ -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))

View file

@ -30,7 +30,6 @@ protected:
private Q_SLOTS:
void lostSelection();
void continueStartupWithScreens();
private:
void crashChecking();

View file

@ -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

View file

@ -17,8 +17,8 @@
#include <main.h>
#include <platform.h>
#include <session.h>
#include <workspace.h>
#include <logind.h>
#include <KGlobalAccel>
#include <KLocalizedString>
@ -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

View file

@ -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<UdevDevice::Ptr> 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)

View file

@ -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<Udev> m_udev;
QScopedPointer<UdevMonitor> m_udevMonitor;
Session *m_session = nullptr;
// active output pipelines (planes + crtc + encoder + connector)
QVector<DrmOutput*> m_outputs;
// active and enabled pipelines (above + wl_output)

View file

@ -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 = {};

View file

@ -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 <KWaylandServer/output_interface.h>
@ -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;
}

View file

@ -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"

View file

@ -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 <fcntl.h>
@ -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) {

View file

@ -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<FramebufferOutput*> m_outputs;
Session *m_session;
QByteArray m_id;
struct Color {
quint32 offset;

View file

@ -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 <QPainter>
@ -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;

View file

@ -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

View file

@ -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<VirtualOutput*> m_outputs;
QVector<VirtualOutput*> m_outputsEnabled;
QScopedPointer<QTemporaryDir> m_screenshotDir;
Session *m_session;
};
}

View file

@ -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)

View file

@ -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;

View file

@ -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()

View file

@ -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;

View file

@ -14,6 +14,7 @@
#include "xcbutils.h"
#include "egl_x11_backend.h"
#include "screens.h"
#include "session.h"
#include <kwinxrenderutils.h>
#include <cursor.h>
#include <pointer_input.h>
@ -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

View file

@ -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;

52
src/session.cpp Normal file
View file

@ -0,0 +1,52 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
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<Session *(QObject *)> 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

103
src/session.h Normal file
View file

@ -0,0 +1,103 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <kwinglobals.h>
#include <QObject>
#include <QString>
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)

334
src/session_consolekit.cpp Normal file
View file

@ -0,0 +1,334 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "session_consolekit.h"
#include "utils.h"
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QDBusMetaType>
#include <QDBusObjectPath>
#include <QDBusPendingCall>
#include <QDBusUnixFileDescriptor>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#if HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#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<QDBusObjectPath>().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<QDBusUnixFileDescriptor>();
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<DBusConsoleKitSeat>();
}
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<QVariant> activeReply =
QDBusConnection::systemBus().asyncCall(activeMessage);
QDBusPendingReply<QVariant> terminalReply =
QDBusConnection::systemBus().asyncCall(terminalMessage);
QDBusPendingReply<QVariant> 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<DBusConsoleKitSeat>(seatReply.value().value<QDBusArgument>());
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

48
src/session_consolekit.h Normal file
View file

@ -0,0 +1,48 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
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

57
src/session_direct.h Normal file
View file

@ -0,0 +1,57 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "session.h"
#include <QSocketNotifier>
#include <sys/types.h>
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<DirectSessionDevice> m_devices;
uint m_terminal = 0;
int m_ttyFd = -1;
int m_keyboardMode = 0;
bool m_isActive = false;
};
} // namespace KWin

View file

@ -0,0 +1,331 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "session_direct.h"
#include "utils.h"
#include <QScopeGuard>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <sys/consio.h>
#include <sys/event.h>
#include <sys/ioctl.h>
#include <sys/kbio.h>
#include <sys/stat.h>
#include <unistd.h>
#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

View file

@ -0,0 +1,313 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "session_direct.h"
#include "utils.h"
#include <QScopeGuard>
#include <errno.h>
#include <fcntl.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <signal.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/signalfd.h>
#include <sys/stat.h>
#include <unistd.h>
#if HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#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

332
src/session_logind.cpp Normal file
View file

@ -0,0 +1,332 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "session_logind.h"
#include "utils.h"
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QDBusMetaType>
#include <QDBusObjectPath>
#include <QDBusPendingCall>
#include <QDBusUnixFileDescriptor>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#if HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#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<QDBusObjectPath>().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<QDBusUnixFileDescriptor>();
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<DBusLogindSeat>();
}
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<QVariant> activeReply =
QDBusConnection::systemBus().asyncCall(activeMessage);
QDBusPendingReply<QVariant> terminalReply =
QDBusConnection::systemBus().asyncCall(terminalMessage);
QDBusPendingReply<QVariant> 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<DBusLogindSeat>(seatReply.value().value<QDBusArgument>());
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

48
src/session_logind.h Normal file
View file

@ -0,0 +1,48 @@
/*
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
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

62
src/session_noop.cpp Normal file
View file

@ -0,0 +1,62 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
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

34
src/session_noop.h Normal file
View file

@ -0,0 +1,34 @@
/*
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
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

View file

@ -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 <QByteArray>
#include <QScopedPointer>
@ -111,7 +113,7 @@ std::vector<UdevDevice::Ptr> 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));

View file

@ -1,215 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "virtual_terminal.h"
// kwin
#include "logind.h"
#include "main.h"
#include "utils.h"
// Qt
#include <QDebug>
#include <QSocketNotifier>
// linux
#ifdef Q_OS_LINUX
#include <linux/major.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <sys/sysmacros.h>
#endif
#ifdef Q_OS_FREEBSD
#include <sys/consio.h>
#endif
// system
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/signalfd.h>
#include <sys/stat.h>
// c++
#include <csignal>
#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);
}
}

View file

@ -1,50 +0,0 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KWIN_VIRTUAL_TERMINAL_H
#define KWIN_VIRTUAL_TERMINAL_H
#include <kwinglobals.h>
#include <QObject>
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