This commit is contained in:
lbia.xyz 2023-04-13 17:09:46 +02:00
commit 8445da4853
No known key found for this signature in database
GPG key ID: 6774C7B38E986DF1
103 changed files with 1655 additions and 579 deletions

View file

@ -1,112 +0,0 @@
stages:
- build
variables:
GIT_SUBMODULE_STRATEGY: normal
before_script:
- apk update
- apk add musl-dev linux-headers meson ninja gcc scdoc ncurses
- apk add libxkbcommon-dev pixman-dev freetype-dev fontconfig-dev harfbuzz-dev utf8proc-dev
- apk add wayland-dev wayland-protocols
- apk add git
- apk add check-dev
- apk add ttf-hack font-noto-emoji
debug-x64:
image: alpine:edge
stage: build
script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd ..
- mkdir -p bld/debug
- cd bld/debug
- meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../
- ninja -v -k0
- ninja -v test
artifacts:
reports:
junit: bld/debug/meson-logs/testlog.junit.xml
debug-x64-no-grapheme-clustering:
image: alpine:edge
stage: build
script:
- apk del harfbuzz harfbuzz-dev utf8proc utf8proc-dev
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd ..
- mkdir -p bld/debug
- cd bld/debug
- meson --buildtype=debug -Dgrapheme-clustering=disabled -Dfcft:grapheme-shaping=disabled -Dfcft:run-shaping=disabled -Dfcft:test-text-shaping=false ../../
- ninja -v -k0
- ninja -v test
- ./foot --version
- ./footclient --version
artifacts:
reports:
junit: bld/debug/meson-logs/testlog.junit.xml
release-x64:
image: alpine:edge
stage: build
script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd ..
- mkdir -p bld/release
- cd bld/release
- meson --buildtype=release -Db_pgo=generate -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../
- ninja -v -k0
- ninja -v test
- ./foot --version
- ./footclient --version
artifacts:
reports:
junit: bld/release/meson-logs/testlog.junit.xml
debug-x86:
image: i386/alpine:edge
stage: build
script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd ..
- mkdir -p bld/debug
- cd bld/debug
- meson --buildtype=debug -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../
- ninja -v -k0
- ninja -v test
- ./foot --version
- ./footclient --version
artifacts:
reports:
junit: bld/debug/meson-logs/testlog.junit.xml
release-x86:
image: i386/alpine:edge
stage: build
script:
- cd subprojects
- git clone https://codeberg.org/dnkl/fcft.git
- cd ..
- mkdir -p bld/release
- cd bld/release
- meson --buildtype=release -Db_pgo=generate -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../../
- ninja -v -k0
- ninja -v test
- ./foot --version
- ./footclient --version
artifacts:
reports:
junit: bld/release/meson-logs/testlog.junit.xml
codespell:
image: alpine:edge
stage: build
script:
- apk add python3
- apk add py3-pip
- pip install codespell
- codespell -Lser,doas,zar README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd

View file

@ -33,7 +33,7 @@ pipeline:
image: alpine:latest image: alpine:latest
commands: commands:
- apk update - apk update
- apk add musl-dev linux-headers meson ninja gcc scdoc ncurses - apk add musl-dev linux-headers meson ninja gcc clang scdoc ncurses
- apk add libxkbcommon-dev pixman-dev freetype-dev fontconfig-dev harfbuzz-dev utf8proc-dev - apk add libxkbcommon-dev pixman-dev freetype-dev fontconfig-dev harfbuzz-dev utf8proc-dev
- apk add wayland-dev wayland-protocols - apk add wayland-dev wayland-protocols
- apk add git - apk add git
@ -50,7 +50,7 @@ pipeline:
- ./footclient --version - ./footclient --version
- cd ../.. - cd ../..
# Release # Release (gcc)
- mkdir -p bld/release-x64 - mkdir -p bld/release-x64
- cd bld/release-x64 - cd bld/release-x64
- meson --buildtype=release -Db_pgo=generate -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../.. - meson --buildtype=release -Db_pgo=generate -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../..
@ -60,6 +60,16 @@ pipeline:
- ./footclient --version - ./footclient --version
- cd ../.. - cd ../..
# Release (clang)
- mkdir -p bld/release-x64-clang
- cd bld/release-x64-clang
- CC=clang meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../..
- ninja -v -k0
- ninja -v test
- ./foot --version
- ./footclient --version
- cd ../..
# no grapheme clustering # no grapheme clustering
- apk del harfbuzz harfbuzz-dev utf8proc utf8proc-dev - apk del harfbuzz harfbuzz-dev utf8proc utf8proc-dev
- mkdir -p bld/debug - mkdir -p bld/debug
@ -80,7 +90,7 @@ pipeline:
image: i386/alpine:latest image: i386/alpine:latest
commands: commands:
- apk update - apk update
- apk add musl-dev linux-headers meson ninja gcc scdoc ncurses - apk add musl-dev linux-headers meson ninja gcc clang scdoc ncurses
- apk add libxkbcommon-dev pixman-dev freetype-dev fontconfig-dev harfbuzz-dev utf8proc-dev - apk add libxkbcommon-dev pixman-dev freetype-dev fontconfig-dev harfbuzz-dev utf8proc-dev
- apk add wayland-dev wayland-protocols - apk add wayland-dev wayland-protocols
- apk add git - apk add git
@ -97,7 +107,7 @@ pipeline:
- ./footclient --version - ./footclient --version
- cd ../.. - cd ../..
# Release # Release (gcc)
- mkdir -p bld/release-x86 - mkdir -p bld/release-x86
- cd bld/release-x86 - cd bld/release-x86
- meson --buildtype=release -Db_pgo=generate -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../.. - meson --buildtype=release -Db_pgo=generate -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../..
@ -106,3 +116,13 @@ pipeline:
- ./foot --version - ./foot --version
- ./footclient --version - ./footclient --version
- cd ../.. - cd ../..
# Release (clang)
- mkdir -p bld/release-x86-clang
- cd bld/release-x86-clang
- CC=clang meson --buildtype=release -Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true ../..
- ninja -v -k0
- ninja -v test
- ./foot --version
- ./footclient --version
- cd ../..

View file

