[plasma/plasma-workspace] /: [startplasma] Migrate autostart scripts to .desktop files

Henri Chain null at kde.org
Wed Apr 28 17:02:34 BST 2021


Git commit 93f9ef0304f300dc3952ac9a25cbd82996eec263 by Henri Chain.
Committed on 28/04/2021 at 16:02.
Pushed by hchain into branch 'master'.

[startplasma] Migrate autostart scripts to .desktop files

M  +7    -5    doc/kcontrol/autostart/index.docbook
M  +1    -1    kcms/autostart/CMakeLists.txt
M  +59   -72   kcms/autostart/autostartmodel.cpp
M  +7    -2    kcms/autostart/autostartmodel.h
M  +5    -2    kcms/autostart/package/contents/ui/main.qml
M  +2    -0    libkworkspace/CMakeLists.txt
A  +34   -0    libkworkspace/autostartscriptdesktopfile.cpp     [License: LGPL(v2.1+)]
A  +32   -0    libkworkspace/autostartscriptdesktopfile.h     [License: LGPL(v2.1+)]
M  +10   -38   startkde/plasma-session/startup.cpp
M  +48   -0    startkde/startplasma.cpp
M  +1    -0    startkde/startplasma.h

https://invent.kde.org/plasma/plasma-workspace/commit/93f9ef0304f300dc3952ac9a25cbd82996eec263

diff --git a/doc/kcontrol/autostart/index.docbook b/doc/kcontrol/autostart/index.docbook
index e5e7ed442..240f79ed0 100644
--- a/doc/kcontrol/autostart/index.docbook
+++ b/doc/kcontrol/autostart/index.docbook
@@ -31,8 +31,10 @@
 
 <note><para>Please note that in this module all changes are immediately applied.</para></note>
 
-<para>The program scans <filename>$HOME/.config/autostart/</filename>,
-<filename class="directory">$HOME/.config/plasma-workspace/env</filename> and <filename class="directory">$HOME/.config/plasma-workspace/shutdown</filename> folders to check what programs and scripts are already there and displays them. It allows you to manage them easily.
+<para>The program scans <filename>$HOME/.config/autostart/</filename> for applications and login scripts, <filename class="directory">$HOME/.config/plasma-workspace/env</filename> for pre-startup scripts and <filename class="directory">$HOME/.config/plasma-workspace/shutdown</filename> for logout scripts to check what programs and scripts are already there and displays them. It allows you to manage them easily.
+</para>
+
+<para>Login scripts are <filename class="extension">.desktop</filename> files with a <literal>X-KDE-AutostartScript=true</literal> key. Pre-startup scripts are run earlier and can be used to set environment variables.
 </para>
 
 <note><para>Note that you can change the location of your <filename class="directory">Autostart</filename>
@@ -87,10 +89,10 @@ This column shows the name of the program or script you want to start with &plas
 <varlistentry><term><guilabel>Properties</guilabel></term>
 <listitem>
 <para>
-This button is only shown when you hover the item with the mouse pointer. The button (only enabled for programs &ie; <filename class="extension">.desktop</filename> files) allows you to change the properties of the program or script. You have general properties, permissions properties, a preview when applicable, and properties related to the application for programs. The default command is extracted from the <filename class="extension">.desktop</filename> file from the <literal>Exec</literal> key.
+This button is only shown when you hover the item with the mouse pointer. The button (only enabled for programs and login scripts &ie; <filename class="extension">.desktop</filename> files) allows you to change the properties of the program or script. You have general properties, permissions properties, a preview when applicable, and properties related to the application or login script. The default command is extracted from the <filename class="extension">.desktop</filename> file from the <literal>Exec</literal> key.
 </para>
 <para>
-For a script, the command is the path to the script and can not be modified.
+For a logout script, the command is the path to the script and can not be modified.
 </para>
 </listitem>
 </varlistentry>
@@ -126,7 +128,7 @@ This will copy the program <filename class="extension">.desktop</filename> file
 <varlistentry><term><guimenuitem>Add Login Script...</guimenuitem></term>
 <listitem>
 <para>
