135 lines
3.8 KiB
C++
135 lines
3.8 KiB
C++
#include "ollie_tray.h"
|
|
|
|
#include <QApplication>
|
|
#include <QDir>
|
|
#include <QStandardPaths>
|
|
|
|
static QString ninepBin()
|
|
{
|
|
QString bin = QStandardPaths::findExecutable("9p");
|
|
if (!bin.isEmpty()) return bin;
|
|
QString plan9 = qEnvironmentVariable("PLAN9");
|
|
if (!plan9.isEmpty() && QFileInfo::exists(plan9 + "/bin/9p"))
|
|
return plan9 + "/bin/9p";
|
|
return "9p";
|
|
}
|
|
|
|
OllieTray::OllieTray(QObject *parent)
|
|
: QObject(parent)
|
|
{
|
|
m_sni = new KStatusNotifierItem("ollie-kde", this);
|
|
m_sni->setCategory(KStatusNotifierItem::ApplicationStatus);
|
|
m_sni->setTitle("Ollie");
|
|
m_sni->setIconByName("system-run");
|
|
m_sni->setStatus(KStatusNotifierItem::Active);
|
|
|
|
connect(m_sni, &KStatusNotifierItem::activateRequested, this, &OllieTray::openGui);
|
|
|
|
setupMenu();
|
|
refreshSessions();
|
|
|
|
// Poll session state every 5 seconds (lightweight — single 9p read)
|
|
m_pollTimer = new QTimer(this);
|
|
connect(m_pollTimer, &QTimer::timeout, this, &OllieTray::refreshSessions);
|
|
m_pollTimer->start(5000);
|
|
}
|
|
|
|
OllieTray::~OllieTray()
|
|
{
|
|
}
|
|
|
|
QString OllieTray::serverAddr()
|
|
{
|
|
QString ns = qEnvironmentVariable("NAMESPACE");
|
|
if (ns.isEmpty()) {
|
|
QString user = qEnvironmentVariable("USER");
|
|
QString display = qEnvironmentVariable("DISPLAY");
|
|
ns = "/tmp/ns." + user + "." + display;
|
|
}
|
|
return "unix!" + ns + "/ollie";
|
|
}
|
|
|
|
QByteArray OllieTray::run9p(const QStringList &args)
|
|
{
|
|
QProcess proc;
|
|
QStringList fullArgs = {"-a", serverAddr()};
|
|
fullArgs.append(args);
|
|
proc.start(ninepBin(), fullArgs);
|
|
if (!proc.waitForFinished(5000)) return {};
|
|
return proc.readAllStandardOutput();
|
|
}
|
|
|
|
void OllieTray::write9p(const QString &path, const QByteArray &data)
|
|
{
|
|
QProcess proc;
|
|
proc.start(ninepBin(), {"-a", serverAddr(), "write", path});
|
|
proc.waitForStarted(3000);
|
|
proc.write(data);
|
|
proc.closeWriteChannel();
|
|
proc.waitForFinished(3000);
|
|
}
|
|
|
|
void OllieTray::setupMenu()
|
|
{
|
|
m_menu = new QMenu();
|
|
m_sessionsMenu = m_menu->addMenu("Sessions");
|
|
m_menu->addSeparator();
|
|
m_menu->addAction("Open GUI", this, &OllieTray::openGui);
|
|
m_menu->addAction("Stop All", this, &OllieTray::stopAll);
|
|
m_menu->addSeparator();
|
|
m_menu->addAction("Quit", qApp, &QApplication::quit);
|
|
m_sni->setContextMenu(m_menu);
|
|
}
|
|
|
|
void OllieTray::refreshSessions()
|
|
{
|
|
QByteArray out = run9p({"read", "session/idx"});
|
|
QString raw = QString::fromUtf8(out).trimmed();
|
|
|
|
m_sessionsMenu->clear();
|
|
|
|
if (raw.isEmpty()) {
|
|
m_sessionsMenu->addAction("(no sessions)")->setEnabled(false);
|
|
m_sni->setToolTip("system-run", "Ollie", "No active sessions");
|
|
return;
|
|
}
|
|
|
|
QStringList lines = raw.split('\n');
|
|
int active = 0;
|
|
for (const QString &line : lines) {
|
|
QStringList parts = line.split('\t');
|
|
if (parts.size() < 2) continue;
|
|
|
|
QString id = parts[0];
|
|
QString state = parts[1];
|
|
if (state != "idle") active++;
|
|
|
|
QString label = (state == "idle" ? "○ " : "● ") + id;
|
|
m_sessionsMenu->addAction(label);
|
|
}
|
|
|
|
m_sni->setToolTip("system-run", "Ollie",
|
|
QString("%1 sessions, %2 active").arg(lines.size()).arg(active));
|
|
}
|
|
|
|
void OllieTray::openGui()
|
|
{
|
|
QProcess::startDetached("ollie-gui", {});
|
|
}
|
|
|
|
void OllieTray::stopAll()
|
|
{
|
|
QByteArray out = run9p({"read", "session/idx"});
|
|
QString raw = QString::fromUtf8(out).trimmed();
|
|
for (const QString &line : raw.split('\n')) {
|
|
QStringList parts = line.split('\t');
|
|
if (parts.size() < 2 || parts[1] == "idle") continue;
|
|
QString id = parts[0];
|
|
// Find agent, send stop
|
|
QByteArray agentOut = run9p({"ls", "session/" + id + "/agent"});
|
|
QString agentId = QString::fromUtf8(agentOut).trimmed().split('\n').value(0);
|
|
if (!agentId.isEmpty())
|
|
write9p("session/" + id + "/agent/" + agentId + "/ctl", "stop");
|
|
}
|
|
}
|