From b9bc61c0a40660a93f51dc9e5ea90c7428948d17 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 20 Jun 2023 09:34:29 -0700 Subject: [PATCH] terminal: parse underline color sequences (but do not handle yet) --- src/terminal/Terminal.zig | 3 +++ src/terminal/sgr.zig | 56 +++++++++++++++++++++++++++++++++++++-- src/terminal/stream.zig | 2 +- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 57f95841f..5a7622bae 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -461,6 +461,9 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void { self.screen.cursor.pen.attrs.underline = .none; }, + // TODO + .underline_color, .reset_underline_color => {}, + .blink => { log.warn("blink requested, but not implemented", .{}); self.screen.cursor.pen.attrs.blink = true; diff --git a/src/terminal/sgr.zig b/src/terminal/sgr.zig index 5d57162e8..c7fcda035 100644 --- a/src/terminal/sgr.zig +++ b/src/terminal/sgr.zig @@ -33,6 +33,8 @@ pub const Attribute = union(enum) { /// Underline the text underline: Underline, reset_underline: void, + underline_color: RGB, + reset_underline_color: void, /// Blink the text blink: void, @@ -210,8 +212,12 @@ pub const Parser = struct { 48 => if (slice.len >= 5 and slice[1] == 2) { self.idx += 4; - // In the 6-len form, ignore the 3rd param. - const rgb = slice[2..5]; + // In the 6-len form, ignore the 3rd param. Otherwise, use it. + const rgb = if (slice.len == 5) slice[2..5] else rgb: { + // Consume one more element + self.idx += 1; + break :rgb slice[3..6]; + }; // We use @truncate because the value should be 0 to 255. If // it isn't, the behavior is undefined so we just... truncate it. @@ -231,6 +237,29 @@ pub const Parser = struct { 49 => return Attribute{ .reset_bg = {} }, + 58 => if (slice.len >= 5 and slice[1] == 2) { + self.idx += 4; + + // In the 6-len form, ignore the 3rd param. Otherwise, use it. + const rgb = if (slice.len == 5) slice[2..5] else rgb: { + // Consume one more element + self.idx += 1; + break :rgb slice[3..6]; + }; + + // We use @truncate because the value should be 0 to 255. If + // it isn't, the behavior is undefined so we just... truncate it. + return Attribute{ + .underline_color = .{ + .r = @truncate(u8, rgb[0]), + .g = @truncate(u8, rgb[1]), + .b = @truncate(u8, rgb[2]), + }, + }; + }, + + 59 => return Attribute{ .reset_underline_color = {} }, + 90...97 => return Attribute{ // 82 instead of 90 to offset to "bright" colors .@"8_bright_fg" = @intToEnum(color.Name, slice[0] - 82), @@ -437,3 +466,26 @@ test "sgr: 256 color" { try testing.expect(p.next().? == .@"256_fg"); try testing.expect(p.next().? == .@"256_bg"); } + +test "sgr: underline color" { + { + const v = testParseColon(&[_]u16{ 58, 2, 1, 2, 3 }); + try testing.expect(v == .underline_color); + try testing.expectEqual(@as(u8, 1), v.underline_color.r); + try testing.expectEqual(@as(u8, 2), v.underline_color.g); + try testing.expectEqual(@as(u8, 3), v.underline_color.b); + } + + { + const v = testParseColon(&[_]u16{ 58, 2, 0, 1, 2, 3 }); + try testing.expect(v == .underline_color); + try testing.expectEqual(@as(u8, 1), v.underline_color.r); + try testing.expectEqual(@as(u8, 2), v.underline_color.g); + try testing.expectEqual(@as(u8, 3), v.underline_color.b); + } +} + +test "sgr: reset underline color" { + var p: Parser = .{ .params = &[_]u16{59} }; + try testing.expect(p.next().? == .reset_underline_color); +} diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig index b121f123e..93f3f5bc8 100644 --- a/src/terminal/stream.zig +++ b/src/terminal/stream.zig @@ -386,7 +386,7 @@ pub fn Stream(comptime Handler: type) type { 'm' => if (@hasDecl(T, "setAttribute")) { var p: sgr.Parser = .{ .params = action.params, .colon = action.sep == .colon }; while (p.next()) |attr| { - //log.info("SGR attribute: {}", .{attr}); + log.info("SGR attribute: {}", .{attr}); try self.handler.setAttribute(attr); } } else log.warn("unimplemented CSI callback: {}", .{action}),