-This item opens a dialog that asks you for the location of the script you want to add. Scripts set to run on login are copied or symlinked in <filename class="directory">$HOME/.config/autostart</filename> and will be run during Plasma startup.
+This item opens a dialog that asks you for the location of the script you want to add. Scripts set to run on login will have a corresponding <filename class="extension">.desktop</filename> file created in your <filename class="directory">Autostart</filename> folder and will be run during Plasma startup.
 </para>
 </listitem>
 </varlistentry>
diff --git a/kcms/autostart/CMakeLists.txt b/kcms/autostart/CMakeLists.txt
index 93644f68c..f36f4d742 100644
--- a/kcms/autostart/CMakeLists.txt
+++ b/kcms/autostart/CMakeLists.txt
@@ -7,7 +7,7 @@ set(kcm_autostart_PART_SRCS
 
 add_library(kcm_autostart MODULE ${kcm_autostart_PART_SRCS})
 
-target_link_libraries(kcm_autostart KF5::I18n KF5::KIOCore KF5::KIOWidgets KF5::QuickAddons)
+target_link_libraries(kcm_autostart KF5::I18n KF5::KIOCore KF5::KIOWidgets KF5::QuickAddons PW::KWorkspace)
 
 kcoreaddons_desktop_to_json(kcm_autostart "package/metadata.desktop")
 
diff --git a/kcms/autostart/autostartmodel.cpp b/kcms/autostart/autostartmodel.cpp
index 94d800c84..777e2b3f0 100644
--- a/kcms/autostart/autostartmodel.cpp
+++ b/kcms/autostart/autostartmodel.cpp
@@ -34,16 +34,15 @@
 #include <KLocalizedString>
 #include <KOpenWithDialog>
 #include <KPropertiesDialog>
-
-#include <optional>
+#include <autostartscriptdesktopfile.h>
 
 // FDO user autostart directories are
-// .config/autostart which has .desktop files executed by klaunch
+// .config/autostart which has .desktop files executed by klaunch or systemd, some of which might be scripts
 
 // Then we have Plasma-specific locations which run scripts
-// .config/autostart-scripts which has scripts executed by ksmserver
-// .config/plasma-workspace/shutdown which has scripts executed by startkde
-// .config/plasma-workspace/env which has scripts executed by startkde
+// .config/autostart-scripts which has scripts executed by plasma_session (now migrated to .desktop files)
+// .config/plasma-workspace/shutdown which has scripts executed by plasma-shutdown
+// .config/plasma-workspace/env which has scripts executed by startplasma
 
 // in the case of pre-startup they have to end in .sh
 // everywhere else it doesn't matter
@@ -52,7 +51,7 @@
 
 // share/autostart shouldn't be an option as this should be reserved for global autostart entries
 
-static std::optional<AutostartEntry> loadDesktopEntry(const QString &fileName)
+std::optional<AutostartEntry> AutostartModel::loadDesktopEntry(const QString &fileName)
 {
     KDesktopFile config(fileName);
     const KConfigGroup grp = config.desktopGroup();
@@ -75,7 +74,7 @@ static std::optional<AutostartEntry> loadDesktopEntry(const QString &fileName)
     const auto lstEntry = grp.readXdgListEntry("OnlyShowIn");
     const bool onlyInPlasma = lstEntry.contains(QLatin1String("KDE"));
     const QString iconName = config.readIcon();
-
+    const auto kind = AutostartScriptDesktopFile::isAutostartScript(config) ? XdgScripts : XdgAutoStart; // .config/autostart load desktop at startup
     const QString tryCommand = grp.readEntry("TryExec");
 
     // Try to filter out entries that point to nonexistant programs
@@ -85,38 +84,28 @@ static std::optional<AutostartEntry> loadDesktopEntry(const QString &fileName)
         return {};
     }
 
-    return std::optional<AutostartEntry>({name,
-                                          AutostartModel::AutostartEntrySource::XdgAutoStart, // .config/autostart load desktop at startup
-                                          enabled,
-                                          fileName,
-                                          onlyInPlasma,
-                                          iconName});
+    return AutostartEntry{name, kind, enabled, fileName, onlyInPlasma, iconName};
 }
 
 AutostartModel::AutostartModel(QObject *parent)
     : QAbstractListModel(parent)
