action_print() is in the hot path, and having if-statement here *does*
have an impact on performance.
Much more so when that if-statement involves a functional call to
wcwidth().
Closes#330
Take ‘\E(#0’ for example - this is *not* the same as ‘\E(0’.
Up until now, foot has however treated them as the same escape,
because the handler for ‘\E(0’ didn’t verify there weren’t any _other_
private characters present.
Fix this by turning the ‘private’ array into a single 4-byte
integer. This allows us to match *all* privates with a single
comparison.
Private characters are added to the LSB first, and MSB last. This
means we can check for single privates in pretty much the same way as
before:
switch (term->vt.private) {
case ‘?’:
...
break;
}
Checking for two (or more) is much uglier, but foot only supports
a *single* escape with two privates, and no escapes with three or
more:
switch (term->vt.private) {
case 0x243f: /* ‘?$’ */
...
break;
}
The ‘clear’ action remains simple (and fast), with a single write
operation.
Collecting privates is potentially _slightly_ more complex than
before; we now need mask and compare, instead of simply comparing,
when checking how many privates we already have.
We _could_ add a counter, which would make collecting privates easier,
but this would add an additional write to the ‘clean’ action which is
really bad since it’s in the hot path.
Add anew config option, ‘bell=none|set-urgency’. When set to
‘set-urgency’, the margins will be painted in red (if the window did
not have keyboard focus).
This is intended as a cheap replacement for the ‘urgency’ hint, that
doesn’t (yet) exist on Wayland.
Closes#157
The switch statements use the GCC extension "case X ... Y", and here
it doesn't really make any sense to convert it to "case X: case Y:",
so hide the warnings instead.
Previously, C0::VT was implemented as a simple 'cursor down'. I.e. it
would behave as LF **until** it reached the bottom of the screen,
where instead of scrolling, it became a no-op.
See https://vt100.net/docs/vt102-ug/chapter5.html
This is slightly faster, since we don't need to initialize an
mbstate_t struct (using mbrtowc() with a NULL-pointer for 'ps' also
works).
Also, avoid a branch by setting wc=0 and then ignoring the
result/error code from mbtowc().
Since the pre-composing functionality is now part of fcft, it makes
little sense to have a compile time option - there's no size benefit
to be had.
Furthermore, virtually all terminal emulators do
pre-composing (alacritty being an exception), this really isn't that
controversial.
This allows us more options when determining whether to use a
pre-composed character or not:
We now only use the pre-composed character if it's from the primary
font, or if at least one of the base or combining characters are from
a fallback font.
I.e. use glyphs from the primary font if possible. But, if one or more
of the decomposed glyphs are from a fallback font, use the
pre-composed character anyway.
We currently store up to 5 combining characters in any given
base+combining chain.
This adds a check for when that limit is about to be exceeded. When
this happens, we log the chain + the new combining character.
Since things will break anyway, we simply overwrite the last combining
character.
Instead of storing combining data per cell, realize that most
combinations are re-occurring and that there's lots of available space
left in the unicode range, and store seen base+combining combinations
chains in a per-terminal array.
When we encounter a combining character, we first try to pre-compose,
like before. If that fails, we then search for the current
base+combining combo in the list of previously seen combinations. If
not found there either, we allocate a new combo and add it to the
list. Regardless, the result is an index into this array. We store
this index, offsetted by COMB_CHARS_LO=0x40000000ul in the cell.
When rendering, we need to check if the cell character is a plain
character, or if it's a composed character (identified by checking if
the cell character is >= COMB_CHARS_LO).
Then we render the grapheme pretty much like before.
We only used utf8proc to try to pre-compose a glyph from a base and
combining character.
We can do this ourselves by using a pre-compiled table of valid
pre-compositions. This table isn't _that_ big, and binary searching it
is fast.
That is, for a very small amount of code, and not too much extra RO
data, we can get rid of the utf8proc dependency.
If the client sent the sequence SAB, where SA does NOT have a composed
representation, but SB does, the old code would compose SB and throw
away A.
This patch fixes this by only allowing a compose if there aren't
any pre-existing combining characters.
When we detect a combining character, we first try to compose it with
the base character (like before).
When this fails, we instead add the combining character to the base
cell's combining characters array.
The reason for using a composed character when possible is twofold:
one, the rendered glyph will look better since it will be a single
glyph instead of two separate glyphs (possibly from different
fonts(!)). And two, for performance. A composed glyph is a single
glyph to render, while a decomposed glyph sequence means the renderer
has to render multiple glyphs for a single cell.
action_clear() is in the super hot code path. Avoid resetting utf8
state there, as utf8 input is relatively uncommon.
Instead, reset it when we explicitly enter any of the utf8 collecting
states, as this is exactly the point where we need it.
This feature lets foot combine e.g. "a\u0301" to "á".
We first check if the current character (that we're about to print) is
a combining character, by checking if it's in one of the following
ranges:
* Combining Diacritical Marks (0300–036F), since version 1.0, with
modifications in subsequent versions down to 4.1
* Combining Diacritical Marks Extended (1AB0–1AFF), version 7.0
* Combining Diacritical Marks Supplement (1DC0–1DFF), versions 4.1 to 5.2
* Combining Diacritical Marks for Symbols (20D0–20FF), since version
1.0, with modifications in subsequent versions down to 5.1
* Combining Half Marks (FE20–FE2F), versions 1.0, with modifications
in subsequent versions down to 8.0
If it is, we check if the last cell appears to contain a valid symbol,
and if so, we attempt to compose (combine) the last cell with the
current character, using utf8proc.
If the result is a combined character, we replace the content in the
previous cell with the new, combined character.
Thus, if you select and copy the printed character, you would get
e.g. "\u00e1" instead of "a\u0301".
This feature can be disabled. By default, it is enabled if the
utf8proc library is found, but can be explicitly disabled, or enabled,
with 'meson -Dunicode-combining=disabled|enabled'.
This fixes an issue where we failed to restore the cursor correctly
when exiting from the alternate screen, if the client had sent escapes
to save the cursor position while inside the alternate screen.
This was because we used the *same* storage for saving the cursor
position through escapes, as for saving it when entering the alternate
screen.
Fix by using a custom variable dedicated to normal <--> alt screen
switching.
To handle text reflow correctly when a line has a printable character
in the last column, but was still line breaked, we need to track the
fact that the slave inserted a line break here.
Otherwise, when the window width is increased, we'll end up pulling up
the next line, when we really should have inserted a line break.
We only support 16 parameters, and for each parameter, 16
sub-parameters. If we ever hit that limit (or rather, if the client
writes 17 (sub) parameters), log this and stop incrementing the
parameter index variable.
For performance reason, we implement the following behavior:
* We never increment the parameter index past the supported
number. This ensures all code *accessing* the parameter list can do
so without verifying the validity of the index.
* The *first* time we see too many parameters, and the first time we
see too many sub parameters, log this. Then *never* log again. Even
if we see too many parameters in a completely different escape. This
is so that we don't have to keep a "have warned" boolean in the
terminal struct, but can use a simple function local static
variable.