From 4fd682b4e8d985ce25d2bd599c1d855bc1489650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 10 Mar 2026 07:59:40 +0100 Subject: [PATCH 01/11] meson: clang: add -Wno-wc2y-extensions Recent clang versions warn on __COUNTER__, unless compiling with -std=c2y (which breaks other things). "Fixes" '__COUNTER__' is a C2y extension (__COUNTER__ is used by our UNITTEST macro). --- meson.build | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/meson.build b/meson.build index 66b3d6bc..16e8e3c0 100644 --- a/meson.build +++ b/meson.build @@ -12,6 +12,11 @@ is_debug_build = get_option('buildtype').startswith('debug') cc = meson.get_compiler('c') +# Newer clang versions warns when using __COUNTER__ without -std=c2y +if cc.has_argument('-Wc2y-extensions') + add_project_arguments('-Wno-c2y-extensions', language: 'c') +endif + if cc.has_function('memfd_create', args: ['-D_GNU_SOURCE'], prefix: '#include ') From 657db18a4ec4df93689c3eaae03b70f851724001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 10 Mar 2026 07:46:03 +0100 Subject: [PATCH 02/11] wayland: do all surface unmap and roundtrips before waiting for pre-apply damage The pre-apply damage thread may be running when we destroy a terminal instance, and we need to wait for it to finish before destroying the underlying buffer. c291194a4e593bbbb91420e81fa0111508084448 did this, but failed to realize the thread may get re-started in the roundtrips done later in wayl_win_destroy(); after the wait added in c291194a4e593bbbb91420e81fa0111508084448, we unmap all surfaces (including the main grid), and roundtrip. This means the compositor will release the currently active buffer, and that means _we_ will trigger the pre-apply damage thread on it. This introduces a race, where wayl_win_destroy() may reach its shm_purge() calls before the pre-apply damage thread has finished. That typically causes foot to crash. Closes #2288 --- CHANGELOG.md | 3 +++ wayland.c | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30a73369..30b3e1e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -77,8 +77,11 @@ * Wrong documented default value for `initial-color-theme` in `foot.ini(5)` ([#2292][2292]). +* Occasional crashes when closing a window and + `tweak.pre-apply-damage=yes` (the default) ([#2288][2288]). [2292]: https://codeberg.org/dnkl/foot/issues/2292 +[2288]: https://codeberg.org/dnkl/foot/issues/2288 ### Security diff --git a/wayland.c b/wayland.c index 1d258213..1ffd62a6 100644 --- a/wayland.c +++ b/wayland.c @@ -2177,8 +2177,6 @@ wayl_win_destroy(struct wl_window *win) struct terminal *term = win->term; - render_wait_for_preapply_damage(term); - if (win->csd.move_timeout_fd != -1) close(win->csd.move_timeout_fd); @@ -2236,6 +2234,8 @@ wayl_win_destroy(struct wl_window *win) tll_remove(win->urls, it); } + render_wait_for_preapply_damage(term); + csd_destroy(win); wayl_win_subsurface_destroy(&win->search); wayl_win_subsurface_destroy(&win->scrollback_indicator); From eed2d668ecdb0705142d27950a6d8c1923df32f1 Mon Sep 17 00:00:00 2001 From: vlkrs Date: Thu, 12 Mar 2026 18:47:50 +0100 Subject: [PATCH 03/11] OpenBSD has UTF-32 --- char32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/char32.c b/char32.c index 3d6c2c78..be5bf229 100644 --- a/char32.c +++ b/char32.c @@ -34,7 +34,7 @@ _Static_assert( #if !defined(__STDC_UTF_32__) || !__STDC_UTF_32__ #error "char32_t does not use UTF-32" #endif -#if (!defined(__STDC_ISO_10646__) || !__STDC_ISO_10646__) && !defined(__FreeBSD__) +#if (!defined(__STDC_ISO_10646__) || !__STDC_ISO_10646__) && !defined(__FreeBSD__) && !defined(__OpenBSD__) #error "wchar_t does not use UTF-32" #endif From 370adaf6975c7128107d187928c7f9cdff247930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 14 Mar 2026 08:35:15 +0100 Subject: [PATCH 04/11] changelog: prepare for 1.26.1 --- CHANGELOG.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30b3e1e4..b62f5c92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -* [Unreleased](#unreleased) +* [1.26.1](#1-26-1) * [1.26.0](#1-26-0) * [1.25.0](#1-25-0) * [1.24.0](#1-24-0) @@ -68,11 +68,8 @@ * [1.2.0](#1-2-0) -## Unreleased -### Added -### Changed -### Deprecated -### Removed +## 1.26.1 + ### Fixed * Wrong documented default value for `initial-color-theme` in @@ -84,9 +81,11 @@ [2288]: https://codeberg.org/dnkl/foot/issues/2288 -### Security ### Contributors +* Roshless +* vlkrs + ## 1.26.0 From ef15414b301513a75193fd872de79d6379f41a79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 14 Mar 2026 08:35:28 +0100 Subject: [PATCH 05/11] meson: bump version to 1.26.1 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 16e8e3c0..a0e602bb 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('foot', 'c', - version: '1.26.0', + version: '1.26.1', license: 'MIT', meson_version: '>=0.59.0', default_options: [ From 2fb7bb0ea4a240c6a8d921698d89d6044dea16e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 14 Mar 2026 08:38:15 +0100 Subject: [PATCH 06/11] changelog: add new 'unreleased' section --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b62f5c92..a0654b08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.26.1](#1-26-1) * [1.26.0](#1-26-0) * [1.25.0](#1-25-0) @@ -68,6 +69,16 @@ * [1.2.0](#1-2-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.26.1 ### Fixed From 037a2f4fa2c6fab014248d62efa8a6e14f617832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 21 Mar 2026 14:43:27 +0100 Subject: [PATCH 07/11] term: enqueue data to slave if there are queued paste data buffers When writing paste data to the terminal (either interactively, or as an OSC-52 reply), we enqueue other data (key presses, for examples, or query replies) while the paste is happening. The idea is to send the key press _after_ all paste data has been written, to ensure consistency. Unfortunately, we only checked for an on-going paste. I.e. where the paste itself hasn't yet finished. It is also possible the paste itself has finished, but we haven't yet flushed all the paste buffers. That is, if we were able to *receive* paste data faster than the terminal client was able to *consume* it. In this case, we've queued up paste data in the terminal. These are in separate queues, and when emitting e.g. a key press, we didn't check if all _those_ queues had been flushed yet. Closes #2307 --- CHANGELOG.md | 7 +++++++ terminal.c | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0654b08..f554124b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,13 @@ ### Deprecated ### Removed ### Fixed + +* Other output (key presses, query replies etc) being mixed with paste + data, both interactive pastes and OSC-52 ([#2307][2307]). + +[2307]: https://codeberg.org/dnkl/foot/issues/2307 + + ### Security ### Contributors diff --git a/terminal.c b/terminal.c index ac7922a7..8eafbcbe 100644 --- a/terminal.c +++ b/terminal.c @@ -120,7 +120,10 @@ term_to_slave(struct terminal *term, const void *data, size_t len) return false; } - if (tll_length(term->ptmx_buffers) > 0 || term->is_sending_paste_data) { + if (unlikely(tll_length(term->ptmx_buffers) > 0 || + term->is_sending_paste_data || + tll_length(term->ptmx_paste_buffers) > 0)) + { /* * Don't even try to send data *now* if there's queued up * data, since that would result in events arriving out of From 46a9cb8aab3145c8d4083aea485eaf7e2d44bf51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 2 Mar 2026 08:11:13 +0100 Subject: [PATCH 08/11] osc-52: don't strip any control characters, and don't do newline conversion --- osc.c | 4 ++-- search.c | 4 ++-- selection.c | 39 ++++++++++++++++++++++++++------------- selection.h | 4 ++-- 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/osc.c b/osc.c index 4dc47172..82793fb5 100644 --- a/osc.c +++ b/osc.c @@ -261,12 +261,12 @@ osc_from_clipboard(struct terminal *term, const char *source) if (from_clipboard) { text_from_clipboard( - seat, term, &from_clipboard_cb, &from_clipboard_done, ctx); + seat, term, true, &from_clipboard_cb, &from_clipboard_done, ctx); } if (from_primary) { text_from_primary( - seat, term, &from_clipboard_cb, &from_clipboard_done, ctx); + seat, term, true, &from_clipboard_cb, &from_clipboard_done, ctx); } } diff --git a/search.c b/search.c index 5228bf61..5386ffd3 100644 --- a/search.c +++ b/search.c @@ -1375,13 +1375,13 @@ execute_binding(struct seat *seat, struct terminal *term, case BIND_ACTION_SEARCH_CLIPBOARD_PASTE: text_from_clipboard( - seat, term, &from_clipboard_cb, &from_clipboard_done, term); + seat, term, false, &from_clipboard_cb, &from_clipboard_done, term); *update_search_result = *redraw = true; return true; case BIND_ACTION_SEARCH_PRIMARY_PASTE: text_from_primary( - seat, term, &from_clipboard_cb, &from_clipboard_done, term); + seat, term, false, &from_clipboard_cb, &from_clipboard_done, term); *update_search_result = *redraw = true; return true; diff --git a/selection.c b/selection.c index f07396a5..0a479ee8 100644 --- a/selection.c +++ b/selection.c @@ -2006,6 +2006,7 @@ struct clipboard_receive { int timeout_fd; struct itimerspec timeout; bool bracketed; + bool no_strip; bool quote_paths; void (*decoder)(struct clipboard_receive *ctx, char *data, size_t size); @@ -2153,6 +2154,8 @@ static bool fdm_receive(struct fdm *fdm, int fd, int events, void *data) { struct clipboard_receive *ctx = data; + const bool no_strip = ctx->no_strip; + const bool bracketed = ctx->bracketed; if ((events & EPOLLHUP) && !(events & EPOLLIN)) goto done; @@ -2204,13 +2207,14 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) break; case '\n': - if (!ctx->bracketed) + if (!no_strip && !bracketed) { p[i] = '\r'; + } break; case '\r': /* Convert \r\n -> \r */ - if (!ctx->bracketed && i + 1 < left && p[i + 1] == '\n') { + if (!no_strip && !bracketed && i + 1 < left && p[i + 1] == '\n') { i++; skip_one(); goto again; @@ -2223,8 +2227,11 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) case '\x11': case '\x12': case '\x13': case '\x14': case '\x15': case '\x16': case '\x17': case '\x18': case '\x19': case '\x1a': case '\x1b': case '\x1c': case '\x1d': case '\x1e': case '\x1f': - skip_one(); - goto again; + if (!no_strip) { + skip_one(); + goto again; + } + break; /* * In addition to stripping non-formatting C0 controls, @@ -2242,7 +2249,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) * handled above. */ case '\b': case '\x7f': case '\x00': - if (!ctx->bracketed) { + if (!no_strip && !bracketed) { skip_one(); goto again; } @@ -2263,8 +2270,8 @@ done: } static void -begin_receive_clipboard(struct terminal *term, int read_fd, - enum data_offer_mime_type mime_type, +begin_receive_clipboard(struct terminal *term, bool no_strip, + int read_fd, enum data_offer_mime_type mime_type, void (*cb)(char *data, size_t size, void *user), void (*done)(void *user), void *user) { @@ -2297,6 +2304,7 @@ begin_receive_clipboard(struct terminal *term, int read_fd, .timeout_fd = timeout_fd, .timeout = timeout, .bracketed = term->bracketed_paste, + .no_strip = no_strip, .quote_paths = term->grid == &term->normal, .decoder = (mime_type == DATA_OFFER_MIME_URI_LIST ? &fdm_receive_decoder_uri @@ -2326,6 +2334,7 @@ err: void text_from_clipboard(struct seat *seat, struct terminal *term, + bool no_strip, void (*cb)(char *data, size_t size, void *user), void (*done)(void *user), void *user) { @@ -2358,7 +2367,8 @@ text_from_clipboard(struct seat *seat, struct terminal *term, /* Don't keep our copy of the write-end open (or we'll never get EOF) */ close(write_fd); - begin_receive_clipboard(term, read_fd, clipboard->mime_type, cb, done, user); + begin_receive_clipboard( + term, no_strip, read_fd, clipboard->mime_type, cb, done, user); } static void @@ -2401,7 +2411,8 @@ selection_from_clipboard(struct seat *seat, struct terminal *term, uint32_t seri if (term->bracketed_paste) term_paste_data_to_slave(term, "\033[200~", 6); - text_from_clipboard(seat, term, &receive_offer, &receive_offer_done, term); + text_from_clipboard( + seat, term, false, &receive_offer, &receive_offer_done, term); } bool @@ -2470,7 +2481,7 @@ selection_to_primary(struct seat *seat, struct terminal *term, uint32_t serial) void text_from_primary( - struct seat *seat, struct terminal *term, + struct seat *seat, struct terminal *term, bool no_strip, void (*cb)(char *data, size_t size, void *user), void (*done)(void *user), void *user) { @@ -2508,7 +2519,8 @@ text_from_primary( /* Don't keep our copy of the write-end open (or we'll never get EOF) */ close(write_fd); - begin_receive_clipboard(term, read_fd, primary->mime_type, cb, done, user); + begin_receive_clipboard( + term, no_strip, read_fd, primary->mime_type, cb, done, user); } void @@ -2530,7 +2542,8 @@ selection_from_primary(struct seat *seat, struct terminal *term) if (term->bracketed_paste) term_paste_data_to_slave(term, "\033[200~", 6); - text_from_primary(seat, term, &receive_offer, &receive_offer_done, term); + text_from_primary( + seat, term, false, &receive_offer, &receive_offer_done, term); } static void @@ -2819,7 +2832,7 @@ drop(void *data, struct wl_data_device *wl_data_device) term_paste_data_to_slave(term, "\033[200~", 6); begin_receive_clipboard( - term, read_fd, clipboard->mime_type, + term, false, read_fd, clipboard->mime_type, &receive_dnd, &receive_dnd_done, ctx); /* data offer is now "owned" by the receive context */ diff --git a/selection.h b/selection.h index 26298457..b6ad099a 100644 --- a/selection.h +++ b/selection.h @@ -63,12 +63,12 @@ bool text_to_primary( * point). */ void text_from_clipboard( - struct seat *seat, struct terminal *term, + struct seat *seat, struct terminal *term, bool no_strip, void (*cb)(char *data, size_t size, void *user), void (*done)(void *user), void *user); void text_from_primary( - struct seat *seat, struct terminal *term, + struct seat *seat, struct terminal *term, bool no_strip, void (*cb)(char *data, size_t size, void *user), void (*dont)(void *user), void *user); From 89d6ff10fa816f11c1fa20368e4cc3464549955e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 6 Apr 2026 10:45:43 +0200 Subject: [PATCH 09/11] input: no extra key processing in alternate input modes In 3a2eb80d83d59d194a3d07da227431c634892ff5, we fixed an issue where key *releases* triggered a selection reset, and viewport reset. The same issue still exists in other input modes (unicode, search and url mode); if the kitty keyboard protocol has been enabled, with release events, any key press directed to e.g. search input handling, will was not remembered, and the corresponding release event generated a kitty keyboard event to the terminal application, and reset the viewport and selection. This means, for example, that scrollback search was unusable. Fix by *never* doing any further (generic) key processing if an alternate input mode is active. Closes #2316 --- CHANGELOG.md | 4 ++++ input.c | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f554124b..78803ea3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,8 +78,12 @@ * Other output (key presses, query replies etc) being mixed with paste data, both interactive pastes and OSC-52 ([#2307][2307]). +* Scrollback search not working correctly when the terminal + application has enabled the kitty keyboard protocol with release + event reporting ([#2316][2316]). [2307]: https://codeberg.org/dnkl/foot/issues/2307 +[2316]: https://codeberg.org/dnkl/foot/issues/2316 ### Security diff --git a/input.c b/input.c index aa6b7f1d..6a829a70 100644 --- a/input.c +++ b/input.c @@ -1643,31 +1643,34 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, seat->wayl->key_binding_manager, term->conf, seat); xassert(bindings != NULL); - if (pressed) { - if (term->unicode_mode.active) { + if (term->unicode_mode.active) { + if (pressed) unicode_mode_input(seat, term, sym); - return; - } + return; + } - else if (term->is_searching) { + else if (term->is_searching) { + if (pressed) { if (should_repeat) start_repeater(seat, key); search_input( seat, term, bindings, key, sym, mods, consumed, raw_syms, raw_count, serial); - return; } + return; + } - else if (urls_mode_is_active(term)) { + else if (urls_mode_is_active(term)) { + if (pressed) { if (should_repeat) start_repeater(seat, key); urls_input( seat, term, bindings, key, sym, mods, consumed, raw_syms, raw_count, serial); - return; } + return; } #if defined(_DEBUG) && defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG From 48aa5decef4db82092617e720b758a3a6f956bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 6 Apr 2026 15:19:59 +0200 Subject: [PATCH 10/11] wayland: shm: included decoded fourcc name when logging shm formats --- wayland.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/wayland.c b/wayland.c index 1ffd62a6..246a5ceb 100644 --- a/wayland.c +++ b/wayland.c @@ -1,5 +1,6 @@ #include "wayland.h" +#include #include #include #include @@ -247,17 +248,32 @@ shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) #if defined(_DEBUG) bool have_description = false; + const char c4 = (format >> 24) & 0xff; + const char c3 = (format >> 16) & 0xff; + const char c2 = (format >> 8) & 0xff; + const char c1 = (format >> 0) & 0xff; for (size_t i = 0; i < ALEN(shm_formats); i++) { if (shm_formats[i].format == format) { - LOG_DBG("shm: 0x%08x: %s", format, shm_formats[i].description); + LOG_DBG("shm: 0x%08x: %c%c%c%c - %s", + format, + isprint(c1) ? c1 : ' ', + isprint(c2) ? c2 : ' ', + isprint(c3) ? c3 : ' ', + isprint(c4) ? c4 : ' ', + shm_formats[i].description); have_description = true; break; } } if (!have_description) - LOG_DBG("shm: 0x%08x: unknown", format); + LOG_DBG("shm: 0x%08x: %c%c%c%c - unknown", + format, + isprint(c1) ? c1 : ' ', + isprint(c2) ? c2 : ' ', + isprint(c3) ? c3 : ' ', + isprint(c4) ? c4 : ' '); #endif } From ecf3b864e461bf6bf5033ac794cc6109013cd816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 6 Apr 2026 15:25:08 +0200 Subject: [PATCH 11/11] wayland: shm: fix debug builds when LOG_ENABLE_DBG == 0 --- wayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wayland.c b/wayland.c index 246a5ceb..f5737c1e 100644 --- a/wayland.c +++ b/wayland.c @@ -246,7 +246,7 @@ shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) case WL_SHM_FORMAT_ABGR16161616: wayl->shm_have_abgr161616 = true; break; } -#if defined(_DEBUG) +#if defined(_DEBUG) && LOG_ENABLE_DBG == 1 bool have_description = false; const char c4 = (format >> 24) & 0xff; const char c3 = (format >> 16) & 0xff;