+    , m_xdgConfigPath(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation))
+    , m_xdgAutoStartPath(m_xdgConfigPath.filePath(QStringLiteral("autostart")))
 {
 }
 
-QString AutostartModel::XdgAutoStartPath() const
-{
-    return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/autostart/");
-}
-
 void AutostartModel::load()
 {
     beginResetModel();
 
     m_entries.clear();
 
-    QDir autostartdir(XdgAutoStartPath());
-    if (!autostartdir.exists()) {
-        autostartdir.mkpath(XdgAutoStartPath());
-    }
-
-    autostartdir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
+    // Creates if doesn't already exist
+    m_xdgAutoStartPath.mkpath(QStringLiteral("."));
 
-    const auto filesInfo = autostartdir.entryInfoList();
+    // Needed to add all script entries after application entries
+    QVector<AutostartEntry> scriptEntries;
+    const auto filesInfo = m_xdgAutoStartPath.entryInfoList(QDir::Files);
     for (const QFileInfo &fi : filesInfo) {
         if (!KDesktopFile::isDesktopFile(fi.fileName())) {
             continue;
@@ -128,12 +117,16 @@ void AutostartModel::load()
             continue;
         }
 
-        m_entries.push_back(entry.value());
+        if (entry->source == XdgScripts) {
+            scriptEntries.push_back(entry.value());
+        } else {
+            m_entries.push_back(entry.value());
+        }
     }
 
-    loadScriptsFromDir(QStringLiteral("/autostart-scripts/"), AutostartModel::AutostartEntrySource::XdgScripts);
-    // Treat them as XdgScripts so they appear together in the UI
-    loadScriptsFromDir(QStringLiteral("/plasma-workspace/env/"), AutostartModel::AutostartEntrySource::XdgScripts);
+    m_entries.append(scriptEntries);
+
+    loadScriptsFromDir(QStringLiteral("/plasma-workspace/env/"), AutostartModel::AutostartEntrySource::PlasmaEnvScripts);
 
     loadScriptsFromDir(QStringLiteral("/plasma-workspace/shutdown/"), AutostartModel::AutostartEntrySource::PlasmaShutdown);
 
@@ -142,15 +135,11 @@ void AutostartModel::load()
 
 void AutostartModel::loadScriptsFromDir(const QString &subDir, AutostartModel::AutostartEntrySource kind)
 {
-    const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + subDir;
-    QDir dir(path);
-    if (!dir.exists()) {
-        dir.mkpath(path);
-    }
+    QDir dir(m_xdgConfigPath.filePath(subDir));
+    // Creates if doesn't already exist
+    dir.mkpath(QStringLiteral("."));
 
-    dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
-
-    const auto autostartDirFilesInfo = dir.entryInfoList();
+    const auto autostartDirFilesInfo = dir.entryInfoList(QDir::Files);
     for (const QFileInfo &fi : autostartDirFilesInfo) {
         QString fileName = fi.absoluteFilePath();
         const bool isSymlink = fi.isSymLink();
@@ -223,7 +212,7 @@ void AutostartModel::addApplication(const KService::Ptr &service)
     // https://bugs.launchpad.net/ubuntu/+source/kde-workspace/+bug/923360
     if (service->desktopEntryName().isEmpty() || service->entryPath().isEmpty()) {
         // create a new desktop file in s_desktopPath
-        desktopPath = XdgAutoStartPath() + service->name() + QStringLiteral(".desktop");
+        desktopPath = m_xdgAutoStartPath.filePath(service->name() + QStringLiteral(".desktop"));
 
         KDesktopFile desktopFile(desktopPath);
         KConfigGroup kcg = desktopFile.desktopGroup();
@@ -236,7 +225,7 @@ void AutostartModel::addApplication(const KService::Ptr &service)
         desktopFile.sync();
 
     } else {
-        desktopPath = XdgAutoStartPath() + service->desktopEntryName() + QStringLiteral(".desktop");
+        desktopPath = m_xdgAutoStartPath.filePath(service->desktopEntryName() + QStringLiteral(".desktop"));
 
         QFile::remove(desktopPath);
 
@@ -320,8 +309,6 @@ void AutostartModel::addScript(const QUrl &url, AutostartModel::AutostartEntrySo
     }
 
     const QString fileName = url.fileName();
-    int index = 0;
-    QString folder;
 
     if (kind == AutostartModel::AutostartEntrySource::XdgScripts) {
         int lastLoginScript = -1;
@@ -332,44 +319,44 @@ void AutostartModel::addScript(const QUrl &url, AutostartModel::AutostartEntrySo
             ++lastLoginScript;
         }
 
-        index = lastLoginScript + 1;
-        folder = QStringLiteral("/autostart-scripts/");
+        AutostartScriptDesktopFile desktopFile(fileName, file.filePath());
+        insertScriptEntry(lastLoginScript + 1, fileName, desktopFile.fileName(), kind);
     } else if (kind == AutostartModel::AutostartEntrySource::PlasmaShutdown) {
-        index = m_entries.size();
-        folder = QStringLiteral("/plasma-workspace/shutdown/");
+        const QUrl destinationScript = QUrl::fromLocalFile(QDir(m_xdgConfigPath.filePath(QStringLiteral("/plasma-workspace/shutdown/"))).filePath(fileName));
+        KIO::CopyJob *job = KIO::link(url, destinationScript, KIO::HideProgressInfo);
+        job->setAutoRename(true);
+        job->setProperty("finalUrl", destinationScript);
+
+        connect(job, &KIO::CopyJob::renamed, this, [](KIO::Job *job, const QUrl &from, const QUrl &to) {
+            Q_UNUSED(from)
+            // in case the destination filename had to be renamed
+            job->setProperty("finalUrl", to);
+        });
+
+        connect(job, &KJob::finished, this, [this, url, kind](KJob *theJob) {
+            if (theJob->error()) {
+                qWarning() << "Could not add script entry" << theJob->errorString();
+                return;
+            }
+            const QUrl dest = theJob->property("finalUrl").toUrl();
+            insertScriptEntry(m_entries.size(), dest.fileName(), dest.path(), kind);
+        });
+
+        job->start();
     } else {
         Q_ASSERT(0);
     }
+}
 
-    QUrl destinationScript = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + folder + fileName);
-    KIO::CopyJob *job = KIO::link(url, destinationScript, KIO::HideProgressInfo);
-    job->setAutoRename(true);
-    job->setProperty("finalUrl", destinationScript);
-
-    connect(job, &KIO::CopyJob::renamed, this, [](KIO::Job *job, const QUrl &from, const QUrl &to) {
-        Q_UNUSED(from)
-        // in case the destination filename had to be renamed
-        job->setProperty("finalUrl", to);
-    });
-
-    connect(job, &KJob::finished, this, [this, index, url, kind](KJob *theJob) {
-        if (theJob->error()) {
-            qWarning() << "Could add script entry" << theJob->errorString();
-            return;
-        }
-
-        beginInsertRows(QModelIndex(), index, index);
-
-        const QUrl dest = theJob->property("finalUrl").toUrl();
-
-        AutostartEntry entry = AutostartEntry{dest.fileName(), kind, true, dest.path(), false, QStringLiteral("dialog-scripts")};
+void AutostartModel::insertScriptEntry(int index, const QString &name, const QString &path, AutostartEntrySource kind)
+{
+    beginInsertRows(QModelIndex(), index, index);
 
-        m_entries.insert(index, entry);
+    AutostartEntry entry = AutostartEntry{name, kind, true, path, false, QStringLiteral("dialog-scripts")};
 
-        endInsertRows();
-    });
+    m_entries.insert(index, entry);
 
-    job->start();
+    endInsertRows();
 }
 
 void AutostartModel::removeEntry(int row)
diff --git a/kcms/autostart/autostartmodel.h b/kcms/autostart/autostartmodel.h
index 201725d5f..f12909f76 100644
--- a/kcms/autostart/autostartmodel.h
+++ b/kcms/autostart/autostartmodel.h
@@ -20,8 +20,10 @@
 #define AUTOSTARTMODEL_H
 
 #include <QAbstractListModel>
+#include <QDir>
 
 #include <KService>
+#include <optional>
 
 struct AutostartEntry;
 class QQuickItem;
@@ -46,7 +48,7 @@ public:
         XdgAutoStart = 0,
         XdgScripts = 1,
         PlasmaShutdown = 2,
-        PlasmaStart = 3,
+        PlasmaEnvScripts = 3,
     };
     Q_ENUM(AutostartEntrySource)
 
@@ -69,8 +71,11 @@ Q_SIGNALS:
 private:
     void addApplication(const KService::Ptr &service);
     void loadScriptsFromDir(const QString &subDir, AutostartEntrySource kind);
-    QString XdgAutoStartPath() const;
+    void insertScriptEntry(int index, const QString &name, const QString &path, AutostartModel::AutostartEntrySource kind);
+    static std::optional<AutostartEntry> loadDesktopEntry(const QString &fileName);
 
+    QDir m_xdgConfigPath;
+    QDir m_xdgAutoStartPath;
     QVector<AutostartEntry> m_entries;
 };
 
