diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,8 @@ # locate qdbus in the Qt path because not every distro makes a symlink at /usr/bin/qdbus query_qmake(QtBinariesDir QT_INSTALL_BINS) +option(PLASMA_SYSTEMD_BOOT "Use systemd units for startup of plasma (WIP)" FALSE) + add_subdirectory(doc) add_subdirectory(libkworkspace) add_subdirectory(libdbusmenuqt) diff --git a/config-workspace.h.cmake b/config-workspace.h.cmake --- a/config-workspace.h.cmake +++ b/config-workspace.h.cmake @@ -155,3 +155,5 @@ /** place where plasma-frameworks things are installed */ #define PLASMA_RELATIVE_DATA_INSTALL_DIR "@PLASMA_RELATIVE_DATA_INSTALL_DIR@" + +#cmakedefine PLASMA_SYSTEMD_BOOT 1 diff --git a/krunner/CMakeLists.txt b/krunner/CMakeLists.txt --- a/krunner/CMakeLists.txt +++ b/krunner/CMakeLists.txt @@ -41,4 +41,8 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KRunnerAppDBusInterfaceConfig.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) +configure_file(krunner.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/krunner.service @ONLY) +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/krunner.service DESTINATION + ${SYSTEMD_USER_UNIT_INSTALL_DIR}) + add_subdirectory(update) diff --git a/krunner/dbus/org.kde.krunner.service.in b/krunner/dbus/org.kde.krunner.service.in --- a/krunner/dbus/org.kde.krunner.service.in +++ b/krunner/dbus/org.kde.krunner.service.in @@ -1,3 +1,4 @@ [D-BUS Service] Name=org.kde.krunner Exec=@KDE_INSTALL_FULL_BINDIR@/krunner +SystemdService=krunner.service diff --git a/krunner/krunner.service.cmake b/krunner/krunner.service.cmake new file mode 100644 --- /dev/null +++ b/krunner/krunner.service.cmake @@ -0,0 +1,10 @@ +[Unit] +Description=KRunner + +[Service] +ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/krunner +KillMode=process +Type=dbus +BusName=org.kde.krunner +Slice=plasma.slice +TimeoutSec=5sec diff --git a/ksmserver/CMakeLists.txt b/ksmserver/CMakeLists.txt --- a/ksmserver/CMakeLists.txt +++ b/ksmserver/CMakeLists.txt @@ -73,3 +73,8 @@ ########### install files ############### install( FILES org.kde.KSMServerInterface.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR}) + +configure_file(ksmserver.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/ksmserver.service @ONLY) + +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/ksmserver.service DESTINATION + ${SYSTEMD_USER_UNIT_INSTALL_DIR}) diff --git a/ksmserver/ksmserver.service.cmake b/ksmserver/ksmserver.service.cmake new file mode 100644 --- /dev/null +++ b/ksmserver/ksmserver.service.cmake @@ -0,0 +1,13 @@ +[Unit] +Description=KDE Session Management Server +Wants=kcminit.service + +[Service] +ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/ksmserver +# This magic minus sign means don't fail if exit code is non-zero... +ExecStartPost=-@QtBinariesDir@/qdbus org.kde.KSplash /KSplash org.kde.KSplash.setStage ksmserver +BusName=org.kde.ksmserver +Slice=plasma.slice + +[Install] +WantedBy=plasma-core.target diff --git a/ksmserver/server.cpp b/ksmserver/server.cpp --- a/ksmserver/server.cpp +++ b/ksmserver/server.cpp @@ -95,6 +95,8 @@ #include "kscreenlocker_interface.h" #include "kwinsession_interface.h" +#include "../config-workspace.h" + KSMServer* the_server = nullptr; KSMServer* KSMServer::self() @@ -1063,7 +1065,16 @@ { assert( state == LaunchingWM ); - if (!(qEnvironmentVariableIsSet("WAYLAND_DISPLAY") || qEnvironmentVariableIsSet("WAYLAND_SOCKET"))) { + bool shouldLaunchWm = true; + if (qEnvironmentVariableIsSet("WAYLAND_DISPLAY") || qEnvironmentVariableIsSet("WAYLAND_SOCKET")) { + shouldLaunchWm = false; + } + +#ifdef PLASMA_SYSTEMD_BOOT + shouldLaunchWm = false; +#endif + + if (shouldLaunchWm) { // when we have a window manager, we start it first and give // it some time before launching other processes. Results in a // visually more appealing startup. diff --git a/login-sessions/plasmawayland-dev.desktop.cmake b/login-sessions/plasmawayland-dev.desktop.cmake --- a/login-sessions/plasmawayland-dev.desktop.cmake +++ b/login-sessions/plasmawayland-dev.desktop.cmake @@ -1,5 +1,5 @@ [Desktop Entry] -Exec=dbus-run-session @CMAKE_INSTALL_FULL_LIBEXECDIR@/startplasma-dev.sh -wayland +Exec=@CMAKE_INSTALL_FULL_LIBEXECDIR@/startplasma-dev.sh -wayland DesktopNames=KDE Name=Plasma (Development, Wayland ${CMAKE_INSTALL_FULL_BINDIR}) Name[ast]=Plasma (Desendolcu, Wayland ${CMAKE_INSTALL_FULL_BINDIR}) diff --git a/login-sessions/plasmawayland.desktop.cmake b/login-sessions/plasmawayland.desktop.cmake --- a/login-sessions/plasmawayland.desktop.cmake +++ b/login-sessions/plasmawayland.desktop.cmake @@ -1,5 +1,5 @@ [Desktop Entry] -Exec=dbus-run-session ${CMAKE_INSTALL_FULL_BINDIR}/startplasma-wayland +Exec=${CMAKE_INSTALL_FULL_BINDIR}/startplasma-wayland TryExec=${CMAKE_INSTALL_FULL_BINDIR}/startplasma-wayland DesktopNames=KDE Name=Plasma (Wayland) diff --git a/login-sessions/startplasma-dev.sh.cmake b/login-sessions/startplasma-dev.sh.cmake --- a/login-sessions/startplasma-dev.sh.cmake +++ b/login-sessions/startplasma-dev.sh.cmake @@ -2,4 +2,10 @@ source @CMAKE_INSTALL_FULL_LIBEXECDIR@/plasma-dev-prefix.sh +# Copy systemd unit files in a custom prefix dir into the user's runtime directory + +mkdir -p "$XDG_RUNTIME_DIR/systemd/user.control" +command cp -r @KDE_INSTALL_FULL_SYSTEMDUSERUNITDIR@/* $XDG_RUNTIME_DIR/systemd/user.control +systemctl --user daemon-reload + startplasma$@ diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -87,11 +87,16 @@ configure_file(org.kde.plasmashell.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.desktop @ONLY) +configure_file(plasmashell.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/plasmashell.service @ONLY) + install(TARGETS plasmashell ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.plasmashell.desktop DESTINATION ${KDE_INSTALL_AUTOSTARTDIR}) install( FILES dbus/org.kde.PlasmaShell.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} ) +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/plasmashell.service DESTINATION + ${SYSTEMD_USER_UNIT_INSTALL_DIR}) + install(FILES scripting/plasma-layouttemplate.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR}) diff --git a/shell/org.kde.plasmashell.desktop.cmake b/shell/org.kde.plasmashell.desktop.cmake --- a/shell/org.kde.plasmashell.desktop.cmake +++ b/shell/org.kde.plasmashell.desktop.cmake @@ -52,6 +52,7 @@ Name[zh_TW]=Plasma 桌面工作空間 Type=Application X-KDE-StartupNotify=false +X-KDE-HiddenUnderSystemd=true X-DBUS-ServiceName=org.kde.plasmashell OnlyShowIn=KDE; X-KDE-autostart-phase=0 diff --git a/shell/plasmashell.service.cmake b/shell/plasmashell.service.cmake new file mode 100644 --- /dev/null +++ b/shell/plasmashell.service.cmake @@ -0,0 +1,15 @@ +[Unit] +Description=KDE Plasma Workspace +Wants=ksmserver.service kcminit.service + +[Service] +ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/plasmashell --no-respawn +Restart=on-failure +KillMode=process +Type=dbus +BusName=org.kde.plasmashell +Slice=plasma.slice +TimeoutSec=5sec + +[Install] +WantedBy=plasma-core.target diff --git a/startkde/CMakeLists.txt b/startkde/CMakeLists.txt --- a/startkde/CMakeLists.txt +++ b/startkde/CMakeLists.txt @@ -33,7 +33,14 @@ configure_file(config-startplasma.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-startplasma.h) +configure_file(ksplash-ready.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/ksplash-ready.service @ONLY) + install(TARGETS startplasma-x11 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS startplasma-wayland ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS startplasma-waylandsession DESTINATION ${KDE_INSTALL_LIBEXECDIR}) install(PROGRAMS plasma-sourceenv.sh DESTINATION ${KDE_INSTALL_LIBEXECDIR}) + +install(FILES plasma-workspace.target DESTINATION ${SYSTEMD_USER_UNIT_INSTALL_DIR}) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ksplash-ready.service DESTINATION ${SYSTEMD_USER_UNIT_INSTALL_DIR}) + +install(FILES plasma-core.target DESTINATION ${SYSTEMD_USER_UNIT_INSTALL_DIR}) diff --git a/startkde/kcminit/CMakeLists.txt b/startkde/kcminit/CMakeLists.txt --- a/startkde/kcminit/CMakeLists.txt +++ b/startkde/kcminit/CMakeLists.txt @@ -32,6 +32,14 @@ qt5_add_dbus_interface(kcminit_startup_KDEINIT_SRCS ${klauncher_xml} klauncher_iface) kf5_add_kdeinit_executable( kcminit_startup ${kcminit_startup_KDEINIT_SRCS}) +configure_file(kcminit.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/kcminit.service @ONLY) +install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kcminit.service DESTINATION + ${SYSTEMD_USER_UNIT_INSTALL_DIR}) + +configure_file(kcminit-phase1.service.cmake ${CMAKE_CURRENT_BINARY_DIR}/kcminit-phase1.service @ONLY) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kcminit-phase1.service DESTINATION + ${SYSTEMD_USER_UNIT_INSTALL_DIR}) + target_link_libraries(kdeinit_kcminit_startup Qt5::Core Qt5::Gui Qt5::DBus KF5::CoreAddons KF5::Service KF5::I18n PW::KWorkspace) if (XCB_XCB_FOUND) target_link_libraries(kdeinit_kcminit_startup XCB::XCB) diff --git a/startkde/kcminit/kcminit-phase1.service.cmake b/startkde/kcminit/kcminit-phase1.service.cmake new file mode 100644 --- /dev/null +++ b/startkde/kcminit/kcminit-phase1.service.cmake @@ -0,0 +1,8 @@ +[Unit] +Description=KDE Configuration Module Initialization (Phase 1) +Requires=kcminit.service +After=kcminit.service kded.service + +[Service] +Type=oneshot +ExecStart=@QtBinariesDir@/qdbus org.kde.kcminit /kcminit org.kde.KCMInit.runPhase1 diff --git a/startkde/kcminit/kcminit.service.cmake b/startkde/kcminit/kcminit.service.cmake new file mode 100644 --- /dev/null +++ b/startkde/kcminit/kcminit.service.cmake @@ -0,0 +1,10 @@ +[Unit] +Description=KDE Config Module Initialization + +[Service] +ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/kcminit_startup +Restart=no +Type=forking + +[Install] +Alias=plasma-workspace.service diff --git a/startkde/ksplash-ready.service.cmake b/startkde/ksplash-ready.service.cmake new file mode 100644 --- /dev/null +++ b/startkde/ksplash-ready.service.cmake @@ -0,0 +1,8 @@ +[Unit] +Description=KSplash "ready" Stage +Wants=plasma-core.target +After=plasma-core.target + +[Service] +Type=oneshot +ExecStart=-@QtBinariesDir@/qdbus org.kde.KSplash /KSplash org.kde.KSplash.setStage ready diff --git a/startkde/plasma-core.target b/startkde/plasma-core.target new file mode 100644 --- /dev/null +++ b/startkde/plasma-core.target @@ -0,0 +1,5 @@ +[Unit] +Description=KDE Plasma Workspace Core +Wants=plasmashell.service kwin_x11.service kcminit.service kded.service kcminit-phase1.service +Requires=ksmserver.service +BindsTo=ksmserver.service diff --git a/startkde/plasma-workspace.target b/startkde/plasma-workspace.target new file mode 100644 --- /dev/null +++ b/startkde/plasma-workspace.target @@ -0,0 +1,5 @@ +[Unit] +Description=KDE Plasma Workspace +Requires=plasma-core.target ksplash-ready.service +Wants=xdg-desktop.target +After=plasma-core.target diff --git a/startkde/startplasma.cpp b/startkde/startplasma.cpp --- a/startkde/startplasma.cpp +++ b/startkde/startplasma.cpp @@ -33,6 +33,9 @@ #include "startplasma.h" +#include "../config-workspace.h" + + QTextStream out(stderr); void messageBox(const QString &text) @@ -350,6 +353,30 @@ // If the session should be locked from the start (locked autologin), // lock now and do the rest of the KDE startup underneath the locker. + bool rc = true; + QEventLoop e; + + QDBusServiceWatcher serviceWatcher; + serviceWatcher.setConnection(QDBusConnection::sessionBus()); + + // We want to exit when both ksmserver and plasma-session-shutdown have finished + // This also closes if ksmserver crashes unexpectedly, as in those cases plasma-shutdown is not running + serviceWatcher.addWatchedService(QStringLiteral("org.kde.ksmserver")); + serviceWatcher.addWatchedService(QStringLiteral("org.kde.shutdown")); + serviceWatcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration); + + QObject::connect(&serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, [&]() { + const QStringList watchedServices = serviceWatcher.watchedServices(); + bool plasmaSessionRunning = std::any_of(watchedServices.constBegin(), watchedServices.constEnd(), [](const QString &service) { + return QDBusConnection::sessionBus().interface()->isServiceRegistered(service); + }); + if (!plasmaSessionRunning) { + e.quit(); + } + }); + +#ifndef PLASMA_SYSTEMD_BOOT + QProcess startPlasmaSession; QStringList plasmaSessionOptions; if (wayland) { @@ -363,21 +390,7 @@ } } - bool rc = true; - QEventLoop e; - - QProcess startPlasmaSession; startPlasmaSession.setProcessChannelMode(QProcess::ForwardedChannels); - - QDBusServiceWatcher serviceWatcher; - serviceWatcher.setConnection(QDBusConnection::sessionBus()); - - // We want to exit when both ksmserver and plasma-session-shutdown have finished - // This also closes if ksmserver crashes unexpectedly, as in those cases plasma-shutdown is not running - serviceWatcher.addWatchedService(QStringLiteral("org.kde.ksmserver")); - serviceWatcher.addWatchedService(QStringLiteral("org.kde.shutdown")); - serviceWatcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration); - QObject::connect(&startPlasmaSession, QOverload::of(&QProcess::finished), [&rc, &e](int exitCode, QProcess::ExitStatus) { if (exitCode == 255) { // Startup error @@ -387,18 +400,23 @@ } }); - QObject::connect(&serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, [&]() { - const QStringList watchedServices = serviceWatcher.watchedServices(); - bool plasmaSessionRunning = std::any_of(watchedServices.constBegin(), watchedServices.constEnd(), [](const QString &service) { - return QDBusConnection::sessionBus().interface()->isServiceRegistered(service); - }); - if (!plasmaSessionRunning) { - e.quit(); + startPlasmaSession.start(QStringLiteral(CMAKE_INSTALL_FULL_BINDIR "/plasma_session"), plasmaSessionOptions); + rc = startPlasmaSession.waitForStarted(); +#else + auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"), + QStringLiteral("/org/freedesktop/systemd1"), + QStringLiteral("org.freedesktop.systemd1.Manager"), + QStringLiteral("StartUnit")); + msg << QStringLiteral("plasma-workspace.target") << QStringLiteral("fail"); + auto reply = QDBusConnection::sessionBus().call(msg); + if (reply.type() == QDBusMessage::ErrorMessage) { + rc = false; } - }); +#endif - startPlasmaSession.start(QStringLiteral(CMAKE_INSTALL_FULL_BINDIR "/plasma_session"), plasmaSessionOptions); - e.exec(); + if (rc) { + e.exec(); + } return rc; }