Fix pager rendering slowness.
This commit is contained in:
+64
-3
@@ -38,6 +38,11 @@ watch: c_uint = 0,
|
||||
/// True once the child process has exited and the PTY hung up.
|
||||
exited: bool = false,
|
||||
|
||||
/// Cell size in pixels, as last set by a resize. Kept because the XTWINOPS
|
||||
/// size queries report it and the answer has to come from somewhere; the
|
||||
/// widget owns the real geometry and hands it down here.
|
||||
cell_px: struct { w: u32 = 8, h: u32 = 16 } = .{},
|
||||
|
||||
/// Latest state reported by the child. Kept so a repeated report of the
|
||||
/// state we are already in doesn't wake the UI for nothing.
|
||||
status: Status = .idle,
|
||||
@@ -139,13 +144,23 @@ pub fn create(
|
||||
errdefer self.stream.deinit();
|
||||
|
||||
// Wire up the side effects we care about. `readonly` handles terminal
|
||||
// state but silently drops anything that needs to talk back to the
|
||||
// child; we need the writes so that queries (cursor position, device
|
||||
// attributes, in-band resize) get answers.
|
||||
// state but silently drops anything that needs to talk back to the child.
|
||||
//
|
||||
// Every query below has to be answered, not merely permitted to write.
|
||||
// `write_pty` is only the pipe; a query whose own effect is null is
|
||||
// dropped before it ever reaches that pipe. And an unanswered query is
|
||||
// not a missing feature, it is a stall: the standard way to test for a
|
||||
// capability is to send the query followed by a device attributes
|
||||
// request, and take the DA1 reply arriving alone as "unsupported".
|
||||
// A terminal that answers neither leaves the program waiting out its
|
||||
// own timeout, once per run, for every capability it probes.
|
||||
var effects = vt.TerminalStream.Handler.Effects.readonly;
|
||||
effects.write_pty = &effectWritePty;
|
||||
effects.title_changed = &effectTitleChanged;
|
||||
effects.progress_report = &effectProgressReport;
|
||||
effects.device_attributes = &effectDeviceAttributes;
|
||||
effects.color_scheme = &effectColorScheme;
|
||||
effects.size = &effectSize;
|
||||
self.stream.handler.effects = effects;
|
||||
|
||||
const shell = try defaultShell(alloc);
|
||||
@@ -268,6 +283,48 @@ fn effectWritePty(handler: *vt.TerminalStream.Handler, data: [:0]const u8) void
|
||||
}
|
||||
|
||||
/// Effect callback: OSC 0/2 changed the window title.
|
||||
/// The reply type an effect has to return, recovered from the effect's own
|
||||
/// signature. libghostty-vt exports the device attributes *request* enum by
|
||||
/// name but not the reply struct, and naming it this way can't drift from
|
||||
/// what the callback is actually required to return.
|
||||
fn EffectReply(comptime name: []const u8) type {
|
||||
const Opt = @FieldType(vt.TerminalStream.Handler.Effects, name);
|
||||
const Fn = @typeInfo(@typeInfo(Opt).optional.child).pointer.child;
|
||||
return @typeInfo(Fn).@"fn".return_type.?;
|
||||
}
|
||||
|
||||
/// Effect callback: the child asked what kind of terminal this is (CSI c and
|
||||
/// friends). The defaults describe a VT220 that does ANSI colour, which is
|
||||
/// what this actually is — the answer matters less than there being one.
|
||||
fn effectDeviceAttributes(
|
||||
_: *vt.TerminalStream.Handler,
|
||||
) EffectReply("device_attributes") {
|
||||
return .{};
|
||||
}
|
||||
|
||||
/// Effect callback: the child asked whether it is drawing on a light or dark
|
||||
/// background (CSI ? 996 n), so it can pick readable colours. Answered from
|
||||
/// the scheme the grid is actually painted in.
|
||||
fn effectColorScheme(
|
||||
_: *vt.TerminalStream.Handler,
|
||||
) ?vt.device_status.ColorScheme {
|
||||
return switch (theme.currentScheme()) {
|
||||
.light => .light,
|
||||
.dark => .dark,
|
||||
};
|
||||
}
|
||||
|
||||
/// Effect callback: the child asked for the terminal geometry (CSI 14/16/18 t).
|
||||
fn effectSize(handler: *vt.TerminalStream.Handler) ?vt.size_report.Size {
|
||||
const self = fromHandler(handler);
|
||||
return .{
|
||||
.rows = self.term.rows,
|
||||
.columns = self.term.cols,
|
||||
.cell_width = self.cell_px.w,
|
||||
.cell_height = self.cell_px.h,
|
||||
};
|
||||
}
|
||||
|
||||
fn effectTitleChanged(handler: *vt.TerminalStream.Handler) void {
|
||||
const self = fromHandler(handler);
|
||||
self.on_title(self.ctx, self.term.title.items);
|
||||
@@ -320,6 +377,10 @@ pub fn write(self: *Session, bytes: []const u8) void {
|
||||
|
||||
/// Resize the terminal grid and tell the child about it.
|
||||
pub fn resize(self: *Session, cols: u16, rows: u16, cell_w: u32, cell_h: u32) !void {
|
||||
// Recorded before the early return: the grid can keep its dimensions
|
||||
// across a font change that moves every pixel measurement.
|
||||
self.cell_px = .{ .w = cell_w, .h = cell_h };
|
||||
|
||||
if (cols == self.term.cols and rows == self.term.rows) return;
|
||||
|
||||
try self.stream.handler.resize(.{
|
||||
|
||||
Reference in New Issue
Block a user