diff options
Diffstat (limited to 'wayland/democompositor/processlauncher.cpp')
-rw-r--r-- | wayland/democompositor/processlauncher.cpp | 124 |
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); } - |