terminal: stylistic changes for OSC terminators, 10/11 params

This commit is contained in:
Mitchell Hashimoto
2023-09-14 13:12:41 -07:00
parent dc14ca86ca
commit 19ef4a22a9
4 changed files with 84 additions and 33 deletions

View File

@ -6,6 +6,7 @@ const ansi = @import("ansi.zig");
const csi = @import("csi.zig");
const sgr = @import("sgr.zig");
pub const apc = @import("apc.zig");
pub const osc = @import("osc.zig");
pub const point = @import("point.zig");
pub const color = @import("color.zig");
pub const kitty = @import("kitty.zig");

View File

@ -95,17 +95,53 @@ pub const Command = union(enum) {
/// OSC 10 and OSC 11 default color report.
report_default_color: struct {
/// OSC 10 requests the foreground color, OSC 11 the background color.
kind: enum { foreground, background },
kind: DefaultColorKind,
/// We must reply with the same string terminator (ST) as used in the
/// request.
string_terminator: ?enum {
/// The preferred string terminator is ESC followed by \
st,
/// Some applications and terminals use BELL (0x07) as the string terminator.
bel,
} = null,
terminator: Terminator = .st,
},
pub const DefaultColorKind = enum {
foreground,
background,
pub fn code(self: DefaultColorKind) []const u8 {
return switch (self) {
.foreground => "10",
.background => "11",
};
}
};
};
/// The terminator used to end an OSC command. For OSC commands that demand
/// a response, we try to match the terminator used in the request since that
/// is most likely to be accepted by the calling program.
pub const Terminator = enum {
/// The preferred string terminator is ESC followed by \
st,
/// Some applications and terminals use BELL (0x07) as the string terminator.
bel,
/// Initialize the terminator based on the last byte seen. If the
/// last byte is a BEL then we use BEL, otherwise we just assume ST.
pub fn init(ch: ?u8) Terminator {
return switch (ch orelse return .st) {
0x07 => .bel,
else => .st,
};
}
/// The terminator as a string. This is static memory so it doesn't
/// need to be freed.
pub fn string(self: Terminator) []const u8 {
return switch (self) {
.st => "\x1b\\",
.bel => "\x07",
};
}
};
pub const Parser = struct {
@ -496,8 +532,10 @@ pub const Parser = struct {
}
/// End the sequence and return the command, if any. If the return value
/// is null, then no valid command was found.
pub fn end(self: *Parser, string_separator: ?u8) ?Command {
/// is null, then no valid command was found. The optional terminator_ch
/// is the final character in the OSC sequence. This is used to determine
/// the response terminator.
pub fn end(self: *Parser, terminator_ch: ?u8) ?Command {
if (!self.complete) {
log.warn("invalid OSC command: {s}", .{self.buf[0..self.buf_idx]});
return null;
@ -512,11 +550,10 @@ pub const Parser = struct {
}
switch (self.command) {
.report_default_color => |*c| {
c.string_terminator = if (string_separator == 0x07) .bel else .st;
},
.report_default_color => |*c| c.terminator = Terminator.init(terminator_ch),
else => {},
}
return self.command;
}
};
@ -763,7 +800,7 @@ test "OSC: report default foreground color" {
const cmd = p.end('\x1b').?;
try testing.expect(cmd == .report_default_color);
try testing.expectEqual(cmd.report_default_color.kind, .foreground);
try testing.expectEqual(cmd.report_default_color.string_terminator, .st);
try testing.expectEqual(cmd.report_default_color.terminator, .st);
}
test "OSC: report default background color" {
@ -778,5 +815,5 @@ test "OSC: report default background color" {
const cmd = p.end('\x07').?;
try testing.expect(cmd == .report_default_color);
try testing.expectEqual(cmd.report_default_color.kind, .background);
try testing.expectEqual(cmd.report_default_color.string_terminator.?, .bel);
try testing.expectEqual(cmd.report_default_color.terminator, .bel);
}

View File

@ -863,10 +863,7 @@ pub fn Stream(comptime Handler: type) type {
.report_default_color => |v| {
if (@hasDecl(T, "reportDefaultColor")) {
try self.handler.reportDefaultColor(
if (v.kind == .foreground) "10" else "11",
if (v.string_terminator == .st) "\x1b\\" else "\x07",
);
try self.handler.reportDefaultColor(v.kind, v.terminator);
} else log.warn("unimplemented OSC callback: {}", .{cmd});
},

View File

@ -1804,29 +1804,45 @@ const StreamHandler = struct {
}
}
/// Implements OSC 10 and OSC 11, which reports default foreground and background color respectively.
pub fn reportDefaultColor(self: *StreamHandler, osc_code: []const u8, string_terminator: ?[]const u8) !void {
/// Implements OSC 10 and OSC 11, which reports default foreground and
/// background color respectively.
pub fn reportDefaultColor(
self: *StreamHandler,
kind: terminal.osc.Command.DefaultColorKind,
terminator: terminal.osc.Terminator,
) !void {
if (self.osc_color_report_format == .none) return;
const color = switch (kind) {
.foreground => self.default_foreground_color,
.background => self.default_background_color,
};
var msg: termio.Message = .{ .write_small = .{} };
const color = if (std.mem.eql(u8, osc_code, "10")) self.default_foreground_color else self.default_background_color;
const resp = resp: {
if (self.osc_color_report_format == .bits16) {
break :resp try std.fmt.bufPrint(&msg.write_small.data, "\x1B]{s};rgb:{x:0>4}/{x:0>4}/{x:0>4}{s}", .{
osc_code,
const resp = switch (self.osc_color_report_format) {
.bits16 => try std.fmt.bufPrint(
&msg.write_small.data,
"\x1B]{s};rgb:{x:0>4}/{x:0>4}/{x:0>4}{s}",
.{
kind.code(),
@as(u16, color.r) * 257,
@as(u16, color.g) * 257,
@as(u16, color.b) * 257,
if (string_terminator) |st| st else "\x1b\\",
});
} else {
break :resp try std.fmt.bufPrint(&msg.write_small.data, "\x1B]{s};rgb:{x:0>2}/{x:0>2}/{x:0>2}{s}", .{
osc_code,
terminator.string(),
},
),
else => try std.fmt.bufPrint(
&msg.write_small.data,
"\x1B]{s};rgb:{x:0>2}/{x:0>2}/{x:0>2}{s}",
.{
kind.code(),
@as(u16, color.r),
@as(u16, color.g),
@as(u16, color.b),
if (string_terminator) |st| st else "\x1b\\",
});
}
terminator.string(),
},
),
};
msg.write_small.len = @intCast(resp.len);
self.messageWriter(msg);