diff --git a/libkworkspace/CMakeLists.txt b/libkworkspace/CMakeLists.txt index f66013f9a..94576213e 100644 --- a/libkworkspace/CMakeLists.txt +++ b/libkworkspace/CMakeLists.txt @@ -1,79 +1,103 @@ set(kworkspace_LIB_SRCS kdisplaymanager.cpp kworkspace.cpp + sessionmanagement.cpp + sessionmanagementbackend.cpp ) add_definitions(-DTRANSLATION_DOMAIN=\"libkworkspace\") remove_definitions(-DQT_NO_CAST_FROM_ASCII) remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY) remove_definitions(-DQT_NO_CAST_TO_ASCII) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) -set(ksmserver_xml ${plasma-workspace_SOURCE_DIR}/ksmserver/org.kde.KSMServerInterface.xml) +qt5_add_dbus_interface(kworkspace_LIB_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/kf5_org.freedesktop.ScreenSaver.xml screenlocker_interface ) +qt5_add_dbus_interface(kworkspace_LIB_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/org.kde.screensaver.xml kscreenlocker_interface ) +qt5_add_dbus_interface(kworkspace_LIB_SRCS ${plasma-workspace_SOURCE_DIR}/ksmserver/org.kde.LogoutPrompt.xml logoutprompt_interface) +qt5_add_dbus_interface(kworkspace_LIB_SRCS ${plasma-workspace_SOURCE_DIR}/ksmserver/org.kde.Shutdown.xml shutdown_interface) + +set_source_files_properties("${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.Manager.xml" + "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Seat.xml" + "${CMAKE_SOURCE_DIR}/data/interfaces/org.freedesktop.login1.Session.xml" + PROPERTIES INCLUDE "loginddbustypes.h" ) + +qt5_add_dbus_interface(kworkspace_LIB_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.Manager.xml" login1_manager_interface) +# The autogenerated cpp file hardcodes the interface name given to the QDBusAbstractAdaptor +# This "hack" swaps the implementation for one that can switch between login1 and consolekit2 +list(REMOVE_ITEM kworkspace_LIB_SRCS "${CMAKE_CURRENT_BINARY_DIR}/login1_manager_interface.cpp") +list(APPEND kworkspace_LIB_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/login1_manager_interface.cpp") + +qt5_add_dbus_interface(kworkspace_LIB_SRCS "org.freedesktop.UPower.xml" upower_interface) +qt5_add_dbus_interface(kworkspace_LIB_SRCS "org.freedesktop.ConsoleKit.Manager.xml" consolekit_manager_interface) + +set(ksmserver_xml ${plasma-workspace_SOURCE_DIR}/ksmserver/org.kde.KSMServerInterface.xml) qt5_add_dbus_interface( kworkspace_LIB_SRCS ${ksmserver_xml} ksmserver_interface ) set_source_files_properties(${KWIN_INTERFACE} PROPERTIES INCLUDE "interface_util.h") qt5_add_dbus_interface( kworkspace_LIB_SRCS ${KWIN_INTERFACE} kwin_interface ) add_library(kworkspace ${kworkspace_LIB_SRCS}) add_library(PW::KWorkspace ALIAS kworkspace) set_target_properties(kworkspace PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR} EXPORT_NAME KWorkspace OUTPUT_NAME kworkspace5 ) generate_export_header(kworkspace) target_link_libraries(kworkspace PUBLIC Qt5::Core PRIVATE Qt5::DBus KF5::I18n KF5::WindowSystem KF5::Plasma ) target_include_directories(kworkspace PUBLIC "$" INTERFACE "$" ) configure_file(config-libkworkspace.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-libkworkspace.h) if(X11_FOUND) if(NOT X11_Xau_FOUND) message(FATAL_ERROR "Found X11, but not libXau which is required for building kworkspace") endif() target_link_libraries(kworkspace PRIVATE Qt5::X11Extras ${X11_LIBRARIES} ${X11_Xau_LIB}) endif() write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/LibKWorkspaceConfigVersion.cmake VERSION "${PROJECT_VERSION}" COMPATIBILITY AnyNewerVersion) install(TARGETS kworkspace EXPORT libkworkspaceLibraryTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS} ) install( FILES kdisplaymanager.h kworkspace.h + sessionmanagement.h ${CMAKE_CURRENT_BINARY_DIR}/config-libkworkspace.h ${CMAKE_CURRENT_BINARY_DIR}/kworkspace_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kworkspace5 COMPONENT Devel ) set(CMAKECONFIG_INSTALL_DIR ${KDE_INSTALL_LIBDIR}/cmake/LibKWorkspace) configure_package_config_file(LibKWorkspaceConfig.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/LibKWorkspaceConfig.cmake" INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/LibKWorkspaceConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/LibKWorkspaceConfigVersion.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) install(EXPORT libkworkspaceLibraryTargets NAMESPACE PW:: DESTINATION ${CMAKECONFIG_INSTALL_DIR} FILE LibKWorkspaceLibraryTargets.cmake ) if(BUILD_TESTING) add_subdirectory(autotests) + add_subdirectory(tests) endif() diff --git a/libkworkspace/kworkspace.cpp b/libkworkspace/kworkspace.cpp index 4e5386f05..31eb1c490 100644 --- a/libkworkspace/kworkspace.cpp +++ b/libkworkspace/kworkspace.cpp @@ -1,289 +1,139 @@ /* This file is part of the KDE libraries Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kworkspace.h" #include "config-libkworkspace.h" #include #include #include #include #include +#include #include #include // getenv() #include #include #if HAVE_X11 #include #include #include #include #include #endif #if HAVE_X11 #define DISPLAY "DISPLAY" #elif defined(Q_WS_QWS) #define DISPLAY "QWS_DISPLAY" #endif #include "config-workspace.h" #ifdef HAVE_UNISTD_H #include #endif // HAVE_UNISTD_H #include #include -#include "kworkspace_p.h" - namespace KWorkSpace { -#if HAVE_X11 -static void save_yourself_callback( SmcConn conn_P, SmPointer, int, Bool , int, Bool ) - { - SmcSaveYourselfDone( conn_P, True ); - } - -static void dummy_callback( SmcConn, SmPointer ) - { - } -#endif -KRequestShutdownHelper::KRequestShutdownHelper() - { -#if HAVE_X11 - SmcCallbacks calls; - calls.save_yourself.callback = save_yourself_callback; - calls.die.callback = dummy_callback; - calls.save_complete.callback = dummy_callback; - calls.shutdown_cancelled.callback = dummy_callback; - char* id = nullptr; - char err[ 11 ]; - conn = SmcOpenConnection( nullptr, nullptr, 1, 0, - SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask - | SmcShutdownCancelledProcMask, &calls, nullptr, &id, 10, err ); - if( id != nullptr ) - free( id ); - if( conn == nullptr ) - return; // no SM - // set the required properties, mostly dummy values - SmPropValue propvalue[ 5 ]; - SmProp props[ 5 ]; - propvalue[ 0 ].length = sizeof( unsigned char ); - unsigned char value0 = SmRestartNever; // so that this extra SM connection doesn't interfere - propvalue[ 0 ].value = &value0; - props[ 0 ].name = const_cast< char* >( SmRestartStyleHint ); - props[ 0 ].type = const_cast< char* >( SmCARD8 ); - props[ 0 ].num_vals = 1; - props[ 0 ].vals = &propvalue[ 0 ]; - struct passwd* entry = getpwuid( geteuid() ); - propvalue[ 1 ].length = entry != nullptr ? strlen( entry->pw_name ) : 0; - propvalue[ 1 ].value = (SmPointer)( entry != nullptr ? entry->pw_name : "" ); - props[ 1 ].name = const_cast< char* >( SmUserID ); - props[ 1 ].type = const_cast< char* >( SmARRAY8 ); - props[ 1 ].num_vals = 1; - props[ 1 ].vals = &propvalue[ 1 ]; - propvalue[ 2 ].length = 0; - propvalue[ 2 ].value = (SmPointer)( "" ); - props[ 2 ].name = const_cast< char* >( SmRestartCommand ); - props[ 2 ].type = const_cast< char* >( SmLISTofARRAY8 ); - props[ 2 ].num_vals = 1; - props[ 2 ].vals = &propvalue[ 2 ]; - propvalue[ 3 ].length = strlen( "requestshutdownhelper" ); - propvalue[ 3 ].value = (SmPointer)"requestshutdownhelper"; - props[ 3 ].name = const_cast< char* >( SmProgram ); - props[ 3 ].type = const_cast< char* >( SmARRAY8 ); - props[ 3 ].num_vals = 1; - props[ 3 ].vals = &propvalue[ 3 ]; - propvalue[ 4 ].length = 0; - propvalue[ 4 ].value = (SmPointer)( "" ); - props[ 4 ].name = const_cast< char* >( SmCloneCommand ); - props[ 4 ].type = const_cast< char* >( SmLISTofARRAY8 ); - props[ 4 ].num_vals = 1; - props[ 4 ].vals = &propvalue[ 4 ]; - SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] }; - SmcSetProperties( conn, 5, p ); - notifier = new QSocketNotifier( IceConnectionNumber( SmcGetIceConnection( conn )), - QSocketNotifier::Read, this ); - connect( notifier, &QSocketNotifier::activated, this, &KRequestShutdownHelper::processData); -#endif - } - -KRequestShutdownHelper::~KRequestShutdownHelper() - { -#if HAVE_X11 - if( conn != nullptr ) - { - delete notifier; - SmcCloseConnection( conn, 0, nullptr ); - } -#endif - } - -void KRequestShutdownHelper::processData() - { -#if HAVE_X11 - if( conn != nullptr ) - IceProcessMessages( SmcGetIceConnection( conn ), nullptr, nullptr ); -#endif - } - -bool KRequestShutdownHelper::requestShutdown( ShutdownConfirm confirm ) - { -#if HAVE_X11 - if( conn == nullptr ) - return false; - SmcRequestSaveYourself( conn, SmSaveBoth, True, SmInteractStyleAny, - confirm == ShutdownConfirmNo, True ); - // flush the request - IceFlush(SmcGetIceConnection(conn)); -#endif - return true; - } -#if HAVE_X11 -static KRequestShutdownHelper* helper = nullptr; - -static void cleanup_sm() -{ - delete helper; -} -#endif - -void requestShutDown(ShutdownConfirm confirm, ShutdownType sdtype, ShutdownMode sdmode) -{ -#if HAVE_X11 - /* use ksmserver's dcop interface if necessary */ - if ( confirm == ShutdownConfirmYes || - sdtype != ShutdownTypeDefault || - sdmode != ShutdownModeDefault ) - { - org::kde::KSMServerInterface ksmserver(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus()); - ksmserver.logout((int)confirm, (int)sdtype, (int)sdmode); - return; - } - - if( helper == nullptr ) - { - helper = new KRequestShutdownHelper(); - qAddPostRoutine(cleanup_sm); - } - helper->requestShutdown( confirm ); -#endif -} - -bool canShutDown( ShutdownConfirm confirm, - ShutdownType sdtype, - ShutdownMode sdmode ) -{ -#if HAVE_X11 - if ( confirm == ShutdownConfirmYes || - sdtype != ShutdownTypeDefault || - sdmode != ShutdownModeDefault ) - { - org::kde::KSMServerInterface ksmserver(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus()); - QDBusReply reply = ksmserver.canShutdown(); - if (!reply.isValid()) { - return false; - } - return reply; - } - - return true; -#else - return false; -#endif -} bool isShuttingDown() { org::kde::KSMServerInterface ksmserver(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus()); QDBusReply reply = ksmserver.isShuttingDown(); if (!reply.isValid()) { return false; } return reply; } +void requestShutDown(KWorkSpace::ShutdownConfirm, KWorkSpace::ShutdownType, KWorkSpace::ShutdownMode) +{ + qDebug() << "Old code path used"; +} + static QTime smModificationTime; void propagateSessionManager() { #if HAVE_X11 QByteArray fName = QFile::encodeName(QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation)+"/KSMserver"); QString display = QString::fromLocal8Bit( ::getenv(DISPLAY) ); // strip the screen number from the display display.remove(QRegExp(QStringLiteral("\\.[0-9]+$"))); int i; while( (i = display.indexOf(':')) >= 0) display[i] = '_'; while( (i = display.indexOf('/')) >= 0) display[i] = '_'; fName += '_'; fName += display.toLocal8Bit(); QByteArray smEnv = ::getenv("SESSION_MANAGER"); bool check = smEnv.isEmpty(); if ( !check && smModificationTime.isValid() ) { QFileInfo info( fName ); QTime current = info.lastModified().time(); check = current > smModificationTime; } if ( check ) { QFile f( fName ); if ( !f.open( QIODevice::ReadOnly ) ) return; QFileInfo info ( f ); smModificationTime = QTime( info.lastModified().time() ); QTextStream t(&f); t.setCodec( "ISO 8859-1" ); QString s = t.readLine(); f.close(); ::setenv( "SESSION_MANAGER", s.toLatin1(), true ); } #endif } void detectPlatform(int argc, char **argv) { if (qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) { return; } for (int i = 0; i < argc; i++) { if (qstrcmp(argv[i], "-platform") == 0 || qstrcmp(argv[i], "--platform") == 0 || QByteArray(argv[i]).startsWith("-platform=") || QByteArray(argv[i]).startsWith("--platform=")) { return; } } const QByteArray sessionType = qgetenv("XDG_SESSION_TYPE"); if (sessionType.isEmpty()) { return; } if (qstrcmp(sessionType, "wayland") == 0) { qputenv("QT_QPA_PLATFORM", "wayland"); } else if (qstrcmp(sessionType, "x11") == 0) { qputenv("QT_QPA_PLATFORM", "xcb"); } } } // end namespace diff --git a/libkworkspace/kworkspace_p.h b/libkworkspace/kworkspace_p.h deleted file mode 100644 index 44bde1476..000000000 --- a/libkworkspace/kworkspace_p.h +++ /dev/null @@ -1,58 +0,0 @@ -/* This file is part of the KDE libraries - Copyright (C) 2007 Lubos Lunak - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef KWORKSPACE_P_H -#define KWORKSPACE_P_H - -#include -#include "kworkspace.h" -#include "config-libkworkspace.h" - -#if HAVE_X11 -#include -#endif - -class QSocketNotifier; - -namespace KWorkSpace -{ - -// A class that creates another connection to ksmserver and handles it properly. -class KRequestShutdownHelper - : public QObject - { - Q_OBJECT - public: - KRequestShutdownHelper(); - ~KRequestShutdownHelper() override; - bool requestShutdown( ShutdownConfirm confirm ); - private Q_SLOTS: - void processData(); - private: -#if HAVE_X11 - SmcConn connection() const { return conn; } - SmcConn conn; -#endif - QSocketNotifier* notifier; - }; - -} - - -#endif diff --git a/libkworkspace/login1_manager_interface.cpp b/libkworkspace/login1_manager_interface.cpp new file mode 100644 index 000000000..c00e4b0b7 --- /dev/null +++ b/libkworkspace/login1_manager_interface.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 David Edmundson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License version 2 as + * published by the Free Software Foundation + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// This file overrides the auto-generated login1_manager_interface.cpp in order +// change the interface name and act as a completely transparent Logind1 / CK2 wrapper + +#include "login1_manager_interface.h" +#include "sessionmanagementbackend.h" + +enum LogindProvider { + Unknown, + None, + ConsoleKit2, + Logind1 +}; +static LogindProvider s_mode = Unknown; + +static LogindProvider getLogindMode() { + if (s_mode != Unknown) { + return s_mode; + } + if (QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.login1"))) { + s_mode = Logind1; + } else if (QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.ConsoleKit"))) { + s_mode = ConsoleKit2; + } else { + s_mode = None; + } + return s_mode; +} + +bool LogindSessionBackend::exists() +{ + return getLogindMode() != None; +} + +OrgFreedesktopLogin1ManagerInterface::OrgFreedesktopLogin1ManagerInterface(const QString &, const QString &, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(getLogindMode() == Logind1 ? QStringLiteral("org.freedesktop.login1") : QStringLiteral("org.freedesktop.ConsoleKit"), + getLogindMode() == Logind1 ? QStringLiteral("/org/freedesktop/login1") : QStringLiteral("/org/freedesktop/ConsoleKit/Manager"), + getLogindMode() == Logind1 ? "org.freedesktop.login1.Manager" : "org.freedesktop.ConsoleKit.Manager", + connection, + parent) +{ +} + +OrgFreedesktopLogin1ManagerInterface::~OrgFreedesktopLogin1ManagerInterface() +{ +} + + +#include "login1_manager_interface.moc" diff --git a/libkworkspace/loginddbustypes.h b/libkworkspace/loginddbustypes.h new file mode 100644 index 000000000..d6b6a737a --- /dev/null +++ b/libkworkspace/loginddbustypes.h @@ -0,0 +1,180 @@ +#pragma once + +#include +#include + +struct SessionInfo +{ + QString sessionId; + uint userId; + QString userName; + QString seatId; + QDBusObjectPath sessionPath; +}; + +typedef QList SessionInfoList; + +inline QDBusArgument &operator<<(QDBusArgument &argument, const SessionInfo& sessionInfo) +{ + argument.beginStructure(); + argument << sessionInfo.sessionId; + argument << sessionInfo.userId; + argument << sessionInfo.userName; + argument << sessionInfo.seatId; + argument << sessionInfo.sessionPath; + argument.endStructure(); + + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, SessionInfo &sessionInfo) +{ + argument.beginStructure(); + argument >> sessionInfo.sessionId; + argument >> sessionInfo.userId; + argument >> sessionInfo.userName; + argument >> sessionInfo.seatId; + argument >> sessionInfo.sessionPath; + argument.endStructure(); + + return argument; +} + +struct UserInfo +{ + uint userId; + QString name; + QDBusObjectPath path; +}; + +typedef QList UserInfoList; + +inline QDBusArgument &operator<<(QDBusArgument &argument, const UserInfo& userInfo) +{ + argument.beginStructure(); + argument << userInfo.userId; + argument << userInfo.name; + argument << userInfo.path; + argument.endStructure(); + + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, UserInfo& userInfo) +{ + argument.beginStructure(); + argument >> userInfo.userId; + argument >> userInfo.name; + argument >> userInfo.path; + argument.endStructure(); + + return argument; +} + +struct NamedSeatPath +{ + QString name; + QDBusObjectPath path; +}; + +inline QDBusArgument &operator<<(QDBusArgument &argument, const NamedSeatPath& namedSeat) +{ + argument.beginStructure(); + argument << namedSeat.name; + argument << namedSeat.path; + argument.endStructure(); + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, NamedSeatPath& namedSeat) +{ + argument.beginStructure(); + argument >> namedSeat.name; + argument >> namedSeat.path; + argument.endStructure(); + return argument; +} + +typedef QList NamedSeatPathList; + +typedef NamedSeatPath NamedSessionPath; +typedef NamedSeatPathList NamedSessionPathList; + +class NamedUserPath +{ +public: + uint userId; + QDBusObjectPath path; +}; + +inline QDBusArgument &operator<<(QDBusArgument &argument, const NamedUserPath& namedUser) +{ + argument.beginStructure(); + argument << namedUser.userId; + argument << namedUser.path; + argument.endStructure(); + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, NamedUserPath& namedUser) +{ + argument.beginStructure(); + argument >> namedUser.userId; + argument >> namedUser.path; + argument.endStructure(); + return argument; +} + +class Inhibitor +{ +public: + QString what; + QString who; + QString why; + QString mode; + int userId; + uint processId; +}; + +typedef QList InhibitorList; + +inline QDBusArgument &operator<<(QDBusArgument &argument, const Inhibitor& inhibitor) +{ + argument.beginStructure(); + argument << inhibitor.what; + argument << inhibitor.who; + argument << inhibitor.why; + argument << inhibitor.mode; + argument << inhibitor.userId; + argument << inhibitor.processId; + argument.endStructure(); + return argument; +} + +inline const QDBusArgument &operator>>(const QDBusArgument &argument, Inhibitor& inhibitor) +{ + argument.beginStructure(); + argument >> inhibitor.what; + argument >> inhibitor.who; + argument >> inhibitor.why; + argument >> inhibitor.mode; + argument >> inhibitor.userId; + argument >> inhibitor.processId; + argument.endStructure(); + return argument; +} + +Q_DECLARE_METATYPE(SessionInfo); +Q_DECLARE_METATYPE(QList); + +Q_DECLARE_METATYPE(UserInfo); +Q_DECLARE_METATYPE(QList); + +Q_DECLARE_METATYPE(NamedSeatPath); +Q_DECLARE_METATYPE(QList); + +Q_DECLARE_METATYPE(NamedUserPath); + +Q_DECLARE_METATYPE(Inhibitor); +Q_DECLARE_METATYPE(QList); + diff --git a/libkworkspace/org.freedesktop.ConsoleKit.Manager.xml b/libkworkspace/org.freedesktop.ConsoleKit.Manager.xml new file mode 100644 index 000000000..6e1d316d7 --- /dev/null +++ b/libkworkspace/org.freedesktop.ConsoleKit.Manager.xml @@ -0,0 +1,299 @@ + + + + + + + + + This method initiates a request to restart (ie. reboot) the computer system. + + + + + + + + + + + + + + This method initiates a request to stop (ie. shutdown) the computer system. + + + + + + + + + + + + + + The secret cookie that is used to identify the new session + + + + + This method requests that a new Session + be created for the calling process. The properties of this new Session are set automatically + from information collected about the calling process. + + This new session exists until the calling process disconnects from the system bus or + calls CloseSession(). + + It is the responsibility of the calling process to set the environment variable + XDG_SESSION_COOKIE to the value of the returned cookie. This cookie should only + be made available to child processes of the caller so that they may be identified + as members of this session. + + See this simple example: + + DBusError error; + DBusMessage *message; + DBusMessage *reply; + + message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager", + "OpenSession"); + if (message == NULL) { + goto out; + } + + dbus_error_init (&error); + reply = dbus_connection_send_with_reply_and_block (connector->connection, + message, + -1, + &error); + if (reply == NULL) { + goto out; + } + + dbus_error_init (&error); + if (! dbus_message_get_args (reply, + &error, + DBUS_TYPE_STRING, &cookie, + DBUS_TYPE_INVALID)) { + goto out; + } + + + + OpenSessionWithParameters() + + + + + + an array of Seat IDs + + + + + This gets a list of all the Seats + that are currently present on the system. + Each Seat ID is an D-Bus object path for the object that implements the + Seat interface. + + org.freedesktop.ConsoleKit.Seat + + + + + + + an array of Session IDs + + + + + This gets a list of all the Sessions + that are currently present on the system. + Each Session ID is an D-Bus object path for the object that implements the + Session interface. + + org.freedesktop.ConsoleKit.Session + + + + + + + + The secret cookie that is used to identify the session + + + + + The object identifier for the current session + + + + + Returns the session ID that is associated with the specified cookie. + + + + + + + + + The POSIX process ID + + + + + The object identifier for the current session + + + + + Attempts to determine the session ID for the specified + POSIX process ID (pid). + + + + + + + + + The object identifier for the current session + + + + + Attempts to determine the session ID that the caller belongs to. + + See this example of using dbus-send: + + dbus-send --system --dest=org.freedesktop.ConsoleKit \ + --type=method_call --print-reply --reply-timeout=2000 \ + /org/freedesktop/ConsoleKit/Manager \ + org.freedesktop.ConsoleKit.Manager.GetCurrentSession + + + + + + + + + POSIX User identification + + + + + an array of Session IDs + + + + + This gets a list of all the Sessions + that are currently open for the specified user. + Each Session ID is an D-Bus object path for the object that implements the + Session interface. + + + + + + + + User identification + + + + + an array of Session IDs + + + + + This gets a list of all the Sessions + that are currently open for the specified user. + Each Session ID is an D-Bus object path for the object that implements the + Session interface. + + + + + + + + + The value of the system-idle-hint + + + + + Returns TRUE if the idle-hint + property of every open session is TRUE or if there are no open sessions. + + + + + + + + An ISO 8601 format date-type string + + + + + Returns an ISO 8601 date-time string that corresponds to + the time of the last change of the system-idle-hint. + + + + + + + + + The Seat ID for the added seat + + + + + Emitted when a Seat has been added to the system. + + + + + + + + The Seat ID for the removed seat + + + + + Emitted when a Seat has been removed from the system. + + + + + + + + The value of the system-idle-hint + + + + + Emitted when the value of the system-idle-hint has changed. + + + + + + diff --git a/libkworkspace/org.freedesktop.UPower.xml b/libkworkspace/org.freedesktop.UPower.xml new file mode 100644 index 000000000..807f4e6ae --- /dev/null +++ b/libkworkspace/org.freedesktop.UPower.xml @@ -0,0 +1,395 @@ + + + + + + + + The UPower service is available via the system message + bus. To access the service, use + the org.freedesktop.UPower interface on + the /org/freedesktop/UPower object on + the D-Bus system bus service with the well-known + name org.freedesktop.UPower. + + + + +$ dbus-send --print-reply \ + --system \ + --dest=org.freedesktop.UPower \ + /org/freedesktop/UPower \ + org.freedesktop.UPower.EnumerateDevices + +method return sender=:1.386 -> dest=:1.451 reply_serial=2 + array [ + object path "/org/freedesktop/UPower/devices/line_power_AC" + object path "/org/freedesktop/UPower/devices/battery_BAT0" + ] + + + + + + + + + + + + An array of object paths for devices. + + + + + + Enumerate all power objects on the system. + + + + + + + + + + Object path of device that was added. + + + + + + Emitted when a device is added. + + + + + + + + + + Object path of device that was removed. + + + + + + Emitted when a device is removed. + + + + + + + + + + Object path of device that was changed. + + + + + + Emitted when a device changed. + + + + + + + + + + + + Emitted when one or more properties on the object changes. + + + + + + + + + + + + This signal is sent when the session is about to be suspended or + hibernated. + + + This signal is DEPRECATED. Use NotifySleep() instead. + + + + + + + + + + + + This signal is sent when the session is about to be suspended or + hibernated. + Session and system programs have one second to do anything required + before the sleep action is taken (such as sending out Avahi or + Jabber messages). + + + + + + + The sleep action type, e.g. suspend, + hibernate or hybrid. + + + + + + + + + + + + This signal is sent when the session has just returned from + Suspend() or Hibernate(). + + + This signal is DEPRECATED. Use NotifyResume() instead. + + + + + + + + + + + + This signal is sent when the session has just returned from + Suspend() or Hibernate(). + Session and system programs can then do anything required (such as + sending out Avahi or Jabber messages). + + + + + + + The sleep action type, e.g. suspend, + hibernate or hybrid. + + + + + + + + + + + + + This method tells UPower that the Suspend() or Hibernate() method + is about to be called. + This allows UPower to emit the Suspending signal whilst + session activities are happening that have to be done before the + suspend process is started. + + + This method would typically be called by the session power + management daemon, before it locks the screen and waits for the + screen to fade to black. + The session power management component would then call Suspend() or + Hibernate() when these syncronous tasks have completed. + + + If this method is not called than nothing bad will happen and + Suspend() or Hibernate() will block for the required second. + + + + + + + The sleep action type, e.g. suspend or + hibernate. + + + + + + + + + + + + + Suspends the computer into a low power state. + System state is not preserved if the power is lost. + + + If AboutToRequestSleep() has not been called then UPower will send + the Sleeping() signal and block for one second. + + + If AboutToRequestSleep() has been called less than one second + before this method is called then UPower will block for the + remaining time to complete one second of delay. + + + + + + + + + + + TRUE if allowed, otherwise FALSE + + + + + Check if the caller has (or can get) the PolicyKit privilege to call + Suspend. + + + + + + + + + + + + + Hibernates the computer into a low power state. + System state is preserved if the power is lost. + + + If AboutToRequestSleep() has not been called then UPower will send + the Sleeping() signal and block for one second. + + + If AboutToRequestSleep() has been called less than one second + before this method is called then UPower will block for the + remaining time to complete one second of delay. + + + + + + + + + + + TRUE if allowed, otherwise FALSE + + + + + Check if the caller has (or can get) the PolicyKit privilege to call + Hibernate. + + + + + + + + + + Version of the running daemon, e.g. 002. + + + + + + Whether the system is able to suspend. + + + + + + Whether the system is able to hibernate. + + + + + + Indicates whether the system is running on battery power. + This property is provided for convenience. + + + + + + Indicates whether the system is running on battery power and if the battery is critically low. + This property is provided for convenience. + + + + + + + + Indicates if the laptop lid is closed where the display cannot be seen. + + + + + + + + + + If the system has a lid device. + + + + + + + + + + If the system really has to sleep when the lid is closed. + Some laptops actually melt (!) if the lid is closed and the + computer keeps running. We blacklist those, and do something + sane for the other machines. + + + This allows us to set the default session policy to not + suspend on lid close if the laptop is docked, and be sure + the machine is not going to melt. + + + + + + + + + + If the system is currently docked. + Note: the "is-docked" value is the result of a heuristic, + which may involve testing the display output. + + + + + + + + diff --git a/libkworkspace/org.freedesktop.login1.Manager.xml b/libkworkspace/org.freedesktop.login1.Manager.xml new file mode 100644 index 000000000..800d2bc35 --- /dev/null +++ b/libkworkspace/org.freedesktop.login1.Manager.xml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libkworkspace/org.freedesktop.login1.Seat.xml b/libkworkspace/org.freedesktop.login1.Seat.xml new file mode 100644 index 000000000..a726b95ec --- /dev/null +++ b/libkworkspace/org.freedesktop.login1.Seat.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libkworkspace/org.freedesktop.login1.Session.xml b/libkworkspace/org.freedesktop.login1.Session.xml new file mode 100644 index 000000000..eb450d1ea --- /dev/null +++ b/libkworkspace/org.freedesktop.login1.Session.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libkworkspace/org.freedesktop.login1.User.xml b/libkworkspace/org.freedesktop.login1.User.xml new file mode 100644 index 000000000..802efaadb --- /dev/null +++ b/libkworkspace/org.freedesktop.login1.User.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libkworkspace/sessionmanagement.cpp b/libkworkspace/sessionmanagement.cpp new file mode 100644 index 000000000..43916de17 --- /dev/null +++ b/libkworkspace/sessionmanagement.cpp @@ -0,0 +1,192 @@ +/* + Copyright (C) 2019 David Edmundson + + This program is free software; you can redistribute it and/or + modify it under the terms of the Lesser GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "sessionmanagement.h" + +#include "sessionmanagementbackend.h" + +#include +#include + +#include "kscreenlocker_interface.h" +#include "screenlocker_interface.h" +#include "logoutprompt_interface.h" +#include "shutdown_interface.h" + +// add a constructor with the servica names and paths pre-populated +class LogoutPromptIface : public OrgKdeLogoutPromptInterface { + Q_OBJECT +public: + LogoutPromptIface(): + OrgKdeLogoutPromptInterface(QStringLiteral("org.kde.LogoutPrompt"), QStringLiteral("/LogoutPrompt"), QDBusConnection::sessionBus()) + {} +}; + +class ShutdownIface : public OrgKdeShutdownInterface { + Q_OBJECT +public: + ShutdownIface(): + OrgKdeShutdownInterface(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/Shutdown"), QDBusConnection::sessionBus()) + {} +}; + +SessionManagement::SessionManagement(QObject *parent): + QObject(parent) +{ + auto backend = SessionBackend::self(); + connect(backend, &SessionBackend::stateChanged, this, &SessionManagement::stateChanged); + connect(backend, &SessionBackend::canShutdownChanged, this, &SessionManagement::canShutdownChanged); + connect(backend, &SessionBackend::canRebootChanged, this, &SessionManagement::canRebootChanged); + connect(backend, &SessionBackend::canSuspendChanged, this, &SessionManagement::canSuspendChanged); + connect(backend, &SessionBackend::canHybridSuspendChanged, this, &SessionManagement::canHybridSuspendChanged); + connect(backend, &SessionBackend::canHibernateChanged, this, &SessionManagement::canHibernateChanged); +} + +bool SessionManagement::canShutdown() +{ + return canLogout() && SessionBackend::self()->canShutdown(); +} + +bool SessionManagement::canReboot() +{ + return canLogout() && SessionBackend::self()->canReboot(); +} + +bool SessionManagement::canLogout() +{ + // checking both is for compatibility with old kiosk configs + // authorizeAction is the "correct" one + return KAuthorized::authorizeAction(QStringLiteral("logout")) && KAuthorized::authorize(QStringLiteral("logout")); +} + +bool SessionManagement::canSuspend() +{ + return SessionBackend::self()->canSuspend(); +} + +bool SessionManagement::canHybridSuspend() +{ + return SessionBackend::self()->canHybridSuspend(); +} + +bool SessionManagement::canHibernate() +{ + return SessionBackend::self()->canHibernate(); +} + +bool SessionManagement::canSwitchUser() +{ + return KAuthorized::authorizeAction(QStringLiteral("start_new_session")); +} + +bool SessionManagement::canLock() +{ + return KAuthorized::authorizeAction(QStringLiteral("lock_screen")); +} + +SessionManagement::State SessionManagement::state() +{ + return SessionBackend::self()->state(); +} + +void SessionManagement::requestShutdown() +{ + if (!canShutdown()) { + return; + } + if (SessionBackend::self()->confirmLogout()) { + LogoutPromptIface iface; + iface.promptShutDown(); + } else { + ShutdownIface iface; + iface.logoutAndShutdown(); + } +} + +void SessionManagement::requestReboot() +{ + if (!canReboot()) { + return; + } + if (SessionBackend::self()->confirmLogout()) { + LogoutPromptIface iface; + iface.promptReboot(); + } else { + ShutdownIface iface; + iface.logoutAndReboot(); + } +} + +void SessionManagement::requestLogout() +{ + if (!canLogout()) { + return; + } + if (SessionBackend::self()->confirmLogout()) { + LogoutPromptIface iface; + iface.promptLogout(); + } else { + ShutdownIface iface; + iface.logout(); + } +} + +void SessionManagement::suspend() +{ + if (!canSuspend()) { + return; + } + SessionBackend::self()->suspend(); +} + +void SessionManagement::hybridSuspend() +{ + if (!canHybridSuspend()) { + return; + } + SessionManagement::hybridSuspend(); +} + +void SessionManagement::hibernate() +{ + if (!canHibernate()) { + return; + } + SessionBackend::self()->hibernate(); +} + +void SessionManagement::lock() +{ + if (!canLock()) { + return; + } + OrgFreedesktopScreenSaverInterface iface(QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QDBusConnection::sessionBus()); + iface.Lock(); +} + +void SessionManagement::switchUser() +{ + if (!canSwitchUser()) { + return; + } + OrgKdeScreensaverInterface iface(QStringLiteral("org.freedesktop.ScreenSaver"), QStringLiteral("/ScreenSaver"), QDBusConnection::sessionBus()); + iface.SwitchUser(); +} + +#include "sessionmanagement.moc" diff --git a/libkworkspace/sessionmanagement.h b/libkworkspace/sessionmanagement.h new file mode 100644 index 000000000..62a4c0125 --- /dev/null +++ b/libkworkspace/sessionmanagement.h @@ -0,0 +1,94 @@ +/* + Copyright (C) 2019 David Edmundson + + This program is free software; you can redistribute it and/or + modify it under the terms of the Lesser GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#pragma once + +#include +#include "kworkspace_export.h" + +/** + * Public facing (plasma/krunner whatever API) + * + */ +class KWORKSPACE_EXPORT SessionManagement: public QObject +{ + Q_OBJECT + Q_PROPERTY(State state READ state NOTIFY stateChanged) + + Q_PROPERTY(bool canShutdown READ canShutdown NOTIFY canShutdownChanged) + Q_PROPERTY(bool canReboot READ canReboot NOTIFY canRebootChanged) + Q_PROPERTY(bool canLogout READ canLogout NOTIFY canLogoutChanged) + Q_PROPERTY(bool canSuspend READ canSuspend NOTIFY canSuspendChanged) + Q_PROPERTY(bool canHibernate READ canHibernate NOTIFY canHibernateChanged) + Q_PROPERTY(bool canSwitchUser READ canSwitchUser NOTIFY canSwitchUserChanged) + Q_PROPERTY(bool canLock READ canLock NOTIFY canLockChanged) + +public: + enum State { + Loading, + Ready, + Error + }; + Q_ENUM(State) + + SessionManagement(QObject *parent = nullptr); + ~SessionManagement() override = default; + + State state(); + + bool canShutdown(); + bool canReboot(); + bool canLogout(); + bool canSuspend(); + bool canHybridSuspend(); + bool canHibernate(); + bool canSwitchUser(); + bool canLock(); + +public Q_SLOTS: + /** + * These requestX methods will either launch a prompt to shutdown or + * The user may cancel it at any point in the process + */ + void requestShutdown(); + void requestReboot(); + void requestLogout(); + + void suspend(); + void hybridSuspend(); + void hibernate(); + + void switchUser(); + void lock(); + +Q_SIGNALS: + void stateChanged(); + void canShutdownChanged(); + void canRebootChanged(); + void canLogoutChanged(); + void canSuspendChanged(); + void canHybridSuspendChanged(); + void canHibernateChanged(); + void canSwitchUserChanged(); + void canLockChanged(); + void aboutToSuspend(); + void resumingFromSuspend(); +private: + void *d; //unused, just reserving the space in case we go ABI stable +}; + diff --git a/libkworkspace/sessionmanagementbackend.cpp b/libkworkspace/sessionmanagementbackend.cpp new file mode 100644 index 000000000..7801ec6fd --- /dev/null +++ b/libkworkspace/sessionmanagementbackend.cpp @@ -0,0 +1,256 @@ +/* + Copyright (C) 2019 David Edmundson + + This program is free software; you can redistribute it and/or + modify it under the terms of the Lesser GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include "sessionmanagementbackend.h" + +#include +#include +#include + +#include +#include +#include + +#include + +#include "login1_manager_interface.h" +#include "consolekit_manager_interface.h" +#include "upower_interface.h" + +static SessionBackend* s_backend = nullptr; + +SessionBackend* SessionBackend::self() { + static QMutex mutex; + QMutexLocker lock(&mutex); + + if (s_backend) { + return s_backend; + } + if (LogindSessionBackend::exists()) { + s_backend = new LogindSessionBackend(); + } else if (ConsoleKitSessionBackend::exists() ){ + s_backend = new ConsoleKitSessionBackend(); + } else { + s_backend = new DummySessionBackend(); + } + Q_ASSERT(s_backend); + + return s_backend; +} + +SessionBackend::SessionBackend() +{ + m_kserverConfig = KSharedConfig::openConfig("ksmserverrc"); +} + +bool SessionBackend::confirmLogout() const +{ + return m_kserverConfig->group("General").readEntry("ConfirmLogout", true); // TODO +} + +DummySessionBackend::DummySessionBackend() +{ + qCritical() << "Could not load a session backend. Session management operations such as shutdown will not be operational. This is a setup issue."; +} + +/*********************************************************************************/ + +LogindSessionBackend::LogindSessionBackend() +{ + m_login1 = new OrgFreedesktopLogin1ManagerInterface(QStringLiteral("org.freedesktop.login1"), QStringLiteral("/org/freedesktop/login1"), QDBusConnection::systemBus(), this); + + auto propLoaded = [this](QDBusPendingCallWatcher *watcher, bool *argToUpdate) { + watcher->deleteLater(); + m_pendingJobs--; + QDBusPendingReply reply = *watcher; + if (reply.isError()) { + *argToUpdate = false; + } else { + // DECISION FIXME, do we want to show the option in the menu if the response is "challenge"? + *argToUpdate = reply.value() == QLatin1String("yes") ? true : false; + } + + if (m_pendingJobs == 0) { + m_state = SessionManagement::Ready; + emit stateChanged(); + emit canShutdownChanged(); + emit canRebootChanged(); + emit canSuspendChanged(); + emit canHibernateChanged(); + } + }; + + m_pendingJobs = 5; + { + auto watcher = new QDBusPendingCallWatcher(m_login1->CanPowerOff(), this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canShutdown)); + } + { + auto watcher = new QDBusPendingCallWatcher(m_login1->CanReboot(), this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canReboot)); + } + { + auto watcher = new QDBusPendingCallWatcher(m_login1->CanSuspend(), this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canSuspend)); + } + { + auto watcher = new QDBusPendingCallWatcher(m_login1->CanHybridSleep(), this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canHybridSuspend)); + } + { + auto watcher = new QDBusPendingCallWatcher(m_login1->CanHibernate(), this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, std::bind(propLoaded, std::placeholders::_1, &m_canHibernate)); + } + + connect(m_login1, &OrgFreedesktopLogin1ManagerInterface::PrepareForSleep, this, [this](bool sleeping) { + if (sleeping) { + emit aboutToSuspend(); + } else { + emit resumingFromSuspend(); + } + }); +} + +SessionManagement::State LogindSessionBackend::state() const +{ + return m_state; +} + +void LogindSessionBackend::shutdown() +{ + m_login1->PowerOff(true); +} + +void LogindSessionBackend::reboot() +{ + m_login1->Reboot(true); +} + +void LogindSessionBackend::suspend() +{ + m_login1->Suspend(true); +} + +void LogindSessionBackend::hybridSuspend() +{ + m_login1->HybridSleep(true); +} + +void LogindSessionBackend::hibernate() +{ + m_login1->Hibernate(true); +} + +bool LogindSessionBackend::canShutdown() const +{ + return m_canShutdown; +} + +bool LogindSessionBackend::canReboot() const +{ + return m_canReboot; +} + +bool LogindSessionBackend::canSuspend() const +{ + return m_canSuspend; +} + +bool LogindSessionBackend::canHybridSuspend() const +{ + return m_canHybridSuspend; +} + +bool LogindSessionBackend::canHibernate() const +{ + return m_canHibernate; +} + +/*********************************************************************************/ + +bool ConsoleKitSessionBackend::exists() +{ + return QDBusConnection::systemBus().interface()->isServiceRegistered(QStringLiteral("org.freedesktop.login1")); +} + +ConsoleKitSessionBackend::ConsoleKitSessionBackend() +{ + m_ck = new OrgFreedesktopConsoleKitManagerInterface(QStringLiteral("org.freedesktop.ConsoleKit"), QStringLiteral("/org/freedesktop/ConsoleKit/Manager"), QDBusConnection::systemBus(), this); + m_upower = new OrgFreedesktopUPowerInterface(QStringLiteral("org.freedesktop.UPower"), QStringLiteral("/org/freedesktop/UPower"), QDBusConnection::systemBus(), this); + + auto canStop = m_ck->CanStop(); + canStop.waitForFinished(); + m_canShutdown = canStop.value(); + + auto canRestart = m_ck->CanRestart(); + canRestart.waitForFinished(); + m_canReboot = canRestart.value(); + + m_canSuspend = m_upower->canSuspend(); + m_canHibernate = m_upower->canHibernate(); + + connect(m_upower, &OrgFreedesktopUPowerInterface::AboutToSleep, this, &SessionBackend::aboutToSuspend); + connect(m_upower, &OrgFreedesktopUPowerInterface::Resuming, this, &SessionBackend::resumingFromSuspend); + + m_state = SessionManagement::Ready; +} + +SessionManagement::State ConsoleKitSessionBackend::state() const +{ + return m_state; +} + +void ConsoleKitSessionBackend::shutdown() +{ + m_ck->Stop(); +} + +void ConsoleKitSessionBackend::reboot() +{ + m_ck->Restart(); +} + +void ConsoleKitSessionBackend::suspend() +{ + m_upower->Suspend(); +} + +void ConsoleKitSessionBackend::hibernate() +{ + m_upower->Hibernate(); +} + +bool ConsoleKitSessionBackend::canShutdown() const +{ + return m_canShutdown; +} + +bool ConsoleKitSessionBackend::canReboot() const +{ + return m_canReboot; +} + +bool ConsoleKitSessionBackend::canSuspend() const +{ + return m_canSuspend; +} + +bool ConsoleKitSessionBackend::canHibernate() const +{ + return m_canHibernate; +} diff --git a/libkworkspace/sessionmanagementbackend.h b/libkworkspace/sessionmanagementbackend.h new file mode 100644 index 000000000..724e30afb --- /dev/null +++ b/libkworkspace/sessionmanagementbackend.h @@ -0,0 +1,160 @@ +/* + Copyright (C) 2019 David Edmundson + + This program is free software; you can redistribute it and/or + modify it under the terms of the Lesser GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#pragma once + +#include +#include + +#include "sessionmanagement.h" +#include "kworkspace_export.h" + +class OrgFreedesktopLogin1ManagerInterface; +class OrgFreedesktopUPowerInterface; +class OrgFreedesktopConsoleKitManagerInterface; + +/** + * Performs direct system actions that could kill the session + * + * Semi-internal. Symbols exported, but not the header + * To be used only by the daemon that performs logout (currently ksmserver) + * + * All other users should go via the public SessionManagement that prompts and logs out properly. + */ +class KWORKSPACE_EXPORT SessionBackend: public QObject +{ + Q_OBJECT +public: + static SessionBackend* self(); + virtual SessionManagement::State state() const = 0; + + virtual void shutdown() = 0; + virtual void reboot() = 0; + virtual void suspend() = 0; + virtual void hybridSuspend() = 0; + virtual void hibernate() = 0; + + virtual bool canShutdown() const = 0; + virtual bool canReboot() const = 0; + virtual bool canSuspend() const = 0; + virtual bool canHybridSuspend() const = 0; + virtual bool canHibernate() const = 0; + + bool confirmLogout() const; + +Q_SIGNALS: + void stateChanged(); + void canShutdownChanged(); + void canRebootChanged(); + void canSuspendChanged(); + void canHybridSuspendChanged(); + void canHibernateChanged(); + + void aboutToSuspend(); + void resumingFromSuspend(); +protected: + SessionBackend(); + ~SessionBackend() override = default; +private: + KSharedConfigPtr m_kserverConfig; +}; + +/* + * This class wraps both Logind and CK2 + * Abstraction for that is handled in OrgFreedesktopLogin1ManagerInterface + */ +class LogindSessionBackend: public SessionBackend +{ + Q_OBJECT +public: + static bool exists(); + LogindSessionBackend(); + + SessionManagement::State state() const override; + void shutdown() override; + void reboot() override; + void suspend() override; + void hybridSuspend(); + void hibernate() override; + bool canShutdown() const override; + bool canReboot() const override; + bool canSuspend() const override; + bool canHybridSuspend() const; + bool canHibernate() const override; +private: + OrgFreedesktopLogin1ManagerInterface *m_login1; + SessionManagement::State m_state = SessionManagement::Loading; + bool m_canShutdown = false; + bool m_canReboot = false; + bool m_canSuspend = false; + bool m_canHybridSuspend = false; + bool m_canHibernate = false; + uint m_pendingJobs = 0; +}; + +/* Maybe misleadingly named, consolekit doesn't support suspend directly so it's + * suplemented with upower where available + */ +class ConsoleKitSessionBackend: public SessionBackend +{ + Q_OBJECT +public: + static bool exists(); + ConsoleKitSessionBackend(); + + SessionManagement::State state() const override; + void shutdown() override; + void reboot() override; + void suspend() override; + void hybridSuspend() override {} + void hibernate() override; + bool canShutdown() const override; + bool canReboot() const override; + bool canSuspend() const override; + bool canHybridSuspend() const override {return false;} + bool canHibernate() const override; +private: + OrgFreedesktopUPowerInterface *m_upower; + OrgFreedesktopConsoleKitManagerInterface *m_ck; + + SessionManagement::State m_state = SessionManagement::Loading; + bool m_canShutdown = false; + bool m_canReboot = false; + bool m_canSuspend = false; + bool m_canHibernate = false; + uint m_pendingJobs = 0; +}; + +class DummySessionBackend: public SessionBackend +{ + Q_OBJECT +public: + DummySessionBackend(); + + SessionManagement::State state() const override {return SessionManagement::Error;} + void shutdown() override {} + void reboot() override {} + void suspend() override {} + void hybridSuspend() override {} + void hibernate() override {} + bool canShutdown() const override {return false;} + bool canReboot() const override {return false;} + bool canSuspend() const override {return false;} + bool canHybridSuspend() const override {return false;} + bool canHibernate() const override {return false;} +}; diff --git a/libkworkspace/tests/CMakeLists.txt b/libkworkspace/tests/CMakeLists.txt new file mode 100644 index 000000000..31867134e --- /dev/null +++ b/libkworkspace/tests/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(sessiontest sessiontest.cpp) +target_link_libraries(sessiontest PW::KWorkspace) diff --git a/libkworkspace/tests/sessiontest.cpp b/libkworkspace/tests/sessiontest.cpp new file mode 100644 index 000000000..15111b910 --- /dev/null +++ b/libkworkspace/tests/sessiontest.cpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2019 David Edmundson + + This program is free software; you can redistribute it and/or + modify it under the terms of the Lesser GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ +#include + +#include +#include +#include + +int main(int argc, char ** argv) +{ + QCoreApplication app(argc, argv); + auto session = new SessionManagement(&app); + + QEventLoop e; + if (session->state() == SessionManagement::Loading) { + QObject::connect(session, &SessionManagement::stateChanged, &e, &QEventLoop::quit); + e.exec(); + } + + qDebug() << session->state(); + qDebug() << "canShutdown" << session->canShutdown(); + qDebug() << "canReboot" << session->canReboot(); + qDebug() << "canLogout" << session->canLogout(); + qDebug() << "canSuspend" << session->canSuspend(); + qDebug() << "canHibernate" << session->canHibernate(); + qDebug() << "canSwitchUser" << session->canSwitchUser(); + qDebug() << "canLock" << session->canLock(); +}