From 341193a627287e3b0006fde84e706306db919b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 13:44:18 +0200 Subject: [PATCH 01/75] =?UTF-8?q?changelog:=20add=20new=20=E2=80=98unrelea?= =?UTF-8?q?sed=E2=80=99=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ed83e6e..51f06141 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +* [Unreleased](#unreleased) * [1.9.0](#1-9-0) * [1.8.2](#1-8-2) * [1.8.1](#1-8-1) @@ -29,6 +30,16 @@ * [1.2.0](#1-2-0) +## Unreleased +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +### Contributors + + ## 1.9.0 ### Added From fd78fa98b4765771b65140bc6edfbd472b4f666b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 20:25:26 +0200 Subject: [PATCH 02/75] =?UTF-8?q?doc:=20how=20to=20pass=20TERMINFO=20throu?= =?UTF-8?q?gh=20=E2=80=98doas=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See #692 --- doc/foot.1.scd | 17 ++++++++++++----- doc/footclient.1.scd | 17 ++++++++++++----- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/doc/foot.1.scd b/doc/foot.1.scd index cbebd087..26fee4a1 100644 --- a/doc/foot.1.scd +++ b/doc/foot.1.scd @@ -409,15 +409,22 @@ The following environment variables are set in the child process: definitions in ncurses. Note that applications will search the default location(s) if *TERM* does not exist in *TERMINFO*. - Note that applications like sudo and ssh will remove this - environment variable. + Note that applications like *sudo*, *doas* and *ssh* may remove + this environment variable. - For sudo, you can create a file under _/etc/sudoers.d_ (name does - not matter) with the following content: + For *sudo*, you can create a file under _/etc/sudoers.d_ (name + does not matter) with the following content: *Defaults env_keep += "TERMINFO"* - For ssh, you can edit _/etc/ssh/sshd_config_ (on the *server*), + For *doas*, edit _/etc/doas.conf_ and add: + + *permit setenv { TERMINFO } :wheel* + + Or, if you are not in the *wheel* group, replace *:wheel* with + your username. + + For *ssh*, you can edit _/etc/ssh/sshd_config_ (on the *server*), and add: *SetEnv TERMINFO=*__ diff --git a/doc/footclient.1.scd b/doc/footclient.1.scd index 155683da..6d5839f0 100644 --- a/doc/footclient.1.scd +++ b/doc/footclient.1.scd @@ -153,15 +153,22 @@ The following environment variables are set in the child process: definitions in ncurses. Note that applications will search the default location(s) if *TERM* does not exist in *TERMINFO*. - Note that applications like sudo and ssh will remove this - environment variable. + Note that applications like *sudo*, *doas* and *ssh* may remove + this environment variable. - For sudo, you can create a file under _/etc/sudoers.d_ (name does - not matter) with the following content: + For *sudo*, you can create a file under _/etc/sudoers.d_ (name + does not matter) with the following content: *Defaults env_keep += "TERMINFO"* - For ssh, you can edit _/etc/ssh/sshd_config_ (on the *server*), + For *doas*, edit _/etc/doas.conf_ and add: + + *permit setenv { TERMINFO } :wheel* + + Or, if you are not in the *wheel* group, replace *:wheel* with + your username. + + For *ssh*, you can edit _/etc/ssh/sshd_config_ (on the *server*), and add: *SetEnv TERMINFO=*__ From 3990cd43929cc4d036d8cf3f365d0e13349ef4bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 27 Aug 2021 20:45:19 +0200 Subject: [PATCH 03/75] =?UTF-8?q?ci:=20codespell:=20ignore=20=E2=80=98doas?= =?UTF-8?q?=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .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 6823c834..df08c6f9 100644 --- a/.builds/alpine-x64.yml +++ b/.builds/alpine-x64.yml @@ -45,4 +45,4 @@ tasks: - codespell: | pip install codespell cd foot - ~/.local/bin/codespell README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd + ~/.local/bin/codespell -Ldoas README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 05992cf8..b7fbb972 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -86,4 +86,4 @@ codespell: - apk add python3 - apk add py3-pip - pip install codespell - - codespell README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd + - codespell -Ldoas README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd From 6bd151438fc5b3b4b56de084f03afcdde19203fe Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Mon, 30 Aug 2021 07:40:03 +0100 Subject: [PATCH 04/75] csi: various, minor code/formatting improvements --- csi.c | 45 +++++++++++++-------------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/csi.c b/csi.c index dc5a018a..4a9658f3 100644 --- a/csi.c +++ b/csi.c @@ -795,8 +795,8 @@ csi_dispatch(struct terminal *term, uint8_t final) * * Note: tertiary DA responds with "FOOT". */ - const char *reply = "\033[?62;4;22c"; - term_to_slave(term, reply, strlen(reply)); + static const char reply[] = "\033[?62;4;22c"; + term_to_slave(term, reply, sizeof(reply) - 1); break; } @@ -1062,17 +1062,15 @@ csi_dispatch(struct terminal *term, uint8_t final) } case 'S': { - int amount = min( - vt_param_get(term, 0, 1), - term->scroll_region.end - term->scroll_region.start); + const struct scroll_region *r = &term->scroll_region; + int amount = min(vt_param_get(term, 0, 1), r->end - r->start); term_scroll(term, amount); break; } case 'T': { - int amount = min( - vt_param_get(term, 0, 1), - term->scroll_region.end - term->scroll_region.start); + const struct scroll_region *r = &term->scroll_region; + int amount = min(vt_param_get(term, 0, 1), r->end - r->start); term_scroll_reverse(term, amount); break; } @@ -1219,20 +1217,12 @@ csi_dispatch(struct terminal *term, uint8_t final) break; case 13: { /* report window position */ - - int x = -1; - int y = -1; - /* We don't know our position - always report (0,0) */ + static const char reply[] = "\033[3;0;0t"; switch (vt_param_get(term, 1, 0)) { - case 0: - /* window position */ - x = y = 0; - break; - - case 2: - /* text area position */ - x = y = 0; + case 0: /* window position */ + case 2: /* text area position */ + term_to_slave(term, reply, sizeof(reply) - 1); break; default: @@ -1240,12 +1230,6 @@ csi_dispatch(struct terminal *term, uint8_t final) break; } - if (x >= 0 && y >= 0) { - char reply[64]; - size_t n = xsnprintf(reply, sizeof(reply), "\033[3;%d;%dt", - x / term->scale, y / term->scale); - term_to_slave(term, reply, n); - } break; } @@ -1616,15 +1600,12 @@ csi_dispatch(struct terminal *term, uint8_t final) } case '!': { - switch (final) { - case 'p': + if (final == 'p') { term_reset(term, false); break; - - default: - UNHANDLED(); - break; } + + UNHANDLED(); break; /* private[0] == '!' */ } From 064121ee95268b9968d9b9bc54e6008f96ac91ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 30 Aug 2021 17:55:16 +0200 Subject: [PATCH 05/75] slave: log _which_ CWD we failed to change to --- slave.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slave.c b/slave.c index 9cf49cfa..b826411f 100644 --- a/slave.c +++ b/slave.c @@ -269,7 +269,7 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv, if (chdir(cwd) < 0) { const int errno_copy = errno; - LOG_ERRNO("failed to change working directory"); + LOG_ERRNO("failed to change working directory to %s", cwd); (void)!write(fork_pipe[1], &errno_copy, sizeof(errno_copy)); _exit(errno_copy); } From ac30da7a01b55ec4778f96c70dc3b8b6ab791272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 30 Aug 2021 17:55:36 +0200 Subject: [PATCH 06/75] =?UTF-8?q?spawn:=20don=E2=80=99t=20error=20out=20if?= =?UTF-8?q?=20we=20fail=20to=20chdir()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 ++ spawn.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51f06141..e168cdc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,8 @@ now translated to pixel values using the monitor’s scaling factor when `dpi-aware=no`, or `dpi-aware=auto` and the scaling factor is larger than 1 (https://codeberg.org/dnkl/foot/issues/680). +* Spawning a new terminal with a working directory that does not exist + is no longer a fatal error. ### Removed diff --git a/spawn.c b/spawn.c index 8adef19d..d02ab131 100644 --- a/spawn.c +++ b/spawn.c @@ -48,6 +48,11 @@ spawn(struct reaper *reaper, const char *cwd, char *const argv[], if (sigaction(SIGHUP, &(struct sigaction){.sa_handler = SIG_DFL}, NULL) < 0) goto child_err; + if (cwd != NULL && chdir(cwd) < 0) { + LOG_WARN("failed to change working directory to %s: %s", + cwd, strerror(errno)); + } + bool close_stderr = stderr_fd >= 0; bool close_stdout = stdout_fd >= 0 && stdout_fd != stderr_fd; bool close_stdin = stdin_fd >= 0 && stdin_fd != stdout_fd && stdin_fd != stderr_fd; @@ -58,7 +63,6 @@ spawn(struct reaper *reaper, const char *cwd, char *const argv[], || (close_stdout && close(stdout_fd) < 0))) || (stderr_fd >= 0 && (dup2(stderr_fd, STDERR_FILENO) < 0 || (close_stderr && close(stderr_fd) < 0))) || - (cwd != NULL && chdir(cwd) < 0) || execvp(argv[0], argv) < 0) { goto child_err; From 9434066546a8409117083bd64ad43f97a3a236e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 29 Aug 2021 11:06:45 +0200 Subject: [PATCH 07/75] meson: terminfo install location now defaults to $datadir/terminfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The meson command line option -Dcustom-terminfo-install-location has been changed in the following ways: * If unset, $datadir/terminfo is used, and TERMINFO is *not* exported * If set, that value (relative to $prefix) is used, and TERMINFO *is* exported. * The special value ‘no’ is removed. -Ddefault-terminfo now also changes the terminfo names generated when -Dterminfo=enabled. Furthermore, the documentation for the TERMINFO environment variable has been removed from the foot.1 and footclient.1 man pages (but as mentioned above, foot *will* still set it if -Dcustom-terminfo-install-location has been used). INSTALL.md has been updated to now recommend using ncurses’ terminfo definitions, if available. But also to document the other alternatives; installing the terminfo definitions in a custom location, or installing them with a diferent name. It also describes the general problem, and the disadvantages of each alternative (but without going into too much depth). --- INSTALL.md | 89 +++++++++++++++++++++++++------------------- doc/foot.1.scd | 30 --------------- doc/footclient.1.scd | 30 --------------- foot.info | 10 ++--- meson.build | 32 ++++++++++------ meson_options.txt | 4 +- 6 files changed, 77 insertions(+), 118 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 81e9ec3e..6a380803 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -128,23 +128,35 @@ reasons for this: used by e.g. tmux. * New capabilities added to the `xterm-256color` terminfo could potentially break foot. +* There may be future additions or changes to foot’s terminfo. -As of ncurses 2021-07-31, ncurses ships a version of foot’s -terminfo. I still recommend building and installing the version -shipped with foot, since: +As of ncurses 2021-07-31, ncurses includes a version of foot’s +terminfo. **The recommendation is to use those**, and only install the +terminfo definitions from this git repo if the system’s ncurses +predates 2021-07-31. -* It will be more up to date (and more importantly, guaranteed to - match the installed version of foot). -* The ncurses version is missing several of the non-standard capabilities. +But, note that the foot terminfo definitions in ncurses’ lack the +non-standard capabilities. This mostly affects tmux; without them, +`terminal-overrides` must be configured to enable truecolor +support. For this reason, it _is_ possible to install “our” terminfo +definitions as well, either in a non-default location, or under a +different name. -Foot’s terminfo will by default be built, and installed along with -foot itself. This can be disabled (for example, to simplify packaging -when the terminfo definitions are packaged in a separate -package). Instructions on how to do so is in [terminfo](#terminfo). +Both have their set of issues. When installing to a non-default +location, foot will set the environment variable `TERMINFO` in the +child process. However, there are many situations where this simply +does not work. See https://codeberg.org/dnkl/foot/issues/695 for +details. -I recommend packaging foot’s terminfo files in a separate package, to -allow them to be installed on remote systems without having to install -foot itself. +Installing them under a different name generally works well, but will +break applications that check if `$TERM == foot`. + +Hence the recommendation to simply use ncurses’ terminfo definitions +if available. + +If packaging “our” terminfo definitions, I recommend doing that as a +separate package, to allow them to be installed on remote systems +without having to install foot itself. ### Setup @@ -158,44 +170,43 @@ mkdir -p bld/release && cd bld/release Available compile-time options: -| Option | Type | Default | Description | Extra dependencies | -|--------------------------------------|---------|----------------------------|----------------------------------|--------------------| -| `-Ddocs` | feature | `auto` | Builds and install documentation | scdoc | -| `-Dime` | bool | `true` | Enables IME support | None | -| `-Dgrapheme-clustering` | feature | `auto` | Enables grapheme clustering | libutf8proc | -| `-Dterminfo` | feature | `enabled` | Build and install terminfo files | tic (ncurses) | -| `-Ddefault-terminfo` | string | `foot` | Default value of `TERM` | none | -| `-Dcustom-terminfo-install-location` | string | `${datadir}/foot/terminfo` | Value to set `TERMINFO` to | None | +| Option | Type | Default | Description | Extra dependencies | +|--------------------------------------|---------|-----------------------|----------------------------------|--------------------| +| `-Ddocs` | feature | `auto` | Builds and install documentation | scdoc | +| `-Dime` | bool | `true` | Enables IME support | None | +| `-Dgrapheme-clustering` | feature | `auto` | Enables grapheme clustering | libutf8proc | +| `-Dterminfo` | feature | `enabled` | Build and install terminfo files | tic (ncurses) | +| `-Ddefault-terminfo` | string | `foot` | Default value of `TERM` | none | +| `-Dcustom-terminfo-install-location` | string | `${datadir}/terminfo` | Value to set `TERMINFO` to | None | Documentation includes the man pages, the example `foot.ini`, readme, changelog and license files. `-Ddefault-terminfo`: I strongly recommend leaving the default -value. This option is meant to be used as a last resort on platforms -where individual terminfo files cannot easily be installed. +value. Use this option if you plan on installing the terminfo files +under a different name. Setting this changes the default value of +`$TERM`, and the names of the terminfo files (if +`-Dterminfo=enabled`). `-Dcustom-terminfo-install-location` enables foot’s terminfo to -co-exist with ncurses’ version. The idea is that you install foot’s -terminfo to a non-standard location, for example -`/usr/share/foot/terminfo`. Use `-Dcustom-terminfo-install-location` -to tell foot where the terminfo is. Foot will set the environment -variable `TERMINFO` to this value (with `${prefix}` added). The value -is **relative to ${prefix}**. +co-exist with ncurses’ version, without changing the terminfo +names. The idea is that you install foot’s terminfo to a non-standard +location, for example `/usr/share/foot/terminfo`. Use +`-Dcustom-terminfo-install-location` to tell foot where the terminfo +is. Foot will set the environment variable `TERMINFO` to this value +(with `${prefix}` added). The value is **relative to ${prefix}**. -Conforming applications _should_ look in `TERMINFO` first, and -fallback to the builtin default (e.g. `/usr/share/terminfo`) if not -found. Thus, it will prefer foot’s version, if it exists (which it -typically will on localhost), and fallback to ncurses’ version if not -(e.g. on remote systems, where foot’s terminfo package has not been -installed). +Note that there are several issues with this approach: +https://codeberg.org/dnkl/foot/issues/695. -If set to `no`, foot will **not** set or modify `TERMINFO` at all. Use -this if you do not intend to use/support foot’s terminfo definitions -at all. +If left unset, foot will **not** set or modify `TERMINFO`. `-Dterminfo` can be used to disable building the terminfo definitions in the meson build. It does **not** change the default value of -`TERM`, and it does **not** disable `TERMINFO`. +`TERM`, and it does **not** disable `TERMINFO`, if +`-Dcustom-terminfo-install-location` has been set. Use this if +packaging the terminfo definitions in a separate package (and the +build script isn’t shared with the ‘foot’ package). Example: diff --git a/doc/foot.1.scd b/doc/foot.1.scd index 26fee4a1..bbe784bc 100644 --- a/doc/foot.1.scd +++ b/doc/foot.1.scd @@ -402,36 +402,6 @@ The following environment variables are set in the child process: set according to either the *--term* command-line option or the *term* config option in *foot.ini*(5). -*TERMINFO* - Path to foot's terminfo definitions. These are typically installed - in a non-standard location (though distributions may override - this), to allow them to co-exist with the foot terminfo - definitions in ncurses. Note that applications will search the - default location(s) if *TERM* does not exist in *TERMINFO*. - - Note that applications like *sudo*, *doas* and *ssh* may remove - this environment variable. - - For *sudo*, you can create a file under _/etc/sudoers.d_ (name - does not matter) with the following content: - - *Defaults env_keep += "TERMINFO"* - - For *doas*, edit _/etc/doas.conf_ and add: - - *permit setenv { TERMINFO } :wheel* - - Or, if you are not in the *wheel* group, replace *:wheel* with - your username. - - For *ssh*, you can edit _/etc/ssh/sshd_config_ (on the *server*), - and add: - - *SetEnv TERMINFO=*__ - - Where path is the location of the terminfo definitions on the - *server*, typically /usr/share/foot/terminfo. - *COLORTERM* This variable is set to *truecolor*, to indicate to client applications that 24-bit RGB colors are supported. diff --git a/doc/footclient.1.scd b/doc/footclient.1.scd index 6d5839f0..b252d5e1 100644 --- a/doc/footclient.1.scd +++ b/doc/footclient.1.scd @@ -146,36 +146,6 @@ The following environment variables are set in the child process: set according to either the *--term* command-line option or the *term* config option in *foot.ini*(5). -*TERMINFO* - Path to foot's terminfo definitions. These are typically installed - in a non-standard location (though distributions may override - this), to allow them to co-exist with the foot terminfo - definitions in ncurses. Note that applications will search the - default location(s) if *TERM* does not exist in *TERMINFO*. - - Note that applications like *sudo*, *doas* and *ssh* may remove - this environment variable. - - For *sudo*, you can create a file under _/etc/sudoers.d_ (name - does not matter) with the following content: - - *Defaults env_keep += "TERMINFO"* - - For *doas*, edit _/etc/doas.conf_ and add: - - *permit setenv { TERMINFO } :wheel* - - Or, if you are not in the *wheel* group, replace *:wheel* with - your username. - - For *ssh*, you can edit _/etc/ssh/sshd_config_ (on the *server*), - and add: - - *SetEnv TERMINFO=*__ - - Where path is the location of the terminfo definitions on the - *server*, typically /usr/share/foot/terminfo. - *COLORTERM* This variable is set to *truecolor*, to indicate to client applications that 24-bit RGB colors are supported. diff --git a/foot.info b/foot.info index 30e9e3de..f99e2aab 100644 --- a/foot.info +++ b/foot.info @@ -1,17 +1,17 @@ -foot|foot terminal emulator, - use=foot+base, +@default_terminfo@|foot terminal emulator, + use=@default_terminfo@+base, colors#256, setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48\:5\:%p1%d%;m, setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38\:5\:%p1%d%;m, -foot-direct|foot with direct color indexing, - use=foot+base, +@default_terminfo@-direct|foot with direct color indexing, + use=@default_terminfo@+base, colors#16777216, RGB, setab=\E[%?%p1%{8}%<%t4%p1%d%e48\:2\:\:%p1%{65536}%/%d\:%p1%{256}%/%{255}%&%d\:%p1%{255}%&%d%;m, setaf=\E[%?%p1%{8}%<%t3%p1%d%e38\:2\:\:%p1%{65536}%/%d\:%p1%{256}%/%{255}%&%d\:%p1%{255}%&%d%;m, -foot+base|foot base fragment, +@default_terminfo@+base|foot base fragment, am, bce, bw, diff --git a/meson.build b/meson.build index d9762f91..81dc9006 100644 --- a/meson.build +++ b/meson.build @@ -35,17 +35,15 @@ add_project_arguments( language: 'c', ) -default_terminfo_install_location = join_paths(get_option('datadir'), 'foot', 'terminfo') terminfo_install_location = get_option('custom-terminfo-install-location') -if terminfo_install_location == '' - terminfo_install_location = default_terminfo_install_location -endif -if terminfo_install_location != 'no' +if terminfo_install_location != '' add_project_arguments( ['-DFOOT_TERMINFO_PATH="@0@"'.format( join_paths(get_option('prefix'), terminfo_install_location))], language: 'c') +else + terminfo_install_location = join_paths(get_option('datadir'), 'terminfo') endif # Compute the relative path used by compiler invocations. @@ -246,15 +244,24 @@ endif tic = find_program('tic', native: true, required: get_option('terminfo')) if tic.found() + conf_data = configuration_data( + { + 'default_terminfo': get_option('default-terminfo'), + } + ) + + preprocessed = configure_file( + input: 'foot.info', + output: 'foot.info.preprocessed', + configuration: conf_data, + ) custom_target( 'terminfo', - output: 'f', - input: 'foot.info', - command: [tic, '-x', '-o', '@OUTDIR@', '-e', 'foot,foot-direct', '@INPUT@'], + output: get_option('default-terminfo')[0], + input: preprocessed, + command: [tic, '-x', '-o', '@OUTDIR@', '-e', '@0@,@0@-direct'.format(get_option('default-terminfo')), '@INPUT@'], install: true, - install_dir: (terminfo_install_location != 'no' - ? terminfo_install_location - : default_terminfo_install_location) + install_dir: terminfo_install_location ) endif @@ -267,8 +274,9 @@ summary( 'IME': get_option('ime'), 'Grapheme clustering': utf8proc.found(), 'Build terminfo': tic.found(), + 'Terminfo install location': terminfo_install_location, 'Default TERM': get_option('default-terminfo'), - 'Terminfo custom install location': terminfo_install_location, + 'Set TERMINFO': get_option('custom-terminfo-install-location') != '', }, bool_yn: true ) diff --git a/meson_options.txt b/meson_options.txt index d133f106..b47cfc7b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,5 +11,5 @@ option('terminfo', type: 'feature', value: 'enabled', description: 'Build and in option('default-terminfo', type: 'string', value: 'foot', description: 'Default value of the "term" option in foot.ini.') -option('custom-terminfo-install-location', type: 'string', - description: 'Path to foot\'s terminfo, relative to ${prefix}. If set to anything but “no“, foot will set TERMINFO to this value in the client process.') +option('custom-terminfo-install-location', type: 'string', value: '', + description: 'Path to foot\'s terminfo, relative to ${prefix}. If set, foot will set $TERMINFO to this value in the client process.') From cf767427d7daae39ee0cb03cfb54cce5596eaff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 30 Aug 2021 19:33:22 +0200 Subject: [PATCH 08/75] pkgbuild: fix terminfo removal --- PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index 52c78a77..00e10ee0 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -98,7 +98,7 @@ package_foot-git() { provides=('foot') DESTDIR="${pkgdir}/" ninja install - rm -rf "${pkgdir}/usr/share/foot/terminfo" + rm -rf "${pkgdir}/usr/share/terminfo" } package_foot-terminfo-git() { From 051745d7b1654ebfa11ff556bb8413ca8f2de996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 30 Aug 2021 19:35:56 +0200 Subject: [PATCH 09/75] install: document how to manually pre-process the terminfo source file Related to #700 --- INSTALL.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 6a380803..3ded1a0e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -223,11 +223,7 @@ be built as part of the regular build process, and installed to the specified location. Packagers may want to set `-Dterminfo=disabled`, and manually build -and install the terminfo files instead: - -```sh -tic -o -x -e foot,foot-direct foot.info -``` +and [install the terminfo](#terminfo) files instead. ### Release build @@ -424,7 +420,8 @@ terminfo files manually instead. To build the terminfo files, run: ```sh -tic -o -x -e foot,foot-direct foot.info +sed 's/@default_terminfo@/foot/g' foot.info > /tmp/foot-preprocessed.info +tic -o -x -e foot,foot-direct /tmp/foot-preprocessed.info ``` Where _”output-directory”_ **must** match the value passed to From 55433d77976d9b48e1c0d23fb4bc40e9191ac69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 30 Aug 2021 19:42:02 +0200 Subject: [PATCH 10/75] install: terminfo: pipe sed output to tic Related to #700 --- INSTALL.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index 3ded1a0e..9543948e 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -420,8 +420,8 @@ terminfo files manually instead. To build the terminfo files, run: ```sh -sed 's/@default_terminfo@/foot/g' foot.info > /tmp/foot-preprocessed.info -tic -o -x -e foot,foot-direct /tmp/foot-preprocessed.info +sed 's/@default_terminfo@/foot/g' foot.info | \ + tic -o -x -e foot,foot-direct - ``` Where _”output-directory”_ **must** match the value passed to From 173bb805c247a72069ed00cf91cc184e2d282592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 30 Aug 2021 20:20:12 +0200 Subject: [PATCH 11/75] pkgbuild: line-wrap meson configure command line --- PKGBUILD | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index 00e10ee0..1b77f7d7 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -41,7 +41,11 @@ build() { ;; esac - meson --prefix=/usr --buildtype=release --wrap-mode=nofallback -Db_lto=true .. + meson \ + --prefix=/usr \ + --buildtype=release \ + --wrap-mode=nofallback \ + -Db_lto=true .. if [[ ${do_pgo} == yes ]]; then find -name "*.gcda" -delete From fe0b348f891592705966b5aa292bf3228d20ef77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 30 Aug 2021 20:20:37 +0200 Subject: [PATCH 12/75] pkgbuild: fix terminfo package install location --- PKGBUILD | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 1b77f7d7..b05d7078 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -111,6 +111,6 @@ package_foot-terminfo-git() { conflicts=('foot-terminfo') provides=('foot-terminfo') - install -dm 755 "${pkgdir}/usr/share/foot/terminfo/f/" - cp f/* "${pkgdir}/usr/share/foot/terminfo/f/" + install -dm 755 "${pkgdir}/usr/share/terminfo/f/" + cp f/* "${pkgdir}/usr/share/terminfo/f/" } From 83c8eeb575df1ea8b178cbfdab49627f8b05aea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 30 Aug 2021 20:31:28 +0200 Subject: [PATCH 13/75] changelog: terminfo changes --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e168cdc4..f983202c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,17 @@ ## Unreleased ### Added ### Changed + +* `-Ddefault-terminfo` is now also applied to the generated terminfo + definitions when `-Dterminfo=enabled`. +* `-Dcustom-terminfo-install-location` no longer accepts `no` as a + special value, to disable exporting `TERMINFO`. To achieve the same + result, simply don’t set it at all. If it _is_ set, `TERMINFO` is + still exported, like before. +* The default install location for the terminfo definitions have been + changed back to `${datadir}/terminfo`. + + ### Deprecated ### Removed ### Fixed From bb948d03e199870da6b35ba6f88ea88be12cfe21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 19:56:59 +0200 Subject: [PATCH 14/75] =?UTF-8?q?terminal:=20prefer=20the=20advance=20widt?= =?UTF-8?q?h=20of=20=E2=80=98M=E2=80=99=20over=20that=20of=20a=20space?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes some non-monospaced fonts more readable, allowing users to read errors and warnings printed in the window. Furthermore, fcft-3.0 will remove the space_advance member, so once we upgrade, we’ll have to rasterize a glyph ourselves anyway. --- terminal.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/terminal.c b/terminal.c index cb1641f2..a95a5c61 100644 --- a/terminal.c +++ b/terminal.c @@ -657,10 +657,15 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) const struct config *conf = term->conf; + const struct fcft_glyph *M = fcft_glyph_rasterize( + term->fonts[0], L'M', term->font_subpixel); + term->cell_width = - (term->fonts[0]->space_advance.x > 0 - ? term->fonts[0]->space_advance.x - : term->fonts[0]->max_advance.x) + (M != NULL + ? M->advance.x + : (term->fonts[0]->space_advance.x > 0 + ? term->fonts[0]->space_advance.x + : term->fonts[0]->max_advance.x)) + term_pt_or_px_as_pixels(term, &conf->letter_spacing); term->cell_height = term->font_line_height.px >= 0 From 8ffc556d441013e06236f144020b91ea4e3a3f86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 19:42:22 +0200 Subject: [PATCH 15/75] main: verify primary font is monospaced at startup Load a couple of ASCII glyphs and check if their advance widths matches. If not, warn the user that the font is probably not monospaced. This can be disabled by setting tweak.font-monospace-warn=no. Closes #704. --- config.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++ config.h | 4 ++++ doc/foot.ini.5.scd | 11 ++++++++++ main.c | 5 +++++ 4 files changed, 72 insertions(+) diff --git a/config.c b/config.c index ac6dd24d..ca1278df 100644 --- a/config.c +++ b/config.c @@ -2452,6 +2452,9 @@ parse_section_tweak( conf->tweak.box_drawing_solid_shades ? "yes" : "no"); } + else if (strcmp(key, "font-monospace-warn") == 0) + conf->tweak.font_monospace_warn = str_to_bool(value); + else { LOG_AND_NOTIFY_ERR("%s:%u: [tweak]: %s: invalid key", path, lineno, key); return false; @@ -2957,6 +2960,7 @@ config_load(struct config *conf, const char *conf_path, .damage_whole_window = false, .box_drawing_base_thickness = 0.04, .box_drawing_solid_shades = true, + .font_monospace_warn = true, }, .notifications = tll_init(), @@ -3356,3 +3360,51 @@ config_font_list_destroy(struct config_font_list *font_list) font_list->count = 0; font_list->arr = NULL; } + + +bool +check_if_font_is_monospaced(const char *pattern, + user_notifications_t *notifications) +{ + struct fcft_font *f = fcft_from_name( + 1, (const char *[]){pattern}, ":size=8"); + + if (f == NULL) + return true; + + static const wchar_t chars[] = {L'a', L'i', L'l', L'M', L'W'}; + + bool is_monospaced = true; + int last_width = -1; + + for (size_t i = 0; i < sizeof(chars) / sizeof(chars[0]); i++) { + const struct fcft_glyph *g = fcft_glyph_rasterize( + f, chars[i], FCFT_SUBPIXEL_NONE); + + if (g == NULL) + continue; + + if (last_width >= 0 && g->advance.x != last_width) { + LOG_WARN("%s: font does not appear to be monospace; " + "check your config, or disable this warning by " + "setting [tweak].font-monospace-warn=no", + pattern); + + user_notification_add( + notifications, + USER_NOTIFICATION_WARNING, + "%s: font does not appear to be monospace; " + "check your config, or disable this warning by " + "settting \033[1m[tweak].font-monospace-warn=no\033[22m", + pattern); + + is_monospaced = false; + break; + } + + last_width = g->advance.x; + } + + fcft_destroy(f); + return is_monospaced; +} diff --git a/config.h b/config.h index 40daaf3b..6646cca3 100644 --- a/config.h +++ b/config.h @@ -253,6 +253,7 @@ struct config { off_t max_shm_pool_size; float box_drawing_base_thickness; bool box_drawing_solid_shades; + bool font_monospace_warn; } tweak; user_notifications_t notifications; @@ -269,3 +270,6 @@ struct config *config_clone(const struct config *old); bool config_font_parse(const char *pattern, struct config_font *font); void config_font_list_destroy(struct config_font_list *font_list); + +bool check_if_font_is_monospaced( + const char *pattern, user_notifications_t *notifications); diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 65a3a4dc..a124316e 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -1022,6 +1022,17 @@ any of these options. Default: _double-width_ +*font-monospace-warn* + Boolean. When enabled, foot will use heuristics to try to verify + the primary font is a monospace font, and warn if it is not. + + Disable this if you still want to use the font, even if foot + thinks it is not monospaced. + + You may also want to disable it to get slightly faster startup times. + + Default: _yes_ + *max-shm-pool-size-mb* This option controls the amount of virtual address space used by the pixmap memory to which the terminal screen content is diff --git a/main.c b/main.c index b036b9f6..4f7b027d 100644 --- a/main.c +++ b/main.c @@ -480,6 +480,11 @@ main(int argc, char *const *argv) conf.presentation_timings = presentation_timings; conf.hold_at_exit = hold; + if (conf.tweak.font_monospace_warn && conf.fonts[0].count > 0) { + check_if_font_is_monospaced( + conf.fonts[0].arr[0].pattern, &conf.notifications); + } + struct fdm *fdm = NULL; struct reaper *reaper = NULL; struct wayland *wayl = NULL; From 1233e000f02ce664134fbbba7b46b710f544436d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 19:52:31 +0200 Subject: [PATCH 16/75] server: verify primary font is monospaced MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ... unless we’re re-using the main conf as-is, in which case we will already have done this (and the conf’s user-notification list will already contain a warning). --- server.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server.c b/server.c index 7d3d3ca0..5144ed17 100644 --- a/server.c +++ b/server.c @@ -291,6 +291,12 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) conf->hold_at_exit = cdata.hold; config_override_apply(conf, &overrides, false); + + if (conf->tweak.font_monospace_warn && conf->fonts[0].count > 0) { + check_if_font_is_monospaced( + conf->fonts[0].arr[0].pattern, + &conf->notifications); + } } *instance = (struct terminal_instance) { From e51e085d7a9713a21bfaebaab233b3df40f64e27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 19:54:31 +0200 Subject: [PATCH 17/75] changelog: check if primary font is monospaced --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f983202c..cf13d300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,12 @@ ## Unreleased ### Added + +* Warn when it appears the primary font is not monospaced. Can be + disabled by setting `[tweak].font-monospace-warn=no` + (https://codeberg.org/dnkl/foot/issues/704). + + ### Changed * `-Ddefault-terminfo` is now also applied to the generated terminfo From 73b488a2b217bf43fb88a14f6c664e33b0827663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 20:01:35 +0200 Subject: [PATCH 18/75] =?UTF-8?q?config:=20codespell:=20setting=20has=20on?= =?UTF-8?q?ly=20two=20=E2=80=98t=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.c b/config.c index ca1278df..4241060d 100644 --- a/config.c +++ b/config.c @@ -3395,7 +3395,7 @@ check_if_font_is_monospaced(const char *pattern, USER_NOTIFICATION_WARNING, "%s: font does not appear to be monospace; " "check your config, or disable this warning by " - "settting \033[1m[tweak].font-monospace-warn=no\033[22m", + "setting \033[1m[tweak].font-monospace-warn=no\033[22m", pattern); is_monospaced = false; From 26859a516805cdd8aa8ffd3dc6a9478c8e8ef9bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 20:08:23 +0200 Subject: [PATCH 19/75] render: unref clip region --- render.c | 1 + 1 file changed, 1 insertion(+) diff --git a/render.c b/render.c index 23ad460f..cfb6f0bb 100644 --- a/render.c +++ b/render.c @@ -624,6 +624,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, &clip, x, y, render_width, term->cell_height); pixman_image_set_clip_region32(pix, &clip); + pixman_region32_fini(&clip); /* Background */ pixman_image_fill_rectangles( From f39a62fa5e9207a6f8c16066527c68ca9a5524a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 5 Sep 2021 09:28:19 +0200 Subject: [PATCH 20/75] server: no need to clone the config to handle -N,--no-wait --- config.h | 1 - server.c | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/config.h b/config.h index 6646cca3..599bc464 100644 --- a/config.h +++ b/config.h @@ -70,7 +70,6 @@ struct config { char *app_id; wchar_t *word_delimiters; bool login_shell; - bool no_wait; bool locked_title; struct { diff --git a/server.c b/server.c index 5144ed17..a5d87b04 100644 --- a/server.c +++ b/server.c @@ -277,16 +277,12 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) const bool need_to_clone_conf = tll_length(overrides)> 0 || - cdata.hold != server->conf->hold_at_exit || - cdata.no_wait != server->conf->no_wait; + cdata.hold != server->conf->hold_at_exit; struct config *conf = NULL; if (need_to_clone_conf) { conf = config_clone(server->conf); - if (cdata.no_wait != server->conf->no_wait) - conf->no_wait = cdata.no_wait; - if (cdata.hold != server->conf->hold_at_exit) conf->hold_at_exit = cdata.hold; @@ -317,7 +313,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data) goto shutdown; } - if (conf != NULL && conf->no_wait) { + if (cdata.no_wait) { // the server owns the instance tll_push_back(server->terminals, instance); client_send_exit_code(client, 0); From f9642e959725fc43ad7dac0e3c8be842d53aa903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 5 Sep 2021 10:27:13 +0200 Subject: [PATCH 21/75] sixel: calculate default bg once, in init We have all information we need to calculate the default background color in sixel_init(): * Whether the image have transparency or not * The current ANSI background color --- sixel.c | 25 ++++++++++--------------- terminal.h | 1 + 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/sixel.c b/sixel.c index 18a9c146..170ef4e4 100644 --- a/sixel.c +++ b/sixel.c @@ -15,16 +15,6 @@ static size_t count; -static uint32_t -get_bg(const struct terminal *term) -{ - return term->sixel.transparent_bg - ? 0x00000000u - : 0xffu << 24 | (term->vt.attrs.have_bg - ? term->vt.attrs.bg - : term->colors.bg); -} - void sixel_fini(struct terminal *term) { @@ -78,9 +68,14 @@ sixel_init(struct terminal *term, int p1, int p2, int p3) term->sixel.palette = term->sixel.shared_palette; } - uint32_t bg = get_bg(term); + term->sixel.default_bg = term->sixel.transparent_bg + ? 0x00000000u + : 0xffu << 24 | (term->vt.attrs.have_bg + ? term->vt.attrs.bg + : term->colors.bg); + for (size_t i = 0; i < 1 * 6; i++) - term->sixel.image.data[i] = bg; + term->sixel.image.data[i] = term->sixel.default_bg; count = 0; } @@ -1118,7 +1113,7 @@ resize_horizontally(struct terminal *term, int new_width) /* Width (and thus stride) change - need to allocate a new buffer */ uint32_t *new_data = xmalloc(new_width * alloc_height * sizeof(uint32_t)); - uint32_t bg = get_bg(term); + uint32_t bg = term->sixel.default_bg; /* Copy old rows, and initialize new columns to background color */ for (int r = 0; r < height; r++) { @@ -1167,7 +1162,7 @@ resize_vertically(struct terminal *term, int new_height) return false; } - uint32_t bg = get_bg(term); + uint32_t bg = term->sixel.default_bg; /* Initialize new rows to background color */ for (int r = old_height; r < new_height; r++) { @@ -1204,7 +1199,7 @@ resize(struct terminal *term, int new_width, int new_height) xassert(alloc_new_height - new_height < 6); uint32_t *new_data = NULL; - uint32_t bg = get_bg(term); + uint32_t bg = term->sixel.default_bg; if (new_width == old_width) { /* Width (and thus stride) is the same, so we can simply diff --git a/terminal.h b/terminal.h index df253434..26dcd847 100644 --- a/terminal.h +++ b/terminal.h @@ -576,6 +576,7 @@ struct terminal { unsigned param_idx; /* Parameters seen */ bool transparent_bg; + uint32_t default_bg; /* Application configurable */ unsigned palette_size; /* Number of colors in palette */ From 47c32d5913044912f5e0974ab6554de1f2a62b3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 5 Sep 2021 11:08:13 +0200 Subject: [PATCH 22/75] sixel: avoid looking up color from palette for each sixel Instead, do the palette lookup when we receive the DECGCI (i.e. when the palette entry is selected), and store the actual color value in our sixel struct. --- sixel.c | 6 +++--- terminal.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sixel.c b/sixel.c index 170ef4e4..3298f3f4 100644 --- a/sixel.c +++ b/sixel.c @@ -1272,8 +1272,6 @@ sixel_add(struct terminal *term, int col, int width, uint32_t color, uint8_t six static void sixel_add_many(struct terminal *term, uint8_t c, unsigned count) { - uint32_t color = term->sixel.palette[term->sixel.color_idx]; - int col = term->sixel.pos.col; int width = term->sixel.image.width; @@ -1283,6 +1281,7 @@ sixel_add_many(struct terminal *term, uint8_t c, unsigned count) return; } + uint32_t color = term->sixel.color; for (unsigned i = 0; i < count; i++, col++) sixel_add(term, col, width, color, c); @@ -1509,7 +1508,8 @@ decgci(struct terminal *term, uint8_t c) break; } } - } + } else + term->sixel.color = term->sixel.palette[term->sixel.color_idx]; term->sixel.state = SIXEL_DECSIXEL; sixel_put(term, c); diff --git a/terminal.h b/terminal.h index 26dcd847..620678b1 100644 --- a/terminal.h +++ b/terminal.h @@ -560,6 +560,7 @@ struct terminal { uint32_t *private_palette; /* Private palette, used when private mode 1070 is enabled */ uint32_t *shared_palette; /* Shared palette, used when private mode 1070 is disabled */ uint32_t *palette; /* Points to either private_palette or shared_palette */ + uint32_t color; struct { uint32_t *data; /* Raw image data, in ARGB */ From b1a4d4384571a6aa2bfc97644a6dff7066afef0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 5 Sep 2021 11:09:45 +0200 Subject: [PATCH 23/75] sixel: call decsixel() directly, instead of going through sixel_put() --- sixel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sixel.c b/sixel.c index 3298f3f4..acb3f31b 100644 --- a/sixel.c +++ b/sixel.c @@ -1401,7 +1401,7 @@ decgra(struct terminal *term, uint8_t c) } term->sixel.state = SIXEL_DECSIXEL; - sixel_put(term, c); + decsixel(term, c); break; } } @@ -1512,7 +1512,7 @@ decgci(struct terminal *term, uint8_t c) term->sixel.color = term->sixel.palette[term->sixel.color_idx]; term->sixel.state = SIXEL_DECSIXEL; - sixel_put(term, c); + decsixel(term, c); break; } } From d1266625608f8f593f4df76c1b6ff1897c89adc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 5 Sep 2021 12:39:25 +0200 Subject: [PATCH 24/75] term: reduce scope of variable --- terminal.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/terminal.c b/terminal.c index a95a5c61..0138aa15 100644 --- a/terminal.c +++ b/terminal.c @@ -230,13 +230,11 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data) } uint8_t buf[24 * 1024]; - ssize_t count = sizeof(buf); - const size_t max_iterations = !hup ? 10 : (size_t)-1ll; for (size_t i = 0; i < max_iterations && pollin; i++) { xassert(pollin); - count = read(term->ptmx, buf, sizeof(buf)); + ssize_t count = read(term->ptmx, buf, sizeof(buf)); if (count < 0) { if (errno == EAGAIN || errno == EIO) { From b26fda7ef0e606d7a5f9e774548bdc282a3d7965 Mon Sep 17 00:00:00 2001 From: Arnavion Date: Wed, 8 Sep 2021 22:42:58 -0700 Subject: [PATCH 25/75] doc: Document that the default section can be reopened as [main] --- doc/foot.ini.5.scd | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index a124316e..6a9b5de5 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -6,8 +6,9 @@ foot.ini - configuration file for *foot*(1) # DESCRIPTION *foot* uses the standard _unix configuration format_, with section based -key/value pairs. The default section is unnamed (i.e. not prefixed -with a _[section]_). +key/value pairs. The default section is usually unnamed, i.e. not prefixed +with a _[section]_. However it can also be explicitly named _[main]_, +say if it needs to be reopened after any of the other sections. foot will search for a configuration file in the following locations, in this order: @@ -16,7 +17,7 @@ in this order: - *~/.config/foot/foot.ini* - *XDG_CONFIG_DIRS/foot/foot.ini* -# SECTION: default +# SECTION: main *shell* Executable to launch. Typically a shell. Default: _$SHELL_ if set, From e553e1076c78fe99a7ea1faa5f2fd28a4f529d10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 6 Sep 2021 18:12:45 +0200 Subject: [PATCH 26/75] input: workaround GNOME issue with pointer button events Under certain circumstances, GNOME will send multiple pointer button press events, without any release or leave events in between. This trips up our button tracking. Workaround, by replacing the existing state for the pressed button with the new state. Previously, debug builds would assert (and thus crash), while release builds would have multiple states for the same button, causing (probably) issues like the title bar not being usable (as in, cannot be dragged, buttons not working etc). Hopefully closes #709 --- CHANGELOG.md | 6 +++ input.c | 102 ++++++++++++++++++++++++++++----------------------- 2 files changed, 63 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf13d300..57984c49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,12 @@ ### Deprecated ### Removed ### Fixed + +* Added workaround for GNOME bug where multiple button press events + (for the same button) is sent to the CSDs without any release or + leave events in between (https://codeberg.org/dnkl/foot/issues/709). + + ### Security ### Contributors diff --git a/input.c b/input.c index ec76c42a..853bc495 100644 --- a/input.c +++ b/input.c @@ -1694,50 +1694,6 @@ fdm_csd_move(struct fdm *fdm, int fd, int events, void *data) struct wl_window *win = seat->mouse_focus->window; - /* - * Workaround GNOME bug - * - * Dragging the window, then stopping the drag (releasing the - * mouse button), *without* moving the mouse, and then clicking - * twice, waiting for the CSD timer, and finally clicking once - * more, results in the following sequence (keyboard and other - * irrelevant events filtered out, unless they’re needed to prove - * a point): - * - * dbg: input.c:1551: cancelling drag timer, moving window - * dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=873, surface=0x6070000036d0 - * dbg: input.c:1432: seat0: pointer-leave: pointer=0x607000003660, serial=874, surface = 0x6070000396e0, old-moused = 0x622000006100 - * - * --> drag stopped here - * - * --> LMB clicked first time after the drag (generates the enter event on *release*, but no button events) - * dbg: input.c:1360: pointer-enter: pointer=0x607000003660, serial=876, surface = 0x6070000396e0, new-moused = 0x622000006100 - * - * --> LMB clicked, and held until the timer times out, second time after the drag - * dbg: input.c:1712: BUTTON: pointer=0x607000003660, serial=877, button=110, state=1 - * dbg: input.c:1806: starting move timer - * dbg: input.c:1692: move timer timed out - * dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=878, surface=0x6070000036d0 - * - * --> NOTE: ^^ no pointer leave event this time, only the keyboard leave - * - * --> LMB clicked one last time - * dbg: input.c:697: seat0: keyboard_enter: keyboard=0x607000003580, serial=879, surface=0x6070000036d0 - * dbg: input.c:1712: BUTTON: pointer=0x607000003660, serial=880, button=110, state=1 - * err: input.c:1741: BUG in wl_pointer_button(): assertion failed: 'it->item.button != button' - * - * What are we seeing? - * - * - GNOME does *not* send a pointer *enter* event after the drag - * has stopped - * - The second drag does *not* generate a pointer *leave* event - * - The missing leave event means we’re still tracking LMB as - * being held down in our seat struct. - * - This leads to an assert (debug builds) when LMB is clicked - * again (seat’s button list already contains LMB). - */ - tll_free(seat->mouse.buttons); - win->csd.move_timeout_fd = -1; xdg_toplevel_move(win->xdg_toplevel, seat->wl_seat, win->csd.serial); return true; @@ -1774,6 +1730,62 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, } else seat->mouse.count = 1; + /* + * Workaround GNOME bug + * + * Dragging the window, then stopping the drag (releasing the + * mouse button), *without* moving the mouse, and then + * clicking twice, waiting for the CSD timer, and finally + * clicking once more, results in the following sequence + * (keyboard and other irrelevant events filtered out, unless + * they’re needed to prove a point): + * + * dbg: input.c:1551: cancelling drag timer, moving window + * dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=873, surface=0x6070000036d0 + * dbg: input.c:1432: seat0: pointer-leave: pointer=0x607000003660, serial=874, surface = 0x6070000396e0, old-moused = 0x622000006100 + * + * --> drag stopped here + * + * --> LMB clicked first time after the drag (generates the + * enter event on *release*, but no button events) + * dbg: input.c:1360: pointer-enter: pointer=0x607000003660, serial=876, surface = 0x6070000396e0, new-moused = 0x622000006100 + * + * --> LMB clicked, and held until the timer times out, second + * time after the drag + * dbg: input.c:1712: BUTTON: pointer=0x607000003660, serial=877, button=110, state=1 + * dbg: input.c:1806: starting move timer + * dbg: input.c:1692: move timer timed out + * dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=878, surface=0x6070000036d0 + * + * --> NOTE: ^^ no pointer leave event this time, only the + * keyboard leave + * + * --> LMB clicked one last time + * dbg: input.c:697: seat0: keyboard_enter: keyboard=0x607000003580, serial=879, surface=0x6070000036d0 + * dbg: input.c:1712: BUTTON: pointer=0x607000003660, serial=880, button=110, state=1 + * err: input.c:1741: BUG in wl_pointer_button(): assertion failed: 'it->item.button != button' + * + * What are we seeing? + * + * - GNOME does *not* send a pointer *enter* event after the drag + * has stopped + * - The second drag does *not* generate a pointer *leave* event + * - The missing leave event means we’re still tracking LMB as + * being held down in our seat struct. + * - This leads to an assert (debug builds) when LMB is clicked + * again (seat’s button list already contains LMB). + * + * Note: I’ve also observed variants of the above + */ + tll_foreach(seat->mouse.buttons, it) { + if (it->item.button == button) { + LOG_WARN("multiple button press events for button %d " + "(compositor bug?)", button); + tll_remove(seat->mouse.buttons, it); + break; + } + } + #if defined(_DEBUG) tll_foreach(seat->mouse.buttons, it) xassert(it->item.button != button); @@ -1817,7 +1829,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, * 4. Release mouse button * 5. BAM! */ - LOG_WARN("stray button release event"); + LOG_WARN("stray button release event (compositor bug?)"); return; } From 996356983ba7b3c92f98b434a8ee8677ce2660de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 14:15:21 +0200 Subject: [PATCH 27/75] meson-pgo: initial version of a meson wrapper script for PGO builds Usage: meson-pgo.sh auto|partial|full Note: build-dir must *not* exist before the script is run (but if it does, the script will tell you so, and exit). Supported compilers: gcc and clang The script does *not* add _any_ custom meson options, except configure -Db_pgo. Thus, you probably want to call it like this: meson-pgo.sh auto . /tmp/foot-pgo-build --buildtype=release -Db_lto=true --- meson-pgo.sh | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100755 meson-pgo.sh diff --git a/meson-pgo.sh b/meson-pgo.sh new file mode 100755 index 00000000..8aa0e97e --- /dev/null +++ b/meson-pgo.sh @@ -0,0 +1,109 @@ +#!/bin/sh + +set -e + +mode=${1}; shift +source_dir=$(realpath "${1}"); shift +build_dir=${1}; shift + +if [ -d ${build_dir} ]; then + echo "${build_dir}: build directory already exists" + exit 1 +fi + +case ${mode} in + auto|partial|full) + ;; + + *) + echo "${mode}: invalid PGO mode (must be one of 'auto', 'partial' or 'full')" + exit 1 + ;; +esac + +compiler=other +do_pgo=no + +CFLAGS="${CFLAGS} -O3" + +case $(${CC-cc} --version) in + *GCC*) + compiler=gcc + do_pgo=yes + ;; + + *clang*) + compiler=clang + + if command -v llvm-profdata > /dev/null; then + do_pgo=yes + CFLAGS="${CFLAGS} -Wno-ignored-optimization-argument" + fi + ;; +esac + +if [ ${mode} = auto ]; then + if [ -n "${WAYLAND_DISPLAY+x}" ]; then + mode=full + else + mode=partial + fi +fi + +# echo "source: ${source_dir}" +# echo "build: ${build_dir}" +# echo "compiler: ${compiler}" +# echo "mode: ${mode}" + +export CFLAGS +meson "${@}" "${build_dir}" + +if [ ${do_pgo} = yes ]; then + find . -name "*.gcda" -delete + meson configure "${build_dir}" -Db_pgo=generate + ninja -C "${build_dir}" + + # If fcft/tllist are subprojects, we need to ensure their tests + # have been executed, or we’ll get “profile count data file not + # found” errors. + ninja -C "${build_dir}" test + + script_options="--scroll --scroll-region --colors-regular --colors-bright --colors-256 --colors-rgb --attr-bold --attr-italic --attr-underline --sixel" + + tmp_file=$(mktemp) + pwd=$(pwd) + + cleanup() { + rm -f "${tmp_file}" + cd "${pwd}" + } + trap cleanup EXIT INT HUP TERM + + cd "${build_dir}" + if [ ${mode} = full ]; then + ./footclient --version + ./foot \ + --config=/dev/null \ + --term=xterm \ + sh -c "${source_dir}/scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}" + else + ./footclient --version + ./foot --version + "${source_dir}/scripts/generate-alt-random-writes.py" \ + --rows=67 \ + --cols=135 \ + ${script_options} \ + "${tmp_file}" + ./pgo "${tmp_file}" + fi + cd "${pwd}" + rm "${tmp_file}" + + if [ ${compiler} = clang ]; then + llvm-profdata merge "${build_dir}"/default_*.profraw --output="${build_dir}/default.profdata" + fi + + meson configure "${build_dir}" -Db_pgo=use +else + echo "Not doing PGO" +fi From ffc6fd7e03e145deec9300a9bff0ac7d8620ac7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 14:22:56 +0200 Subject: [PATCH 28/75] meson-pgo: realpath() on build dir too --- meson-pgo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson-pgo.sh b/meson-pgo.sh index 8aa0e97e..ba3ea954 100755 --- a/meson-pgo.sh +++ b/meson-pgo.sh @@ -4,7 +4,7 @@ set -e mode=${1}; shift source_dir=$(realpath "${1}"); shift -build_dir=${1}; shift +build_dir=$(realpath ${1}); shift if [ -d ${build_dir} ]; then echo "${build_dir}: build directory already exists" From ecf1b30d53f85023bd8a9b9776e9621536f217a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 14:24:36 +0200 Subject: [PATCH 29/75] meson-pgo: remove debug output --- meson-pgo.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/meson-pgo.sh b/meson-pgo.sh index ba3ea954..09dc9c5d 100755 --- a/meson-pgo.sh +++ b/meson-pgo.sh @@ -104,6 +104,4 @@ if [ ${do_pgo} = yes ]; then fi meson configure "${build_dir}" -Db_pgo=use -else - echo "Not doing PGO" fi From 5ab1cd3d644f8d4af2746a767e1ece7ded119170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 16:25:44 +0200 Subject: [PATCH 30/75] meson-pgo: initial support for full PGO through headless Sway --- meson-pgo.sh | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/meson-pgo.sh b/meson-pgo.sh index 09dc9c5d..dc0157f0 100755 --- a/meson-pgo.sh +++ b/meson-pgo.sh @@ -12,7 +12,7 @@ if [ -d ${build_dir} ]; then fi case ${mode} in - auto|partial|full) + auto|partial|full|full-headless-sway) ;; *) @@ -45,6 +45,8 @@ esac if [ ${mode} = auto ]; then if [ -n "${WAYLAND_DISPLAY+x}" ]; then mode=full + elif command -v sway > /dev/null; then + mode=full-headless-sway else mode=partial fi @@ -80,22 +82,36 @@ if [ ${do_pgo} = yes ]; then trap cleanup EXIT INT HUP TERM cd "${build_dir}" - if [ ${mode} = full ]; then - ./footclient --version - ./foot \ - --config=/dev/null \ - --term=xterm \ - sh -c "${source_dir}/scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}" - else - ./footclient --version - ./foot --version - "${source_dir}/scripts/generate-alt-random-writes.py" \ - --rows=67 \ - --cols=135 \ - ${script_options} \ - "${tmp_file}" - ./pgo "${tmp_file}" - fi + case ${mode} in + full) + ./footclient --version + ./foot \ + --config=/dev/null \ + --term=xterm \ + sh -c "${source_dir}/scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}" + ;; + + full-headless-sway) + ./footclient --version + sway_conf=$(mktemp) + echo "exec ${build_dir}/foot -o tweak.render-timer=log --config=/dev/null --term=xterm sh -c \"${source_dir}/scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}\" && swaymsg exit" > "${sway_conf}" + export WLR_BACKENDS=headless + sway -c "${sway_conf}" + rm "${sway_conf}" + ;; + + partial) + ./footclient --version + ./foot --version + "${source_dir}/scripts/generate-alt-random-writes.py" \ + --rows=67 \ + --cols=135 \ + ${script_options} \ + "${tmp_file}" + ./pgo "${tmp_file}" + ;; + esac + cd "${pwd}" rm "${tmp_file}" From 8ea6c9351512b833c048db8bfedd4f4075942815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 16:38:12 +0200 Subject: [PATCH 31/75] =?UTF-8?q?meson-pgo:=20=E2=80=98auto=E2=80=99=20now?= =?UTF-8?q?=20prefers=20full-headless-sway?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- meson-pgo.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meson-pgo.sh b/meson-pgo.sh index dc0157f0..2057c2a6 100755 --- a/meson-pgo.sh +++ b/meson-pgo.sh @@ -43,10 +43,10 @@ case $(${CC-cc} --version) in esac if [ ${mode} = auto ]; then - if [ -n "${WAYLAND_DISPLAY+x}" ]; then - mode=full - elif command -v sway > /dev/null; then + if command -v sway > /dev/null; then mode=full-headless-sway + elif [ -n "${WAYLAND_DISPLAY+x}" ]; then + mode=full else mode=partial fi From a3b6ecf3b22d3980cde0f74bde677ba114927af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 16:48:49 +0200 Subject: [PATCH 32/75] meson-pgo: add mode=none --- meson-pgo.sh | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/meson-pgo.sh b/meson-pgo.sh index 2057c2a6..ca542351 100755 --- a/meson-pgo.sh +++ b/meson-pgo.sh @@ -12,7 +12,7 @@ if [ -d ${build_dir} ]; then fi case ${mode} in - auto|partial|full|full-headless-sway) + none|auto|partial|full|full-headless-sway) ;; *) @@ -42,15 +42,21 @@ case $(${CC-cc} --version) in ;; esac -if [ ${mode} = auto ]; then - if command -v sway > /dev/null; then - mode=full-headless-sway - elif [ -n "${WAYLAND_DISPLAY+x}" ]; then - mode=full - else - mode=partial - fi -fi +case ${mode} in + none) + do_pgo=no + ;; + + auto) + if command -v sway > /dev/null; then + mode=full-headless-sway + elif [ -n "${WAYLAND_DISPLAY+x}" ]; then + mode=full + else + mode=partial + fi + ;; +esac # echo "source: ${source_dir}" # echo "build: ${build_dir}" From ac9b3025d87f85b06ebe85aba610dfe1e3424801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 21:07:03 +0200 Subject: [PATCH 33/75] meson-pgo: improved argument validation --- meson-pgo.sh | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/meson-pgo.sh b/meson-pgo.sh index ca542351..6a366f79 100755 --- a/meson-pgo.sh +++ b/meson-pgo.sh @@ -2,25 +2,34 @@ set -e -mode=${1}; shift -source_dir=$(realpath "${1}"); shift -build_dir=$(realpath ${1}); shift - -if [ -d ${build_dir} ]; then - echo "${build_dir}: build directory already exists" +usage_and_die() { + echo "Usage: ${0} none|partial|full|full-headless-sway|[auto] " exit 1 -fi +} + +[ ${#} -ge 3 ] || usage_and_die + +mode=${1} +source_dir=$(realpath "${2}") +build_dir=$(realpath "${3}") +shift 3 case ${mode} in none|auto|partial|full|full-headless-sway) - ;; + ;; *) - echo "${mode}: invalid PGO mode (must be one of 'auto', 'partial' or 'full')" - exit 1 + usage_and_die ;; esac +# meson will complain if source dir is invalid + +if [ -d "${build_dir}" ]; then + echo "${build_dir}: build directory already exists" + exit 1 +fi + compiler=other do_pgo=no From 575f9135ae70d21a113e26f780e9341c1927f36a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 21:10:53 +0200 Subject: [PATCH 34/75] meson-pgo: do mode validation and mode evaluation at the same time --- meson-pgo.sh | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/meson-pgo.sh b/meson-pgo.sh index 6a366f79..ed673316 100755 --- a/meson-pgo.sh +++ b/meson-pgo.sh @@ -14,15 +14,6 @@ source_dir=$(realpath "${2}") build_dir=$(realpath "${3}") shift 3 -case ${mode} in - none|auto|partial|full|full-headless-sway) - ;; - - *) - usage_and_die - ;; -esac - # meson will complain if source dir is invalid if [ -d "${build_dir}" ]; then @@ -52,6 +43,9 @@ case $(${CC-cc} --version) in esac case ${mode} in + partial|full|full-headless-sway) + ;; + none) do_pgo=no ;; @@ -65,6 +59,10 @@ case ${mode} in mode=partial fi ;; + + *) + usage_and_die + ;; esac # echo "source: ${source_dir}" From 098f2c0c88d7ba4c0a57bea572d9d4819eea3b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 31 Aug 2021 21:14:29 +0200 Subject: [PATCH 35/75] meson-pgo: use as little path quoting as possible --- meson-pgo.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/meson-pgo.sh b/meson-pgo.sh index ed673316..9ad2ad26 100755 --- a/meson-pgo.sh +++ b/meson-pgo.sh @@ -116,7 +116,7 @@ if [ ${do_pgo} = yes ]; then partial) ./footclient --version ./foot --version - "${source_dir}/scripts/generate-alt-random-writes.py" \ + "${source_dir}"/scripts/generate-alt-random-writes.py \ --rows=67 \ --cols=135 \ ${script_options} \ @@ -129,7 +129,10 @@ if [ ${do_pgo} = yes ]; then rm "${tmp_file}" if [ ${compiler} = clang ]; then - llvm-profdata merge "${build_dir}"/default_*.profraw --output="${build_dir}/default.profdata" + llvm-profdata \ + merge \ + "${build_dir}"/default_*.profraw \ + --output="${build_dir}"/default.profdata fi meson configure "${build_dir}" -Db_pgo=use From 87b68f23cba3ef6b733c162eeb991779220fc2f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 1 Sep 2021 18:57:57 +0200 Subject: [PATCH 36/75] meson-pgo: headless-sway: export XDG_RUNTIME_DIR --- meson-pgo.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/meson-pgo.sh b/meson-pgo.sh index 9ad2ad26..86a1483f 100755 --- a/meson-pgo.sh +++ b/meson-pgo.sh @@ -106,11 +106,18 @@ if [ ${do_pgo} = yes ]; then full-headless-sway) ./footclient --version + + runtime_dir=$(mktemp -d) sway_conf=$(mktemp) + echo "exec ${build_dir}/foot -o tweak.render-timer=log --config=/dev/null --term=xterm sh -c \"${source_dir}/scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}\" && swaymsg exit" > "${sway_conf}" + export XDG_RUNTIME_DIR=${runtime_dir} export WLR_BACKENDS=headless + sway -c "${sway_conf}" + rm "${sway_conf}" + rm -rf "${runtime_dir}" ;; partial) From 540310924b08914ff2b00efd1ec70264195b9493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 1 Sep 2021 19:03:07 +0200 Subject: [PATCH 37/75] meson-pgo: document limitations/requirements --- meson-pgo.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/meson-pgo.sh b/meson-pgo.sh index 86a1483f..cbd355f1 100755 --- a/meson-pgo.sh +++ b/meson-pgo.sh @@ -1,5 +1,13 @@ #!/bin/sh +# Requirements: +# +# * all: build directory must not exist; it is created by the script +# * all: LC_CTYPE must be set to an UTF-8 locale +# * full*: must have at least one font installed +# * full-headless-sway: Sway 1.6.2 must be installed + + set -e usage_and_die() { From a43ae0d098da2b0a5d77b6ecbc578f6d14b22d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 10:38:41 +0200 Subject: [PATCH 38/75] pgo: replace meson-pgo.sh with several script primitives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All scripts are in the ‘pgo’ directory: * options: command line options for generate-alt-random-writes.py, sourced by other scripts. * pgo.sh: top-level script, generates a meson build directory, selects a PGO method, generates the profiling data, re-configures the meson build directory and does the final build. This script is intended to be used by end-users, and shows _how_ to integrate the script primitives. Build servers will most likely *not* want to use this script as-is. * partial.sh: generates alt-random-write data and runs “foot{,client} --version”, and then feeds the alt-random data to the PGO helper binary. Does not require a running Wayland session. Touches $blddir/pgo-ok on success. * full-inner.sh: runs “footclient --version”, and then a complex “foot” command that first generates alt-random-write data, and then “cat’s” it. Requires a running Wayland session, *but*, this script is usually not called directly (see below). Touches $blddir/pgo-ok onsucces.. * full-current-session.sh: runs full-inner.sh. That is, it runs foot in the currently running Wayland session. Note that this will pop up a foot window. * full-headless-sway.sh: generates a custom Sway configuration that exec’s foot-headless-sway-inner.sh (see below), and then executes a headless Sway. In other words, this script does a *full* PGO build, but *without* requiring a running Wayland session. Requires Sway >= 1.6.2. * full-headless-sway.sh: runs full-inner.sh + “swaymsg exit”. To do a custom PGO build, without using pgo.sh, you’d need to: CFLAGS=”$CFLAGS -O3” meson --buildtype=release -Db_lto=true meson configure -Db_pgo=generate ninja ninja test (only needed if tllist+fcft are built as subprojects) Run *one* of: - partial.sh - full-current-session.sh - full-headless-sway.sh meson configure -D b_pgo=use ninja --- meson-pgo.sh | 154 -------------------------------- pgo/full-current-session.sh | 8 ++ pgo/full-headless-sway-inner.sh | 9 ++ pgo/full-headless-sway.sh | 24 +++++ pgo/full-inner.sh | 31 +++++++ pgo/options | 1 + pgo/partial.sh | 28 ++++++ pgo/pgo.sh | 101 +++++++++++++++++++++ 8 files changed, 202 insertions(+), 154 deletions(-) delete mode 100755 meson-pgo.sh create mode 100755 pgo/full-current-session.sh create mode 100755 pgo/full-headless-sway-inner.sh create mode 100755 pgo/full-headless-sway.sh create mode 100755 pgo/full-inner.sh create mode 100644 pgo/options create mode 100755 pgo/partial.sh create mode 100755 pgo/pgo.sh diff --git a/meson-pgo.sh b/meson-pgo.sh deleted file mode 100755 index cbd355f1..00000000 --- a/meson-pgo.sh +++ /dev/null @@ -1,154 +0,0 @@ -#!/bin/sh - -# Requirements: -# -# * all: build directory must not exist; it is created by the script -# * all: LC_CTYPE must be set to an UTF-8 locale -# * full*: must have at least one font installed -# * full-headless-sway: Sway 1.6.2 must be installed - - -set -e - -usage_and_die() { - echo "Usage: ${0} none|partial|full|full-headless-sway|[auto] " - exit 1 -} - -[ ${#} -ge 3 ] || usage_and_die - -mode=${1} -source_dir=$(realpath "${2}") -build_dir=$(realpath "${3}") -shift 3 - -# meson will complain if source dir is invalid - -if [ -d "${build_dir}" ]; then - echo "${build_dir}: build directory already exists" - exit 1 -fi - -compiler=other -do_pgo=no - -CFLAGS="${CFLAGS} -O3" - -case $(${CC-cc} --version) in - *GCC*) - compiler=gcc - do_pgo=yes - ;; - - *clang*) - compiler=clang - - if command -v llvm-profdata > /dev/null; then - do_pgo=yes - CFLAGS="${CFLAGS} -Wno-ignored-optimization-argument" - fi - ;; -esac - -case ${mode} in - partial|full|full-headless-sway) - ;; - - none) - do_pgo=no - ;; - - auto) - if command -v sway > /dev/null; then - mode=full-headless-sway - elif [ -n "${WAYLAND_DISPLAY+x}" ]; then - mode=full - else - mode=partial - fi - ;; - - *) - usage_and_die - ;; -esac - -# echo "source: ${source_dir}" -# echo "build: ${build_dir}" -# echo "compiler: ${compiler}" -# echo "mode: ${mode}" - -export CFLAGS -meson "${@}" "${build_dir}" - -if [ ${do_pgo} = yes ]; then - find . -name "*.gcda" -delete - meson configure "${build_dir}" -Db_pgo=generate - ninja -C "${build_dir}" - - # If fcft/tllist are subprojects, we need to ensure their tests - # have been executed, or we’ll get “profile count data file not - # found” errors. - ninja -C "${build_dir}" test - - script_options="--scroll --scroll-region --colors-regular --colors-bright --colors-256 --colors-rgb --attr-bold --attr-italic --attr-underline --sixel" - - tmp_file=$(mktemp) - pwd=$(pwd) - - cleanup() { - rm -f "${tmp_file}" - cd "${pwd}" - } - trap cleanup EXIT INT HUP TERM - - cd "${build_dir}" - case ${mode} in - full) - ./footclient --version - ./foot \ - --config=/dev/null \ - --term=xterm \ - sh -c "${source_dir}/scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}" - ;; - - full-headless-sway) - ./footclient --version - - runtime_dir=$(mktemp -d) - sway_conf=$(mktemp) - - echo "exec ${build_dir}/foot -o tweak.render-timer=log --config=/dev/null --term=xterm sh -c \"${source_dir}/scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}\" && swaymsg exit" > "${sway_conf}" - export XDG_RUNTIME_DIR=${runtime_dir} - export WLR_BACKENDS=headless - - sway -c "${sway_conf}" - - rm "${sway_conf}" - rm -rf "${runtime_dir}" - ;; - - partial) - ./footclient --version - ./foot --version - "${source_dir}"/scripts/generate-alt-random-writes.py \ - --rows=67 \ - --cols=135 \ - ${script_options} \ - "${tmp_file}" - ./pgo "${tmp_file}" - ;; - esac - - cd "${pwd}" - rm "${tmp_file}" - - if [ ${compiler} = clang ]; then - llvm-profdata \ - merge \ - "${build_dir}"/default_*.profraw \ - --output="${build_dir}"/default.profdata - fi - - meson configure "${build_dir}" -Db_pgo=use -fi diff --git a/pgo/full-current-session.sh b/pgo/full-current-session.sh new file mode 100755 index 00000000..3f62a6b5 --- /dev/null +++ b/pgo/full-current-session.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +set -eu + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +"${srcdir}"/pgo/full-inner.sh "${srcdir}" "${blddir}" diff --git a/pgo/full-headless-sway-inner.sh b/pgo/full-headless-sway-inner.sh new file mode 100755 index 00000000..5bf20390 --- /dev/null +++ b/pgo/full-headless-sway-inner.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +set -u + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +"${srcdir}"/pgo/full-inner.sh "${srcdir}" "${blddir}" +swaymsg exit diff --git a/pgo/full-headless-sway.sh b/pgo/full-headless-sway.sh new file mode 100755 index 00000000..ef34c560 --- /dev/null +++ b/pgo/full-headless-sway.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +set -eu + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +runtime_dir=$(mktemp -d) +sway_conf=$(mktemp) + +cleanup() { + rm -f "${sway_conf}" + rm -rf "${runtime_dir}" +} +trap cleanup EXIT INT HUP TERM + +# Generate a custom config that executes our generate-pgo-data script +> "${sway_conf}" echo "exec '${srcdir}'/pgo/full-headless-sway-inner.sh '${srcdir}' '${blddir}'" + +# Run Sway. full-headless-sway-inner.sh ends with a ‘swaymsg exit’ +XDG_RUNTIME_DIR="${runtime_dir}" WLR_BACKENDS=headless sway -c "${sway_conf}" + +# Sway’s exit code doesn’t reflect our script’s exit code +[ -f "${blddir}"/pgo-ok ] || exit 1 diff --git a/pgo/full-inner.sh b/pgo/full-inner.sh new file mode 100755 index 00000000..14a3528c --- /dev/null +++ b/pgo/full-inner.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +set -eu + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +. "${srcdir}"/pgo/options + +pgo_data=$(mktemp) +trap "rm -f ${pgo_data}" EXIT INT HUP TERM + +rm -f "${blddir}"/pgo-ok + +# To ensure profiling data is generated in the build directory +cd "${blddir}" + +LC_CTYPE=en_US.UTF-8 "${blddir}"/footclient --version +LC_CTYPE=en_US.UTF-8 "${blddir}"/foot \ + -o tweak.render-timer=log \ + --config=/dev/null \ + --term=xterm \ + sh -c " + set -eu + + '${srcdir}/scripts/generate-alt-random-writes.py' \ + ${script_options} \"${pgo_data}\" + + cat \"${pgo_data}\" + " +touch "${blddir}"/pgo-ok diff --git a/pgo/options b/pgo/options new file mode 100644 index 00000000..3fb821e9 --- /dev/null +++ b/pgo/options @@ -0,0 +1 @@ +script_options="--scroll --scroll-region --colors-regular --colors-bright --colors-256 --colors-rgb --attr-bold --attr-italic --attr-underline --sixel" diff --git a/pgo/partial.sh b/pgo/partial.sh new file mode 100755 index 00000000..9cb8bd59 --- /dev/null +++ b/pgo/partial.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -eu + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +. "${srcdir}"/pgo/options + +pgo_data=$(mktemp) +trap "rm -f ${pgo_data}" EXIT INT HUP TERM + +rm -f "${blddir}"/pgo-ok + +"${srcdir}"/scripts/generate-alt-random-writes.py \ + --rows=67 \ + --cols=135 \ + ${script_options} \ + "${pgo_data}" + +# To ensure profiling data is generated in the build directory +cd "${blddir}" + +"${blddir}"/footclient --version +"${blddir}"/foot --version +"${blddir}"/pgo "${pgo_data}" + +touch "${blddir}"/pgo-ok diff --git a/pgo/pgo.sh b/pgo/pgo.sh new file mode 100755 index 00000000..fbd7fbe2 --- /dev/null +++ b/pgo/pgo.sh @@ -0,0 +1,101 @@ +#!/bin/sh + +set -eu + +usage_and_die() { + echo "Usage: ${0} none|partial|full-current-session|full-headless-sway|[auto] [meson options]" + exit 1 +} + +[ ${#} -ge 3 ] || usage_and_die + +mode=${1} +srcdir=$(realpath "${2}") +blddir=$(realpath "${3}") +shift 3 + +if [ -e "${blddir}" ]; then + echo "error: ${blddir}: build directory already exists" + exit 1 +fi + +compiler=other +do_pgo=no + +CFLAGS="${CFLAGS-} -O3" + +case $(${CC-cc} --version) in + *GCC*) + compiler=gcc + do_pgo=yes + ;; + + *clang*) + compiler=clang + + if command -v llvm-profdata > /dev/null; then + do_pgo=yes + CFLAGS="${CFLAGS} -Wno-ignored-optimization-argument" + fi + ;; +esac + +case ${mode} in + partial|full-current-session|full-headless-sway) + ;; + + none) + do_pgo=no + ;; + + auto) + # TODO: once Sway 1.6.2 has been released, prefer + # full-headless-sway + + if [ -n "${WAYLAND_DISPLAY+x}" ]; then + mode=full-current-session + elif command -v sway > /dev/null; then + mode=full-headless-sway + else + mode=partial + fi + ;; + + *) + usage_and_die + ;; +esac + +# echo "source: ${srcdir}" +# echo "build: ${blddir}" +# echo "compiler: ${compiler}" +# echo "mode: ${mode}" +# echo "CFLAGS: ${CFLAGS}" + +export CFLAGS +meson "${@}" "${blddir}" "${srcdir}" --buildtype=release -Db_lto=true + +if [ ${do_pgo} = yes ]; then + find . -name "*.gcda" -delete + meson configure "${blddir}" -Db_pgo=generate + ninja -C "${blddir}" + + # If fcft/tllist are subprojects, we need to ensure their tests + # have been executed, or we’ll get “profile count data file not + # found” errors. + ninja -C "${blddir}" test + + # Run mode-dependent script to generate profiling data + "${srcdir}"/pgo/${mode}.sh "${srcdir}" "${blddir}" + + if [ ${compiler} = clang ]; then + llvm-profdata \ + merge \ + "${blddir}"/default_*.profraw \ + --output="${blddir}"/default.profdata + fi + + meson configure "${blddir}" -Db_pgo=use +fi + +ninja -C "${blddir}" From aeb510182b32c342a9175045aed37f5e1023384e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 10:55:57 +0200 Subject: [PATCH 39/75] pgo: full-inner: remove -o tweak.render-timer=log --- pgo/full-inner.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/pgo/full-inner.sh b/pgo/full-inner.sh index 14a3528c..4e8265fd 100755 --- a/pgo/full-inner.sh +++ b/pgo/full-inner.sh @@ -17,7 +17,6 @@ cd "${blddir}" LC_CTYPE=en_US.UTF-8 "${blddir}"/footclient --version LC_CTYPE=en_US.UTF-8 "${blddir}"/foot \ - -o tweak.render-timer=log \ --config=/dev/null \ --term=xterm \ sh -c " From 3a34b94f2e8dadf845889bec2fa8cea5f89665d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 11:04:27 +0200 Subject: [PATCH 40/75] =?UTF-8?q?pgo:=20full-inner:=20quote=20path=20in=20?= =?UTF-8?q?trap=E2=80=99s=20=E2=80=9Crm=E2=80=9D=20command?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pgo/full-inner.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgo/full-inner.sh b/pgo/full-inner.sh index 4e8265fd..2125885d 100755 --- a/pgo/full-inner.sh +++ b/pgo/full-inner.sh @@ -8,7 +8,7 @@ blddir=$(realpath "${2}") . "${srcdir}"/pgo/options pgo_data=$(mktemp) -trap "rm -f ${pgo_data}" EXIT INT HUP TERM +trap "rm -f '${pgo_data}'" EXIT INT HUP TERM rm -f "${blddir}"/pgo-ok From 801bce335be8c3cf6d3793a50c2dfcf2ada29f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 11:04:54 +0200 Subject: [PATCH 41/75] pgo: full-headless-cage: new headless variant Appears to work, but cage spams a lot of 00:00:08.026 [types/wlr_output.c:720] Basic output test failed for HEADLESS-1 00:00:08.036 [types/wlr_output.c:720] Basic output test failed for HEADLESS-1 --- pgo/full-headless-cage.sh | 15 +++++++++++++++ pgo/pgo.sh | 6 ++++-- 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100755 pgo/full-headless-cage.sh diff --git a/pgo/full-headless-cage.sh b/pgo/full-headless-cage.sh new file mode 100755 index 00000000..c85efd81 --- /dev/null +++ b/pgo/full-headless-cage.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -eu + +srcdir=$(realpath "${1}") +blddir=$(realpath "${2}") + +runtime_dir=$(mktemp -d) +trap "rm -rf '${runtime_dir}'" EXIT INT HUP TERM + +# Run Sway. full-headless-sway-inner.sh ends with a ‘swaymsg exit’ +XDG_RUNTIME_DIR="${runtime_dir}" WLR_BACKENDS=headless cage "${srcdir}"/pgo/full-inner.sh "${srcdir}" "${blddir}" + +# Cage’s exit code doesn’t reflect our script’s exit code +[ -f "${blddir}"/pgo-ok ] || exit 1 diff --git a/pgo/pgo.sh b/pgo/pgo.sh index fbd7fbe2..56db6684 100755 --- a/pgo/pgo.sh +++ b/pgo/pgo.sh @@ -3,7 +3,7 @@ set -eu usage_and_die() { - echo "Usage: ${0} none|partial|full-current-session|full-headless-sway|[auto] [meson options]" + echo "Usage: ${0} none|partial|full-current-session|full-headless-sway|full-headless-cage|[auto] [meson options]" exit 1 } @@ -41,7 +41,7 @@ case $(${CC-cc} --version) in esac case ${mode} in - partial|full-current-session|full-headless-sway) + partial|full-current-session|full-headless-sway|full-headless-cage) ;; none) @@ -56,6 +56,8 @@ case ${mode} in mode=full-current-session elif command -v sway > /dev/null; then mode=full-headless-sway + elif command -v cage > /dev/null; then + mode=full-headless-cage else mode=partial fi From f25055f101d031244a88dc37cef20f2c46d060f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 11:06:34 +0200 Subject: [PATCH 42/75] pgo: full-headless-cage: remove references to Sway --- pgo/full-headless-cage.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/pgo/full-headless-cage.sh b/pgo/full-headless-cage.sh index c85efd81..a4fd5699 100755 --- a/pgo/full-headless-cage.sh +++ b/pgo/full-headless-cage.sh @@ -8,7 +8,6 @@ blddir=$(realpath "${2}") runtime_dir=$(mktemp -d) trap "rm -rf '${runtime_dir}'" EXIT INT HUP TERM -# Run Sway. full-headless-sway-inner.sh ends with a ‘swaymsg exit’ XDG_RUNTIME_DIR="${runtime_dir}" WLR_BACKENDS=headless cage "${srcdir}"/pgo/full-inner.sh "${srcdir}" "${blddir}" # Cage’s exit code doesn’t reflect our script’s exit code From 99c4e51e199f87c0b4d97d43a2d34af62c6b7ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 11:14:09 +0200 Subject: [PATCH 43/75] pgo: add set -x to all PGO scripts Without this, debugging error reports from users is going to be _very_ difficult... --- pgo/full-current-session.sh | 2 +- pgo/full-headless-cage.sh | 2 +- pgo/full-headless-sway-inner.sh | 2 +- pgo/full-headless-sway.sh | 2 +- pgo/full-inner.sh | 4 ++-- pgo/partial.sh | 2 +- pgo/pgo.sh | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pgo/full-current-session.sh b/pgo/full-current-session.sh index 3f62a6b5..363ee793 100755 --- a/pgo/full-current-session.sh +++ b/pgo/full-current-session.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -eu +set -eux srcdir=$(realpath "${1}") blddir=$(realpath "${2}") diff --git a/pgo/full-headless-cage.sh b/pgo/full-headless-cage.sh index a4fd5699..ad5d0264 100755 --- a/pgo/full-headless-cage.sh +++ b/pgo/full-headless-cage.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -eu +set -eux srcdir=$(realpath "${1}") blddir=$(realpath "${2}") diff --git a/pgo/full-headless-sway-inner.sh b/pgo/full-headless-sway-inner.sh index 5bf20390..dd612a2a 100755 --- a/pgo/full-headless-sway-inner.sh +++ b/pgo/full-headless-sway-inner.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -u +set -ux srcdir=$(realpath "${1}") blddir=$(realpath "${2}") diff --git a/pgo/full-headless-sway.sh b/pgo/full-headless-sway.sh index ef34c560..bbf038b9 100755 --- a/pgo/full-headless-sway.sh +++ b/pgo/full-headless-sway.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -eu +set -eux srcdir=$(realpath "${1}") blddir=$(realpath "${2}") diff --git a/pgo/full-inner.sh b/pgo/full-inner.sh index 2125885d..37cb2fb6 100755 --- a/pgo/full-inner.sh +++ b/pgo/full-inner.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -eu +set -eux srcdir=$(realpath "${1}") blddir=$(realpath "${2}") @@ -20,7 +20,7 @@ LC_CTYPE=en_US.UTF-8 "${blddir}"/foot \ --config=/dev/null \ --term=xterm \ sh -c " - set -eu + set -eux '${srcdir}/scripts/generate-alt-random-writes.py' \ ${script_options} \"${pgo_data}\" diff --git a/pgo/partial.sh b/pgo/partial.sh index 9cb8bd59..c16de324 100755 --- a/pgo/partial.sh +++ b/pgo/partial.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -eu +set -eux srcdir=$(realpath "${1}") blddir=$(realpath "${2}") diff --git a/pgo/pgo.sh b/pgo/pgo.sh index 56db6684..7d842036 100755 --- a/pgo/pgo.sh +++ b/pgo/pgo.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -eu +set -eux usage_and_die() { echo "Usage: ${0} none|partial|full-current-session|full-headless-sway|full-headless-cage|[auto] [meson options]" From cf7c5050f64709bea48e3175a3218f7b74d19f36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 18:17:18 +0200 Subject: [PATCH 44/75] =?UTF-8?q?pgo:=20pgo.sh:=20auto:=20don=E2=80=99t=20?= =?UTF-8?q?use=20full-headless-sway?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This requires Sway >= 1.6.2, which hasn’t been released yet. Thus, don’t auto-use it. Users can still request it explicitly. --- pgo/pgo.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pgo/pgo.sh b/pgo/pgo.sh index 7d842036..d608930d 100755 --- a/pgo/pgo.sh +++ b/pgo/pgo.sh @@ -54,8 +54,8 @@ case ${mode} in if [ -n "${WAYLAND_DISPLAY+x}" ]; then mode=full-current-session - elif command -v sway > /dev/null; then - mode=full-headless-sway + # elif command -v sway > /dev/null; then # Requires 1.6.2 + # mode=full-headless-sway elif command -v cage > /dev/null; then mode=full-headless-cage else From 579e1f80b4d4b3955fe783663ce9dead08cff15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 18:18:58 +0200 Subject: [PATCH 45/75] pkgbuild: build using pgo/pgo.sh --- PKGBUILD | 78 +++----------------------------------------------------- 1 file changed, 4 insertions(+), 74 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index b05d7078..68ecfccf 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,3 +1,5 @@ +PGO=auto # none|partial|full-current-session|full-headless-sway|full-headless-cage + pkgname=('foot-git' 'foot-terminfo-git') pkgver=1.9.0 pkgrel=1 @@ -14,80 +16,8 @@ pkgver() { } build() { - local compiler=other - local do_pgo=no - - # makepkg uses -O2 by default, but we *really* want -O3 - CFLAGS+=" -O3" - - # Figure out which compiler we're using, and whether or not to do PGO - case $(${CC-cc} --version) in - *GCC*) - compiler=gcc - do_pgo=yes - ;; - - *clang*) - compiler=clang - - # We need llvm to be able to manage the profiling data - if command -v llvm-profdata > /dev/null; then - do_pgo=yes - - # Meson adds -fprofile-correction, which Clang doesn't - # understand - CFLAGS+=" -Wno-ignored-optimization-argument" - fi - ;; - esac - - meson \ - --prefix=/usr \ - --buildtype=release \ - --wrap-mode=nofallback \ - -Db_lto=true .. - - if [[ ${do_pgo} == yes ]]; then - find -name "*.gcda" -delete - meson configure -Db_pgo=generate - ninja - - # If fcft/tllist are subprojects, we need to ensure their tests - # have been executed, or we’ll get “profile count data file not - # found” errors. - ninja test - - local script_options="--scroll --scroll-region --colors-regular --colors-bright --colors-256 --colors-rgb --attr-bold --attr-italic --attr-underline --sixel" - - tmp_file=$(mktemp) - - if [[ -v WAYLAND_DISPLAY ]]; then - ./footclient --version - ./foot \ - --config /dev/null \ - --term=xterm \ - sh -c "../scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}" - else - ./footclient --version - ./foot --version - ../scripts/generate-alt-random-writes.py \ - --rows=67 \ - --cols=135 \ - ${script_options} \ - ${tmp_file} - ./pgo ${tmp_file} ${tmp_file} ${tmp_file} - fi - - rm "${tmp_file}" - - if [[ ${compiler} == clang ]]; then - llvm-profdata merge default_*profraw --output=default.profdata - fi - - meson configure -Db_pgo=use - fi - - ninja + rm -rf * + ../pgo/pgo.sh ${PGO} .. . } check() { From e907ec209cf9b3e2f7ef06e0ffd71b9735f1e702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 18:29:20 +0200 Subject: [PATCH 46/75] pgo: pgo.sh: allow a pre-existing build directory --- pgo/pgo.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pgo/pgo.sh b/pgo/pgo.sh index d608930d..39da2a30 100755 --- a/pgo/pgo.sh +++ b/pgo/pgo.sh @@ -14,10 +14,10 @@ srcdir=$(realpath "${2}") blddir=$(realpath "${3}") shift 3 -if [ -e "${blddir}" ]; then - echo "error: ${blddir}: build directory already exists" - exit 1 -fi +# if [ -e "${blddir}" ]; then +# echo "error: ${blddir}: build directory already exists" +# exit 1 +# fi compiler=other do_pgo=no From 6d67c2b4ab3194df40d383841fb95bcc2f033fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 18:29:55 +0200 Subject: [PATCH 47/75] =?UTF-8?q?pgo:=20pgo.sh:=20run=20=E2=80=98find?= =?UTF-8?q?=E2=80=99=20in=20the=20build-directory,=20not=20cwd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pgo/pgo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgo/pgo.sh b/pgo/pgo.sh index 39da2a30..af84e7ce 100755 --- a/pgo/pgo.sh +++ b/pgo/pgo.sh @@ -78,7 +78,7 @@ export CFLAGS meson "${@}" "${blddir}" "${srcdir}" --buildtype=release -Db_lto=true if [ ${do_pgo} = yes ]; then - find . -name "*.gcda" -delete + find "${blddir}" -name "*.gcda" -delete meson configure "${blddir}" -Db_pgo=generate ninja -C "${blddir}" From a941253ba74d5ca48cbb1401b4ced8d0fa2b6a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 18:41:49 +0200 Subject: [PATCH 48/75] pgo: pgo.sh: add meson bld/src dir arguments last --- pgo/pgo.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgo/pgo.sh b/pgo/pgo.sh index af84e7ce..fd7d05a3 100755 --- a/pgo/pgo.sh +++ b/pgo/pgo.sh @@ -75,7 +75,7 @@ esac # echo "CFLAGS: ${CFLAGS}" export CFLAGS -meson "${@}" "${blddir}" "${srcdir}" --buildtype=release -Db_lto=true +meson setup --buildtype=release -Db_lto=true "${@}" "${blddir}" "${srcdir}" if [ ${do_pgo} = yes ]; then find "${blddir}" -name "*.gcda" -delete From 2181af05521dbbf7a02fae632006109dc7d5b5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 18:42:04 +0200 Subject: [PATCH 49/75] pkgbuild: the pgo script erases the old profdata files --- PKGBUILD | 1 - 1 file changed, 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index 68ecfccf..a4e873be 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -16,7 +16,6 @@ pkgver() { } build() { - rm -rf * ../pgo/pgo.sh ${PGO} .. . } From 5493267bf7ca08527e563717b4bf7812388f79f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 18:50:05 +0200 Subject: [PATCH 50/75] pgo: pgo.sh: remove both gcc and clang generated profile stats --- pgo/pgo.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pgo/pgo.sh b/pgo/pgo.sh index fd7d05a3..9db50e08 100755 --- a/pgo/pgo.sh +++ b/pgo/pgo.sh @@ -78,7 +78,14 @@ export CFLAGS meson setup --buildtype=release -Db_lto=true "${@}" "${blddir}" "${srcdir}" if [ ${do_pgo} = yes ]; then - find "${blddir}" -name "*.gcda" -delete + find "${blddir}" \ + '(' \ + -name "*.gcda" -o \ + -name "*.profraw" -o \ + -name default.profdata \ + ')' \ + -delete + meson configure "${blddir}" -Db_pgo=generate ninja -C "${blddir}" From e77c3b3e4f82cf02a03573369900771c12bd0862 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 18:52:36 +0200 Subject: [PATCH 51/75] =?UTF-8?q?pkgbuild:=20add=20=E2=80=98auto=E2=80=99?= =?UTF-8?q?=20to=20the=20list=20of=20possible=20PGO=20values?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index a4e873be..340f4687 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,4 +1,4 @@ -PGO=auto # none|partial|full-current-session|full-headless-sway|full-headless-cage +PGO=auto # auto|none|partial|full-current-session|full-headless-sway|full-headless-cage pkgname=('foot-git' 'foot-terminfo-git') pkgver=1.9.0 From 7e53de263b4ba15bf7e4b6e91f08dd3d3c5a9d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sat, 4 Sep 2021 18:59:32 +0200 Subject: [PATCH 52/75] pkgbuild: add back --prefix --wrap-mode=nofallback --- PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index 340f4687..4cff4a6c 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -16,7 +16,7 @@ pkgver() { } build() { - ../pgo/pgo.sh ${PGO} .. . + ../pgo/pgo.sh ${PGO} .. . --prefix=/usr --wrap-mode=nofallback } check() { From b6df5f6456f4e4a897cb30f814c7d85254efbc8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 12 Sep 2021 16:11:42 +0200 Subject: [PATCH 53/75] pgo.sh: set -x *after* verifying input --- pgo/pgo.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pgo/pgo.sh b/pgo/pgo.sh index 9db50e08..57a016cc 100755 --- a/pgo/pgo.sh +++ b/pgo/pgo.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -eux +set -eu usage_and_die() { echo "Usage: ${0} none|partial|full-current-session|full-headless-sway|full-headless-cage|[auto] [meson options]" @@ -68,6 +68,8 @@ case ${mode} in ;; esac +set -x + # echo "source: ${srcdir}" # echo "build: ${blddir}" # echo "compiler: ${compiler}" From aa0f2a71cce125ae30c2baa05e7a1802d30fe1f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 12 Sep 2021 16:57:07 +0200 Subject: [PATCH 54/75] install.md: document pgo/*.sh scripts --- INSTALL.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/INSTALL.md b/INSTALL.md index 9543948e..40b128e7 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -274,6 +274,32 @@ slower!) binary. #### Performance optimized, PGO +There are a lot more steps involved in a PGO build, and for this +reason there are a number of helper scripts available. + +`pgo/pgo.sh` is a standalone script that pieces together the other +scripts in the `pgo` directory to do a complete PGO build. This script +is intended to be used when doing manual builds. + +Example: + +```sh +cd foot +./pgo/pgo.sh auto . /tmp/foot-pgo-build-output +``` + +(run `./pgo/pgo.sh` to get help on usage) + +It supports a couple of different PGO builds; partial (covered in +detail below), full (also covered in detail below), and (full) +headless builds using Sway or cage. + +Packagers may want to use it as inspiration, but may choose to support +only a specific build type; e.g. full/headless with Sway. + +To do a manual PGO build, instead of using the script(s) mentioned +above, detailed instructions follows: + First, configure the build directory: ```sh From 32900207f989d2a1e5e54d75e8c0729cd423e176 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 12 Sep 2021 16:59:23 +0200 Subject: [PATCH 55/75] changelog: pgo build scripts --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57984c49..fef6c13f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,9 @@ * Warn when it appears the primary font is not monospaced. Can be disabled by setting `[tweak].font-monospace-warn=no` (https://codeberg.org/dnkl/foot/issues/704). +* PGO build scripts, in the `pgo` directory. See INSTALL.md - + _Performance optimized, PGO_, for details + (https://codeberg.org/dnkl/foot/issues/701). ### Changed From b4c759e2de429c20bf07c9e16162d154152014d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 2 Sep 2021 14:55:26 +0200 Subject: [PATCH 56/75] box-drawing: add braille characters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render braille ourselves, instead of using font glyphs. Decoding a braille character is easy enough; there are 256 codepoints, represented by an 8-bit integer (i.e. subtract the Unicode codepoint offset, 0x2800, and you’re left with an integer in the range 0-255). Each bit corresponds to a dot. The first 6 bits represent the upper 6 dots, while the two last bits represent the fourth (and last) row of dots. The hard part is sizing the dots and the spacing between them. The aim is to have the spacing between the dots be the same size as the dots themselves, and to have the margins on each side be half the size of the dots. In a perfectly sized cell, this means two braille characters next to each other will be evenly spaced. This is however almost never the case. The layout logic currently: * Set dot size to either the width / 4, or height / 8, depending on which one is smallest. * Horizontal spacing is initialized to the width / 4 * Vertical spacing is initialized to the height / 8 * Horizontal margins are initialized to the horizontal spacing / 2 * Vertical margins are initialized to the vertical spacing / 2. Next, we calculate the number of “remaining” pixels. That is, if we add the left margin, two dots and the spacing between, how many pixels are left on the horizontal axis? These pixels are distributed in the following order (we “stop” as soon as we run out of pixels): * If the dot size is 0 (happens for very small font sizes), increase it to 1. * If the margins are 0, increase them to 1. * If we have enough pixels (need at 2 horizontal and 4 vertical), increase the dot size. * Increase spacing. * Increase margins. Closes #702 --- CHANGELOG.md | 2 ++ box-drawing.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++ render.c | 15 ++++++--- terminal.h | 7 ++-- 4 files changed, 105 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fef6c13f..0b0c2011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ * PGO build scripts, in the `pgo` directory. See INSTALL.md - _Performance optimized, PGO_, for details (https://codeberg.org/dnkl/foot/issues/701). +* Braille characters (U+2800 - U+28FF) are now rendered by foot + itself (https://codeberg.org/dnkl/foot/issues/702). ### Changed diff --git a/box-drawing.c b/box-drawing.c index f246fd01..a1940392 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -1953,6 +1953,92 @@ draw_quadrant(struct buf *buf, wchar_t wc) quad_lower_right(buf); } +static void +draw_braille(struct buf *buf, wchar_t wc) +{ + int w = min(buf->width / 4, buf->height / 8); + int x_spacing = buf->width / 4; + int y_spacing = buf->height / 8; + int x_margin = x_spacing / 2; + int y_margin = y_spacing / 2; + + int x_pix_left = buf->width - 2 * x_margin - x_spacing - 2 * w; + int y_pix_left = buf->height - 2 * y_margin - 3 * y_spacing - 4 * w; + + LOG_DBG( + "braille: before adjusting: " + "cell: %dx%d, margin=%dx%d, spacing=%dx%d, width=%d, left=%dx%d", + buf->width, buf->height, x_margin, y_margin, x_spacing, y_spacing, + w, x_pix_left, y_pix_left); + + /* First, try hard to ensure the DOT width is non-zero */ + if (x_pix_left >= 2 && y_pix_left >= 4 && w == 0) { + w++; + x_pix_left -= 2; + y_pix_left -= 4; + } + + /* Second, prefer a non-zero margin */ + if (x_pix_left >= 2 && x_margin == 0) { x_margin = 1; x_pix_left -= 2; } + if (y_pix_left >= 2 && y_margin == 0) { y_margin = 1; y_pix_left -= 2; } + + if (x_pix_left >= 2 && y_pix_left >= 4) { + w++; + x_pix_left -= 2; + y_pix_left -= 4; + } + + if (x_pix_left >= 1) { x_spacing++; x_pix_left--; } + if (y_pix_left >= 3) { y_spacing++; y_pix_left -= 3; } + + if (x_pix_left >= 2) { x_margin++; x_pix_left -= 2; } + if (y_pix_left >= 2) { y_margin++; y_pix_left -= 2; } + + LOG_DBG( + "braille: after adjusting: " + "cell: %dx%d, margin=%dx%d, spacing=%dx%d, width=%d, left=%dx%d", + buf->width, buf->height, x_margin, y_margin, x_spacing, y_spacing, + w, x_pix_left, y_pix_left); + + xassert(x_pix_left <= 1 || y_pix_left <= 1); + xassert(2 * x_margin + 2 * w + x_spacing <= buf->width); + xassert(2 * y_margin + 4 * w + 3 * y_spacing <= buf->height); + + int x[2], y[4]; + x[0] = x_margin; + x[1] = x_margin + w + x_spacing; + y[0] = y_margin; + y[1] = y[0] + w + y_spacing; + y[2] = y[1] + w + y_spacing; + y[3] = y[2] + w + y_spacing; + + assert(wc >= 0x2800); + assert(wc <= 0x28ff); + uint8_t sym = wc - 0x2800; + + /* Left side */ + if (sym & 1) + rect(x[0], y[0], x[0] + w, y[0] + w); + if (sym & 2) + rect(x[0], y[1], x[0] + w, y[1] + w); + if (sym & 4) + rect(x[0], y[2], x[0] + w, y[2] + w); + + /* Right side */ + if (sym & 8) + rect(x[1], y[0], x[1] + w, y[0] + w); + if (sym & 16) + rect(x[1], y[1], x[1] + w, y[1] + w); + if (sym & 32) + rect(x[1], y[2], x[1] + w, y[2] + w); + + /* 8-dot patterns */ + if (sym & 64) + rect(x[0], y[3], x[0] + w, y[3] + w); + if (sym & 128) + rect(x[1], y[3], x[1] + w, y[3] + w); +} + static void sextant_upper_left(struct buf *buf) { @@ -2653,6 +2739,8 @@ draw_glyph(struct buf *buf, wchar_t wc) case 0x2595: draw_right_one_eighth_block(buf); break; case 0x2596 ... 0x259f: draw_quadrant(buf, wc); break; + case 0x2800 ... 0x28ff: draw_braille(buf, wc); break; + case 0x1fb00 ... 0x1fb3b: draw_sextant(buf, wc); break; case 0x1fb3c ... 0x1fb40: diff --git a/render.c b/render.c index cfb6f0bb..1cbff10d 100644 --- a/render.c +++ b/render.c @@ -515,6 +515,9 @@ render_cell(struct terminal *term, pixman_image_t *pix, /* Classic box drawings */ (base >= 0x2500 && base <= 0x259f) || + /* Braille */ + (base >= 0x2800 && base <= 0x28ff) || + /* * Unicode 13 "Symbols for Legacy Computing" * sub-ranges below. @@ -531,9 +534,12 @@ render_cell(struct terminal *term, pixman_image_t *pix, /* Box drawing characters */ size_t idx = base >= 0x1fb00 ? (base >= 0x1fb9a - ? base - 0x1fb9a + 300 - : base - 0x1fb00 + 160) - : base - 0x2500; + ? base - 0x1fb9a + 556 + : base - 0x1fb00 + 416) + : (base >= 0x2800 + ? base - 0x2800 + 160 + : base - 0x2500); + xassert(idx < ALEN(term->box_drawing)); if (likely(term->box_drawing[idx] != NULL)) @@ -541,7 +547,8 @@ render_cell(struct terminal *term, pixman_image_t *pix, else { mtx_lock(&term->render.workers.lock); - /* Parallel thread may have instantiated it while we took the lock */ + /* Other thread may have instantiated it while we + * aquired the lock */ if (term->box_drawing[idx] == NULL) term->box_drawing[idx] = box_drawing(term, base); mtx_unlock(&term->render.workers.lock); diff --git a/terminal.h b/terminal.h index 620678b1..bfb327e3 100644 --- a/terminal.h +++ b/terminal.h @@ -335,10 +335,11 @@ struct terminal { /* * 0-159: U+02500+0259F - * 160-299: U+1FB00-1FB8B - * 300-301: U+1FB9A-1FB9B + * 160-415: U+02800-028FF + * 416-555: U+1FB00-1FB8B + * 556-557: U+1FB9A-1FB9B */ - struct fcft_glyph *box_drawing[302]; + struct fcft_glyph *box_drawing[558]; bool is_sending_paste_data; ptmx_buffer_list_t ptmx_buffers; From 896825f50c685507797d66c8f0428c3fe8c2d2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 3 Sep 2021 21:38:08 +0200 Subject: [PATCH 57/75] render: codespell: aquire -> acquire --- render.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/render.c b/render.c index 1cbff10d..684c86e4 100644 --- a/render.c +++ b/render.c @@ -548,7 +548,7 @@ render_cell(struct terminal *term, pixman_image_t *pix, mtx_lock(&term->render.workers.lock); /* Other thread may have instantiated it while we - * aquired the lock */ + * acquired the lock */ if (term->box_drawing[idx] == NULL) term->box_drawing[idx] = box_drawing(term, base); mtx_unlock(&term->render.workers.lock); From ac2091f107cbcca546d9f62fc81c5a519aec67dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 5 Sep 2021 10:05:35 +0200 Subject: [PATCH 58/75] box-drawing: NOINLINE braille --- box-drawing.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/box-drawing.c b/box-drawing.c index a1940392..1c694627 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -1953,7 +1953,7 @@ draw_quadrant(struct buf *buf, wchar_t wc) quad_lower_right(buf); } -static void +static void NOINLINE draw_braille(struct buf *buf, wchar_t wc) { int w = min(buf->width / 4, buf->height / 8); From 37b15adcd8306652ce21804cb6140d1361adc94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Sep 2021 09:50:49 +0200 Subject: [PATCH 59/75] =?UTF-8?q?term:=20turn=20=E2=80=98box-drawings?= =?UTF-8?q?=E2=80=99=20array=20into=20three=20dynamically=20allocated=20ar?= =?UTF-8?q?rays?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The box_drawings array is now quite large, and uses up ~4K when *empty*. This patch splits it up into three separate, dynamically allocated arrays; one for the traditional box+line drawing and block elements glyphs, one for braille, and one for the legacy computing symbols. When we need to render a glyph, the *entire* array (that it belongs to) is allocated. I.e this is one step closer to a dynamic glyph cache (like the one fcft uses), but doesn’t go all the way. This is especially nice for people with ‘box-drawings-uses-font-glyphs=yes’; for them, the custom glyphs now uses 3*8 bytes (for the three array pointers), instead of 4K. --- render.c | 60 +++++++++++++++++++++++++++++++----------------------- terminal.c | 42 +++++++++++++++++++++++++++++--------- terminal.h | 27 +++++++++++++++++------- 3 files changed, 87 insertions(+), 42 deletions(-) diff --git a/render.c b/render.c index 684c86e4..b9acc8e1 100644 --- a/render.c +++ b/render.c @@ -513,10 +513,12 @@ render_cell(struct terminal *term, pixman_image_t *pix, if (base != 0) { if (unlikely( /* Classic box drawings */ - (base >= 0x2500 && base <= 0x259f) || + (base >= GLYPH_BOX_DRAWING_FIRST && + base <= GLYPH_BOX_DRAWING_LAST) || /* Braille */ - (base >= 0x2800 && base <= 0x28ff) || + (base >= GLYPH_BRAILLE_FIRST && + base <= GLYPH_BRAILLE_LAST) || /* * Unicode 13 "Symbols for Legacy Computing" @@ -524,42 +526,50 @@ render_cell(struct terminal *term, pixman_image_t *pix, * * Note, the full range is U+1FB00 - U+1FBF9 */ - - /* Unicode 13 sextants */ - (base >= 0x1fb00 && base <= 0x1fb8b) || - (base >= 0x1fb9a && base <= 0x1fb9b)) && + (base >= GLYPH_LEGACY_FIRST && + base <= GLYPH_LEGACY_LAST)) && likely(!term->conf->box_drawings_uses_font_glyphs)) { - /* Box drawing characters */ - size_t idx = base >= 0x1fb00 - ? (base >= 0x1fb9a - ? base - 0x1fb9a + 556 - : base - 0x1fb00 + 416) - : (base >= 0x2800 - ? base - 0x2800 + 160 - : base - 0x2500); + struct fcft_glyph ***arr; + size_t count; + size_t idx; - xassert(idx < ALEN(term->box_drawing)); + if (base >= GLYPH_LEGACY_FIRST) { + arr = &term->custom_glyphs.legacy; + count = GLYPH_LEGACY_COUNT; + idx = base - GLYPH_LEGACY_FIRST; + } else if (base >= GLYPH_BRAILLE_FIRST) { + arr = &term->custom_glyphs.braille; + count = GLYPH_BRAILLE_COUNT; + idx = base - GLYPH_BRAILLE_FIRST; + } else { + arr = &term->custom_glyphs.box_drawing; + count = GLYPH_BOX_DRAWING_COUNT; + idx = base - GLYPH_BOX_DRAWING_FIRST; + } - if (likely(term->box_drawing[idx] != NULL)) - single = term->box_drawing[idx]; + if (unlikely(*arr == NULL)) + *arr = xcalloc(count, sizeof((*arr)[0])); + + if (likely((*arr)[idx] != NULL)) + single = (*arr)[idx]; else { mtx_lock(&term->render.workers.lock); /* Other thread may have instantiated it while we * acquired the lock */ - if (term->box_drawing[idx] == NULL) - term->box_drawing[idx] = box_drawing(term, base); + single = (*arr)[idx]; + if (likely(single == NULL)) + single = (*arr)[idx] = box_drawing(term, base); mtx_unlock(&term->render.workers.lock); - - single = term->box_drawing[idx]; - xassert(single != NULL); } - glyph_count = 1; - glyphs = &single; - cell_cols = single->cols; + if (single != NULL) { + glyph_count = 1; + glyphs = &single; + cell_cols = single->cols; + } } else if (base >= CELL_COMB_CHARS_LO && base <= CELL_COMB_CHARS_HI) diff --git a/terminal.c b/terminal.c index 0138aa15..e6837977 100644 --- a/terminal.c +++ b/terminal.c @@ -626,15 +626,28 @@ err_sem_destroy: } static void -free_box_drawing(struct fcft_glyph **box_drawing) +free_custom_glyph(struct fcft_glyph **glyph) { - if (*box_drawing == NULL) + if (*glyph == NULL) return; - free(pixman_image_get_data((*box_drawing)->pix)); - pixman_image_unref((*box_drawing)->pix); - free(*box_drawing); - *box_drawing = NULL; + free(pixman_image_get_data((*glyph)->pix)); + pixman_image_unref((*glyph)->pix); + free(*glyph); + *glyph = NULL; +} + +static void +free_custom_glyphs(struct fcft_glyph ***glyphs, size_t count) +{ + if (*glyphs == NULL) + return; + + for (size_t i = 0; i < count; i++) + free_custom_glyph(&(*glyphs)[i]); + + free(*glyphs); + *glyphs = NULL; } static bool @@ -647,8 +660,12 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) term->fonts[i] = fonts[i]; } - for (size_t i = 0; i < ALEN(term->box_drawing); i++) - free_box_drawing(&term->box_drawing[i]); + free_custom_glyphs( + &term->custom_glyphs.box_drawing, GLYPH_BOX_DRAWING_COUNT); + free_custom_glyphs( + &term->custom_glyphs.braille, GLYPH_BRAILLE_COUNT); + free_custom_glyphs( + &term->custom_glyphs.legacy, GLYPH_LEGACY_COUNT); const int old_cell_width = term->cell_width; const int old_cell_height = term->cell_height; @@ -1590,8 +1607,13 @@ term_destroy(struct terminal *term) for (size_t i = 0; i < 4; i++) free(term->font_sizes[i]); - for (size_t i = 0; i < ALEN(term->box_drawing); i++) - free_box_drawing(&term->box_drawing[i]); + + free_custom_glyphs( + &term->custom_glyphs.box_drawing, GLYPH_BOX_DRAWING_COUNT); + free_custom_glyphs( + &term->custom_glyphs.braille, GLYPH_BRAILLE_COUNT); + free_custom_glyphs( + &term->custom_glyphs.legacy, GLYPH_LEGACY_COUNT); free(term->search.buf); diff --git a/terminal.h b/terminal.h index bfb327e3..028b4844 100644 --- a/terminal.h +++ b/terminal.h @@ -333,13 +333,26 @@ struct terminal { int16_t font_y_ofs; enum fcft_subpixel font_subpixel; - /* - * 0-159: U+02500+0259F - * 160-415: U+02800-028FF - * 416-555: U+1FB00-1FB8B - * 556-557: U+1FB9A-1FB9B - */ - struct fcft_glyph *box_drawing[558]; + struct { + struct fcft_glyph **box_drawing; + struct fcft_glyph **braille; + struct fcft_glyph **legacy; + + #define GLYPH_BOX_DRAWING_FIRST 0x2500 + #define GLYPH_BOX_DRAWING_LAST 0x259F + #define GLYPH_BOX_DRAWING_COUNT \ + (GLYPH_BOX_DRAWING_LAST - GLYPH_BOX_DRAWING_FIRST + 1) + + #define GLYPH_BRAILLE_FIRST 0x2800 + #define GLYPH_BRAILLE_LAST 0x28FF + #define GLYPH_BRAILLE_COUNT \ + (GLYPH_BRAILLE_LAST - GLYPH_BRAILLE_FIRST + 1) + + #define GLYPH_LEGACY_FIRST 0x1FB00 + #define GLYPH_LEGACY_LAST 0x1FB9B + #define GLYPH_LEGACY_COUNT \ + (GLYPH_LEGACY_LAST - GLYPH_LEGACY_FIRST + 1) + } custom_glyphs; bool is_sending_paste_data; ptmx_buffer_list_t ptmx_buffers; From e426e77b1d3640868b521b65617525f76ab3b35c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Tue, 14 Sep 2021 10:20:07 +0200 Subject: [PATCH 60/75] box-drawing: braille: prefer increasing spacing over dot width Too large dots make them harder to distinguish, when the spacing between them is small. Prefer increasing the spacing, instead of increasing the dot size. This looks better at small font sizes in particular. --- box-drawing.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/box-drawing.c b/box-drawing.c index 1c694627..f1789f64 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -1962,8 +1962,8 @@ draw_braille(struct buf *buf, wchar_t wc) int x_margin = x_spacing / 2; int y_margin = y_spacing / 2; - int x_pix_left = buf->width - 2 * x_margin - x_spacing - 2 * w; - int y_pix_left = buf->height - 2 * y_margin - 3 * y_spacing - 4 * w; + int x_px_left = buf->width - 2 * x_margin - x_spacing - 2 * w; + int y_px_left = buf->height - 2 * y_margin - 3 * y_spacing - 4 * w; LOG_DBG( "braille: before adjusting: " @@ -1972,35 +1972,38 @@ draw_braille(struct buf *buf, wchar_t wc) w, x_pix_left, y_pix_left); /* First, try hard to ensure the DOT width is non-zero */ - if (x_pix_left >= 2 && y_pix_left >= 4 && w == 0) { + if (x_px_left >= 2 && y_px_left >= 4 && w == 0) { w++; - x_pix_left -= 2; - y_pix_left -= 4; + x_px_left -= 2; + y_px_left -= 4; } /* Second, prefer a non-zero margin */ - if (x_pix_left >= 2 && x_margin == 0) { x_margin = 1; x_pix_left -= 2; } - if (y_pix_left >= 2 && y_margin == 0) { y_margin = 1; y_pix_left -= 2; } + if (x_px_left >= 2 && x_margin == 0) { x_margin = 1; x_px_left -= 2; } + if (y_px_left >= 2 && y_margin == 0) { y_margin = 1; y_px_left -= 2; } - if (x_pix_left >= 2 && y_pix_left >= 4) { + /* Third, increase spacing */ + if (x_px_left >= 1) { x_spacing++; x_px_left--; } + if (y_px_left >= 3) { y_spacing++; y_px_left -= 3; } + + /* Fourth, margins (“spacing”, but on the sides) */ + if (x_px_left >= 2) { x_margin++; x_px_left -= 2; } + if (y_px_left >= 2) { y_margin++; y_px_left -= 2; } + + /* Last - increase dot width */ + if (x_px_left >= 2 && y_px_left >= 4) { w++; - x_pix_left -= 2; - y_pix_left -= 4; + x_px_left -= 2; + y_px_left -= 4; } - if (x_pix_left >= 1) { x_spacing++; x_pix_left--; } - if (y_pix_left >= 3) { y_spacing++; y_pix_left -= 3; } - - if (x_pix_left >= 2) { x_margin++; x_pix_left -= 2; } - if (y_pix_left >= 2) { y_margin++; y_pix_left -= 2; } - LOG_DBG( "braille: after adjusting: " "cell: %dx%d, margin=%dx%d, spacing=%dx%d, width=%d, left=%dx%d", buf->width, buf->height, x_margin, y_margin, x_spacing, y_spacing, w, x_pix_left, y_pix_left); - xassert(x_pix_left <= 1 || y_pix_left <= 1); + xassert(x_px_left <= 1 || y_px_left <= 1); xassert(2 * x_margin + 2 * w + x_spacing <= buf->width); xassert(2 * y_margin + 4 * w + 3 * y_spacing <= buf->height); From ed0ef4bb1dd2b6a110a3bebe73bdfbe8b254675d Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Wed, 15 Sep 2021 16:18:05 +0100 Subject: [PATCH 61/75] log: simplify Boolean logic for setting "colorize" var in log_init() --- log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log.c b/log.c index 9dfc842d..b93b2cde 100644 --- a/log.c +++ b/log.c @@ -40,7 +40,7 @@ log_init(enum log_colorize _colorize, bool _do_syslog, [LOG_FACILITY_DAEMON] = LOG_DAEMON, }; - colorize = _colorize == LOG_COLORIZE_NEVER ? false : _colorize == LOG_COLORIZE_ALWAYS ? true : isatty(STDERR_FILENO); + colorize = _colorize == LOG_COLORIZE_ALWAYS || (_colorize == LOG_COLORIZE_AUTO && isatty(STDERR_FILENO)); do_syslog = _do_syslog; log_level = _log_level; From 537cd9b367beece55691fb739f0834e2e96571b6 Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Sat, 18 Sep 2021 19:44:04 +0100 Subject: [PATCH 62/75] main/client: replace some uses of printf() with puts() in print_usage() FOOT_DEFAULT_TERM is a string literal passed as a -D argument to the compiler, so it can just be concatenated with the other string literals, instead of being formatted with printf(). --- client.c | 39 ++++++++++++++++++++------------------- main.c | 16 ++++++++-------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/client.c b/client.c index e5b9a777..7cfcb0ef 100644 --- a/client.c +++ b/client.c @@ -54,27 +54,28 @@ version_and_features(void) static void print_usage(const char *prog_name) { + static const char options[] = + "\nOptions:\n" + " -t,--term=TERM value to set the environment variable TERM to (" FOOT_DEFAULT_TERM ")\n" + " -T,--title=TITLE initial window title (foot)\n" + " -a,--app-id=ID window application ID (foot)\n" + " -w,--window-size-pixels=WIDTHxHEIGHT initial width and height, in pixels\n" + " -W,--window-size-chars=WIDTHxHEIGHT initial width and height, in characters\n" + " -m,--maximized start in maximized mode\n" + " -F,--fullscreen start in fullscreen mode\n" + " -L,--login-shell start shell as a login shell\n" + " -D,--working-directory=DIR directory to start in (CWD)\n" + " -s,--server-socket=PATH path to the server UNIX domain socket (default=$XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock)\n" + " -H,--hold remain open after child process exits\n" + " -N,--no-wait detach the client process from the running terminal, exiting immediately\n" + " -o,--override=[section.]key=value override configuration option\n" + " -d,--log-level={info|warning|error|none} log level (info)\n" + " -l,--log-colorize=[{never|always|auto}] enable/disable colorization of log output on stderr\n" + " -v,--version show the version number and quit\n"; + printf("Usage: %s [OPTIONS...]\n", prog_name); printf("Usage: %s [OPTIONS...] command [ARGS...]\n", prog_name); - printf("\n"); - printf("Options:\n"); - printf(" -t,--term=TERM value to set the environment variable TERM to (%s)\n" - " -T,--title=TITLE initial window title (foot)\n" - " -a,--app-id=ID window application ID (foot)\n" - " -w,--window-size-pixels=WIDTHxHEIGHT initial width and height, in pixels\n" - " -W,--window-size-chars=WIDTHxHEIGHT initial width and height, in characters\n" - " -m,--maximized start in maximized mode\n" - " -F,--fullscreen start in fullscreen mode\n" - " -L,--login-shell start shell as a login shell\n" - " -D,--working-directory=DIR directory to start in (CWD)\n" - " -s,--server-socket=PATH path to the server UNIX domain socket (default=$XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock)\n" - " -H,--hold remain open after child process exits\n" - " -N,--no-wait detach the client process from the running terminal, exiting immediately\n" - " -o,--override=[section.]key=value override configuration option\n" - " -d,--log-level={info|warning|error|none} log level (info)\n" - " -l,--log-colorize=[{never|always|auto}] enable/disable colorization of log output on stderr\n" - " -v,--version show the version number and quit\n", - FOOT_DEFAULT_TERM); + puts(options); } static bool NOINLINE diff --git a/main.c b/main.c index 4f7b027d..7d4cbbcf 100644 --- a/main.c +++ b/main.c @@ -56,16 +56,13 @@ version_and_features(void) static void print_usage(const char *prog_name) { - printf( - "Usage: %s [OPTIONS...]\n" - "Usage: %s [OPTIONS...] command [ARGS...]\n" - "\n" - "Options:\n" + static const char options[] = + "\nOptions:\n" " -c,--config=PATH load configuration from PATH ($XDG_CONFIG_HOME/foot/foot.ini)\n" " -C,--check-config verify configuration, exit with 0 if ok, otherwise exit with 1\n" " -o,--override=[section.]key=value override configuration option\n" " -f,--font=FONT comma separated list of fonts in fontconfig format (monospace)\n" - " -t,--term=TERM value to set the environment variable TERM to (%s)\n" + " -t,--term=TERM value to set the environment variable TERM to (" FOOT_DEFAULT_TERM ")\n" " -T,--title=TITLE initial window title (foot)\n" " -a,--app-id=ID window application ID (foot)\n" " -m,--maximized start in maximized mode\n" @@ -81,8 +78,11 @@ print_usage(const char *prog_name) " -d,--log-level={info|warning|error|none} log level (info)\n" " -l,--log-colorize=[{never|always|auto}] enable/disable colorization of log output on stderr\n" " -s,--log-no-syslog disable syslog logging (only applicable in server mode)\n" - " -v,--version show the version number and quit\n", - prog_name, prog_name, FOOT_DEFAULT_TERM); + " -v,--version show the version number and quit\n"; + + printf("Usage: %s [OPTIONS...]\n", prog_name); + printf("Usage: %s [OPTIONS...] command [ARGS...]\n", prog_name); + puts(options); } bool From ef7919e64d09d4b29a1557641883e78f5896841d Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Sat, 18 Sep 2021 23:40:40 +0100 Subject: [PATCH 63/75] main/client: add no-op "-e" command-line option --- CHANGELOG.md | 5 +++++ client.c | 8 ++++++-- doc/foot.1.scd | 10 ++++++++++ doc/footclient.1.scd | 4 ++++ main.c | 8 ++++++-- 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b0c2011..d229c4ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,9 @@ (https://codeberg.org/dnkl/foot/issues/701). * Braille characters (U+2800 - U+28FF) are now rendered by foot itself (https://codeberg.org/dnkl/foot/issues/702). +* `-e` command-line option. This option is simply ignored, to appease + program launchers that blindly pass `-e` to any terminal emulator + (https://codeberg.org/dnkl/foot/issues/184). ### Changed @@ -67,6 +70,8 @@ ### Security ### Contributors +* [craigbarnes](https://codeberg.org/craigbarnes) + ## 1.9.0 diff --git a/client.c b/client.c index 7cfcb0ef..5c767e11 100644 --- a/client.c +++ b/client.c @@ -71,7 +71,8 @@ print_usage(const char *prog_name) " -o,--override=[section.]key=value override configuration option\n" " -d,--log-level={info|warning|error|none} log level (info)\n" " -l,--log-colorize=[{never|always|auto}] enable/disable colorization of log output on stderr\n" - " -v,--version show the version number and quit\n"; + " -v,--version show the version number and quit\n" + " -e ignored (for compatibility with xterm -e)\n"; printf("Usage: %s [OPTIONS...]\n", prog_name); printf("Usage: %s [OPTIONS...] command [ARGS...]\n", prog_name); @@ -145,7 +146,7 @@ main(int argc, char *const *argv) struct client_string *cargv = NULL; while (true) { - int c = getopt_long(argc, argv, "+t:T:a:w:W:mFLD:s:HNo:d:l::vh", longopts, NULL); + int c = getopt_long(argc, argv, "+t:T:a:w:W:mFLD:s:HNo:d:l::veh", longopts, NULL); if (c == -1) break; @@ -273,6 +274,9 @@ main(int argc, char *const *argv) ret = EXIT_SUCCESS; goto err; + case 'e': + break; + case '?': goto err; } diff --git a/doc/foot.1.scd b/doc/foot.1.scd index bbe784bc..91a7808a 100644 --- a/doc/foot.1.scd +++ b/doc/foot.1.scd @@ -141,6 +141,16 @@ the foot command line *-v*,*--version* Show the version number and quit. +*-e* + Ignored; for compatibility with *xterm -e*. + + This option was added in response to several program launchers + passing *-e* to arbitrary terminals, under the assumption that + they all implement the same semantics for it as *xterm*(1). + Ignoring it allows foot to be invoked as e.g. *foot -e man foot* + with the same results as with xterm, instead of producing an + "invalid option" error. + # EXIT STATUS Foot will exit with code 230 if there is a failure in foot itself. diff --git a/doc/footclient.1.scd b/doc/footclient.1.scd index b252d5e1..9cb4ef69 100644 --- a/doc/footclient.1.scd +++ b/doc/footclient.1.scd @@ -79,6 +79,10 @@ terminal has terminated. *-v*,*--version* Show the version number and quit +*-e* + Ignored; for compatibility with *xterm -e*. See *foot*(1) for more + details. + # EXIT STATUS Footlient will exit with code 220 if there is a failure in footclient diff --git a/main.c b/main.c index 7d4cbbcf..fd27aebd 100644 --- a/main.c +++ b/main.c @@ -78,7 +78,8 @@ print_usage(const char *prog_name) " -d,--log-level={info|warning|error|none} log level (info)\n" " -l,--log-colorize=[{never|always|auto}] enable/disable colorization of log output on stderr\n" " -s,--log-no-syslog disable syslog logging (only applicable in server mode)\n" - " -v,--version show the version number and quit\n"; + " -v,--version show the version number and quit\n" + " -e ignored (for compatibility with xterm -e)\n"; printf("Usage: %s [OPTIONS...]\n", prog_name); printf("Usage: %s [OPTIONS...] command [ARGS...]\n", prog_name); @@ -216,7 +217,7 @@ main(int argc, char *const *argv) config_override_t overrides = tll_init(); while (true) { - int c = getopt_long(argc, argv, "+c:Co:t:T:a:LD:f:w:W:s::HmFPp:d:l::Svh", longopts, NULL); + int c = getopt_long(argc, argv, "+c:Co:t:T:a:LD:f:w:W:s::HmFPp:d:l::Sveh", longopts, NULL); if (c == -1) break; @@ -375,6 +376,9 @@ main(int argc, char *const *argv) print_usage(prog_name); return EXIT_SUCCESS; + case 'e': + break; + case '?': return ret; } From 705b5d786ca369f8c9d9ea2186ac77dffec6e809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 19 Sep 2021 11:49:49 +0200 Subject: [PATCH 64/75] box-drawing: repair debug logs --- box-drawing.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/box-drawing.c b/box-drawing.c index f1789f64..887aa0c9 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -1969,7 +1969,7 @@ draw_braille(struct buf *buf, wchar_t wc) "braille: before adjusting: " "cell: %dx%d, margin=%dx%d, spacing=%dx%d, width=%d, left=%dx%d", buf->width, buf->height, x_margin, y_margin, x_spacing, y_spacing, - w, x_pix_left, y_pix_left); + w, x_px_left, y_px_left); /* First, try hard to ensure the DOT width is non-zero */ if (x_px_left >= 2 && y_px_left >= 4 && w == 0) { @@ -2001,7 +2001,7 @@ draw_braille(struct buf *buf, wchar_t wc) "braille: after adjusting: " "cell: %dx%d, margin=%dx%d, spacing=%dx%d, width=%d, left=%dx%d", buf->width, buf->height, x_margin, y_margin, x_spacing, y_spacing, - w, x_pix_left, y_pix_left); + w, x_px_left, y_px_left); xassert(x_px_left <= 1 || y_px_left <= 1); xassert(2 * x_margin + 2 * w + x_spacing <= buf->width); @@ -2889,8 +2889,7 @@ box_drawing(const struct terminal *term, wchar_t wc) }, }; - LOG_DBG("LIGHT=%d, HEAVY=%d", - _thickness(&buf, LIGHT), _thickness(&buf, HEAVY)); + LOG_DBG("LIGHT=%d, HEAVY=%d", buf.thickness[LIGHT], buf.thickness[HEAVY]); draw_glyph(&buf, wc); From 14e77fec32f0da5425cc0e688ed8cab85fcfa385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 22 Sep 2021 22:00:28 +0200 Subject: [PATCH 65/75] doc: foot.ini: mention how dpi-aware interacts with bitmap fonts --- doc/foot.ini.5.scd | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 6a9b5de5..3e187042 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -145,11 +145,12 @@ in this order: Default: _no_. *dpi-aware* - *auto*, *yes*, or *no*. When set to *yes*, fonts are sized using - the monitor's DPI, making a font of a given size have the same - physical size, regardless of monitor. In other words, if you drag - a foot window between different monitors, the font size remains - the same. + *auto*, *yes*, or *no*. + + When set to *yes*, fonts are sized using the monitor's DPI, making + a font of a given size have the same physical size, regardless of + monitor. In other words, if you drag a foot window between + different monitors, the font size remains the same. In this mode, the monitor's scaling factor is ignored; doubling the scaling factor will *not* double the font size. @@ -162,6 +163,11 @@ in this order: DPI on monitors with a scaling factor of 1, but otherwise using the scaling factor. + Note that this option typically does not work with bitmap fonts, + which only contains a pre-defined set of sizes, and cannot be + dynamically scaled. Whichever size (of the available ones) that + best matches the DPI or scaling factor, will be used. + Default: _auto_ *pad* From ab34288e18132177ba991755f784305a19c5804d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 22 Sep 2021 22:01:17 +0200 Subject: [PATCH 66/75] doc: foot.ini: mention how dpi-aware interacts with :pixelsize=N --- doc/foot.ini.5.scd | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 3e187042..32d838a7 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -168,6 +168,13 @@ in this order: dynamically scaled. Whichever size (of the available ones) that best matches the DPI or scaling factor, will be used. + Also note that if the font size has been specified in pixels + (*:pixelsize=*_N_, instead of *:size=*_N_), DPI scaling + (*dpi-aware=yes*) will have no effect (the specified pixel size + will be used as is). But, if the monitor's scaling factor is used + to size the font (*dpi-aware=no*), the font's pixel size will be + multiplied with the scaling factor. + Default: _auto_ *pad* From af1a01b2526be0b9ae36fb498c6faaba24d4a1c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 22 Sep 2021 22:01:35 +0200 Subject: [PATCH 67/75] doc: foot.ini: font: add reference to dpi-aware option --- doc/foot.ini.5.scd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index 32d838a7..aed491aa 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -37,7 +37,8 @@ in this order: 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. + font size. Note that the font size is also affected by the + *dpi-aware* option. Examples: - Dina:weight=bold:slant=italic From 49d02bb3893f8c84df6550efdbe41a9e88c9b915 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 22 Sep 2021 22:01:54 +0200 Subject: [PATCH 68/75] =?UTF-8?q?readme:=20avoid=20having=20two=20?= =?UTF-8?q?=E2=80=9Csee...=E2=80=9D=20right=20next=20after=20each=20other?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36abec00..93d783c4 100644 --- a/README.md +++ b/README.md @@ -352,7 +352,7 @@ been enabled, fonts will instead be sized using the scaling factor. This can be changed to either **always** use the monitor’s DPI -(regardless of scaling factor), or to **never** use it. See the +(regardless of scaling factor), or to **never** use it, with the `dpi-aware` option in `foot.ini`. See the man page, **foot.ini**(5) for more information. From 2934a4e96c34cf81131005fd28fd0860da8c90fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Wed, 22 Sep 2021 22:04:30 +0200 Subject: [PATCH 69/75] doc: foot.ini: remove stray double quote character --- 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 aed491aa..ea4779c5 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -194,7 +194,7 @@ in this order: Default: _2x2_. *resize-delay-ms* - Time, in milliseconds, of "idle time" "before foot sends the new + Time, in milliseconds, of "idle time" before foot sends the new window dimensions to the client application while doing an interactive resize of a foot window. Idle time in this context is a period of time where the window size is not changing. From 3693f57780951663ce9ef00b7abb5b6f7dfbf493 Mon Sep 17 00:00:00 2001 From: grtcdr Date: Wed, 22 Sep 2021 23:20:29 +0100 Subject: [PATCH 70/75] add hacktoberfest theme --- themes/hacktoberfest | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 themes/hacktoberfest diff --git a/themes/hacktoberfest b/themes/hacktoberfest new file mode 100644 index 00000000..4c7c6233 --- /dev/null +++ b/themes/hacktoberfest @@ -0,0 +1,22 @@ +[cursor] +color=141414 c9c9c9 + +[colors] +foreground=c9c9c9 +background=141414 +regular0=191918 # black +regular1=b34538 # red +regular2=587744 # green +regular3=d08949 # yellow +regular4=206ec5 # blue +regular5=864651 # magenta +regular6=ac9166 # cyan +regular7=f1eee7 # white +bright0=2c2b2a # bright black +bright1=b33323 # bright red +bright2=42824a # bright green +bright3=c75a22 # bright yellow +bright4=5389c5 # bright blue +bright5=e795a5 # bright magenta +bright6=ebc587 # bright cyan +bright7=ffffff # bright white From fb77637eb96a71de48b49e40fbe7dd71ec85bcbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 19 Sep 2021 11:58:34 +0200 Subject: [PATCH 71/75] term: only scale using DPI if *all* monitors have a scaling factor or one With dpi-aware=auto (the default), scale fonts using DPI *only* if *all* available monitors have a scaling factor of one. The idea is this: if a user, with multiple monitors, have enabled scaling on *at least* one monitor, he/she has most likely done so to match the size of his/hers other monitors. For example, if the user has one monitor with a scaling factor of one, and another one with a scaling factor of two, he/she expects things to be twice as large on the second monitor. If we (foot) scale using DPI on the first monitor, and using the scaling factor on the second monitor, foot will *not* look twice as big on the second monitor (this was the old behavior of dpi-aware=auto). Part of #714 --- CHANGELOG.md | 3 +++ README.md | 6 ++--- box-drawing.c | 4 ++-- doc/foot.ini.5.scd | 6 +++-- terminal.c | 59 ++++++++++++++++++++++++++++++++-------------- terminal.h | 3 +-- wayland.c | 5 ++++ 7 files changed, 59 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d229c4ec..c08b29c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,9 @@ still exported, like before. * The default install location for the terminfo definitions have been changed back to `${datadir}/terminfo`. +* `dpi-aware=auto`: fonts are now scaled using the monitor’s DPI only + when **all** monitors have a scaling factor of one + (https://codeberg.org/dnkl/foot/issues/714). ### Deprecated diff --git a/README.md b/README.md index 93d783c4..d90302f8 100644 --- a/README.md +++ b/README.md @@ -347,9 +347,9 @@ This is not how it is meant to be. Fonts are measured in _point sizes_ all mediums, be it printers or monitors, regardless of their DPI. Foot’s default behavior is to use the monitor’s DPI to size fonts when -output scaling has been disabled. On monitors where output scaling has -been enabled, fonts will instead be sized using the scaling -factor. +output scaling has been disabled on **all** monitors. If at least one +monitor has output scaling enabled, fonts will instead by sized using +the scaling factor. This can be changed to either **always** use the monitor’s DPI (regardless of scaling factor), or to **never** use it, with the diff --git a/box-drawing.c b/box-drawing.c index 887aa0c9..3f8a38ef 100644 --- a/box-drawing.c +++ b/box-drawing.c @@ -2837,8 +2837,8 @@ box_drawing(const struct terminal *term, wchar_t wc) abort(); } - double dpi = term_font_sized_by_dpi(term, term->scale) ? term->font_dpi : 96.; - double scale = term_font_sized_by_scale(term, term->scale) ? term->scale : 1.; + double dpi = term->font_is_sized_by_dpi ? term->font_dpi : 96.; + double scale = term->font_is_sized_by_dpi ? 1. : term->scale; double cell_size = sqrt(pow(term->cell_width, 2) + pow(term->cell_height, 2)); int base_thickness = diff --git a/doc/foot.ini.5.scd b/doc/foot.ini.5.scd index ea4779c5..6bc93cc4 100644 --- a/doc/foot.ini.5.scd +++ b/doc/foot.ini.5.scd @@ -161,8 +161,10 @@ in this order: scaling factor *does* double the font size. Finally, if set to *auto*, fonts will be sized using the monitor's - DPI on monitors with a scaling factor of 1, but otherwise using - the scaling factor. + DPI if _all_ monitors have a scaling factor of 1. If at least one + monitor as a scaling factor larger than 1 (regardless of whether + the foot window is mapped on that monitor or not), fonts will be + scaled using the scaling factor. Note that this option typically does not work with bitmap fonts, which only contains a pre-defined set of sizes, and cannot be diff --git a/terminal.c b/terminal.c index e6837977..2738291d 100644 --- a/terminal.c +++ b/terminal.c @@ -813,25 +813,48 @@ get_font_subpixel(const struct terminal *term) return FCFT_SUBPIXEL_DEFAULT; } -bool -term_font_sized_by_dpi(const struct terminal *term, int scale) +static bool +term_font_size_by_dpi(const struct terminal *term) { - return term->conf->dpi_aware == DPI_AWARE_YES || - (term->conf->dpi_aware == DPI_AWARE_AUTO && scale <= 1); -} + switch (term->conf->dpi_aware) { + case DPI_AWARE_YES: return true; + case DPI_AWARE_NO: return false; -bool -term_font_sized_by_scale(const struct terminal *term, int scale) -{ - return !term_font_sized_by_dpi(term, scale); + case DPI_AWARE_AUTO: + /* + * Scale using DPI if all monitors have a scaling factor or 1. + * + * The idea is this: if a user, with multiple monitors, have + * enabled scaling on at least one monitor, then he/she has + * most likely done so to match the size of his/hers other + * monitors. + * + * I.e. if the user has one monitor with a scaling factor of + * one, and another with a scaling factor of two, he/she + * expects things to be twice as large on the second + * monitor. + * + * If we (foot) scale using DPI on the first monitor, and + * using the scaling factor on the second monitor, foot will + * *not* look twice as big on the second monitor. + */ + tll_foreach(term->wl->monitors, it) { + const struct monitor *mon = &it->item; + if (mon->scale > 1) + return false; + } + return true; + } + + BUG("unhandled DPI awareness value"); } int term_pt_or_px_as_pixels(const struct terminal *term, const struct pt_or_px *pt_or_px) { - double scale = term_font_sized_by_scale(term, term->scale) ? term->scale : 1.; - double dpi = term_font_sized_by_dpi(term, term->scale) ? term->font_dpi : 96.; + double scale = !term->font_is_sized_by_dpi ? term->scale : 1.; + double dpi = term->font_is_sized_by_dpi ? term->font_dpi : 96.; return pt_or_px->px == 0 ? round(pt_or_px->pt * scale * dpi / 72) @@ -878,8 +901,7 @@ reload_fonts(struct terminal *term) bool use_px_size = term->font_sizes[i][j].px_size > 0; char size[64]; - const int scale = - term_font_sized_by_scale(term, term->scale) ? term->scale : 1; + const int scale = term->font_is_sized_by_dpi ? 1 : term->scale; if (use_px_size) snprintf(size, sizeof(size), ":pixelsize=%d", @@ -914,7 +936,7 @@ reload_fonts(struct terminal *term) const size_t count_bold_italic = custom_bold_italic ? counts[3] : counts[0]; const char **names_bold_italic = (const char **)(custom_bold_italic ? names[3] : names[0]); - const bool use_dpi = term_font_sized_by_dpi(term, term->scale); + const bool use_dpi = term->font_is_sized_by_dpi; char *attrs[4] = {NULL}; int attr_len[4] = {-1, -1, -1, -1}; /* -1, so that +1 (below) results in 0 */ @@ -2008,8 +2030,8 @@ term_font_dpi_changed(struct terminal *term, int old_scale) float dpi = get_font_dpi(term); xassert(term->scale > 0); - bool was_scaled_using_dpi = term_font_sized_by_dpi(term, old_scale); - bool will_scale_using_dpi = term_font_sized_by_dpi(term, term->scale); + bool was_scaled_using_dpi = term->font_is_sized_by_dpi; + bool will_scale_using_dpi = term_font_size_by_dpi(term); bool need_font_reload = was_scaled_using_dpi != will_scale_using_dpi || @@ -2019,15 +2041,16 @@ term_font_dpi_changed(struct terminal *term, int old_scale) if (need_font_reload) { LOG_DBG("DPI/scale change: DPI-awareness=%s, " - "DPI: %.2f -> %.2f, scale: %d, " + "DPI: %.2f -> %.2f, scale: %d -> %d, " "sizing font based on monitor's %s", term->conf->dpi_aware == DPI_AWARE_AUTO ? "auto" : term->conf->dpi_aware == DPI_AWARE_YES ? "yes" : "no", - term->font_dpi, dpi, term->scale, + term->font_dpi, dpi, old_scale, term->scale, will_scale_using_dpi ? "DPI" : "scaling factor"); } term->font_dpi = dpi; + term->font_is_sized_by_dpi = will_scale_using_dpi; if (!need_font_reload) return true; diff --git a/terminal.h b/terminal.h index 028b4844..b3b4a90e 100644 --- a/terminal.h +++ b/terminal.h @@ -329,6 +329,7 @@ struct terminal { struct config_font *font_sizes[4]; struct pt_or_px font_line_height; float font_dpi; + bool font_is_sized_by_dpi; int16_t font_x_ofs; int16_t font_y_ofs; enum fcft_subpixel font_subpixel; @@ -660,8 +661,6 @@ bool term_font_size_reset(struct terminal *term); bool term_font_dpi_changed(struct terminal *term, int old_scale); void term_font_subpixel_changed(struct terminal *term); -bool term_font_sized_by_dpi(const struct terminal *term, int scale); -bool term_font_sized_by_scale(const struct terminal *term, int scale); int term_pt_or_px_as_pixels( const struct terminal *term, const struct pt_or_px *pt_or_px); diff --git a/wayland.c b/wayland.c index 911694ff..49c8ed92 100644 --- a/wayland.c +++ b/wayland.c @@ -340,6 +340,11 @@ update_terms_on_monitor(struct monitor *mon) tll_foreach(wayl->terms, it) { struct terminal *term = it->item; + if (term->conf->dpi_aware == DPI_AWARE_AUTO) { + update_term_for_output_change(term); + continue; + } + tll_foreach(term->window->on_outputs, it2) { if (it2->item == mon) { update_term_for_output_change(term); From a669ba0bf6b764d0b25a83f887a41b30dd23eacf Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Sat, 25 Sep 2021 02:46:22 +0100 Subject: [PATCH 72/75] config: simplify initialization of color table in config_load() --- config.c | 77 +++++++++++++++++++++++--------------------------------- 1 file changed, 31 insertions(+), 46 deletions(-) diff --git a/config.c b/config.c index 4241060d..b148ec47 100644 --- a/config.c +++ b/config.c @@ -31,7 +31,19 @@ static const uint32_t default_foreground = 0xdcdccc; static const uint32_t default_background = 0x111111; -static const uint32_t default_regular[] = { +#define cube6(r, g) \ + r|g|0x00, r|g|0x5f, r|g|0x87, r|g|0xaf, r|g|0xd7, r|g|0xff + +#define cube36(r) \ + cube6(r, 0x0000), \ + cube6(r, 0x5f00), \ + cube6(r, 0x8700), \ + cube6(r, 0xaf00), \ + cube6(r, 0xd700), \ + cube6(r, 0xff00) + +static const uint32_t default_color_table[256] = { + // Regular 0x222222, 0xcc9393, 0x7f9f7f, @@ -40,9 +52,8 @@ static const uint32_t default_regular[] = { 0xdc8cc3, 0x93e0e3, 0xdcdccc, -}; -static const uint32_t default_bright[] = { + // Bright 0x666666, 0xdca3a3, 0xbfebbf, @@ -51,6 +62,22 @@ static const uint32_t default_bright[] = { 0xfcace3, 0xb3ffff, 0xffffff, + + // 6x6x6 RGB cube + cube36(0x000000), + cube36(0x5f0000), + cube36(0x870000), + cube36(0xaf0000), + cube36(0xd70000), + cube36(0xff0000), + + // 24 shades of gray + 0x080808, 0x121212, 0x1c1c1c, 0x262626, + 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, + 0x585858, 0x626262, 0x6c6c6c, 0x767676, + 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, + 0xa8a8a8, 0xb2b2b2, 0xbcbcbc, 0xc6c6c6, + 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee }; static const char *const binding_action_map[] = { @@ -2887,25 +2914,6 @@ config_load(struct config *conf, const char *conf_path, .colors = { .fg = default_foreground, .bg = default_background, - .table = { - default_regular[0], - default_regular[1], - default_regular[2], - default_regular[3], - default_regular[4], - default_regular[5], - default_regular[6], - default_regular[7], - - default_bright[0], - default_bright[1], - default_bright[2], - default_bright[3], - default_bright[4], - default_bright[5], - default_bright[6], - default_bright[7], - }, .alpha = 0xffff, .selection_fg = 0x80000000, /* Use default bg */ .selection_bg = 0x80000000, /* Use default fg */ @@ -2966,30 +2974,7 @@ config_load(struct config *conf, const char *conf_path, .notifications = tll_init(), }; - /* Initialize the color cube */ - { - /* First 16 entries correspond to the "regular" and "bright" - * colors, and have already been initialized */ - - /* Then follows 216 RGB shades */ - for (size_t r = 0; r < 6; r++) { - for (size_t g = 0; g < 6; g++) { - for (size_t b = 0; b < 6; b++) { - uint8_t red = r ? r * 40 + 55 : 0; - uint8_t green = g ? g * 40 + 55 : 0; - uint8_t blue = b ? b * 40 + 55 : 0; - conf->colors.table[16 + r * 6 * 6 + g * 6 + b] - = red << 16 | green << 8 | blue << 0; - } - } - } - - /* And finally 24 shades of gray */ - for (size_t i = 0; i < 24; i++) { - uint8_t level = i * 10 + 8; - conf->colors.table[232 + i] = level << 16 | level << 8 | level << 0; - } - } + memcpy(conf->colors.table, default_color_table, sizeof(default_color_table)); tokenize_cmdline("notify-send -a ${app-id} -i ${app-id} ${title} ${body}", &conf->notify.argv.args); From 9361e7c0723c15328d4e66747d5d1822ccb5c484 Mon Sep 17 00:00:00 2001 From: Craig Barnes Date: Sat, 25 Sep 2021 22:01:03 +0100 Subject: [PATCH 73/75] config: add comments to default_color_table[] about color channel values --- config.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.c b/config.c index b148ec47..e3b79e3e 100644 --- a/config.c +++ b/config.c @@ -64,6 +64,7 @@ static const uint32_t default_color_table[256] = { 0xffffff, // 6x6x6 RGB cube + // (color channels = i ? i*40+55 : 0, where i = 0..5) cube36(0x000000), cube36(0x5f0000), cube36(0x870000), @@ -72,6 +73,7 @@ static const uint32_t default_color_table[256] = { cube36(0xff0000), // 24 shades of gray + // (color channels = i*10+8, where i = 0..23) 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 0x585858, 0x626262, 0x6c6c6c, 0x767676, From ed125cbf58d824e0f36a062fcb78cf2744293d9d Mon Sep 17 00:00:00 2001 From: grtcdr Date: Sun, 26 Sep 2021 20:17:43 +0100 Subject: [PATCH 74/75] Rename to Hacktober --- themes/{hacktoberfest => hacktober} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename themes/{hacktoberfest => hacktober} (100%) diff --git a/themes/hacktoberfest b/themes/hacktober similarity index 100% rename from themes/hacktoberfest rename to themes/hacktober From 548d7be4c60601fe9321d31aa3d8c47c2fa039fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Thu, 30 Sep 2021 13:39:23 +0200 Subject: [PATCH 75/75] selection: line-wise selection now handles soft line-wrapping Previously, soft-wrapped lines were not selected correctly, as the selection logic was hardcoded to simply select everything between the first and last column on the current terminal row. Now, we scan backward and forward, looking for hard-wrapped lines. This is similar to how word-based selection works. Closes #726 --- CHANGELOG.md | 2 + selection.c | 101 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 86 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c08b29c5..f15256b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,8 @@ * Added workaround for GNOME bug where multiple button press events (for the same button) is sent to the CSDs without any release or leave events in between (https://codeberg.org/dnkl/foot/issues/709). +* Line-wise selection not taking soft line-wrapping into account + (https://codeberg.org/dnkl/foot/issues/726). ### Security diff --git a/selection.c b/selection.c index b40215e6..b6bae978 100644 --- a/selection.c +++ b/selection.c @@ -378,6 +378,50 @@ selection_find_word_boundary_right(struct terminal *term, struct coord *pos, } } +void +selection_find_line_boundary_left(struct terminal *term, struct coord *pos, + bool spaces_only) +{ + int next_row = pos->row; + pos->col = 0; + + while (true) { + if (--next_row < 0) + return; + + const struct row *row = grid_row_in_view(term->grid, next_row); + assert(row != NULL); + + if (row->linebreak) + return; + + pos->col = 0; + pos->row = next_row; + } +} + +void +selection_find_line_boundary_right(struct terminal *term, struct coord *pos, + bool spaces_only) +{ + int next_row = pos->row; + pos->col = term->cols - 1; + + while (true) { + const struct row *row = grid_row_in_view(term->grid, next_row); + assert(row != NULL); + + if (row->linebreak) + return; + + if (++next_row >= term->rows) + return; + + pos->col = term->cols - 1; + pos->row = next_row; + } +} + void selection_start(struct terminal *term, int col, int row, enum selection_kind kind, @@ -421,13 +465,19 @@ selection_start(struct terminal *term, int col, int row, break; } - case SELECTION_LINE_WISE: - term->selection.start = (struct coord){0, term->grid->view + row}; - term->selection.pivot.start = term->selection.start; - term->selection.pivot.end = (struct coord){term->cols - 1, term->grid->view + row}; + case SELECTION_LINE_WISE: { + struct coord start = {0, row}, end = {term->cols - 1, row}; + selection_find_line_boundary_left(term, &start, spaces_only); + selection_find_line_boundary_right(term, &end, spaces_only); - selection_update(term, term->cols - 1, row); + term->selection.start = (struct coord){ + start.col, term->grid->view + start.row}; + term->selection.pivot.start = term->selection.start; + term->selection.pivot.end = (struct coord){end.col, term->grid->view + end.row}; + + selection_update(term, end.col, end.row); break; + } case SELECTION_NONE: BUG("Invalid selection kind"); @@ -756,13 +806,21 @@ selection_update(struct terminal *term, int col, int row) case SELECTION_LINE_WISE: switch (term->selection.direction) { - case SELECTION_LEFT: - new_end.col = 0; + case SELECTION_LEFT: { + struct coord end = {0, row}; + selection_find_line_boundary_left( + term, &end, term->selection.spaces_only); + new_end = (struct coord){end.col, term->grid->view + end.row}; break; + } - case SELECTION_RIGHT: - new_end.col = term->cols - 1; + case SELECTION_RIGHT: { + struct coord end = {col, row}; + selection_find_line_boundary_right( + term, &end, term->selection.spaces_only); + new_end = (struct coord){end.col, term->grid->view + end.row}; break; + } case SELECTION_UNDIR: break; @@ -870,6 +928,8 @@ selection_extend_normal(struct terminal *term, int col, int row, } } + const bool spaces_only = term->selection.spaces_only; + switch (term->selection.kind) { case SELECTION_CHAR_WISE: xassert(new_kind == SELECTION_CHAR_WISE); @@ -883,10 +943,8 @@ selection_extend_normal(struct terminal *term, int col, int row, struct coord pivot_start = {new_start.col, new_start.row - term->grid->view}; struct coord pivot_end = pivot_start; - selection_find_word_boundary_left( - term, &pivot_start, term->selection.spaces_only); - selection_find_word_boundary_right( - term, &pivot_end, term->selection.spaces_only); + selection_find_word_boundary_left(term, &pivot_start, spaces_only); + selection_find_word_boundary_right(term, &pivot_end, spaces_only); term->selection.pivot.start = (struct coord){pivot_start.col, term->grid->view + pivot_start.row}; @@ -895,13 +953,22 @@ selection_extend_normal(struct terminal *term, int col, int row, break; } - case SELECTION_LINE_WISE: + case SELECTION_LINE_WISE: { xassert(new_kind == SELECTION_CHAR_WISE || - new_kind == SELECTION_LINE_WISE); + new_kind == SELECTION_LINE_WISE); - term->selection.pivot.start = (struct coord){0, new_start.row}; - term->selection.pivot.end = (struct coord){term->cols - 1, new_start.row}; + struct coord pivot_start = {new_start.col, new_start.row - term->grid->view}; + struct coord pivot_end = pivot_start; + + selection_find_line_boundary_left(term, &pivot_start, spaces_only); + selection_find_line_boundary_right(term, &pivot_end, spaces_only); + + term->selection.pivot.start = + (struct coord){pivot_start.col, term->grid->view + pivot_start.row}; + term->selection.pivot.end = + (struct coord){pivot_end.col, term->grid->view + pivot_end.row}; break; + } case SELECTION_BLOCK: case SELECTION_NONE: