From f64ee82d3feed4311b9bdcda20f9d6109c63c75f Mon Sep 17 00:00:00 2001 From: Florian Best Date: Sat, 17 Jan 2026 03:38:53 +0100 Subject: [PATCH] feat(render): add configurable attribute colors for bold, italic, and underline Introduce optional color theme entries for bold, italic, and underline text when the foreground color is default. This allows legacy applications using SGR attributes instead of explicit colors to be rendered with user-defined attribute colors. Brightening via `bold_in_bright` is suppressed when an explicit attribute color is applied. This adds compatibility with the rxvt-unicode settings `colorBD`, `colorIT` and `colorUL`. Fixes: #2261 --- config.c | 6 ++++++ config.h | 3 +++ doc/foot.ini.5.scd | 15 +++++++++++++++ foot.ini | 5 +++++ render.c | 18 ++++++++++++++++-- terminal.c | 6 ++++++ terminal.h | 3 +++ tests/test-config.c | 6 ++++++ 8 files changed, 60 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index 12c594bc..f7610d7a 100644 --- a/config.c +++ b/config.c @@ -1464,6 +1464,9 @@ parse_color_theme(struct context *ctx, struct color_theme *theme) else if (streq(key, "flash")) color = &theme->flash; else if (streq(key, "foreground")) color = &theme->fg; else if (streq(key, "background")) color = &theme->bg; + else if (streq(key, "bold")) color = &theme->bold; + else if (streq(key, "italic")) color = &theme->italic; + else if (streq(key, "underline")) color = &theme->underline; else if (streq(key, "selection-foreground")) color = &theme->selection_fg; else if (streq(key, "selection-background")) color = &theme->selection_bg; @@ -3540,6 +3543,9 @@ config_load(struct config *conf, const char *conf_path, .dim_blend_towards = DIM_BLEND_TOWARDS_BLACK, .selection_fg = 0x80000000, /* Use default bg */ .selection_bg = 0x80000000, /* Use default fg */ + .bold = 0x80000000, /* default: unset */ + .italic = 0x80000000, /* default: unset */ + .underline = 0x80000000, /* default: unset */ .cursor = { .text = 0, .cursor = 0, diff --git a/config.h b/config.h index a3522f44..94214a23 100644 --- a/config.h +++ b/config.h @@ -141,6 +141,9 @@ struct color_theme { uint32_t selection_fg; uint32_t selection_bg; uint32_t url; + uint32_t bold; + uint32_t italic; + uint32_t underline; uint32_t dim[8]; uint32_t sixel[16]; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 2f5fc38c..ea08f7a6 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -1128,6 +1128,21 @@ The default theme used is *colors-dark*, unless Foreground (text) and background color to use in selected text. Default: _inverse foreground/background_. +*bold* + Specifies the color to use for bold characters when the foreground color is the default (undefined). + This setting applies primarily to legacy applications that utilize SGR 1 codes instead of SGR 9 codes. + If not set, the behavior defaults to the terminal's standard bold rendering. Default: unset. + +*italic* + Specifies the color to use for italic characters when the foreground color is the default (undefined). + This setting applies primarily to legacy applications that utilize SGR 1 codes instead of SGR 9 codes. + If not set, the behavior defaults to the terminal's standard italic rendering. Default: unset. + +*underline* + Specifies the color to use for underline characters when the foreground color is the default (undefined). + This setting applies primarily to legacy applications that utilize SGR 1 codes instead of SGR 9 codes. + If not set, the behavior defaults to the terminal's standard underline rendering. Default: unset. + *jump-labels* Two color values specifying the foreground (text) and background colors to use when rendering jump labels in URL mode. Default: diff --git a/foot.ini b/foot.ini index a9b4b83d..46f33263 100644 --- a/foot.ini +++ b/foot.ini @@ -160,6 +160,11 @@ # sixel14 = 999954 # sixel15 = cccccc +## Attribute colorization +# bold = +# italic = +# underline = + ## Misc colors # selection-foreground= # selection-background= diff --git a/render.c b/render.c index c47133b3..f30daf0b 100644 --- a/render.c +++ b/render.c @@ -701,6 +701,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, uint32_t _fg = 0; uint32_t _bg = 0; + bool allow_brighten = true; uint16_t alpha = 0xffff; const bool is_selected = cell->attrs.selected; @@ -718,7 +719,20 @@ render_cell(struct terminal *term, pixman_image_t *pix, break; case COLOR_DEFAULT: - _fg = term->reverse ? term->colors.bg : term->colors.fg; + if (term->reverse) { + _fg = term->colors.bg; + } else if (unlikely(cell->attrs.bold && term->colors.bold >> 24 == 0)) { + _fg = term->colors.bold; + allow_brighten = false; + } else if (unlikely(cell->attrs.italic && term->colors.italic >> 24 == 0)) { + _fg = term->colors.italic; + allow_brighten = false; + } else if (unlikely(cell->attrs.underline && term->colors.underline >> 24 == 0)) { + _fg = term->colors.underline; + allow_brighten = false; + } else { + _fg = term->colors.fg; + } break; } @@ -837,7 +851,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, if (cell->attrs.dim) _fg = color_dim(term, _fg); - if (term->conf->bold_in_bright.enabled && cell->attrs.bold) + if (term->conf->bold_in_bright.enabled && cell->attrs.bold && allow_brighten) _fg = color_brighten(term, _fg); if (cell->attrs.blink && term->blink.state == BLINK_OFF) diff --git a/terminal.c b/terminal.c index ac7922a7..b2bf88de 100644 --- a/terminal.c +++ b/terminal.c @@ -1317,6 +1317,9 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, .cursor_bg = (theme->use_custom.cursor ? 1u << 31 : 0) | theme->cursor.cursor, .selection_fg = theme->selection_fg, .selection_bg = theme->selection_bg, + .bold = theme->bold, + .italic = theme->italic, + .underline = theme->underline, .active_theme = conf->initial_color_theme, }, .color_stack = { @@ -2090,6 +2093,9 @@ term_theme_apply(struct terminal *term, const struct color_theme *theme) term->colors.cursor_bg = (theme->use_custom.cursor ? 1u << 31 : 0) | theme->cursor.cursor; term->colors.selection_fg = theme->selection_fg; term->colors.selection_bg = theme->selection_bg; + term->colors.bold = theme->bold; + term->colors.italic = theme->italic; + term->colors.underline = theme->underline; memcpy(term->colors.table, theme->table, sizeof(term->colors.table)); } diff --git a/terminal.h b/terminal.h index 5a2a57aa..acb7fd9b 100644 --- a/terminal.h +++ b/terminal.h @@ -404,6 +404,9 @@ struct colors { uint32_t cursor_bg; /* cursor color */ uint32_t selection_fg; uint32_t selection_bg; + uint32_t bold; + uint32_t italic; + uint32_t underline; enum which_color_theme active_theme; }; diff --git a/tests/test-config.c b/tests/test-config.c index 9774cba9..bfecd522 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -739,6 +739,9 @@ test_section_colors_dark(void) test_color(&ctx, &parse_section_colors_dark, "dim7", false, &conf.colors_dark.dim[7]); test_color(&ctx, &parse_section_colors_dark, "selection-foreground", false, &conf.colors_dark.selection_fg); test_color(&ctx, &parse_section_colors_dark, "selection-background", false, &conf.colors_dark.selection_bg); + test_color(&ctx, &parse_section_colors_dark, "bold", false, &conf.colors_dark.bold); + test_color(&ctx, &parse_section_colors_dark, "italic", false, &conf.colors_dark.italic); + test_color(&ctx, &parse_section_colors_dark, "underline", false, &conf.colors_dark.underline); test_color(&ctx, &parse_section_colors_dark, "urls", false, &conf.colors_dark.url); test_two_colors(&ctx, &parse_section_colors_dark, "jump-labels", false, &conf.colors_dark.jump_label.fg, @@ -820,6 +823,9 @@ test_section_colors_light(void) test_color(&ctx, &parse_section_colors_light, "dim7", false, &conf.colors_light.dim[7]); test_color(&ctx, &parse_section_colors_light, "selection-foreground", false, &conf.colors_light.selection_fg); test_color(&ctx, &parse_section_colors_light, "selection-background", false, &conf.colors_light.selection_bg); + test_color(&ctx, &parse_section_colors_light, "bold", false, &conf.colors_light.bold); + test_color(&ctx, &parse_section_colors_light, "italic", false, &conf.colors_light.italic); + test_color(&ctx, &parse_section_colors_light, "underline", false, &conf.colors_light.underline); test_color(&ctx, &parse_section_colors_light, "urls", false, &conf.colors_light.url); test_two_colors(&ctx, &parse_section_colors_light, "jump-labels", false, &conf.colors_light.jump_label.fg,