sixel: fix cursor positioning logic

This adjusts the logic that positions the text cursor after emitting a
sixel, when sixel scrolling mode is *enabled*.

We’ve always mimicked XTerm’s behavior. However, XTerm recently
changed its behavior, to better match that of an VT382.

Now, the cursor is placed *on* the last row of the sixel, instead of
on a new row after the sixel.

This allows applications to print sixels to the bottom row of the
terminal, without causing the content to scroll.

Finally, there was a bug in the horizontal positioning of the cursor;
it was placed on the *first* column of the row, instead of on the
first column of the sixel.
This commit is contained in:
Daniel Eklöf 2023-06-16 16:20:37 +02:00
parent 8a3620bafa
commit 66d9b8da60
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 26 additions and 20 deletions

View file

@ -65,6 +65,15 @@
* Opaque sixels now retain the background opacity (when current
background color is the **default** background color)
([#1360][1360]).
* Text cursors vertical position after emitting a sixel, when sixel
scrolling is **enabled** (the default) has been updated to match
XTerms, and the VT382s behavior: the cursor is positioned **on**
the last sixel row, rather than _after_ it. This allows printing
sixels on the last row without scrolling up, but also means
applications may have to explicitly emit a newline to ensure the
sixel is visible. For example, `cat`:ing a sixel in the shell will
typically result in the last row not being visible, unless a newline
is explicitly added.
[1371]: https://codeberg.org/dnkl/foot/pulls/1371
[1360]: https://codeberg.org/dnkl/foot/issues/1360

37
sixel.c
View file

@ -1043,6 +1043,23 @@ sixel_unhook(struct terminal *term)
pixel_rows_left -= height;
rows_avail -= image.rows;
if (do_scroll) {
/* Yes, truncate last row. This matches XTerms, and VT382s behavior */
const int linefeed_count = image.height / term->cell_height;
for (size_t i = 0; i < linefeed_count; i++)
term_linefeed(term);
/* Position text cursor if this is the last image chunk */
if (rows_avail == 0) {
term_cursor_to(
term,
term->grid->cursor.point.row,
(term->sixel.cursor_right_of_graphics
? min(image.pos.col + image.cols, term->cols - 1)
: image.pos.col));
}
}
/* Dirty touched cells, and scroll terminal content if necessary */
for (size_t i = 0; i < image.rows; i++) {
struct row *row = term->grid->rows[cur_row + i];
@ -1055,26 +1072,6 @@ sixel_unhook(struct terminal *term)
row->cells[col].attrs.clean = 0;
}
if (do_scroll) {
/*
* Linefeed, *unless* we're on the very last row of
* the final image (not just this chunk) and private
* mode 8452 (leave cursor at the right of graphics)
* is enabled.
*/
if (term->sixel.cursor_right_of_graphics &&
rows_avail == 0 &&
i >= image.rows - 1)
{
term_cursor_to(
term,
term->grid->cursor.point.row,
min(image.pos.col + image.cols, term->cols - 1));
} else {
term_linefeed(term);
term_carriage_return(term);
}
}
}
_sixel_overwrite_by_rectangle(