mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
add ability to specify RGB colors as names from the X11 rgb name list
This commit is contained in:

committed by
Mitchell Hashimoto

parent
c24d7d6de6
commit
bc1544a3f0
53
build.zig
53
build.zig
@ -213,6 +213,8 @@ pub fn build(b: *std.Build) !void {
|
|||||||
exe_options.addOption(renderer.Impl, "renderer", renderer_impl);
|
exe_options.addOption(renderer.Impl, "renderer", renderer_impl);
|
||||||
exe_options.addOption(bool, "libadwaita", libadwaita);
|
exe_options.addOption(bool, "libadwaita", libadwaita);
|
||||||
|
|
||||||
|
createRGBNames(b);
|
||||||
|
|
||||||
// Exe
|
// Exe
|
||||||
if (exe_) |exe| {
|
if (exe_) |exe| {
|
||||||
exe.root_module.addOptions("build_options", exe_options);
|
exe.root_module.addOptions("build_options", exe_options);
|
||||||
@ -220,6 +222,8 @@ pub fn build(b: *std.Build) !void {
|
|||||||
// Add the shared dependencies
|
// Add the shared dependencies
|
||||||
_ = try addDeps(b, exe, static);
|
_ = try addDeps(b, exe, static);
|
||||||
|
|
||||||
|
addRGBNames(exe);
|
||||||
|
|
||||||
// If we're in NixOS but not in the shell environment then we issue
|
// If we're in NixOS but not in the shell environment then we issue
|
||||||
// a warning because the rpath may not be setup properly.
|
// a warning because the rpath may not be setup properly.
|
||||||
const is_nixos = is_nixos: {
|
const is_nixos = is_nixos: {
|
||||||
@ -461,6 +465,8 @@ pub fn build(b: *std.Build) !void {
|
|||||||
lib.linkLibC();
|
lib.linkLibC();
|
||||||
lib.root_module.addOptions("build_options", exe_options);
|
lib.root_module.addOptions("build_options", exe_options);
|
||||||
|
|
||||||
|
addRGBNames(lib);
|
||||||
|
|
||||||
// Create a single static lib with all our dependencies merged
|
// Create a single static lib with all our dependencies merged
|
||||||
var lib_list = try addDeps(b, lib, true);
|
var lib_list = try addDeps(b, lib, true);
|
||||||
try lib_list.append(lib.getEmittedBin());
|
try lib_list.append(lib.getEmittedBin());
|
||||||
@ -490,6 +496,8 @@ pub fn build(b: *std.Build) !void {
|
|||||||
lib.linkLibC();
|
lib.linkLibC();
|
||||||
lib.root_module.addOptions("build_options", exe_options);
|
lib.root_module.addOptions("build_options", exe_options);
|
||||||
|
|
||||||
|
addRGBNames(lib);
|
||||||
|
|
||||||
// Create a single static lib with all our dependencies merged
|
// Create a single static lib with all our dependencies merged
|
||||||
var lib_list = try addDeps(b, lib, true);
|
var lib_list = try addDeps(b, lib, true);
|
||||||
try lib_list.append(lib.getEmittedBin());
|
try lib_list.append(lib.getEmittedBin());
|
||||||
@ -603,6 +611,9 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.root_source_file = .{ .path = "src/main_wasm.zig" },
|
.root_source_file = .{ .path = "src/main_wasm.zig" },
|
||||||
.target = b.resolveTargetQuery(wasm_crosstarget),
|
.target = b.resolveTargetQuery(wasm_crosstarget),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addRGBNames(main_test);
|
||||||
|
|
||||||
main_test.root_module.addOptions("build_options", exe_options);
|
main_test.root_module.addOptions("build_options", exe_options);
|
||||||
_ = try addDeps(b, main_test, true);
|
_ = try addDeps(b, main_test, true);
|
||||||
test_step.dependOn(&main_test.step);
|
test_step.dependOn(&main_test.step);
|
||||||
@ -638,6 +649,9 @@ pub fn build(b: *std.Build) !void {
|
|||||||
.target = target,
|
.target = target,
|
||||||
.filter = test_filter,
|
.filter = test_filter,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addRGBNames(main_test);
|
||||||
|
|
||||||
{
|
{
|
||||||
if (emit_test_exe) b.installArtifact(main_test);
|
if (emit_test_exe) b.installArtifact(main_test);
|
||||||
_ = try addDeps(b, main_test, true);
|
_ = try addDeps(b, main_test, true);
|
||||||
@ -916,6 +930,45 @@ fn addDeps(
|
|||||||
return static_libs;
|
return static_libs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var generate_rgb_names: *std.Build.Step.Run = undefined;
|
||||||
|
var generate_rgb_names_output: std.Build.LazyPath = undefined;
|
||||||
|
|
||||||
|
fn createRGBNames(b: *std.Build) void {
|
||||||
|
const gen = b.addExecutable(
|
||||||
|
.{
|
||||||
|
.name = "generate-rgb-names",
|
||||||
|
.root_source_file = .{
|
||||||
|
.path = "src/generate_rgb_names.zig",
|
||||||
|
},
|
||||||
|
.target = b.host,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const rgb = b.dependency("rgb", .{});
|
||||||
|
|
||||||
|
gen.root_module.addAnonymousImport(
|
||||||
|
"rgb",
|
||||||
|
.{
|
||||||
|
.root_source_file = .{
|
||||||
|
.path = rgb.builder.pathFromRoot("rgb.txt"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
generate_rgb_names = b.addRunArtifact(gen);
|
||||||
|
generate_rgb_names_output = generate_rgb_names.captureStdOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addRGBNames(exe: *std.Build.Step.Compile) void {
|
||||||
|
exe.step.dependOn(&generate_rgb_names.step);
|
||||||
|
exe.root_module.addAnonymousImport(
|
||||||
|
"rgb_names",
|
||||||
|
.{
|
||||||
|
.root_source_file = generate_rgb_names_output,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn benchSteps(
|
fn benchSteps(
|
||||||
b: *std.Build,
|
b: *std.Build,
|
||||||
target: std.Build.ResolvedTarget,
|
target: std.Build.ResolvedTarget,
|
||||||
|
@ -48,5 +48,9 @@
|
|||||||
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/53acae071801e0de6ed160315869abb9bdaf20fa.tar.gz",
|
.url = "https://github.com/mbadolato/iTerm2-Color-Schemes/archive/53acae071801e0de6ed160315869abb9bdaf20fa.tar.gz",
|
||||||
.hash = "12201575c5a2b21c2e110593773040cddcd357544038092d18bd98fc5a2141354bbd",
|
.hash = "12201575c5a2b21c2e110593773040cddcd357544038092d18bd98fc5a2141354bbd",
|
||||||
},
|
},
|
||||||
|
.rgb = .{
|
||||||
|
.url = "https://gitlab.freedesktop.org/xorg/app/rgb/-/archive/master/rgb-master.tar.gz",
|
||||||
|
.hash = "12201ecce35845b829edf31f5b1b751b24efe6bdc20a8acf06f5c0f2bd83fdd69158",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ const list_fonts = @import("list_fonts.zig");
|
|||||||
const version = @import("version.zig");
|
const version = @import("version.zig");
|
||||||
const list_keybinds = @import("list_keybinds.zig");
|
const list_keybinds = @import("list_keybinds.zig");
|
||||||
const list_themes = @import("list_themes.zig");
|
const list_themes = @import("list_themes.zig");
|
||||||
|
const list_colors = @import("list_colors.zig");
|
||||||
|
|
||||||
/// Special commands that can be invoked via CLI flags. These are all
|
/// Special commands that can be invoked via CLI flags. These are all
|
||||||
/// invoked by using `+<action>` as a CLI flag. The only exception is
|
/// invoked by using `+<action>` as a CLI flag. The only exception is
|
||||||
@ -22,6 +23,9 @@ pub const Action = enum {
|
|||||||
/// List available themes
|
/// List available themes
|
||||||
@"list-themes",
|
@"list-themes",
|
||||||
|
|
||||||
|
/// List named RGB colors
|
||||||
|
@"list-colors",
|
||||||
|
|
||||||
pub const Error = error{
|
pub const Error = error{
|
||||||
/// Multiple actions were detected. You can specify at most one
|
/// Multiple actions were detected. You can specify at most one
|
||||||
/// action on the CLI otherwise the behavior desired is ambiguous.
|
/// action on the CLI otherwise the behavior desired is ambiguous.
|
||||||
@ -62,6 +66,7 @@ pub const Action = enum {
|
|||||||
.@"list-fonts" => try list_fonts.run(alloc),
|
.@"list-fonts" => try list_fonts.run(alloc),
|
||||||
.@"list-keybinds" => try list_keybinds.run(alloc),
|
.@"list-keybinds" => try list_keybinds.run(alloc),
|
||||||
.@"list-themes" => try list_themes.run(alloc),
|
.@"list-themes" => try list_themes.run(alloc),
|
||||||
|
.@"list-colors" => try list_colors.run(alloc),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
32
src/cli/list_colors.zig
Normal file
32
src/cli/list_colors.zig
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const inputpkg = @import("../input.zig");
|
||||||
|
const args = @import("args.zig");
|
||||||
|
const RGBName = @import("rgb_names").RGBName;
|
||||||
|
|
||||||
|
pub const Options = struct {
|
||||||
|
pub fn deinit(self: Options) void {
|
||||||
|
_ = self;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The "list-colors" command is used to list all the named RGB colors in
|
||||||
|
/// Ghostty.
|
||||||
|
pub fn run(alloc: std.mem.Allocator) !u8 {
|
||||||
|
var opts: Options = .{};
|
||||||
|
defer opts.deinit();
|
||||||
|
|
||||||
|
{
|
||||||
|
var iter = try std.process.argsWithAllocator(alloc);
|
||||||
|
defer iter.deinit();
|
||||||
|
try args.parse(Options, alloc, &opts, &iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stdout = std.io.getStdOut().writer();
|
||||||
|
|
||||||
|
inline for (std.meta.fields(RGBName)) |f| {
|
||||||
|
const rgb = @field(RGBName, f.name).toRGB();
|
||||||
|
try stdout.print("{s} = #{x:0>2}{x:0>2}{x:0>2}\n", .{ f.name, rgb.r, rgb.g, rgb.b });
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -20,6 +20,7 @@ const Key = @import("key.zig").Key;
|
|||||||
const KeyValue = @import("key.zig").Value;
|
const KeyValue = @import("key.zig").Value;
|
||||||
const ErrorList = @import("ErrorList.zig");
|
const ErrorList = @import("ErrorList.zig");
|
||||||
const MetricModifier = fontpkg.face.Metrics.Modifier;
|
const MetricModifier = fontpkg.face.Metrics.Modifier;
|
||||||
|
const RGBName = @import("rgb_names").RGBName;
|
||||||
|
|
||||||
const log = std.log.scoped(.config);
|
const log = std.log.scoped(.config);
|
||||||
|
|
||||||
@ -2139,7 +2140,12 @@ pub const Color = packed struct(u24) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn parseCLI(input: ?[]const u8) !Color {
|
pub fn parseCLI(input: ?[]const u8) !Color {
|
||||||
return fromHex(input orelse return error.ValueRequired);
|
if (input == null) return error.ValueRequred;
|
||||||
|
if (RGBName.fromString(input.?)) |name| {
|
||||||
|
const rgb = name.toRGB();
|
||||||
|
return Color{ .r = rgb.r, .g = rgb.g, .b = rgb.b };
|
||||||
|
}
|
||||||
|
return fromHex(input.?);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deep copy of the struct. Required by Config.
|
/// Deep copy of the struct. Required by Config.
|
||||||
@ -2188,6 +2194,10 @@ pub const Color = packed struct(u24) {
|
|||||||
try testing.expectEqual(Color{ .r = 10, .g = 11, .b = 12 }, try Color.fromHex("0A0B0C"));
|
try testing.expectEqual(Color{ .r = 10, .g = 11, .b = 12 }, try Color.fromHex("0A0B0C"));
|
||||||
try testing.expectEqual(Color{ .r = 255, .g = 255, .b = 255 }, try Color.fromHex("FFFFFF"));
|
try testing.expectEqual(Color{ .r = 255, .g = 255, .b = 255 }, try Color.fromHex("FFFFFF"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "fromName" {
|
||||||
|
try std.testing.expectEqual(Color{ .r = 0, .g = 0, .b = 0 }, try Color.parseCLI("black"));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Palette is the 256 color palette for 256-color mode. This is still
|
/// Palette is the 256 color palette for 256-color mode. This is still
|
||||||
|
120
src/generate_rgb_names.zig
Normal file
120
src/generate_rgb_names.zig
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const rgb = @embedFile("rgb");
|
||||||
|
|
||||||
|
const RGB = struct {
|
||||||
|
r: u8,
|
||||||
|
g: u8,
|
||||||
|
b: u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
var arena = std.heap.ArenaAllocator.init(gpa.allocator());
|
||||||
|
const alloc = arena.allocator();
|
||||||
|
|
||||||
|
const stdout_file = std.io.getStdOut().writer();
|
||||||
|
var bw = std.io.bufferedWriter(stdout_file);
|
||||||
|
const stdout = bw.writer();
|
||||||
|
|
||||||
|
var set = std.StringHashMap(RGB).init(alloc);
|
||||||
|
defer set.deinit();
|
||||||
|
var list = std.ArrayList([]const u8).init(alloc);
|
||||||
|
defer list.deinit();
|
||||||
|
|
||||||
|
try stdout.writeAll(
|
||||||
|
\\// THIS FILE IS AUTO-GENERATED! DO NOT MAKE ANY CHANGES!
|
||||||
|
\\
|
||||||
|
\\const std = @import("std");
|
||||||
|
\\
|
||||||
|
\\pub const RGB = struct {
|
||||||
|
\\ r: u8,
|
||||||
|
\\ g: u8,
|
||||||
|
\\ b: u8,
|
||||||
|
\\};
|
||||||
|
\\
|
||||||
|
\\/// RGB color names, taken from the X11 rgb.txt file.
|
||||||
|
\\pub const RGBName = enum {
|
||||||
|
\\
|
||||||
|
\\ const Self = @This();
|
||||||
|
\\
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
|
var iter = std.mem.splitScalar(u8, rgb, '\n');
|
||||||
|
while (iter.next()) |line| {
|
||||||
|
if (line.len < 12) continue;
|
||||||
|
const r = try std.fmt.parseInt(u8, std.mem.trim(u8, line[0..3], &std.ascii.whitespace), 10);
|
||||||
|
const g = try std.fmt.parseInt(u8, std.mem.trim(u8, line[4..7], &std.ascii.whitespace), 10);
|
||||||
|
const b = try std.fmt.parseInt(u8, std.mem.trim(u8, line[8..11], &std.ascii.whitespace), 10);
|
||||||
|
var n = try alloc.alloc(u8, line[12..].len);
|
||||||
|
defer alloc.free(n);
|
||||||
|
var i: usize = 0;
|
||||||
|
for (line[12..]) |c| {
|
||||||
|
if (std.ascii.isWhitespace(c)) continue;
|
||||||
|
n[i] = std.ascii.toLower(c);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
const m = try alloc.dupe(u8, n[0..i]);
|
||||||
|
if (set.get(m) == null) {
|
||||||
|
try set.put(m, RGB{ .r = r, .g = g, .b = b });
|
||||||
|
try list.append(m);
|
||||||
|
try stdout.print(" {s},\n", .{
|
||||||
|
m,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try stdout.writeAll(
|
||||||
|
\\
|
||||||
|
\\ pub fn fromString(str: []const u8) ?Self {
|
||||||
|
\\ const max = 64;
|
||||||
|
\\ var n: [max]u8 = undefined;
|
||||||
|
\\ var i: usize = 0;
|
||||||
|
\\ for (str, 0..) |c, j| {
|
||||||
|
\\ if (std.ascii.isWhitespace(c)) continue;
|
||||||
|
\\ n[i] = std.ascii.toLower(c);
|
||||||
|
\\ i += 1;
|
||||||
|
\\ if (i == max) {
|
||||||
|
\\ if (j >= str.len - 1) std.log.warn("color name '{s}' longer than {d} characters", .{str, max});
|
||||||
|
\\ break;
|
||||||
|
\\ }
|
||||||
|
\\ }
|
||||||
|
\\ return std.meta.stringToEnum(Self, n[0..i]);
|
||||||
|
\\ }
|
||||||
|
\\
|
||||||
|
\\ pub fn toRGB(self: Self) RGB {
|
||||||
|
\\ return switch(self) {
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
|
for (list.items) |name| {
|
||||||
|
if (set.get(name)) |value| {
|
||||||
|
try stdout.print(" .{s} => RGB{{ .r = {d}, .g = {d}, .b = {d} }},\n", .{ name, value.r, value.g, value.b });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try stdout.writeAll(
|
||||||
|
\\ };
|
||||||
|
\\ }
|
||||||
|
\\};
|
||||||
|
\\
|
||||||
|
\\test "RGBName" {
|
||||||
|
\\ try std.testing.expectEqual(null, RGBName.fromString("nosuchcolor"));
|
||||||
|
\\ try std.testing.expectEqual(RGBName.white, RGBName.fromString("white"));
|
||||||
|
\\ try std.testing.expectEqual(RGBName.mediumspringgreen, RGBName.fromString("medium spring green"));
|
||||||
|
\\ try std.testing.expectEqual(RGBName.forestgreen, RGBName.fromString("ForestGreen"));
|
||||||
|
\\
|
||||||
|
\\ try std.testing.expectEqual(RGB{ .r = 0, .g = 0, .b = 0 }, RGBName.black.toRGB());
|
||||||
|
\\ try std.testing.expectEqual(RGB{ .r = 255, .g = 0, .b = 0 }, RGBName.red.toRGB());
|
||||||
|
\\ try std.testing.expectEqual(RGB{ .r = 0, .g = 255, .b = 0 }, RGBName.green.toRGB());
|
||||||
|
\\ try std.testing.expectEqual(RGB{ .r = 0, .g = 0, .b = 255 }, RGBName.blue.toRGB());
|
||||||
|
\\ try std.testing.expectEqual(RGB{ .r = 255, .g = 255, .b = 255 }, RGBName.white.toRGB());
|
||||||
|
\\ try std.testing.expectEqual(RGB{ .r = 124, .g = 252, .b = 0 }, RGBName.lawngreen.toRGB());
|
||||||
|
\\ try std.testing.expectEqual(RGB{ .r = 0, .g = 250, .b = 154 }, RGBName.mediumspringgreen.toRGB());
|
||||||
|
\\ try std.testing.expectEqual(RGB{ .r = 34, .g = 139, .b = 34 }, RGBName.forestgreen.toRGB());
|
||||||
|
\\}
|
||||||
|
\\
|
||||||
|
);
|
||||||
|
|
||||||
|
try bw.flush();
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
|
const RGBName = @import("rgb_names").RGBName;
|
||||||
|
|
||||||
/// The default palette.
|
/// The default palette.
|
||||||
pub const default: Palette = default: {
|
pub const default: Palette = default: {
|
||||||
@ -216,6 +217,15 @@ pub const RGB = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RGBName.fromString(value)) |name| {
|
||||||
|
const rgb = name.toRGB();
|
||||||
|
return RGB{
|
||||||
|
.r = rgb.r,
|
||||||
|
.g = rgb.g,
|
||||||
|
.b = rgb.b,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (value.len < "rgb:a/a/a".len or !std.mem.eql(u8, value[0..3], "rgb")) {
|
if (value.len < "rgb:a/a/a".len or !std.mem.eql(u8, value[0..3], "rgb")) {
|
||||||
return error.InvalidFormat;
|
return error.InvalidFormat;
|
||||||
}
|
}
|
||||||
@ -293,6 +303,16 @@ test "RGB.parse" {
|
|||||||
try testing.expectEqual(RGB{ .r = 255, .g = 255, .b = 255 }, try RGB.parse("#ffffff"));
|
try testing.expectEqual(RGB{ .r = 255, .g = 255, .b = 255 }, try RGB.parse("#ffffff"));
|
||||||
try testing.expectEqual(RGB{ .r = 255, .g = 0, .b = 16 }, try RGB.parse("#ff0010"));
|
try testing.expectEqual(RGB{ .r = 255, .g = 0, .b = 16 }, try RGB.parse("#ff0010"));
|
||||||
|
|
||||||
|
try testing.expectEqual(RGB{ .r = 0, .g = 0, .b = 0 }, try RGB.parse("black"));
|
||||||
|
try testing.expectEqual(RGB{ .r = 255, .g = 0, .b = 0 }, try RGB.parse("red"));
|
||||||
|
try testing.expectEqual(RGB{ .r = 0, .g = 255, .b = 0 }, try RGB.parse("green"));
|
||||||
|
try testing.expectEqual(RGB{ .r = 0, .g = 0, .b = 255 }, try RGB.parse("blue"));
|
||||||
|
try testing.expectEqual(RGB{ .r = 255, .g = 255, .b = 255 }, try RGB.parse("white"));
|
||||||
|
|
||||||
|
try testing.expectEqual(RGB{ .r = 124, .g = 252, .b = 0 }, try RGB.parse("LawnGreen"));
|
||||||
|
try testing.expectEqual(RGB{ .r = 0, .g = 250, .b = 154 }, try RGB.parse("medium spring green"));
|
||||||
|
try testing.expectEqual(RGB{ .r = 34, .g = 139, .b = 34 }, try RGB.parse(" Forest Green "));
|
||||||
|
|
||||||
// Invalid format
|
// Invalid format
|
||||||
try testing.expectError(error.InvalidFormat, RGB.parse("rgb;"));
|
try testing.expectError(error.InvalidFormat, RGB.parse("rgb;"));
|
||||||
try testing.expectError(error.InvalidFormat, RGB.parse("rgb:"));
|
try testing.expectError(error.InvalidFormat, RGB.parse("rgb:"));
|
||||||
|
Reference in New Issue
Block a user