mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-02-07 04:06:07 -05:00
Merge branch 'sixel-performance'
This commit is contained in:
commit
08309537ce
6 changed files with 202 additions and 149 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
|
@ -63,24 +63,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.
|
||||
* Sixel handling when resizing window.
|
||||
* Sixel handling when scrollback wraps around.
|
||||
|
||||
|
||||
### Security
|
||||
|
|
|
|||
94
grid.c
94
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);
|
||||
}
|
||||
|
|
|
|||
36
render.c
36
render.c
|
|
@ -795,8 +795,42 @@ 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)
|
||||
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 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
|
||||
- scrollback_end
|
||||
+ term->grid->num_rows) & (term->grid->num_rows - 1);
|
||||
const int end = start + six->rows - 1;
|
||||
|
||||
//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
|
||||
|
|
|
|||
188
sixel.c
188
sixel.c
|
|
@ -98,102 +98,92 @@ sixel_erase(struct terminal *term, struct sixel *sixel)
|
|||
sixel_destroy(sixel);
|
||||
}
|
||||
|
||||
/* Row numbers are absolute */
|
||||
static void
|
||||
sixel_delete_at_point(struct terminal *term, int row, int col)
|
||||
static int
|
||||
rebase_row(const struct terminal *term, int abs_row)
|
||||
{
|
||||
assert(row >= 0);
|
||||
assert(row < term->grid->num_rows);
|
||||
assert(col < term->grid->num_cols);
|
||||
int scrollback_start = term->grid->offset + term->rows;
|
||||
int rebased_row = abs_row - scrollback_start + term->grid->num_rows;
|
||||
|
||||
if (likely(tll_length(term->grid->sixel_images) == 0))
|
||||
return;
|
||||
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) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
/* We should never generate scrollback wrapping sixels */
|
||||
assert(six_end >= six_start);
|
||||
static void
|
||||
sixel_insert(struct terminal *term, struct sixel sixel)
|
||||
{
|
||||
int end_row = rebase_row(term, sixel.pos.row + sixel.rows - 1);
|
||||
|
||||
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);
|
||||
}
|
||||
tll_foreach(term->grid->sixel_images, it) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: remove */
|
||||
void
|
||||
sixel_delete_at_row(struct terminal *term, int row)
|
||||
{
|
||||
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_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
|
||||
verify_sixel_list_order(term);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
sixel_scroll_up(struct terminal *term, int rows)
|
||||
{
|
||||
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
|
||||
|
|
@ -295,7 +285,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]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -304,24 +294,27 @@ 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_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);
|
||||
|
||||
if (six_scrollback_rel_end < scrollback_rel_start) {
|
||||
/* All remaining sixels are *before* our rectangle */
|
||||
break;
|
||||
}
|
||||
|
||||
/* We should never generate scrollback wrapping sixels */
|
||||
assert(six_end >= six_start);
|
||||
|
|
@ -365,20 +358,23 @@ 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 row = (term->grid->offset + _row) & (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;
|
||||
const int six_start = six->pos.row;
|
||||
|
|
@ -387,6 +383,13 @@ _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_scrollback_rel_end = rebase_row(term, six_end);
|
||||
|
||||
if (six_scrollback_rel_end < scrollback_rel_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;
|
||||
|
|
@ -404,19 +407,10 @@ _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)
|
||||
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
|
||||
|
|
@ -475,7 +469,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;
|
||||
|
|
|
|||
13
sixel.h
13
sixel.h
|
|
@ -13,15 +13,8 @@ void sixel_unhook(struct terminal *term);
|
|||
void sixel_destroy(struct sixel *sixel);
|
||||
void sixel_destroy_all(struct terminal *term);
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
|
@ -33,7 +26,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);
|
||||
|
|
|
|||
|
|
@ -1802,6 +1802,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;
|
||||
|
|
@ -1823,7 +1825,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);
|
||||
|
||||
|
|
@ -1864,6 +1865,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)
|
||||
|
|
@ -1890,7 +1893,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);
|
||||
|
||||
|
|
@ -2407,7 +2409,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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue