Something went wrong. Try again.
The agentic engineering control plane for the posthuman future
Something went wrong. Try again.
4.0 kB · 91 lines
C
at main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091// ProcessScanner.h — process-table lookups for the app.//// Two paths share the same `ps -Ao pid=,command=` snapshot://// 1. SYNCHRONOUS (quit-time): at quit, the Shell asks which panes run a// whitelisted program. scanForeground() spawns one `ps` subprocess and// parses the full table on the calling thread. This must stay// synchronous: it runs from Component.onDestruction, where no event// loop is left to deliver an async result.//// 2. ASYNCHRONOUS (sidebar previews): requestPrograms() runs `ps` off the// GUI thread (QProcess is event-driven, so the subprocess never blocks// the UI) and answers each request with a per-request signal. A single// snapshot is shared: results are cached for a short window// (kCacheTtlMs), so concurrent requests — e.g. every sidebar card's 2s// label timer — are served from ONE subprocess instead of one per card.// Multiple requests arriving while a snapshot is in flight are all// answered when that snapshot completes.//// A pane's "program" is the foreground process group of its PTY// (tcgetpgrp), captured by TerminalQuickItem::foregroundPid; the snapshot// maps those pids to command lines.//// Entries have { "kind": "name"|"path", "value": QString }:// - kind "name": value is a regular expression matched against the running// program's argv[0] basename (e.g. "vim", "^nvim$"). Unanchored: use// ^...$ for an exact-name match. Invalid regexes never match.// - kind "path": value is compared for exact equality against the running// program's argv[0] as `ps` prints it, i.e. the exact program path the// user picked in the file browser (launching via PATH, e.g. `vim` in a// shell, produces argv[0] "vim" and does NOT match "/usr/bin/vim").#pragma once
#include <QObject>#include <QtQml/qqmlregistration.h>
#include <QElapsedTimer>#include <QHash>#include <QProcess>#include <QTimer>#include <QVariantList>#include <QVariantMap>
class ProcessScanner : public QObject { Q_OBJECT QML_ELEMENT
public: explicit ProcessScanner(QObject* parent = nullptr);
// --- Synchronous (quit-time) path --- // Returns a map { "<pid>": { "matched": true, "argv": QStringList } } // where argv is the full token list from `ps` (argv[0] first). Empty when // the whitelist is empty, the ps snapshot failed, or nothing matched. Q_INVOKABLE static QVariantMap scanForeground(const QVariantList& pids, const QVariantList& entries);
// --- Asynchronous (live refresh) path --- // Request the command lines for `pids`. Runs at most one `ps` snapshot at // a time, off the GUI thread. Each call is answered exactly once by // programsUpdated(requestId, pids, { "<pid>": { "argv": [...] } }), // where requestId is this function's return value. If a fresh-enough // snapshot is cached the answer is delivered immediately (synchronously) // from cache; otherwise the request joins the in-flight snapshot. Q_INVOKABLE int requestPrograms(const QVariantList& pids);
signals: // Emitted on the GUI thread with the answer to one requestPrograms call. // `programs` contains an entry for each of `pids` the snapshot resolved. // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) void programsUpdated(int requestId, const QVariantList& pids, const QVariantMap& programs);
private: void onFinished();
// All outstanding requests: requestId -> pid set. Entries are answered // exactly once (from a fresh snapshot, or from the cache via a deferred // single-shot), then removed. QHash<int, QVariantList> m_requests; // Requests awaiting the in-flight snapshot (subset of m_requests). QHash<int, QVariantList> m_pendingRequests; // Cached full-table snapshot and when it was taken. QHash<int, QString> m_cache; QElapsedTimer m_cacheAge; bool m_cacheValid = false; bool m_scanInFlight = false; int m_nextRequestId = 1; QProcess m_process;};