Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ set(libinputactions_SRCS
libinputactions/scripting/modules/fs/FSModule.cpp
libinputactions/scripting/modules/main/MainModule.cpp
libinputactions/scripting/modules/main/ModuleScript.cpp
libinputactions/scripting/modules/os/Environment.cpp
libinputactions/scripting/modules/os/OSModule.cpp
libinputactions/scripting/modules/os/Process.cpp
libinputactions/scripting/modules/main/Timer.cpp
libinputactions/scripting/modules/Module.cpp
libinputactions/scripting/JSFunctionAction.cpp
Expand Down
10 changes: 10 additions & 0 deletions src/libinputactions/helpers/QProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,14 @@ QString commandOutput(const QString &command, const CommandOutputArguments &args
return processOutput("/bin/sh", {"-c", command}, extraEnvironment);
}

const QProcessEnvironment &cachedSystemEnvironment()
{
static std::optional<QProcessEnvironment> cache;
if (!cache.has_value()) {
cache = QProcessEnvironment::systemEnvironment();
}

return cache.value();
}

}
3 changes: 3 additions & 0 deletions src/libinputactions/helpers/QProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#pragma once

#include <QProcessEnvironment>
#include <QString>

namespace InputActions::QProcessHelpers
Expand Down Expand Up @@ -50,4 +51,6 @@ void command(const QString &command, const CommandArguments &args = {});
*/
QString commandOutput(const QString &command, const CommandOutputArguments &args = {});

const QProcessEnvironment &cachedSystemEnvironment();

}
14 changes: 14 additions & 0 deletions src/libinputactions/scripting/ScriptingEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "modules/desktop/generic/DesktopGenericModule.h"
#include "modules/fs/FSModule.h"
#include "modules/main/MainModule.h"
#include "modules/os/OSModule.h"
#include <libinputactions/InputActionsMain.h>
#include <libinputactions/globals.h>
#include <libinputactions/helpers/QString.h>
Expand Down Expand Up @@ -60,6 +61,18 @@ ScriptingEngine::~ScriptingEngine()

