From 66d9b8da604e4fd3aa6ee96da3609ff6b9896f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Fri, 16 Jun 2023 16:20:37 +0200 Subject: [PATCH] sixel: fix cursor positioning logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- CHANGELOG.md | 9 +++++++++ sixel.c | 37 +++++++++++++++++-------------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 658e0c1d..a2fc9ec8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -65,6 +65,15 @@ * Opaque sixels now retain the background opacity (when current background color is the **default** background color) ([#1360][1360]). +* Text cursor’s vertical position after emitting a sixel, when sixel + scrolling is **enabled** (the default) has been updated to match + XTerm’s, and the VT382’s 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 diff --git a/sixel.c b/sixel.c index 70ec2ee5..1af214a9 100644 --- a/sixel.c +++ b/sixel.c @@ -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 XTerm’s, and VT382’s 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(