diff --git a/ksmserver/CMakeLists.txt b/ksmserver/CMakeLists.txt index 84711d15c..761c00957 100644 --- a/ksmserver/CMakeLists.txt +++ b/ksmserver/CMakeLists.txt @@ -1,75 +1,75 @@ add_definitions(-DTRANSLATION_DOMAIN=\"ksmserver\") check_library_exists(ICE _IceTransNoListen "" HAVE__ICETRANSNOLISTEN) configure_file(config-ksmserver.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ksmserver.h) add_definitions("-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII") add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) include_directories(${CMAKE_CURRENT_BINARY_DIR}) ########### next target ############### set(ksmserver_KDEINIT_SRCS main.cpp server.cpp legacy.cpp logout.cpp client.cpp ) ecm_qt_declare_logging_category(ksmserver_KDEINIT_SRCS HEADER ksmserver_debug.h IDENTIFIER KSMSERVER CATEGORY_NAME org.kde.kf5.ksmserver) qt5_add_dbus_adaptor( ksmserver_KDEINIT_SRCS org.kde.KSMServerInterface.xml server.h KSMServer ) qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/org.kde.screensaver.xml kscreenlocker_interface ) qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS org.kde.LogoutPrompt.xml logoutprompt_interface) qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS org.kde.KWin.Session.xml kwinsession_interface) set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml) qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS ${klauncher_xml} klauncher_interface ) qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS ../startkde/plasma-session/org.kde.Startup.xml startup_interface ) -qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS ../startkde/plasma-session/org.kde.Shutdown.xml shutdown_interface ) +qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS ../startkde/plasma-shutdown/org.kde.Shutdown.xml shutdown_interface ) kf5_add_kdeinit_executable( ksmserver ${ksmserver_KDEINIT_SRCS}) set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KSMServerDBusInterface") configure_package_config_file(KSMServerDBusInterfaceConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/KSMServerDBusInterfaceConfig.cmake PATH_VARS KDE_INSTALL_DBUSINTERFACEDIR INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}) target_link_libraries(kdeinit_ksmserver PW::KScreenLocker PW::KWorkspace KF5::XmlGui KF5::GlobalAccel KF5::I18n KF5::KIOCore KF5::KIOWidgets ${X11_LIBRARIES} ${X11_Xrender_LIB} Qt5::X11Extras KF5::Solid Qt5::Quick KF5::Declarative KF5::DBusAddons KF5::Notifications KF5::Package KF5::WindowSystem Qt5::Concurrent ) install(TARGETS kdeinit_ksmserver ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS ksmserver ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KSMServerDBusInterfaceConfig.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) ########### install files ############### install( FILES org.kde.KSMServerInterface.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR}) diff --git a/libkworkspace/CMakeLists.txt b/libkworkspace/CMakeLists.txt index a1869fae2..85cbb5e6c 100644 --- a/libkworkspace/CMakeLists.txt +++ b/libkworkspace/CMakeLists.txt @@ -1,107 +1,107 @@ set(kworkspace_LIB_SRCS kdisplaymanager.cpp kworkspace.cpp sessionmanagement.cpp sessionmanagementbackend.cpp updatelaunchenvjob.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}) qt5_add_dbus_interface(kworkspace_LIB_SRCS ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml klauncher_interface) 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}/startkde/plasma-session/org.kde.Startup.xml startup_interface) -qt5_add_dbus_interface(kworkspace_LIB_SRCS ${plasma-workspace_SOURCE_DIR}/startkde/plasma-session/org.kde.Shutdown.xml shutdown_interface) +qt5_add_dbus_interface(kworkspace_LIB_SRCS ${plasma-workspace_SOURCE_DIR}/startkde/plasma-shutdown/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 updatelaunchenvjob.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/startkde/CMakeLists.txt b/startkde/CMakeLists.txt index ac997d838..f152bd187 100644 --- a/startkde/CMakeLists.txt +++ b/startkde/CMakeLists.txt @@ -1,38 +1,39 @@ add_subdirectory(kcminit) add_subdirectory(ksyncdbusenv) add_subdirectory(waitforname) add_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII) add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) qt5_add_dbus_interface( startplasma_SRCS ${CMAKE_SOURCE_DIR}/ksplash/ksplashqml/org.kde.KSplash.xml ksplashinterface ) add_executable(startplasma-x11 startplasma.cpp startplasma-x11.cpp kcheckrunning/kcheckrunning.cpp ${startplasma_SRCS}) add_executable(startplasma-wayland startplasma.cpp startplasma-wayland.cpp ${startplasma_SRCS}) add_executable(startplasma-waylandsession startplasma.cpp startplasma-waylandsession.cpp ${startplasma_SRCS}) target_include_directories(startplasma-x11 PRIVATE ${X11_X11_INCLUDE_PATH}) target_link_libraries(startplasma-x11 PRIVATE Qt5::Core Qt5::DBus KF5::ConfigCore ${X11_X11_LIB} # for kcheckrunning ) target_link_libraries(startplasma-wayland PRIVATE Qt5::Core Qt5::DBus KF5::ConfigCore) target_link_libraries(startplasma-waylandsession PRIVATE Qt5::Core Qt5::DBus KF5::ConfigCore) add_subdirectory(plasma-session) +add_subdirectory(plasma-shutdown) #FIXME: reconsider, looks fishy if(NOT CMAKE_INSTALL_PREFIX STREQUAL "/usr") set_property(SOURCE startplasma.cpp APPEND PROPERTY COMPILE_DEFINITIONS XCURSOR_PATH="${KDE_INSTALL_FULL_DATAROOTDIR}/icons:$XCURSOR_PATH:~/.icons:/usr/share/icons:/usr/share/pixmaps:/usr/X11R6/lib/X11/icons") endif() configure_file(config-startplasma.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-startplasma.h) 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}) diff --git a/startkde/plasma-session/CMakeLists.txt b/startkde/plasma-session/CMakeLists.txt index 8cf92cf8e..7b98a112a 100644 --- a/startkde/plasma-session/CMakeLists.txt +++ b/startkde/plasma-session/CMakeLists.txt @@ -1,43 +1,40 @@ add_subdirectory(plasma-autostart-list) set(plasma_session_SRCS main.cpp autostart.cpp startup.cpp - shutdown.cpp ) ecm_qt_declare_logging_category(plasma_session_SRCS HEADER debug.h IDENTIFIER PLASMA_SESSION CATEGORY_NAME org.kde.plasma.session) qt5_add_dbus_adaptor( plasma_session_SRCS org.kde.Startup.xml startup.h Startup) -qt5_add_dbus_adaptor( plasma_session_SRCS org.kde.Shutdown.xml shutdown.h Shutdown) -qt5_add_dbus_interface( plasma_session_SRCS org.kde.Shutdown.xml shutdown_interface) set(kcminit_adaptor ${plasma-workspace_SOURCE_DIR}/startkde/kcminit/main.h) set(kcminit_xml ${CMAKE_CURRENT_BINARY_DIR}/org.kde.KCMinit.xml) qt5_generate_dbus_interface( ${kcminit_adaptor} ${kcminit_xml} ) qt5_add_dbus_interface( plasma_session_SRCS ${kcminit_xml} kcminit_interface ) qt5_add_dbus_interface( plasma_session_SRCS ${KDED_DBUS_INTERFACE} kded_interface ) set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml) qt5_add_dbus_interface( plasma_session_SRCS ${klauncher_xml} klauncher_interface ) qt5_add_dbus_interface( plasma_session_SRCS ../../ksmserver/org.kde.KSMServerInterface.xml ksmserver_interface ) add_executable(plasma_session ${plasma_session_SRCS}) target_link_libraries(plasma_session Qt5::Core Qt5::DBus KF5::ConfigCore KF5::Service KF5::CoreAddons KF5::Notifications KF5::KIOCore PW::KWorkspace ${PHONON_LIBRARIES} ) install(TARGETS plasma_session ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/startkde/plasma-session/main.cpp b/startkde/plasma-session/main.cpp index 9bea801e6..5cdcfc01a 100644 --- a/startkde/plasma-session/main.cpp +++ b/startkde/plasma-session/main.cpp @@ -1,36 +1,33 @@ /* * Copyright © 2018 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. */ #include "startup.h" -#include "shutdown.h" #include int main(int argc, char **argv) { QCoreApplication app(argc, argv); // This is needed with KJobs in a QCoreApplication as when they finish // the internal QEventLoopLocker expires and our app exits app.setQuitLockEnabled(false); new Startup(&app); - new Shutdown(&app); - app.exec(); } diff --git a/startkde/plasma-session/startup.cpp b/startkde/plasma-session/startup.cpp index 834c50efe..760b6585a 100644 --- a/startkde/plasma-session/startup.cpp +++ b/startkde/plasma-session/startup.cpp @@ -1,467 +1,468 @@ /***************************************************************** Copyright 2000 Matthias Ettrich Copyright 2005 Lubos Lunak Copyright 2018 David Edmundson relatively small extensions by Oswald Buddenhagen some code taken from the dcopserver (part of the KDE libraries), which is Copyright 1999 Matthias Ettrich Copyright 1999 Preston Brown Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************/ #include "startup.h" #include "debug.h" #include "kcminit_interface.h" #include "kded_interface.h" #include #include "ksmserver_interface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "startupadaptor.h" class Phase: public KCompositeJob { Q_OBJECT public: Phase(const AutoStart &autostart, QObject *parent) : KCompositeJob(parent) , m_autostart(autostart) {} bool addSubjob(KJob *job) override { bool rc = KCompositeJob::addSubjob(job); job->start(); return rc; } void slotResult(KJob *job) override { KCompositeJob::slotResult(job); if (!hasSubjobs()) { emitResult(); } } protected: const AutoStart m_autostart; }; class StartupPhase0: public Phase { Q_OBJECT public: StartupPhase0(const AutoStart& autostart, QObject *parent) : Phase(autostart, parent) {} void start() override { qCDebug(PLASMA_SESSION) << "Phase 0"; addSubjob(new AutoStartAppsJob(m_autostart, 0)); addSubjob(new KCMInitJob()); addSubjob(new SleepJob()); } }; class StartupPhase1: public Phase { Q_OBJECT public: StartupPhase1(const AutoStart& autostart, QObject *parent) : Phase(autostart, parent) {} void start() override { qCDebug(PLASMA_SESSION) << "Phase 1"; addSubjob(new AutoStartAppsJob(m_autostart, 1)); } }; class StartupPhase2: public Phase { Q_OBJECT public: StartupPhase2(const AutoStart& autostart, QObject *parent) : Phase(autostart, parent) {} void runUserAutostart(); bool migrateKDE4Autostart(const QString &folder); void start() override { qCDebug(PLASMA_SESSION) << "Phase 2"; addSubjob(new AutoStartAppsJob(m_autostart, 2)); addSubjob(new KDEDInitJob()); runUserAutostart(); } }; SleepJob::SleepJob() { } void SleepJob::start() { auto t = new QTimer(this); connect(t, &QTimer::timeout, this, [this]() {emitResult();}); t->start(100); } // Put the notification in its own thread as it can happen that // PulseAudio will start initializing with this, so let's not // block the main thread with waiting for PulseAudio to start class NotificationThread : public QThread { Q_OBJECT void run() override { // We cannot parent to the thread itself so let's create // a QObject on the stack and parent everythign to it QObject parent; KNotifyConfig notifyConfig(QStringLiteral("plasma_workspace"), QList< QPair >(), QStringLiteral("startkde")); const QString action = notifyConfig.readEntry(QStringLiteral("Action")); if (action.isEmpty() || !action.split(QLatin1Char('|')).contains(QLatin1String("Sound"))) { // no startup sound configured return; } Phonon::AudioOutput *m_audioOutput = new Phonon::AudioOutput(Phonon::NotificationCategory, &parent); QString soundFilename = notifyConfig.readEntry(QStringLiteral("Sound")); if (soundFilename.isEmpty()) { qCWarning(PLASMA_SESSION) << "Audio notification requested, but no sound file provided in notifyrc file, aborting audio notification"; return; } QUrl soundURL; const auto dataLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); for (const QString &dataLocation: dataLocations) { soundURL = QUrl::fromUserInput(soundFilename, dataLocation + QStringLiteral("/sounds"), QUrl::AssumeLocalFile); if (soundURL.isLocalFile() && QFile::exists(soundURL.toLocalFile())) { break; } else if (!soundURL.isLocalFile() && soundURL.isValid()) { break; } soundURL.clear(); } if (soundURL.isEmpty()) { qCWarning(PLASMA_SESSION) << "Audio notification requested, but sound file from notifyrc file was not found, aborting audio notification"; return; } Phonon::MediaObject *m = new Phonon::MediaObject(&parent); connect(m, &Phonon::MediaObject::finished, this, &NotificationThread::quit); Phonon::createPath(m, m_audioOutput); m->setCurrentSource(soundURL); m->play(); exec(); } }; Startup::Startup(QObject *parent): QObject(parent) { new StartupAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/Startup"), QStringLiteral("org.kde.Startup"), this); QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.Startup")); upAndRunning(QStringLiteral("ksmserver")); const AutoStart autostart; KJob* phase1; QProcessEnvironment kdedProcessEnv; kdedProcessEnv.insert(QStringLiteral("KDED_STARTED_BY_KDEINIT"), QStringLiteral("1")); const QVector sequence = { new StartProcessJob(QStringLiteral("kcminit_startup"), {}), new StartServiceJob(QStringLiteral("kded5"), {}, QStringLiteral("org.kde.kded5"), kdedProcessEnv), new StartServiceJob(QStringLiteral("ksmserver"), QCoreApplication::instance()->arguments().mid(1), QStringLiteral("org.kde.ksmserver")), new StartupPhase0(autostart, this), phase1 = new StartupPhase1(autostart, this), new RestoreSessionJob(), new StartupPhase2(autostart, this), }; KJob* last = nullptr; for(KJob* job : sequence) { if (last) { connect(last, &KJob::finished, job, &KJob::start); } last = job; } connect(phase1, &KJob::finished, this, []() { NotificationThread *loginSound = new NotificationThread(); connect(loginSound, &NotificationThread::finished, loginSound, &NotificationThread::deleteLater); loginSound->start();}); connect(sequence.last(), &KJob::finished, this, &Startup::finishStartup); sequence.first()->start(); } void Startup::upAndRunning( const QString& msg ) { QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), QStringLiteral("/KSplash"), QStringLiteral("org.kde.KSplash"), QStringLiteral("setStage")); ksplashProgressMessage.setArguments(QList() << msg); QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); } void Startup::finishStartup() { qCDebug(PLASMA_SESSION) << "Finished"; upAndRunning(QStringLiteral("ready")); + qApp->quit(); } void Startup::updateLaunchEnv(const QString &key, const QString &value) { qputenv(key.toLatin1(), value.toLatin1()); } KCMInitJob::KCMInitJob() : KJob() { } void KCMInitJob::start() { org::kde::KCMInit kcminit(QStringLiteral("org.kde.kcminit"), QStringLiteral("/kcminit"), QDBusConnection::sessionBus()); kcminit.setTimeout(10 * 1000); QDBusPendingReply pending = kcminit.runPhase1(); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this]() {emitResult();}); connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater); } KDEDInitJob::KDEDInitJob() { } void KDEDInitJob::start() { qCDebug(PLASMA_SESSION()); org::kde::kded5 kded( QStringLiteral("org.kde.kded5"), QStringLiteral("/kded"), QDBusConnection::sessionBus()); auto pending = kded.loadSecondPhase(); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this]() {emitResult();}); connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater); } RestoreSessionJob::RestoreSessionJob(): KJob() {} void RestoreSessionJob::start() { OrgKdeKSMServerInterfaceInterface ksmserverIface(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus()); auto pending = ksmserverIface.restoreSession(); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [this]() {emitResult();}); connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater); } void StartupPhase2::runUserAutostart() { // Now let's execute the scripts in the KDE-specific autostart-scripts folder. const QString autostartFolder = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QDir::separator() + QStringLiteral("autostart-scripts"); QDir dir(autostartFolder); if (!dir.exists()) { // Create dir in all cases, so that users can find it :-) dir.mkpath(QStringLiteral(".")); if (!migrateKDE4Autostart(autostartFolder)) { return; } } const QStringList entries = dir.entryList(QDir::Files); for (const QString &file : entries) { // Don't execute backup files if (!file.endsWith(QLatin1Char('~')) && !file.endsWith(QLatin1String(".bak")) && (file[0] != QLatin1Char('%') || !file.endsWith(QLatin1Char('%'))) && (file[0] != QLatin1Char('#') || !file.endsWith(QLatin1Char('#')))) { const QString fullPath = dir.absolutePath() + QLatin1Char('/') + file; qCInfo(PLASMA_SESSION) << "Starting autostart script " << fullPath; auto p = new KProcess; //deleted in onFinished lambda p->setProgram(fullPath); p->start(); connect(p, static_cast(&QProcess::finished), [p](int exitCode) { qCInfo(PLASMA_SESSION) << "autostart script" << p->program() << "finished with exit code " << exitCode; p->deleteLater(); }); } } } bool StartupPhase2::migrateKDE4Autostart(const QString &autostartFolder) { // Migrate user autostart from kde4 Kdelibs4Migration migration; if (!migration.kdeHomeFound()) { return false; } // KDEHOME/Autostart was the default value for KGlobalSettings::autostart() QString oldAutostart = migration.kdeHome() + QStringLiteral("/Autostart"); // That path could be customized in kdeglobals const QString oldKdeGlobals = migration.locateLocal("config", QStringLiteral("kdeglobals")); if (!oldKdeGlobals.isEmpty()) { oldAutostart = KConfig(oldKdeGlobals).group("Paths").readEntry("Autostart", oldAutostart); } const QDir oldFolder(oldAutostart); qCDebug(PLASMA_SESSION) << "Copying autostart files from" << oldFolder.path(); const QStringList entries = oldFolder.entryList(QDir::Files); for (const QString &file : entries) { const QString src = oldFolder.absolutePath() + QLatin1Char('/') + file; const QString dest = autostartFolder + QLatin1Char('/') + file; QFileInfo info(src); bool success; if (info.isSymLink()) { // This will only work with absolute symlink targets success = QFile::link(info.symLinkTarget(), dest); } else { success = QFile::copy(src, dest); } if (!success) { qCWarning(PLASMA_SESSION) << "Error copying" << src << "to" << dest; } } return true; } AutoStartAppsJob::AutoStartAppsJob(const AutoStart & autostart, int phase) : m_autoStart(autostart) { m_autoStart.setPhase(phase); } void AutoStartAppsJob::start() { qCDebug(PLASMA_SESSION); QTimer::singleShot(0, this, [=]() { do { QString serviceName = m_autoStart.startService(); if (serviceName.isEmpty()) { // Done if (!m_autoStart.phaseDone()) { m_autoStart.setPhaseDone(); } emitResult(); return; } KService service(serviceName); auto arguments = KIO::DesktopExecParser(service, QList()).resultingArguments(); if (arguments.isEmpty()) { qCWarning(PLASMA_SESSION) << "failed to parse" << serviceName << "for autostart"; continue; } qCInfo(PLASMA_SESSION) << "Starting autostart service " << serviceName << arguments; auto program = arguments.takeFirst(); if (!QProcess::startDetached(program, arguments)) qCWarning(PLASMA_SESSION) << "could not start" << serviceName << ":" << program << arguments; } while (true); }); } StartServiceJob::StartServiceJob(const QString &process, const QStringList &args, const QString &serviceId, const QProcessEnvironment &additionalEnv) : KJob() , m_process(new QProcess(this)) , m_serviceId(serviceId) { m_process->setProgram(process); m_process->setArguments(args); auto env = QProcessEnvironment::systemEnvironment(); env.insert(additionalEnv); m_process->setProcessEnvironment(env); auto watcher = new QDBusServiceWatcher(serviceId, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForRegistration, this); connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, &StartServiceJob::emitResult); } void StartServiceJob::start() { if (QDBusConnection::sessionBus().interface()->isServiceRegistered(m_serviceId)) { qCDebug(PLASMA_SESSION) << m_process << "already running"; emitResult(); return; } qCDebug(PLASMA_SESSION) << "Starting " << m_process->program() << m_process->arguments(); if (!m_process->startDetached()) { qCWarning(PLASMA_SESSION) << "error starting process" << m_process->program() << m_process->arguments(); emitResult(); } } StartProcessJob::StartProcessJob(const QString &process, const QStringList &args, const QProcessEnvironment &additionalEnv) : KJob() , m_process(new QProcess(this)) { m_process->setProgram(process); m_process->setArguments(args); auto env = QProcessEnvironment::systemEnvironment(); env.insert(additionalEnv); m_process->setProcessEnvironment(env); connect(m_process, static_cast(&QProcess::finished), [this](int exitCode) { qCInfo(PLASMA_SESSION) << "process job " << m_process->program() << "finished with exit code " << exitCode; emitResult(); }); } void StartProcessJob::start() { qCDebug(PLASMA_SESSION) << "Starting " << m_process->program() << m_process->arguments(); m_process->start(); } #include "startup.moc" diff --git a/startkde/plasma-shutdown/CMakeLists.txt b/startkde/plasma-shutdown/CMakeLists.txt new file mode 100644 index 000000000..2393d7363 --- /dev/null +++ b/startkde/plasma-shutdown/CMakeLists.txt @@ -0,0 +1,23 @@ +set(plasma_shutdown_SRCS + main.cpp + shutdown.cpp +) + +ecm_qt_declare_logging_category(plasma_shutdown_SRCS HEADER debug.h IDENTIFIER PLASMA_SESSION CATEGORY_NAME org.kde.plasma.shutdown) + +qt5_add_dbus_adaptor(plasma_shutdown_SRCS org.kde.Shutdown.xml shutdown.h Shutdown) +qt5_add_dbus_interface(plasma_shutdown_SRCS org.kde.Shutdown.xml shutdown_interface) +qt5_add_dbus_interface( plasma_shutdown_SRCS ../../ksmserver/org.kde.KSMServerInterface.xml ksmserver_interface ) + +add_executable(plasma-shutdown ${plasma_shutdown_SRCS}) + +target_link_libraries(plasma-shutdown + Qt5::Core + Qt5::DBus + KF5::ConfigCore + PW::KWorkspace +) + +kdbusaddons_generate_dbus_service_file(plasma-shutdown org.kde.Shutdown ${KDE_INSTALL_FULL_BINDIR}) +install(TARGETS plasma-shutdown ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) + diff --git a/startkde/plasma-session/main.cpp b/startkde/plasma-shutdown/main.cpp similarity index 80% copy from startkde/plasma-session/main.cpp copy to startkde/plasma-shutdown/main.cpp index 9bea801e6..6b05a26df 100644 --- a/startkde/plasma-session/main.cpp +++ b/startkde/plasma-shutdown/main.cpp @@ -1,36 +1,28 @@ /* * Copyright © 2018 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. */ -#include "startup.h" #include "shutdown.h" #include int main(int argc, char **argv) { QCoreApplication app(argc, argv); - - // This is needed with KJobs in a QCoreApplication as when they finish - // the internal QEventLoopLocker expires and our app exits - app.setQuitLockEnabled(false); - - new Startup(&app); new Shutdown(&app); - app.exec(); } diff --git a/startkde/plasma-session/org.kde.Shutdown.xml b/startkde/plasma-shutdown/org.kde.Shutdown.xml similarity index 100% rename from startkde/plasma-session/org.kde.Shutdown.xml rename to startkde/plasma-shutdown/org.kde.Shutdown.xml diff --git a/startkde/plasma-session/shutdown.cpp b/startkde/plasma-shutdown/shutdown.cpp similarity index 96% rename from startkde/plasma-session/shutdown.cpp rename to startkde/plasma-shutdown/shutdown.cpp index 2092c185a..2ed40795d 100644 --- a/startkde/plasma-session/shutdown.cpp +++ b/startkde/plasma-shutdown/shutdown.cpp @@ -1,97 +1,99 @@ #include "shutdown.h" #include "shutdownadaptor.h" #include #include #include #include #include #include "sessionmanagementbackend.h" #include "ksmserver_interface.h" #include "debug.h" Shutdown::Shutdown(QObject *parent): QObject(parent) { new ShutdownAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/Shutdown"), QStringLiteral("org.kde.Shutdown"), this); //registered as a new service name for easy moving to new process QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.Shutdown")); } void Shutdown::logout() { startLogout(KWorkSpace::ShutdownTypeNone); } void Shutdown::logoutAndShutdown() { startLogout(KWorkSpace::ShutdownTypeHalt); } void Shutdown::logoutAndReboot() { startLogout(KWorkSpace::ShutdownTypeReboot); } void Shutdown::startLogout(KWorkSpace::ShutdownType shutdownType) { m_shutdownType = shutdownType; OrgKdeKSMServerInterfaceInterface ksmserverIface(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus()); auto closeSessionReply = ksmserverIface.closeSession(); auto watcher = new QDBusPendingCallWatcher(closeSessionReply, this); connect(watcher, &QDBusPendingCallWatcher::finished, this, [closeSessionReply, watcher, this]() { watcher->deleteLater(); if (closeSessionReply.isError()) { - qDebug() << "ksmserver failed to complete logout"; + qCWarning(PLASMA_SESSION) << "ksmserver failed to complete logout"; + qApp->quit(); } if (closeSessionReply.value()) { logoutComplete(); } else { logoutCancelled(); } }); } void Shutdown::logoutCancelled() { m_shutdownType = KWorkSpace::ShutdownTypeNone; + qApp->quit(); } void Shutdown::logoutComplete() { runShutdownScripts(); if (m_shutdownType == KWorkSpace::ShutdownTypeHalt) { SessionBackend::self()->shutdown(); } else if (m_shutdownType == KWorkSpace::ShutdownTypeReboot) { SessionBackend::self()->reboot(); } else { //logout qApp->quit(); } } void Shutdown::runShutdownScripts() { const QStringList shutdownFolders = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("plasma-workspace/shutdown"), QStandardPaths::LocateDirectory); for (const QString &shutDownFolder : shutdownFolders) { QDir dir(shutDownFolder); const QStringList entries = dir.entryList(QDir::Files); for (const QString &file : entries) { // Don't execute backup files if (!file.endsWith(QLatin1Char('~')) && !file.endsWith(QLatin1String(".bak")) && (file[0] != QLatin1Char('%') || !file.endsWith(QLatin1Char('%'))) && (file[0] != QLatin1Char('#') || !file.endsWith(QLatin1Char('#')))) { const QString fullPath = dir.absolutePath() + QLatin1Char('/') + file; qCDebug(PLASMA_SESSION) << "running shutdown script" << fullPath; QProcess::execute(fullPath, QStringList()); } } } } diff --git a/startkde/plasma-session/shutdown.h b/startkde/plasma-shutdown/shutdown.h similarity index 100% rename from startkde/plasma-session/shutdown.h rename to startkde/plasma-shutdown/shutdown.h diff --git a/startkde/startplasma-wayland.cpp b/startkde/startplasma-wayland.cpp index 457b9015c..03210ce77 100644 --- a/startkde/startplasma-wayland.cpp +++ b/startkde/startplasma-wayland.cpp @@ -1,101 +1,102 @@ /* This file is part of the KDE project Copyright (C) 2019 Aleix Pol Gonzalez 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 "startplasma.h" #include #include #include #include int main(int argc, char** argv) { + QCoreApplication app(argc, argv); createConfigDirectory(); setupCursor(true); { KConfig fonts(QStringLiteral("kcmfonts")); KConfigGroup group = fonts.group("General"); auto dpiSetting = group.readEntry("forceFontDPIWayland", 96); auto dpi = dpiSetting == 0 ? 96 : dpiSetting; qputenv("QT_WAYLAND_FORCE_DPI", QByteArray::number(dpi)); } // Query whether org.freedesktop.locale1 is available. If it is, try to // set XKB_DEFAULT_{MODEL,LAYOUT,VARIANT,OPTIONS} accordingly. { const QString locale1Service = QStringLiteral("org.freedesktop.locale1"); const QString locale1Path = QStringLiteral("/org/freedesktop/locale1"); QDBusMessage message = QDBusMessage::createMethodCall(locale1Service, locale1Path, QStringLiteral("org.freedesktop.DBus.Properties"), QLatin1String("GetAll")); message << locale1Service; QDBusMessage resultMessage = QDBusConnection::systemBus().call(message); if (resultMessage.type() == QDBusMessage::ReplyMessage) { QVariantMap result; QDBusArgument dbusArgument = resultMessage.arguments().at(0).value(); while (!dbusArgument.atEnd()) { dbusArgument >> result; } auto queryAndSet = [&](const QByteArray &var, const QString & value) { const auto r = result.value(value).toString(); if (!r.isEmpty()) qputenv(var, r.toUtf8()); }; queryAndSet("X11MODEL", QStringLiteral("X11Model")); queryAndSet("X11LAYOUT", QStringLiteral("X11Layout")); queryAndSet("X11VARIANT", QStringLiteral("X11Variant")); queryAndSet("X11OPTIONS", QStringLiteral("X11Options")); } else { qWarning() << "not a reply org.freedesktop.locale1" << resultMessage; } } runEnvironmentScripts(); if (!qEnvironmentVariableIsSet("DBUS_SESSION_BUS_ADDRESS")) { out << "startplasmacompositor: Could not start D-Bus. Can you call qdbus?\n"; return 1; } setupPlasmaEnvironment(); qputenv("XDG_SESSION_TYPE", "wayland"); if (!syncDBusEnvironment()) { out << "Could not sync environment to dbus.\n"; return 1; } QStringList args; if (argc > 1) { args.reserve(argc); for (int i = 1; i < argc; ++i) { args << QString::fromLocal8Bit(argv[i]); } } else { args = QStringList { QStringLiteral("--xwayland"), QStringLiteral("--libinput"), QStringLiteral("--exit-with-session=" CMAKE_INSTALL_FULL_LIBEXECDIR "/startplasma-waylandsession") }; } runSync(QStringLiteral(KWIN_WAYLAND_BIN_PATH), args); out << "startplasmacompositor: Shutting down...\n"; cleanupPlasmaEnvironment(); out << "startplasmacompositor: Done.\n"; return 0; } diff --git a/startkde/startplasma-x11.cpp b/startkde/startplasma-x11.cpp index e35f0e8c5..ae1c4d101 100644 --- a/startkde/startplasma-x11.cpp +++ b/startkde/startplasma-x11.cpp @@ -1,119 +1,121 @@ /* This file is part of the KDE project Copyright (C) 2019 Aleix Pol Gonzalez 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 "startplasma.h" #include #include #include #include void sighupHandler(int) { out << "GOT SIGHUP\n"; } -int main(int /*argc*/, char** /*argv*/) +int main(int argc, char** argv) { // When the X server dies we get a HUP signal from xinit. We must ignore it // because we still need to do some cleanup. signal(SIGHUP, sighupHandler); + QCoreApplication app(argc, argv); + // Boot sequence: // // kdeinit is used to fork off processes which improves memory usage // and startup time. // // * kdeinit starts klauncher first. // * Then kded is started. kded is responsible for keeping the sycoca // database up to date. When an up to date database is present it goes // into the background and the startup continues. // * Then kdeinit starts kcminit. kcminit performs initialisation of // certain devices according to the user's settings // // * Then ksmserver is started which takes control of the rest of the startup sequence // Check if a Plasma session already is running and whether it's possible to connect to X switch (kCheckRunning()) { case NoX11: out << "$DISPLAY is not set or cannot connect to the X server.\n"; return 1; case PlasmaRunning: messageBox(QStringLiteral("Plasma seems to be already running on this display.\n")); return 1; case NoPlasmaRunning: break; } createConfigDirectory(); runStartupConfig(); //Do not sync any of this section with the wayland versions as there scale factors are //sent properly over wl_output { KConfig cfg(QStringLiteral("kdeglobals")); KConfigGroup kscreenGroup = cfg.group("KScreen"); const auto screenScaleFactors = kscreenGroup.readEntry("ScreenScaleFactors", QByteArray()); if (!screenScaleFactors.isEmpty()) { qputenv("QT_SCREEN_SCALE_FACTORS", screenScaleFactors); qreal scaleFactor = qFloor(kscreenGroup.readEntry("ScaleFactor", 1.0)); if (scaleFactor > 1) { qputenv("GDK_SCALE", QByteArray::number(scaleFactor, 'g', 0)); qputenv("GDK_DPI_SCALE", QByteArray::number(1.0/scaleFactor, 'g', 3)); } } } setupCursor(false); setupFontDpi(); QScopedPointer ksplash(setupKSplash()); runEnvironmentScripts(); out << "startkde: Starting up...\n"; setupPlasmaEnvironment(); setupX11(); if (!syncDBusEnvironment()) { // Startup error messageBox(QStringLiteral("Could not sync environment to dbus.\n")); return 1; } if (!startPlasmaSession(false)) return 1; // Anything after here is logout // It is not called after shutdown/restart waitForKonqi(); out << "startkde: Shutting down...\n"; runSync(QStringLiteral("kdeinit5_shutdown"), {}); cleanupPlasmaEnvironment(); cleanupX11(); out << "startkde: Done.\n"; return 0; } diff --git a/startkde/startplasma.cpp b/startkde/startplasma.cpp index 14ad78a1a..1b2592593 100644 --- a/startkde/startplasma.cpp +++ b/startkde/startplasma.cpp @@ -1,394 +1,425 @@ /* This file is part of the KDE project Copyright (C) 2019 Aleix Pol Gonzalez 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 #include #include #include +#include + #include +#include + #include #include #include #include "startplasma.h" QTextStream out(stderr); void messageBox(const QString &text) { out << text; runSync(QStringLiteral("xmessage"), {QStringLiteral("-geometry"), QStringLiteral("500x100"), text}); } QStringList allServices(const QLatin1String& prefix) { QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); const QStringList services = bus->registeredServiceNames(); QMap servicesWithAliases; for (const QString &serviceName : services) { QDBusReply reply = bus->serviceOwner(serviceName); QString owner = reply; if (owner.isEmpty()) owner = serviceName; servicesWithAliases[owner].append(serviceName); } QStringList names; for (auto it = servicesWithAliases.constBegin(); it != servicesWithAliases.constEnd(); ++it) { if (it.value().startsWith(prefix)) names << it.value(); } names.removeDuplicates(); names.sort(); return names; } int runSync(const QString& program, const QStringList &args, const QStringList &env) { QProcess p; if (!env.isEmpty()) p.setEnvironment(QProcess::systemEnvironment() << env); p.setProcessChannelMode(QProcess::ForwardedChannels); p.start(program, args); // qDebug() << "started..." << program << args; p.waitForFinished(-1); if (p.exitCode()) { qWarning() << program << args << "exited with code" << p.exitCode(); } return p.exitCode(); } void sourceFiles(const QStringList &files) { QStringList filteredFiles; std::copy_if(files.begin(), files.end(), std::back_inserter(filteredFiles), [](const QString& i){ return QFileInfo(i).isReadable(); } ); if (filteredFiles.isEmpty()) return; filteredFiles.prepend(QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR "/plasma-sourceenv.sh")); QProcess p; p.start(QStringLiteral("/bin/sh"), filteredFiles); p.waitForFinished(-1); const auto fullEnv = p.readAllStandardOutput(); auto envs = fullEnv.split('\0'); for (auto &env: envs) { if (env.startsWith("_=") || env.startsWith("SHLVL")) continue; const int idx = env.indexOf('='); if (Q_UNLIKELY(idx <= 0)) continue; if (qgetenv(env.left(idx)) != env.mid(idx+1)) { // qDebug() << "setting..." << env.left(idx) << env.mid(idx+1) << "was" << qgetenv(env.left(idx)); qputenv(env.left(idx), env.mid(idx+1)); } } } void createConfigDirectory() { const QString configDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); if (!QDir().mkpath(configDir)) out << "Could not create config directory XDG_CONFIG_HOME: " << configDir << '\n'; } void runStartupConfig() { //export LC_* variables set by kcmshell5 formats into environment //so it can be picked up by QLocale and friends. KConfig config(QStringLiteral("plasma-localerc")); KConfigGroup formatsConfig = KConfigGroup(&config, "Formats"); const auto lcValues = { "LANG", "LC_NUMERIC", "LC_TIME", "LC_MONETARY", "LC_MEASUREMENT", "LC_COLLATE", "LC_CTYPE" }; for (auto lc : lcValues) { const QString value = formatsConfig.readEntry(lc, QString()); if (!value.isEmpty()) { qputenv(lc, value.toUtf8()); } } KConfigGroup languageConfig = KConfigGroup(&config, "Translations"); const QString value = languageConfig.readEntry("LANGUAGE", QString()); if (!value.isEmpty()) { qputenv("LANGUAGE", value.toUtf8()); } if (!formatsConfig.hasKey("LANG") && !qEnvironmentVariableIsEmpty("LANG")) { formatsConfig.writeEntry("LANG", qgetenv("LANG")); formatsConfig.sync(); } } void setupCursor(bool wayland) { const KConfig cfg(QStringLiteral("kcminputrc")); const KConfigGroup inputCfg = cfg.group("Mouse"); const auto kcminputrc_mouse_cursorsize = inputCfg.readEntry("cursorSize", QString()); const auto kcminputrc_mouse_cursortheme = inputCfg.readEntry("cursorTheme", QStringLiteral("breeze_cursors")); if (!kcminputrc_mouse_cursortheme.isEmpty() || !kcminputrc_mouse_cursorsize.isEmpty()) { #ifdef XCURSOR_PATH QByteArray path(XCURSOR_PATH); path.replace("$XCURSOR_PATH", qgetenv("XCURSOR_PATH")); qputenv("XCURSOR_PATH", path); #endif } //TODO: consider linking directly const int applyMouseStatus = wayland ? 0 : runSync(QStringLiteral("kapplymousetheme"), { kcminputrc_mouse_cursortheme, kcminputrc_mouse_cursorsize }); if (applyMouseStatus == 10) { qputenv("XCURSOR_THEME", "breeze_cursors"); } else if (!kcminputrc_mouse_cursortheme.isEmpty()) { qputenv("XCURSOR_THEME", kcminputrc_mouse_cursortheme.toUtf8()); } if (!kcminputrc_mouse_cursorsize.isEmpty()) { qputenv("XCURSOR_SIZE", kcminputrc_mouse_cursorsize.toUtf8()); } } // Source scripts found in /plasma-workspace/env/*.sh // (where correspond to the system and user's configuration // directory. // // This is where you can define environment variables that will be available to // all KDE programs, so this is where you can run agents using e.g. eval `ssh-agent` // or eval `gpg-agent --daemon`. // Note: if you do that, you should also put "ssh-agent -k" as a shutdown script // // (see end of this file). // For anything else (that doesn't set env vars, or that needs a window manager), // better use the Autostart folder. void runEnvironmentScripts() { QStringList scripts; const auto locations = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("plasma-workspace/env"), QStandardPaths::LocateDirectory); for (const QString & location : locations) { QDir dir(location); const auto dirScripts = dir.entryInfoList({QStringLiteral("*.sh")}); for (const auto script : dirScripts) { scripts << script.absoluteFilePath(); } } sourceFiles(scripts); } // Mark that full KDE session is running (e.g. Konqueror preloading works only // with full KDE running). The KDE_FULL_SESSION property can be detected by // any X client connected to the same X session, even if not launched // directly from the KDE session but e.g. using "ssh -X", kdesu. $KDE_FULL_SESSION // however guarantees that the application is launched in the same environment // like the KDE session and that e.g. KDE utilities/libraries are available. // KDE_FULL_SESSION property is also only available since KDE 3.5.5. // The matching tests are: // For $KDE_FULL_SESSION: // if test -n "$KDE_FULL_SESSION"; then ... whatever // For KDE_FULL_SESSION property (on X11): // xprop -root | grep "^KDE_FULL_SESSION" >/dev/null 2>/dev/null // if test $? -eq 0; then ... whatever // // Additionally there is $KDE_SESSION_UID with the uid // of the user running the KDE session. It should be rarely needed (e.g. // after sudo to prevent desktop-wide functionality in the new user's kded). // // Since KDE4 there is also KDE_SESSION_VERSION, containing the major version number. // void setupPlasmaEnvironment() { //Manually disable auto scaling because we are scaling above //otherwise apps that manually opt in for high DPI get auto scaled by the developer AND manually scaled by us qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); qputenv("KDE_FULL_SESSION", "true"); qputenv("KDE_SESSION_VERSION", "5"); qputenv("KDE_SESSION_UID", QByteArray::number(getuid())); qputenv("XDG_CURRENT_DESKTOP", "KDE"); } void setupX11() { // Set a left cursor instead of the standard X11 "X" cursor, since I've heard // from some users that they're confused and don't know what to do. This is // especially necessary on slow machines, where starting KDE takes one or two // minutes until anything appears on the screen. // // If the user has overwritten fonts, the cursor font may be different now // so don't move this up. runSync(QStringLiteral("xsetroot"), {QStringLiteral("-cursor_name"), QStringLiteral("left_ptr")}); runSync(QStringLiteral("xprop"), {QStringLiteral("-root"), QStringLiteral("-f"), QStringLiteral("KDE_FULL_SESSION"), QStringLiteral("8t"), QStringLiteral("-set"), QStringLiteral("KDE_FULL_SESSION"), QStringLiteral("true")}); runSync(QStringLiteral("xprop"), {QStringLiteral("-root"), QStringLiteral("-f"), QStringLiteral("KDE_SESSION_VERSION"), QStringLiteral("32c"), QStringLiteral("-set"), QStringLiteral("KDE_SESSION_VERSION"), QStringLiteral("5")}); } void cleanupX11() { runSync(QStringLiteral("xprop"), { QStringLiteral("-root"), QStringLiteral("-remove"), QStringLiteral("KDE_FULL_SESSION") }); runSync(QStringLiteral("xprop"), { QStringLiteral("-root"), QStringLiteral("-remove"), QStringLiteral("KDE_SESSION_VERSION") }); } // TODO: Check if Necessary void cleanupPlasmaEnvironment() { qunsetenv("KDE_FULL_SESSION"); qunsetenv("KDE_SESSION_VERSION"); qunsetenv("KDE_SESSION_UID"); } // kwin_wayland can possibly also start dbus-activated services which need env variables. // In that case, the update in startplasma might be too late. bool syncDBusEnvironment() { int exitCode; // At this point all environment variables are set, let's send it to the DBus session server to update the activation environment if (!QStandardPaths::findExecutable(QStringLiteral("dbus-update-activation-environment")).isEmpty()) { exitCode = runSync(QStringLiteral("dbus-update-activation-environment"), { QStringLiteral("--systemd"), QStringLiteral("--all") }); } else { exitCode = runSync(QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR "/ksyncdbusenv"), {}); } return exitCode == 0; } void setupFontDpi() { KConfig cfg(QStringLiteral("kcmfonts")); KConfigGroup fontsCfg(&cfg, "General"); if (!fontsCfg.hasKey("forceFontDPI")) { return; } //TODO port to c++? const QByteArray input = "Xft.dpi: " + QByteArray::number(fontsCfg.readEntry("forceFontDPI", 0)); QProcess p; p.start(QStringLiteral("xrdb"), { QStringLiteral("-quiet"), QStringLiteral("-merge"), QStringLiteral("-nocpp") }); p.setProcessChannelMode(QProcess::ForwardedChannels); p.write(input); p.closeWriteChannel(); p.waitForFinished(-1); } static bool desktopLockedAtStart = false; QProcess* setupKSplash() { const auto dlstr = qgetenv("DESKTOP_LOCKED"); desktopLockedAtStart = dlstr == "true" || dlstr == "1"; qunsetenv("DESKTOP_LOCKED"); // Don't want it in the environment QProcess* p = nullptr; if (!desktopLockedAtStart) { const KConfig cfg(QStringLiteral("ksplashrc")); // the splashscreen and progress indicator KConfigGroup ksplashCfg = cfg.group("KSplash"); if (ksplashCfg.readEntry("Engine", QStringLiteral("KSplashQML")) == QLatin1String("KSplashQML")) { p = new QProcess; p->start(QStringLiteral("ksplashqml"), { ksplashCfg.readEntry("Theme", QStringLiteral("Breeze")) }); } } return p; } void setupGSLib() // Get Ghostscript to look into user's KDE fonts dir for additional Fontmap { const QByteArray usr_fdir = QFile::encodeName(QDir::home().absoluteFilePath(QStringLiteral(".fonts"))); if (qEnvironmentVariableIsSet("GS_LIB")) { qputenv("GS_LIB", usr_fdir + ':' + qgetenv("GS_LIB")); } else { qputenv("GS_LIB", usr_fdir); } } bool startPlasmaSession(bool wayland) { OrgKdeKSplashInterface iface(QStringLiteral("org.kde.KSplash"), QStringLiteral("/KSplash"), QDBusConnection::sessionBus()); iface.setStage(QStringLiteral("kinit")); // finally, give the session control to the session manager // see kdebase/ksmserver for the description of the rest of the startup sequence // if the KDEWM environment variable has been set, then it will be used as KDE's // window manager instead of kwin. // if KDEWM is not set, ksmserver will ensure kwin is started. // kwrapper5 is used to reduce startup time and memory usage // kwrapper5 does not return useful error codes such as the exit code of ksmserver. // We only check for 255 which means that the ksmserver process could not be // started, any problems thereafter, e.g. ksmserver failing to initialize, // will remain undetected. // If the session should be locked from the start (locked autologin), // lock now and do the rest of the KDE startup underneath the locker. QStringList plasmaSessionOptions; if (wayland) { plasmaSessionOptions << QStringLiteral("--no-lockscreen"); } else { if (qEnvironmentVariableIsSet("KDEWM")) { plasmaSessionOptions << QStringLiteral("--windowmanager") << qEnvironmentVariable("KDEWM"); } if (desktopLockedAtStart) { plasmaSessionOptions << QStringLiteral("--lockscreen"); } } - const auto exitCode = runSync(QStringLiteral(CMAKE_INSTALL_FULL_BINDIR "/plasma_session"), plasmaSessionOptions); + bool rc = true; + QEventLoop e; + + QProcess startPlasmaSession; + 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 + messageBox(QStringLiteral("startkde: Could not start ksmserver. Check your installation.\n")); + rc = false; + e.quit(); + } + }); + + 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(); + } + }); - if (exitCode == 255) { - // Startup error - messageBox(QStringLiteral("startkde: Could not start ksmserver. Check your installation.\n")); - return false; - } - return true; + startPlasmaSession.start(QStringLiteral(CMAKE_INSTALL_FULL_BINDIR "/plasma_session"), plasmaSessionOptions); + e.exec(); + return rc; } void waitForKonqi() { const KConfig cfg(QStringLiteral("startkderc")); const KConfigGroup grp = cfg.group("WaitForDrKonqi"); bool wait_drkonqi = grp.readEntry("Enabled", true); if (wait_drkonqi) { // wait for remaining drkonqi instances with timeout (in seconds) const int wait_drkonqi_timeout = grp.readEntry("Timeout", 900) * 1000; QElapsedTimer wait_drkonqi_counter; wait_drkonqi_counter.start(); QStringList services = allServices(QLatin1String("org.kde.drkonqi-")); while (!services.isEmpty()) { sleep(5); services = allServices(QLatin1String("org.kde.drkonqi-")); if (wait_drkonqi_counter.elapsed() >= wait_drkonqi_timeout) { // ask remaining drkonqis to die in a graceful way for (const auto &service: services) { QDBusInterface iface(service, QStringLiteral("/MainApplication")); iface.call(QStringLiteral("quit")); } break; } } } }