terminal: report overline in DECRQSS SGR response (#13653)

#11638

Report SGR 53 when the active cursor style has overline enabled.
This commit is contained in:
Mitchell Hashimoto
2026-08-05 15:20:19 -07:00
committed by GitHub
3 changed files with 27 additions and 16 deletions

View File

@@ -3600,58 +3600,63 @@ pub fn printAttributes(self: *Terminal, buf: []u8) ![]const u8 {
try writer.writeByte('0');
const pen = self.screens.active.cursor.style;
var attrs: [8]u8 = @splat(0);
var attrs: [9]u8 = @splat(0);
var i: usize = 0;
if (pen.flags.bold) {
attrs[i] = '1';
attrs[i] = 1;
i += 1;
}
if (pen.flags.faint) {
attrs[i] = '2';
attrs[i] = 2;
i += 1;
}
if (pen.flags.italic) {
attrs[i] = '3';
attrs[i] = 3;
i += 1;
}
if (pen.flags.underline != .none) {
attrs[i] = '4';
attrs[i] = 4;
i += 1;
}
if (pen.flags.overline) {
attrs[i] = 53;
i += 1;
}
if (pen.flags.blink) {
attrs[i] = '5';
attrs[i] = 5;
i += 1;
}
if (pen.flags.inverse) {
attrs[i] = '7';
attrs[i] = 7;
i += 1;
}
if (pen.flags.invisible) {
attrs[i] = '8';
attrs[i] = 8;
i += 1;
}
if (pen.flags.strikethrough) {
attrs[i] = '9';
attrs[i] = 9;
i += 1;
}
for (attrs[0..i]) |c| {
// Preserve underline styles. Kind of a hack to special case '4'
for (attrs[0..i]) |attr| {
// Preserve underline styles. Kind of a hack to special case 4
// here but its easier than changing how we do all attributes.
if (c == '4' and pen.flags.underline != .single) {
if (attr == 4 and pen.flags.underline != .single) {
try writer.print(";4:{}", .{@intFromEnum(pen.flags.underline)});
continue;
}
try writer.print(";{c}", .{c});
try writer.print(";{}", .{attr});
}
switch (pen.fg_color) {
@@ -14057,11 +14062,12 @@ test "Terminal: printAttributes" {
try t.setAttribute(.inverse);
try t.setAttribute(.invisible);
try t.setAttribute(.strikethrough);
try t.setAttribute(.overline);
try t.setAttribute(.{ .direct_color_fg = .{ .r = 100, .g = 200, .b = 255 } });
try t.setAttribute(.{ .direct_color_bg = .{ .r = 101, .g = 102, .b = 103 } });
defer t.setAttribute(.unset) catch unreachable;
const buf = try t.printAttributes(&storage);
try testing.expectEqualStrings("0;1;2;3;4;5;7;8;9;38:2::100:200:255;48:2::101:102:103", buf);
try testing.expectEqualStrings("0;1;2;3;4;53;5;7;8;9;38:2::100:200:255;48:2::101:102:103", buf);
}
const Case = struct {

View File

@@ -523,6 +523,7 @@ test "DECRQSS largest response fits fixed buffer" {
try t.setAttribute(.inverse);
try t.setAttribute(.invisible);
try t.setAttribute(.strikethrough);
try t.setAttribute(.overline);
try t.setAttribute(.{ .direct_color_fg = .{
.r = 255,
.g = 255,
@@ -538,7 +539,7 @@ test "DECRQSS largest response fits fixed buffer" {
const encoded = try Command.DECRQSS.sgr.encode(&t, &buf);
const expected =
"\x1BP1$r0;1;2;3;4:5;5;7;8;9" ++
"\x1BP1$r0;1;2;3;4:5;53;5;7;8;9" ++
";38:2::255:255:255" ++
";48:2::255:255:255m\x1B\\";
try testing.expectEqualStrings(expected, encoded);

View File

@@ -1449,13 +1449,17 @@ test "DECRQSS responses" {
s.nextSlice("\x1B[1m\x1BP$qm\x1B\\");
try S.expectResponse("\x1BP1$r0;1m\x1B\\");
// Overline
s.nextSlice("\x1B[0;53m\x1BP$qm\x1B\\");
try S.expectResponse("\x1BP1$r0;53m\x1B\\");
// Requests larger than the parser's fixed request buffer are ignored,
// and the next DCS command must still be processed normally.
s.nextSlice("\x1BP$qfoo\x1B\\");
try testing.expectEqual(@as(usize, 0), S.calls);
try testing.expect(!s.handler.semantic_failure);
s.nextSlice("\x1BP$qm\x1B\\");
try S.expectResponse("\x1BP1$r0;1m\x1B\\");
try S.expectResponse("\x1BP1$r0;53m\x1B\\");
}
test "DECRQSS without write effect is ignored" {