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.
The algorithm is as follows:
Start at the beginning of the scrollback. That is, at the oldest
emitted lines. This is done by taking the current offset, and adding
the number of (old) screen rows, and then iterating until we find the
first allocated line.
Next, we iterate the entire old grid. At the beginning, we allocate a
line for the new grid, and setup a global pointer for that line, and
the current cell index.
For each line in the old grid, iterate its cells. Copy the the cells
over to the new line. Whenever the new line reaches its maximum number
of columns, we line break it by increasing the current row index and
allocating a new row (if necessary - we may be overwriting old
scrollback if the new grid is smaller than the old grid).
Whenever we reach the end of a line of the old grid, we insert a line
break in the new grid's line too **if** the last cell in the old line
was empty. If it was **not** empty, we **don't** line break the new
line.
Furthermore, empty cells in general need special consideration. A line
ending with a string of empty cells doesn't have to be copied the new
line. And more importantly, should **not** increase the new line's
cell index (which may cause line breaks, which is incorrect).
However, if a string of empty cells is followed by non empty cells, we
need to copy all the preceding empty cells to the line too.
When the entire scrollback history has been reflowed, we need to
figure out the new grid's offset.
This is done by trying to put the **last** emitted line at the bottom
of the screen. I.e. the new offset is typically "last_line_idx -
term->rows". However, we need to handle empty lines. So, after
subtracting the number of screen rows, we _increase_ the offset until
we see a non-empty line. This ensures we handle grid's that doesn't
fill an entire screen.
Finally, we need to re-position the cursor. This is done by trying to
place the cursor **at** (_not_ after) the last emitted line. We keep
the current cursor column as is (but possibly truncated, if the grid's
width decreased).
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.
Assume we don't handle 'CSI 4 X'. Furthermore, assume we receive the
following: 'CSI 1;2;3;4X'. In this case, only log '4X' as un-handled,
not the entire CSI string.
This implements the following queries:
* report window size in pixels
* report cell size in pixels
* report window size in chars
We also log a warning for the remaining window operation queries.
0x3a/0x3b are ':' and ';'. These should not only switch to the 'csi
param' state, but also be parsed as a parameter.
This fixes an issue where a multi-parameter escape with the first
parameter omitted was parsed incorrectly - as if the first parameter
wasn't there.
I.e. "\e[;123r" was parsed as "\e[123r"
mbrtowc() returns an unsigned. Need to cast to signed before checking
if less than zero.
This fixes an issue where invalid utf-8 sequences where treated as valid.
When term_print() was implemented, it introduced a regression where
printing a character when the last cursor was in the last column on a
line would print the character in the wrong column.
This is because term_print() retrieved a pointer to the current
cell *before* line wrapping (and possibly inserting empty cells).