diff --git a/kcms/autostart/package/contents/ui/main.qml b/kcms/autostart/package/contents/ui/main.qml
index 644c417cc..3ef2a2c5a 100644
--- a/kcms/autostart/package/contents/ui/main.qml
+++ b/kcms/autostart/package/contents/ui/main.qml
@@ -75,7 +75,7 @@ KCM.ScrollViewKCM {
                     text: i18n("Properties")
                     icon.name: "document-properties"
                     onTriggered: kcm.model.editApplication(model.index, root)
-                    visible: model.source === AutostartModel.XdgAutoStart
+                    visible: model.source === AutostartModel.XdgAutoStart || model.source === AutostartModel.XdgScripts
                 },
                 Kirigami.Action {
                     text: i18n("Remove")
@@ -91,9 +91,12 @@ KCM.ScrollViewKCM {
                 if (section == AutostartModel.XdgAutoStart) {
                     return i18n("Applications")
                 }
-                if (section == AutostartModel.XdgScripts || section == AutostartModel.PlasmaStart) {
+                if (section == AutostartModel.XdgScripts) {
                     return i18n("Login Scripts")
                 }
+                if (section == AutostartModel.PlasmaEnvScripts) {
+                    return i18n("Pre-startup Scripts")
+                }
                 if (section == AutostartModel.PlasmaShutdown) {
                     return i18n("Logout Scripts")
                 }
diff --git a/libkworkspace/CMakeLists.txt b/libkworkspace/CMakeLists.txt
index cca11dc2a..015c5181f 100644
--- a/libkworkspace/CMakeLists.txt
+++ b/libkworkspace/CMakeLists.txt
@@ -4,6 +4,7 @@ set(kworkspace_LIB_SRCS kdisplaymanager.cpp
                         sessionmanagement.cpp
                         sessionmanagementbackend.cpp
                         updatelaunchenvjob.cpp
+                        autostartscriptdesktopfile.cpp
    )
 
 add_definitions(-DTRANSLATION_DOMAIN=\"libkworkspace\")
@@ -73,6 +74,7 @@ install( FILES kdisplaymanager.h
                kworkspace.h
                sessionmanagement.h
                updatelaunchenvjob.h
+               autostartscriptdesktopfile.h
                ${CMAKE_CURRENT_BINARY_DIR}/config-libkworkspace.h
                ${CMAKE_CURRENT_BINARY_DIR}/kworkspace_export.h
          DESTINATION ${KDE_INSTALL_INCLUDEDIR}/kworkspace5 COMPONENT Devel )
diff --git a/libkworkspace/autostartscriptdesktopfile.cpp b/libkworkspace/autostartscriptdesktopfile.cpp
new file mode 100644
index 000000000..4211267e6
--- /dev/null
+++ b/libkworkspace/autostartscriptdesktopfile.cpp
@@ -0,0 +1,34 @@
+/*
+ * SPDX-FileCopyrightText: 2021 Henri Chain <henri.chain at enioka.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "autostartscriptdesktopfile.h"
+#include <KConfigGroup>
+#include <KDesktopFile>
+#include <QDir>
+#include <QStandardPaths>
+
+static const auto autostartScriptKey = QStringLiteral("X-KDE-AutostartScript");
+
+QDir AutostartScriptDesktopFile::autostartLocation()
+{
+    return QDir(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)).filePath("autostart");
+}
+
+AutostartScriptDesktopFile::AutostartScriptDesktopFile(const QString &name, const QString &execPath)
+    : KDesktopFile(autostartLocation().absoluteFilePath(name + QStringLiteral(".desktop")))
+{
+    KConfigGroup kcg = desktopGroup();
+    kcg.writeEntry("Type", "Application");
+    kcg.writeEntry("Name", name);
+    kcg.writeEntry("Exec", execPath);
+    kcg.writeEntry("Icon", "dialog-scripts");
+    kcg.writeEntry(autostartScriptKey, "true");
+    kcg.writeEntry("Path", "");
+}
+
+bool AutostartScriptDesktopFile::isAutostartScript(const KDesktopFile &file)
+{
+    return file.desktopGroup().readEntry<bool>(autostartScriptKey, false);
+}
diff --git a/libkworkspace/autostartscriptdesktopfile.h b/libkworkspace/autostartscriptdesktopfile.h
new file mode 100644
index 000000000..451c00262
--- /dev/null
+++ b/libkworkspace/autostartscriptdesktopfile.h
@@ -0,0 +1,32 @@
+/*
+ * SPDX-FileCopyrightText: 2021 Henri Chain <henri.chain at enioka.com>
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#pragma once
+#include "kworkspace_export.h"
+
+#include <KDesktopFile>
+#include <QDir>
+#include <QString>
+
+/**
+ * Corresponds to a .desktop file in $XDG_CONFIG_HOME/autostart that points to
+ * an autostart script and has X-KDE-AutostartScript=true
+ */
+class KWORKSPACE_EXPORT AutostartScriptDesktopFile : public KDesktopFile
+{
+public:
+    explicit AutostartScriptDesktopFile(const QString &name, const QString &execPath);
+
+    /**
+     * Checks whether this KDesktopFile has X-KDE-AutostartScript=true
+     */
+    static bool isAutostartScript(const KDesktopFile &file);
+
+    /**
+     * The location of autostart .desktop application and script files
+     * ($XDG_CONFIG_HOME/autostart)
+     */
+    static QDir autostartLocation();
+};
diff --git a/startkde/plasma-session/startup.cpp b/startkde/plasma-session/startup.cpp
index e563e3b80..81afee89b 100644
--- a/startkde/plasma-session/startup.cpp
+++ b/startkde/plasma-session/startup.cpp
@@ -132,15 +132,14 @@ public:
         : Phase(autostart, parent)
     {
     }
-    void runUserAutostart();
-    bool migrateKDE4Autostart(const QString &folder);
+    void migrateKDE4Autostart();
 
     void start() override
     {
         qCDebug(PLASMA_SESSION) << "Phase 2";
+        migrateKDE4Autostart();
         addSubjob(new AutoStartAppsJob(m_autostart, 2));
         addSubjob(new KDEDInitJob());
-        runUserAutostart();
     }
 };
 
@@ -352,48 +351,21 @@ void RestoreSessionJob::start()
     connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater);
 }
 
-void StartupPhase2::runUserAutostart()
+void StartupPhase2::migrateKDE4Autostart()
 {
-    // Now let's execute the scripts in the KDE-specific autostart-scripts folder.
+    // Migrate user autostart from kde4
+    Kdelibs4Migration migration;
+    if (!migration.kdeHomeFound()) {
+        return;
+    }
+
     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, QOverload<int, QProcess::ExitStatus>::of(&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
@@ -420,7 +392,7 @@ bool StartupPhase2::migrateKDE4Autostart(const QString &autostartFolder)
             qCWarning(PLASMA_SESSION) << "Error copying" << src << "to" << dest;
         }
     }
-    return true;
+    return;
 }
 
 AutoStartAppsJob::AutoStartAppsJob(const AutoStart &autostart, int phase)
