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:
parent
aad767f91f
commit
ade861d6de
49 changed files with 1921 additions and 1152 deletions
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -76,7 +76,6 @@ protected:
|
|||
void performStartup() override;
|
||||
|
||||
private:
|
||||
void createBackend();
|
||||
void continueStartupWithScreens();
|
||||
void continueStartupWithScene();
|
||||
void finalizeStartup();
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
451
src/logind.cpp
451
src/logind.cpp
|
@ -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
|
102
src/logind.h
102
src/logind.h
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -225,7 +225,6 @@ protected:
|
|||
|
||||
void notifyKSplash();
|
||||
void notifyStarted();
|
||||
void createSession();
|
||||
void createInput();
|
||||
void createWorkspace();
|
||||
void createAtoms();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -51,7 +51,6 @@ protected:
|
|||
void performStartup() override;
|
||||
|
||||
private:
|
||||
void createBackend();
|
||||
void continueStartupWithScreens();
|
||||
void continueStartupWithScene();
|
||||
void finalizeStartup();
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -30,7 +30,6 @@ protected:
|
|||
|
||||
private Q_SLOTS:
|
||||
void lostSelection();
|
||||
void continueStartupWithScreens();
|
||||
|
||||
private:
|
||||
void crashChecking();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
52
src/session.cpp
Normal 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
103
src/session.h
Normal 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
334
src/session_consolekit.cpp
Normal 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
48
src/session_consolekit.h
Normal 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
57
src/session_direct.h
Normal 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
|
331
src/session_direct_freebsd.cpp
Normal file
331
src/session_direct_freebsd.cpp
Normal 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
|
313
src/session_direct_linux.cpp
Normal file
313
src/session_direct_linux.cpp
Normal 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
332
src/session_logind.cpp
Normal 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
48
src/session_logind.h
Normal 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
62
src/session_noop.cpp
Normal 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
34
src/session_noop.h
Normal 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
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
Loading…
Reference in a new issue