terminal: convert modes

pull/9342/head
Mitchell Hashimoto 2025-10-23 20:07:39 -07:00
parent dc5406781f
commit 94a8fa05cb
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
2 changed files with 48 additions and 39 deletions

View File

@ -69,6 +69,10 @@ pub const Action = union(Key) {
tab_clear_all,
tab_set,
tab_reset,
set_mode: Mode,
reset_mode: Mode,
save_mode: Mode,
restore_mode: Mode,
pub const Key = lib.Enum(
lib_target,
@ -112,6 +116,10 @@ pub const Action = union(Key) {
"tab_clear_all",
"tab_set",
"tab_reset",
"set_mode",
"reset_mode",
"save_mode",
"restore_mode",
},
);
@ -160,6 +168,16 @@ pub const Action = union(Key) {
row: u16,
col: u16,
};
pub const Mode = struct {
mode: modes.Mode,
pub const C = u16;
pub fn cval(self: Mode) Mode.C {
return @bitCast(self.mode);
}
};
};
/// Returns a type that can process a stream of tty control characters.
@ -1022,7 +1040,7 @@ pub fn Stream(comptime Handler: type) type {
},
// SM - Set Mode
'h' => if (@hasDecl(T, "setMode")) mode: {
'h' => mode: {
const ansi_mode = ansi: {
if (input.intermediates.len == 0) break :ansi true;
if (input.intermediates.len == 1 and
@ -1034,15 +1052,15 @@ pub fn Stream(comptime Handler: type) type {
for (input.params) |mode_int| {
if (modes.modeFromInt(mode_int, ansi_mode)) |mode| {
try self.handler.setMode(mode, true);
try self.handler.vt(.set_mode, .{ .mode = mode });
} else {
log.warn("unimplemented mode: {}", .{mode_int});
}
}
} else log.warn("unimplemented CSI callback: {f}", .{input}),
},
// RM - Reset Mode
'l' => if (@hasDecl(T, "setMode")) mode: {
'l' => mode: {
const ansi_mode = ansi: {
if (input.intermediates.len == 0) break :ansi true;
if (input.intermediates.len == 1 and
@ -1054,12 +1072,12 @@ pub fn Stream(comptime Handler: type) type {
for (input.params) |mode_int| {
if (modes.modeFromInt(mode_int, ansi_mode)) |mode| {
try self.handler.setMode(mode, false);
try self.handler.vt(.reset_mode, .{ .mode = mode });
} else {
log.warn("unimplemented mode: {}", .{mode_int});
}
}
} else log.warn("unimplemented CSI callback: {f}", .{input}),
},
// SGR - Select Graphic Rendition
'm' => switch (input.intermediates.len) {
@ -1304,10 +1322,10 @@ pub fn Stream(comptime Handler: type) type {
1 => switch (input.intermediates[0]) {
// Restore Mode
'?' => if (@hasDecl(T, "restoreMode")) {
'?' => {
for (input.params) |mode_int| {
if (modes.modeFromInt(mode_int, false)) |mode| {
try self.handler.restoreMode(mode);
try self.handler.vt(.restore_mode, .{ .mode = mode });
} else {
log.warn(
"unimplemented restore mode: {}",
@ -1348,10 +1366,10 @@ pub fn Stream(comptime Handler: type) type {
),
1 => switch (input.intermediates[0]) {
'?' => if (@hasDecl(T, "saveMode")) {
'?' => {
for (input.params) |mode_int| {
if (modes.modeFromInt(mode_int, false)) |mode| {
try self.handler.saveMode(mode);
try self.handler.vt(.save_mode, .{ .mode = mode });
} else {
log.warn(
"unimplemented save mode: {}",
@ -2091,19 +2109,17 @@ test "stream: cursor right (CUF)" {
test "stream: dec set mode (SM) and reset mode (RM)" {
const H = struct {
mode: modes.Mode = @as(modes.Mode, @enumFromInt(1)),
pub fn setMode(self: *@This(), mode: modes.Mode, v: bool) !void {
self.mode = @as(modes.Mode, @enumFromInt(1));
if (v) self.mode = mode;
}
pub fn vt(
self: *@This(),
comptime action: anytype,
value: anytype,
) !void {
_ = self;
_ = action;
_ = value;
switch (action) {
.set_mode => self.mode = value.mode,
.reset_mode => self.mode = @as(modes.Mode, @enumFromInt(1)),
else => {},
}
}
};
@ -2123,19 +2139,16 @@ test "stream: ansi set mode (SM) and reset mode (RM)" {
const H = struct {
mode: ?modes.Mode = null,
pub fn setMode(self: *@This(), mode: modes.Mode, v: bool) !void {
self.mode = null;
if (v) self.mode = mode;
}
pub fn vt(
self: *@This(),
comptime action: anytype,
value: anytype,
) !void {
_ = self;
_ = action;
_ = value;
switch (action) {
.set_mode => self.mode = value.mode,
.reset_mode => self.mode = null,
else => {},
}
}
};

View File

@ -243,6 +243,16 @@ pub const StreamHandler = struct {
.tab_clear_all => self.terminal.tabClear(.all),
.tab_set => self.terminal.tabSet(),
.tab_reset => self.terminal.tabReset(),
.set_mode => try self.setMode(value.mode, true),
.reset_mode => try self.setMode(value.mode, false),
.save_mode => self.terminal.modes.save(value.mode),
.restore_mode => {
// For restore mode we have to restore but if we set it, we
// always have to call setMode because setting some modes have
// side effects and we want to make sure we process those.
const v = self.terminal.modes.restore(value.mode);
try self.setMode(value.mode, v);
},
}
}
@ -470,20 +480,6 @@ pub const StreamHandler = struct {
self.messageWriter(msg);
}
pub inline fn saveMode(self: *StreamHandler, mode: terminal.Mode) !void {
// log.debug("save mode={}", .{mode});
self.terminal.modes.save(mode);
}
pub inline fn restoreMode(self: *StreamHandler, mode: terminal.Mode) !void {
// For restore mode we have to restore but if we set it, we
// always have to call setMode because setting some modes have
// side effects and we want to make sure we process those.
const v = self.terminal.modes.restore(mode);
// log.debug("restore mode={} v={}", .{ mode, v });
try self.setMode(mode, v);
}
pub fn setMode(self: *StreamHandler, mode: terminal.Mode, enabled: bool) !void {
// Note: this function doesn't need to grab the render state or
// terminal locks because it is only called from process() which