1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
const std = @import("std");
const assert = @import("../quirks.zig").inlineAssert;
const RGB = @import("color.zig").RGB;
/// A single X11 color entry.
pub const Entry = struct {
/// Color name. Null-terminated so it can be exposed through the C API
/// without runtime allocation.
name: [:0]const u8,
color: RGB,
};
/// All X11 colors in rgb.txt file order.
///
/// This is kept separate from `map` because the C API needs stable file order
/// and null-terminated names. `ColorMap` keys do not provide that contract.
pub const entries: []const Entry = entriesArray();
/// The map of all available X11 colors.
pub const map = colorMap();
pub const ColorMap = std.StaticStringMapWithEql(
RGB,
std.static_string_map.eqlAsciiIgnoreCase,
);
fn entriesArray() []const Entry {
@setEvalBranchQuota(1_000_000);
const len = std.mem.count(u8, data, "\n");
var result: [len]Entry = undefined;
// Parse the line. This is not very robust parsing, because we expect
// a very exact format for rgb.txt. However, this is all done at comptime
// so if our data is bad, we should hopefully get an error here or one
// of our unit tests will catch it.
var iter = std.mem.splitScalar(u8, data, '\n');
var i: usize = 0;
while (iter.next()) |raw_line| {
// Trim \r so this works with both LF and CRLF line endings,
// since git may convert rgb.txt to CRLF on Windows checkouts.
const line = if (raw_line.len > 0 and raw_line[raw_line.len - 1] == '\r')
raw_line[0 .. raw_line.len - 1]
else
raw_line;
if (line.len == 0) continue;
const r = try std.fmt.parseInt(u8, std.mem.trim(u8, line[0..3], " "), 10);
const g = try std.fmt.parseInt(u8, std.mem.trim(u8, line[4..7], " "), 10);
const b = try std.fmt.parseInt(u8, std.mem.trim(u8, line[8..11], " "), 10);
const name = std.mem.trim(u8, line[12..], " \t");
var name_z: [name.len:0]u8 = undefined;
@memcpy(name_z[0..name.len], name);
name_z[name.len] = 0;
const final_name = name_z;
result[i] = .{
.name = final_name[0..name.len :0],
.color = .{ .r = r, .g = g, .b = b },
};
i += 1;
}
assert(i == len);
const final = result;
return &final;
}
fn colorMap() ColorMap {
@setEvalBranchQuota(1_000_000);
const KV = struct { []const u8, RGB };
var kvs: [entries.len]KV = undefined;
for (entries, 0..) |entry, i| kvs[i] = .{ entry.name, entry.color };
return .initComptime(kvs);
}
/// This is the rgb.txt file from the X11 project. This was last sourced
/// from this location: https://gitlab.freedesktop.org/xorg/app/rgb
/// This data is licensed under the MIT/X11 license while this Zig file is
/// licensed under the same license as Ghostty.
const data = @embedFile("res/rgb.txt");
test {
const testing = std.testing;
try testing.expectEqual(null, map.get("nosuchcolor"));
try testing.expectEqual(RGB{ .r = 255, .g = 255, .b = 255 }, map.get("white").?);
try testing.expectEqual(RGB{ .r = 0, .g = 250, .b = 154 }, map.get("medium spring green"));
try testing.expectEqual(RGB{ .r = 34, .g = 139, .b = 34 }, map.get("ForestGreen"));
try testing.expectEqual(RGB{ .r = 34, .g = 139, .b = 34 }, map.get("FoReStGReen"));
try testing.expectEqual(RGB{ .r = 0, .g = 0, .b = 0 }, map.get("black"));
try testing.expectEqual(RGB{ .r = 255, .g = 0, .b = 0 }, map.get("red"));
try testing.expectEqual(RGB{ .r = 0, .g = 255, .b = 0 }, map.get("green"));
try testing.expectEqual(RGB{ .r = 0, .g = 0, .b = 255 }, map.get("blue"));
try testing.expectEqual(RGB{ .r = 255, .g = 255, .b = 255 }, map.get("white"));
try testing.expectEqual(RGB{ .r = 124, .g = 252, .b = 0 }, map.get("lawngreen"));
try testing.expectEqual(RGB{ .r = 0, .g = 250, .b = 154 }, map.get("mediumspringgreen"));
try testing.expectEqual(RGB{ .r = 34, .g = 139, .b = 34 }, map.get("forestgreen"));
}
test "entries" {
const testing = std.testing;
try testing.expect(entries.len > 700);
for (entries) |entry| {
try testing.expectEqual(entry.color, map.get(entry.name).?);
try testing.expectEqual(@as(u8, 0), entry.name.ptr[entry.name.len]);
}
try testing.expectEqualStrings("snow", entries[0].name);
}
|