@ -1,6 +1,7 @@
# Changelog # Changelog
* [Unreleased](#unreleased) * [Unreleased](#unreleased)
* [1.14.0](#1-14-0)
* [1.13.1](#1-13-1) * [1.13.1](#1-13-1)
* [1.13.0](#1-13-0) * [1.13.0](#1-13-0)
* [1.12.1](#1-12-1) * [1.12.1](#1-12-1)
@ -42,6 +43,24 @@
## Unreleased ## Unreleased
### Added
### Changed
### Deprecated
### Removed
### Fixed
* Incorrect icon in dock and window switcher on Gnome ([#1317][1317])
* Crash when scrolling after resizing the window with non-zero
scrolling regions.
[1317]: https://codeberg.org/dnkl/foot/issues/1317
### Security
### Contributors
## 1.14.0
### Added ### Added
@ -49,8 +68,24 @@
* Support (optional) for utmp logging with libutempter. * Support (optional) for utmp logging with libutempter.
* `kxIN` and `kxOUT` (focus in/out events) to terminfo. * `kxIN` and `kxOUT` (focus in/out events) to terminfo.
* `name` capability to `XTGETTCAP`. * `name` capability to `XTGETTCAP`.
* String values in `foot.ini` may now be quoted. This can be used to
set a value to the empty string, for example.
* Environment variables can now be **unset**, by setting
`[environment].<variable>=""` (quotes are required) ([#1225][1225]).
* `font-size-adjustment=N[px]` option, letting you configure how much
to increment/decrement the font size when zooming in or out
([#1188][1188]).
* Bracketed paste terminfo entries (`BD`, `BE`, `PE` and `PS`, added
to ncurses in 2022-12-24). Vim makes use of these.
* “Report version” terminfo entries (`XR`/`xr`).
* “Report DA2” terminfo entries (`RV`/`rv`).
* `XF` terminfo capability (focus in/out events available).
* `$TERM_PROGRAM` and `$TERM_PROGRAM_VERSION` environment variables
set in the slave process.
[1136]: https://codeberg.org/dnkl/foot/issues/1136 [1136]: https://codeberg.org/dnkl/foot/issues/1136
[1225]: https://codeberg.org/dnkl/foot/issues/1225
[1188]: https://codeberg.org/dnkl/foot/issues/1188
### Changed ### Changed
@ -64,13 +99,20 @@
("permanently reset") instead of `2` ("reset") for DEC private ("permanently reset") instead of `2` ("reset") for DEC private
modes that are known but unsupported. modes that are known but unsupported.
* Set `PWD` environment variable in the slave process ([#1179][1179]). * Set `PWD` environment variable in the slave process ([#1179][1179]).
* DPI is now forced to 96 when found to be unreasonably high.
* Set default log level to warning ([#1215][1215]).
* Default `grapheme-width-method` from `wcswidth` to `double-width`.
* When determining initial font size, do FontConfig config
substitution if the user-provided font pattern has no {pixel}size
option ([#1287][1287]).
* DECRST of DECCOLM and DECSCLM removed from terminfo.
[1166]: https://codeberg.org/dnkl/foot/issues/1166 [1166]: https://codeberg.org/dnkl/foot/issues/1166
[1179]: https://codeberg.org/dnkl/foot/issues/1179 [1179]: https://codeberg.org/dnkl/foot/issues/1179
[1215]: https://codeberg.org/dnkl/foot/pulls/1215
[1287]: https://codeberg.org/dnkl/foot/issues/1287
### Deprecated
### Removed
### Fixed ### Fixed
* Crash in `foot --server` on key press, after another `footclient` * Crash in `foot --server` on key press, after another `footclient`
@ -80,16 +122,76 @@
that does not allow Wayland buffer re-use (e.g. KDE/plasma) that does not allow Wayland buffer re-use (e.g. KDE/plasma)
([#1173][1173]) ([#1173][1173])
* Scrollback search matches not being highlighted correctly, on * Scrollback search matches not being highlighted correctly, on
compositors that does now allow Wayland buffer re-use compositors that does not allow Wayland buffer re-use
(e.g. KDE/plasma). (e.g. KDE/plasma).
* Nanosecs "overflow" when calculating timeout value for
`resize-delay-ms` option.
* Missing backslash in ST terminator in escape sequences in the
built-in terminfo (accessed via XTGETTCAP).
* Crash when interactively resizing the window with a very large
scrollback.
* Crash when a sixel image exceeds the current sixel max height.
* Crash after reverse-scrolling (`CSI Ps T`) in the normal
(non-alternate) screen ([#1190][1190]).
* Background transparency being applied to the text "behind" the
cursor. Only applies to block cursor using inversed fg/bg
colors. ([#1205][1205]).
* Crash when monitors physical size is "too small" ([#1209][1209]).
* Line-height adjustment when incrementing/decrementing the font size
with a user-set line-height ([#1218][1218]).
* Scaling factor not being correctly applied when converting pt-or-px
config values (e.g. letter offsets, line height etc).
* Selection being stuck visually when `IL` and `DL`.
* URL underlines sometimes still being visible after exiting URL mode.
* Text-bindings, and pipe-* bindings, with multiple key mappings
causing a crash (double-free) on exit ([#1259][1259]).
* Double-width glyphs glitching when surrounded by glyphs overflowing
into the double-width glyph ([#1256][1256]).
* Wayland protocol violation when ack:ing a configure event for an
unmapped surface ([#1249][1249]).
* `xdg_toplevel::set_min_size()` not being called.
* Key bindings with consumed modifiers masking other key bindings
([#1280][1280]).
* Multi-character compose sequences with the kitty keyboard protocol
([#1288][1288]).
* Crash when application output scrolls very fast, e.g. `yes`
([#1305][1305]).
* Crash when application scrolls **many** lines (> ~2³¹).
* DECCOLM erasing the screen ([#1265][1265]).
[1173]: https://codeberg.org/dnkl/foot/issues/1173 [1173]: https://codeberg.org/dnkl/foot/issues/1173
[1190]: https://codeberg.org/dnkl/foot/issues/1190
[1205]: https://codeberg.org/dnkl/foot/issues/1205
[1209]: https://codeberg.org/dnkl/foot/issues/1209
[1218]: https://codeberg.org/dnkl/foot/issues/1218
[1259]: https://codeberg.org/dnkl/foot/issues/1259
[1256]: https://codeberg.org/dnkl/foot/issues/1256
[1249]: https://codeberg.org/dnkl/foot/issues/1249
[1280]: https://codeberg.org/dnkl/foot/issues/1280
[1288]: https://codeberg.org/dnkl/foot/issues/1288
[1305]: https://codeberg.org/dnkl/foot/issues/1305
[1265]: https://codeberg.org/dnkl/foot/issues/1265
### Security
### Contributors ### Contributors
* Alexey Sakovets
* Andrea Pappacoda
* Antoine Beaupré
* argosatcore
* Craig Barnes * Craig Barnes
* EuCaue
* Grigory Kirillov
* Harri Nieminen
* Hugo Osvaldo Barrera
* jaroeichler
* Joakim Nohlgård
* Nick Hastings
* Soren A D
* Torsten Trautwein
* Vladimír Magyar
* woojiq
* Yorick Peterse
## 1.13.1 ## 1.13.1

View file

@ -327,6 +327,7 @@ We will use the `pgo` binary along with input corpus generated by
`scripts/generate-alt-random-writes.py`: `scripts/generate-alt-random-writes.py`:
```sh ```sh
./utils/xtgettcap
./footclient --version ./footclient --version
./foot --version ./foot --version
tmp_file=$(mktemp) tmp_file=$(mktemp)
@ -349,9 +350,10 @@ rm ${tmp_file}
``` ```
The first step, running `./foot --version` and `./footclient The first step, running `./foot --version` and `./footclient
--version` might seem unnecessary, but is needed to ensure we have --version` etc, might seem unnecessary, but is needed to ensure we
_some_ profiling data for functions not covered by the PGO helper have _some_ profiling data for functions not covered by the PGO helper
binary. Without this, the final link phase will fail. binary, for **all** binaries. Without this, the final link phase will
fail.
The snippet above then creates an (empty) temporary file. Then, it The snippet above then creates an (empty) temporary file. Then, it
runs a script that generates random escape sequences (if you cat runs a script that generates random escape sequences (if you cat
@ -371,6 +373,7 @@ This method requires a running Wayland session.
We will use the script `scripts/generate-alt-random-writes.py`: We will use the script `scripts/generate-alt-random-writes.py`:
```sh ```sh
./utils/xtgettcap
./footclient --version ./footclient --version
foot_tmp_file=$(mktemp) foot_tmp_file=$(mktemp)
./foot \ ./foot \
@ -384,9 +387,10 @@ rm ${foot_tmp_file}
You should see a foot window open up, with random colored text. The You should see a foot window open up, with random colored text. The
window should close after ~1-2s. window should close after ~1-2s.
The first step, `./footclient --version` might seem unnecessary, but The first step, `./utils/xtgettcap && ./footclient --version`
is needed to ensure we have _some_ profiling data for might seem unnecessary, but is needed to ensure we have _some_
`footclient`. Without this, the final link phase will fail. profiling data for **all** binaries we build. Without this, the final
link phase will fail.
##### Use the generated PGO data ##### Use the generated PGO data
@ -436,7 +440,7 @@ sed 's/@default_terminfo@/foot/g' foot.info | \
Where _”output-directory”_ **must** match the value passed to Where _”output-directory”_ **must** match the value passed to
`-Dcustom-terminfo-install-location` in the foot build. If `-Dcustom-terminfo-install-location` in the foot build. If
`-Dcustom-terminfo-install-location` has not been set, `-o `-Dcustom-terminfo-install-location` has not been set, `-o
<output-directoty>` can simply be omitted. <output-directory>` can simply be omitted.
Or, if packaging: Or, if packaging:

View file

@ -569,7 +569,7 @@ reported the same issue.
The report should contain the following: The report should contain the following:
- Foot version (`foot --version`). - Foot version (`foot --version`).
- Log output from foot (start foot from another terminal). - Log output from foot (run `foot -d info` from another terminal).
- Which Wayland compositor (and version) you are running. - Which Wayland compositor (and version) you are running.
- If reporting a crash, please try to provide a `bt full` backtrace - If reporting a crash, please try to provide a `bt full` backtrace
with symbols. with symbols.

View file

@ -94,7 +94,7 @@ print_usage(const char *prog_name)
" -N,--no-wait detach the client process from the running terminal, exiting immediately\n" " -N,--no-wait detach the client process from the running terminal, exiting immediately\n"
" -o,--override=[section.]key=value override configuration option\n" " -o,--override=[section.]key=value override configuration option\n"
" -E, --client-environment exec shell using footclient's environment, instead of the server's\n" " -E, --client-environment exec shell using footclient's environment, instead of the server's\n"
" -d,--log-level={info|warning|error|none} log level (info)\n" " -d,--log-level={info|warning|error|none} log level (warning)\n"
" -l,--log-colorize=[{never|always|auto}] enable/disable colorization of log output on stderr\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"; " -e ignored (for compatibility with xterm -e)\n";
@ -178,7 +178,7 @@ main(int argc, char *const *argv)
const char *custom_cwd = NULL; const char *custom_cwd = NULL;
const char *server_socket_path = NULL; const char *server_socket_path = NULL;
enum log_class log_level = LOG_CLASS_INFO; enum log_class log_level = LOG_CLASS_WARNING;
enum log_colorize log_colorize = LOG_COLORIZE_AUTO; enum log_colorize log_colorize = LOG_COLORIZE_AUTO;
bool hold = false; bool hold = false;
bool client_environment = false; bool client_environment = false;

View file

@ -31,7 +31,7 @@ _foot()
cur=${COMP_WORDS[COMP_CWORD]} cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]} prev=${COMP_WORDS[COMP_CWORD-1]}
# check if positional argument is completed # Check if positional argument is completed
previous_words=( "${COMP_WORDS[@]}" ) previous_words=( "${COMP_WORDS[@]}" )
unset previous_words[-1] unset previous_words[-1]
commands=$(compgen -c | grep -vFx "$(compgen -k)" | grep -vE '^([.:[]|foot)$' | sort -u) commands=$(compgen -c | grep -vFx "$(compgen -k)" | grep -vE '^([.:[]|foot)$' | sort -u)
@ -43,41 +43,45 @@ _foot()
(( i++ )) (( i++ ))
continue continue
fi fi
# positional argument found # Positional argument found
offset=$i offset=$i
fi fi
(( i++ )) (( i++ ))
done done
if [[ ! -z "$offset" ]] ; then if [[ ! -z "$offset" ]] ; then
# depends on bash_completion being available # Depends on bash_completion being available
declare -F _command_offset >/dev/null || return 1 declare -F _command_offset >/dev/null || return 1
_command_offset $offset _command_offset $offset
return 0
elif [[ ${cur} == --* ]] ; then elif [[ ${cur} == --* ]] ; then
COMPREPLY=( $(compgen -W "${flags}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${flags}" -- ${cur}) )
elif [[ ${prev} =~ ^(--config|--print-pid|--server)$ ]] ; then return 0
compopt -o default
elif [[ ${prev} == '--working-directory' ]] ; then
compopt -o dirnames
elif [[ ${prev} == '--term' ]] ; then
# check if toe is available
which toe > /dev/null || return 1
COMPREPLY=( $(compgen -W "$(toe -a | awk '$1 ~ /[+]/ {next}; {print $1}')" -- ${cur}) )
elif [[ ${prev} == '--font' ]] ; then
# check if fc-list is available
which fc-list > /dev/null || return 1
COMPREPLY=( $(compgen -W "$(fc-list : family | sed 's/,/\n/g' | uniq | tr -d ' ')" -- ${cur}) )
elif [[ ${prev} == '--log-level' ]] ; then
COMPREPLY=( $(compgen -W "none error warning info" -- ${cur}) )
elif [[ ${prev} == '--log-colorize' ]] ; then
COMPREPLY=( $(compgen -W "never always auto" -- ${cur}) )
elif [[ ${prev} =~ ^(--app-id|--help|--override|--title|--version|--window-size-chars|--window-size-pixels|--check-config)$ ]] ; then
: # don't autocomplete for these flags
else
# complete commands from $PATH
COMPREPLY=( $(compgen -c -- ${cur}) )
fi fi
case "$prev" in
--config|--print-pid|--server|-[cps])
compopt -o default ;;
--working-directory|-D)
compopt -o dirnames ;;
--term|-t)
command -v toe > /dev/null || return 1
COMPREPLY=( $(compgen -W "$(toe -a | awk '$1 !~ /[+]/ {print $1}')" -- ${cur}) ) ;;
--font|-f)
command -v fc-list > /dev/null || return 1
COMPREPLY=( $(compgen -W "$(fc-list : family | sed 's/,/\n/g' | uniq | tr -d ' ')" -- ${cur}) ) ;;
--log-level|-d)
COMPREPLY=( $(compgen -W "none error warning info" -- ${cur}) ) ;;
--log-colorize|-l)
COMPREPLY=( $(compgen -W "never always auto" -- ${cur}) ) ;;
--app-id|--help|--override|--title|--version|--window-size-chars|--window-size-pixels|--check-config|-[ahoTvWwC])
# Don't autocomplete for these flags
: ;;
*)
# Complete commands from $PATH
COMPREPLY=( $(compgen -c -- ${cur}) ) ;;
esac
return 0 return 0
} }

View file

@ -27,7 +27,7 @@ _footclient()
cur=${COMP_WORDS[COMP_CWORD]} cur=${COMP_WORDS[COMP_CWORD]}
prev=${COMP_WORDS[COMP_CWORD-1]} prev=${COMP_WORDS[COMP_CWORD-1]}
# check if positional argument is completed # Check if positional argument is completed
previous_words=( "${COMP_WORDS[@]}" ) previous_words=( "${COMP_WORDS[@]}" )
unset previous_words[-1] unset previous_words[-1]
commands=$(compgen -c | grep -vFx "$(compgen -k)" | grep -vE '^([.:[]|footclient)$' | sort -u) commands=$(compgen -c | grep -vFx "$(compgen -k)" | grep -vE '^([.:[]|footclient)$' | sort -u)
@ -39,37 +39,42 @@ _footclient()
(( i++ )) (( i++ ))
continue continue
fi fi
# positional argument found # Positional argument found
offset=$i offset=$i
fi fi
(( i++ )) (( i++ ))
done done
if [[ ! -z "$offset" ]] ; then if [[ ! -z "$offset" ]] ; then
# depends on bash_completion being available # Depends on bash_completion being available
declare -F _command_offset >/dev/null || return 1 declare -F _command_offset >/dev/null || return 1
_command_offset $offset _command_offset $offset
return 0
elif [[ ${cur} == --* ]] ; then elif [[ ${cur} == --* ]] ; then
COMPREPLY=( $(compgen -W "${flags}" -- ${cur}) ) COMPREPLY=( $(compgen -W "${flags}" -- ${cur}) )
elif [[ ${prev} == '--server-socket' ]] ; then return 0
compopt -o default
elif [[ ${prev} == '--working-directory' ]] ; then
compopt -o dirnames
elif [[ ${prev} == '--term' ]] ; then
# check if toe is available
which toe > /dev/null || return 1
COMPREPLY=( $(compgen -W "$(toe -a | awk '$1 ~ /[+]/ {next}; {print $1}')" -- ${cur}) )
elif [[ ${prev} == '--log-level' ]] ; then
COMPREPLY=( $(compgen -W "none error warning info" -- ${cur}) )
elif [[ ${prev} == '--log-colorize' ]] ; then
COMPREPLY=( $(compgen -W "never always auto" -- ${cur}) )
elif [[ ${prev} =~ ^(--app-id|--help|--override|--title|--version|--window-size-chars|--window-size-pixels|)$ ]] ; then
: # don't autocomplete for these flags
else
# complete commands from $PATH
COMPREPLY=( $(compgen -c -- ${cur}) )
fi fi
case "$prev" in
--server-socket|-s)
compopt -o default ;;
--working-directory|-D)
compopt -o dirnames ;;
--term|-t)
command -v toe > /dev/null || return 1
COMPREPLY=( $(compgen -W "$(toe -a | awk '$1 ~ /[+]/ {next}; {print $1}')" -- ${cur}) ) ;;
--log-level|-d)
COMPREPLY=( $(compgen -W "none error warning info" -- ${cur}) ) ;;
--log-colorize|-l)
COMPREPLY=( $(compgen -W "never always auto" -- ${cur}) ) ;;
--app-id|--help|--override|--title|--version|--window-size-chars|--window-size-pixels|-[ahoTvWw])
# Don't autocomplete for these flags
: ;;
*)
# Complete commands from $PATH
COMPREPLY=( $(compgen -c -- ${cur}) ) ;;
esac
return 0 return 0
} }

View file

@ -15,7 +15,7 @@ complete -c foot -x -s W -l window-size-chars
complete -c foot -F -s s -l server -d "run as server; open terminals by running footclient" complete -c foot -F -s s -l server -d "run as server; open terminals by running footclient"
complete -c foot -s H -l hold -d "remain open after child process exits" complete -c foot -s H -l hold -d "remain open after child process exits"
complete -c foot -r -s p -l print-pid -d "print PID to this file or FD when up and running (server mode only)" complete -c foot -r -s p -l print-pid -d "print PID to this file or FD when up and running (server mode only)"
complete -c foot -x -s d -l log-level -a "info warning error none" -d "log-level (info)" complete -c foot -x -s d -l log-level -a "info warning error none" -d "log-level (warning)"
complete -c foot -x -s l -l log-colorize -a "always never auto" -d "enable or disable colorization of log output on stderr" complete -c foot -x -s l -l log-colorize -a "always never auto" -d "enable or disable colorization of log output on stderr"
complete -c foot -s S -l log-no-syslog -d "disable syslog logging (server mode only)" complete -c foot -s S -l log-no-syslog -d "disable syslog logging (server mode only)"
complete -c foot -s v -l version -d "show the version number and quit" complete -c foot -s v -l version -d "show the version number and quit"

View file

@ -18,7 +18,7 @@ _arguments \
'(-s --server)'{-s,--server}'[run as server; open terminals by running footclient]:server:_files' \ '(-s --server)'{-s,--server}'[run as server; open terminals by running footclient]:server:_files' \
'(-H --hold)'{-H,--hold}'[remain open after child process exits]' \ '(-H --hold)'{-H,--hold}'[remain open after child process exits]' \
'(-p --print-pid)'{-p,--print-pid}'[print PID to this file or FD when up and running (server mode only)]:pidfile:_files' \ '(-p --print-pid)'{-p,--print-pid}'[print PID to this file or FD when up and running (server mode only)]:pidfile:_files' \
'(-d --log-level)'{-d,--log-level}'[log level (info)]:loglevel:(info warning error none)' \ '(-d --log-level)'{-d,--log-level}'[log level (warning)]:loglevel:(info warning error none)' \
'(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \ '(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \
'(-S --log-no-syslog)'{-s,--log-no-syslog}'[disable syslog logging (server mode only)]' \ '(-S --log-no-syslog)'{-s,--log-no-syslog}'[disable syslog logging (server mode only)]' \
'(-v --version)'{-v,--version}'[show the version number and quit]' \ '(-v --version)'{-v,--version}'[show the version number and quit]' \

View file

@ -16,7 +16,7 @@ _arguments \
'(-N --no-wait)'{-N,--no-wait}'[detach the client process from the running terminal, exiting immediately]' \ '(-N --no-wait)'{-N,--no-wait}'[detach the client process from the running terminal, exiting immediately]' \
'(-o --override)'{-o,--override}'[configuration option to override, in form SECTION.KEY=VALUE]:()' \ '(-o --override)'{-o,--override}'[configuration option to override, in form SECTION.KEY=VALUE]:()' \
'(-E --client-environment)'{-E,--client-environment}"[child process inherits footclient's environment, instead of the server's]" \ '(-E --client-environment)'{-E,--client-environment}"[child process inherits footclient's environment, instead of the server's]" \
'(-d --log-level)'{-d,--log-level}'[log level (info)]:loglevel:(info warning error none)' \ '(-d --log-level)'{-d,--log-level}'[log level (warning)]:loglevel:(info warning error none)' \
'(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \ '(-l --log-colorize)'{-l,--log-colorize}'[enable or disable colorization of log output on stderr]:logcolor:(never always auto)' \
'(-v --version)'{-v,--version}'[show the version number and quit]' \ '(-v --version)'{-v,--version}'[show the version number and quit]' \
'(-h --help)'{-h,--help}'[show help message and quit]' \ '(-h --help)'{-h,--help}'[show help message and quit]' \

138
config.c
View file

@ -503,8 +503,48 @@ value_to_double(struct context *ctx, float *res)
static bool NOINLINE static bool NOINLINE
value_to_str(struct context *ctx, char **res) value_to_str(struct context *ctx, char **res)
{ {
char *copy = xstrdup(ctx->value);
char *end = copy + strlen(copy) - 1;
/* Un-quote
*
* Note: this is very simple; we only support the *entire* value
* being quoted. That is, no mid-value quotes. Both double and
* single quotes are supported.
*
* - key="value" OK
* - key=abc "quote" def NOT OK
* - key=value OK
*
* Finally, we support escaping the quote character, and the
* escape character itself:
*
* - key="value \"quotes\""
* - key="backslash: \\"
*
* ONLY the "current" quote character can be escaped:
*
* key="value \'" NOt OK (both backslash and single quote is kept)
*/
if ((copy[0] == '"' && *end == '"') ||
(copy[0] == '\'' && *end == '\''))
{
const char quote = copy[0];
*end = '\0';
memmove(copy, copy + 1, end - copy);
/* Un-escape */
for (char *p = copy; *p != '\0'; p++) {
if (p[0] == '\\' && (p[1] == '\\' || p[1] == quote)) {
memmove(p, p + 1, end - p);
}
}
}
free(*res); free(*res);
*res = xstrdup(ctx->value); *res = copy;
return true; return true;
} }
@ -612,7 +652,7 @@ value_to_pt_or_px(struct context *ctx, struct pt_or_px *res)
char *end = NULL; char *end = NULL;
long value = strtol(s, &end, 10); long value = strtol(s, &end, 10);
if (!(errno == 0 && end == s + len - 2)) { if (!(len > 2 && errno == 0 && end == s + len - 2)) {
LOG_CONTEXTUAL_ERR("invalid px value (must be in the form 12px)"); LOG_CONTEXTUAL_ERR("invalid px value (must be in the form 12px)");
return false; return false;
} }
@ -886,6 +926,31 @@ parse_section_main(struct context *ctx)
return true; return true;
} }
else if (strcmp(key, "font-size-adjustment") == 0) {
const size_t len = strlen(ctx->value);
if (len >= 1 && ctx->value[len - 1] == '%') {
errno = 0;
char *end = NULL;
float percent = strtof(ctx->value, &end);
if (!(len > 1 && errno == 0 && end == ctx->value + len - 1)) {
LOG_CONTEXTUAL_ERR(
"invalid percent value (must be in the form 10.5%%)");
return false;
}
conf->font_size_adjustment.percent = percent / 100.;
conf->font_size_adjustment.pt_or_px.pt = 0;
conf->font_size_adjustment.pt_or_px.px = 0;
return true;
} else {
bool ret = value_to_pt_or_px(ctx, &conf->font_size_adjustment.pt_or_px);
if (ret)
conf->font_size_adjustment.percent = 0.;
return ret;
}
}
else if (strcmp(key, "line-height") == 0) else if (strcmp(key, "line-height") == 0)
return value_to_pt_or_px(ctx, &conf->line_height); return value_to_pt_or_px(ctx, &conf->line_height);
@ -1413,6 +1478,9 @@ parse_section_csd(struct context *ctx)
static void static void
free_binding_aux(struct binding_aux *aux) free_binding_aux(struct binding_aux *aux)
{ {
if (!aux->master_copy)
return;
switch (aux->type) { switch (aux->type) {
case BINDING_AUX_NONE: break; case BINDING_AUX_NONE: break;
case BINDING_AUX_PIPE: free_argv(&aux->pipe); break; case BINDING_AUX_PIPE: free_argv(&aux->pipe); break;
@ -2256,21 +2324,22 @@ parse_section_environment(struct context *ctx)
{ {
struct config *conf = ctx->conf; struct config *conf = ctx->conf;
const char *key = ctx->key; const char *key = ctx->key;
const char *value = ctx->value;
/* Check for pre-existing env variable */
tll_foreach(conf->env_vars, it) { tll_foreach(conf->env_vars, it) {
if (strcmp(it->item.name, key) == 0) { if (strcmp(it->item.name, key) == 0)
free(it->item.value); return value_to_str(ctx, &it->item.value);
it->item.value = xstrdup(value);
return true;
}
} }
struct env_var var = { /*
.name = xstrdup(key), * No pre-existing variable - allocate a new one
.value = xstrdup(value), */
};
tll_push_back(conf->env_vars, var); char *value = NULL;
if (!value_to_str(ctx, &value))
return false;
tll_push_back(conf->env_vars, ((struct env_var){xstrdup(key), value}));
return true; return true;
} }
@ -2847,6 +2916,7 @@ config_load(struct config *conf, const char *conf_path,
}, },
.startup_mode = STARTUP_WINDOWED, .startup_mode = STARTUP_WINDOWED,
.fonts = {{0}}, .fonts = {{0}},
.font_size_adjustment = {.percent = 0., .pt_or_px = {.pt = 0.5, .px = 0}},
.line_height = {.pt = 0, .px = -1}, .line_height = {.pt = 0, .px = -1},
.letter_spacing = {.pt = 0, .px = 0}, .letter_spacing = {.pt = 0, .px = 0},
.horizontal_letter_offset = {.pt = 0, .px = 0}, .horizontal_letter_offset = {.pt = 0, .px = 0},
@ -2938,7 +3008,7 @@ config_load(struct config *conf, const char *conf_path,
#if defined(FOOT_GRAPHEME_CLUSTERING) && FOOT_GRAPHEME_CLUSTERING #if defined(FOOT_GRAPHEME_CLUSTERING) && FOOT_GRAPHEME_CLUSTERING
.grapheme_shaping = fcft_caps & FCFT_CAPABILITY_GRAPHEME_SHAPING, .grapheme_shaping = fcft_caps & FCFT_CAPABILITY_GRAPHEME_SHAPING,
#endif #endif
.grapheme_width_method = GRAPHEME_WIDTH_WCSWIDTH, .grapheme_width_method = GRAPHEME_WIDTH_DOUBLE,
.delayed_render_lower_ns = 500000, /* 0.5ms */ .delayed_render_lower_ns = 500000, /* 0.5ms */
.delayed_render_upper_ns = 16666666 / 2, /* half a frame period (60Hz) */ .delayed_render_upper_ns = 16666666 / 2, /* half a frame period (60Hz) */
.max_shm_pool_size = 512 * 1024 * 1024, .max_shm_pool_size = 512 * 1024 * 1024,
@ -3322,20 +3392,48 @@ config_font_parse(const char *pattern, struct config_font *font)
if (pat == NULL) if (pat == NULL)
return false; return false;
/*
* First look for user specified {pixel}size option
* e.g. font-name:size=12
*/
double pt_size = -1.0; double pt_size = -1.0;
FcPatternGetDouble(pat, FC_SIZE, 0, &pt_size); FcResult have_pt_size = FcPatternGetDouble(pat, FC_SIZE, 0, &pt_size);
FcPatternRemove(pat, FC_SIZE, 0);
int px_size = -1; int px_size = -1;
FcPatternGetInteger(pat, FC_PIXEL_SIZE, 0, &px_size); FcResult have_px_size = FcPatternGetInteger(pat, FC_PIXEL_SIZE, 0, &px_size);
FcPatternRemove(pat, FC_PIXEL_SIZE, 0);
if (pt_size == -1. && px_size == -1) if (have_pt_size != FcResultMatch && have_px_size != FcResultMatch) {
pt_size = 8.0; /*
* Apply fontconfig config. Cant do that until weve first
* checked for a user provided size, since we may end up with
* both size and pixelsize being set, and we dont know
* which one takes priority.
*/
FcPattern *pat_copy = FcPatternDuplicate(pat);
if (pat_copy == NULL ||
!FcConfigSubstitute(NULL, pat_copy, FcMatchPattern))
{
LOG_WARN("%s: failed to do config substitution", pattern);
} else {
have_pt_size = FcPatternGetDouble(pat_copy, FC_SIZE, 0, &pt_size);
have_px_size = FcPatternGetInteger(pat_copy, FC_PIXEL_SIZE, 0, &px_size);
}
FcPatternDestroy(pat_copy);
if (have_pt_size != FcResultMatch && have_px_size != FcResultMatch)
pt_size = 8.0;
}
FcPatternRemove(pat, FC_SIZE, 0);
FcPatternRemove(pat, FC_PIXEL_SIZE, 0);
char *stripped_pattern = (char *)FcNameUnparse(pat); char *stripped_pattern = (char *)FcNameUnparse(pat);
FcPatternDestroy(pat); FcPatternDestroy(pat);
LOG_DBG("%s: pt-size=%.2f, px-size=%d", stripped_pattern, pt_size, px_size);
*font = (struct config_font){ *font = (struct config_font){
.pattern = stripped_pattern, .pattern = stripped_pattern,
.pt_size = pt_size, .pt_size = pt_size,

View file

@ -22,6 +22,11 @@ struct pt_or_px {
float pt; float pt;
}; };
struct font_size_adjustment {
struct pt_or_px pt_or_px;
float percent;
};
enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BEAM }; enum cursor_style { CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BEAM };
enum conf_size_type {CONF_SIZE_PX, CONF_SIZE_CELLS}; enum conf_size_type {CONF_SIZE_PX, CONF_SIZE_CELLS};
@ -69,11 +74,6 @@ enum key_binding_type {
MOUSE_BINDING, MOUSE_BINDING,
}; };
struct config_key_binding_text {
char *text;
bool master_copy;
};
struct config_key_binding { struct config_key_binding {
int action; /* One of the varios bind_action_* enums from wayland.h */ int action; /* One of the varios bind_action_* enums from wayland.h */
struct config_key_modifiers modifiers; struct config_key_modifiers modifiers;
@ -139,6 +139,7 @@ struct config {
enum {DPI_AWARE_AUTO, DPI_AWARE_YES, DPI_AWARE_NO} dpi_aware; enum {DPI_AWARE_AUTO, DPI_AWARE_YES, DPI_AWARE_NO} dpi_aware;
struct config_font_list fonts[4]; struct config_font_list fonts[4];
struct font_size_adjustment font_size_adjustment;
/* Custom font metrics (-1 = use real font metrics) */ /* Custom font metrics (-1 = use real font metrics) */
struct pt_or_px line_height; struct pt_or_px line_height;

27
csi.c
View file

@ -276,21 +276,6 @@ decset_decrst(struct terminal *term, unsigned param, bool enable)
enable ? CURSOR_KEYS_APPLICATION : CURSOR_KEYS_NORMAL; enable ? CURSOR_KEYS_APPLICATION : CURSOR_KEYS_NORMAL;
break; break;
case 3:
/* DECCOLM */
if (enable)
LOG_WARN("unimplemented: 132 column mode (DECCOLM)");
term_erase(term, 0, 0, term->rows - 1, term->cols - 1);
term_cursor_home(term);
break;
case 4:
/* DECSCLM - Smooth scroll */
if (enable)
LOG_WARN("unimplemented: Smooth (Slow) Scroll (DECSCLM)");
break;
case 5: case 5:
/* DECSCNM */ /* DECSCNM */
term->reverse = enable; term->reverse = enable;
@ -558,8 +543,6 @@ decrqm(const struct terminal *term, unsigned param)
{ {
switch (param) { switch (param) {
case 1: return decrpm(term->cursor_keys_mode == CURSOR_KEYS_APPLICATION); case 1: return decrpm(term->cursor_keys_mode == CURSOR_KEYS_APPLICATION);
case 3: return DECRPM_PERMANENTLY_RESET;
case 4: return DECRPM_PERMANENTLY_RESET;
case 5: return decrpm(term->reverse); case 5: return decrpm(term->reverse);
case 6: return decrpm(term->origin); case 6: return decrpm(term->origin);
case 7: return decrpm(term->auto_margin); case 7: return decrpm(term->auto_margin);
@ -601,8 +584,6 @@ xtsave(struct terminal *term, unsigned param)
{ {
switch (param) { switch (param) {
case 1: term->xtsave.application_cursor_keys = term->cursor_keys_mode == CURSOR_KEYS_APPLICATION; break; case 1: term->xtsave.application_cursor_keys = term->cursor_keys_mode == CURSOR_KEYS_APPLICATION; break;
case 3: break;
case 4: break;
case 5: term->xtsave.reverse = term->reverse; break; case 5: term->xtsave.reverse = term->reverse; break;
case 6: term->xtsave.origin = term->origin; break; case 6: term->xtsave.origin = term->origin; break;
case 7: term->xtsave.auto_margin = term->auto_margin; break; case 7: term->xtsave.auto_margin = term->auto_margin; break;
@ -644,8 +625,6 @@ xtrestore(struct terminal *term, unsigned param)
bool enable; bool enable;
switch (param) { switch (param) {
case 1: enable = term->xtsave.application_cursor_keys; break; case 1: enable = term->xtsave.application_cursor_keys; break;
case 3: return;
case 4: return;
case 5: enable = term->xtsave.reverse; break; case 5: enable = term->xtsave.reverse; break;
case 6: enable = term->xtsave.origin; break; case 6: enable = term->xtsave.origin; break;
case 7: enable = term->xtsave.auto_margin; break; case 7: enable = term->xtsave.auto_margin; break;
@ -933,7 +912,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
break; break;
} }
case 'L': { case 'L': { /* IL */
if (term->grid->cursor.point.row < term->scroll_region.start || if (term->grid->cursor.point.row < term->scroll_region.start ||
term->grid->cursor.point.row >= term->scroll_region.end) term->grid->cursor.point.row >= term->scroll_region.end)
break; break;
@ -953,7 +932,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
break; break;
} }
case 'M': { case 'M': { /* DL */
if (term->grid->cursor.point.row < term->scroll_region.start || if (term->grid->cursor.point.row < term->scroll_region.start ||
term->grid->cursor.point.row >= term->scroll_region.end) term->grid->cursor.point.row >= term->scroll_region.end)
break; break;
@ -1518,7 +1497,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
break; /* final == 'm' */ break; /* final == 'm' */
case 'n': { case 'n': {
int resource = vt_param_get(term, 0, 2); /* Default is modifyFuncionKeys */ int resource = vt_param_get(term, 0, 2); /* Default is modifyFunctionKeys */
switch (resource) { switch (resource) {
case 0: /* modifyKeyboard */ case 0: /* modifyKeyboard */
case 1: /* modifyCursorKeys */ case 1: /* modifyCursorKeys */

View file

@ -22,7 +22,7 @@ This document describes all the control sequences supported by foot.
[[ *Sequence* [[ *Sequence*
:[ *Name* :[ *Name*
:[ *Description* :< *Description*
| \\a | \\a
: BEL : BEL
: Depends on what *bell* in *foot.ini*(5) is set to. : Depends on what *bell* in *foot.ini*(5) is set to.
@ -60,7 +60,7 @@ equivalent to 8-bit C1 controls.
[[ *Sequence* [[ *Sequence*
:[ *Name* :[ *Name*
:[ *Origin* :[ *Origin*
:[ *Description* :< *Description*
| \\E 7 | \\E 7
: DECSC : DECSC
: VT100 : VT100
@ -140,7 +140,7 @@ single CSI sequence by separating them with semicolons: *\\E[ 1;2;3
m*. m*.
[[ *Parameter* [[ *Parameter*
:[ *Description* :< *Description*
| 0 | 0
: Reset all attributes : Reset all attributes
| 1 | 1
@ -223,7 +223,7 @@ following 4 escape sequences:
[[ *Sequence* [[ *Sequence*
:[ *Name* :[ *Name*
:[ *Description* :< *Description*
| \\E[ ? _Pm_ h | \\E[ ? _Pm_ h
: DECSET : DECSET
: Enable private mode : Enable private mode
@ -243,7 +243,7 @@ that corresponds to one of the following modes:
[[ *Parameter* [[ *Parameter*
:[ *Origin* :[ *Origin*
:[ *Description* :< *Description*
| 1 | 1
: VT100 : VT100
: Cursor keys mode (DECCKM) : Cursor keys mode (DECCKM)
@ -344,7 +344,7 @@ manipulation sequences. The generic format is:
[[ *Parameter 1* [[ *Parameter 1*
:[ *Parameter 2* :[ *Parameter 2*
:[ *Description* :< *Description*
| 11 | 11
: - : -
: Report if window is iconified. Foot always reports *1* - not iconified. : Report if window is iconified. Foot always reports *1* - not iconified.
@ -394,7 +394,7 @@ manipulation sequences. The generic format is:
[[ *Parameter* [[ *Parameter*
:[ *Name* :[ *Name*
:[ *Origin* :[ *Origin*
:[ *Description* :< *Description*
| \\E[ _Ps_ c | \\E[ _Ps_ c
: DA : DA
: VT100 : VT100
@ -504,7 +504,7 @@ manipulation sequences. The generic format is:
| \\E[ = _Ps_ c | \\E[ = _Ps_ c
: DA3 : DA3
: VT510 : VT510
: send tertiary device attributes. Foot responds with "FOOT", in : Send tertiary device attributes. Foot responds with "FOOT", in
hexadecimal. hexadecimal.
| \\E[ _Pm_ d | \\E[ _Pm_ d
: VPA : VPA
@ -595,7 +595,7 @@ All _OSC_ sequences begin with *\\E]*, sometimes abbreviated _OSC_.
[[ *Sequence* [[ *Sequence*
:[ *Origin* :[ *Origin*
:[ *Description* :< *Description*
| \\E] 0 ; _Pt_ \\E\\ | \\E] 0 ; _Pt_ \\E\\
: xterm : xterm
: Set window icon and title to _Pt_ (foot does not support setting the : Set window icon and title to _Pt_ (foot does not support setting the
@ -693,7 +693,7 @@ All _DCS_ sequences begin with *\\EP* (sometimes abbreviated _DCS_),
and are terminated by *\\E\\* (ST). and are terminated by *\\E\\* (ST).
[[ *Sequence* [[ *Sequence*
:[ *Description* :< *Description*
| \\EP q <sixel data> \\E\\ | \\EP q <sixel data> \\E\\
: Emit a sixel image at the current cursor position : Emit a sixel image at the current cursor position
| \\P $ q <query> \\E\\ | \\P $ q <query> \\E\\
@ -703,7 +703,7 @@ and are terminated by *\\E\\* (ST).
: Begin (_C_=*1*) or end (_C_=*2*) application synchronized updates. : Begin (_C_=*1*) or end (_C_=*2*) application synchronized updates.
This sequence is supported for compatibility reasons, but it's This sequence is supported for compatibility reasons, but it's
recommended to use private mode 2026 (see above) instead. recommended to use private mode 2026 (see above) instead.
| \\EP + q <hex encoded capability name> \\E\\ | \\EP + q <hex encoded capability name> \\E\\
: Query builtin terminfo database (XTGETTCAP) : Query builtin terminfo database (XTGETTCAP)

View file

@ -142,7 +142,7 @@ the foot command line
*-d*,*--log-level*={*info*,*warning*,*error*,*none*} *-d*,*--log-level*={*info*,*warning*,*error*,*none*}
Log level, used both for log output on stderr as well as Log level, used both for log output on stderr as well as
syslog. Default: _info_. syslog. Default: _warning_.
*-l*,*--log-colorize*=[{*never*,*always*,*auto*}] *-l*,*--log-colorize*=[{*never*,*always*,*auto*}]
Enables or disables colorization of log output on stderr. Default: Enables or disables colorization of log output on stderr. Default:
@ -543,10 +543,24 @@ In all other cases, the exit code is that of the client application
set according to either the *--term* command-line option or the set according to either the *--term* command-line option or the
*term* config option in *foot.ini*(5). *term* config option in *foot.ini*(5).
*PWD*
Current working directory (at the time of launching foot)
*COLORTERM* *COLORTERM*
This variable is set to *truecolor*, to indicate to client This variable is set to *truecolor*, to indicate to client
applications that 24-bit RGB colors are supported. applications that 24-bit RGB colors are supported.
*TERM_PROGRAM*
Always set to *foot*. This can be used by client applications to
check which terminal is in use, but with the caveat that it may
have been inherited from a parent process in other terminals that
aren't known to set the variable.
*TERM_PROGRAM_VERSION*
Set to the foot version string, in the format _major_*.*_minor_*.*_patch_
or _major_*.*_minor_*.*_patch_*-*_revision_*-\g*_commit_ for inter-release
builds. The same caveat as for *TERM_PROGRAM* applies.
In addition to the variables listed above, custom environment In addition to the variables listed above, custom environment
variables may be defined in *foot.ini*(5). variables may be defined in *foot.ini*(5).
@ -561,7 +575,7 @@ the same issue.
The report should contain the following: The report should contain the following:
- Foot version (*foot --version*). - Foot version (*foot --version*).
- Log output from foot (start foot from another terminal). - Log output from foot (run *foot -d info* from another terminal).
- Which Wayland compositor (and version) you are running. - Which Wayland compositor (and version) you are running.
- If reporting a crash, please try to provide a *bt full* backtrace - If reporting a crash, please try to provide a *bt full* backtrace
with symbols. with symbols.

View file

@ -22,6 +22,15 @@ in this order:
An example configuration file containing all options with their default value An example configuration file containing all options with their default value
commented out will usually be installed to */etc/xdg/foot/foot.ini*. commented out will usually be installed to */etc/xdg/foot/foot.ini*.
Options are set using KEY=VALUE pairs:
*\[colors\]*++
*background=000000*++
*foreground=ffffff*
Empty values (*KEY=*) are not supported. String options do allow the
empty string to be set, but it must be quoted: *KEY=""*)
# SECTION: main # SECTION: main
*shell* *shell*
@ -71,6 +80,19 @@ commented out will usually be installed to */etc/xdg/foot/foot.ini*.
Default: _monospace:size=8_ (*font*), _not set_ (*font-bold*, Default: _monospace:size=8_ (*font*), _not set_ (*font-bold*,
*font-italic*, *font-bold-italic*). *font-italic*, *font-bold-italic*).
*font-size-adjustment*
Amount, in _points_, _pixels_ or _percent_, to increment/decrement
the font size when zooming in our out.
Examples:
```
font-size-adjustment=0.5 # Adjust by 0.5 points
font-size-adjustment=10xp # Adjust by 10 pixels
font-size-adjustment=7.5% # Adjust by 7.5 percent
```
Default: _0.5_
*include* *include*
Absolute path to configuration file to import. Absolute path to configuration file to import.
@ -213,15 +235,19 @@ commented out will usually be installed to */etc/xdg/foot/foot.ini*.
Default: _0x0_. Default: _0x0_.
*resize-delay-ms* *resize-delay-ms*
Time, in milliseconds, of "idle time" before foot sends the new
window dimensions to the client application while doing an Time, in milliseconds, of "idle time" before foot performs text
interactive resize of a foot window. Idle time in this context is reflow, and sends the new window dimensions to the client
a period of time where the window size is not changing. 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.
In other words, while you are fiddling with the window size, foot In other words, while you are fiddling with the window size, foot
does not send the updated dimensions to the client. Only when you does not send the updated dimensions to the client. It also does a
pause the fiddling for *resize-delay-ms* milliseconds is the fast "truncating" resize of the grid, instead of actually
client updated. reflowing the contents. Only when you pause the fiddling for
*resize-delay-ms* milliseconds is the client updated, and the
contents properly reflowed.
Emphasis is on _while_ here; as soon as the interactive resize Emphasis is on _while_ here; as soon as the interactive resize
ends (i.e. when you let go of the window border), the final ends (i.e. when you let go of the window border), the final
@ -339,7 +365,7 @@ Note: do not set *TERM* here; use the *term* option in the main
*urgent* *urgent*
When set to _yes_, foot will signal urgency to the compositor When set to _yes_, foot will signal urgency to the compositor
through the XDG activation protocol whenever *BEL* is received, through the XDG activation protocol whenever *BEL* is received,
and the window does NOT have keyboard foccus. and the window does NOT have keyboard focus.
If the compositor does not implement this protocol, the margins If the compositor does not implement this protocol, the margins
will be painted in red instead. will be painted in red instead.
@ -448,14 +474,13 @@ applications can change these at runtime.
by applications. Default: _no_. by applications. Default: _no_.
*color* *color*
Two RRGGBB values (i.e. plain old 6-digit hex values, without Two space separated RRGGBB values (i.e. plain old 6-digit hex
prefix) specifying the foreground (text) and background (cursor) values, without prefix) specifying the foreground (text) and
colors for the cursor. background (cursor) colors for the cursor.
Default: _inverse foreground/background colors_. Example: *ff0000 00ff00* (green cursor, red text)
Note that this value only applies to the block cursor. The other Default: the regular foreground and background colors, reversed.
cursor styles are always rendered with the foreground color.
*beam-thickness* *beam-thickness*
Thickness (width) of the beam styled cursor. The value is in Thickness (width) of the beam styled cursor. The value is in
@ -551,7 +576,7 @@ can configure the background transparency with the _alpha_ option.
options are unconfigured). 24-bit RGB colors will typically fall options are unconfigured). 24-bit RGB colors will typically fall
into this category. into this category.
Note that applications can change the *regularN* and *brighN* Note that applications can change the *regularN* and *brightN*
colors at runtime. However, they have no way of changing the colors at runtime. However, they have no way of changing the
*dimN* colors. If an application has changed the *regularN* *dimN* colors. If an application has changed the *regularN*
colors, foot will still use the corresponding *dimN* color, as colors, foot will still use the corresponding *dimN* color, as
@ -939,7 +964,7 @@ Be careful; do not use single-letter keys that are also used in
original text. original text.
But with e.g. OSC-8 URLs (the terminal version of HTML anchors, But with e.g. OSC-8 URLs (the terminal version of HTML anchors,
i.e. "links"), the text on the screen can be something completey i.e. "links"), the text on the screen can be something completely
different than the URL. different than the URL.
This action toggles between showing and hiding the URL on the jump This action toggles between showing and hiding the URL on the jump
@ -1235,7 +1260,7 @@ any of these options.
*max* uses the width of the largest codepoint in the cluster. *max* uses the width of the largest codepoint in the cluster.
Default: _wcswidth_ Default: _double-width_
*font-monospace-warn* *font-monospace-warn*
Boolean. When enabled, foot will use heuristics to try to verify Boolean. When enabled, foot will use heuristics to try to verify

View file

@ -75,7 +75,7 @@ terminal has terminated.
*-d*,*--log-level*={*info*,*warning*,*error*,*none*} *-d*,*--log-level*={*info*,*warning*,*error*,*none*}
Log level, used both for log output on stderr as well as Log level, used both for log output on stderr as well as
syslog. Default: _info_. syslog. Default: _warning_.
*-l*,*--log-colorize*=[{*never*,*always*,*auto*}] *-l*,*--log-colorize*=[{*never*,*always*,*auto*}]
Enables or disables colorization of log output on stderr. Enables or disables colorization of log output on stderr.
@ -89,7 +89,7 @@ terminal has terminated.
# EXIT STATUS # EXIT STATUS
Footlient will exit with code 220 if there is a failure in footclient Footclient will exit with code 220 if there is a failure in footclient
itself (for example, the server socket does not exist). itself (for example, the server socket does not exist).
If *-N*,*--no-wait* is used, footclient exits with code 0 as soon as If *-N*,*--no-wait* is used, footclient exits with code 0 as soon as
@ -158,6 +158,17 @@ terminfo entries manually, by copying *foot* and *foot-direct* to
This variable is set to *truecolor*, to indicate to client This variable is set to *truecolor*, to indicate to client
applications that 24-bit RGB colors are supported. applications that 24-bit RGB colors are supported.
*TERM_PROGRAM*
Always set to *foot*. This can be used by client applications to
check which terminal is in use, but with the caveat that it may
have been inherited from a parent process in other terminals that
aren't known to set the variable.
*TERM_PROGRAM_VERSION*
Set to the foot version string, in the format _major_*.*_minor_*.*_patch_
or _major_*.*_minor_*.*_patch_*-*_revision_*-\g*_commit_ for inter-release
builds. The same caveat as for *TERM_PROGRAM* applies.
In addition to the variables listed above, custom environment In addition to the variables listed above, custom environment
variables may be defined in *foot.ini*(5). variables may be defined in *foot.ini*(5).

View file

@ -12,6 +12,10 @@
setaf=\E[%?%p1%{8}%<%t3%p1%d%e38\: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,
@default_terminfo@+base|foot base fragment, @default_terminfo@+base|foot base fragment,
AX,
Tc,
XF,
XT,
am, am,
bce, bce,
bw, bw,
@ -21,21 +25,24 @@
msgr, msgr,
npc, npc,
xenl, xenl,
AX,
XT,
Tc,
cols#80, cols#80,
it#8, it#8,
lines#24, lines#24,
pairs#0x10000, pairs#0x10000,
BD=\E[?2004l,
BE=\E[?2004h,
Cr=\E]112\E\\, Cr=\E]112\E\\,
Cs=\E]12;%p1%s\E\\, Cs=\E]12;%p1%s\E\\,
E3=\E[3J, E3=\E[3J,
Ms=\E]52;%p1%s;%p2%s\E\\, Ms=\E]52;%p1%s;%p2%s\E\\,
PE=\E[201~,
PS=\E[200~,
RV=\E[>c,
Se=\E[ q, Se=\E[ q,
Ss=\E[%p1%d q, Ss=\E[%p1%d q,
Sync=\E[?2026%?%p1%{1}%-%tl%eh, Sync=\E[?2026%?%p1%{1}%-%tl%eh,
XM=\E[?1006;1000%?%p1%{1}%=%th%el%;, XM=\E[?1006;1000%?%p1%{1}%=%th%el%;,
XR=\E[>0q,
acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
bel=^G, bel=^G,
blink=\E[5m, blink=\E[5m,
@ -79,7 +86,7 @@
indn=\E[%p1%dS, indn=\E[%p1%dS,
initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\,
invis=\E[8m, invis=\E[8m,
is2=\E[!p\E[?3;4l\E[4l\E>, is2=\E[!p\E[4l\E>,
kDC3=\E[3;3~, kDC3=\E[3;3~,
kDC4=\E[3;4~, kDC4=\E[3;4~,
kDC5=\E[3;5~, kDC5=\E[3;5~,
@ -237,7 +244,8 @@
rmul=\E[24m, rmul=\E[24m,
rmxx=\E[29m, rmxx=\E[29m,
rs1=\Ec, rs1=\Ec,
rs2=\E[!p\E[?3;4l\E[4l\E>, rs2=\E[!p\E[4l\E>,
rv=\E\\[[0-9]+;[0-9]+;[0-9]+c,
sc=\E7, sc=\E7,
setrgbb=\E[48\:2\:\:%p1%d\:%p2%d\:%p3%dm, setrgbb=\E[48\:2\:\:%p1%d\:%p2%d\:%p3%dm,
setrgbf=\E[38\:2\:\:%p1%d\:%p2%d\:%p3%dm, setrgbf=\E[38\:2\:\:%p1%d\:%p2%d\:%p3%dm,
@ -260,6 +268,7 @@
u9=\E[c, u9=\E[c,
vpa=\E[%i%p1%dd, vpa=\E[%i%p1%dd,
xm=\E[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;, xm=\E[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;,
xr=\EP>\\|[ -~]+\E\\\\,
# XT, # XT,
# AX, # AX,

View file

@ -12,6 +12,7 @@
# font-bold=<bold variant of regular font> # font-bold=<bold variant of regular font>
# font-italic=<italic variant of regular font> # font-italic=<italic variant of regular font>
# font-bold-italic=<bold+italic variant of regular font> # font-bold-italic=<bold+italic variant of regular font>
# font-size-adjustment=0.5
# line-height=<font metrics> # line-height=<font metrics>
# letter-spacing=0 # letter-spacing=0
# horizontal-letter-offset=0 # horizontal-letter-offset=0
@ -48,7 +49,7 @@
# lines=1000 # lines=1000
# multiplier=3.0 # multiplier=3.0
# indicator-position=relative # indicator-position=relative
# indicator-format= # indicator-format=""
[url] [url]
# launch=xdg-open ${url} # launch=xdg-open ${url}

View file

@ -41,6 +41,7 @@ patch=$(echo "${new_version}" | sed -r 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\3/')
extra=$(echo "${new_version}" | sed -r 's/([0-9]+)\.([0-9]+)\.([0-9]+)(-([0-9]+-g[a-z0-9]+) .*)?.*/\5/') extra=$(echo "${new_version}" | sed -r 's/([0-9]+)\.([0-9]+)\.([0-9]+)(-([0-9]+-g[a-z0-9]+) .*)?.*/\5/')
new_version="#define FOOT_VERSION \"${new_version}\" new_version="#define FOOT_VERSION \"${new_version}\"
#define FOOT_VERSION_SHORT \"${git_version:-${default_version}}\"
#define FOOT_MAJOR ${major} #define FOOT_MAJOR ${major}
#define FOOT_MINOR ${minor} #define FOOT_MINOR ${minor}
#define FOOT_PATCH ${patch} #define FOOT_PATCH ${patch}

11
grid.c
View file

@ -210,6 +210,8 @@ grid_snapshot(const struct grid *grid)
clone->offset = grid->offset; clone->offset = grid->offset;
clone->view = grid->view; clone->view = grid->view;
clone->cursor = grid->cursor; clone->cursor = grid->cursor;
clone->saved_cursor = grid->saved_cursor;
clone->kitty_kbd = grid->kitty_kbd;
clone->rows = xcalloc(grid->num_rows, sizeof(clone->rows[0])); clone->rows = xcalloc(grid->num_rows, sizeof(clone->rows[0]));
memset(&clone->scroll_damage, 0, sizeof(clone->scroll_damage)); memset(&clone->scroll_damage, 0, sizeof(clone->scroll_damage));
memset(&clone->sixel_images, 0, sizeof(clone->sixel_images)); memset(&clone->sixel_images, 0, sizeof(clone->sixel_images));
@ -285,6 +287,9 @@ grid_snapshot(const struct grid *grid)
void void
grid_free(struct grid *grid) grid_free(struct grid *grid)
{ {
if (grid == NULL)
return;
for (int r = 0; r < grid->num_rows; r++) for (int r = 0; r < grid->num_rows; r++)
grid_row_free(grid->rows[r]); grid_row_free(grid->rows[r]);
@ -483,6 +488,8 @@ grid_resize_without_reflow(
grid->saved_cursor.point = saved_cursor; grid->saved_cursor.point = saved_cursor;
grid->cur_row = new_grid[(grid->offset + cursor.row) & (new_rows - 1)]; grid->cur_row = new_grid[(grid->offset + cursor.row) & (new_rows - 1)];
xassert(grid->cur_row != NULL);
grid->cursor.lcf = false; grid->cursor.lcf = false;
grid->saved_cursor.lcf = false; grid->saved_cursor.lcf = false;
@ -786,7 +793,7 @@ grid_resize_and_reflow(
/* /*
* Set end-coordinate for this chunk, by finding the next * Set end-coordinate for this chunk, by finding the next
* point-of-interrest on this row. * point-of-interest on this row.
* *
* If there are no more tracking points, or URI ranges, * If there are no more tracking points, or URI ranges,
* the end-coordinate will be at the end of the row, * the end-coordinate will be at the end of the row,
@ -1045,6 +1052,8 @@ grid_resize_and_reflow(
saved_cursor.col = min(saved_cursor.col, new_cols - 1); saved_cursor.col = min(saved_cursor.col, new_cols - 1);
grid->cur_row = new_grid[(grid->offset + cursor.row) & (new_rows - 1)]; grid->cur_row = new_grid[(grid->offset + cursor.row) & (new_rows - 1)];
xassert(grid->cur_row != NULL);
grid->cursor.point = cursor; grid->cursor.point = cursor;
grid->saved_cursor.point = saved_cursor; grid->saved_cursor.point = saved_cursor;

41
input.c
View file

@ -898,7 +898,7 @@ struct kbd_ctx {
const uint8_t *buf; const uint8_t *buf;
size_t count; size_t count;
} utf8; } utf8;
uint32_t utf32; uint32_t *utf32;
enum xkb_compose_status compose_status; enum xkb_compose_status compose_status;
enum wl_keyboard_key_state key_state; enum wl_keyboard_key_state key_state;
@ -1121,12 +1121,18 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term,
(seat->kbd.mod_num != XKB_MOD_INVALID ? 1 << seat->kbd.mod_num : 0); (seat->kbd.mod_num != XKB_MOD_INVALID ? 1 << seat->kbd.mod_num : 0);
const xkb_keysym_t sym = ctx->sym; const xkb_keysym_t sym = ctx->sym;
const uint32_t utf32 = ctx->utf32; const uint32_t *utf32 = ctx->utf32;
const uint8_t *const utf8 = ctx->utf8.buf; const uint8_t *const utf8 = ctx->utf8.buf;
const bool is_text = iswprint(utf32) && (effective & ~caps_num) == 0;
const size_t count = ctx->utf8.count; const size_t count = ctx->utf8.count;
bool is_text = count > 0 && utf32 != NULL && (effective & ~caps_num) == 0;
for (size_t i = 0; utf32[i] != U'\0'; i++) {
if (!iswprint(utf32[i])) {
is_text = false;
break;
}
}
const bool report_associated_text = const bool report_associated_text =
(flags & KITTY_KBD_REPORT_ASSOCIATED) && is_text && !released; (flags & KITTY_KBD_REPORT_ASSOCIATED) && is_text && !released;
@ -1245,7 +1251,7 @@ emit_escapes:
: sym; : sym;
if (composed) if (composed)
key = utf32; key = utf32[0]; /* TODO: what if there are multiple codepoints? */
else { else {
key = xkb_keysym_to_utf32(sym_to_use); key = xkb_keysym_to_utf32(sym_to_use);
if (key == 0) if (key == 0)
@ -1284,7 +1290,7 @@ emit_escapes:
} else } else
event[0] = '\0'; event[0] = '\0';
char buf[64], *p = buf; char buf[128], *p = buf;
size_t left = sizeof(buf); size_t left = sizeof(buf);
size_t bytes; size_t bytes;
@ -1316,8 +1322,16 @@ emit_escapes:
} }
if (report_associated_text) { if (report_associated_text) {
bytes = snprintf(p, left, "%s;%u", !emit_mods ? ";" : "", utf32); bytes = snprintf(p, left, "%s;%u", !emit_mods ? ";" : "", utf32[0]);
p += bytes; left -= bytes; p += bytes; left -= bytes;
/* Additional text codepoints */
if (utf32[0] != U'\0') {
for (size_t i = 1; utf32[i] != U'\0'; i++) {
bytes = snprintf(p, left, ":%u", utf32[i]);
p += bytes; left -= bytes;
}
}
} }
bytes = snprintf(p, left, "%c", final); bytes = snprintf(p, left, "%c", final);
@ -1514,19 +1528,20 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
* and use a malloc:ed buffer when necessary */ * and use a malloc:ed buffer when necessary */
uint8_t buf[32]; uint8_t buf[32];
uint8_t *utf8 = count < sizeof(buf) ? buf : xmalloc(count + 1); uint8_t *utf8 = count < sizeof(buf) ? buf : xmalloc(count + 1);
uint32_t utf32 = (uint32_t)-1; uint32_t *utf32 = NULL;
if (composed) { if (composed) {
xkb_compose_state_get_utf8( xkb_compose_state_get_utf8(
seat->kbd.xkb_compose_state, (char *)utf8, count + 1); seat->kbd.xkb_compose_state, (char *)utf8, count + 1);
char32_t wc; if (count > 0)
if (mbrtoc32(&wc, (const char *)utf8, count, &(mbstate_t){0}) == count) utf32 = ambstoc32((const char *)utf8);
utf32 = wc;
} else { } else {
xkb_state_key_get_utf8( xkb_state_key_get_utf8(
seat->kbd.xkb_state, key, (char *)utf8, count + 1); seat->kbd.xkb_state, key, (char *)utf8, count + 1);
utf32 = xkb_state_key_get_utf32(seat->kbd.xkb_state, key);
utf32 = xcalloc(2, sizeof(utf32[0]));
utf32[0] = xkb_state_key_get_utf32(seat->kbd.xkb_state, key);
} }
struct kbd_ctx ctx = { struct kbd_ctx ctx = {
@ -1563,6 +1578,8 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
selection_cancel(term); selection_cancel(term);
} }
free(utf32);
maybe_repeat: maybe_repeat:
clock_gettime( clock_gettime(
term->wl->presentation_clock_id, &term->render.input_time); term->wl->presentation_clock_id, &term->render.input_time);

View file

@ -350,6 +350,60 @@ maybe_repair_key_combo(const struct seat *seat,
return sym; return sym;
} }
static int
key_cmp(struct key_binding a, struct key_binding b)
{
xassert(a.type == b.type);
/*
* Sort bindings such that bindings with the same symbol are
* sorted with the binding having the most modifiers comes first.
*
* This fixes an issue where the wrong key binding are triggered
* when used with consumed modifiers.
*
* For example: if Control+BackSpace is bound before
* Control+Shift+BackSpace, then the latter binding is never
* triggered.
*
* Why? Because Shift is a consumed modifier. This means
* Control+BackSpace is the same as Control+Shift+BackSpace.
*
* By sorting bindings with more modifiers first, we work around
* the problem. But note that it is *just* a workaround, and Im
* not confident there arent cases where it doesnt work.
*
* See https://codeberg.org/dnkl/foot/issues/1280
*/
const int a_mod_count = __builtin_popcount(a.mods);
const int b_mod_count = __builtin_popcount(b.mods);
switch (a.type) {
case KEY_BINDING:
if (a.k.sym != b.k.sym)
return b.k.sym - a.k.sym;
return b_mod_count - a_mod_count;
case MOUSE_BINDING: {
if (a.m.button != b.m.button)
return b.m.button - a.m.button;
if (a_mod_count != b_mod_count)
return b_mod_count - a_mod_count;
return b.m.count - a.m.count;
}
}
BUG("invalid key binding type");
return 0;
}
static void NOINLINE
sort_binding_list(key_binding_list_t *list)
{
tll_sort(*list, key_cmp);
}
static void NOINLINE static void NOINLINE
convert_key_binding(struct key_set *set, convert_key_binding(struct key_set *set,
const struct config_key_binding *conf_binding, const struct config_key_binding *conf_binding,
@ -371,6 +425,7 @@ convert_key_binding(struct key_set *set,
}, },
}; };
tll_push_back(*bindings, binding); tll_push_back(*bindings, binding);
sort_binding_list(bindings);
} }
static void static void
@ -421,6 +476,7 @@ convert_mouse_binding(struct key_set *set,
}, },
}; };
tll_push_back(set->public.mouse, binding); tll_push_back(set->public.mouse, binding);
sort_binding_list(&set->public.mouse);
} }
static void static void

9
log.c
View file

@ -15,7 +15,7 @@
#include "xsnprintf.h" #include "xsnprintf.h"
static bool colorize = false; static bool colorize = false;
static bool do_syslog = true; static bool do_syslog = false;
static enum log_class log_level = LOG_CLASS_NONE; static enum log_class log_level = LOG_CLASS_NONE;
static const struct { static const struct {
@ -45,8 +45,13 @@ log_init(enum log_colorize _colorize, bool _do_syslog,
log_level = _log_level; log_level = _log_level;
int slvl = log_level_map[_log_level].syslog_equivalent; int slvl = log_level_map[_log_level].syslog_equivalent;
if (do_syslog && slvl != -1) { if (slvl < 0)
do_syslog = false;
if (do_syslog) {
openlog(NULL, /*LOG_PID*/0, facility_map[syslog_facility]); openlog(NULL, /*LOG_PID*/0, facility_map[syslog_facility]);
xassert(slvl >= 0);
setlogmask(LOG_UPTO(slvl)); setlogmask(LOG_UPTO(slvl));
} }
} }

13
main.c
View file

@ -83,7 +83,7 @@ print_usage(const char *prog_name)
" Without PATH, $XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock will be used.\n" " Without PATH, $XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock will be used.\n"
" -H,--hold remain open after child process exits\n" " -H,--hold remain open after child process exits\n"
" -p,--print-pid=FILE|FD print PID to file or FD (only applicable in server mode)\n" " -p,--print-pid=FILE|FD print PID to file or FD (only applicable in server mode)\n"
" -d,--log-level={info|warning|error|none} log level (info)\n" " -d,--log-level={info|warning|error|none} log level (warning)\n"
" -l,--log-colorize=[{never|always|auto}] enable/disable colorization of log output on stderr\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" " -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"
@ -236,7 +236,7 @@ main(int argc, char *const *argv)
bool fullscreen = false; bool fullscreen = false;
bool unlink_pid_file = false; bool unlink_pid_file = false;
const char *pid_file = NULL; const char *pid_file = NULL;
enum log_class log_level = LOG_CLASS_INFO; enum log_class log_level = LOG_CLASS_WARNING;
enum log_colorize log_colorize = LOG_COLORIZE_AUTO; enum log_colorize log_colorize = LOG_COLORIZE_AUTO;
bool log_syslog = true; bool log_syslog = true;
user_notifications_t user_notifications = tll_init(); user_notifications_t user_notifications = tll_init();
@ -433,8 +433,13 @@ main(int argc, char *const *argv)
const char *locale = setlocale(LC_CTYPE, ""); const char *locale = setlocale(LC_CTYPE, "");
if (locale == NULL) { if (locale == NULL) {
LOG_ERR("setlocale() failed"); /*
return ret; * If the user has configured an invalid locale, or a name of a locale
* that does not exist on this system, then the above call may return
* NULL. We should just continue with the fallback method below.
*/
LOG_WARN("setlocale() failed");
locale = "C";
} }
LOG_INFO("locale: %s", locale); LOG_INFO("locale: %s", locale);

View file

@ -1,5 +1,5 @@
project('foot', 'c', project('foot', 'c',
version: '1.13.1', version: '1.14.0',
license: 'MIT', license: 'MIT',
meson_version: '>=0.58.0', meson_version: '>=0.58.0',
default_options: [ default_options: [
@ -110,7 +110,7 @@ if utf8proc.found()
add_project_arguments('-DFOOT_GRAPHEME_CLUSTERING=1', language: 'c') add_project_arguments('-DFOOT_GRAPHEME_CLUSTERING=1', language: 'c')
endif endif
tllist = dependency('tllist', version: '>=1.0.4', fallback: 'tllist') tllist = dependency('tllist', version: '>=1.1.0', fallback: 'tllist')
fcft = dependency('fcft', version: ['>=3.0.1', '<4.0.0'], fallback: 'fcft') fcft = dependency('fcft', version: ['>=3.0.1', '<4.0.0'], fallback: 'fcft')
wayland_protocols_datadir = wayland_protocols.get_variable('pkgdatadir') wayland_protocols_datadir = wayland_protocols.get_variable('pkgdatadir')
@ -331,6 +331,7 @@ endif
subdir('completions') subdir('completions')
subdir('icons') subdir('icons')
subdir('utils')
if (get_option('tests')) if (get_option('tests'))
subdir('tests') subdir('tests')

View file

@ -9,3 +9,4 @@ Keywords=shell;prompt;command;commandline;
Name=Foot Server Name=Foot Server
GenericName=Terminal GenericName=Terminal
Comment=A wayland native terminal emulator (server) Comment=A wayland native terminal emulator (server)
StartupWMClass=foot

View file

@ -9,3 +9,4 @@ Keywords=shell;prompt;command;commandline;
Name=Foot Name=Foot
GenericName=Terminal GenericName=Terminal
Comment=A wayland native terminal emulator Comment=A wayland native terminal emulator
StartupWMClass=foot

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application"> <component type="desktop-application">
<id>org.codeberg.dnkl.foot</id> <id>org.codeberg.dnkl.foot</id>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>MIT</metadata_license>
<project_license>MIT</project_license> <project_license>MIT</project_license>
<developer_name>dnkl</developer_name> <developer_name>dnkl</developer_name>
<name>foot</name> <name>foot</name>

View file

@ -9,3 +9,4 @@ Keywords=shell;prompt;command;commandline;
Name=Foot Client Name=Foot Client
GenericName=Terminal GenericName=Terminal
Comment=A wayland native terminal emulator (client) Comment=A wayland native terminal emulator (client)
StartupWMClass=foot

View file

@ -15,6 +15,7 @@ rm -f "${blddir}"/pgo-ok
# To ensure profiling data is generated in the build directory # To ensure profiling data is generated in the build directory
cd "${blddir}" cd "${blddir}"
"${blddir}"/utils/xtgettcap
"${blddir}"/footclient --version "${blddir}"/footclient --version
"${blddir}"/foot \ "${blddir}"/foot \
--config=/dev/null \ --config=/dev/null \

View file

@ -21,6 +21,7 @@ rm -f "${blddir}"/pgo-ok
# To ensure profiling data is generated in the build directory # To ensure profiling data is generated in the build directory
cd "${blddir}" cd "${blddir}"
"${blddir}"/utils/xtgettcap
"${blddir}"/footclient --version "${blddir}"/footclient --version
"${blddir}"/foot --version "${blddir}"/foot --version
"${blddir}"/pgo "${pgo_data}" "${blddir}"/pgo "${pgo_data}"

View file

@ -228,10 +228,14 @@ main(int argc, const char *const *argv)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
struct row **rows = calloc(grid_row_count, sizeof(rows[0])); struct row **normal_rows = calloc(grid_row_count, sizeof(normal_rows[0]));
struct row **alt_rows = calloc(grid_row_count, sizeof(alt_rows[0]));
for (int i = 0; i < grid_row_count; i++) { for (int i = 0; i < grid_row_count; i++) {
rows[i] = calloc(1, sizeof(*rows[i])); normal_rows[i] = calloc(1, sizeof(*normal_rows[i]));
rows[i]->cells = calloc(col_count, sizeof(rows[i]->cells[0])); normal_rows[i]->cells = calloc(col_count, sizeof(normal_rows[i]->cells[0]));
alt_rows[i] = calloc(1, sizeof(*alt_rows[i]));
alt_rows[i]->cells = calloc(col_count, sizeof(alt_rows[i]->cells[0]));
} }
struct config conf = { struct config conf = {
@ -254,14 +258,14 @@ main(int argc, const char *const *argv)
.normal = { .normal = {
.num_rows = grid_row_count, .num_rows = grid_row_count,
.num_cols = col_count, .num_cols = col_count,
.rows = rows, .rows = normal_rows,
.cur_row = rows[0], .cur_row = normal_rows[0],
}, },
.alt = { .alt = {
.num_rows = grid_row_count, .num_rows = grid_row_count,
.num_cols = col_count, .num_cols = col_count,
.rows = rows, .rows = alt_rows,
.cur_row = rows[0], .cur_row = alt_rows[0],
}, },
.scale = 1, .scale = 1,
.width = col_count * 8, .width = col_count * 8,
@ -371,11 +375,17 @@ out:
tll_free(wayl.terms); tll_free(wayl.terms);
for (int i = 0; i < grid_row_count; i++) { for (int i = 0; i < grid_row_count; i++) {
free(rows[i]->cells); if (normal_rows[i] != NULL)
free(rows[i]); free(normal_rows[i]->cells);
free(normal_rows[i]);
if (alt_rows[i] != NULL)
free(alt_rows[i]->cells);
free(alt_rows[i]);
} }
free(rows); free(normal_rows);
free(alt_rows);
close(lower_fd); close(lower_fd);
close(upper_fd); close(upper_fd);
return ret; return ret;

View file

@ -30,7 +30,7 @@ do_pgo=no
CFLAGS="${CFLAGS-} -O3" CFLAGS="${CFLAGS-} -O3"
case $(${CC-cc} --version) in case $(${CC-cc} --version) in
*GCC*) *Free\ Software\ Foundation*)
compiler=gcc compiler=gcc
do_pgo=yes do_pgo=yes
;; ;;

269
render.c
View file

@ -420,6 +420,12 @@ cursor_colors_for_cell(const struct terminal *term, const struct cell *cell,
} else { } else {
*cursor_color = *fg; *cursor_color = *fg;
*text_color = *bg; *text_color = *bg;
if (unlikely(text_color->alpha != 0xffff)) {
/* The *only* color that can have transparency is the
* default background color */
*text_color = color_hex_to_pixman(term->colors.bg);
}
} }
} }
@ -639,17 +645,21 @@ render_cell(struct terminal *term, pixman_image_t *pix,
} }
} }
if (single == NULL && grapheme == NULL) { if (single == NULL && grapheme == NULL) {
xassert(base != 0); if (unlikely(base >= CELL_SPACER)) {
single = fcft_rasterize_char_utf32(font, base, term->font_subpixel);
if (single == NULL) {
glyph_count = 0; glyph_count = 0;
cell_cols = 1; cell_cols = 1;
} else { } else {
glyph_count = 1; xassert(base != 0);
glyphs = &single; single = fcft_rasterize_char_utf32(font, base, term->font_subpixel);
cell_cols = single->cols; if (single == NULL) {
glyph_count = 0;
cell_cols = 1;
} else {
glyph_count = 1;
glyphs = &single;
cell_cols = single->cols;
}
} }
} }
} }
@ -919,14 +929,19 @@ static void
grid_render_scroll(struct terminal *term, struct buffer *buf, grid_render_scroll(struct terminal *term, struct buffer *buf,
const struct damage *dmg) const struct damage *dmg)
{ {
int height = (dmg->region.end - dmg->region.start - dmg->lines) * term->cell_height;
LOG_DBG( LOG_DBG(
"damage: SCROLL: %d-%d by %d lines", "damage: SCROLL: %d-%d by %d lines",
dmg->region.start, dmg->region.end, dmg->lines); dmg->region.start, dmg->region.end, dmg->lines);
if (height <= 0) const int region_size = dmg->region.end - dmg->region.start;
if (dmg->lines >= region_size) {
/* The entire scroll region will be scrolled out (i.e. replaced) */
return; return;
}
const int height = (region_size - dmg->lines) * term->cell_height;
xassert(height > 0);
#if TIME_SCROLL_DAMAGE #if TIME_SCROLL_DAMAGE
struct timespec start_time; struct timespec start_time;
@ -1027,14 +1042,19 @@ static void
grid_render_scroll_reverse(struct terminal *term, struct buffer *buf, grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
const struct damage *dmg) const struct damage *dmg)
{ {
int height = (dmg->region.end - dmg->region.start - dmg->lines) * term->cell_height;
LOG_DBG( LOG_DBG(
"damage: SCROLL REVERSE: %d-%d by %d lines", "damage: SCROLL REVERSE: %d-%d by %d lines",
dmg->region.start, dmg->region.end, dmg->lines); dmg->region.start, dmg->region.end, dmg->lines);
if (height <= 0) const int region_size = dmg->region.end - dmg->region.start;
if (dmg->lines >= region_size) {
/* The entire scroll region will be scrolled out (i.e. replaced) */
return; return;
}
const int height = (region_size - dmg->lines) * term->cell_height;
xassert(height > 0);
#if TIME_SCROLL_DAMAGE #if TIME_SCROLL_DAMAGE
struct timespec start_time; struct timespec start_time;
@ -3663,13 +3683,67 @@ tiocswinsz(struct terminal *term)
} }
} }
static void
delayed_reflow_of_normal_grid(struct terminal *term)
{
if (term->interactive_resizing.grid == NULL)
return;
xassert(term->interactive_resizing.new_rows > 0);
struct coord *const tracking_points[] = {
&term->selection.coords.start,
&term->selection.coords.end,
};
/* Reflow the original (since before the resize was started) grid,
* to the *current* dimensions */
grid_resize_and_reflow(
term->interactive_resizing.grid,
term->interactive_resizing.new_rows, term->normal.num_cols,
term->interactive_resizing.old_screen_rows, term->rows,
term->selection.coords.end.row >= 0 ? ALEN(tracking_points) : 0,
tracking_points);
/* Replace the current, truncated, “normal” grid with the
* correctly reflowed one */
grid_free(&term->normal);
term->normal = *term->interactive_resizing.grid;
free(term->interactive_resizing.grid);
term->hide_cursor = term->interactive_resizing.old_hide_cursor;
/* Reset */
term->interactive_resizing.grid = NULL;
term->interactive_resizing.old_screen_rows = 0;
term->interactive_resizing.new_rows = 0;
term->interactive_resizing.old_hide_cursor = false;
/* Invalidate render pointers */
shm_unref(term->render.last_buf);
term->render.last_buf = NULL;
term->render.last_cursor.row = NULL;
tll_free(term->normal.scroll_damage);
sixel_reflow_grid(term, &term->normal);
if (term->grid == &term->normal) {
term_damage_view(term);
render_refresh(term);
}
term_ptmx_resume(term);
}
static bool static bool
fdm_tiocswinsz(struct fdm *fdm, int fd, int events, void *data) fdm_tiocswinsz(struct fdm *fdm, int fd, int events, void *data)
{ {
struct terminal *term = data; struct terminal *term = data;
if (events & EPOLLIN) if (events & EPOLLIN) {
tiocswinsz(term); tiocswinsz(term);
delayed_reflow_of_normal_grid(term);
}
if (term->window->resize_timeout_fd >= 0) { if (term->window->resize_timeout_fd >= 0) {
fdm_del(fdm, term->window->resize_timeout_fd); fdm_del(fdm, term->window->resize_timeout_fd);
@ -3686,6 +3760,7 @@ send_dimensions_to_client(struct terminal *term)
if (!win->is_resizing || term->conf->resize_delay_ms == 0) { if (!win->is_resizing || term->conf->resize_delay_ms == 0) {
/* Send new dimensions to client immediately */ /* Send new dimensions to client immediately */
tiocswinsz(term); tiocswinsz(term);
delayed_reflow_of_normal_grid(term);
/* And make sure to reset and deallocate a lingering timer */ /* And make sure to reset and deallocate a lingering timer */
if (win->resize_timeout_fd >= 0) { if (win->resize_timeout_fd >= 0) {
@ -3716,7 +3791,10 @@ send_dimensions_to_client(struct terminal *term)
if (fd >= 0) { if (fd >= 0) {
/* Reset timeout */ /* Reset timeout */
const struct itimerspec timeout = { const struct itimerspec timeout = {
.it_value = {.tv_sec = 0, .tv_nsec = delay_ms * 1000000}, .it_value = {
.tv_sec = delay_ms / 1000,
.tv_nsec = (delay_ms % 1000) * 1000000,
},
}; };
if (timerfd_settime(fd, 0, &timeout, NULL) < 0) { if (timerfd_settime(fd, 0, &timeout, NULL) < 0) {
@ -3727,8 +3805,10 @@ send_dimensions_to_client(struct terminal *term)
successfully_scheduled = true; successfully_scheduled = true;
} }
if (!successfully_scheduled) if (!successfully_scheduled) {
tiocswinsz(term); tiocswinsz(term);
delayed_reflow_of_normal_grid(term);
}
} }
} }
@ -3815,9 +3895,9 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
const int min_cols = 2; const int min_cols = 2;
const int min_rows = 1; const int min_rows = 1;
/* Minimum window size */ /* Minimum window size (must be divisible by the scaling factor)*/
const int min_width = min_cols * term->cell_width; const int min_width = (min_cols * term->cell_width + scale - 1) / scale * scale;
const int min_height = min_rows * term->cell_height; const int min_height = (min_rows * term->cell_height + scale - 1) / scale * scale;
width = max(width, min_width); width = max(width, min_width);
height = max(height, min_height); height = max(height, min_height);
@ -3844,8 +3924,8 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
const uint32_t scrollback_lines = term->render.scrollback_lines; const uint32_t scrollback_lines = term->render.scrollback_lines;
/* Screen rows/cols before resize */ /* Screen rows/cols before resize */
const int old_cols = term->cols; int old_cols = term->cols;
const int old_rows = term->rows; int old_rows = term->rows;
/* Screen rows/cols after resize */ /* Screen rows/cols after resize */
const int new_cols = (term->width - 2 * pad_x) / term->cell_width; const int new_cols = (term->width - 2 * pad_x) / term->cell_width;
@ -3881,9 +3961,86 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
if (new_cols == old_cols && new_rows == old_rows) { if (new_cols == old_cols && new_rows == old_rows) {
LOG_DBG("grid layout unaffected; skipping reflow"); LOG_DBG("grid layout unaffected; skipping reflow");
term->interactive_resizing.new_rows = new_normal_grid_rows;
goto damage_view; goto damage_view;
} }
/*
* Since text reflow is slow, dont do it *while* resizing. Only
* do it when done, or after pausing the resize for sufficiently
* long. We re-use the TIOCSWINSZ timer to handle this. See
* send_dimensions_to_client() and fdm_tiocswinsz().
*
* To be able to do the final reflow correctly, we need a copy of
* the original grid, before the resize started.
*/
if (term->window->is_resizing && term->conf->resize_delay_ms > 0) {
if (term->interactive_resizing.grid == NULL) {
term_ptmx_pause(term);
/* Stash the current normal grid, as-is, to be used when
* doing the final reflow */
term->interactive_resizing.old_screen_rows = term->rows;
term->interactive_resizing.old_cols = term->cols;
term->interactive_resizing.old_hide_cursor = term->hide_cursor;
term->interactive_resizing.grid = xmalloc(sizeof(*term->interactive_resizing.grid));
*term->interactive_resizing.grid = term->normal;
term->interactive_resizing.selection_coords = term->selection.coords;
} else {
/* Well replace the current temporary grid, with a new
* one (again based on the original grid) */
grid_free(&term->normal);
}
struct grid *orig = term->interactive_resizing.grid;
/*
* Copy the current viewport (of the original grid) to a new
* grid that will be used during the resize. For now, throw
* away sixels and OSC-8 URLs. Theyll be "restored" when we
* do the final reflow.
*
* Note that OSC-8 URLs are perfectly ok to throw away; they
* cannot be interacted with during the resize. And, even if
* url.osc8-underline=always, the underline attribute is
* part of the cell, not the URI struct (and thus our faked
* grid will still render OSC-8 links underlined).
*
* TODO:
* - sixels?
*/
struct grid g = {
.num_rows = 1 << (32 - __builtin_clz(term->interactive_resizing.old_screen_rows)),
.num_cols = term->interactive_resizing.old_cols,
.offset = 0,
.view = 0,
.cursor = orig->cursor,
.saved_cursor = orig->saved_cursor,
.rows = xcalloc(g.num_rows, sizeof(g.rows[0])),
.cur_row = NULL,
.scroll_damage = tll_init(),
.sixel_images = tll_init(),
.kitty_kbd = orig->kitty_kbd,
};
term->selection.coords.start.row -= orig->view;
term->selection.coords.end.row -= orig->view;
for (size_t i = 0, j = orig->view;
i < term->interactive_resizing.old_screen_rows;
i++, j = (j + 1) & (orig->num_rows - 1))
{
g.rows[i] = grid_row_alloc(g.num_cols, false);
memcpy(g.rows[i]->cells,
orig->rows[j]->cells,
g.num_cols * sizeof(g.rows[i]->cells[0]));
}
term->normal = g;
term->hide_cursor = true;
}
if (term->grid == &term->alt) if (term->grid == &term->alt)
selection_cancel(term); selection_cancel(term);
else { else {
@ -3903,16 +4060,51 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
* selections pivot point coordinates *must* be added to the * selections pivot point coordinates *must* be added to the
* tracking points list. * tracking points list.
*/ */
struct coord *const tracking_points[] = {
&term->selection.coords.start,
&term->selection.coords.end,
};
/* Resize grids */ /* Resize grids */
grid_resize_and_reflow( if (term->window->is_resizing && term->conf->resize_delay_ms > 0) {
&term->normal, new_normal_grid_rows, new_cols, old_rows, new_rows, /* Simple truncating resize, *while* an interactive resize is
term->selection.coords.end.row >= 0 ? ALEN(tracking_points) : 0, * ongoing. */
tracking_points); xassert(term->interactive_resizing.grid != NULL);
xassert(new_normal_grid_rows > 0);
term->interactive_resizing.new_rows = new_normal_grid_rows;
grid_resize_without_reflow(
&term->normal, new_alt_grid_rows, new_cols,
term->interactive_resizing.old_screen_rows, new_rows);
} else {
/* Full text reflow */
if (term->interactive_resizing.grid != NULL) {
/* Throw away the current, truncated, “normal” grid, and
* use the original grid instead (from before the resize
* started) */
grid_free(&term->normal);
term->normal = *term->interactive_resizing.grid;
free(term->interactive_resizing.grid);
term->hide_cursor = term->interactive_resizing.old_hide_cursor;
term->selection.coords = term->interactive_resizing.selection_coords;
old_rows = term->interactive_resizing.old_screen_rows;
term->interactive_resizing.grid = NULL;
term->interactive_resizing.old_screen_rows = 0;
term->interactive_resizing.new_rows = 0;
term->interactive_resizing.old_hide_cursor = false;
term->interactive_resizing.selection_coords = (struct range){{-1, -1}, {-1, -1}};
term_ptmx_resume(term);
}
struct coord *const tracking_points[] = {
&term->selection.coords.start,
&term->selection.coords.end,
};
grid_resize_and_reflow(
&term->normal, new_normal_grid_rows, new_cols, old_rows, new_rows,
term->selection.coords.end.row >= 0 ? ALEN(tracking_points) : 0,
tracking_points);
}
grid_resize_without_reflow( grid_resize_without_reflow(
&term->alt, new_alt_grid_rows, new_cols, old_rows, new_rows); &term->alt, new_alt_grid_rows, new_cols, old_rows, new_rows);
@ -3936,9 +4128,11 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
if (term->scroll_region.start >= term->rows) if (term->scroll_region.start >= term->rows)
term->scroll_region.start = 0; term->scroll_region.start = 0;
if (term->scroll_region.end > term->rows ||
if (term->scroll_region.end >= old_rows) term->scroll_region.end >= old_rows)
{
term->scroll_region.end = term->rows; term->scroll_region.end = term->rows;
}
term->render.last_cursor.row = NULL; term->render.last_cursor.row = NULL;
@ -3956,12 +4150,6 @@ damage_view:
term->stashed_height = term->height; term->stashed_height = term->height;
} }
#if 0
/* TODO: doesn't include CSD title bar */
xdg_toplevel_set_min_size(
term->window->xdg_toplevel, min_width / scale, min_height / scale);
#endif
{ {
const bool title_shown = wayl_win_csd_titlebar_visible(term->window); const bool title_shown = wayl_win_csd_titlebar_visible(term->window);
const bool border_shown = wayl_win_csd_borders_visible(term->window); const bool border_shown = wayl_win_csd_borders_visible(term->window);
@ -3971,6 +4159,11 @@ damage_view:
const int border_width = const int border_width =
border_shown ? term->conf->csd.border_width_visible : 0; border_shown ? term->conf->csd.border_width_visible : 0;
xdg_toplevel_set_min_size(
term->window->xdg_toplevel,
min_width / scale + 2 * border_width,
min_height / scale + title_height + 2 * border_width);
xdg_surface_set_window_geometry( xdg_surface_set_window_geometry(
term->window->xdg_surface, term->window->xdg_surface,
-border_width, -border_width,

View file

@ -52,14 +52,25 @@ class StringCapability(Capability):
def __init__(self, name: str, value: str): def __init__(self, name: str, value: str):
# Expand \E to literal ESC in non-parameterized capabilities # Expand \E to literal ESC in non-parameterized capabilities
if '%' not in value: if '%' not in value:
# Ensure e.g. \E7 doesnt get translated to “\0337”, which
# would be interpreted as octal 337 by the C compiler
value = re.sub(r'\\E([0-7])', r'\\033" "\1', value) value = re.sub(r'\\E([0-7])', r'\\033" "\1', value)
value = re.sub(r'\\E', r'\\033', value)
else:
# Need to double-escape \E in C string literals
value = value.replace('\\E', '\\\\E')
# Dont escape : # Replace \E with an actual escape
value = value.replace('\\:', ':') value = re.sub(r'\\E', r'\\033', value)
# Dont escape :
value = value.replace('\\:', ':')
else:
value = value.replace("\\", "\\\\")
# # Need to double-escape backslashes. These only occur in
# # \E\ combos. Note that \E itself is updated below
# value = value.replace('\\E\\\\', '\\E\\\\\\\\')
# # Need to double-escape \E in C string literals
# value = value.replace('\\E', '\\\\E')
super().__init__(name, value) super().__init__(name, value)

View file

@ -86,7 +86,7 @@ selection_on_rows(const struct terminal *term, int row_start, int row_end)
const int rel_row_start = const int rel_row_start =
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, row_start); grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, row_start);
const int rel_row_end = const int rel_row_end =
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, row_start); grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, row_end);
int rel_sel_start = int rel_sel_start =
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, start->row); grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, start->row);
int rel_sel_end = int rel_sel_end =
@ -133,13 +133,18 @@ selection_scroll_down(struct terminal *term, int rows)
{ {
xassert(term->selection.coords.end.row >= 0); xassert(term->selection.coords.end.row >= 0);
const struct grid *grid = term->grid;
const struct range *sel = &term->selection.coords;
const int screen_end =
grid_row_abs_to_sb(grid, term->rows, grid->offset + term->rows - 1);
const int rel_row_start = const int rel_row_start =
grid_row_abs_to_sb(term->grid, term->rows, term->selection.coords.start.row); grid_row_abs_to_sb(term->grid, term->rows, sel->start.row);
const int rel_row_end = const int rel_row_end =
grid_row_abs_to_sb(term->grid, term->rows, term->selection.coords.end.row); grid_row_abs_to_sb(term->grid, term->rows, sel->end.row);
const int actual_end = max(rel_row_start, rel_row_end); const int actual_end = max(rel_row_start, rel_row_end);
if (actual_end + rows <= term->grid->num_rows) { if (actual_end > screen_end - rows) {
/* Part of the selection will be scrolled out, cancel it */ /* Part of the selection will be scrolled out, cancel it */
selection_cancel(term); selection_cancel(term);
} }

150
sixel.c
View file

@ -154,7 +154,7 @@ verify_list_order(const struct terminal *term)
int prev_col_count = 0; int prev_col_count = 0;
/* To aid debugging */ /* To aid debugging */
size_t idx = 0; size_t UNUSED idx = 0;
tll_foreach(term->grid->sixel_images, it) { tll_foreach(term->grid->sixel_images, it) {
int row = grid_row_abs_to_sb( int row = grid_row_abs_to_sb(
@ -838,85 +838,89 @@ sixel_cell_size_changed(struct terminal *term)
} }
void void
sixel_reflow(struct terminal *term) sixel_reflow_grid(struct terminal *term, struct grid *grid)
{ {
struct grid *g = term->grid; /* Meh - the sixel functions we call use term->grid... */
struct grid *active_grid = term->grid;
term->grid = grid;
for (size_t i = 0; i < 2; i++) { /* Need the “real” list to be empty from the beginning */
struct grid *grid = i == 0 ? &term->normal : &term->alt; tll(struct sixel) copy = tll_init();
tll_foreach(grid->sixel_images, it)
tll_push_back(copy, it->item);
tll_free(grid->sixel_images);
term->grid = grid; tll_rforeach(copy, it) {
struct sixel *six = &it->item;
int start = six->pos.row;
int end = (start + six->rows - 1) & (grid->num_rows - 1);
/* Need the “real” list to be empty from the beginning */ if (end < start) {
tll(struct sixel) copy = tll_init(); /* Crosses scrollback wrap-around */
tll_foreach(grid->sixel_images, it) /* TODO: split image */
tll_push_back(copy, it->item); sixel_destroy(six);
tll_free(grid->sixel_images); continue;
tll_rforeach(copy, it) {
struct sixel *six = &it->item;
int start = six->pos.row;
int end = (start + six->rows - 1) & (grid->num_rows - 1);
if (end < start) {
/* Crosses scrollback wrap-around */
/* TODO: split image */
sixel_destroy(six);
continue;
}
if (six->rows > grid->num_rows) {
/* Image too large */
/* TODO: keep bottom part? */
sixel_destroy(six);
continue;
}
/* Drop sixels that now cross the current scrollback end
* border. This is similar to a sixel that have been
* scrolled out */
/* TODO: should be possible to optimize this */
bool sixel_destroyed = false;
int last_row = -1;
for (int j = 0; j < six->rows; j++) {
int row_no = grid_row_abs_to_sb(
term->grid, term->rows, six->pos.row + j);
if (last_row != -1 && last_row >= row_no) {
sixel_destroy(six);
sixel_destroyed = true;
break;
}
last_row = row_no;
}
if (sixel_destroyed) {
LOG_WARN("destroyed sixel that now crossed history");
continue;
}
/* Sixels that didnt overlap may now do so, which isnt
* allowed of course */
_sixel_overwrite_by_rectangle(
term, six->pos.row, six->pos.col, six->rows, six->cols,
&it->item.pix, &it->item.opaque);
if (it->item.data != pixman_image_get_data(it->item.pix)) {
it->item.data = pixman_image_get_data(it->item.pix);
it->item.width = pixman_image_get_width(it->item.pix);
it->item.height = pixman_image_get_height(it->item.pix);
it->item.cols = (it->item.width + term->cell_width - 1) / term->cell_width;
it->item.rows = (it->item.height + term->cell_height - 1) / term->cell_height;
}
sixel_insert(term, it->item);
} }
tll_free(copy); if (six->rows > grid->num_rows) {
/* Image too large */
/* TODO: keep bottom part? */
sixel_destroy(six);
continue;
}
/* Drop sixels that now cross the current scrollback end
* border. This is similar to a sixel that have been
* scrolled out */
/* TODO: should be possible to optimize this */
bool sixel_destroyed = false;
int last_row = -1;
for (int j = 0; j < six->rows; j++) {
int row_no = grid_row_abs_to_sb(
term->grid, term->rows, six->pos.row + j);
if (last_row != -1 && last_row >= row_no) {
sixel_destroy(six);
sixel_destroyed = true;
break;
}
last_row = row_no;
}
if (sixel_destroyed) {
LOG_WARN("destroyed sixel that now crossed history");
continue;
}
/* Sixels that didnt overlap may now do so, which isnt
* allowed of course */
_sixel_overwrite_by_rectangle(
term, six->pos.row, six->pos.col, six->rows, six->cols,
&it->item.pix, &it->item.opaque);
if (it->item.data != pixman_image_get_data(it->item.pix)) {
it->item.data = pixman_image_get_data(it->item.pix);
it->item.width = pixman_image_get_width(it->item.pix);
it->item.height = pixman_image_get_height(it->item.pix);
it->item.cols = (it->item.width + term->cell_width - 1) / term->cell_width;
it->item.rows = (it->item.height + term->cell_height - 1) / term->cell_height;
}
sixel_insert(term, it->item);
} }
term->grid = g; tll_free(copy);
term->grid = active_grid;
}
void
sixel_reflow(struct terminal *term)
{
for (size_t i = 0; i < 2; i++) {
struct grid *grid = i == 0 ? &term->normal : &term->alt;
sixel_reflow_grid(term, grid);
}
} }
void void
@ -1291,7 +1295,7 @@ sixel_add_many(struct terminal *term, uint8_t c, unsigned count)
if (unlikely(col + count - 1 >= width)) { if (unlikely(col + count - 1 >= width)) {
resize_horizontally(term, col + count); resize_horizontally(term, col + count);
width = term->sixel.image.width; width = term->sixel.image.width;
count = min(count, width - col); count = min(count, max(width - col, 0));
} }
uint32_t color = term->sixel.color; uint32_t color = term->sixel.color;

View file

@ -19,6 +19,10 @@ void sixel_scroll_up(struct terminal *term, int rows);
void sixel_scroll_down(struct terminal *term, int rows); void sixel_scroll_down(struct terminal *term, int rows);
void sixel_cell_size_changed(struct terminal *term); void sixel_cell_size_changed(struct terminal *term);
void sixel_reflow_grid(struct terminal *term, struct grid *grid);
/* Shortcut for sixel_reflow_grid(normal) + sixel_reflow_grid(alt) */
void sixel_reflow(struct terminal *term); void sixel_reflow(struct terminal *term);
/* /*

14
slave.c
View file

@ -21,6 +21,7 @@
#include "macros.h" #include "macros.h"
#include "terminal.h" #include "terminal.h"
#include "tokenize.h" #include "tokenize.h"
#include "version.h"
#include "xmalloc.h" #include "xmalloc.h"
extern char **environ; extern char **environ;
@ -351,6 +352,8 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
} }
setenv("TERM", term_env, 1); setenv("TERM", term_env, 1);
setenv("TERM_PROGRAM", "foot", 1);
setenv("TERM_PROGRAM_VERSION", FOOT_VERSION_SHORT, 1);
setenv("COLORTERM", "truecolor", 1); setenv("COLORTERM", "truecolor", 1);
setenv("PWD", cwd, 1); setenv("PWD", cwd, 1);
@ -359,8 +362,15 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
#endif #endif
if (extra_env_vars != NULL) { if (extra_env_vars != NULL) {
tll_foreach(*extra_env_vars, it) tll_foreach(*extra_env_vars, it) {
setenv(it->item.name, it->item.value, 1); const char *name = it->item.name;
const char *value = it->item.value;
if (strlen(value) == 0)
unsetenv(name);
else
setenv(name, value, 1);
}
} }
char **_shell_argv = NULL; char **_shell_argv = NULL;

View file

@ -7,6 +7,7 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <limits.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h> #include <sys/wait.h>
@ -255,8 +256,18 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
cursor_blink_rearm_timer(term); cursor_blink_rearm_timer(term);
} }
if (unlikely(term->interactive_resizing.grid != NULL)) {
/*
* Dont consume PTMX while were doing an interactive resize,
* since the normal grid were currently using is a
* temporary one - all changes done to it will be lost when
* the interactive resize ends.
*/
return true;
}
uint8_t buf[24 * 1024]; uint8_t buf[24 * 1024];
const size_t max_iterations = !hup ? 10 : (size_t)-1ll; const size_t max_iterations = !hup ? 10 : SIZE_MAX;
for (size_t i = 0; i < max_iterations && pollin; i++) { for (size_t i = 0; i < max_iterations && pollin; i++) {
xassert(pollin); xassert(pollin);
@ -278,6 +289,7 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
break; break;
} }
xassert(term->interactive_resizing.grid == NULL);
vt_from_slave(term, buf, count); vt_from_slave(term, buf, count);
} }
@ -358,6 +370,18 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
return true; return true;
} }
bool
term_ptmx_pause(struct terminal *term)
{
return fdm_event_del(term->fdm, term->ptmx, EPOLLIN);
}
bool
term_ptmx_resume(struct terminal *term)
{
return fdm_event_add(term->fdm, term->ptmx, EPOLLIN);
}
static bool static bool
fdm_flash(struct fdm *fdm, int fd, int events, void *data) fdm_flash(struct fdm *fdm, int fd, int events, void *data)
{ {
@ -680,6 +704,37 @@ free_custom_glyphs(struct fcft_glyph ***glyphs, size_t count)
*glyphs = NULL; *glyphs = NULL;
} }
static void
term_line_height_update(struct terminal *term)
{
const struct config *conf = term->conf;
if (term->conf->line_height.px < 0) {
term->font_line_height.pt = 0;
term->font_line_height.px = -1;
return;
}
const float dpi = term->font_is_sized_by_dpi ? term->font_dpi : 96.;
const float font_original_pt_size =
conf->fonts[0].arr[0].px_size > 0
? conf->fonts[0].arr[0].px_size * 72. / dpi
: conf->fonts[0].arr[0].pt_size;
const float font_current_pt_size =
term->font_sizes[0][0].px_size > 0
? term->font_sizes[0][0].px_size * 72. / dpi
: term->font_sizes[0][0].pt_size;
const float change = font_current_pt_size / font_original_pt_size;
const float line_original_pt_size = conf->line_height.px > 0
? conf->line_height.px * 72. / dpi
: conf->line_height.pt;
term->font_line_height.px = 0;
term->font_line_height.pt = fmaxf(line_original_pt_size * change, 0.);
}
static bool static bool
term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4]) term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
{ {
@ -706,6 +761,8 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
fonts[0], U'M', term->font_subpixel); fonts[0], U'M', term->font_subpixel);
int advance = M != NULL ? M->advance.x : term->fonts[0]->max_advance.x; int advance = M != NULL ? M->advance.x : term->fonts[0]->max_advance.x;
term_line_height_update(term);
term->cell_width = advance + term->cell_width = advance +
term_pt_or_px_as_pixels(term, &conf->letter_spacing); term_pt_or_px_as_pixels(term, &conf->letter_spacing);
@ -889,7 +946,7 @@ term_pt_or_px_as_pixels(const struct terminal *term,
return pt_or_px->px == 0 return pt_or_px->px == 0
? round(pt_or_px->pt * scale * dpi / 72) ? round(pt_or_px->pt * scale * dpi / 72)
: pt_or_px->px; : pt_or_px->px * scale;
} }
struct font_load_data { struct font_load_data {
@ -1054,7 +1111,6 @@ load_fonts_from_conf(struct terminal *term)
} }
} }
term->font_line_height = term->conf->line_height;
return reload_fonts(term); return reload_fonts(term);
} }
@ -1274,7 +1330,6 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.pt_size = font->pt_size, .px_size = font->px_size}; .pt_size = font->pt_size, .px_size = font->px_size};
} }
} }
term->font_line_height = conf->line_height;
add_utmp_record(conf, reaper, ptmx); add_utmp_record(conf, reaper, ptmx);
@ -1728,6 +1783,8 @@ term_destroy(struct terminal *term)
grid_free(&term->normal); grid_free(&term->normal);
grid_free(&term->alt); grid_free(&term->alt);
grid_free(term->interactive_resizing.grid);
free(term->interactive_resizing.grid);
free(term->foot_exe); free(term->foot_exe);
free(term->cwd); free(term->cwd);
@ -1978,39 +2035,71 @@ term_reset(struct terminal *term, bool hard)
} }
static bool static bool
term_font_size_adjust(struct terminal *term, double amount) term_font_size_adjust_by_points(struct terminal *term, float amount)
{ {
const struct config *conf = term->conf; const struct config *conf = term->conf;
const float dpi = term->font_is_sized_by_dpi ? term->font_dpi : 96.; const float dpi = term->font_is_sized_by_dpi ? term->font_dpi : 96.;
for (size_t i = 0; i < 4; i++) { for (size_t i = 0; i < 4; i++) {
const struct config_font_list *font_list = &conf->fonts[i]; const struct config_font_list *font_list = &conf->fonts[i];
for (size_t j = 0; j < font_list->count; j++) { for (size_t j = 0; j < font_list->count; j++) {
float old_pt_size = term->font_sizes[i][j].pt_size; struct config_font *font = &term->font_sizes[i][j];
float old_pt_size = font->pt_size;
/* if (font->px_size > 0)
* To ensure primary and user-configured fallback fonts are old_pt_size = font->px_size * 72. / dpi;
* resizes by the same amount, convert pixel sizes to point
* sizes, and to the adjustment on point sizes only.
*/
if (term->font_sizes[i][j].px_size > 0) font->pt_size = fmaxf(old_pt_size + amount, 0.);
old_pt_size = term->font_sizes[i][j].px_size * 72. / dpi; font->px_size = -1;
term->font_sizes[i][j].pt_size = fmaxf(old_pt_size + amount, 0.);
term->font_sizes[i][j].px_size = -1;
} }
} }
if (term->font_line_height.px >= 0) { return reload_fonts(term);
float old_pt_size = term->font_line_height.px > 0 }
? term->font_line_height.px * 72. / dpi
: term->font_line_height.pt;
term->font_line_height.px = 0; static bool
term->font_line_height.pt = fmaxf(old_pt_size + amount, 0.); term_font_size_adjust_by_pixels(struct terminal *term, int amount)
{
const struct config *conf = term->conf;
const float dpi = term->font_is_sized_by_dpi ? term->font_dpi : 96.;
for (size_t i = 0; i < 4; i++) {
const struct config_font_list *font_list = &conf->fonts[i];
for (size_t j = 0; j < font_list->count; j++) {
struct config_font *font = &term->font_sizes[i][j];
int old_px_size = font->px_size;
if (font->px_size <= 0)
old_px_size = font->pt_size * dpi / 72.;
font->px_size = max(old_px_size + amount, 1);
}
}
return reload_fonts(term);
}
static bool
term_font_size_adjust_by_percent(struct terminal *term, bool increment, float percent)
{
const struct config *conf = term->conf;
const float multiplier = increment
? 1. + percent
: 1. / (1. + percent);
for (size_t i = 0; i < 4; i++) {
const struct config_font_list *font_list = &conf->fonts[i];
for (size_t j = 0; j < font_list->count; j++) {
struct config_font *font = &term->font_sizes[i][j];
if (font->px_size > 0)
font->px_size = max(font->px_size * multiplier, 1);
else
font->pt_size = fmax(font->pt_size * multiplier, 0);
}
} }
return reload_fonts(term); return reload_fonts(term);
@ -2019,19 +2108,29 @@ term_font_size_adjust(struct terminal *term, double amount)
bool bool
term_font_size_increase(struct terminal *term) term_font_size_increase(struct terminal *term)
{ {
if (!term_font_size_adjust(term, 0.5)) const struct config *conf = term->conf;
return false; const struct font_size_adjustment *inc_dec = &conf->font_size_adjustment;
return true; if (inc_dec->percent > 0.)
return term_font_size_adjust_by_percent(term, true, inc_dec->percent);
else if (inc_dec->pt_or_px.px > 0)
return term_font_size_adjust_by_pixels(term, inc_dec->pt_or_px.px);
else
return term_font_size_adjust_by_points(term, inc_dec->pt_or_px.pt);
} }
bool bool
term_font_size_decrease(struct terminal *term) term_font_size_decrease(struct terminal *term)
{ {
if (!term_font_size_adjust(term, -0.5)) const struct config *conf = term->conf;
return false; const struct font_size_adjustment *inc_dec = &conf->font_size_adjustment;
return true; if (inc_dec->percent > 0.)
return term_font_size_adjust_by_percent(term, false, inc_dec->percent);
else if (inc_dec->pt_or_px.px > 0)
return term_font_size_adjust_by_pixels(term, -inc_dec->pt_or_px.px);
else
return term_font_size_adjust_by_points(term, -inc_dec->pt_or_px.pt);
} }
bool bool
@ -2153,15 +2252,20 @@ void
term_damage_scroll(struct terminal *term, enum damage_type damage_type, term_damage_scroll(struct terminal *term, enum damage_type damage_type,
struct scroll_region region, int lines) struct scroll_region region, int lines)
{ {
if (tll_length(term->grid->scroll_damage) > 0) { if (likely(tll_length(term->grid->scroll_damage) > 0)) {
struct damage *dmg = &tll_back(term->grid->scroll_damage); struct damage *dmg = &tll_back(term->grid->scroll_damage);
if (dmg->type == damage_type && if (likely(
dmg->region.start == region.start && dmg->type == damage_type &&
dmg->region.end == region.end) dmg->region.start == region.start &&
dmg->region.end == region.end))
{ {
dmg->lines += lines; /* Make sure we dont overflow... */
return; int new_line_count = (int)dmg->lines + lines;
if (likely(new_line_count <= UINT16_MAX)) {
dmg->lines = new_line_count;
return;
}
} }
} }
struct damage dmg = { struct damage dmg = {
@ -2612,13 +2716,13 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
erase_line(term, row); erase_line(term, row);
} }
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
#if defined(_DEBUG) #if defined(_DEBUG)
for (int r = 0; r < term->rows; r++) for (int r = 0; r < term->rows; r++)
xassert(grid_row(term->grid, r) != NULL); xassert(grid_row(term->grid, r) != NULL);
#endif #endif
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
} }
void void
@ -2652,6 +2756,18 @@ term_scroll_reverse_partial(struct terminal *term,
selection_scroll_down(term, rows); selection_scroll_down(term, rows);
} }
/* Unallocate scrolled out lines */
for (int r = region.end - rows; r < region.end; r++) {
const int abs_r = grid_row_absolute(term->grid, r);
struct row *row = term->grid->rows[abs_r];
grid_row_free(row);
term->grid->rows[abs_r] = NULL;
if (term->render.last_cursor.row == row)
term->render.last_cursor.row = NULL;
}
sixel_scroll_down(term, rows); sixel_scroll_down(term, rows);
bool view_follows = term->grid->view == term->grid->offset; bool view_follows = term->grid->view == term->grid->offset;

View file

@ -96,7 +96,7 @@ enum damage_type {DAMAGE_SCROLL, DAMAGE_SCROLL_REVERSE,
struct damage { struct damage {
enum damage_type type; enum damage_type type;
struct scroll_region region; struct scroll_region region;
int lines; uint16_t lines;
}; };
struct row_uri_range { struct row_uri_range {
@ -598,6 +598,15 @@ struct terminal {
struct timespec input_time; struct timespec input_time;
} render; } render;
struct {
struct grid *grid; /* Original normal grid, before resize started */
int old_screen_rows; /* term->rows before resize started */
int old_cols; /* term->cols before resize started */
int old_hide_cursor; /* term->hide_cursor before resize started */
int new_rows; /* New number of scrollback rows */
struct range selection_coords;
} interactive_resizing;
struct { struct {
enum { enum {
SIXEL_DECSIXEL, /* DECSIXEL body part ", $, -, ? ... ~ */ SIXEL_DECSIXEL, /* DECSIXEL body part ", $, -, ? ... ~ */
@ -805,6 +814,9 @@ void term_collect_urls(struct terminal *term);
void term_osc8_open(struct terminal *term, uint64_t id, const char *uri); void term_osc8_open(struct terminal *term, uint64_t id, const char *uri);
void term_osc8_close(struct terminal *term); void term_osc8_close(struct terminal *term);
bool term_ptmx_pause(struct terminal *term);
bool term_ptmx_resume(struct terminal *term);
static inline void term_reset_grapheme_state(struct terminal *term) static inline void term_reset_grapheme_state(struct terminal *term)
{ {
#if defined(FOOT_GRAPHEME_CLUSTERING) #if defined(FOOT_GRAPHEME_CLUSTERING)

View file

@ -467,6 +467,7 @@ test_section_main(void)
test_boolean(&ctx, &parse_section_main, "locked-title", &conf.locked_title); test_boolean(&ctx, &parse_section_main, "locked-title", &conf.locked_title);
test_boolean(&ctx, &parse_section_main, "notify-focus-inhibit", &conf.notify_focus_inhibit); test_boolean(&ctx, &parse_section_main, "notify-focus-inhibit", &conf.notify_focus_inhibit);
test_pt_or_px(&ctx, &parse_section_main, "font-size-adjustment", &conf.font_size_adjustment.pt_or_px); /* TODO: test N% values too */
test_pt_or_px(&ctx, &parse_section_main, "line-height", &conf.line_height); test_pt_or_px(&ctx, &parse_section_main, "line-height", &conf.line_height);
test_pt_or_px(&ctx, &parse_section_main, "letter-spacing", &conf.letter_spacing); test_pt_or_px(&ctx, &parse_section_main, "letter-spacing", &conf.letter_spacing);
test_pt_or_px(&ctx, &parse_section_main, "horizontal-letter-offset", &conf.horizontal_letter_offset); test_pt_or_px(&ctx, &parse_section_main, "horizontal-letter-offset", &conf.horizontal_letter_offset);
@ -894,7 +895,7 @@ enum collision_test_mode {
FAIL_DIFFERENT_ACTION, FAIL_DIFFERENT_ACTION,
FAIL_DIFFERENT_ARGV, FAIL_DIFFERENT_ARGV,
FAIL_MOUSE_OVERRIDE, FAIL_MOUSE_OVERRIDE,
SUCCED_SAME_ACTION_AND_ARGV, SUCCEED_SAME_ACTION_AND_ARGV,
}; };
static void static void
@ -948,7 +949,7 @@ _test_binding_collisions(struct context *ctx,
break; break;
case FAIL_DIFFERENT_ARGV: case FAIL_DIFFERENT_ARGV:
case SUCCED_SAME_ACTION_AND_ARGV: case SUCCEED_SAME_ACTION_AND_ARGV:
bindings.arr[0].aux.type = BINDING_AUX_PIPE; bindings.arr[0].aux.type = BINDING_AUX_PIPE;
bindings.arr[0].aux.master_copy = true; bindings.arr[0].aux.master_copy = true;
bindings.arr[0].aux.pipe.args = xcalloc( bindings.arr[0].aux.pipe.args = xcalloc(
@ -964,13 +965,13 @@ _test_binding_collisions(struct context *ctx,
bindings.arr[1].aux.pipe.args[0] = xstrdup("/usr/bin/foobar"); bindings.arr[1].aux.pipe.args[0] = xstrdup("/usr/bin/foobar");
bindings.arr[1].aux.pipe.args[1] = xstrdup("hello"); bindings.arr[1].aux.pipe.args[1] = xstrdup("hello");
if (test_mode == SUCCED_SAME_ACTION_AND_ARGV) if (test_mode == SUCCEED_SAME_ACTION_AND_ARGV)
bindings.arr[1].aux.pipe.args[2] = xstrdup("world"); bindings.arr[1].aux.pipe.args[2] = xstrdup("world");
break; break;
} }
bool expected_result = bool expected_result =
test_mode == SUCCED_SAME_ACTION_AND_ARGV ? true : false; test_mode == SUCCEED_SAME_ACTION_AND_ARGV ? true : false;
if (resolve_key_binding_collisions( if (resolve_key_binding_collisions(
ctx->conf, ctx->section, map, &bindings, type) != expected_result) ctx->conf, ctx->section, map, &bindings, type) != expected_result)
@ -1003,7 +1004,7 @@ test_binding_collisions(struct context *ctx,
{ {
_test_binding_collisions(ctx, max_action, map, type, FAIL_DIFFERENT_ACTION); _test_binding_collisions(ctx, max_action, map, type, FAIL_DIFFERENT_ACTION);
_test_binding_collisions(ctx, max_action, map, type, FAIL_DIFFERENT_ARGV); _test_binding_collisions(ctx, max_action, map, type, FAIL_DIFFERENT_ARGV);
_test_binding_collisions(ctx, max_action, map, type, SUCCED_SAME_ACTION_AND_ARGV); _test_binding_collisions(ctx, max_action, map, type, SUCCEED_SAME_ACTION_AND_ARGV);
if (type == MOUSE_BINDING) { if (type == MOUSE_BINDING) {
_test_binding_collisions( _test_binding_collisions(
@ -1303,6 +1304,7 @@ test_section_tweak(void)
int int
main(int argc, const char *const *argv) main(int argc, const char *const *argv)
{ {
FcInit();
log_init(LOG_COLORIZE_AUTO, false, 0, LOG_CLASS_ERROR); log_init(LOG_COLORIZE_AUTO, false, 0, LOG_CLASS_ERROR);
test_section_main(); test_section_main();
test_section_bell(); test_section_bell();
@ -1324,5 +1326,6 @@ main(int argc, const char *const *argv)
test_section_environment(); test_section_environment();
test_section_tweak(); test_section_tweak();
log_deinit(); log_deinit();
FcFini();
return 0; return 0;
} }

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# https://github.com/romainl/Apprentice # https://github.com/romainl/Apprentice
[cursor] [cursor]

View file

@ -1,10 +1,10 @@
# -*- conf -*-
# Catppuccin # Catppuccin
[cursor] [cursor]
color=1A1826 D9E0EE color=1A1826 D9E0EE
[colors] [colors]
alpha=1.0
foreground=D9E0EE foreground=D9E0EE
background=1E1D2F background=1E1D2F
regular0=6E6C7E # black regular0=6E6C7E # black

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Derp # Derp
[cursor] [cursor]

31
themes/deus Normal file
View file

@ -0,0 +1,31 @@
# -*- conf -*-
# Deus
# Color palette based on: https://github.com/ajmwagar/vim-deus
[cursor]
color=2c323b eaeaea
[colors]
background=2c323b
foreground=eaeaea
regular0=242a32
regular1=d54e53
regular2=98c379
regular3=e5c07b
regular4=83a598
regular5=c678dd
regular6=70c0ba
regular7=eaeaea
bright0=666666
bright1=ec3e45
bright2=90c966
bright3=edbf69
bright4=73ba9f
bright5=c858e9
bright6=2bcec2
bright7=ffffff
# Enable if prefer Deus colors instead of inverterd fg/bg for
# highlighting (mouse selection)
# selection-foreground=2c323b
# selection-background=eaeaea

View file

@ -1,10 +1,10 @@
# -*- conf -*-
# Dracula # Dracula
[cursor] [cursor]
color=282a36 f8f8f2 color=282a36 f8f8f2
[colors] [colors]
alpha=1.0
foreground=f8f8f2 foreground=f8f8f2
background=282a36 background=282a36
regular0=000000 # black regular0=000000 # black

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Gruvbox # Gruvbox
[colors] [colors]

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Gruvbox - Light # Gruvbox - Light
[colors] [colors]

View file

@ -1,3 +1,4 @@
# -*- conf -*-
[cursor] [cursor]
color=141414 c9c9c9 color=141414 c9c9c9

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# JetBrains Darcula # JetBrains Darcula
# Palette based on the same theme from https://github.com/dexpota/kitty-themes # Palette based on the same theme from https://github.com/dexpota/kitty-themes
@ -5,7 +6,6 @@
color=202020 ffffff color=202020 ffffff
[colors] [colors]
#alpha=0.80
background=202020 background=202020
foreground=adadad foreground=adadad
regular0=000000 # black regular0=000000 # black
@ -24,5 +24,5 @@ bright4=6d9df1 # bright blue
bright5=fb82ff # bright magenta bright5=fb82ff # bright magenta
bright6=60d3d1 # bright cyan bright6=60d3d1 # bright cyan
bright7=eeeeee # bright white bright7=eeeeee # bright white
selection-foreground=202020 # selection-foreground=202020
selection-background=1a3272 # selection-background=1a3272

View file

@ -1,3 +1,5 @@
# -*- conf -*-
[cursor] [cursor]
color=111111 cccccc color=111111 cccccc

40
themes/material-amber Normal file
View file

@ -0,0 +1,40 @@
# -*- conf -*-
# Material Amber
# Based on material.io guidelines with Amber 50 background
# [cursor]
# color=fff8e1 21201d
[colors]
foreground = 21201d
background = fff8e1
regular0 = 21201d # black
regular1 = cd4340 # red
regular2 = 498d49 # green
regular3 = fab32d # yellow
regular4 = 3378c4 # blue
regular5 = b83269 # magenta
regular6 = 21929a # cyan
regular7 = ffd7d7 # white
bright0 = 66635a # bright black
bright1 = dd7b72 # bright red
bright2 = 82ae78 # bright green
bright3 = fbc870 # bright yellow
bright4 = 73a0cd # bright blue
bright5 = ce6f8e # bright magenta
bright6 = 548c94 # bright cyan
bright7 = ffe1da # bright white
dim0 = 9e9a8c # dim black
dim1 = e9a99b # dim red
dim2 = b0c99f # dim green
dim3 = fdda9a # dim yellow
dim4 = a6c0d4 # dim blue
dim5 = e0a1ad # dim magenta
dim6 = 3c6064 # dim cyan
dim7 = ffe9dd # dim white
# selection-foreground=fff8e1
# selection-background=21201d

View file

@ -1,11 +1,10 @@
# -*- conf -*-
# Material # Material
# From https://github.com/MartinSeeler/iterm2-material-design # From https://github.com/MartinSeeler/iterm2-material-design
[colors] [colors]
foreground=ECEFF1 foreground=ECEFF1
background=263238 background=263238
selection-foreground=ECEFF1
selection-background=607D8B
regular0=546E7A # black regular0=546E7A # black
regular1=FF5252 # red regular1=FF5252 # red
regular2=5CF19E # green regular2=5CF19E # green
@ -22,3 +21,5 @@ bright4=80D8FF # bright blue
bright5=FF80AB # bright magenta bright5=FF80AB # bright magenta
bright6=A7FDEB # bright cyan bright6=A7FDEB # bright cyan
bright7=FFFFFF # bright white bright7=FFFFFF # bright white
# selection-foreground=ECEFF1
# selection-background=607D8B

24
themes/modus-operandi Normal file
View file

@ -0,0 +1,24 @@
# -*- conf -*-
#
# modus-operandi
# See: https://protesilaos.com/emacs/modus-themes
#
[colors]
background=ffffff
foreground=000000
regular0=000000
regular1=a60000
regular2=005e00
regular3=813e00
regular4=0031a9
regular5=721045
regular6=00538b
regular7=bfbfbf
bright0=595959
bright1=972500
bright2=315b00
bright3=70480f
bright4=2544bb
bright5=5317ac
bright6=005a5f
bright7=ffffff

25
themes/modus-vivendi Normal file
View file

@ -0,0 +1,25 @@
# -*- conf -*-
#
# modus-vivendi
# See: https://protesilaos.com/emacs/modus-themes
#
[colors]
background=000000
foreground=ffffff
regular0=000000
regular1=ff8059
regular2=44bc44
regular3=d0bc00
regular4=2fafff
regular5=feacd0
regular6=00d3d0
regular7=bfbfbf
bright0=595959
bright1=ef8b50
bright2=70b900
bright3=c0c530
bright4=79a8ff
bright5=b6a0ff
bright6=6ae4b9
bright7=ffffff

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Monokai Pro # Monokai Pro
[colors] [colors]

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# moonfly # moonfly
# Based on https://github.com/bluz71/vim-moonfly-colors # Based on https://github.com/bluz71/vim-moonfly-colors
@ -7,8 +8,9 @@ color = 080808 9e9e9e
[colors] [colors]
foreground = b2b2b2 foreground = b2b2b2
background = 080808 background = 080808
selection-foreground = 080808
selection-background = b2ceee # selection-foreground = 080808
# selection-background = b2ceee
regular0 = 323437 regular0 = 323437
regular1 = ff5454 regular1 = ff5454

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# nightfly # nightfly
# Based on https://github.com/bluz71/vim-nightfly-guicolors # Based on https://github.com/bluz71/vim-nightfly-guicolors
@ -7,8 +8,9 @@ color = 080808 9ca1aa
[colors] [colors]
foreground = acb4c2 foreground = acb4c2
background = 011627 background = 011627
selection-foreground = 080808
selection-background = b2ceee # selection-foreground = 080808
# selection-background = b2ceee
regular0 = 1d3b53 regular0 = 1d3b53
regular1 = fc514e regular1 = fc514e

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Nord # theme: Nord
# author: Arctic Ice Studio <development@arcticicestudio.com>, Sven Greb <code@svengreb.de> # author: Arctic Ice Studio <development@arcticicestudio.com>, Sven Greb <code@svengreb.de>
# description: „Nord“ — An arctic, north-bluish color palette # description: „Nord“ — An arctic, north-bluish color palette
@ -11,8 +12,9 @@ color = 2e3440 d8dee9
[colors] [colors]
foreground = d8dee9 foreground = d8dee9
background = 2e3440 background = 2e3440
#selection-foreground = d8dee9
#selection-background = 4c566a # selection-foreground = d8dee9
# selection-background = 4c566a
regular0 = 3b4252 regular0 = 3b4252
regular1 = bf616a regular1 = bf616a

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Nordiq # Nordiq
[cursor] [cursor]

27
themes/onedark Normal file
View file

@ -0,0 +1,27 @@
# OneDark
# Palette based on the same theme from https://github.com/dexpota/kitty-themes
[cursor]
color=111111 cccccc
[colors]
foreground=979eab
background=282c34
regular0=282c34 # black
regular1=e06c75 # red
regular2=98c379 # green
regular3=e5c07b # yellow
regular4=61afef # blue
regular5=be5046 # magenta
regular6=56b6c2 # cyan
regular7=979eab # white
bright0=393e48 # bright black
bright1=d19a66 # bright red
bright2=56b6c2 # bright green
bright3=e5c07b # bright yellow
bright4=61afef # bright blue
bright5=be5046 # bright magenta
bright6=56b6c2 # bright cyan
bright7=abb2bf # bright white
# selection-foreground=282c34
# selection-background=979eab

View file

@ -1,28 +1,28 @@
# -*- conf -*-
# PaperColorDark # PaperColorDark
# Palette based on https://github.com/NLKNguyen/papercolor-theme # Palette based on https://github.com/NLKNguyen/papercolor-theme
[cursor] [cursor]
color=1c1c1c eeeeee color=1c1c1c eeeeee
[colors] [colors]
alpha=0.80 background=1c1c1c
background=1c1c1c foreground=eeeeee
foreground=eeeeee regular0=1c1c1c # black
regular0=1c1c1c # black regular1=af005f # red
regular1=af005f # red regular2=5faf00 # green
regular2=5faf00 # green regular3=d7af5f # yellow
regular3=d7af5f # yellow regular4=5fafd7 # blue
regular4=5fafd7 # blue regular5=808080 # magenta
regular5=808080 # magenta regular6=d7875f # cyan
regular6=d7875f # cyan regular7=d0d0d0 # white
regular7=d0d0d0 # white bright0=bcbcbc # bright black
bright0=bcbcbc # bright black bright1=5faf5f # bright red
bright1=5faf5f # bright red bright2=afd700 # bright green
bright2=afd700 # bright green bright3=af87d7 # bright yellow
bright3=af87d7 # bright yellow bright4=ffaf00 # bright blue
bright4=ffaf00 # bright blue bright5=ff5faf # bright magenta
bright5=ff5faf # bright magenta bright6=00afaf # bright cyan
bright6=00afaf # bright cyan bright7=5f8787 # bright white
bright7=5f8787 # bright white # selection-foreground=1c1c1c
#selection-foreground=1c1c1c # selection-background=af87d7
#selection-background=af87d7

View file

@ -1,28 +1,28 @@
# -*- conf -*-
# PaperColor Light # PaperColor Light
# Palette based on https://github.com/NLKNguyen/papercolor-theme # Palette based on https://github.com/NLKNguyen/papercolor-theme
[cursor] [cursor]
color=eeeeee 444444 color=eeeeee 444444
[colors] [colors]
alpha=1.0 background=eeeeee
background=eeeeee foreground=444444
foreground=444444 regular0=eeeeee # black
regular0=eeeeee # black regular1=af0000 # red
regular1=af0000 # red regular2=008700 # green
regular2=008700 # green regular3=5f8700 # yellow
regular3=5f8700 # yellow regular4=0087af # blue
regular4=0087af # blue regular5=878787 # magenta
regular5=878787 # magenta regular6=005f87 # cyan
regular6=005f87 # cyan regular7=764e37 # white
regular7=764e37 # white bright0=bcbcbc # bright black
bright0=bcbcbc # bright black bright1=d70000 # bright red
bright1=d70000 # bright red bright2=d70087 # bright green
bright2=d70087 # bright green bright3=8700af # bright yellow
bright3=8700af # bright yellow bright4=d75f00 # bright blue
bright4=d75f00 # bright blue bright5=d75f00 # bright magenta
bright5=d75f00 # bright magenta bright6=4c7a5d # bright cyan
bright6=4c7a5d # bright cyan bright7=005faf # bright white
bright7=005faf # bright white # selection-foreground=eeeeee
#selection-foreground=eeeeee # selection-background=0087af
#selection-background=0087af

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: rezza # theme: rezza
# author: Doug Whiteley (rezza) # author: Doug Whiteley (rezza)
# original URL: http://metawire.org/~rezza/index.php # original URL: http://metawire.org/~rezza/index.php

26
themes/rose-pine Normal file
View file

@ -0,0 +1,26 @@
# -*- conf -*-
# Rose-Piné
[cursor]
color=191724 e0def4
[colors]
background=191724
foreground=e0def4
regular0=26233a # black
regular1=eb6f92 # red
regular2=31748f # green
regular3=f6c177 # yellow
regular4=9ccfd8 # blue
regular5=c4a7e7 # magenta
regular6=ebbcba # cyan
regular7=e0def4 # white
bright0=6e6a86 # bright black
bright1=eb6f92 # bright red
bright2=31748f # bright green
bright3=f6c177 # bright yellow
bright4=9ccfd8 # bright blue
bright5=c4a7e7 # bright magenta
bright6=ebbcba # bright cyan
bright7=e0def4 # bright white

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Selenized black # Selenized black
[cursor] [cursor]

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Selenized dark # Selenized dark
[cursor] [cursor]

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Selenized light # Selenized light
[cursor] [cursor]

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Selenized white # Selenized white
[cursor] [cursor]

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Solarized dark # Solarized dark
[cursor] [cursor]
@ -23,7 +24,7 @@ bright5= 6c71c4
bright6= 93a1a1 bright6= 93a1a1
bright7= fdf6e3 bright7= fdf6e3
## Enable if prefer solarized colors instead of inverterd fg/bg for # Enable if prefer solarized colors instead of inverterd fg/bg for
## highlighting (mouse selection) # highlighting (mouse selection)
# selection-foreground=93a1a1 # selection-foreground=93a1a1
# selection-background=073642 # selection-background=073642

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Solarized dark # Solarized dark
[cursor] [cursor]
@ -25,7 +26,7 @@ bright5= dc619d
bright6= 32c1b6 bright6= 32c1b6
bright7= ffffff bright7= ffffff
## Enable if prefer solarized colors instead of inverterd fg/bg for # Enable if prefer solarized colors instead of inverterd fg/bg for
## highlighting (mouse selection) # highlighting (mouse selection)
# selection-foreground=93a1a1 # selection-foreground=93a1a1
# selection-background=073642 # selection-background=073642

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Solarized light # Solarized light
[cursor] [cursor]

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# Tango # Tango
[cursor] [cursor]

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Autumn # theme: Tempus Autumn
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Dark theme with a palette inspired by earthly colours (WCAG AA compliant) # description: Dark theme with a palette inspired by earthly colours (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 958fdf
bright5 = ce7dc4 bright5 = ce7dc4
bright6 = 2fa6b7 bright6 = 2fa6b7
bright7 = a9a2a6 bright7 = a9a2a6
#selection-foreground = a8948a # selection-foreground = a8948a
#selection-background = 36302a # selection-background = 36302a

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Classic # theme: Tempus Classic
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Dark theme with warm hues (WCAG AA compliant) # description: Dark theme with warm hues (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 8e9cc0
bright5 = d58888 bright5 = d58888
bright6 = 7aa880 bright6 = 7aa880
bright7 = aeadaf bright7 = aeadaf
#selection-foreground = 949d9f # selection-foreground = 949d9f
#selection-background = 312e30 # selection-background = 312e30

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Dawn # theme: Tempus Dawn
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Light theme with a soft, slightly desaturated palette (WCAG AA compliant) # description: Light theme with a soft, slightly desaturated palette (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 5c59b2
bright5 = 8e45a8 bright5 = 8e45a8
bright6 = 3f649c bright6 = 3f649c
bright7 = eff0f2 bright7 = eff0f2
#selection-foreground = 676364 # selection-foreground = 676364
#selection-background = dee2e0 # selection-background = dee2e0

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Day # theme: Tempus Day
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Light theme with warm colours (WCAG AA compliant) # description: Light theme with warm colours (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 0f64c4
bright5 = 8050a7 bright5 = 8050a7
bright6 = 336c87 bright6 = 336c87
bright7 = f8f2e5 bright7 = f8f2e5
#selection-foreground = 68607d # selection-foreground = 68607d
#selection-background = e7e3d7 # selection-background = e7e3d7

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Dusk # theme: Tempus Dusk
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Dark theme with a deep blue-ish, slightly desaturated palette (WCAG AA compliant) # description: Dark theme with a deep blue-ish, slightly desaturated palette (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 9ca5de
bright5 = c69ac6 bright5 = c69ac6
bright6 = 8caeb6 bright6 = 8caeb6
bright7 = a2a8ba bright7 = a2a8ba
#selection-foreground = a29899 # selection-foreground = a29899
#selection-background = 2c3150 # selection-background = 2c3150

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Fugit # theme: Tempus Fugit
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Light, pleasant theme optimised for long writing/coding sessions (WCAG AA compliant) # description: Light, pleasant theme optimised for long writing/coding sessions (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 485adf
bright5 = a234c0 bright5 = a234c0
bright6 = 00756a bright6 = 00756a
bright7 = fff5f3 bright7 = fff5f3
#selection-foreground = 796271 # selection-foreground = 796271
#selection-background = efe6e4 # selection-background = efe6e4

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Future # theme: Tempus Future
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Dark theme with colours inspired by concept art of outer space (WCAG AAA compliant) # description: Dark theme with colours inspired by concept art of outer space (WCAG AAA compliant)
@ -24,5 +25,5 @@ bright4 = 8ba7ea
bright5 = e08bd6 bright5 = e08bd6
bright6 = 2cbab6 bright6 = 2cbab6
bright7 = b4abac bright7 = b4abac
#selection-foreground = a7a2c4 # selection-foreground = a7a2c4
#selection-background = 2b1329 # selection-background = 2b1329

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Night # theme: Tempus Night
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: High contrast dark theme with bright colours (WCAG AAA compliant) # description: High contrast dark theme with bright colours (WCAG AAA compliant)
@ -24,5 +25,5 @@ bright4 = 8cb4f0
bright5 = de99f0 bright5 = de99f0
bright6 = 00ca9a bright6 = 00ca9a
bright7 = e0e0e0 bright7 = e0e0e0
#selection-foreground = c4bdaf # selection-foreground = c4bdaf
#selection-background = 242536 # selection-background = 242536

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Past # theme: Tempus Past
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Light theme inspired by old vaporwave concept art (WCAG AA compliant) # description: Light theme inspired by old vaporwave concept art (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 5559bb
bright5 = b022a7 bright5 = b022a7
bright6 = 07707a bright6 = 07707a
bright7 = f3f2f4 bright7 = f3f2f4
#selection-foreground = 80565d # selection-foreground = 80565d
#selection-background = eae2de # selection-background = eae2de

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Rift # theme: Tempus Rift
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Dark theme with a subdued palette on the green side of the spectrum (WCAG AA compliant) # description: Dark theme with a subdued palette on the green side of the spectrum (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 56bdad
bright5 = cca0ba bright5 = cca0ba
bright6 = 10c480 bright6 = 10c480
bright7 = bbbcbc bright7 = bbbcbc
#selection-foreground = ab9aa9 # selection-foreground = ab9aa9
#selection-background = 283431 # selection-background = 283431

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Spring # theme: Tempus Spring
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Dark theme with a palette inspired by early spring colours (WCAG AA compliant) # description: Dark theme with a palette inspired by early spring colours (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 70afef
bright5 = d095e2 bright5 = d095e2
bright6 = 3cbfaf bright6 = 3cbfaf
bright7 = b5b8b7 bright7 = b5b8b7
#selection-foreground = 99afae # selection-foreground = 99afae
#selection-background = 2a453d # selection-background = 2a453d

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Summer # theme: Tempus Summer
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Dark theme with colours inspired by summer evenings by the sea (WCAG AA compliant) # description: Dark theme with colours inspired by summer evenings by the sea (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 8599ef
bright5 = cc82d7 bright5 = cc82d7
bright6 = 2aacbf bright6 = 2aacbf
bright7 = a0abae bright7 = a0abae
#selection-foreground = 949cbf # selection-foreground = 949cbf
#selection-background = 39304f # selection-background = 39304f

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Tempest # theme: Tempus Tempest
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: A green-scale, subtle theme for late night hackers (WCAG AAA compliant) # description: A green-scale, subtle theme for late night hackers (WCAG AAA compliant)
@ -24,5 +25,5 @@ bright4 = 74e4cd
bright5 = d2d4aa bright5 = d2d4aa
bright6 = 9bdfc4 bright6 = 9bdfc4
bright7 = b6e0ca bright7 = b6e0ca
#selection-foreground = b0c8ca # selection-foreground = b0c8ca
#selection-background = 323535 # selection-background = 323535

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Totus # theme: Tempus Totus
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Light theme for prose or for coding in an open space (WCAG AAA compliant) # description: Light theme for prose or for coding in an open space (WCAG AAA compliant)
@ -24,5 +25,5 @@ bright4 = 2d45b0
bright5 = 700dc9 bright5 = 700dc9
bright6 = 005289 bright6 = 005289
bright7 = ffffff bright7 = ffffff
#selection-foreground = 5e4b4f # selection-foreground = 5e4b4f
#selection-background = efefef # selection-background = efefef

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Warp # theme: Tempus Warp
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Dark theme with a vibrant palette (WCAG AA compliant) # description: Dark theme with a vibrant palette (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 8887f0
bright5 = d85cf2 bright5 = d85cf2
bright6 = 1da1af bright6 = 1da1af
bright7 = a29fa0 bright7 = a29fa0
#selection-foreground = 968282 # selection-foreground = 968282
#selection-background = 261c2c # selection-background = 261c2c

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# theme: Tempus Winter # theme: Tempus Winter
# author: Protesilaos Stavrou (https://protesilaos.com) # author: Protesilaos Stavrou (https://protesilaos.com)
# description: Dark theme with a palette inspired by winter nights at the city (WCAG AA compliant) # description: Dark theme with a palette inspired by winter nights at the city (WCAG AA compliant)
@ -24,5 +25,5 @@ bright4 = 329fcb
bright5 = ca77c5 bright5 = ca77c5
bright6 = 1ba6a4 bright6 = 1ba6a4
bright7 = 8da3b8 bright7 = 8da3b8
#selection-foreground = 91959b # selection-foreground = 91959b
#selection-background = 2a2e38 # selection-background = 2a2e38

View file

@ -1,3 +1,5 @@
# -*- conf -*-
[colors] [colors]
background=e1e2e7 background=e1e2e7
foreground=3760bf foreground=3760bf

View file

@ -1,3 +1,5 @@
# -*- conf -*-
[colors] [colors]
background=1a1b26 background=1a1b26
foreground=c0caf5 foreground=c0caf5

View file

@ -1,3 +1,5 @@
# -*- conf -*-
[colors] [colors]
background=24283b background=24283b
foreground=c0caf5 foreground=c0caf5

View file

@ -1,3 +1,4 @@
# -*- conf -*-
# VisiBone # VisiBone
[cursor] [cursor]

25
themes/zenburn Normal file
View file

@ -0,0 +1,25 @@
# -*- conf -*-
[colors]
foreground=dcdccc
background=111111
## Normal/regular colors (color palette 0-7)
regular0=222222 # black
regular1=cc9393 # red
regular2=7f9f7f # green
regular3=d0bf8f # yellow
regular4=6ca0a3 # blue
regular5=dc8cc3 # magenta
regular6=93e0e3 # cyan
regular7=dcdccc # white
## Bright colors (color palette 8-15)
bright0=666666 # bright black
bright1=dca3a3 # bright red
bright2=bfebbf # bright green
bright3=f0dfaf # bright yellow
bright4=8cd0d3 # bright blue
bright5=fcace3 # bright magenta
bright6=b3ffff # bright cyan
bright7=ffffff # bright white

2
uri.c
View file

@ -159,7 +159,7 @@ uri_parse(const char *uri, size_t len,
char *p = decoded; char *p = decoded;
size_t encoded_len = path_len; size_t encoded_len = path_len;
size_t decoded_len = 0; size_t UNUSED decoded_len = 0;
while (true) { while (true) {
/* Find next '%' */ /* Find next '%' */

View file

@ -746,15 +746,18 @@ tag_cells_for_url(struct terminal *term, const struct url *url, bool value)
if (url->url_mode_dont_change_url_attr) if (url->url_mode_dont_change_url_attr)
return; return;
struct grid *grid = term->url_grid_snapshot;
xassert(grid != NULL);
const struct coord *start = &url->range.start; const struct coord *start = &url->range.start;
const struct coord *end = &url->range.end; const struct coord *end = &url->range.end;
size_t end_r = end->row & (term->grid->num_rows - 1); size_t end_r = end->row & (grid->num_rows - 1);
size_t r = start->row & (term->grid->num_rows - 1); size_t r = start->row & (grid->num_rows - 1);
size_t c = start->col; size_t c = start->col;
struct row *row = term->grid->rows[r]; struct row *row = grid->rows[r];
row->dirty = true; row->dirty = true;
while (true) { while (true) {
@ -766,10 +769,10 @@ tag_cells_for_url(struct terminal *term, const struct url *url, bool value)
break; break;
if (++c >= term->cols) { if (++c >= term->cols) {
r = (r + 1) & (term->grid->num_rows - 1); r = (r + 1) & (grid->num_rows - 1);
c = 0; c = 0;
row = term->grid->rows[r]; row = grid->rows[r];
if (row == NULL) { if (row == NULL) {
/* Un-allocated scrollback. This most likely means a /* Un-allocated scrollback. This most likely means a
* runaway OSC-8 URL. */ * runaway OSC-8 URL. */
@ -788,15 +791,6 @@ urls_render(struct terminal *term)
if (tll_length(win->term->urls) == 0) if (tll_length(win->term->urls) == 0)
return; return;
xassert(tll_length(win->urls) == 0);
tll_foreach(win->term->urls, it) {
struct wl_url url = {.url = &it->item};
wayl_win_subsurface_new(win, &url.surf, false);
tll_push_back(win->urls, url);
tag_cells_for_url(term, &it->item, true);
}
/* Dirty the last cursor, to ensure it is erased */ /* Dirty the last cursor, to ensure it is erased */
{ {
struct row *cursor_row = term->render.last_cursor.row; struct row *cursor_row = term->render.last_cursor.row;
@ -819,6 +813,15 @@ urls_render(struct terminal *term)
/* Snapshot the current grid */ /* Snapshot the current grid */
term->url_grid_snapshot = grid_snapshot(term->grid); term->url_grid_snapshot = grid_snapshot(term->grid);
xassert(tll_length(win->urls) == 0);
tll_foreach(win->term->urls, it) {
struct wl_url url = {.url = &it->item};
wayl_win_subsurface_new(win, &url.surf, false);
tll_push_back(win->urls, url);
tag_cells_for_url(term, &it->item, true);
}
render_refresh_urls(term); render_refresh_urls(term);
render_refresh(term); render_refresh(term);
} }
@ -860,7 +863,6 @@ urls_reset(struct terminal *term)
} }
tll_foreach(term->urls, it) { tll_foreach(term->urls, it) {
tag_cells_for_url(term, &it->item, false);
url_destroy(&it->item); url_destroy(&it->item);
tll_remove(term->urls, it); tll_remove(term->urls, it);
} }

1
utils/meson.build Normal file
View file

@ -0,0 +1 @@
executable('xtgettcap', 'xtgettcap.c')

Some files were not shown because too many files have changed in this diff Show more