#include "nativestreamer.h" #include #include QString NativeStreamer::ollie9pBin() { return QStandardPaths::findExecutable("ollie-9p"); } QString NativeStreamer::serverAddr() { QString ns = qEnvironmentVariable("NAMESPACE"); if (ns.isEmpty()) { // Plan 9 default: /tmp/ns.$USER.$DISPLAY QString user = qEnvironmentVariable("USER"); QString display = qEnvironmentVariable("DISPLAY"); ns = "/tmp/ns." + user + "." + display; } return "unix!" + ns + "/ollie"; } NativeStreamer::NativeStreamer(RestartPolicy policy, QObject *parent) : QObject(parent) , m_policy(policy) { m_restartTimer = new QTimer(this); m_restartTimer->setSingleShot(true); m_restartTimer->setInterval(500); // 500ms delay before restart connect(m_restartTimer, &QTimer::timeout, this, [this]() { if (!m_stopping && !m_path.isEmpty()) { start(m_path); } }); } NativeStreamer::~NativeStreamer() { stop(); } void NativeStreamer::start(const QString &path) { if (m_process) { stop(); } m_path = path; m_stopping = false; m_process = new QProcess(this); connect(m_process, &QProcess::readyReadStandardOutput, this, &NativeStreamer::onReadyRead); connect(m_process, QOverload::of(&QProcess::finished), this, &NativeStreamer::onProcessFinished); m_process->start(ollie9pBin(), {"-a", serverAddr(), "read", path}); } void NativeStreamer::stop() { m_stopping = true; m_restartTimer->stop(); if (m_process) { disconnect(m_process, nullptr, this, nullptr); if (m_process->state() != QProcess::NotRunning) { m_process->terminate(); // SIGTERM first if (!m_process->waitForFinished(200)) { m_process->kill(); // SIGKILL if still alive m_process->waitForFinished(100); } } m_process->deleteLater(); m_process = nullptr; } } bool NativeStreamer::isRunning() const { return m_process && m_process->state() == QProcess::Running; } void NativeStreamer::setGuard(std::function guard) { m_guard = guard; } void NativeStreamer::onReadyRead() { if (m_process) { QByteArray data = m_process->readAllStandardOutput(); if (!data.isEmpty()) { emit dataReady(data); } } } void NativeStreamer::onProcessFinished(int exitCode, QProcess::ExitStatus status) { Q_UNUSED(exitCode) Q_UNUSED(status) if (m_process) { m_process->deleteLater(); m_process = nullptr; } scheduleRestart(); } void NativeStreamer::scheduleRestart() { if (m_stopping) { return; } bool shouldRestart = false; switch (m_policy) { case Oneshot: shouldRestart = false; break; case Looping: shouldRestart = true; break; case Guarded: shouldRestart = m_guard && m_guard(); break; } if (shouldRestart) { m_restartTimer->start(); } else { emit finished(); } }