diff --git a/startkde/startplasma.cpp b/startkde/startplasma.cpp
index 997f91e88..f6b078e7e 100644
--- a/startkde/startplasma.cpp
+++ b/startkde/startplasma.cpp
@@ -34,6 +34,7 @@
 
 #include <unistd.h>
 
+#include <autostartscriptdesktopfile.h>
 #include <updatelaunchenvjob.h>
 
 #include "startplasma.h"
@@ -538,6 +539,9 @@ bool startPlasmaSession(bool wayland)
         }
     });
 
+    // Create .desktop files for the scripts in .config/autostart-scripts
+    migrateUserScriptsAutostart();
+
     if (!useSystemdBoot()) {
         qCDebug(PLASMA_STARTUP) << "Using classic boot";
         QProcess startPlasmaSession;
@@ -611,3 +615,47 @@ void waitForKonqi()
         }
     }
 }
+
+static void migrateUserScriptsAutostart()
+{
+    QDir configLocation(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation));
+    QDir autostartScriptsLocation(configLocation.filePath(QStringLiteral("autostart-scripts")));
+    if (!autostartScriptsLocation.exists()) {
+        return;
+    }
+    const QDir autostartScriptsMovedLocation(configLocation.filePath(QStringLiteral("old-autostart-scripts")));
+    const auto entries = autostartScriptsLocation.entryInfoList(QDir::Files);
+    for (const auto &info : entries) {
+        const auto scriptName = info.fileName();
+        const auto scriptPath = info.absoluteFilePath();
+        const auto scriptMovedPath = autostartScriptsMovedLocation.filePath(scriptName);
+
+        // Don't migrate backup files
+        if (scriptName.endsWith(QLatin1Char('~')) || scriptName.endsWith(QLatin1String(".bak"))
+            || (scriptName[0] == QLatin1Char('%') && scriptName.endsWith(QLatin1Char('%')))
+            || (scriptName[0] == QLatin1Char('#') && scriptName.endsWith(QLatin1Char('#')))) {
+            qCDebug(PLASMA_STARTUP) << "Not migrating backup autostart script" << scriptName;
+            continue;
+        }
+
+        // Migrate autostart script to a standard .desktop autostart file
+        AutostartScriptDesktopFile desktopFile(scriptName, info.isSymLink() ? info.symLinkTarget() : scriptMovedPath);
+        qCInfo(PLASMA_STARTUP) << "Migrated legacy autostart script" << scriptPath << "to" << desktopFile.fileName();
+
+        if (info.isSymLink() && QFile::remove(scriptPath)) {
+            qCInfo(PLASMA_STARTUP) << "Removed legacy autostart script" << scriptPath << "that pointed to" << info.symLinkTarget();
+        }
+    }
+    // Delete or rename autostart-scripts to old-autostart-scripts to avoid running the migration again
+    if (autostartScriptsLocation.entryInfoList(QDir::Files).empty()) {
+        autostartScriptsLocation.removeRecursively();
+    } else {
+        configLocation.rename(autostartScriptsLocation.dirName(), autostartScriptsMovedLocation.dirName());
+    }
+    // Reload systemd so that the XDG autostart generator is run again to pick up the new .desktop files
+    QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"),
+                                                          QStringLiteral("/org/freedesktop/systemd1"),
+                                                          QStringLiteral("org.freedesktop.systemd1.Manager"),
+                                                          QStringLiteral("Reload"));
+    QDBusConnection::sessionBus().call(message);
+}
diff --git a/startkde/startplasma.h b/startkde/startplasma.h
index 40a78a0c2..a7c9641b4 100644
--- a/startkde/startplasma.h
+++ b/startkde/startplasma.h
@@ -54,6 +54,7 @@ void waitForKonqi();
 static void resetSystemdFailedUnits();
 static bool hasSystemdService(const QString &serviceName);
 static bool useSystemdBoot();
+static void migrateUserScriptsAutostart();
 
 struct KillBeforeDeleter {
     static inline void cleanup(QProcess *pointer)


More information about the kde-doc-english mailing list