Merge branch 'master' into releases/1.7

This commit is contained in:
Daniel Eklöf 2021-03-28 14:24:38 +02:00
commit 0d0ebebb64
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
21 changed files with 464 additions and 263 deletions

View file

@ -1,5 +1,6 @@
# Changelog
* [Unreleased](#unreleased)
* [1.7.0](#1-7-0)
* [1.6.4](#1-6-4)
* [1.6.3](#1-6-3)
@ -23,6 +24,40 @@
* [1.2.0](#1-2-0)
## Unreleased
### Added
### Changed
* Update PGO build instructions in `INSTALL.md`
(https://codeberg.org/dnkl/foot/issues/418).
* In scrollback search mode, empty cells can now be matched by spaces.
### Deprecated
### Removed
### Fixed
* Logic that repairs invalid key bindings ended up breaking valid key
bindings instead (https://codeberg.org/dnkl/foot/issues/407).
* Custom `line-height` settings now scale when increasing or
decreasing the font size at run-time.
* Newlines sometimes incorrectly inserted into copied text
(https://codeberg.org/dnkl/foot/issues/410).
* Crash when compositor send `text-input-v3::enter` events without
first having sent a `keyboard::enter` event
(https://codeberg.org/dnkl/foot/issues/411).
* Deadlock when rendering sixel images.
* URL labels, scrollback search box or scrollback position indicator
sometimes not showing up, caused by invalidly sized surface buffers
when output scaling was enabled
(https://codeberg.org/dnkl/foot/issues/409).
* Empty sixels resulted in non-empty images.
### Security
### Contributors
## 1.7.0
### Added

View file

@ -65,6 +65,7 @@ In addition to the dev variant of the packages above, you need:
* wayland protocols
* ncurses (needed to generate terminfo)
* scdoc (for man page generation)
* llvm (for PGO builds with Clang)
* [tllist](https://codeberg.org/dnkl/tllist) [^1]
A note on compilers; in general, foot runs **much** faster when
@ -184,21 +185,15 @@ slower!) binary.
First, configure the build directory:
```sh
export CFLAGS="$CFLAGS -O3 -Wno-missing-profile"
export CFLAGS="$CFLAGS -O3"
meson --buildtype=release --prefix=/usr -Db_lto=true ../..
```
It is **very** important `-O3` is being used here, as GCC-10.1.x and
later have a regression where PGO with `-O2` is **much** slower.
If you are using Clang instead of GCC, use the following `CFLAGS` instead:
```sh
export CFLAGS="$CFLAGS -O3 \
-Wno-ignored-optimization-argument \
-Wno-profile-instr-out-of-date \
-Wno-profile-instr-unprofiled"
```
Clang users **must** add `-Wno-ignored-optimization-argument` to
`CFLAGS`.
Then, tell meson we want to _generate_ profiling data, and build:
@ -234,6 +229,8 @@ We will use the `pgo` binary along with input corpus generated by
`scripts/generate-alt-random-writes.py`:
```sh
./footclient --version
./foot --version
tmp_file=$(mktemp)
../../scripts/generate-alt-random-writes \
--rows=67 \
@ -253,7 +250,12 @@ tmp_file=$(mktemp)
rm ${tmp_file}
```
The snippet above first creates an (empty) temporary file. Then, it
The first step, running `./foot --version` and `./footclient
--version` might seem unnecessary, but is needed to ensure we have
_some_ profiling data for functions not covered by the PGO helper
binary. Without this, the final link phase will fail.
The snippet above then creates an (empty) temporary file. Then, it
runs a script that generates random escape sequences (if you cat
`${tmp_file}` in a terminal, youll see random colored characters all
over the screen). Finally, we feed the randomly generated escape
@ -271,14 +273,19 @@ This method requires a running Wayland session.
We will use the script `scripts/generate-alt-random-writes.py`:
```sh
./footclient --version
foot_tmp_file=$(mktemp)
./foot --config=/dev/null --term=xterm sh -c "<path-to-generate-alt-random-writes.py> --scroll --scroll-region --colors-regular --colors-bright --colors-256 --colors-rgb --attr-bold --attr-italic --attr-underline ${foot_tmp_file} && cat ${foot_tmp_file}"
./foot --config=/dev/null --term=xterm sh -c "<path-to-generate-alt-random-writes.py> --scroll --scroll-region --colors-regular --colors-bright --colors-256 --colors-rgb --attr-bold --attr-italic --attr-underline --sixel ${foot_tmp_file} && cat ${foot_tmp_file}"
rm ${foot_tmp_file}
```
You should see a foot window open up, with random colored text. The
window should close after ~1-2s.
The first step, `./footclient --version` might seem unnecessary, but
is needed to ensure we have _some_ profiling data for
`footclient`. Without this, the final link phase will fail.
##### Use the generated PGO data

View file

@ -14,37 +14,70 @@ pkgver() {
}
build() {
local compiler=other
local do_pgo=no
# makepkg uses -O2 by default, but we *really* want -O3
# -Wno-missing-profile since we're not exercising everything when doing PGO builds
export CFLAGS+=" -O3 -Wno-missing-profile"
CFLAGS+=" -O3"
# Figure out which compiler we're using, and whether or not to do PGO
case $(${CC-cc} --version) in
*GCC*)
compiler=gcc
do_pgo=yes
;;
*clang*)
compiler=clang
# We need llvm to be able to manage the profiling data
if command -v llvm-profdata > /dev/null; then
do_pgo=yes
# Meson adds -fprofile-correction, which Clang doesn't
# understand
CFLAGS+=" -Wno-ignored-optimization-argument"
fi
;;
esac
meson --prefix=/usr --buildtype=release --wrap-mode=nofallback -Db_lto=true ..
find -name "*.gcda" -delete
meson configure -Db_pgo=generate
ninja
if [[ ${do_pgo} == yes ]]; then
find -name "*.gcda" -delete
meson configure -Db_pgo=generate
ninja
script_options="--scroll --scroll-region --colors-regular --colors-bright --colors-256 --colors-rgb --attr-bold --attr-italic --attr-underline --sixel"
local script_options="--scroll --scroll-region --colors-regular --colors-bright --colors-256 --colors-rgb --attr-bold --attr-italic --attr-underline --sixel"
tmp_file=$(mktemp)
tmp_file=$(mktemp)
if [[ -v WAYLAND_DISPLAY ]]; then
./foot \
--config /dev/null \
--term=xterm \
sh -c "../scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}"
else
../scripts/generate-alt-random-writes.py \
--rows=67 \
--cols=135 \
${script_options} \
${tmp_file}
./pgo ${tmp_file} ${tmp_file} ${tmp_file}
if [[ -v WAYLAND_DISPLAY ]]; then
./footclient --version
./foot \
--config /dev/null \
--term=xterm \
sh -c "../scripts/generate-alt-random-writes.py ${script_options} ${tmp_file} && cat ${tmp_file}"
else
./footclient --version
./foot --version
../scripts/generate-alt-random-writes.py \
--rows=67 \
--cols=135 \
${script_options} \
${tmp_file}
./pgo ${tmp_file} ${tmp_file} ${tmp_file}
fi
rm "${tmp_file}"
if [[ ${compiler} == clang ]]; then
llvm-profdata merge default_*profraw --output=default.profdata
fi
meson configure -Db_pgo=use
fi
rm "${tmp_file}"
meson configure -Db_pgo=use
ninja
}

View file

@ -345,22 +345,23 @@ This is not how it is meant to be. Fonts are measured in _point sizes_
**for a reason**; a given point size should have the same height on
all mediums, be it printers or monitors, regardless of their DPI.
Foot will always use the monitor's physical DPI value. Scale factors
are irrelevant (well, they affect e.g. padding, but not the font
size). This means the glyphs rendered by foot should always have the
same physical height, regardless of monitor.
Foots default behavior is to use the monitors DPI to size fonts when
output scaling has been disabled. On monitors where output scaling has
been enabled, fonts will instead be sized using the scaling
factor.
Foot will re-size the fonts on-the-fly when the window is moved
between screens with different DPIs values. If the window covers
This can be changed to either **always** use the monitors DPI
(regardless of scaling factor), or to **never** use it. See the
`dpi-aware` option in `foot.ini`. See the man page, **foot.ini**(5)
for more information.
When fonts are sized using the monitors DPI, glyphs should always
have the same physical height, regardless of monitor.
Furthermore, foot will re-size the fonts on-the-fly when the window is
moved between screens with different DPIs values. If the window covers
multiple screens, with different DPIs, the highest DPI will be used.
Starting with foot-1.6, the _default_ behavior is to use the monitors
DPI to size fonts when output scaling has been disabled. On monitors
where output scaling has been enabled, fonts will instead be sized
using the scaling factor. This can be changed with the `dpi-aware`
option in `foot.ini`. See the man page, **foot.ini**(5) for more
information.
_Note_: if you configure **pixelsize**, rather than **size**, then DPI
changes will **not** change the font size. Pixels are always pixels.

View file

@ -35,8 +35,10 @@ static const char *
version_and_features(void)
{
static char buf[256];
snprintf(buf, sizeof(buf), "version: %s %cime",
FOOT_VERSION, feature_ime() ? '+' : '-');
snprintf(buf, sizeof(buf), "version: %s %cime %cpgo",
FOOT_VERSION,
feature_ime() ? '+' : '-',
feature_pgo() ? '+' : '-');
return buf;
}

View file

@ -53,12 +53,6 @@ struct config_mouse_binding {
struct config_binding_pipe pipe;
};
/* If px != 0 then px is valid, otherwise pt is valid */
struct pt_or_px {
int16_t px;
float pt;
};
struct config_spawn_template {
char *raw_cmd;
char **argv;

View file

@ -18,7 +18,7 @@ terminal. This is done **20** times for each test. Then it calculates
the _mean_ and _standard deviation_ for each test.
## 2020-10-09
## 2021-03-20
### System
@ -41,16 +41,16 @@ Scrollback: 10000 lines
### Results
| Benchmark | Foot (GCC+PGO) 1.5.0.r90 | Foot 1.5.0.r90 | Alacritty 0.5.0 | URxvt 9.22 | XTerm 360 |
|------------------------|-------------------------:|---------------:|-------------------:|---------------:|---------------:|
| alt-random | 0.353s ±0.007 | 0.685s ±0.005 | 0.903s ±0.011 | 1.102s ±0.004 | 12.886s ±0.064 |
| alt-random-colors | 0.354s ±0.019 | 0.665s ±0.004 | 0.933s ±0.004 | 1.149s ±0.013 | 11.739s ±0.093 |
| scrolling | 1.387s ±0.077 | 1.257s ±0.032 | 1.048s ±0.011 | 1.001s ±0.023 | 38.187s ±0.192 |
| scrolling-filled-lines | 0.607s ±0.008 | 0.834s ±0.038 | 1.246s ±0.020 | 1.224s ±0.008 | 6.619s ±0.166 |
| unicode-random | 0.224s ±0.001 | 0.144s ±0.001 | 0.092s ±0.004 [^1] | 21.294s ±1.580 | 26.594s ±3.801 |
| Benchmark | Foot (GCC+PGO) 1.7.0.r2 | Foot 1.7.0.r2 | Alacritty 0.7.2 | URxvt 9.22 | XTerm 366 |
|------------------------|------------------------:|--------------:|-------------------:|---------------:|---------------:|
| alt-random | 0.382s ±0.003 | 0.550s ±0.007 | 0.995s ±0.010 | 1.201s ±0.006 | 12.756s ±0.045 |
| alt-random-colors | 0.380s ±0.002 | 0.543s ±0.003 | 1.017s ±0.013 | 1.399s ±0.018 | 11.591s ±0.141 |
| scrolling | 1.302s ±0.019 | 1.284s ±0.052 | 1.107s ±0.028 | 1.097s ±0.015 | 37.537s ±0.121 |
| scrolling-filled-lines | 0.646s ±0.016 | 0.610s ±0.003 | 1.290s ±0.012 | 1.325s ±0.037 | 6.817s ±0.084 |
| unicode-random | 0.167s ±0.001 | 0.276s ±0.445 | 0.097s ±0.002 [^1] | 18.032s ±0.334 | 29.731s ±3.746 |
## 2020-12-21
## 2021-03-20
### System
@ -73,13 +73,13 @@ Scrollback=10000 lines
### Results
| Benchmark | Foot (GCC+PGO) 1.6.0.r30 | Foot (no PGO) 1.6.0.r30 | Alacritty 0.6.0 | URxvt 9.22 | St 0.8.4 | XTerm 362 |
|------------------------|-------------------------:|------------------------:|-------------------:|-----------------:|--------------:|----------------:|
| alt-random | 0.734s ±0.051 | 1.186s ±0.101 | 1.580s ±0.083 | 1.709s ±0.090 | 1.953s ±0.038 | 38.693s ±0.298 |
| alt-random-colors | 0.728s ±0.047 | 1.267s ±0.090 | 1.579s ±0.073 | 2.108s ±0.121 | 2.185s ±0.099 | 34.123s ±0.194 |
| scrolling | 1.639s ±0.040 | 1.641s ±0.053 | 1.397s ±0.048 | 1.389s ±0.046 | 3.599s ±0.124 | 136.514s ±0.534 |
| scrolling-filled-lines | 1.328s ±0.050 | 1.640s ±0.052 | 2.108s ±0.068 | 2.032s ±0.121 | 2.718s ±0.088 | 21.383s ±0.072 |
| unicode-random | 0.304s ±0.018 | 0.271s ±0.017 | 0.143s ±0.002 [^1] | 20.543s ±0.098 | crashed | 16.013s ±0.253 |
| Benchmark | Foot (GCC+PGO) 1.7.0.r2 | Foot (no PGO) 1.7.0.r2 | Alacritty 0.7.2 | URxvt 9.22 | St 0.8.4 | XTerm 366 |
|------------------------|------------------------:|-----------------------:|-------------------:|-----------------:|--------------:|----------------:|
| alt-random | 0.714s ±0.047 | 0.900s ±0.041 | 1.586s ±0.045 | 1.684s ±0.034 | 2.054s ±0.121 | 37.205s ±0.252 |
| alt-random-colors | 0.736s ±0.054 | 0.950s ±0.082 | 1.565s ±0.043 | 2.150s ±0.137 | 2.195s ±0.154 | 33.112s ±0.167 |
| scrolling | 1.593s ±0.070 | 1.559s ±0.055 | 1.517s ±0.079 | 1.462s ±0.052 | 3.308s ±0.133 | 134.432s ±0.436 |
| scrolling-filled-lines | 1.178s ±0.044 | 1.309s ±0.045 | 2.281s ±0.086 | 2.044s ±0.060 | 2.732s ±0.056 | 20.753s ±0.067 |
| unicode-random | 0.349s ±0.009 | 0.352s ±0.007 | 0.148s ±0.010 [^1] | 19.090s ±0.363 | crashed | 15.579s ±0.093 |
[^1]: [Alacritty and "unicode-random"](#alacritty-and-unicode-random)

View file

@ -10,3 +10,12 @@ static inline bool feature_ime(void)
return false;
#endif
}
static inline bool feature_pgo(void)
{
#if defined(FOOT_PGO_ENABLED) && FOOT_PGO_ENABLED
return true;
#else
return false;
#endif
}

171
ime.c
View file

@ -37,10 +37,6 @@ leave(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
struct seat *seat = data;
LOG_DBG("leave: seat=%s", seat->name);
struct terminal *term = seat->kbd_focus;
if (term != NULL)
term_ime_reset(term);
ime_disable(seat);
seat->ime.focused = false;
}
@ -53,7 +49,7 @@ preedit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
struct seat *seat = data;
ime_reset_preedit(seat);
ime_reset_pending_preedit(seat);
if (text != NULL) {
seat->ime.preedit.pending.text = xstrdup(text);
@ -70,7 +66,7 @@ commit_string(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
struct seat *seat = data;
ime_reset_commit(seat);
ime_reset_pending_commit(seat);
if (text != NULL)
seat->ime.commit.pending.text = xstrdup(text);
@ -107,6 +103,7 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
LOG_DBG("done: serial=%u", serial);
struct seat *seat = data;
struct terminal *term = seat->kbd_focus;
if (seat->ime.serial != serial) {
LOG_DBG("IME serial mismatch: expected=0x%08x, got 0x%08x",
@ -114,16 +111,26 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
return;
}
xassert(seat->kbd_focus);
struct terminal *term = seat->kbd_focus;
if (term == NULL) {
static bool have_warned = false;
if (!have_warned) {
LOG_WARN(
"%s: text-input::done() received on seat that isn't "
"focusing a terminal window", seat->name);
have_warned = true;
}
}
/* 1. Delete existing pre-edit text */
if (term->ime.preedit.cells != NULL) {
term_ime_reset(term);
if (term->is_searching)
render_refresh_search(term);
else
render_refresh(term);
if (seat->ime.preedit.cells != NULL) {
ime_reset_preedit(seat);
if (term != NULL) {
if (term->is_searching)
render_refresh_search(term);
else
render_refresh(term);
}
}
/*
@ -139,12 +146,14 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
const char *text = seat->ime.commit.pending.text;
size_t len = strlen(text);
if (term->is_searching) {
search_add_chars(term, text, len);
render_refresh_search(term);
} else
term_to_slave(term, text, len);
ime_reset_commit(seat);
if (term != NULL) {
if (term->is_searching) {
search_add_chars(term, text, len);
render_refresh_search(term);
} else
term_to_slave(term, text, len);
}
ime_reset_pending_commit(seat);
}
/* 4. Calculate surrounding text to send - not supported */
@ -155,41 +164,41 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
: 0;
if (wchars == 0 || wchars == (size_t)-1) {
ime_reset_preedit(seat);
ime_reset_pending_preedit(seat);
return;
}
/* First, convert to unicode */
term->ime.preedit.text = xmalloc((wchars + 1) * sizeof(wchar_t));
mbstowcs(term->ime.preedit.text, seat->ime.preedit.pending.text, wchars);
term->ime.preedit.text[wchars] = L'\0';
seat->ime.preedit.text = xmalloc((wchars + 1) * sizeof(wchar_t));
mbstowcs(seat->ime.preedit.text, seat->ime.preedit.pending.text, wchars);
seat->ime.preedit.text[wchars] = L'\0';
/* Next, count number of cells needed */
size_t cell_count = 0;
size_t widths[wchars + 1];
for (size_t i = 0; i < wchars; i++) {
int width = max(wcwidth(term->ime.preedit.text[i]), 1);
int width = max(wcwidth(seat->ime.preedit.text[i]), 1);
widths[i] = width;
cell_count += width;
}
/* Allocate cells */
term->ime.preedit.cells = xmalloc(
cell_count * sizeof(term->ime.preedit.cells[0]));
term->ime.preedit.count = cell_count;
seat->ime.preedit.cells = xmalloc(
cell_count * sizeof(seat->ime.preedit.cells[0]));
seat->ime.preedit.count = cell_count;
/* Populate cells */
for (size_t i = 0, cell_idx = 0; i < wchars; i++) {
struct cell *cell = &term->ime.preedit.cells[cell_idx];
struct cell *cell = &seat->ime.preedit.cells[cell_idx];
int width = widths[i];
cell->wc = term->ime.preedit.text[i];
cell->wc = seat->ime.preedit.text[i];
cell->attrs = (struct attributes){.clean = 0};
for (int j = 1; j < width; j++) {
cell = &term->ime.preedit.cells[cell_idx + j];
cell = &seat->ime.preedit.cells[cell_idx + j];
cell->wc = CELL_MULT_COL_SPACER;
cell->attrs = (struct attributes){.clean = 1};
}
@ -206,18 +215,18 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
/* Note: docs says *both* begin and end should be -1,
* but what else can we do if only one is -1? */
LOG_DBG("pre-edit cursor is hidden");
term->ime.preedit.cursor.hidden = true;
term->ime.preedit.cursor.start = -1;
term->ime.preedit.cursor.end = -1;
seat->ime.preedit.cursor.hidden = true;
seat->ime.preedit.cursor.start = -1;
seat->ime.preedit.cursor.end = -1;
}
else if (seat->ime.preedit.pending.cursor_begin == byte_len &&
seat->ime.preedit.pending.cursor_end == byte_len)
{
/* Cursor is *after* the entire pre-edit string */
term->ime.preedit.cursor.hidden = false;
term->ime.preedit.cursor.start = cell_count;
term->ime.preedit.cursor.end = cell_count;
seat->ime.preedit.cursor.hidden = false;
seat->ime.preedit.cursor.start = cell_count;
seat->ime.preedit.cursor.end = cell_count;
}
else {
@ -271,7 +280,7 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
/* Expand cursor end to end of glyph */
while (cell_end > cell_begin && cell_end < cell_count &&
term->ime.preedit.cells[cell_end].wc == CELL_MULT_COL_SPACER)
seat->ime.preedit.cells[cell_end].wc == CELL_MULT_COL_SPACER)
{
cell_end++;
}
@ -284,54 +293,69 @@ done(void *data, struct zwp_text_input_v3 *zwp_text_input_v3,
xassert(cell_end >= 0);
xassert(cell_end <= cell_count);
term->ime.preedit.cursor.hidden = false;
term->ime.preedit.cursor.start = cell_begin;
term->ime.preedit.cursor.end = cell_end;
seat->ime.preedit.cursor.hidden = false;
seat->ime.preedit.cursor.start = cell_begin;
seat->ime.preedit.cursor.end = cell_end;
}
/* Underline pre-edit string that is *not* covered by the cursor */
bool hidden = term->ime.preedit.cursor.hidden;
int start = term->ime.preedit.cursor.start;
int end = term->ime.preedit.cursor.end;
bool hidden = seat->ime.preedit.cursor.hidden;
int start = seat->ime.preedit.cursor.start;
int end = seat->ime.preedit.cursor.end;
for (size_t i = 0, cell_idx = 0; i < wchars; cell_idx += widths[i], i++) {
if (hidden || start == end || cell_idx < start || cell_idx >= end) {
struct cell *cell = &term->ime.preedit.cells[cell_idx];
struct cell *cell = &seat->ime.preedit.cells[cell_idx];
cell->attrs.underline = true;
}
}
ime_reset_preedit(seat);
ime_reset_pending_preedit(seat);
if (term->is_searching)
render_refresh_search(term);
else
render_refresh(term);
if (term != NULL) {
if (term->is_searching)
render_refresh_search(term);
else
render_refresh(term);
}
}
void
ime_reset_preedit(struct seat *seat)
ime_reset_pending_preedit(struct seat *seat)
{
free(seat->ime.preedit.pending.text);
seat->ime.preedit.pending.text = NULL;
}
void
ime_reset_commit(struct seat *seat)
ime_reset_pending_commit(struct seat *seat)
{
free(seat->ime.commit.pending.text);
seat->ime.commit.pending.text = NULL;
}
void
ime_reset(struct seat *seat)
ime_reset_pending(struct seat *seat)
{
ime_reset_preedit(seat);
ime_reset_commit(seat);
ime_reset_pending_preedit(seat);
ime_reset_pending_commit(seat);
}
void
ime_send_cursor_rect(struct seat *seat, struct terminal *term)
ime_reset_preedit(struct seat *seat)
{
if (seat->ime.preedit.cells == NULL)
return;
free(seat->ime.preedit.text);
free(seat->ime.preedit.cells);
seat->ime.preedit.text = NULL;
seat->ime.preedit.cells = NULL;
seat->ime.preedit.count = 0;
}
static void
ime_send_cursor_rect(struct seat *seat)
{
if (unlikely(seat->wayl->text_input_manager == NULL))
return;
@ -339,7 +363,9 @@ ime_send_cursor_rect(struct seat *seat, struct terminal *term)
if (!seat->ime.focused)
return;
if (!term->ime.enabled)
struct terminal *term = seat->kbd_focus;
if (!term->ime_enabled)
return;
if (seat->ime.cursor_rect.pending.x == seat->ime.cursor_rect.sent.x &&
@ -369,16 +395,18 @@ ime_enable(struct seat *seat)
if (unlikely(seat->wayl->text_input_manager == NULL))
return;
struct terminal *term = seat->kbd_focus;
xassert(term != NULL);
if (!seat->ime.focused)
return;
if (!term->ime.enabled)
struct terminal *term = seat->kbd_focus;
if (term == NULL)
return;
ime_reset(seat);
if (!term->ime_enabled)
return;
ime_reset_pending(seat);
ime_reset_preedit(seat);
zwp_text_input_v3_enable(seat->wl_text_input);
zwp_text_input_v3_set_content_type(
@ -408,7 +436,8 @@ ime_disable(struct seat *seat)
if (!seat->ime.focused)
return;
ime_reset(seat);
ime_reset_pending(seat);
ime_reset_preedit(seat);
zwp_text_input_v3_disable(seat->wl_text_input);
zwp_text_input_v3_commit(seat->wl_text_input);
@ -416,10 +445,12 @@ ime_disable(struct seat *seat)
}
void
ime_update_cursor_rect(struct seat *seat, struct terminal *term)
ime_update_cursor_rect(struct seat *seat)
{
struct terminal *term = seat->kbd_focus;
/* Set in render_ime_preedit() */
if (term->ime.preedit.cells != NULL)
if (seat->ime.preedit.cells != NULL)
goto update;
/* Set in render_search_box() */
@ -448,7 +479,7 @@ ime_update_cursor_rect(struct seat *seat, struct terminal *term)
seat->ime.cursor_rect.pending.height = height;
update:
ime_send_cursor_rect(seat, term);
ime_send_cursor_rect(seat);
}
const struct zwp_text_input_v3_listener text_input_listener = {
@ -464,11 +495,11 @@ const struct zwp_text_input_v3_listener text_input_listener = {
void ime_enable(struct seat *seat) {}
void ime_disable(struct seat *seat) {}
void ime_update_cursor_rect(struct seat *seat, struct terminal *term) {}
void ime_update_cursor_rect(struct seat *seat) {}
void ime_reset_pending_preedit(struct seat *seat) {}
void ime_reset_pending_commit(struct seat *seat) {}
void ime_reset_pending(struct seat *seat) {}
void ime_reset_preedit(struct seat *seat) {}
void ime_reset_commit(struct seat *seat) {}
void ime_reset(struct seat *seat) {}
void ime_send_cursor_rect(struct seat *seat, struct terminal *term) {}
#endif

8
ime.h
View file

@ -13,9 +13,9 @@ struct terminal;
void ime_enable(struct seat *seat);
void ime_disable(struct seat *seat);
void ime_update_cursor_rect(struct seat *seat, struct terminal *term);
void ime_update_cursor_rect(struct seat *seat);
void ime_reset_pending_preedit(struct seat *seat);
void ime_reset_pending_commit(struct seat *seat);
void ime_reset_pending(struct seat *seat);
void ime_reset_preedit(struct seat *seat);
void ime_reset_commit(struct seat *seat);
void ime_reset(struct seat *seat);
void ime_send_cursor_rect(struct seat *seat, struct terminal *term);

View file

@ -450,7 +450,7 @@ maybe_repair_key_combo(const struct seat *seat,
/* Check if key combos modifier set intersects */
for (size_t j = 0; j < mod_mask_count; j++) {
if (!(mod_masks[j] & mods))
if ((mod_masks[j] & mods) != mod_masks[j])
continue;
char combo[64] = {0};

6
main.c
View file

@ -45,8 +45,10 @@ static const char *
version_and_features(void)
{
static char buf[256];
snprintf(buf, sizeof(buf), "version: %s %cime",
FOOT_VERSION, feature_ime() ? '+' : '-');
snprintf(buf, sizeof(buf), "version: %s %cime %cpgo",
FOOT_VERSION,
feature_ime() ? '+' : '-',
feature_pgo() ? '+' : '-');
return buf;
}

View file

@ -24,6 +24,9 @@ add_project_arguments(
(get_option('ime')
? ['-DFOOT_IME_ENABLED=1']
: []) +
(get_option('b_pgo') == 'use'
? ['-DFOOT_PGO_ENABLED=1']
: []) +
cc.get_supported_arguments(
['-pedantic',
'-fstrict-aliasing',
@ -114,16 +117,20 @@ version = custom_target(
output: 'version.h',
command: [generate_version_sh, meson.project_version(), '@SOURCE_ROOT@', '@OUTPUT@'])
common = static_library(
'common',
'log.c', 'log.h',
'debug.c', 'debug.h',
'xmalloc.c', 'xmalloc.h',
'xsnprintf.c', 'xsnprintf.h'
)
misc = static_library(
'misc',
'debug.c', 'debug.h',
'hsl.c', 'hsl.h',
'log.c', 'log.h',
'macros.h',
'misc.c', 'misc.h',
'uri.c', 'uri.h',
'xmalloc.c', 'xmalloc.h',
'xsnprintf.c', 'xsnprintf.h',
'uri.c', 'uri.h'
)
vtlib = static_library(
@ -137,7 +144,7 @@ vtlib = static_library(
wl_proto_src + wl_proto_headers,
version,
dependencies: [libepoll, pixman, fcft, tllist, wayland_client],
link_with: misc,
link_with: [common, misc],
)
pgolib = static_library(
@ -150,13 +157,15 @@ pgolib = static_library(
link_with: vtlib,
)
executable(
'pgo',
'pgo/pgo.c',
wl_proto_src + wl_proto_headers,
dependencies: [math, threads, libepoll, pixman, wayland_client, fcft, tllist],
link_with: pgolib,
)
if get_option('b_pgo') == 'generate'
executable(
'pgo',
'pgo/pgo.c',
wl_proto_src + wl_proto_headers,
dependencies: [math, threads, libepoll, pixman, wayland_client, fcft, tllist],
link_with: pgolib,
)
endif
executable(
'foot',
@ -192,14 +201,11 @@ executable(
executable(
'footclient',
'client.c', 'client-protocol.h',
'debug.c', 'debug.h',
'foot-features.h',
'log.c', 'log.h',
'macros.h',
'util.h',
'xmalloc.c', 'xmalloc.h',
'xsnprintf.c', 'xsnprintf.h',
version,
link_with: common,
install: true)
if tic.found()

View file

@ -129,6 +129,7 @@ void cmd_scrollback_down(struct terminal *term, int rows) {}
void ime_enable(struct seat *seat) {}
void ime_disable(struct seat *seat) {}
void ime_reset_preedit(struct seat *seat) {}
void
notify_notify(const struct terminal *term, const char *title, const char *body)

168
render.c
View file

@ -1114,12 +1114,12 @@ render_sixel_images(struct terminal *term, pixman_image_t *pix,
}
}
static void
render_ime_preedit(struct terminal *term, struct buffer *buf)
{
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
if (likely(term->ime.preedit.cells == NULL))
static void
render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
struct buffer *buf)
{
if (likely(seat->ime.preedit.cells == NULL))
return;
if (unlikely(term->is_searching))
@ -1135,10 +1135,10 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
if (cursor.row < 0 || cursor.row >= term->rows)
return;
int cells_needed = term->ime.preedit.count;
int cells_needed = seat->ime.preedit.count;
if (term->ime.preedit.cursor.start == cells_needed &&
term->ime.preedit.cursor.end == cells_needed)
if (seat->ime.preedit.cursor.start == cells_needed &&
seat->ime.preedit.cursor.end == cells_needed)
{
/* Cursor will be drawn *after* the pre-edit string, i.e. in
* the cell *after*. This means we need to copy, and dirty,
@ -1160,8 +1160,8 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
col_idx -= cells_used - cells_left;
if (cells_needed > cells_used) {
int start = term->ime.preedit.cursor.start;
int end = term->ime.preedit.cursor.end;
int start = seat->ime.preedit.cursor.start;
int end = seat->ime.preedit.cursor.end;
if (start == end) {
/* Ensure *end* of pre-edit string is visible */
@ -1177,7 +1177,7 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
/* Make sure we don't start in the middle of a character */
while (ime_ofs < cells_needed &&
term->ime.preedit.cells[ime_ofs].wc == CELL_MULT_COL_SPACER)
seat->ime.preedit.cells[ime_ofs].wc == CELL_MULT_COL_SPACER)
{
ime_ofs++;
}
@ -1208,9 +1208,9 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
row->dirty = true;
/* Render pre-edit text */
xassert(term->ime.preedit.cells[ime_ofs].wc != CELL_MULT_COL_SPACER);
for (int i = 0, idx = ime_ofs; idx < term->ime.preedit.count; i++, idx++) {
const struct cell *cell = &term->ime.preedit.cells[idx];
xassert(seat->ime.preedit.cells[ime_ofs].wc != CELL_MULT_COL_SPACER);
for (int i = 0, idx = ime_ofs; idx < seat->ime.preedit.count; i++, idx++) {
const struct cell *cell = &seat->ime.preedit.cells[idx];
if (cell->wc == CELL_MULT_COL_SPACER)
continue;
@ -1223,11 +1223,11 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
render_cell(term, buf->pix[0], row, col_idx + i, row_idx, false);
}
int start = term->ime.preedit.cursor.start - ime_ofs;
int end = term->ime.preedit.cursor.end - ime_ofs;
int start = seat->ime.preedit.cursor.start - ime_ofs;
int end = seat->ime.preedit.cursor.end - ime_ofs;
if (!term->ime.preedit.cursor.hidden) {
const struct cell *start_cell = &term->ime.preedit.cells[0];
if (!seat->ime.preedit.cursor.hidden) {
const struct cell *start_cell = &seat->ime.preedit.cells[0];
pixman_color_t fg = color_hex_to_pixman(term->colors.fg);
pixman_color_t bg = color_hex_to_pixman(term->colors.bg);
@ -1271,6 +1271,17 @@ render_ime_preedit(struct terminal *term, struct buffer *buf)
term->margins.top + row_idx * term->cell_height,
term->width - term->margins.left - term->margins.right,
1 * term->cell_height);
}
#endif
static void
render_ime_preedit(struct terminal *term, struct buffer *buf)
{
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
tll_foreach(term->wl->seats, it) {
if (it->item.kbd_focus == term)
render_ime_preedit_for_seat(term, &it->item, buf);
}
#endif
}
@ -1398,6 +1409,9 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx)
static void
csd_commit(struct terminal *term, struct wl_surface *surf, struct buffer *buf)
{
xassert(buf->width % term->scale == 0);
xassert(buf->height % term->scale == 0);
wl_surface_attach(surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height);
wl_surface_set_buffer_scale(surf, term->scale);
@ -1429,6 +1443,9 @@ render_csd_title(struct terminal *term)
xassert(info.width > 0 && info.height > 0);
xassert(info.width % term->scale == 0);
xassert(info.height % term->scale == 0);
unsigned long cookie = shm_cookie_csd(term, CSD_SURF_TITLE);
struct buffer *buf = shm_get_buffer(
term->wl->shm, info.width, info.height, cookie, false, 1);
@ -1461,6 +1478,9 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx)
if (info.width == 0 || info.height == 0)
return;
xassert(info.width % term->scale == 0);
xassert(info.height % term->scale == 0);
unsigned long cookie = shm_cookie_csd(term, surf_idx);
struct buffer *buf = shm_get_buffer(
term->wl->shm, info.width, info.height, cookie, false, 1);
@ -1630,6 +1650,9 @@ render_csd_button(struct terminal *term, enum csd_surface surf_idx)
if (info.width == 0 || info.height == 0)
return;
xassert(info.width % term->scale == 0);
xassert(info.height % term->scale == 0);
unsigned long cookie = shm_cookie_csd(term, surf_idx);
struct buffer *buf = shm_get_buffer(
term->wl->shm, info.width, info.height, cookie, false, 1);
@ -1740,12 +1763,12 @@ render_osd(struct terminal *term,
struct wl_surface *surf, struct wl_subsurface *sub_surf,
struct buffer *buf,
const wchar_t *text, uint32_t _fg, uint32_t _bg,
unsigned width, unsigned height, unsigned x, unsigned y)
unsigned x, unsigned y)
{
pixman_color_t bg = color_hex_to_pixman(_bg);
pixman_image_fill_rectangles(
PIXMAN_OP_SRC, buf->pix[0], &bg, 1,
&(pixman_rectangle16_t){0, 0, width, height});
&(pixman_rectangle16_t){0, 0, buf->width, buf->height});
struct fcft_font *font = term->fonts[0];
pixman_color_t fg = color_hex_to_pixman(_fg);
@ -1769,14 +1792,17 @@ render_osd(struct terminal *term,
x += term->cell_width;
}
xassert(buf->width % term->scale == 0);
xassert(buf->height % term->scale == 0);
quirk_weston_subsurface_desync_on(sub_surf);
wl_surface_attach(surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(surf, 0, 0, width, height);
wl_surface_damage_buffer(surf, 0, 0, buf->width, buf->height);
wl_surface_set_buffer_scale(surf, term->scale);
struct wl_region *region = wl_compositor_create_region(term->wl->compositor);
if (region != NULL) {
wl_region_add(region, 0, 0, width, height);
wl_region_add(region, 0, 0, buf->width, buf->height);
wl_surface_set_opaque_region(surf, region);
wl_region_destroy(region);
}
@ -1862,8 +1888,11 @@ render_scrollback_position(struct terminal *term)
const int scale = term->scale;
const int margin = 3 * scale;
const int width = 2 * margin + cell_count * term->cell_width;
const int height = 2 * margin + term->cell_height;
const int width =
(2 * margin + cell_count * term->cell_width + scale - 1) / scale * scale;
const int height =
(2 * margin + term->cell_height + scale - 1) / scale * scale;
unsigned long cookie = shm_cookie_scrollback_indicator(term);
struct buffer *buf = shm_get_buffer(
@ -1905,7 +1934,7 @@ render_scrollback_position(struct terminal *term)
win->scrollback_indicator.sub,
buf, text,
term->colors.table[0], term->colors.table[8 + 4],
width, height, width - margin - wcslen(text) * term->cell_width, margin);
width - margin - wcslen(text) * term->cell_width, margin);
}
@ -1918,10 +1947,13 @@ render_render_timer(struct terminal *term, struct timeval render_time)
double usecs = render_time.tv_sec * 1000000 + render_time.tv_usec;
swprintf(text, sizeof(text) / sizeof(text[0]), L"%.2f µs", usecs);
const int scale = term->scale;
const int cell_count = wcslen(text);
const int margin = 3 * term->scale;
const int width = 2 * margin + cell_count * term->cell_width;
const int height = 2 * margin + term->cell_height;
const int margin = 3 * scale;
const int width =
(2 * margin + cell_count * term->cell_width + scale - 1) / scale * scale;
const int height =
(2 * margin + term->cell_height + scale - 1) / scale * scale;
unsigned long cookie = shm_cookie_render_timer(term);
struct buffer *buf = shm_get_buffer(
@ -1938,7 +1970,7 @@ render_render_timer(struct terminal *term, struct timeval render_time)
win->render_timer.sub,
buf, text,
term->colors.table[0], term->colors.table[8 + 1],
width, height, margin, margin);
margin, margin);
}
static void frame_callback(
@ -2079,6 +2111,8 @@ grid_render(struct terminal *term)
cursor.row &= term->grid->num_rows - 1;
}
render_sixel_images(term, buf->pix[0], &cursor);
if (term->render.workers.count > 0) {
mtx_lock(&term->render.workers.lock);
term->render.workers.buf = buf;
@ -2088,8 +2122,6 @@ grid_render(struct terminal *term)
xassert(tll_length(term->render.workers.queue) == 0);
}
render_sixel_images(term, buf->pix[0], &cursor);
int first_dirty_row = -1;
for (int r = 0; r < term->rows; r++) {
struct row *row = grid_row_in_view(term->grid, r);
@ -2216,6 +2248,9 @@ grid_render(struct terminal *term)
term->window->surface, 0, 0, INT32_MAX, INT32_MAX);
}
xassert(buf->width % term->scale == 0);
xassert(buf->height % term->scale == 0);
wl_surface_attach(term->window->surface, buf->wl_buf, 0, 0);
quirk_kde_damage_before_attach(term->window->surface);
wl_surface_commit(term->window->surface);
@ -2238,9 +2273,18 @@ render_search_box(struct terminal *term)
*/
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
/* TODO: do we want to/need to handle multi-seat? */
struct seat *ime_seat = NULL;
tll_foreach(term->wl->seats, it) {
if (it->item.kbd_focus == term) {
ime_seat = &it->item;
break;
}
}
size_t text_len = term->search.len;
if (term->ime.preedit.text != NULL)
text_len += wcslen(term->ime.preedit.text);
if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL)
text_len += wcslen(ime_seat->ime.preedit.text);
wchar_t *text = xmalloc((text_len + 1) * sizeof(wchar_t));
text[0] = L'\0';
@ -2250,8 +2294,8 @@ render_search_box(struct terminal *term)
text[term->search.cursor] = L'\0';
/* Insert pre-edit text at cursor */
if (term->ime.preedit.text != NULL)
wcscat(text, term->ime.preedit.text);
if (ime_seat != NULL && ime_seat->ime.preedit.text != NULL)
wcscat(text, ime_seat->ime.preedit.text);
/* And finally everything after the cursor */
wcsncat(text, &term->search.buf[term->search.cursor],
@ -2278,10 +2322,10 @@ render_search_box(struct terminal *term)
const size_t width = term->width - 2 * margin;
const size_t visible_width = min(
term->width - 2 * margin,
2 * margin + wanted_visible_cells * term->cell_width);
(2 * margin + wanted_visible_cells * term->cell_width + scale - 1) / scale * scale);
const size_t height = min(
term->height - 2 * margin,
2 * margin + 1 * term->cell_height);
(2 * margin + 1 * term->cell_height + scale - 1) / scale * scale);
const size_t visible_cells = (visible_width - 2 * margin) / term->cell_width;
size_t glyph_offset = term->render.search_glyph_offset;
@ -2319,18 +2363,18 @@ render_search_box(struct terminal *term)
continue;
#if (FOOT_IME_ENABLED) && FOOT_IME_ENABLED
if (term->ime.preedit.cells != NULL) {
if (term->ime.preedit.cursor.start == term->ime.preedit.cursor.end) {
if (ime_seat != NULL && ime_seat->ime.preedit.cells != NULL) {
if (ime_seat->ime.preedit.cursor.start == ime_seat->ime.preedit.cursor.end) {
/* All IME's I've seen so far keeps the cursor at
* index 0, so ensure the *end* of the pre-edit string
* is visible */
cell_idx += term->ime.preedit.count;
cell_idx += ime_seat->ime.preedit.count;
} else {
/* Try to predict in which direction we'll shift the text */
if (cell_idx + term->ime.preedit.cursor.start > glyph_offset)
cell_idx += term->ime.preedit.cursor.end;
if (cell_idx + ime_seat->ime.preedit.cursor.start > glyph_offset)
cell_idx += ime_seat->ime.preedit.cursor.end;
else
cell_idx += term->ime.preedit.cursor.start;
cell_idx += ime_seat->ime.preedit.cursor.start;
}
}
#endif
@ -2383,8 +2427,10 @@ render_search_box(struct terminal *term)
/* Render cursor */
if (i == term->search.cursor) {
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
bool have_preedit = term->ime.preedit.cells != NULL;
bool hidden = term->ime.preedit.cursor.hidden;
bool have_preedit =
ime_seat != NULL && ime_seat->ime.preedit.cells != NULL;
bool hidden =
ime_seat != NULL && ime_seat->ime.preedit.cursor.hidden;
if (have_preedit && !hidden) {
/* Cursor may be outside the visible area:
@ -2394,13 +2440,13 @@ render_search_box(struct terminal *term)
/* If cursor is outside the visible area, we need to
* adjust our rectangle's position */
int start = term->ime.preedit.cursor.start
int start = ime_seat->ime.preedit.cursor.start
+ min((ssize_t)(cell_idx - glyph_offset), 0);
int end = term->ime.preedit.cursor.end
int end = ime_seat->ime.preedit.cursor.end
+ min((ssize_t)(cell_idx - glyph_offset), 0);
if (start == end) {
int count = min(term->ime.preedit.count, cells_left);
int count = min(ime_seat->ime.preedit.count, cells_left);
/* Underline the entire (visible part of) pre-edit text */
draw_underline(term, buf->pix[0], font, &fg, x, y, count);
@ -2415,7 +2461,7 @@ render_search_box(struct terminal *term)
/* Underline everything before and after the cursor */
int count1 = min(start, cells_left);
int count2 = max(
min(term->ime.preedit.count - term->ime.preedit.cursor.end,
min(ime_seat->ime.preedit.count - ime_seat->ime.preedit.cursor.end,
cells_left - end),
0);
draw_underline(term, buf->pix[0], font, &fg, x, y, count1);
@ -2487,7 +2533,7 @@ render_search_box(struct terminal *term)
}
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
if (term->ime.preedit.cells != NULL)
if (ime_seat != NULL && ime_seat->ime.preedit.cells != NULL)
/* Already rendered */;
else
#endif
@ -2505,6 +2551,9 @@ render_search_box(struct terminal *term)
margin / scale,
max(0, (int32_t)term->height - height - margin) / scale);
xassert(buf->width % scale == 0);
xassert(buf->height % scale == 0);
wl_surface_attach(term->window->search.surf, buf->wl_buf, 0, 0);
wl_surface_damage_buffer(term->window->search.surf, 0, 0, width, height);
wl_surface_set_buffer_scale(term->window->search.surf, scale);
@ -2612,10 +2661,13 @@ render_urls(struct terminal *term)
size_t len = wcslen(label);
int cols = wcswidth(label, len);
const int x_margin = 2 * term->scale;
const int y_margin = 1 * term->scale;
int width = 2 * x_margin + cols * term->cell_width;
int height = 2 * y_margin + term->cell_height;
const int scale = term->scale;
const int x_margin = 2 * scale;
const int y_margin = 1 * scale;
const int width =
(2 * x_margin + cols * term->cell_width + scale - 1) / scale * scale;
const int height =
(2 * y_margin + term->cell_height + scale - 1) / scale * scale;
struct buffer *buf = shm_get_buffer(
term->wl->shm, width, height, shm_cookie_url(url), false, 1);
@ -2648,8 +2700,8 @@ render_urls(struct terminal *term)
? term->conf->colors.jump_label.bg
: term->colors.table[3];
render_osd(term, surf, sub_surf, buf, label,
fg, bg, width, height, x_margin, y_margin);
render_osd(
term, surf, sub_surf, buf, label, fg, bg, x_margin, y_margin);
}
}
@ -2717,7 +2769,7 @@ frame_callback(void *data, struct wl_callback *wl_callback, uint32_t callback_da
tll_foreach(term->wl->seats, it) {
if (it->item.kbd_focus == term)
ime_update_cursor_rect(&it->item, term);
ime_update_cursor_rect(&it->item);
}
term->grid = original_grid;
@ -3173,7 +3225,7 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
tll_foreach(term->wl->seats, it) {
if (it->item.kbd_focus == term)
ime_update_cursor_rect(&it->item, term);
ime_update_cursor_rect(&it->item);
}
term->grid = original_grid;

View file

@ -241,6 +241,9 @@ matches_cell(const struct terminal *term, const struct cell *cell, size_t search
base = composed->base;
}
if (composed == NULL && base == 0 && term->search.buf[search_ofs] == L' ')
return 1;
if (wcsncasecmp(&base, &term->search.buf[search_ofs], 1) != 0)
return -1;

15
sixel.c
View file

@ -48,7 +48,7 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
term->sixel.state = SIXEL_DECSIXEL;
term->sixel.pos = (struct coord){0, 0};
term->sixel.max_non_empty_row_no = 0;
term->sixel.max_non_empty_row_no = -1;
term->sixel.row_byte_ofs = 0;
term->sixel.color_idx = 0;
term->sixel.param = 0;
@ -726,6 +726,11 @@ sixel_unhook(struct terminal *term)
term->sixel.image.height = term->sixel.max_non_empty_row_no + 1;
}
if (term->sixel.image.height == 0 || term->sixel.image.width == 0) {
/* We wont be emitting any sixels - free backing image */
free(term->sixel.image.data);
}
int pixel_row_idx = 0;
int pixel_rows_left = term->sixel.image.height;
const int stride = term->sixel.image.width * sizeof(uint32_t);
@ -1032,7 +1037,7 @@ sixel_add(struct terminal *term, int col, int width, uint32_t color, uint8_t six
size_t ofs = term->sixel.row_byte_ofs + col;
uint32_t *data = &term->sixel.image.data[ofs];
int max_non_empty_row = 0;
int max_non_empty_row = -1;
int row = term->sixel.pos.row;
for (int i = 0; i < 6; i++, sixel >>= 1, data += width) {
@ -1175,7 +1180,11 @@ decgra(struct terminal *term, uint8_t c)
ph <= term->sixel.max_height && pv <= term->sixel.max_width)
{
resize(term, ph, pv);
term->sixel.max_non_empty_row_no = pv - 1;
if (!term->sixel.transparent_bg) {
/* This ensures the sixels final image size is *at
* least* this large */
term->sixel.max_non_empty_row_no = pv - 1;
}
}
term->sixel.state = SIXEL_DECSIXEL;

View file

@ -649,8 +649,8 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
: term->fonts[0]->max_advance.x)
+ pt_or_px_as_pixels(term, &conf->letter_spacing);
term->cell_height = conf->line_height.px >= 0
? pt_or_px_as_pixels(term, &conf->line_height)
term->cell_height = term->font_line_height.px >= 0
? pt_or_px_as_pixels(term, &term->font_line_height)
: max(term->fonts[0]->height,
term->fonts[0]->ascent + term->fonts[0]->descent);
@ -979,6 +979,7 @@ load_fonts_from_conf(struct terminal *term)
}
}
term->font_line_height = term->conf->line_height;
return reload_fonts(term);
}
@ -1181,9 +1182,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.foot_exe = xstrdup(foot_exe),
.cwd = xstrdup(cwd),
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
.ime = {
.enabled = true,
},
.ime_enabled = true,
#endif
};
@ -1196,6 +1195,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
.pt_size = it->item.pt_size, .px_size = it->item.px_size};
}
}
term->font_line_height = conf->line_height;
/* Start the slave/client */
if ((term->slave = slave_spawn(
@ -1780,6 +1780,15 @@ term_font_size_adjust(struct terminal *term, double amount)
}
}
if (term->font_line_height.px >= 0) {
double old_pt_size = term->font_line_height.px > 0
? term->font_line_height.px * 72. / term->font_dpi
: term->font_line_height.pt;
term->font_line_height.px = 0;
term->font_line_height.pt = fmax(old_pt_size + amount, 0);
}
return reload_fonts(term);
}
@ -2394,10 +2403,8 @@ term_kbd_focus_out(struct terminal *term)
return;
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
if (term->ime.preedit.cells != NULL) {
term_ime_reset(term);
if (term_ime_reset(term))
render_refresh(term);
}
#endif
term->kbd_focus = false;
@ -2859,6 +2866,7 @@ term_print(struct terminal *term, wchar_t wc, int width)
cell->attrs = term->vt.attrs;
row->dirty = true;
row->linebreak = false;
cell->attrs.clean = 0;
/* Advance cursor the 'additional' columns while dirty:ing the cells */
@ -2898,6 +2906,7 @@ ascii_printer_fast(struct terminal *term, wchar_t wc)
cell->attrs = term->vt.attrs;
row->dirty = true;
row->linebreak = false;
cell->attrs.clean = 0;
/* Advance cursor */
@ -3026,7 +3035,7 @@ bool
term_ime_is_enabled(const struct terminal *term)
{
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
return term->ime.enabled;
return term->ime_enabled;
#else
return false;
#endif
@ -3036,13 +3045,12 @@ void
term_ime_enable(struct terminal *term)
{
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
if (term->ime.enabled)
if (term->ime_enabled)
return;
LOG_DBG("IME enabled");
term->ime.enabled = true;
term_ime_reset(term);
term->ime_enabled = true;
/* IME is per seat - enable on all seat currently focusing us */
tll_foreach(term->wl->seats, it) {
@ -3056,13 +3064,12 @@ void
term_ime_disable(struct terminal *term)
{
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
if (!term->ime.enabled)
if (!term->ime_enabled)
return;
LOG_DBG("IME disabled");
term->ime.enabled = false;
term_ime_reset(term);
term->ime_enabled = false;
/* IME is per seat - disable on all seat currently focusing us */
tll_foreach(term->wl->seats, it) {
@ -3072,18 +3079,24 @@ term_ime_disable(struct terminal *term)
#endif
}
void
bool
term_ime_reset(struct terminal *term)
{
bool at_least_one_seat_was_reset = false;
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
if (term->ime.preedit.cells != NULL) {
free(term->ime.preedit.text);
free(term->ime.preedit.cells);
term->ime.preedit.text = NULL;
term->ime.preedit.cells = NULL;
term->ime.preedit.count = 0;
tll_foreach(term->wl->seats, it) {
struct seat *seat = &it->item;
if (seat->kbd_focus != term)
continue;
ime_reset_preedit(seat);
at_least_one_seat_was_reset = true;
}
#endif
return at_least_one_seat_was_reset;
}
void

View file

@ -261,6 +261,12 @@ struct url {
};
typedef tll(struct url) url_list_t;
/* If px != 0 then px is valid, otherwise pt is valid */
struct pt_or_px {
int16_t px;
float pt;
};
struct terminal {
struct fdm *fdm;
struct reaper *reaper;
@ -312,6 +318,7 @@ struct terminal {
struct fcft_font *fonts[4];
struct config_font *font_sizes[4];
struct pt_or_px font_line_height;
float font_dpi;
int font_scale;
int16_t font_x_ofs;
@ -560,20 +567,7 @@ struct terminal {
struct grid *url_grid_snapshot;
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
struct {
bool enabled;
struct {
wchar_t *text;
struct cell *cells;
int count;
struct {
bool hidden;
int start; /* Cell index, inclusive */
int end; /* Cell index, exclusive */
} cursor;
} preedit;
} ime;
bool ime_enabled;
#endif
bool is_shutting_down;
@ -706,7 +700,7 @@ bool term_view_to_text(
bool term_ime_is_enabled(const struct terminal *term);
void term_ime_enable(struct terminal *term);
void term_ime_disable(struct terminal *term);
void term_ime_reset(struct terminal *term);
bool term_ime_reset(struct terminal *term);
void term_ime_set_cursor_rect(
struct terminal *term, int x, int y, int width, int height);

View file

@ -189,7 +189,7 @@ seat_destroy(struct seat *seat)
if (seat->wl_seat != NULL)
wl_seat_release(seat->wl_seat);
ime_reset(seat);
ime_reset_pending(seat);
free(seat->clipboard.text);
free(seat->primary.text);
free(seat->name);

View file

@ -256,6 +256,15 @@ struct seat {
int32_t cursor_begin;
int32_t cursor_end;
} pending;
wchar_t *text;
struct cell *cells;
int count;
struct {
bool hidden;
int start; /* Cell index, inclusive */
int end; /* Cell index, exclusive */
} cursor;
} preedit;
struct {