terminal: convert C0

pull/9342/head
Mitchell Hashimoto 2025-10-23 15:09:30 -07:00
parent f7189d14b9
commit 2ef89c153a
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
4 changed files with 71 additions and 73 deletions

View File

@ -145,6 +145,7 @@ const Handler = struct {
) !void {
switch (action) {
.print => try self.t.print(value.cp),
else => {},
}
}
};

View File

@ -1,16 +1,19 @@
const std = @import("std");
const build_options = @import("terminal_options");
const assert = std.debug.assert;
const LibEnum = @import("../lib/enum.zig").Enum;
/// The available charset slots for a terminal.
pub const Slots = enum(u3) {
G0 = 0,
G1 = 1,
G2 = 2,
G3 = 3,
};
pub const Slots = LibEnum(
if (build_options.c_abi) .c else .zig,
&.{ "G0", "G1", "G2", "G3" },
);
/// The name of the active slots.
pub const ActiveSlot = enum { GL, GR };
pub const ActiveSlot = LibEnum(
if (build_options.c_abi) .c else .zig,
&.{ "GL", "GR" },
);
/// The list of supported character sets and their associated tables.
pub const Charset = enum {

View File

@ -4,8 +4,7 @@ const build_options = @import("terminal_options");
const assert = std.debug.assert;
const testing = std.testing;
const simd = @import("../simd/main.zig");
const LibEnum = @import("../lib/enum.zig").Enum;
const LibUnion = @import("../lib/union.zig").TaggedUnion;
const lib = @import("../lib/main.zig");
const Parser = @import("Parser.zig");
const ansi = @import("ansi.zig");
const charsets = @import("charsets.zig");
@ -28,20 +27,40 @@ const log = std.log.scoped(.stream);
/// do something else.
const debug = false;
const lib_target: lib.Target = if (build_options.c_abi) .c else .zig;
pub const Action = union(Key) {
print: Print,
bell,
backspace,
horizontal_tab: HorizontalTab,
linefeed,
carriage_return,
enquiry,
invoke_charset: InvokeCharset,
pub const Key = LibEnum(
if (build_options.c_abi) .c else .zig,
pub const Key = lib.Enum(
lib_target,
&.{
"print",
"bell",
"backspace",
"horizontal_tab",
"linefeed",
"carriage_return",
"enquiry",
"invoke_charset",
},
);
/// C ABI functions.
const c_union = LibUnion(@This(), extern struct {
x: u64,
});
const c_union = lib.TaggedUnion(
lib_target,
@This(),
// TODO: Before shipping an ABI-compatible libghostty, verify this.
// This was just arbitrarily chosen for now.
[8]u64,
);
pub const Tag = c_union.Tag;
pub const Value = c_union.Value;
pub const C = c_union.C;
@ -60,6 +79,16 @@ pub const Action = union(Key) {
return .{ .cp = @intCast(self.cp) };
}
};
pub const HorizontalTab = lib.Struct(lib_target, struct {
count: u16,
});
pub const InvokeCharset = lib.Struct(lib_target, struct {
bank: charsets.ActiveSlot,
charset: charsets.Slots,
locking: bool,
});
};
/// Returns a type that can process a stream of tty control characters.
@ -383,45 +412,14 @@ pub fn Stream(comptime Handler: type) type {
// We ignore SOH/STX: https://github.com/microsoft/terminal/issues/10786
.NUL, .SOH, .STX => {},
.ENQ => if (@hasDecl(T, "enquiry"))
try self.handler.enquiry()
else
log.warn("unimplemented execute: {x}", .{c}),
.BEL => if (@hasDecl(T, "bell"))
try self.handler.bell()
else
log.warn("unimplemented execute: {x}", .{c}),
.BS => if (@hasDecl(T, "backspace"))
try self.handler.backspace()
else
log.warn("unimplemented execute: {x}", .{c}),
.HT => if (@hasDecl(T, "horizontalTab"))
try self.handler.horizontalTab(1)
else
log.warn("unimplemented execute: {x}", .{c}),
.LF, .VT, .FF => if (@hasDecl(T, "linefeed"))
try self.handler.linefeed()
else
log.warn("unimplemented execute: {x}", .{c}),
.CR => if (@hasDecl(T, "carriageReturn"))
try self.handler.carriageReturn()
else
log.warn("unimplemented execute: {x}", .{c}),
.SO => if (@hasDecl(T, "invokeCharset"))
try self.handler.invokeCharset(.GL, .G1, false)
else
log.warn("unimplemented invokeCharset: {x}", .{c}),
.SI => if (@hasDecl(T, "invokeCharset"))
try self.handler.invokeCharset(.GL, .G0, false)
else
log.warn("unimplemented invokeCharset: {x}", .{c}),
.ENQ => try self.handler.vt(.enquiry, {}),
.BEL => try self.handler.vt(.bell, {}),
.BS => try self.handler.vt(.backspace, {}),
.HT => try self.handler.vt(.horizontal_tab, .{ .count = 1 }),
.LF, .VT, .FF => try self.handler.vt(.linefeed, {}),
.CR => try self.handler.vt(.carriage_return, {}),
.SO => try self.handler.vt(.invoke_charset, .{ .bank = .GL, .charset = .G1, .locking = false }),
.SI => try self.handler.vt(.invoke_charset, .{ .bank = .GL, .charset = .G0, .locking = false }),
else => log.warn("invalid C0 character, ignoring: 0x{x}", .{c}),
}
@ -1902,6 +1900,12 @@ pub fn Stream(comptime Handler: type) type {
};
}
test Action {
// Forces the C type to be reified when the target is C, ensuring
// all our types are C ABI compatible.
_ = Action.C;
}
test "stream: print" {
const H = struct {
c: ?u21 = 0,

View File

@ -195,6 +195,13 @@ pub const StreamHandler = struct {
) !void {
switch (action) {
.print => try self.terminal.print(value.cp),
.bell => self.bell(),
.backspace => self.terminal.backspace(),
.horizontal_tab => try self.horizontalTab(value.count),
.linefeed => try self.linefeed(),
.carriage_return => self.terminal.carriageReturn(),
.enquiry => try self.enquiry(),
.invoke_charset => self.terminal.invokeCharset(value.bank, value.charset, value.locking),
}
}
@ -338,15 +345,11 @@ pub const StreamHandler = struct {
try self.terminal.printRepeat(count);
}
pub inline fn bell(self: *StreamHandler) !void {
inline fn bell(self: *StreamHandler) void {
self.surfaceMessageWriter(.ring_bell);
}
pub inline fn backspace(self: *StreamHandler) !void {
self.terminal.backspace();
}
pub inline fn horizontalTab(self: *StreamHandler, count: u16) !void {
inline fn horizontalTab(self: *StreamHandler, count: u16) !void {
for (0..count) |_| {
const x = self.terminal.screen.cursor.x;
try self.terminal.horizontalTab();
@ -362,16 +365,12 @@ pub const StreamHandler = struct {
}
}
pub inline fn linefeed(self: *StreamHandler) !void {
inline fn linefeed(self: *StreamHandler) !void {
// Small optimization: call index instead of linefeed because they're
// identical and this avoids one layer of function call overhead.
try self.terminal.index();
}
pub inline fn carriageReturn(self: *StreamHandler) !void {
self.terminal.carriageReturn();
}
pub inline fn setCursorLeft(self: *StreamHandler, amount: u16) !void {
self.terminal.cursorLeft(amount);
}
@ -896,15 +895,6 @@ pub const StreamHandler = struct {
self.terminal.configureCharset(slot, set);
}
pub fn invokeCharset(
self: *StreamHandler,
active: terminal.CharsetActiveSlot,
slot: terminal.CharsetSlot,
single: bool,
) !void {
self.terminal.invokeCharset(active, slot, single);
}
pub fn fullReset(
self: *StreamHandler,
) !void {