sixel: improve handling of images when reflowing the grids

Update the sixels' 'row' attribute when re-flowing a grid, to ensure
it is rendered at the correct place.

This should work in most cases, but will break when the cell size has
changed (e.g. font size increase/decrease, or a DPI change).

This patch also moves the sixel image list from the terminal struct
into the grid struct. The sixels are per-grid after all.
This commit is contained in:
Daniel Eklöf 2020-03-13 18:44:23 +01:00
parent 62a5805d4b
commit d482bf0a30
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
7 changed files with 69 additions and 42 deletions

View file

@ -17,6 +17,7 @@
### Fixed
* Window size doubling when moving window between outputs with
different scaling factors (https://codeberg.org/dnkl/foot/issues/3).
* Sixel images moved or deleted on window resize.
### Security

8
csi.c
View file

@ -1143,11 +1143,9 @@ csi_dispatch(struct terminal *term, uint8_t final)
tll_free(term->alt.scroll_damage);
/* Delete all sixel images on the alt screen */
tll_foreach(term->sixel_images, it) {
if (it->item.grid == &term->alt) {
sixel_destroy(&it->item);
tll_remove(term->sixel_images, it);
}
tll_foreach(term->alt.sixel_images, it) {
sixel_destroy(&it->item);
tll_remove(term->alt.sixel_images, it);
}
term_damage_all(term);

35
grid.c
View file

@ -6,6 +6,7 @@
#define LOG_MODULE "grid"
#define LOG_ENABLE_DBG 0
#include "log.h"
#include "sixel.h"
#define max(x, y) ((x) > (y) ? (x) : (y))
@ -76,6 +77,8 @@ 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();
/*
* Walk the old grid
*/
@ -86,6 +89,28 @@ 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 == ((offset + r) & (old_rows - 1))) {
struct sixel six = it->item;
six.pos.row = new_row_idx;
tll_push_back(new_sixels, six);
tll_remove(grid->sixel_images, it);
}
}
/*
* Keep track of empty cells. If the old line ends with a
* string of empty cells, we don't need to, nor do we want to,
@ -191,5 +216,15 @@ grid_reflow(struct grid *grid, int new_rows, int new_cols,
grid->num_rows = new_rows;
grid->num_cols = new_cols;
/* Destroy any non-moved sixels */
tll_foreach(grid->sixel_images, 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);
return new_row_idx;
}

View file

