summaryrefslogtreecommitdiffstats
path: root/wayland/democompositor/processlauncher.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'wayland/democompositor/processlauncher.cpp')
-rw-r--r--wayland/democompositor/processlauncher.cpp124
1 files changed, 117 insertions, 7 deletions
diff --git a/wayland/democompositor/processlauncher.cpp b/wayland/democompositor/processlauncher.cpp
index a25fbd5..29e4d64 100644
--- a/wayland/democompositor/processlauncher.cpp
+++ b/wayland/democompositor/processlauncher.cpp
@@ -49,8 +49,22 @@
****************************************************************************/
#include "processlauncher.h"
+#include "apps/appentry.h"
#include <QProcess>
+#include <QTimer>
+
+Q_LOGGING_CATEGORY(procs, "launcher.procs")
+
+/*
+ * Two AppState's are equal if they are managing the same
+ * QProcess. It is assumed that no AppState survives beyond
+ * the QProcess.
+ */
+bool operator==(const AppState& lhs, const AppState& rhs)
+{
+ return lhs.process == rhs.process;
+}
WaylandProcessLauncher::WaylandProcessLauncher(QObject *parent)
: QObject(parent)
@@ -59,18 +73,114 @@ WaylandProcessLauncher::WaylandProcessLauncher(QObject *parent)
WaylandProcessLauncher::~WaylandProcessLauncher()
{
+ for (auto state : m_appStates) {
+ state.process->disconnect(state.finishedConn);
+ state.process->disconnect(state.errorOccurredConn);
+ state.process->disconnect(state.startedConn);
+ delete state.process;
+ }
+ m_appStates.clear();
+}
+
+QVariant WaylandProcessLauncher::appStateForPid(int pid) const
+{
+ for (auto state : m_appStates) {
+ if (state.process->pid() == pid) {
+ qCDebug(procs) << "Found state for" << pid << state.appEntry.executableName;
+ return QVariant::fromValue(state);
+ }
+ }
+
+ qCDebug(procs) << "Couldn't find entry for" << pid;
+ return QVariant();
+}
+
+bool WaylandProcessLauncher::isRunning(const AppEntry& entry) const
+{
+ for (auto state : m_appStates) {
+ if (state.appEntry.sourceFileName == entry.sourceFileName) {
+ qCDebug(procs) << "AppEntry associated to a state" << entry.executableName;
+ return true;
+ }
+ }
+
+ qCDebug(procs) << "AppEntry not associated to a state " << entry.executableName;
+ return false;
+}
+
+void WaylandProcessLauncher::kill(const AppEntry& entry)
+{
+ for (auto state : m_appStates) {
+ if (state.appEntry.sourceFileName != entry.sourceFileName)
+ continue;
+
+ qCDebug(procs) << "Killing process " << state.process->pid() << " for " << entry.sourceFileName;
+ state.process->kill();
+ }
}
-void WaylandProcessLauncher::launch(const QString &program)
+void WaylandProcessLauncher::stop(const AppEntry& entry, int ms)
{
+ for (auto state : m_appStates) {
+ if (state.appEntry.sourceFileName != entry.sourceFileName)
+ continue;
+
+ auto timer = new QTimer(state.process);
+ connect(timer, &QTimer::timeout, [entry, state, timer] {
+ qCDebug(procs) << "Sending SIGKILL " << state.process->pid() << " for " << entry.sourceFileName;
+ timer->deleteLater();
+ state.process->kill();
+ });
+ timer->start(ms);
+ qCDebug(procs) << "Sending SIGTERM " << state.process->pid() << " for " << entry.sourceFileName;
+ state.process->terminate();
+ }
+}
+
+void WaylandProcessLauncher::launch(const AppEntry &entry)
+{
+ qCDebug(procs) << "Launching" << entry.executableName;
+
QProcess *process = new QProcess(this);
- connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
- process, &QProcess::deleteLater);
- connect(process, &QProcess::errorOccurred, &QProcess::deleteLater);
+ process->setProcessChannelMode(QProcess::ForwardedChannels);
+
+ QMetaObject::Connection conn;
+ AppState state{process, entry, conn, conn, conn};
+ m_appStates.push_back(state);
+
+ /* handle potential errors and life cycle */
+ state.finishedConn = connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
+ [state, this](int exitCode, QProcess::ExitStatus status) {
+ qCDebug(procs) << "AppEntry finished" << state.appEntry.executableName << exitCode << status;
+ emit appFinished(state, exitCode, status);
+ m_appStates.removeOne(state);
+ state.process->deleteLater();
+ });
+ state.errorOccurredConn = connect(process, &QProcess::errorOccurred,
+ [state, this](QProcess::ProcessError err) {
+ qCDebug(procs) << "AppEntry error occurred" << state.appEntry.executableName << err;
+
+ /* Maybe finished was already emitted. Let's not emit an error after that */
+ if (!m_appStates.removeOne(state))
+ return;
+
+ if (err == QProcess::FailedToStart || err == QProcess::UnknownError)
+ emit appNotStarted(state);
+ state.process->deleteLater();
+ });
+ state.startedConn = connect(process, &QProcess::started,
+ [state, this]() {
+ qCDebug(procs) << "AppEntry started" << state.appEntry.executableName;
+ emit appStarted(state);
+ });
+
+ if (!entry.executablePath.isNull()) {
+ auto env = QProcessEnvironment::systemEnvironment();
+ env.insert(QStringLiteral("PATH"), entry.executablePath);
+ process->setProcessEnvironment(env);
+ }
QStringList arguments;
arguments << "-platform" << "wayland";
- process->start(program, arguments);
-
+ process->start(entry.executableName, arguments);
}
-