diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ include(ECMQueryQmake) include(KDEPackageAppTemplates) include(KDEClangFormat) +include(ECMConfiguredInstall) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma DocTools Runner NotifyConfig Su NewStuff Wallet @@ -149,6 +150,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,7 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KRunnerAppDBusInterfaceConfig.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) +ecm_install_configured_files(TEMPLATES plasma-krunner.service.in @ONLY 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/plasma-krunner.service.in b/krunner/plasma-krunner.service.in new file mode 100644 --- /dev/null +++ b/krunner/plasma-krunner.service.in @@ -0,0 +1,10 @@ +[Unit] +Description=KRunner + +[Service] +ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/krunner +KillMode=process +Type=dbus +BusName=org.kde.krunner +TimeoutSec=5sec +Slice=background.slice diff --git a/ksmserver/CMakeLists.txt b/ksmserver/CMakeLists.txt --- a/ksmserver/CMakeLists.txt +++ b/ksmserver/CMakeLists.txt @@ -73,3 +73,5 @@ ########### install files ############### install( FILES org.kde.KSMServerInterface.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR}) + +ecm_install_configured_files(TEMPLATES plasma-ksmserver.service.in DESTINATION ${SYSTEMD_USER_UNIT_INSTALL_DIR}) diff --git a/ksmserver/plasma-ksmserver.service.in b/ksmserver/plasma-ksmserver.service.in new file mode 100644 --- /dev/null +++ b/ksmserver/plasma-ksmserver.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=KDE Session Management Server +Wants=plasma-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=session.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,11 @@ source @CMAKE_INSTALL_FULL_LIBEXECDIR@/plasma-dev-prefix.sh +# This is a bit of a hack done because systemd starts in pam, and we only set our dev paths after all that is complete +# This copies everything into a transient runtime directory that systemd reads and reloads the units + +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 @@ -92,6 +92,8 @@ 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} ) +ecm_install_configured_files(TEMPLATES plasma-plasmashell.service.in @ONLY 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/plasma-plasmashell.service.in b/shell/plasma-plasmashell.service.in new file mode 100644 --- /dev/null +++ b/shell/plasma-plasmashell.service.in @@ -0,0 +1,15 @@ +[Unit] +Description=KDE Plasma Workspace +Wants=plasma-ksmserver.service plasma-kcminit.service + +[Service] +ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/plasmashell --no-respawn +Restart=on-failure +KillMode=process +Type=dbus +BusName=org.kde.plasmashell +Slice=session.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 @@ -37,3 +37,8 @@ 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}) +ecm_install_configured_files(TEMPLATES plasma-ksplash-ready.service.in @ONLY + 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,9 @@ qt5_add_dbus_interface(kcminit_startup_KDEINIT_SRCS ${klauncher_xml} klauncher_iface) kf5_add_kdeinit_executable( kcminit_startup ${kcminit_startup_KDEINIT_SRCS}) +ecm_install_configured_files(TEMPLATES plasma-kcminit-phase1.service.in plasma-kcminit.service.in + 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/plasma-kcminit-phase1.service.in b/startkde/kcminit/plasma-kcminit-phase1.service.in new file mode 100644 --- /dev/null +++ b/startkde/kcminit/plasma-kcminit-phase1.service.in @@ -0,0 +1,9 @@ +[Unit] +Description=KDE Configuration Module Initialization (Phase 1) +Requires=kcminit.service +After=kcminit.service kded.service +Slice=session.slice + +[Service] +Type=oneshot +ExecStart=@QtBinariesDir@/qdbus org.kde.kcminit /kcminit org.kde.KCMInit.runPhase1 diff --git a/startkde/kcminit/plasma-kcminit.service.in b/startkde/kcminit/plasma-kcminit.service.in new file mode 100644 --- /dev/null +++ b/startkde/kcminit/plasma-kcminit.service.in @@ -0,0 +1,11 @@ +[Unit] +Description=KDE Config Module Initialization + +[Service] +ExecStart=@CMAKE_INSTALL_FULL_BINDIR@/kcminit_startup +Restart=no +Type=forking +Slice=session.slice + +[Install] +Alias=plasma-workspace.service 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-ksplash-ready.service.in b/startkde/plasma-ksplash-ready.service.in new file mode 100644 --- /dev/null +++ b/startkde/plasma-ksplash-ready.service.in @@ -0,0 +1,9 @@ +[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 +Slice=session.slice 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; }