@ -531,9 +531,6 @@ static void
render_sixel(struct terminal *term, pixman_image_t *pix,
const struct sixel *sixel)
{
if (sixel->grid != term->grid)
return;
int view_end = (term->grid->view + term->rows - 1) & (term->grid->num_rows - 1);
int first_visible_row = -1;
@ -599,7 +596,7 @@ render_sixel(struct terminal *term, pixman_image_t *pix,
static void
render_sixel_images(struct terminal *term, pixman_image_t *pix)
{
tll_foreach(term->sixel_images, it)
tll_foreach(term->grid->sixel_images, it)
render_sixel(term, pix, &it->item);
}

21
sixel.c
View file

@ -76,22 +76,19 @@ sixel_erase(struct terminal *term, struct sixel *sixel)
void
sixel_delete_at_row(struct terminal *term, int _row)
{
if (likely(tll_length(term->sixel_images) == 0))
if (likely(tll_length(term->grid->sixel_images) == 0))
return;
tll_foreach(term->sixel_images, it) {
tll_foreach(term->grid->sixel_images, it) {
struct sixel *six = &it->item;
if (six->grid != term->grid)
continue;
const int row = (term->grid->offset + _row) & (term->grid->num_rows - 1);
const int six_start = six->pos.row;
const int six_end = six_start + six->rows - 1;
if (row >= six_start && row <= six_end) {
sixel_erase(term, six);
tll_remove(term->sixel_images, it);
tll_remove(term->grid->sixel_images, it);
}
}
}
@ -101,18 +98,15 @@ sixel_delete_in_range(struct terminal *term, int _start, int _end)
{
assert(_end >= _start);
if (likely(tll_length(term->sixel_images) == 0))
if (likely(tll_length(term->grid->sixel_images) == 0))
return;
if (_start == _end)
return sixel_delete_at_row(term, _start);
tll_foreach(term->sixel_images, it) {
tll_foreach(term->grid->sixel_images, it) {
struct sixel *six = &it->item;
if (six->grid != term->grid)
continue;
const int start = (term->grid->offset + _start) & (term->grid->num_rows - 1);
const int end = start + (_end - _start);
const int six_start = six->pos.row;
@ -123,7 +117,7 @@ sixel_delete_in_range(struct terminal *term, int _start, int _end)
(start >= six_start && end <= six_end)) /* Fully within sixel range */
{
sixel_erase(term, six);
tll_remove(term->sixel_images, it);
tll_remove(term->grid->sixel_images, it);
}
}
}
@ -147,7 +141,6 @@ sixel_unhook(struct terminal *term)
.width = term->sixel.image.width,
.height = term->sixel.image.height,
.rows = (term->sixel.image.height + term->cell_height - 1) / term->cell_height,
.grid = term->grid,
.pos = (struct coord){
term->cursor.point.col,
(term->grid->offset + term->cursor.point.row) & (term->grid->num_rows - 1)},
@ -172,7 +165,7 @@ sixel_unhook(struct terminal *term)
term_formfeed(term);
render_refresh(term);
tll_push_back(term->sixel_images, image);
tll_push_back(term->grid->sixel_images, image);
}
static unsigned

View file

@ -721,8 +721,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
.start = {-1, -1},
.end = {-1, -1},
},
.normal = {.damage = tll_init(), .scroll_damage = tll_init()},
.alt = {.damage = tll_init(), .scroll_damage = tll_init()},
.normal = {.damage = tll_init(), .scroll_damage = tll_init(), .sixel_images = tll_init()},
.alt = {.damage = tll_init(), .scroll_damage = tll_init(), .sixel_images = tll_init()},
.grid = &term->normal,
.meta = {
.esc_prefix = true,
@ -747,7 +747,6 @@ term_init(const struct config *conf, struct fdm *fdm, struct wayland *wayl,
.sixel = {
.palette_size = SIXEL_MAX_COLORS,
},
.sixel_images = tll_init(),
.hold_at_exit = conf->hold_at_exit,
.shutdown_cb = shutdown_cb,
.shutdown_data = shutdown_data,
@ -978,9 +977,12 @@ term_destroy(struct terminal *term)
tll_free(term->ptmx_buffer);
tll_free(term->tab_stops);
tll_foreach(term->sixel_images, it)
tll_foreach(term->normal.sixel_images, it)
sixel_destroy(&it->item);
tll_free(term->sixel_images);
tll_free(term->normal.sixel_images);
tll_foreach(term->alt.sixel_images, it)
sixel_destroy(&it->item);
tll_free(term->alt.sixel_images);
free(term->foot_exe);
free(term->cwd);
@ -1104,9 +1106,12 @@ term_reset(struct terminal *term, bool hard)
term->meta.esc_prefix = true;
term->meta.eight_bit = true;
tll_foreach(term->sixel_images, it)
tll_foreach(term->normal.sixel_images, it)
sixel_destroy(&it->item);
tll_free(term->sixel_images);
tll_free(term->normal.sixel_images);
tll_foreach(term->alt.sixel_images, it)
sixel_destroy(&it->item);
tll_free(term->alt.sixel_images);
if (!hard)
return;

View file

@ -86,6 +86,15 @@ struct row {
bool linebreak;
};
struct sixel {
void *data;
pixman_image_t *pix;
int width;
int height;
int rows;
struct coord pos;
};
struct grid {
int num_rows;
int num_cols;
@ -97,6 +106,7 @@ struct grid {
tll(struct damage) damage;
tll(struct damage) scroll_damage;
tll(struct sixel) sixel_images;
};
struct vt_subparams {
@ -175,16 +185,6 @@ struct ptmx_buffer {
size_t idx;
};
struct sixel {
void *data;
pixman_image_t *pix;
int width;
int height;
int rows;
const struct grid *grid;
struct coord pos;
};
enum term_surface {
TERM_SURF_NONE,
TERM_SURF_GRID,
@ -417,8 +417,6 @@ struct terminal {
unsigned max_height; /* Maximum image height, in pixels */
} sixel;
tll(struct sixel) sixel_images;
bool hold_at_exit;
bool is_shutting_down;
void (*shutdown_cb)(void *data, int exit_code);