feat(inspector): port the terminal inspector

Ports ZIG/src/inspector (the Dear ImGui terminal inspector) as the
ghostty-inspector crate using dear-imgui-rs + dear-imgui-glow (imgui-rs
lacks a safe docking API): screen, modes, keyboard events, terminal IO
/ VT sequence log with filtering, cell inspector with picking, page
list, renderer and size/font info, with Ghostty's docking layout. The
crate denies unsafe except a single dock.rs module wrapping DockBuilder.

GTK: imgui_widget, inspector_widget and inspector_window classes with
their blueprints replace the former no-op; ctrl+shift+i toggles the
inspector window.

Core: activate/deactivate hooks, key event and mouse recording, and VT
stream recording in termio while an inspector is active, as in Zig;
overlay features reach the renderer through an InspectorHook on the
render state. The debug input replay hook gained inspector-screenshot/
click/scroll steps used to verify all tabs in the real app.

Deviations: font size scales with the display scale instead of Zig's
fixed 16*2; pop-out dock splits apply one frame later; DCS/APC
descriptions are approximated.

Test Plan:
- cargo test -p ghostty-inspector (15 incl. headless full-UI draw)
- cargo test -p ghostty-core --test e2e_inspector (4)
This commit is contained in:
ddidderr committed 2026-10-12 04:39:14 +02:00
1 parent 2dc1c6028d
commit 90ad602969
25 files changed
+6693

No files matched your search

@@ -0,0 +1,51 @@
using Gtk 4.0;
using Adw 1;
template $GhosttyImguiWidget: Adw.Bin {
styles [
"imgui",
]
Adw.Bin {
Gtk.GLArea gl_area {
auto-render: true;
// needs to be focusable so that we can receive events
focusable: true;
focus-on-click: true;
allowed-apis: gl;
realize => $realize();
unrealize => $unrealize();
resize => $resize();
render => $render();
EventControllerFocus {
enter => $focus_enter();
leave => $focus_leave();
}
EventControllerKey {
key-pressed => $key_pressed();
key-released => $key_released();
}
GestureClick {
pressed => $mouse_pressed();
released => $mouse_released();
button: 0;
}
EventControllerMotion {
motion => $mouse_motion();
}
EventControllerScroll {
scroll => $scroll();
flags: both_axes;
}
}
}
}
IMMulticontext im_context {
commit => $im_commit();
}
@@ -0,0 +1,10 @@
using Gtk 4.0;
template $GhosttyInspectorWidget: $GhosttyImguiWidget {
styles [
"inspector",
]
hexpand: true;
vexpand: true;
}
@@ -0,0 +1,38 @@
using Gtk 4.0;
using Adw 1;
template $GhosttyInspectorWindow: Adw.ApplicationWindow {
title: _("Ghostty: Terminal Inspector");
icon-name: "com.mitchellh.ghostty";
default-width: 1000;
default-height: 600;
styles [
"inspector",
]
content: Adw.ToolbarView {
[top]
Adw.HeaderBar {
title-widget: Adw.WindowTitle {
title: bind template.title;
};
}
Gtk.Box {
orientation: vertical;
spacing: 0;
hexpand: true;
vexpand: true;
$GhosttyDebugWarning {
visible: bind template.debug;
}
$GhosttyInspectorWidget inspector_widget {
notify::surface => $notify_inspector_surface();
surface: bind template.surface;
}
}
};
}