mirror of
https://github.com/tmux/tmux.git
synced 2026-09-23 21:33:57 +00:00
image: never let a Kitty image drop to zero placements mid-redraw
Every redraw that replaces an image's placements (a pane revealed after an overlapping floating pane closes, a full window redraw, etc.) deleted all of an image's existing placements before creating their replacements. That briefly left the image with none at all - and some Kitty implementations free an image's underlying pixel data once it has no placements left, so the replacement placements just placed would then reference already-discarded data and render as nothing: an empty hole where the image used to be. Not reproducible with SIXEL, which has no separate placement/data lifecycle to race. kitty_redraw_start() now only marks placements intersecting a redraw area as stale instead of deleting them immediately; a new kitty_redraw_finish() (called via image_redraw_finish() after a redraw's replacement placements have already been created) deletes whatever is still marked. The image now always has at least one live placement throughout the transition.
This commit is contained in:
@@ -131,6 +131,7 @@ struct kitty_placement_cache {
|
||||
u_int y;
|
||||
u_int width;
|
||||
u_int height;
|
||||
int pending_delete;
|
||||
struct kitty_placement_cache *next;
|
||||
};
|
||||
|
||||
@@ -177,13 +178,49 @@ kitty_free_placements(struct kitty_image_cache *entry)
|
||||
entry->placements = NULL;
|
||||
}
|
||||
|
||||
/* Delete Kitty placements intersecting a redraw area. */
|
||||
/*
|
||||
* Mark Kitty placements intersecting a redraw area as stale, without
|
||||
* deleting them yet - see kitty_redraw_finish(). Deleting immediately here
|
||||
* would, for an image whose only placements are in this area, leave it
|
||||
* with none at all until the replacement is placed - some Kitty
|
||||
* implementations free an image's pixel data once it has no placements
|
||||
* left, which would leave the replacement referencing already-discarded
|
||||
* data and render as nothing.
|
||||
*/
|
||||
void
|
||||
kitty_redraw_start(struct tty *tty, u_int x, u_int y, u_int width,
|
||||
u_int height)
|
||||
{
|
||||
struct kitty_output *ko = tty->image_data;
|
||||
struct kitty_image_cache *entry;
|
||||
struct kitty_output *ko = tty->image_data;
|
||||
struct kitty_image_cache *entry;
|
||||
struct kitty_placement_cache *placement;
|
||||
|
||||
if (ko == NULL)
|
||||
return;
|
||||
for (entry = ko->images; entry != NULL; entry = entry->next) {
|
||||
for (placement = entry->placements; placement != NULL;
|
||||
placement = placement->next) {
|
||||
if (placement->x >= x + width ||
|
||||
placement->x + placement->width <= x ||
|
||||
placement->y >= y + height ||
|
||||
placement->y + placement->height <= y)
|
||||
continue;
|
||||
placement->pending_delete = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete placements marked stale by kitty_redraw_start() - called once any
|
||||
* replacement placements have already been created, so an image already
|
||||
* placed elsewhere in the same redraw is never left with none at all in
|
||||
* between the two.
|
||||
*/
|
||||
void
|
||||
kitty_redraw_finish(struct tty *tty)
|
||||
{
|
||||
struct kitty_output *ko = tty->image_data;
|
||||
struct kitty_image_cache *entry;
|
||||
struct kitty_placement_cache **pp, *placement;
|
||||
char s[64];
|
||||
|
||||
@@ -191,15 +228,12 @@ kitty_redraw_start(struct tty *tty, u_int x, u_int y, u_int width,
|
||||
return;
|
||||
for (entry = ko->images; entry != NULL; entry = entry->next) {
|
||||
for (pp = &entry->placements; (placement = *pp) != NULL; ) {
|
||||
if (placement->x >= x + width ||
|
||||
placement->x + placement->width <= x ||
|
||||
placement->y >= y + height ||
|
||||
placement->y + placement->height <= y) {
|
||||
if (!placement->pending_delete) {
|
||||
pp = &placement->next;
|
||||
continue;
|
||||
}
|
||||
xsnprintf(s, sizeof s,
|
||||
"\033_Ga=d,d=i,i=%u,p=%u,q=2\033\\", entry->kitty_id,
|
||||
}
|
||||
xsnprintf(s, sizeof s,
|
||||
"\033_Ga=d,d=i,i=%u,p=%u,q=2\033\\", entry->kitty_id,
|
||||
placement->id);
|
||||
tty_puts(tty, s);
|
||||
*pp = placement->next;
|
||||
|
||||
14
image.c
14
image.c
@@ -156,6 +156,20 @@ image_redraw_start(struct tty *tty, u_int x, u_int y, u_int width,
|
||||
sixel_redraw_start(tty, x, y, width, height);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete placements marked stale by image_redraw_start() - called once any
|
||||
* replacement placements a redraw is making have already been created. Only
|
||||
* meaningful for Kitty (see kitty_redraw_finish()) - SIXEL has no separate
|
||||
* placement/data distinction for sixel_redraw_start()'s plain erase to
|
||||
* leave dangling.
|
||||
*/
|
||||
void
|
||||
image_redraw_finish(struct tty *tty)
|
||||
{
|
||||
if (tty->image_backend == &image_backend_kitty)
|
||||
kitty_redraw_finish(tty);
|
||||
}
|
||||
|
||||
/* Write out any image output the backend is still holding back. */
|
||||
void
|
||||
image_draw_flush(struct tty *tty)
|
||||
|
||||
@@ -1806,6 +1806,10 @@ redraw_draw_pane_lines(struct redraw_draw_ctx *dctx, struct window_pane *wp,
|
||||
image_draw_flush(&scene->c->tty);
|
||||
#endif
|
||||
}
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (flags & REDRAW_PANE)
|
||||
image_redraw_finish(&scene->c->tty);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Draw lines. */
|
||||
@@ -2139,8 +2143,13 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
||||
|
||||
if (wp != NULL)
|
||||
redraw_draw_pane_lines(&dctx, wp, flags);
|
||||
else
|
||||
else {
|
||||
redraw_draw_lines(&dctx, flags);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (flags & REDRAW_PANE)
|
||||
image_redraw_finish(tty);
|
||||
#endif
|
||||
}
|
||||
#ifdef ENABLE_IMAGES
|
||||
if ((flags & REDRAW_PANE) &&
|
||||
(image_backend_flags(tty) &
|
||||
@@ -2541,6 +2550,10 @@ redraw_draw_damage_rect(struct redraw_draw_ctx *dctx, u_int x, u_int y,
|
||||
image_draw_flush(&scene->c->tty);
|
||||
#endif
|
||||
}
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (!skip_images)
|
||||
image_redraw_finish(&scene->c->tty);
|
||||
#endif
|
||||
|
||||
/* SIXEL image output may disturb status cells; compose them last. */
|
||||
for (yy = y; yy < y + sy; yy++) {
|
||||
|
||||
2
tmux.h
2
tmux.h
@@ -4341,6 +4341,7 @@ void image_redraw_area(struct screen_write_ctx *, u_int, u_int,
|
||||
void image_redraw_all(struct screen_write_ctx *);
|
||||
void image_redraw_scroll(struct screen_write_ctx *, u_int);
|
||||
void image_redraw_start(struct tty *, u_int, u_int, u_int, u_int);
|
||||
void image_redraw_finish(struct tty *);
|
||||
void image_draw_flush(struct tty *);
|
||||
int image_backend_flags(struct tty *);
|
||||
int image_tty_update(struct tty *);
|
||||
@@ -4398,6 +4399,7 @@ void kitty_free_state(void *);
|
||||
void kitty_draw_rect(struct tty *,
|
||||
const struct image_rect *, const struct tty_style_ctx *);
|
||||
void kitty_redraw_start(struct tty *, u_int, u_int, u_int, u_int);
|
||||
void kitty_redraw_finish(struct tty *);
|
||||
void kitty_free_output_state(struct tty *, int);
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user