diff --git a/src/benchmark/TerminalStream.zig b/src/benchmark/TerminalStream.zig index 23356ba22..0a993c42b 100644 --- a/src/benchmark/TerminalStream.zig +++ b/src/benchmark/TerminalStream.zig @@ -145,6 +145,7 @@ const Handler = struct { ) !void { switch (action) { .print => try self.t.print(value.cp), + else => {}, } } }; diff --git a/src/terminal/charsets.zig b/src/terminal/charsets.zig index 66d6566e3..9d49832df 100644 --- a/src/terminal/charsets.zig +++ b/src/terminal/charsets.zig @@ -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 { diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index a26118f3f..555326607 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -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, diff --git a/src/termio/stream_handler.zig b/src/termio/stream_handler.zig index b9ab7a2b4..d8c05f2f2 100644 --- a/src/termio/stream_handler.zig +++ b/src/termio/stream_handler.zig @@ -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 {