From e06b07ac01e80d561b0abb9e3b23faf233120823 Mon Sep 17 00:00:00 2001 From: outfoxxed Date: Sun, 3 Mar 2024 19:35:10 -0800 Subject: [PATCH] feat(process): add Process.environment --- docs | 2 +- src/io/process.cpp | 55 +++++++++++++++++++++++++++----- src/io/process.hpp | 78 +++++++++++++++++++++++++++++++++++++++------- 3 files changed, 115 insertions(+), 20 deletions(-) diff --git a/docs b/docs index 8b40a46..1ca13d9 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 8b40a464af5c1338d842e61f8ec517d142454cbc +Subproject commit 1ca13d9ffded3fbddb0380c0d0df85304065efa9 diff --git a/src/io/process.cpp b/src/io/process.cpp index 7a88fd5..7147b56 100644 --- a/src/io/process.cpp +++ b/src/io/process.cpp @@ -5,10 +5,12 @@ #include #include #include +#include #include #include #include #include +#include #include "../core/qmlglobal.hpp" #include "datastream.hpp" @@ -35,6 +37,16 @@ QVariant Process::pid() const { return QVariant::fromValue(this->process->processId()); } +QList Process::command() const { return this->mCommand; } + +void Process::setCommand(QList command) { + if (this->mCommand == command) return; + this->mCommand = std::move(command); + emit this->commandChanged(); + + this->startProcessIfReady(); +} + QString Process::workingDirectory() const { if (this->mWorkingDirectory.isEmpty()) return QDir::current().absolutePath(); else return this->mWorkingDirectory; @@ -54,14 +66,20 @@ void Process::onGlobalWorkingDirectoryChanged() { } } -QList Process::command() const { return this->mCommand; } +QMap Process::environment() const { return this->mEnvironment; } -void Process::setCommand(QList command) { - if (this->mCommand == command) return; - this->mCommand = std::move(command); - emit this->commandChanged(); +void Process::setEnvironment(QMap environment) { + if (environment == this->mEnvironment) return; + this->mEnvironment = std::move(environment); + emit this->environmentChanged(); +} - this->startProcessIfReady(); +bool Process::environmentCleared() const { return this->mClearEnvironment; } + +void Process::setEnvironmentCleared(bool cleared) { + if (cleared == this->mClearEnvironment) return; + this->mClearEnvironment = cleared; + emit this->environmentClearChanged(); } DataStreamParser* Process::stdoutParser() const { return this->mStdoutParser; } @@ -167,8 +185,31 @@ void Process::startProcessIfReady() { if (this->mStderrParser == nullptr) this->process->closeReadChannel(QProcess::StandardError); if (!this->mStdinEnabled) this->process->closeWriteChannel(); - if (!this->mWorkingDirectory.isEmpty()) + if (!this->mWorkingDirectory.isEmpty()) { this->process->setWorkingDirectory(this->mWorkingDirectory); + } + + if (!this->mEnvironment.isEmpty() || this->mClearEnvironment) { + auto sysenv = QProcessEnvironment::systemEnvironment(); + auto env = this->mClearEnvironment ? QProcessEnvironment() : sysenv; + + for (auto& name: this->mEnvironment.keys()) { + auto value = this->mEnvironment.value(name); + if (!value.isValid()) continue; + + if (this->mClearEnvironment) { + if (value.isNull()) { + if (sysenv.contains(name)) env.insert(name, sysenv.value(name)); + } else env.insert(name, value.toString()); + } else { + if (value.isNull()) env.remove(name); + else env.insert(name, value.toString()); + } + } + + this->process->setProcessEnvironment(env); + } + this->process->start(cmd, args); } diff --git a/src/io/process.hpp b/src/io/process.hpp index ab3f0ff..7d13f2c 100644 --- a/src/io/process.hpp +++ b/src/io/process.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -42,14 +43,6 @@ class Process: public QObject { 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 pid READ pid NOTIFY pidChanged); - /// The working directory of the process. Defaults to [quickshell's working directory]. - /// - /// 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 - /// return the new value, not the one for the currently running process. - /// - /// [quickshell's working directory]: ../../quickshell/quickshell#prop.workingDirectory - Q_PROPERTY(QString workingDirectory READ workingDirectory WRITE setWorkingDirectory NOTIFY workingDirectoryChanged); /// The command to execute. /// /// If the process is already running changing this property will affect the next @@ -59,6 +52,57 @@ class Process: public QObject { /// > [!INFO] You can use `["sh", "-c", ]` to execute your command with /// > the system shell. Q_PROPERTY(QList command READ command WRITE setCommand NOTIFY commandChanged); + /// The working directory of the process. Defaults to [quickshell's working directory]. + /// + /// 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 + /// return the new value, not the one for the currently running process. + /// + /// [quickshell's working directory]: ../../quickshell/quickshell#prop.workingDirectory + Q_PROPERTY(QString workingDirectory READ workingDirectory WRITE setWorkingDirectory NOTIFY workingDirectoryChanged); + /// Environment of the executed process. + /// + /// This is a javascript object (json). Environment variables can be added by setting + /// them to a string and removed by setting them to null (except when [clearEnvironment] is true, + /// in which case this behavior is inverted, see [clearEnvironment] for details). + /// + /// + /// ```qml + /// environment: ({ + /// ADDED: "value", + /// REMOVED: null, + /// "i'm different": "value", + /// }) + /// ``` + /// + /// > [!INFO] You need to wrap the returned object in () otherwise it won't parse due to javascript ambiguity. + /// + /// 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 + /// return the new value, not the one for the currently running process. + /// + /// [clearEnvironment]: #prop.clearEnvironment + Q_PROPERTY(QMap environment READ environment WRITE setEnvironment NOTIFY environmentChanged); + /// If the process's environment should be cleared prior to applying [environment](#prop.environment). + /// Defaults to false. + /// + /// If true, all environment variables will be removed before the [environment](#prop.environment) + /// object is applied, meaning the variables listed will be the only ones visible to the process. + /// This changes the behavior of `null` to pass in the system value of the variable if present instead + /// of removing it. + /// + /// ```qml + /// clearEnvironment: true + /// environment: ({ + /// ADDED: "value", + /// PASSED_FROM_SYSTEM: null, + /// }) + /// ``` + /// + /// 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 + /// return the new value, not the one for the currently running process. + Q_PROPERTY(bool clearEnvironment READ environmentCleared WRITE setEnvironmentCleared NOTIFY environmentClearChanged); /// The parser for stdout. If the parser is null the process's stdout channel will be closed /// and no further data will be read, even if a new parser is attached. Q_PROPERTY(DataStreamParser* stdout READ stdoutParser WRITE setStdoutParser NOTIFY stdoutParserChanged); @@ -85,11 +129,17 @@ public: [[nodiscard]] QVariant pid() const; + [[nodiscard]] QList command() const; + void setCommand(QList command); + [[nodiscard]] QString workingDirectory() const; void setWorkingDirectory(const QString& workingDirectory); - [[nodiscard]] QList command() const; - void setCommand(QList command); + [[nodiscard]] QMap environment() const; + void setEnvironment(QMap environment); + + [[nodiscard]] bool environmentCleared() const; + void setEnvironmentCleared(bool cleared); [[nodiscard]] DataStreamParser* stdoutParser() const; void setStdoutParser(DataStreamParser* parser); @@ -106,8 +156,10 @@ signals: void runningChanged(); void pidChanged(); - void workingDirectoryChanged(); void commandChanged(); + void workingDirectoryChanged(); + void environmentChanged(); + void environmentClearChanged(); void stdoutParserChanged(); void stderrParserChanged(); void stdinEnabledChanged(); @@ -126,8 +178,9 @@ private: void startProcessIfReady(); QProcess* process = nullptr; - QString mWorkingDirectory; QList mCommand; + QString mWorkingDirectory; + QMap mEnvironment; DataStreamParser* mStdoutParser = nullptr; DataStreamParser* mStderrParser = nullptr; QByteArray stdoutBuffer; @@ -135,4 +188,5 @@ private: bool targetRunning = false; bool mStdinEnabled = false; + bool mClearEnvironment = false; };