diff --git a/libkworkspace/CMakeLists.txt b/libkworkspace/CMakeLists.txt index f66013f9a..d33c13a57 100644 --- a/libkworkspace/CMakeLists.txt +++ b/libkworkspace/CMakeLists.txt @@ -1,79 +1,87 @@ set(kworkspace_LIB_SRCS kdisplaymanager.cpp kworkspace.cpp + session.cpp + sessionbackend.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(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 ${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) endif() diff --git a/libkworkspace/session.cpp b/libkworkspace/session.cpp new file mode 100644 index 000000000..03a2f5df6 --- /dev/null +++ b/libkworkspace/session.cpp @@ -0,0 +1,156 @@ +#include "session.h" + +#include "sessionbackend_p.h" + +#include +#include + +#include "kscreenlocker_interface.h" +#include "screenlocker_interface.h" +#include "logoutprompt_interface.h" +#include "shutdown_interface.h" + +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::canSuspendChanged, this, &SessionManagement::canSuspendChanged); + connect(backend, &SessionBackend::canShutdownChanged, this, &SessionManagement::canShutdownChanged); + connect(backend, &SessionBackend::canRebootChanged, this, &SessionManagement::canRebootChanged); + 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() +{ + return KAuthorized::authorizeAction("logout") && KAuthorized::authorize("logout"); +} + +bool SessionManagement::canSuspend() +{ + return SessionBackend::self()->canShutdown(); +} + +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::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 "session.moc" diff --git a/libkworkspace/session.h b/libkworkspace/session.h new file mode 100644 index 000000000..d82a70cbe --- /dev/null +++ b/libkworkspace/session.h @@ -0,0 +1,71 @@ +#pragma once + +#include + +/** + * Public facing (plasma/krunner whatever API) + * + */ +class /*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(); + + bool canShutdown(); + bool canReboot(); + bool canLogout(); + bool canSuspend(); + bool canHibernate(); + bool canSwitchUser(); + bool canLock(); + + State state(); + +public Q_SLOTS: + /** + * These methods will either launch a prompt to shutdown or + * + * The user may cancel it at any time + */ + void requestShutdown(); + void requestReboot(); + void requestLogout(); + + void suspend(); + void hibernate(); + + void switchUser(); + void lock(); + +Q_SIGNALS: + void stateChanged(); + void canShutdownChanged(); + void canRebootChanged(); + void canLogoutChanged(); + void canSuspendChanged(); + void canHibernateChanged(); + void canSwitchUserChanged(); + void canLockChanged(); +private: + void *d; //unused, just reserving the space in case we need it +}; + diff --git a/libkworkspace/sessionbackend.cpp b/libkworkspace/sessionbackend.cpp new file mode 100644 index 000000000..877af9bed --- /dev/null +++ b/libkworkspace/sessionbackend.cpp @@ -0,0 +1,8 @@ +#include "sessionbackend_p.h" + +#include + +DummySessionBackend::DummySessionBackend() +{ + qCritical() << "Could not load a session backend. Session management such as shutdown will not be operational. Fix your system"; +} diff --git a/libkworkspace/sessionbackend_p.h b/libkworkspace/sessionbackend_p.h new file mode 100644 index 000000000..1dbc6a2ed --- /dev/null +++ b/libkworkspace/sessionbackend_p.h @@ -0,0 +1,109 @@ +#pragma once + +#include + +#include "session.h" + +/** + * Performs direct system actions that could kill the session + * + * Semi-internal + * 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 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 hibernate() = 0; + + virtual bool canShutdown() const = 0; + virtual bool canReboot() const = 0; + virtual bool canSuspend() const = 0; + virtual bool canHibernate() const = 0; + + bool confirmLogout() const; + +Q_SIGNALS: + void stateChanged(); + void canShutdownChanged(); + void canRebootChanged(); + void canSuspendChanged(); + void canHibernateChanged(); +protected: + SessionBackend(); +}; + +//logind / ck2 backend +class LogindSessionBackend: SessionBackend +{ + Q_OBJECT +public: + LogindSessionBackend(const QString &address); + + SessionManagement::State state() const; + void shutdown(); + void reboot(); + void suspend(); + void hibernate(); + bool canShutdown() const; + bool canReboot() const; + bool canSuspend() const; + bool canHibernate() const; +private: +// SytemdIface *m_systemd; + bool m_canShutdown = false; + bool m_canReboot = false; + bool m_canSuspend = false; + bool m_canHibernate = false; +}; + +// Maybe misleadingly named, consolekit doesn't support suspend directly so it's +// suplemented with upower where available +class ConsoleKitSessionBackend: SessionBackend +{ + Q_OBJECT +public: + ConsoleKitSessionBackend(); + + SessionManagement::State state() const; + void shutdown(); + void reboot(); + void suspend(); + void hibernate(); + bool canShutdown() const; + bool canReboot() const; + bool canSuspend() const; + bool canHibernate() const; +private: + // SytemdIface *m_ck; + // UpowerIface *m_upower; + bool m_canShutdown = false; + bool m_canReboot = false; + bool m_canSuspend = false; + bool m_canHibernate = false; +}; + +class DummySessionBackend: SessionBackend +{ + Q_OBJECT +public: + DummySessionBackend(); + + SessionManagement::State state() const {return SessionManagement::Ready;} + void shutdown() {} + void reboot() {} + void suspend() {} + void hibernate() {} + bool canShutdown() const {return false;} + bool canReboot() const {return false;} + bool canSuspend() const {return false;} + bool canHibernate() const {return false;} +};