Skip to content

Commit 7bab2fd

Browse files
committed
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.
1 parent c5bea85 commit 7bab2fd

File tree

4 files changed

+36
-74
lines changed

4 files changed

+36
-74
lines changed

src/io/CMakeLists.txt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ qt_add_library(quickshell-io STATIC
77
ipchandler.cpp
88
)
99

10-
add_library(quickshell-io-init OBJECT init.cpp)
11-
1210
if (SOCKETS)
1311
target_sources(quickshell-io PRIVATE socket.cpp)
1412
endif()
@@ -24,9 +22,7 @@ qt_add_qml_module(quickshell-io
2422
install_qml_module(quickshell-io)
2523

2624
target_link_libraries(quickshell-io PRIVATE Qt::Quick)
27-
target_link_libraries(quickshell-io-init PRIVATE Qt::Qml)
28-
29-
target_link_libraries(quickshell PRIVATE quickshell-ioplugin quickshell-io-init)
25+
target_link_libraries(quickshell PRIVATE quickshell-ioplugin)
3026

3127
qs_module_pch(quickshell-io)
3228

src/io/init.cpp

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/io/process.cpp

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <qmap.h>
99
#include <qobject.h>
1010
#include <qprocess.h>
11+
#include <qqmlinfo.h>
1112
#include <qtmetamacros.h>
1213
#include <qtypes.h>
1314
#include <qvariant.h>
@@ -16,10 +17,6 @@
1617
#include "../core/qmlglobal.hpp"
1718
#include "datastream.hpp"
1819

19-
// When the process ends this have no parent and is just leaked,
20-
// meaning the destructor never runs and they are never killed.
21-
static DisownedProcessContext* disownedCtx; // NOLINT
22-
2320
Process::Process(QObject* parent): QObject(parent) {
2421
QObject::connect(
2522
QuickshellSettings::instance(),
@@ -29,13 +26,6 @@ Process::Process(QObject* parent): QObject(parent) {
2926
);
3027
}
3128

32-
Process::~Process() {
33-
if (!this->mLifetimeManaged && this->process != nullptr) {
34-
if (disownedCtx == nullptr) disownedCtx = new DisownedProcessContext(); // NOLINT
35-
disownedCtx->reparent(this->process);
36-
}
37-
}
38-
3929
bool Process::isRunning() const { return this->process != nullptr; }
4030

4131
void Process::setRunning(bool running) {
@@ -183,14 +173,6 @@ void Process::setStdinEnabled(bool enabled) {
183173
emit this->stdinEnabledChanged();
184174
}
185175

186-
bool Process::isLifetimeManaged() const { return this->mLifetimeManaged; }
187-
188-
void Process::setLifetimeManaged(bool managed) {
189-
if (managed == this->mLifetimeManaged) return;
190-
this->mLifetimeManaged = managed;
191-
emit this->lifetimeManagedChanged();
192-
}
193-
194176
void Process::startProcessIfReady() {
195177
if (this->process != nullptr || !this->targetRunning || this->mCommand.isEmpty()) return;
196178
this->targetRunning = false;
@@ -215,8 +197,30 @@ void Process::startProcessIfReady() {
215197
if (this->mStderrParser == nullptr) this->process->closeReadChannel(QProcess::StandardError);
216198
if (!this->mStdinEnabled) this->process->closeWriteChannel();
217199

200+
this->setupEnvironment(this->process);
201+
this->process->start(cmd, args);
202+
}
203+
204+
void Process::startDetached() {
205+
if (this->mCommand.isEmpty()) {
206+
qmlWarning(this) << "Cannot start process as command is empty.";
207+
return;
208+
}
209+
210+
auto& cmd = this->mCommand.first();
211+
auto args = this->mCommand.sliced(1);
212+
213+
QProcess process;
214+
215+
this->setupEnvironment(&process);
216+
process.setProgram(cmd);
217+
process.setArguments(args);
218+
process.startDetached();
219+
}
220+
221+
void Process::setupEnvironment(QProcess* process) {
218222
if (!this->mWorkingDirectory.isEmpty()) {
219-
this->process->setWorkingDirectory(this->mWorkingDirectory);
223+
process->setWorkingDirectory(this->mWorkingDirectory);
220224
}
221225

222226
if (!this->mEnvironment.isEmpty() || this->mClearEnvironment) {
@@ -237,10 +241,8 @@ void Process::startProcessIfReady() {
237241
}
238242
}
239243

240-
this->process->setProcessEnvironment(env);
244+
process->setProcessEnvironment(env);
241245
}
242-
243-
this->process->start(cmd, args);
244246
}
245247

246248
void Process::onStarted() {
@@ -291,13 +293,3 @@ void Process::write(const QString& data) {
291293
if (this->process == nullptr) return;
292294
this->process->write(data.toUtf8());
293295
}
294-
295-
void DisownedProcessContext::reparent(QProcess* process) {
296-
process->setParent(this);
297-
QObject::connect(process, &QProcess::finished, this, [process]() { process->deleteLater(); });
298-
}
299-
300-
void DisownedProcessContext::destroyInstance() {
301-
delete disownedCtx;
302-
disownedCtx = nullptr;
303-
}

src/io/process.hpp

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ class Process: public QObject {
4646
/// onRunningChanged: if (!running) running = true
4747
/// }
4848
/// ```
49+
///
50+
/// > [!NOTE] See @@startDetached() to prevent the process from being killed by Quickshell
51+
/// > if Quickshell is killed or the configuration is reloaded.
4952
Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged);
5053
/// The process ID of the running process or `null` if @@running is false.
5154
Q_PROPERTY(QVariant processId READ processId NOTIFY processIdChanged);
@@ -125,20 +128,11 @@ class Process: public QObject {
125128
/// If stdin is enabled. Defaults to false. If this property is false the process's stdin channel
126129
/// will be closed and @@write() will do nothing, even if set back to true.
127130
Q_PROPERTY(bool stdinEnabled READ stdinEnabled WRITE setStdinEnabled NOTIFY stdinEnabledChanged);
128-
/// If the process should be killed when the Process object is destroyed or quickshell exits.
129-
/// Defaults to true.
130-
///
131-
/// This property may be changed while the process is running and will affect it.
132-
///
133-
/// > [!WARNING] If set to false the process will still be killed if the quickshell config reloads.
134-
/// > It will not be killed if quickshell exits normally or crashes.
135-
Q_PROPERTY(bool manageLifetime READ isLifetimeManaged WRITE setLifetimeManaged NOTIFY lifetimeManagedChanged);
136131
// clang-format on
137132
QML_ELEMENT;
138133

139134
public:
140135
explicit Process(QObject* parent = nullptr);
141-
~Process() override;
142136
Q_DISABLE_COPY_MOVE(Process);
143137

144138
/// Sends a signal to the process if @@running is true, otherwise does nothing.
@@ -147,6 +141,12 @@ class Process: public QObject {
147141
/// Writes to the process's stdin. Does nothing if @@running is false.
148142
Q_INVOKABLE void write(const QString& data);
149143

144+
/// Launches an instance of the process detached from quickshell.
145+
///
146+
/// The subprocess will not be tracked, @@running will be false,
147+
/// and the subprocess will not be killed by Quickshell.
148+
Q_INVOKABLE void startDetached();
149+
150150
[[nodiscard]] bool isRunning() const;
151151
void setRunning(bool running);
152152

@@ -173,9 +173,6 @@ class Process: public QObject {
173173
[[nodiscard]] bool stdinEnabled() const;
174174
void setStdinEnabled(bool enabled);
175175

176-
[[nodiscard]] bool isLifetimeManaged() const;
177-
void setLifetimeManaged(bool managed);
178-
179176
signals:
180177
void started();
181178
void exited(qint32 exitCode, QProcess::ExitStatus exitStatus);
@@ -189,7 +186,6 @@ class Process: public QObject {
189186
void stdoutParserChanged();
190187
void stderrParserChanged();
191188
void stdinEnabledChanged();
192-
void lifetimeManagedChanged();
193189

194190
private slots:
195191
void onStarted();
@@ -203,6 +199,7 @@ private slots:
203199

204200
private:
205201
void startProcessIfReady();
202+
void setupEnvironment(QProcess* process);
206203

207204
QProcess* process = nullptr;
208205
QList<QString> mCommand;
@@ -216,15 +213,4 @@ private slots:
216213
bool targetRunning = false;
217214
bool mStdinEnabled = false;
218215
bool mClearEnvironment = false;
219-
bool mLifetimeManaged = true;
220-
};
221-
222-
class DisownedProcessContext: public QObject {
223-
Q_OBJECT;
224-
225-
void reparent(QProcess* process);
226-
friend class Process;
227-
228-
public:
229-
static void destroyInstance();
230216
};

0 commit comments

Comments
 (0)