Something went wrong. Try again.
The agentic engineering control plane for the posthuman future
Something went wrong. Try again.
12 kB · 254 lines
C++
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255#include <QtCore/QString>#include <QtCore/QUrl>#include <QtCore/QCommandLineParser>#include <QtCore/QRegularExpression>#include <QtCore/QStringList>#include <QtCore/QReadWriteLock>#include <QtGui/QColorSpace>#include <QtGui/QGuiApplication>#include <QtGui/QImage>#include <QtGui/QSurfaceFormat>#include <QtQml/QQmlApplicationEngine>#include <QtQml/QQmlContext>#include <QtQuick/QQuickImageProvider>#include <QtQuick/QQuickWindow>#include <QtQuickControls2/QQuickStyle>#include <QWKQuick/quickwindowagent.h>
#include "DevDriver.h"
// Qt defines `emit` as an empty macro, and ghostty's C headers declare struct// fields by that name. The collision is silent on clang, which only warns and// suppresses the warning inside a system header, so the struct loses the field.// gcc treats it as a hard error. Suspend the macro while ghostty's headers// parse, exactly as TerminalQuickItem.h does. Nothing below uses the keyword.#undef emit#include <ghostty/vt.h>
#include <cstring>
#include <ranges>#include <span>
// PNG decoder for the Kitty graphics protocol. ghostty-vt parses APC// sequences and stores images, but it needs an external decoder to turn// raw PNG bytes into RGBA pixels. We use QImage (QtGui is already linked).// The decoded buffer is allocated through ghostty's allocator and the// library takes ownership, freeing it with the same allocator.static bool decodePng(void* userdata, const GhosttyAllocator* allocator, const uint8_t* data, size_t data_len, GhosttySysImage* out) { (void)userdata; QImage img = QImage::fromData(data, static_cast<int>(data_len), "PNG"); if (img.isNull()) { return false; } img = img.convertToFormat(QImage::Format_RGBA8888); // Premultiply alpha: our renderer blends with src=One (premultiplied). // PNG is straight alpha; without this, image edges over-brighten. // Rows are independent: walk each scanline as a span of exactly `width` // pixels (never the padding past the row), and premultiply in place. const auto rows = std::views::iota(0, img.height()) | std::views::transform([&img](int y) { return std::span<QRgb>( reinterpret_cast<QRgb*>(img.scanLine(y)), // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast) static_cast<size_t>(img.width())); }); for (const std::span<QRgb> row : rows) { for (QRgb& px : row) { const int a = qAlpha(px); px = qRgba(qRed(px) * a / 255, qGreen(px) * a / 255, qBlue(px) * a / 255, a); } } const size_t len = static_cast<size_t>(img.width()) * static_cast<size_t>(img.height()) * 4; uint8_t* buf = ghostty_alloc(allocator, len); if (buf == nullptr) { return false; } std::memcpy(buf, img.constBits(), len); out->width = static_cast<uint32_t>(img.width()); out->height = static_cast<uint32_t>(img.height()); out->data = buf; out->data_len = len; return true;}
// Image provider for the pane-drag snapshot. TerminalQuickItem delivers the// dragged pane's QImage via the snapshotReady signal (QML thread), and// PaneDrag stores it here so the drag Image can reference it by URL// ("image://snapshot/current"). The read/write lock guards the hand-off// between the snapshotReady writer and the image-request read.//// QQuickImageProvider already derives from QObject (via QQmlImageProviderBase);// give it a meta-object with Q_OBJECT so the Q_INVOKABLE setImage is visible// to QML as a callable method.class SnapshotImageProvider : public QQuickImageProvider { Q_OBJECTpublic: SnapshotImageProvider() : QQuickImageProvider(QQuickImageProvider::Image) {}
QImage requestImage(const QString& id, QSize* size, const QSize& requestedSize) override { QReadLocker lock(&m_lock); Q_UNUSED(id); Q_UNUSED(requestedSize); if (size != nullptr) { *size = m_image.size(); } return m_image; }
Q_INVOKABLE void setImage(const QImage& image) { QWriteLocker lock(&m_lock); m_image = image; }
private: QImage m_image; QReadWriteLock m_lock;};#include "main.moc"
int main(int argc, char *argv[]) { // Pin the surface format to sRGB before the GUI app exists. The QRhi text // shader reads sRGB-encoded colors and writes linear values; the backbuffer // must be tagged sRGB so the GPU encodes back on display. On macOS Metal // and most Vulkan drivers this picks RGBA8_sRGB; X11 EGL on some Linux // distros defaults to RGBA8 if we do not set this explicitly. QSurfaceFormat fmt = QSurfaceFormat::defaultFormat(); fmt.setAlphaBufferSize(8); fmt.setColorSpace(QColorSpace::SRgb); QSurfaceFormat::setDefaultFormat(fmt); QQuickWindow::setDefaultAlphaBuffer(true); // Wheel-momentum scrolling. Qt 6.11 Flickable has no QML switch for the // wheel-decay rate: wheelDeceleration is a private member whose default // (15000) disables wheel momentum and steps each notch a fixed 72px // (wheelScrollLines()*24) with no flick. The only way to select the // momentum path is this environment variable, read once in // QQuickFlickablePrivate's constructor from the process environment, so // it applies to every Flickable/ListView in the app (dialogs, settings, // font lists, palette) — no per-view work needed. // The feel: one notch glides 72px (~968 px/s at deceleration), and the // velocity cap (maximumFlickVelocity) bounds fast rolls: at 6500 a // capped notch glides 307px (1.6 cards), too heavy. 12000 cuts that to // ~167px per capped notch and a fast three-notch roll to ~350px (~1.9 // cards); single notches stay 72px. // NOTE: the sidebar does NOT use this path. Its Flickable owns a // WheelHandler (Sidebar.qml) that consumes wheel events first and maps // trackpad pixelDelta 1:1 plus fixed wheel steps itself, so this env // only affects the remaining Flickables. // Set BEFORE QGuiApplication exists so the first Flickable picks it up. qputenv("QT_QUICK_FLICKABLE_WHEEL_DECELERATION", "12000"); // Native text rendering bypasses the Metal-killing distance-field glyph // cache (QSGRhiDistanceFieldGlyphCache). Required on Apple Silicon. QQuickWindow::setTextRenderType(QQuickWindow::NativeTextRendering); QGuiApplication app(argc, argv); QQuickStyle::setStyle(QStringLiteral("Basic"));
// Install the Kitty graphics PNG decoder before any terminal exists. // ghostty-vt needs it to turn APC-transmitted PNG bytes into RGBA. (void)ghostty_sys_set( GHOSTTY_SYS_OPT_DECODE_PNG, reinterpret_cast<const void*>(&decodePng)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
// CLI: -e/--execute (repeatable) feeds lines to the PTY after spawn; // -c/--command is the existing single-program path. QCommandLineParser parser; parser.setApplicationDescription( QStringLiteral("Xenomorphic terminal emulator")); parser.addHelpOption(); parser.addVersionOption(); QCommandLineOption execOpt(QStringList{"e", "execute"}, QStringLiteral("Execute a shell line after the PTY ready (repeatable)." " Each value is written to the shell followed by '\\n'."), QStringLiteral("line")); parser.addOption(execOpt); QCommandLineOption cmdOpt(QStringList{"c", "command"}, QStringLiteral("Run a single program instead of the login shell" " (space-split argv)."), QStringLiteral("program")); parser.addOption(cmdOpt); QCommandLineOption cwdOpt({"C", "cwd"}, QStringLiteral("Set the spawned shell's working directory" " (default: inherited CWD)."), QStringLiteral("dir")); parser.addOption(cwdOpt); // Scroll-hitch instrumentation gate (XENO_SIDEBAR_DEBUG also enables the // C++ ps-scan timings; this flag reaches QML via Qt.application.arguments). QCommandLineOption debugOpt(QStringList{"xeno-sidebar-debug"}, QStringLiteral("Enable sidebar scroll-hitch debug logging.")); parser.addOption(debugOpt); parser.process(app);
QStringList startupCommands = parser.values(execOpt); if (startupCommands.isEmpty()) { // Allow a single --execute with semicolon- or newline-separated // entries as a convenience for shells that don't repeat CLI flags. const QString single = parser.value(execOpt); if (!single.isEmpty()) { startupCommands = single.split(QRegularExpression{"[;\\n]"}, Qt::SkipEmptyParts); for (QString& s : startupCommands) { s = s.trimmed(); } startupCommands.removeAll(QString{}); } } const QString command = parser.value(cmdOpt);
QQmlApplicationEngine engine; engine.rootContext()->setContextProperty( QStringLiteral("xenomorphicStartupCommands"), startupCommands); engine.rootContext()->setContextProperty( QStringLiteral("xenomorphicStartupCommand"), command); engine.rootContext()->setContextProperty( QStringLiteral("xenomorphicWorkingDir"), parser.value(cwdOpt)); auto* snapshotProvider = new SnapshotImageProvider(); engine.addImageProvider(QStringLiteral("snapshot"), snapshotProvider); engine.rootContext()->setContextProperty( QStringLiteral("xenomorphicSnapshotProvider"), QVariant::fromValue(snapshotProvider)); // The window agent is a context property (not a registered QML type): // qmlcachegen cannot resolve context properties, so calls on it compile // to runtime name-based dispatch. AOT-compiled calls against a resolved // QML type bake the metaobject's relative method indices in; QWindowKit // registers its types at runtime, so a build-time view of that type // (e.g. a tooling shim) desynchronizes the indices and segfaults. auto* windowAgent = new QWK::QuickWindowAgent(&engine); engine.rootContext()->setContextProperty( QStringLiteral("windowAgent"), windowAgent); engine.load(QUrl(QStringLiteral("qrc:/qt/qml/Xenomorphic/main.qml"))); if (engine.rootObjects().isEmpty()) { return -1; }
// Optional live-driving server for development; inert unless // XENO_DEV_SERVER is set in the environment. Its constructor also // installs the "model"/"pool" root-context properties used below. DevDriver devDriver(&engine);
// Quit-time session save, including the one-shot restart scan // (buildSessionRoot(true)). The scan reads the daemon's pane cache, // not the QML items, so it does not depend on QML surviving teardown. // The 500 ms debounce path (AppModel::saveSessionNow) keeps passing // scanQuit=false — only the quit path may run the `ps` subprocess. // // Reached through the engine's root-context property (DevDriver // installs the "model" singleton) with a name-based invoke: this // target compiles main.cpp with no models/ include dir, and keeping // the metaobject lookup by name avoids a CMake change. Null-safe for // a quit before the singletons exist. if (auto* model = engine.rootContext()->contextProperty(QStringLiteral("model")) .value<QObject*>()) { QObject::connect(&app, &QGuiApplication::aboutToQuit, model, [model]() { QMetaObject::invokeMethod( model, "saveSessionForQuit"); }); }
return QGuiApplication::exec();}