forked from quickshell/quickshell
feat: add Process.manageLifetime
This commit is contained in:
parent
4cfe6ee0a1
commit
62f99f5754
|
@ -25,3 +25,9 @@ void QuickshellPlugin::initPlugins() {
|
||||||
plugin->registerTypes();
|
plugin->registerTypes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuickshellPlugin::runOnReload() {
|
||||||
|
for (QuickshellPlugin* plugin: plugins) {
|
||||||
|
plugin->onReload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,9 +15,11 @@ public:
|
||||||
virtual bool applies() { return true; }
|
virtual bool applies() { return true; }
|
||||||
virtual void init() {}
|
virtual void init() {}
|
||||||
virtual void registerTypes() {}
|
virtual void registerTypes() {}
|
||||||
|
virtual void onReload() {}
|
||||||
|
|
||||||
static void registerPlugin(QuickshellPlugin& plugin);
|
static void registerPlugin(QuickshellPlugin& plugin);
|
||||||
static void initPlugins();
|
static void initPlugins();
|
||||||
|
static void runOnReload();
|
||||||
};
|
};
|
||||||
|
|
||||||
// NOLINTBEGIN
|
// NOLINTBEGIN
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <qtimer.h>
|
#include <qtimer.h>
|
||||||
#include <qurl.h>
|
#include <qurl.h>
|
||||||
|
|
||||||
|
#include "plugin.hpp"
|
||||||
#include "qmlglobal.hpp"
|
#include "qmlglobal.hpp"
|
||||||
#include "reload.hpp"
|
#include "reload.hpp"
|
||||||
#include "shell.hpp"
|
#include "shell.hpp"
|
||||||
|
@ -41,9 +42,8 @@ RootWrapper::~RootWrapper() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RootWrapper::reloadGraph(bool hard) {
|
void RootWrapper::reloadGraph(bool hard) {
|
||||||
QuickshellGlobal::deleteInstance();
|
|
||||||
|
|
||||||
if (this->root != nullptr) {
|
if (this->root != nullptr) {
|
||||||
|
QuickshellGlobal::deleteInstance();
|
||||||
this->engine.clearComponentCache();
|
this->engine.clearComponentCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,10 +77,14 @@ void RootWrapper::reloadGraph(bool hard) {
|
||||||
oldRoot->deleteLater();
|
oldRoot->deleteLater();
|
||||||
|
|
||||||
QTimer::singleShot(0, [this, newRoot]() {
|
QTimer::singleShot(0, [this, newRoot]() {
|
||||||
if (this->root == newRoot) PostReloadHook::postReloadTree(this->root);
|
if (this->root == newRoot) {
|
||||||
|
QuickshellPlugin::runOnReload();
|
||||||
|
PostReloadHook::postReloadTree(this->root);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
PostReloadHook::postReloadTree(newRoot);
|
PostReloadHook::postReloadTree(newRoot);
|
||||||
|
QuickshellPlugin::runOnReload();
|
||||||
}
|
}
|
||||||
|
|
||||||
this->onConfigChanged();
|
this->onConfigChanged();
|
||||||
|
|
|
@ -3,14 +3,18 @@ qt_add_library(quickshell-io STATIC
|
||||||
process.cpp
|
process.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_library(quickshell-io-init OBJECT init.cpp)
|
||||||
|
|
||||||
if (SOCKETS)
|
if (SOCKETS)
|
||||||
target_sources(quickshell-io PRIVATE socket.cpp)
|
target_sources(quickshell-io PRIVATE socket.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
qt_add_qml_module(quickshell-io URI Quickshell.Io)
|
qt_add_qml_module(quickshell-io URI Quickshell.Io)
|
||||||
target_link_libraries(quickshell-io PRIVATE ${QT_DEPS})
|
|
||||||
|
|
||||||
target_link_libraries(quickshell PRIVATE quickshell-ioplugin)
|
target_link_libraries(quickshell-io PRIVATE ${QT_DEPS})
|
||||||
|
target_link_libraries(quickshell-io-init PRIVATE ${QT_DEPS})
|
||||||
|
|
||||||
|
target_link_libraries(quickshell PRIVATE quickshell-ioplugin quickshell-io-init)
|
||||||
|
|
||||||
if (TESTS)
|
if (TESTS)
|
||||||
add_subdirectory(test)
|
add_subdirectory(test)
|
||||||
|
|
12
src/io/init.cpp
Normal file
12
src/io/init.cpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "../core/plugin.hpp"
|
||||||
|
#include "process.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class IoPlugin: public QuickshellPlugin {
|
||||||
|
void onReload() override { DisownedProcessContext::destroyInstance(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
QS_REGISTER_PLUGIN(IoPlugin);
|
||||||
|
|
||||||
|
} // namespace
|
0
src/io/plugin.cpp
Normal file
0
src/io/plugin.cpp
Normal file
|
@ -15,6 +15,10 @@
|
||||||
#include "../core/qmlglobal.hpp"
|
#include "../core/qmlglobal.hpp"
|
||||||
#include "datastream.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) {
|
Process::Process(QObject* parent): QObject(parent) {
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
QuickshellGlobal::instance(),
|
QuickshellGlobal::instance(),
|
||||||
|
@ -24,6 +28,13 @@ 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; }
|
bool Process::isRunning() const { return this->process != nullptr; }
|
||||||
|
|
||||||
void Process::setRunning(bool running) {
|
void Process::setRunning(bool running) {
|
||||||
|
@ -161,6 +172,14 @@ void Process::setStdinEnabled(bool enabled) {
|
||||||
emit this->stdinEnabledChanged();
|
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() {
|
void Process::startProcessIfReady() {
|
||||||
if (this->process != nullptr || !this->targetRunning || this->mCommand.isEmpty()) return;
|
if (this->process != nullptr || !this->targetRunning || this->mCommand.isEmpty()) return;
|
||||||
this->targetRunning = false;
|
this->targetRunning = false;
|
||||||
|
@ -261,3 +280,13 @@ void Process::write(const QString& data) {
|
||||||
if (this->process == nullptr) return;
|
if (this->process == nullptr) return;
|
||||||
this->process->write(data.toUtf8());
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qprocess.h>
|
#include <qprocess.h>
|
||||||
#include <qqmlintegration.h>
|
#include <qqmlintegration.h>
|
||||||
|
#include <qtclasshelpermacros.h>
|
||||||
#include <qtmetamacros.h>
|
#include <qtmetamacros.h>
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
#include <qvariant.h>
|
#include <qvariant.h>
|
||||||
|
@ -43,7 +44,8 @@ class Process: public QObject {
|
||||||
Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged);
|
Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged);
|
||||||
/// The process ID of the running process or `null` if `running` is false.
|
/// The process ID of the running process or `null` if `running` is false.
|
||||||
Q_PROPERTY(QVariant pid READ pid NOTIFY pidChanged);
|
Q_PROPERTY(QVariant pid READ pid NOTIFY pidChanged);
|
||||||
/// The command to execute.
|
/// The command to execute. Each argument is its own string, which means you don't have
|
||||||
|
/// to deal with quoting anything.
|
||||||
///
|
///
|
||||||
/// If the process is already running changing this property will affect the next
|
/// If the process is already running changing this property will affect the next
|
||||||
/// started process. If the property has been changed after starting a process it will
|
/// started process. If the property has been changed after starting a process it will
|
||||||
|
@ -112,11 +114,21 @@ class Process: public QObject {
|
||||||
/// If stdin is enabled. Defaults to true. If this property is set to false the process's stdin channel
|
/// If stdin is enabled. Defaults to true. If this property is set to false the process's stdin channel
|
||||||
/// will be closed and [write](#func.write) will do nothing, even if set back to true.
|
/// will be closed and [write](#func.write) will do nothing, even if set back to true.
|
||||||
Q_PROPERTY(bool stdinEnabled READ stdinEnabled WRITE setStdinEnabled NOTIFY stdinEnabledChanged);
|
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
|
// clang-format on
|
||||||
QML_ELEMENT;
|
QML_ELEMENT;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit Process(QObject* parent = nullptr);
|
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.
|
/// Sends a signal to the process if `running` is true, otherwise does nothing.
|
||||||
Q_INVOKABLE void signal(qint32 signal);
|
Q_INVOKABLE void signal(qint32 signal);
|
||||||
|
@ -150,6 +162,9 @@ public:
|
||||||
[[nodiscard]] bool stdinEnabled() const;
|
[[nodiscard]] bool stdinEnabled() const;
|
||||||
void setStdinEnabled(bool enabled);
|
void setStdinEnabled(bool enabled);
|
||||||
|
|
||||||
|
[[nodiscard]] bool isLifetimeManaged() const;
|
||||||
|
void setLifetimeManaged(bool managed);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void started();
|
void started();
|
||||||
void exited(qint32 exitCode, QProcess::ExitStatus exitStatus);
|
void exited(qint32 exitCode, QProcess::ExitStatus exitStatus);
|
||||||
|
@ -163,6 +178,7 @@ signals:
|
||||||
void stdoutParserChanged();
|
void stdoutParserChanged();
|
||||||
void stderrParserChanged();
|
void stderrParserChanged();
|
||||||
void stdinEnabledChanged();
|
void stdinEnabledChanged();
|
||||||
|
void lifetimeManagedChanged();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onStarted();
|
void onStarted();
|
||||||
|
@ -189,4 +205,15 @@ private:
|
||||||
bool targetRunning = false;
|
bool targetRunning = false;
|
||||||
bool mStdinEnabled = false;
|
bool mStdinEnabled = false;
|
||||||
bool mClearEnvironment = false;
|
bool mClearEnvironment = false;
|
||||||
|
bool mLifetimeManaged = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DisownedProcessContext: public QObject {
|
||||||
|
Q_OBJECT;
|
||||||
|
|
||||||
|
void reparent(QProcess* process);
|
||||||
|
friend class Process;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void destroyInstance();
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue