terminal: margins

pull/9342/head
Mitchell Hashimoto 2025-10-23 20:37:32 -07:00
parent c1e57dd330
commit 0df4d5c5a4
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
3 changed files with 52 additions and 70 deletions

View File

@ -121,7 +121,7 @@ pub fn TaggedUnion(
/// Returns the value type for the given tag.
pub fn Value(comptime tag: Tag) type {
@setEvalBranchQuota(2000);
@setEvalBranchQuota(10000);
inline for (@typeInfo(Union).@"union".fields) |field| {
const field_tag = @field(Tag, field.name);
if (field_tag == tag) return field.type;

View File

@ -75,6 +75,9 @@ pub const Action = union(Key) {
restore_mode: Mode,
request_mode: Mode,
request_mode_unknown: RawMode,
top_and_bottom_margin: Margin,
left_and_right_margin: Margin,
left_and_right_margin_ambiguous,
modify_key_format: ansi.ModifyKeyFormat,
protected_mode_off,
protected_mode_iso,
@ -128,6 +131,9 @@ pub const Action = union(Key) {
"restore_mode",
"request_mode",
"request_mode_unknown",
"top_and_bottom_margin",
"left_and_right_margin",
"left_and_right_margin_ambiguous",
"modify_key_format",
"protected_mode_off",
"protected_mode_iso",
@ -195,6 +201,11 @@ pub const Action = union(Key) {
mode: u16,
ansi: bool,
};
pub const Margin = extern struct {
top_left: u16,
bottom_right: u16,
};
};
/// Returns a type that can process a stream of tty control characters.
@ -1336,17 +1347,12 @@ pub fn Stream(comptime Handler: type) type {
'r' => switch (input.intermediates.len) {
// DECSTBM - Set Top and Bottom Margins
0 => if (@hasDecl(T, "setTopAndBottomMargin")) {
switch (input.params.len) {
0 => try self.handler.setTopAndBottomMargin(0, 0),
1 => try self.handler.setTopAndBottomMargin(input.params[0], 0),
2 => try self.handler.setTopAndBottomMargin(input.params[0], input.params[1]),
else => log.warn("invalid DECSTBM command: {f}", .{input}),
}
} else log.warn(
"unimplemented CSI callback: {f}",
.{input},
),
0 => switch (input.params.len) {
0 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = 0, .bottom_right = 0 }),
1 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = input.params[0], .bottom_right = 0 }),
2 => try self.handler.vt(.top_and_bottom_margin, .{ .top_left = input.params[0], .bottom_right = input.params[1] }),
else => log.warn("invalid DECSTBM command: {f}", .{input}),
},
1 => switch (input.intermediates[0]) {
// Restore Mode
@ -1377,21 +1383,16 @@ pub fn Stream(comptime Handler: type) type {
's' => switch (input.intermediates.len) {
// DECSLRM
0 => if (@hasDecl(T, "setLeftAndRightMargin")) {
switch (input.params.len) {
// CSI S is ambiguous with zero params so we defer
// to our handler to do the proper logic. If mode 69
// is set, then we should invoke DECSLRM, otherwise
// we should invoke SC.
0 => try self.handler.setLeftAndRightMarginAmbiguous(),
1 => try self.handler.setLeftAndRightMargin(input.params[0], 0),
2 => try self.handler.setLeftAndRightMargin(input.params[0], input.params[1]),
else => log.warn("invalid DECSLRM command: {f}", .{input}),
}
} else log.warn(
"unimplemented CSI callback: {f}",
.{input},
),
0 => switch (input.params.len) {
// CSI S is ambiguous with zero params so we defer
// to our handler to do the proper logic. If mode 69
// is set, then we should invoke DECSLRM, otherwise
// we should invoke SC.
0 => try self.handler.vt(.left_and_right_margin_ambiguous, {}),
1 => try self.handler.vt(.left_and_right_margin, .{ .top_left = input.params[0], .bottom_right = 0 }),
2 => try self.handler.vt(.left_and_right_margin, .{ .top_left = input.params[0], .bottom_right = input.params[1] }),
else => log.warn("invalid DECSLRM command: {f}", .{input}),
},
1 => switch (input.intermediates[0]) {
'?' => {
@ -2227,20 +2228,16 @@ test "stream: restore mode" {
const Self = @This();
called: bool = false,
pub fn setTopAndBottomMargin(self: *Self, t: u16, b: u16) !void {
_ = t;
_ = b;
self.called = true;
}
pub fn vt(
self: *@This(),
comptime action: anytype,
value: anytype,
self: *Self,
comptime action: Stream(Self).Action.Tag,
value: Stream(Self).Action.Value(action),
) !void {
_ = self;
_ = action;
_ = value;
switch (action) {
.top_and_bottom_margin => self.called = true,
else => {},
}
}
};
@ -2654,25 +2651,17 @@ test "stream: SCOSC" {
const Self = @This();
called: bool = false,
pub fn setLeftAndRightMargin(self: *Self, left: u16, right: u16) !void {
_ = self;
_ = left;
_ = right;
@panic("bad");
}
pub fn setLeftAndRightMarginAmbiguous(self: *Self) !void {
self.called = true;
}
pub fn vt(
self: *@This(),
comptime action: anytype,
value: anytype,
self: *Self,
comptime action: Stream(Self).Action.Tag,
value: Stream(Self).Action.Value(action),
) !void {
_ = self;
_ = action;
_ = value;
switch (action) {
.left_and_right_margin => @panic("bad"),
.left_and_right_margin_ambiguous => self.called = true,
else => {},
}
}
};

View File

@ -255,6 +255,15 @@ pub const StreamHandler = struct {
},
.request_mode => try self.requestMode(value.mode),
.request_mode_unknown => try self.requestModeUnknown(value.mode, value.ansi),
.top_and_bottom_margin => self.terminal.setTopAndBottomMargin(value.top_left, value.bottom_right),
.left_and_right_margin => self.terminal.setLeftAndRightMargin(value.top_left, value.bottom_right),
.left_and_right_margin_ambiguous => {
if (self.terminal.modes.get(.enable_left_and_right_margin)) {
self.terminal.setLeftAndRightMargin(0, 0);
} else {
self.terminal.saveCursor();
}
},
.modify_key_format => try self.setModifyKeyFormat(value),
.protected_mode_off => self.terminal.setProtectedMode(.off),
.protected_mode_iso => self.terminal.setProtectedMode(.iso),
@ -437,22 +446,6 @@ pub const StreamHandler = struct {
self.terminal.carriageReturn();
}
pub inline fn setTopAndBottomMargin(self: *StreamHandler, top: u16, bot: u16) !void {
self.terminal.setTopAndBottomMargin(top, bot);
}
pub inline fn setLeftAndRightMarginAmbiguous(self: *StreamHandler) !void {
if (self.terminal.modes.get(.enable_left_and_right_margin)) {
try self.setLeftAndRightMargin(0, 0);
} else {
try self.saveCursor();
}
}
pub inline fn setLeftAndRightMargin(self: *StreamHandler, left: u16, right: u16) !void {
self.terminal.setLeftAndRightMargin(left, right);
}
pub fn setModifyKeyFormat(self: *StreamHandler, format: terminal.ModifyKeyFormat) !void {
self.terminal.flags.modify_other_keys_2 = false;
switch (format) {