forked from quickshell/quickshell
core/qmlglobal: add execDetached functions for spawning processes
This commit is contained in:
parent
0140356d99
commit
0499518143
10 changed files with 167 additions and 30 deletions
|
@ -8,8 +8,10 @@
|
||||||
#include <qguiapplication.h>
|
#include <qguiapplication.h>
|
||||||
#include <qicon.h>
|
#include <qicon.h>
|
||||||
#include <qjsengine.h>
|
#include <qjsengine.h>
|
||||||
|
#include <qlist.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
|
#include <qprocess.h>
|
||||||
#include <qqmlcontext.h>
|
#include <qqmlcontext.h>
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
#include <qqmllist.h>
|
#include <qqmllist.h>
|
||||||
|
@ -21,6 +23,7 @@
|
||||||
#include <qwindowdefs.h>
|
#include <qwindowdefs.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "../io/processcore.hpp"
|
||||||
#include "generation.hpp"
|
#include "generation.hpp"
|
||||||
#include "iconimageprovider.hpp"
|
#include "iconimageprovider.hpp"
|
||||||
#include "paths.hpp"
|
#include "paths.hpp"
|
||||||
|
@ -246,6 +249,36 @@ QVariant QuickshellGlobal::env(const QString& variable) { // NOLINT
|
||||||
return qEnvironmentVariable(vstr.data());
|
return qEnvironmentVariable(vstr.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QuickshellGlobal::execDetached(QList<QString> command) {
|
||||||
|
QuickshellGlobal::execDetached(ProcessContext(std::move(command)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuickshellGlobal::execDetached(const ProcessContext& context) {
|
||||||
|
if (context.command.isEmpty()) {
|
||||||
|
qWarning() << "Cannot start process as command is empty.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& cmd = context.command.first();
|
||||||
|
auto args = context.command.sliced(1);
|
||||||
|
|
||||||
|
QProcess process;
|
||||||
|
|
||||||
|
qs::core::process::setupProcessEnvironment(
|
||||||
|
&process,
|
||||||
|
context.clearEnvironment,
|
||||||
|
context.environment
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!context.workingDirectory.isEmpty()) {
|
||||||
|
process.setWorkingDirectory(context.workingDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
process.setProgram(cmd);
|
||||||
|
process.setArguments(args);
|
||||||
|
process.startDetached();
|
||||||
|
}
|
||||||
|
|
||||||
QString QuickshellGlobal::iconPath(const QString& icon) {
|
QString QuickshellGlobal::iconPath(const QString& icon) {
|
||||||
return IconImageProvider::requestString(icon);
|
return IconImageProvider::requestString(icon);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include <qclipboard.h>
|
#include <qclipboard.h>
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qhash.h>
|
||||||
#include <qjsengine.h>
|
#include <qjsengine.h>
|
||||||
|
#include <qlist.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qqmlengine.h>
|
#include <qqmlengine.h>
|
||||||
#include <qqmlintegration.h>
|
#include <qqmlintegration.h>
|
||||||
|
@ -15,6 +19,25 @@
|
||||||
|
|
||||||
#include "qmlscreen.hpp"
|
#include "qmlscreen.hpp"
|
||||||
|
|
||||||
|
class ProcessContext {
|
||||||
|
Q_PROPERTY(QList<QString> command MEMBER command);
|
||||||
|
Q_PROPERTY(QHash<QString, QVariant> environment MEMBER environment);
|
||||||
|
Q_PROPERTY(bool clearEnvironment MEMBER clearEnvironment);
|
||||||
|
Q_PROPERTY(QString workingDirectory MEMBER workingDirectory);
|
||||||
|
Q_GADGET;
|
||||||
|
QML_STRUCTURED_VALUE;
|
||||||
|
QML_VALUE_TYPE(processContext);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ProcessContext() = default;
|
||||||
|
explicit ProcessContext(QList<QString> command): command(std::move(command)) {}
|
||||||
|
|
||||||
|
QList<QString> command;
|
||||||
|
QHash<QString, QVariant> environment;
|
||||||
|
bool clearEnvironment = false;
|
||||||
|
QString workingDirectory;
|
||||||
|
};
|
||||||
|
|
||||||
///! Accessor for some options under the Quickshell type.
|
///! Accessor for some options under the Quickshell type.
|
||||||
class QuickshellSettings: public QObject {
|
class QuickshellSettings: public QObject {
|
||||||
Q_OBJECT;
|
Q_OBJECT;
|
||||||
|
@ -152,6 +175,46 @@ public:
|
||||||
/// Returns the string value of an environment variable or null if it is not set.
|
/// Returns the string value of an environment variable or null if it is not set.
|
||||||
Q_INVOKABLE QVariant env(const QString& variable);
|
Q_INVOKABLE QVariant env(const QString& variable);
|
||||||
|
|
||||||
|
/// Launch a process detached from Quickshell.
|
||||||
|
///
|
||||||
|
/// Each command argument is its own string, meaning arguments do
|
||||||
|
/// not have to be escaped.
|
||||||
|
///
|
||||||
|
/// > [!WARNING] This does not run command in a shell. All arguments to the command
|
||||||
|
/// > must be in separate values in the list, e.g. `["echo", "hello"]`
|
||||||
|
/// > and not `["echo hello"]`.
|
||||||
|
/// >
|
||||||
|
/// > Additionally, shell scripts must be run by your shell,
|
||||||
|
/// > e.g. `["sh", "script.sh"]` instead of `["script.sh"]` unless the script
|
||||||
|
/// > has a shebang.
|
||||||
|
///
|
||||||
|
/// > [!INFO] You can use `["sh", "-c", <your command>]` to execute your command with
|
||||||
|
/// > the system shell.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to @@Quickshell.Io.Process.startDetached().
|
||||||
|
Q_INVOKABLE static void execDetached(QList<QString> command);
|
||||||
|
/// Launch a process detached from Quickshell.
|
||||||
|
///
|
||||||
|
/// The context parameter is a JS object with the following fields:
|
||||||
|
/// - `command`: A list containing the command and all its arguments. See @@Quickshell.Io.Process.command.
|
||||||
|
/// - `environment`: Changes to make to the process environment. See @@Quickshell.Io.Process.environment.
|
||||||
|
/// - `clearEnvironment`: Removes all variables from the environment if true.
|
||||||
|
/// - `workingDirectory`: The working directory the command should run in.
|
||||||
|
///
|
||||||
|
/// > [!WARNING] This does not run command in a shell. All arguments to the command
|
||||||
|
/// > must be in separate values in the list, e.g. `["echo", "hello"]`
|
||||||
|
/// > and not `["echo hello"]`.
|
||||||
|
/// >
|
||||||
|
/// > Additionally, shell scripts must be run by your shell,
|
||||||
|
/// > e.g. `["sh", "script.sh"]` instead of `["script.sh"]` unless the script
|
||||||
|
/// > has a shebang.
|
||||||
|
///
|
||||||
|
/// > [!INFO] You can use `["sh", "-c", <your command>]` to execute your command with
|
||||||
|
/// > the system shell.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to @@Quickshell.Io.Process.startDetached().
|
||||||
|
Q_INVOKABLE static void execDetached(const ProcessContext& context);
|
||||||
|
|
||||||
/// Returns a string usable for a @@QtQuick.Image.source for a given system icon.
|
/// Returns a string usable for a @@QtQuick.Image.source for a given system icon.
|
||||||
///
|
///
|
||||||
/// > [!INFO] By default, icons are loaded from the theme selected by the qt platform theme,
|
/// > [!INFO] By default, icons are loaded from the theme selected by the qt platform theme,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
function (qs_test name)
|
function (qs_test name)
|
||||||
add_executable(${name} ${ARGN})
|
add_executable(${name} ${ARGN})
|
||||||
target_link_libraries(${name} PRIVATE Qt::Quick Qt::Test quickshell-core quickshell-window quickshell-ui)
|
target_link_libraries(${name} PRIVATE Qt::Quick Qt::Test quickshell-core quickshell-window quickshell-ui quickshell-io)
|
||||||
add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $<TARGET_FILE:${name}>)
|
add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $<TARGET_FILE:${name}>)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
qt_add_library(quickshell-io STATIC
|
qt_add_library(quickshell-io STATIC
|
||||||
datastream.cpp
|
datastream.cpp
|
||||||
|
processcore.cpp
|
||||||
process.cpp
|
process.cpp
|
||||||
fileview.cpp
|
fileview.cpp
|
||||||
jsonadapter.cpp
|
jsonadapter.cpp
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include <qdir.h>
|
#include <qdir.h>
|
||||||
|
#include <qhash.h>
|
||||||
#include <qlist.h>
|
#include <qlist.h>
|
||||||
#include <qlogging.h>
|
#include <qlogging.h>
|
||||||
#include <qmap.h>
|
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qprocess.h>
|
#include <qprocess.h>
|
||||||
#include <qqmlinfo.h>
|
#include <qqmlinfo.h>
|
||||||
|
@ -13,10 +13,10 @@
|
||||||
#include <qtypes.h>
|
#include <qtypes.h>
|
||||||
#include <qvariant.h>
|
#include <qvariant.h>
|
||||||
|
|
||||||
#include "../core/common.hpp"
|
|
||||||
#include "../core/generation.hpp"
|
#include "../core/generation.hpp"
|
||||||
#include "../core/qmlglobal.hpp"
|
#include "../core/qmlglobal.hpp"
|
||||||
#include "datastream.hpp"
|
#include "datastream.hpp"
|
||||||
|
#include "processcore.hpp"
|
||||||
|
|
||||||
Process::Process(QObject* parent): QObject(parent) {
|
Process::Process(QObject* parent): QObject(parent) {
|
||||||
QObject::connect(
|
QObject::connect(
|
||||||
|
@ -79,9 +79,10 @@ void Process::onGlobalWorkingDirectoryChanged() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QMap<QString, QVariant> Process::environment() const { return this->mEnvironment; }
|
QHash<QString, QVariant> Process::environment() const { return this->mEnvironment; }
|
||||||
|
|
||||||
void Process::setEnvironment(QMap<QString, QVariant> environment) {
|
void Process::setEnvironment(QHash<QString, QVariant> environment) {
|
||||||
|
qDebug() << "setEnv" << environment;
|
||||||
if (environment == this->mEnvironment) return;
|
if (environment == this->mEnvironment) return;
|
||||||
this->mEnvironment = std::move(environment);
|
this->mEnvironment = std::move(environment);
|
||||||
emit this->environmentChanged();
|
emit this->environmentChanged();
|
||||||
|
@ -224,24 +225,7 @@ void Process::setupEnvironment(QProcess* process) {
|
||||||
process->setWorkingDirectory(this->mWorkingDirectory);
|
process->setWorkingDirectory(this->mWorkingDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& sysenv = qs::Common::INITIAL_ENVIRONMENT;
|
qs::core::process::setupProcessEnvironment(process, this->mClearEnvironment, this->mEnvironment);
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
process->setProcessEnvironment(env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Process::onStarted() {
|
void Process::onStarted() {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <qcontainerfwd.h>
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qhash.h>
|
||||||
#include <qobject.h>
|
#include <qobject.h>
|
||||||
#include <qprocess.h>
|
#include <qprocess.h>
|
||||||
#include <qqmlintegration.h>
|
#include <qqmlintegration.h>
|
||||||
|
@ -98,7 +99,7 @@ class Process: public QObject {
|
||||||
/// 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
|
||||||
/// return the new value, not the one for the currently running process.
|
/// return the new value, not the one for the currently running process.
|
||||||
Q_PROPERTY(QMap<QString, QVariant> environment READ environment WRITE setEnvironment NOTIFY environmentChanged);
|
Q_PROPERTY(QHash<QString, QVariant> environment READ environment WRITE setEnvironment NOTIFY environmentChanged);
|
||||||
/// If the process's environment should be cleared prior to applying @@environment.
|
/// If the process's environment should be cleared prior to applying @@environment.
|
||||||
/// Defaults to false.
|
/// Defaults to false.
|
||||||
///
|
///
|
||||||
|
@ -140,10 +141,12 @@ public:
|
||||||
/// Writes to the process's stdin. Does nothing if @@running is false.
|
/// Writes to the process's stdin. Does nothing if @@running is false.
|
||||||
Q_INVOKABLE void write(const QString& data);
|
Q_INVOKABLE void write(const QString& data);
|
||||||
|
|
||||||
/// Launches an instance of the process detached from quickshell.
|
/// Launches an instance of the process detached from Quickshell.
|
||||||
///
|
///
|
||||||
/// The subprocess will not be tracked, @@running will be false,
|
/// The subprocess will not be tracked, @@running will be false,
|
||||||
/// and the subprocess will not be killed by Quickshell.
|
/// and the subprocess will not be killed by Quickshell.
|
||||||
|
///
|
||||||
|
/// This function is equivalent to @@Quickshell.Quickshell.execDetached().
|
||||||
Q_INVOKABLE void startDetached();
|
Q_INVOKABLE void startDetached();
|
||||||
|
|
||||||
[[nodiscard]] bool isRunning() const;
|
[[nodiscard]] bool isRunning() const;
|
||||||
|
@ -157,8 +160,8 @@ public:
|
||||||
[[nodiscard]] QString workingDirectory() const;
|
[[nodiscard]] QString workingDirectory() const;
|
||||||
void setWorkingDirectory(const QString& workingDirectory);
|
void setWorkingDirectory(const QString& workingDirectory);
|
||||||
|
|
||||||
[[nodiscard]] QMap<QString, QVariant> environment() const;
|
[[nodiscard]] QHash<QString, QVariant> environment() const;
|
||||||
void setEnvironment(QMap<QString, QVariant> environment);
|
void setEnvironment(QHash<QString, QVariant> environment);
|
||||||
|
|
||||||
[[nodiscard]] bool environmentCleared() const;
|
[[nodiscard]] bool environmentCleared() const;
|
||||||
void setEnvironmentCleared(bool cleared);
|
void setEnvironmentCleared(bool cleared);
|
||||||
|
@ -203,7 +206,7 @@ private:
|
||||||
QProcess* process = nullptr;
|
QProcess* process = nullptr;
|
||||||
QList<QString> mCommand;
|
QList<QString> mCommand;
|
||||||
QString mWorkingDirectory;
|
QString mWorkingDirectory;
|
||||||
QMap<QString, QVariant> mEnvironment;
|
QHash<QString, QVariant> mEnvironment;
|
||||||
DataStreamParser* mStdoutParser = nullptr;
|
DataStreamParser* mStdoutParser = nullptr;
|
||||||
DataStreamParser* mStderrParser = nullptr;
|
DataStreamParser* mStderrParser = nullptr;
|
||||||
QByteArray stdoutBuffer;
|
QByteArray stdoutBuffer;
|
||||||
|
|
37
src/io/processcore.cpp
Normal file
37
src/io/processcore.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include "processcore.hpp"
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qhash.h>
|
||||||
|
#include <qprocess.h>
|
||||||
|
#include <qvariant.h>
|
||||||
|
|
||||||
|
#include "../core/common.hpp"
|
||||||
|
|
||||||
|
namespace qs::core::process {
|
||||||
|
|
||||||
|
void setupProcessEnvironment(
|
||||||
|
QProcess* process,
|
||||||
|
bool clear,
|
||||||
|
const QHash<QString, QVariant>& envChanges
|
||||||
|
) {
|
||||||
|
const auto& sysenv = qs::Common::INITIAL_ENVIRONMENT;
|
||||||
|
auto env = clear ? QProcessEnvironment() : sysenv;
|
||||||
|
|
||||||
|
for (auto& name: envChanges.keys()) {
|
||||||
|
auto value = envChanges.value(name);
|
||||||
|
if (!value.isValid()) continue;
|
||||||
|
|
||||||
|
if (clear) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process->setProcessEnvironment(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace qs::core::process
|
16
src/io/processcore.hpp
Normal file
16
src/io/processcore.hpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <qcontainerfwd.h>
|
||||||
|
#include <qhash.h>
|
||||||
|
#include <qprocess.h>
|
||||||
|
#include <qvariant.h>
|
||||||
|
|
||||||
|
namespace qs::core::process {
|
||||||
|
|
||||||
|
void setupProcessEnvironment(
|
||||||
|
QProcess* process,
|
||||||
|
bool clear,
|
||||||
|
const QHash<QString, QVariant>& envChanges
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
function (qs_test name)
|
function (qs_test name)
|
||||||
add_executable(${name} ${ARGN})
|
add_executable(${name} ${ARGN})
|
||||||
target_link_libraries(${name} PRIVATE Qt::Quick Qt::Network Qt::Test)
|
target_link_libraries(${name} PRIVATE Qt::Quick Qt::Network Qt::Test quickshell-io)
|
||||||
add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $<TARGET_FILE:${name}>)
|
add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $<TARGET_FILE:${name}>)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
function (qs_test name)
|
function (qs_test name)
|
||||||
add_executable(${name} ${ARGN})
|
add_executable(${name} ${ARGN})
|
||||||
target_link_libraries(${name} PRIVATE Qt::Quick Qt::Test quickshell-window quickshell-core quickshell-ui)
|
target_link_libraries(${name} PRIVATE Qt::Quick Qt::Test quickshell-window quickshell-core quickshell-ui quickshell-io)
|
||||||
add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $<TARGET_FILE:${name}>)
|
add_test(NAME ${name} WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" COMMAND $<TARGET_FILE:${name}>)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue