mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-06-05 11:24:13 +00:00
401 lines
14 KiB
Zig
401 lines
14 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const assert = @import("../../quirks.zig").inlineAssert;
|
|
const Allocator = std.mem.Allocator;
|
|
const cimgui = @import("dcimgui");
|
|
const widgets = @import("../widgets.zig");
|
|
const units = @import("../units.zig");
|
|
const terminal = @import("../../terminal/main.zig");
|
|
const stylepkg = @import("../../terminal/style.zig");
|
|
|
|
/// Window names for the screen dockspace.
|
|
const window_info = "Info";
|
|
const window_cell = "Cell";
|
|
const window_pagelist = "PageList";
|
|
|
|
/// Screen information inspector widget.
|
|
pub const Info = struct {
|
|
pagelist: widgets.pagelist.Inspector,
|
|
cell_chooser: widgets.pagelist.CellChooser,
|
|
|
|
pub const empty: Info = .{
|
|
.pagelist = .empty,
|
|
.cell_chooser = .empty,
|
|
};
|
|
|
|
/// Draw the screen info contents.
|
|
pub fn draw(self: *Info, open: bool, data: struct {
|
|
/// The screen that we're inspecting.
|
|
screen: *terminal.Screen,
|
|
|
|
/// Which screen key we're viewing.
|
|
key: terminal.ScreenSet.Key,
|
|
|
|
/// Which screen is active (primary or alternate).
|
|
active_key: terminal.ScreenSet.Key,
|
|
|
|
/// Whether xterm modify other keys mode 2 is enabled.
|
|
modify_other_keys_2: bool,
|
|
|
|
/// Color palette for cursor color resolution.
|
|
color_palette: *const terminal.color.DynamicPalette,
|
|
}) void {
|
|
// Create the dockspace for this screen
|
|
const dockspace_id = cimgui.c.ImGui_GetID("Screen Dockspace");
|
|
_ = createDockSpace(dockspace_id);
|
|
|
|
const screen = data.screen;
|
|
|
|
// Info window
|
|
info: {
|
|
defer cimgui.c.ImGui_End();
|
|
if (!cimgui.c.ImGui_Begin(
|
|
window_info,
|
|
null,
|
|
cimgui.c.ImGuiWindowFlags_NoFocusOnAppearing,
|
|
)) break :info;
|
|
|
|
if (cimgui.c.ImGui_CollapsingHeader(
|
|
"Cursor",
|
|
cimgui.c.ImGuiTreeNodeFlags_DefaultOpen,
|
|
)) {
|
|
cursorTable(&screen.cursor);
|
|
cimgui.c.ImGui_Separator();
|
|
cursorStyle(
|
|
&screen.cursor,
|
|
&data.color_palette.current,
|
|
);
|
|
}
|
|
|
|
if (cimgui.c.ImGui_CollapsingHeader(
|
|
"Keyboard",
|
|
cimgui.c.ImGuiTreeNodeFlags_DefaultOpen,
|
|
)) keyboardTable(
|
|
screen,
|
|
data.modify_other_keys_2,
|
|
);
|
|
|
|
if (cimgui.c.ImGui_CollapsingHeader(
|
|
"Semantic Prompt",
|
|
cimgui.c.ImGuiTreeNodeFlags_DefaultOpen,
|
|
)) semanticPromptTable(&screen.semantic_prompt);
|
|
|
|
if (cimgui.c.ImGui_CollapsingHeader(
|
|
"Kitty Graphics",
|
|
cimgui.c.ImGuiTreeNodeFlags_DefaultOpen,
|
|
)) kittyGraphicsTable(&screen.kitty_images);
|
|
|
|
if (cimgui.c.ImGui_CollapsingHeader(
|
|
"Other Screen State",
|
|
cimgui.c.ImGuiTreeNodeFlags_DefaultOpen,
|
|
)) internalStateTable(screen);
|
|
}
|
|
|
|
// Cell window
|
|
cell: {
|
|
defer cimgui.c.ImGui_End();
|
|
if (!cimgui.c.ImGui_Begin(
|
|
window_cell,
|
|
null,
|
|
cimgui.c.ImGuiWindowFlags_NoFocusOnAppearing,
|
|
)) break :cell;
|
|
self.cell_chooser.draw(&screen.pages);
|
|
}
|
|
|
|
// PageList window
|
|
pagelist: {
|
|
defer cimgui.c.ImGui_End();
|
|
if (!cimgui.c.ImGui_Begin(
|
|
window_pagelist,
|
|
null,
|
|
cimgui.c.ImGuiWindowFlags_NoFocusOnAppearing,
|
|
)) break :pagelist;
|
|
self.pagelist.draw(&screen.pages);
|
|
}
|
|
|
|
// The remainder is the open state
|
|
if (!open) return;
|
|
|
|
// Show warning if viewing an inactive screen
|
|
if (data.key != data.active_key) {
|
|
cimgui.c.ImGui_TextColored(
|
|
.{ .x = 1.0, .y = 0.8, .z = 0.0, .w = 1.0 },
|
|
"⚠ Viewing inactive screen",
|
|
);
|
|
cimgui.c.ImGui_Separator();
|
|
}
|
|
}
|
|
|
|
/// Create the dock space for the screen inspector. This creates
|
|
/// a dedicated dock space for the screen inspector windows. But they
|
|
/// can of course be undocked and moved around as desired.
|
|
fn createDockSpace(dockspace_id: cimgui.c.ImGuiID) bool {
|
|
// Check if we need to set up the dockspace
|
|
const setup = cimgui.ImGui_DockBuilderGetNode(dockspace_id) == null;
|
|
|
|
if (setup) {
|
|
// Register our dockspace node
|
|
assert(cimgui.ImGui_DockBuilderAddNodeEx(
|
|
dockspace_id,
|
|
cimgui.ImGuiDockNodeFlagsPrivate.DockSpace,
|
|
) == dockspace_id);
|
|
|
|
// Dock windows into the space
|
|
cimgui.ImGui_DockBuilderDockWindow(window_info, dockspace_id);
|
|
cimgui.ImGui_DockBuilderDockWindow(window_cell, dockspace_id);
|
|
cimgui.ImGui_DockBuilderDockWindow(window_pagelist, dockspace_id);
|
|
cimgui.ImGui_DockBuilderFinish(dockspace_id);
|
|
}
|
|
|
|
// Create the dockspace
|
|
assert(cimgui.c.ImGui_DockSpaceEx(
|
|
dockspace_id,
|
|
.{ .x = 0, .y = 0 },
|
|
cimgui.c.ImGuiDockNodeFlags_None,
|
|
null,
|
|
) == dockspace_id);
|
|
return setup;
|
|
}
|
|
};
|
|
|
|
/// Render cursor state with a table of cursor-specific fields.
|
|
pub fn cursorTable(
|
|
cursor: *const terminal.Screen.Cursor,
|
|
) void {
|
|
if (!cimgui.c.ImGui_BeginTable(
|
|
"table_cursor",
|
|
2,
|
|
cimgui.c.ImGuiTableFlags_None,
|
|
)) return;
|
|
defer cimgui.c.ImGui_EndTable();
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Position (x, y)");
|
|
cimgui.c.ImGui_SameLine();
|
|
widgets.helpMarker("The current cursor position in the terminal grid (0-indexed).");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text("(%d, %d)", cursor.x, cursor.y);
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Hyperlink");
|
|
cimgui.c.ImGui_SameLine();
|
|
widgets.helpMarker("The active OSC8 hyperlink for newly printed characters.");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
if (cursor.hyperlink) |link| {
|
|
cimgui.c.ImGui_Text("%.*s", link.uri.len, link.uri.ptr);
|
|
} else {
|
|
cimgui.c.ImGui_TextDisabled("(none)");
|
|
}
|
|
|
|
{
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Pending Wrap");
|
|
cimgui.c.ImGui_SameLine();
|
|
widgets.helpMarker("The 'last column flag' (LCF). If set, the next character will force a soft-wrap to the next line.");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
var value: bool = cursor.pending_wrap;
|
|
_ = cimgui.c.ImGui_Checkbox("##pending_wrap", &value);
|
|
}
|
|
|
|
{
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Protected");
|
|
cimgui.c.ImGui_SameLine();
|
|
widgets.helpMarker("If enabled, new characters will have the protected attribute set, preventing erasure by certain sequences.");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
var value: bool = cursor.protected;
|
|
_ = cimgui.c.ImGui_Checkbox("##protected", &value);
|
|
}
|
|
}
|
|
|
|
/// Render cursor style information using the shared style table.
|
|
pub fn cursorStyle(cursor: *const terminal.Screen.Cursor, palette: ?*const terminal.color.Palette) void {
|
|
widgets.style.table(cursor.style, palette);
|
|
}
|
|
|
|
/// Render keyboard information with a table.
|
|
fn keyboardTable(
|
|
screen: *const terminal.Screen,
|
|
modify_other_keys_2: bool,
|
|
) void {
|
|
if (!cimgui.c.ImGui_BeginTable(
|
|
"table_keyboard",
|
|
2,
|
|
cimgui.c.ImGuiTableFlags_None,
|
|
)) return;
|
|
defer cimgui.c.ImGui_EndTable();
|
|
|
|
const kitty_flags = screen.kitty_keyboard.current();
|
|
|
|
{
|
|
cimgui.c.ImGui_TableNextRow();
|
|
{
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Mode");
|
|
}
|
|
{
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
const mode = if (kitty_flags.int() != 0) "kitty" else "legacy";
|
|
cimgui.c.ImGui_Text("%s", mode.ptr);
|
|
}
|
|
}
|
|
|
|
if (kitty_flags.int() != 0) {
|
|
const Flags = @TypeOf(kitty_flags);
|
|
inline for (@typeInfo(Flags).@"struct".fields) |field| {
|
|
{
|
|
const value = @field(kitty_flags, field.name);
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
{
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
const field_name = std.fmt.comptimePrint("{s}", .{field.name});
|
|
cimgui.c.ImGui_Text("%s", field_name.ptr);
|
|
}
|
|
{
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text(
|
|
"%s",
|
|
if (value) "true".ptr else "false".ptr,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
{
|
|
cimgui.c.ImGui_TableNextRow();
|
|
{
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Xterm modify keys");
|
|
}
|
|
{
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text(
|
|
"%s",
|
|
if (modify_other_keys_2) "true".ptr else "false".ptr,
|
|
);
|
|
}
|
|
}
|
|
} // keyboard mode info
|
|
}
|
|
|
|
/// Render kitty graphics information table.
|
|
pub fn kittyGraphicsTable(
|
|
kitty_images: *const terminal.kitty.graphics.ImageStorage,
|
|
) void {
|
|
if (!kitty_images.enabled()) {
|
|
cimgui.c.ImGui_TextDisabled("(Kitty graphics are disabled)");
|
|
return;
|
|
}
|
|
|
|
if (!cimgui.c.ImGui_BeginTable(
|
|
"##kitty_graphics",
|
|
2,
|
|
cimgui.c.ImGuiTableFlags_None,
|
|
)) return;
|
|
defer cimgui.c.ImGui_EndTable();
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Memory Usage");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text("%d bytes (%d KiB)", kitty_images.total_bytes, units.toKibiBytes(kitty_images.total_bytes));
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Memory Limit");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text("%d bytes (%d KiB)", kitty_images.total_limit, units.toKibiBytes(kitty_images.total_limit));
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Image Count");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text("%d", kitty_images.images.count());
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Placement Count");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text("%d", kitty_images.placements.count());
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Image Loading");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text("%s", if (kitty_images.loading != null) "true".ptr else "false".ptr);
|
|
}
|
|
|
|
/// Render internal terminal state table.
|
|
pub fn internalStateTable(
|
|
screen: *const terminal.Screen,
|
|
) void {
|
|
const pages = &screen.pages;
|
|
|
|
if (!cimgui.c.ImGui_BeginTable(
|
|
"##terminal_state",
|
|
2,
|
|
cimgui.c.ImGuiTableFlags_None,
|
|
)) return;
|
|
defer cimgui.c.ImGui_EndTable();
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Memory Usage");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text("%d bytes (%d KiB)", pages.page_size, units.toKibiBytes(pages.page_size));
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Memory Limit");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text("%d bytes (%d KiB)", pages.maxSize(), units.toKibiBytes(pages.maxSize()));
|
|
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Viewport Location");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
cimgui.c.ImGui_Text("%s", @tagName(pages.viewport).ptr);
|
|
}
|
|
|
|
/// Render semantic prompt state table.
|
|
pub fn semanticPromptTable(
|
|
semantic_prompt: *const terminal.Screen.SemanticPrompt,
|
|
) void {
|
|
if (!cimgui.c.ImGui_BeginTable(
|
|
"##semantic_prompt",
|
|
2,
|
|
cimgui.c.ImGuiTableFlags_None,
|
|
)) return;
|
|
defer cimgui.c.ImGui_EndTable();
|
|
|
|
{
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Seen");
|
|
cimgui.c.ImGui_SameLine();
|
|
widgets.helpMarker("Whether any semantic prompt markers (OSC 133) have been seen in this screen.");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
var value: bool = semantic_prompt.seen;
|
|
_ = cimgui.c.ImGui_Checkbox("##seen", &value);
|
|
}
|
|
|
|
{
|
|
cimgui.c.ImGui_TableNextRow();
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(0);
|
|
cimgui.c.ImGui_Text("Click Handling");
|
|
cimgui.c.ImGui_SameLine();
|
|
widgets.helpMarker("How click events are handled in prompts. Set via 'cl' or 'click_events' options in OSC 133.");
|
|
_ = cimgui.c.ImGui_TableSetColumnIndex(1);
|
|
switch (semantic_prompt.click) {
|
|
.none => cimgui.c.ImGui_TextDisabled("(none)"),
|
|
.click_events => cimgui.c.ImGui_Text("click_events"),
|
|
.cl => |cl| cimgui.c.ImGui_Text("cl=%s", @tagName(cl).ptr),
|
|
}
|
|
}
|
|
}
|