Commit graph

4483 commits

Author SHA1 Message Date
Daniel Eklöf
3bad062f8a
vt: utf8: rotate instead of just shifting when updating compose key
This reduces the number of collisions in even more workloads.
2021-06-24 19:36:39 +02:00
Daniel Eklöf
88ce0e4375
vt: improved key hash algorithm -> reduces number of key collisions 2021-06-24 19:18:06 +02:00
Daniel Eklöf
9a7c6bdcf2
term: CELL_COMB_CHARS chars are keys, not indices -> more range is better
Bump the available key range to 30 bits. This helps reduce key
collisions when handling a large amount of grapheme clusters.
2021-06-24 19:15:53 +02:00
Daniel Eklöf
f20956ff1b
composed: insert: require key to be unique 2021-06-24 19:12:25 +02:00
Daniel Eklöf
4a6dea04c2
install: add -Dgrapheme-clustering to the list of custom meson options 2021-06-24 17:57:34 +02:00
Daniel Eklöf
80c2d9d89d
Merge branch 'harfbuzz' 2021-06-24 17:50:55 +02:00
Daniel Eklöf
3f0f5ec3b7
client: add +/-graphemes to version output 2021-06-24 17:50:44 +02:00
Daniel Eklöf
a319ddf094
foot: add +/-graphemes to version output 2021-06-24 17:50:30 +02:00
Daniel Eklöf
4ea7c5b63f
features: add feature_graphemes()
Returns true if we’re compiled with grapheme shaping support, false
otherwise.
2021-06-24 17:50:04 +02:00
Daniel Eklöf
cf101ea300
changelog: describe what (does not) happens when grapheme-shaping=no 2021-06-24 17:36:57 +02:00
Daniel Eklöf
d5d57c1b20
changelog: composed -> combining 2021-06-24 17:36:52 +02:00
Daniel Eklöf
09c4d16232
changelog: put emphasis on ‘opt-in’ as well 2021-06-24 17:33:09 +02:00
Daniel Eklöf
f19797a5af
changelog: updates to “grapheme shaping” 2021-06-24 17:30:50 +02:00
Daniel Eklöf
415ecfc6fa
vt: codespell: bumb -> bump 2021-06-24 17:30:50 +02:00
Daniel Eklöf
fe8ca23cfe
composed: store compose chains in a binary search tree
The previous implementation stored compose chains in a dynamically
allocated array. Adding a chain was easy: resize the array and append
the new chain at the end. Looking up a compose chain given a compose
chain key/index was also easy: just index into the array.

However, searching for a pre-existing chain given a codepoint sequence
was very slow. Since the array wasn’t sorted, we typically had to scan
through the entire array, just to realize that there is no
pre-existing chain, and that we need to add a new one.

Since this happens for *each* codepoint in a grapheme cluster, things
quickly became really slow.

Things were ok:ish as long as the compose chain struct was small, as
that made it possible to hold all the chains in the cache. Once the
number of chains reached a certain point, or when we were forced to
bump maximum number of allowed codepoints in a chain, we started
thrashing the cache and things got much much worse.

So what can we do?

We can’t sort the array, because

a) that would invalidate all existing chain keys in the grid (and
iterating the entire scrollback and updating compose keys is *not* an
option).

b) inserting a chain becomes slow as we need to first find _where_ to
insert it, and then memmove() the rest of the array.

This patch uses a binary search tree to store the chains instead of a
simple array.

The tree is sorted on a “key”, which is the XOR of all codepoints,
truncated to the CELL_COMB_CHARS_HI-CELL_COMB_CHARS_LO range.

The grid now stores CELL_COMB_CHARS_LO+key, instead of
CELL_COMB_CHARS_LO+index.

Since the key is truncated, collisions may occur. This is handled by
incrementing the key by 1.

Lookup is of course slower than before, O(log n) instead of
O(1).

Insertion is slightly slower as well: technically it’s O(log n)
instead of O(1). However, we also need to take into account the
re-allocating the array will occasionally force a full copy of the
array when it cannot simply be growed.

But finding a pre-existing chain is now *much* faster: O(log n)
instead of O(n). In most cases, the first lookup will either
succeed (return a true match), or fail (return NULL). However, since
key collisions are possible, it may also return false matches. This
means we need to verify the contents of the chain before deciding to
use it instead of inserting a new chain. But remember that this
comparison was being done for each and every chain in the previous
implementation.

With lookups being much faster, and in particular, no longer requiring
us to check the chain contents for every singlec chain, we can now use
a dynamically allocated ‘chars’ array in the chain. This was
previously a hardcoded array of 10 chars.

