This repository has been archived on 2026-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
ollie-kde/plasmoid/plugin/olliedbusclient.cpp

285 lines
8.5 KiB
C++

#include "olliedbusclient.h"
#include <QDBusReply>
#include <QDBusConnection>
#include <QDBusServiceWatcher>
#include <QDir>
static const char *SERVICE = "org.ollie.SessionManager";
static const char *OBJ_PATH = "/org/ollie/SessionManager";
static const char *IFACE = "org.ollie.SessionManager";
OllieDBusClient::OllieDBusClient(QObject *parent)
: QObject(parent)
{
m_iface = new QDBusInterface(SERVICE, OBJ_PATH, IFACE,
QDBusConnection::sessionBus(), this);
// Watch for service appearing/disappearing
auto *watcher = new QDBusServiceWatcher(SERVICE,
QDBusConnection::sessionBus(),
QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration,
this);
connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, [this]() {
refreshSessions();
});
connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, [this]() {
m_sessions.clear();
m_activeSessionId.clear();
m_chatText.clear();
m_chatOffset = 0;
m_activeState = "idle";
m_activeCwd.clear();
emit sessionsChanged();
emit activeSessionIdChanged();
emit chatTextChanged();
emit activeStateChanged();
emit activeCwdChanged();
});
// Connect D-Bus signals
QDBusConnection bus = QDBusConnection::sessionBus();
bus.connect(SERVICE, OBJ_PATH, IFACE, "SessionCreated",
this, SLOT(onSessionCreated(QString)));
bus.connect(SERVICE, OBJ_PATH, IFACE, "SessionKilled",
this, SLOT(onSessionKilled(QString)));
bus.connect(SERVICE, OBJ_PATH, IFACE, "StateChanged",
this, SLOT(onStateChanged(QString,QString)));
bus.connect(SERVICE, OBJ_PATH, IFACE, "ChatUpdated",
this, SLOT(onChatUpdated(QString,qlonglong,QString)));
refreshSessions();
}
void OllieDBusClient::setActiveSessionId(const QString &id)
{
if (m_activeSessionId == id) return;
m_activeSessionId = id;
m_chatText.clear();
m_chatOffset = 0;
emit activeSessionIdChanged();
emit chatTextChanged();
fetchChat();
fetchConfig();
}
void OllieDBusClient::refreshSessions()
{
QDBusReply<QStringList> reply = m_iface->call("ListSessions");
if (!reply.isValid()) return;
m_sessions.clear();
for (const QString &entry : reply.value()) {
QStringList parts = entry.split('\t');
if (parts.size() >= 4) {
QVariantMap session;
session["id"] = parts[0];
session["state"] = parts[1];
session["model"] = parts[2];
session["agent"] = parts[3];
m_sessions.append(session);
}
}
emit sessionsChanged();
// Update active state
for (const QVariant &v : m_sessions) {
QVariantMap s = v.toMap();
if (s["id"].toString() == m_activeSessionId) {
QString newState = s["state"].toString();
if (newState != m_activeState) {
m_activeState = newState;
emit activeStateChanged();
}
return;
}
}
// Active session no longer exists
if (!m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) {
setActiveSessionId(m_sessions.first().toMap()["id"].toString());
} else if (m_activeSessionId.isEmpty() && !m_sessions.isEmpty()) {
setActiveSessionId(m_sessions.first().toMap()["id"].toString());
} else if (m_sessions.isEmpty() && !m_activeSessionId.isEmpty()) {
m_activeSessionId.clear();
m_activeState = "idle";
m_chatText.clear();
m_chatOffset = 0;
m_activeCwd.clear();
emit activeSessionIdChanged();
emit activeStateChanged();
emit chatTextChanged();
emit activeCwdChanged();
}
}
void OllieDBusClient::fetchChat()
{
if (m_activeSessionId.isEmpty()) return;
QDBusMessage msg = QDBusMessage::createMethodCall(SERVICE, OBJ_PATH, IFACE, "GetChat");
msg << m_activeSessionId << m_chatOffset;
QDBusMessage reply = QDBusConnection::sessionBus().call(msg);
if (reply.type() != QDBusMessage::ReplyMessage) return;
QList<QVariant> args = reply.arguments();
if (args.size() < 2) return;
QString text = args[0].toString();
qlonglong newOffset = args[1].toLongLong();
if (!text.isEmpty()) {
if (m_chatOffset == 0) {
m_chatText = text;
} else {
m_chatText += text;
}
m_chatOffset = newOffset;
emit chatTextChanged();
}
}
void OllieDBusClient::fetchConfig()
{
if (m_activeSessionId.isEmpty()) {
if (!m_activeCwd.isEmpty()) {
m_activeCwd.clear();
emit activeCwdChanged();
}
return;
}
QDBusMessage msg = QDBusMessage::createMethodCall(SERVICE, OBJ_PATH, IFACE, "GetConfig");
msg << m_activeSessionId;
QDBusMessage reply = QDBusConnection::sessionBus().call(msg);
if (reply.type() != QDBusMessage::ReplyMessage) return;
QList<QVariant> args = reply.arguments();
if (args.isEmpty()) return;
QString config = args[0].toString();
QString newCwd;
const QStringList lines = config.split('\n', Qt::SkipEmptyParts);
for (const QString &line : lines) {
if (line.startsWith(QLatin1String("cwd="))) {
newCwd = line.mid(4);
break;
}
}
if (newCwd != m_activeCwd) {
m_activeCwd = newCwd;
emit activeCwdChanged();
}
}
QString OllieDBusClient::createSession(const QString &cwd)
{
QString dir = cwd.isEmpty() ? QDir::homePath() : cwd;
QDBusReply<QString> reply = m_iface->call("CreateSession", dir, QString(), QString(), "default");
if (reply.isValid()) {
QString id = reply.value();
refreshSessions();
setActiveSessionId(id);
return id;
}
return QString();
}
bool OllieDBusClient::killSession(const QString &sessionId)
{
QDBusReply<bool> reply = m_iface->call("KillSession", sessionId);
if (reply.isValid() && reply.value()) {
if (sessionId == m_activeSessionId) {
m_activeSessionId.clear();
m_chatText.clear();
m_chatOffset = 0;
m_activeState = "idle";
m_activeCwd.clear();
emit activeSessionIdChanged();
emit chatTextChanged();
emit activeStateChanged();
emit activeCwdChanged();
}
refreshSessions();
return true;
}
return false;
}
bool OllieDBusClient::renameSession(const QString &sessionId, const QString &newName)
{
QDBusReply<bool> reply = m_iface->call("RenameSession", sessionId, newName);
if (reply.isValid() && reply.value()) {
if (sessionId == m_activeSessionId) {
m_activeSessionId = newName;
emit activeSessionIdChanged();
}
refreshSessions();
return true;
}
return false;
}
bool OllieDBusClient::setConfig(const QString &sessionId, const QString &key, const QString &value)
{
QDBusReply<bool> reply = m_iface->call("SetConfig", sessionId, key, value);
if (reply.isValid() && reply.value()) {
if (sessionId == m_activeSessionId && key == "cwd") {
m_activeCwd = value;
emit activeCwdChanged();
}
return true;
}
return false;
}
bool OllieDBusClient::submit(const QString &sessionId, const QString &prompt)
{
QDBusReply<bool> reply = m_iface->call("Submit", sessionId, prompt);
return reply.isValid() && reply.value();
}
bool OllieDBusClient::interrupt(const QString &sessionId)
{
QDBusReply<bool> reply = m_iface->call("Interrupt", sessionId);
return reply.isValid() && reply.value();
}
// --- D-Bus signal handlers ---
void OllieDBusClient::onSessionCreated(const QString &session_id)
{
Q_UNUSED(session_id);
refreshSessions();
}
void OllieDBusClient::onSessionKilled(const QString &session_id)
{
Q_UNUSED(session_id);
refreshSessions();
}
void OllieDBusClient::onStateChanged(const QString &session_id, const QString &new_state)
{
if (session_id == m_activeSessionId) {
m_activeState = new_state;
emit activeStateChanged();
}
refreshSessions();
}
void OllieDBusClient::onChatUpdated(const QString &session_id, qlonglong offset, const QString &new_text)
{
if (session_id != m_activeSessionId) return;
// Append directly from the signal — no need to poll
if (offset == m_chatOffset) {
m_chatText += new_text;
m_chatOffset += new_text.size();
emit chatTextChanged();
} else {
// Offset mismatch — do a full fetch
fetchChat();
}
}