sgr: make C compat

pull/9342/head
Mitchell Hashimoto 2025-10-24 11:40:10 -07:00
parent 5ba451d073
commit 56376a8a38
No known key found for this signature in database
GPG Key ID: 523D5DC389D273BC
3 changed files with 114 additions and 11 deletions

View File

@ -104,7 +104,7 @@ pub fn TaggedUnion(
@tagName(tag), @tagName(tag),
value: { value: {
switch (@typeInfo(@TypeOf(v))) { switch (@typeInfo(@TypeOf(v))) {
.@"enum", .@"struct", .@"union" => if (@hasDecl(@TypeOf(v), "cval")) v.cval(), .@"enum", .@"struct", .@"union" => if (@hasDecl(@TypeOf(v), "cval")) break :value v.cval(),
else => {}, else => {},
} }

View File

@ -68,6 +68,12 @@ pub const Name = enum(u8) {
// Remainders are valid unnamed values in the 256 color palette. // Remainders are valid unnamed values in the 256 color palette.
_, _,
pub const C = u8;
pub fn cval(self: Name) C {
return @intFromEnum(self);
}
/// Default colors for tagged values. /// Default colors for tagged values.
pub fn default(self: Name) !RGB { pub fn default(self: Name) !RGB {
return switch (self) { return switch (self) {
@ -179,6 +185,20 @@ pub const RGB = packed struct(u24) {
g: u8 = 0, g: u8 = 0,
b: u8 = 0, b: u8 = 0,
pub const C = extern struct {
r: u8,
g: u8,
b: u8,
};
pub fn cval(self: RGB) C {
return .{
.r = self.r,
.g = self.g,
.b = self.b,
};
}
pub fn eql(self: RGB, other: RGB) bool { pub fn eql(self: RGB, other: RGB) bool {
return self.r == other.r and self.g == other.g and self.b == other.b; return self.r == other.r and self.g == other.g and self.b == other.b;
} }

View File

@ -1,26 +1,22 @@
//! SGR (Select Graphic Rendition) attrinvbute parsing and types. //! SGR (Select Graphic Rendition) attrinvbute parsing and types.
const std = @import("std"); const std = @import("std");
const build_options = @import("terminal_options");
const assert = std.debug.assert; const assert = std.debug.assert;
const testing = std.testing; const testing = std.testing;
const lib = @import("../lib/main.zig");
const color = @import("color.zig"); const color = @import("color.zig");
const SepList = @import("Parser.zig").Action.CSI.SepList; const SepList = @import("Parser.zig").Action.CSI.SepList;
/// Attribute type for SGR const lib_target: lib.Target = if (build_options.c_abi) .c else .zig;
pub const Attribute = union(enum) {
pub const Tag = std.meta.FieldEnum(Attribute);
/// Attribute type for SGR
pub const Attribute = union(Tag) {
/// Unset all attributes /// Unset all attributes
unset, unset,
/// Unknown attribute, the raw CSI command parameters are here. /// Unknown attribute, the raw CSI command parameters are here.
unknown: struct { unknown: Unknown,
/// Full is the full SGR input.
full: []const u16,
/// Partial is the remaining, where we got hung up.
partial: []const u16,
},
/// Bold the text. /// Bold the text.
bold, bold,
@ -85,6 +81,68 @@ pub const Attribute = union(enum) {
/// Set foreground color as 256-color palette. /// Set foreground color as 256-color palette.
@"256_fg": u8, @"256_fg": u8,
pub const Tag = lib.Enum(
lib_target,
&.{
"unset",
"unknown",
"bold",
"reset_bold",
"italic",
"reset_italic",
"faint",
"underline",
"reset_underline",
"underline_color",
"256_underline_color",
"reset_underline_color",
"overline",
"reset_overline",
"blink",
"reset_blink",
"inverse",
"reset_inverse",
"invisible",
"reset_invisible",
"strikethrough",
"reset_strikethrough",
"direct_color_fg",
"direct_color_bg",
"8_bg",
"8_fg",
"reset_fg",
"reset_bg",
"8_bright_bg",
"8_bright_fg",
"256_bg",
"256_fg",
},
);
pub const Unknown = struct {
/// Full is the full SGR input.
full: []const u16,
/// Partial is the remaining, where we got hung up.
partial: []const u16,
pub const C = extern struct {
full_ptr: [*]const u16,
full_len: usize,
partial_ptr: [*]const u16,
partial_len: usize,
};
pub fn cval(self: Unknown) Unknown.C {
return .{
.full_ptr = self.full.ptr,
.full_len = self.full.len,
.partial_ptr = self.partial.ptr,
.partial_len = self.partial.len,
};
}
};
pub const Underline = enum(u3) { pub const Underline = enum(u3) {
none = 0, none = 0,
single = 1, single = 1,
@ -92,7 +150,28 @@ pub const Attribute = union(enum) {
curly = 3, curly = 3,
dotted = 4, dotted = 4,
dashed = 5, dashed = 5,
pub const C = u8;
pub fn cval(self: Underline) Underline.C {
return @intFromEnum(self);
}
}; };
/// C ABI functions.
const c_union = lib.TaggedUnion(
lib_target,
@This(),
// Padding size for C ABI compatibility.
// Largest variant is Unknown.C: 2 pointers + 2 usize = 32 bytes on 64-bit.
// We use [8]u64 (64 bytes) to allow room for future expansion while
// maintaining ABI compatibility.
[8]u64,
);
pub const Value = c_union.Value;
pub const C = c_union.C;
pub const CValue = c_union.CValue;
pub const cval = c_union.cval;
}; };
/// Parser parses the attributes from a list of SGR parameters. /// Parser parses the attributes from a list of SGR parameters.
@ -380,6 +459,10 @@ fn testParseColon(params: []const u16) Attribute {
return p.next().?; return p.next().?;
} }
test "sgr: Attribute C compat" {
_ = Attribute.C;
}
test "sgr: Parser" { test "sgr: Parser" {
try testing.expect(testParse(&[_]u16{}) == .unset); try testing.expect(testParse(&[_]u16{}) == .unset);
try testing.expect(testParse(&[_]u16{0}) == .unset); try testing.expect(testParse(&[_]u16{0}) == .unset);