From 0662c37d673e318b171c66ea19c4b9a7dd87b2a2 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Wed, 12 Mar 2025 00:53:36 -0700 Subject: [PATCH] io/process!: replace manageLifetime with startDetached In most cases this is what was desired for usages of manageLifetime. Starting the process in a detached state also makes sure the process hierarchy will not result in the child being killed when Quickshell is killed. --- src/io/CMakeLists.txt | 6 +--- src/io/init.cpp | 12 -------- src/io/process.cpp | 58 +++++++++++++++-------------------- src/io/process.hpp | 35 ++++++--------------- src/services/mpris/player.cpp | 2 +- 5 files changed, 37 insertions(+), 76 deletions(-) delete mode 100644 src/io/init.cpp diff --git a/src/io/CMakeLists.txt b/src/io/CMakeLists.txt index 6299b397..79e6aed6 100644 --- a/src/io/CMakeLists.txt +++ b/src/io/CMakeLists.txt @@ -7,8 +7,6 @@ qt_add_library(quickshell-io STATIC ipchandler.cpp ) -add_library(quickshell-io-init OBJECT init.cpp) - if (SOCKETS) target_sources(quickshell-io PRIVATE socket.cpp) endif() @@ -24,9 +22,7 @@ qt_add_qml_module(quickshell-io install_qml_module(quickshell-io) target_link_libraries(quickshell-io PRIVATE Qt::Quick) -target_link_libraries(quickshell-io-init PRIVATE Qt::Qml) - -target_link_libraries(quickshell PRIVATE quickshell-ioplugin quickshell-io-init) +target_link_libraries(quickshell PRIVATE quickshell-ioplugin) qs_module_pch(quickshell-io) diff --git a/src/io/init.cpp b/src/io/init.cpp deleted file mode 100644 index 33c3dc35..00000000 --- a/src/io/init.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "../core/plugin.hpp" -#include "process.hpp" - -namespace { - -class IoPlugin: public QsEnginePlugin { - void onReload() override { DisownedProcessContext::destroyInstance(); } -}; - -QS_REGISTER_PLUGIN(IoPlugin); - -} // namespace diff --git a/src/io/process.cpp b/src/io/process.cpp index 3e3292d0..6b1ab5d6 100644 --- a/src/io/process.cpp +++ b/src/io/process.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -16,10 +17,6 @@ #include "../core/qmlglobal.hpp" #include "datastream.hpp" -// When the process ends this have no parent and is just leaked, -// meaning the destructor never runs and they are never killed. -static DisownedProcessContext* disownedCtx; // NOLINT - Process::Process(QObject* parent): QObject(parent) { QObject::connect( QuickshellSettings::instance(), @@ -29,13 +26,6 @@ Process::Process(QObject* parent): QObject(parent) { ); } -Process::~Process() { - if (!this->mLifetimeManaged && this->process != nullptr) { - if (disownedCtx == nullptr) disownedCtx = new DisownedProcessContext(); // NOLINT - disownedCtx->reparent(this->process); - } -} - bool Process::isRunning() const { return this->process != nullptr; } void Process::setRunning(bool running) { @@ -183,14 +173,6 @@ void Process::setStdinEnabled(bool enabled) { emit this->stdinEnabledChanged(); } -bool Process::isLifetimeManaged() const { return this->mLifetimeManaged; } - -void Process::setLifetimeManaged(bool managed) { - if (managed == this->mLifetimeManaged) return; - this->mLifetimeManaged = managed; - emit this->lifetimeManagedChanged(); -} - void Process::startProcessIfReady() { if (this->process != nullptr || !this->targetRunning || this->mCommand.isEmpty()) return; this->targetRunning = false; @@ -215,8 +197,30 @@ void Process::startProcessIfReady() { if (this->mStderrParser == nullptr) this->process->closeReadChannel(QProcess::StandardError); if (!this->mStdinEnabled) this->process->closeWriteChannel(); + this->setupEnvironment(this->process); + this->process->start(cmd, args); +} + +void Process::startDetached() { + if (this->mCommand.isEmpty()) { + qmlWarning(this) << "Cannot start process as command is empty."; + return; + } + + auto& cmd = this->mCommand.first(); + auto args = this->mCommand.sliced(1); + + QProcess process; + + this->setupEnvironment(&process); + process.setProgram(cmd); + process.setArguments(args); + process.startDetached(); +} + +void Process::setupEnvironment(QProcess* process) { if (!this->mWorkingDirectory.isEmpty()) { - this->process->setWorkingDirectory(this->mWorkingDirectory); + process->setWorkingDirectory(this->mWorkingDirectory); } if (!this->mEnvironment.isEmpty() || this->mClearEnvironment) { @@ -237,10 +241,8 @@ void Process::startProcessIfReady() { } } - this->process->setProcessEnvironment(env); + process->setProcessEnvironment(env); } - - this->process->start(cmd, args); } void Process::onStarted() { @@ -291,13 +293,3 @@ void Process::write(const QString& data) { if (this->process == nullptr) return; this->process->write(data.toUtf8()); } - -void DisownedProcessContext::reparent(QProcess* process) { - process->setParent(this); - QObject::connect(process, &QProcess::finished, this, [process]() { process->deleteLater(); }); -} - -void DisownedProcessContext::destroyInstance() { - delete disownedCtx; - disownedCtx = nullptr; -} diff --git a/src/io/process.hpp b/src/io/process.hpp index 8fcffbc5..7f448c4e 100644 --- a/src/io/process.hpp +++ b/src/io/process.hpp @@ -46,6 +46,9 @@ class Process: public QObject { /// onRunningChanged: if (!running) running = true /// } /// ``` + /// + /// > [!NOTE] See @@startDetached() to prevent the process from being killed by Quickshell + /// > if Quickshell is killed or the configuration is reloaded. Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged); /// The process ID of the running process or `null` if @@running is false. Q_PROPERTY(QVariant processId READ processId NOTIFY processIdChanged); @@ -125,21 +128,11 @@ class Process: public QObject { /// If stdin is enabled. Defaults to false. If this property is false the process's stdin channel /// will be closed and @@write() will do nothing, even if set back to true. Q_PROPERTY(bool stdinEnabled READ stdinEnabled WRITE setStdinEnabled NOTIFY stdinEnabledChanged); - /// If the process should be killed when the Process object is destroyed or quickshell exits. - /// Defaults to true. - /// - /// This property may be changed while the process is running and will affect it. - /// - /// > [!WARNING] If set to false the process will still be killed if the quickshell config reloads. - /// > It will not be killed if quickshell exits normally or crashes. - Q_PROPERTY(bool manageLifetime READ isLifetimeManaged WRITE setLifetimeManaged NOTIFY lifetimeManagedChanged); // clang-format on QML_ELEMENT; public: explicit Process(QObject* parent = nullptr); - ~Process() override; - Q_DISABLE_COPY_MOVE(Process); /// Sends a signal to the process if @@running is true, otherwise does nothing. Q_INVOKABLE void signal(qint32 signal); @@ -147,6 +140,12 @@ public: /// Writes to the process's stdin. Does nothing if @@running is false. Q_INVOKABLE void write(const QString& data); + /// Launches an instance of the process detached from quickshell. + /// + /// The subprocess will not be tracked, @@running will be false, + /// and the subprocess will not be killed by Quickshell. + Q_INVOKABLE void startDetached(); + [[nodiscard]] bool isRunning() const; void setRunning(bool running); @@ -173,9 +172,6 @@ public: [[nodiscard]] bool stdinEnabled() const; void setStdinEnabled(bool enabled); - [[nodiscard]] bool isLifetimeManaged() const; - void setLifetimeManaged(bool managed); - signals: void started(); void exited(qint32 exitCode, QProcess::ExitStatus exitStatus); @@ -189,7 +185,6 @@ signals: void stdoutParserChanged(); void stderrParserChanged(); void stdinEnabledChanged(); - void lifetimeManagedChanged(); private slots: void onStarted(); @@ -203,6 +198,7 @@ private slots: private: void startProcessIfReady(); + void setupEnvironment(QProcess* process); QProcess* process = nullptr; QList mCommand; @@ -216,15 +212,4 @@ private: bool targetRunning = false; bool mStdinEnabled = false; bool mClearEnvironment = false; - bool mLifetimeManaged = true; -}; - -class DisownedProcessContext: public QObject { - Q_OBJECT; - - void reparent(QProcess* process); - friend class Process; - -public: - static void destroyInstance(); }; diff --git a/src/services/mpris/player.cpp b/src/services/mpris/player.cpp index b57ad48a..a2cdfa7d 100644 --- a/src/services/mpris/player.cpp +++ b/src/services/mpris/player.cpp @@ -1,6 +1,5 @@ #include "player.hpp" -#include #include #include #include @@ -11,6 +10,7 @@ #include #include #include +#include #include #include