From 005e37ec1015fc0dbf7ed68e78a104dc6566ff28 Mon Sep 17 00:00:00 2001 From: wrmr Date: Wed, 8 Oct 2025 18:56:33 -0400 Subject: .cbinkrc colors --- main.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 139 insertions(+), 27 deletions(-) diff --git a/main.c b/main.c index f9adb85..16b632c 100644 --- a/main.c +++ b/main.c @@ -65,6 +65,7 @@ #define CPAIR_ENUM_X(cp, fg, bg, name) cp, #define CPAIR_NAME_X(cp, fg, bg, name) S(name), #define CPAIR_INIT_X(cp, fg, bg, name) init_pair(cp, fg, bg); +#define CPAIR_OPT_X(cp, f, b, name) [cp] = { .fg = f, .bg = b, .init = 1 }, #define COLOR_NORM -1 #define COLOR_FG COLOR_NORM #define COLOR_BG COLOR_NORM @@ -90,7 +91,7 @@ typedef enum { } ColorPair; Str cpair_name[CPAIR_MAX] = { - S("N/A"), + S("default"), CPAIR_LIST(CPAIR_NAME_X) }; @@ -145,6 +146,9 @@ int utf8_cp_to_byte(Str s, int dest) { /* options */ +/* + * If the high bit of fg or bg is set, the lower 24 bits are a RGB value. + */ typedef struct { int fg, bg, init; } ColorOpt; @@ -158,7 +162,6 @@ typedef struct { int left, right, top, bottom; int text_x, text_y; } margin; - int default_colors; ColorOpt color[CPAIR_MAX]; } Options; @@ -180,7 +183,9 @@ Options opt = { .text_x = GFX_TEXT_MARGIN_X, .text_y = GFX_TEXT_MARGIN_Y, }, - .default_colors = 1 + .color = { + CPAIR_LIST(CPAIR_OPT_X) + } }; /* msg & logging */ @@ -862,20 +867,44 @@ void new_post(Arena *temp) { /* curses setup/cleanup */ +/* currently RGB colors are broken */ +int custom_color; +int init_cfg_color(int c) { + if (c < 0 && c != -1) { + int r = (c >> 16) & 0xff; + int g = (c >> 8) & 0xff; + int b = c & 0xff; + init_color(custom_color, + ((r & 0xff) * 1000) / 0xff, + ((g & 0xff) * 1000) / 0xff, + ((b & 0xff) * 1000) / 0xff); + return custom_color--; + } else { + return c; + } +} + void init_curses(void) { setlocale(LC_ALL, ""); initscr(); - start_color(); - init_color(COLOR_BG, 0, 0, 0); cbreak(); noecho(); intrflush(stdscr, FALSE); keypad(stdscr, TRUE); curs_set(0); - if (opt.default_colors) use_default_colors(); - CPAIR_LIST(CPAIR_INIT_X) - for (ColorPair i = 0; i < CPAIR_MAX; i++) { + start_color(); + custom_color = COLORS - 1; + for (size_t i = 0; i < sizeof opt.color / sizeof *opt.color; i++) { + opt.color[i].fg = init_cfg_color(opt.color[i].fg); + opt.color[i].bg = init_cfg_color(opt.color[i].bg); + } + if (opt.color[0].init) { + assume_default_colors(opt.color[0].fg, opt.color[0].bg); + } else { + use_default_colors(); + } + for (ColorPair i = 1; i < CPAIR_MAX; i++) { if (opt.color[i].init) { init_pair(i, opt.color[i].fg, opt.color[i].bg); } @@ -904,35 +933,118 @@ typedef struct { int (*fn)(Str key, Str value, Arena *perm); } Section; -int sect_margin(Str key, Str value, Arena *perm) { - (void)perm; +int prop_bool(Str name, Str key, Str value, int *ptr) { + uint64_t u; + if (!str_eql(name, key)) return 0; + if (str_to_u64(value, &u)) return 0; + if (u != !!u) return 0; + *ptr = u; + return 1; +} + +int prop_int(Str name, Str key, Str value, int *ptr) { uint64_t u; - if (str_to_u64(value, &u)) return -1; - if (str_eql(key, S("left"))) { opt.margin.left = u; return 0; } - if (str_eql(key, S("right"))) { opt.margin.right = u; return 0; } - if (str_eql(key, S("top"))) { opt.margin.top = u; return 0; } - if (str_eql(key, S("bottom"))) { opt.margin.bottom = u; return 0; } - if (str_eql(key, S("text.x"))) { opt.margin.text_x = u; return 0; } - if (str_eql(key, S("text.y"))) { opt.margin.text_y = u; return 0; } + if (!str_eql(name, key)) return 0; + if (str_to_u64(value, &u)) return 0; + *ptr = u; + return 1; +} + +int prop_cstr(Str name, Str key, Str value, const char **ptr, Arena *perm) { + if (!str_eql(name, key)) return 0; + *ptr = str_to_cstr(value, perm); + return 1; +} + +int sect_margin(Str k, Str v, Arena *perm) { + (void)perm; + if (prop_int(S("left"), k, v, &opt.margin.left)) return 0; + if (prop_int(S("right"), k, v, &opt.margin.right)) return 0; + if (prop_int(S("top"), k, v, &opt.margin.top)) return 0; + if (prop_int(S("bottom"), k, v, &opt.margin.bottom)) return 0; + if (prop_int(S("text.x"), k, v, &opt.margin.text_x)) return 0; + if (prop_int(S("text.y"), k, v, &opt.margin.text_y)) return 0; + return -1; +} + +int sect_post(Str k, Str v, Arena *perm) { + if (prop_cstr(S("datefmt"), k, v, &opt.post.datefmt, perm)) return 0; + if (prop_bool(S("pfp"), k, v, &opt.post.pfp)) return 0; + if (prop_bool(S("pronouns"), k, v, &opt.post.pronouns)) return 0; + if (prop_bool(S("date"), k, v, &opt.post.date)) return 0; + return -1; +} + +#define BAD_COLOR -2 + +/* i can't tell if RGB colors are broken, or just the terminal doesn't support it */ +int parse_hex_nybble(char c) { + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return 10 + c - 'a'; + if (c >= 'A' && c <= 'F') return 10 + c - 'A'; return -1; } +int parse_hex_byte(const char *s) { + int l = parse_hex_nybble(s[0]); + int r = parse_hex_nybble(s[1]); + if (l == -1 || r == -1) return -1; + return ((l&0xf) << 4) | (r&0xf); +} +int parse_rgb(Str s) { + if (s.n != 6) return BAD_COLOR; + int r = parse_hex_byte(&s.s[0]); + int g = parse_hex_byte(&s.s[2]); + int b = parse_hex_byte(&s.s[4]); + if (r == -1 || g == -1 || b == -1) return BAD_COLOR; + return (1 << 31) | (r << 16) | (g << 8) | b; +} -int sect_post(Str key, Str value, Arena *perm) { +int parse_color(Str s) { + if (!s.n) return -1; /* default */ + if (s.s[0] == '#') { + return parse_rgb(str_skip(s, 1)); + } + static struct { + Str s; + int v; + } color_map[] = { + { S("default"), -1 }, + { S("black"), COLOR_BLACK }, + { S("red"), COLOR_RED }, + { S("green"), COLOR_GREEN }, + { S("yellow"), COLOR_YELLOW }, + { S("blue"), COLOR_BLUE }, + { S("magenta"), COLOR_MAGENTA }, + { S("cyan"), COLOR_CYAN }, + { S("white"), COLOR_WHITE }, + }; + for (size_t i = 0; i < sizeof color_map / sizeof *color_map; i++) { + if (str_eql(s, color_map[i].s)) return color_map[i].v; + } + return BAD_COLOR; +} + +int sect_color(Str k, Str v, Arena *perm) { (void)perm; - if (str_eql(key, S("datefmt"))) { opt.post.datefmt = str_to_cstr(value, perm); return 0; } - /* bools */ - uint64_t u; - if (str_to_u64(value, &u)) return -1; - if (u != !!u) return -1; - if (str_eql(key, S("pfp"))) { opt.post.pfp = u; return 0; } - if (str_eql(key, S("pronouns"))) { opt.post.pronouns = u; return 0; } - if (str_eql(key, S("date"))) { opt.post.date = u; return 0; } - return 0; + for (int i = 0; i < CPAIR_MAX; i++) { + if (!str_eql(k, cpair_name[i])) continue; + Cut c = str_cut(v, ':'); + int fg = parse_color(str_trim(c.head)); + int bg = parse_color(str_trim(c.tail)); + if (fg == BAD_COLOR || bg == BAD_COLOR) return -1; + opt.color[i] = (ColorOpt) { + .fg = fg, + .bg = bg, + .init = 1 + }; + } + return -1; } Section sections[] = { { "margin", §_margin }, { "post", §_post }, + { "color", §_color }, }; int load_config(Arena *perm, Arena *temp) { -- cgit v1.2.3