From bc325999de88d9e6025678cd6e2eff6cece4473b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 21 Jan 2021 11:43:22 +0100 Subject: [PATCH 01/89] changelog: add empty 1.6.3 section --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3418d9ac..4ec039ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [1.6.3](#1-6-3) * [1.6.2](#1-6-2) * [1.6.1](#1-6-1) * [1.6.0](#1-6-0) @@ -20,6 +21,16 @@ * [1.2.0](#1-2-0) +## 1.6.3 +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.6.2 ### Fixed From dd3d5cec433e818d1c573cf05c2d548b6441296b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 21 Dec 2020 20:12:03 +0100 Subject: [PATCH 02/89] =?UTF-8?q?doc:=20updated=20=E2=80=98laptop=E2=80=99?= =?UTF-8?q?=20benchmarks=20for=201.6.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/benchmark.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/doc/benchmark.md b/doc/benchmark.md index f4d87e2d..a5cc416a 100644 --- a/doc/benchmark.md +++ b/doc/benchmark.md @@ -50,7 +50,7 @@ Scrollback: 10000 lines | unicode-random | 0.224s ±0.001 | 0.144s ±0.001 | 0.092s ±0.004 [^1] | 21.294s ±1.580 | 26.594s ±3.801 | -## 2020-07-25 +## 2020-12-21 ### System @@ -72,13 +72,14 @@ Scrollback=10000 lines ### Results -| Benchmark | Foot (GCC+PGO) 1.5.0.r27 | Alacritty 0.5.0 | URxvt 9.22 | St 0.8.4 | XTerm 360 | -|------------------------|-------------------------:|--------------------:|-----------------:|--------------:|----------------:| -| alt-random | 0.741s ±0.067 | 1.472s ±0.126 | 1.717s ±0.141 | 1.910s ±0.030 | 37.832s ±0.193 | -| alt-random-colors | 0.735s ±0.050 | 1.510s ±0.084 | 1.936s ±0.121 | 2.114s ±0.116 | 33.759s ±0.344 | -| scrolling | 1.687s ±0.044 | 1.440s ±0.128 | 1.485s ±0.032 | 3.485s ±0.142 | 134.590s ±0.602 | -| scrolling-filled-lines | 1.331s ±0.041 | 2.072s ±0.073 | 2.031s ±0.087 | 2.658s ±0.084 | 20.508s ±0.063 | -| unicode-random | 0.303s ±0.010 | 0.155s ±0.006 [^1] | 130.967s ±28.161 | crashed | 170.444s ±7.798 | + +| Benchmark | Foot (GCC+PGO) 1.6.0.r30 | Foot (no PGO) 1.6.0.r30 | Alacritty 0.6.0 | URxvt 9.22 | St 0.8.4 | XTerm 362 | +|------------------------|-------------------------:|------------------------:|-------------------:|-----------------:|--------------:|----------------:| +| alt-random | 0.734s ±0.051 | 1.186s ±0.101 | 1.580s ±0.083 | 1.709s ±0.090 | 1.953s ±0.038 | 38.693s ±0.298 | +| alt-random-colors | 0.728s ±0.047 | 1.267s ±0.090 | 1.579s ±0.073 | 2.108s ±0.121 | 2.185s ±0.099 | 34.123s ±0.194 | +| scrolling | 1.639s ±0.040 | 1.641s ±0.053 | 1.397s ±0.048 | 1.389s ±0.046 | 3.599s ±0.124 | 136.514s ±0.534 | +| scrolling-filled-lines | 1.328s ±0.050 | 1.640s ±0.052 | 2.108s ±0.068 | 2.032s ±0.121 | 2.718s ±0.088 | 21.383s ±0.072 | +| unicode-random | 0.304s ±0.018 | 0.271s ±0.017 | 0.143s ±0.002 [^1] | 20.543s ±0.098 | crashed | 16.013s ±0.253 | [^1]: [Alacritty and "unicode-random"](#alacritty-and-unicode-random) From 02493278b8e59d1342a1774f3c55c47a3bfff737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 22 Dec 2020 10:53:57 +0100 Subject: [PATCH 03/89] readme: add repology packaging status badge --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index deeffdda..18178256 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ The fast, lightweight and minimalistic Wayland terminal emulator. +[![Packaging status](https://repology.org/badge/vertical-allrepos/foot.svg)](https://repology.org/project/foot/versions) + + ## Index 1. [Features](#features) From aeca6a8ea642289210d46874396790c3b741e222 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 23 Dec 2020 18:13:40 +0100 Subject: [PATCH 04/89] input: remove non-existing state from debug log --- input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/input.c b/input.c index 2c858bc6..b1154e9f 100644 --- a/input.c +++ b/input.c @@ -750,7 +750,7 @@ keymap_lookup(struct seat *seat, struct terminal *term, const enum keypad_keys keypad_keys_mode = term->num_lock_modifier ? KEYPAD_NUMERICAL : term->keypad_keys_mode; - LOG_DBG("keypad mode: %d, num-lock=%d", keypad_keys_mode, seat->kbd.num); + LOG_DBG("keypad mode: %d", keypad_keys_mode); for (size_t j = 0; j < count; j++) { if (info[j].modifiers != MOD_ANY && info[j].modifiers != mods) From dc0e1c3b62987e60e6f508c3bfad86ace997ef05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 24 Dec 2020 13:44:09 +0100 Subject: [PATCH 05/89] =?UTF-8?q?ci:=20sr.ht:=20cd=20into=20=E2=80=98foot?= =?UTF-8?q?=E2=80=99=20before=20running=20codespell?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .builds/alpine-x64.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index ee81d59e..561043a6 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -49,4 +49,5 @@ tasks: meson test -C bld/release --print-errorlogs - codespell: | pip install codespell + cd foot ~/.local/bin/codespell *.c *.h From ca37dd416442a5c726890e4f41d7e7c244273a28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:34:51 +0100 Subject: [PATCH 06/89] readme: test: add gitlab pipeline badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 18178256..73851fc3 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ The fast, lightweight and minimalistic Wayland terminal emulator. +[https://gitlab.com/dnkl/foot/badges/master/pipeline.svg](https://gitlab.com/dnkl/foot/commits/master) [![Packaging status](https://repology.org/badge/vertical-allrepos/foot.svg)](https://repology.org/project/foot/versions) From f5dd88249ecd74c20b99dd1406c134635dda2f1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:36:00 +0100 Subject: [PATCH 07/89] readme: try to fix gitlab pipeline status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73851fc3..be546da4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ The fast, lightweight and minimalistic Wayland terminal emulator. -[https://gitlab.com/dnkl/foot/badges/master/pipeline.svg](https://gitlab.com/dnkl/foot/commits/master) +[![Pipeline status](https://gitlab.com/dnkl/foot/badges/master/pipeline.svg)](https://gitlab.com/dnkl/foot/commits/master) [![Packaging status](https://repology.org/badge/vertical-allrepos/foot.svg)](https://repology.org/project/foot/versions) From 387f4514f3674e9ccc94c5599d8e736b95180a02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:36:40 +0100 Subject: [PATCH 08/89] readme: try to position pipeline badge after description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index be546da4..954df52e 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # ![Logo: a terminal with a foot shaped prompt](icons/hicolor/48x48/apps/foot.png) foot The fast, lightweight and minimalistic Wayland terminal emulator. - [![Pipeline status](https://gitlab.com/dnkl/foot/badges/master/pipeline.svg)](https://gitlab.com/dnkl/foot/commits/master) + [![Packaging status](https://repology.org/badge/vertical-allrepos/foot.svg)](https://repology.org/project/foot/versions) From 6f3c30ded80dc3730c7a4c4611880dabc21b7f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 12:39:57 +0100 Subject: [PATCH 09/89] readme: add builds.sr.ht status badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 954df52e..3ff90c01 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # ![Logo: a terminal with a foot shaped prompt](icons/hicolor/48x48/apps/foot.png) foot The fast, lightweight and minimalistic Wayland terminal emulator. + [![Pipeline status](https://gitlab.com/dnkl/foot/badges/master/pipeline.svg)](https://gitlab.com/dnkl/foot/commits/master) +[![builds.sr.ht status](https://builds.sr.ht/~dnkl/foot.svg)](https://builds.sr.ht/~dnkl/foot?) [![Packaging status](https://repology.org/badge/vertical-allrepos/foot.svg)](https://repology.org/project/foot/versions) From c28bba7a55866abcdab38422026dced7d6d4940d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 01:26:54 +0100 Subject: [PATCH 10/89] reaper: add optional callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When calling ‘reaper_add()’, the caller can provide a callback. If non-NULL, the reaper will call the callback to handle the actual reaping. If the callback is NULL, or if it returns false, the reaper reaps the child process. --- reaper.c | 54 +++++++++++++++++++++++++++++++++++------------------- reaper.h | 5 ++++- spawn.c | 2 +- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/reaper.c b/reaper.c index 04a444f1..8ebd31a4 100644 --- a/reaper.c +++ b/reaper.c @@ -11,10 +11,16 @@ #include "log.h" #include "tllist.h" +struct child { + pid_t pid; + reaper_cb cb; + void *cb_data; +}; + struct reaper { struct fdm *fdm; int fd; - tll(pid_t) children; + tll(struct child) children; }; static bool fdm_reap(struct fdm *fdm, int fd, int events, void *data); @@ -84,10 +90,12 @@ reaper_destroy(struct reaper *reaper) } void -reaper_add(struct reaper *reaper, pid_t pid) +reaper_add(struct reaper *reaper, pid_t pid, reaper_cb cb, void *cb_data) { LOG_DBG("adding pid=%d", pid); - tll_push_back(reaper->children, pid); + tll_push_back( + reaper->children, + ((struct child){.pid = pid, .cb = cb, .cb_data = cb_data})); } static bool @@ -119,26 +127,34 @@ fdm_reap(struct fdm *fdm, int fd, int events, void *data) } tll_foreach(reaper->children, it) { - /* Don't use wait() since we don't want to accidentally reap - * the PTS slave */ + struct child *child = &it->item; + pid_t pid = child->pid; - pid_t pid = it->item; - - int result; - int res = waitpid(pid, &result, WNOHANG); - - if (res <= 0) { - if (res < 0) - LOG_ERRNO("waitpid failed for pid=%d", pid); + if (pid != (pid_t)info.ssi_pid) continue; + + bool reap_ourselves = true; + if (child->cb != NULL) + reap_ourselves = !child->cb(reaper, pid, child->cb_data); + + if (reap_ourselves) { + int result; + int res = waitpid(pid, &result, WNOHANG); + + if (res <= 0) { + if (res < 0) + LOG_ERRNO("waitpid failed for pid=%d", pid); + continue; + } + + else if (WIFEXITED(result)) + LOG_DBG("pid=%d: exited with status=%d", pid, WEXITSTATUS(result)); + else if (WIFSIGNALED(result)) + LOG_DBG("pid=%d: killed by signal=%d", pid, WTERMSIG(result)); + else + LOG_DBG("pid=%d: died of unknown resason", pid); } - else if (WIFEXITED(result)) - LOG_DBG("pid=%d: exited with status=%d", pid, WEXITSTATUS(result)); - else if (WIFSIGNALED(result)) - LOG_DBG("pid=%d: killed by signal=%d", pid, WTERMSIG(result)); - else - LOG_DBG("pid=%d: died of unknown resason", pid); tll_remove(reaper->children, it); } diff --git a/reaper.h b/reaper.h index 2daa9a8b..f955aa44 100644 --- a/reaper.h +++ b/reaper.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "fdm.h" @@ -9,4 +10,6 @@ struct reaper; struct reaper *reaper_init(struct fdm *fdm); void reaper_destroy(struct reaper *reaper); -void reaper_add(struct reaper *reaper, pid_t pid); +typedef bool (*reaper_cb)(struct reaper *reaper, pid_t pid, void *data); + +void reaper_add(struct reaper *reaper, pid_t pid, reaper_cb cb, void *cb_data); diff --git a/spawn.c b/spawn.c index cc4755f4..6d96cf54 100644 --- a/spawn.c +++ b/spawn.c @@ -55,7 +55,7 @@ spawn(struct reaper *reaper, const char *cwd, char *const argv[], close(pipe_fds[0]); if (ret == 0) { - reaper_add(reaper, pid); + reaper_add(reaper, pid, NULL, NULL); return true; } else if (ret < 0) { LOG_ERRNO("failed to read from pipe"); From ce90480011c9d78275a069e0514e9a03813edffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 01:29:40 +0100 Subject: [PATCH 11/89] terminal: shutdown (or --hold) when the client process terminates Shutdown the terminal when the client process terminates, not when the ptmx file descriptor is closed. This fixes an issue where the terminal remains running after the client process has terminated, if it spawned child processes that inherited the ptmx file descriptor. --- CHANGELOG.md | 5 +++++ pgo/pgo.c | 4 ++++ terminal.c | 25 +++++++++++++++++++------ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ec039ae..5f174cd9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,11 @@ ### Deprecated ### Removed ### Fixed + +* Exit when the client application terminates, not when the TTY file + descriptor is closed. + + ### Security ### Contributors diff --git a/pgo/pgo.c b/pgo/pgo.c index 7874cc8d..120b8628 100644 --- a/pgo/pgo.c +++ b/pgo/pgo.c @@ -13,6 +13,7 @@ #include "async.h" #include "config.h" +#include "reaper.h" #include "sixel.h" #include "user-notification.h" #include "vt.h" @@ -134,6 +135,9 @@ notify_notify(const struct terminal *term, const char *title, const char *body) { } +void reaper_add(struct reaper *reaper, pid_t pid, reaper_cb cb, void *cb_data) {} + + int main(int argc, const char *const *argv) { diff --git a/terminal.c b/terminal.c index 208c8700..78969e9e 100644 --- a/terminal.c +++ b/terminal.c @@ -314,12 +314,8 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) } if (hup) { - if (term->conf->hold_at_exit) { - fdm_del(fdm, fd); - term->ptmx = -1; - return true; - } else - return term_shutdown(term); + fdm_del(fdm, fd); + term->ptmx = -1; } return true; @@ -954,6 +950,21 @@ load_fonts_from_conf(struct terminal *term) return reload_fonts(term); } +static bool +slave_died(struct reaper *reaper, pid_t pid, void *data) +{ + struct terminal *term = data; + LOG_DBG("slave (PID=%u) died", pid); + + if (term->conf->hold_at_exit) { + fdm_del(term->fdm, term->ptmx); + term->ptmx = -1; + return true; + } + + return term_shutdown(term); +} + struct terminal * term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, struct wayland *wayl, const char *foot_exe, const char *cwd, @@ -1159,6 +1170,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper, goto err; } + reaper_add(term->reaper, term->slave, &slave_died, term); + /* Guess scale; we're not mapped yet, so we don't know on which * output we'll be. Pick highest scale we find for now */ tll_foreach(term->wl->monitors, it) { From 981fb0a755f3fb29c5634d3050492426f26f97c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 01:35:17 +0100 Subject: [PATCH 12/89] =?UTF-8?q?terminal:=20reaper=20callback:=20don?= =?UTF-8?q?=E2=80=99t=20close=20ptmx=20if=20already=20closed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- terminal.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/terminal.c b/terminal.c index 78969e9e..ecb4e6cc 100644 --- a/terminal.c +++ b/terminal.c @@ -957,8 +957,11 @@ slave_died(struct reaper *reaper, pid_t pid, void *data) LOG_DBG("slave (PID=%u) died", pid); if (term->conf->hold_at_exit) { - fdm_del(term->fdm, term->ptmx); - term->ptmx = -1; + /* The PTMX FDM handler may already have closed our end */ + if (term->ptmx >= 0) { + fdm_del(term->fdm, term->ptmx); + term->ptmx = -1; + } return true; } From 152fb0186bd847b6632e8be8c7cad766e5c13d1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 22:03:52 +0100 Subject: [PATCH 13/89] wayland: log warning when compositor does not implement text-input --- wayland.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/wayland.c b/wayland.c index f156533e..7dbf8593 100644 --- a/wayland.c +++ b/wayland.c @@ -1130,6 +1130,13 @@ wayl_init(const struct config *conf, struct fdm *fdm) goto out; } +#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED + if (wayl->text_input_manager == NULL) { + LOG_WARN("text input interface not implemented by compositor; " + "IME will be disabled"); + } +#endif + /* Trigger listeners registered when handling globals */ wl_display_roundtrip(wayl->display); From 797e79d3c7aecf27b04ed100581345ebc0f70ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 25 Dec 2020 22:04:19 +0100 Subject: [PATCH 14/89] =?UTF-8?q?ime:=20don=E2=80=99t=20call=20text-input?= =?UTF-8?q?=20functions=20when=20it=20isn=E2=80=99t=20available?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #259 --- CHANGELOG.md | 2 ++ ime.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f174cd9..b6b804ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,8 @@ * Exit when the client application terminates, not when the TTY file descriptor is closed. +* Crash on compositors not implementing the _text input_ interface + (https://codeberg.org/dnkl/foot/issues/259). ### Security diff --git a/ime.c b/ime.c index 24237d69..9e2699f2 100644 --- a/ime.c +++ b/ime.c @@ -327,6 +327,9 @@ ime_reset(struct seat *seat) void ime_enable(struct seat *seat) { + if (unlikely(seat->wayl->text_input_manager == NULL)) + return; + struct terminal *term = seat->kbd_focus; assert(term != NULL); @@ -348,6 +351,9 @@ ime_enable(struct seat *seat) void ime_disable(struct seat *seat) { + if (unlikely(seat->wayl->text_input_manager == NULL)) + return; + ime_reset(seat); zwp_text_input_v3_disable(seat->wl_text_input); From da0af3621e7abb85413f015ba2386cabd193143e Mon Sep 17 00:00:00 2001 From: Birger Schacht Date: Sat, 26 Dec 2020 09:17:34 +0100 Subject: [PATCH 15/89] Fix typo programatically -> programmatically --- 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 c03342cf..d4fa1e01 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -135,7 +135,7 @@ in this order: protocol is added in the future (in which case foot will use that instead of painting its margins red). - Applications can enable/disable this feature programatically with + Applications can enable/disable this feature programmatically with the *CSI ? 1042 h* and *CSI ? 1042 l* escape sequences. _Note_: expect this feature to be *replaced* with proper From 841618bb8d24224c6cccd20287e886ec28e9641b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 12:14:07 +0100 Subject: [PATCH 16/89] =?UTF-8?q?changelog:=20update=20=E2=80=98contributo?= =?UTF-8?q?rs=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b6b804ee..c3ba4654 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,8 @@ ### Security ### Contributors +* [birger](https://codeberg.org/birger) + ## 1.6.2 From 8dd58a4220dff0645ff00addb38d0258a7eed31f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 12:15:52 +0100 Subject: [PATCH 17/89] ci: run codespell on the man pages (the source files) --- .builds/alpine-x64.yml | 2 +- .gitlab-ci.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 561043a6..944db519 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -50,4 +50,4 @@ tasks: - codespell: | pip install codespell cd foot - ~/.local/bin/codespell *.c *.h + ~/.local/bin/codespell *.c *.h doc/*.scd diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index caab2812..41af846f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -77,4 +77,4 @@ codespell: - apk add python3 - apk add py3-pip - pip install codespell - - codespell *.c *.h + - codespell *.c *.h doc/*.scd From 8b4c4d672838a7d479f78938314c7e99b2eea0f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 26 Dec 2020 13:08:03 +0100 Subject: [PATCH 18/89] ci: run codespell on README/INSTALL/CHANGELOG --- .builds/alpine-x64.yml | 3 ++- .gitlab-ci.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.builds/alpine-x64.yml b/.builds/alpine-x64.yml index 944db519..bff859fa 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -1,3 +1,4 @@ + image: alpine/edge packages: - musl-dev @@ -50,4 +51,4 @@ tasks: - codespell: | pip install codespell cd foot - ~/.local/bin/codespell *.c *.h doc/*.scd + ~/.local/bin/codespell README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 41af846f..cf4b4adc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -77,4 +77,4 @@ codespell: - apk add python3 - apk add py3-pip - pip install codespell - - codespell *.c *.h doc/*.scd + - codespell README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd From 7394d7d659ebb384acc705e3ba15f7e22922febd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 31 Dec 2020 12:48:20 +0100 Subject: [PATCH 19/89] foot.ini: add (default) size to font --- foot.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/foot.ini b/foot.ini index 29ab2922..adaf6088 100644 --- a/foot.ini +++ b/foot.ini @@ -1,6 +1,6 @@ # -*- conf -*- -# font=monospace +# font=monospace:size=8 # font-bold= # font-italic= # font-bold-italic= From 94cacab2f95427b2e9a9dfda2ad5c66ea468fcef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 31 Dec 2020 12:57:37 +0100 Subject: [PATCH 20/89] =?UTF-8?q?doc:=20foot.ini:=20remove=20FONT=20FORMAT?= =?UTF-8?q?=20section,=20expand=20description=20of=20=E2=80=98font?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/foot.ini.5.scd | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index d4fa1e01..9cabc1d2 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -19,8 +19,16 @@ in this order: # SECTION: default *font*, *font-bold*, *font-italic*, *font-bold-italic* - Comma separated list of fonts to use, in fontconfig format (see - *FONT FORMAT*). + + Comma separated list of fonts to use, in fontconfig format. That + is, a font name followed by a list of colon-separated + options. Most noteworthy is *:size=n*, which is used to set the + font size. + + Examples: + - Dina:weight=bold:slant=italic + - Courier New:size=12 + - Fantasque Sans Mono:fontfeatures=ss01 For each option, the first font is the primary font. The remaining fonts are fallback fonts that will be used whenever a glyph cannot @@ -40,7 +48,7 @@ in this order: To disable bold and/or italic fonts, set e.g. *font-bold* to _exactly_ the same value as *font*. - Default: _monospace_ (*font*), _not set_ (*font-bold*, + Default: _monospace:size=8_ (*font*), _not set_ (*font-bold*, *font-italic*, *font-bold-italic*). *dpi-aware* @@ -554,15 +562,6 @@ All actions listed under *key-bindings* can be user here as well. Pastes from the _primary selection_. Default: _BTN\_MIDDLE_. -# FONT FORMAT - -The font is specified in FontConfig syntax. That is, a colon-separated -list of font name and font options. - -_Examples_: -- Dina:weight=bold:slant=italic -- Courier New:size=12 - # TWEAK This section is for advanced users and describes configuration options From 9c705b26ee2cefe8e9710c6dc8dbbd1ac5a5c53f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 2 Jan 2021 22:24:49 +0100 Subject: [PATCH 21/89] render: mark cell overflowed into as dirty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When tweak.allow-overflowing-double-width-glyphs=yes, then certain glyphs are allowed to overflow into the neighbouring cell. However, if the cell “owning” the double-width glyph is erased (_only_ that cell), then the cell overflowed into is not redrawn, causing part of the double-width glyph to remain on screen. To avoid checking for these glyphs when printing to the terminal (i.e at parse time), simply mark both cells as dirty when we render the overflowing glyph. Yes, this means that the cells will always be re-rendered. We count on them only making up a small portion of the screen. --- CHANGELOG.md | 3 +++ render.c | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3ba4654..8cb6fe1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,9 @@ descriptor is closed. * Crash on compositors not implementing the _text input_ interface (https://codeberg.org/dnkl/foot/issues/259). +* Erased, overflowing glyphs (when + `tweak.allow-overflowing-double-width-glyphs=yes` - the default) not + properly erasing the cell overflowed **into**. ### Security diff --git a/render.c b/render.c index f66e277e..2951c466 100644 --- a/render.c +++ b/render.c @@ -481,7 +481,16 @@ render_cell(struct terminal *term, pixman_image_t *pix, col < term->cols - 1 && (row->cells[col + 1].wc == 0 || row->cells[col + 1].wc == L' ')) { - cell_cols = min(2, cols_left); + cell_cols = 2; + + /* + * Ensure the cell we’re overflowing into gets re-rendered, to + * ensure it is erased if *this* cell is erased. Note that we + * do *not* mark the row as dirty - we don’t need to re-render + * the cell if nothing else on the row has changed. + */ + row->cells[col].attrs.clean = 0; + row->cells[col + 1].attrs.clean = 0; } pixman_region32_t clip; From 0aa086065c5bb0eef3ea53ed0f3e464ebcbb6853 Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Sun, 3 Jan 2021 14:08:25 -0500 Subject: [PATCH 22/89] Require trailing comment to be preceded by a space or tab Fixes `word-delimiters` option to not ignore `#` and subsequent characters. Closes #270 --- CHANGELOG.md | 7 +++++++ config.c | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cb6fe1a..4246f025 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,11 @@ ## 1.6.3 ### Added ### Changed + +* Trailing comments in `foot.ini` must now be preceded by a space or tab + (https://codeberg.org/dnkl/foot/issues/270) + + ### Deprecated ### Removed ### Fixed @@ -35,6 +40,8 @@ * Erased, overflowing glyphs (when `tweak.allow-overflowing-double-width-glyphs=yes` - the default) not properly erasing the cell overflowed **into**. +* `word-delimiters` option ignores `#` and subsequent characters + (https://codeberg.org/dnkl/foot/issues/270) ### Security diff --git a/config.c b/config.c index bf184a16..883cf27c 100644 --- a/config.c +++ b/config.c @@ -1731,9 +1731,18 @@ parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_ar if (line[0] == '\0' || line[0] == '#') continue; - /* Split up into key/value pair + trailing comment */ - char *key_value = strtok(line, "#"); - char UNUSED *comment = strtok(NULL, "\n"); + /* Split up into key/value pair + trailing comment separated by blank */ + char *key_value = line; + char *comment = line; + while (comment[0] != '\0') { + const char c = comment[0]; + comment++; + if (isblank(c) && comment[0] == '#') { + comment[0] = '\0'; /* Terminate key/value pair */ + comment++; + break; + } + } /* Check for new section */ if (key_value[0] == '[') { From 10043872235c8b5745b456cabb91aa6fa7946abf Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Sun, 3 Jan 2021 15:49:24 -0500 Subject: [PATCH 23/89] Add myself to Contributors --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4246f025..73e64399 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ ### Contributors * [birger](https://codeberg.org/birger) +* [pc](https://codeberg.org/pc) ## 1.6.2 From f22d4e9587edf7624dcd6a4c60b8eafa73373e3e Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Mon, 4 Jan 2021 05:25:14 +0000 Subject: [PATCH 24/89] uri: use nibble2hex() instead of isxdigit(3) to check valid hex digits --- uri.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/uri.c b/uri.c index 31bbc7b6..57852f1b 100644 --- a/uri.c +++ b/uri.c @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -12,6 +11,10 @@ #include "log.h" #include "xmalloc.h" +enum { + HEX_DIGIT_INVALID = 16 +}; + static uint8_t nibble2hex(char c) { @@ -27,8 +30,7 @@ nibble2hex(char c) return c - 'A' + 10; } - assert(false); - return 0; + return HEX_DIGIT_INVALID; } bool @@ -199,7 +201,7 @@ uri_parse(const char *uri, size_t len, encoded_len -= prefix_len; decoded_len += prefix_len; - if (isxdigit(next[1]) && isxdigit(next[2])) { + if (nibble2hex(next[1]) <= 15 && nibble2hex(next[2]) <= 15) { *p++ = nibble2hex(next[1]) << 4 | nibble2hex(next[2]); decoded_len++; encoded_len -= 3; From da2b4e1809872df7e1120b710e1d92900decc01b Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Mon, 4 Jan 2021 05:31:19 +0000 Subject: [PATCH 25/89] uri: rename nibbletohex() function to hex2nibble() It converts a hex digit to a nibble, not the other way around. --- uri.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uri.c b/uri.c index 57852f1b..a0e3838a 100644 --- a/uri.c +++ b/uri.c @@ -16,7 +16,7 @@ enum { }; static uint8_t -nibble2hex(char c) +hex2nibble(char c) { switch (c) { case '0': case '1': case '2': case '3': case '4': @@ -201,8 +201,8 @@ uri_parse(const char *uri, size_t len, encoded_len -= prefix_len; decoded_len += prefix_len; - if (nibble2hex(next[1]) <= 15 && nibble2hex(next[2]) <= 15) { - *p++ = nibble2hex(next[1]) << 4 | nibble2hex(next[2]); + if (hex2nibble(next[1]) <= 15 && hex2nibble(next[2]) <= 15) { + *p++ = hex2nibble(next[1]) << 4 | hex2nibble(next[2]); decoded_len++; encoded_len -= 3; encoded = next + 3; From 43b8b5f7794329a3ab9565d7c4c9e8415258f54e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Jan 2021 18:32:00 +0100 Subject: [PATCH 26/89] =?UTF-8?q?input:=20don=E2=80=99t=20assert=20when=20?= =?UTF-8?q?receiving=20a=20button=20release=20without=20a=20press=20event?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- input.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/input.c b/input.c index b1154e9f..fe9e143a 100644 --- a/input.c +++ b/input.c @@ -1537,6 +1537,11 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, assert(it->item.button != button); #endif + /* + * Remember which surface "owns" this button, so that we can + * send motion and button release events to that surface, even + * if the pointer is no longer over it. + */ tll_push_back( seat->mouse.buttons, ((struct button_tracker){ @@ -1559,7 +1564,21 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, break; } } - assert(have_button); + + if (!have_button) { + /* + * Seen on Sway with slurp + * + * 1. Run slurp + * 2. Press, and hold left mouse button + * 3. Press escape, to cancel slurp + * 4. Release mouse button + * 5. BAM! + */ + LOG_WARN("stray button release event"); + return; + } + seat->mouse.last_released_button = button; } From 367faeb6eeedf2c6aac7c940e9abc01278d91f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Jan 2021 18:32:22 +0100 Subject: [PATCH 27/89] =?UTF-8?q?input:=20don=E2=80=99t=20try=20to=20close?= =?UTF-8?q?=20an=20invalid=20FD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- input.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/input.c b/input.c index fe9e143a..e4a1f306 100644 --- a/input.c +++ b/input.c @@ -1596,7 +1596,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, xdg_toplevel_set_maximized(win->xdg_toplevel); } - else if (button == BTN_LEFT && win->csd.move_timeout_fd == -1) { + else if (button == BTN_LEFT && win->csd.move_timeout_fd < 0) { const struct itimerspec timeout = { .it_value = {.tv_nsec = 200000000}, }; @@ -1610,14 +1610,15 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, win->csd.serial = serial; } else { LOG_ERRNO("failed to configure XDG toplevel move timer FD"); - close(fd); + if (fd >= 0) + close(fd); } } } else if (state == WL_POINTER_BUTTON_STATE_RELEASED) { struct wl_window *win = term->window; - if (win->csd.move_timeout_fd != -1) { + if (win->csd.move_timeout_fd >= 0) { fdm_del(wayl->fdm, win->csd.move_timeout_fd); win->csd.move_timeout_fd = -1; } From d10fc3dc797708127ff235a60c0f26627885baa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Jan 2021 18:32:55 +0100 Subject: [PATCH 28/89] render: draw combining characters on top of colored bitmap glyphs (emoji) --- CHANGELOG.md | 2 ++ render.c | 54 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73e64399..1f42147e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,8 @@ properly erasing the cell overflowed **into**. * `word-delimiters` option ignores `#` and subsequent characters (https://codeberg.org/dnkl/foot/issues/270) +* Combining characters not being rendered when composed with colored + bitmap glyhps (i.e. colored emojis). ### Security diff --git a/render.c b/render.c index 2951c466..2220c127 100644 --- a/render.c +++ b/render.c @@ -534,26 +534,44 @@ render_cell(struct terminal *term, pixman_image_t *pix, x + glyph->x, y + font_baseline(term) - glyph->y, glyph->width, glyph->height); - /* Combining characters */ - if (composed != NULL) { - for (size_t i = 0; i < composed->count; i++) { - const struct fcft_glyph *g = fcft_glyph_rasterize( - font, composed->combining[i], term->font_subpixel); - - if (g == NULL) - continue; - - pixman_image_composite32( - PIXMAN_OP_OVER, clr_pix, g->pix, pix, 0, 0, 0, 0, - /* Some fonts use a negative offset, while others use a - * "normal" offset */ - x + (g->x < 0 ? term->cell_width : 0) + g->x, - y + font_baseline(term) - g->y, - g->width, g->height); - } - } } + /* Combining characters */ + if (composed != NULL) { + for (size_t i = 0; i < composed->count; i++) { + const struct fcft_glyph *g = fcft_glyph_rasterize( + font, composed->combining[i], term->font_subpixel); + + if (g == NULL) + continue; + + /* + * Fonts _should_ assume the pen position is now + * *after* the base glyph, and thus use negative + * offsets for combining glyphs. + * + * Not all fonts behave like this however, and we + * try to accomodate both variants. + * + * Since we haven't moved our pen position yet, we + * add a full cell width to the offset (or two, in + * case of double-width characters). + * + * If the font does *not* use negative offsets, + * we'd normally use an offset of 0. However, to + * somewhat deal with double-width glyphs we use + * an offset of *one* cell. + */ + int x_ofs = g->x < 0 + ? cell_cols * term->cell_width + : (cell_cols - 1) * term->cell_width; + + pixman_image_composite32( + PIXMAN_OP_OVER, clr_pix, g->pix, pix, 0, 0, 0, 0, + x + x_ofs + g->x, y + font_baseline(term) - g->y, + g->width, g->height); + } + } } pixman_image_unref(clr_pix); From 8953840e385fea4ddd31647e7724db6870149bdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 4 Jan 2021 19:49:24 +0100 Subject: [PATCH 29/89] render: codespell: accomodate -> accommodate --- render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.c b/render.c index 2220c127..602de729 100644 --- a/render.c +++ b/render.c @@ -551,7 +551,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, * offsets for combining glyphs. * * Not all fonts behave like this however, and we - * try to accomodate both variants. + * try to accommodate both variants. * * Since we haven't moved our pen position yet, we * add a full cell width to the offset (or two, in From 44d7242d1460543b720bfb537805798dcead987e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 6 Jan 2021 16:57:07 +0100 Subject: [PATCH 30/89] render: sixel: fix assertion: x/y coords may be outside visible area But when they are, width/height must be 0. --- render.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/render.c b/render.c index 602de729..ba6019a3 100644 --- a/render.c +++ b/render.c @@ -872,8 +872,8 @@ render_sixel_chunk(struct terminal *term, pixman_image_t *pix, const struct sixe /* Verify we're not stepping outside the grid */ assert(x >= term->margins.left); assert(y >= term->margins.top); - assert(x + width <= term->width - term->margins.right); - assert(y + height <= term->height - term->margins.bottom); + assert(width == 0 || x + width <= term->width - term->margins.right); + assert(height == 0 || y + height <= term->height - term->margins.bottom); //LOG_DBG("sixel chunk: %dx%d %dx%d", x, y, width, height); From 547b91e42b4fe750403270c1a84067ddbc416be4 Mon Sep 17 00:00:00 2001 From: FollieHiyuki Date: Thu, 7 Jan 2021 22:21:07 +0300 Subject: [PATCH 31/89] Add fish completion --- CHANGELOG.md | 6 ++++++ completions/fish/foot.fish | 19 +++++++++++++++++++ completions/fish/footclient.fish | 14 ++++++++++++++ completions/meson.build | 3 +++ 4 files changed, 42 insertions(+) create mode 100644 completions/fish/foot.fish create mode 100644 completions/fish/footclient.fish diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f42147e..68a81a63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,11 @@ ## 1.6.3 ### Added + +* Completions for fish shell + (https://codeberg.org/dnkl/foot/issues/11) + + ### Changed * Trailing comments in `foot.ini` must now be preceded by a space or tab @@ -51,6 +56,7 @@ * [birger](https://codeberg.org/birger) * [pc](https://codeberg.org/pc) +* [FollieHiyuki](https://codeberg.org/FollieHiyuki) ## 1.6.2 diff --git a/completions/fish/foot.fish b/completions/fish/foot.fish new file mode 100644 index 00000000..ab59fb75 --- /dev/null +++ b/completions/fish/foot.fish @@ -0,0 +1,19 @@ +complete -c foot -x -a "(__fish_complete_subcommand)" +complete -c foot -r -s c -l config -d "path to configuration file (XDG_CONFIG_HOME/foot/foot.ini)" +complete -c foot -s C -l check-config -d "verify configuration and exit with 0 if ok, otherwise exit with 1" +complete -c foot -x -s f -l font -a "(fc-list : family | sed 's/,/\n/g' | sort | uniq)" -d "font name and style in fontconfig format (monospace)" +complete -c foot -x -s t -l term -a '(find /usr/share/terminfo -type f -printf "%f\n")' -d "value to set the environment variable TERM to (foot)" +complete -c foot -x -s T -l title -d "initial window title" +complete -c foot -x -s a -l app-id -d "value to set the app-id property on the Wayland window to (foot)" +complete -c foot -s m -l maximized -d "start in maximized mode" +complete -c foot -s F -l fullscreen -d "start in fullscreen mode" +complete -c foot -s L -l login-shell -d "start shell as a login shell" +complete -c foot -x -s w -l window-size-pixels -d "window WIDTHxHEIGHT, in pixels (700x500)" +complete -c foot -x -s W -l window-size-chars -d "window WIDTHxHEIGHT, in characters (not set)" +complete -c foot -F -s s -l server -d "run as server; open terminals by running footclient" +complete -c foot -s H -l hold -d "remain open after child process exits" +complete -c foot -r -s p -l print-pid -d "print PID to this file or FD when up and running (server mode only)" +complete -c foot -x -s l -l log-colorize -a "always never auto" -d "enable or disable colorization of log output on stderr" +complete -c foot -s S -l log-no-syslog -d "disable syslog logging (server mode only)" +complete -c foot -s v -l version -d "show the version number and quit" +complete -c foot -s h -l help -d "show help message and quit" diff --git a/completions/fish/footclient.fish b/completions/fish/footclient.fish new file mode 100644 index 00000000..9ffeeff5 --- /dev/null +++ b/completions/fish/footclient.fish @@ -0,0 +1,14 @@ +complete -c footclient -x -a "(__fish_complete_subcommand)" +complete -c footclient -x -s t -l term -a '(find /usr/share/terminfo -type f -printf "%f\n")' -d "value to set the environment variable TERM to (foot)" +complete -c footclient -x -s T -l title -d "initial window title" +complete -c footclient -x -s a -l app-id -d "value to set the app-id property on the Wayland window to (foot)" +complete -c footclient -s m -l maximized -d "start in maximized mode" +complete -c footclient -s F -l fullscreen -d "start in fullscreen mode" +complete -c footclient -s L -l login-shell -d "start shell as a login shell" +complete -c footclient -x -s w -l window-size-pixels -d "window WIDTHxHEIGHT, in pixels (700x500)" +complete -c footclient -x -s W -l window-size-chars -d "window WIDTHxHEIGHT, in characters (not set)" +complete -c footclient -F -s s -l server-socket -d "override the default path to the foot server socket ($XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock)" +complete -c footclient -s H -l hold -d "remain open after child process exits" +complete -c footclient -x -s l -l log-colorize -a "always never auto" -d "enable or disable colorization of log output on stderr" +complete -c footclient -s v -l version -d "show the version number and quit" +complete -c footclient -s h -l help -d "show help message and quit" diff --git a/completions/meson.build b/completions/meson.build index 213241c6..82198d0b 100644 --- a/completions/meson.build +++ b/completions/meson.build @@ -1,3 +1,6 @@ zsh_install_dir = join_paths(get_option('datadir'), 'zsh', 'site-functions') +fish_install_dir = join_paths(get_option('datadir'), 'fish', 'vendor_completions.d') install_data('zsh/_foot', install_dir: zsh_install_dir) install_data('zsh/_footclient', install_dir: zsh_install_dir) +install_data('fish/foot.fish', install_dir: fish_install_dir) +install_data('fish/footclient.fish', install_dir: fish_install_dir) From 1df78932d72efaee2af895451442a8eaeec7b674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Jan 2021 17:53:27 +0100 Subject: [PATCH 32/89] wayland: take rotation into account when calculating the logical PPI --- wayland.c | 20 ++++++++++++++++++++ wayland.h | 1 + 2 files changed, 21 insertions(+) diff --git a/wayland.c b/wayland.c index 7dbf8593..cf6f563b 100644 --- a/wayland.c +++ b/wayland.c @@ -331,6 +331,25 @@ output_update_ppi(struct monitor *mon) mon->ppi.real.x = mon->dim.px_real.width / x_inches; mon->ppi.real.y = mon->dim.px_real.height / y_inches; + /* The *logical* size is affected by the transform */ + switch (mon->transform) { + case WL_OUTPUT_TRANSFORM_90: + case WL_OUTPUT_TRANSFORM_270: + case WL_OUTPUT_TRANSFORM_FLIPPED_90: + case WL_OUTPUT_TRANSFORM_FLIPPED_270: { + int swap = x_inches; + x_inches = y_inches; + y_inches = swap; + break; + } + + case WL_OUTPUT_TRANSFORM_NORMAL: + case WL_OUTPUT_TRANSFORM_180: + case WL_OUTPUT_TRANSFORM_FLIPPED: + case WL_OUTPUT_TRANSFORM_FLIPPED_180: + break; + } + mon->ppi.scaled.x = mon->dim.px_scaled.width / x_inches; mon->ppi.scaled.y = mon->dim.px_scaled.height / y_inches; @@ -354,6 +373,7 @@ output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, mon->make = make != NULL ? xstrdup(make) : NULL; mon->model = model != NULL ? xstrdup(model) : NULL; mon->subpixel = subpixel; + mon->transform = transform; output_update_ppi(mon); } diff --git a/wayland.h b/wayland.h index 7e67dd1b..93244353 100644 --- a/wayland.h +++ b/wayland.h @@ -321,6 +321,7 @@ struct monitor { int scale; float refresh; enum wl_output_subpixel subpixel; + enum wl_output_transform transform; /* From wl_output */ char *make; From a698fa62e28468ec52b2408cdc293803a3965dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 12 Jan 2021 09:20:02 +0100 Subject: [PATCH 33/89] reaper: add reaper_del() - pid is no longer watched/monitored after this --- reaper.c | 11 +++++++++++ reaper.h | 1 + 2 files changed, 12 insertions(+) diff --git a/reaper.c b/reaper.c index 8ebd31a4..15e08539 100644 --- a/reaper.c +++ b/reaper.c @@ -98,6 +98,17 @@ reaper_add(struct reaper *reaper, pid_t pid, reaper_cb cb, void *cb_data) ((struct child){.pid = pid, .cb = cb, .cb_data = cb_data})); } +void +reaper_del(struct reaper *reaper, pid_t pid) +{ + tll_foreach(reaper->children, it) { + if (it->item.pid == pid) { + tll_remove(reaper->children, it); + break; + } + } +} + static bool fdm_reap(struct fdm *fdm, int fd, int events, void *data) { diff --git a/reaper.h b/reaper.h index f955aa44..2cd6dd6e 100644 --- a/reaper.h +++ b/reaper.h @@ -13,3 +13,4 @@ void reaper_destroy(struct reaper *reaper); typedef bool (*reaper_cb)(struct reaper *reaper, pid_t pid, void *data); void reaper_add(struct reaper *reaper, pid_t pid, reaper_cb cb, void *cb_data); +void reaper_del(struct reaper *reaper, pid_t pid); From 1d6b7d7a356530867b6c182c11a85498f1c6dee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 12 Jan 2021 09:20:54 +0100 Subject: [PATCH 34/89] pgo: stub implementation of reaper_del() --- pgo/pgo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/pgo/pgo.c b/pgo/pgo.c index 120b8628..39c20de0 100644 --- a/pgo/pgo.c +++ b/pgo/pgo.c @@ -136,6 +136,7 @@ notify_notify(const struct terminal *term, const char *title, const char *body) } void reaper_add(struct reaper *reaper, pid_t pid, reaper_cb cb, void *cb_data) {} +void reaper_del(struct reaper *reaper, pid_t pid) {} int From e563c863d174ecdf924ab9bb4fc19a248748e8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 12 Jan 2021 09:21:01 +0100 Subject: [PATCH 35/89] =?UTF-8?q?term:=20remove=20client=20application?= =?UTF-8?q?=E2=80=99s=20pid=20from=20reaper=20when=20shutting=20down?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we are shutting down the terminal, we explicitly wait for the child application to terminate (with a timeout, after which the child process is killed). I.e. there’s no need to let the reaper handle it. In fact, doing so leads to a crash since we will have destroyed (and thus free:d) the terminal instance when the reaper callback is called. --- terminal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/terminal.c b/terminal.c index ecb4e6cc..5b1d6190 100644 --- a/terminal.c +++ b/terminal.c @@ -1305,6 +1305,9 @@ term_shutdown(struct terminal *term) fdm_del(term->fdm, term->blink.fd); fdm_del(term->fdm, term->flash.fd); + /* We’ll deal with this explicitly */ + reaper_del(term->reaper, term->slave); + if (term->window != NULL && term->window->is_configured) fdm_del(term->fdm, term->ptmx); else From 324a1f80a8a90cb4d087c8056241cf5649dde5ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 12 Jan 2021 09:30:27 +0100 Subject: [PATCH 36/89] reaper: remove child from list *before* calling the user provided callback The user provided callback may call reaper_del(), in which case we will crash when we also try to remove the child from the list. Remove it from the list before the callback means reaper_del() (if called by the callback) will just loop through the entire list without finding the pid and thus do nothing. --- reaper.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/reaper.c b/reaper.c index 15e08539..9d98854e 100644 --- a/reaper.c +++ b/reaper.c @@ -138,23 +138,27 @@ fdm_reap(struct fdm *fdm, int fd, int events, void *data) } tll_foreach(reaper->children, it) { - struct child *child = &it->item; - pid_t pid = child->pid; + struct child *_child = &it->item; - if (pid != (pid_t)info.ssi_pid) + if (_child->pid != (pid_t)info.ssi_pid) continue; + /* Make sure we remove it *before* the callback, since it too + * may remove it */ + struct child child = it->item; + tll_remove(reaper->children, it); + bool reap_ourselves = true; - if (child->cb != NULL) - reap_ourselves = !child->cb(reaper, pid, child->cb_data); + if (child.cb != NULL) + reap_ourselves = !child.cb(reaper, child.pid, child.cb_data); if (reap_ourselves) { int result; - int res = waitpid(pid, &result, WNOHANG); + int res = waitpid(child.pid, &result, WNOHANG); if (res <= 0) { if (res < 0) - LOG_ERRNO("waitpid failed for pid=%d", pid); + LOG_ERRNO("waitpid failed for pid=%d", child.pid); continue; } @@ -165,8 +169,6 @@ fdm_reap(struct fdm *fdm, int fd, int events, void *data) else LOG_DBG("pid=%d: died of unknown resason", pid); } - - tll_remove(reaper->children, it); } if (hup) From 7f5e25956660c0eccf6058ba6998ec42057851d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 12 Jan 2021 14:45:04 +0100 Subject: [PATCH 37/89] selection: URI decoder: break out decoding of a single URI --- selection.c | 56 +++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/selection.c b/selection.c index db5a0b9f..05c699d6 100644 --- a/selection.c +++ b/selection.c @@ -1202,6 +1202,34 @@ fdm_receive_decoder_plain(struct clipboard_receive *ctx, char *data, size_t size ctx->cb(data, size, ctx->user); } +static bool +decode_one_uri(struct clipboard_receive *ctx, char *uri, size_t len) +{ + LOG_DBG("URI: \"%.*s\"", (int)len, uri); + + char *scheme, *host, *path; + if (!uri_parse(uri, len, &scheme, NULL, NULL, &host, NULL, &path, NULL, NULL)) { + LOG_ERR("drag-and-drop: invalid URI: %.*s", (int)len, uri); + return false; + } + + if (ctx->add_space) + ctx->cb(" ", 1, ctx->user); + ctx->add_space = true; + + if (strcmp(scheme, "file") == 0 && hostname_is_localhost(host)) { + ctx->cb("'", 1, ctx->user); + ctx->cb(path, strlen(path), ctx->user); + ctx->cb("'", 1, ctx->user); + } else + ctx->cb(uri, len, ctx->user); + + free(scheme); + free(host); + free(path); + return true; +} + static void fdm_receive_decoder_uri(struct clipboard_receive *ctx, char *data, size_t size) { @@ -1218,34 +1246,8 @@ fdm_receive_decoder_uri(struct clipboard_receive *ctx, char *data, size_t size) char *end = NULL; while ((end = memchr(start, '\n', ctx->buf.idx - (start - ctx->buf.data))) != NULL) { - const size_t len = end - start; - - LOG_DBG("URI: \"%.*s\"", (int)len, start); - - char *scheme, *host, *path; - if (!uri_parse(start, len, &scheme, NULL, NULL, &host, NULL, &path, NULL, NULL)) { - LOG_ERR("drag-and-drop: invalid URI: %.*s", (int)len, start); - start = end + 1; - continue; - } - - if (ctx->add_space) - ctx->cb(" ", 1, ctx->user); - ctx->add_space = true; - - - if (strcmp(scheme, "file") == 0 && hostname_is_localhost(host)) { - ctx->cb("'", 1, ctx->user); - ctx->cb(path, strlen(path), ctx->user); - ctx->cb("'", 1, ctx->user); - } else - ctx->cb(start, len, ctx->user); - + decode_one_uri(ctx, start, end - start); start = end + 1; - - free(scheme); - free(host); - free(path); } const size_t ofs = start - ctx->buf.data; From 4303401f69a92cfc1939354fe6ac2fb6489c01be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 12 Jan 2021 14:45:41 +0100 Subject: [PATCH 38/89] =?UTF-8?q?selection:=20add=20a=20=E2=80=98finish?= =?UTF-8?q?=E2=80=99=20function,=20called=20at=20the=20end=20of=20receivin?= =?UTF-8?q?g=20clipboard=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is necessary to decode the final URI in a text/uri-list offer if it hasn’t been newline terminated. --- selection.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/selection.c b/selection.c index 05c699d6..8a1e2943 100644 --- a/selection.c +++ b/selection.c @@ -1145,6 +1145,7 @@ struct clipboard_receive { struct itimerspec timeout; void (*decoder)(struct clipboard_receive *ctx, char *data, size_t size); + void (*finish)(struct clipboard_receive *ctx); /* URI state */ bool add_space; @@ -1202,6 +1203,11 @@ fdm_receive_decoder_plain(struct clipboard_receive *ctx, char *data, size_t size ctx->cb(data, size, ctx->user); } +static void +fdm_receive_finish_plain(struct clipboard_receive *ctx) +{ +} + static bool decode_one_uri(struct clipboard_receive *ctx, char *uri, size_t len) { @@ -1257,6 +1263,14 @@ fdm_receive_decoder_uri(struct clipboard_receive *ctx, char *data, size_t size) ctx->buf.idx = left; } +static void +fdm_receive_finish_uri(struct clipboard_receive *ctx) +{ + LOG_DBG("finish: %.*s", (int)ctx->buf.idx, ctx->buf.data); + if (ctx->buf.idx > 0) + decode_one_uri(ctx, ctx->buf.data, ctx->buf.idx); +} + static bool fdm_receive(struct fdm *fdm, int fd, int events, void *data) { @@ -1307,6 +1321,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) } done: + ctx->finish(ctx); clipboard_receive_done(fdm, ctx); return true; } @@ -1348,6 +1363,9 @@ begin_receive_clipboard(struct terminal *term, int read_fd, .decoder = (mime_type == DATA_OFFER_MIME_URI_LIST ? &fdm_receive_decoder_uri : &fdm_receive_decoder_plain), + .finish = (mime_type == DATA_OFFER_MIME_URI_LIST + ? &fdm_receive_finish_uri + : &fdm_receive_finish_plain), .cb = cb, .done = done, .user = user, @@ -1389,6 +1407,9 @@ text_from_clipboard(struct seat *seat, struct terminal *term, return; } + LOG_DBG("receive from clipboard: mime-type=%s", + mime_type_map[clipboard->mime_type]); + int read_fd = fds[0]; int write_fd = fds[1]; @@ -1533,6 +1554,9 @@ text_from_primary( return; } + LOG_DBG("receive from primary: mime-type=%s", + mime_type_map[primary->mime_type]); + int read_fd = fds[0]; int write_fd = fds[1]; From b645e35d5fa015bb1f3c4b98a56ee1c9aca5f788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 11 Jan 2021 09:35:01 +0100 Subject: [PATCH 39/89] changelog: glyhps -> glyphs --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68a81a63..91d479bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,7 @@ * `word-delimiters` option ignores `#` and subsequent characters (https://codeberg.org/dnkl/foot/issues/270) * Combining characters not being rendered when composed with colored - bitmap glyhps (i.e. colored emojis). + bitmap glyphs (i.e. colored emojis). ### Security From 641905548c5d99fc0989cd414f1e74383f7f3ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 12 Jan 2021 14:48:20 +0100 Subject: [PATCH 40/89] changelog: pasting non-newline terminated text/uri-list offers --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91d479bc..ade96df4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,8 @@ (https://codeberg.org/dnkl/foot/issues/270) * Combining characters not being rendered when composed with colored bitmap glyphs (i.e. colored emojis). +* Pasting URIs from the clipboard when the source has not newline + terminated the last URI (https://codeberg.org/dnkl/foot/issues/291). ### Security From 21053f2c6be18eed8a33986c263f2fd43909ad2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 12 Jan 2021 14:55:21 +0100 Subject: [PATCH 41/89] =?UTF-8?q?changelog:=20add=20hyphen=20between=20?= =?UTF-8?q?=E2=80=98newline=E2=80=99=20and=20=E2=80=98terminated=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ade96df4..1e16ab80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,8 +49,9 @@ (https://codeberg.org/dnkl/foot/issues/270) * Combining characters not being rendered when composed with colored bitmap glyphs (i.e. colored emojis). -* Pasting URIs from the clipboard when the source has not newline - terminated the last URI (https://codeberg.org/dnkl/foot/issues/291). +* Pasting URIs from the clipboard when the source has not + newline-terminated the last URI + (https://codeberg.org/dnkl/foot/issues/291). ### Security From bd49260e413dfecf6ce32c771cb81f2586cc67da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 12 Jan 2021 14:56:47 +0100 Subject: [PATCH 42/89] selection: uri decode: move zero-length check into decode_one_uri() --- selection.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/selection.c b/selection.c index 8a1e2943..7f94b0a7 100644 --- a/selection.c +++ b/selection.c @@ -1213,6 +1213,9 @@ decode_one_uri(struct clipboard_receive *ctx, char *uri, size_t len) { LOG_DBG("URI: \"%.*s\"", (int)len, uri); + if (len == 0) + return false; + char *scheme, *host, *path; if (!uri_parse(uri, len, &scheme, NULL, NULL, &host, NULL, &path, NULL, NULL)) { LOG_ERR("drag-and-drop: invalid URI: %.*s", (int)len, uri); @@ -1267,8 +1270,7 @@ static void fdm_receive_finish_uri(struct clipboard_receive *ctx) { LOG_DBG("finish: %.*s", (int)ctx->buf.idx, ctx->buf.data); - if (ctx->buf.idx > 0) - decode_one_uri(ctx, ctx->buf.data, ctx->buf.idx); + decode_one_uri(ctx, ctx->buf.data, ctx->buf.idx); } static bool From 72eed46a11346e08d80e15ba66d1d9ff75a11909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 15:12:10 +0100 Subject: [PATCH 43/89] readme: add IME to the feature list --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3ff90c01..e55d3e82 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ The fast, lightweight and minimalistic Wayland terminal emulator. * Scrollback search * Color emoji support * Server/daemon mode +* IME (via `text-input-v3`) * Multi-seat * [Synchronized Updates](https://gitlab.freedesktop.org/terminal-wg/specifications/-/merge_requests/2) support * [Sixel image support](https://en.wikipedia.org/wiki/Sixel) From acb3f1cd10213b95d9fc1b8f1010e105d5ebab9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 14 Jan 2021 14:41:34 +0100 Subject: [PATCH 44/89] =?UTF-8?q?sixel:=20current=20geometry:=20don?= =?UTF-8?q?=E2=80=99t=20exceed=20current=20window=20dimensions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is similar to what XTerm does, and fixes an issue with lsix, where the output did not wrap. --- CHANGELOG.md | 2 ++ sixel.c | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e16ab80..8acc6666 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,8 @@ * Pasting URIs from the clipboard when the source has not newline-terminated the last URI (https://codeberg.org/dnkl/foot/issues/291). +* Sixel “current geometry” query response not being bounded by the + current window dimensions (fixes `lsix` output) ### Security diff --git a/sixel.c b/sixel.c index bf06ef3e..cfca4d4a 100644 --- a/sixel.c +++ b/sixel.c @@ -1145,7 +1145,8 @@ sixel_geometry_report_current(struct terminal *term) { char reply[64]; snprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS", - term->sixel.max_width, term->sixel.max_height); + min(term->cols * term->cell_width, term->sixel.max_width), + min(term->rows * term->cell_height, term->sixel.max_height)); term_to_slave(term, reply, strlen(reply)); LOG_DBG("query response for current sixel geometry: %ux%u", From d2c00d73ec087e2266d8e07daacb0732fc1ca761 Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Thu, 14 Jan 2021 21:30:06 +0000 Subject: [PATCH 45/89] Add xsnprintf() and remove some unnecessary strlen(3) calls --- csi.c | 37 +++++++++++++++++++------------------ input.c | 5 +++-- main.c | 5 +++-- meson.build | 1 + osc.c | 13 +++++++------ sixel.c | 17 +++++++++-------- xsnprintf.c | 36 ++++++++++++++++++++++++++++++++++++ xsnprintf.h | 8 ++++++++ 8 files changed, 86 insertions(+), 36 deletions(-) create mode 100644 xsnprintf.c create mode 100644 xsnprintf.h diff --git a/csi.c b/csi.c index 14d41c12..e833d2a7 100644 --- a/csi.c +++ b/csi.c @@ -23,6 +23,7 @@ #include "version.h" #include "vt.h" #include "xmalloc.h" +#include "xsnprintf.h" #define UNHANDLED() LOG_DBG("unhandled: %s", csi_as_string(term, final, -1)) #define UNHANDLED_SGR(idx) LOG_DBG("unhandled: %s", csi_as_string(term, 'm', idx)) @@ -1204,9 +1205,9 @@ csi_dispatch(struct terminal *term, uint8_t final) if (x >= 0 && y >= 0) { char reply[64]; - snprintf(reply, sizeof(reply), "\033[3;%d;%dt", + size_t n = xsnprintf(reply, sizeof(reply), "\033[3;%d;%dt", x / term->scale, y / term->scale); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); } break; } @@ -1235,9 +1236,9 @@ csi_dispatch(struct terminal *term, uint8_t final) if (width >= 0 && height >= 0) { char reply[64]; - snprintf(reply, sizeof(reply), "\033[4;%d;%dt", + size_t n = xsnprintf(reply, sizeof(reply), "\033[4;%d;%dt", height / term->scale, width / term->scale); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); } break; } @@ -1245,10 +1246,10 @@ csi_dispatch(struct terminal *term, uint8_t final) case 15: /* report screen size in pixels */ tll_foreach(term->window->on_outputs, it) { char reply[64]; - snprintf(reply, sizeof(reply), "\033[5;%d;%dt", + size_t n = xsnprintf(reply, sizeof(reply), "\033[5;%d;%dt", it->item->dim.px_scaled.height, it->item->dim.px_scaled.width); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); break; } @@ -1258,28 +1259,28 @@ csi_dispatch(struct terminal *term, uint8_t final) case 16: { /* report cell size in pixels */ char reply[64]; - snprintf(reply, sizeof(reply), "\033[6;%d;%dt", + size_t n = xsnprintf(reply, sizeof(reply), "\033[6;%d;%dt", term->cell_height / term->scale, term->cell_width / term->scale); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); break; } case 18: { /* text area size in chars */ char reply[64]; - snprintf(reply, sizeof(reply), "\033[8;%d;%dt", + size_t n = xsnprintf(reply, sizeof(reply), "\033[8;%d;%dt", term->rows, term->cols); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); break; } case 19: { /* report screen size in chars */ tll_foreach(term->window->on_outputs, it) { char reply[64]; - snprintf(reply, sizeof(reply), "\033[9;%d;%dt", + size_t n = xsnprintf(reply, sizeof(reply), "\033[9;%d;%dt", it->item->dim.px_real.height / term->cell_height / term->scale, it->item->dim.px_real.width / term->cell_width / term->scale); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); break; } @@ -1341,9 +1342,9 @@ csi_dispatch(struct terminal *term, uint8_t final) * terminfo says the receiver of the reply should * decrement, hence we must add 1 */ char reply[64]; - snprintf(reply, sizeof(reply), "\x1b[%d;%dR", + size_t n = xsnprintf(reply, sizeof(reply), "\x1b[%d;%dR", row + 1, term->grid->cursor.point.col + 1); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); break; } @@ -1468,10 +1469,10 @@ csi_dispatch(struct terminal *term, uint8_t final) static_assert(FOOT_PATCH < 100, "Patch version must not exceed 99"); char reply[64]; - snprintf(reply, sizeof(reply), "\033[>1;%02u%02u%02u;0c", + size_t n = xsnprintf(reply, sizeof(reply), "\033[>1;%02u%02u%02u;0c", FOOT_MAJOR, FOOT_MINOR, FOOT_PATCH); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); break; case 'm': @@ -1625,8 +1626,8 @@ csi_dispatch(struct terminal *term, uint8_t final) value = 0; char reply[32]; - snprintf(reply, sizeof(reply), "\033[?%u;%u$y", param, value); - term_to_slave(term, reply, strlen(reply)); + size_t n = xsnprintf(reply, sizeof(reply), "\033[?%u;%u$y", param, value); + term_to_slave(term, reply, n); break; } diff --git a/input.c b/input.c index e4a1f306..205d7559 100644 --- a/input.c +++ b/input.c @@ -37,6 +37,7 @@ #include "util.h" #include "vt.h" #include "xmalloc.h" +#include "xsnprintf.h" struct pipe_context { char *text; @@ -963,8 +964,8 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial, assert(modify_param != 0); char reply[1024]; - snprintf(reply, sizeof(reply), "\x1b[27;%d;%d~", modify_param, sym); - term_to_slave(term, reply, strlen(reply)); + size_t n = xsnprintf(reply, sizeof(reply), "\x1b[27;%d;%d~", modify_param, sym); + term_to_slave(term, reply, n); } else { diff --git a/main.c b/main.c index 094c242a..4423ec7a 100644 --- a/main.c +++ b/main.c @@ -31,6 +31,7 @@ #include "util.h" #include "version.h" #include "xmalloc.h" +#include "xsnprintf.h" static volatile sig_atomic_t aborted = 0; @@ -127,9 +128,9 @@ print_pid(const char *pid_file, bool *unlink_at_exit) if (pid_fd >= 0) { char pid[32]; - snprintf(pid, sizeof(pid), "%u\n", getpid()); + size_t n = xsnprintf(pid, sizeof(pid), "%u\n", getpid()); - ssize_t bytes = write(pid_fd, pid, strlen(pid)); + ssize_t bytes = write(pid_fd, pid, n); close(pid_fd); if (bytes < 0) { diff --git a/meson.build b/meson.build index b382b496..de159204 100644 --- a/meson.build +++ b/meson.build @@ -112,6 +112,7 @@ misc = static_library( 'misc.c', 'misc.h', 'uri.c', 'uri.h', 'xmalloc.c', 'xmalloc.h', + 'xsnprintf.c', 'xsnprintf.h', ) vtlib = static_library( diff --git a/osc.c b/osc.c index d24a4603..0e658ce6 100644 --- a/osc.c +++ b/osc.c @@ -17,6 +17,7 @@ #include "uri.h" #include "vt.h" #include "xmalloc.h" +#include "xsnprintf.h" #define UNHANDLED() LOG_DBG("unhandled: OSC: %.*s", (int)term->vt.osc.idx, term->vt.osc.data) @@ -522,9 +523,9 @@ osc_dispatch(struct terminal *term) uint8_t b = (color >> 0) & 0xff; char reply[32]; - snprintf(reply, sizeof(reply), "\033]4;%u;rgb:%02x/%02x/%02x\033\\", + size_t n = xsnprintf(reply, sizeof(reply), "\033]4;%u;rgb:%02x/%02x/%02x\033\\", idx, r, g, b); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); } else { @@ -568,11 +569,11 @@ osc_dispatch(struct terminal *term) * E.g. for color 0xdcdccc we reply "\033]10;rgb:dc/dc/cc\033\\" */ char reply[32]; - snprintf( + size_t n = xsnprintf( reply, sizeof(reply), "\033]%u;rgb:%02x/%02x/%02x\033\\", param, r, g, b); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); break; } @@ -602,8 +603,8 @@ osc_dispatch(struct terminal *term) uint8_t b = (term->cursor_color.cursor >> 0) & 0xff; char reply[32]; - snprintf(reply, sizeof(reply), "\033]12;rgb:%02x/%02x/%02x\033\\", r, g, b); - term_to_slave(term, reply, strlen(reply)); + size_t n = xsnprintf(reply, sizeof(reply), "\033]12;rgb:%02x/%02x/%02x\033\\", r, g, b); + term_to_slave(term, reply, n); break; } diff --git a/sixel.c b/sixel.c index cfca4d4a..96d2aba2 100644 --- a/sixel.c +++ b/sixel.c @@ -10,6 +10,7 @@ #include "hsl.h" #include "util.h" #include "xmalloc.h" +#include "xsnprintf.h" static size_t count; @@ -1101,8 +1102,8 @@ void sixel_colors_report_current(struct terminal *term) { char reply[24]; - snprintf(reply, sizeof(reply), "\033[?1;0;%uS", term->sixel.palette_size); - term_to_slave(term, reply, strlen(reply)); + size_t n = xsnprintf(reply, sizeof(reply), "\033[?1;0;%uS", term->sixel.palette_size); + term_to_slave(term, reply, n); LOG_DBG("query response for current color count: %u", term->sixel.palette_size); } @@ -1135,8 +1136,8 @@ void sixel_colors_report_max(struct terminal *term) { char reply[24]; - snprintf(reply, sizeof(reply), "\033[?1;0;%uS", SIXEL_MAX_COLORS); - term_to_slave(term, reply, strlen(reply)); + size_t n = xsnprintf(reply, sizeof(reply), "\033[?1;0;%uS", SIXEL_MAX_COLORS); + term_to_slave(term, reply, n); LOG_DBG("query response for max color count: %u", SIXEL_MAX_COLORS); } @@ -1144,10 +1145,10 @@ void sixel_geometry_report_current(struct terminal *term) { char reply[64]; - snprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS", + size_t n = xsnprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS", min(term->cols * term->cell_width, term->sixel.max_width), min(term->rows * term->cell_height, term->sixel.max_height)); - term_to_slave(term, reply, strlen(reply)); + term_to_slave(term, reply, n); LOG_DBG("query response for current sixel geometry: %ux%u", term->sixel.max_width, term->sixel.max_height); @@ -1178,8 +1179,8 @@ sixel_geometry_report_max(struct terminal *term) unsigned max_height = term->rows * term->cell_height; char reply[64]; - snprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS", max_width, max_height); - term_to_slave(term, reply, strlen(reply)); + size_t n = xsnprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS", max_width, max_height); + term_to_slave(term, reply, n); LOG_DBG("query response for max sixel geometry: %ux%u", max_width, max_height); diff --git a/xsnprintf.c b/xsnprintf.c new file mode 100644 index 00000000..e8e8d25d --- /dev/null +++ b/xsnprintf.c @@ -0,0 +1,36 @@ +#include "xsnprintf.h" + +#include +#include +#include + +size_t +xvsnprintf(char *buf, size_t n, const char *format, va_list ap) +{ + assert(n <= INT_MAX); + int len = vsnprintf(buf, n, format, ap); + + /* + * ISO C11 §7.21.6.5 states: + * "The snprintf function returns the number of characters that + * would have been written had n been sufficiently large, not + * counting the terminating null character, or a negative value + * if an encoding error occurred. Thus, the null-terminated output + * has been completely written if and only if the returned value + * is nonnegative and less than n." + */ + assert(len >= 0); + assert(len < (int)n); + + return (size_t)len; +} + +size_t +xsnprintf(char *buf, size_t n, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + size_t len = xvsnprintf(buf, n, format, ap); + va_end(ap); + return len; +} diff --git a/xsnprintf.h b/xsnprintf.h new file mode 100644 index 00000000..c745ef3e --- /dev/null +++ b/xsnprintf.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include +#include "macros.h" + +size_t xsnprintf(char *buf, size_t len, const char *fmt, ...) PRINTF(3) NONNULL_ARGS; +size_t xvsnprintf(char *buf, size_t len, const char *fmt, va_list ap) VPRINTF(3) NONNULL_ARGS; From b2a3cdb7230e0740dc1e476f1e0a6c6b62302836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 16 Jan 2021 12:27:58 +0100 Subject: [PATCH 46/89] =?UTF-8?q?input:=20don=E2=80=99t=20enable=20keyboar?= =?UTF-8?q?d=20repeat=20timer=20when=20repeat=20rate=20is=20zero?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wayland-client-protocol.h: Negative values for either rate or delay are illegal. A rate of zero will disable any repeating (regardless of the value of delay). This fixes a division-by-zero crash. --- CHANGELOG.md | 1 + input.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8acc6666..ff7e943a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ (https://codeberg.org/dnkl/foot/issues/291). * Sixel “current geometry” query response not being bounded by the current window dimensions (fixes `lsix` output) +* Crash on keyboard input when repeat rate was zero (i.e. no repeat). ### Security diff --git a/input.c b/input.c index 205d7559..6d260291 100644 --- a/input.c +++ b/input.c @@ -569,6 +569,9 @@ start_repeater(struct seat *seat, uint32_t key) if (seat->kbd.repeat.dont_re_repeat) return true; + if (seat->kbd.repeat.rate == 0) + return true; + struct itimerspec t = { .it_value = {.tv_sec = 0, .tv_nsec = seat->kbd.repeat.delay * 1000000}, .it_interval = {.tv_sec = 0, .tv_nsec = 1000000000 / seat->kbd.repeat.rate}, From fd4511a3dc9f29587cf71c549b282393f1315ae0 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 14:38:45 +0000 Subject: [PATCH 47/89] main: correct header for mode macros main.c:13:10: fatal error: 'sys/sysinfo.h' file not found #include ^~~~~~~~~~~~~~~ main.c:122:28: error: use of undeclared identifier 'S_IRUSR' S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { ^ main.c:122:38: error: use of undeclared identifier 'S_IWUSR' S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { ^ main.c:122:48: error: use of undeclared identifier 'S_IRGRP' S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { ^ main.c:122:58: error: use of undeclared identifier 'S_IROTH' S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) { ^ --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index 4423ec7a..f8da2fb3 100644 --- a/main.c +++ b/main.c @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include From dd485d941499703f92515440d3a75e62a7fa81d5 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 14:34:30 +0000 Subject: [PATCH 48/89] client/server: switch to POSIX header for sockaddr_un client.c:13:10: fatal error: 'linux/un.h' file not found #include ^~~~~~~~~~~~ client.c:195:24: error: variable has incomplete type 'struct sockaddr_un' struct sockaddr_un addr = {.sun_family = AF_UNIX}; ^ client.c:195:12: note: forward declaration of 'struct sockaddr_un' struct sockaddr_un addr = {.sun_family = AF_UNIX}; ^ server.c:11:10: fatal error: 'linux/un.h' file not found #include ^~~~~~~~~~~~ server.c:307:24: error: variable has incomplete type 'struct sockaddr_un' struct sockaddr_un addr; ^ server.c:307:12: note: forward declaration of 'struct sockaddr_un' struct sockaddr_un addr; ^ server.c:347:24: error: variable has incomplete type 'struct sockaddr_un' struct sockaddr_un addr = {.sun_family = AF_UNIX}; ^ server.c:347:12: note: forward declaration of 'struct sockaddr_un' struct sockaddr_un addr = {.sun_family = AF_UNIX}; ^ server.c:394:24: error: variable has incomplete type 'struct sockaddr_un' struct sockaddr_un addr = {.sun_family = AF_UNIX}; ^ server.c:394:12: note: forward declaration of 'struct sockaddr_un' struct sockaddr_un addr = {.sun_family = AF_UNIX}; ^ --- client.c | 2 +- server.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client.c b/client.c index 58a500eb..cb24ab12 100644 --- a/client.c +++ b/client.c @@ -10,7 +10,7 @@ #include #include -#include +#include #define LOG_MODULE "foot-client" #define LOG_ENABLE_DBG 0 diff --git a/server.c b/server.c index e6c3aae3..bfd9fc46 100644 --- a/server.c +++ b/server.c @@ -8,7 +8,7 @@ #include #include -#include +#include #include From 14e761179b8cdafc6088bf221c8a5f7d70ca56ec Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 15:05:22 +0000 Subject: [PATCH 49/89] terminal: add/exclude missing headers terminal.c:3:10: fatal error: 'malloc.h' file not found #include ^~~~~~~~~~ terminal.c:1512:9: error: implicit declaration of function 'sigaction' is invalid in C99 [-Werror,-Wimplicit-function-declaration] sigaction(SIGALRM, &(const struct sigaction){.sa_handler = &sig_alarm}, NULL); ^ terminal.c:1532:21: error: implicit declaration of function 'kill' is invalid in C99 [-Werror,-Wimplicit-function-declaration] kill(term->slave, kill_signal); ^ --- terminal.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/terminal.c b/terminal.c index 5b1d6190..e07ac50e 100644 --- a/terminal.c +++ b/terminal.c @@ -1,6 +1,9 @@ #include "terminal.h" +#if defined(__GLIBC__) #include +#endif +#include #include #include #include From fb660262ab929688f4c7ac1fccd3d6d360ec3475 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 14:41:02 +0000 Subject: [PATCH 50/89] uri: switch to conservative maximum hostname length Current maximum is provided by sysconf(_SC_HOST_NAME_MAX) instead. uri.c:269:20: error: use of undeclared identifier 'HOST_NAME_MAX' char this_host[HOST_NAME_MAX]; ^ --- uri.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uri.c b/uri.c index a0e3838a..be1e0afe 100644 --- a/uri.c +++ b/uri.c @@ -266,7 +266,7 @@ err: bool hostname_is_localhost(const char *hostname) { - char this_host[HOST_NAME_MAX]; + char this_host[_POSIX_HOST_NAME_MAX]; if (gethostname(this_host, sizeof(this_host)) < 0) this_host[0] = '\0'; From e462419e348ac47ef093c1e7090284160ece3ed9 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 15:09:06 +0000 Subject: [PATCH 51/89] slave: skip e310487dae99 if not supported slave.c:188:26: error: use of undeclared identifier 'IUTF8' flags.c_iflag |= IUTF8; ^ --- slave.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slave.c b/slave.c index eaa21060..d380749e 100644 --- a/slave.c +++ b/slave.c @@ -178,6 +178,7 @@ slave_exec(int ptmx, char *argv[], int err_fd, bool login_shell, goto err; } +#ifdef IUTF8 { struct termios flags; if (tcgetattr(pts, &flags) < 0) { @@ -191,6 +192,7 @@ slave_exec(int ptmx, char *argv[], int err_fd, bool login_shell, goto err; } } +#endif if (tll_length(*notifications) > 0) { int flags = fcntl(pts, F_GETFL); From 6626ef5807a65bba514a80cad6a2a4312cb73cb1 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 15:17:38 +0000 Subject: [PATCH 52/89] shm: silence -Wunused-function on i386 shm.c:134:1: error: unused function 'page_size' [-Werror,-Wunused-function] page_size(void) ^ --- shm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shm.c b/shm.c index 1d08d56c..c2cd4dcf 100644 --- a/shm.c +++ b/shm.c @@ -130,6 +130,7 @@ static const struct wl_buffer_listener buffer_listener = { .release = &buffer_release, }; +#if !defined(__i386__) static size_t page_size(void) { @@ -146,6 +147,7 @@ page_size(void) assert(size > 0); return size; } +#endif static bool instantiate_offset(struct wl_shm *shm, struct buffer *buf, off_t new_offset) From 93fd77e01b25f2cec62b252e077d48f73dc76c9a Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 15:22:51 +0000 Subject: [PATCH 53/89] shm: disable fallocate optimization if not supported shm.c:301:26: error: implicit declaration of function 'fallocate' is invalid in C99 [-Werror,-Wimplicit-function-declaration] can_punch_hole = fallocate( ^ shm.c:302:22: error: use of undeclared identifier 'FALLOC_FL_PUNCH_HOLE' pool_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 1) == 0; ^ shm.c:302:45: error: use of undeclared identifier 'FALLOC_FL_KEEP_SIZE' pool_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 1) == 0; ^ shm.c:432:9: error: implicit declaration of function 'fallocate' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (fallocate( ^ shm.c:434:13: error: use of undeclared identifier 'FALLOC_FL_PUNCH_HOLE' FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ^ shm.c:434:36: error: use of undeclared identifier 'FALLOC_FL_KEEP_SIZE' FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ^ shm.c:501:9: error: implicit declaration of function 'fallocate' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (fallocate( ^ shm.c:503:13: error: use of undeclared identifier 'FALLOC_FL_PUNCH_HOLE' FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ^ shm.c:503:36: error: use of undeclared identifier 'FALLOC_FL_KEEP_SIZE' FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ^ shm.c:597:9: error: implicit declaration of function 'fallocate' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (fallocate( ^ shm.c:599:13: error: use of undeclared identifier 'FALLOC_FL_PUNCH_HOLE' FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ^ shm.c:599:36: error: use of undeclared identifier 'FALLOC_FL_KEEP_SIZE' FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, ^ --- shm.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/shm.c b/shm.c index c2cd4dcf..81de653b 100644 --- a/shm.c +++ b/shm.c @@ -299,7 +299,7 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie, if (!can_punch_hole_initialized) { can_punch_hole_initialized = true; -#if defined(__x86_64__) +#if defined(__x86_64__) && defined(FALLOC_FL_PUNCH_HOLE) can_punch_hole = fallocate( pool_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 1) == 0; @@ -410,6 +410,7 @@ shm_can_scroll(const struct buffer *buf) #endif } +#if defined(FALLOC_FL_PUNCH_HOLE) static bool wrap_buffer(struct wl_shm *shm, struct buffer *buf, off_t new_offset) { @@ -642,12 +643,14 @@ err: abort(); return false; } +#endif /* FALLOC_FL_PUNCH_HOLE */ bool shm_scroll(struct wl_shm *shm, struct buffer *buf, int rows, int top_margin, int top_keep_rows, int bottom_margin, int bottom_keep_rows) { +#if defined(FALLOC_FL_PUNCH_HOLE) if (!shm_can_scroll(buf)) return false; @@ -655,6 +658,9 @@ shm_scroll(struct wl_shm *shm, struct buffer *buf, int rows, return rows > 0 ? shm_scroll_forward(shm, buf, rows, top_margin, top_keep_rows, bottom_margin, bottom_keep_rows) : shm_scroll_reverse(shm, buf, -rows, top_margin, top_keep_rows, bottom_margin, bottom_keep_rows); +#else + return false; +#endif } void From fcf3f124d626afbc2438756efb7605a4324eb94d Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 14:20:55 +0000 Subject: [PATCH 54/89] shm: unbreak build without memfd_create New FreeBSD versions have memfd_create but other BSDs don't. pgo/pgo.c:260:22: error: implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration] int mem_fd = memfd_create("foot-pgo-ptmx", MFD_CLOEXEC); ^ pgo/pgo.c:260:52: error: use of undeclared identifier 'MFD_CLOEXEC' int mem_fd = memfd_create("foot-pgo-ptmx", MFD_CLOEXEC); ^ shm.c:13:10: fatal error: 'linux/mman.h' file not found #include ^~~~~~~~~~~~~~ shm.c:277:15: error: implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration] pool_fd = memfd_create("foot-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); ^ shm.c:277:60: error: use of undeclared identifier 'MFD_CLOEXEC' pool_fd = memfd_create("foot-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); ^ shm.c:277:74: error: use of undeclared identifier 'MFD_ALLOW_SEALING' pool_fd = memfd_create("foot-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); ^ shm.c:339:15: error: use of undeclared identifier 'F_SEAL_GROW' F_SEAL_GROW | F_SEAL_SHRINK | /*F_SEAL_FUTURE_WRITE |*/ F_SEAL_SEAL) < 0) ^ shm.c:339:29: error: use of undeclared identifier 'F_SEAL_SHRINK' F_SEAL_GROW | F_SEAL_SHRINK | /*F_SEAL_FUTURE_WRITE |*/ F_SEAL_SEAL) < 0) ^ shm.c:339:71: error: use of undeclared identifier 'F_SEAL_SEAL' F_SEAL_GROW | F_SEAL_SHRINK | /*F_SEAL_FUTURE_WRITE |*/ F_SEAL_SEAL) < 0) ^ shm.c:338:24: error: use of undeclared identifier 'F_ADD_SEALS' if (fcntl(pool_fd, F_ADD_SEALS, ^ --- meson.build | 4 ++++ pgo/pgo.c | 9 +++++++++ shm.c | 14 ++++++++++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index de159204..e1b9eb51 100644 --- a/meson.build +++ b/meson.build @@ -12,6 +12,10 @@ is_debug_build = get_option('buildtype').startswith('debug') cc = meson.get_compiler('c') +if cc.has_function('memfd_create') + add_project_arguments('-DMEMFD_CREATE', language: 'c') +endif + add_project_arguments( ['-D_POSIX_C_SOURCE=200809L', '-D_GNU_SOURCE=200809L'] + (is_debug_build diff --git a/pgo/pgo.c b/pgo/pgo.c index 39c20de0..fa95a0ec 100644 --- a/pgo/pgo.c +++ b/pgo/pgo.c @@ -257,7 +257,16 @@ main(int argc, const char *const *argv) close(fd); +#if defined(MEMFD_CREATE) int mem_fd = memfd_create("foot-pgo-ptmx", MFD_CLOEXEC); +#elif defined(__FreeBSD__) + // memfd_create on FreeBSD 13 is SHM_ANON without sealing support + int mem_fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600); +#else + char name[] = "/tmp/foot-pgo-ptmx-XXXXXX"; + int mem_fd = mkostemp(name, O_CLOEXEC); + unlink(name); +#endif if (mem_fd < 0) { fprintf(stderr, "error: failed to create memory FD\n"); goto out; diff --git a/shm.c b/shm.c index 81de653b..85d67f9c 100644 --- a/shm.c +++ b/shm.c @@ -10,8 +10,7 @@ #include #include #include -#include -#include +#include #include #include @@ -276,7 +275,16 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie, LOG_DBG("cookie=%lx: allocating new buffer: %zu KB", cookie, size / 1024); /* Backing memory for SHM */ +#if defined(MEMFD_CREATE) pool_fd = memfd_create("foot-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); +#elif defined(__FreeBSD__) + // memfd_create on FreeBSD 13 is SHM_ANON without sealing support + pool_fd = shm_open(SHM_ANON, O_RDWR | O_CLOEXEC, 0600); +#else + char name[] = "/tmp/foot-wayland-shm-buffer-pool-XXXXXX"; + pool_fd = mkostemp(name, O_CLOEXEC); + unlink(name); +#endif if (pool_fd == -1) { LOG_ERRNO("failed to create SHM backing memory file"); goto err; @@ -335,6 +343,7 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie, goto err; } +#if defined(MEMFD_CREATE) /* Seal file - we no longer allow any kind of resizing */ /* TODO: wayland mmaps(PROT_WRITE), for some unknown reason, hence we cannot use F_SEAL_FUTURE_WRITE */ if (fcntl(pool_fd, F_ADD_SEALS, @@ -343,6 +352,7 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie, LOG_ERRNO("failed to seal SHM backing memory file"); /* This is not a fatal error */ } +#endif pool = wl_shm_create_pool(shm, pool_fd, memfd_size); if (pool == NULL) { From 9b09049c1ccfe70cfdadb1de92c0115b283f986d Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 14:49:43 +0000 Subject: [PATCH 55/89] render: set thread name in a portable way prctl is Linux-only but pthread_setname_np is same as PR_SET_NAME. Solaris and FreeBSD >= 13 have pthread_setname_np similar to Linux. DragonFly, OpenBSD, FreeBSD < 13 lack pthread_setname_np but provide pthread_set_name_np which doesn't return a value. NetBSD requires 3 arguments for pthread_setname_np where the last one is void *. render.c:8:10: fatal error: 'sys/prctl.h' file not found #include ^~~~~~~~~~~~~ render.c:1234:9: error: implicit declaration of function 'prctl' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (prctl(PR_SET_NAME, proc_title, 0, 0, 0) < 0) ^ render.c:1234:15: error: use of undeclared identifier 'PR_SET_NAME' if (prctl(PR_SET_NAME, proc_title, 0, 0, 0) < 0) ^ --- render.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/render.c b/render.c index ba6019a3..0828e21a 100644 --- a/render.c +++ b/render.c @@ -5,7 +5,13 @@ #include #include #include -#include +#include +#if __has_include() +#include +#define pthread_setname_np(thread, name) (pthread_set_name_np(thread, name), 0) +#elif defined(__NetBSD__) +#define pthread_setname_np(thread, name) pthread_setname_np(thread, "%s", (void *)name) +#endif #include #include @@ -1208,7 +1214,7 @@ render_worker_thread(void *_ctx) char proc_title[16]; snprintf(proc_title, sizeof(proc_title), "foot:render:%d", my_id); - if (prctl(PR_SET_NAME, proc_title, 0, 0, 0) < 0) + if (pthread_setname_np(pthread_self(), proc_title) < 0) LOG_ERRNO("render worker %d: failed to set process title", my_id); sem_t *start = &term->render.workers.start; From 909938b83c7f62302f94e8184e7717737265fe08 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 15:40:32 +0000 Subject: [PATCH 56/89] meson: don't force POSIX compliance foot uses a number of functions not in any POSIX version. On non-glibc systems defining _POSIX_C_SOURCE usually hides non-compliant interfaces. In file included from grid.c:1: In file included from grid.h:4: terminal.h:45:15: error: expected parameter declarator static_assert(sizeof(struct attributes) == 8, "bad size"); ^ terminal.h:45:15: error: expected ')' terminal.h:45:14: note: to match this '(' static_assert(sizeof(struct attributes) == 8, "bad size"); ^ terminal.h:45:1: error: type specifier missing, defaults to 'int' [-Werror,-Wimplicit-int] static_assert(sizeof(struct attributes) == 8, "bad size"); ^ terminal.h:55:15: error: expected parameter declarator static_assert(sizeof(struct cell) == 12, "bad size"); ^ terminal.h:55:15: error: expected ')' terminal.h:55:14: note: to match this '(' static_assert(sizeof(struct cell) == 12, "bad size"); ^ terminal.h:55:1: error: type specifier missing, defaults to 'int' [-Werror,-Wimplicit-int] static_assert(sizeof(struct cell) == 12, "bad size"); ^ grid.c:317:32: error: implicit declaration of function 'wcwidth' is invalid in C99 [-Werror,-Wimplicit-function-declaration] int width = max(1, wcwidth(wc)); ^ grid.c:317:32: note: did you mean '__wcwidth'? /usr/include/_ctype.h:161:1: note: '__wcwidth' declared here __wcwidth(__ct_rune_t _c) ^ selection.c:1695:9: error: implicit declaration of function 'pipe2' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (pipe2(fds, O_CLOEXEC) == -1) { ^ selection.c:1695:9: note: did you mean 'pipe'? /usr/include/unistd.h:358:6: note: 'pipe' declared here int pipe(int *); ^ selection.c:1842:9: error: implicit declaration of function 'pipe2' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (pipe2(fds, O_CLOEXEC) == -1) { ^ selection.c:2129:9: error: implicit declaration of function 'pipe2' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (pipe2(fds, O_CLOEXEC) == -1) { ^ vt.c:241:12: error: implicit declaration of function 'wcwidth' is invalid in C99 [-Werror,-Wimplicit-function-declaration] assert(wcwidth(c) == 1); ^ vt.c:241:12: note: did you mean '__wcwidth'? /usr/include/_ctype.h:161:1: note: '__wcwidth' declared here __wcwidth(__ct_rune_t _c) ^ vt.c:544:17: error: implicit declaration of function 'wcwidth' is invalid in C99 [-Werror,-Wimplicit-function-declaration] int width = wcwidth(wc); ^ csi.c:713:35: error: implicit declaration of function 'wcwidth' is invalid in C99 [-Werror,-Wimplicit-function-declaration] const int width = wcwidth(term->vt.last_printed); ^ csi.c:713:35: note: did you mean '__wcwidth'? /usr/include/_ctype.h:161:1: note: '__wcwidth' declared here __wcwidth(__ct_rune_t _c) ^ ime.c:169:25: error: implicit declaration of function 'wcwidth' is invalid in C99 [-Werror,-Wimplicit-function-declaration] int width = max(wcwidth(term->ime.preedit.text[i]), 1); ^ ime.c:169:25: note: did you mean '__wcwidth'? /usr/include/_ctype.h:161:1: note: '__wcwidth' declared here __wcwidth(__ct_rune_t _c) ^ quirks.c:81:22: error: implicit declaration of function 'strcasestr' is invalid in C99 [-Werror,-Wimplicit-function-declaration] is_kde = strcasestr(cur_desktop, "kde") != NULL; ^ quirks.c:81:53: error: comparison between pointer and integer ('int' and 'void *') [-Werror,-Wpointer-integer-compare] is_kde = strcasestr(cur_desktop, "kde") != NULL; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~ config.c:89:15: error: expected ')' static_assert(ALEN(binding_action_map) == BIND_ACTION_COUNT, ^ util.h:5:18: note: expanded from macro 'ALEN' #define ALEN(v) (sizeof(v) / sizeof((v)[0])) ^ config.c:358:12: error: implicit declaration of function 'strcasecmp' is invalid in C99 [-Werror,-Wimplicit-function-declaration] return strcasecmp(s, "on") == 0 || ^ config.c:358:12: note: did you mean 'wcscasecmp'? /usr/include/wchar.h:223:5: note: 'wcscasecmp' declared here int wcscasecmp(const wchar_t *, const wchar_t *); ^ config.c:510:23: error: implicit declaration of function 'strcasecmp' is invalid in C99 [-Werror,-Wimplicit-function-declaration] bool center = strcasecmp(mode, "center") == 0; ^ config.c:1243:13: error: implicit declaration of function 'strcasecmp' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (strcasecmp(value, "none") == 0) { ^ config.c:1330:13: error: implicit declaration of function 'strcasecmp' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (strcasecmp(value, "none") == 0) { ^ config.c:1534:13: error: implicit declaration of function 'strcasecmp' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (strcasecmp(value, "none") == 0) { ^ spawn.c:20:9: error: implicit declaration of function 'pipe2' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (pipe2(pipe_fds, O_CLOEXEC) < 0) { ^ spawn.c:20:9: note: did you mean 'pipe'? /usr/include/unistd.h:358:6: note: 'pipe' declared here int pipe(int *); ^ spawn.c:52:5: error: implicit declaration of function 'static_assert' is invalid in C99 [-Werror,-Wimplicit-function-declaration] static_assert(sizeof(_errno) == sizeof(errno), "errno size mismatch"); ^ server.c:309:21: error: implicit declaration of function 'accept4' is invalid in C99 [-Werror,-Wimplicit-function-declaration] int client_fd = accept4( ^ server.c:309:21: note: did you mean 'accept'? /usr/include/sys/socket.h:679:5: note: 'accept' declared here int accept(int, struct sockaddr * __restrict, socklen_t * __restrict); ^ server.c:310:59: error: use of undeclared identifier 'SOCK_CLOEXEC' server->fd, (struct sockaddr *)&addr, &addr_size, SOCK_CLOEXEC | SOCK_NONBLOCK); ^ server.c:310:74: error: use of undeclared identifier 'SOCK_NONBLOCK' server->fd, (struct sockaddr *)&addr, &addr_size, SOCK_CLOEXEC | SOCK_NONBLOCK); ^ server.c:341:44: error: use of undeclared identifier 'SOCK_CLOEXEC' int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); ^ server.c:341:59: error: use of undeclared identifier 'SOCK_NONBLOCK' int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); ^ server.c:371:44: error: use of undeclared identifier 'SOCK_CLOEXEC' int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); ^ server.c:371:59: error: use of undeclared identifier 'SOCK_NONBLOCK' int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); ^ shm.c:138:26: error: use of undeclared identifier '_SC_PAGE_SIZE' long n = sysconf(_SC_PAGE_SIZE); ^ shm.c:279:15: error: implicit declaration of function 'memfd_create' is invalid in C99 [-Werror,-Wimplicit-function-declaration] pool_fd = memfd_create("foot-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); ^ shm.c:279:15: note: did you mean 'timer_create'? /usr/include/time.h:170:5: note: 'timer_create' declared here int timer_create(clockid_t, struct sigevent *__restrict, timer_t *__restrict); ^ shm.c:279:60: error: use of undeclared identifier 'MFD_CLOEXEC' pool_fd = memfd_create("foot-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); ^ shm.c:279:74: error: use of undeclared identifier 'MFD_ALLOW_SEALING' pool_fd = memfd_create("foot-wayland-shm-buffer-pool", MFD_CLOEXEC | MFD_ALLOW_SEALING); ^ shm.c:350:15: error: use of undeclared identifier 'F_SEAL_GROW' F_SEAL_GROW | F_SEAL_SHRINK | /*F_SEAL_FUTURE_WRITE |*/ F_SEAL_SEAL) < 0) ^ shm.c:350:29: error: use of undeclared identifier 'F_SEAL_SHRINK' F_SEAL_GROW | F_SEAL_SHRINK | /*F_SEAL_FUTURE_WRITE |*/ F_SEAL_SEAL) < 0) ^ shm.c:350:71: error: use of undeclared identifier 'F_SEAL_SEAL' F_SEAL_GROW | F_SEAL_SHRINK | /*F_SEAL_FUTURE_WRITE |*/ F_SEAL_SEAL) < 0) ^ shm.c:349:24: error: use of undeclared identifier 'F_ADD_SEALS' if (fcntl(pool_fd, F_ADD_SEALS, ^ slave.c:151:28: error: implicit declaration of function 'ptsname' is invalid in C99 [-Werror,-Wimplicit-function-declaration] const char *pts_name = ptsname(ptmx); ^ slave.c:151:28: note: did you mean 'ttyname'? /usr/include/unistd.h:371:7: note: 'ttyname' declared here char *ttyname(int); ^ slave.c:151:17: error: incompatible integer to pointer conversion initializing 'const char *' with an expression of type 'int' [-Werror,-Wint-conversion] const char *pts_name = ptsname(ptmx); ^ ~~~~~~~~~~~~~ slave.c:153:9: error: implicit declaration of function 'grantpt' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (grantpt(ptmx) == -1) { ^ slave.c:157:9: error: implicit declaration of function 'unlockpt' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (unlockpt(ptmx) == -1) { ^ slave.c:252:9: error: implicit declaration of function 'pipe2' is invalid in C99 [-Werror,-Wimplicit-function-declaration] if (pipe2(fork_pipe, O_CLOEXEC) < 0) { ^ slave.c:252:9: note: did you mean 'pipe'? /usr/include/unistd.h:358:6: note: 'pipe' declared here int pipe(int *); ^ render.c:1166:28: error: implicit declaration of function 'wcwidth' is invalid in C99 [-Werror,-Wimplicit-function-declaration] int width = max(1, wcwidth(cell->wc)); ^ render.c:1166:28: note: did you mean '__wcwidth'? /usr/include/_ctype.h:161:1: note: '__wcwidth' declared here __wcwidth(__ct_rune_t _c) ^ render.c:1932:9: error: implicit declaration of function 'gettimeofday' is invalid in C99 [-Werror,-Wimplicit-function-declaration] gettimeofday(&start_time, NULL); ^ render.c:2240:28: error: implicit declaration of function 'wcwidth' is invalid in C99 [-Werror,-Wimplicit-function-declaration] widths[i] = max(1, wcwidth(text[i])); ^ render.c:2243:32: error: implicit declaration of function 'wcswidth' is invalid in C99 [-Werror,-Wimplicit-function-declaration] const size_t total_cells = wcswidth(text, text_len); ^ render.c:2243:32: note: did you mean 'wcwidth'? render.c:1166:28: note: 'wcwidth' declared here int width = max(1, wcwidth(cell->wc)); ^ util.h:7:27: note: expanded from macro 'max' #define max(x, y) ((x) > (y) ? (x) : (y)) ^ input.c:1540:9: error: implicit declaration of function 'gettimeofday' is invalid in C99 [-Werror,-Wimplicit-function-declaration] gettimeofday(&now, NULL); ^ --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index e1b9eb51..4b73a093 100644 --- a/meson.build +++ b/meson.build @@ -17,7 +17,7 @@ if cc.has_function('memfd_create') endif add_project_arguments( - ['-D_POSIX_C_SOURCE=200809L', '-D_GNU_SOURCE=200809L'] + + ['-D_GNU_SOURCE=200809L'] + (is_debug_build ? ['-D_DEBUG'] : [cc.get_supported_arguments('-fno-asynchronous-unwind-tables')]) + From 796301f2281f3805567e75bfadb4f1f0ab7be1e3 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 15:48:56 +0000 Subject: [PATCH 57/89] meson: libpthread may lack C11 threads e.g., on FreeBSD ld: error: undefined symbol: mtx_init >>> referenced by terminal.c:579 (terminal.c:579) >>> terminal.c.o:(initialize_render_workers) in archive libpgolib.a ld: error: undefined symbol: thrd_create >>> referenced by terminal.c:595 (terminal.c:595) >>> terminal.c.o:(initialize_render_workers) in archive libpgolib.a >>> referenced by terminal.c:935 (terminal.c:935) >>> terminal.c.o:(reload_fonts) in archive libpgolib.a >>> did you mean: thr_create >>> defined in: /lib/libc.so.7 ld: error: undefined symbol: mtx_lock >>> referenced by terminal.c:1410 (terminal.c:1410) >>> terminal.c.o:(term_destroy) in archive libpgolib.a ld: error: undefined symbol: mtx_unlock >>> referenced by terminal.c:1427 (terminal.c:1427) >>> terminal.c.o:(term_destroy) in archive libpgolib.a ld: error: undefined symbol: thrd_join >>> referenced by terminal.c:1462 (terminal.c:1462) >>> terminal.c.o:(term_destroy) in archive libpgolib.a >>> referenced by terminal.c:947 (terminal.c:947) >>> terminal.c.o:(reload_fonts) in archive libpgolib.a ld: error: undefined symbol: mtx_destroy >>> referenced by terminal.c:1466 (terminal.c:1466) >>> terminal.c.o:(term_destroy) in archive libpgolib.a --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 4b73a093..9d68e9cc 100644 --- a/meson.build +++ b/meson.build @@ -60,7 +60,7 @@ if cc.has_argument('-fmacro-prefix-map=/foo=') endif math = cc.find_library('m') -threads = dependency('threads') +threads = [dependency('threads'), cc.find_library('stdthreads', required: false)] pixman = dependency('pixman-1') wayland_protocols = dependency('wayland-protocols') wayland_client = dependency('wayland-client') From 3ac107ce821fb7c1638ebc0e7de2ac35fbceb043 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 15:54:18 +0000 Subject: [PATCH 58/89] meson: require epoll-shim on BSDs epoll/timerfd are Linux-only but BSDs have a shim via kqueue. libwayland on FreeBSD uses epoll-shim by default, so the build may fail at linking instead of compilation stage. csi.c:13:10: fatal error: 'sys/timerfd.h' file not found #include ^~~~~~~~~~~~~~~ selection.c:10:10: fatal error: 'sys/epoll.h' file not found #include ^~~~~~~~~~~~~ terminal.c:15:10: fatal error: 'sys/epoll.h' file not found #include ^~~~~~~~~~~~~ ld: error: undefined symbol: timerfd_create >>> referenced by pgo.c:154 (pgo/pgo.c:154) >>> pgo.p/pgo_pgo.c.o:(main) >>> referenced by pgo.c:158 (pgo/pgo.c:158) >>> pgo.p/pgo_pgo.c.o:(main) >>> referenced by terminal.c:2022 (terminal.c:2022) >>> terminal.c.o:(cursor_blink_rearm_timer) in archive libpgolib.a >>> referenced 7 more times ld: error: undefined symbol: epoll_shim_close >>> referenced by pgo.c:160 (pgo/pgo.c:160) >>> pgo.p/pgo_pgo.c.o:(main) >>> referenced by pgo.c:258 (pgo/pgo.c:258) >>> pgo.p/pgo_pgo.c.o:(main) >>> referenced by pgo.c:277 (pgo/pgo.c:277) >>> pgo.p/pgo_pgo.c.o:(main) >>> referenced 14 more times ld: error: undefined symbol: epoll_shim_read >>> referenced by pgo.c:251 (pgo/pgo.c:251) >>> pgo.p/pgo_pgo.c.o:(main) >>> referenced by terminal.c:237 (terminal.c:237) >>> terminal.c.o:(fdm_ptmx) in archive libpgolib.a >>> referenced by terminal.c:363 (terminal.c:363) >>> terminal.c.o:(fdm_blink) in archive libpgolib.a >>> referenced 8 more times ld: error: undefined symbol: timerfd_settime >>> referenced by terminal.c:301 (terminal.c:301) >>> terminal.c.o:(fdm_ptmx) in archive libpgolib.a >>> referenced by terminal.c:309 (terminal.c:309) >>> terminal.c.o:(fdm_ptmx) in archive libpgolib.a >>> referenced by terminal.c:2041 (terminal.c:2041) >>> terminal.c.o:(cursor_blink_rearm_timer) in archive libpgolib.a >>> referenced 11 more times ld: error: undefined symbol: timerfd_gettime >>> referenced by selection.c:1165 (selection.c:1165) >>> selection.c.o:(selection_start_scroll_timer) in archive libpgolib.a --- meson.build | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 9d68e9cc..f5aec308 100644 --- a/meson.build +++ b/meson.build @@ -61,6 +61,7 @@ endif math = cc.find_library('m') threads = [dependency('threads'), cc.find_library('stdthreads', required: false)] +libepoll = dependency('epoll-shim', required: false) pixman = dependency('pixman-1') wayland_protocols = dependency('wayland-protocols') wayland_client = dependency('wayland-client') @@ -129,7 +130,7 @@ vtlib = static_library( 'vt.c', 'vt.h', wl_proto_src + wl_proto_headers, version, - dependencies: [pixman, fcft, tllist], + dependencies: [libepoll, pixman, fcft, tllist], link_with: misc, ) @@ -139,7 +140,7 @@ pgolib = static_library( 'selection.c', 'selection.h', 'terminal.c', 'terminal.h', wl_proto_src + wl_proto_headers, - dependencies: [pixman, fcft, tllist], + dependencies: [libepoll, pixman, fcft, tllist], link_with: vtlib, ) @@ -147,7 +148,7 @@ executable( 'pgo', 'pgo/pgo.c', wl_proto_src + wl_proto_headers, - dependencies: [math, threads, pixman, wayland_client, fcft, tllist], + dependencies: [math, threads, libepoll, pixman, wayland_client, fcft, tllist], link_with: pgolib, ) @@ -175,7 +176,7 @@ executable( 'user-notification.h', 'wayland.c', 'wayland.h', wl_proto_src + wl_proto_headers, version, - dependencies: [math, threads, pixman, wayland_client, wayland_cursor, xkb, fontconfig, + dependencies: [math, threads, libepoll, pixman, wayland_client, wayland_cursor, xkb, fontconfig, tllist, fcft], link_with: pgolib, install: true) From 0b07bf78f0556a0c4c7290ee8fc6b95a382cf2e9 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Thu, 21 Jan 2021 07:00:57 +0000 Subject: [PATCH 59/89] meson: depend on wayland-client when using wayland-protocols Base compiler on BSDs doesn't look where packages are installed to avoid tainting build environment. When system fcft is installed to the same prefix as wayland-client it passes CFLAGS that satisfy both but when using subprojects/fcft there's a build error. xdg-shell.c:33:10: fatal error: 'wayland-util.h' file not found xdg-decoration-unstable-v1.c:28:10: fatal error: 'wayland-util.h' file not found xdg-output-unstable-v1.c:28:10: fatal error: 'wayland-util.h' file not found presentation-time.c:28:10: fatal error: 'wayland-util.h' file not found primary-selection-unstable-v1.c:28:10: fatal error: 'wayland-util.h' file not found text-input-unstable-v3.c:33:10: fatal error: 'wayland-util.h' file not found In file included from ../../foot/grid.c:1: In file included from ../../foot/grid.h:5: In file included from ../../foot/terminal.h:19: ../../foot/wayland.h:8:10: fatal error: 'wayland-client.h' file not found In file included from ../../foot/selection.c:1: ../../foot/selection.h:4:10: fatal error: 'wayland-client.h' file not found --- meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index f5aec308..b57e4f3a 100644 --- a/meson.build +++ b/meson.build @@ -130,7 +130,7 @@ vtlib = static_library( 'vt.c', 'vt.h', wl_proto_src + wl_proto_headers, version, - dependencies: [libepoll, pixman, fcft, tllist], + dependencies: [libepoll, pixman, fcft, tllist, wayland_client], link_with: misc, ) @@ -140,7 +140,7 @@ pgolib = static_library( 'selection.c', 'selection.h', 'terminal.c', 'terminal.h', wl_proto_src + wl_proto_headers, - dependencies: [libepoll, pixman, fcft, tllist], + dependencies: [libepoll, pixman, fcft, tllist, wayland_client], link_with: vtlib, ) From 8b092256d1cec7f93ac7d0cfec073c31b455a42e Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Tue, 19 Jan 2021 14:22:41 +0000 Subject: [PATCH 60/89] meson: optionalize terminfo dependency ncurses on FreeBSD still uses termcap(5) while foot works fine with xterm-256color sans status line and visible bell. Having more than one ncurses version installed may break other applications. Document -Dterminfo=false uses --term=xterm-256color by default --- config.c | 4 ++++ doc/foot.ini.5.scd | 3 ++- foot.ini | 2 +- meson.build | 22 ++++++++++++++-------- meson_options.txt | 1 + 5 files changed, 22 insertions(+), 10 deletions(-) diff --git a/config.c b/config.c index 883cf27c..54c8e60f 100644 --- a/config.c +++ b/config.c @@ -1956,7 +1956,11 @@ config_load(struct config *conf, const char *conf_path, bool ret = false; *conf = (struct config) { +#ifdef HAVE_TERMINFO .term = xstrdup("foot"), +#else + .term = xstrdup("xterm-256color"), +#endif .shell = get_shell(), .title = xstrdup("foot"), .app_id = xstrdup("foot"), diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 9cabc1d2..94596fdf 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -111,7 +111,8 @@ in this order: by prepending a '-' to argv[0]. Default: _no_. *term* - Value to set the environment variable *TERM* to. Default: _foot_. + Value to set the environment variable *TERM* to. Default: _foot_ + or _xterm-256color_ if built with _-Dterminfo=false_ *title* Initial window title. Default: _foot_. diff --git a/foot.ini b/foot.ini index adaf6088..98e25b93 100644 --- a/foot.ini +++ b/foot.ini @@ -10,7 +10,7 @@ # initial-window-mode=windowed # pad=2x2 # shell=$SHELL (if set, otherwise user's default shell from /etc/passwd) -# term=foot +# term=foot (or xterm-256color if built with -Dterminfo=false) # login-shell=no # workers= # bold-text-in-bright=no diff --git a/meson.build b/meson.build index b57e4f3a..01010162 100644 --- a/meson.build +++ b/meson.build @@ -72,6 +72,11 @@ fontconfig = dependency('fontconfig') tllist = dependency('tllist', version: '>=1.0.4', fallback: 'tllist') fcft = dependency('fcft', version: ['>=2.3.0', '<3.0.0'], fallback: 'fcft') +tic = find_program('tic', native: true, required: get_option('terminfo')) +if tic.found() + add_project_arguments('-DHAVE_TERMINFO', language: 'c') +endif + wayland_protocols_datadir = wayland_protocols.get_pkgconfig_variable('pkgdatadir') wscanner = dependency('wayland-scanner', native: true) @@ -191,14 +196,15 @@ executable( version, install: true) -tic = find_program('tic', native: true) -custom_target( - 'terminfo', - output: 'f', - input: 'foot.info', - command: [tic, '-x', '-o', '@OUTDIR@', '-e', 'foot,foot-direct', '@INPUT@'], - install: true, - install_dir: join_paths(get_option('datadir'), 'terminfo')) +if tic.found() + custom_target( + 'terminfo', + output: 'f', + input: 'foot.info', + command: [tic, '-x', '-o', '@OUTDIR@', '-e', 'foot,foot-direct', '@INPUT@'], + install: true, + install_dir: join_paths(get_option('datadir'), 'terminfo')) +endif install_data( 'LICENSE', 'README.md', 'CHANGELOG.md', diff --git a/meson_options.txt b/meson_options.txt index fc3c1b0f..0a5b0a3d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1 +1,2 @@ option('ime', type: 'boolean', value: true, description: 'IME (Input Method Editor) support') +option('terminfo', type: 'boolean', value: true, description: 'Install terminfo') From d31cc9b5ef2e57ff29bb364da68f50319baa0a65 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Thu, 21 Jan 2021 06:52:22 +0000 Subject: [PATCH 61/89] ci: enable freebsd-x64 job - Add missing dependencies - Disable terminfo to match downstream --- .builds/{freebsd-x64.yml.disabled => freebsd-x64.yml} | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) rename .builds/{freebsd-x64.yml.disabled => freebsd-x64.yml} (71%) diff --git a/.builds/freebsd-x64.yml.disabled b/.builds/freebsd-x64.yml similarity index 71% rename from .builds/freebsd-x64.yml.disabled rename to .builds/freebsd-x64.yml index 0f35826e..8481c67f 100644 --- a/.builds/freebsd-x64.yml.disabled +++ b/.builds/freebsd-x64.yml @@ -1,5 +1,7 @@ image: freebsd/latest packages: + - evdev-proto + - libepoll-shim - meson - ninja - pkgconf @@ -11,10 +13,9 @@ packages: - harfbuzz - pixman - libxkbcommon - - ncurses - check - - ttf-hack - - font-noto-emoji + - hack-font + - noto-emoji sources: - https://codeberg.org/dnkl/foot @@ -33,11 +34,11 @@ tasks: ln -s ../../fcft foot/subprojects/fcft - debug: | mkdir -p bld/debug - meson --buildtype=debug -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug + meson --buildtype=debug -Dterminfo=false -Dfcft:text-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 -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release + meson --buildtype=minsize -Dterminfo=false -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release ninja -C bld/release -k0 meson test -C bld/release --print-errorlogs From ab040d0d6f5cd92a6e9d4093395ae6e151128915 Mon Sep 17 00:00:00 2001 From: Jan Beich Date: Thu, 21 Jan 2021 14:23:20 +0000 Subject: [PATCH 62/89] completions/zsh: unbreak with BSD find(1) find: -printf: unknown primary or operator --- completions/zsh/_foot | 2 +- completions/zsh/_footclient | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/completions/zsh/_foot b/completions/zsh/_foot index 24ddd671..c6f0f93b 100644 --- a/completions/zsh/_foot +++ b/completions/zsh/_foot @@ -31,6 +31,6 @@ case ${state} in ;; terms) - _values 'terminal definitions' $(find /usr/share/terminfo -type f -printf "%f\n") + _values 'terminal definitions' /usr/share/terminfo/**/*(.:t) ;; esac diff --git a/completions/zsh/_footclient b/completions/zsh/_footclient index 26aa2c5c..e355527f 100644 --- a/completions/zsh/_footclient +++ b/completions/zsh/_footclient @@ -20,6 +20,6 @@ _arguments \ case ${state} in terms) - _values 'terminal definitions' $(find /usr/share/terminfo -type f -printf "%f\n") + _values 'terminal definitions' /usr/share/terminfo/**/*(.:t) ;; esac From c70694bd8f40a6e0aee38b9d0be821ab18f4db33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 21 Jan 2021 12:15:40 +0100 Subject: [PATCH 63/89] =?UTF-8?q?client:=20explicitly=20use=20a=2064-bit?= =?UTF-8?q?=20type=20for=20=E2=80=98total=5Flen=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes an out-of-range comparison in 32-bit builds: client.c:289:19: error: result of comparison of constant 4294967296 with expression of type 'size_t' (aka 'unsigned int') is always false [-Werror,-Wtautological-constant-out-of-range-compare] if (total_len >= 1llu << (8 * sizeof(uint32_t)) || ~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client.c b/client.c index cb24ab12..c4a36f74 100644 --- a/client.c +++ b/client.c @@ -263,7 +263,7 @@ main(int argc, char *const *argv) }; /* Total packet length, not (yet) including argv[] */ - size_t total_len = ( + uint64_t total_len = ( sizeof(data) + cwd_len + term_len + From 1f745655ac985a78b97f5457c5f5b7251bb028a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 21 Jan 2021 12:39:51 +0100 Subject: [PATCH 64/89] =?UTF-8?q?shm:=20don=E2=80=99t=20check=20for=20=5F?= =?UTF-8?q?=5Fi386=5F=5F=20and=20=5F=5Fx86=5F64=5F=5F=20-=20there=20are=20?= =?UTF-8?q?other=20architectures=20out=20there?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead, check for __SIZEOF_POINTER__ == 8, since what we’re really interested in is whether we have enough virtual address space or not. --- shm.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/shm.c b/shm.c index 85d67f9c..533d9fbc 100644 --- a/shm.c +++ b/shm.c @@ -129,7 +129,7 @@ static const struct wl_buffer_listener buffer_listener = { .release = &buffer_release, }; -#if !defined(__i386__) +#if __SIZEOF_POINTER__ == 8 static size_t page_size(void) { @@ -290,12 +290,12 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie, goto err; } -#if defined(__i386__) - off_t initial_offset = 0; - off_t memfd_size = size; -#else +#if __SIZEOF_POINTER__ == 8 off_t initial_offset = scrollable && max_pool_size > 0 ? (max_pool_size / 4) & ~(page_size() - 1) : 0; off_t memfd_size = scrollable && max_pool_size > 0 ? max_pool_size : size; +#else + off_t initial_offset = 0; + off_t memfd_size = size; #endif LOG_DBG("memfd-size: %lu, initial offset: %lu", memfd_size, initial_offset); @@ -307,7 +307,7 @@ shm_get_buffer(struct wl_shm *shm, int width, int height, unsigned long cookie, if (!can_punch_hole_initialized) { can_punch_hole_initialized = true; -#if defined(__x86_64__) && defined(FALLOC_FL_PUNCH_HOLE) +#if __SIZEOF_POINTER__ == 8 && defined(FALLOC_FL_PUNCH_HOLE) can_punch_hole = fallocate( pool_fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 1) == 0; @@ -412,15 +412,15 @@ err: bool shm_can_scroll(const struct buffer *buf) { -#if defined(__i386__) +#if __SIZEOF_POINTER__ == 8 + return can_punch_hole && max_pool_size > 0 && buf->scrollable; +#else /* Not enough virtual address space in 32-bit */ return false; -#else - return can_punch_hole && max_pool_size > 0 && buf->scrollable; #endif } -#if defined(FALLOC_FL_PUNCH_HOLE) +#if __SIZEOF_POINTER__ == 8 && defined(FALLOC_FL_PUNCH_HOLE) static bool wrap_buffer(struct wl_shm *shm, struct buffer *buf, off_t new_offset) { @@ -660,7 +660,7 @@ shm_scroll(struct wl_shm *shm, struct buffer *buf, int rows, int top_margin, int top_keep_rows, int bottom_margin, int bottom_keep_rows) { -#if defined(FALLOC_FL_PUNCH_HOLE) +#if __SIZEOF_POINTER__ == 8 && defined(FALLOC_FL_PUNCH_HOLE) if (!shm_can_scroll(buf)) return false; From 207fdaf06c0ae2efc4f751a16e7c5b33054f76ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 23 Jan 2021 10:07:02 +0100 Subject: [PATCH 65/89] ci: freebsd-x64: compile with -Wno-missing-braces The freebsd/latest image is still FreeBSD-12, with clang-8. It generate false positive warnings: ../../foot/search.c:357:21: error: suggest braces around initialization of subobject [-Werror,-Wmissing-braces] mbstate_t ps = {0}; --- .builds/freebsd-x64.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.builds/freebsd-x64.yml b/.builds/freebsd-x64.yml index 8481c67f..db6b8432 100644 --- a/.builds/freebsd-x64.yml +++ b/.builds/freebsd-x64.yml @@ -27,6 +27,8 @@ sources: # condition: failure # to: +# TODO: drop -Dc_args=-Wno-missing-braces when freebsd/latest is >= 13 + tasks: - subprojects: | mkdir foot/subprojects @@ -34,11 +36,11 @@ tasks: ln -s ../../fcft foot/subprojects/fcft - debug: | mkdir -p bld/debug - meson --buildtype=debug -Dterminfo=false -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug + meson --buildtype=debug -Dc_args=-Wno-missing-braces -Dterminfo=false -Dfcft:text-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 -Dterminfo=false -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release + meson --buildtype=minsize -Dc_args=-Wno-missing-braces -Dterminfo=false -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release ninja -C bld/release -k0 meson test -C bld/release --print-errorlogs From 07bb18847db20665eb29d4b74472f9b9f3ebbcd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 23 Jan 2021 10:12:39 +0100 Subject: [PATCH 66/89] changelog: freebsd support --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff7e943a..78b08cf5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ * Completions for fish shell (https://codeberg.org/dnkl/foot/issues/11) +* FreeBSD support ### Changed @@ -63,6 +64,7 @@ * [birger](https://codeberg.org/birger) * [pc](https://codeberg.org/pc) * [FollieHiyuki](https://codeberg.org/FollieHiyuki) +* jbeich ## 1.6.2 From 30ef3fe4b676a6a29daf1095e498bbf5adaec744 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 23 Jan 2021 10:19:28 +0100 Subject: [PATCH 67/89] =?UTF-8?q?changelog:=20add=20bug=20ref=20to=20the?= =?UTF-8?q?=20=E2=80=98FreeBSD=20support=E2=80=99=20entry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b08cf5..4b7ade8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,7 +26,7 @@ * Completions for fish shell (https://codeberg.org/dnkl/foot/issues/11) -* FreeBSD support +* FreeBSD support (https://codeberg.org/dnkl/foot/issues/238). ### Changed From 3252131dd4330a5eacd4053f850c249e0b153308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 22 Jan 2021 17:03:43 +0100 Subject: [PATCH 68/89] terminal: fix encoding of mouse buttons 6 and 7 in mouse events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These two buttons were encoded using the *exact* same numbers as button 4 and 5 (scroll wheel up/down), making it impossible to distinguish them. The relevant section from XTerm’s control sequences documentation is: Some wheel mice can send additional button events, e.g., by tilting the scroll wheel left and right. Additional buttons are encoded like the wheel mice, o by adding 64 (for buttons 6 and 7), or o by adding 128 (for buttons 8 through 11). --- CHANGELOG.md | 1 + terminal.c | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b7ade8b..5a30366b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,7 @@ * Sixel “current geometry” query response not being bounded by the current window dimensions (fixes `lsix` output) * Crash on keyboard input when repeat rate was zero (i.e. no repeat). +* Wrong button encoding of mouse buttons 6 and 7 in mouse events. ### Security diff --git a/terminal.c b/terminal.c index e07ac50e..1208474d 100644 --- a/terminal.c +++ b/terminal.c @@ -2321,14 +2321,10 @@ encode_xbutton(int xbutton) case 1: case 2: case 3: return xbutton - 1; - case 4: case 5: + case 4: case 5: case 6: case 7: /* Like button 1 and 2, but with 64 added */ return xbutton - 4 + 64; - case 6: case 7: - /* Same as 4 and 5. Note: the offset should be something else? */ - return xbutton - 6 + 64; - case 8: case 9: case 10: case 11: /* Similar to 4 and 5, but adding 128 instead of 64 */ return xbutton - 8 + 128; From 96af3971de77c057379687ad88b2c9bc5fd8df9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Jan 2021 11:32:45 +0100 Subject: [PATCH 69/89] search: filter out non-printable characters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don’t allow non-printable characters in the search input string. --- search.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/search.c b/search.c index a7b6f112..61f231f4 100644 --- a/search.c +++ b/search.c @@ -353,14 +353,28 @@ search_find_next(struct terminal *term) void search_add_chars(struct terminal *term, const char *src, size_t count) { + const char *_src = src; mbstate_t ps = {0}; - size_t wchars = mbsnrtowcs(NULL, &src, count, 0, &ps); + size_t wchars = mbsnrtowcs(NULL, &_src, count, 0, &ps); if (wchars == -1) { LOG_ERRNO("failed to convert %.*s to wchars", (int)count, src); return; } + _src = src; + ps = (mbstate_t){0}; + wchar_t wcs[wchars + 1]; + mbsnrtowcs(wcs, &_src, count, wchars, &ps); + + /* Strip non-printable characters */ + for (size_t i = 0, j = 0, orig_wchars = wchars; i < orig_wchars; i++) { + if (iswprint(wcs[i])) + wcs[j++] = wcs[i]; + else + wchars--; + } + if (!search_ensure_size(term, term->search.len + wchars)) return; @@ -370,9 +384,7 @@ search_add_chars(struct terminal *term, const char *src, size_t count) &term->search.buf[term->search.cursor], (term->search.len - term->search.cursor) * sizeof(wchar_t)); - memset(&ps, 0, sizeof(ps)); - mbsnrtowcs(&term->search.buf[term->search.cursor], &src, count, - wchars, &ps); + memcpy(&term->search.buf[term->search.cursor], wcs, wchars * sizeof(wchar_t)); term->search.len += wchars; term->search.cursor += wchars; From b4448d2c03cf0c775966f6f4769c3736014319bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Jan 2021 11:33:59 +0100 Subject: [PATCH 70/89] changelog: search input box no longer accepts non-printables --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a30366b..c01dfb51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ * Trailing comments in `foot.ini` must now be preceded by a space or tab (https://codeberg.org/dnkl/foot/issues/270) +* The scrollback search box no longer accepts non-printable characters. ### Deprecated From 8df49c7f84d96e10a60da0f449758e74ac72a390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Jan 2021 12:02:20 +0100 Subject: [PATCH 71/89] search: fix matching against composed characters --- CHANGELOG.md | 1 + search.c | 40 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c01dfb51..0c567a44 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ current window dimensions (fixes `lsix` output) * Crash on keyboard input when repeat rate was zero (i.e. no repeat). * Wrong button encoding of mouse buttons 6 and 7 in mouse events. +* Scrollback search not matching composed characters. ### Security diff --git a/search.c b/search.c index 61f231f4..901bfef1 100644 --- a/search.c +++ b/search.c @@ -237,6 +237,37 @@ search_update_selection(struct terminal *term, } } +static ssize_t +matches_cell(const struct terminal *term, const struct cell *cell, size_t search_ofs) +{ + assert(search_ofs < term->search.len); + + wchar_t base = cell->wc; + const struct composed *composed = NULL; + + if (base >= CELL_COMB_CHARS_LO && + base < (CELL_COMB_CHARS_LO + term->composed_count)) + { + composed = &term->composed[base - CELL_COMB_CHARS_LO]; + base = composed->base; + } + + if (wcsncasecmp(&base, &term->search.buf[search_ofs], 1) != 0) + return -1; + + if (composed != NULL) { + if (search_ofs + 1 + composed->count > term->search.len) + return -1; + + for (size_t j = 0; j < composed->count; j++) { + if (composed->combining[j] != term->search.buf[search_ofs + 1 + j]) + return -1; + } + } + + return composed != NULL ? 1 + composed->count : 1; +} + static void search_find_next(struct terminal *term) { @@ -288,7 +319,7 @@ search_find_next(struct terminal *term) if (row == NULL) continue; - if (wcsncasecmp(&row->cells[start_col].wc, term->search.buf, 1) != 0) + if (matches_cell(term, &row->cells[start_col], 0) < 0) continue; /* @@ -302,7 +333,7 @@ search_find_next(struct terminal *term) int end_col = start_col; size_t match_len = 0; - for (size_t i = 0; i < term->search.len; i++, match_len++) { + for (size_t i = 0; i < term->search.len;) { if (end_col >= term->cols) { if (end_row + 1 > grid_row_absolute(term->grid, term->grid->offset + term->rows - 1)) { /* Don't continue past end of the world */ @@ -314,9 +345,12 @@ search_find_next(struct terminal *term) row = term->grid->rows[end_row]; } - if (wcsncasecmp(&row->cells[end_col].wc, &term->search.buf[i], 1) != 0) + ssize_t additional_chars = matches_cell(term, &row->cells[end_col], i); + if (additional_chars < 0) break; + i += additional_chars; + match_len += additional_chars; end_col++; } From 9d51f2cb1a8a6bb10d1b8d43934c33e3567b03fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 24 Jan 2021 20:31:22 +0100 Subject: [PATCH 72/89] =?UTF-8?q?search:=20don=E2=80=99t=20re-scan=20the?= =?UTF-8?q?=20scrollback=20unless=20the=20search=20string=20actually=20has?= =?UTF-8?q?=20changed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- search.c | 116 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 36 deletions(-) diff --git a/search.c b/search.c index 901bfef1..30332d2e 100644 --- a/search.c +++ b/search.c @@ -573,8 +573,11 @@ from_clipboard_done(void *user) static bool execute_binding(struct seat *seat, struct terminal *term, - enum bind_action_search action, uint32_t serial) + enum bind_action_search action, uint32_t serial, + bool *update_search_result, bool *redraw) { + *update_search_result = *redraw = false; + switch (action) { case BIND_ACTION_SEARCH_NONE: return false; @@ -610,7 +613,8 @@ execute_binding(struct seat *seat, struct terminal *term, term->search.match.row = new_row; } } - return false; + *update_search_result = *redraw = true; + return true; case BIND_ACTION_SEARCH_FIND_NEXT: if (term->search.match_len > 0) { @@ -626,43 +630,58 @@ execute_binding(struct seat *seat, struct terminal *term, term->search.match.col = new_col; term->search.match.row = new_row; term->search.direction = SEARCH_FORWARD; - } + } } - return false; + *update_search_result = *redraw = true; + return true; case BIND_ACTION_SEARCH_EDIT_LEFT: - if (term->search.cursor > 0) + if (term->search.cursor > 0) { term->search.cursor--; - return false; + *redraw = true; + } + return true; case BIND_ACTION_SEARCH_EDIT_LEFT_WORD: { size_t diff = distance_prev_word(term); term->search.cursor -= diff; assert(term->search.cursor >= 0); assert(term->search.cursor <= term->search.len); - return false; + if (diff > 0) + *redraw = true; + return true; } case BIND_ACTION_SEARCH_EDIT_RIGHT: - if (term->search.cursor < term->search.len) + if (term->search.cursor < term->search.len) { term->search.cursor++; - return false; + *redraw = true; + } + return true; case BIND_ACTION_SEARCH_EDIT_RIGHT_WORD: { size_t diff = distance_next_word(term); term->search.cursor += diff; assert(term->search.cursor >= 0); assert(term->search.cursor <= term->search.len); - return false; + if (diff > 0) + *redraw = true; + return true; } case BIND_ACTION_SEARCH_EDIT_HOME: - term->search.cursor = 0; - return false; + if (term->search.cursor != 0) { + term->search.cursor = 0; + *redraw = true; + } + return true; case BIND_ACTION_SEARCH_EDIT_END: - term->search.cursor = term->search.len; - return false; + if (term->search.cursor != term->search.len) { + term->search.cursor = term->search.len; + *redraw = true; + } + return true; case BIND_ACTION_SEARCH_DELETE_PREV: if (term->search.cursor > 0) { @@ -672,21 +691,25 @@ execute_binding(struct seat *seat, struct terminal *term, (term->search.len - term->search.cursor) * sizeof(wchar_t)); term->search.cursor--; term->search.buf[--term->search.len] = L'\0'; + *update_search_result = *redraw = true; } - return false; + return true; case BIND_ACTION_SEARCH_DELETE_PREV_WORD: { size_t diff = distance_prev_word(term); size_t old_cursor = term->search.cursor; size_t new_cursor = old_cursor - diff; - memmove(&term->search.buf[new_cursor], - &term->search.buf[old_cursor], - (term->search.len - old_cursor) * sizeof(wchar_t)); + if (diff > 0) { + memmove(&term->search.buf[new_cursor], + &term->search.buf[old_cursor], + (term->search.len - old_cursor) * sizeof(wchar_t)); - term->search.len -= diff; - term->search.cursor = new_cursor; - return false; + term->search.len -= diff; + term->search.cursor = new_cursor; + *update_search_result = *redraw = true; + } + return true; } case BIND_ACTION_SEARCH_DELETE_NEXT: @@ -696,42 +719,50 @@ execute_binding(struct seat *seat, struct terminal *term, &term->search.buf[term->search.cursor + 1], (term->search.len - term->search.cursor - 1) * sizeof(wchar_t)); term->search.buf[--term->search.len] = L'\0'; + *update_search_result = *redraw = true; } - return false; + return true; case BIND_ACTION_SEARCH_DELETE_NEXT_WORD: { size_t diff = distance_next_word(term); size_t cursor = term->search.cursor; - memmove(&term->search.buf[cursor], - &term->search.buf[cursor + diff], - (term->search.len - (cursor + diff)) * sizeof(wchar_t)); + if (diff > 0) { + memmove(&term->search.buf[cursor], + &term->search.buf[cursor + diff], + (term->search.len - (cursor + diff)) * sizeof(wchar_t)); - term->search.len -= diff; - return false; + term->search.len -= diff; + *update_search_result = *redraw = true; + } + return true; } case BIND_ACTION_SEARCH_EXTEND_WORD: search_match_to_end_of_word(term, false); - return false; + *update_search_result = *redraw = true; + return true; case BIND_ACTION_SEARCH_EXTEND_WORD_WS: search_match_to_end_of_word(term, true); - return false; + *update_search_result = *redraw = true; + return true; case BIND_ACTION_SEARCH_CLIPBOARD_PASTE: text_from_clipboard( seat, term, &from_clipboard_cb, &from_clipboard_done, term); - return false; + *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); - return false; + *update_search_result = *redraw = true; + return true; case BIND_ACTION_SEARCH_COUNT: assert(false); - return false; + return true; } assert(false); @@ -747,6 +778,9 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key, enum xkb_compose_status compose_status = xkb_compose_state_get_status( seat->kbd.xkb_compose_state); + bool update_search_result = false; + bool redraw = false; + /* Key bindings */ tll_foreach(seat->kbd.bindings.search, it) { if (it->item.bind.mods != mods) @@ -754,16 +788,22 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key, /* Match symbol */ if (it->item.bind.sym == sym) { - if (!execute_binding(seat, term, it->item.action, serial)) + if (execute_binding(seat, term, it->item.action, serial, + &update_search_result, &redraw)) + { goto update_search; + } return; } /* Match raw key code */ tll_foreach(it->item.bind.key_codes, code) { if (code->item == key) { - if (!execute_binding(seat, term, it->item.action, serial)) + if (execute_binding(seat, term, it->item.action, serial, + &update_search_result, &redraw)) + { goto update_search; + } return; } } @@ -783,6 +823,8 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key, seat->kbd.xkb_state, key, (char *)buf, sizeof(buf)); } + update_search_result = redraw = count > 0; + if (count == 0) return; @@ -790,6 +832,8 @@ search_input(struct seat *seat, struct terminal *term, uint32_t key, update_search: LOG_DBG("search: buffer: %ls", term->search.buf); - search_find_next(term); - render_refresh_search(term); + if (update_search_result) + search_find_next(term); + if (redraw) + render_refresh_search(term); } From 569795a529144957a08d73d1cbf09e583239c202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 25 Jan 2021 10:05:51 +0100 Subject: [PATCH 73/89] changelog: high cpu usage while holding down e.g. arrow keys in search mode --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c567a44..4bf9f586 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,8 @@ * Crash on keyboard input when repeat rate was zero (i.e. no repeat). * Wrong button encoding of mouse buttons 6 and 7 in mouse events. * Scrollback search not matching composed characters. +* High CPU usage when holding down e.g. arrow keys while in scrollback + search mode. ### Security From f9a43209f2481cf31fe39c08fdfa7ce27cbfcab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 25 Jan 2021 10:00:43 +0100 Subject: [PATCH 74/89] search: ad-hoc workaround for combining characters with positive x-offsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When rendering the search input box, do the same ad-hoc workaround for combining characters with a positive x-offset as we do when rendering normal grid cells. In this case, we don’t *know* when we’re dealing with combining characters. But we can detect zero-width characters. For these, check their glyph’s x-offset. If positive, adjust it like we do when rendering combining glyphs in the main grid, to ensure the glyph is positioned over the _previous_ character, not the next. --- CHANGELOG.md | 1 + render.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bf9f586..7e20731e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ * Scrollback search not matching composed characters. * High CPU usage when holding down e.g. arrow keys while in scrollback search mode. +* Rendering of composed characters in the scrollback search box. ### Security diff --git a/render.c b/render.c index 0828e21a..9b86cc72 100644 --- a/render.c +++ b/render.c @@ -2212,7 +2212,7 @@ render_search_box(struct terminal *term) /* Calculate the width of each character */ int widths[text_len + 1]; for (size_t i = 0; i < text_len; i++) - widths[i] = max(1, wcwidth(text[i])); + widths[i] = max(0, wcwidth(text[i])); widths[text_len] = 0; const size_t total_cells = wcswidth(text, text_len); @@ -2403,10 +2403,16 @@ render_search_box(struct terminal *term) x + glyph->x, y + font_baseline(term) - glyph->y, glyph->width, glyph->height); } else { + int combining_ofs = width == 0 + ? (glyph->x < 0 + ? width * term->cell_width + : (width - 1) * term->cell_width) + : 0; /* Not a zero-width character - no additional offset */ pixman_image_t *src = pixman_image_create_solid_fill(&fg); pixman_image_composite32( PIXMAN_OP_OVER, src, glyph->pix, buf->pix[0], 0, 0, 0, 0, - x + glyph->x, y + font_baseline(term) - glyph->y, + x + combining_ofs + glyph->x, + y + font_baseline(term) - glyph->y, glyph->width, glyph->height); pixman_image_unref(src); } From adbf036053db00ef474ae53eadae93f901a1459e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 25 Jan 2021 21:57:42 +0100 Subject: [PATCH 75/89] ime: fix rendering of pre-edit cursor when positioned after the pre-edit string We failed to convert the byte-indices to cell indices, resulting in a box cursor covering the entire pre-edit string. Note that in addition to fixing the translation from byte index to cell index, the rendered had to be updated to dirty one extra cell from the original grid. Without this, we left trailing cursors behind us when the user deleted text from the pre-edit string. --- CHANGELOG.md | 2 ++ ime.c | 19 +++++++++++-------- render.c | 13 ++++++++++++- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e20731e..c0ccf31b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,8 @@ * High CPU usage when holding down e.g. arrow keys while in scrollback search mode. * Rendering of composed characters in the scrollback search box. +* IME pre-edit cursor when positioned at the end of the pre-edit + string. ### Security diff --git a/ime.c b/ime.c index 9e2699f2..0f3ccf58 100644 --- a/ime.c +++ b/ime.c @@ -194,6 +194,8 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, cell_idx += width; } + const size_t byte_len = strlen(seat->ime.preedit.pending.text); + /* Pre-edit cursor - hidden */ if (seat->ime.preedit.pending.cursor_begin == -1 || seat->ime.preedit.pending.cursor_end == -1) @@ -206,6 +208,15 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, term->ime.preedit.cursor.end = -1; } + else if (seat->ime.preedit.pending.cursor_begin == byte_len && + seat->ime.preedit.pending.cursor_end == byte_len) + { + /* Cursor is *after* the entire pre-edit string */ + term->ime.preedit.cursor.hidden = false; + term->ime.preedit.cursor.start = cell_count; + term->ime.preedit.cursor.end = cell_count; + } + else { /* * Translate cursor position to cell indices @@ -220,16 +231,8 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3, * * When we find the matching *byte* index, we at the same * time know both the unicode *and* cell index. - * - * Note that this has only been tested with - * - * cursor_begin == cursor_end == 0 - * - * I haven't found an IME that requests anything else */ - const size_t byte_len = strlen(seat->ime.preedit.pending.text); - int cell_begin = -1, cell_end = -1; for (size_t byte_idx = 0, wc_idx = 0, cell_idx = 0; byte_idx < byte_len && diff --git a/render.c b/render.c index 9b86cc72..f2ccae65 100644 --- a/render.c +++ b/render.c @@ -1073,6 +1073,17 @@ render_ime_preedit(struct terminal *term, struct buffer *buf) int cells_needed = term->ime.preedit.count; + if (term->ime.preedit.cursor.start == cells_needed && + term->ime.preedit.cursor.end == cells_needed) + { + /* Cursor will be drawn *after* the pre-edit string, i.e. in + * the cell *after*. This means we need to copy, and dirty, + * one extra cell from the original grid, or we’ll leave + * trailing “cursors” after us if the user deletes text while + * pre-editing */ + cells_needed++; + } + int row_idx = cursor.row; int col_idx = cursor.col; int ime_ofs = 0; /* Offset into pre-edit string to start rendering at */ @@ -1152,7 +1163,7 @@ render_ime_preedit(struct terminal *term, struct buffer *buf) int end = term->ime.preedit.cursor.end - ime_ofs; if (!term->ime.preedit.cursor.hidden) { - const struct cell *start_cell = &term->ime.preedit.cells[start + ime_ofs]; + const struct cell *start_cell = &term->ime.preedit.cells[0]; pixman_color_t fg = color_hex_to_pixman(term->colors.fg); pixman_color_t bg = color_hex_to_pixman(term->colors.bg); From f32b9242419edea9c6c4812ee17fbbe5a9514e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 20 Jan 2021 19:20:03 +0100 Subject: [PATCH 76/89] selection: replace \r\n and \n with \r, and strip \e from pasted text Closes #305 Closes #306 --- selection.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/selection.c b/selection.c index 7f94b0a7..4d6bddf4 100644 --- a/selection.c +++ b/selection.c @@ -1303,12 +1303,36 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) if (count == 0) break; - /* Call cb while at same time replacing \r\n with \n */ + /* + * Call cb while at same time replace: + * - \r\n -> \r + * - \n -> \r + * - \e -> (i.e. strip ESC) + */ char *p = text; size_t left = count; again: - for (size_t i = 0; i < left - 1; i++) { - if (p[i] == '\r' && p[i + 1] == '\n') { + for (size_t i = 0; i < left; i++) { + switch (p[i]) { + default: + break; + + case '\n': + p[i] = '\r'; + break; + + case '\r': + if (i + 1 < left && p[i + 1] == '\n') { + ctx->decoder(ctx, p, i + 1); + + xassert(i + 2 <= left); + p += i + 2; + left -= i + 2; + goto again; + } + break; + + case '\x1b': ctx->decoder(ctx, p, i); assert(i + 1 <= left); From 579f3cf8e397bb427098f1bf2bdf3cd2cd19152e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 21 Jan 2021 15:18:33 +0100 Subject: [PATCH 77/89] =?UTF-8?q?selection:=20unbreak=20text/uri-list=20de?= =?UTF-8?q?coding:=20we=E2=80=99re=20not=20using=20\r,=20not=20\n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before passing the pasted text to the decoder, we now replace \r\n, and \n, with \r. The URI decoder was looking for a \n, which meant we failed to split up the list and instead pasted a single “multi-line” URI. --- selection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selection.c b/selection.c index 4d6bddf4..fa8ef7a5 100644 --- a/selection.c +++ b/selection.c @@ -1254,7 +1254,7 @@ fdm_receive_decoder_uri(struct clipboard_receive *ctx, char *data, size_t size) char *start = ctx->buf.data; char *end = NULL; - while ((end = memchr(start, '\n', ctx->buf.idx - (start - ctx->buf.data))) != NULL) { + while ((end = memchr(start, '\r', ctx->buf.idx - (start - ctx->buf.data))) != NULL) { decode_one_uri(ctx, start, end - start); start = end + 1; } From 9f8856ee7d4343ee4f868dfd5b2841f2c0db5ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 23 Jan 2021 11:22:22 +0100 Subject: [PATCH 78/89] selection: strip non-formatting C0, BS, HT and DEL from pasted text --- CHANGELOG.md | 2 ++ selection.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0ccf31b..198c8624 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,8 @@ * Trailing comments in `foot.ini` must now be preceded by a space or tab (https://codeberg.org/dnkl/foot/issues/270) * The scrollback search box no longer accepts non-printable characters. +* Non-formatting C0 control characters, `BS`, `HT` and `DEL` are now + stripped from pasted text. ### Deprecated diff --git a/selection.c b/selection.c index fa8ef7a5..110b24d3 100644 --- a/selection.c +++ b/selection.c @@ -1307,6 +1307,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) * Call cb while at same time replace: * - \r\n -> \r * - \n -> \r + * - C0 -> (stript non-formatting C0 characters) * - \e -> (i.e. strip ESC) */ char *p = text; @@ -1322,6 +1323,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) break; case '\r': + /* Convert \r\n -> \r */ if (i + 1 < left && p[i + 1] == '\n') { ctx->decoder(ctx, p, i + 1); @@ -1332,6 +1334,21 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) } break; + /* C0 non-formatting control characters (\b \t \n \r excluded) */ + case '\x01': case '\x02': case '\x03': case '\x04': case '\x05': + case '\x06': case '\x07': case '\x0b': case '\x0c': case '\x0e': + case '\x0f': case '\x10': 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': + /* FALLTHROUGH */ + + /* Additional control characters stripped by default (but + * configurable) in XTerm: BS, HT, DEL */ + case '\b': case '\t': case '\x1f': + /* FALLTHROUGH */ + + /* ESC */ case '\x1b': ctx->decoder(ctx, p, i); From 94d587fa6a3f09b8fa2c4259b27ab04e86388077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 23 Jan 2021 11:28:41 +0100 Subject: [PATCH 79/89] selection: codespell: stript -> strip --- selection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selection.c b/selection.c index 110b24d3..92da56c5 100644 --- a/selection.c +++ b/selection.c @@ -1307,7 +1307,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) * Call cb while at same time replace: * - \r\n -> \r * - \n -> \r - * - C0 -> (stript non-formatting C0 characters) + * - C0 -> (strip non-formatting C0 characters) * - \e -> (i.e. strip ESC) */ char *p = text; From c8fdd3a2149b1212506037d1cb86e15fb3411865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 23 Jan 2021 11:29:27 +0100 Subject: [PATCH 80/89] selection: DEL is 0x7f, not 0x1f --- selection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selection.c b/selection.c index 92da56c5..ca175eeb 100644 --- a/selection.c +++ b/selection.c @@ -1345,7 +1345,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) /* Additional control characters stripped by default (but * configurable) in XTerm: BS, HT, DEL */ - case '\b': case '\t': case '\x1f': + case '\b': case '\t': case '\x7f': /* FALLTHROUGH */ /* ESC */ From 7611a6c7fcc8f0f2d70fed5ba435d669376b723e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 23 Jan 2021 11:30:32 +0100 Subject: [PATCH 81/89] =?UTF-8?q?selection:=20remove=20duplicate=20?= =?UTF-8?q?=E2=80=98ESC=E2=80=99=20in=20switch=20case?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- selection.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/selection.c b/selection.c index ca175eeb..b1129152 100644 --- a/selection.c +++ b/selection.c @@ -1346,10 +1346,6 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) /* Additional control characters stripped by default (but * configurable) in XTerm: BS, HT, DEL */ case '\b': case '\t': case '\x7f': - /* FALLTHROUGH */ - - /* ESC */ - case '\x1b': ctx->decoder(ctx, p, i); assert(i + 1 <= left); From 709d6deeae82049ef48af0c8a2c5076557517a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 26 Jan 2021 20:05:14 +0100 Subject: [PATCH 82/89] search: fix matching of multi-column characters We were matching the sub-sequence SPACERs as if they were regular characters, which obviously failed. --- CHANGELOG.md | 1 + search.c | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 198c8624..c702ed9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,7 @@ * Rendering of composed characters in the scrollback search box. * IME pre-edit cursor when positioned at the end of the pre-edit string. +* Scrollback search not matching multi-column characters. ### Security diff --git a/search.c b/search.c index 30332d2e..0b498c17 100644 --- a/search.c +++ b/search.c @@ -345,6 +345,11 @@ search_find_next(struct terminal *term) row = term->grid->rows[end_row]; } + if (row->cells[end_col].wc == CELL_MULT_COL_SPACER) { + end_col++; + continue; + } + ssize_t additional_chars = matches_cell(term, &row->cells[end_col], i); if (additional_chars < 0) break; @@ -440,9 +445,11 @@ search_match_to_end_of_word(struct terminal *term, bool spaces_only) /* Calculate end coord - note: assumed to be valid */ for (size_t i = 0; i < len; i++) { - if (++end_col >= term->cols) { - end_row = (end_row + 1) & (term->grid->num_rows - 1); - end_col = 0; + for (size_t j = 0; j < wcwidth(term->search.buf[i]); j++) { + if (++end_col >= term->cols) { + end_row = (end_row + 1) & (term->grid->num_rows - 1); + end_col = 0; + } } } @@ -462,6 +469,9 @@ search_match_to_end_of_word(struct terminal *term, bool spaces_only) bool done = false; for (; end_col < term->cols; end_col++) { wchar_t wc = row->cells[end_col].wc; + if (wc == CELL_MULT_COL_SPACER) + continue; + if (wc == 0 || (!first && !isword(wc, spaces_only, term->conf->word_delimiters))) { done = true; break; From 5db389da21ce2d4132f4416545da4f8a50217109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 26 Jan 2021 20:26:34 +0100 Subject: [PATCH 83/89] search: match composed characters when extending the search string to the next word boundary --- search.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/search.c b/search.c index 0b498c17..b173f694 100644 --- a/search.c +++ b/search.c @@ -472,6 +472,14 @@ search_match_to_end_of_word(struct terminal *term, bool spaces_only) if (wc == CELL_MULT_COL_SPACER) continue; + const struct composed *composed = NULL; + if (wc >= CELL_COMB_CHARS_LO && + wc < (CELL_COMB_CHARS_LO + term->composed_count)) + { + composed = &term->composed[wc - CELL_COMB_CHARS_LO]; + wc = composed->base; + } + if (wc == 0 || (!first && !isword(wc, spaces_only, term->conf->word_delimiters))) { done = true; break; @@ -479,6 +487,11 @@ search_match_to_end_of_word(struct terminal *term, bool spaces_only) first = false; tll_push_back(new_chars, wc); + + if (composed != NULL) { + for (size_t i = 0; i < composed->count; i++) + tll_push_back(new_chars, composed->combining[i]); + } } if (done) From 567071e48a8e2f677198bd9fc0e6099f54730a56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 26 Jan 2021 20:48:56 +0100 Subject: [PATCH 84/89] selection: no xassert() in the releases/1.6 branch --- selection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selection.c b/selection.c index b1129152..4a63b538 100644 --- a/selection.c +++ b/selection.c @@ -1327,7 +1327,7 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) if (i + 1 < left && p[i + 1] == '\n') { ctx->decoder(ctx, p, i + 1); - xassert(i + 2 <= left); + assert(i + 2 <= left); p += i + 2; left -= i + 2; goto again; From 7049c95b62312ac57cbc67a6030ddb77a75f66a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 27 Jan 2021 10:44:28 +0100 Subject: [PATCH 85/89] =?UTF-8?q?selection:=20don=E2=80=99t=20replace=20\r?= =?UTF-8?q?\n=20and=20\n=20with=20\r=20in=20bracketed=20paste=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- selection.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/selection.c b/selection.c index 4a63b538..b83331a6 100644 --- a/selection.c +++ b/selection.c @@ -1143,6 +1143,7 @@ struct clipboard_receive { int read_fd; int timeout_fd; struct itimerspec timeout; + bool bracketed; void (*decoder)(struct clipboard_receive *ctx, char *data, size_t size); void (*finish)(struct clipboard_receive *ctx); @@ -1254,7 +1255,15 @@ fdm_receive_decoder_uri(struct clipboard_receive *ctx, char *data, size_t size) char *start = ctx->buf.data; char *end = NULL; - while ((end = memchr(start, '\r', ctx->buf.idx - (start - ctx->buf.data))) != NULL) { + while (true) { + for (end = start; end < &ctx->buf.data[ctx->buf.idx]; end++) { + if (*end == '\r' || *end == '\n') + break; + } + + if (end >= &ctx->buf.data[ctx->buf.idx]) + break; + decode_one_uri(ctx, start, end - start); start = end + 1; } @@ -1319,12 +1328,13 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data) break; case '\n': - p[i] = '\r'; + if (!ctx->bracketed) + p[i] = '\r'; break; case '\r': /* Convert \r\n -> \r */ - if (i + 1 < left && p[i + 1] == '\n') { + if (!ctx->bracketed && i + 1 < left && p[i + 1] == '\n') { ctx->decoder(ctx, p, i + 1); assert(i + 2 <= left); @@ -1399,6 +1409,7 @@ begin_receive_clipboard(struct terminal *term, int read_fd, .read_fd = read_fd, .timeout_fd = timeout_fd, .timeout = timeout, + .bracketed = term->bracketed_paste, .decoder = (mime_type == DATA_OFFER_MIME_URI_LIST ? &fdm_receive_decoder_uri : &fdm_receive_decoder_plain), From 9411054b50c91c9af465dc9b0fab7fb71b2831f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 28 Jan 2021 07:34:44 +0100 Subject: [PATCH 86/89] =?UTF-8?q?ci:=20freebsd:=20don=E2=80=99t=20need=20-?= =?UTF-8?q?Wno-missing-braces=20anymore?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit builds.sr.ht has been updated to FreeBSD-12.2 --- .builds/freebsd-x64.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.builds/freebsd-x64.yml b/.builds/freebsd-x64.yml index db6b8432..8481c67f 100644 --- a/.builds/freebsd-x64.yml +++ b/.builds/freebsd-x64.yml @@ -27,8 +27,6 @@ sources: # condition: failure # to: -# TODO: drop -Dc_args=-Wno-missing-braces when freebsd/latest is >= 13 - tasks: - subprojects: | mkdir foot/subprojects @@ -36,11 +34,11 @@ tasks: ln -s ../../fcft foot/subprojects/fcft - debug: | mkdir -p bld/debug - meson --buildtype=debug -Dc_args=-Wno-missing-braces -Dterminfo=false -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug + meson --buildtype=debug -Dterminfo=false -Dfcft:text-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 -Dc_args=-Wno-missing-braces -Dterminfo=false -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release + meson --buildtype=minsize -Dterminfo=false -Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true foot bld/release ninja -C bld/release -k0 meson test -C bld/release --print-errorlogs From 8f0a3ee7a9cbb1b0fc8011875a0895bfd331e0a8 Mon Sep 17 00:00:00 2001 From: Tadeo Kondrak Date: Sun, 20 Dec 2020 15:01:21 -0700 Subject: [PATCH 87/89] Send text_input_rectangle requests for text-input --- CHANGELOG.md | 3 ++ ime.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ime.h | 3 ++ render.c | 31 ++++++++++++++++++++- terminal.c | 16 +++++++++++ terminal.h | 2 ++ wayland.h | 12 ++++++++ 7 files changed, 143 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c702ed9b..d642a6c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ * Completions for fish shell (https://codeberg.org/dnkl/foot/issues/11) * FreeBSD support (https://codeberg.org/dnkl/foot/issues/238). +* IME popup location support: foot now sends the location of the cursor + so any popup can be displayed near the text that is being typed. ### Changed @@ -76,6 +78,7 @@ * [pc](https://codeberg.org/pc) * [FollieHiyuki](https://codeberg.org/FollieHiyuki) * jbeich +* [tdeo](https://codeberg.org/tdeo) ## 1.6.2 diff --git a/ime.c b/ime.c index 0f3ccf58..d420fc9f 100644 --- a/ime.c +++ b/ime.c @@ -327,6 +327,36 @@ ime_reset(struct seat *seat) ime_reset_commit(seat); } +void +ime_send_cursor_rect(struct seat *seat, struct terminal *term) +{ + if (unlikely(seat->wayl->text_input_manager == NULL)) + return; + + if (!term->ime.enabled) + return; + + if (seat->ime.cursor_rect.pending.x == seat->ime.cursor_rect.sent.x && + seat->ime.cursor_rect.pending.y == seat->ime.cursor_rect.sent.y && + seat->ime.cursor_rect.pending.width == seat->ime.cursor_rect.sent.width && + seat->ime.cursor_rect.pending.height == seat->ime.cursor_rect.sent.height) + { + return; + } + + zwp_text_input_v3_set_cursor_rectangle( + seat->wl_text_input, + seat->ime.cursor_rect.pending.x / term->scale, + seat->ime.cursor_rect.pending.y / term->scale, + seat->ime.cursor_rect.pending.width / term->scale, + seat->ime.cursor_rect.pending.height / term->scale); + + zwp_text_input_v3_commit(seat->wl_text_input); + seat->ime.serial++; + + seat->ime.cursor_rect.sent = seat->ime.cursor_rect.pending; +} + void ime_enable(struct seat *seat) { @@ -347,6 +377,15 @@ ime_enable(struct seat *seat) ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL); + zwp_text_input_v3_set_cursor_rectangle( + seat->wl_text_input, + seat->ime.cursor_rect.pending.x / term->scale, + seat->ime.cursor_rect.pending.y / term->scale, + seat->ime.cursor_rect.pending.width / term->scale, + seat->ime.cursor_rect.pending.height / term->scale); + + seat->ime.cursor_rect.sent = seat->ime.cursor_rect.pending; + zwp_text_input_v3_commit(seat->wl_text_input); seat->ime.serial++; } @@ -364,6 +403,42 @@ ime_disable(struct seat *seat) seat->ime.serial++; } +void +ime_update_cursor_rect(struct seat *seat, struct terminal *term) +{ + /* Set in render_ime_preedit() */ + if (term->ime.preedit.cells != NULL) + goto update; + + /* Set in render_search_box() */ + if (term->is_searching) + goto update; + + int x, y, width, height; + int col = term->grid->cursor.point.col; + int row = term->grid->cursor.point.row; + row += term->grid->offset; + row -= term->grid->view; + row &= term->grid->num_rows - 1; + x = term->margins.left + col * term->cell_width; + y = term->margins.top + row * term->cell_height; + + if (term->cursor_style == CURSOR_BAR) + width = 1; + else + width = term->cell_width; + + height = term->cell_height; + + seat->ime.cursor_rect.pending.x = x; + seat->ime.cursor_rect.pending.y = y; + seat->ime.cursor_rect.pending.width = width; + seat->ime.cursor_rect.pending.height = height; + +update: + ime_send_cursor_rect(seat, term); +} + const struct zwp_text_input_v3_listener text_input_listener = { .enter = &enter, .leave = &leave, @@ -377,9 +452,11 @@ const struct zwp_text_input_v3_listener text_input_listener = { void ime_enable(struct seat *seat) {} void ime_disable(struct seat *seat) {} +void ime_update_cursor_rect(struct seat *seat, struct terminal *term) {} void ime_reset_preedit(struct seat *seat) {} void ime_reset_commit(struct seat *seat) {} void ime_reset(struct seat *seat) {} +void ime_send_cursor_rect(struct seat *seat, struct terminal *term) {} #endif diff --git a/ime.h b/ime.h index 7036bbde..ed2fe507 100644 --- a/ime.h +++ b/ime.h @@ -9,10 +9,13 @@ extern const struct zwp_text_input_v3_listener text_input_listener; #endif /* FOOT_IME_ENABLED */ struct seat; +struct terminal; void ime_enable(struct seat *seat); void ime_disable(struct seat *seat); +void ime_update_cursor_rect(struct seat *seat, struct terminal *term); void ime_reset_preedit(struct seat *seat); void ime_reset_commit(struct seat *seat); void ime_reset(struct seat *seat); +void ime_send_cursor_rect(struct seat *seat, struct terminal *term); diff --git a/render.c b/render.c index f2ccae65..311f2b2b 100644 --- a/render.c +++ b/render.c @@ -31,6 +31,7 @@ #include "shm.h" #include "util.h" #include "xmalloc.h" +#include "ime.h" #define TIME_SCROLL_DAMAGE 0 @@ -1181,6 +1182,7 @@ render_ime_preedit(struct terminal *term, struct buffer *buf) struct fcft_font *font = attrs_to_font(term, &start_cell->attrs); draw_bar(term, buf->pix[0], font, &cursor_color, x, y); } + term_ime_set_cursor_rect(term, x, y, 1, term->cell_height); } else if (end > start) { @@ -1189,6 +1191,9 @@ render_ime_preedit(struct terminal *term, struct buffer *buf) int cols = end - start; draw_unfocused_block(term, buf->pix[0], &cursor_color, x, y, cols); } + + term_ime_set_cursor_rect( + term, x, y, (end - start) * term->cell_width, term->cell_height); } } @@ -2248,6 +2253,9 @@ render_search_box(struct terminal *term) unsigned long cookie = shm_cookie_search(term); struct buffer *buf = shm_get_buffer(term->wl->shm, width, height, cookie, false, 1); +#define WINDOW_X(x) (margin + x) +#define WINDOW_Y(y) (term->height - margin - height + y) + /* Background - yellow on empty/match, red on mismatch */ pixman_color_t color = color_hex_to_pixman( term->search.match_len == text_len @@ -2334,6 +2342,7 @@ render_search_box(struct terminal *term) width = widths[i], next_cell_idx += width) { + /* Convert subsurface coordinates to window coordinates*/ /* Render cursor */ if (i == term->search.cursor) { #if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED @@ -2362,6 +2371,9 @@ render_search_box(struct terminal *term) /* Bar-styled cursor, if in the visible area */ if (start >= 0 && start <= visible_cells) draw_bar(term, buf->pix[0], font, &fg, x + start * term->cell_width, y); + term_ime_set_cursor_rect(term, + WINDOW_X(x + start * term->cell_width), WINDOW_Y(y), + 1, term->cell_height); } else { /* Underline everything before and after the cursor */ int count1 = min(start, cells_left); @@ -2377,6 +2389,9 @@ render_search_box(struct terminal *term) draw_unfocused_block( term, buf->pix[0], &fg, x + start * term->cell_width, y, end - start); } + term_ime_set_cursor_rect(term, + WINDOW_X(x + start * term->cell_width), WINDOW_Y(y), + term->cell_width * (end - start), term->cell_height); } } else if (!have_preedit) #endif @@ -2385,6 +2400,8 @@ render_search_box(struct terminal *term) assert(cell_idx >= glyph_offset); assert(cell_idx <= glyph_offset + visible_cells); draw_bar(term, buf->pix[0], font, &fg, x, y); + term_ime_set_cursor_rect( + term, WINDOW_X(x), WINDOW_Y(y), 1, term->cell_height); } } @@ -2437,8 +2454,11 @@ render_search_box(struct terminal *term) /* Already rendered */; else #endif - if (term->search.cursor >= term->search.len) + if (term->search.cursor >= term->search.len) { draw_bar(term, buf->pix[0], font, &fg, x, y); + term_ime_set_cursor_rect( + term, WINDOW_X(x), WINDOW_Y(y), 1, term->cell_height); + } quirk_weston_subsurface_desync_on(term->window->search_sub_surface); @@ -2465,6 +2485,8 @@ render_search_box(struct terminal *term) #if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED free(text); #endif +#undef WINDOW_X +#undef WINDOW_Y } static void @@ -2515,6 +2537,10 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da if (search && term->is_searching) render_search_box(term); + tll_foreach(term->wl->seats, it) + if (it->item.kbd_focus == term) + ime_update_cursor_rect(&it->item, term); + if (grid && (!term->delayed_render_timer.is_armed || csd || search)) grid_render(term); } @@ -2874,6 +2900,9 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data) render_update_title(term); if (search) render_search_box(term); + tll_foreach(term->wl->seats, it) + if (it->item.kbd_focus == term) + ime_update_cursor_rect(&it->item, term); if (grid) grid_render(term); } else { diff --git a/terminal.c b/terminal.c index 1208474d..c65fe974 100644 --- a/terminal.c +++ b/terminal.c @@ -2895,3 +2895,19 @@ term_ime_reset(struct terminal *term) } #endif } + +void +term_ime_set_cursor_rect(struct terminal *term, int x, int y, int width, + int height) +{ +#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED + tll_foreach(term->wl->seats, it) { + if (it->item.kbd_focus == term) { + it->item.ime.cursor_rect.pending.x = x; + it->item.ime.cursor_rect.pending.y = y; + it->item.ime.cursor_rect.pending.width = width; + it->item.ime.cursor_rect.pending.height = height; + } + } +#endif +} diff --git a/terminal.h b/terminal.h index e13243a2..b6b150ae 100644 --- a/terminal.h +++ b/terminal.h @@ -614,3 +614,5 @@ bool term_ime_is_enabled(const struct terminal *term); void term_ime_enable(struct terminal *term); void term_ime_disable(struct terminal *term); void term_ime_reset(struct terminal *term); +void term_ime_set_cursor_rect( + struct terminal *term, int x, int y, int width, int height); diff --git a/wayland.h b/wayland.h index 93244353..bdfe4e23 100644 --- a/wayland.h +++ b/wayland.h @@ -138,6 +138,13 @@ struct button_tracker { bool send_to_client; /* Only valid when surface is the main grid surface */ }; +struct rect { + int x; + int y; + int width; + int height; +}; + struct seat { struct wayland *wayl; struct wl_seat *wl_seat; @@ -235,6 +242,11 @@ struct seat { /* Input Method Editor */ struct zwp_text_input_v3 *wl_text_input; struct { + struct { + struct rect pending; + struct rect sent; + } cursor_rect; + struct { struct { char *text; From efd1e05b4810fa57b92f88852a063711cdbb2670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 29 Jan 2021 19:52:27 +0100 Subject: [PATCH 88/89] changelog: prepare for 1.6.3 --- CHANGELOG.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d642a6c0..0ab193c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ ## 1.6.3 + ### Added * Completions for fish shell @@ -40,8 +41,6 @@ stripped from pasted text. -### Deprecated -### Removed ### Fixed * Exit when the client application terminates, not when the TTY file @@ -71,7 +70,6 @@ * Scrollback search not matching multi-column characters. -### Security ### Contributors * [birger](https://codeberg.org/birger) From 4c168b84cf2d0a8259c3297902597a0ecf433b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 29 Jan 2021 19:53:10 +0100 Subject: [PATCH 89/89] meson/pkgbuild: bump version to 1.6.3 --- PKGBUILD | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 19a6a0c3..86c3377e 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,5 +1,5 @@ pkgname=('foot-git' 'foot-terminfo-git') -pkgver=1.6.2 +pkgver=1.6.3 pkgrel=1 arch=('x86_64' 'aarch64') url=https://codeberg.org/dnkl/foot diff --git a/meson.build b/meson.build index 01010162..659fed65 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('foot', 'c', - version: '1.6.2', + version: '1.6.3', license: 'MIT', meson_version: '>=0.53.0', default_options: [