From 247e0c42d38ee79c36fc2104c9e93666a29b1b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Jun 2020 08:37:25 +0200 Subject: [PATCH 01/11] sixel: add local function sixel_insert() Replace all tll_push_*() calls with calls to this function. --- sixel.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sixel.c b/sixel.c index a4b27416..3c6d67af 100644 --- a/sixel.c +++ b/sixel.c @@ -87,6 +87,12 @@ sixel_erase(struct terminal *term, struct sixel *sixel) sixel_destroy(sixel); } +static void +sixel_insert(struct terminal *term, struct sixel sixel) +{ + tll_push_back(term->grid->sixel_images, sixel); +} + /* Row numbers are absolute */ static void sixel_delete_at_point(struct terminal *term, int row, int col) @@ -284,7 +290,7 @@ sixel_overwrite(struct terminal *term, struct sixel *six, PIXMAN_a8r8g8b8, imgs[i].width, imgs[i].height, imgs[i].data, imgs[i].width * sizeof(uint32_t)); - tll_push_front(term->grid->sixel_images, imgs[i]); + sixel_insert(term, imgs[i]); } } @@ -464,7 +470,7 @@ sixel_unhook(struct terminal *term) term_formfeed(term); render_refresh(term); - tll_push_back(term->grid->sixel_images, image); + sixel_insert(term, image); pixel_row_idx += height; pixel_rows_left -= height; From 4a0042ba1563b8d2f0669c7650e997b6c1a9d95e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Jun 2020 09:16:43 +0200 Subject: [PATCH 02/11] sixel: insert: sort sixels such that those furthest up in the scrollback is at the back --- sixel.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/sixel.c b/sixel.c index 3c6d67af..ddb0091c 100644 --- a/sixel.c +++ b/sixel.c @@ -90,6 +90,25 @@ sixel_erase(struct terminal *term, struct sixel *sixel) static void sixel_insert(struct terminal *term, struct sixel sixel) { + const int scrollback_end + = (term->grid->offset + term->rows) & (term->grid->num_rows - 1); + + int end_row = sixel.pos.row + sixel.rows - scrollback_end; + while (end_row < 0) + end_row += term->grid->num_rows; + + tll_foreach(term->grid->sixel_images, it) { + int e = it->item.pos.row + it->item.rows - scrollback_end; + e -= scrollback_end; + while (e < 0) + e += term->grid->num_rows; + + if (e < end_row) { + tll_insert_before(term->grid->sixel_images, it, sixel); + return; + } + } + tll_push_back(term->grid->sixel_images, sixel); } From 0483466f68467694e960497e5f33cf0bf2f2cb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Jun 2020 10:45:05 +0200 Subject: [PATCH 03/11] sixel: insert: calculate end rows in a single statement --- sixel.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/sixel.c b/sixel.c index ddb0091c..beab0896 100644 --- a/sixel.c +++ b/sixel.c @@ -93,23 +93,36 @@ sixel_insert(struct terminal *term, struct sixel sixel) const int scrollback_end = (term->grid->offset + term->rows) & (term->grid->num_rows - 1); - int end_row = sixel.pos.row + sixel.rows - scrollback_end; - while (end_row < 0) - end_row += term->grid->num_rows; + const int end_row + = (sixel.pos.row + sixel.rows + - scrollback_end + + term->grid->num_rows) & (term->grid->num_rows - 1); + assert(end_row >= 0 && end_row < term->grid->num_rows); tll_foreach(term->grid->sixel_images, it) { - int e = it->item.pos.row + it->item.rows - scrollback_end; - e -= scrollback_end; - while (e < 0) - e += term->grid->num_rows; + const int e + = (it->item.pos.row + it->item.rows + - scrollback_end + + term->grid->num_rows) & (term->grid->num_rows - 1); + assert(e >= 0 && e < term->grid->num_rows); if (e < end_row) { tll_insert_before(term->grid->sixel_images, it, sixel); - return; + goto out; } } tll_push_back(term->grid->sixel_images, sixel); + +out: +#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG + LOG_DBG("sixel list after insertion:"); + tll_foreach(term->grid->sixel_images, it) { + LOG_DBG(" rows=%d+%d", it->item.pos.row, it->item.rows); + } +#else + ; +#endif } /* Row numbers are absolute */ From 5158be86d2a1be657260c3283548e9fde17ddd7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Jun 2020 10:45:30 +0200 Subject: [PATCH 04/11] render: sixels: break out of loop when we're sure there aren't any more visible images The sixel list is sorted, with the most recent images *first* in the list (and thus the "oldest" images are at the back). This means we can break out of the loop when we see a sixel that *ends before* the current view starts. As a minor optimization, we also recognize sixels that *start after* the current view ends. We can't break out of the loop, but we can skip trying to render them (they wouldn't have been rendered, but more work would have been done in render_sixel() to reach this conclusion). --- render.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/render.c b/render.c index 8fd7d42e..8bb85f9d 100644 --- a/render.c +++ b/render.c @@ -795,8 +795,32 @@ render_sixel(struct terminal *term, pixman_image_t *pix, static void render_sixel_images(struct terminal *term, pixman_image_t *pix) { - tll_foreach(term->grid->sixel_images, it) + const int view_start = term->grid->view; + const int view_end = view_start + term->rows; + + LOG_DBG("SIXELS: %zu images, view=%d-%d", + tll_length(term->grid->sixel_images), view_start, view_end); + + tll_foreach(term->grid->sixel_images, it) { + const struct sixel *six = &it->item; + const int start = six->pos.row; + const int end = start + six->rows - 1; + + /* Sixels aren't allowed to cross the wrap-around */ + assert(end < term->grid->num_rows); + + //LOG_DBG(" sixel: %d-%d", start, end); + if (start > view_end) { + /* Sixel starts after view ends, no need to try to render it */ + continue; + } else if (end < view_start) { + /* Image ends before view starts. Since the image list is + * sorted, we can safely stop here */ + break; + } + render_sixel(term, pix, &it->item); + } } static void From 4006fc86e4af90bb302e0da98dbbcc8152950f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Jun 2020 11:01:19 +0200 Subject: [PATCH 05/11] sixel: overwrite: pass 'width' to sixel_overwrite_at_cursor() This is necessary to handle multi-column characters correctly. --- sixel.c | 4 ++-- sixel.h | 2 +- terminal.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sixel.c b/sixel.c index beab0896..8ecc025a 100644 --- a/sixel.c +++ b/sixel.c @@ -440,10 +440,10 @@ sixel_overwrite_by_row(struct terminal *term, int row, int col, int width) } void -sixel_overwrite_at_cursor(struct terminal *term) +sixel_overwrite_at_cursor(struct terminal *term, int width) { sixel_overwrite_by_row( - term, term->grid->cursor.point.row, term->grid->cursor.point.col, 1); + term, term->grid->cursor.point.row, term->grid->cursor.point.col, width); } void diff --git a/sixel.h b/sixel.h index bb3eb730..07e15196 100644 --- a/sixel.h +++ b/sixel.h @@ -32,7 +32,7 @@ void sixel_delete_at_row(struct terminal *term, int row); void sixel_overwrite_by_rectangle( struct terminal *term, int row, int col, int height, int width); void sixel_overwrite_by_row(struct terminal *term, int row, int col, int width); -void sixel_overwrite_at_cursor(struct terminal *term); +void sixel_overwrite_at_cursor(struct terminal *term, int width); void sixel_colors_report_current(struct terminal *term); void sixel_colors_reset(struct terminal *term); diff --git a/terminal.c b/terminal.c index 504dec57..667d91d1 100644 --- a/terminal.c +++ b/terminal.c @@ -2384,7 +2384,7 @@ term_print(struct terminal *term, wchar_t wc, int width) print_linewrap(term); print_insert(term, width); - sixel_overwrite_at_cursor(term); + sixel_overwrite_at_cursor(term, width); /* *Must* get current cell *after* linewrap+insert */ struct row *row = term->grid->cur_row; From c7b2dcc0f4ba6ec451bbfe2b51ac1096aa1505d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Jun 2020 14:19:43 +0200 Subject: [PATCH 06/11] render: sixel: regression: need to take current offset into account when early-quitting sixel rendering The sixel images are sorted, that's true. But in order for our row numer comparisons to actually work, we need to rebase all numbers against the current scrollback offset (or, the scrollback *end*, to be precise). --- render.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/render.c b/render.c index 8bb85f9d..391115d6 100644 --- a/render.c +++ b/render.c @@ -795,20 +795,30 @@ render_sixel(struct terminal *term, pixman_image_t *pix, static void render_sixel_images(struct terminal *term, pixman_image_t *pix) { - const int view_start = term->grid->view; - const int view_end = view_start + term->rows; + if (likely(tll_length(term->grid->sixel_images)) == 0) + return; - LOG_DBG("SIXELS: %zu images, view=%d-%d", - tll_length(term->grid->sixel_images), view_start, view_end); + const int scrollback_end + = (term->grid->offset + term->rows) & (term->grid->num_rows - 1); + + const int view_start + = (term->grid->view + - scrollback_end + + term->grid->num_rows) & (term->grid->num_rows - 1); + + const int view_end = view_start + term->rows - 1; + + //LOG_DBG("SIXELS: %zu images, view=%d-%d", + // tll_length(term->grid->sixel_images), view_start, view_end); tll_foreach(term->grid->sixel_images, it) { const struct sixel *six = &it->item; - const int start = six->pos.row; + const int start + = (six->pos.row + - scrollback_end + + term->grid->num_rows) & (term->grid->num_rows - 1); const int end = start + six->rows - 1; - /* Sixels aren't allowed to cross the wrap-around */ - assert(end < term->grid->num_rows); - //LOG_DBG(" sixel: %d-%d", start, end); if (start > view_end) { /* Sixel starts after view ends, no need to try to render it */ From aa1951a4d28789426aca2702f6dfce642e3b3d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Jun 2020 14:24:30 +0200 Subject: [PATCH 07/11] sixel: overwrite-by-row: optimize: break out of loop as soon as possible Since the images are sorted, we can break out of the loop as soon as we detect an image that *ends before* the row we're looking for. In order for the row comparisons to work, the row numbers must be re-based against the current scrollback offset, or the scrollback *end*, to be precise. --- sixel.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/sixel.c b/sixel.c index 8ecc025a..ba7566f0 100644 --- a/sixel.c +++ b/sixel.c @@ -392,20 +392,29 @@ sixel_overwrite_by_rectangle( _sixel_overwrite_by_rectangle(term, start, col, height, width); } -/* Row numbers are absolute */ -static void -_sixel_overwrite_by_row(struct terminal *term, int row, int col, int width) +/* Row numbers are relative to grid offset */ +void +sixel_overwrite_by_row(struct terminal *term, int _row, int col, int width) { assert(col >= 0); - assert(row >= 0); - assert(row < term->grid->num_rows); + assert(_row >= 0); + assert(_row < term->rows); assert(col >= 0); assert(col + width <= term->grid->num_cols); if (likely(tll_length(term->grid->sixel_images) == 0)) return; + const int scrollback_end + = (term->grid->offset + term->rows) & (term->grid->num_rows - 1); + + const int row = (term->grid->offset + _row) & (term->grid->num_rows - 1); + const int grid_relative_row + = (term->grid->offset + row + - scrollback_end + + term->grid->num_rows) & (term->grid->num_rows - 1); + tll_foreach(term->grid->sixel_images, it) { struct sixel *six = &it->item; const int six_start = six->pos.row; @@ -414,6 +423,16 @@ _sixel_overwrite_by_row(struct terminal *term, int row, int col, int width) /* We should never generate scrollback wrapping sixels */ assert(six_end >= six_start); + const int six_grid_relative_end + = (six->pos.row + six->rows - 1 + - scrollback_end + + term->grid->num_rows) & (term->grid->num_rows - 1); + + if (six_grid_relative_end < grid_relative_row) { + /* All remaining sixels are *before* "our" row */ + break; + } + if (row >= six_start && row <= six_end) { const int col_start = six->pos.col; const int col_end = six->pos.col + six->cols - 1; @@ -430,15 +449,6 @@ _sixel_overwrite_by_row(struct terminal *term, int row, int col, int width) } } -void -sixel_overwrite_by_row(struct terminal *term, int row, int col, int width) -{ - _sixel_overwrite_by_row( - term, - (term->grid->offset + row) & (term->grid->num_rows - 1), - col, width); -} - void sixel_overwrite_at_cursor(struct terminal *term, int width) { From 1140dd37d3d50f9ef003957f877464005d1ede11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Sun, 28 Jun 2020 19:22:23 +0200 Subject: [PATCH 08/11] sixel: overwrite-by-rectangle: optimize: break out of loop as soon as possible Since the images are sorted, we can break out of the loop as soon as we detect an image that *ends before* the rectangle's top starts. In order for the row comparisons to work, the row numbers must be re-based against the current scrollback offset, or the scrollback *end*, to be precise. --- sixel.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sixel.c b/sixel.c index ba7566f0..429a7c66 100644 --- a/sixel.c +++ b/sixel.c @@ -344,12 +344,30 @@ _sixel_overwrite_by_rectangle( const int start = row; const int end = row + height - 1; + const int scrollback_end + = (term->grid->offset + term->rows) & (term->grid->num_rows - 1); + + const int grid_relative_start + = (start + - scrollback_end + + term->grid->num_rows) & (term->grid->num_rows - 1); + tll_foreach(term->grid->sixel_images, it) { struct sixel *six = &it->item; const int six_start = six->pos.row; const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); + const int six_grid_relative_end = + (six_end + - scrollback_end + + + term->grid->num_rows) & (term->grid->num_rows - 1); + + if (six_grid_relative_end < grid_relative_start) { + /* All remaining sixels are *before* our rectangle */ + break; + } + /* We should never generate scrollback wrapping sixels */ assert(six_end >= six_start); From 6ee76c21f2ee03bcff6b083d783df9da49f5eced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 29 Jun 2020 21:57:01 +0200 Subject: [PATCH 09/11] changelog: group sixel related changes toghether --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8691a3f..761beb2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,24 +60,24 @@ `0x0` terminal size (https://codeberg.org/dnkl/foot/issues/20). * Glyphs overflowing into surrounding cells (https://codeberg.org/dnkl/foot/issues/21). -* Sixel images being erased when printing text next to them. * Crash when last rendered cursor cell had scrolled off screen and `\E[J3` was executed. * Assert (debug builds) when an `\e]4` OSC escape was not followed by a `;`. +* Window title always being set to "foot" on reset. +* Terminfo entry `kb2` (center keypad key); it is now set to `\EOu` + (which is what foot emits) instead of the incorrect value `\EOE`. * Palette re-use in sixel images. Previously, the palette was reset after each image. * Do not auto-resize a sixel image for which the cllent has specified a size. This fixes an issue where an image would incorrectly overflow into the cell row beneath. -* Window title always being set to "foot" on reset. * Erase scrolled out sixel image that crossed the scrollback wrap around boundary. * Text printed, or other sixel images drawn, on top of a sixel image no longer erases the entire image, only the part(s) covered by the new text or image. -* Terminfo entry `kb2` (center keypad key); it is now set to `\EOu` - (which is what foot emits) instead of the incorrect value `\EOE`. +* Sixel images being erased when printing text next to them. ### Security From a136987678afcaa7b8ad22456a0632ee8a743943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 29 Jun 2020 21:59:40 +0200 Subject: [PATCH 10/11] reflow: ensure sixels are correctly sorted when re-inserted And make sure to remove re-inserted sixels that has wrapped around the scrollback (this may happen when the window size decreases). --- CHANGELOG.md | 1 + grid.c | 94 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 63 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 761beb2f..5da11320 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -78,6 +78,7 @@ no longer erases the entire image, only the part(s) covered by the new text or image. * Sixel images being erased when printing text next to them. +* Sixel handling when resizing window. ### Security diff --git a/grid.c b/grid.c index d1d625c6..cdc5db72 100644 --- a/grid.c +++ b/grid.c @@ -76,7 +76,10 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, * at the output that is *oldest* */ int offset = grid->offset + old_screen_rows; - tll(struct sixel) new_sixels = tll_init(); + tll(struct sixel) old_sixels = tll_init(); + tll_foreach(grid->sixel_images, it) + tll_push_back(old_sixels, it->item); + tll_free(grid->sixel_images); /* Turn cursor coordinates into grid absolute coordinates */ struct coord cursor = grid->cursor.point; @@ -106,31 +109,57 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, if (old_row == NULL) continue; - /* - * Update 'row' in all sixels that *begin* at current row - * - * Since we might end up pushing the sixel down, we can't - * simply update the row inline - we'd then end up pushing the - * sixel down again, when we reach the next 'old' - * row. Instead, copy the sixel (with 'row' updated), to a - * temporary list and remove the original sixel. - * - * After we've reflowed the grid we'll move the sixels back to - * the "real" sixel list. - */ - tll_foreach(grid->sixel_images, it) { - if (it->item.pos.row == old_row_idx) { - struct sixel six = it->item; - six.pos.row = new_row_idx; + /* Map sixels on current "old" row to current "new row" */ + tll_foreach(old_sixels, it) { + if (it->item.pos.row != old_row_idx) + continue; + + struct sixel sixel = it->item; + sixel.pos.row = new_row_idx; + + /* Make sure it doesn't cross the wrap-around after being re-based */ + int end = (sixel.pos.row + sixel.rows - 1) & (new_rows - 1); + if (end < sixel.pos.row) { + /* TODO: split instead of destroying */ + sixel_destroy(&it->item); + } else { + + /* Insert sixel into the *sorted* list. */ + + /* Based on rebase_row() in sixel.c */ + /* Uses 'old' offset to ensure old sixels are treated as such */ +#define rebase_row(t, row) \ + (((row) - (grid->offset + new_screen_rows) + new_rows) & (new_rows - 1)) + + int end_row = rebase_row(term, sixel.pos.row + sixel.rows - 1); + + /* + * TODO: this is basically sixel_insert(), except we + * cannot use it since: + * + * a) we don't have a 'term' reference + * b) the grid hasn't been fully * updated yet + * (e.g. grid->num_rows is invalid etc). + */ + + bool inserted = false; + tll_foreach(grid->sixel_images, it2) { + const struct sixel *s = &it2->item; + if (rebase_row(term, s->pos.row + s->rows - 1) < end_row) { + tll_insert_before(grid->sixel_images, it2, sixel); + inserted = true; + break; + } + } + + if (!inserted) + tll_push_back(grid->sixel_images, sixel); - int end = (six.pos.row + six.rows - 1) & (new_rows - 1); - if (end < six.pos.row) { - /* TODO: split sixel instead of removing it... */ - sixel_destroy(&it->item); - } else - tll_push_back(new_sixels, six); - tll_remove(grid->sixel_images, it); } + + /* Sixel has been either re-mapped, or destroyed */ + tll_remove(old_sixels, it); +#undef rebase_row } #define line_wrap() \ @@ -145,6 +174,12 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, } else { \ memset(new_row->cells, 0, new_cols * sizeof(new_row->cells[0])); \ new_row->linebreak = false; \ + tll_foreach(grid->sixel_images, it) { \ + if (it->item.pos.row == new_row_idx) { \ + sixel_destroy(&it->item); \ + tll_remove(grid->sixel_images, it); \ + } \ + } \ } \ } while(0) @@ -279,15 +314,10 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols, grid->cursor.lcf = false; grid->saved_cursor.lcf = false; - /* Destroy any non-moved sixels */ - tll_foreach(grid->sixel_images, it) + /* Free sixels we failed to "map" to the new grid */ + tll_foreach(old_sixels, it) sixel_destroy(&it->item); - tll_free(grid->sixel_images); - - /* Move updated sixels back */ - tll_foreach(new_sixels, it) - tll_push_back(grid->sixel_images, it->item); - tll_free(new_sixels); + tll_free(old_sixels); tll_free(tracking_points); } From 62be729c45ade3759ab0084808bbea491ca5c587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Ekl=C3=B6f?= Date: Mon, 29 Jun 2020 22:01:02 +0200 Subject: [PATCH 11/11] scroll: destroy scrolled out sixels before scroll is applied The logic that breaks out of sixel loops does not work for rows that has already wrapped around. Thus, we need to destroy sixels that are about to be scrolled out *before* we actually scroll. Since this is the *only* time we destroy sixels (instead of overwriting it), rename the sixel functions. And, since they now do a very specific thing, they can be greatly simplified (and thus faster). --- CHANGELOG.md | 3 +- sixel.c | 186 ++++++++++++++++----------------------------------- sixel.h | 11 +-- terminal.c | 6 +- 4 files changed, 64 insertions(+), 142 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5da11320..8a26a22a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -72,13 +72,12 @@ * Do not auto-resize a sixel image for which the cllent has specified a size. This fixes an issue where an image would incorrectly overflow into the cell row beneath. -* Erase scrolled out sixel image that crossed the scrollback wrap - around boundary. * Text printed, or other sixel images drawn, on top of a sixel image no longer erases the entire image, only the part(s) covered by the new text or image. * Sixel images being erased when printing text next to them. * Sixel handling when resizing window. +* Sixel handling when scrollback wraps around. ### Security diff --git a/sixel.c b/sixel.c index 429a7c66..5973e097 100644 --- a/sixel.c +++ b/sixel.c @@ -87,26 +87,40 @@ sixel_erase(struct terminal *term, struct sixel *sixel) sixel_destroy(sixel); } +static int +rebase_row(const struct terminal *term, int abs_row) +{ + int scrollback_start = term->grid->offset + term->rows; + int rebased_row = abs_row - scrollback_start + term->grid->num_rows; + + rebased_row &= term->grid->num_rows - 1; + return rebased_row; +} + +static bool +verify_sixel_list_order(const struct terminal *term) +{ +#if defined(_DEBUG) + int prev_row = INT_MAX; + + tll_foreach(term->grid->sixel_images, it) { + int row = rebase_row(term, it->item.pos.row + it->item.rows - 1); + assert(row < prev_row); + if (row >= prev_row) + return false; + prev_row = row; + } +#endif + return true; +} + static void sixel_insert(struct terminal *term, struct sixel sixel) { - const int scrollback_end - = (term->grid->offset + term->rows) & (term->grid->num_rows - 1); - - const int end_row - = (sixel.pos.row + sixel.rows - - scrollback_end - + term->grid->num_rows) & (term->grid->num_rows - 1); - assert(end_row >= 0 && end_row < term->grid->num_rows); + int end_row = rebase_row(term, sixel.pos.row + sixel.rows - 1); tll_foreach(term->grid->sixel_images, it) { - const int e - = (it->item.pos.row + it->item.rows - - scrollback_end - + term->grid->num_rows) & (term->grid->num_rows - 1); - assert(e >= 0 && e < term->grid->num_rows); - - if (e < end_row) { + if (rebase_row(term, it->item.pos.row + it->item.rows - 1) < end_row) { tll_insert_before(term->grid->sixel_images, it, sixel); goto out; } @@ -121,106 +135,44 @@ out: LOG_DBG(" rows=%d+%d", it->item.pos.row, it->item.rows); } #else - ; + verify_sixel_list_order(term); #endif } -/* Row numbers are absolute */ -static void -sixel_delete_at_point(struct terminal *term, int row, int col) -{ - assert(row >= 0); - assert(row < term->grid->num_rows); - assert(col < term->grid->num_cols); - - if (likely(tll_length(term->grid->sixel_images) == 0)) - return; - - tll_foreach(term->grid->sixel_images, it) { - struct sixel *six = &it->item; - const int six_start = six->pos.row; - const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); - - /* We should never generate scrollback wrapping sixels */ - assert(six_end >= six_start); - - if (row >= six_start && row <= six_end) { - const int col_start = six->pos.col; - const int col_end = six->pos.col + six->cols; - - if (col < 0 || (col >= col_start && col < col_end)) { - sixel_erase(term, six); - tll_remove(term->grid->sixel_images, it); - } - } - } -} - -/* TODO: remove */ void -sixel_delete_at_row(struct terminal *term, int row) +sixel_scroll_up(struct terminal *term, int rows) { - if (likely(tll_length(term->grid->sixel_images) == 0)) - return; - - sixel_delete_at_point( - term, (term->grid->offset + row) & (term->grid->num_rows - 1), -1); -} - -/* Row numbers are absolute */ -static void -_sixel_delete_in_range(struct terminal *term, int start, int end) -{ - assert(end >= start); - assert(start >= 0); - assert(start < term->grid->num_rows); - assert(end >= 0); - assert(end < term->grid->num_rows); - - tll_foreach(term->grid->sixel_images, it) { + tll_rforeach(term->grid->sixel_images, it) { struct sixel *six = &it->item; - const int six_start = six->pos.row; - const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); - - /* We should never generate scrollback wrapping sixels */ - assert(six_end >= six_start); - - if ((start <= six_start && end >= six_start) || /* Crosses sixel start boundary */ - (start <= six_end && end >= six_end) || /* Crosses sixel end boundary */ - (start >= six_start && end <= six_end)) /* Fully within sixel range */ - { - sixel_erase(term, six); + int six_start = rebase_row(term, six->pos.row); + if (six_start < rows) { + sixel_destroy(six); tll_remove(term->grid->sixel_images, it); - } + } else + break; } + + verify_sixel_list_order(term); } void -sixel_delete_in_range(struct terminal *term, int _start, int _end) +sixel_scroll_down(struct terminal *term, int rows) { - if (likely(tll_length(term->grid->sixel_images) == 0)) - return; + assert(term->grid->num_rows >= rows); - if (_start == _end) { - /* Avoid expensive wrap calculation */ - return sixel_delete_at_point( - term, (term->grid->offset + _start) & (term->grid->num_rows - 1), -1); + tll_foreach(term->grid->sixel_images, it) { + struct sixel *six = &it->item; + + int six_end = rebase_row(term, six->pos.row + six->rows - 1); + if (six_end >= term->grid->num_rows - rows) { + sixel_destroy(six); + tll_remove(term->grid->sixel_images, it); + } else + break; } - assert(_end >= _start); - const int lines = _end - _start + 1; - const int start = (term->grid->offset + _start) & (term->grid->num_rows - 1); - const int end = (start + lines - 1) & (term->grid->num_rows - 1); - const bool wraps = end < start; - - if (wraps) { - int rows_to_wrap_around = term->grid->num_rows - start; - assert(lines - rows_to_wrap_around > 0); - _sixel_delete_in_range(term, start, term->grid->num_rows); - _sixel_delete_in_range(term, 0, lines - rows_to_wrap_around); - } else - _sixel_delete_in_range(term, start, end); + verify_sixel_list_order(term); } static void @@ -331,39 +283,24 @@ static void _sixel_overwrite_by_rectangle( struct terminal *term, int row, int col, int height, int width) { - assert(row + height <= term->grid->num_rows); - assert(row >= 0); assert(row + height <= term->grid->num_rows); assert(col >= 0); assert(col + width <= term->grid->num_cols); - /* We don't handle rectangle wrapping around */ - assert(row + height <= term->grid->num_rows); - const int start = row; const int end = row + height - 1; - const int scrollback_end - = (term->grid->offset + term->rows) & (term->grid->num_rows - 1); - - const int grid_relative_start - = (start - - scrollback_end - + term->grid->num_rows) & (term->grid->num_rows - 1); + const int scrollback_rel_start = rebase_row(term, start); tll_foreach(term->grid->sixel_images, it) { struct sixel *six = &it->item; const int six_start = six->pos.row; const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1); + const int six_scrollback_rel_end = rebase_row(term, six_end); - const int six_grid_relative_end = - (six_end - - scrollback_end - + + term->grid->num_rows) & (term->grid->num_rows - 1); - - if (six_grid_relative_end < grid_relative_start) { + if (six_scrollback_rel_end < scrollback_rel_start) { /* All remaining sixels are *before* our rectangle */ break; } @@ -424,14 +361,8 @@ sixel_overwrite_by_row(struct terminal *term, int _row, int col, int width) if (likely(tll_length(term->grid->sixel_images) == 0)) return; - const int scrollback_end - = (term->grid->offset + term->rows) & (term->grid->num_rows - 1); - const int row = (term->grid->offset + _row) & (term->grid->num_rows - 1); - const int grid_relative_row - = (term->grid->offset + row - - scrollback_end - + term->grid->num_rows) & (term->grid->num_rows - 1); + const int scrollback_rel_row = rebase_row(term, row); tll_foreach(term->grid->sixel_images, it) { struct sixel *six = &it->item; @@ -441,12 +372,9 @@ sixel_overwrite_by_row(struct terminal *term, int _row, int col, int width) /* We should never generate scrollback wrapping sixels */ assert(six_end >= six_start); - const int six_grid_relative_end - = (six->pos.row + six->rows - 1 - - scrollback_end - + term->grid->num_rows) & (term->grid->num_rows - 1); + const int six_scrollback_rel_end = rebase_row(term, six_end); - if (six_grid_relative_end < grid_relative_row) { + if (six_scrollback_rel_end < scrollback_rel_row) { /* All remaining sixels are *before* "our" row */ break; } diff --git a/sixel.h b/sixel.h index 07e15196..3154e27d 100644 --- a/sixel.h +++ b/sixel.h @@ -12,15 +12,8 @@ void sixel_unhook(struct terminal *term); void sixel_destroy(struct sixel *sixel); -/* - * Deletes all sixels that are touched by the specified row(s). Used - * when scrolling, to competely remove sixels that has either - * completely, or partly scrolled out of history. - * - * Row numbers are relative to the current grid offset. - */ -void sixel_delete_in_range(struct terminal *term, int row_start, int row_end); -void sixel_delete_at_row(struct terminal *term, int row); +void sixel_scroll_up(struct terminal *term, int rows); +void sixel_scroll_down(struct terminal *term, int rows); /* * Remove sixel data from the specified location. Used when printing diff --git a/terminal.c b/terminal.c index 667d91d1..2497dde5 100644 --- a/terminal.c +++ b/terminal.c @@ -1779,6 +1779,8 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows } } + sixel_scroll_up(term, rows); + bool view_follows = term->grid->view == term->grid->offset; term->grid->offset += rows; term->grid->offset &= term->grid->num_rows - 1; @@ -1800,7 +1802,6 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows for (int r = region.end - rows; r < region.end; r++) erase_line(term, grid_row_and_alloc(term->grid, r)); - sixel_delete_in_range(term, region.end - rows, region.end - 1); term_damage_scroll(term, DAMAGE_SCROLL, region, rows); term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row); @@ -1841,6 +1842,8 @@ term_scroll_reverse_partial(struct terminal *term, } } + sixel_scroll_down(term, rows); + bool view_follows = term->grid->view == term->grid->offset; term->grid->offset -= rows; while (term->grid->offset < 0) @@ -1867,7 +1870,6 @@ term_scroll_reverse_partial(struct terminal *term, for (int r = region.start; r < region.start + rows; r++) erase_line(term, grid_row_and_alloc(term->grid, r)); - sixel_delete_in_range(term, region.start, region.start + rows - 1); term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows); term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);