diff options
| author | Can Celik <ogulcancelik@gmail.com> | 2026-04-04 19:22:04 +0300 |
|---|---|---|
| committer | Can Celik <ogulcancelik@gmail.com> | 2026-04-05 01:23:06 +0300 |
| commit | 41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch) | |
| tree | 3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/src/inspector/widgets/screen.zig | |
| parent | 55c138263e190a1e79d4b997bfb9397306c0ec1f (diff) | |
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/src/inspector/widgets/screen.zig')
| -rw-r--r-- | vendor/libghostty-vt/src/inspector/widgets/screen.zig | 400 |
1 files changed, 400 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/src/inspector/widgets/screen.zig b/vendor/libghostty-vt/src/inspector/widgets/screen.zig new file mode 100644 index 00000000..bfe961f4 --- /dev/null +++ b/vendor/libghostty-vt/src/inspector/widgets/screen.zig @@ -0,0 +1,400 @@ +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), + } + } +} |