void ScriptingEngine::initialize()
{
static bool registeredConverters{};
if (!registeredConverters) {
QMetaType::registerConverter<QVariantMap, std::map<QString, QString>>([](const QVariantMap &map) {
std::map<QString, QString> result;
for (auto it = map.cbegin(); it != map.cend(); ++it) {
result[it.key()] = it->value<QString>();
}
return result;
});
registeredConverters = true;
}

m_engine.installExtensions(QJSEngine::ConsoleExtension);

initializeWatchdog();
Expand All @@ -71,6 +84,7 @@ void ScriptingEngine::initialize()
registerBuiltinModule("inputactions", new MainModule(*this));
registerBuiltinModule("inputactions/desktop/generic", new DesktopGenericModule(*this));
registerBuiltinModule("inputactions/fs", new FSModule(*this));
registerBuiltinModule("inputactions/os", new OSModule(*this));

// TODO Maybe perform the unhandled promise check after garbage collection if possible
const auto initFunc = evaluate(R"(
Expand Down
19 changes: 19 additions & 0 deletions src/libinputactions/scripting/ScriptingEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,25 @@ class ScriptingEngine : public QObject
return instanceMetaObject;
}

/**
* Converts a JS object to a default-constructible gadget instance with optional properties.
*/
template<typename T>
T objectToGadget(const QJSValue &object)
{
T result;

const QMetaObject &metaObject = T::staticMetaObject;
for (qsizetype i = 0; i < metaObject.propertyCount(); i++) {
const auto metaProperty = metaObject.property(i);
if (object.hasOwnProperty(metaProperty.name())) {
metaProperty.writeOnGadget(&result, object.property(metaProperty.name()).toVariant());
}
}

return result;
}

Promise newPromise();

template<typename T>
Expand Down
39 changes: 39 additions & 0 deletions src/libinputactions/scripting/modules/os/Environment.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 2024-2026 Marcin Woźniak

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include "Environment.h"
#include <libinputactions/helpers/QProcess.h>

namespace InputActions
{

bool Environment::isSet(const QString &variable) const
{
return QProcessHelpers::cachedSystemEnvironment().contains(variable);
}

QJSValue Environment::get(const QString &variable) const
{
if (!isSet(variable)) {
return QJSValue::NullValue;
}

return QProcessHelpers::cachedSystemEnvironment().value(variable);
}

}
36 changes: 36 additions & 0 deletions src/libinputactions/scripting/modules/os/Environment.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 2024-2026 Marcin Woźniak

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include <QJSValue>
#include <QObject>

namespace InputActions
{

class Environment : public QObject
{
Q_OBJECT

public:
Q_INVOKABLE bool isSet(const QString &variable) const;
Q_INVOKABLE QJSValue get(const QString &variable) const;
};

}
38 changes: 38 additions & 0 deletions src/libinputactions/scripting/modules/os/OSModule.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 2024-2026 Marcin Woźniak

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include "OSModule.h"
#include "Process.h"
#include <libinputactions/scripting/ScriptingEngine.h>

namespace InputActions
{

OSModule::OSModule(ScriptingEngine &engine)
: Module(engine)
{
QJSEngine::setObjectOwnership(&m_environment, QJSEngine::CppOwnership);
}

void OSModule::initialize(QJSValue &self)
{
self.setProperty("environment", engine().qtEngine().newQMetaObject(&Environment::staticMetaObject));
self.setProperty("Process", engine().newQMetaObject<Process, ProcessStatic>());
}

}
44 changes: 44 additions & 0 deletions src/libinputactions/scripting/modules/os/OSModule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 2024-2026 Marcin Woźniak

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

#include "Environment.h"
#include <libinputactions/scripting/modules/Module.h>

namespace InputActions
{

class OSModule : public Module
{
Q_OBJECT

Q_PROPERTY(Environment *environment READ environment)

public:
OSModule(ScriptingEngine &engine);

Environment *environment() { return &m_environment; }

void initialize(QJSValue &self) override;

private:
Environment m_environment;
};

}
99 changes: 99 additions & 0 deletions src/libinputactions/scripting/modules/os/Process.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
Input Actions - Input handler that executes user-defined actions
Copyright (C) 2024-2026 Marcin Woźniak

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include "Process.h"
#include <QProcess>
#include <libinputactions/helpers/QProcess.h>
#include <libinputactions/scripting/Promise.h>
#include <libinputactions/scripting/ScriptingEngine.h>

namespace InputActions
{

QJSValue ProcessStatic::run(const QString &program, const QJSValue &argumentsObject)
{
auto *engine = ScriptingEngine::engineForObject(this);
auto error = engine->qtEngine().newErrorObject(QJSValue::GenericError);

const auto arguments = g_scriptingEngine->objectToGadget<ProcessRunArguments>(argumentsObject);
auto promise = engine->newPromise();
auto *process = new QProcess;
connect(process, &QProcess::errorOccurred, this, [promise, process, error](const auto processError) mutable {
if (processError == QProcess::ProcessError::FailedToStart) {
error.setProperty("message", QString("Failed to start process: %1.").arg(process->errorString()));
process->deleteLater();
promise.reject(error);
}
});
connect(process, &QProcess::finished, this, [promise, process, arguments](const auto exitCode) {
auto *finishedProcess = new FinishedProcess(exitCode);
QJSEngine::setObjectOwnership(finishedProcess, QJSEngine::JavaScriptOwnership);

if (arguments.captureOutput()) {
if (arguments.mergeOutput()) {
finishedProcess->setStandardOutput(process->readAll());
} else {
finishedProcess->setStandardOutput(process->readAllStandardOutput());
finishedProcess->setStandardError(process->readAllStandardError());
}
}

promise.fulfill(finishedProcess);
process->deleteLater();
});

process->setProgram(program);
process->setArguments(arguments.arguments());
if (arguments.captureOutput() && arguments.mergeOutput()) {
process->setProcessChannelMode(QProcess::MergedChannels);
}
if (const auto &workingDirectory = arguments.workingDirectory()) {
process->setWorkingDirectory(workingDirectory.value());
}

if (const auto &extraEnvironment = arguments.extraEnvironment(); !extraEnvironment.empty()) {
auto environment = QProcessHelpers::cachedSystemEnvironment();
for (const auto &[key, value] : extraEnvironment) {
environment.insert(key, value);
}
process->setProcessEnvironment(environment);
}

process->start();
return promise.promise();
}

FinishedProcess::FinishedProcess(int exitCode)
: m_exitCode(exitCode)
{
}

void ProcessRunArguments::setWorkingDirectory(QString value)
{
if (value.isEmpty()) {
return;
}
m_workingDirectory = std::move(value);
}

QString ProcessRunArguments::_workingDirectory() const
{
return m_workingDirectory.value_or("");
}

}
Loading
Loading