From 01573819ea8bd55218b81a38221dcc5501550514 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 20 Nov 2022 15:25:51 -0800 Subject: [PATCH] Configurable 256 Color Palette (#50) The 256 color palette can now be configured with the `palette=N=HEX` format in the config. Example, Dracula: ``` foreground=#f8f8f2 background=#282a36 palette=0=#21222c palette=8=#6272a4 palette=1=#ff5555 palette=9=#ff6e6e palette=2=#50fa7b palette=10=#69ff94 palette=3=#f1fa8c palette=11=#ffffa5 palette=4=#bd93f9 palette=12=#d6acff palette=5=#ff79c6 palette=13=#ff92df palette=6=#8be9fd palette=14=#a4ffff palette=7=#f8f8f2 palette=15=#ffffff ``` --- src/config.zig | 54 +++++++++++++++++++++++++++++++++++++++ src/terminal/Terminal.zig | 15 ++++++----- src/termio/Exec.zig | 7 ++++- 3 files changed, 69 insertions(+), 7 deletions(-) diff --git a/src/config.zig b/src/config.zig index 74926d65e..8a4d64fae 100644 --- a/src/config.zig +++ b/src/config.zig @@ -4,6 +4,7 @@ const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const inputpkg = @import("input.zig"); const passwd = @import("passwd.zig"); +const terminal = @import("terminal/main.zig"); const log = std.log.scoped(.config); @@ -35,6 +36,16 @@ pub const Config = struct { /// Foreground color for the window. foreground: Color = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF }, + /// Color palette for the 256 color form that many terminal applications + /// use. The syntax of this configuration is "N=HEXCODE" where "n" + /// is 0 to 255 (for the 256 colors) and HEXCODE is a typical RGB + /// color code such as "#AABBCC". The 0 to 255 correspond to the + /// terminal color table. + /// + /// For definitions on all the codes: + /// https://www.ditig.com/256-colors-cheat-sheet + palette: Palette = .{}, + /// The command to run, usually a shell. If this is not an absolute path, /// it'll be looked up in the PATH. If this is not set, a default will /// be looked up from your system. The rules for the default lookup are: @@ -347,6 +358,49 @@ pub const Color = struct { } }; +/// Palette is the 256 color palette for 256-color mode. This is still +/// used by many terminal applications. +pub const Palette = struct { + const Self = @This(); + + /// The actual value that is updated as we parse. + value: terminal.color.Palette = terminal.color.default, + + pub const Error = error{ + InvalidFormat, + }; + + pub fn parseCLI( + self: *Self, + input: ?[]const u8, + ) !void { + const value = input orelse return error.ValueRequired; + const eqlIdx = std.mem.indexOf(u8, value, "=") orelse + return Error.InvalidFormat; + + const key = try std.fmt.parseInt(u8, value[0..eqlIdx], 10); + const rgb = try Color.parseCLI(value[eqlIdx + 1 ..]); + self.value[key] = .{ .r = rgb.r, .g = rgb.g, .b = rgb.b }; + } + + test "parseCLI" { + const testing = std.testing; + + var p: Self = .{}; + try p.parseCLI("0=#AABBCC"); + try testing.expect(p.value[0].r == 0xAA); + try testing.expect(p.value[0].g == 0xBB); + try testing.expect(p.value[0].b == 0xCC); + } + + test "parseCLI overflow" { + const testing = std.testing; + + var p: Self = .{}; + try testing.expectError(error.Overflow, p.parseCLI("256=#AABBCC")); + } +}; + /// RepeatableString is a string value that can be repeated to accumulate /// a list of strings. This isn't called "StringList" because I find that /// sometimes leads to confusion that it _accepts_ a list such as diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 738e876e9..91b1ac21d 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -60,6 +60,9 @@ scrolling_region: ScrollingRegion, /// The charset state charset: CharsetState = .{}, +/// The color palette to use +color_palette: color.Palette = color.default, + /// The previous printed character. This is used for the repeat previous /// char CSI (ESC [ b). previous_char: ?u21 = null, @@ -429,12 +432,12 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void { .@"8_fg" => |n| { self.screen.cursor.pen.attrs.has_fg = true; - self.screen.cursor.pen.fg = color.default[@enumToInt(n)]; + self.screen.cursor.pen.fg = self.color_palette[@enumToInt(n)]; }, .@"8_bg" => |n| { self.screen.cursor.pen.attrs.has_bg = true; - self.screen.cursor.pen.bg = color.default[@enumToInt(n)]; + self.screen.cursor.pen.bg = self.color_palette[@enumToInt(n)]; }, .reset_fg => self.screen.cursor.pen.attrs.has_fg = false, @@ -443,22 +446,22 @@ pub fn setAttribute(self: *Terminal, attr: sgr.Attribute) !void { .@"8_bright_fg" => |n| { self.screen.cursor.pen.attrs.has_fg = true; - self.screen.cursor.pen.fg = color.default[@enumToInt(n)]; + self.screen.cursor.pen.fg = self.color_palette[@enumToInt(n)]; }, .@"8_bright_bg" => |n| { self.screen.cursor.pen.attrs.has_bg = true; - self.screen.cursor.pen.bg = color.default[@enumToInt(n)]; + self.screen.cursor.pen.bg = self.color_palette[@enumToInt(n)]; }, .@"256_fg" => |idx| { self.screen.cursor.pen.attrs.has_fg = true; - self.screen.cursor.pen.fg = color.default[idx]; + self.screen.cursor.pen.fg = self.color_palette[idx]; }, .@"256_bg" => |idx| { self.screen.cursor.pen.attrs.has_bg = true; - self.screen.cursor.pen.bg = color.default[idx]; + self.screen.cursor.pen.bg = self.color_palette[idx]; }, .unknown => return error.InvalidAttribute, diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig index e8828fd56..deea06466 100644 --- a/src/termio/Exec.zig +++ b/src/termio/Exec.zig @@ -107,8 +107,13 @@ pub fn init(alloc: Allocator, opts: termio.Options) !Exec { log.info("started subcommand path={s} pid={?}", .{ path, cmd.pid }); // Create our terminal - var term = try terminal.Terminal.init(alloc, opts.grid_size.columns, opts.grid_size.rows); + var term = try terminal.Terminal.init( + alloc, + opts.grid_size.columns, + opts.grid_size.rows, + ); errdefer term.deinit(alloc); + term.color_palette = opts.config.palette.value; return Exec{ .alloc = alloc,