From 6bb0eb1dd318bc911b2b492641b83848094f2dd1 Mon Sep 17 00:00:00 2001 From: txf Date: Wed, 28 Jan 2026 19:02:26 +0000 Subject: [PATCH] Add pad-extend option Adds a new boolean option `pad-extend` that extends edge cell background colors into the terminal padding/margins, similar to Ghostty's behavior. When enabled, the left/right margins use the background color of the adjacent edge cells, and top/bottom margins use the colors of the first/last row cells. Co-Authored-By: Claude Opus 4.5 --- config.c | 4 ++ config.h | 1 + doc/foot.ini.5.scd | 7 +++ render.c | 121 +++++++++++++++++++++++++++++++++++++++------ 4 files changed, 118 insertions(+), 15 deletions(-) diff --git a/config.c b/config.c index 12c594bc..99d958dc 100644 --- a/config.c +++ b/config.c @@ -994,6 +994,9 @@ parse_section_main(struct context *ctx) return true; } + else if (streq(key, "pad-extend")) + return value_to_bool(ctx, &conf->pad_extend); + else if (streq(key, "resize-delay-ms")) return value_to_uint16(ctx, 10, &conf->resize_delay_ms); @@ -3478,6 +3481,7 @@ config_load(struct config *conf, const char *conf_path, .pad_top = 0, .pad_right = 0, .pad_bottom = 0, + .pad_extend = false, .center_when = CENTER_MAXIMIZED_AND_FULLSCREEN, .resize_by_cells = true, .resize_keep_grid = true, diff --git a/config.h b/config.h index a3522f44..1e8f7362 100644 --- a/config.h +++ b/config.h @@ -239,6 +239,7 @@ struct config { unsigned pad_top; unsigned pad_right; unsigned pad_bottom; + bool pad_extend; enum center_when center_when; bool resize_by_cells; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index a1ee326f..4a02cb92 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -331,6 +331,13 @@ empty string to be set, but it must be quoted: *KEY=""* Default: _0x0_ center-when-maximized-and-fullscreen. +*pad-extend* + Extend the background color of edge cells into the padding area. + Left/right margins use the adjacent edge cell colors, top/bottom + margins use the first/last row cell colors. + + Default: _no_ + *resize-delay-ms* Time, in milliseconds, of "idle time" before foot performs text diff --git a/render.c b/render.c index c47133b3..cb900579 100644 --- a/render.c +++ b/render.c @@ -682,6 +682,25 @@ draw_cursor(const struct terminal *term, const struct cell *cell, } } +static uint32_t +cell_bg_color(const struct terminal *term, const struct cell *cell) +{ + switch (cell->attrs.bg_src) { + case COLOR_RGB: + return cell->attrs.bg; + + case COLOR_BASE16: + case COLOR_BASE256: + xassert(cell->attrs.bg < ALEN(term->colors.table)); + return term->colors.table[cell->attrs.bg]; + + case COLOR_DEFAULT: + return term->reverse ? term->colors.fg : term->colors.bg; + } + + UNREACHABLE(); +} + static int render_cell(struct terminal *term, pixman_image_t *pix, pixman_region32_t *damage, struct row *row, int row_no, int col, @@ -722,21 +741,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, break; } - switch (cell->attrs.bg_src) { - case COLOR_RGB: - _bg = cell->attrs.bg; - break; - - case COLOR_BASE16: - case COLOR_BASE256: - xassert(cell->attrs.bg < ALEN(term->colors.table)); - _bg = term->colors.table[cell->attrs.bg]; - break; - - case COLOR_DEFAULT: - _bg = term->reverse ? term->colors.fg : term->colors.bg; - break; - } + _bg = cell_bg_color(term, cell); if (unlikely(is_selected)) { const uint32_t cell_fg = _fg; @@ -1264,6 +1269,88 @@ render_margin(struct terminal *term, struct buffer *buf, term->margins.right, line_count * term->cell_height}, }); + /* Extend edge cell colors into margins if enabled */ + if (term->conf->pad_extend) { + /* Left/right margins: extend each row's edge cell colors */ + for (int r = 0; r < term->rows; r++) { + struct row *row = grid_row_in_view(term->grid, r); + if (row == NULL || row->cells == NULL) + continue; + + int y_pos = term->margins.top + r * term->cell_height; + + uint32_t left_bg = cell_bg_color(term, &row->cells[0]); + if (left_bg != _bg) { + pixman_color_t c = color_hex_to_pixman_with_alpha(left_bg, alpha, gamma_correct); + pixman_image_fill_rectangles(PIXMAN_OP_SRC, buf->pix[0], &c, 1, + &(pixman_rectangle16_t){0, y_pos, term->margins.left, term->cell_height}); + } + + uint32_t right_bg = cell_bg_color(term, &row->cells[term->cols - 1]); + if (right_bg != _bg) { + pixman_color_t c = color_hex_to_pixman_with_alpha(right_bg, alpha, gamma_correct); + pixman_image_fill_rectangles(PIXMAN_OP_SRC, buf->pix[0], &c, 1, + &(pixman_rectangle16_t){rmargin, y_pos, term->margins.right, term->cell_height}); + } + } + + /* Top/bottom margins: extend each column's edge cell colors */ + struct row *top_row = grid_row_in_view(term->grid, 0); + struct row *bot_row = grid_row_in_view(term->grid, term->rows - 1); + + for (int c = 0; c < term->cols; c++) { + int x_pos = term->margins.left + c * term->cell_width; + + if (top_row != NULL && top_row->cells != NULL) { + uint32_t top_bg = cell_bg_color(term, &top_row->cells[c]); + if (top_bg != _bg) { + pixman_color_t col = color_hex_to_pixman_with_alpha(top_bg, alpha, gamma_correct); + pixman_image_fill_rectangles(PIXMAN_OP_SRC, buf->pix[0], &col, 1, + &(pixman_rectangle16_t){x_pos, 0, term->cell_width, term->margins.top}); + } + } + + if (bot_row != NULL && bot_row->cells != NULL) { + uint32_t bot_bg = cell_bg_color(term, &bot_row->cells[c]); + if (bot_bg != _bg) { + pixman_color_t col = color_hex_to_pixman_with_alpha(bot_bg, alpha, gamma_correct); + pixman_image_fill_rectangles(PIXMAN_OP_SRC, buf->pix[0], &col, 1, + &(pixman_rectangle16_t){x_pos, bmargin, term->cell_width, term->margins.bottom}); + } + } + } + + /* Fill corners with corner cell colors */ + if (top_row != NULL && top_row->cells != NULL) { + uint32_t tl = cell_bg_color(term, &top_row->cells[0]); + uint32_t tr = cell_bg_color(term, &top_row->cells[term->cols - 1]); + if (tl != _bg) { + pixman_color_t c = color_hex_to_pixman_with_alpha(tl, alpha, gamma_correct); + pixman_image_fill_rectangles(PIXMAN_OP_SRC, buf->pix[0], &c, 1, + &(pixman_rectangle16_t){0, 0, term->margins.left, term->margins.top}); + } + if (tr != _bg) { + pixman_color_t c = color_hex_to_pixman_with_alpha(tr, alpha, gamma_correct); + pixman_image_fill_rectangles(PIXMAN_OP_SRC, buf->pix[0], &c, 1, + &(pixman_rectangle16_t){rmargin, 0, term->margins.right, term->margins.top}); + } + } + if (bot_row != NULL && bot_row->cells != NULL) { + uint32_t bl = cell_bg_color(term, &bot_row->cells[0]); + uint32_t br = cell_bg_color(term, &bot_row->cells[term->cols - 1]); + if (bl != _bg) { + pixman_color_t c = color_hex_to_pixman_with_alpha(bl, alpha, gamma_correct); + pixman_image_fill_rectangles(PIXMAN_OP_SRC, buf->pix[0], &c, 1, + &(pixman_rectangle16_t){0, bmargin, term->margins.left, term->margins.bottom}); + } + if (br != _bg) { + pixman_color_t c = color_hex_to_pixman_with_alpha(br, alpha, gamma_correct); + pixman_image_fill_rectangles(PIXMAN_OP_SRC, buf->pix[0], &c, 1, + &(pixman_rectangle16_t){rmargin, bmargin, term->margins.right, term->margins.bottom}); + } + } + } + if (term->render.urgency) render_urgency(term, buf); @@ -3598,6 +3685,10 @@ grid_render(struct terminal *term) pixman_region32_fini(&damage); + /* Re-render margins to extend edge cell colors if enabled */ + if (term->conf->pad_extend) + render_margin(term, buf, 0, term->rows, true); + render_overlay(term); render_ime_preedit(term, buf); render_scrollback_position(term);