diff --git a/CHANGELOG.md b/CHANGELOG.md index 50e87711..92612adb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/INSTALL.md b/INSTALL.md index 1cb0d9a7..9fa651e1 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -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, you’ll 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 " --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 " --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 diff --git a/PKGBUILD b/PKGBUILD index d5a99516..67894b80 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -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 } diff --git a/README.md b/README.md index 2c3281ad..644b2b55 100644 --- a/README.md +++ b/README.md @@ -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. +Foot’s default behavior is to use the monitor’s DPI to size fonts when +output scaling has been disabled. On monitors where output scaling has +been enabled, fonts will instead be sized using the scaling +factor. -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 monitor’s 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 monitor’s 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 monitor’s -DPI to size fonts when output scaling has been disabled. On monitors -where output scaling has been enabled, fonts will instead be sized -using the scaling factor. 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. diff --git a/client.c b/client.c index 975269b1..93dcc666 100644 --- a/client.c +++ b/client.c @@ -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; } diff --git a/config.h b/config.h index 1fe82257..f8c7aaf0 100644 --- a/config.h +++ b/config.h @@ -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; diff --git a/doc/benchmark.md b/doc/benchmark.md index a5cc416a..244f25b9 100644 --- a/doc/benchmark.md +++ b/doc/benchmark.md @@ -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) diff --git a/foot-features.h b/foot-features.h index 8da22f6c..ae00c564 100644 --- a/foot-features.h +++ b/foot-features.h @@ -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 +} diff --git a/ime.c b/ime.c index ec38be18..91a746e9 100644 --- a/ime.c +++ b/ime.c @@ -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 diff --git a/ime.h b/ime.h index ed2fe507..2aa4efe4 100644 --- a/ime.h +++ b/ime.h @@ -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); diff --git a/input.c b/input.c index bc28bbf2..9a26e778 100644 --- a/input.c +++ b/input.c @@ -450,7 +450,7 @@ maybe_repair_key_combo(const struct seat *seat, /* Check if key combo’s 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}; diff --git a/main.c b/main.c index 0941d3db..2c14d6d0 100644 --- a/main.c +++ b/main.c @@ -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; } diff --git a/meson.build b/meson.build index 01f27be9..9fbf99a6 100644 --- a/meson.build +++ b/meson.build @@ -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() diff --git a/pgo/pgo.c b/pgo/pgo.c index 933d100c..ec8a7883 100644 --- a/pgo/pgo.c +++ b/pgo/pgo.c @@ -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) diff --git a/render.c b/render.c index fcd07f56..b857e360 100644 --- a/render.c +++ b/render.c @@ -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; diff --git a/search.c b/search.c index 30a50ae5..02cea9e0 100644 --- a/search.c +++ b/search.c @@ -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; diff --git a/sixel.c b/sixel.c index 3953f735..cac60493 100644 --- a/sixel.c +++ b/sixel.c @@ -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 won’t 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 sixel’s final image size is *at + * least* this large */ + term->sixel.max_non_empty_row_no = pv - 1; + } } term->sixel.state = SIXEL_DECSIXEL; diff --git a/terminal.c b/terminal.c index 68a7a138..1984562f 100644 --- a/terminal.c +++ b/terminal.c @@ -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 diff --git a/terminal.h b/terminal.h index 044fc581..ce808ce8 100644 --- a/terminal.h +++ b/terminal.h @@ -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); diff --git a/wayland.c b/wayland.c index a092e054..edc9b482 100644 --- a/wayland.c +++ b/wayland.c @@ -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); diff --git a/wayland.h b/wayland.h index 34314414..0b916ccc 100644 --- a/wayland.h +++ b/wayland.h @@ -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 {