Using a dynamic allocated array means looking in the array is slower,
since we now need two loads: one to load the pointer, and a second to
load _from_ the pointer.

As a result, the base size of a compose chain (i.e. an “empty” chain)
has now been reduced from 48 bytes to 32. A chain with two codepoints
is 40 bytes. This means we have up to 4 codepoints while still using
less, or the same amount, of memory as before.

Furthermore, the Unicode random test (i.e. write random “unicode”
chars) is now **faster** than current master (i.e. before text-shaping
support was added), **with** test-shaping enabled. With text-shaping
disabled, we’re _even_ faster.
2021-06-24 17:30:49 +02:00
Daniel Eklöf
fcd6327297
term: bump compose chain char array to 10 chars
There are some very long sequences out there... e.g. 👩🏼‍❤️‍💋‍👨🏽
2021-06-24 17:30:49 +02:00
Daniel Eklöf
fd70058795
changelog: add a “grapheme shaping” section 2021-06-24 17:30:49 +02:00
Daniel Eklöf
f3e5c3deb9
doc: foot.ini: grapheme-shaping: mention regular compose characters 2021-06-24 17:30:49 +02:00
Daniel Eklöf
b14524215b
render: use cell cols from compose chain, not grapheme
fcft’s view of how many columns a grapheme cluster is may differ from
our own. Make sure the rendered glyph matches the number of columns
that were allocated when the cluster was printed.
2021-06-24 17:30:48 +02:00
Daniel Eklöf
34e85e7726
scripts: generate-alt-random: add emoji sequences 2021-06-24 17:30:48 +02:00
Daniel Eklöf
81131e3a87
vt: utf8: don’t scan *all* previous chains
When checking if we already have a compose chain for the current
sequence of characters, don’t search the list from the beginning,
unless we have to.

Taking the following things into consideration:

* New compose chains are always appended at the end of the list
* If the current sequence is 3 or more characters, it *must* consist
  of an existing compose chain, plus the new character.

Thus, when searching, start at index 0 if we only have two characters,
since then the base cell originally contained a regular base
character, and not a compose chain. I.e. the new chain may be
_anywhere_ in the chain list.

If however we have a sequence of three or more characters, start at
the index the *base* chain was at. If the chain we’re searching for
exists, it *must* have been added *after* the base chain, and thus
it *must* be located *after* the base chain in the chain list.
2021-06-24 17:30:48 +02:00
Daniel Eklöf
e81d1845bf
vt: utf8: de-duplicate; jump to end of function to print to grid 2021-06-24 17:30:48 +02:00
Daniel Eklöf
c0d9f92e1a
render: don’t modify the cell’s x position. Fixes broken underlines 2021-06-24 17:30:47 +02:00
Daniel Eklöf
dc5019a535
vt: utf8-print: don’t build a compose chain on a zero-width base character 2021-06-24 17:30:47 +02:00
Daniel Eklöf
6187aa0b1b
term: lower maximum number of characters in a compose chain
Before the grapheme cluster segmentation work, we limited the number
of combining characters to base+5. I.e. 6 in total.

For a while now, we’ve had it bumped all the way up to 20. This was
the reason the unicode-random benchmark ran so much slower (i.e. cache
contention).

Looking at emoji’s, there are a couple that need 6 code points,
and *three* that needs 7.

