From 1760cb6ab82b355a0751f614f3b45c7446e23e95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 2 Apr 2025 08:41:46 +0200 Subject: [PATCH 01/86] config: update default URL regex The old one is in some cases too liberal. The new one is stricter in two ways: 1. The protocol list is now explicit, rather than matching anything:// 2. Allowed characters are now limited to the "safe character set", the "reserved character set", and some from the "unsafe character set" Furthermore, some of the characters are restricted in how/when they are allowed: 1. Periods, commas, question marks etc are allowed inside an URL, but not at the end. 2. [ ], ( ), " " and ' ' are allowed but only when balanced. This allows us to match e.g. [http://foo.bar/foo[bar]] correctly. Closes #2016 --- CHANGELOG.md | 5 ++++ config.c | 69 +++++++++++++++++++++++++--------------------- doc/foot.ini.5.scd | 2 +- foot.ini | 2 +- 4 files changed, 45 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc297e6c..3e421014 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,9 +79,14 @@ `Mod4` etc) in key bindings are now recognized as being virtual, and are automatically mapped to the corresponding real modifier. This means you can use e.g. `Alt+b` instead of `Mod1+b`. +* Default URL regex changed to a much more strict variant + ([#2016][2016]). You can manually set the [old + one](https://codeberg.org/dnkl/foot/src/tag/1.21.0/foot.ini#L72), if + you prefer it over the new regex. [2006]: https://codeberg.org/dnkl/foot/issues/2006 [2009]: https://codeberg.org/dnkl/foot/issues/2009 +[2016]: https://codeberg.org/dnkl/foot/issues/2016 ### Deprecated diff --git a/config.c b/config.c index a8cdb34a..bfd3ffed 100644 --- a/config.c +++ b/config.c @@ -3446,39 +3446,46 @@ config_load(struct config *conf, const char *conf_path, tokenize_cmdline("xdg-open ${url}", &conf->url.launch.argv.args); { - /* - * Based on https://gist.github.com/gruber/249502, but modified: - * - Do not allow {} at all - * - Do allow matched [] - */ - const char *url_regex_string = + const char *url_regex_string = + "(" "(" - "(" - "[a-z][[:alnum:]-]+:" // protocol - "(" - "/{1,3}|[a-z0-9%]" // slashes (what's the OR part for?) - ")" - "|" - "www[:digit:]{0,3}[.]" - //"|" - //"[a-z0-9.\\-]+[.][a-z]{2,4}/" /* "looks like domain name followed by a slash" - remove? */ - ")" - "(" - "[^[:space:](){}<>]+" - "|" - "\\(([^[:space:](){}<>]+|(\\([^[:space:](){}<>]+\\)))*\\)" - "|" - "\\[([^]\\[[:space:](){}<>]+|(\\[[^]\\[[:space:](){}<>]+\\]))*\\]" - ")+" - "(" - "\\(([^[:space:](){}<>]+|(\\([^[:space:](){}<>]+\\)))*\\)" - "|" - "\\[([^]\\[[:space:](){}<>]+|(\\[[^]\\[[:space:](){}<>]+\\]))*\\]" - "|" - "[^]\\[[:space:]`!(){};:'\".,<>?«»“”‘’]" - ")" + "(https?://|mailto:|ftp://|file:|ssh:|ssh://|git://|tel:|magnet:|ipfs://|ipns://|gemini://|gopher://|news:)" + "|" + "www\\." ")" - ; + "(" + /* Safe + reserved + some unsafe characters parenthesis and double quotes omitted (we only allow them when balanced) */ + "[0-9a-zA-Z:/?#@!$&*+,;=.~_%^\\-]+" + "|" + /* Balanced "(...)". Content is same as above, plus all _other_ characters we require to be balanced */ + "\\([]\\[\"0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\\-]*\\)" + "|" + /* Balanced "[...]". Content is same as above, plus all _other_ characters we require to be balanced */ + "\\[[\\(\\)\"0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\\-]*\\]" + "|" + /* Balanced '"..."'. Content is same as above, plus all _other_ characters we require to be balanced */ + "\"[]\\[\\(\\)0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\\-]*\"" + "|" + /* Balanced "'...'". Content is same as above, plus all _other_ characters we require to be balanced */ + "'[]\\[\\(\\)0-9a-zA-Z:/?#@!$&*+,;=.~_%^\\-]*'" + ")+" + "(" + /* Same as above, except :?!,;. are excluded */ + "[0-9a-zA-Z/#@$&*+=~_%^\\-]" + "|" + /* Balanced "(...)". Content is same as above, plus all _other_ characters we require to be balanced */ + "\\([]\\[\"0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\\-]*\\)" + "|" + /* Balanced "[...]". Content is same as above, plus all _other_ characters we require to be balanced */ + "\\[[\\(\\)\"0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\\-]*\\]" + "|" + /* Balanced '"..."'. Content is same as above, plus all _other_ characters we require to be balanced */ + "\"[]\\[\\(\\)0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\\-]*\"" + "|" + /* Balanced "'...'". Content is same as above, plus all _other_ characters we require to be balanced */ + "'[]\\[\\(\\)0-9a-zA-Z:/?#@!$&*+,;=.~_%^\\-]*'" + ")" + ")"; int r = regcomp(&conf->url.preg, url_regex_string, REG_EXTENDED); xassert(r == 0); diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 043600d2..c32a8e06 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -828,7 +828,7 @@ section. whole regex match to be used as an URL, surround all of it with parenthesis: *(regex-pattern)*. - Default: _(([a-z][[:alnum:]-]+:(/{1,3}|[a-z0-9%])|www[:digit:]{0,3}[.])([^[:space:](){}<>]+|\(([^[:space:](){}<>]+|(\([^[:space:](){}<>]+\)))\*\)|\[([^]\[[:space:](){}<>]+|(\[[^]\[[:space:](){}<>]+\]))\*\])+(\(([^[:space:](){}<>]+|(\([^[:space:](){}<>]+\)))\*\)|\[([^]\[[:space:](){}<>]+|(\[[^]\[[:space:](){}<>]+\]))\*\]|[^]\[[:space:]`!(){};:'".,<>?«»“”‘’]))_ + Default: _(((https?://|mailto:|ftp://|file:|ssh:|ssh://|git://|tel:|magnet:|ipfs://|ipns://|gemini://|gopher://|news:)|www\.)([0-9a-zA-Z:/?#@!$&\*+,;=.~\_%^\-]+|\([]\["0-9a-zA-Z:/?#@!$&'\*+,;=.~\_%^\-]\*\)|\[[\(\)"0-9a-zA-Z:/?#@!$&'\*+,;=.~\_%^\-]\*\]|"[]\[\(\)0-9a-zA-Z:/?#@!$&'\*+,;=.~\_%^\-]\*"|'[]\[\(\)0-9a-zA-Z:/?#@!$&\*+,;=.~\_%^\-]\*')+([0-9a-zA-Z/#@$&\*+=~\_%^\-]|\([]\["0-9a-zA-Z:/?#@!$&'\*+,;=.~\_%^\-]\*\)|\[[\(\)"0-9a-zA-Z:/?#@!$&'\*+,;=.~\_%^\-]\*\]|"[]\[\(\)0-9a-zA-Z:/?#@!$&'\*+,;=.~\_%^\-]\*"|'[]\[\(\)0-9a-zA-Z:/?#@!$&\*+,;=.~\_%^\-]\*'))_ # SECTION: regex diff --git a/foot.ini b/foot.ini index b852da07..b170dc34 100644 --- a/foot.ini +++ b/foot.ini @@ -69,7 +69,7 @@ # launch=xdg-open ${url} # label-letters=sadfjklewcmpgh # osc8-underline=url-mode -# regex=(([a-z][[:alnum:]-]+:(/{1,3}|[a-z0-9%])|www[:digit:]{0,3}[.])([^[:space:](){}<>]+|\(([^[:space:](){}<>]+|(\([^[:space:](){}<>]+\)))*\)|\[([^]\[[:space:](){}<>]+|(\[[^]\[[:space:](){}<>]+\]))*\])+(\(([^[:space:](){}<>]+|(\([^[:space:](){}<>]+\)))*\)|\[([^]\[[:space:](){}<>]+|(\[[^]\[[:space:](){}<>]+\]))*\]|[^]\[[:space:]`!(){};:'".,<>?«»“”‘’])) +# regex=(((https?://|mailto:|ftp://|file:|ssh:|ssh://|git://|tel:|magnet:|ipfs://|ipns://|gemini://|gopher://|news:)|www\.)([0-9a-zA-Z:/?#@!$&*+,;=.~_%^\-]+|\([]\["0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\-]*\)|\[[\(\)"0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\-]*\]|"[]\[\(\)0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\-]*"|'[]\[\(\)0-9a-zA-Z:/?#@!$&*+,;=.~_%^\-]*')+([0-9a-zA-Z/#@$&*+=~_%^\-]|\([]\["0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\-]*\)|\[[\(\)"0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\-]*\]|"[]\[\(\)0-9a-zA-Z:/?#@!$&'*+,;=.~_%^\-]*"|'[]\[\(\)0-9a-zA-Z:/?#@!$&*+,;=.~_%^\-]*')) # You can define your own regex's, by adding a section called # 'regex:' with a 'regex' and 'launch' key. These can then be tied From bdf65672c0d7567b78d065583bac3c2473690a0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Wojni=C5=82owicz?= Date: Thu, 3 Apr 2025 18:09:53 +0200 Subject: [PATCH 02/86] Themes: Add 'Molokai' theme --- themes/molokai | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 themes/molokai diff --git a/themes/molokai b/themes/molokai new file mode 100644 index 00000000..c3935f69 --- /dev/null +++ b/themes/molokai @@ -0,0 +1,23 @@ +# -*- conf -*- +# Molokai +# Based on zhou13's at https://github.com/zhou13/molokai-terminal/blob/master/xterm/Xresources + +[colors] +background=1B1D1E +foreground=CCCCCC +regular0=1B1D1E +regular1=FF0044 +regular2=82B414 +regular3=FD971F +regular4=266C98 +regular5=AC0CB1 +regular6=AE81FF +regular7=CCCCCC +bright0=808080 +bright1=F92672 +bright2=A6E22E +bright3=E6DB74 +bright4=7070F0 +bright5=D63AE1 +bright6=66D9EF +bright7=F8F8F2 From 34d3f4664b93d42ec3e1eef9a11e78756465d25a Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sun, 6 Apr 2025 15:35:54 +0900 Subject: [PATCH 03/86] xkbcommon: require libxkbcommon >= 1.8.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trying to build with an older libxkbcommon fails as follow: ``` ../input.c: In function ‘keyboard_keymap’: ../input.c:648:82: error: ‘XKB_VMOD_NAME_ALT’ undeclared (first use in this function); did you mean ‘XKB_MOD_NAME_ALT’? 648 | xkb_mod_index_t alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_VMOD_NAME_ALT); | ^~~~~~~~~~~~~~~~~ | XKB_MOD_NAME_ALT ../input.c:648:82: note: each undeclared identifier is reported only once for each function it appears in ../input.c:649:83: error: ‘XKB_VMOD_NAME_META’ undeclared (first use in this function); did you mean XKB_MOD_NAME_ALT’? 649 | xkb_mod_index_t meta = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_VMOD_NAME_META); | ^~~~~~~~~~~~~~~~~~ | XKB_MOD_NAME_ALT ../input.c:650:84: error: ‘XKB_VMOD_NAME_SUPER’ undeclared (first use in this function); did you mean ‘XKB_MOD_NAME_NUM’? 650 | xkb_mod_index_t super = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_VMOD_NAME_SUPER); | ^~~~~~~~~~~~~~~~~~~ | XKB_MOD_NAME_NUM ../input.c:651:84: error: ‘XKB_VMOD_NAME_HYPER’ undeclared (first use in this function); did you mean ‘XKB_MOD_NAME_CAPS’? 651 | xkb_mod_index_t hyper = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_VMOD_NAME_HYPER); | ^~~~~~~~~~~~~~~~~~~ | XKB_MOD_NAME_CAPS ../input.c:652:87: error: ‘XKB_VMOD_NAME_NUM’ undeclared (first use in this function); did you mean ‘XKB_MOD_NAME_NUM’? 652 | xkb_mod_index_t num_lock = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_VMOD_NAME_NUM); | ^~~~~~~~~~~~~~~~~ | XKB_MOD_NAME_NUM ../input.c:653:90: error: ‘XKB_VMOD_NAME_SCROLL’ undeclared (first use in this function); did you mean ‘XKB_LED_NAME_SCROLL’? 653 | xkb_mod_index_t scroll_lock = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_VMOD_NAME_SCROLL); | ^~~~~~~~~~~~~~~~~~~~ | XKB_LED_NAME_SCROLL ../input.c:654:90: error: ‘XKB_VMOD_NAME_LEVEL3’ undeclared (first use in this function); did you mean ‘XKB_MOD_NAME_CTRL’? 654 | xkb_mod_index_t level_three = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_VMOD_NAME_LEVEL3); | ^~~~~~~~~~~~~~~~~~~~ | XKB_MOD_NAME_CTRL ../input.c:655:89: error: ‘XKB_VMOD_NAME_LEVEL5’ undeclared (first use in this function); did you mean ‘XKB_MOD_NAME_CTRL’? 655 | xkb_mod_index_t level_five = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_VMOD_NAME_LEVEL5); | ^~~~~~~~~~~~~~~~~~~~ | XKB_MOD_NAME_CTRL ``` --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a9e47b3b..77869cc7 100644 --- a/meson.build +++ b/meson.build @@ -137,7 +137,7 @@ wayland_protocols = dependency('wayland-protocols', version: '>=1.41', default_options: ['tests=false']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') -xkb = dependency('xkbcommon', version: '>=1.0.0') +xkb = dependency('xkbcommon', version: '>=1.8.0') fontconfig = dependency('fontconfig') utf8proc = dependency('libutf8proc', required: get_option('grapheme-clustering')) From 091aa90f1a726507803108b217f52224904115be Mon Sep 17 00:00:00 2001 From: Dominique Martinet Date: Sun, 6 Apr 2025 15:48:29 +0900 Subject: [PATCH 04/86] wayland: handle xdg-shell edge constraints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wayland-protocols commit 86750c99ed06 ("xdg-shell: Add edge constraints") added a few more enums to handle, making the build fail with -Werror: ../wayland.c: In function ‘xdg_toplevel_configure’: ../wayland.c:878:9: error: enumeration value ‘XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT’ not handled in switch [-Werror=switch] 878 | switch (*state) { | ^~~~~~ ../wayland.c:878:9: error: enumeration value ‘XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT’ not handled in switch [-Werror=switch] ../wayland.c:878:9: error: enumeration value ‘XDG_TOPLEVEL_STATE_CONSTRAINED_TOP’ not handled in switch [-Werror=switch] ../wayland.c:878:9: error: enumeration value ‘XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM’ not handled in switch [-Werror=switch] (This is not part of any release yet, but can be used when building with the submodule) From a quick look it sounds like the meaning is the same as tiling as far as we are concerned so handle these as we do of tiling. --- wayland.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wayland.c b/wayland.c index 14d9bed9..b13e801d 100644 --- a/wayland.c +++ b/wayland.c @@ -887,6 +887,12 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, #if defined(XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION) case XDG_TOPLEVEL_STATE_SUSPENDED: is_suspended = true; break; +#endif +#if defined(XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION) + case XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT: is_tiled_left = true; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT: is_tiled_right = true; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_TOP: is_tiled_top = true; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM: is_tiled_bottom = true; break; #endif } From 23431e3ecfb59f71627b332852b906dc3d0bfad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 7 Apr 2025 13:32:30 +0200 Subject: [PATCH 05/86] wayland+input: add support for toplevel edge constraints Edge constraints are new (not yet available in a wayland-protocols release) toplevel states, acting as a complement to the existing tiled states. Tiled tells us we shouldn't draw shadows etc *outside our window geometry*. Constrained tells us the window cannot be resized in the constrained direction. This patch does a couple of things: * Recognize the new states when debug logging * Change is_top_left() etc to look at the new constrained state instead of the tiled state. These functions are used when both choosing cursor shape, and when determining if/how to resize a window on a CSD edge click-and-drag. * Update cursor shape selection to use the default (left_ptr) shape when on a constrained edge (or corner). * Update CSD resize triggering, to not trigger a resize when attempted on a constrained edge (or corner). See https://gitlab.freedesktop.org/wayland/wayland-protocols/-/commit/86750c99ed062c306e837f11bb9492df572ad677: An edge constraint is an complementery state to the tiled state, meaning that it's not only tiled, but constrained in a way that it can't resize in that direction. This typically means that the constrained edge is tiled against a monitor edge. An example configuration is two windows tiled next to each other on a single monitor. Together they cover the whole work area. The left window would have the following tiled and edge constraint state: [ tiled_top, tiled_right, tiled_bottom, tiled_left, constrained_top, constrained_bottom, constrained_left ] while the right window would have the following: [ tiled_top, tiled_right, tiled_bottom, tiled_left, constrained_top, constrained_bottom, constrained_right ] This aims to replace and deprecate the `gtk_surface1.configure_edges` event and the `gtk_surface1.edge_constraint` enum. --- CHANGELOG.md | 8 ++++++ input.c | 71 +++++++++++++++++++++++++++++++++++++--------------- wayland.c | 52 ++++++++++++++++++++++++++++++-------- wayland.h | 13 ++++++++++ 4 files changed, 114 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e421014..bf01bb7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,12 @@ ## Unreleased ### Added + +* Support for toplevel edge constraints. When the compositor indicates + the toplevel has edge constraints, foot will not allow the window to + be resized (via CSDs) in the constrained directions. + + ### Changed * UTF-8 error recovery now discards fewer bytes. @@ -83,6 +89,8 @@ ([#2016][2016]). You can manually set the [old one](https://codeberg.org/dnkl/foot/src/tag/1.21.0/foot.ini#L72), if you prefer it over the new regex. +* A tiled window can now be resized in the corners (via CSDs), unless + the compositor has indicated the toplevel has edge constraints. [2006]: https://codeberg.org/dnkl/foot/issues/2006 [2009]: https://codeberg.org/dnkl/foot/issues/2009 diff --git a/input.c b/input.c index abaac8eb..0f2a8446 100644 --- a/input.c +++ b/input.c @@ -2280,7 +2280,7 @@ is_top_left(const struct terminal *term, int x, int y) { int csd_border_size = term->conf->csd.border_width; return ( - (!term->window->is_tiled_top && !term->window->is_tiled_left) && + (!term->window->is_constrained_top && !term->window->is_constrained_left) && ((term->active_surface == TERM_SURF_BORDER_LEFT && y < 10 * term->scale) || (term->active_surface == TERM_SURF_BORDER_TOP && x < (10 + csd_border_size) * term->scale))); } @@ -2290,7 +2290,7 @@ is_top_right(const struct terminal *term, int x, int y) { int csd_border_size = term->conf->csd.border_width; return ( - (!term->window->is_tiled_top && !term->window->is_tiled_right) && + (!term->window->is_constrained_top && !term->window->is_constrained_right) && ((term->active_surface == TERM_SURF_BORDER_RIGHT && y < 10 * term->scale) || (term->active_surface == TERM_SURF_BORDER_TOP && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale))); } @@ -2301,7 +2301,7 @@ is_bottom_left(const struct terminal *term, int x, int y) int csd_title_size = term->conf->csd.title_height; int csd_border_size = term->conf->csd.border_width; return ( - (!term->window->is_tiled_bottom && !term->window->is_tiled_left) && + (!term->window->is_constrained_bottom && !term->window->is_constrained_left) && ((term->active_surface == TERM_SURF_BORDER_LEFT && y > csd_title_size * term->scale + term->height) || (term->active_surface == TERM_SURF_BORDER_BOTTOM && x < (10 + csd_border_size) * term->scale))); } @@ -2312,7 +2312,7 @@ is_bottom_right(const struct terminal *term, int x, int y) int csd_title_size = term->conf->csd.title_height; int csd_border_size = term->conf->csd.border_width; return ( - (!term->window->is_tiled_bottom && !term->window->is_tiled_right) && + (!term->window->is_constrained_bottom && !term->window->is_constrained_right) && ((term->active_surface == TERM_SURF_BORDER_RIGHT && y > csd_title_size * term->scale + term->height) || (term->active_surface == TERM_SURF_BORDER_BOTTOM && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale))); } @@ -2324,10 +2324,23 @@ xcursor_for_csd_border(struct terminal *term, int x, int y) else if (is_top_right(term, x, y)) return CURSOR_SHAPE_TOP_RIGHT_CORNER; else if (is_bottom_left(term, x, y)) return CURSOR_SHAPE_BOTTOM_LEFT_CORNER; else if (is_bottom_right(term, x, y)) return CURSOR_SHAPE_BOTTOM_RIGHT_CORNER; - else if (term->active_surface == TERM_SURF_BORDER_LEFT) return CURSOR_SHAPE_LEFT_SIDE; - else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return CURSOR_SHAPE_RIGHT_SIDE; - else if (term->active_surface == TERM_SURF_BORDER_TOP) return CURSOR_SHAPE_TOP_SIDE; - else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return CURSOR_SHAPE_BOTTOM_SIDE; + + else if (term->active_surface == TERM_SURF_BORDER_LEFT) + return !term->window->is_constrained_left + ? CURSOR_SHAPE_LEFT_SIDE : CURSOR_SHAPE_LEFT_PTR; + + else if (term->active_surface == TERM_SURF_BORDER_RIGHT) + return !term->window->is_constrained_right + ? CURSOR_SHAPE_RIGHT_SIDE : CURSOR_SHAPE_LEFT_PTR; + + else if (term->active_surface == TERM_SURF_BORDER_TOP) + return !term->window->is_constrained_top + ? CURSOR_SHAPE_TOP_SIDE : CURSOR_SHAPE_LEFT_PTR; + + else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) + return !term->window->is_constrained_bottom + ? CURSOR_SHAPE_BOTTOM_SIDE : CURSOR_SHAPE_LEFT_PTR; + else { BUG("Unreachable"); return CURSOR_SHAPE_NONE; @@ -3095,15 +3108,8 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, case TERM_SURF_BORDER_RIGHT: case TERM_SURF_BORDER_TOP: case TERM_SURF_BORDER_BOTTOM: { - static const enum xdg_toplevel_resize_edge map[] = { - [TERM_SURF_BORDER_LEFT] = XDG_TOPLEVEL_RESIZE_EDGE_LEFT, - [TERM_SURF_BORDER_RIGHT] = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT, - [TERM_SURF_BORDER_TOP] = XDG_TOPLEVEL_RESIZE_EDGE_TOP, - [TERM_SURF_BORDER_BOTTOM] = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM, - }; - if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) { - enum xdg_toplevel_resize_edge resize_type; + enum xdg_toplevel_resize_edge resize_type = XDG_TOPLEVEL_RESIZE_EDGE_NONE; int x = seat->mouse.x; int y = seat->mouse.y; @@ -3116,11 +3122,36 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, resize_type = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT; else if (is_bottom_right(term, x, y)) resize_type = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT; - else - resize_type = map[term->active_surface]; + else { + if (term->active_surface == TERM_SURF_BORDER_LEFT && + !term->window->is_constrained_left) + { + resize_type = XDG_TOPLEVEL_RESIZE_EDGE_LEFT; + } - xdg_toplevel_resize( - term->window->xdg_toplevel, seat->wl_seat, serial, resize_type); + else if (term->active_surface == TERM_SURF_BORDER_RIGHT && + !term->window->is_constrained_right) + { + resize_type = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT; + } + + else if (term->active_surface == TERM_SURF_BORDER_TOP && + !term->window->is_constrained_top) + { + resize_type = XDG_TOPLEVEL_RESIZE_EDGE_TOP; + } + + else if (term->active_surface == TERM_SURF_BORDER_BOTTOM && + !term->window->is_constrained_bottom) + { + resize_type = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM; + } + } + + if (resize_type != XDG_TOPLEVEL_RESIZE_EDGE_NONE) { + xdg_toplevel_resize( + term->window->xdg_toplevel, seat->wl_seat, serial, resize_type); + } } return; } diff --git a/wayland.c b/wayland.c index b13e801d..853124be 100644 --- a/wayland.c +++ b/wayland.c @@ -852,6 +852,10 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, bool is_tiled_bottom = false; bool is_tiled_left = false; bool is_tiled_right = false; + bool is_constrained_top = false; + bool is_constrained_bottom = false; + bool is_constrained_left = false; + bool is_constrained_right = false; bool is_suspended UNUSED = false; #if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG @@ -869,6 +873,12 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, [XDG_TOPLEVEL_STATE_TILED_BOTTOM] = "tiled:bottom", #if defined(XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION) /* wayland-protocols >= 1.32 */ [XDG_TOPLEVEL_STATE_SUSPENDED] = "suspended", +#endif +#if defined(XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION) + [XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT] = "constrained:left", + [XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT] = "constrained:right", + [XDG_TOPLEVEL_STATE_CONSTRAINED_TOP] = "constrained:top", + [XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM] = "constrained:bottom", #endif }; #endif @@ -889,10 +899,10 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, case XDG_TOPLEVEL_STATE_SUSPENDED: is_suspended = true; break; #endif #if defined(XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION) - case XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT: is_tiled_left = true; break; - case XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT: is_tiled_right = true; break; - case XDG_TOPLEVEL_STATE_CONSTRAINED_TOP: is_tiled_top = true; break; - case XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM: is_tiled_bottom = true; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT: is_constrained_left = true; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT: is_constrained_right = true; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_TOP: is_constrained_top = true; break; + case XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM: is_constrained_bottom = true; break; #endif } @@ -933,6 +943,10 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, win->configure.is_tiled_bottom = is_tiled_bottom; win->configure.is_tiled_left = is_tiled_left; win->configure.is_tiled_right = is_tiled_right; + win->configure.is_constrained_top = is_constrained_top; + win->configure.is_constrained_bottom = is_constrained_bottom; + win->configure.is_constrained_left = is_constrained_left; + win->configure.is_constrained_right = is_constrained_right; win->configure.width = width; win->configure.height = height; } @@ -1056,14 +1070,22 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, win->is_maximized = win->configure.is_maximized; win->is_fullscreen = win->configure.is_fullscreen; win->is_resizing = win->configure.is_resizing; + win->is_tiled_top = win->configure.is_tiled_top; win->is_tiled_bottom = win->configure.is_tiled_bottom; win->is_tiled_left = win->configure.is_tiled_left; win->is_tiled_right = win->configure.is_tiled_right; + + win->is_constrained_top = win->configure.is_constrained_top; + win->is_constrained_bottom = win->configure.is_constrained_bottom; + win->is_constrained_left = win->configure.is_constrained_left; + win->is_constrained_right = win->configure.is_constrained_right; + win->is_tiled = (win->is_tiled_top || win->is_tiled_bottom || win->is_tiled_left || win->is_tiled_right); + win->csd_mode = win->configure.csd_mode; bool enable_csd = win->csd_mode == CSD_YES && !win->is_fullscreen; @@ -1239,13 +1261,23 @@ handle_global(void *data, struct wl_registry *registry, return; /* - * We *require* version 1, but _can_ use version 5. Version 2 - * adds 'tiled' window states. We use that information to - * restore the window size when window is un-tiled. Version 5 - * adds 'wm_capabilities'. We use that information to draw - * window decorations. + * We *require* version 1, but _can_ use version 2, 5 or 7, if + * available. + * + * Version 2 adds 'tiled' window states. We use this + * information to restore the window size when window is + * un-tiled. + * + * Version 5 adds 'wm_capabilities'. We use this information + * to draw window decorations. + * + * Version 7 adds 'constrained' window states. We use this + * information to determine whether to allow window resize + * (via CSDs) or not. */ -#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) +#if defined(XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION) + const uint32_t preferred = XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION; +#elif defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) const uint32_t preferred = XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION; #elif defined(XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) const uint32_t preferred = XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION; diff --git a/wayland.h b/wayland.h index 37dd7860..a9d6858c 100644 --- a/wayland.h +++ b/wayland.h @@ -402,6 +402,12 @@ struct wl_window { bool is_tiled_left; bool is_tiled_right; bool is_tiled; /* At least one of is_tiled_{top,bottom,left,right} is true */ + + bool is_constrained_top; + bool is_constrained_bottom; + bool is_constrained_left; + bool is_constrained_right; + struct { int width; int height; @@ -409,10 +415,17 @@ struct wl_window { bool is_fullscreen:1; bool is_maximized:1; bool is_resizing:1; + bool is_tiled_top:1; bool is_tiled_bottom:1; bool is_tiled_left:1; bool is_tiled_right:1; + + bool is_constrained_top:1; + bool is_constrained_bottom:1; + bool is_constrained_left:1; + bool is_constrained_right:1; + enum csd_mode csd_mode; } configure; From bc2e0a29bba936728cd2033fcb795351b738e8a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 10 Apr 2025 12:18:34 +0200 Subject: [PATCH 06/86] changelog: move vmod support in config from "changed" to "added" --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf01bb7c..c2f18c5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,10 @@ * Support for toplevel edge constraints. When the compositor indicates the toplevel has edge constraints, foot will not allow the window to be resized (via CSDs) in the constrained directions. +* Virtual modifiers (e.g. `Alt` instead of `Mod1`, `Super` instead of + `Mod4` etc) in key bindings are now recognized as being virtual, and + are automatically mapped to the corresponding real modifier. This + means you can use e.g. `Alt+b` instead of `Mod1+b`. ### Changed @@ -81,10 +85,6 @@ kitty keyboard protocol. - some of foot's default shortcuts not working (mainly those using `Mod1`) out of the box. -* Virtual modifiers (e.g. `Alt` instead of `Mod1`, `Super` instead of - `Mod4` etc) in key bindings are now recognized as being virtual, and - are automatically mapped to the corresponding real modifier. This - means you can use e.g. `Alt+b` instead of `Mod1+b`. * Default URL regex changed to a much more strict variant ([#2016][2016]). You can manually set the [old one](https://codeberg.org/dnkl/foot/src/tag/1.21.0/foot.ini#L72), if From b93d2f042c378e9a2a6d0882c79331492d5dee09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 13 Apr 2025 08:26:20 +0200 Subject: [PATCH 07/86] url-mode: fix double-width characters not being handled correctly When a regex matches a string containing double-width characters, the CELL_SPACER values were included in the URL string. This meant the final URL (either launched, or copied) weren't handled correctly, as invalid UTF-8 sequences were inserted in the middle of the string. Closes #2027 --- CHANGELOG.md | 3 +++ url-mode.c | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2f18c5f..e9f82999 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,9 +106,12 @@ * Build failure (`srgb.h` not found) when doing a parallel build. * Regression: reflowing (changing the window size) removing empty lines ([#2011][2011]). +* `url/regex-copy` not handling double-width characters correctly + ([#2027][2027]). [2000]: https://codeberg.org/dnkl/foot/issues/2000 [2011]: https://codeberg.org/dnkl/foot/issues/2011 +[2027]: https://codeberg.org/dnkl/foot/issues/2027 ### Security diff --git a/url-mode.c b/url-mode.c index ed260597..d0f7fc53 100644 --- a/url-mode.c +++ b/url-mode.c @@ -347,6 +347,9 @@ regex_detected(const struct terminal *term, enum url_action action, wc_count = composed->count; } + else if (wc[0] >= CELL_SPACER) + continue; + /* Convert wide character to utf8 */ for (size_t i = 0; i < wc_count; i++) { char buf[16]; @@ -355,6 +358,7 @@ regex_detected(const struct terminal *term, enum url_action action, if (char_len == (size_t)-1) continue; + for (size_t j = 0; j < char_len; j++) { const size_t requires_size = vline->len + char_len; @@ -411,9 +415,9 @@ regex_detected(const struct terminal *term, enum url_action action, const size_t end = start + mlen; LOG_DBG( - "regex match at row %d: %.*srow/col = %dx%d", + "regex match at row %d: %.*s (%zu bytes), row/col = %dx%d", matches[1].rm_so, (int)mlen, &search_string[matches[1].rm_so], - v->map[start].row, v->map[start].col); + mlen, v->map[start].row, v->map[start].col); tll_push_back( *urls, From 9a6227acb354255aec507329d29b3f4a31429ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 14 Apr 2025 07:03:37 +0200 Subject: [PATCH 08/86] doc: foot.ini: workers: "if you have a ridiculous number of cores" --- doc/foot.ini.5.scd | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index c32a8e06..24df3cbb 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -419,6 +419,10 @@ empty string to be set, but it must be quoted: *KEY=""*) multithreading. Default: the number of available logical CPUs (including SMT). Note that this is not always the best value. In some cases, the number of physical _cores_ is better. + + In case you have a ridiculous amount of cores and/or threads, + consider limiting the number of *workers*, since foot cannot + parallelize more than the number of visible rows. *utmp-helper* Path to utmp logging helper binary. From 5f83278afd0530c323d4192e1095b3d1dea644c9 Mon Sep 17 00:00:00 2001 From: Fazzi Date: Mon, 9 Oct 2023 18:47:09 +0100 Subject: [PATCH 09/86] config: add alpha_mode option --- CHANGELOG.md | 3 +++ config.c | 10 ++++++++++ config.h | 2 ++ foot.ini | 2 ++ render.c | 21 +++++++++++++++++++++ 5 files changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e9f82999..f3ef0622 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -788,6 +788,9 @@ ### Added +* `alpha-mode` option to `foot.ini`. Defaults to `default`. This + config changes how alpha is handled on background colours not set by + the terminal.(e.g. vim) ([#1510](1510)) * Support for building with _wayland-protocols_ as a subproject. * Mouse wheel scrolls can now be used in `mouse-bindings` ([#1077][1077]). diff --git a/config.c b/config.c index bfd3ffed..aa52b89b 100644 --- a/config.c +++ b/config.c @@ -1095,6 +1095,15 @@ parse_section_main(struct context *ctx) return true; } + else if (strcmp(key, "alpha-mode") == 0) { + _Static_assert(sizeof(conf->alpha_mode) == sizeof(int), + "enum is not 32-bit"); + return value_to_enum( + ctx, + (const char *[]){"default", "matching", "all", NULL}, + (int *)&conf->alpha_mode); + } + else { LOG_CONTEXTUAL_ERR("not a valid option: %s", key); return false; @@ -3338,6 +3347,7 @@ config_load(struct config *conf, const char *conf_path, }, .multiplier = 3., }, + .alpha_mode = ALPHA_MODE_DEFAULT, .colors = { .fg = default_foreground, .bg = default_background, diff --git a/config.h b/config.h index a08fae31..18d1a477 100644 --- a/config.h +++ b/config.h @@ -167,6 +167,8 @@ struct config { enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode; + enum { ALPHA_MODE_DEFAULT, ALPHA_MODE_MATCHING, ALPHA_MODE_ALL } alpha_mode; + bool dpi_aware; enum {GAMMA_CORRECT_DISABLED, GAMMA_CORRECT_ENABLED, diff --git a/foot.ini b/foot.ini index b170dc34..0981e180 100644 --- a/foot.ini +++ b/foot.ini @@ -38,6 +38,8 @@ # utmp-helper=/usr/lib/utempter/utempter # When utmp backend is ‘libutempter’ (Linux) # utmp-helper=/usr/libexec/ulog-helper # When utmp backend is ‘ulog’ (FreeBSD) +# alpha-mode=default # Can be `default`, `matching` or `all` + [environment] # name=value diff --git a/render.c b/render.c index d2202468..2766e5ee 100644 --- a/render.c +++ b/render.c @@ -788,6 +788,27 @@ render_cell(struct terminal *term, pixman_image_t *pix, alpha = term->colors.alpha; } } + + if (!term->window->is_fullscreen) { + switch (term->conf->alpha_mode) { + case ALPHA_MODE_DEFAULT: { + if (cell->attrs.bg_src == COLOR_DEFAULT) { + alpha = term->colors.alpha; + } + break; + } + case ALPHA_MODE_MATCHING: { + if (cell->attrs.bg == term->colors.bg) { + alpha = term->colors.alpha; + } + break; + } + case ALPHA_MODE_ALL: { + alpha = term->colors.alpha; + break; + } + } + } } if (unlikely(is_selected && _fg == _bg)) { From bacfba135da5326d72508ba788a5cd8db3dcd671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 14 Apr 2025 16:48:44 +0200 Subject: [PATCH 10/86] changelog: move 'alpha-mode' to next-release --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3ef0622..d28f567a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -69,6 +69,11 @@ `Mod4` etc) in key bindings are now recognized as being virtual, and are automatically mapped to the corresponding real modifier. This means you can use e.g. `Alt+b` instead of `Mod1+b`. +* `alpha-mode` option to `foot.ini`. Defaults to `default`. This + config changes how alpha is handled on background colours not set by + the terminal.(e.g. vim) ([#2026](2026)) + +[2026]: https://codeberg.org/dnkl/foot/issues/2026 ### Changed @@ -788,9 +793,6 @@ ### Added -* `alpha-mode` option to `foot.ini`. Defaults to `default`. This - config changes how alpha is handled on background colours not set by - the terminal.(e.g. vim) ([#1510](1510)) * Support for building with _wayland-protocols_ as a subproject. * Mouse wheel scrolls can now be used in `mouse-bindings` ([#1077][1077]). From d2d4f538619177bc3af6c09e2465272f564ee780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 14 Apr 2025 16:58:23 +0200 Subject: [PATCH 11/86] config+render: move alpha-mode to colors.alpha-mode, fix cursor handling Move main.alpha-mode to colors.alpha-mode. Fix (inverted) cursor handling, by always using the bg color without alpha. Do a minor optimization, where we don't even lock at colors.alpha-mode if there's no transparency configured. --- config.c | 20 ++++----- config.h | 8 +++- render.c | 124 ++++++++++++++++++++++++++----------------------------- 3 files changed, 74 insertions(+), 78 deletions(-) diff --git a/config.c b/config.c index aa52b89b..347cc1ec 100644 --- a/config.c +++ b/config.c @@ -1095,15 +1095,6 @@ parse_section_main(struct context *ctx) return true; } - else if (strcmp(key, "alpha-mode") == 0) { - _Static_assert(sizeof(conf->alpha_mode) == sizeof(int), - "enum is not 32-bit"); - return value_to_enum( - ctx, - (const char *[]){"default", "matching", "all", NULL}, - (int *)&conf->alpha_mode); - } - else { LOG_CONTEXTUAL_ERR("not a valid option: %s", key); return false; @@ -1490,6 +1481,15 @@ parse_section_colors(struct context *ctx) return true; } + else if (strcmp(key, "alpha-mode") == 0) { + _Static_assert(sizeof(conf->colors.alpha_mode) == sizeof(int), + "enum is not 32-bit"); + + return value_to_enum( + ctx, + (const char *[]){"default", "matching", "all", NULL}, + (int *)&conf->colors.alpha_mode); + } else { LOG_CONTEXTUAL_ERR("not valid option"); @@ -3347,13 +3347,13 @@ config_load(struct config *conf, const char *conf_path, }, .multiplier = 3., }, - .alpha_mode = ALPHA_MODE_DEFAULT, .colors = { .fg = default_foreground, .bg = default_background, .flash = 0x7f7f00, .flash_alpha = 0x7fff, .alpha = 0xffff, + .alpha_mode = ALPHA_MODE_DEFAULT, .selection_fg = 0x80000000, /* Use default bg */ .selection_bg = 0x80000000, /* Use default fg */ .use_custom = { diff --git a/config.h b/config.h index 18d1a477..2dec82c1 100644 --- a/config.h +++ b/config.h @@ -167,8 +167,6 @@ struct config { enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode; - enum { ALPHA_MODE_DEFAULT, ALPHA_MODE_MATCHING, ALPHA_MODE_ALL } alpha_mode; - bool dpi_aware; enum {GAMMA_CORRECT_DISABLED, GAMMA_CORRECT_ENABLED, @@ -260,6 +258,12 @@ struct config { uint32_t dim[8]; uint32_t sixel[16]; + enum { + ALPHA_MODE_DEFAULT, + ALPHA_MODE_MATCHING, + ALPHA_MODE_ALL + } alpha_mode; + struct { uint32_t fg; uint32_t bg; diff --git a/render.c b/render.c index 2766e5ee..fdf7015c 100644 --- a/render.c +++ b/render.c @@ -605,13 +605,8 @@ cursor_colors_for_cell(const struct terminal *term, const struct cell *cell, if (term->colors.cursor_fg >> 31) *text_color = color_hex_to_pixman(term->colors.cursor_fg, gamma_correct); else { + xassert(bg->alpha == 0xffff); *text_color = *bg; - - if (unlikely(text_color->alpha != 0xffff)) { - /* The *only* color that can have transparency is the - * default background color */ - *text_color = color_hex_to_pixman(term->colors.bg, gamma_correct); - } } if (text_color->red == cursor_color->red && @@ -749,65 +744,58 @@ render_cell(struct terminal *term, pixman_image_t *pix, _bg = swap; } - else if (cell->attrs.bg_src == COLOR_DEFAULT) { - if (term->window->is_fullscreen) { - /* - * Note: disable transparency when fullscreened. - * - * This is because the wayland protocol mandates no - * screen content is shown behind the fullscreened - * window. - * - * The _intent_ of the specification is that a black - * (or other static color) should be used as - * background. - * - * There's a bit of gray area however, and some - * compositors have chosen to interpret the - * specification in a way that allows wallpapers to be - * seen through a fullscreen window. - * - * Given that a) the intent of the specification, and - * b) we don't know what the compositor will do, we - * simply disable transparency while in fullscreen. - * - * To see why, consider what happens if we keep our - * transparency. For example, if the background color - * is white, and alpha is 0.5, then the window will be - * drawn in a shade of gray while fullscreened. - * - * See - * https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/116 - * for a discussion on whether transparent, fullscreen - * windows should be allowed in some way or not. - * - * NOTE: if changing this, also update render_margin() - */ - xassert(alpha == 0xffff); - } else { - alpha = term->colors.alpha; - } - } - - if (!term->window->is_fullscreen) { - switch (term->conf->alpha_mode) { - case ALPHA_MODE_DEFAULT: { - if (cell->attrs.bg_src == COLOR_DEFAULT) { - alpha = term->colors.alpha; - } - break; - } - case ALPHA_MODE_MATCHING: { - if (cell->attrs.bg == term->colors.bg) { - alpha = term->colors.alpha; - } - break; - } - case ALPHA_MODE_ALL: { + if (!term->window->is_fullscreen && term->colors.alpha != 0xffff) { + switch (term->conf->colors.alpha_mode) { + case ALPHA_MODE_DEFAULT: { + if (cell->attrs.bg_src == COLOR_DEFAULT) { alpha = term->colors.alpha; - break; } + break; } + + case ALPHA_MODE_MATCHING: { + if (cell->attrs.bg == term->colors.bg) + alpha = term->colors.alpha; + break; + } + + case ALPHA_MODE_ALL: { + alpha = term->colors.alpha; + break; + } + } + } else { + /* + * Note: disable transparency when fullscreened. + * + * This is because the wayland protocol mandates no screen + * content is shown behind the fullscreened window. + * + * The _intent_ of the specification is that a black (or + * other static color) should be used as background. + * + * There's a bit of gray area however, and some + * compositors have chosen to interpret the specification + * in a way that allows wallpapers to be seen through a + * fullscreen window. + * + * Given that a) the intent of the specification, and b) + * we don't know what the compositor will do, we simply + * disable transparency while in fullscreen. + * + * To see why, consider what happens if we keep our + * transparency. For example, if the background color is + * white, and alpha is 0.5, then the window will be drawn + * in a shade of gray while fullscreened. + * + * See + * https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/116 + * for a discussion on whether transparent, fullscreen + * windows should be allowed in some way or not. + * + * NOTE: if changing this, also update render_margin() + */ + xassert(alpha == 0xffff); } } @@ -1012,8 +1000,10 @@ render_cell(struct terminal *term, pixman_image_t *pix, mtx_unlock(&term->render.workers.lock); } - if (unlikely(has_cursor && term->cursor_style == CURSOR_BLOCK && term->kbd_focus)) - draw_cursor(term, cell, font, pix, &fg, &bg, x, y, cell_cols); + if (unlikely(has_cursor && term->cursor_style == CURSOR_BLOCK && term->kbd_focus)) { + const pixman_color_t bg_without_alpha = color_hex_to_pixman(_bg, gamma_correct); + draw_cursor(term, cell, font, pix, &fg, &bg_without_alpha, x, y, cell_cols); + } if (cell->wc == 0 || cell->wc >= CELL_SPACER || cell->wc == U'\t' || (unlikely(cell->attrs.conceal) && !is_selected)) @@ -1161,8 +1151,10 @@ render_cell(struct terminal *term, pixman_image_t *pix, } draw_cursor: - if (has_cursor && (term->cursor_style != CURSOR_BLOCK || !term->kbd_focus)) - draw_cursor(term, cell, font, pix, &fg, &bg, x, y, cell_cols); + if (has_cursor && (term->cursor_style != CURSOR_BLOCK || !term->kbd_focus)) { + const pixman_color_t bg_without_alpha = color_hex_to_pixman(_bg, gamma_correct); + draw_cursor(term, cell, font, pix, &fg, &bg_without_alpha, x, y, cell_cols); + } pixman_image_set_clip_region32(pix, NULL); return cell_cols; From f7807c0f4c5f1e01b72157bbad650d20a1a9d4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 14 Apr 2025 17:00:07 +0200 Subject: [PATCH 12/86] tests: config: test colors.alpha-mode --- tests/test-config.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/test-config.c b/tests/test-config.c index f431f4ab..69d349b4 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -720,6 +720,11 @@ test_section_colors(void) &conf.colors.search_box.match.fg, &conf.colors.search_box.match.bg); + test_enum(&ctx, &parse_section_colors, "alpha-mode", 3, + (const char *[]){"default", "matching", "all"}, + (int []){ALPHA_MODE_DEFAULT, ALPHA_MODE_MATCHING, ALPHA_MODE_ALL}, + (int *)&conf.colors.alpha_mode); + for (size_t i = 0; i < 255; i++) { char key_name[4]; sprintf(key_name, "%zu", i); From 9ba8caf30b9d042260e4125b30a17a0f3c96382e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 14 Apr 2025 17:02:45 +0200 Subject: [PATCH 13/86] doc: foot.ini: add colors.alpha-mode --- doc/foot.ini.5.scd | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 24df3cbb..083a1087 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -1031,6 +1031,21 @@ can configure the background transparency with the _alpha_ option. Background translucency. A value in the range 0.0-1.0, where 0.0 means completely transparent, and 1.0 is opaque. Default: _1.0_. +*alpha-mode* + Specifies when *alpha* is applied. One of *default*, *matching* or + *all*. + + *default* applies *alpha* to cells with the default background + color, excluding cells with the same RGB value as the default + background color. + + *matching* is the same as *default*, but also applies *alpha* to + cells with the same RGB value as the default background color. + + *all* applies *alpha* to all cells, regardless of background color. + + Default: _default_ + *selection-foreground*, *selection-background* Foreground (text) and background color to use in selected text. Note that *both* options must be set, or the default will be From b46a9aa6d7b76712ea0dca4f3799ae4a504843db Mon Sep 17 00:00:00 2001 From: datsudo <76833632+datsudo@users.noreply.github.com> Date: Mon, 14 Apr 2025 22:01:54 +0800 Subject: [PATCH 14/86] themes: add "Night Owl" theme --- themes/night-owl | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 themes/night-owl diff --git a/themes/night-owl b/themes/night-owl new file mode 100644 index 00000000..03e1d8f7 --- /dev/null +++ b/themes/night-owl @@ -0,0 +1,30 @@ +# _*_ conf _*_ +# Night Owl + +[cursor] +color=011627 80a4c2 + +[colors] +foreground=d6deeb +background=011627 + +regular0=011627 +regular1=ef5350 +regular2=22da6e +regular3=addb67 +regular4=82aaff +regular5=c792ea +regular6=21c7a8 +regular7=ffffff + +bright0=575656 +bright1=ef5350 +bright2=22da6e +bright3=ffeb95 +bright4=82aaff +bright5=c792ea +bright6=7fdbca +bright7=ffffff + +selection-background=5f7e97 +selection-foreground=dfe5ee From 2c8214f6eac2ad8def58ecce76e806db18b4a4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 17 Apr 2025 14:41:13 +0200 Subject: [PATCH 15/86] changelog: prepare for 1.22.0 --- CHANGELOG.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d28f567a..374d0c2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* [Unreleased](#unreleased) +* [1.22.0](#1-22-0) * [1.21.0](#1-21-0) * [1.20.2](#1-20-2) * [1.20.1](#1-20-1) @@ -59,7 +59,8 @@ * [1.2.0](#1-2-0) -## Unreleased +## 1.22.0 + ### Added * Support for toplevel edge constraints. When the compositor indicates @@ -102,8 +103,6 @@ [2016]: https://codeberg.org/dnkl/foot/issues/2016 -### Deprecated -### Removed ### Fixed * Regression: assertion in `url-mode.c` when activating a second URL @@ -119,9 +118,16 @@ [2027]: https://codeberg.org/dnkl/foot/issues/2027 -### Security ### Contributors +* Alex Xu (Hello71) +* datsudo +* Dominique Martinet +* Fazzi +* llyyr +* Łukasz Wojniłowicz +* Sam McCall + ## 1.21.0 From 95f7b7105841f81788229dbc29055b0a98fff041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 17 Apr 2025 14:41:32 +0200 Subject: [PATCH 16/86] meson: bump version to 1.22.0 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 77869cc7..ed2dc7e4 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('foot', 'c', - version: '1.21.0', + version: '1.22.0', license: 'MIT', meson_version: '>=0.59.0', default_options: [ From 6e5a602f67a1c95fbec07a1e19027c4454e8ab81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 17 Apr 2025 14:44:05 +0200 Subject: [PATCH 17/86] changelog: add new 'unreleased' section --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 374d0c2c..d8787775 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.22.0](#1-22-0) * [1.21.0](#1-21-0) * [1.20.2](#1-20-2) @@ -59,6 +60,16 @@ * [1.2.0](#1-2-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.22.0 ### Added From 30aafce82d344e0ec77e4409945b733c66011433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 18 Apr 2025 13:59:43 +0200 Subject: [PATCH 18/86] foot.ini: move alpha-mode to colors section This is where the config parser expects it --- foot.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/foot.ini b/foot.ini index 0981e180..7d96ca0f 100644 --- a/foot.ini +++ b/foot.ini @@ -38,8 +38,6 @@ # utmp-helper=/usr/lib/utempter/utempter # When utmp backend is ‘libutempter’ (Linux) # utmp-helper=/usr/libexec/ulog-helper # When utmp backend is ‘ulog’ (FreeBSD) -# alpha-mode=default # Can be `default`, `matching` or `all` - [environment] # name=value @@ -102,6 +100,7 @@ [colors] # alpha=1.0 +# alpha-mode=default # Can be `default`, `matching` or `all` # background=242424 # foreground=ffffff # flash=7f7f00 From 155c7c96b7a384913d62021b1fa039a1e9d01b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 18 Apr 2025 14:43:36 +0200 Subject: [PATCH 19/86] doc: foot.ini: key-bindings: untranslated symbols are tried before translated --- doc/foot.ini.5.scd | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 083a1087..d51409b8 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -1190,17 +1190,18 @@ different approaches. As an example, let's say you press ctrl+shift+c (assume plain us ASCII layout). XKB will tell foot *Control+C* was pressed. Note the lack of the shift modifier, and the upper case 'C'. Internally, this is called -the "translated" form, and is what foot tries to match first. +the "translated" form. -If no "translated" key bindings can be found, foot proceeds to -checking the "untranslated" variant. Using the same example as above, -this will match *Control+Shift+c* (shift modifier present, lower case -'c'). +The "untranslated" form (*Control+Shift+c*) is derived from the +translated form, and is what foot tries to match first. + +If no "untranslated" key bindings can be found, foot proceeds to +checking the "translated" variant. This means you can use either form in your foot configuration, and -that *Control+C* (and similar) has higher priority than -*Control+Shift+c*. Also note that while foot normally detects when the -same combination is assigned to multiple actions, it will not detect +that *Control+Shift+c* (and similar) has higher priority than +*Control+C*. Also note that while foot normally detects when the same +combination is assigned to multiple actions, it will not detect *Control+C* vs. *Control+Shift+c* collisions. Call it a known bug... Finally, foot tries to match the raw key code. Here, the primary From 179e14e0a1792ebf7a5c2774a66b822ce2017a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Apr 2025 09:16:28 +0200 Subject: [PATCH 20/86] doc: foot.ini: gamma-correct-blending: mention colors being off --- doc/foot.ini.5.scd | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index d51409b8..26eb1780 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -220,11 +220,12 @@ empty string to be set, but it must be quoted: *KEY=""*) than intended when rendered with gamma-correct blending, since the font designer set the font weight based on incorrect rendering. - You may also want to enable 10-bit image buffers when - gamma-correct blending is enabled. Though probably only if you do - not use a transparent background (with 10-bit buffers, you only - get 2 bits alpha). See *tweak.surface-bit-depth*. - + Note that some colors (especially dark ones) will look a bit + off. The reason for this is loss of color precision, due to foot + using 8-bit surfaces (i.e. each color channel is 8 bits). The + amount of errors can be reduced by using 10-bit surfaces; see + *tweak.surface-bit-depth*. + Default: enabled when compositor support is available *box-drawings-uses-font-glyphs* @@ -1978,13 +1979,13 @@ any of these options. best option. When *gamma-correct-blending* is enabled, you may want to enable - 10-bit surfaces, as that improves the color resolution. Be aware + 10-bit surfaces, as that improves color precision. Be aware however, that in this mode, the alpha channel is only 2 bits instead of 8 bits. Thus, if you are using a transparent background, you may want to use the default, *8-bit*, even if you have gamma-correct blending enabled. - You should also note that 10-bit surface is slower. This will + You should also note that 10-bit surface is much slower. This will increase input latency and decrease rendering throughput. Default: _8-bit_ From 1bf91566287904664f5c7f68ad09082148eceab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Apr 2025 11:59:50 +0200 Subject: [PATCH 21/86] doc: foot.ini: spaces -> tab (for indentation) --- doc/foot.ini.5.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 26eb1780..cdfc65a0 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -225,7 +225,7 @@ empty string to be set, but it must be quoted: *KEY=""*) using 8-bit surfaces (i.e. each color channel is 8 bits). The amount of errors can be reduced by using 10-bit surfaces; see *tweak.surface-bit-depth*. - + Default: enabled when compositor support is available *box-drawings-uses-font-glyphs* From 1a2e5f4932a17c9804ca6d201b7d8cf84d7f19d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Apr 2025 07:46:06 +0200 Subject: [PATCH 22/86] render: fix colors.alpha-mode=matching Before this patch, it only matched RGB color sources. It did not match the default bg color, or indexed colors. That is, e.g. CSI 43m didn't apply alpha, even if the color3 matched the default background color. --- CHANGELOG.md | 4 ++++ render.c | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8787775..6958c869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,10 @@ ### Deprecated ### Removed ### Fixed + +* `colors.alpha-mode=matching` not working as intended. + + ### Security ### Contributors diff --git a/render.c b/render.c index fdf7015c..0e403949 100644 --- a/render.c +++ b/render.c @@ -754,8 +754,15 @@ render_cell(struct terminal *term, pixman_image_t *pix, } case ALPHA_MODE_MATCHING: { - if (cell->attrs.bg == term->colors.bg) + if (cell->attrs.bg_src == COLOR_DEFAULT || + ((cell->attrs.bg_src == COLOR_BASE16 || + cell->attrs.bg_src == COLOR_BASE256) && + term->colors.table[cell->attrs.bg] == term->colors.bg) || + (cell->attrs.bg_src == COLOR_RGB && + cell->attrs.bg == term->colors.bg)) + { alpha = term->colors.alpha; + } break; } From cb2a64c5854205092150afe80f96177764bd4038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Apr 2025 12:16:48 +0200 Subject: [PATCH 23/86] csi: don't allow client app to enable grapheme-shaping when disabled at compile-time Closes #2039 --- CHANGELOG.md | 5 +++++ csi.c | 2 ++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6958c869..c15e05bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,11 @@ ### Fixed * `colors.alpha-mode=matching` not working as intended. +* Grapheme shaping was allowed to be "enabled" at runtime, even though + disabled at compile time. This caused mis-rendering of certain + codepoints ([#2039][2039]). + +[2039]: https://codeberg.org/dnkl/foot/issues/2039 ### Security diff --git a/csi.c b/csi.c index 81c71e31..b66fda21 100644 --- a/csi.c +++ b/csi.c @@ -558,7 +558,9 @@ decset_decrst(struct terminal *term, unsigned param, bool enable) break; case 2027: +#if defined(FOOT_GRAPHEME_CLUSTERING) term->grapheme_shaping = enable; +#endif break; case 2048: From ef4a680ae813b23e7b9d1b6dd67413dcad377655 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Apr 2025 08:05:15 +0200 Subject: [PATCH 24/86] input: reset modifiers in keyboard_leave() Closes #2034 --- CHANGELOG.md | 3 +++ input.c | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c15e05bf..35669f03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,8 +71,11 @@ * Grapheme shaping was allowed to be "enabled" at runtime, even though disabled at compile time. This caused mis-rendering of certain codepoints ([#2039][2039]). +* Keyboard modifiers not being reset on keyboard leave events + ([#2034][2034]). [2039]: https://codeberg.org/dnkl/foot/issues/2039 +[2034]: https://codeberg.org/dnkl/foot/issues/2034 ### Security diff --git a/input.c b/input.c index 0f2a8446..d7a7975a 100644 --- a/input.c +++ b/input.c @@ -765,9 +765,17 @@ keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, seat->kbd.alt = false; seat->kbd.ctrl = false; seat->kbd.super = false; + if (seat->kbd.xkb_compose_state != NULL) xkb_compose_state_reset(seat->kbd.xkb_compose_state); + if (seat->kbd.xkb_state != NULL && seat->kbd.xkb_keymap != NULL) { + const xkb_layout_index_t layout_count = xkb_keymap_num_layouts(seat->kbd.xkb_keymap); + + for (xkb_layout_index_t i = 0; i < layout_count; i++) + xkb_state_update_mask(seat->kbd.xkb_state, 0, 0, 0, i, i, i); + } + if (old_focused != NULL) { seat->pointer.hidden = false; term_xcursor_update_for_seat(old_focused, seat); From 8bded8ce8cf22db51dd42bb71a2a5d39624df402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 19 Apr 2025 17:10:52 +0200 Subject: [PATCH 25/86] doc: foot.ini: add newish Unicode range to 'box-drawings-uses-font-glyphs' --- doc/foot.ini.5.scd | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index cdfc65a0..cffbf9c5 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -250,6 +250,7 @@ empty string to be set, but it must be quoted: *KEY=""*) - U+02500 - U+0259F - U+02800 - U+028FF + - U+1CD00 - U+1CDE5 - U+1Fb00 - U+1FB9B Default: _no_. From bc8d6d1ff350672cae7d7e6572eab2d10b1b415e Mon Sep 17 00:00:00 2001 From: Jan Palus Date: Wed, 23 Apr 2025 11:44:41 +0200 Subject: [PATCH 26/86] build: fix race when generating emoji-variation-sequences.h d3f692990ef6 moved emoji-variation-sequences.h header inclusion from vt.c to terminal.c. these two files are part of different libraries hence target for generating emoji-variation-sequences.h needs to be moved too. --- meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ed2dc7e4..3d97040d 100644 --- a/meson.build +++ b/meson.build @@ -253,7 +253,7 @@ vtlib = static_library( 'osc.c', 'osc.h', 'sixel.c', 'sixel.h', 'vt.c', 'vt.h', - builtin_terminfo, emoji_variation_sequences, srgb_funcs, + builtin_terminfo, srgb_funcs, wl_proto_src + wl_proto_headers, version, dependencies: [libepoll, pixman, fcft, tllist, wayland_client, xkb, utf8proc], @@ -265,6 +265,7 @@ pgolib = static_library( 'grid.c', 'grid.h', 'selection.c', 'selection.h', 'terminal.c', 'terminal.h', + emoji_variation_sequences, wl_proto_src + wl_proto_headers, dependencies: [libepoll, pixman, fcft, tllist, wayland_client, xkb, utf8proc], link_with: vtlib, From b2dfd339e4478ab4d16e249bf491c45f7f4ae587 Mon Sep 17 00:00:00 2001 From: valoq Date: Mon, 21 Apr 2025 13:35:05 +0000 Subject: [PATCH 27/86] Add alacritty theme This adds the default colors from alacritty as an additional theme --- themes/alacritty | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 themes/alacritty diff --git a/themes/alacritty b/themes/alacritty new file mode 100644 index 00000000..a5e4d2c1 --- /dev/null +++ b/themes/alacritty @@ -0,0 +1,59 @@ +# -*- conf -*- +# Alacritty + +[cursor] +color = 181818 56d8c9 + +[colors] +background= 181818 +foreground= d8d8d8 + +#black +regular0= 181818 + +#red +regular1= ac4242 + +#green +regular2= 90a959 + +#yellow +regular3= f4bf75 + +#blue +regular4= 6a9fb5 + +#magenta +regular5= aa759f + +#cyan +regular6= 75b5aa + +#white/grey +regular7= d8d8d8 + + + +#grey/black +bright0= 6b6b6b + +#red +bright1= c55555 + +#green +bright2= aac474 + +#yellow +bright3= feca88 + +#blue +bright4= 82b8c8 + +#pink +bright5= c28cb8 + +#cyan +bright6= 93d3c3 + +#grey +bright7= f8f8f8 \ No newline at end of file From 70b324b24c86473a99528feb6f1f91ca70e11a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Apr 2025 08:23:56 +0200 Subject: [PATCH 28/86] term: ignore LTR+RTL markers (U+200E + U+200F) Foot doesn't implement RTL, and explicit LTR markers is neither needed, nor used in anyway. In fact, they cause issues with font lookup, as fcft often fails to find the marker codepoint in the primary font, causing a fallback font to be used instead. Closes #2049 --- CHANGELOG.md | 9 +++++++++ terminal.c | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35669f03..62dc7e03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,13 @@ ## Unreleased ### Added ### Changed + +* Left-to-right and right-to-left markers (U+200E and U+200F) are now + ignored ([#2049][2049]). + +[2049]: https://codeberg.org/dnkl/foot/issues/2049 + + ### Deprecated ### Removed ### Fixed @@ -73,6 +80,8 @@ codepoints ([#2039][2039]). * Keyboard modifiers not being reset on keyboard leave events ([#2034][2034]). +* Last character in a `remind` calendar event being in the wrong font + and color ([#2049][2049]). [2039]: https://codeberg.org/dnkl/foot/issues/2039 [2034]: https://codeberg.org/dnkl/foot/issues/2034 diff --git a/terminal.c b/terminal.c index ae1adb1a..59c39760 100644 --- a/terminal.c +++ b/terminal.c @@ -4156,6 +4156,14 @@ term_process_and_print_non_ascii(struct terminal *term, char32_t wc) (grapheme_clustering || (!grapheme_clustering && width == 0 && wc >= 0x300))) { + if (unlikely(wc == 0x200e || wc == 0x200f)) { + /* + * Ignore left-to-right and right-to-left markers + * see https://codeberg.org/dnkl/foot/issues/2049 + */ + return; + } + int col = term->grid->cursor.point.col; if (!term->grid->cursor.lcf) col--; From 1b15cc5f3d633809114d9d569b34abf934426c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Apr 2025 18:20:18 +0200 Subject: [PATCH 29/86] Revert "term: ignore LTR+RTL markers (U+200E + U+200F)" This reverts commit 70b324b24c86473a99528feb6f1f91ca70e11a02. --- CHANGELOG.md | 9 --------- terminal.c | 8 -------- 2 files changed, 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62dc7e03..35669f03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,13 +63,6 @@ ## Unreleased ### Added ### Changed - -* Left-to-right and right-to-left markers (U+200E and U+200F) are now - ignored ([#2049][2049]). - -[2049]: https://codeberg.org/dnkl/foot/issues/2049 - - ### Deprecated ### Removed ### Fixed @@ -80,8 +73,6 @@ codepoints ([#2039][2039]). * Keyboard modifiers not being reset on keyboard leave events ([#2034][2034]). -* Last character in a `remind` calendar event being in the wrong font - and color ([#2049][2049]). [2039]: https://codeberg.org/dnkl/foot/issues/2039 [2034]: https://codeberg.org/dnkl/foot/issues/2034 diff --git a/terminal.c b/terminal.c index 59c39760..ae1adb1a 100644 --- a/terminal.c +++ b/terminal.c @@ -4156,14 +4156,6 @@ term_process_and_print_non_ascii(struct terminal *term, char32_t wc) (grapheme_clustering || (!grapheme_clustering && width == 0 && wc >= 0x300))) { - if (unlikely(wc == 0x200e || wc == 0x200f)) { - /* - * Ignore left-to-right and right-to-left markers - * see https://codeberg.org/dnkl/foot/issues/2049 - */ - return; - } - int col = term->grid->cursor.point.col; if (!term->grid->cursor.lcf) col--; From 1fec0cf5ea0c3fe3be92a49eaee5eef5aafa9c49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Apr 2025 18:22:37 +0200 Subject: [PATCH 30/86] Revert "term: append zero-width grapheme breaking characters to previous cell" This reverts commit 76503fb86a8b8a6b5c3ce1be87c15a55af38508d. --- CHANGELOG.md | 4 ++-- terminal.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35669f03..1aedf331 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -229,9 +229,9 @@ enabled ([#1947][1947]). * Reflow of the cursor (active + saved) when at the end of the line with a pending wrap (LCF set) ([#1954][1954]). -* Zero-width characters that also are grapheme breaks (e.g. U+200B, +* ~~Zero-width characters that also are grapheme breaks (e.g. U+200B, ZERO WIDTH SPACE) being ignored (discarded and never stored in the - grid) ([#1960][1960]). + grid) ([#1960][1960]).~~ (reverted) * `--server=` not working on FreeBSD ([#1956][1956]). * Crash when resetting the terminal and an application had previously set a custom app ID ([#1963][1963]) diff --git a/terminal.c b/terminal.c index ae1adb1a..f2d03e77 100644 --- a/terminal.c +++ b/terminal.c @@ -4188,7 +4188,7 @@ term_process_and_print_non_ascii(struct terminal *term, char32_t wc) if (grapheme_clustering) { /* Check if we're on a grapheme cluster break */ if (utf8proc_grapheme_break_stateful( - last, wc, &term->vt.grapheme_state) && width > 0) + last, wc, &term->vt.grapheme_state)) { term_reset_grapheme_state(term); goto out; From d43326d2b5775f832c50c9cb297c25c5950d6a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Apr 2025 18:40:22 +0200 Subject: [PATCH 31/86] changelog: zero-width grapheme breaking codepoints causing fallback font to be used --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aedf331..3edd8212 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,9 +73,13 @@ codepoints ([#2039][2039]). * Keyboard modifiers not being reset on keyboard leave events ([#2034][2034]). +* Fallback font (and possibly wrong color) being used when a character + was followed by a zero-width grapheme breaking codepoint (for + example, _LEFT-TO-RIGHT MARK_) ([#2049][2049]). [2039]: https://codeberg.org/dnkl/foot/issues/2039 [2034]: https://codeberg.org/dnkl/foot/issues/2034 +[2049]: https://codeberg.org/dnkl/foot/issues/2049 ### Security From cb1b7ba0c5752eeb92c72a80640fbd1f09ad4b7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Apr 2025 19:20:36 +0200 Subject: [PATCH 32/86] render: regression: alpha applied to inversed text/selections Introduced by 5f83278afd0530c323d4192e1095b3d1dea644c9 Closes #2073 --- CHANGELOG.md | 2 ++ render.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3edd8212..3e9ce91c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,8 @@ * Fallback font (and possibly wrong color) being used when a character was followed by a zero-width grapheme breaking codepoint (for example, _LEFT-TO-RIGHT MARK_) ([#2049][2049]). +* Regression: alpha applied to inversed text/selections + ([#2073][2073]). [2039]: https://codeberg.org/dnkl/foot/issues/2039 [2034]: https://codeberg.org/dnkl/foot/issues/2034 diff --git a/render.c b/render.c index 0e403949..b0d21d18 100644 --- a/render.c +++ b/render.c @@ -744,7 +744,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, _bg = swap; } - if (!term->window->is_fullscreen && term->colors.alpha != 0xffff) { + else if (!term->window->is_fullscreen && term->colors.alpha != 0xffff) { switch (term->conf->colors.alpha_mode) { case ALPHA_MODE_DEFAULT: { if (cell->attrs.bg_src == COLOR_DEFAULT) { From 0020ef12b472f8a57dc67fc912d64872f05b3760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Apr 2025 10:31:09 +0200 Subject: [PATCH 33/86] changelog: add missing bug ref --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e9ce91c..2f2e9d88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,7 @@ [2039]: https://codeberg.org/dnkl/foot/issues/2039 [2034]: https://codeberg.org/dnkl/foot/issues/2034 [2049]: https://codeberg.org/dnkl/foot/issues/2049 +[2073]: https://codeberg.org/dnkl/foot/issues/2073 ### Security From 89bfac00e7cc4f36a17d91208678412a6cba4f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Apr 2025 10:36:13 +0200 Subject: [PATCH 34/86] changelog: prepare for 1.22.1 --- CHANGELOG.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f2e9d88..aeabbe14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* [Unreleased](#unreleased) +* [1.22.1](#1-22-1) * [1.22.0](#1-22-0) * [1.21.0](#1-21-0) * [1.20.2](#1-20-2) @@ -60,11 +60,8 @@ * [1.2.0](#1-2-0) -## Unreleased -### Added -### Changed -### Deprecated -### Removed +## 1.22.1 + ### Fixed * `colors.alpha-mode=matching` not working as intended. @@ -85,9 +82,11 @@ [2073]: https://codeberg.org/dnkl/foot/issues/2073 -### Security ### Contributors +* Jan Palus +* valoq + ## 1.22.0 From c85d5d50965d7fdc5718b3d1b69b9e09e90e44f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Apr 2025 10:36:23 +0200 Subject: [PATCH 35/86] meson: bump version to 1.22.1 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 3d97040d..7ac033b5 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('foot', 'c', - version: '1.22.0', + version: '1.22.1', license: 'MIT', meson_version: '>=0.59.0', default_options: [ From 79f6b4b1deefac678665a7420cd68fd410c80f46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Apr 2025 10:41:14 +0200 Subject: [PATCH 36/86] changelog: add new 'unreleased' section --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aeabbe14..3c48c41c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.22.1](#1-22-1) * [1.22.0](#1-22-0) * [1.21.0](#1-21-0) @@ -60,6 +61,16 @@ * [1.2.0](#1-2-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.22.1 ### Fixed From a7276d9dff31f5a8d889a9a0c5da974019e1a623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Apr 2025 06:54:58 +0200 Subject: [PATCH 37/86] config: refactor: break out 'colors' to a color_theme struct --- config.h | 106 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/config.h b/config.h index 2dec82c1..4a4e4ae9 100644 --- a/config.h +++ b/config.h @@ -131,6 +131,59 @@ struct custom_regex { struct config_spawn_template launch; }; +struct color_theme { + uint32_t fg; + uint32_t bg; + uint32_t flash; + uint32_t flash_alpha; + uint32_t table[256]; + uint16_t alpha; + uint32_t selection_fg; + uint32_t selection_bg; + uint32_t url; + + uint32_t dim[8]; + uint32_t sixel[16]; + + enum { + ALPHA_MODE_DEFAULT, + ALPHA_MODE_MATCHING, + ALPHA_MODE_ALL + } alpha_mode; + + struct { + uint32_t fg; + uint32_t bg; + } jump_label; + + struct { + uint32_t fg; + uint32_t bg; + } scrollback_indicator; + + struct { + struct { + uint32_t fg; + uint32_t bg; + } no_match; + + struct { + uint32_t fg; + uint32_t bg; + } match; + } search_box; + + struct { + bool selection:1; + bool jump_label:1; + bool scrollback_indicator:1; + bool url:1; + bool search_box_no_match:1; + bool search_box_match:1; + uint8_t dim; + } use_custom; +}; + struct config { char *term; char *shell; @@ -244,58 +297,7 @@ struct config { tll(struct custom_regex) custom_regexes; - struct { - uint32_t fg; - uint32_t bg; - uint32_t flash; - uint32_t flash_alpha; - uint32_t table[256]; - uint16_t alpha; - uint32_t selection_fg; - uint32_t selection_bg; - uint32_t url; - - uint32_t dim[8]; - uint32_t sixel[16]; - - enum { - ALPHA_MODE_DEFAULT, - ALPHA_MODE_MATCHING, - ALPHA_MODE_ALL - } alpha_mode; - - struct { - uint32_t fg; - uint32_t bg; - } jump_label; - - struct { - uint32_t fg; - uint32_t bg; - } scrollback_indicator; - - struct { - struct { - uint32_t fg; - uint32_t bg; - } no_match; - - struct { - uint32_t fg; - uint32_t bg; - } match; - } search_box; - - struct { - bool selection:1; - bool jump_label:1; - bool scrollback_indicator:1; - bool url:1; - bool search_box_no_match:1; - bool search_box_match:1; - uint8_t dim; - } use_custom; - } colors; + struct color_theme colors; struct { enum cursor_style style; From 624c383a1f45a4fbd5ebf687613c509e6c22d909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Apr 2025 07:16:18 +0200 Subject: [PATCH 38/86] config: move cursor.color to colors.cursor --- CHANGELOG.md | 8 ++++++++ config.c | 41 +++++++++++++++++++++++++++++++---------- config.h | 12 +++++++----- doc/foot.ini.5.scd | 18 +++++++++--------- foot.ini | 3 ++- osc.c | 10 ++++++++-- terminal.c | 8 ++++---- 7 files changed, 69 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c48c41c..7b8348ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,7 +64,15 @@ ## Unreleased ### Added ### Changed + +* `cursor.color` moved to `colors.cursor`. + + ### Deprecated + +* `cursor.color` config option; use `colors.cursor` instead. + + ### Removed ### Fixed ### Security diff --git a/config.c b/config.c index 347cc1ec..4337d001 100644 --- a/config.c +++ b/config.c @@ -1445,6 +1445,20 @@ parse_section_colors(struct context *ctx) return true; } + else if (streq(key, "cursor")) { + if (!value_to_two_colors( + ctx, + &conf->colors.cursor.text, + &conf->colors.cursor.cursor, + false)) + { + return false; + } + + conf->colors.use_custom.cursor = true; + return true; + } + else if (streq(key, "urls")) { if (!value_to_color(ctx, &conf->colors.url, false)) return false; @@ -1537,17 +1551,24 @@ parse_section_cursor(struct context *ctx) return value_to_uint32(ctx, 10, &conf->cursor.blink.rate_ms); else if (streq(key, "color")) { + LOG_WARN("%s:%d: cursor.color: deprecated; use colors.cursor instead", + ctx->path, ctx->lineno); + + user_notification_add( + &conf->notifications, + USER_NOTIFICATION_DEPRECATED, + xstrdup("cursor.color: use colors.cursor instead")); + if (!value_to_two_colors( - ctx, - &conf->cursor.color.text, - &conf->cursor.color.cursor, - false)) + ctx, + &conf->colors.cursor.text, + &conf->colors.cursor.cursor, + false)) { return false; } - conf->cursor.color.text |= 1u << 31; - conf->cursor.color.cursor |= 1u << 31; + conf->colors.use_custom.cursor = true; return true; } @@ -3356,6 +3377,10 @@ config_load(struct config *conf, const char *conf_path, .alpha_mode = ALPHA_MODE_DEFAULT, .selection_fg = 0x80000000, /* Use default bg */ .selection_bg = 0x80000000, /* Use default fg */ + .cursor = { + .text = 0, + .cursor = 0, + }, .use_custom = { .selection = false, .jump_label = false, @@ -3371,10 +3396,6 @@ config_load(struct config *conf, const char *conf_path, .enabled = false, .rate_ms = 500, }, - .color = { - .text = 0, - .cursor = 0, - }, .beam_thickness = {.pt = 1.5}, .underline_thickness = {.pt = 0., .px = -1}, }, diff --git a/config.h b/config.h index 4a4e4ae9..89740db3 100644 --- a/config.h +++ b/config.h @@ -149,7 +149,12 @@ struct color_theme { ALPHA_MODE_DEFAULT, ALPHA_MODE_MATCHING, ALPHA_MODE_ALL - } alpha_mode; + } alpha_mode; + + struct { + uint32_t text; + uint32_t cursor; + } cursor; struct { uint32_t fg; @@ -174,6 +179,7 @@ struct color_theme { } search_box; struct { + bool cursor:1; bool selection:1; bool jump_label:1; bool scrollback_indicator:1; @@ -306,10 +312,6 @@ struct config { bool enabled; uint32_t rate_ms; } blink; - struct { - uint32_t text; - uint32_t cursor; - } color; struct pt_or_px beam_thickness; struct pt_or_px underline_thickness; } cursor; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index cffbf9c5..13f768c2 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -898,15 +898,6 @@ applications can change these at runtime. enabled. Expressed in milliseconds between each blink. Default: _500_. -*color* - Two space separated RRGGBB values (i.e. plain old 6-digit hex - values, without prefix) specifying the foreground (text) and - background (cursor) colors for the cursor. - - Example: *ff0000 00ff00* (green cursor, red text) - - Default: the regular foreground and background colors, reversed. - *beam-thickness* Thickness (width) of the beam styled cursor. The value is in points, and its exact value thus depends on the monitor's DPI. To @@ -967,6 +958,15 @@ The colors are in RRGGBB format (i.e. plain old 6-digit hex values, without prefix). That is, they do *not* have an alpha component. You can configure the background transparency with the _alpha_ option. +*cursor* + Two space separated RRGGBB values (i.e. plain old 6-digit hex + values, without prefix) specifying the foreground (text) and + background (cursor) colors for the cursor. + + Example: *ff0000 00ff00* (green cursor, red text) + + Default: the regular foreground and background colors, reversed. + *foreground* Default foreground color. This is the color used when no ANSI color is being used. Default: _839496_. diff --git a/foot.ini b/foot.ini index 7d96ca0f..dc2ad6cd 100644 --- a/foot.ini +++ b/foot.ini @@ -85,7 +85,6 @@ [cursor] # style=block -# color= # blink=no # blink-rate=500 # beam-thickness=1.5 @@ -106,6 +105,8 @@ # flash=7f7f00 # flash-alpha=0.5 +# cursor= + ## Normal/regular colors (color palette 0-7) # regular0=242424 # black # regular1=f62b5a # red diff --git a/osc.c b/osc.c index eaf6e33e..7e3e6376 100644 --- a/osc.c +++ b/osc.c @@ -1570,8 +1570,14 @@ osc_dispatch(struct terminal *term) case 112: LOG_DBG("resetting cursor color"); - term->colors.cursor_fg = term->conf->cursor.color.text; - term->colors.cursor_bg = term->conf->cursor.color.cursor; + term->colors.cursor_fg = term->conf->colors.cursor.text; + term->colors.cursor_bg = term->conf->colors.cursor.cursor; + + if (term->conf->colors.use_custom.cursor) { + term->colors.cursor_fg |= 1u << 31; + term->colors.cursor_bg |= 1u << 31; + } + term_damage_cursor(term); break; diff --git a/terminal.c b/terminal.c index f2d03e77..16663647 100644 --- a/terminal.c +++ b/terminal.c @@ -1298,8 +1298,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, .fg = conf->colors.fg, .bg = conf->colors.bg, .alpha = conf->colors.alpha, - .cursor_fg = conf->cursor.color.text, - .cursor_bg = conf->cursor.color.cursor, + .cursor_fg = (conf->colors.use_custom.cursor ? 1u << 31 : 0) | conf->colors.cursor.text, + .cursor_bg = (conf->colors.use_custom.cursor ? 1u << 31 : 0) | conf->colors.cursor.cursor, .selection_fg = conf->colors.selection_fg, .selection_bg = conf->colors.selection_bg, .use_custom_selection = conf->colors.use_custom.selection, @@ -2153,8 +2153,8 @@ term_reset(struct terminal *term, bool hard) term->colors.fg = term->conf->colors.fg; term->colors.bg = term->conf->colors.bg; term->colors.alpha = term->conf->colors.alpha; - term->colors.cursor_fg = term->conf->cursor.color.text; - term->colors.cursor_bg = term->conf->cursor.color.cursor; + term->colors.cursor_fg = (term->conf->colors.use_custom.cursor ? 1u << 31 : 0) | term->conf->colors.cursor.text; + term->colors.cursor_bg = (term->conf->colors.use_custom.cursor ? 1u << 31 : 0) | term->conf->colors.cursor.cursor; term->colors.selection_fg = term->conf->colors.selection_fg; term->colors.selection_bg = term->conf->colors.selection_bg; term->colors.use_custom_selection = term->conf->colors.use_custom.selection; From 5406ae335530e37f2e6ead3132b15cb5cb71499d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Apr 2025 07:16:37 +0200 Subject: [PATCH 39/86] themes: cursor.color -> colors.cursor --- themes/aeroroot | 4 +--- themes/alacritty | 4 +--- themes/apprentice | 4 +--- themes/ayu-mirage | 4 +--- themes/chiba-dark | 4 +--- themes/derp | 4 +--- themes/deus | 4 +--- themes/dracula | 4 +--- themes/dracula-iterm | 4 +--- themes/electrophoretic | 4 +--- themes/hacktober | 3 +-- themes/jetbrains-darcula | 4 +--- themes/kitty | 4 +--- themes/material-amber | 4 +--- themes/moonfly | 4 +--- themes/night-owl | 4 +--- themes/nightfly | 4 +--- themes/noirblaze | 4 +--- themes/nord | 4 +--- themes/nordiq | 4 +--- themes/nvim-dark | 4 +--- themes/nvim-light | 4 +--- themes/onedark | 4 +--- themes/onehalf-dark | 4 +--- themes/paper-color-dark | 4 +--- themes/paper-color-light | 4 +--- themes/poimandres | 4 +--- themes/rose-pine | 4 +--- themes/rose-pine-dawn | 4 +--- themes/rose-pine-moon | 4 +--- themes/selenized-black | 4 +--- themes/selenized-dark | 4 +--- themes/selenized-light | 4 +--- themes/selenized-white | 4 +--- themes/solarized-dark | 4 +--- themes/solarized-dark-normal-brights | 4 +--- themes/solarized-light | 4 +--- themes/tango | 4 +--- themes/tempus-autumn | 4 +--- themes/tempus-classic | 4 +--- themes/tempus-dawn | 4 +--- themes/tempus-day | 4 +--- themes/tempus-dusk | 4 +--- themes/tempus-fugit | 4 +--- themes/tempus-future | 4 +--- themes/tempus-night | 4 +--- themes/tempus-past | 4 +--- themes/tempus-rift | 4 +--- themes/tempus-spring | 4 +--- themes/tempus-summer | 4 +--- themes/tempus-tempest | 4 +--- themes/tempus-totus | 4 +--- themes/tempus-warp | 4 +--- themes/tempus-winter | 4 +--- themes/visibone | 4 +--- 55 files changed, 55 insertions(+), 164 deletions(-) diff --git a/themes/aeroroot b/themes/aeroroot index 3b887448..2a0e0985 100644 --- a/themes/aeroroot +++ b/themes/aeroroot @@ -1,10 +1,8 @@ # -*- conf -*- # Aero root theme -[cursor] -color=1a1a1a 9fd5f5 - [colors] +cursor=1a1a1a 9fd5f5 foreground=dedeef background=1a1a1a diff --git a/themes/alacritty b/themes/alacritty index a5e4d2c1..14503887 100644 --- a/themes/alacritty +++ b/themes/alacritty @@ -1,10 +1,8 @@ # -*- conf -*- # Alacritty -[cursor] -color = 181818 56d8c9 - [colors] +cursor = 181818 56d8c9 background= 181818 foreground= d8d8d8 diff --git a/themes/apprentice b/themes/apprentice index 941a27b4..6b67d21d 100644 --- a/themes/apprentice +++ b/themes/apprentice @@ -1,10 +1,8 @@ # -*- conf -*- # https://github.com/romainl/Apprentice -[cursor] -color=262626 6c6c6c - [colors] +cursor=262626 6c6c6c foreground=bcbcbc background=262626 regular0=1c1c1c diff --git a/themes/ayu-mirage b/themes/ayu-mirage index 64e85a4e..4646e418 100644 --- a/themes/ayu-mirage +++ b/themes/ayu-mirage @@ -2,10 +2,8 @@ # theme: Ayu Mirage # description: a theme based on Ayu Mirage for Sublime Text (original: https://github.com/dempfi/ayu) -[cursor] -color = ffcc66 665a44 - [colors] +cursor = ffcc66 665a44 foreground = cccac2 background = 242936 diff --git a/themes/chiba-dark b/themes/chiba-dark index bc3b1420..8727f684 100644 --- a/themes/chiba-dark +++ b/themes/chiba-dark @@ -3,10 +3,8 @@ # author: ayushnix (https://sr.ht/~ayushnix) # description: A dark theme with bright cyberpunk colors (WCAG AAA compliant) -[cursor] -color = 181818 cdcdcd - [colors] +cursor = 181818 cdcdcd foreground = cdcdcd background = 181818 regular0 = 181818 diff --git a/themes/derp b/themes/derp index 0925d2c2..45eed752 100644 --- a/themes/derp +++ b/themes/derp @@ -1,10 +1,8 @@ # -*- conf -*- # Derp -[cursor] -color=000000 ffffff - [colors] +cursor=000000 ffffff foreground=ffffff background=000000 regular0=111111 diff --git a/themes/deus b/themes/deus index 8fb37f75..0d52e55b 100644 --- a/themes/deus +++ b/themes/deus @@ -2,10 +2,8 @@ # Deus # Color palette based on: https://github.com/ajmwagar/vim-deus -[cursor] -color=2c323b eaeaea - [colors] +cursor=2c323b eaeaea background=2c323b foreground=eaeaea regular0=242a32 diff --git a/themes/dracula b/themes/dracula index 8b6ab542..008fc150 100644 --- a/themes/dracula +++ b/themes/dracula @@ -1,10 +1,8 @@ # -*- conf -*- # Dracula -[cursor] -color=282a36 f8f8f2 - [colors] +cursor=282a36 f8f8f2 foreground=f8f8f2 background=282a36 regular0=000000 # black diff --git a/themes/dracula-iterm b/themes/dracula-iterm index 8c2f66c3..249bb6ab 100644 --- a/themes/dracula-iterm +++ b/themes/dracula-iterm @@ -1,10 +1,8 @@ # -*- conf -*- # Dracula iTerm2 variant -[cursor] -color=ffffff bbbbbb - [colors] +cursor=ffffff bbbbbb foreground=f8f8f2 background=1e1f29 regular0=000000 # black diff --git a/themes/electrophoretic b/themes/electrophoretic index d2b67434..e0bf6e79 100644 --- a/themes/electrophoretic +++ b/themes/electrophoretic @@ -5,10 +5,8 @@ # text and the white background. # author: Eugen Rahaian -[cursor] -color=ffffff 515151 - [colors] +cursor=ffffff 515151 background= ffffff foreground= 000000 diff --git a/themes/hacktober b/themes/hacktober index acb6c0b1..dfcc4c7e 100644 --- a/themes/hacktober +++ b/themes/hacktober @@ -1,8 +1,7 @@ # -*- conf -*- -[cursor] -color=141414 c9c9c9 [colors] +cursor=141414 c9c9c9 foreground=c9c9c9 background=141414 regular0=191918 # black diff --git a/themes/jetbrains-darcula b/themes/jetbrains-darcula index 82528498..e6997848 100644 --- a/themes/jetbrains-darcula +++ b/themes/jetbrains-darcula @@ -2,10 +2,8 @@ # JetBrains Darcula # Palette based on the same theme from https://github.com/dexpota/kitty-themes -[cursor] -color=202020 ffffff - [colors] +cursor=202020 ffffff background=202020 foreground=adadad regular0=000000 # black diff --git a/themes/kitty b/themes/kitty index b5b813cc..f43eea9d 100644 --- a/themes/kitty +++ b/themes/kitty @@ -1,9 +1,7 @@ # -*- conf -*- -[cursor] -color=111111 cccccc - [colors] +cursor=111111 cccccc foreground=dddddd background=000000 regular0=000000 # black diff --git a/themes/material-amber b/themes/material-amber index ad844a9a..27983833 100644 --- a/themes/material-amber +++ b/themes/material-amber @@ -2,10 +2,8 @@ # Material Amber # Based on material.io guidelines with Amber 50 background -[cursor] -color=fff8e1 21201d - [colors] +cursor=fff8e1 21201d foreground = 21201d background = fff8e1 diff --git a/themes/moonfly b/themes/moonfly index 870de9d0..0dbe0e95 100644 --- a/themes/moonfly +++ b/themes/moonfly @@ -2,10 +2,8 @@ # moonfly # Based on https://github.com/bluz71/vim-moonfly-colors -[cursor] -color = 080808 9e9e9e - [colors] +cursor = 080808 9e9e9e foreground = b2b2b2 background = 080808 diff --git a/themes/night-owl b/themes/night-owl index 03e1d8f7..43a5c054 100644 --- a/themes/night-owl +++ b/themes/night-owl @@ -1,10 +1,8 @@ # _*_ conf _*_ # Night Owl -[cursor] -color=011627 80a4c2 - [colors] +cursor=011627 80a4c2 foreground=d6deeb background=011627 diff --git a/themes/nightfly b/themes/nightfly index 2a27fb2d..37205f0f 100644 --- a/themes/nightfly +++ b/themes/nightfly @@ -2,10 +2,8 @@ # nightfly # Based on https://github.com/bluz71/vim-nightfly-guicolors -[cursor] -color = 080808 9ca1aa - [colors] +cursor = 080808 9ca1aa foreground = acb4c2 background = 011627 diff --git a/themes/noirblaze b/themes/noirblaze index 3cf452e6..42daf11b 100644 --- a/themes/noirblaze +++ b/themes/noirblaze @@ -3,10 +3,8 @@ # https://github.com/n1ghtmare/noirblaze-kitty -[cursor] -color=121212 ff0088 - [colors] +cursor=121212 ff0088 foreground=d5d5d5 background=121212 diff --git a/themes/nord b/themes/nord index 4ce3a53e..9b988ad6 100644 --- a/themes/nord +++ b/themes/nord @@ -6,10 +6,8 @@ # this specific foot theme is based on nord-alacritty: # https://github.com/arcticicestudio/nord-alacritty/blob/develop/src/nord.yml -[cursor] -color = 2e3440 d8dee9 - [colors] +cursor = 2e3440 d8dee9 foreground = d8dee9 background = 2e3440 diff --git a/themes/nordiq b/themes/nordiq index f309de23..0df5c7de 100644 --- a/themes/nordiq +++ b/themes/nordiq @@ -1,10 +1,8 @@ # -*- conf -*- # Nordiq -[cursor] -color=eeeeee 9f515a - [colors] +cursor=eeeeee 9f515a foreground=dbdee9 background=0e1420 regular0=5b6272 diff --git a/themes/nvim-dark b/themes/nvim-dark index 4c13770a..9a177770 100644 --- a/themes/nvim-dark +++ b/themes/nvim-dark @@ -3,10 +3,8 @@ # Uses the dark color palette from the default Neovim color scheme # See: https://github.com/neovim/neovim/blob/fb6c059dc55c8d594102937be4dd70f5ff51614a/src/nvim/highlight_group.c#L419 -[cursor] -color=14161b e0e2ea # NvimDarkGrey2 NvimLightGrey2 - [colors] +cursor=14161b e0e2ea # NvimDarkGrey2 NvimLightGrey2 foreground=e0e2ea # NvimLightGrey2 background=14161b # NvimDarkGrey2 diff --git a/themes/nvim-light b/themes/nvim-light index 5afec9d7..aca4e156 100644 --- a/themes/nvim-light +++ b/themes/nvim-light @@ -3,10 +3,8 @@ # Uses the light color palette from the default Neovim color scheme # See: https://github.com/neovim/neovim/blob/fb6c059dc55c8d594102937be4dd70f5ff51614a/src/nvim/highlight_group.c#L334 -[cursor] -color=e0e2ea 14161b # NvimLightGrey2 NvimDarkGrey2 - [colors] +cursor=e0e2ea 14161b # NvimLightGrey2 NvimDarkGrey2 foreground=14161b # NvimDarkGrey2 background=e0e2ea # NvimLightGrey2 diff --git a/themes/onedark b/themes/onedark index ac5cc834..0932960b 100644 --- a/themes/onedark +++ b/themes/onedark @@ -1,10 +1,8 @@ # OneDark # Palette based on the same theme from https://github.com/dexpota/kitty-themes -[cursor] -color=111111 cccccc - [colors] +cursor=111111 cccccc foreground=979eab background=282c34 regular0=282c34 # black diff --git a/themes/onehalf-dark b/themes/onehalf-dark index c37a7984..1adc9e23 100644 --- a/themes/onehalf-dark +++ b/themes/onehalf-dark @@ -7,10 +7,8 @@ # + cursor colors from: # https://github.com/sonph/onehalf/blob/master/iterm/OneHalfDark.itermcolors -[cursor] -color=dcdfe4 a3b3cc - [colors] +cursor=dcdfe4 a3b3cc foreground=dcdfe4 background=282c34 regular0=282c34 # black diff --git a/themes/paper-color-dark b/themes/paper-color-dark index 18cd7f17..991bcc9d 100644 --- a/themes/paper-color-dark +++ b/themes/paper-color-dark @@ -2,10 +2,8 @@ # PaperColorDark # Palette based on https://github.com/NLKNguyen/papercolor-theme -[cursor] -color=1c1c1c eeeeee - [colors] +cursor=1c1c1c eeeeee background=1c1c1c foreground=eeeeee regular0=1c1c1c # black diff --git a/themes/paper-color-light b/themes/paper-color-light index b08ea707..b8a6ceec 100644 --- a/themes/paper-color-light +++ b/themes/paper-color-light @@ -2,10 +2,8 @@ # PaperColor Light # Palette based on https://github.com/NLKNguyen/papercolor-theme -[cursor] -color=eeeeee 444444 - [colors] +cursor=eeeeee 444444 background=eeeeee foreground=444444 regular0=eeeeee # black diff --git a/themes/poimandres b/themes/poimandres index d8a6b0a7..b4edc175 100644 --- a/themes/poimandres +++ b/themes/poimandres @@ -1,10 +1,8 @@ # Based on Poimandres color theme for kitti terminal emulator # https://github.com/ubmit/poimandres-kitty -[cursor] -color=1b1e28 ffffff - [colors] +cursor=1b1e28 ffffff foreground=a6accd background=1b1e28 diff --git a/themes/rose-pine b/themes/rose-pine index 78d77dd9..2cae00e8 100644 --- a/themes/rose-pine +++ b/themes/rose-pine @@ -1,10 +1,8 @@ # -*- conf -*- # Rosé Pine -[cursor] -color=191724 e0def4 - [colors] +cursor=191724 e0def4 background=191724 foreground=e0def4 diff --git a/themes/rose-pine-dawn b/themes/rose-pine-dawn index 52008b44..674c7a21 100644 --- a/themes/rose-pine-dawn +++ b/themes/rose-pine-dawn @@ -1,10 +1,8 @@ # -*- conf -*- # Rosé Pine Dawn -[cursor] -color=faf4ed 575279 - [colors] +cursor=faf4ed 575279 background=faf4ed foreground=575279 diff --git a/themes/rose-pine-moon b/themes/rose-pine-moon index 732e5943..cbc81451 100644 --- a/themes/rose-pine-moon +++ b/themes/rose-pine-moon @@ -1,10 +1,8 @@ # -*- conf -*- # Rosé Pine Moon -[cursor] -color=232136 e0def4 - [colors] +cursor=232136 e0def4 background=232136 foreground=e0def4 diff --git a/themes/selenized-black b/themes/selenized-black index 28392add..591751f0 100644 --- a/themes/selenized-black +++ b/themes/selenized-black @@ -1,10 +1,8 @@ # -*- conf -*- # Selenized black -[cursor] -color = 181818 56d8c9 - [colors] +cursor = 181818 56d8c9 background= 181818 foreground= b9b9b9 diff --git a/themes/selenized-dark b/themes/selenized-dark index ed74cdfc..5d062dec 100644 --- a/themes/selenized-dark +++ b/themes/selenized-dark @@ -1,10 +1,8 @@ # -*- conf -*- # Selenized dark -[cursor] -color = 103c48 53d6c7 - [colors] +cursor = 103c48 53d6c7 background= 103c48 foreground= adbcbc diff --git a/themes/selenized-light b/themes/selenized-light index 7e599d8e..04dffbea 100644 --- a/themes/selenized-light +++ b/themes/selenized-light @@ -1,10 +1,8 @@ # -*- conf -*- # Selenized light -[cursor] -color=fbf3db 00978a - [colors] +cursor=fbf3db 00978a background= fbf3db foreground= 53676d diff --git a/themes/selenized-white b/themes/selenized-white index b4d25315..5a7d68b2 100644 --- a/themes/selenized-white +++ b/themes/selenized-white @@ -1,10 +1,8 @@ # -*- conf -*- # Selenized white -[cursor] -color=ffffff 009a8a - [colors] +cursor=ffffff 009a8a background= ffffff foreground= 474747 diff --git a/themes/solarized-dark b/themes/solarized-dark index cad2945e..4997eb4a 100644 --- a/themes/solarized-dark +++ b/themes/solarized-dark @@ -1,10 +1,8 @@ # -*- conf -*- # Solarized dark -[cursor] -color= 002b36 93a1a1 - [colors] +cursor= 002b36 93a1a1 background= 002b36 foreground= 839496 regular0= 073642 diff --git a/themes/solarized-dark-normal-brights b/themes/solarized-dark-normal-brights index 1ab7d375..f0c2172d 100644 --- a/themes/solarized-dark-normal-brights +++ b/themes/solarized-dark-normal-brights @@ -1,10 +1,8 @@ # -*- conf -*- # Solarized dark -[cursor] -color= 002b36 93a1a1 - [colors] +cursor= 002b36 93a1a1 background= 002b36 foreground= 839496 regular0= 073642 diff --git a/themes/solarized-light b/themes/solarized-light index 74474573..3d750277 100644 --- a/themes/solarized-light +++ b/themes/solarized-light @@ -1,10 +1,8 @@ # -*- conf -*- # Solarized light -[cursor] -color=fdf6e3 586e75 - [colors] +cursor= fdf6e3 586e75 background= fdf6e3 foreground= 657b83 regular0= eee8d5 diff --git a/themes/tango b/themes/tango index a326f8ad..a93d29cb 100644 --- a/themes/tango +++ b/themes/tango @@ -1,10 +1,8 @@ # -*- conf -*- # Tango -[cursor] -color=000000 babdb6 - [colors] +cursor=000000 babdb6 foreground=babdb6 background=000000 regular0=2e3436 diff --git a/themes/tempus-autumn b/themes/tempus-autumn index 9c1f8797..74228e90 100644 --- a/themes/tempus-autumn +++ b/themes/tempus-autumn @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Dark theme with a palette inspired by earthly colours (WCAG AA compliant) -#[cursor] -#color = 302420 a9a2a6 - [colors] +#cursor = 302420 a9a2a6 foreground = a9a2a6 background = 302420 regular0 = 302420 diff --git a/themes/tempus-classic b/themes/tempus-classic index 0164605b..b35dc5e5 100644 --- a/themes/tempus-classic +++ b/themes/tempus-classic @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Dark theme with warm hues (WCAG AA compliant) -#[cursor] -#color = 232323 aeadaf - [colors] +#cursor = 232323 aeadaf foreground = aeadaf background = 232323 regular0 = 232323 diff --git a/themes/tempus-dawn b/themes/tempus-dawn index cf143fba..dc45f29d 100644 --- a/themes/tempus-dawn +++ b/themes/tempus-dawn @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Light theme with a soft, slightly desaturated palette (WCAG AA compliant) -#[cursor] -#color = eff0f2 4a4b4e - [colors] +#cursor = eff0f2 4a4b4e foreground = 4a4b4e background = eff0f2 regular0 = 4a4b4e diff --git a/themes/tempus-day b/themes/tempus-day index b287d45c..1df70137 100644 --- a/themes/tempus-day +++ b/themes/tempus-day @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Light theme with warm colours (WCAG AA compliant) -#[cursor] -#color = f8f2e5 464340 - [colors] +#cursor = f8f2e5 464340 foreground = 464340 background = f8f2e5 regular0 = 464340 diff --git a/themes/tempus-dusk b/themes/tempus-dusk index 2c0308e1..5b4d1bea 100644 --- a/themes/tempus-dusk +++ b/themes/tempus-dusk @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Dark theme with a deep blue-ish, slightly desaturated palette (WCAG AA compliant) -#[cursor] -#color = 1f252d a2a8ba - [colors] +#cursor = 1f252d a2a8ba foreground = a2a8ba background = 1f252d regular0 = 1f252d diff --git a/themes/tempus-fugit b/themes/tempus-fugit index 9ebbcee7..ebd082fe 100644 --- a/themes/tempus-fugit +++ b/themes/tempus-fugit @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Light, pleasant theme optimised for long writing/coding sessions (WCAG AA compliant) -#[cursor] -#color = fff5f3 4d595f - [colors] +#cursor = fff5f3 4d595f foreground = 4d595f background = fff5f3 regular0 = 4d595f diff --git a/themes/tempus-future b/themes/tempus-future index 3dd8c7a6..c97d379d 100644 --- a/themes/tempus-future +++ b/themes/tempus-future @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Dark theme with colours inspired by concept art of outer space (WCAG AAA compliant) -#[cursor] -#color = 090a18 b4abac - [colors] +#cursor = 090a18 b4abac foreground = b4abac background = 090a18 regular0 = 090a18 diff --git a/themes/tempus-night b/themes/tempus-night index de7be5ff..7c97681d 100644 --- a/themes/tempus-night +++ b/themes/tempus-night @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: High contrast dark theme with bright colours (WCAG AAA compliant) -#[cursor] -#color = 1a1a1a e0e0e0 - [colors] +#cursor = 1a1a1a e0e0e0 foreground = e0e0e0 background = 1a1a1a regular0 = 1a1a1a diff --git a/themes/tempus-past b/themes/tempus-past index 8c66f54d..af408b00 100644 --- a/themes/tempus-past +++ b/themes/tempus-past @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Light theme inspired by old vaporwave concept art (WCAG AA compliant) -#[cursor] -#color = f3f2f4 53545b - [colors] +#cursor = f3f2f4 53545b foreground = 53545b background = f3f2f4 regular0 = 53545b diff --git a/themes/tempus-rift b/themes/tempus-rift index 3657a7fe..e0cea4da 100644 --- a/themes/tempus-rift +++ b/themes/tempus-rift @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Dark theme with a subdued palette on the green side of the spectrum (WCAG AA compliant) -#[cursor] -#color = 162c22 bbbcbc - [colors] +#cursor = 162c22 bbbcbc foreground = bbbcbc background = 162c22 regular0 = 162c22 diff --git a/themes/tempus-spring b/themes/tempus-spring index d50e6d06..b98be3b4 100644 --- a/themes/tempus-spring +++ b/themes/tempus-spring @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Dark theme with a palette inspired by early spring colours (WCAG AA compliant) -#[cursor] -#color = 283a37 b5b8b7 - [colors] +#cursor = 283a37 b5b8b7 foreground = b5b8b7 background = 283a37 regular0 = 283a37 diff --git a/themes/tempus-summer b/themes/tempus-summer index 7da1d8c4..cd904010 100644 --- a/themes/tempus-summer +++ b/themes/tempus-summer @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Dark theme with colours inspired by summer evenings by the sea (WCAG AA compliant) -#[cursor] -#color = 202c3d a0abae - [colors] +#cursor = 202c3d a0abae foreground = a0abae background = 202c3d regular0 = 202c3d diff --git a/themes/tempus-tempest b/themes/tempus-tempest index 57c300aa..2c84454e 100644 --- a/themes/tempus-tempest +++ b/themes/tempus-tempest @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: A green-scale, subtle theme for late night hackers (WCAG AAA compliant) -#[cursor] -#color = 282b2b b6e0ca - [colors] +#cursor = 282b2b b6e0ca foreground = b6e0ca background = 282b2b regular0 = 282b2b diff --git a/themes/tempus-totus b/themes/tempus-totus index 01e84692..3eb21644 100644 --- a/themes/tempus-totus +++ b/themes/tempus-totus @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Light theme for prose or for coding in an open space (WCAG AAA compliant) -#[cursor] -#color = ffffff 4a484d - [colors] +#cursor = ffffff 4a484d foreground = 4a484d background = ffffff regular0 = 4a484d diff --git a/themes/tempus-warp b/themes/tempus-warp index fa8c21c2..911fb266 100644 --- a/themes/tempus-warp +++ b/themes/tempus-warp @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Dark theme with a vibrant palette (WCAG AA compliant) -#[cursor] -#color = 001514 a29fa0 - [colors] +#cursor = 001514 a29fa0 foreground = a29fa0 background = 001514 regular0 = 001514 diff --git a/themes/tempus-winter b/themes/tempus-winter index 8db97057..e4307142 100644 --- a/themes/tempus-winter +++ b/themes/tempus-winter @@ -3,10 +3,8 @@ # author: Protesilaos Stavrou (https://protesilaos.com) # description: Dark theme with a palette inspired by winter nights at the city (WCAG AA compliant) -#[cursor] -#color = 202427 8da3b8 - [colors] +#cursor = 202427 8da3b8 foreground = 8da3b8 background = 202427 regular0 = 202427 diff --git a/themes/visibone b/themes/visibone index 3ee665d0..9979bee0 100644 --- a/themes/visibone +++ b/themes/visibone @@ -1,10 +1,8 @@ # -*- conf -*- # VisiBone -[cursor] -color=010101 ffffff - [colors] +cursor=010101 ffffff foreground=ffffff background=010101 regular0=666666 From b24a9a59b9db95fe9a5195a30d982d9ed2150a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Apr 2025 07:29:54 +0200 Subject: [PATCH 40/86] tests: config: colors: verify loaded color is correct --- tests/test-config.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test-config.c b/tests/test-config.c index 69d349b4..7dfb8556 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -399,6 +399,16 @@ test_color(struct context *ctx, bool (*parse_fun)(struct context *ctx), BUG("[%s].%s=%s: failed to parse", ctx->section, ctx->key, ctx->value); } + + uint32_t color = input[i].color; + if (alpha_allowed && strlen(input[i].option_string) == 6) + color |= 0xff000000; + + if (*ptr != color) { + BUG("[%s].%s=%s: expected 0x%08x, got 0x%08x", + ctx->section, ctx->key, ctx->value, + color, *ptr); + } } } } @@ -445,6 +455,18 @@ test_two_colors(struct context *ctx, bool (*parse_fun)(struct context *ctx), BUG("[%s].%s=%s: failed to parse", ctx->section, ctx->key, ctx->value); } + + if (*ptr1 != input[i].color1) { + BUG("[%s].%s=%s: expected 0x%08x, got 0x%08x", + ctx->section, ctx->key, ctx->value, + input[i].color1, *ptr1); + } + + if (*ptr2 != input[i].color2) { + BUG("[%s].%s=%s: expected 0x%08x, got 0x%08x", + ctx->section, ctx->key, ctx->value, + input[i].color2, *ptr2); + } } } } @@ -720,6 +742,10 @@ test_section_colors(void) &conf.colors.search_box.match.fg, &conf.colors.search_box.match.bg); + test_two_colors(&ctx, &parse_section_colors, "cursor", false, + &conf.colors.cursor.text, + &conf.colors.cursor.cursor); + test_enum(&ctx, &parse_section_colors, "alpha-mode", 3, (const char *[]){"default", "matching", "all"}, (int []){ALPHA_MODE_DEFAULT, ALPHA_MODE_MATCHING, ALPHA_MODE_ALL}, From 01c43f1644a691a01266cc4ec013dafae9eaf984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Apr 2025 07:32:48 +0200 Subject: [PATCH 41/86] config: refactor: break out color theme parsing to a separate function --- config.c | 79 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 41 insertions(+), 38 deletions(-) diff --git a/config.c b/config.c index 4337d001..ce88d6ce 100644 --- a/config.c +++ b/config.c @@ -1339,9 +1339,8 @@ parse_section_regex(struct context *ctx) } static bool -parse_section_colors(struct context *ctx) +parse_color_theme(struct context *ctx, struct color_theme *theme) { - struct config *conf = ctx->conf; const char *key = ctx->key; size_t key_len = strlen(key); @@ -1350,28 +1349,26 @@ parse_section_colors(struct context *ctx) if (isdigit(key[0])) { unsigned long index; - if (!str_to_ulong(key, 0, &index) || - index >= ALEN(conf->colors.table)) - { + if (!str_to_ulong(key, 0, &index) || index >= ALEN(theme->table)) { LOG_CONTEXTUAL_ERR( "invalid color palette index: %s (not in range 0-%zu)", - key, ALEN(conf->colors.table)); + key, ALEN(theme->table)); return false; } - color = &conf->colors.table[index]; + color = &theme->table[index]; } else if (key_len == 8 && str_has_prefix(key, "regular") && last_digit < 8) - color = &conf->colors.table[last_digit]; + color = &theme->table[last_digit]; else if (key_len == 7 && str_has_prefix(key, "bright") && last_digit < 8) - color = &conf->colors.table[8 + last_digit]; + color = &theme->table[8 + last_digit]; else if (key_len == 4 && str_has_prefix(key, "dim") && last_digit < 8) { - if (!value_to_color(ctx, &conf->colors.dim[last_digit], false)) + if (!value_to_color(ctx, &theme->dim[last_digit], false)) return false; - conf->colors.use_custom.dim |= 1 << last_digit; + theme->use_custom.dim |= 1 << last_digit; return true; } @@ -1380,90 +1377,90 @@ parse_section_colors(struct context *ctx) (key_len == 7 && key[5] == '1' && last_digit < 6))) { size_t idx = key_len == 6 ? last_digit : 10 + last_digit; - return value_to_color(ctx, &conf->colors.sixel[idx], false); + return value_to_color(ctx, &theme->sixel[idx], false); } - else if (streq(key, "flash")) color = &conf->colors.flash; - else if (streq(key, "foreground")) color = &conf->colors.fg; - else if (streq(key, "background")) color = &conf->colors.bg; - else if (streq(key, "selection-foreground")) color = &conf->colors.selection_fg; - else if (streq(key, "selection-background")) color = &conf->colors.selection_bg; + 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, "selection-foreground")) color = &theme->selection_fg; + else if (streq(key, "selection-background")) color = &theme->selection_bg; else if (streq(key, "jump-labels")) { if (!value_to_two_colors( ctx, - &conf->colors.jump_label.fg, - &conf->colors.jump_label.bg, + &theme->jump_label.fg, + &theme->jump_label.bg, false)) { return false; } - conf->colors.use_custom.jump_label = true; + theme->use_custom.jump_label = true; return true; } else if (streq(key, "scrollback-indicator")) { if (!value_to_two_colors( ctx, - &conf->colors.scrollback_indicator.fg, - &conf->colors.scrollback_indicator.bg, + &theme->scrollback_indicator.fg, + &theme->scrollback_indicator.bg, false)) { return false; } - conf->colors.use_custom.scrollback_indicator = true; + theme->use_custom.scrollback_indicator = true; return true; } else if (streq(key, "search-box-no-match")) { if (!value_to_two_colors( ctx, - &conf->colors.search_box.no_match.fg, - &conf->colors.search_box.no_match.bg, + &theme->search_box.no_match.fg, + &theme->search_box.no_match.bg, false)) { return false; } - conf->colors.use_custom.search_box_no_match = true; + theme->use_custom.search_box_no_match = true; return true; } else if (streq(key, "search-box-match")) { if (!value_to_two_colors( ctx, - &conf->colors.search_box.match.fg, - &conf->colors.search_box.match.bg, + &theme->search_box.match.fg, + &theme->search_box.match.bg, false)) { return false; } - conf->colors.use_custom.search_box_match = true; + theme->use_custom.search_box_match = true; return true; } else if (streq(key, "cursor")) { if (!value_to_two_colors( ctx, - &conf->colors.cursor.text, - &conf->colors.cursor.cursor, + &theme->cursor.text, + &theme->cursor.cursor, false)) { return false; } - conf->colors.use_custom.cursor = true; + theme->use_custom.cursor = true; return true; } else if (streq(key, "urls")) { - if (!value_to_color(ctx, &conf->colors.url, false)) + if (!value_to_color(ctx, &theme->url, false)) return false; - conf->colors.use_custom.url = true; + theme->use_custom.url = true; return true; } @@ -1477,7 +1474,7 @@ parse_section_colors(struct context *ctx) return false; } - conf->colors.alpha = alpha * 65535.; + theme->alpha = alpha * 65535.; return true; } @@ -1491,18 +1488,18 @@ parse_section_colors(struct context *ctx) return false; } - conf->colors.flash_alpha = alpha * 65535.; + theme->flash_alpha = alpha * 65535.; return true; } else if (strcmp(key, "alpha-mode") == 0) { - _Static_assert(sizeof(conf->colors.alpha_mode) == sizeof(int), + _Static_assert(sizeof(theme->alpha_mode) == sizeof(int), "enum is not 32-bit"); return value_to_enum( ctx, (const char *[]){"default", "matching", "all", NULL}, - (int *)&conf->colors.alpha_mode); + (int *)&theme->alpha_mode); } else { @@ -1518,6 +1515,12 @@ parse_section_colors(struct context *ctx) return true; } +static bool +parse_section_colors(struct context *ctx) +{ + return parse_color_theme(ctx, &ctx->conf->colors); +} + static bool parse_section_cursor(struct context *ctx) { From 1423babc35e2896b602d124b634816ebfaeae243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Apr 2025 07:36:58 +0200 Subject: [PATCH 42/86] config: add new section 'colors2' This section defines an alternative color theme. The keys are the same as in the 'colors' section, as are the default values. Values are *not* inherited from 'colors'. That is, if you set a value in 'colors', but not in 'colors2', it is *not* inherited by 'colors2'. --- config.c | 8 ++++++++ config.h | 1 + doc/foot.ini.5.scd | 9 +++++++++ foot.ini | 3 +++ 4 files changed, 21 insertions(+) diff --git a/config.c b/config.c index ce88d6ce..1b73fbcf 100644 --- a/config.c +++ b/config.c @@ -1521,6 +1521,12 @@ parse_section_colors(struct context *ctx) return parse_color_theme(ctx, &ctx->conf->colors); } +static bool +parse_section_colors2(struct context *ctx) +{ + return parse_color_theme(ctx, &ctx->conf->colors2); +} + static bool parse_section_cursor(struct context *ctx) { @@ -2900,6 +2906,7 @@ enum section { SECTION_URL, SECTION_REGEX, SECTION_COLORS, + SECTION_COLORS2, SECTION_CURSOR, SECTION_MOUSE, SECTION_CSD, @@ -2930,6 +2937,7 @@ static const struct { [SECTION_URL] = {&parse_section_url, "url"}, [SECTION_REGEX] = {&parse_section_regex, "regex", true}, [SECTION_COLORS] = {&parse_section_colors, "colors"}, + [SECTION_COLORS2] = {&parse_section_colors2, "colors2"}, [SECTION_CURSOR] = {&parse_section_cursor, "cursor"}, [SECTION_MOUSE] = {&parse_section_mouse, "mouse"}, [SECTION_CSD] = {&parse_section_csd, "csd"}, diff --git a/config.h b/config.h index 89740db3..8954b270 100644 --- a/config.h +++ b/config.h @@ -304,6 +304,7 @@ struct config { tll(struct custom_regex) custom_regexes; struct color_theme colors; + struct color_theme colors2; struct { enum cursor_style style; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 13f768c2..45084ab6 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -1084,6 +1084,15 @@ can configure the background transparency with the _alpha_ option. Flash translucency. A value in the range 0.0-1.0, where 0.0 means completely transparent, and 1.0 is opaque. Default: _0.5_. +# SECTION: colors2 + +This section defines an alternative color theme. It has the exact same +keys as the *colors* section. The default values are the same. + +Note that values are not inherited. That is, if you set a value in +*colors*, but not in *colors2*, the value from *colors* is not +inherited by *colors2*. + # SECTION: csd This section controls the look of the _CSDs_ (Client Side diff --git a/foot.ini b/foot.ini index dc2ad6cd..8482af6b 100644 --- a/foot.ini +++ b/foot.ini @@ -164,6 +164,9 @@ # search-box-match= # black-on-yellow # urls= +[colors2] +# Alternative color theme, see man page foot.ini(5) + [csd] # preferred=server # size=26 From 6bc91b5e288ef16613a4e6f9d0a9f6077cedc267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Apr 2025 07:58:02 +0200 Subject: [PATCH 43/86] key-bindings: add bindings to switch between color themes * color-theme-switch-1: select the primary color theme * color-theme-switch-2: select the alternative color theme * color-theme-toggle: toggle between the primary and alternative color themes --- CHANGELOG.md | 10 ++++++++++ config.c | 4 ++++ doc/foot.ini.5.scd | 16 ++++++++++++++++ foot.ini | 3 +++ input.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ key-binding.h | 5 ++++- terminal.c | 27 +++++++++++++++++---------- terminal.h | 3 +++ 8 files changed, 102 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b8348ed..4296da09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,16 @@ ## Unreleased ### Added + +* `colors2` config section. This section duplicates the `colors` + section, and lets you define an alternative color theme. +* `key-bindings.color-theme-switch-1`, + `key-bindings.color-theme-switch-2` and + `key-bindings.color-theme-toggle` key bindings. These can be used to + switch between the primary and alternative color themes. They are + not bound by default. + + ### Changed * `cursor.color` moved to `colors.cursor`. diff --git a/config.c b/config.c index 1b73fbcf..921b2d68 100644 --- a/config.c +++ b/config.c @@ -142,6 +142,9 @@ static const char *const binding_action_map[] = { [BIND_ACTION_QUIT] = "quit", [BIND_ACTION_REGEX_LAUNCH] = "regex-launch", [BIND_ACTION_REGEX_COPY] = "regex-copy", + [BIND_ACTION_THEME_SWITCH_1] = "color-theme-switch-1", + [BIND_ACTION_THEME_SWITCH_2] = "color-theme-switch-2", + [BIND_ACTION_THEME_TOGGLE] = "color-theme-toggle", /* Mouse-specific actions */ [BIND_ACTION_SCROLLBACK_UP_MOUSE] = "scrollback-up-mouse", @@ -3479,6 +3482,7 @@ config_load(struct config *conf, const char *conf_path, memcpy(conf->colors.table, default_color_table, sizeof(default_color_table)); memcpy(conf->colors.sixel, default_sixel_colors, sizeof(default_sixel_colors)); + memcpy(&conf->colors2, &conf->colors, sizeof(conf->colors)); parse_modifiers(XKB_MOD_NAME_SHIFT, 5, &conf->mouse.selection_override_modifiers); tokenize_cmdline( diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 45084ab6..af6f7875 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -1396,6 +1396,22 @@ e.g. *search-start=none*. Default: _Control+Shift+u_. +*color-theme-switch-1*, *color-theme-switch-2*, *color-theme-toggle* + Switch between the primary color theme (defined in the *colors* + section), and the alternative color theme (defined in the + *colors2* section). + + *color-theme-switch-1* applies the primary color theme regardless + of which color theme is currently active. + + *color-theme-switch-2* applies the alternative color theme regardless + of which color theme is currently active. + + *color-theme-toggle* toggles between the primary and alternative + color themes. + + Default: _none_ + *quit* Quit foot. Default: _none_. diff --git a/foot.ini b/foot.ini index 8482af6b..ebbc8ca7 100644 --- a/foot.ini +++ b/foot.ini @@ -212,6 +212,9 @@ # prompt-prev=Control+Shift+z # prompt-next=Control+Shift+x # unicode-input=Control+Shift+u +# color-theme-switch-1=none +# color-theme-switch-2=none +# color-theme-toggle=none # noop=none # quit=none diff --git a/input.c b/input.c index d7a7975a..ebb646c6 100644 --- a/input.c +++ b/input.c @@ -484,6 +484,51 @@ execute_binding(struct seat *seat, struct terminal *term, return true; + case BIND_ACTION_THEME_SWITCH_1: + if (term->colors.active_theme != COLOR_THEME1) { + term_theme_apply(term, &term->conf->colors); + term->colors.active_theme = COLOR_THEME1; + + wayl_win_alpha_changed(term->window); + term_font_subpixel_changed(term); + + term_damage_view(term); + term_damage_margins(term); + render_refresh(term); + } + return true; + + case BIND_ACTION_THEME_SWITCH_2: + if (term->colors.active_theme != COLOR_THEME2) { + term_theme_apply(term, &term->conf->colors2); + term->colors.active_theme = COLOR_THEME2; + + wayl_win_alpha_changed(term->window); + term_font_subpixel_changed(term); + + term_damage_view(term); + term_damage_margins(term); + render_refresh(term); + } + return true; + + case BIND_ACTION_THEME_TOGGLE: + if (term->colors.active_theme == COLOR_THEME1) { + term_theme_apply(term, &term->conf->colors2); + term->colors.active_theme = COLOR_THEME2; + } else { + term_theme_apply(term, &term->conf->colors); + term->colors.active_theme = COLOR_THEME1; + } + + wayl_win_alpha_changed(term->window); + term_font_subpixel_changed(term); + + term_damage_view(term); + term_damage_margins(term); + render_refresh(term); + return true; + case BIND_ACTION_SELECT_BEGIN: selection_start( term, seat->mouse.col, seat->mouse.row, SELECTION_CHAR_WISE, false); diff --git a/key-binding.h b/key-binding.h index 89398859..5f0c1f1e 100644 --- a/key-binding.h +++ b/key-binding.h @@ -43,6 +43,9 @@ enum bind_action_normal { BIND_ACTION_QUIT, BIND_ACTION_REGEX_LAUNCH, BIND_ACTION_REGEX_COPY, + BIND_ACTION_THEME_SWITCH_1, + BIND_ACTION_THEME_SWITCH_2, + BIND_ACTION_THEME_TOGGLE, /* Mouse specific actions - i.e. they require a mouse coordinate */ BIND_ACTION_SCROLLBACK_UP_MOUSE, @@ -56,7 +59,7 @@ enum bind_action_normal { BIND_ACTION_SELECT_QUOTE, BIND_ACTION_SELECT_ROW, - BIND_ACTION_KEY_COUNT = BIND_ACTION_REGEX_COPY + 1, + BIND_ACTION_KEY_COUNT = BIND_ACTION_THEME_TOGGLE + 1, BIND_ACTION_COUNT = BIND_ACTION_SELECT_ROW + 1, }; diff --git a/terminal.c b/terminal.c index 16663647..29e03b8e 100644 --- a/terminal.c +++ b/terminal.c @@ -1303,6 +1303,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, .selection_fg = conf->colors.selection_fg, .selection_bg = conf->colors.selection_bg, .use_custom_selection = conf->colors.use_custom.selection, + .active_theme = COLOR_THEME1, }, .color_stack = { .stack = NULL, @@ -2150,16 +2151,8 @@ term_reset(struct terminal *term, bool hard) term->flash.active = false; term->blink.state = BLINK_ON; fdm_del(term->fdm, term->blink.fd); term->blink.fd = -1; - term->colors.fg = term->conf->colors.fg; - term->colors.bg = term->conf->colors.bg; - term->colors.alpha = term->conf->colors.alpha; - term->colors.cursor_fg = (term->conf->colors.use_custom.cursor ? 1u << 31 : 0) | term->conf->colors.cursor.text; - term->colors.cursor_bg = (term->conf->colors.use_custom.cursor ? 1u << 31 : 0) | term->conf->colors.cursor.cursor; - term->colors.selection_fg = term->conf->colors.selection_fg; - term->colors.selection_bg = term->conf->colors.selection_bg; - term->colors.use_custom_selection = term->conf->colors.use_custom.selection; - memcpy(term->colors.table, term->conf->colors.table, - sizeof(term->colors.table)); + term_theme_apply(term, &term->conf->colors); + term->colors.active_theme = COLOR_THEME1; free(term->color_stack.stack); term->color_stack.stack = NULL; term->color_stack.size = 0; @@ -4693,3 +4686,17 @@ term_send_size_notification(struct terminal *term) term->rows, term->cols, height, width); term_to_slave(term, buf, n); } + +void +term_theme_apply(struct terminal *term, const struct color_theme *theme) +{ + term->colors.fg = theme->fg; + term->colors.bg = theme->bg; + term->colors.alpha = theme->alpha; + term->colors.cursor_fg = (theme->use_custom.cursor ? 1u << 31 : 0) | theme->cursor.text; + 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.use_custom_selection = theme->use_custom.selection; + memcpy(term->colors.table, theme->table, sizeof(term->colors.table)); +} diff --git a/terminal.h b/terminal.h index 518e36ef..45e13925 100644 --- a/terminal.h +++ b/terminal.h @@ -405,6 +405,7 @@ struct colors { uint32_t selection_fg; uint32_t selection_bg; bool use_custom_selection; + enum { COLOR_THEME1, COLOR_THEME2 } active_theme; }; struct terminal { @@ -982,6 +983,8 @@ void term_enable_size_notifications(struct terminal *term); void term_disable_size_notifications(struct terminal *term); void term_send_size_notification(struct terminal *term); +void term_theme_apply(struct terminal *term, const struct color_theme *theme); + static inline void term_reset_grapheme_state(struct terminal *term) { #if defined(FOOT_GRAPHEME_CLUSTERING) From 10e7f291498a6600cfecc3cde604ea51de5f0f5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 20 Apr 2025 12:48:37 +0200 Subject: [PATCH 44/86] csi: implement private mode 2031 (dark/light mode detection) * Recognize 'CSI ? 996 n', and respond with - 'CSI ? 997 ; 1 n' if the primary theme is active - 'CSI ? 997 ; 2 n' if the alternative theme is actice * Implement private mode 2031, where changing the color theme (currently only possible via key bindings) causes the terminal to send the same CSI sequences as above. In this context, foot's primary theme is considered dark, and the alternative theme light (since the default theme is dark). Closes #2025 --- CHANGELOG.md | 5 +++++ csi.c | 33 +++++++++++++++++++++++++++++++++ doc/foot-ctlseqs.7.scd | 10 ++++++++++ doc/foot.ini.5.scd | 8 ++++++++ input.c | 12 ++++++++++++ terminal.h | 2 ++ 6 files changed, 70 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4296da09..5c40b8cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,11 @@ `key-bindings.color-theme-toggle` key bindings. These can be used to switch between the primary and alternative color themes. They are not bound by default. +* Support for private mode 2031 - [_Dark and Light Mode + Detection_](https://contour-terminal.org/vt-extensions/color-palette-update-notifications/) + ([#2025][2025]) + +[2025]: https://codeberg.org/dnkl/foot/issues/2025 ### Changed diff --git a/csi.c b/csi.c index b66fda21..e8b2c492 100644 --- a/csi.c +++ b/csi.c @@ -563,6 +563,10 @@ decset_decrst(struct terminal *term, unsigned param, bool enable) #endif break; + case 2031: + term->report_theme_changes = enable; + break; + case 2048: if (enable) term_enable_size_notifications(term); @@ -657,6 +661,7 @@ decrqm(const struct terminal *term, unsigned param) case 2027: return term->conf->tweak.grapheme_width_method != GRAPHEME_WIDTH_DOUBLE ? DECRPM_PERMANENTLY_RESET : decrpm(term->grapheme_shaping); + case 2031: return decrpm(term->report_theme_changes); case 2048: return decrpm(term->size_notifications); case 8452: return decrpm(term->sixel.cursor_right_of_graphics); case 737769: return decrpm(term_ime_is_enabled(term)); @@ -702,6 +707,7 @@ xtsave(struct terminal *term, unsigned param) case 2004: term->xtsave.bracketed_paste = term->bracketed_paste; break; case 2026: term->xtsave.app_sync_updates = term->render.app_sync_updates.enabled; break; case 2027: term->xtsave.grapheme_shaping = term->grapheme_shaping; break; + case 2031: term->xtsave.report_theme_changes = term->report_theme_changes; break; case 2048: term->xtsave.size_notifications = term->size_notifications; break; case 8452: term->xtsave.sixel_cursor_right_of_graphics = term->sixel.cursor_right_of_graphics; break; case 737769: term->xtsave.ime = term_ime_is_enabled(term); break; @@ -746,6 +752,7 @@ xtrestore(struct terminal *term, unsigned param) case 2004: enable = term->xtsave.bracketed_paste; break; case 2026: enable = term->xtsave.app_sync_updates; break; case 2027: enable = term->xtsave.grapheme_shaping; break; + case 2031: enable = term->xtsave.report_theme_changes; break; case 2048: enable = term->xtsave.size_notifications; break; case 8452: enable = term->xtsave.sixel_cursor_right_of_graphics; break; case 737769: enable = term->xtsave.ime; break; @@ -1539,6 +1546,32 @@ csi_dispatch(struct terminal *term, uint8_t final) break; } + case 'n': { + const int param = vt_param_get(term, 0, 0); + + switch (param) { + case 996: { /* Query current theme mode (see private mode 2031) */ + /* + * 1 - dark mode + * 2 - light mode + * + * In foot, the themes aren't necessarily light/dark, + * but by convention, the primary theme is dark, and + * the alternative theme is light. + */ + char reply[16] = {0}; + int chars = snprintf( + reply, sizeof(reply), + "\033[?997;%dn", + term->colors.active_theme == COLOR_THEME1 ? 1 : 2); + + term_to_slave(term, reply, chars); + break; + } + } + break; + } + case 'p': { /* * Request status of ECMA-48/"ANSI" private mode (DECRQM diff --git a/doc/foot-ctlseqs.7.scd b/doc/foot-ctlseqs.7.scd index 6c702738..40906ebf 100644 --- a/doc/foot-ctlseqs.7.scd +++ b/doc/foot-ctlseqs.7.scd @@ -337,6 +337,9 @@ that corresponds to one of the following modes: | 2027 : contour : Grapheme cluster processing +| 2031 +: contour +: Request color theme updates | 2048 : TODO : In-band window resize notifications @@ -657,6 +660,13 @@ manipulation sequences. The generic format is: : xterm : Report the current entry on the palette stack, and the number of palettes stored on the stack. +| \\E[ ? 996 n +: Query the current (color) theme mode +: contour +: The current color theme mode (light or dark) is reported as *CSI ? + 997 ; 1|2 n*, where *1* means dark and *2* light. By convention, the + primary theme in foot is considered dark, and the alternative theme + light. # OSC diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index af6f7875..85a7cf7b 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -958,6 +958,10 @@ The colors are in RRGGBB format (i.e. plain old 6-digit hex values, without prefix). That is, they do *not* have an alpha component. You can configure the background transparency with the _alpha_ option. +In the context of private mode 2031 (Dark and Light Mode detection), +the primary theme (i.e. the *colors* section) is considered to be the +dark theme (since the default theme is dark). + *cursor* Two space separated RRGGBB values (i.e. plain old 6-digit hex values, without prefix) specifying the foreground (text) and @@ -1093,6 +1097,10 @@ Note that values are not inherited. That is, if you set a value in *colors*, but not in *colors2*, the value from *colors* is not inherited by *colors2*. +In the context of private mode 2031 (Dark and Light Mode detection), +the primary theme (i.e. the *colors2* section) is considered to be the +light theme (since the default theme is dark). + # SECTION: csd This section controls the look of the _CSDs_ (Client Side diff --git a/input.c b/input.c index ebb646c6..b6c56fde 100644 --- a/input.c +++ b/input.c @@ -492,6 +492,9 @@ execute_binding(struct seat *seat, struct terminal *term, wayl_win_alpha_changed(term->window); term_font_subpixel_changed(term); + if (term->report_theme_changes) + term_to_slave(term, "\033[?997;1n", 9); + term_damage_view(term); term_damage_margins(term); render_refresh(term); @@ -506,6 +509,9 @@ execute_binding(struct seat *seat, struct terminal *term, wayl_win_alpha_changed(term->window); term_font_subpixel_changed(term); + if (term->report_theme_changes) + term_to_slave(term, "\033[?997;2n", 9); + term_damage_view(term); term_damage_margins(term); render_refresh(term); @@ -516,9 +522,15 @@ execute_binding(struct seat *seat, struct terminal *term, if (term->colors.active_theme == COLOR_THEME1) { term_theme_apply(term, &term->conf->colors2); term->colors.active_theme = COLOR_THEME2; + + if (term->report_theme_changes) + term_to_slave(term, "\033[?997;2n", 9); } else { term_theme_apply(term, &term->conf->colors); term->colors.active_theme = COLOR_THEME1; + + if (term->report_theme_changes) + term_to_slave(term, "\033[?997;1n", 9); } wayl_win_alpha_changed(term->window); diff --git a/terminal.h b/terminal.h index 45e13925..e6499ef7 100644 --- a/terminal.h +++ b/terminal.h @@ -518,6 +518,7 @@ struct terminal { bool num_lock_modifier; bool bell_action_enabled; + bool report_theme_changes; /* Saved DECSET modes - we save the SET state */ struct { @@ -548,6 +549,7 @@ struct terminal { bool ime:1; bool app_sync_updates:1; bool grapheme_shaping:1; + bool report_theme_changes:1; bool size_notifications:1; From bc5b71666867c00e7dc850740bb5cc2a13a9c2a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 21 Apr 2025 12:19:11 +0200 Subject: [PATCH 45/86] config: add initial-color-theme=1|2 This option selects which color theme to use by default. I.e. at startup, and after a reset. This is useful with combined theme files, where a single file defines e.g. both a dark and light version of the theme. --- CHANGELOG.md | 2 ++ config.c | 11 ++++++++++- config.h | 6 ++++++ doc/foot.ini.5.scd | 12 ++++++++++++ foot.ini | 1 + terminal.c | 39 ++++++++++++++++++++++++++------------- terminal.h | 2 +- 7 files changed, 58 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c40b8cf..41457205 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,8 @@ * Support for private mode 2031 - [_Dark and Light Mode Detection_](https://contour-terminal.org/vt-extensions/color-palette-update-notifications/) ([#2025][2025]) +* Added `initial-color-theme=1|2` config option. `1` uses colors from + the `[colors]` section, `2` uses `[colors2]`. [2025]: https://codeberg.org/dnkl/foot/issues/2025 diff --git a/config.c b/config.c index 921b2d68..7dc41aa6 100644 --- a/config.c +++ b/config.c @@ -1098,6 +1098,15 @@ parse_section_main(struct context *ctx) return true; } + else if (streq(key, "initial-color-theme")) { + _Static_assert( + sizeof(conf->initial_color_theme) == sizeof(int), + "enum is not 32-bit"); + + return value_to_enum(ctx, (const char*[]){"1", "2", NULL}, + (int *)&conf->initial_color_theme); + } + else { LOG_CONTEXTUAL_ERR("not a valid option: %s", key); return false; @@ -3402,7 +3411,7 @@ config_load(struct config *conf, const char *conf_path, .url = false, }, }, - + .initial_color_theme = COLOR_THEME1, .cursor = { .style = CURSOR_BLOCK, .unfocused_style = CURSOR_UNFOCUSED_HOLLOW, diff --git a/config.h b/config.h index 8954b270..cbdf11b1 100644 --- a/config.h +++ b/config.h @@ -190,6 +190,11 @@ struct color_theme { } use_custom; }; +enum which_color_theme { + COLOR_THEME1, + COLOR_THEME2, +}; + struct config { char *term; char *shell; @@ -305,6 +310,7 @@ struct config { struct color_theme colors; struct color_theme colors2; + enum which_color_theme initial_color_theme; struct { enum cursor_style style; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 85a7cf7b..f7da2c53 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -349,6 +349,18 @@ empty string to be set, but it must be quoted: *KEY=""*) Default: _yes_ +*initial-color-theme* + Selects which color theme to use, *1*, or *2*. + + *1* uses the colors defined in the *colors* section, while *2* + uses the colors from the *colors2* section. + + Use the *color-theme-switch-1*, *color-theme-switch-2* and + *color-theme-toggle* key bindings to switch between the two themes + at runtime. + + Default: _1_ + *initial-window-size-pixels* Initial window width and height in _pixels_ (subject to output scaling), in the form _WIDTHxHEIGHT_. The height _includes_ the diff --git a/foot.ini b/foot.ini index ebbc8ca7..563558db 100644 --- a/foot.ini +++ b/foot.ini @@ -23,6 +23,7 @@ # box-drawings-uses-font-glyphs=no # dpi-aware=no +# initial-color-theme=1 # initial-window-size-pixels=700x500 # Or, # initial-window-size-chars= # initial-window-mode=windowed diff --git a/terminal.c b/terminal.c index 29e03b8e..d25516cb 100644 --- a/terminal.c +++ b/terminal.c @@ -1262,6 +1262,12 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, const bool ten_bit_surfaces = conf->tweak.surface_bit_depth == SHM_10_BIT; + const struct color_theme *theme = NULL; + switch (conf->initial_color_theme) { + case COLOR_THEME1: theme = &conf->colors; break; + case COLOR_THEME2: theme = &conf->colors2; break; + } + /* Initialize configure-based terminal attributes */ *term = (struct terminal) { .fdm = fdm, @@ -1279,7 +1285,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, }, .font_dpi = 0., .font_dpi_before_unmap = -1., - .font_subpixel = (conf->colors.alpha == 0xffff /* Can't do subpixel rendering on transparent background */ + .font_subpixel = (theme->alpha == 0xffff /* Can't do subpixel rendering on transparent background */ ? FCFT_SUBPIXEL_DEFAULT : FCFT_SUBPIXEL_NONE), .cursor_keys_mode = CURSOR_KEYS_NORMAL, @@ -1295,15 +1301,15 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, .state = 0, /* STATE_GROUND */ }, .colors = { - .fg = conf->colors.fg, - .bg = conf->colors.bg, - .alpha = conf->colors.alpha, - .cursor_fg = (conf->colors.use_custom.cursor ? 1u << 31 : 0) | conf->colors.cursor.text, - .cursor_bg = (conf->colors.use_custom.cursor ? 1u << 31 : 0) | conf->colors.cursor.cursor, - .selection_fg = conf->colors.selection_fg, - .selection_bg = conf->colors.selection_bg, - .use_custom_selection = conf->colors.use_custom.selection, - .active_theme = COLOR_THEME1, + .fg = theme->fg, + .bg = theme->bg, + .alpha = theme->alpha, + .cursor_fg = (theme->use_custom.cursor ? 1u << 31 : 0) | theme->cursor.text, + .cursor_bg = (theme->use_custom.cursor ? 1u << 31 : 0) | theme->cursor.cursor, + .selection_fg = theme->selection_fg, + .selection_bg = theme->selection_bg, + .use_custom_selection = theme->use_custom.selection, + .active_theme = conf->initial_color_theme, }, .color_stack = { .stack = NULL, @@ -1434,7 +1440,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, xassert(tll_length(term->wl->monitors) > 0); term->scale = tll_front(term->wl->monitors).scale; - memcpy(term->colors.table, term->conf->colors.table, sizeof(term->colors.table)); + memcpy(term->colors.table, theme->table, sizeof(term->colors.table)); /* Initialize the Wayland window backend */ if ((term->window = wayl_win_init(term, token)) == NULL) @@ -2148,11 +2154,18 @@ term_reset(struct terminal *term, bool hard) if (!hard) return; + const struct color_theme *theme = NULL; + + switch (term->conf->initial_color_theme) { + case COLOR_THEME1: theme = &term->conf->colors; break; + case COLOR_THEME2: theme = &term->conf->colors2; break; + } + term->flash.active = false; term->blink.state = BLINK_ON; fdm_del(term->fdm, term->blink.fd); term->blink.fd = -1; - term_theme_apply(term, &term->conf->colors); - term->colors.active_theme = COLOR_THEME1; + term_theme_apply(term, theme); + term->colors.active_theme = term->conf->initial_color_theme; free(term->color_stack.stack); term->color_stack.stack = NULL; term->color_stack.size = 0; diff --git a/terminal.h b/terminal.h index e6499ef7..4639fa69 100644 --- a/terminal.h +++ b/terminal.h @@ -405,7 +405,7 @@ struct colors { uint32_t selection_fg; uint32_t selection_bg; bool use_custom_selection; - enum { COLOR_THEME1, COLOR_THEME2 } active_theme; + enum which_color_theme active_theme; }; struct terminal { From 537092e6434361307f2c7d88b327920dc176c8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 21 Apr 2025 12:20:28 +0200 Subject: [PATCH 46/86] themes: solarized: add dark/light combined theme file These themes uses the 'colors' section to define the dark variant, and 'colors2' to define the light variant. --- themes/solarized | 47 ++++++++++++++++++++++++++++ themes/solarized-normal-brights | 54 +++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 themes/solarized create mode 100644 themes/solarized-normal-brights diff --git a/themes/solarized b/themes/solarized new file mode 100644 index 00000000..335c738e --- /dev/null +++ b/themes/solarized @@ -0,0 +1,47 @@ +# -*- conf -*- +# Solarized dark+light + +# Dark +[colors] +cursor= 002b36 93a1a1 +background= 002b36 +foreground= 839496 +regular0= 073642 +regular1= dc322f +regular2= 859900 +regular3= b58900 +regular4= 268bd2 +regular5= d33682 +regular6= 2aa198 +regular7= eee8d5 +bright0= 002b36 +bright1= cb4b16 +bright2= 586e75 +bright3= 657b83 +bright4= 839496 +bright5= 6c71c4 +bright6= 93a1a1 +bright7= fdf6e3 + + +# Light +[colors2] +cursor= fdf6e3 586e75 +background= fdf6e3 +foreground= 657b83 +regular0= eee8d5 +regular1= dc322f +regular2= 859900 +regular3= b58900 +regular4= 268bd2 +regular5= d33682 +regular6= 2aa198 +regular7= 073642 +bright0= cb4b16 +bright1= fdf6e3 +bright2= 93a1a1 +bright3= 839496 +bright4= 657b83 +bright5= 6c71c4 +bright6= 586e75 +bright7= 002b36 diff --git a/themes/solarized-normal-brights b/themes/solarized-normal-brights new file mode 100644 index 00000000..a7724cd3 --- /dev/null +++ b/themes/solarized-normal-brights @@ -0,0 +1,54 @@ +# -*- conf -*- +# Solarized dark+light +# +# Bright colors are brighter versions of the regular colors, instead +# of the normal solarized palette. +# +# They were generated by taking the regular colors, decoding from sRGB +# to linear, multiplying the linear RGB values by 1.8, and then +# encoding to sRGB again. + +# Dark +[colors] +cursor= 002b36 93a1a1 +background= 002b36 +foreground= 839496 +regular0= 073642 +regular1= dc322f +regular2= 859900 +regular3= b58900 +regular4= 268bd2 +regular5= d33682 +regular6= 2aa198 +regular7= eee8d5 +bright0= 0c4958 +bright1= ff4440 +bright2= aec700 +bright3= ebb300 +bright4= 34b5ff +bright5= ff49aa +bright6= 3ad2c6 +bright7= ffffff + + +# Light +[colors2] +cursor= fdf6e3 586e75 +background= fdf6e3 +foreground= 657b83 +regular0= eee8d5 +regular1= dc322f +regular2= 859900 +regular3= b58900 +regular4= 268bd2 +regular5= d33682 +regular6= 2aa198 +regular7= 073642 +bright0= ffffff +bright1= ff4440 +bright2= aec700 +bright3= ebb300 +bright4= 34b5ff +bright5= ff49aa +bright6= 3ad2c6 +bright7= 0c4958 From 1dc14a300107e13b87662a010968763f64ed9321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Apr 2025 15:23:44 +0200 Subject: [PATCH 47/86] themes: selenized: add dark/light combined theme file --- themes/selenized | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 themes/selenized diff --git a/themes/selenized b/themes/selenized new file mode 100644 index 00000000..cde35723 --- /dev/null +++ b/themes/selenized @@ -0,0 +1,48 @@ +# -*- conf -*- +# Selenized dark + +[colors] +cursor = 103c48 53d6c7 +background= 103c48 +foreground= adbcbc + +regular0= 184956 +regular1= fa5750 +regular2= 75b938 +regular3= dbb32d +regular4= 4695f7 +regular5= f275be +regular6= 41c7b9 +regular7= 72898f + +bright0= 2d5b69 +bright1= ff665c +bright2= 84c747 +bright3= ebc13d +bright4= 58a3ff +bright5= ff84cd +bright6= 53d6c7 +bright7= cad8d9 + +[colors2] +cursor=fbf3db 00978a +background= fbf3db +foreground= 53676d + +regular0= ece3cc +regular1= d2212d +regular2= 489100 +regular3= ad8900 +regular4= 0072d4 +regular5= ca4898 +regular6= 009c8f +regular7= 909995 + +bright0= d5cdb6 +bright1= cc1729 +bright2= 428b00 +bright3= a78300 +bright4= 006dce +bright5= c44392 +bright6= 00978a +bright7= 3a4d53 From 6a1c3b89c2f0b5f34d7c2f9957846eb90ca1f62c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Apr 2025 15:26:22 +0200 Subject: [PATCH 48/86] themes: gruvbox: add dark/light combined theme file --- themes/gruvbox | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 themes/gruvbox diff --git a/themes/gruvbox b/themes/gruvbox new file mode 100644 index 00000000..6bc97352 --- /dev/null +++ b/themes/gruvbox @@ -0,0 +1,42 @@ +# -*- conf -*- +# Gruvbox + +[colors] +background=282828 +foreground=ebdbb2 +regular0=282828 +regular1=cc241d +regular2=98971a +regular3=d79921 +regular4=458588 +regular5=b16286 +regular6=689d6a +regular7=a89984 +bright0=928374 +bright1=fb4934 +bright2=b8bb26 +bright3=fabd2f +bright4=83a598 +bright5=d3869b +bright6=8ec07c +bright7=ebdbb2 + +[colors2] +background=fbf1c7 +foreground=3c3836 +regular0=fbf1c7 +regular1=cc241d +regular2=98971a +regular3=d79921 +regular4=458588 +regular5=b16286 +regular6=689d6a +regular7=7c6f64 +bright0=928374 +bright1=9d0006 +bright2=79740e +bright3=b57614 +bright4=076678 +bright5=8f3f71 +bright6=427b58 +bright7=3c3836 From d3e45791bde9af9c6ae8e0380f4f810778f59a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Apr 2025 15:26:31 +0200 Subject: [PATCH 49/86] themes: nvim: add dark/light combined theme file --- themes/nvim | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 themes/nvim diff --git a/themes/nvim b/themes/nvim new file mode 100644 index 00000000..bf629c0a --- /dev/null +++ b/themes/nvim @@ -0,0 +1,56 @@ +# -*- conf -*- +# Neovim Dark theme +# Uses the dark color palette from the default Neovim color scheme +# See: https://github.com/neovim/neovim/blob/fb6c059dc55c8d594102937be4dd70f5ff51614a/src/nvim/highlight_group.c#L419 + +[colors] +cursor=14161b e0e2ea # NvimDarkGrey2 NvimLightGrey2 +foreground=e0e2ea # NvimLightGrey2 +background=14161b # NvimDarkGrey2 + +selection-foreground=e0e2ea # NvimLightGrey2 +selection-background=4f5258 # NvimDarkGrey4 + +regular0=07080d # NvimDarkGrey1 +regular1=ffc0b9 # NvimLightRed +regular2=b3f6c0 # NvimLightGreen +regular3=fce094 # NvimLightYellow +regular4=a6dbff # NvimLightBlue +regular5=ffcaff # NvimLightMagenta +regular6=8cf8f7 # NvimLightCyan +regular7=c4c6cd # NvimLightGrey3 + +bright0=2c2e33 # NvimDarkGrey3 +bright1=ffc0b9 # NvimLightRed +bright2=b3f6c0 # NvimLightGreen +bright3=fce094 # NvimLightYellow +bright4=a6dbff # NvimLightBlue +bright5=ffcaff # NvimLightMagenta +bright6=8cf8f7 # NvimLightCyan +bright7=eef1f8 # NvimLightGrey1 + +[colors2] +cursor=e0e2ea 14161b # NvimLightGrey2 NvimDarkGrey2 +foreground=14161b # NvimDarkGrey2 +background=e0e2ea # NvimLightGrey2 + +selection-foreground=14161b # NvimDarkGrey2 +selection-background=9b9ea4 # NvimLightGrey4 + +regular0=eef1f8 # NvimLightGrey1 +regular1=590008 # NvimDarkRed +regular2=005523 # NvimDarkGreen +regular3=6b5300 # NvimDarkYellow +regular4=004c73 # NvimDarkBlue +regular5=470045 # NvimDarkMagenta +regular6=007373 # NvimDarkCyan +regular7=2c2e33 # NvimDarkGrey3 + +bright0=c4c6cd # NvimLightGrey3 +bright1=590008 # NvimDarkRed +bright2=005523 # NvimDarkGreen +bright3=6b5300 # NvimDarkYellow +bright4=004c73 # NvimDarkBlue +bright5=470045 # NvimDarkMagenta +bright6=007373 # NvimDarkCyan +bright7=07080d # NvimDarkGrey1 From 8273514d3c98108fbd02316fb5f44a721cca8e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Apr 2025 15:26:36 +0200 Subject: [PATCH 50/86] themes: paper-color: add dark/light combined theme file --- themes/paper-color | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 themes/paper-color diff --git a/themes/paper-color b/themes/paper-color new file mode 100644 index 00000000..f158c148 --- /dev/null +++ b/themes/paper-color @@ -0,0 +1,49 @@ +# -*- conf -*- +# PaperColorDark +# Palette based on https://github.com/NLKNguyen/papercolor-theme + +[colors] +cursor=1c1c1c eeeeee +background=1c1c1c +foreground=eeeeee +regular0=1c1c1c # black +regular1=af005f # red +regular2=5faf00 # green +regular3=d7af5f # yellow +regular4=5fafd7 # blue +regular5=808080 # magenta +regular6=d7875f # cyan +regular7=d0d0d0 # white +bright0=bcbcbc # bright black +bright1=5faf5f # bright red +bright2=afd700 # bright green +bright3=af87d7 # bright yellow +bright4=ffaf00 # bright blue +bright5=ff5faf # bright magenta +bright6=00afaf # bright cyan +bright7=5f8787 # bright white +# selection-foreground=1c1c1c +# selection-background=af87d7 + +[colors2] +cursor=eeeeee 444444 +background=eeeeee +foreground=444444 +regular0=eeeeee # black +regular1=af0000 # red +regular2=008700 # green +regular3=5f8700 # yellow +regular4=0087af # blue +regular5=878787 # magenta +regular6=005f87 # cyan +regular7=764e37 # white +bright0=bcbcbc # bright black +bright1=d70000 # bright red +bright2=d70087 # bright green +bright3=8700af # bright yellow +bright4=d75f00 # bright blue +bright5=d75f00 # bright magenta +bright6=4c7a5d # bright cyan +bright7=005faf # bright white +# selection-foreground=eeeeee +# selection-background=0087af From 4d70bb7b420c748ed6fc33262a3b0cf0bc0865eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Apr 2025 18:15:31 +0200 Subject: [PATCH 51/86] changelog: mention the new combined dark/light theme files --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41457205..91627d0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,13 @@ ([#2025][2025]) * Added `initial-color-theme=1|2` config option. `1` uses colors from the `[colors]` section, `2` uses `[colors2]`. +* Combined dark/light theme files for (dark variant is the default, + set `initial-color-theme=2` to use the light variant by default): + - gruvbox + - nvim + - paper-color + - selenized + - solarized [2025]: https://codeberg.org/dnkl/foot/issues/2025 From d20fbc68078fe5b3bc8782e8e7827caf406708e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Apr 2025 07:46:09 +0200 Subject: [PATCH 52/86] config: parse_color_theme(): make NOINLINE --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index 7dc41aa6..04a9c9d4 100644 --- a/config.c +++ b/config.c @@ -1350,7 +1350,7 @@ parse_section_regex(struct context *ctx) } } -static bool +static bool NOINLINE parse_color_theme(struct context *ctx, struct color_theme *theme) { const char *key = ctx->key; From 97910a5cbac84bb99254849ee1b5c00b00b983c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Apr 2025 10:14:45 +0200 Subject: [PATCH 53/86] scripts: srgb: use 2.2 gamma TF instead of piece-wise sRGB TF --- CHANGELOG.md | 12 ++++++++++++ scripts/srgb.py | 29 +++++------------------------ 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91627d0d..f231a1be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -90,6 +90,9 @@ ### Changed * `cursor.color` moved to `colors.cursor`. +* `gamma-correct-blending=yes` now uses a pure gamma 2.2 transfer + function, instead of the piece-wise sRGB transfer function, to match + what compositors do. ### Deprecated @@ -99,6 +102,15 @@ ### Removed ### Fixed + +* Wrong colors when `gamma-correct-blending=yes` (the default when + there is compositor support). Note that some colors will still be + off by a **very** small amount, due to loss of precision when + converting to a linear color space. ([#2035][2035]). + +[2035]: https://codeberg.org/dnkl/foot/issues/2035 + + ### Security ### Contributors diff --git a/scripts/srgb.py b/scripts/srgb.py index 7655dbe4..12056956 100755 --- a/scripts/srgb.py +++ b/scripts/srgb.py @@ -5,21 +5,16 @@ import math import sys +# Note: we use a pure gamma 2.2 function, rather than the piece-wise +# sRGB transfer function, since that is what all compositors do. + def srgb_to_linear(f: float) -> float: assert(f >= 0 and f <= 1.0) - - if f <= 0.04045: - return f / 12.92 - - return math.pow((f + 0.055) / 1.055, 2.4) + return math.pow(f, 2.2) def linear_to_srgb(f: float) -> float: - if f < 0.0031308: - return f * 12.92 - - return 1.055 * math.pow(f, 1 / 2.4) - 0.055 - + return math.pow(f, 1 / 2.2) def main(): @@ -29,24 +24,10 @@ def main(): opts = parser.parse_args() linear_table: list[int] = [] - srgb_table: list[int] = [] for i in range(256): linear_table.append(int(srgb_to_linear(float(i) / 255) * 65535 + 0.5)) - for i in range(4096): - srgb_table.append(int(linear_to_srgb(float(i) / 4095) * 255 + 0.5)) - - for i in range(256): - while True: - linear = linear_table[i] - srgb = srgb_table[linear >> 4] - - if i == srgb: - break - - linear_table[i] += 1 - opts.h_output.write("#pragma once\n") opts.h_output.write("#include \n") From d7b48d3924eac9013fbd6b1f2220f44d2f392b9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 28 Apr 2025 12:32:40 +0200 Subject: [PATCH 54/86] doc: foot.ini: gamma-correct: tweak wording of 8- vs. 10-bit surfaces --- doc/foot.ini.5.scd | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index f7da2c53..215809f8 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -220,11 +220,12 @@ empty string to be set, but it must be quoted: *KEY=""*) than intended when rendered with gamma-correct blending, since the font designer set the font weight based on incorrect rendering. - Note that some colors (especially dark ones) will look a bit + Note that some colors (especially dark ones) may be slightly off. The reason for this is loss of color precision, due to foot - using 8-bit surfaces (i.e. each color channel is 8 bits). The - amount of errors can be reduced by using 10-bit surfaces; see - *tweak.surface-bit-depth*. + using 8-bit surfaces (i.e. each color channel is 8 bits). In all + known cases, the difference is small enough not to be noticed + though. The amount of errors can be reduced by using 10-bit + surfaces; see *tweak.surface-bit-depth*. Default: enabled when compositor support is available From eb79a27900603aeb171e3c43c00452a11d695a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 30 Apr 2025 09:28:35 +0200 Subject: [PATCH 55/86] readme: donations: add liberapay --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3395aff0..95f43fb4 100644 --- a/README.md +++ b/README.md @@ -689,6 +689,7 @@ Every now and then I post foot related updates on # Sponsoring/donations +* Liberapay: https://liberapay.com/dnkl * GitHub Sponsors: https://github.com/sponsors/dnkl From 1ea20b1b707ded204e6239f2704ae97ec9cd8852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Apr 2025 10:41:14 +0200 Subject: [PATCH 56/86] changelog: add new 'unreleased' section --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aeabbe14..3c48c41c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.22.1](#1-22-1) * [1.22.0](#1-22-0) * [1.21.0](#1-21-0) @@ -60,6 +61,16 @@ * [1.2.0](#1-2-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.22.1 ### Fixed From ce424e0990f9bf20995cfde36f3ec635512d64fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 27 Apr 2025 10:14:45 +0200 Subject: [PATCH 57/86] scripts: srgb: use 2.2 gamma TF instead of piece-wise sRGB TF --- CHANGELOG.md | 15 +++++++++++++++ scripts/srgb.py | 29 +++++------------------------ 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c48c41c..645c6c38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,9 +64,24 @@ ## Unreleased ### Added ### Changed + +* `gamma-correct-blending=yes` now uses a pure gamma 2.2 transfer + function, instead of the piece-wise sRGB transfer function, to match + what compositors do. + + ### Deprecated ### Removed ### Fixed + +* Wrong colors when `gamma-correct-blending=yes` (the default when + there is compositor support). Note that some colors will still be + off by a **very** small amount, due to loss of precision when + converting to a linear color space. ([#2035][2035]). + +[2035]: https://codeberg.org/dnkl/foot/issues/2035 + + ### Security ### Contributors diff --git a/scripts/srgb.py b/scripts/srgb.py index 7655dbe4..12056956 100755 --- a/scripts/srgb.py +++ b/scripts/srgb.py @@ -5,21 +5,16 @@ import math import sys +# Note: we use a pure gamma 2.2 function, rather than the piece-wise +# sRGB transfer function, since that is what all compositors do. + def srgb_to_linear(f: float) -> float: assert(f >= 0 and f <= 1.0) - - if f <= 0.04045: - return f / 12.92 - - return math.pow((f + 0.055) / 1.055, 2.4) + return math.pow(f, 2.2) def linear_to_srgb(f: float) -> float: - if f < 0.0031308: - return f * 12.92 - - return 1.055 * math.pow(f, 1 / 2.4) - 0.055 - + return math.pow(f, 1 / 2.2) def main(): @@ -29,24 +24,10 @@ def main(): opts = parser.parse_args() linear_table: list[int] = [] - srgb_table: list[int] = [] for i in range(256): linear_table.append(int(srgb_to_linear(float(i) / 255) * 65535 + 0.5)) - for i in range(4096): - srgb_table.append(int(linear_to_srgb(float(i) / 4095) * 255 + 0.5)) - - for i in range(256): - while True: - linear = linear_table[i] - srgb = srgb_table[linear >> 4] - - if i == srgb: - break - - linear_table[i] += 1 - opts.h_output.write("#pragma once\n") opts.h_output.write("#include \n") From 172f67a8df71d859e3f610dabffabff0623775a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 28 Apr 2025 12:32:40 +0200 Subject: [PATCH 58/86] doc: foot.ini: gamma-correct: tweak wording of 8- vs. 10-bit surfaces --- doc/foot.ini.5.scd | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index cffbf9c5..95e491eb 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -220,11 +220,12 @@ empty string to be set, but it must be quoted: *KEY=""*) than intended when rendered with gamma-correct blending, since the font designer set the font weight based on incorrect rendering. - Note that some colors (especially dark ones) will look a bit + Note that some colors (especially dark ones) may be slightly off. The reason for this is loss of color precision, due to foot - using 8-bit surfaces (i.e. each color channel is 8 bits). The - amount of errors can be reduced by using 10-bit surfaces; see - *tweak.surface-bit-depth*. + using 8-bit surfaces (i.e. each color channel is 8 bits). In all + known cases, the difference is small enough not to be noticed + though. The amount of errors can be reduced by using 10-bit + surfaces; see *tweak.surface-bit-depth*. Default: enabled when compositor support is available From fc293bad5e3989a7aef778ad8d0dc054794a990c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 30 Apr 2025 10:23:20 +0200 Subject: [PATCH 59/86] changelog: prepare 1.22.2 --- CHANGELOG.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 645c6c38..8ef3ae43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* [Unreleased](#unreleased) +* [1.22.2](#1-22-2) * [1.22.1](#1-22-1) * [1.22.0](#1-22-0) * [1.21.0](#1-21-0) @@ -61,8 +61,8 @@ * [1.2.0](#1-2-0) -## Unreleased -### Added +## 1.22.2 + ### Changed * `gamma-correct-blending=yes` now uses a pure gamma 2.2 transfer @@ -70,8 +70,6 @@ what compositors do. -### Deprecated -### Removed ### Fixed * Wrong colors when `gamma-correct-blending=yes` (the default when @@ -82,10 +80,6 @@ [2035]: https://codeberg.org/dnkl/foot/issues/2035 -### Security -### Contributors - - ## 1.22.1 ### Fixed From 513e91c33a0b1593f80b887e42c38ef4ba981634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 30 Apr 2025 10:23:51 +0200 Subject: [PATCH 60/86] meson: bump version to 1.22.2 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 7ac033b5..b3163586 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('foot', 'c', - version: '1.22.1', + version: '1.22.2', license: 'MIT', meson_version: '>=0.59.0', default_options: [ From 1dc8354534c9b1f1c7ae7e1bbe1cbd3df0cd1260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 30 Apr 2025 11:43:13 +0200 Subject: [PATCH 61/86] readme: add liberapay donation button --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 95f43fb4..e8f3c8cd 100644 --- a/README.md +++ b/README.md @@ -692,6 +692,8 @@ Every now and then I post foot related updates on * Liberapay: https://liberapay.com/dnkl * GitHub Sponsors: https://github.com/sponsors/dnkl +[![Donate using Liberapay](https://liberapay.com/assets/widgets/donate.svg)](https://liberapay.com/dnkl/donate) + # License From b07ce56321404a34fc02635ecf4d5b63ddddf1ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 May 2025 08:09:08 +0200 Subject: [PATCH 62/86] config: gamma-correct-blending: disable by default --- CHANGELOG.md | 1 + config.c | 15 +++------------ config.h | 4 +--- doc/foot.ini.5.scd | 2 +- foot.ini | 1 + render.c | 2 +- tests/test-config.c | 1 + wayland.c | 6 ++---- 8 files changed, 11 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f57ca9fb..8521fd86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,7 @@ ### Changed * `cursor.color` moved to `colors.cursor`. +* `gamma-correct-blending` now defaults to `no` instead of `yes`. ### Deprecated diff --git a/config.c b/config.c index 04a9c9d4..f182241c 100644 --- a/config.c +++ b/config.c @@ -1086,17 +1086,8 @@ parse_section_main(struct context *ctx) return true; } - else if (streq(key, "gamma-correct-blending")) { - bool gamma_correct; - if (!value_to_bool(ctx, &gamma_correct)) - return false; - - conf->gamma_correct = - gamma_correct - ? GAMMA_CORRECT_ENABLED - : GAMMA_CORRECT_DISABLED; - return true; - } + else if (streq(key, "gamma-correct-blending")) + return value_to_bool(ctx, &conf->gamma_correct); else if (streq(key, "initial-color-theme")) { _Static_assert( @@ -3362,7 +3353,7 @@ config_load(struct config *conf, const char *conf_path, .underline_thickness = {.pt = 0., .px = -1}, .strikeout_thickness = {.pt = 0., .px = -1}, .dpi_aware = false, - .gamma_correct = GAMMA_CORRECT_AUTO, + .gamma_correct = false, .security = { .osc52 = OSC52_ENABLED, }, diff --git a/config.h b/config.h index cbdf11b1..be465d68 100644 --- a/config.h +++ b/config.h @@ -232,9 +232,7 @@ struct config { enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode; bool dpi_aware; - enum {GAMMA_CORRECT_DISABLED, - GAMMA_CORRECT_ENABLED, - GAMMA_CORRECT_AUTO} gamma_correct; + bool gamma_correct; struct config_font_list fonts[4]; struct font_size_adjustment font_size_adjustment; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 215809f8..0f06d0ca 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -227,7 +227,7 @@ empty string to be set, but it must be quoted: *KEY=""*) though. The amount of errors can be reduced by using 10-bit surfaces; see *tweak.surface-bit-depth*. - Default: enabled when compositor support is available + Default: _no_. *box-drawings-uses-font-glyphs* Boolean. When disabled, foot generates box/line drawing characters diff --git a/foot.ini b/foot.ini index 563558db..f3ef6d85 100644 --- a/foot.ini +++ b/foot.ini @@ -22,6 +22,7 @@ # strikeout-thickness= # box-drawings-uses-font-glyphs=no # dpi-aware=no +# gamma-correct-blending=no # initial-color-theme=1 # initial-window-size-pixels=700x500 # Or, diff --git a/render.c b/render.c index b0d21d18..0ee60d65 100644 --- a/render.c +++ b/render.c @@ -5251,6 +5251,6 @@ render_xcursor_set(struct seat *seat, struct terminal *term, bool render_do_linear_blending(const struct terminal *term) { - return term->conf->gamma_correct != GAMMA_CORRECT_DISABLED && + return term->conf->gamma_correct && term->wl->color_management.img_description != NULL; } diff --git a/tests/test-config.c b/tests/test-config.c index 7dfb8556..bab57788 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -490,6 +490,7 @@ test_section_main(void) test_boolean(&ctx, &parse_section_main, "box-drawings-uses-font-glyphs", &conf.box_drawings_uses_font_glyphs); test_boolean(&ctx, &parse_section_main, "locked-title", &conf.locked_title); test_boolean(&ctx, &parse_section_main, "dpi-aware", &conf.dpi_aware); + test_boolean(&ctx, &parse_section_main, "gamma-correct-blending", &conf.gamma_correct); test_pt_or_px(&ctx, &parse_section_main, "font-size-adjustment", &conf.font_size_adjustment.pt_or_px); /* TODO: test ‘N%’ values too */ test_pt_or_px(&ctx, &parse_section_main, "line-height", &conf.line_height); diff --git a/wayland.c b/wayland.c index 853124be..9b143508 100644 --- a/wayland.c +++ b/wayland.c @@ -1980,7 +1980,7 @@ wayl_win_init(struct terminal *term, const char *token) xdg_toplevel_icon_v1_destroy(icon); } - if (term->conf->gamma_correct != GAMMA_CORRECT_DISABLED) { + if (term->conf->gamma_correct) { if (wayl->color_management.img_description != NULL) { xassert(wayl->color_management.manager != NULL); @@ -1990,7 +1990,7 @@ wayl_win_init(struct terminal *term, const char *token) wp_color_management_surface_v1_set_image_description( win->surface.color_management, wayl->color_management.img_description, WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL); - } else if (term->conf->gamma_correct == GAMMA_CORRECT_ENABLED) { + } else { if (wayl->color_management.manager == NULL) { LOG_WARN( "gamma-corrected-blending: disabling; " @@ -2005,8 +2005,6 @@ wayl_win_init(struct terminal *term, const char *token) LOG_WARN(" - TF: ext_linear"); LOG_WARN(" - primaries: sRGB"); } - } else { - /* "auto" - don't warn */ } } From e5a0755451b736c635be58764799ef8d7b536e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 May 2025 08:34:49 +0200 Subject: [PATCH 63/86] config: tweak.surface-bit-depth now defaults to 'auto' When set to 'auto', use 10-bit surfaces if gamma-correct blending is enabled, and 8-bit surfaces otherwise. Note that we may still fallback to 8-bit surfaces (without disabling gamma-correct blending) if the compositor does not support 10-bit surfaces. Closes #2082 --- CHANGELOG.md | 10 ++++++++++ config.c | 4 ++-- config.h | 8 +++++++- doc/foot.ini.5.scd | 41 ++++++++++++++++++++++------------------- pgo/pgo.c | 5 +++-- render.c | 31 +++++++++++++------------------ render.h | 2 -- shm.c | 15 ++++++++++++--- shm.h | 5 ++++- sixel.c | 4 ++-- terminal.c | 42 +++++++++++++++++++++--------------------- wayland.c | 7 +++++++ wayland.h | 2 ++ 13 files changed, 105 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8521fd86..aada556e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -84,6 +84,7 @@ - paper-color - selenized - solarized +* `auto` to the `tweak.surface-bit-depth` option. [2025]: https://codeberg.org/dnkl/foot/issues/2025 @@ -92,6 +93,9 @@ * `cursor.color` moved to `colors.cursor`. * `gamma-correct-blending` now defaults to `no` instead of `yes`. +* `tweak.surface-bit-depth` default value changed to `auto`; uses + 10-bit surfaces when `gamma-correct-blending=yes`, and 8-bit + surfaces otherwise. ### Deprecated @@ -101,6 +105,12 @@ ### Removed ### Fixed + +* Inaccurate colors when `gamma-correct-blending=yes` ([#2082][2082]). + +[2082]: https://codeberg.org/dnkl/foot/issues/2082 + + ### Security ### Contributors diff --git a/config.c b/config.c index f182241c..64e45135 100644 --- a/config.c +++ b/config.c @@ -2813,7 +2813,7 @@ parse_section_tweak(struct context *ctx) return value_to_enum( ctx, - (const char *[]){"8-bit", "10-bit", NULL}, + (const char *[]){"auto", "8-bit", "10-bit", NULL}, (int *)&conf->tweak.surface_bit_depth); } @@ -3463,7 +3463,7 @@ config_load(struct config *conf, const char *conf_path, .box_drawing_solid_shades = true, .font_monospace_warn = true, .sixel = true, - .surface_bit_depth = 8, + .surface_bit_depth = SHM_BITS_AUTO, }, .touch = { diff --git a/config.h b/config.h index be465d68..80081906 100644 --- a/config.h +++ b/config.h @@ -195,6 +195,12 @@ enum which_color_theme { COLOR_THEME2, }; +enum shm_bit_depth { + SHM_BITS_AUTO, + SHM_BITS_8, + SHM_BITS_10 +}; + struct config { char *term; char *shell; @@ -419,7 +425,7 @@ struct config { bool box_drawing_solid_shades; bool font_monospace_warn; bool sixel; - enum { SHM_8_BIT, SHM_10_BIT } surface_bit_depth; + enum shm_bit_depth surface_bit_depth; } tweak; struct { diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 0f06d0ca..b9ab9c6a 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -220,12 +220,13 @@ empty string to be set, but it must be quoted: *KEY=""*) than intended when rendered with gamma-correct blending, since the font designer set the font weight based on incorrect rendering. - Note that some colors (especially dark ones) may be slightly - off. The reason for this is loss of color precision, due to foot - using 8-bit surfaces (i.e. each color channel is 8 bits). In all - known cases, the difference is small enough not to be noticed - though. The amount of errors can be reduced by using 10-bit - surfaces; see *tweak.surface-bit-depth*. + In order to represent colors faithfully, higher precision image + buffers are required. By default, foot will use 10-bit color + channels, if available, when gamma-correct blending is + enabled. However, the high precision buffers are slow; if you want + to use gamma-correct blending, but prefer speed (throughput and + input latency) over accurate colors, you can force 8-bit color + channels by setting *tweak.surface-bit-depth=8-bit*. Default: _no_. @@ -2019,23 +2020,25 @@ any of these options. *surface-bit-depth* Selects which RGB bit depth to use for image buffers. One of - *8-bit*, or *10-bit*. + *auto*, *8-bit*, or *10-bit*. - The default, *8-bit*, uses 8 bits for all channels, alpha - included. When *gamma-correct-blending* is disabled, this is the - best option. + *auto* chooses bit depth depending on other settings, and + availability. - When *gamma-correct-blending* is enabled, you may want to enable - 10-bit surfaces, as that improves color precision. Be aware - however, that in this mode, the alpha channel is only 2 bits - instead of 8 bits. Thus, if you are using a transparent - background, you may want to use the default, *8-bit*, even if you - have gamma-correct blending enabled. + *8-bit*, uses 8 bits for each color channel, alpha included. This + is the default when *gamma-correct-blending=no*. - You should also note that 10-bit surface is much slower. This will - increase input latency and decrease rendering throughput. + *10-bit* uses 10 bits for each RGB channel, and 2 bits for the + alpha channel. Thus, it provides higher precision color channels, + but a lower precision alpha channel. It is the default when + *gamma-correct-blending=yes*, if supported by the compositor. - Default: _8-bit_ + Note that *10-bit* is much slower than *8-bit*; if you want to use + gamma-correct blending, and if you prefer speed (throughput and + input latency) over accurate colors, you can set + *surface-bit-depth=8-bit* explicitly. + + Default: _auto_ # SEE ALSO diff --git a/pgo/pgo.c b/pgo/pgo.c index 8a4967ba..757dcd06 100644 --- a/pgo/pgo.c +++ b/pgo/pgo.c @@ -129,7 +129,7 @@ render_worker_thread(void *_ctx) } bool -render_do_linear_blending(const struct terminal *term) +wayl_do_linear_blending(const struct wayland *wayl, const struct config *conf) { return false; } @@ -201,11 +201,12 @@ void urls_reset(struct terminal *term) {} void shm_unref(struct buffer *buf) {} void shm_chain_free(struct buffer_chain *chain) {} +enum shm_bit_depth shm_chain_bit_depth(const struct buffer_chain *chain) { return SHM_BITS_8; } struct buffer_chain * shm_chain_new( struct wayland *wayl, bool scrollable, size_t pix_instances, - bool ten_bit_it_if_capable) + enum shm_bit_depth desired_bit_depth) { return NULL; } diff --git a/render.c b/render.c index 0ee60d65..55c2ec4d 100644 --- a/render.c +++ b/render.c @@ -626,7 +626,7 @@ draw_cursor(const struct terminal *term, const struct cell *cell, pixman_color_t cursor_color; pixman_color_t text_color; cursor_colors_for_cell(term, cell, fg, bg, &cursor_color, &text_color, - render_do_linear_blending(term)); + wayl_do_linear_blending(term->wl, term->conf)); if (unlikely(!term->kbd_focus)) { switch (term->conf->cursor.unfocused_style) { @@ -820,7 +820,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, if (cell->attrs.blink && term->blink.state == BLINK_OFF) _fg = color_blend_towards(_fg, 0x00000000, term->conf->dim.amount); - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); pixman_color_t fg = color_hex_to_pixman(_fg, gamma_correct); pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha, gamma_correct); @@ -1180,7 +1180,8 @@ static void render_urgency(struct terminal *term, struct buffer *buf) { uint32_t red = term->colors.table[1]; - pixman_color_t bg = color_hex_to_pixman(red, render_do_linear_blending(term)); + pixman_color_t bg = color_hex_to_pixman( + red, wayl_do_linear_blending(term->wl, term->conf)); int width = min(min(term->margins.left, term->margins.right), min(term->margins.top, term->margins.bottom)); @@ -1211,7 +1212,7 @@ render_margin(struct terminal *term, struct buffer *buf, const int bmargin = term->height - term->margins.bottom; const int line_count = end_line - start_line; - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); const uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg; uint16_t alpha = term->colors.alpha; @@ -1699,7 +1700,7 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat, if (unlikely(term->is_searching)) return; - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); /* Adjust cursor position to viewport */ struct coord cursor; @@ -1970,7 +1971,8 @@ render_overlay(struct terminal *term) case OVERLAY_FLASH: color = color_hex_to_pixman_with_alpha( term->conf->colors.flash, - term->conf->colors.flash_alpha, render_do_linear_blending(term)); + term->conf->colors.flash_alpha, + wayl_do_linear_blending(term->wl, term->conf)); break; case OVERLAY_NONE: @@ -2312,7 +2314,7 @@ render_osd(struct terminal *term, const struct wayl_sub_surface *sub_surf, pixman_image_set_clip_region32(buf->pix[0], &clip); pixman_region32_fini(&clip); - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); uint16_t alpha = _bg >> 24 | (_bg >> 24 << 8); pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha, gamma_correct); pixman_image_fill_rectangles( @@ -2453,7 +2455,7 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx, if (info->width == 0 || info->height == 0) return; - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); { /* Fully transparent - no need to do a color space transform */ @@ -2542,7 +2544,7 @@ get_csd_button_fg_color(const struct terminal *term) } return color_hex_to_pixman_with_alpha( - _color, alpha, render_do_linear_blending(term)); + _color, alpha, wayl_do_linear_blending(term->wl, term->conf)); } static void @@ -2819,7 +2821,7 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx, if (!term->visual_focus) _color = color_dim(term, _color); - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha, gamma_correct); render_csd_part(term, surf->surf, buf, info->width, info->height, &color); @@ -3678,7 +3680,7 @@ render_search_box(struct terminal *term) : term->conf->colors.use_custom.search_box_no_match; /* Background - yellow on empty/match, red on mismatch (default) */ - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); const pixman_color_t color = color_hex_to_pixman( is_match ? (custom_colors @@ -5247,10 +5249,3 @@ render_xcursor_set(struct seat *seat, struct terminal *term, seat->pointer.xcursor_pending = true; return true; } - -bool -render_do_linear_blending(const struct terminal *term) -{ - return term->conf->gamma_correct && - term->wl->color_management.img_description != NULL; -} diff --git a/render.h b/render.h index c7b8e4a5..81d2a905 100644 --- a/render.h +++ b/render.h @@ -47,5 +47,3 @@ struct csd_data { }; struct csd_data get_csd_data(const struct terminal *term, enum csd_surface surf_idx); - -bool render_do_linear_blending(const struct terminal *term); diff --git a/shm.c b/shm.c index 32e6bdd0..38944020 100644 --- a/shm.c +++ b/shm.c @@ -972,7 +972,7 @@ shm_unref(struct buffer *_buf) struct buffer_chain * shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances, - bool ten_bit_if_capable) + enum shm_bit_depth desired_bit_depth) { pixman_format_code_t pixman_fmt_without_alpha = PIXMAN_x8r8g8b8; enum wl_shm_format shm_fmt_without_alpha = WL_SHM_FORMAT_XRGB8888; @@ -982,8 +982,7 @@ shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances, static bool have_logged = false; - - if (ten_bit_if_capable) { + if (desired_bit_depth == SHM_BITS_10) { if (wayl->shm_have_argb2101010 && wayl->shm_have_xrgb2101010) { pixman_fmt_without_alpha = PIXMAN_x2r10g10b10; shm_fmt_without_alpha = WL_SHM_FORMAT_XRGB2101010; @@ -1058,3 +1057,13 @@ shm_chain_free(struct buffer_chain *chain) free(chain); } + +enum shm_bit_depth +shm_chain_bit_depth(const struct buffer_chain *chain) +{ + const pixman_format_code_t fmt = chain->pixman_fmt_with_alpha; + + return (fmt == PIXMAN_a2r10g10b10 || fmt == PIXMAN_a2b10g10r10) + ? SHM_BITS_10 + : SHM_BITS_8; +} diff --git a/shm.h b/shm.h index 2af185c9..8f8c406a 100644 --- a/shm.h +++ b/shm.h @@ -9,6 +9,7 @@ #include +#include "config.h" #include "wayland.h" struct damage; @@ -46,9 +47,11 @@ void shm_set_max_pool_size(off_t max_pool_size); struct buffer_chain; struct buffer_chain *shm_chain_new( struct wayland *wayl, bool scrollable, size_t pix_instances, - bool ten_bit_it_if_capable); + enum shm_bit_depth desired_bit_depth); void shm_chain_free(struct buffer_chain *chain); +enum shm_bit_depth shm_chain_bit_depth(const struct buffer_chain *chain); + /* * Returns a single buffer. * diff --git a/sixel.c b/sixel.c index dd933d7a..680c258f 100644 --- a/sixel.c +++ b/sixel.c @@ -110,10 +110,10 @@ sixel_init(struct terminal *term, int p1, int p2, int p3) term->sixel.image.height = 0; term->sixel.image.alloc_height = 0; term->sixel.image.bottom_pixel = 0; - term->sixel.linear_blending = render_do_linear_blending(term); + term->sixel.linear_blending = wayl_do_linear_blending(term->wl, term->conf); term->sixel.pixman_fmt = PIXMAN_a8r8g8b8; - if (term->conf->tweak.surface_bit_depth == SHM_10_BIT) { + if (term->conf->tweak.surface_bit_depth == SHM_BITS_10) { if (term->wl->shm_have_argb2101010 && term->wl->shm_have_xrgb2101010) { term->sixel.use_10bit = true; term->sixel.pixman_fmt = PIXMAN_a2r10g10b10; diff --git a/terminal.c b/terminal.c index d25516cb..793a1616 100644 --- a/terminal.c +++ b/terminal.c @@ -1073,19 +1073,16 @@ reload_fonts(struct terminal *term, bool resize_grid) options->scaling_filter = conf->tweak.fcft_filter; options->color_glyphs.format = PIXMAN_a8r8g8b8; - options->color_glyphs.srgb_decode = render_do_linear_blending(term); + options->color_glyphs.srgb_decode = + wayl_do_linear_blending(term->wl, term->conf); - if (conf->tweak.surface_bit_depth == SHM_10_BIT) { - if ((term->wl->shm_have_argb2101010 && term->wl->shm_have_xrgb2101010) || - (term->wl->shm_have_abgr2101010 && term->wl->shm_have_xbgr2101010)) - { - /* - * Use a high-res buffer type for emojis. We don't want to - * use an a2r10g0b10 type of surface, since we need more - * than 2 bits for alpha. - */ - options->color_glyphs.format = PIXMAN_rgba_float; - } + if (shm_chain_bit_depth(term->render.chains.grid) >= SHM_BITS_10) { + /* + * Use a high-res buffer type for emojis. We don't want to use + * an a2r10g0b10 type of surface, since we need more than 2 + * bits for alpha. + */ + options->color_glyphs.format = PIXMAN_rgba_float; } struct fcft_font *fonts[4]; @@ -1260,7 +1257,10 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, goto err; } - const bool ten_bit_surfaces = conf->tweak.surface_bit_depth == SHM_10_BIT; + const enum shm_bit_depth desired_bit_depth = + conf->tweak.surface_bit_depth == SHM_BITS_AUTO + ? wayl_do_linear_blending(wayl, conf) ? SHM_BITS_10 : SHM_BITS_8 + : conf->tweak.surface_bit_depth; const struct color_theme *theme = NULL; switch (conf->initial_color_theme) { @@ -1353,13 +1353,13 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, .render = { .chains = { .grid = shm_chain_new(wayl, true, 1 + conf->render_worker_count, - ten_bit_surfaces), - .search = shm_chain_new(wayl, false, 1 ,ten_bit_surfaces), - .scrollback_indicator = shm_chain_new(wayl, false, 1, ten_bit_surfaces), - .render_timer = shm_chain_new(wayl, false, 1, ten_bit_surfaces), - .url = shm_chain_new(wayl, false, 1, ten_bit_surfaces), - .csd = shm_chain_new(wayl, false, 1, ten_bit_surfaces), - .overlay = shm_chain_new(wayl, false, 1, ten_bit_surfaces), + desired_bit_depth), + .search = shm_chain_new(wayl, false, 1 ,desired_bit_depth), + .scrollback_indicator = shm_chain_new(wayl, false, 1, desired_bit_depth), + .render_timer = shm_chain_new(wayl, false, 1, desired_bit_depth), + .url = shm_chain_new(wayl, false, 1, desired_bit_depth), + .csd = shm_chain_new(wayl, false, 1, desired_bit_depth), + .overlay = shm_chain_new(wayl, false, 1, desired_bit_depth), }, .scrollback_lines = conf->scrollback.lines, .app_sync_updates.timer_fd = app_sync_updates_fd, @@ -1502,7 +1502,7 @@ term_window_configured(struct terminal *term) xassert(term->window->is_configured); fdm_add(term->fdm, term->ptmx, EPOLLIN, &fdm_ptmx, term); - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); LOG_INFO("gamma-correct blending: %s", gamma_correct ? "enabled" : "disabled"); } } diff --git a/wayland.c b/wayland.c index 9b143508..320f03aa 100644 --- a/wayland.c +++ b/wayland.c @@ -2640,3 +2640,10 @@ wayl_activate(struct wayland *wayl, struct wl_window *win, const char *token) xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface.surf); } + +bool +wayl_do_linear_blending(const struct wayland *wayl, const struct config *conf) +{ + return conf->gamma_correct && + wayl->color_management.img_description != NULL; +} diff --git a/wayland.h b/wayland.h index a9d6858c..044b217f 100644 --- a/wayland.h +++ b/wayland.h @@ -26,6 +26,7 @@ #include #include +#include "config.h" #include "cursor-shape.h" #include "fdm.h" @@ -539,3 +540,4 @@ bool wayl_get_activation_token( struct wl_window *win, activation_token_cb_t cb, void *cb_data); void wayl_activate(struct wayland *wayl, struct wl_window *win, const char *token); +bool wayl_do_linear_blending(const struct wayland *wayl, const struct config *conf); From 9ff0151055e4883c588c8ce8c9f683c8b5d475bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 May 2025 10:17:20 +0200 Subject: [PATCH 64/86] changelog: add new 'unreleased' section --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ef3ae43..6bc5b9df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.22.2](#1-22-2) * [1.22.1](#1-22-1) * [1.22.0](#1-22-0) @@ -61,6 +62,16 @@ * [1.2.0](#1-2-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.22.2 ### Changed From 7ced397089fb5724fb2021bea8491f361b289366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 May 2025 08:09:08 +0200 Subject: [PATCH 65/86] config: gamma-correct-blending: disable by default --- CHANGELOG.md | 4 ++++ config.c | 15 +++------------ config.h | 4 +--- doc/foot.ini.5.scd | 2 +- foot.ini | 1 + render.c | 2 +- tests/test-config.c | 1 + wayland.c | 6 ++---- 8 files changed, 14 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bc5b9df..dd6300ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,10 @@ ## Unreleased ### Added ### Changed + +* `gamma-correct-blending` now defaults to `no` instead of `yes`. + + ### Deprecated ### Removed ### Fixed diff --git a/config.c b/config.c index 347cc1ec..c7cf03f9 100644 --- a/config.c +++ b/config.c @@ -1083,17 +1083,8 @@ parse_section_main(struct context *ctx) return true; } - else if (streq(key, "gamma-correct-blending")) { - bool gamma_correct; - if (!value_to_bool(ctx, &gamma_correct)) - return false; - - conf->gamma_correct = - gamma_correct - ? GAMMA_CORRECT_ENABLED - : GAMMA_CORRECT_DISABLED; - return true; - } + else if (streq(key, "gamma-correct-blending")) + return value_to_bool(ctx, &conf->gamma_correct); else { LOG_CONTEXTUAL_ERR("not a valid option: %s", key); @@ -3318,7 +3309,7 @@ config_load(struct config *conf, const char *conf_path, .underline_thickness = {.pt = 0., .px = -1}, .strikeout_thickness = {.pt = 0., .px = -1}, .dpi_aware = false, - .gamma_correct = GAMMA_CORRECT_AUTO, + .gamma_correct = false, .security = { .osc52 = OSC52_ENABLED, }, diff --git a/config.h b/config.h index 2dec82c1..fe7a0331 100644 --- a/config.h +++ b/config.h @@ -168,9 +168,7 @@ struct config { enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode; bool dpi_aware; - enum {GAMMA_CORRECT_DISABLED, - GAMMA_CORRECT_ENABLED, - GAMMA_CORRECT_AUTO} gamma_correct; + bool gamma_correct; struct config_font_list fonts[4]; struct font_size_adjustment font_size_adjustment; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 95e491eb..52a14524 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -227,7 +227,7 @@ empty string to be set, but it must be quoted: *KEY=""*) though. The amount of errors can be reduced by using 10-bit surfaces; see *tweak.surface-bit-depth*. - Default: enabled when compositor support is available + Default: _no_. *box-drawings-uses-font-glyphs* Boolean. When disabled, foot generates box/line drawing characters diff --git a/foot.ini b/foot.ini index 7d96ca0f..2ac0c05e 100644 --- a/foot.ini +++ b/foot.ini @@ -22,6 +22,7 @@ # strikeout-thickness= # box-drawings-uses-font-glyphs=no # dpi-aware=no +# gamma-correct-blending=no # initial-window-size-pixels=700x500 # Or, # initial-window-size-chars= diff --git a/render.c b/render.c index b0d21d18..0ee60d65 100644 --- a/render.c +++ b/render.c @@ -5251,6 +5251,6 @@ render_xcursor_set(struct seat *seat, struct terminal *term, bool render_do_linear_blending(const struct terminal *term) { - return term->conf->gamma_correct != GAMMA_CORRECT_DISABLED && + return term->conf->gamma_correct && term->wl->color_management.img_description != NULL; } diff --git a/tests/test-config.c b/tests/test-config.c index 69d349b4..99398fec 100644 --- a/tests/test-config.c +++ b/tests/test-config.c @@ -468,6 +468,7 @@ test_section_main(void) test_boolean(&ctx, &parse_section_main, "box-drawings-uses-font-glyphs", &conf.box_drawings_uses_font_glyphs); test_boolean(&ctx, &parse_section_main, "locked-title", &conf.locked_title); test_boolean(&ctx, &parse_section_main, "dpi-aware", &conf.dpi_aware); + test_boolean(&ctx, &parse_section_main, "gamma-correct-blending", &conf.gamma_correct); test_pt_or_px(&ctx, &parse_section_main, "font-size-adjustment", &conf.font_size_adjustment.pt_or_px); /* TODO: test ‘N%’ values too */ test_pt_or_px(&ctx, &parse_section_main, "line-height", &conf.line_height); diff --git a/wayland.c b/wayland.c index 853124be..9b143508 100644 --- a/wayland.c +++ b/wayland.c @@ -1980,7 +1980,7 @@ wayl_win_init(struct terminal *term, const char *token) xdg_toplevel_icon_v1_destroy(icon); } - if (term->conf->gamma_correct != GAMMA_CORRECT_DISABLED) { + if (term->conf->gamma_correct) { if (wayl->color_management.img_description != NULL) { xassert(wayl->color_management.manager != NULL); @@ -1990,7 +1990,7 @@ wayl_win_init(struct terminal *term, const char *token) wp_color_management_surface_v1_set_image_description( win->surface.color_management, wayl->color_management.img_description, WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL); - } else if (term->conf->gamma_correct == GAMMA_CORRECT_ENABLED) { + } else { if (wayl->color_management.manager == NULL) { LOG_WARN( "gamma-corrected-blending: disabling; " @@ -2005,8 +2005,6 @@ wayl_win_init(struct terminal *term, const char *token) LOG_WARN(" - TF: ext_linear"); LOG_WARN(" - primaries: sRGB"); } - } else { - /* "auto" - don't warn */ } } From 2a8948a3f32eaa6894457dcbf5aced57bdd91853 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 May 2025 08:34:49 +0200 Subject: [PATCH 66/86] config: tweak.surface-bit-depth now defaults to 'auto' When set to 'auto', use 10-bit surfaces if gamma-correct blending is enabled, and 8-bit surfaces otherwise. Note that we may still fallback to 8-bit surfaces (without disabling gamma-correct blending) if the compositor does not support 10-bit surfaces. Closes #2082 --- CHANGELOG.md | 13 +++++++++++++ config.c | 4 ++-- config.h | 8 +++++++- doc/foot.ini.5.scd | 41 ++++++++++++++++++++++------------------- pgo/pgo.c | 5 +++-- render.c | 31 +++++++++++++------------------ render.h | 2 -- shm.c | 15 ++++++++++++--- shm.h | 5 ++++- sixel.c | 4 ++-- terminal.c | 42 +++++++++++++++++++++--------------------- wayland.c | 7 +++++++ wayland.h | 2 ++ 13 files changed, 108 insertions(+), 71 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd6300ae..9ea93304 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,14 +64,27 @@ ## Unreleased ### Added + +* `auto` to the `tweak.surface-bit-depth` option. + + ### Changed * `gamma-correct-blending` now defaults to `no` instead of `yes`. +* `tweak.surface-bit-depth` default value changed to `auto`; uses + 10-bit surfaces when `gamma-correct-blending=yes`, and 8-bit + surfaces otherwise. ### Deprecated ### Removed ### Fixed + +* Inaccurate colors when `gamma-correct-blending=yes` ([#2082][2082]). + +[2082]: https://codeberg.org/dnkl/foot/issues/2082 + + ### Security ### Contributors diff --git a/config.c b/config.c index c7cf03f9..3f7ffa28 100644 --- a/config.c +++ b/config.c @@ -2771,7 +2771,7 @@ parse_section_tweak(struct context *ctx) return value_to_enum( ctx, - (const char *[]){"8-bit", "10-bit", NULL}, + (const char *[]){"auto", "8-bit", "10-bit", NULL}, (int *)&conf->tweak.surface_bit_depth); } @@ -3419,7 +3419,7 @@ config_load(struct config *conf, const char *conf_path, .box_drawing_solid_shades = true, .font_monospace_warn = true, .sixel = true, - .surface_bit_depth = 8, + .surface_bit_depth = SHM_BITS_AUTO, }, .touch = { diff --git a/config.h b/config.h index fe7a0331..ea0160bf 100644 --- a/config.h +++ b/config.h @@ -131,6 +131,12 @@ struct custom_regex { struct config_spawn_template launch; }; +enum shm_bit_depth { + SHM_BITS_AUTO, + SHM_BITS_8, + SHM_BITS_10 +}; + struct config { char *term; char *shell; @@ -408,7 +414,7 @@ struct config { bool box_drawing_solid_shades; bool font_monospace_warn; bool sixel; - enum { SHM_8_BIT, SHM_10_BIT } surface_bit_depth; + enum shm_bit_depth surface_bit_depth; } tweak; struct { diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 52a14524..650242a2 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -220,12 +220,13 @@ empty string to be set, but it must be quoted: *KEY=""*) than intended when rendered with gamma-correct blending, since the font designer set the font weight based on incorrect rendering. - Note that some colors (especially dark ones) may be slightly - off. The reason for this is loss of color precision, due to foot - using 8-bit surfaces (i.e. each color channel is 8 bits). In all - known cases, the difference is small enough not to be noticed - though. The amount of errors can be reduced by using 10-bit - surfaces; see *tweak.surface-bit-depth*. + In order to represent colors faithfully, higher precision image + buffers are required. By default, foot will use 10-bit color + channels, if available, when gamma-correct blending is + enabled. However, the high precision buffers are slow; if you want + to use gamma-correct blending, but prefer speed (throughput and + input latency) over accurate colors, you can force 8-bit color + channels by setting *tweak.surface-bit-depth=8-bit*. Default: _no_. @@ -1974,23 +1975,25 @@ any of these options. *surface-bit-depth* Selects which RGB bit depth to use for image buffers. One of - *8-bit*, or *10-bit*. + *auto*, *8-bit*, or *10-bit*. - The default, *8-bit*, uses 8 bits for all channels, alpha - included. When *gamma-correct-blending* is disabled, this is the - best option. + *auto* chooses bit depth depending on other settings, and + availability. - When *gamma-correct-blending* is enabled, you may want to enable - 10-bit surfaces, as that improves color precision. Be aware - however, that in this mode, the alpha channel is only 2 bits - instead of 8 bits. Thus, if you are using a transparent - background, you may want to use the default, *8-bit*, even if you - have gamma-correct blending enabled. + *8-bit*, uses 8 bits for each color channel, alpha included. This + is the default when *gamma-correct-blending=no*. - You should also note that 10-bit surface is much slower. This will - increase input latency and decrease rendering throughput. + *10-bit* uses 10 bits for each RGB channel, and 2 bits for the + alpha channel. Thus, it provides higher precision color channels, + but a lower precision alpha channel. It is the default when + *gamma-correct-blending=yes*, if supported by the compositor. - Default: _8-bit_ + Note that *10-bit* is much slower than *8-bit*; if you want to use + gamma-correct blending, and if you prefer speed (throughput and + input latency) over accurate colors, you can set + *surface-bit-depth=8-bit* explicitly. + + Default: _auto_ # SEE ALSO diff --git a/pgo/pgo.c b/pgo/pgo.c index 8a4967ba..757dcd06 100644 --- a/pgo/pgo.c +++ b/pgo/pgo.c @@ -129,7 +129,7 @@ render_worker_thread(void *_ctx) } bool -render_do_linear_blending(const struct terminal *term) +wayl_do_linear_blending(const struct wayland *wayl, const struct config *conf) { return false; } @@ -201,11 +201,12 @@ void urls_reset(struct terminal *term) {} void shm_unref(struct buffer *buf) {} void shm_chain_free(struct buffer_chain *chain) {} +enum shm_bit_depth shm_chain_bit_depth(const struct buffer_chain *chain) { return SHM_BITS_8; } struct buffer_chain * shm_chain_new( struct wayland *wayl, bool scrollable, size_t pix_instances, - bool ten_bit_it_if_capable) + enum shm_bit_depth desired_bit_depth) { return NULL; } diff --git a/render.c b/render.c index 0ee60d65..55c2ec4d 100644 --- a/render.c +++ b/render.c @@ -626,7 +626,7 @@ draw_cursor(const struct terminal *term, const struct cell *cell, pixman_color_t cursor_color; pixman_color_t text_color; cursor_colors_for_cell(term, cell, fg, bg, &cursor_color, &text_color, - render_do_linear_blending(term)); + wayl_do_linear_blending(term->wl, term->conf)); if (unlikely(!term->kbd_focus)) { switch (term->conf->cursor.unfocused_style) { @@ -820,7 +820,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, if (cell->attrs.blink && term->blink.state == BLINK_OFF) _fg = color_blend_towards(_fg, 0x00000000, term->conf->dim.amount); - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); pixman_color_t fg = color_hex_to_pixman(_fg, gamma_correct); pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha, gamma_correct); @@ -1180,7 +1180,8 @@ static void render_urgency(struct terminal *term, struct buffer *buf) { uint32_t red = term->colors.table[1]; - pixman_color_t bg = color_hex_to_pixman(red, render_do_linear_blending(term)); + pixman_color_t bg = color_hex_to_pixman( + red, wayl_do_linear_blending(term->wl, term->conf)); int width = min(min(term->margins.left, term->margins.right), min(term->margins.top, term->margins.bottom)); @@ -1211,7 +1212,7 @@ render_margin(struct terminal *term, struct buffer *buf, const int bmargin = term->height - term->margins.bottom; const int line_count = end_line - start_line; - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); const uint32_t _bg = !term->reverse ? term->colors.bg : term->colors.fg; uint16_t alpha = term->colors.alpha; @@ -1699,7 +1700,7 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat, if (unlikely(term->is_searching)) return; - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); /* Adjust cursor position to viewport */ struct coord cursor; @@ -1970,7 +1971,8 @@ render_overlay(struct terminal *term) case OVERLAY_FLASH: color = color_hex_to_pixman_with_alpha( term->conf->colors.flash, - term->conf->colors.flash_alpha, render_do_linear_blending(term)); + term->conf->colors.flash_alpha, + wayl_do_linear_blending(term->wl, term->conf)); break; case OVERLAY_NONE: @@ -2312,7 +2314,7 @@ render_osd(struct terminal *term, const struct wayl_sub_surface *sub_surf, pixman_image_set_clip_region32(buf->pix[0], &clip); pixman_region32_fini(&clip); - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); uint16_t alpha = _bg >> 24 | (_bg >> 24 << 8); pixman_color_t bg = color_hex_to_pixman_with_alpha(_bg, alpha, gamma_correct); pixman_image_fill_rectangles( @@ -2453,7 +2455,7 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx, if (info->width == 0 || info->height == 0) return; - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); { /* Fully transparent - no need to do a color space transform */ @@ -2542,7 +2544,7 @@ get_csd_button_fg_color(const struct terminal *term) } return color_hex_to_pixman_with_alpha( - _color, alpha, render_do_linear_blending(term)); + _color, alpha, wayl_do_linear_blending(term->wl, term->conf)); } static void @@ -2819,7 +2821,7 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx, if (!term->visual_focus) _color = color_dim(term, _color); - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); pixman_color_t color = color_hex_to_pixman_with_alpha(_color, alpha, gamma_correct); render_csd_part(term, surf->surf, buf, info->width, info->height, &color); @@ -3678,7 +3680,7 @@ render_search_box(struct terminal *term) : term->conf->colors.use_custom.search_box_no_match; /* Background - yellow on empty/match, red on mismatch (default) */ - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); const pixman_color_t color = color_hex_to_pixman( is_match ? (custom_colors @@ -5247,10 +5249,3 @@ render_xcursor_set(struct seat *seat, struct terminal *term, seat->pointer.xcursor_pending = true; return true; } - -bool -render_do_linear_blending(const struct terminal *term) -{ - return term->conf->gamma_correct && - term->wl->color_management.img_description != NULL; -} diff --git a/render.h b/render.h index c7b8e4a5..81d2a905 100644 --- a/render.h +++ b/render.h @@ -47,5 +47,3 @@ struct csd_data { }; struct csd_data get_csd_data(const struct terminal *term, enum csd_surface surf_idx); - -bool render_do_linear_blending(const struct terminal *term); diff --git a/shm.c b/shm.c index 32e6bdd0..38944020 100644 --- a/shm.c +++ b/shm.c @@ -972,7 +972,7 @@ shm_unref(struct buffer *_buf) struct buffer_chain * shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances, - bool ten_bit_if_capable) + enum shm_bit_depth desired_bit_depth) { pixman_format_code_t pixman_fmt_without_alpha = PIXMAN_x8r8g8b8; enum wl_shm_format shm_fmt_without_alpha = WL_SHM_FORMAT_XRGB8888; @@ -982,8 +982,7 @@ shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances, static bool have_logged = false; - - if (ten_bit_if_capable) { + if (desired_bit_depth == SHM_BITS_10) { if (wayl->shm_have_argb2101010 && wayl->shm_have_xrgb2101010) { pixman_fmt_without_alpha = PIXMAN_x2r10g10b10; shm_fmt_without_alpha = WL_SHM_FORMAT_XRGB2101010; @@ -1058,3 +1057,13 @@ shm_chain_free(struct buffer_chain *chain) free(chain); } + +enum shm_bit_depth +shm_chain_bit_depth(const struct buffer_chain *chain) +{ + const pixman_format_code_t fmt = chain->pixman_fmt_with_alpha; + + return (fmt == PIXMAN_a2r10g10b10 || fmt == PIXMAN_a2b10g10r10) + ? SHM_BITS_10 + : SHM_BITS_8; +} diff --git a/shm.h b/shm.h index 2af185c9..8f8c406a 100644 --- a/shm.h +++ b/shm.h @@ -9,6 +9,7 @@ #include +#include "config.h" #include "wayland.h" struct damage; @@ -46,9 +47,11 @@ void shm_set_max_pool_size(off_t max_pool_size); struct buffer_chain; struct buffer_chain *shm_chain_new( struct wayland *wayl, bool scrollable, size_t pix_instances, - bool ten_bit_it_if_capable); + enum shm_bit_depth desired_bit_depth); void shm_chain_free(struct buffer_chain *chain); +enum shm_bit_depth shm_chain_bit_depth(const struct buffer_chain *chain); + /* * Returns a single buffer. * diff --git a/sixel.c b/sixel.c index dd933d7a..680c258f 100644 --- a/sixel.c +++ b/sixel.c @@ -110,10 +110,10 @@ sixel_init(struct terminal *term, int p1, int p2, int p3) term->sixel.image.height = 0; term->sixel.image.alloc_height = 0; term->sixel.image.bottom_pixel = 0; - term->sixel.linear_blending = render_do_linear_blending(term); + term->sixel.linear_blending = wayl_do_linear_blending(term->wl, term->conf); term->sixel.pixman_fmt = PIXMAN_a8r8g8b8; - if (term->conf->tweak.surface_bit_depth == SHM_10_BIT) { + if (term->conf->tweak.surface_bit_depth == SHM_BITS_10) { if (term->wl->shm_have_argb2101010 && term->wl->shm_have_xrgb2101010) { term->sixel.use_10bit = true; term->sixel.pixman_fmt = PIXMAN_a2r10g10b10; diff --git a/terminal.c b/terminal.c index f2d03e77..1ebe067e 100644 --- a/terminal.c +++ b/terminal.c @@ -1073,19 +1073,16 @@ reload_fonts(struct terminal *term, bool resize_grid) options->scaling_filter = conf->tweak.fcft_filter; options->color_glyphs.format = PIXMAN_a8r8g8b8; - options->color_glyphs.srgb_decode = render_do_linear_blending(term); + options->color_glyphs.srgb_decode = + wayl_do_linear_blending(term->wl, term->conf); - if (conf->tweak.surface_bit_depth == SHM_10_BIT) { - if ((term->wl->shm_have_argb2101010 && term->wl->shm_have_xrgb2101010) || - (term->wl->shm_have_abgr2101010 && term->wl->shm_have_xbgr2101010)) - { - /* - * Use a high-res buffer type for emojis. We don't want to - * use an a2r10g0b10 type of surface, since we need more - * than 2 bits for alpha. - */ - options->color_glyphs.format = PIXMAN_rgba_float; - } + if (shm_chain_bit_depth(term->render.chains.grid) >= SHM_BITS_10) { + /* + * Use a high-res buffer type for emojis. We don't want to use + * an a2r10g0b10 type of surface, since we need more than 2 + * bits for alpha. + */ + options->color_glyphs.format = PIXMAN_rgba_float; } struct fcft_font *fonts[4]; @@ -1260,7 +1257,10 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, goto err; } - const bool ten_bit_surfaces = conf->tweak.surface_bit_depth == SHM_10_BIT; + const enum shm_bit_depth desired_bit_depth = + conf->tweak.surface_bit_depth == SHM_BITS_AUTO + ? wayl_do_linear_blending(wayl, conf) ? SHM_BITS_10 : SHM_BITS_8 + : conf->tweak.surface_bit_depth; /* Initialize configure-based terminal attributes */ *term = (struct terminal) { @@ -1346,13 +1346,13 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, .render = { .chains = { .grid = shm_chain_new(wayl, true, 1 + conf->render_worker_count, - ten_bit_surfaces), - .search = shm_chain_new(wayl, false, 1 ,ten_bit_surfaces), - .scrollback_indicator = shm_chain_new(wayl, false, 1, ten_bit_surfaces), - .render_timer = shm_chain_new(wayl, false, 1, ten_bit_surfaces), - .url = shm_chain_new(wayl, false, 1, ten_bit_surfaces), - .csd = shm_chain_new(wayl, false, 1, ten_bit_surfaces), - .overlay = shm_chain_new(wayl, false, 1, ten_bit_surfaces), + desired_bit_depth), + .search = shm_chain_new(wayl, false, 1 ,desired_bit_depth), + .scrollback_indicator = shm_chain_new(wayl, false, 1, desired_bit_depth), + .render_timer = shm_chain_new(wayl, false, 1, desired_bit_depth), + .url = shm_chain_new(wayl, false, 1, desired_bit_depth), + .csd = shm_chain_new(wayl, false, 1, desired_bit_depth), + .overlay = shm_chain_new(wayl, false, 1, desired_bit_depth), }, .scrollback_lines = conf->scrollback.lines, .app_sync_updates.timer_fd = app_sync_updates_fd, @@ -1495,7 +1495,7 @@ term_window_configured(struct terminal *term) xassert(term->window->is_configured); fdm_add(term->fdm, term->ptmx, EPOLLIN, &fdm_ptmx, term); - const bool gamma_correct = render_do_linear_blending(term); + const bool gamma_correct = wayl_do_linear_blending(term->wl, term->conf); LOG_INFO("gamma-correct blending: %s", gamma_correct ? "enabled" : "disabled"); } } diff --git a/wayland.c b/wayland.c index 9b143508..320f03aa 100644 --- a/wayland.c +++ b/wayland.c @@ -2640,3 +2640,10 @@ wayl_activate(struct wayland *wayl, struct wl_window *win, const char *token) xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface.surf); } + +bool +wayl_do_linear_blending(const struct wayland *wayl, const struct config *conf) +{ + return conf->gamma_correct && + wayl->color_management.img_description != NULL; +} diff --git a/wayland.h b/wayland.h index a9d6858c..044b217f 100644 --- a/wayland.h +++ b/wayland.h @@ -26,6 +26,7 @@ #include #include +#include "config.h" #include "cursor-shape.h" #include "fdm.h" @@ -539,3 +540,4 @@ bool wayl_get_activation_token( struct wl_window *win, activation_token_cb_t cb, void *cb_data); void wayl_activate(struct wayland *wayl, struct wl_window *win, const char *token); +bool wayl_do_linear_blending(const struct wayland *wayl, const struct config *conf); From acea863fbef16bdd72e14f8fdbbf51db49bf0fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 May 2025 10:20:22 +0200 Subject: [PATCH 67/86] changelog: prepare for 1.22.3 --- CHANGELOG.md | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ea93304..0f08c500 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* [Unreleased](#unreleased) +* [1.22.3](#1-22-3) * [1.22.2](#1-22-2) * [1.22.1](#1-22-1) * [1.22.0](#1-22-0) @@ -62,7 +62,8 @@ * [1.2.0](#1-2-0) -## Unreleased +## 1.22.3 + ### Added * `auto` to the `tweak.surface-bit-depth` option. @@ -76,8 +77,6 @@ surfaces otherwise. -### Deprecated -### Removed ### Fixed * Inaccurate colors when `gamma-correct-blending=yes` ([#2082][2082]). @@ -85,10 +84,6 @@ [2082]: https://codeberg.org/dnkl/foot/issues/2082 -### Security -### Contributors - - ## 1.22.2 ### Changed From 85c81042d2b16094677ccb383dd527a0df52e755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 May 2025 10:20:38 +0200 Subject: [PATCH 68/86] meson: bump version to 1.22.3 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index b3163586..4bf4993c 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('foot', 'c', - version: '1.22.2', + version: '1.22.3', license: 'MIT', meson_version: '>=0.59.0', default_options: [ From 0ea572dc63083e0d415382da2df8050a70f8cd07 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Mon, 28 Apr 2025 19:56:32 -0400 Subject: [PATCH 69/86] Paste URL/regex selection to prompt if key is uppercase. In copy-regex/show-urls-copy mode, if the last input character was uppercase, copy the selection to the clipboard _and_ paste it. This is useful for taking a file path from a command output:(log, git, test failure, etc.) and using it in another command. This is inspired by the behavior of copy mode in wezterm: https://wezterm.org/quickselect.html I could have made it check every character in the hint, but it seemed fine to assume that if the last character was uppercase, the user wanted this behavior. Closes #1975. --- CHANGELOG.md | 3 +++ doc/foot.ini.5.scd | 9 ++++++--- url-mode.c | 14 ++++++++++++-- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfe3b8bc..533f601c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -85,8 +85,11 @@ - paper-color - selenized - solarized +* `regex-copy`/`show-urls-copy` will copy and paste the selected text if the hint + is completed with an uppercase character ([#1975][1975]). [2025]: https://codeberg.org/dnkl/foot/issues/2025 +[1975]: https://codeberg.org/dnkl/foot/issues/1975 ### Changed diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index b9ab9c6a..3e70074e 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -1358,7 +1358,8 @@ e.g. *search-start=none*. *show-urls-copy* Enter URL mode, where all currently visible URLs are tagged with a jump label with a key sequence that will place the URL in the - clipboard. Default: _none_. + clipboard. If the hint is completed with an uppercase character, + the match will also be pasted. Default: _none_. *regex-launch* Enter regex mode. This works exactly the same as URL mode; all @@ -1381,8 +1382,10 @@ e.g. *search-start=none*. Default: _none_. *regex-copy* - Same as *regex-copy*, but the match is placed in the clipboard, - instead of "launched", upon activation. Default: _none_. + Same as *regex-launch*, but the match is placed in the clipboard, + instead of "launched", upon activation. If the hint is completed + with an uppercase character, the match will also be pasted. + Default: _none_. *prompt-prev* Jump to the previous, currently not visible, prompt (requires diff --git a/url-mode.c b/url-mode.c index d0f7fc53..199ff3f1 100644 --- a/url-mode.c +++ b/url-mode.c @@ -131,7 +131,7 @@ spawn_url_launcher(struct seat *seat, struct terminal *term, const char *url, static void activate_url(struct seat *seat, struct terminal *term, const struct url *url, - uint32_t serial) + uint32_t serial, bool paste_url_to_self) { char *url_string = NULL; @@ -159,6 +159,15 @@ activate_url(struct seat *seat, struct terminal *term, const struct url *url, switch (url->action) { case URL_ACTION_COPY: + if (paste_url_to_self) { + if (term->bracketed_paste) + term_to_slave(term, "\033[200~", 6); + + term_to_slave(term, url_string, strlen(url_string)); + + if (term->bracketed_paste) + term_to_slave(term, "\033[201~", 6); + } if (text_to_clipboard(seat, term, url_string, seat->kbd.serial)) { /* Now owned by our clipboard “manager” */ url_string = NULL; @@ -273,7 +282,8 @@ urls_input(struct seat *seat, struct terminal *term, } if (match) { - activate_url(seat, term, match, serial); + // If the last hint character was uppercase, copy and paste + activate_url(seat, term, match, serial, wc == toc32upper(wc)); switch (match->action) { case URL_ACTION_COPY: From 237db6e771293689d6739de5aac7358d35dce5d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 2 May 2025 13:43:59 +0200 Subject: [PATCH 70/86] wayland: always call wl_display_dispatch_pending() at least once, after reading This fixes an issue where protocol errors aren't reported. I'm guessing the read succeeds, but that prepare_read() _also_ succeeds immediately, since there aren't any events to dispatch (only log the protocol error). By calling dispatch unconditionally, we ensure any error messages are printed. Then we proceed to loop prepare_read() + dispatch_pending() until the queue is empty. --- wayland.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wayland.c b/wayland.c index 320f03aa..a41b5060 100644 --- a/wayland.c +++ b/wayland.c @@ -1650,6 +1650,8 @@ fdm_wayl(struct fdm *fdm, int fd, int events, void *data) return false; } + wl_display_dispatch_pending(wayl->display); + while (wl_display_prepare_read(wayl->display) != 0) { if (wl_display_dispatch_pending(wayl->display) < 0) { LOG_ERRNO("failed to dispatch pending Wayland events"); From 5080e271c2fa5899ccb37384c26491e025b225ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 2 May 2025 13:46:18 +0200 Subject: [PATCH 71/86] wayland: attempt to log protocol errors on failure to flush When failing to flush, and the error is EPIPE, attempt to read and dispatch events. This ensures protocol errors are logged. --- wayland.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wayland.c b/wayland.c index a41b5060..08994202 100644 --- a/wayland.c +++ b/wayland.c @@ -2247,7 +2247,14 @@ wayl_flush(struct wayland *wayl) } if (errno != EAGAIN) { - LOG_ERRNO("failed to flush wayland socket"); + const int saved_errno = errno; + + if (errno == EPIPE) { + wl_display_read_events(wayl->display); + wl_display_dispatch_pending(wayl->display); + } + + LOG_ERRNO_P(saved_errno, "failed to flush wayland socket"); return; } From 7354b94f737c9f589803b23714e7eddfb2f0fd69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 2 May 2025 08:53:43 +0200 Subject: [PATCH 72/86] osc: restore configured alpha if OSC-11 has no alpha value When parsing an OSC-11 without an alpha value (i.e. standard OSC-11, not rxvt's extended variant), restore the alpha value from the configuration, rather than keeping whatever the current alpha is. --- CHANGELOG.md | 3 +++ osc.c | 21 +++++++++++++-------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 533f601c..54ad3a25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,9 @@ ### Changed * `cursor.color` moved to `colors.cursor`. +* OSC-11 without an alpha value will now restore the configured + (i.e. from `foot.ini`) alpha, rather than keeping whatever the + current alpha value is, unchanged. ### Deprecated diff --git a/osc.c b/osc.c index 7e3e6376..78f335e1 100644 --- a/osc.c +++ b/osc.c @@ -1455,15 +1455,20 @@ osc_dispatch(struct terminal *term) case 11: term->colors.bg = color; - if (have_alpha) { - const bool changed = term->colors.alpha != alpha; - term->colors.alpha = alpha; - - if (changed) { - wayl_win_alpha_changed(term->window); - term_font_subpixel_changed(term); - } + if (!have_alpha) { + alpha = term->colors.active_theme == COLOR_THEME1 + ? term->conf->colors.alpha + : term->conf->colors2.alpha; } + + const bool changed = term->colors.alpha != alpha; + term->colors.alpha = alpha; + + if (changed) { + wayl_win_alpha_changed(term->window); + term_font_subpixel_changed(term); + } + term_damage_color(term, COLOR_DEFAULT, 0); term_damage_margins(term); break; From 970e13db8deebf6c7542c7aede4f34703fa86769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 1 May 2025 09:37:47 +0200 Subject: [PATCH 73/86] config: tweak.surface-bit-depth: add support for 16-bit surfaces This adds supports for 16-bit surfaces, using the new PIXMAN_a16b16g16r16 buffer format. This maps to WL_SHM_FORMAT_ABGR16161616 (little-endian). Use the new 16-bit surfaces by default, when gamma-correct-blending=yes. --- CHANGELOG.md | 5 +++++ config.c | 9 +++++++- config.h | 3 ++- doc/foot.ini.5.scd | 29 +++++++++++++------------ meson.build | 4 ++++ shm.c | 53 +++++++++++++++++++++++++++++++++++++++------- sixel.c | 13 +++++++++++- terminal.c | 6 +++++- wayland.c | 2 ++ wayland.h | 2 ++ 10 files changed, 101 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54ad3a25..1a9917b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,9 @@ - solarized * `regex-copy`/`show-urls-copy` will copy and paste the selected text if the hint is completed with an uppercase character ([#1975][1975]). +* `16-bit` to `tweak.surface-bit-depth`. Makes foot use 16-bit image + buffers. They provide the necessary color precision required by + `gamma-correct-blending=yes`. [2025]: https://codeberg.org/dnkl/foot/issues/2025 [1975]: https://codeberg.org/dnkl/foot/issues/1975 @@ -98,6 +101,8 @@ * OSC-11 without an alpha value will now restore the configured (i.e. from `foot.ini`) alpha, rather than keeping whatever the current alpha value is, unchanged. +* `gamma-correct-blending=yes` now defaults to `16-bit` image buffers, + instead of `10-bit`. ### Deprecated diff --git a/config.c b/config.c index 64e45135..d0aae6a5 100644 --- a/config.c +++ b/config.c @@ -2809,12 +2809,19 @@ parse_section_tweak(struct context *ctx) else if (streq(key, "surface-bit-depth")) { _Static_assert(sizeof(conf->tweak.surface_bit_depth) == sizeof(int), - "enum is not 32-bit"); + "enum is not 32-bit"); +#if defined(HAVE_PIXMAN_RGBA_16) + return value_to_enum( + ctx, + (const char *[]){"auto", "8-bit", "10-bit", "16-bit", NULL}, + (int *)&conf->tweak.surface_bit_depth); +#else return value_to_enum( ctx, (const char *[]){"auto", "8-bit", "10-bit", NULL}, (int *)&conf->tweak.surface_bit_depth); +#endif } else { diff --git a/config.h b/config.h index 80081906..197b67cd 100644 --- a/config.h +++ b/config.h @@ -198,7 +198,8 @@ enum which_color_theme { enum shm_bit_depth { SHM_BITS_AUTO, SHM_BITS_8, - SHM_BITS_10 + SHM_BITS_10, + SHM_BITS_16, }; struct config { diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 3e70074e..c1847932 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -207,7 +207,7 @@ empty string to be set, but it must be quoted: *KEY=""*) Compared to the default (disabled), bright glyphs on a dark background will appear thicker, and dark glyphs on a light background will appear thinner. - + FreeType can limit the effect of the latter, with a technique called stem darkening. It is only available for CFF fonts (OpenType, .otf) and disabled by default (in FreeType). You can @@ -221,12 +221,13 @@ empty string to be set, but it must be quoted: *KEY=""*) font designer set the font weight based on incorrect rendering. In order to represent colors faithfully, higher precision image - buffers are required. By default, foot will use 10-bit color - channels, if available, when gamma-correct blending is - enabled. However, the high precision buffers are slow; if you want - to use gamma-correct blending, but prefer speed (throughput and - input latency) over accurate colors, you can force 8-bit color - channels by setting *tweak.surface-bit-depth=8-bit*. + buffers are required. By default, foot will use either 16-bit, or + 10-bit color channels, depending on availability, when + gamma-correct blending is enabled. However, the high precision + buffers are slow; if you want to use gamma-correct blending, but + prefer speed (throughput and input latency) over accurate colors, + you can force 8-bit color channels by setting + *tweak.surface-bit-depth=8-bit*. Default: _no_. @@ -2023,7 +2024,7 @@ any of these options. *surface-bit-depth* Selects which RGB bit depth to use for image buffers. One of - *auto*, *8-bit*, or *10-bit*. + *auto*, *8-bit*, *10-bit* or *16-bit*. *auto* chooses bit depth depending on other settings, and availability. @@ -2033,12 +2034,14 @@ any of these options. *10-bit* uses 10 bits for each RGB channel, and 2 bits for the alpha channel. Thus, it provides higher precision color channels, - but a lower precision alpha channel. It is the default when - *gamma-correct-blending=yes*, if supported by the compositor. + but a lower precision alpha channel. - Note that *10-bit* is much slower than *8-bit*; if you want to use - gamma-correct blending, and if you prefer speed (throughput and - input latency) over accurate colors, you can set + *16-bit* 16 bits for each color channel, alpha included. If + available, this is the default when *gamma-correct-blending=yes*. + + Note that both *10-bit* and *16-bit* are much slower than *8-bit*; + if you want to use gamma-correct blending, and if you prefer speed + (throughput and input latency) over accurate colors, you can set *surface-bit-depth=8-bit* explicitly. Default: _auto_ diff --git a/meson.build b/meson.build index 4bf4993c..a884e533 100644 --- a/meson.build +++ b/meson.build @@ -145,6 +145,10 @@ if utf8proc.found() add_project_arguments('-DFOOT_GRAPHEME_CLUSTERING=1', language: 'c') endif +if pixman.version().version_compare('>=0.46.0') + add_project_arguments('-DHAVE_PIXMAN_RGBA_16', language: 'c') +endif + tllist = dependency('tllist', version: '>=1.1.0', fallback: 'tllist') fcft = dependency('fcft', version: ['>=3.3.1', '<4.0.0'], fallback: 'fcft') diff --git a/shm.c b/shm.c index 38944020..b586b504 100644 --- a/shm.c +++ b/shm.c @@ -338,7 +338,10 @@ get_new_buffers(struct buffer_chain *chain, size_t count, size_t total_size = 0; for (size_t i = 0; i < count; i++) { stride[i] = stride_for_format_and_width( - with_alpha ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8, widths[i]); + with_alpha + ? chain->pixman_fmt_with_alpha + : chain->pixman_fmt_without_alpha, + widths[i]); sizes[i] = stride[i] * heights[i]; total_size += sizes[i]; } @@ -981,8 +984,38 @@ shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances, enum wl_shm_format shm_fmt_with_alpha = WL_SHM_FORMAT_ARGB8888; static bool have_logged = false; + static bool have_logged_10_fallback = false; - if (desired_bit_depth == SHM_BITS_10) { +#if defined(HAVE_PIXMAN_RGBA_16) + static bool have_logged_16_fallback = false; + + if (desired_bit_depth == SHM_BITS_16) { + if (wayl->shm_have_abgr161616 && wayl->shm_have_xbgr161616) { + pixman_fmt_without_alpha = PIXMAN_a16b16g16r16; + shm_fmt_without_alpha = WL_SHM_FORMAT_XBGR16161616; + + pixman_fmt_without_alpha = PIXMAN_a16b16g16r16; + shm_fmt_with_alpha = WL_SHM_FORMAT_ABGR16161616; + + if (!have_logged) { + have_logged = true; + LOG_INFO("using 16-bit BGR surfaces"); + } + } else { + if (!have_logged_16_fallback) { + have_logged_16_fallback = true; + + LOG_WARN( + "16-bit surfaces requested, but compositor does not " + "implement ABGR161616+XBGR161616"); + } + } + } +#endif + + if (desired_bit_depth >= SHM_BITS_10 && + pixman_fmt_with_alpha == PIXMAN_a8r8g8b8) + { if (wayl->shm_have_argb2101010 && wayl->shm_have_xrgb2101010) { pixman_fmt_without_alpha = PIXMAN_x2r10g10b10; shm_fmt_without_alpha = WL_SHM_FORMAT_XRGB2101010; @@ -1010,13 +1043,13 @@ shm_chain_new(struct wayland *wayl, bool scrollable, size_t pix_instances, } else { - if (!have_logged) { - have_logged = true; + if (!have_logged_10_fallback) { + have_logged_10_fallback = true; LOG_WARN( "10-bit surfaces requested, but compositor does not " "implement ARGB2101010+XRGB2101010, or " - "ABGR2101010+XBGR2101010. Falling back to 8-bit surfaces"); + "ABGR2101010+XBGR2101010"); } } } else { @@ -1063,7 +1096,11 @@ shm_chain_bit_depth(const struct buffer_chain *chain) { const pixman_format_code_t fmt = chain->pixman_fmt_with_alpha; - return (fmt == PIXMAN_a2r10g10b10 || fmt == PIXMAN_a2b10g10r10) - ? SHM_BITS_10 - : SHM_BITS_8; + return fmt == PIXMAN_a8r8g8b8 + ? SHM_BITS_8 +#if defined(HAVE_PIXMAN_RGBA_16) + : fmt == PIXMAN_a16b16g16r16 + ? SHM_BITS_16 +#endif + : SHM_BITS_10; } diff --git a/sixel.c b/sixel.c index 680c258f..c5ef01a1 100644 --- a/sixel.c +++ b/sixel.c @@ -113,7 +113,18 @@ sixel_init(struct terminal *term, int p1, int p2, int p3) term->sixel.linear_blending = wayl_do_linear_blending(term->wl, term->conf); term->sixel.pixman_fmt = PIXMAN_a8r8g8b8; - if (term->conf->tweak.surface_bit_depth == SHM_BITS_10) { + /* + * Use higher-precision sixel surfaces if we're using + * higher-precision window surfaces. + * + * This is to a) get more accurate colors when doing gamma-correct + * blending, and b) use the same pixman format as the main + * surfaces, for (hopefully) better performance. + * + * For now, don't support 16-bit surfaces (too much sixel logic + * that assumes 32-bit pixels). + */ + if (shm_chain_bit_depth(term->render.chains.grid) >= SHM_BITS_10) { if (term->wl->shm_have_argb2101010 && term->wl->shm_have_xrgb2101010) { term->sixel.use_10bit = true; term->sixel.pixman_fmt = PIXMAN_a2r10g10b10; diff --git a/terminal.c b/terminal.c index 793a1616..f3a4b7d0 100644 --- a/terminal.c +++ b/terminal.c @@ -1082,7 +1082,11 @@ reload_fonts(struct terminal *term, bool resize_grid) * an a2r10g0b10 type of surface, since we need more than 2 * bits for alpha. */ +#if defined(HAVE_PIXMAN_RGBA_16) + options->color_glyphs.format = PIXMAN_a16b16g16r16; +#else options->color_glyphs.format = PIXMAN_rgba_float; +#endif } struct fcft_font *fonts[4]; @@ -1259,7 +1263,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, const enum shm_bit_depth desired_bit_depth = conf->tweak.surface_bit_depth == SHM_BITS_AUTO - ? wayl_do_linear_blending(wayl, conf) ? SHM_BITS_10 : SHM_BITS_8 + ? wayl_do_linear_blending(wayl, conf) ? SHM_BITS_16 : SHM_BITS_8 : conf->tweak.surface_bit_depth; const struct color_theme *theme = NULL; diff --git a/wayland.c b/wayland.c index 08994202..368b3be7 100644 --- a/wayland.c +++ b/wayland.c @@ -244,6 +244,8 @@ shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) case WL_SHM_FORMAT_ARGB2101010: wayl->shm_have_argb2101010 = true; break; case WL_SHM_FORMAT_XBGR2101010: wayl->shm_have_xbgr2101010 = true; break; case WL_SHM_FORMAT_ABGR2101010: wayl->shm_have_abgr2101010 = true; break; + case WL_SHM_FORMAT_XBGR16161616: wayl->shm_have_xbgr161616 = true; break; + case WL_SHM_FORMAT_ABGR16161616: wayl->shm_have_abgr161616 = true; break; } #if defined(_DEBUG) diff --git a/wayland.h b/wayland.h index 044b217f..b7e8e79f 100644 --- a/wayland.h +++ b/wayland.h @@ -496,6 +496,8 @@ struct wayland { bool shm_have_xrgb2101010:1; bool shm_have_abgr2101010:1; bool shm_have_xbgr2101010:1; + bool shm_have_abgr161616:1; + bool shm_have_xbgr161616:1; }; struct wayland *wayl_init( From c6db0bed42d85776e5e74d2f56e355da2962b886 Mon Sep 17 00:00:00 2001 From: Chen Mulong Date: Sat, 3 May 2025 09:31:24 +0800 Subject: [PATCH 74/86] Update catppuccin themes From https://github.com/catppuccin/foot Without the 'cursor.color', those themes have problems with cursor display problems in the zsh vi normal mode. --- themes/catppuccin-frappe | 5 +++++ themes/catppuccin-latte | 5 +++++ themes/catppuccin-macchiato | 5 +++++ themes/catppuccin-mocha | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/themes/catppuccin-frappe b/themes/catppuccin-frappe index 3b2e0131..44bef16c 100644 --- a/themes/catppuccin-frappe +++ b/themes/catppuccin-frappe @@ -23,6 +23,11 @@ bright5=f4b8e4 bright6=81c8be bright7=a5adce +cursor=232634 f2d5cf + +16=ef9f76 +17=f2d5cf + selection-foreground=c6d0f5 selection-background=4f5369 diff --git a/themes/catppuccin-latte b/themes/catppuccin-latte index 8e545f70..d0b90e64 100644 --- a/themes/catppuccin-latte +++ b/themes/catppuccin-latte @@ -23,6 +23,11 @@ bright5=ea76cb bright6=179299 bright7=bcc0cc +cursor=eff1f5 dc8a78 + +16=fe640b +17=dc8a78 + selection-foreground=4c4f69 selection-background=ccced7 diff --git a/themes/catppuccin-macchiato b/themes/catppuccin-macchiato index 50aca7da..ae8adab8 100644 --- a/themes/catppuccin-macchiato +++ b/themes/catppuccin-macchiato @@ -23,6 +23,11 @@ bright5=f5bde6 bright6=8bd5ca bright7=a5adcb +cursor=181926 f4dbd6 + +16=f5a97f +17=f4dbd6 + selection-foreground=cad3f5 selection-background=454a5f diff --git a/themes/catppuccin-mocha b/themes/catppuccin-mocha index 508ca382..d29eb0ec 100644 --- a/themes/catppuccin-mocha +++ b/themes/catppuccin-mocha @@ -23,6 +23,11 @@ bright5=f5c2e7 bright6=94e2d5 bright7=a6adc8 +cursor=11111b f5e0dc + +16=fab387 +17=f5e0dc + selection-foreground=cdd6f4 selection-background=414356 From c037836bbd8415ff0fcd428cbadea3bed7dbe022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 5 May 2025 13:02:04 +0200 Subject: [PATCH 75/86] doc: foot.ini: fix description of dark/light themes --- doc/foot.ini.5.scd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index c1847932..1cc45231 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -1113,8 +1113,8 @@ Note that values are not inherited. That is, if you set a value in inherited by *colors2*. In the context of private mode 2031 (Dark and Light Mode detection), -the primary theme (i.e. the *colors2* section) is considered to be the -light theme (since the default theme is dark). +the alternative theme (i.e. the *colors2* section) is considered to be +the light theme (since the default, the primary theme, is dark). # SECTION: csd From 073b637d4535cd0dfe92ca1ce954b552ae9f1a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 5 May 2025 12:43:02 +0200 Subject: [PATCH 76/86] render: refactor to allow setting only selection bg or fg Before this, we only applied custom selection colors, if *both* the selection bg and fg had been set. Since the options are already split up into two separate options, and since it makes sense to at least be able to keep the foreground colors unchanged (i.e. only setting the selection background), let's allow only having one of the selection colors set. Closes #1846 --- CHANGELOG.md | 4 ++ config.c | 5 --- config.h | 1 - doc/foot.ini.5.scd | 3 +- osc.c | 4 -- render.c | 108 ++++++++++++++++++++++++++------------------- terminal.c | 2 - terminal.h | 1 - 8 files changed, 68 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a9917b5..ea818d53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -103,6 +103,10 @@ current alpha value is, unchanged. * `gamma-correct-blending=yes` now defaults to `16-bit` image buffers, instead of `10-bit`. +* Allow setting either selection background, or selection foreground, + only ([#1846][1846]). + +[1846]: https://codeberg.org/dnkl/foot/issues/1846 ### Deprecated diff --git a/config.c b/config.c index d0aae6a5..07f781d6 100644 --- a/config.c +++ b/config.c @@ -3403,7 +3403,6 @@ config_load(struct config *conf, const char *conf_path, .cursor = 0, }, .use_custom = { - .selection = false, .jump_label = false, .scrollback_indicator = false, .url = false, @@ -3593,10 +3592,6 @@ config_load(struct config *conf, const char *conf_path, if (!config_override_apply(conf, overrides, errors_are_fatal)) ret = !errors_are_fatal; - conf->colors.use_custom.selection = - conf->colors.selection_fg >> 24 == 0 && - conf->colors.selection_bg >> 24 == 0; - if (ret && conf->fonts[0].count == 0) { struct config_font font; if (!config_font_parse("monospace", &font)) { diff --git a/config.h b/config.h index 197b67cd..7cf6f6f5 100644 --- a/config.h +++ b/config.h @@ -180,7 +180,6 @@ struct color_theme { struct { bool cursor:1; - bool selection:1; bool jump_label:1; bool scrollback_indicator:1; bool url:1; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 1cc45231..81b88f64 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -1069,8 +1069,7 @@ dark theme (since the default theme is dark). *selection-foreground*, *selection-background* Foreground (text) and background color to use in selected - text. Note that *both* options must be set, or the default will be - used. Default: _inverse foreground/background_. + text. Default: _inverse foreground/background_. *jump-labels* Two color values specifying the foreground (text) and background diff --git a/osc.c b/osc.c index 78f335e1..d59adc5a 100644 --- a/osc.c +++ b/osc.c @@ -1480,12 +1480,10 @@ osc_dispatch(struct terminal *term) case 17: term->colors.selection_bg = color; - term->colors.use_custom_selection = true; break; case 19: term->colors.selection_fg = color; - term->colors.use_custom_selection = true; break; } @@ -1589,13 +1587,11 @@ osc_dispatch(struct terminal *term) case 117: LOG_DBG("resetting selection background color"); term->colors.selection_bg = term->conf->colors.selection_bg; - term->colors.use_custom_selection = term->conf->colors.use_custom.selection; break; case 119: LOG_DBG("resetting selection foreground color"); term->colors.selection_fg = term->conf->colors.selection_fg; - term->colors.use_custom_selection = term->conf->colors.use_custom.selection; break; case 133: diff --git a/render.c b/render.c index 55c2ec4d..83a160bc 100644 --- a/render.c +++ b/render.c @@ -694,51 +694,75 @@ render_cell(struct terminal *term, pixman_image_t *pix, const int x = term->margins.left + col * width; const int y = term->margins.top + row_no * height; - bool is_selected = cell->attrs.selected; - uint32_t _fg = 0; uint32_t _bg = 0; uint16_t alpha = 0xffff; + const bool is_selected = cell->attrs.selected; + + /* Use cell specific color, if set, otherwise the default colors (possible reversed) */ + switch (cell->attrs.fg_src) { + case COLOR_RGB: + _fg = cell->attrs.fg; + break; + + case COLOR_BASE16: + case COLOR_BASE256: + xassert(cell->attrs.fg < ALEN(term->colors.table)); + _fg = term->colors.table[cell->attrs.fg]; + break; + + case COLOR_DEFAULT: + _fg = term->reverse ? term->colors.bg : term->colors.fg; + 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; + } + + if (unlikely(is_selected)) { + const uint32_t cell_fg = _fg; + const uint32_t cell_bg = _bg; + + const bool custom_fg = term->colors.selection_fg >> 24 == 0; + const bool custom_bg = term->colors.selection_bg >> 24 == 0; + const bool custom_both = custom_fg && custom_bg; + + if (custom_both) { + _fg = term->colors.selection_fg; + _bg = term->colors.selection_bg; + } else if (custom_bg) { + _bg = term->colors.selection_bg; + _fg = cell->attrs.reverse ? cell_bg : cell_fg; + } else if (custom_fg) { + _fg = term->colors.selection_fg; + _bg = cell->attrs.reverse ? cell_fg : cell_bg; + } else { + _bg = cell_fg; + _fg = cell_bg; + } + + if (unlikely(_fg == _bg)) { + /* Invert bg when selected/highlighted text has same fg/bg */ + _bg = ~_bg; + alpha = 0xffff; + } - if (is_selected && term->colors.use_custom_selection) { - _fg = term->colors.selection_fg; - _bg = term->colors.selection_bg; } else { - /* Use cell specific color, if set, otherwise the default colors (possible reversed) */ - switch (cell->attrs.fg_src) { - case COLOR_RGB: - _fg = cell->attrs.fg; - break; - - case COLOR_BASE16: - case COLOR_BASE256: - xassert(cell->attrs.fg < ALEN(term->colors.table)); - _fg = term->colors.table[cell->attrs.fg]; - break; - - case COLOR_DEFAULT: - _fg = term->reverse ? term->colors.bg : term->colors.fg; - 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; - } - - if (cell->attrs.reverse ^ is_selected) { + if (unlikely(cell->attrs.reverse)) { uint32_t swap = _fg; _fg = _bg; _bg = swap; @@ -806,12 +830,6 @@ render_cell(struct terminal *term, pixman_image_t *pix, } } - if (unlikely(is_selected && _fg == _bg)) { - /* Invert bg when selected/highlighted text has same fg/bg */ - _bg = ~_bg; - alpha = 0xffff; - } - if (cell->attrs.dim) _fg = color_dim(term, _fg); if (term->conf->bold_in_bright.enabled && cell->attrs.bold) diff --git a/terminal.c b/terminal.c index f3a4b7d0..9ec538c6 100644 --- a/terminal.c +++ b/terminal.c @@ -1312,7 +1312,6 @@ 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, - .use_custom_selection = theme->use_custom.selection, .active_theme = conf->initial_color_theme, }, .color_stack = { @@ -4714,6 +4713,5 @@ 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.use_custom_selection = theme->use_custom.selection; memcpy(term->colors.table, theme->table, sizeof(term->colors.table)); } diff --git a/terminal.h b/terminal.h index 4639fa69..3122cef3 100644 --- a/terminal.h +++ b/terminal.h @@ -404,7 +404,6 @@ struct colors { uint32_t cursor_bg; /* cursor color */ uint32_t selection_fg; uint32_t selection_bg; - bool use_custom_selection; enum which_color_theme active_theme; }; From 9b0d5e7c96f75d7ca81cd2e452cda54688ecc259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 8 May 2025 10:22:45 +0200 Subject: [PATCH 77/86] term: unittest: auto-scroll timer FD is created on-demand nowadays --- terminal.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/terminal.c b/terminal.c index 9ec538c6..18f3bc9f 100644 --- a/terminal.c +++ b/terminal.c @@ -2769,13 +2769,11 @@ UNITTEST }, .kind = SELECTION_NONE, .auto_scroll = { - .fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK), + .fd = -1, }, }, }; - xassert(term.selection.auto_scroll.fd >= 0); - #define populate_scrollback() do { \ for (int i = 0; i < scrollback_rows; i++) { \ if (term.normal.rows[i] == NULL) { \ @@ -2865,7 +2863,7 @@ UNITTEST /* Cleanup */ tll_free(term.normal.sixel_images); - close(term.selection.auto_scroll.fd); + xassert(term.selection.auto_scroll.fd == -1); for (int i = 0; i < scrollback_rows; i++) grid_row_free(term.normal.rows[i]); free(term.normal.rows); From ebd1614316ea541cd1ac0cc0029f771be333176e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 16 May 2025 10:46:25 +0200 Subject: [PATCH 78/86] csi: when REP:ing a "combining" character, use correct width Before this patch, we just called c32width(), which only works on actual codepoints. If the last printed character is a "combining" character, i.e. a key into our lookup table for multi-codepoint graphemes, we need to lookup the grapheme and pick the width from there. See https://gitlab.com/AutumnMeowMeow/jexer/-/issues/119#note_2499712901 --- CHANGELOG.md | 4 ++++ csi.c | 12 +++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea818d53..00c1f0a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -116,6 +116,10 @@ ### Removed ### Fixed + +* `REP`: wrong width of repeated multi-codepoint graphemes. + + ### Security ### Contributors diff --git a/csi.c b/csi.c index e8b2c492..6d4845be 100644 --- a/csi.c +++ b/csi.c @@ -799,7 +799,17 @@ csi_dispatch(struct terminal *term, uint8_t final) int count = vt_param_get(term, 0, 1); LOG_DBG("REP: '%lc' %d times", (wint_t)term->vt.last_printed, count); - const int width = c32width(term->vt.last_printed); + int width; + + if (term->vt.last_printed >= CELL_COMB_CHARS_LO) { + const struct composed *comp = composed_lookup( + term->composed, term->vt.last_printed - CELL_COMB_CHARS_LO); + + xassert(comp != NULL); + width = comp->forced_width > 0 ? comp->forced_width : comp->width; + } else + width = c32width(term->vt.last_printed); + if (width > 0) { for (int i = 0; i < count; i++) term_print(term, term->vt.last_printed, width, false); From 8bd39b32cd87abc65a30d23a83a0564f6419025e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 18 May 2025 11:29:50 +0200 Subject: [PATCH 79/86] Revert "xkbcommon: require libxkbcommon >= 1.8.0" This reverts commit 34d3f4664b93d42ec3e1eef9a11e78756465d25a. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a884e533..ae263851 100644 --- a/meson.build +++ b/meson.build @@ -137,7 +137,7 @@ wayland_protocols = dependency('wayland-protocols', version: '>=1.41', default_options: ['tests=false']) wayland_client = dependency('wayland-client') wayland_cursor = dependency('wayland-cursor') -xkb = dependency('xkbcommon', version: '>=1.8.0') +xkb = dependency('xkbcommon', version: '>=1.0.0') fontconfig = dependency('fontconfig') utf8proc = dependency('libutf8proc', required: get_option('grapheme-clustering')) From 3e1e3ea38ce8ed43ee61ddb584298f45dd72f1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 18 May 2025 11:35:27 +0200 Subject: [PATCH 80/86] libxkbcommon: don't require 1.8.0 The version bump was done since we now use XKB_VMOD_NAME_*; macros added in libxkbcommon 1.8.0. Not all distros have updated libxkbcommon yet (read: Debian). Since it's fairly easy to work around, let's do that. Closes #2103 --- CHANGELOG.md | 3 +++ input.c | 1 + key-binding.c | 1 + meson.build | 1 + xkbcommon-vmod.h | 18 ++++++++++++++++++ 5 files changed, 24 insertions(+) create mode 100644 xkbcommon-vmod.h diff --git a/CHANGELOG.md b/CHANGELOG.md index 00c1f0a0..cb478f87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,8 +105,11 @@ instead of `10-bit`. * Allow setting either selection background, or selection foreground, only ([#1846][1846]). +* Drop required version of libxkbcommon from 1.8.0 back to 1.0.0 + ([#2103][2103]). [1846]: https://codeberg.org/dnkl/foot/issues/1846 +[2103]: https://codeberg.org/dnkl/foot/issues/2103 ### Deprecated diff --git a/input.c b/input.c index b6c56fde..271ffb88 100644 --- a/input.c +++ b/input.c @@ -40,6 +40,7 @@ #include "url-mode.h" #include "util.h" #include "vt.h" +#include "xkbcommon-vmod.h" #include "xmalloc.h" #include "xsnprintf.h" diff --git a/key-binding.c b/key-binding.c index e5b7ac81..a2883ed5 100644 --- a/key-binding.c +++ b/key-binding.c @@ -11,6 +11,7 @@ #include "terminal.h" #include "util.h" #include "wayland.h" +#include "xkbcommon-vmod.h" #include "xmalloc.h" struct vmod_map { diff --git a/meson.build b/meson.build index ae263851..7b9490d9 100644 --- a/meson.build +++ b/meson.build @@ -319,6 +319,7 @@ executable( 'url-mode.c', 'url-mode.h', 'user-notification.c', 'user-notification.h', 'wayland.c', 'wayland.h', 'shm-formats.h', + 'xkbcommon-vmod.h', srgb_funcs, wl_proto_src + wl_proto_headers, version, dependencies: [math, threads, libepoll, pixman, wayland_client, wayland_cursor, xkb, fontconfig, utf8proc, tllist, fcft], diff --git a/xkbcommon-vmod.h b/xkbcommon-vmod.h new file mode 100644 index 00000000..44d818ec --- /dev/null +++ b/xkbcommon-vmod.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +/* Added in libxkbcommon 1.8.0 */ +#if !defined(XKB_VMOD_NAME_ALT) +/* Common *virtual* modifiers, encoded in xkeyboard-config in the compat and + * symbols files. They have been stable since the beginning of the project and + * are unlikely to ever change. */ +#define XKB_VMOD_NAME_ALT "Alt" +#define XKB_VMOD_NAME_HYPER "Hyper" +#define XKB_VMOD_NAME_LEVEL3 "LevelThree" +#define XKB_VMOD_NAME_LEVEL5 "LevelFive" +#define XKB_VMOD_NAME_META "Meta" +#define XKB_VMOD_NAME_NUM "NumLock" +#define XKB_VMOD_NAME_SCROLL "ScrollLock" +#define XKB_VMOD_NAME_SUPER "Super" +#endif From 456ac5d79f46a670479fcdaa75df0942a1571c78 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Tue, 20 May 2025 15:01:25 +0300 Subject: [PATCH 81/86] render: improve CSD button positioning This commit fixes titlebar button positioning when maximization isn't available but minimization is. --- render.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/render.c b/render.c index 83a160bc..e0f32575 100644 --- a/render.c +++ b/render.c @@ -2254,16 +2254,21 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx) const int button_width = title_visible ? roundf(term->conf->csd.button_width * scale) : 0; - const int button_close_width = term->width >= 1 * button_width - ? button_width : 0; + int remaining_width = term->width; - const int button_maximize_width = - term->width >= 2 * button_width && term->window->wm_capabilities.maximize - ? button_width : 0; + const int button_close_width = remaining_width >= button_width ? button_width : 0; + remaining_width -= button_close_width; + const int button_close_start = remaining_width; - const int button_minimize_width = - term->width >= 3 * button_width && term->window->wm_capabilities.minimize - ? button_width : 0; + const int button_maximize_width = remaining_width >= button_width && + term->window->wm_capabilities.maximize ? button_width : 0; + remaining_width -= button_maximize_width; + const int button_maximize_start = remaining_width; + + const int button_minimize_width = remaining_width >= button_width && + term->window->wm_capabilities.minimize ? button_width : 0; + remaining_width -= button_minimize_width; + const int button_minimize_start = remaining_width; /* * With fractional scaling, we must ensure the offset, when @@ -2288,9 +2293,9 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx) case CSD_SURF_BOTTOM: return (struct csd_data){-border_width, term->height, top_bottom_width, border_width}; /* Positioned relative to CSD_SURF_TITLE */ - case CSD_SURF_MINIMIZE: return (struct csd_data){term->width - 3 * button_width, 0, button_minimize_width, title_height}; - case CSD_SURF_MAXIMIZE: return (struct csd_data){term->width - 2 * button_width, 0, button_maximize_width, title_height}; - case CSD_SURF_CLOSE: return (struct csd_data){term->width - 1 * button_width, 0, button_close_width, title_height}; + case CSD_SURF_MINIMIZE: return (struct csd_data){button_minimize_start, 0, button_minimize_width, title_height}; + case CSD_SURF_MAXIMIZE: return (struct csd_data){button_maximize_start, 0, button_maximize_width, title_height}; + case CSD_SURF_CLOSE: return (struct csd_data){ button_close_start, 0, button_close_width, title_height}; case CSD_SURF_COUNT: break; From d26659988113662d1500f600f5c10899920a5e6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 21 May 2025 13:01:30 +0200 Subject: [PATCH 82/86] wayland: configure: don't commit if we have a pending refresh Currently, if the following occurs: 1. foot has AxB size 2. Compositor sends CxD size 3. foot detects a resize, acks and saves CxD, but doesn't redraw immediately 4. Compositor sends CxD size again (due to a toplevel state array change, for example) Then foot will detect no resize occurred, and will do an "empty" commit immediately. In this particular case that's wrong, since we're effectively acking+committing the initial AxB size. Fix by only doing the immediate commit if there's no size change **and** there's no pending refresh. Note: normally, we'd resize and refresh+commit immediately, but if we're waiting for a frame callback, then the refresh+commit will be delayed (i.e. scheduled). This is what we're checking here. Closes #2105 --- CHANGELOG.md | 4 ++++ wayland.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb478f87..fe149600 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -121,6 +121,10 @@ ### Fixed * `REP`: wrong width of repeated multi-codepoint graphemes. +* Incorrect surface commit after a configure event, under certain + conditions ([#2105][2105]). + +[2105]: https://codeberg.org/dnkl/foot/issues/2105 ### Security diff --git a/wayland.c b/wayland.c index 368b3be7..37fefb29 100644 --- a/wayland.c +++ b/wayland.c @@ -1134,7 +1134,7 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, else term_visual_focus_out(term); - if (!resized) { + if (!resized && !term->render.pending.grid) { /* * If we didn't resize, we won't be committing a new surface * anytime soon. Some compositors require a commit in From 664cdcc65cf42ab269ea4a2c4962c3ee65d7e92f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 21 May 2025 15:25:28 +0200 Subject: [PATCH 83/86] cursor-shape: add 'dnd-ask' and 'all-resize' These (non-css) cursor shapes were added to the cursor-shape-v1 protocol in wayland-protocols 1.42. We don't need (or use them at all) internally, but add them to the list we use to translate from shape names to shape enums. This allows users to set a custom shape (via OSC-22), while still using server side cursors (i.e. no need to fallback to client-side cursors). If we try to set a shape not implemented by the server, we get a protocol error and foot exits. This is bad. So, make sure we don't do that: 1. First, we need to explicitly bind v2 if implemented by the server 2. Track the bound version number in the wayland struct 3. When matching shape enum, skip shapes not supported in the currently bound version of the cursor-shape protocol --- CHANGELOG.md | 1 + cursor-shape.c | 22 +++++++++++++++++++++- cursor-shape.h | 2 +- render.c | 5 +++-- terminal.c | 4 +++- wayland.c | 10 +++++++++- wayland.h | 1 + 7 files changed, 39 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe149600..66fa7c93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -90,6 +90,7 @@ * `16-bit` to `tweak.surface-bit-depth`. Makes foot use 16-bit image buffers. They provide the necessary color precision required by `gamma-correct-blending=yes`. +* New cursor shapes, from `cursor-shape-v1` version 2. [2025]: https://codeberg.org/dnkl/foot/issues/2025 [1975]: https://codeberg.org/dnkl/foot/issues/1975 diff --git a/cursor-shape.c b/cursor-shape.c index bbf75ab8..6e859259 100644 --- a/cursor-shape.c +++ b/cursor-shape.c @@ -54,7 +54,7 @@ cursor_shape_to_server_shape(enum cursor_shape shape) } enum wp_cursor_shape_device_v1_shape -cursor_string_to_server_shape(const char *xcursor) +cursor_string_to_server_shape(const char *xcursor, int bound_version) { if (xcursor == NULL) return 0; @@ -94,9 +94,29 @@ cursor_string_to_server_shape(const char *xcursor) [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_SCROLL] = {"all-scroll", "fleur"}, [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_IN] = {"zoom-in"}, [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_OUT] = {"zoom-out"}, +#if defined(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DND_ASK_SINCE_VERSION) /* 1.42 */ + [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DND_ASK] = {"dnd-ask"}, +#endif +#if defined(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_RESIZE_SINCE_VERSION) /* 1.42 */ + [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_RESIZE] = {"all-resize"}, +#endif }; for (size_t i = 0; i < ALEN(table); i++) { +#if defined(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DND_ASK_SINCE_VERSION) + if (i == WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DND_ASK && + bound_version < WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DND_ASK_SINCE_VERSION) + { + continue; + } +#endif +#if defined(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_RESIZE_SINCE_VERSION) + if (i == WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_RESIZE && + bound_version < WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_RESIZE_SINCE_VERSION) + { + continue; + } +#endif for (size_t j = 0; j < ALEN(table[i]); j++) { if (table[i][j] != NULL && streq(xcursor, table[i][j])) { return i; diff --git a/cursor-shape.h b/cursor-shape.h index 110dbd2e..13690588 100644 --- a/cursor-shape.h +++ b/cursor-shape.h @@ -26,4 +26,4 @@ const char *const *cursor_shape_to_string(enum cursor_shape shape); enum wp_cursor_shape_device_v1_shape cursor_shape_to_server_shape( enum cursor_shape shape); enum wp_cursor_shape_device_v1_shape cursor_string_to_server_shape( - const char *xcursor); + const char *xcursor, int bound_version); diff --git a/render.c b/render.c index e0f32575..a41eee0c 100644 --- a/render.c +++ b/render.c @@ -4929,8 +4929,9 @@ render_xcursor_update(struct seat *seat) const enum wp_cursor_shape_device_v1_shape custom_shape = (shape == CURSOR_SHAPE_CUSTOM && xcursor != NULL - ? cursor_string_to_server_shape(xcursor) - : 0); + ? cursor_string_to_server_shape( + xcursor, seat->wayl->shape_manager_version) + : 0); if (shape != CURSOR_SHAPE_CUSTOM || custom_shape != 0) { xassert(custom_shape == 0 || shape == CURSOR_SHAPE_CUSTOM); diff --git a/terminal.c b/terminal.c index 18f3bc9f..6f66f65b 100644 --- a/terminal.c +++ b/terminal.c @@ -3571,7 +3571,9 @@ term_xcursor_update_for_seat(struct terminal *term, struct seat *seat) if (seat->pointer.hidden) shape = CURSOR_SHAPE_HIDDEN; - else if (cursor_string_to_server_shape(term->mouse_user_cursor) != 0 || + else if (cursor_string_to_server_shape( + term->mouse_user_cursor, + term->wl->shape_manager_version) != 0 || render_xcursor_is_valid(seat, term->mouse_user_cursor)) { shape = CURSOR_SHAPE_CUSTOM; diff --git a/wayland.c b/wayland.c index 37fefb29..7d3c7c67 100644 --- a/wayland.c +++ b/wayland.c @@ -1480,8 +1480,16 @@ handle_global(void *data, struct wl_registry *registry, if (!verify_iface_version(interface, version, required)) return; +#if defined(WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DND_ASK_SINCE_VERSION) /* 1.42 */ + const uint32_t preferred = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DND_ASK_SINCE_VERSION; +#else + const uint32_t preferred = required; +#endif + + wayl->shape_manager_version = min(required, preferred); wayl->cursor_shape_manager = wl_registry_bind( - wayl->registry, name, &wp_cursor_shape_manager_v1_interface, required); + wayl->registry, name, &wp_cursor_shape_manager_v1_interface, + min(required, preferred)); } else if (streq(interface, wp_single_pixel_buffer_manager_v1_interface.name)) { diff --git a/wayland.h b/wayland.h index b7e8e79f..eb1c35a3 100644 --- a/wayland.h +++ b/wayland.h @@ -460,6 +460,7 @@ struct wayland { struct wp_fractional_scale_manager_v1 *fractional_scale_manager; struct wp_cursor_shape_manager_v1 *cursor_shape_manager; + int shape_manager_version; struct wp_single_pixel_buffer_manager_v1 *single_pixel_manager; From 5621829bb00deea6c187c0c328e2560b84f809d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 23 May 2025 13:31:53 +0200 Subject: [PATCH 84/86] cursor-shape: map "dnd-move" to WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE --- cursor-shape.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cursor-shape.c b/cursor-shape.c index 6e859259..c195a554 100644 --- a/cursor-shape.c +++ b/cursor-shape.c @@ -72,7 +72,7 @@ cursor_string_to_server_shape(const char *xcursor, int bound_version) [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_VERTICAL_TEXT] = {"vertical-text"}, [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALIAS] = {"alias", "dnd-link"}, [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COPY] = {"copy", "dnd-copy"}, - [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE] = {"move"}, /* dnd-move? */ + [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE] = {"move", "dnd-move"}, [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP] = {"no-drop", "dnd-no-drop"}, [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NOT_ALLOWED] = {"not-allowed", "crossed_circle"}, [WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB] = {"grab", "hand1"}, From 5a84f8d841d09e9aa91befbbb8e8b634b7f8959a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 23 May 2025 08:38:00 +0200 Subject: [PATCH 85/86] conf: pad: add center-when-fullscreen and center-when-maximized-and-fullscreen Before this patch, the grid content was *always* centered when the window was maximized or fullscreened, regardless of how the user had configured padding. Now, the behavior is controlled by the 'pad' option. Before this patch, the syntax was pad MxN [center] Now it is pad MxN [center|center-when-fullscreen|center-when-maximized-and-fullscreen] The default is "pad 0x0 center-when-maximized-and-fullscreen", to match current behavior. Closes #2111 --- CHANGELOG.md | 4 ++++ config.c | 28 +++++++++++++++++++++------- config.h | 10 +++++++++- doc/foot.ini.5.scd | 27 +++++++++++++++++++-------- foot.ini | 2 +- render.c | 9 ++++++--- 6 files changed, 60 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 66fa7c93..9ade3b6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,9 +91,13 @@ buffers. They provide the necessary color precision required by `gamma-correct-blending=yes`. * New cursor shapes, from `cursor-shape-v1` version 2. +* `center-when-fullscreen` and `center-when-maximized-and-fullscreen` + to the `pad` option. This allows you to configure when the grid is + centered in more detail ([#2111][2111]). [2025]: https://codeberg.org/dnkl/foot/issues/2025 [1975]: https://codeberg.org/dnkl/foot/issues/1975 +[2111]: https://codeberg.org/dnkl/foot/issues/2111 ### Changed diff --git a/config.c b/config.c index 07f781d6..d8f1c0ed 100644 --- a/config.c +++ b/config.c @@ -933,21 +933,34 @@ parse_section_main(struct context *ctx) else if (streq(key, "pad")) { unsigned x, y; - char mode[16] = {0}; + char mode[64] = {0}; + int ret = sscanf(value, "%ux%u %63s", &x, &y, mode); - int ret = sscanf(value, "%ux%u %15s", &x, &y, mode); - bool center = strcasecmp(mode, "center") == 0; - bool invalid_mode = !center && mode[0] != '\0'; + enum center_when center = CENTER_NEVER; - if ((ret != 2 && ret != 3) || invalid_mode) { + if (ret == 3) { + if (strcasecmp(mode, "center") == 0) + center = CENTER_ALWAYS; + else if (strcasecmp(mode, "center-when-fullscreen") == 0) + center = CENTER_FULLSCREEN; + else if (strcasecmp(mode, "center-when-maximized-and-fullscreen") == 0) + center = CENTER_MAXIMIZED_AND_FULLSCREEN; + else + center = CENTER_INVALID; + } + + if ((ret != 2 && ret != 3) || center == CENTER_INVALID) { LOG_CONTEXTUAL_ERR( - "invalid padding (must be in the form PAD_XxPAD_Y [center])"); + "invalid padding (must be in the form PAD_XxPAD_Y " + "[center|" + "center-when-fullscreen|" + "center-when-maximized-and-fullscreen])"); return false; } conf->pad_x = x; conf->pad_y = y; - conf->center = center; + conf->center_when = ret == 2 ? CENTER_NEVER : center; return true; } @@ -3339,6 +3352,7 @@ config_load(struct config *conf, const char *conf_path, }, .pad_x = 0, .pad_y = 0, + .center_when = CENTER_MAXIMIZED_AND_FULLSCREEN, .resize_by_cells = true, .resize_keep_grid = true, .resize_delay_ms = 100, diff --git a/config.h b/config.h index 7cf6f6f5..315f7e24 100644 --- a/config.h +++ b/config.h @@ -201,6 +201,14 @@ enum shm_bit_depth { SHM_BITS_16, }; +enum center_when { + CENTER_INVALID, + CENTER_NEVER, + CENTER_FULLSCREEN, + CENTER_MAXIMIZED_AND_FULLSCREEN, + CENTER_ALWAYS, +}; + struct config { char *term; char *shell; @@ -218,7 +226,7 @@ struct config { unsigned pad_x; unsigned pad_y; - bool center; + enum center_when center_when; bool resize_by_cells; bool resize_keep_grid; diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 81b88f64..74b3f35b 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -289,18 +289,29 @@ empty string to be set, but it must be quoted: *KEY=""*) *pad* Padding between border and glyphs, in pixels (subject to output - scaling), in the form _XxY_. + scaling), in the form + + ``` + _XxY_ [center | center-when-fullscreen | center-when-maximized-and-fullscreen] + ``` This will add _at least_ X pixels on both the left and right - sides, and Y pixels on the top and bottom sides. The grid content - will be anchored in the top left corner. I.e. if the window - manager forces an odd window size on foot, the additional pixels - will be added to the right and bottom sides. + sides, and Y pixels on the top and bottom sides. - To instead center the grid content, append *center* (e.g. *pad=5x5 - center*). + When no centering is specified, the grid content is anchored to + the top left corner. I.e. if the window manager forces an odd + window size on foot, the additional pixels will be added to the + right and bottom sides. - Default: _0x0_. + If *center* is specified, the grid content is instead + centered. This may cause "jumpiness" when resizing the window. + + With *center-when-fullscreen* and + *center-when-maximized-and-fullscreen*, the grid is anchored to + the top left corner, unless the window is maximized, or + fullscreened. + + Default: _0x0_ center-when-maximized-and-fullscreen. *resize-delay-ms* diff --git a/foot.ini b/foot.ini index f3ef6d85..73fdb7ab 100644 --- a/foot.ini +++ b/foot.ini @@ -28,7 +28,7 @@ # initial-window-size-pixels=700x500 # Or, # initial-window-size-chars= # initial-window-mode=windowed -# pad=0x0 # optionally append 'center' +# pad=0x0 center-when-maximized-and-fullscreen # resize-by-cells=yes # resize-keep-grid=yes # resize-delay-ms=100 diff --git a/render.c b/render.c index a41eee0c..244152ef 100644 --- a/render.c +++ b/render.c @@ -4596,9 +4596,12 @@ render_resize(struct terminal *term, int width, int height, uint8_t opts) const int total_x_pad = term->width - grid_width; const int total_y_pad = term->height - grid_height; - const bool centered_padding = term->conf->center - || term->window->is_fullscreen - || term->window->is_maximized; + const enum center_when center = term->conf->center_when; + const bool centered_padding = + center == CENTER_ALWAYS || + (center == CENTER_MAXIMIZED_AND_FULLSCREEN && + (term->window->is_fullscreen || term->window->is_maximized)) || + (center == CENTER_FULLSCREEN && term->window->is_fullscreen); if (centered_padding && !term->window->is_resizing) { term->margins.left = total_x_pad / 2; From eeaecba7238fb3a501cb25b55b7d2ada2bc4d023 Mon Sep 17 00:00:00 2001 From: tokyo4j Date: Sat, 24 May 2025 19:06:29 +0900 Subject: [PATCH 86/86] wayland: fix global listener for xdg_toplevel_icon_manager_v1 --- wayland.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wayland.c b/wayland.c index 7d3c7c67..5f68ecf7 100644 --- a/wayland.c +++ b/wayland.c @@ -1502,13 +1502,13 @@ handle_global(void *data, struct wl_registry *registry, &wp_single_pixel_buffer_manager_v1_interface, required); } - else if (streq(interface, xdg_toplevel_icon_v1_interface.name)) { + else if (streq(interface, xdg_toplevel_icon_manager_v1_interface.name)) { const uint32_t required = 1; if (!verify_iface_version(interface, version, required)) return; wayl->toplevel_icon_manager = wl_registry_bind( - wayl->registry, name, &xdg_toplevel_icon_v1_interface, required); + wayl->registry, name, &xdg_toplevel_icon_manager_v1_interface, required); } else if (streq(interface, xdg_system_bell_v1_interface.name)) {