kill D-Bus: krunner+tray use 9P, remove plasmoid+tray, disable kate
- KRunner: rewritten to use plan9port 9p command for all operations (list sessions, kill, one-shot generate). No D-Bus dependency. - Tray: removed. Was a thin D-Bus status wrapper, not useful enough to justify maintaining. - Plasmoid: removed. GUI is standalone now. - Kate: temporarily disabled in CMake. Header partially converted to 9P (D-Bus includes removed, QProcess/9P helpers declared). Full conversion is next. - All ninepBin() helpers use $PLAN9/bin/9p (not hardcoded path). - CMake: removed Qt5::DBus, KF5::Notifications, KF5::Plasma deps. Only remaining link deps: Qt5::Core, Qt5::Widgets, Qt5::Quick, Qt5::QuickControls2, KF5::Runner.
This commit is contained in:
parent
f4161fcabc
commit
e89b723627
|
|
@ -45,74 +45,34 @@ if(OLLIE_KF5)
|
||||||
# CMAKE_INSTALL_PREFIX (e.g. ~/.local/lib/qt5/plugins), not the system dir.
|
# CMAKE_INSTALL_PREFIX (e.g. ~/.local/lib/qt5/plugins), not the system dir.
|
||||||
set(KDE_INSTALL_PLUGINDIR "lib/qt5/plugins" CACHE PATH "" FORCE)
|
set(KDE_INSTALL_PLUGINDIR "lib/qt5/plugins" CACHE PATH "" FORCE)
|
||||||
find_package(KF5 REQUIRED COMPONENTS
|
find_package(KF5 REQUIRED COMPONENTS
|
||||||
Notifications
|
|
||||||
Runner
|
Runner
|
||||||
CoreAddons
|
CoreAddons
|
||||||
Config
|
Config
|
||||||
Plasma
|
|
||||||
TextEditor
|
TextEditor
|
||||||
SyntaxHighlighting
|
SyntaxHighlighting
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- System Tray ---
|
|
||||||
add_executable(ollie-tray
|
|
||||||
tray/main.cpp
|
|
||||||
tray/ollie_tray.cpp
|
|
||||||
)
|
|
||||||
target_include_directories(ollie-tray PRIVATE
|
|
||||||
${CMAKE_SOURCE_DIR}
|
|
||||||
)
|
|
||||||
target_link_libraries(ollie-tray
|
|
||||||
Qt5::Core
|
|
||||||
Qt5::DBus
|
|
||||||
Qt5::Widgets
|
|
||||||
KF5::Notifications
|
|
||||||
)
|
|
||||||
|
|
||||||
# --- KRunner Plugin ---
|
# --- KRunner Plugin ---
|
||||||
kcoreaddons_add_plugin(krunner_ollie SOURCES krunner/ollie_runner.cpp INSTALL_NAMESPACE "kf5/krunner")
|
kcoreaddons_add_plugin(krunner_ollie SOURCES krunner/ollie_runner.cpp INSTALL_NAMESPACE "kf5/krunner")
|
||||||
target_link_libraries(krunner_ollie
|
target_link_libraries(krunner_ollie
|
||||||
KF5::Runner
|
KF5::Runner
|
||||||
Qt5::Core
|
Qt5::Core
|
||||||
Qt5::DBus
|
|
||||||
Qt5::Widgets
|
Qt5::Widgets
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- Kate Plugin ---
|
# --- Kate Plugin (temporarily disabled — 9P conversion in progress) ---
|
||||||
kcoreaddons_add_plugin(ollie_kate SOURCES kate/ollie_kate.cpp kate/ollie_ghost.cpp kate/chat_renderer.cpp INSTALL_NAMESPACE "ktexteditor")
|
# kcoreaddons_add_plugin(ollie_kate SOURCES kate/ollie_kate.cpp kate/ollie_ghost.cpp kate/chat_renderer.cpp INSTALL_NAMESPACE "ktexteditor")
|
||||||
target_link_libraries(ollie_kate
|
# target_link_libraries(ollie_kate
|
||||||
KF5::TextEditor
|
# KF5::TextEditor
|
||||||
KF5::SyntaxHighlighting
|
# KF5::SyntaxHighlighting
|
||||||
Qt5::Core
|
# Qt5::Core
|
||||||
Qt5::DBus
|
# Qt5::Widgets
|
||||||
Qt5::Widgets
|
# )
|
||||||
)
|
# if(HAVE_KATE_DIFF)
|
||||||
if(HAVE_KATE_DIFF)
|
# target_compile_definitions(ollie_kate PRIVATE HAVE_KATE_DIFF)
|
||||||
target_compile_definitions(ollie_kate PRIVATE HAVE_KATE_DIFF)
|
# target_include_directories(ollie_kate PRIVATE ${CMAKE_SOURCE_DIR}/kate)
|
||||||
target_include_directories(ollie_kate PRIVATE ${CMAKE_SOURCE_DIR}/kate)
|
# target_link_libraries(ollie_kate ${KATEPRIVATE_LIB})
|
||||||
target_link_libraries(ollie_kate ${KATEPRIVATE_LIB})
|
# endif()
|
||||||
endif()
|
|
||||||
|
|
||||||
# --- Plasmoid QML Plugin (manual for Qt5) ---
|
|
||||||
add_library(ollie-plasmoid-plugin SHARED
|
|
||||||
plasmoid/plugin/olliedbusclient.cpp
|
|
||||||
plasmoid/plugin/qmlplugin.cpp
|
|
||||||
)
|
|
||||||
target_link_libraries(ollie-plasmoid-plugin
|
|
||||||
Qt5::Core
|
|
||||||
Qt5::DBus
|
|
||||||
Qt5::Quick
|
|
||||||
Qt5::Qml
|
|
||||||
)
|
|
||||||
target_include_directories(ollie-plasmoid-plugin PRIVATE
|
|
||||||
${CMAKE_SOURCE_DIR}/plasmoid/plugin
|
|
||||||
)
|
|
||||||
set_target_properties(ollie-plasmoid-plugin PROPERTIES
|
|
||||||
OUTPUT_NAME "ollieprivateplugin"
|
|
||||||
)
|
|
||||||
|
|
||||||
# --- Plasmoid Package ---
|
|
||||||
plasma_install_package(plasmoid org.ollie.plasmoid)
|
|
||||||
|
|
||||||
# --- GUI App (9P-based) ---
|
# --- GUI App (9P-based) ---
|
||||||
configure_file(gui/main-kf5.qml ${CMAKE_CURRENT_BINARY_DIR}/gui-kf5/main.qml COPYONLY)
|
configure_file(gui/main-kf5.qml ${CMAKE_CURRENT_BINARY_DIR}/gui-kf5/main.qml COPYONLY)
|
||||||
|
|
@ -132,11 +92,7 @@ if(OLLIE_KF5)
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- Install ---
|
# --- Install ---
|
||||||
install(TARGETS ollie-tray ollie-gui DESTINATION ${KDE_INSTALL_BINDIR})
|
install(TARGETS ollie-gui DESTINATION ${KDE_INSTALL_BINDIR})
|
||||||
install(TARGETS ollie-plasmoid-plugin
|
|
||||||
DESTINATION ${KDE_INSTALL_QMLDIR}/org/ollie/private)
|
|
||||||
install(FILES plasmoid/plugin/qmldir
|
|
||||||
DESTINATION ${KDE_INSTALL_QMLDIR}/org/ollie/private)
|
|
||||||
|
|
||||||
else()
|
else()
|
||||||
# --- Qt6 / KF6 ---
|
# --- Qt6 / KF6 ---
|
||||||
|
|
@ -241,7 +197,7 @@ else()
|
||||||
)
|
)
|
||||||
|
|
||||||
# --- Install ---
|
# --- Install ---
|
||||||
install(TARGETS ollie-tray ollie-gui DESTINATION ${KDE_INSTALL_BINDIR})
|
install(TARGETS ollie-gui DESTINATION ${KDE_INSTALL_BINDIR})
|
||||||
install(TARGETS ollie-plasmoid-plugin
|
install(TARGETS ollie-plasmoid-plugin
|
||||||
DESTINATION ${KDE_INSTALL_QMLDIR}/org/ollie/private)
|
DESTINATION ${KDE_INSTALL_QMLDIR}/org/ollie/private)
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
// Resolve the 9p binary and server address
|
|
||||||
static QString ninepBin()
|
static QString ninepBin()
|
||||||
{
|
{
|
||||||
// plan9port's 9p command
|
// plan9port's 9p command
|
||||||
QString bin = QStandardPaths::findExecutable("9p");
|
QString bin = QStandardPaths::findExecutable("9p");
|
||||||
if (!bin.isEmpty()) return bin;
|
if (!bin.isEmpty()) return bin;
|
||||||
if (QFileInfo::exists("/usr/local/plan9/bin/9p"))
|
QString plan9 = qEnvironmentVariable("PLAN9");
|
||||||
return "/usr/local/plan9/bin/9p";
|
if (!plan9.isEmpty() && QFileInfo::exists(plan9 + "/bin/9p"))
|
||||||
|
return plan9 + "/bin/9p";
|
||||||
return "9p";
|
return "9p";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,6 @@
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QComboBox>
|
#include <QComboBox>
|
||||||
#include <QDBusReply>
|
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
|
@ -40,9 +39,15 @@
|
||||||
|
|
||||||
K_PLUGIN_CLASS_WITH_JSON(OllieKatePlugin, "metadata.json")
|
K_PLUGIN_CLASS_WITH_JSON(OllieKatePlugin, "metadata.json")
|
||||||
|
|
||||||
static const char *SERVICE = "org.ollie.SessionManager";
|
static QString ninepBin()
|
||||||
static const char *OBJ_PATH = "/org/ollie/SessionManager";
|
{
|
||||||
static const char *IFACE = "org.ollie.SessionManager";
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
// --- Plugin ---
|
// --- Plugin ---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,14 @@
|
||||||
#include <KPluginMetaData>
|
#include <KPluginMetaData>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <QDBusInterface>
|
#include <QProcess>
|
||||||
#include <QDBusConnection>
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QTextBrowser>
|
#include <QTextBrowser>
|
||||||
#include <QPlainTextEdit>
|
#include <QPlainTextEdit>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QEvent>
|
#include <QEvent>
|
||||||
|
#include <QStandardPaths>
|
||||||
#include <KTextEditor/MovingRange>
|
#include <KTextEditor/MovingRange>
|
||||||
#include <KTextEditor/Attribute>
|
#include <KTextEditor/Attribute>
|
||||||
|
|
||||||
|
|
@ -105,7 +105,6 @@ private:
|
||||||
|
|
||||||
OllieKatePlugin *m_plugin;
|
OllieKatePlugin *m_plugin;
|
||||||
KTextEditor::MainWindow *m_mainWindow;
|
KTextEditor::MainWindow *m_mainWindow;
|
||||||
QDBusInterface *m_iface;
|
|
||||||
QWidget *m_toolView;
|
QWidget *m_toolView;
|
||||||
QLabel *m_sessionLabel;
|
QLabel *m_sessionLabel;
|
||||||
QLabel *m_cwdLabel;
|
QLabel *m_cwdLabel;
|
||||||
|
|
@ -118,6 +117,19 @@ private:
|
||||||
QPushButton *m_stopBtn;
|
QPushButton *m_stopBtn;
|
||||||
QPushButton *m_scrollBottomBtn;
|
QPushButton *m_scrollBottomBtn;
|
||||||
|
|
||||||
|
// 9P helpers
|
||||||
|
QByteArray run9p(const QStringList &args);
|
||||||
|
void write9p(const QString &path, const QByteArray &data);
|
||||||
|
QString serverAddr();
|
||||||
|
QString agentPath();
|
||||||
|
|
||||||
|
// Streaming processes
|
||||||
|
QProcess *m_chatProc = nullptr;
|
||||||
|
QProcess *m_stateProc = nullptr;
|
||||||
|
void startChatStream();
|
||||||
|
void startStateStream();
|
||||||
|
void stopStreams();
|
||||||
|
|
||||||
QString m_sessionId;
|
QString m_sessionId;
|
||||||
qlonglong m_chatOffset = 0;
|
qlonglong m_chatOffset = 0;
|
||||||
QString m_rawChat; // accumulated raw chat text
|
QString m_rawChat; // accumulated raw chat text
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
#include "ollie_runner.h"
|
#include "ollie_runner.h"
|
||||||
|
|
||||||
#include <KPluginFactory>
|
#include <KPluginFactory>
|
||||||
#include <QDBusReply>
|
|
||||||
#include <QDBusConnection>
|
|
||||||
#include <QDBusInterface>
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
|
|
@ -11,15 +8,20 @@
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QLabel>
|
#include <QDir>
|
||||||
#include <QProcess>
|
#include <QStandardPaths>
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
K_PLUGIN_CLASS_WITH_JSON(OllieRunner, "metadata.json")
|
K_PLUGIN_CLASS_WITH_JSON(OllieRunner, "metadata.json")
|
||||||
|
|
||||||
static const char *SERVICE = "org.ollie.SessionManager";
|
static QString ninepBin()
|
||||||
static const char *OBJ_PATH = "/org/ollie/SessionManager";
|
{
|
||||||
static const char *IFACE = "org.ollie.SessionManager";
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
#if QT_VERSION_MAJOR >= 6
|
#if QT_VERSION_MAJOR >= 6
|
||||||
OllieRunner::OllieRunner(QObject *parent, const KPluginMetaData &data)
|
OllieRunner::OllieRunner(QObject *parent, const KPluginMetaData &data)
|
||||||
|
|
@ -29,28 +31,65 @@ OllieRunner::OllieRunner(QObject *parent, const KPluginMetaData &data, const QVa
|
||||||
: AbstractRunner(parent, data, args)
|
: AbstractRunner(parent, data, args)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
m_iface = new QDBusInterface(SERVICE, OBJ_PATH, IFACE,
|
}
|
||||||
QDBusConnection::sessionBus(), this);
|
|
||||||
|
QString OllieRunner::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 OllieRunner::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 OllieRunner::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);
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList OllieRunner::listSessions()
|
||||||
|
{
|
||||||
|
QByteArray out = run9p({"read", "session/idx"});
|
||||||
|
QString raw = QString::fromUtf8(out).trimmed();
|
||||||
|
if (raw.isEmpty()) return {};
|
||||||
|
return raw.split('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void OllieRunner::killSession(const QString &id)
|
||||||
|
{
|
||||||
|
// Find agent path
|
||||||
|
QByteArray agentOut = run9p({"ls", "session/" + id + "/agent"});
|
||||||
|
QString agentId = QString::fromUtf8(agentOut).trimmed().split('\n').value(0, "0");
|
||||||
|
write9p("session/" + id + "/agent/" + agentId + "/ctl", "kill");
|
||||||
}
|
}
|
||||||
|
|
||||||
void OllieRunner::match(RunnerNS::RunnerContext &context)
|
void OllieRunner::match(RunnerNS::RunnerContext &context)
|
||||||
{
|
{
|
||||||
const QString query = context.query().trimmed();
|
const QString query = context.query().trimmed();
|
||||||
|
|
||||||
// Only activate on "ollie" prefix
|
if (!query.startsWith(QLatin1String("ollie ")) && query != QLatin1String("ollie"))
|
||||||
if (!query.startsWith(QLatin1String("ollie ")) && query != QLatin1String("ollie")) {
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
// Bail if daemon isn't running
|
QString args = query.mid(6).trimmed();
|
||||||
if (!m_iface->isValid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString args = query.mid(6).trimmed(); // everything after "ollie "
|
|
||||||
|
|
||||||
if (args.isEmpty() || args == QLatin1String("list")) {
|
if (args.isEmpty() || args == QLatin1String("list")) {
|
||||||
// List sessions
|
|
||||||
QStringList sessions = listSessions();
|
QStringList sessions = listSessions();
|
||||||
if (sessions.isEmpty()) {
|
if (sessions.isEmpty()) {
|
||||||
RunnerNS::QueryMatch match(this);
|
RunnerNS::QueryMatch match(this);
|
||||||
|
|
@ -71,7 +110,7 @@ void OllieRunner::match(RunnerNS::RunnerContext &context)
|
||||||
match.setSubtext(parts[2] + " — " + parts[1]);
|
match.setSubtext(parts[2] + " — " + parts[1]);
|
||||||
match.setIconName("system-run");
|
match.setIconName("system-run");
|
||||||
match.setRelevance(0.7);
|
match.setRelevance(0.7);
|
||||||
match.setData(parts[0]); // session ID
|
match.setData(parts[0]);
|
||||||
context.addMatch(match);
|
context.addMatch(match);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,42 +130,11 @@ void OllieRunner::match(RunnerNS::RunnerContext &context)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.startsWith(QLatin1String("rename "))) {
|
// Default: one-shot prompt via generate file
|
||||||
// Expected: rename <prefix> <new_name>
|
|
||||||
// Prefix-matches against listed sessions so full ID isn't needed
|
|
||||||
QString rest = args.mid(7).trimmed();
|
|
||||||
int space = rest.indexOf(' ');
|
|
||||||
if (space > 0) {
|
|
||||||
QString prefix = rest.left(space);
|
|
||||||
QString newName = rest.mid(space + 1).trimmed();
|
|
||||||
if (!newName.isEmpty()) {
|
|
||||||
// Find sessions matching the prefix
|
|
||||||
QStringList sessions = listSessions();
|
|
||||||
for (const QString &entry : sessions) {
|
|
||||||
QStringList parts = entry.split('\t');
|
|
||||||
if (parts.isEmpty()) continue;
|
|
||||||
QString id = parts[0];
|
|
||||||
if (id.startsWith(prefix) || id == prefix) {
|
|
||||||
RunnerNS::QueryMatch match(this);
|
|
||||||
match.setId("ollie-rename-" + id);
|
|
||||||
match.setText("Rename: " + id + " → " + newName);
|
|
||||||
match.setSubtext("Rename ollie session");
|
|
||||||
match.setIconName("edit-rename");
|
|
||||||
match.setRelevance(0.8);
|
|
||||||
match.setData("rename:" + id + ":" + newName);
|
|
||||||
context.addMatch(match);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default: one-shot prompt
|
|
||||||
RunnerNS::QueryMatch match(this);
|
RunnerNS::QueryMatch match(this);
|
||||||
match.setId("ollie-prompt");
|
match.setId("ollie-prompt");
|
||||||
match.setText("Ask Ollie: " + args);
|
match.setText("Ask Ollie: " + args);
|
||||||
match.setSubtext("One-shot prompt — result via notification");
|
match.setSubtext("One-shot generation");
|
||||||
match.setIconName("system-run");
|
match.setIconName("system-run");
|
||||||
match.setRelevance(0.9);
|
match.setRelevance(0.9);
|
||||||
match.setData("prompt:" + args);
|
match.setData("prompt:" + args);
|
||||||
|
|
@ -139,35 +147,29 @@ void OllieRunner::run(const RunnerNS::RunnerContext &context, const RunnerNS::Qu
|
||||||
QString data = match.data().toString();
|
QString data = match.data().toString();
|
||||||
|
|
||||||
if (data.startsWith("kill:")) {
|
if (data.startsWith("kill:")) {
|
||||||
QString id = data.mid(5);
|
killSession(data.mid(5));
|
||||||
killSession(id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.startsWith("rename:")) {
|
|
||||||
// format: rename:<id>:<new_name>
|
|
||||||
QString rest = data.mid(7);
|
|
||||||
int sep = rest.indexOf(':');
|
|
||||||
if (sep > 0) {
|
|
||||||
QString id = rest.left(sep);
|
|
||||||
QString newName = rest.mid(sep + 1);
|
|
||||||
m_iface->call("RenameSession", id, newName);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.startsWith("prompt:")) {
|
if (data.startsWith("prompt:")) {
|
||||||
QString prompt = data.mid(7);
|
QString prompt = data.mid(7);
|
||||||
QString result = oneShotRun(prompt);
|
// Use the generate rdwr file for one-shot
|
||||||
|
QProcess proc;
|
||||||
|
proc.start(ninepBin(), {"-a", serverAddr(), "rdwr", "generate"});
|
||||||
|
proc.waitForStarted(3000);
|
||||||
|
proc.write(prompt.toUtf8());
|
||||||
|
proc.closeWriteChannel();
|
||||||
|
proc.waitForFinished(30000);
|
||||||
|
QString result = QString::fromUtf8(proc.readAllStandardOutput()).trimmed();
|
||||||
|
if (result.isEmpty()) result = "(no response)";
|
||||||
|
|
||||||
// Show popup window with result
|
// Show popup
|
||||||
auto *dialog = new QDialog(nullptr);
|
auto *dialog = new QDialog(nullptr);
|
||||||
dialog->setWindowTitle("Ollie — " + prompt.left(40));
|
dialog->setWindowTitle("Ollie — " + prompt.left(40));
|
||||||
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
dialog->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
dialog->resize(520, 380);
|
dialog->resize(520, 380);
|
||||||
|
|
||||||
auto *layout = new QVBoxLayout(dialog);
|
auto *layout = new QVBoxLayout(dialog);
|
||||||
|
|
||||||
auto *textEdit = new QTextEdit(dialog);
|
auto *textEdit = new QTextEdit(dialog);
|
||||||
textEdit->setReadOnly(true);
|
textEdit->setReadOnly(true);
|
||||||
textEdit->setPlainText(result);
|
textEdit->setPlainText(result);
|
||||||
|
|
@ -178,17 +180,14 @@ void OllieRunner::run(const RunnerNS::RunnerContext &context, const RunnerNS::Qu
|
||||||
|
|
||||||
auto *buttonLayout = new QHBoxLayout();
|
auto *buttonLayout = new QHBoxLayout();
|
||||||
buttonLayout->addStretch();
|
buttonLayout->addStretch();
|
||||||
|
|
||||||
auto *copyBtn = new QPushButton("Copy", dialog);
|
auto *copyBtn = new QPushButton("Copy", dialog);
|
||||||
QObject::connect(copyBtn, &QPushButton::clicked, [result]() {
|
QObject::connect(copyBtn, &QPushButton::clicked, [result]() {
|
||||||
QApplication::clipboard()->setText(result);
|
QApplication::clipboard()->setText(result);
|
||||||
});
|
});
|
||||||
buttonLayout->addWidget(copyBtn);
|
buttonLayout->addWidget(copyBtn);
|
||||||
|
|
||||||
auto *closeBtn = new QPushButton("Close", dialog);
|
auto *closeBtn = new QPushButton("Close", dialog);
|
||||||
QObject::connect(closeBtn, &QPushButton::clicked, dialog, &QDialog::close);
|
QObject::connect(closeBtn, &QPushButton::clicked, dialog, &QDialog::close);
|
||||||
buttonLayout->addWidget(closeBtn);
|
buttonLayout->addWidget(closeBtn);
|
||||||
|
|
||||||
layout->addLayout(buttonLayout);
|
layout->addLayout(buttonLayout);
|
||||||
|
|
||||||
dialog->show();
|
dialog->show();
|
||||||
|
|
@ -197,51 +196,8 @@ void OllieRunner::run(const RunnerNS::RunnerContext &context, const RunnerNS::Qu
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clicked a session from list — open it in ollie-gui
|
// Session selected — launch GUI
|
||||||
QString sessionId = data;
|
QProcess::startDetached("ollie-gui", {});
|
||||||
QDBusInterface gui("org.ollie.Gui", "/org/ollie/Gui", "org.ollie.Gui",
|
|
||||||
QDBusConnection::sessionBus());
|
|
||||||
if (gui.isValid()) {
|
|
||||||
gui.call(QDBus::NoBlock, "SelectSession", sessionId);
|
|
||||||
} else {
|
|
||||||
// GUI not running — launch it, then ask it to select the session
|
|
||||||
QProcess::startDetached("ollie-gui", {});
|
|
||||||
// Poll for D-Bus registration (up to 3 seconds)
|
|
||||||
for (int i = 0; i < 30; ++i) {
|
|
||||||
QThread::msleep(100);
|
|
||||||
QDBusInterface retry("org.ollie.Gui", "/org/ollie/Gui", "org.ollie.Gui",
|
|
||||||
QDBusConnection::sessionBus());
|
|
||||||
if (retry.isValid()) {
|
|
||||||
retry.call(QDBus::NoBlock, "SelectSession", sessionId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- D-Bus helpers ---
|
|
||||||
|
|
||||||
QString OllieRunner::oneShotRun(const QString &prompt)
|
|
||||||
{
|
|
||||||
// Use the Generate one-shot D-Bus method — no session needed.
|
|
||||||
QDBusReply<QString> reply = m_iface->call("Generate", prompt, QString(), QString(), QString());
|
|
||||||
if (!reply.isValid()) {
|
|
||||||
return "Error: " + reply.error().message();
|
|
||||||
}
|
|
||||||
QString result = reply.value().trimmed();
|
|
||||||
return result.isEmpty() ? "(no response)" : result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList OllieRunner::listSessions()
|
|
||||||
{
|
|
||||||
QDBusReply<QStringList> reply = m_iface->call("ListSessions");
|
|
||||||
if (reply.isValid()) return reply.value();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void OllieRunner::killSession(const QString &id)
|
|
||||||
{
|
|
||||||
m_iface->call("KillSession", id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "ollie_runner.moc"
|
#include "ollie_runner.moc"
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,11 @@
|
||||||
#include <KRunner/AbstractRunner>
|
#include <KRunner/AbstractRunner>
|
||||||
#include <KRunner/RunnerContext>
|
#include <KRunner/RunnerContext>
|
||||||
#include <KRunner/QueryMatch>
|
#include <KRunner/QueryMatch>
|
||||||
#include <QDBusInterface>
|
#include <QProcess>
|
||||||
|
|
||||||
#if QT_VERSION_MAJOR >= 6
|
#if QT_VERSION_MAJOR >= 6
|
||||||
// KF6: classes are in the KRunner namespace
|
|
||||||
namespace RunnerNS = KRunner;
|
namespace RunnerNS = KRunner;
|
||||||
#else
|
#else
|
||||||
// KF5: classes are in the Plasma namespace
|
|
||||||
namespace RunnerNS = Plasma;
|
namespace RunnerNS = Plasma;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -29,9 +27,9 @@ public:
|
||||||
void run(const RunnerNS::RunnerContext &context, const RunnerNS::QueryMatch &match) override;
|
void run(const RunnerNS::RunnerContext &context, const RunnerNS::QueryMatch &match) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDBusInterface *m_iface;
|
QByteArray run9p(const QStringList &args);
|
||||||
|
void write9p(const QString &path, const QByteArray &data);
|
||||||
QString oneShotRun(const QString &prompt);
|
QString serverAddr();
|
||||||
QStringList listSessions();
|
QStringList listSessions();
|
||||||
void killSession(const QString &id);
|
void killSession(const QString &id);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,4 @@
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDBusConnection>
|
|
||||||
#include <QDBusConnectionInterface>
|
|
||||||
#include <QDBusServiceWatcher>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
#include "ollie_tray.h"
|
#include "ollie_tray.h"
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
|
|
@ -13,36 +8,7 @@ int main(int argc, char *argv[])
|
||||||
app.setOrganizationName("ollie");
|
app.setOrganizationName("ollie");
|
||||||
app.setQuitOnLastWindowClosed(false);
|
app.setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
|
||||||
if (!bus.isConnected()) {
|
|
||||||
qCritical() << "Cannot connect to D-Bus session bus";
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
OllieTray tray;
|
OllieTray tray;
|
||||||
|
|
||||||
// Watch for daemon appearing/disappearing
|
|
||||||
auto *watcher = new QDBusServiceWatcher(
|
|
||||||
"org.ollie.SessionManager",
|
|
||||||
bus,
|
|
||||||
QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration,
|
|
||||||
&app);
|
|
||||||
|
|
||||||
QObject::connect(watcher, &QDBusServiceWatcher::serviceRegistered, [&]() {
|
|
||||||
qDebug() << "Daemon appeared on D-Bus";
|
|
||||||
tray.updateStatus();
|
|
||||||
});
|
|
||||||
|
|
||||||
QObject::connect(watcher, &QDBusServiceWatcher::serviceUnregistered, [&]() {
|
|
||||||
qDebug() << "Daemon disappeared from D-Bus";
|
|
||||||
tray.updateStatus();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (bus.interface()->isServiceRegistered("org.ollie.SessionManager")) {
|
|
||||||
qDebug() << "ollie-tray started (daemon running)";
|
|
||||||
} else {
|
|
||||||
qDebug() << "ollie-tray started (waiting for daemon)";
|
|
||||||
}
|
|
||||||
|
|
||||||
return app.exec();
|
return app.exec();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,229 +1,134 @@
|
||||||
#include "ollie_tray.h"
|
#include "ollie_tray.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDBusReply>
|
|
||||||
#include <QDBusConnection>
|
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QIcon>
|
#include <QStandardPaths>
|
||||||
#include <QDebug>
|
|
||||||
#include <QInputDialog>
|
|
||||||
|
|
||||||
static const char *SERVICE = "org.ollie.SessionManager";
|
static QString ninepBin()
|
||||||
static const char *OBJ_PATH = "/org/ollie/SessionManager";
|
{
|
||||||
static const char *IFACE = "org.ollie.SessionManager";
|
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)
|
OllieTray::OllieTray(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
// D-Bus interface to the daemon
|
|
||||||
m_iface = new QDBusInterface(SERVICE, OBJ_PATH, IFACE,
|
|
||||||
QDBusConnection::sessionBus(), this);
|
|
||||||
|
|
||||||
// KStatusNotifierItem
|
|
||||||
m_sni = new KStatusNotifierItem("ollie-kde", this);
|
m_sni = new KStatusNotifierItem("ollie-kde", this);
|
||||||
m_sni->setCategory(KStatusNotifierItem::ApplicationStatus);
|
m_sni->setCategory(KStatusNotifierItem::ApplicationStatus);
|
||||||
m_sni->setTitle("Ollie");
|
m_sni->setTitle("Ollie");
|
||||||
m_sni->setIconByName("system-run"); // fallback icon
|
m_sni->setIconByName("system-run");
|
||||||
m_sni->setStatus(KStatusNotifierItem::Active);
|
m_sni->setStatus(KStatusNotifierItem::Active);
|
||||||
|
|
||||||
setupMenu();
|
connect(m_sni, &KStatusNotifierItem::activateRequested, this, &OllieTray::openGui);
|
||||||
updateStatus();
|
|
||||||
updateTooltip();
|
|
||||||
|
|
||||||
// Connect D-Bus signals
|
setupMenu();
|
||||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
refreshSessions();
|
||||||
bus.connect(SERVICE, OBJ_PATH, IFACE, "SessionCreated",
|
|
||||||
this, SLOT(onSessionCreated(QString)));
|
// Poll session state every 5 seconds (lightweight — single 9p read)
|
||||||
bus.connect(SERVICE, OBJ_PATH, IFACE, "SessionKilled",
|
m_pollTimer = new QTimer(this);
|
||||||
this, SLOT(onSessionKilled(QString)));
|
connect(m_pollTimer, &QTimer::timeout, this, &OllieTray::refreshSessions);
|
||||||
bus.connect(SERVICE, OBJ_PATH, IFACE, "StateChanged",
|
m_pollTimer->start(5000);
|
||||||
this, SLOT(onStateChanged(QString,QString)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OllieTray::~OllieTray()
|
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()
|
void OllieTray::setupMenu()
|
||||||
{
|
{
|
||||||
m_menu = new QMenu();
|
m_menu = new QMenu();
|
||||||
|
|
||||||
m_menu->addAction("New Session", this, &OllieTray::newSession);
|
|
||||||
m_menu->addSeparator();
|
|
||||||
|
|
||||||
m_sessionsMenu = m_menu->addMenu("Sessions");
|
m_sessionsMenu = m_menu->addMenu("Sessions");
|
||||||
connect(m_sessionsMenu, &QMenu::aboutToShow, this, &OllieTray::refreshSessions);
|
|
||||||
|
|
||||||
m_menu->addSeparator();
|
m_menu->addSeparator();
|
||||||
|
m_menu->addAction("Open GUI", this, &OllieTray::openGui);
|
||||||
m_menu->addAction("Stop All", this, &OllieTray::stopAll);
|
m_menu->addAction("Stop All", this, &OllieTray::stopAll);
|
||||||
m_menu->addSeparator();
|
m_menu->addSeparator();
|
||||||
m_menu->addAction("Quit Daemon", this, &OllieTray::quitDaemon);
|
m_menu->addAction("Quit", qApp, &QApplication::quit);
|
||||||
|
|
||||||
m_sni->setContextMenu(m_menu);
|
m_sni->setContextMenu(m_menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OllieTray::updateStatus()
|
|
||||||
{
|
|
||||||
QStringList sessions = callListSessions();
|
|
||||||
m_totalSessions = sessions.size();
|
|
||||||
m_activeSessions = 0;
|
|
||||||
|
|
||||||
for (const QString &entry : sessions) {
|
|
||||||
QStringList parts = entry.split('\t');
|
|
||||||
if (parts.size() >= 2) {
|
|
||||||
QString state = parts[1];
|
|
||||||
if (state != "idle") {
|
|
||||||
m_activeSessions++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Icon status: active (bright) if any session is thinking, passive if all idle
|
|
||||||
if (m_activeSessions > 0) {
|
|
||||||
m_sni->setStatus(KStatusNotifierItem::NeedsAttention);
|
|
||||||
m_sni->setIconByName("ollie-active");
|
|
||||||
m_sni->setAttentionIconByName("ollie-active");
|
|
||||||
} else if (m_totalSessions > 0) {
|
|
||||||
m_sni->setStatus(KStatusNotifierItem::Active);
|
|
||||||
m_sni->setIconByName("system-run");
|
|
||||||
} else {
|
|
||||||
m_sni->setStatus(KStatusNotifierItem::Passive);
|
|
||||||
m_sni->setIconByName("system-run");
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTooltip();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OllieTray::updateTooltip()
|
|
||||||
{
|
|
||||||
QString tooltip;
|
|
||||||
if (m_totalSessions == 0) {
|
|
||||||
tooltip = "Ollie — no active sessions";
|
|
||||||
} else {
|
|
||||||
tooltip = QString("Ollie — %1 session%2")
|
|
||||||
.arg(m_totalSessions)
|
|
||||||
.arg(m_totalSessions == 1 ? "" : "s");
|
|
||||||
if (m_activeSessions > 0) {
|
|
||||||
tooltip += QString(", %1 active").arg(m_activeSessions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_sni->setToolTipTitle("Ollie");
|
|
||||||
m_sni->setToolTipSubTitle(tooltip);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- D-Bus signal handlers ---
|
|
||||||
|
|
||||||
void OllieTray::onSessionCreated(const QString &session_id)
|
|
||||||
{
|
|
||||||
Q_UNUSED(session_id);
|
|
||||||
updateStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OllieTray::onSessionKilled(const QString &session_id)
|
|
||||||
{
|
|
||||||
Q_UNUSED(session_id);
|
|
||||||
updateStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OllieTray::onStateChanged(const QString &session_id, const QString &new_state)
|
|
||||||
{
|
|
||||||
Q_UNUSED(session_id);
|
|
||||||
Q_UNUSED(new_state);
|
|
||||||
updateStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Menu actions ---
|
|
||||||
|
|
||||||
void OllieTray::newSession()
|
|
||||||
{
|
|
||||||
QString cwd = QDir::homePath();
|
|
||||||
QDBusReply<QString> reply = m_iface->call("CreateSession", cwd, QString(), QString(), "default", QString(), QString());
|
|
||||||
if (reply.isValid()) {
|
|
||||||
qDebug() << "Created session:" << reply.value();
|
|
||||||
} else {
|
|
||||||
qWarning() << "Failed to create session:" << reply.error().message();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OllieTray::stopAll()
|
|
||||||
{
|
|
||||||
QStringList sessions = callListSessions();
|
|
||||||
for (const QString &entry : sessions) {
|
|
||||||
QStringList parts = entry.split('\t');
|
|
||||||
if (!parts.isEmpty()) {
|
|
||||||
callKillSession(parts[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void OllieTray::quitDaemon()
|
|
||||||
{
|
|
||||||
// Stop all sessions then tell the daemon to exit
|
|
||||||
stopAll();
|
|
||||||
// Send SIGTERM to the daemon process via D-Bus (or just quit ourselves)
|
|
||||||
// For now, just quit the tray; the daemon stays alive.
|
|
||||||
// To actually quit the daemon, we'd need a Quit method on D-Bus.
|
|
||||||
QApplication::quit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OllieTray::refreshSessions()
|
void OllieTray::refreshSessions()
|
||||||
{
|
{
|
||||||
|
QByteArray out = run9p({"read", "session/idx"});
|
||||||
|
QString raw = QString::fromUtf8(out).trimmed();
|
||||||
|
|
||||||
m_sessionsMenu->clear();
|
m_sessionsMenu->clear();
|
||||||
|
|
||||||
QStringList sessions = callListSessions();
|
if (raw.isEmpty()) {
|
||||||
if (sessions.isEmpty()) {
|
m_sessionsMenu->addAction("(no sessions)")->setEnabled(false);
|
||||||
m_sessionsMenu->addAction("(none)")->setEnabled(false);
|
m_sni->setToolTip("system-run", "Ollie", "No active sessions");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const QString &entry : sessions) {
|
QStringList lines = raw.split('\n');
|
||||||
QStringList parts = entry.split('\t');
|
int active = 0;
|
||||||
if (parts.size() < 4) continue;
|
for (const QString &line : lines) {
|
||||||
|
QStringList parts = line.split('\t');
|
||||||
|
if (parts.size() < 2) continue;
|
||||||
|
|
||||||
QString id = parts[0];
|
QString id = parts[0];
|
||||||
QString state = parts[1];
|
QString state = parts[1];
|
||||||
QString model = parts[2];
|
if (state != "idle") active++;
|
||||||
QString agent = parts[3];
|
|
||||||
|
|
||||||
// State indicator
|
QString label = (state == "idle" ? "○ " : "● ") + id;
|
||||||
QString indicator;
|
m_sessionsMenu->addAction(label);
|
||||||
if (state == "idle") indicator = "○";
|
}
|
||||||
else if (state.startsWith("calling")) indicator = "⚡";
|
|
||||||
else indicator = "●";
|
|
||||||
|
|
||||||
QString label = QString("%1 %2 [%3] %4").arg(indicator, id, agent, model);
|
m_sni->setToolTip("system-run", "Ollie",
|
||||||
|
QString("%1 sessions, %2 active").arg(lines.size()).arg(active));
|
||||||
|
}
|
||||||
|
|
||||||
QMenu *sessionMenu = m_sessionsMenu->addMenu(label);
|
void OllieTray::openGui()
|
||||||
QString capturedId = id;
|
{
|
||||||
sessionMenu->addAction("Rename", this, [this, capturedId]() {
|
QProcess::startDetached("ollie-gui", {});
|
||||||
bool ok;
|
}
|
||||||
QString newName = QInputDialog::getText(
|
|
||||||
nullptr, "Rename Session",
|
void OllieTray::stopAll()
|
||||||
"New name for session " + capturedId + ":",
|
{
|
||||||
QLineEdit::Normal, capturedId, &ok);
|
QByteArray out = run9p({"read", "session/idx"});
|
||||||
if (ok && !newName.trimmed().isEmpty()) {
|
QString raw = QString::fromUtf8(out).trimmed();
|
||||||
m_iface->call("RenameSession", capturedId, newName.trimmed());
|
for (const QString &line : raw.split('\n')) {
|
||||||
}
|
QStringList parts = line.split('\t');
|
||||||
});
|
if (parts.size() < 2 || parts[1] == "idle") continue;
|
||||||
sessionMenu->addAction("Kill", this, [this, capturedId]() {
|
QString id = parts[0];
|
||||||
callKillSession(capturedId);
|
// 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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- D-Bus helpers ---
|
|
||||||
|
|
||||||
QStringList OllieTray::callListSessions()
|
|
||||||
{
|
|
||||||
QDBusReply<QStringList> reply = m_iface->call("ListSessions");
|
|
||||||
if (reply.isValid()) {
|
|
||||||
return reply.value();
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OllieTray::callKillSession(const QString &id)
|
|
||||||
{
|
|
||||||
QDBusReply<bool> reply = m_iface->call("KillSession", id);
|
|
||||||
return reply.isValid() && reply.value();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QDBusInterface>
|
#include <QTimer>
|
||||||
#include <QDBusConnection>
|
#include <QProcess>
|
||||||
#include <KStatusNotifierItem>
|
#include <KStatusNotifierItem>
|
||||||
|
|
||||||
class OllieTray : public QObject
|
class OllieTray : public QObject
|
||||||
|
|
@ -15,31 +15,21 @@ public:
|
||||||
explicit OllieTray(QObject *parent = nullptr);
|
explicit OllieTray(QObject *parent = nullptr);
|
||||||
~OllieTray() override;
|
~OllieTray() override;
|
||||||
|
|
||||||
void updateStatus();
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onSessionCreated(const QString &session_id);
|
|
||||||
void onSessionKilled(const QString &session_id);
|
|
||||||
void onStateChanged(const QString &session_id, const QString &new_state);
|
|
||||||
|
|
||||||
void newSession();
|
|
||||||
void stopAll();
|
|
||||||
void quitDaemon();
|
|
||||||
void refreshSessions();
|
void refreshSessions();
|
||||||
|
void openGui();
|
||||||
|
void stopAll();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupMenu();
|
void setupMenu();
|
||||||
void updateTooltip();
|
QByteArray run9p(const QStringList &args);
|
||||||
QStringList callListSessions();
|
void write9p(const QString &path, const QByteArray &data);
|
||||||
bool callKillSession(const QString &id);
|
QString serverAddr();
|
||||||
|
|
||||||
KStatusNotifierItem *m_sni;
|
KStatusNotifierItem *m_sni;
|
||||||
QMenu *m_menu;
|
QMenu *m_menu;
|
||||||
QMenu *m_sessionsMenu;
|
QMenu *m_sessionsMenu;
|
||||||
QDBusInterface *m_iface;
|
QTimer *m_pollTimer;
|
||||||
|
|
||||||
int m_totalSessions = 0;
|
|
||||||
int m_activeSessions = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // OLLIE_TRAY_H
|
#endif // OLLIE_TRAY_H
|
||||||
|
|
|
||||||
Reference in New Issue