terminal: osc 104/105

pull/8590/head
Mitchell Hashimoto 2025-09-11 11:18:11 -07:00
parent d7523e3e37
commit 83dd578cc5
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
1 changed files with 136 additions and 0 deletions

View File

@ -22,6 +22,8 @@ pub const Operation = enum {
osc_17,
osc_18,
osc_19,
osc_104,
osc_105,
osc_110,
osc_111,
osc_112,
@ -53,6 +55,8 @@ pub fn parse(
return switch (op) {
.osc_4 => try parseGetSetAnsiColor(alloc, .osc_4, &it),
.osc_5 => try parseGetSetAnsiColor(alloc, .osc_5, &it),
.osc_104 => try parseResetAnsiColor(alloc, .osc_104, &it),
.osc_105 => try parseResetAnsiColor(alloc, .osc_105, &it),
.osc_10 => try parseGetSetDynamicColor(alloc, .foreground, &it),
.osc_11 => try parseGetSetDynamicColor(alloc, .background, &it),
.osc_12 => try parseGetSetDynamicColor(alloc, .cursor, &it),
@ -136,6 +140,60 @@ fn parseGetSetAnsiColor(
}
}
/// OSC 104/105: Reset ANSI Colors
fn parseResetAnsiColor(
alloc: Allocator,
comptime op: Operation,
it: *std.mem.TokenIterator(u8, .scalar),
) Allocator.Error!List {
var result: List = .{};
errdefer result.deinit(alloc);
while (true) {
const color_str = it.next() orelse {
// If no parameters are given, we reset the full table.
if (result.count() == 0) {
const req = try result.addOne(alloc);
req.* = switch (op) {
.osc_104 => .reset_palette,
.osc_105 => .reset_special,
else => comptime unreachable,
};
}
return result;
};
// Color must be numeric. u9 because that'll fit our palette + special
const color: u9 = std.fmt.parseInt(
u9,
color_str,
10,
) catch return result;
// Parse the color.
const target: Request.Target = switch (op) {
// OSC105 maps directly to the Special enum.
.osc_105 => .{ .special = std.meta.intToEnum(
SpecialColor,
std.math.cast(u3, color) orelse return result,
) catch return result },
// OSC104 maps 0-255 to palette, 256-259 to special offset
// by the palette count.
.osc_104 => if (std.math.cast(u8, color)) |idx| .{
.palette = idx,
} else .{ .special = std.meta.intToEnum(
SpecialColor,
std.math.cast(u3, color - 256) orelse return result,
) catch return result },
else => comptime unreachable,
};
const req = try result.addOne(alloc);
req.* = .{ .reset = target };
}
}
/// OSC 10-19: Get/Set Dynamic Colors
fn parseGetSetDynamicColor(
alloc: Allocator,
@ -202,6 +260,7 @@ pub const Request = union(enum) {
query: Target,
reset: Target,
reset_palette,
reset_special,
pub const Target = union(enum) {
palette: u8,
@ -425,6 +484,83 @@ test "osc4: multiple requests" {
}
}
test "osc104" {
const testing = std.testing;
const alloc = testing.allocator;
// Test every palette index
for (0..std.math.maxInt(u8)) |idx| {
// Simple color set
// printf '\e]104;0\\'
{
const body = try std.fmt.allocPrint(
alloc,
"{d}",
.{idx},
);
defer alloc.free(body);
var list = try parse(alloc, .osc_104, body);
defer list.deinit(alloc);
try testing.expectEqual(1, list.count());
try testing.expectEqual(
Request{ .reset = .{ .palette = @intCast(idx) } },
list.at(0).*,
);
}
}
// Test every special color
for (0..@typeInfo(SpecialColor).@"enum".fields.len) |i| {
const special = try std.meta.intToEnum(SpecialColor, i);
// Simple color set
// printf '\e]104;256\\'
{
const body = try std.fmt.allocPrint(
alloc,
"{d}",
.{256 + i},
);
defer alloc.free(body);
var list = try parse(alloc, .osc_104, body);
defer list.deinit(alloc);
try testing.expectEqual(1, list.count());
try testing.expectEqual(
Request{ .reset = .{ .special = special } },
list.at(0).*,
);
}
}
}
test "osc104 reset all" {
const testing = std.testing;
const alloc = testing.allocator;
var list = try parse(alloc, .osc_104, "");
defer list.deinit(alloc);
try testing.expectEqual(1, list.count());
try testing.expectEqual(
Request{ .reset_palette = {} },
list.at(0).*,
);
}
test "osc105 reset all" {
const testing = std.testing;
const alloc = testing.allocator;
var list = try parse(alloc, .osc_105, "");
defer list.deinit(alloc);
try testing.expectEqual(1, list.count());
try testing.expectEqual(
Request{ .reset_special = {} },
list.at(0).*,
);
}
// OSC 10-19: Get/Set Dynamic Colors
test "dynamic" {
const testing = std.testing;