Now, with the limit at 7 chars, and the new ‘width’ member, the
composed struct is 8 bytes larger than before.
2021-06-24 17:30:47 +02:00
Daniel Eklöf
f865612667
vt: utf8-print: check base character before count when looking for existing compose chain
Count is more likely to be the same for many chains. Thus we’re likely
to fail sooner by checking the base character first.
2021-06-24 17:30:47 +02:00
Daniel Eklöf
51295cd7a2
render: we’ve already assigned ‘base’ a couple of lines higher up 2021-06-24 17:30:46 +02:00
Daniel Eklöf
57e636dd8e
vt: don’t call wcwidth() on all combining characters every time we add
We already have all the widths needed to calculate the new one; it’s
the base characters width (base_width), or the previous combining
chain’s width (composed->width) plus the new characters’s
width (width).
2021-06-24 17:30:46 +02:00
Daniel Eklöf
09431dd15c
vt: presentation selectors may be anywhere in the cluster 2021-06-24 17:30:46 +02:00
Daniel Eklöf
96ff29bbd3
render: repair parenthesis after rebase 2021-06-24 17:30:46 +02:00
Daniel Eklöf
b471fe31b1
render: ensure ‘cell_cols’ have been initialized 2021-06-24 17:30:46 +02:00
Daniel Eklöf
50be924285
render: handle fcft_glyph_rasterize() failure correctly 2021-06-24 17:30:46 +02:00
Daniel Eklöf
bd98cb6a73
render: use column count from grapheme instead of first glyph, when we have one 2021-06-24 17:30:46 +02:00
Daniel Eklöf
6c70cd9366
vt: don’t force cols=2 when we see an emoji variant selector
Fish appears to be the only shell expecting this. The rest probably
just does wcswidth(), like usual.
2021-06-24 17:30:45 +02:00
Daniel Eklöf
0a9531ac6c
vt: cache grapheme cluster width in composed struct
* Use regular wcswidth() to calculate the width
* Explicitly set to ‘2’ if we see a emoji variant selector
* Cache the result in the composed struct
2021-06-24 17:30:45 +02:00
Daniel Eklöf
b9ef703eb1
wip: grapheme shaping 2021-06-24 17:30:45 +02:00
Daniel Eklöf
c1cde66f70
Merge branch 'sixel-heap-user-after-free-after-blending' 2021-06-24 17:30:16 +02:00
Daniel Eklöf
a52d867947
sixel: fix crash when splitting up an image across the scrollback
If an image was split up across the scrollback, and the first image
chunk was resized due to it being blended with an already existing
sixel, we crashed.

The reason being the second chunk, emitted *after* the scrollback,
tried to memcpy() image data from a now-free:d image buffer.

The fix is pretty simple: create copies for *all* chunks when an image
has to be split up across the scrollback. Previously, the first chunk
re-used the source image buffer.

Closes #608
2021-06-24 17:08:33 +02:00
Daniel Eklöf
ba26d63829
Merge branch 'custom-terminfo-install-location'
Closes #569
2021-06-24 09:53:09 +02:00
Daniel Eklöf
07bb67130a
changelog: -Dterminfo-install-location meson command line option 2021-06-23 17:04:12 +02:00
Daniel Eklöf
501548080d
install: add the new ‘terminfo-install-location’ option
And flesh out the description of the ‘terminfo’ option, and how it
relates to the new ‘terminfo-install-location’.

Also add instructions on how to manually generate the terminfo files.
2021-06-23 17:04:12 +02:00
Daniel Eklöf
6268fc536b
meson: add -Dterminfo-install-location=disabled|custom-path
Add a new meson option, ‘terminfo-install-location’, that allows you
to customize _where_ the terminfo files are installed, relative to the
installation prefix.

It also recognizes the special value ‘disabled’, in which case the
terminfo files are not installed at all. The terminfo files _are_
however built (allowing us to catch build errors), and foot still
defaults to the ‘foot’ terminfo.

It defaults to $datadir/terminfo

If (the other option) ‘terminfo’ is set to disabled (or tic cannot be
found), terminfo-install-location is automatically set to ‘disabled’.
2021-06-23 16:43:36 +02:00
Daniel Eklöf
ef349bfea2
server: make sure ‘overrides’ have been initialized in all error paths 2021-06-23 15:38:29 +02:00
Daniel Eklöf
15e7e80245
client: make sure ‘fd’ is initialized in all error paths 2021-06-23 15:32:03 +02:00
Daniel Eklöf
68494956b7
Merge branch 'footclient-override'
Closes #600
2021-06-23 15:19:11 +02:00
Daniel Eklöf
5fc8275eb2
client: add and use function push_override()
We now track override data (length + malloc:ed string) in a struct,
and push this struct to our overrides linked list.

There’s a new function, push_override() that takes a string,
calculates its length, strdup() it and pushes it to the linked
list. This function also length-checks the string, to ensure we don’t
overflow.

This way, we don’t have to loop the overrides list twice; once when
calculating the total length of all overrides, and once when sending
the overrides to the server.

Now, we can update the total length as we add overrides (i.e while
parsing the command line, instead of afterwards).

This means we only have to loop the list once, and that’s when sending
it. This also means there’s no longer any need to malloc an array
holding the lengths of each override.
2021-06-23 15:12:09 +02:00
Daniel Eklöf
bac3964039
changelog: add ref to #600 (-o,--override for footclient) 2021-06-23 15:12:09 +02:00
Daniel Eklöf
5b9a000b9b
completions: add -o,--override to footclient 2021-06-23 15:12:09 +02:00
Daniel Eklöf
8640a9c99a
doc: footclient.1: document -o,--override 2021-06-23 15:12:09 +02:00
Daniel Eklöf
fcc20456cd
doc: foot.1: remove trailing space, add ‘=’ between option name and its value 2021-06-23 15:12:09 +02:00