Ignore *old* cells containing spacers.
Pad new grid with spacers if a multi-column character doesn't fit at
the end of a line.
Insert spacers after a multi-column character.
Our sixel handling code requires sixels to *not* cross the scrollback
wrap around.
Until we've fixes the reflow code that split up such sixels (much like
we do when we generate a sixel), simply delete it.
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.
The data is *not* added to the cell struct, since that one is too
performance critical.
Instead, the data is added as a separate array in the row struct.
This allows our performance critical code paths that e.g. clear cells
to perform as before.
Define a list of "tracking points" - coordinates that should be
translated while reflowing.
Add the cursor coordinates to this list.
When a coordinate have been translated, it is removed from the
list. This means we don't have to create a copy of the 'cursor'
coordinate struct.
Update the sixels' 'row' attribute when re-flowing a grid, to ensure
it is rendered at the correct place.
This should work in most cases, but will break when the cell size has
changed (e.g. font size increase/decrease, or a DPI change).
This patch also moves the sixel image list from the terminal struct
into the grid struct. The sixels are per-grid after all.
To do text reflow, we only need to know if a line has been explicitly
linebreaked or not. If not, that means it wrapped, and that we
should *not* insert a linebreak when reflowing text.
When reflowing text, when reaching the end of a row in the old grid,
only insert a linebreak in the new grid if the old row had been
explicitly linebreaked.
Furthermore, when reflowing text and wrapping a row in the new grid,
mark the previous row as linebreaked if either the last cell was
(the last column in the last row) empty, or the current cell (the
first column in the new row) is empty. If both are non-empty, then we
assume a linewrap.
With this assumption, we can replace 'a % b' with 'a & (b - 1)'. In
terms of instructions, this means a fast 'and' instead of a slow
'div'.
Further optimize scrolling by:
* not double-initializing empty rows. Previously, grid_row_alloc()
called calloc(), which was then followed by a memset() when
scrolling. This is of course unnecessary.
* Don't loop the entire set of visible rows (this was done to ensure
all visible rows had been allocated, and to prefetch the cell
contents).
This isn't necessary; only newly pulled in rows can be NULL. For
now, don't prefetch at all.
This patch takes a bit from the foreground color value in a
cell (todo: split up foreground/background into bitfields with a
separate field for 'foreground/background' has been set), and only
re-renders cells that aren't marked as clean.
Note: we use a 'clean' bit rather than a 'dirty' bit to make it easy
to erase cells - we can (keep doing) do that by simply memsetting a
cell range to 0.
The row array may now contain NULL pointers. This means the
corresponding row hasn't yet been allocated and initialized.
On a resize, we explicitly allocate the visible rows.
Uninitialized rows are then allocated the first time they are
referenced.
The grid is now represented with an array of row *pointers*. Each row
contains an array of cells (the row's columns).
The main point of having row pointers is we can now move rows around
almost for free.
This is useful when scrolling with scroll margins for example, where
we previously had to copy the lines in the margins. Now it's just a
matter of swapping two pointers.
This means we don't have to explicitly set the foreground/background
to the grid's default colors whenever we reset/clear a cell, and we
can instead simply memset() the entire cell to 0.
This also means the renderer has to get the default color when
rendering a cell without a foreground/background color set.
Vim, for example, changes the scroll region every time you scroll a
single line. Thus, resetting the damage queue is slow.
This reworks the damage handling of scroll updates:
* Split damage queue into two: one for scroll operations and one for
update/erase operations.
* Don't separate update/erase operations inside/outside the scroll
region
* Store the current scroll region in the scroll damage operation. This
allows us to stack multiple scroll operations with different scroll
regions.
* When updating update/erase operations after a scroll operation,
split the update/erase operations if necessary (the current scroll
operation may have a scroll region different from before, thus
forcing us to split existing update/erase operations.
* The renderer no longer erases after a scroll. The scroll operation
also adds an erase operation. This also means that erase operation
are subject to adjustments by later scroll operations.
This is largely untested, but existing scrolling code has been
converted to using a terminal-global scrolling region that is defined
as start-end of the scrollable region.
This is compared to the old code where the scrolling region where
defined in terms of marginals, counted in lines from top and from
bottom.