diff --git a/CMakeLists.txt b/CMakeLists.txt index 4648e71c0..987019848 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,191 +1,193 @@ cmake_minimum_required(VERSION 3.0) project(plasma-workspace) set(PROJECT_VERSION "5.14.80") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.11.0") set(KF5_MIN_VERSION "5.50.0") set(INSTALL_SDDM_THEME TRUE) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Widgets Quick QuickWidgets Concurrent Test Network) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMMarkNonGuiExecutable) include(CMakePackageConfigHelpers) include(WriteBasicConfigVersionFile) include(CheckIncludeFiles) include(FeatureSummary) include(ECMOptionalAddSubdirectory) include(ECMQtDeclareLoggingCategory) include(KDEPackageAppTemplates) find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Plasma DocTools Runner JsEmbed NotifyConfig Su NewStuff Wallet IdleTime Declarative TextWidgets KDELibs4Support Crash GlobalAccel DBusAddons Wayland CoreAddons) +find_package(KDED CONFIG REQUIRED) + find_package(KF5NetworkManagerQt ${KF5_MIN_VERSION}) set_package_properties(KF5NetworkManagerQt PROPERTIES DESCRIPTION "Qt wrapper for NetworkManager API" TYPE OPTIONAL PURPOSE "Needed by geolocation data engine." ) # WARNING PlasmaQuick provides unversioned CMake config find_package(KF5 REQUIRED COMPONENTS PlasmaQuick) find_package(KF5 REQUIRED COMPONENTS SysGuard) find_package(KF5 REQUIRED COMPONENTS Package) find_package(KF5Baloo) set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "File Searching" TYPE RECOMMENDED PURPOSE "Needed for the File Search runner." ) find_package(KF5TextEditor) find_package(KWinDBusInterface CONFIG REQUIRED) find_package(KScreenLocker 5.13.80 REQUIRED) find_package(ScreenSaverDBusInterface CONFIG REQUIRED) find_package(KF5Holidays) set_package_properties(KF5Holidays PROPERTIES DESCRIPTION "Holidays provider for Plasma calendar" TYPE OPTIONAL PURPOSE "Needed to for holidays plugin for Plasma Calendar." ) find_package(Phonon4Qt5 4.6.60 REQUIRED NO_MODULE) set_package_properties(Phonon4Qt5 PROPERTIES DESCRIPTION "Qt-based audio library" TYPE REQUIRED) find_package(KF5Activities ${KF5_MIN_VERSION}) set_package_properties(KF5Activities PROPERTIES DESCRIPTION "management of Plasma activities" TYPE OPTIONAL PURPOSE "Needed by activity related plasmoids." ) find_package(ZLIB) set_package_properties(ZLIB PROPERTIES DESCRIPTION "Support for gzip compressed files and data streams" URL "http://www.zlib.net" TYPE REQUIRED ) find_package(X11) set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "http://www.x.org" TYPE OPTIONAL PURPOSE "Required for building the X11 based workspace") if(X11_FOUND) find_package(XCB MODULE REQUIRED COMPONENTS XCB RANDR) set_package_properties(XCB PROPERTIES TYPE REQUIRED) if(NOT X11_SM_FOUND) message(FATAL_ERROR "\nThe X11 Session Management (SM) development package could not be found.\nPlease install libSM.\n") endif(NOT X11_SM_FOUND) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS X11Extras) endif() if(X11_FOUND AND XCB_XCB_FOUND) set(HAVE_X11 1) endif() find_package(AppStreamQt 0.10.6) set_package_properties(AppStreamQt PROPERTIES DESCRIPTION "Access metadata for listing available software" URL "https://www.freedesktop.org/wiki/Distributions/AppStream/" TYPE OPTIONAL ) include(ConfigureChecks.cmake) include_directories("${CMAKE_CURRENT_BINARY_DIR}") configure_file(config-workspace.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-workspace.h) configure_file(config-unix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-unix.h ) configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h) configure_file(plasma.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/plasma.desktop) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/plasma.desktop DESTINATION ${KDE_INSTALL_DATADIR}/xsessions ) configure_file(plasmawayland.desktop.cmake ${CMAKE_CURRENT_BINARY_DIR}/plasmawayland.desktop) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/plasmawayland.desktop DESTINATION ${KDE_INSTALL_DATADIR}/wayland-sessions ) plasma_install_package(lookandfeel org.kde.breeze.desktop look-and-feel lookandfeel) if (INSTALL_SDDM_THEME) configure_file(sddm-theme/theme.conf.cmake ${CMAKE_CURRENT_BINARY_DIR}/sddm-theme/theme.conf) # Install the login theme into the SDDM directory # Longer term we need to look at making SDDM load from look and feel somehow.. and allow copying at runtime #NOTE this trailing slash is important to rename the directory install(DIRECTORY sddm-theme/ DESTINATION ${KDE_INSTALL_FULL_DATADIR}/sddm/themes/breeze PATTERN "README.txt" EXCLUDE PATTERN "components" EXCLUDE PATTERN "dummydata" EXCLUDE PATTERN "theme.conf.cmake" EXCLUDE) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/sddm-theme/theme.conf DESTINATION ${KDE_INSTALL_FULL_DATADIR}/sddm/themes/breeze) install(DIRECTORY lookandfeel/contents/components DESTINATION ${KDE_INSTALL_FULL_DATADIR}/sddm/themes/breeze PATTERN "README.txt" EXCLUDE) endif() add_definitions(-DQT_NO_URL_CAST_FROM_STRING) add_subdirectory(doc) add_subdirectory(libkworkspace) add_subdirectory(libdbusmenuqt) add_subdirectory(appmenu) add_subdirectory(libtaskmanager) add_subdirectory(libcolorcorrect) add_subdirectory(components) add_subdirectory(plasma-windowed) add_subdirectory(shell) add_subdirectory(freespacenotifier) add_subdirectory(klipper) add_subdirectory(krunner) add_subdirectory(ksmserver) add_subdirectory(logout-greeter) add_subdirectory(ksplash) add_subdirectory(systemmonitor) add_subdirectory(statusnotifierwatcher) add_subdirectory(startkde) add_subdirectory(themes) add_subdirectory(containmentactions) add_subdirectory(runners) add_subdirectory(applets) add_subdirectory(dataengines) add_subdirectory(wallpapers) add_subdirectory(kioslave) add_subdirectory(ktimezoned) add_subdirectory(kuiserver) add_subdirectory(menu) add_subdirectory(phonon) # This ensures pressing the eject button on a CD drive ejects the disc # It listens to the Solid::OpticalDrive::ejectPressed signal that is only # supported by Solid in the HAL backend and does nothing with UDev if(CMAKE_SYSTEM_NAME MATCHES FreeBSD) add_subdirectory(solidautoeject) endif() ecm_optional_add_subdirectory(xembed-sni-proxy) ecm_optional_add_subdirectory(gmenu-dbusmenu-proxy) add_subdirectory(soliduiserver) if(KF5Holidays_FOUND) add_subdirectory(plasmacalendarintegration) endif() add_subdirectory(templates) install( FILES plasma-workspace.categories DESTINATION ${KDE_INSTALL_CONFDIR} ) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/ksmserver/CMakeLists.txt b/ksmserver/CMakeLists.txt index ffe74f7a1..688e01e48 100644 --- a/ksmserver/CMakeLists.txt +++ b/ksmserver/CMakeLists.txt @@ -1,93 +1,94 @@ add_definitions(-DTRANSLATION_DOMAIN=\"ksmserver\") include_directories(${PHONON_INCLUDE_DIR}) 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}) if(BUILD_TESTING) add_subdirectory(tests) endif() ########### next target ############### set(ksmserver_KDEINIT_SRCS main.cpp server.cpp legacy.cpp startup.cpp autostart.cpp logout.cpp shutdown.cpp client.cpp ) ecm_qt_declare_logging_category(ksmserver_KDEINIT_SRCS HEADER ksmserver_debug.h IDENTIFIER KSMSERVER CATEGORY_NAME org.kde.kf5.ksmserver) 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( ksmserver_KDEINIT_SRCS ${kcminit_xml} kcminit_interface ) +qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS ${KDED_DBUS_INTERFACE} kded_interface ) # FIXME: This is actually not disabled any more because OrgKDEKlauncherInterface isn't provided # otherwise. # # This is actually now disabled, because OrgKDEKlauncherInterface is also provided # # by kdecore, it is not autogenerated and is not binary compatible with a currently # # generated version, thus at certain circumstances leading to strange crashes. # # This should be fixed for KDE5. # # KLauchner.xml is installed by kdelibs, so it is in KDE4_DBUS_INTERFACES_DIR 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 ${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.Shutdown.xml shutdown_interface) qt5_add_dbus_adaptor( ksmserver_KDEINIT_SRCS org.kde.KSMServerInterface.xml server.h KSMServer ) qt5_add_dbus_adaptor( ksmserver_KDEINIT_SRCS org.kde.Shutdown.xml shutdown.h Shutdown) 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::KIOCore KF5::KIOWidgets ${X11_LIBRARIES} ${X11_Xrender_LIB} Qt5::X11Extras KF5::Solid Qt5::Quick KF5::Declarative KF5::DBusAddons KF5::Package KF5::KDELibs4Support # Solid/PowerManagement ${PHONON_LIBRARIES} 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/ksmserver/startup.cpp b/ksmserver/startup.cpp index 49080df1c..37013486d 100644 --- a/ksmserver/startup.cpp +++ b/ksmserver/startup.cpp @@ -1,391 +1,392 @@ /***************************************************************** ksmserver - the KDE session management server 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 "server.h" #include #include // HAVE_LIMITS_H #include #include #include "kcminit_interface.h" +#include "kded_interface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class Phase: public KCompositeJob { Q_OBJECT public: Phase(QObject *parent): KCompositeJob(parent) {} 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(); } } }; class StartupPhase0: public Phase { Q_OBJECT public: StartupPhase0(QObject *parent) : Phase(parent) {} void start() override { qCDebug(KSMSERVER) << "Phase 0"; addSubjob(new AutoStartAppsJob(0)); addSubjob(new KCMInitJob(1)); } }; class StartupPhase1: public Phase { Q_OBJECT public: StartupPhase1(QObject *parent) : Phase(parent) {} void start() override { qCDebug(KSMSERVER) << "Phase 1"; addSubjob(new AutoStartAppsJob(1)); } }; class StartupPhase2: public Phase { Q_OBJECT public: StartupPhase2(QObject *parent) : Phase(parent) {} void runUserAutostart(); bool migrateKDE4Autostart(const QString &folder); void start() override { qCDebug(KSMSERVER) << "Phase 2"; addSubjob(new AutoStartAppsJob(2)); addSubjob(new KDEDInitJob()); addSubjob(new KCMInitJob(2)); runUserAutostart(); } }; // 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(QStringLiteral("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(KSMSERVER) << "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(KSMSERVER) << "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(KSMServer *parent): QObject(parent), ksmserver(parent) { auto phase0 = new StartupPhase0(this); auto phase1 = new StartupPhase1(this); auto phase2 = new StartupPhase2(this); auto restoreSession = new RestoreSessionJob(ksmserver); connect(ksmserver, &KSMServer::windowManagerLoaded, phase0, &KJob::start); connect(phase0, &KJob::finished, phase1, &KJob::start); connect(phase1, &KJob::finished, this, [=]() { ksmserver->setupShortcuts(); // done only here, because it needs kglobalaccel :-/ }); connect(phase1, &KJob::finished, restoreSession, &KJob::start); connect(restoreSession, &KJob::finished, phase2, &KJob::start); connect(phase1, &KJob::finished, this, []() { NotificationThread *loginSound = new NotificationThread(); connect(loginSound, &NotificationThread::finished, loginSound, &NotificationThread::deleteLater); loginSound->start();}); connect(phase2, &KJob::finished, this, &Startup::finishStartup); } 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(KSMSERVER) << "Finished"; ksmserver->state = KSMServer::Idle; ksmserver->setupXIOErrorHandler(); upAndRunning(QStringLiteral("ready")); } KCMInitJob::KCMInitJob(int phase) :m_phase(phase) { } void KCMInitJob::start() { org::kde::KCMInit kcminit(QStringLiteral("org.kde.kcminit"), QStringLiteral("/kcminit"), QDBusConnection::sessionBus()); kcminit.setTimeout(10 * 1000); QDBusPendingReply pending; if (m_phase == 1) { pending = kcminit.runPhase1(); } else { pending = kcminit.runPhase2(); } 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(KSMSERVER()); - QDBusInterface kded( QStringLiteral( "org.kde.kded5" ), - QStringLiteral( "/kded" ), - QStringLiteral( "org.kde.kded5" ) ); - auto pending = kded.asyncCall( QStringLiteral( "loadSecondPhase" ) ); + 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(KSMServer *server): KJob(), m_ksmserver(server) {} void RestoreSessionJob::start() { if (m_ksmserver->defaultSession()) { QTimer::singleShot(0, this, [this]() {emitResult();}); return; } m_ksmserver->lastAppStarted = 0; m_ksmserver->lastIdStarted.clear(); m_ksmserver->state = KSMServer::Restoring; connect(m_ksmserver, &KSMServer::sessionRestored, this, [this]() {emitResult();}); m_ksmserver->tryRestoreNext(); } 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); foreach (const QString &file, entries) { // Don't execute backup files if (!file.endsWith(QLatin1Char('~')) && !file.endsWith(QStringLiteral(".bak")) && (file[0] != QLatin1Char('%') || !file.endsWith(QLatin1Char('%'))) && (file[0] != QLatin1Char('#') || !file.endsWith(QLatin1Char('#')))) { const QString fullPath = dir.absolutePath() + QLatin1Char('/') + file; qCInfo(KSMSERVER) << "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(KSMSERVER) << "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(KSMSERVER) << "Copying autostart files from" << oldFolder.path(); const QStringList entries = oldFolder.entryList(QDir::Files); foreach (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(KSMSERVER) << "Error copying" << src << "to" << dest; } } return true; } AutoStartAppsJob::AutoStartAppsJob(int phase) { m_autoStart.loadAutoStartList(); //FIXME, share this between jobs m_autoStart.setPhase(phase); } void AutoStartAppsJob::start() { qCDebug(KSMSERVER()); 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(KSMSERVER) << "failed to parse" << serviceName << "for autostart"; continue; } qCInfo(KSMSERVER) << "Starting autostart service " << serviceName << arguments; auto program = arguments.takeFirst(); if (!QProcess::startDetached(program, arguments)) qCWarning(KSMSERVER) << "could not start" << serviceName << ":" << program << arguments; } while (true); }); } #include "startup.moc"