From 48758a101e91bc47fbcf2e43de2c59081d1e69af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 14 Nov 2021 18:26:24 +0100 Subject: [PATCH 01/29] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrelea?= =?UTF-8?q?sed=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e6a8206..d191985b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.10.0](#1-10-0) * [1.9.2](#1-9-2) * [1.9.1](#1-9-1) @@ -32,6 +33,16 @@ * [1.2.0](#1-2-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.10.0 ### Added From 31966f5494ed82268c414b04b63b20e88d4fff16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 14 Nov 2021 23:12:18 +0100 Subject: [PATCH 02/29] meson: add -Dthemes=false|true This adds a new meson option that controls whether the example theme files should be installed or not. The default is true. Previously, themes were gated by the -Ddocs option. --- CHANGELOG.md | 6 ++++++ meson.build | 6 +++++- meson_options.txt | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d191985b..3cd9fc61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,13 @@ ## Unreleased + ### Added + +* `-Dthemes=false|true` meson command line option. When disabled, + example theme files are **not** installed. + + ### Changed ### Deprecated ### Removed diff --git a/meson.build b/meson.build index 5dd99652..9037f4a8 100644 --- a/meson.build +++ b/meson.build @@ -240,10 +240,13 @@ if scdoc.found() 'LICENSE', 'README.md', 'CHANGELOG.md', install_dir: join_paths(get_option('datadir'), 'doc', 'foot')) install_data('foot.ini', install_dir: join_paths(get_option('datadir'), 'foot')) - install_subdir('themes', install_dir: join_paths(get_option('datadir'), 'foot')) subdir('doc') endif +if get_option('themes') + install_subdir('themes', install_dir: join_paths(get_option('datadir'), 'foot')) +endif + tic = find_program('tic', native: true, required: get_option('terminfo')) if tic.found() conf_data = configuration_data( @@ -273,6 +276,7 @@ subdir('icons') summary( { 'Documentation': scdoc.found(), + 'Themes': get_option('themes'), 'IME': get_option('ime'), 'Grapheme clustering': utf8proc.found(), 'Build terminfo': tic.found(), diff --git a/meson_options.txt b/meson_options.txt index b47cfc7b..ccc7d5a1 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,6 +1,9 @@ option('docs', type: 'feature', description: 'Build and install documentation (man pages, example foot.ini, readme, changelog, license etc).') +option('themes', type: 'boolean', value: true, + description: 'Install themes (predefined color schemes)') + option('ime', type: 'boolean', value: true, description: 'IME (Input Method Editor) support') From 843ba49565672af75627e46b0958a9209142c2e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 15 Nov 2021 12:13:39 +0100 Subject: [PATCH 03/29] footclient.desktop: new XDG desktop file, for footclient --- CHANGELOG.md | 1 + foot-server.desktop | 2 +- footclient.desktop | 11 +++++++++++ meson.build | 2 +- 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 footclient.desktop diff --git a/CHANGELOG.md b/CHANGELOG.md index 3cd9fc61..ca4d8a7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ * `-Dthemes=false|true` meson command line option. When disabled, example theme files are **not** installed. +* XDG desktop file for footclient. ### Changed diff --git a/foot-server.desktop b/foot-server.desktop index c2e8ccbf..6e8891c0 100644 --- a/foot-server.desktop +++ b/foot-server.desktop @@ -8,4 +8,4 @@ Keywords=shell;prompt;command;commandline; Name=Foot Server GenericName=Terminal -Comment=A wayland native terminal emulator +Comment=A wayland native terminal emulator (server) diff --git a/footclient.desktop b/footclient.desktop new file mode 100644 index 00000000..f82f282b --- /dev/null +++ b/footclient.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Type=Application +Exec=footclient +Icon=foot +Terminal=false +Categories=System;TerminalEmulator; +Keywords=shell;prompt;command;commandline; + +Name=Foot Client +GenericName=Terminal +Comment=A wayland native terminal emulator (client) diff --git a/meson.build b/meson.build index 9037f4a8..5270521d 100644 --- a/meson.build +++ b/meson.build @@ -231,7 +231,7 @@ executable( install: true) install_data( - 'foot.desktop', 'foot-server.desktop', + 'foot.desktop', 'foot-server.desktop', 'footclient.desktop', install_dir: join_paths(get_option('datadir'), 'applications')) scdoc = dependency('scdoc', native: true, required: get_option('docs')) From b22322bfd104949b93d1df22ff36689747917448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 15 Nov 2021 19:22:06 +0100 Subject: [PATCH 04/29] =?UTF-8?q?config:=20letter-spacing:=20add=20missing?= =?UTF-8?q?=20=E2=80=98return=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The missing return caused us to fall through to the bottom of the function, where we ended with an error message: [main].letter-spacing: 0: not a valid option: letter-spacing Closes #795 --- CHANGELOG.md | 5 +++++ config.c | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca4d8a7d..b65f0b63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,11 @@ ### Deprecated ### Removed ### Fixed + +* Regression: `letter-spacing` resulting in a “not a valid option” + error (https://codeberg.org/dnkl/foot/issues/795). + + ### Security ### Contributors diff --git a/config.c b/config.c index 905f26d6..2f12e201 100644 --- a/config.c +++ b/config.c @@ -956,7 +956,7 @@ parse_section_main(struct context *ctx) return value_to_pt_or_px(ctx, &conf->line_height); else if (strcmp(key, "letter-spacing") == 0) - value_to_pt_or_px(ctx, &conf->letter_spacing); + return value_to_pt_or_px(ctx, &conf->letter_spacing); else if (strcmp(key, "horizontal-letter-offset") == 0) return value_to_pt_or_px(ctx, &conf->horizontal_letter_offset); From 6ee0267872eb82a6d063b0295e6bc0e4b36fa5a7 Mon Sep 17 00:00:00 2001 From: Soc Virnyl Silab Estela Date: Thu, 18 Nov 2021 00:29:22 +0800 Subject: [PATCH 05/29] Add PaperColor theme variants - color palettes taken from https://github.com/NLKNguyen/papercolor-theme --- themes/PaperColorDark | 28 ++++++++++++++++++++++++++++ themes/PaperColorLight | 28 ++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 themes/PaperColorDark create mode 100644 themes/PaperColorLight diff --git a/themes/PaperColorDark b/themes/PaperColorDark new file mode 100644 index 00000000..7f3faafa --- /dev/null +++ b/themes/PaperColorDark @@ -0,0 +1,28 @@ +# PaperColorDark +# Palette based on https://github.com/NLKNguyen/papercolor-theme + +[cursor] + color=1c1c1c eeeeee + +[colors] + alpha=0.80 + 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 diff --git a/themes/PaperColorLight b/themes/PaperColorLight new file mode 100644 index 00000000..4e2ba35d --- /dev/null +++ b/themes/PaperColorLight @@ -0,0 +1,28 @@ +# PaperColor Light +# Palette based on https://github.com/NLKNguyen/papercolor-theme + +[cursor] + color=eeeeee 444444 + +[colors] + alpha=1.0 + 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 154caae35af046171150829a852122f2d893f580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 20 Nov 2021 11:24:13 +0100 Subject: [PATCH 06/29] ci: fcft: replace text-shaping with grapheme-shaping+run-shaping --- .builds/alpine-x64.yml | 4 ++-- .builds/alpine-x86.yml.disabled | 4 ++-- .builds/freebsd-x64.yml | 2 +- .gitlab-ci.yml | 8 ++++---- .woodpecker.yml | 8 ++++---- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index df08c6f9..e7ee15c6 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -34,12 +34,12 @@ sources: tasks: - debug: | mkdir -p bld/debug - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug + meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug ninja -C bld/debug -k0 meson test -C bld/debug --print-errorlogs - release: | mkdir -p bld/release - meson --buildtype=minsize -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release + meson --buildtype=minsize -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release ninja -C bld/release -k0 meson test -C bld/release --print-errorlogs - codespell: | diff --git a/.builds/alpine-x86.yml.disabled b/.builds/alpine-x86.yml.disabled index 1866e15a..24909eb2 100644 --- a/.builds/alpine-x86.yml.disabled +++ b/.builds/alpine-x86.yml.disabled @@ -33,11 +33,11 @@ sources: tasks: - debug: | mkdir -p bld/debug - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug + meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug ninja -C bld/debug -k0 meson test -C bld/debug --print-errorlogs - release: | mkdir -p bld/release - meson --buildtype=minsize -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release + meson --buildtype=minsize -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release ninja -C bld/release -k0 meson test -C bld/release --print-errorlogs diff --git a/.builds/freebsd-x64.yml b/.builds/freebsd-x64.yml index 7fc65233..a356db4d 100644 --- a/.builds/freebsd-x64.yml +++ b/.builds/freebsd-x64.yml @@ -34,6 +34,6 @@ tasks: meson test -C bld/debug --print-errorlogs - release: | mkdir -p bld/release - meson --buildtype=minsize -Dterminfo=disabled -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release + meson --buildtype=minsize -Dterminfo=disabled -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release ninja -C bld/release -k0 meson test -C bld/release --print-errorlogs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b7fbb972..99cb2728 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,7 @@ debug-x64: script: - mkdir -p bld/debug - cd bld/debug - - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true ../../ + - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ - ninja -v -k0 - ninja -v test artifacts: @@ -46,7 +46,7 @@ release-x64: script: - mkdir -p bld/release - cd bld/release - - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true ../../ + - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ - ninja -v -k0 - ninja -v test artifacts: @@ -59,7 +59,7 @@ debug-x86: script: - mkdir -p bld/debug - cd bld/debug - - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true ../../ + - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ - ninja -v -k0 - ninja -v test artifacts: @@ -72,7 +72,7 @@ release-x86: script: - mkdir -p bld/release - cd bld/release - - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true ../../ + - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../ - ninja -v -k0 - ninja -v test artifacts: diff --git a/.woodpecker.yml b/.woodpecker.yml index f965dca1..0d97608a 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -43,7 +43,7 @@ pipeline: # Debug - mkdir -p bld/debug-x64 - cd bld/debug-x64 - - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true ../.. + - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../.. - ninja -v -k0 - ninja -v test - ./foot --version @@ -53,7 +53,7 @@ pipeline: # Release - mkdir -p bld/release-x64 - cd bld/release-x64 - - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true ../.. + - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../.. - ninja -v -k0 - ninja -v test - ./foot --version @@ -90,7 +90,7 @@ pipeline: # Debug - mkdir -p bld/debug-x86 - cd bld/debug-x86 - - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true ../.. + - meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../.. - ninja -v -k0 - ninja -v test - ./foot --version @@ -100,7 +100,7 @@ pipeline: # Release - mkdir -p bld/release-x86 - cd bld/release-x86 - - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true ../.. + - meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../.. - ninja -v -k0 - ninja -v test - ./foot --version From 37b82efa77b3c683469f3e44a1890b0359659420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 20 Nov 2021 11:31:31 +0100 Subject: [PATCH 07/29] ci: one more text-shaping -> grapheme-shaping+run-shaping --- .builds/freebsd-x64.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.builds/freebsd-x64.yml b/.builds/freebsd-x64.yml index a356db4d..f2c5c3ef 100644 --- a/.builds/freebsd-x64.yml +++ b/.builds/freebsd-x64.yml @@ -29,7 +29,7 @@ sources: tasks: - debug: | mkdir -p bld/debug - meson --buildtype=debug -Dterminfo=disabled -Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug + meson --buildtype=debug -Dterminfo=disabled -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug ninja -C bld/debug -k0 meson test -C bld/debug --print-errorlogs - release: | From 02bb2768f176164099a30b74504c86e1accb7bb1 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Fri, 19 Nov 2021 16:51:52 +0100 Subject: [PATCH 08/29] config: Remove some "UNREACHABLE" UB footguns Every branch of these long if/else if/else chains returns, so the compiler can figure out on its own that the last line is never reached. If, for some reason, one of the branches does not return (as was the case after 205f1f7, fixed by b22322b), this would usually result in a compiler error ("control reaches end of non-void function"), but adding UNREACHABLE transforms this into silent undefined behaviour. --- config.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/config.c b/config.c index 2f12e201..c655813d 100644 --- a/config.c +++ b/config.c @@ -1012,8 +1012,6 @@ parse_section_main(struct context *ctx) LOG_CONTEXTUAL_ERR("not a valid option: %s", key); return false; } - - UNREACHABLE(); } static bool @@ -2340,8 +2338,6 @@ parse_section_tweak(struct context *ctx) LOG_CONTEXTUAL_ERR("not a valid option: %s", key); return false; } - - UNREACHABLE(); } static bool From 877c1e6f07df5110dd08d6c6aa01987ca1b46a24 Mon Sep 17 00:00:00 2001 From: Xiretza Date: Sat, 20 Nov 2021 11:58:01 +0100 Subject: [PATCH 09/29] config: Simplify render-timer option handling --- config.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/config.c b/config.c index c655813d..f8273484 100644 --- a/config.c +++ b/config.c @@ -2263,29 +2263,10 @@ parse_section_tweak(struct context *ctx) return false; } - switch (mode) { - case 0: - conf->tweak.render_timer_osd = false; - conf->tweak.render_timer_log = false; - return true; - - case 1: - conf->tweak.render_timer_osd = true; - conf->tweak.render_timer_log = false; - return true; - - case 2: - conf->tweak.render_timer_osd = false; - conf->tweak.render_timer_log = true; - return true; - - case 3: - conf->tweak.render_timer_osd = true; - conf->tweak.render_timer_log = true; - return true; - } - - UNREACHABLE(); + xassert(0 <= mode && mode <= 3); + conf->tweak.render_timer_osd = mode == 1 || mode == 3; + conf->tweak.render_timer_log = mode == 2 || mode == 3; + return true; } else if (strcmp(key, "delayed-render-lower") == 0) { From 0db125a1253d2daa33d94c7f1a50b22eeb176be3 Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Sat, 20 Nov 2021 15:12:56 +0000 Subject: [PATCH 10/29] config: fix memory leak in value_to_enum() This could be triggered by running, for example: foot -o tweak.render-timer=invalid For some reason LeakSanitizer was only detecting this leak when the option value was invalid, even though it was occuring in either case. --- config.c | 1 + 1 file changed, 1 insertion(+) diff --git a/config.c b/config.c index 2f12e201..50306b9d 100644 --- a/config.c +++ b/config.c @@ -614,6 +614,7 @@ value_to_enum(struct context *ctx, const char **value_map, int *res) valid_values[idx - 2] = '\0'; LOG_CONTEXTUAL_ERR("not one of %s", valid_values); + free(valid_values); *res = -1; return false; } From e89e735995382de9b5dd8df5d1f45ed9f766f06a Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Sat, 20 Nov 2021 15:23:24 +0000 Subject: [PATCH 11/29] config: use stack allocation for temporary buffer in value_to_enum() The maximum size required for the string buffer is relatively small, known at compile-time and not likely to ever grow beyond 512 bytes. Therefore we may as well just use the stack. --- config.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/config.c b/config.c index 50306b9d..24ed1267 100644 --- a/config.c +++ b/config.c @@ -27,6 +27,7 @@ #include "util.h" #include "wayland.h" #include "xmalloc.h" +#include "xsnprintf.h" static const uint32_t default_foreground = 0xdcdccc; static const uint32_t default_background = 0x111111; @@ -604,17 +605,17 @@ value_to_enum(struct context *ctx, const char **value_map, int *res) } const size_t size = str_len + count * 4 + 1; - char *valid_values = xmalloc(size); - int idx = 0; + char valid_values[512]; + size_t idx = 0; + xassert(size < sizeof(valid_values)); for (size_t i = 0; i < count; i++) - idx += snprintf(&valid_values[idx], size - idx, "'%s', ", value_map[i]); + idx += xsnprintf(&valid_values[idx], size - idx, "'%s', ", value_map[i]); if (count > 0) valid_values[idx - 2] = '\0'; LOG_CONTEXTUAL_ERR("not one of %s", valid_values); - free(valid_values); *res = -1; return false; } From d46af6bd7ac7606bf99d63b2453b656a5defb892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 20 Nov 2021 16:29:57 +0100 Subject: [PATCH 12/29] term: track cell color source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each cell now tracks it’s current color source: * default fg/bg * base16 fg/bg (maps to *both* the regular and bright colors) * base256 fg/bg * RGB Note that we don’t have enough bits to separate the regular from the bright colors. These _shouldn’t_ be the same, so we ought to be fine... --- csi.c | 28 ++++++++++++++-------------- osc.c | 6 +++--- render.c | 6 +++--- sixel.c | 2 +- terminal.c | 6 ++++-- terminal.h | 12 +++++++++--- 6 files changed, 34 insertions(+), 26 deletions(-) diff --git a/csi.c b/csi.c index becfc142..0224dd80 100644 --- a/csi.c +++ b/csi.c @@ -115,7 +115,7 @@ csi_sgr(struct terminal *term) case 35: case 36: case 37: - term->vt.attrs.have_fg = 1; + term->vt.attrs.fg_src = COLOR_BASE16; term->vt.attrs.fg = term->colors.table[param - 30]; break; @@ -125,7 +125,7 @@ csi_sgr(struct terminal *term) term->vt.params.v[i + 1].value == 5) { uint8_t idx = term->vt.params.v[i + 2].value; - term->vt.attrs.have_fg = 1; + term->vt.attrs.fg_src = COLOR_BASE256; term->vt.attrs.fg = term->colors.table[idx]; i += 2; @@ -138,7 +138,7 @@ csi_sgr(struct terminal *term) uint8_t r = term->vt.params.v[i + 2].value; uint8_t g = term->vt.params.v[i + 3].value; uint8_t b = term->vt.params.v[i + 4].value; - term->vt.attrs.have_fg = 1; + term->vt.attrs.fg_src = COLOR_RGB; term->vt.attrs.fg = r << 16 | g << 8 | b; i += 4; } @@ -150,7 +150,7 @@ csi_sgr(struct terminal *term) const struct vt_param *param = &term->vt.params.v[i]; uint8_t idx = param->sub.value[1]; - term->vt.attrs.have_fg = 1; + term->vt.attrs.fg_src = COLOR_BASE256; term->vt.attrs.fg = term->colors.table[idx]; } @@ -180,7 +180,7 @@ csi_sgr(struct terminal *term) uint8_t g = param->sub.value[g_idx]; uint8_t b = param->sub.value[b_idx]; - term->vt.attrs.have_fg = 1; + term->vt.attrs.fg_src = COLOR_RGB; term->vt.attrs.fg = r << 16 | g << 8 | b; } @@ -196,7 +196,7 @@ csi_sgr(struct terminal *term) } case 39: - term->vt.attrs.have_fg = 0; + term->vt.attrs.fg_src = COLOR_DEFAULT; break; /* Regular background colors */ @@ -208,7 +208,7 @@ csi_sgr(struct terminal *term) case 45: case 46: case 47: - term->vt.attrs.have_bg = 1; + term->vt.attrs.bg_src = COLOR_BASE16; term->vt.attrs.bg = term->colors.table[param - 40]; break; @@ -218,7 +218,7 @@ csi_sgr(struct terminal *term) term->vt.params.v[i + 1].value == 5) { uint8_t idx = term->vt.params.v[i + 2].value; - term->vt.attrs.have_bg = 1; + term->vt.attrs.bg_src = COLOR_BASE256; term->vt.attrs.bg = term->colors.table[idx]; i += 2; @@ -231,7 +231,7 @@ csi_sgr(struct terminal *term) uint8_t r = term->vt.params.v[i + 2].value; uint8_t g = term->vt.params.v[i + 3].value; uint8_t b = term->vt.params.v[i + 4].value; - term->vt.attrs.have_bg = 1; + term->vt.attrs.bg_src = COLOR_RGB; term->vt.attrs.bg = r << 16 | g << 8 | b; i += 4; } @@ -243,7 +243,7 @@ csi_sgr(struct terminal *term) const struct vt_param *param = &term->vt.params.v[i]; uint8_t idx = param->sub.value[1]; - term->vt.attrs.have_bg = 1; + term->vt.attrs.bg_src = COLOR_BASE256; term->vt.attrs.bg = term->colors.table[idx]; } @@ -273,7 +273,7 @@ csi_sgr(struct terminal *term) uint8_t g = param->sub.value[g_idx]; uint8_t b = param->sub.value[b_idx]; - term->vt.attrs.have_bg = 1; + term->vt.attrs.bg_src = COLOR_RGB; term->vt.attrs.bg = r << 16 | g << 8 | b; } @@ -287,7 +287,7 @@ csi_sgr(struct terminal *term) break; } case 49: - term->vt.attrs.have_bg = 0; + term->vt.attrs.bg_src = COLOR_DEFAULT; break; /* Bright foreground colors */ @@ -299,7 +299,7 @@ csi_sgr(struct terminal *term) case 95: case 96: case 97: - term->vt.attrs.have_fg = 1; + term->vt.attrs.fg_src = COLOR_BASE16; term->vt.attrs.fg = term->colors.table[param - 90 + 8]; break; @@ -312,7 +312,7 @@ csi_sgr(struct terminal *term) case 105: case 106: case 107: - term->vt.attrs.have_bg = 1; + term->vt.attrs.bg_src = COLOR_BASE16; term->vt.attrs.bg = term->colors.table[param - 100 + 8]; break; diff --git a/osc.c b/osc.c index ecb908ca..910c1e02 100644 --- a/osc.c +++ b/osc.c @@ -503,7 +503,7 @@ osc_notify(struct terminal *term, char *string) * (https://pub.phyks.me/scripts/urxvt/notify) is very simple: * * #!/usr/bin/perl - * + * * sub on_osc_seq_perl { * my ($term, $osc, $resp) = @_; * if ($osc =~ /^notify;(\S+);(.*)$/) { @@ -560,7 +560,7 @@ update_color_in_grids(struct terminal *term, uint32_t old_color, for (size_t c = 0; c < term->grid->num_cols; c++) { struct cell *cell = &row->cells[c]; - if (cell->attrs.have_fg && + if (cell->attrs.fg_src != COLOR_DEFAULT && cell->attrs.fg == old_color) { cell->attrs.fg = new_color; @@ -568,7 +568,7 @@ update_color_in_grids(struct terminal *term, uint32_t old_color, row->dirty = true; } - if ( cell->attrs.have_bg && + if (cell->attrs.bg_src != COLOR_DEFAULT && cell->attrs.bg == old_color) { cell->attrs.bg = new_color; diff --git a/render.c b/render.c index 46cc0f75..2710c0a2 100644 --- a/render.c +++ b/render.c @@ -494,14 +494,14 @@ render_cell(struct terminal *term, pixman_image_t *pix, _bg = term->colors.selection_bg; } else { /* Use cell specific color, if set, otherwise the default colors (possible reversed) */ - _fg = cell->attrs.have_fg ? cell->attrs.fg : term->reverse ? term->colors.bg : term->colors.fg; - _bg = cell->attrs.have_bg ? cell->attrs.bg : term->reverse ? term->colors.fg : term->colors.bg; + _fg = cell->attrs.fg_src != COLOR_DEFAULT ? cell->attrs.fg : term->reverse ? term->colors.bg : term->colors.fg; + _bg = cell->attrs.bg_src != COLOR_DEFAULT ? cell->attrs.bg : term->reverse ? term->colors.fg : term->colors.bg; if (cell->attrs.reverse ^ is_selected) { uint32_t swap = _fg; _fg = _bg; _bg = swap; - } else if (!cell->attrs.have_bg) + } else if (cell->attrs.bg_src == COLOR_DEFAULT) alpha = term->colors.alpha; } diff --git a/sixel.c b/sixel.c index acb3f31b..e50fa511 100644 --- a/sixel.c +++ b/sixel.c @@ -70,7 +70,7 @@ sixel_init(struct terminal *term, int p1, int p2, int p3) term->sixel.default_bg = term->sixel.transparent_bg ? 0x00000000u - : 0xffu << 24 | (term->vt.attrs.have_bg + : 0xffu << 24 | (term->vt.attrs.bg_src != COLOR_DEFAULT ? term->vt.attrs.bg : term->colors.bg); diff --git a/terminal.c b/terminal.c index df8128fc..bf13dd46 100644 --- a/terminal.c +++ b/terminal.c @@ -1770,11 +1770,13 @@ erase_cell_range(struct terminal *term, struct row *row, int start, int end) row->dirty = true; - if (unlikely(term->vt.attrs.have_bg)) { + const enum color_source bg_src = term->vt.attrs.bg_src; + + if (unlikely(bg_src != COLOR_DEFAULT)) { for (int col = start; col <= end; col++) { struct cell *c = &row->cells[col]; c->wc = 0; - c->attrs = (struct attributes){.have_bg = 1, .bg = term->vt.attrs.bg}; + c->attrs = (struct attributes){.bg_src = bg_src, .bg = term->vt.attrs.bg}; } } else memset(&row->cells[start], 0, (end - start + 1) * sizeof(row->cells[0])); diff --git a/terminal.h b/terminal.h index 3516551f..b0526803 100644 --- a/terminal.h +++ b/terminal.h @@ -24,6 +24,13 @@ #include "shm.h" #include "wayland.h" +enum color_source { + COLOR_DEFAULT, + COLOR_BASE16, + COLOR_BASE256, + COLOR_RGB, +}; + /* * Note: we want the cells to be as small as possible. Larger cells * means fewer scrollback lines (or performance drops due to cache @@ -43,12 +50,11 @@ struct attributes { uint32_t fg:24; bool clean:1; + enum color_source fg_src:2; + enum color_source bg_src:2; bool confined:1; - bool have_fg:1; - bool have_bg:1; bool selected:1; bool url:1; - uint32_t reserved:2; uint32_t bg:24; }; static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large"); From 1634b71e6fb8f2f0567cd39ebf2698e1a48a065b Mon Sep 17 00:00:00 2001 From: Soc Virnyl Silab Estela Date: Sun, 21 Nov 2021 06:56:30 +0800 Subject: [PATCH 13/29] Comment out `selection-*` for consistency - Changes in PaperColor Themes --- themes/PaperColorDark | 4 ++-- themes/PaperColorLight | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/themes/PaperColorDark b/themes/PaperColorDark index 7f3faafa..17d569ac 100644 --- a/themes/PaperColorDark +++ b/themes/PaperColorDark @@ -24,5 +24,5 @@ bright5=ff5faf # bright magenta bright6=00afaf # bright cyan bright7=5f8787 # bright white - selection-foreground=1c1c1c - selection-background=af87d7 + #selection-foreground=1c1c1c + #selection-background=af87d7 diff --git a/themes/PaperColorLight b/themes/PaperColorLight index 4e2ba35d..6e9f59f6 100644 --- a/themes/PaperColorLight +++ b/themes/PaperColorLight @@ -24,5 +24,5 @@ bright5=d75f00 # bright magenta bright6=4c7a5d # bright cyan bright7=005faf # bright white - selection-foreground=eeeeee - selection-background=0087af + #selection-foreground=eeeeee + #selection-background=0087af From 31c1b91a7c215135ec1c4a64e39853db9377fd91 Mon Sep 17 00:00:00 2001 From: Arnavion Date: Sat, 20 Nov 2021 20:49:01 -0800 Subject: [PATCH 14/29] Fix parsing key-bindings config to not read past the end of the string. pipe_argv_from_value itself stripped the cmd-to-exec from ctx->value, so the callers should not be doing that themselves. Fixes #809 --- config.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/config.c b/config.c index 24ed1267..9e290c65 100644 --- a/config.c +++ b/config.c @@ -1639,12 +1639,12 @@ argv_compare(char *const *argv1, char *const *argv2) * filled with {'cmd-to-exec', 'arg1', 'arg2', NULL} * * Returns: - * - ssize_t, number of bytes to strip from 'value' to remove the '[]' + * - ssize_t, number of bytes that were stripped from 'value' to remove the '[]' * enclosed cmd and its arguments, including any subsequent * whitespace characters. I.e. if 'value' is "[cmd] BTN_RIGHT", the * return value is 6 (strlen("[cmd] ")). * - cmd: allocated string containing "cmd arg1 arg2...". Caller frees. - * - argv: allocatd array containing {"cmd", "arg1", "arg2", NULL}. Caller frees. + * - argv: allocated array containing {"cmd", "arg1", "arg2", NULL}. Caller frees. */ static ssize_t pipe_argv_from_value(struct context *ctx, char ***argv) @@ -1729,8 +1729,6 @@ parse_key_binding_section(struct context *ctx, if (pipe_remove_len < 0) return false; - ctx->value += pipe_remove_len; - for (int action = 0; action < action_count; action++) { if (action_map[action] == NULL) continue; @@ -2107,8 +2105,6 @@ parse_section_mouse_bindings(struct context *ctx) if (pipe_remove_len < 0) return false; - value += pipe_remove_len; - for (enum bind_action_normal action = 0; action < BIND_ACTION_COUNT; action++) From 9b232e07f9b24a16d496939ec44c37fb772cd0b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 21 Nov 2021 10:55:57 +0100 Subject: [PATCH 15/29] config: create a copy of the section name The parsing context keeps a pointer to the current section name. This is used when logging errors. However, the pointer was into a buffer allocated by getline(). This buffer are often re-used in the next call to getline(), or free:d. Regardless, the section name pointer is invalid after the next call to getline(), which meant all error messages were logging a correct section name. --- CHANGELOG.md | 1 + config.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b65f0b63..1f7208d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ * Regression: `letter-spacing` resulting in a “not a valid option” error (https://codeberg.org/dnkl/foot/issues/795). +* Regression: bad section name in configuration error messages. ### Security diff --git a/config.c b/config.c index 24ed1267..fdca96cd 100644 --- a/config.c +++ b/config.c @@ -2469,9 +2469,11 @@ parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_ar continue; \ } + char *section_name = xstrdup("main"); + struct context context = { .conf = conf, - .section = "main", + .section = section_name, .path = path, .lineno = 0, .errors_are_fatal = errors_are_fatal, @@ -2539,7 +2541,9 @@ parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_ar error_or_continue(); } - context.section = &key_value[1]; + free(section_name); + section_name = xstrdup(&key_value[1]); + context.section = section_name; /* Process next line */ continue; @@ -2569,10 +2573,12 @@ parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_ar error_or_continue(); } + free(section_name); free(_line); return true; err: + free(section_name); free(_line); return false; } From 9174e6d79a1a57016973ef989ff9366b713b3f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 21 Nov 2021 17:51:46 +0100 Subject: [PATCH 16/29] config: pipe_argv_from_value(): plug memory leak When parsing a key binding with a pipe-argv, we failed to free the argv vector (or to be precise, the strdup:ed entries in the array) when failing to parse the remainder of the binding. --- config.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/config.c b/config.c index eac9d3f5..60029ab9 100644 --- a/config.c +++ b/config.c @@ -1645,9 +1645,9 @@ argv_compare(char *const *argv1, char *const *argv2) * - argv: allocated array containing {"cmd", "arg1", "arg2", NULL}. Caller frees. */ static ssize_t -pipe_argv_from_value(struct context *ctx, char ***argv) +pipe_argv_from_value(struct context *ctx, struct argv *argv) { - *argv = NULL; + argv->args = NULL; if (ctx->value[0] != '[') return 0; @@ -1661,7 +1661,7 @@ pipe_argv_from_value(struct context *ctx, char ***argv) size_t pipe_len = pipe_cmd_end - ctx->value - 1; char *cmd = xstrndup(&ctx->value[1], pipe_len); - if (!tokenize_cmdline(cmd, argv)) { + if (!tokenize_cmdline(cmd, &argv->args)) { LOG_CONTEXTUAL_ERR("syntax error in command line"); free(cmd); return -1; @@ -1721,7 +1721,7 @@ parse_key_binding_section(struct context *ctx, const char *const action_map[static action_count], struct config_key_binding_list *bindings) { - char **pipe_argv; + struct argv pipe_argv; ssize_t pipe_remove_len = pipe_argv_from_value(ctx, &pipe_argv); if (pipe_remove_len < 0) @@ -1736,8 +1736,8 @@ parse_key_binding_section(struct context *ctx, /* Unset binding */ if (strcasecmp(ctx->value, "none") == 0) { - remove_action_from_key_bindings_list(bindings, action, pipe_argv); - free(pipe_argv); + remove_action_from_key_bindings_list(bindings, action, pipe_argv.args); + free_argv(&pipe_argv); return true; } @@ -1746,12 +1746,12 @@ parse_key_binding_section(struct context *ctx, has_key_binding_collisions( ctx, action, action_map, bindings, &key_combos)) { - free(pipe_argv); + free_argv(&pipe_argv); free_key_combo_list(&key_combos); return false; } - remove_action_from_key_bindings_list(bindings, action, pipe_argv); + remove_action_from_key_bindings_list(bindings, action, pipe_argv.args); /* Emit key bindings */ size_t ofs = bindings->count; @@ -1767,9 +1767,7 @@ parse_key_binding_section(struct context *ctx, .modifiers = combo->modifiers, .sym = combo->sym, .pipe = { - .argv = { - .args = pipe_argv, - }, + .argv = pipe_argv, .master_copy = first, }, }; @@ -1784,7 +1782,7 @@ parse_key_binding_section(struct context *ctx, } LOG_CONTEXTUAL_ERR("not a valid action: %s", ctx->key); - free(pipe_argv); + free_argv(&pipe_argv); return false; } @@ -2097,7 +2095,7 @@ parse_section_mouse_bindings(struct context *ctx) const char *key = ctx->key; const char *value = ctx->value; - char **pipe_argv; + struct argv pipe_argv; ssize_t pipe_remove_len = pipe_argv_from_value(ctx, &pipe_argv); if (pipe_remove_len < 0) @@ -2125,7 +2123,7 @@ parse_section_mouse_bindings(struct context *ctx) binding->action = BIND_ACTION_NONE; } } - free(pipe_argv); + free_argv(&pipe_argv); return true; } @@ -2133,7 +2131,7 @@ parse_section_mouse_bindings(struct context *ctx) if (!value_to_mouse_combos(ctx, &key_combos) || has_mouse_binding_collisions(ctx, &key_combos)) { - free(pipe_argv); + free_argv(&pipe_argv); free_key_combo_list(&key_combos); return false; } @@ -2143,9 +2141,9 @@ parse_section_mouse_bindings(struct context *ctx) struct config_mouse_binding *binding = &conf->bindings.mouse.arr[i]; if (binding->action == action && - ((binding->pipe.argv.args == NULL && pipe_argv == NULL) || - (binding->pipe.argv.args != NULL && pipe_argv != NULL && - argv_compare(binding->pipe.argv.args, pipe_argv) == 0))) + ((binding->pipe.argv.args == NULL && pipe_argv.args == NULL) || + (binding->pipe.argv.args != NULL && pipe_argv.args != NULL && + argv_compare(binding->pipe.argv.args, pipe_argv.args) == 0))) { if (binding->pipe.master_copy) free_argv(&binding->pipe.argv); @@ -2169,9 +2167,7 @@ parse_section_mouse_bindings(struct context *ctx) .button = combo->m.button, .count = combo->m.count, .pipe = { - .argv = { - .args = pipe_argv, - }, + .argv = pipe_argv, .master_copy = first, }, }; @@ -2185,7 +2181,7 @@ parse_section_mouse_bindings(struct context *ctx) } LOG_CONTEXTUAL_ERR("not a valid option: %s", key); - free(pipe_argv); + free_argv(&pipe_argv); return false; } From cdb2d90b893f35326146dbc657952265f1bf61ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 21 Nov 2021 18:04:36 +0100 Subject: [PATCH 17/29] =?UTF-8?q?config:=20argv=5Fcompare():=20change=20pa?= =?UTF-8?q?rameters=20to=20=E2=80=98struct=20argv=20*=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of passing raw char** pointers to argv_compare(), pass pointers to ‘struct argv’. This lets argv_compare() handle both argv’s being NULL, or one of them being NULL. That is, the caller no longer needs to check that both argv’s are non-NULL before calling argv_compare(). Furthermore, update has_key_binding_collisions() to require the pipe argv to match, in addition to matching the ‘action’, when allowing a key collision. --- config.c | 86 ++++++++++++++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 40 deletions(-) diff --git a/config.c b/config.c index 60029ab9..68061cc3 100644 --- a/config.c +++ b/config.c @@ -1557,11 +1557,40 @@ err: return false; } +static int +argv_compare(const struct argv *argv1, const struct argv *argv2) +{ + if (argv1->args == NULL && argv2->args == NULL) + return 0; + + if (argv1->args == NULL) + return -1; + if (argv2->args == NULL) + return 1; + + for (size_t i = 0; ; i++) { + if (argv1->args[i] == NULL && argv2->args[i] == NULL) + return 0; + if (argv1->args[i] == NULL) + return -1; + if (argv2->args[i] == NULL) + return 1; + + int ret = strcmp(argv1->args[i], argv2->args[i]); + if (ret != 0) + return ret; + } + + BUG("unexpected loop break"); + return 1; +} + static bool has_key_binding_collisions(struct context *ctx, int action, const char *const action_map[], const struct config_key_binding_list *bindings, - const struct key_combo_list *key_combos) + const struct key_combo_list *key_combos, + const struct argv *pipe_argv) { for (size_t j = 0; j < bindings->count; j++) { const struct config_key_binding *combo1 = &bindings->arr[j]; @@ -1569,8 +1598,10 @@ has_key_binding_collisions(struct context *ctx, if (combo1->action == BIND_ACTION_NONE) continue; - if (combo1->action == action) - continue; + if (combo1->action == action) { + if (argv_compare(&combo1->pipe.argv, pipe_argv) == 0) + continue; + } for (size_t i = 0; i < key_combos->count; i++) { const struct key_combo *combo2 = &key_combos->combos[i]; @@ -1600,29 +1631,6 @@ has_key_binding_collisions(struct context *ctx, return false; } -static int -argv_compare(char *const *argv1, char *const *argv2) -{ - xassert(argv1 != NULL); - xassert(argv2 != NULL); - - for (size_t i = 0; ; i++) { - if (argv1[i] == NULL && argv2[i] == NULL) - return 0; - if (argv1[i] == NULL) - return -1; - if (argv2[i] == NULL) - return 1; - - int ret = strcmp(argv1[i], argv2[i]); - if (ret != 0) - return ret; - } - - BUG("unexpected loop break"); - return 1; -} - /* * Parses a key binding value on the form * "[cmd-to-exec arg1 arg2] Mods+Key" @@ -1680,7 +1688,7 @@ pipe_argv_from_value(struct context *ctx, struct argv *argv) static void NOINLINE remove_action_from_key_bindings_list(struct config_key_binding_list *bindings, - int action, char **pipe_argv) + int action, const struct argv *pipe_argv) { size_t remove_first_idx = 0; size_t remove_count = 0; @@ -1688,11 +1696,10 @@ remove_action_from_key_bindings_list(struct config_key_binding_list *bindings, for (size_t i = 0; i < bindings->count; i++) { struct config_key_binding *binding = &bindings->arr[i]; - if (binding->action == action && - ((binding->pipe.argv.args == NULL && pipe_argv == NULL) || - (binding->pipe.argv.args != NULL && pipe_argv != NULL && - argv_compare(binding->pipe.argv.args, pipe_argv) == 0))) - { + if (binding->action != action) + continue; + + if (argv_compare(&binding->pipe.argv, pipe_argv) == 0) { if (remove_count++ == 0) remove_first_idx = i; @@ -1736,7 +1743,7 @@ parse_key_binding_section(struct context *ctx, /* Unset binding */ if (strcasecmp(ctx->value, "none") == 0) { - remove_action_from_key_bindings_list(bindings, action, pipe_argv.args); + remove_action_from_key_bindings_list(bindings, action, &pipe_argv); free_argv(&pipe_argv); return true; } @@ -1744,14 +1751,14 @@ parse_key_binding_section(struct context *ctx, struct key_combo_list key_combos = {0}; if (!value_to_key_combos(ctx, &key_combos) || has_key_binding_collisions( - ctx, action, action_map, bindings, &key_combos)) + ctx, action, action_map, bindings, &key_combos, &pipe_argv)) { free_argv(&pipe_argv); free_key_combo_list(&key_combos); return false; } - remove_action_from_key_bindings_list(bindings, action, pipe_argv.args); + remove_action_from_key_bindings_list(bindings, action, &pipe_argv); /* Emit key bindings */ size_t ofs = bindings->count; @@ -2140,11 +2147,10 @@ parse_section_mouse_bindings(struct context *ctx) for (size_t i = 0; i < conf->bindings.mouse.count; i++) { struct config_mouse_binding *binding = &conf->bindings.mouse.arr[i]; - if (binding->action == action && - ((binding->pipe.argv.args == NULL && pipe_argv.args == NULL) || - (binding->pipe.argv.args != NULL && pipe_argv.args != NULL && - argv_compare(binding->pipe.argv.args, pipe_argv.args) == 0))) - { + if (binding->action != action) + continue; + + if (argv_compare(&binding->pipe.argv, &pipe_argv) == 0) { if (binding->pipe.master_copy) free_argv(&binding->pipe.argv); binding->action = BIND_ACTION_NONE; From 7522c2d2118183a0428ae471b20fe23276307de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 21 Nov 2021 18:09:10 +0100 Subject: [PATCH 18/29] changelog: pipe-* key bindings not being parsed correctly --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f7208d4..47330812 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,9 @@ * Regression: `letter-spacing` resulting in a “not a valid option” error (https://codeberg.org/dnkl/foot/issues/795). * Regression: bad section name in configuration error messages. +* Regression: `pipe-*` key bindings not being parsed correctly, + resulting in invalid error messages + (https://codeberg.org/dnkl/foot/issues/809). ### Security From 1a0de0017f7b2061bad0b695a98963ca0f274e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 20 Nov 2021 13:42:07 +0100 Subject: [PATCH 19/29] grid: add grid_row_uri_range_erase() This function handles erasing of an URI range. That is, a range of the row is being either erased, or overwritten (from the URI perspective, these two are the same thing). We handle both partial overwrites (split up, or truncate URI), as well as complete overwrites (remove URI). --- grid.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++-- grid.h | 3 +- terminal.c | 74 +------------------------ 3 files changed, 155 insertions(+), 78 deletions(-) diff --git a/grid.c b/grid.c index 733d8ded..d98c4696 100644 --- a/grid.c +++ b/grid.c @@ -236,7 +236,7 @@ grid_resize_without_reflow( .id = it->item.id, .uri = xstrdup(it->item.uri), }; - grid_row_add_uri_range(new_row, range); + grid_row_uri_range_add(new_row, range); } } @@ -303,7 +303,7 @@ reflow_uri_range_start(struct row_uri_range *range, struct row *new_row, .uri = range->uri, }; range->uri = NULL; - grid_row_add_uri_range(new_row, new_range); + grid_row_uri_range_add(new_row, new_range); } static void @@ -366,7 +366,7 @@ _line_wrap(struct grid *old_grid, struct row **new_grid, struct row *row, .id = range->id, .uri = xstrdup(range->uri), }; - grid_row_add_uri_range(new_row, new_range); + grid_row_uri_range_add(new_row, new_range); } } @@ -845,14 +845,160 @@ ensure_row_has_extra_data(struct row *row) } void -grid_row_add_uri_range(struct row *row, struct row_uri_range range) +grid_row_uri_range_add(struct row *row, struct row_uri_range range) { ensure_row_has_extra_data(row); tll_rforeach(row->extra->uri_ranges, it) { if (it->item.end < range.start) { tll_insert_after(row->extra->uri_ranges, it, range); - return; + goto out; } } + tll_push_front(row->extra->uri_ranges, range); + +out: + ; +#if defined(_DEBUG) + tll_foreach(row->extra->uri_ranges, it1) { + tll_foreach(row->extra->uri_ranges, it2) { + if (&it1->item == &it2->item) + continue; + + xassert(it1->item.start != it2->item.start); + xassert(it1->item.start != it2->item.end); + xassert(it1->item.end != it2->item.start); + xassert(it1->item.end != it2->item.end); + } + } +#endif +} + +void +grid_row_uri_range_erase(struct row *row, int start, int end) +{ + if (likely(row->extra == NULL)) + return; + + /* Split up, or remove, URI ranges affected by the erase */ + tll_foreach(row->extra->uri_ranges, it) { + if (it->item.start > end) { + /* This range, and all subsequent ranges, start *after* + * the erase range */ + break; + } + + if (it->item.start < start && it->item.end >= start) { + /* + * URI crosses the erase *start* point. + * + * Create a new range for the URI part *before* the erased + * cells. + * + * Also modify this URI range’s start point so that we can + * remove it below. + */ + struct row_uri_range range_before = { + .start = it->item.start, + .end = start - 1, + .id = it->item.id, + .uri = it->item.uri != NULL ? xstrdup(it->item.uri) : NULL, + }; + tll_insert_before(row->extra->uri_ranges, it, range_before); + it->item.start = start; + } + + if (it->item.start <= end && it->item.end > end) { + /* + * URI crosses the erase *end* point. + * + * Create a new range for the URI part *after* the erased + * cells. + * + * Also modify the URI range’s end point so that we can + * remove it below. + */ + struct row_uri_range range_after = { + .start = end + 1, + .end = it->item.end, + .id = it->item.id, + .uri = it->item.uri != NULL ? xstrdup(it->item.uri) : NULL, + }; + tll_insert_before(row->extra->uri_ranges, it, range_after); + it->item.end = end; + } + + if (it->item.start >= start && it->item.end <= end) { + /* URI range completey covered by the erase - remove it */ + free(it->item.uri); + tll_remove(row->extra->uri_ranges, it); + } + } +} + +UNITTEST +{ + struct row_data row_data = {.uri_ranges = tll_init()}; + struct row row = {.extra = &row_data}; + +#define row_has_no_overlapping_uris(row) \ + do { \ + tll_foreach((row)->extra->uri_ranges, it1) { \ + tll_foreach((row)->extra->uri_ranges, it2) { \ + if (&it1->item == &it2->item) \ + continue; \ + xassert(it1->item.start != it2->item.start); \ + xassert(it1->item.start != it2->item.end); \ + xassert(it1->item.end != it2->item.start); \ + xassert(it1->item.end != it2->item.end); \ + } \ + } \ + } while (0) + + grid_row_uri_range_add(&row, (struct row_uri_range){1, 10}); + xassert(tll_length(row_data.uri_ranges) == 1); + xassert(tll_front(row_data.uri_ranges).start == 1); + xassert(tll_front(row_data.uri_ranges).end == 10); + row_has_no_overlapping_uris(&row); + + grid_row_uri_range_add(&row, (struct row_uri_range){11, 20}); + xassert(tll_length(row_data.uri_ranges) == 2); + xassert(tll_back(row_data.uri_ranges).start == 11); + xassert(tll_back(row_data.uri_ranges).end == 20); + row_has_no_overlapping_uris(&row); + + /* Erase both URis */ + grid_row_uri_range_erase(&row, 1, 20); + xassert(tll_length(row_data.uri_ranges) == 0); + row_has_no_overlapping_uris(&row); + + /* Two URIs, then erase second half of the first, first half of + the second */ + grid_row_uri_range_add(&row, (struct row_uri_range){1, 10}); + grid_row_uri_range_add(&row, (struct row_uri_range){11, 20}); + grid_row_uri_range_erase(&row, 5, 15); + xassert(tll_length(row_data.uri_ranges) == 2); + xassert(tll_front(row_data.uri_ranges).start == 1); + xassert(tll_front(row_data.uri_ranges).end == 4); + xassert(tll_back(row_data.uri_ranges).start == 16); + xassert(tll_back(row_data.uri_ranges).end == 20); + row_has_no_overlapping_uris(&row); + + tll_pop_back(row_data.uri_ranges); + tll_pop_back(row_data.uri_ranges); + xassert(tll_length(row_data.uri_ranges) == 0); + + /* One URI, erase middle part of it */ + grid_row_uri_range_add(&row, (struct row_uri_range){1, 10}); + grid_row_uri_range_erase(&row, 5, 6); + xassert(tll_length(row_data.uri_ranges) == 2); + xassert(tll_front(row_data.uri_ranges).start == 1); + xassert(tll_front(row_data.uri_ranges).end == 4); + xassert(tll_back(row_data.uri_ranges).start == 7); + xassert(tll_back(row_data.uri_ranges).end == 10); + row_has_no_overlapping_uris(&row); + +#undef row_has_no_overlapping_uris + + tll_free(row_data.uri_ranges); } diff --git a/grid.h b/grid.h index 3537ddb5..e2cd3a63 100644 --- a/grid.h +++ b/grid.h @@ -74,7 +74,8 @@ grid_row_in_view(struct grid *grid, int row_no) return row; } -void grid_row_add_uri_range(struct row *row, struct row_uri_range range); +void grid_row_uri_range_add(struct row *row, struct row_uri_range range); +void grid_row_uri_range_erase(struct row *row, int start, int end); static inline void grid_row_uri_range_destroy(struct row_uri_range *range) diff --git a/terminal.c b/terminal.c index bf13dd46..3100e3a4 100644 --- a/terminal.c +++ b/terminal.c @@ -1781,63 +1781,7 @@ erase_cell_range(struct terminal *term, struct row *row, int start, int end) } else memset(&row->cells[start], 0, (end - start + 1) * sizeof(row->cells[0])); - if (likely(row->extra == NULL)) - return; - - /* Split up, or remove, URI ranges affected by the erase */ - tll_foreach(row->extra->uri_ranges, it) { - if (it->item.start > end) { - /* This range, and all subsequent ranges, start *after* - * the erase range */ - break; - } - - if (it->item.start < start && it->item.end >= start) { - /* - * URI crosses the erase *start* point. - * - * Create a new range for the URI part *before* the erased - * cells. - * - * Also modify this URI range’s start point so that we can - * remove it below. - */ - struct row_uri_range range_before = { - .start = it->item.start, - .end = start - 1, - .id = it->item.id, - .uri = xstrdup(it->item.uri), - }; - tll_insert_before(row->extra->uri_ranges, it, range_before); - it->item.start = start; - } - - if (it->item.start <= end && it->item.end > end) { - /* - * URI crosses the erase *end* point. - * - * Create a new range for the URI part *after* the erased - * cells. - * - * Also modify the URI range’s end point so that we can - * remove it below. - */ - struct row_uri_range range_after = { - .start = end + 1, - .end = it->item.end, - .id = it->item.id, - .uri = xstrdup(it->item.uri), - }; - tll_insert_before(row->extra->uri_ranges, it, range_after); - it->item.end = end; - } - - if (it->item.start >= start && it->item.end <= end) { - /* URI range completey covered by the erase - remove it */ - free(it->item.uri); - tll_remove(row->extra->uri_ranges, it); - } - } + grid_row_uri_range_erase(row, start, end); } static inline void @@ -3590,21 +3534,7 @@ term_osc8_close(struct terminal *term) .id = term->vt.osc8.id, .uri = xstrdup(term->vt.osc8.uri), }; - grid_row_add_uri_range(row, range); - -#if defined(_DEBUG) - tll_foreach(row->extra->uri_ranges, it1) { - tll_foreach(row->extra->uri_ranges, it2) { - if (&it1->item == &it2->item) - continue; - - xassert(it1->item.start != it2->item.start); - xassert(it1->item.start != it2->item.end); - xassert(it1->item.end != it2->item.start); - xassert(it1->item.end != it2->item.end); - } - } -#endif + grid_row_uri_range_add(row, range); start_col = 0; if (r == end.row) From 09a331857a8480ebce4aec83e665a0c68e6a03c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 20 Nov 2021 13:54:30 +0100 Subject: [PATCH 20/29] term: call grid_row_uri_range_erase() when printing cells --- terminal.c | 53 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 19 deletions(-) diff --git a/terminal.c b/terminal.c index 3100e3a4..7bf912e8 100644 --- a/terminal.c +++ b/terminal.c @@ -3162,6 +3162,8 @@ term_print(struct terminal *term, wchar_t wc, int width) { xassert(width > 0); + struct grid *grid = term->grid; + if (unlikely(term->charsets.set[term->charsets.selected] == CHARSET_GRAPHIC) && wc >= 0x60 && wc <= 0x7e) { @@ -3180,24 +3182,26 @@ term_print(struct terminal *term, wchar_t wc, int width) print_linewrap(term); print_insert(term, width); + int col = grid->cursor.point.col; + if (unlikely(width > 1) && likely(term->auto_margin) && - term->grid->cursor.point.col + width > term->cols) + col + width > term->cols) { /* Multi-column character that doesn't fit on current line - * pad with spacers */ - for (size_t i = term->grid->cursor.point.col; i < term->cols; i++) + for (size_t i = col; i < term->cols; i++) print_spacer(term, i, 0); /* And force a line-wrap */ - term->grid->cursor.lcf = 1; + grid->cursor.lcf = 1; print_linewrap(term); } sixel_overwrite_at_cursor(term, width); /* *Must* get current cell *after* linewrap+insert */ - struct row *row = term->grid->cur_row; - struct cell *cell = &row->cells[term->grid->cursor.point.col]; + struct row *row = grid->cur_row; + struct cell *cell = &row->cells[col]; cell->wc = term->vt.last_printed = wc; cell->attrs = term->vt.attrs; @@ -3205,18 +3209,22 @@ term_print(struct terminal *term, wchar_t wc, int width) row->dirty = true; row->linebreak = true; + grid_row_uri_range_erase(row, col, col + width - 1); + /* Advance cursor the 'additional' columns while dirty:ing the cells */ - for (int i = 1; i < width && term->grid->cursor.point.col < term->cols - 1; i++) { - term->grid->cursor.point.col++; - print_spacer(term, term->grid->cursor.point.col, width - i); + for (int i = 1; i < width && col < term->cols - 1; i++) { + col++; + print_spacer(term, col, width - i); } /* Advance cursor */ - if (unlikely(++term->grid->cursor.point.col >= term->cols)) { - term->grid->cursor.lcf = true; - term->grid->cursor.point.col--; + if (unlikely(++col >= term->cols)) { + grid->cursor.lcf = true; + col--; } else - xassert(!term->grid->cursor.lcf); + xassert(!grid->cursor.lcf); + + grid->cursor.point.col = col; } static void @@ -3228,15 +3236,18 @@ ascii_printer_generic(struct terminal *term, wchar_t wc) static void ascii_printer_fast(struct terminal *term, wchar_t wc) { + struct grid *grid = term->grid; + xassert(term->charsets.set[term->charsets.selected] == CHARSET_ASCII); xassert(!term->insert_mode); - xassert(tll_length(term->grid->sixel_images) == 0); + xassert(tll_length(grid->sixel_images) == 0); print_linewrap(term); /* *Must* get current cell *after* linewrap+insert */ - struct row *row = term->grid->cur_row; - struct cell *cell = &row->cells[term->grid->cursor.point.col]; + int col = grid->cursor.point.col; + struct row *row = grid->cur_row; + struct cell *cell = &row->cells[col]; cell->wc = term->vt.last_printed = wc; cell->attrs = term->vt.attrs; @@ -3244,12 +3255,16 @@ ascii_printer_fast(struct terminal *term, wchar_t wc) row->dirty = true; row->linebreak = true; + grid_row_uri_range_erase(row, col, col); + /* Advance cursor */ - if (unlikely(++term->grid->cursor.point.col >= term->cols)) { - term->grid->cursor.lcf = true; - term->grid->cursor.point.col--; + if (unlikely(++col >= term->cols)) { + grid->cursor.lcf = true; + col--; } else - xassert(!term->grid->cursor.lcf); + xassert(!grid->cursor.lcf); + + grid->cursor.point.col = col; } static void From 53fc1a1cb12fc2367a1ff76e723be52603c39cdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 20 Nov 2021 14:39:32 +0100 Subject: [PATCH 21/29] term: tail-call optimize calls to grid_row_uri_range_erase() --- terminal.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/terminal.c b/terminal.c index 7bf912e8..120f7ac3 100644 --- a/terminal.c +++ b/terminal.c @@ -3209,7 +3209,7 @@ term_print(struct terminal *term, wchar_t wc, int width) row->dirty = true; row->linebreak = true; - grid_row_uri_range_erase(row, col, col + width - 1); + int uri_start = col; /* Advance cursor the 'additional' columns while dirty:ing the cells */ for (int i = 1; i < width && col < term->cols - 1; i++) { @@ -3225,6 +3225,9 @@ term_print(struct terminal *term, wchar_t wc, int width) xassert(!grid->cursor.lcf); grid->cursor.point.col = col; + + if (unlikely(row->extra != NULL)) + grid_row_uri_range_erase(row, uri_start, uri_start + width - 1); } static void @@ -3255,7 +3258,7 @@ ascii_printer_fast(struct terminal *term, wchar_t wc) row->dirty = true; row->linebreak = true; - grid_row_uri_range_erase(row, col, col); + int uri_start = col; /* Advance cursor */ if (unlikely(++col >= term->cols)) { @@ -3265,6 +3268,9 @@ ascii_printer_fast(struct terminal *term, wchar_t wc) xassert(!grid->cursor.lcf); grid->cursor.point.col = col; + + if (unlikely(row->extra != NULL)) + grid_row_uri_range_erase(row, uri_start, uri_start); } static void From 0be55ef74c38140877d82c66159f112517b3825c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 20 Nov 2021 14:40:03 +0100 Subject: [PATCH 22/29] term: erase_cell_range(): check if row->extra != NULL --- terminal.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/terminal.c b/terminal.c index 120f7ac3..ea7da6e3 100644 --- a/terminal.c +++ b/terminal.c @@ -1781,7 +1781,8 @@ erase_cell_range(struct terminal *term, struct row *row, int start, int end) } else memset(&row->cells[start], 0, (end - start + 1) * sizeof(row->cells[0])); - grid_row_uri_range_erase(row, start, end); + if (unlikely(row->extra != NULL)) + grid_row_uri_range_erase(row, start, end); } static inline void From 503c2ebd50caaf1d50b57c42d319973277a583e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 20 Nov 2021 14:40:16 +0100 Subject: [PATCH 23/29] grid: row_uri_range_erase(): assume caller has checked row->extra != NULL --- grid.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/grid.c b/grid.c index d98c4696..22405356 100644 --- a/grid.c +++ b/grid.c @@ -877,8 +877,7 @@ out: void grid_row_uri_range_erase(struct row *row, int start, int end) { - if (likely(row->extra == NULL)) - return; + xassert(row->extra != NULL); /* Split up, or remove, URI ranges affected by the erase */ tll_foreach(row->extra->uri_ranges, it) { From f27ccd999ef6fcf10f99aed9f6aec449fe071551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 20 Nov 2021 15:46:20 +0100 Subject: [PATCH 24/29] grid: refactor grid_row_uri_range_erase() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old algorithm always created a new URI, followed by (maybe) removing the existing URI, when an URI needed to be modified. That is, if e.g. the tail of an URI was being erased, the old algorithm would create a new URI for the part of the URI that should remain, and then removed the old URI. This isn’t very effective. The new algorithm instead identifies all possible overlap cases, and handles each one differently: * URI ends *before* erase range starts - continue with the next URI without further checks * URI starts *after* the erase range ends - return, we’re done * Erase range erases the entire URI - remove the URI * Erase range erases a part in the middle - split the URI * Erase range erases the head of the URI - adjust the URI’s start * Erase range erases the tail of the URI - adjust the URI’s end --- grid.c | 81 +++++++++++++++++++++++++--------------------------------- 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/grid.c b/grid.c index 22405356..459fa369 100644 --- a/grid.c +++ b/grid.c @@ -878,60 +878,49 @@ void grid_row_uri_range_erase(struct row *row, int start, int end) { xassert(row->extra != NULL); + xassert(start <= end); /* Split up, or remove, URI ranges affected by the erase */ tll_foreach(row->extra->uri_ranges, it) { - if (it->item.start > end) { - /* This range, and all subsequent ranges, start *after* - * the erase range */ - break; - } + struct row_uri_range *old = &it->item; - if (it->item.start < start && it->item.end >= start) { - /* - * URI crosses the erase *start* point. - * - * Create a new range for the URI part *before* the erased - * cells. - * - * Also modify this URI range’s start point so that we can - * remove it below. - */ - struct row_uri_range range_before = { - .start = it->item.start, - .end = start - 1, - .id = it->item.id, - .uri = it->item.uri != NULL ? xstrdup(it->item.uri) : NULL, - }; - tll_insert_before(row->extra->uri_ranges, it, range_before); - it->item.start = start; - } + if (old->end < start) + continue; - if (it->item.start <= end && it->item.end > end) { - /* - * URI crosses the erase *end* point. - * - * Create a new range for the URI part *after* the erased - * cells. - * - * Also modify the URI range’s end point so that we can - * remove it below. - */ - struct row_uri_range range_after = { - .start = end + 1, - .end = it->item.end, - .id = it->item.id, - .uri = it->item.uri != NULL ? xstrdup(it->item.uri) : NULL, - }; - tll_insert_before(row->extra->uri_ranges, it, range_after); - it->item.end = end; - } + if (old->start > end) + return; - if (it->item.start >= start && it->item.end <= end) { - /* URI range completey covered by the erase - remove it */ - free(it->item.uri); + if (start <= old->start && end >= old->end) { + /* Erase range covers URI completely - remove it */ + grid_row_uri_range_destroy(old); tll_remove(row->extra->uri_ranges, it); } + + else if (start > old->start && end < old->end) { + /* Erase range erases a part in the middle of the URI */ + struct row_uri_range old_tail = { + .start = end + 1, + .end = old->end, + .id = old->id, + .uri = old->uri != NULL ? xstrdup(old->uri) : NULL, + }; + tll_insert_after(row->extra->uri_ranges, it, old_tail); + old->end = start - 1; + return; /* There can be no more URIs affected by the erase range */ + } + + else if (start <= old->start && end >= old->start) { + /* Erase range erases the head of the URI */ + xassert(start <= old->start); + old->start = end + 1; + return; /* There can be no more overlapping URIs */ + } + + else if (start <= old->end && end >= old->end) { + /* Erase range erases the tail of the URI */ + xassert(end >= old->end); + old->end = start - 1; + } } } From ad8d1bf25c73be4e5561b49e44de663600db4f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 20 Nov 2021 18:10:45 +0100 Subject: [PATCH 25/29] changelog: osc-8 data not being cleared when cell is overwritten --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47330812..3dfd029e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,8 @@ * Regression: `pipe-*` key bindings not being parsed correctly, resulting in invalid error messages (https://codeberg.org/dnkl/foot/issues/809). +* OSC-8 data not being cleared when cell is overwritten + (https://codeberg.org/dnkl/foot/issues/804). ### Security From 3c6239c66f0ea758655a891492073fd3549cd877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 21 Nov 2021 14:13:27 +0100 Subject: [PATCH 26/29] term: print: re-order col/row/cell assigments --- terminal.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/terminal.c b/terminal.c index ea7da6e3..1da97335 100644 --- a/terminal.c +++ b/terminal.c @@ -3202,15 +3202,14 @@ term_print(struct terminal *term, wchar_t wc, int width) /* *Must* get current cell *after* linewrap+insert */ struct row *row = grid->cur_row; - struct cell *cell = &row->cells[col]; - - cell->wc = term->vt.last_printed = wc; - cell->attrs = term->vt.attrs; - row->dirty = true; row->linebreak = true; - int uri_start = col; + struct cell *cell = &row->cells[col]; + cell->wc = term->vt.last_printed = wc; + cell->attrs = term->vt.attrs; + + const int uri_start = col; /* Advance cursor the 'additional' columns while dirty:ing the cells */ for (int i = 1; i < width && col < term->cols - 1; i++) { @@ -3250,16 +3249,16 @@ ascii_printer_fast(struct terminal *term, wchar_t wc) /* *Must* get current cell *after* linewrap+insert */ int col = grid->cursor.point.col; + const int uri_start = col; + struct row *row = grid->cur_row; - struct cell *cell = &row->cells[col]; - - cell->wc = term->vt.last_printed = wc; - cell->attrs = term->vt.attrs; - row->dirty = true; row->linebreak = true; - int uri_start = col; + struct cell *cell = &row->cells[col]; + cell->wc = term->vt.last_printed = wc; + cell->attrs = term->vt.attrs; + /* Advance cursor */ if (unlikely(++col >= term->cols)) { From 65944906bf87bf7ea6c404526fa3f3f88f96194a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 21 Nov 2021 14:14:08 +0100 Subject: [PATCH 27/29] term: print: erase URI range when printing right-margin padding --- terminal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/terminal.c b/terminal.c index 1da97335..21733cf5 100644 --- a/terminal.c +++ b/terminal.c @@ -3193,6 +3193,9 @@ term_print(struct terminal *term, wchar_t wc, int width) for (size_t i = col; i < term->cols; i++) print_spacer(term, i, 0); + if (grid->cur_row->extra != NULL) + grid_row_uri_range_erase(grid->cur_row, col, term->cols - 1); + /* And force a line-wrap */ grid->cursor.lcf = 1; print_linewrap(term); From 315769b50bd3df2d41cb130200bc03eb64be4078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 21 Nov 2021 14:46:05 +0100 Subject: [PATCH 28/29] Revert "term: print: erase URI range when printing right-margin padding" This reverts commit bb967fd2f927d90e881ed2b05db1fa6946033580. --- terminal.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/terminal.c b/terminal.c index 21733cf5..1da97335 100644 --- a/terminal.c +++ b/terminal.c @@ -3193,9 +3193,6 @@ term_print(struct terminal *term, wchar_t wc, int width) for (size_t i = col; i < term->cols; i++) print_spacer(term, i, 0); - if (grid->cur_row->extra != NULL) - grid_row_uri_range_erase(grid->cur_row, col, term->cols - 1); - /* And force a line-wrap */ grid->cursor.lcf = 1; print_linewrap(term); From 5b176dd5d6e55dd666b0e424cae20e7a97e3bede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 21 Nov 2021 14:47:24 +0100 Subject: [PATCH 29/29] =?UTF-8?q?term:=20print:=20reset=20=E2=80=98col?= =?UTF-8?q?=E2=80=99=20after=20padding=20the=20right=20margin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terminal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/terminal.c b/terminal.c index 1da97335..4cc9ed09 100644 --- a/terminal.c +++ b/terminal.c @@ -3196,6 +3196,7 @@ term_print(struct terminal *term, wchar_t wc, int width) /* And force a line-wrap */ grid->cursor.lcf = 1; print_linewrap(term); + col = 0; } sixel_overwrite_at_cursor(term, width);