From 03efb5e293808ebae346cfc2bb99555592b5ac25 Mon Sep 17 00:00:00 2001 From: Michael Grant Date: Fri, 7 Aug 2026 22:06:09 +0100 Subject: [PATCH] Preserve text behind transparent Kitty images. --- image-kitty.c | 206 +++++++++++++++++++++--------------------------- image.c | 9 +++ screen-redraw.c | 28 +++++++ tmux.h | 2 + 4 files changed, 130 insertions(+), 115 deletions(-) diff --git a/image-kitty.c b/image-kitty.c index 418d308b1..489af71f1 100644 --- a/image-kitty.c +++ b/image-kitty.c @@ -116,9 +116,20 @@ struct kitty_image_cache { u_int kitty_id; u_int xpixel; u_int ypixel; + struct kitty_placement_cache *placements; + u_int next_placement; struct kitty_image_cache *next; }; +struct kitty_placement_cache { + u_int id; + u_int x; + u_int y; + u_int width; + u_int height; + struct kitty_placement_cache *next; +}; + struct kitty_output { struct kitty_image_cache *images; u_int next_id; @@ -137,31 +148,6 @@ kitty_get_output(struct tty *tty) return (ko); } -static size_t -kitty_utf8(char *out, uint32_t value) -{ - if (value < 0x80) { - out[0] = value; - return (1); - } - if (value < 0x800) { - out[0] = 0xc0 | (value >> 6); - out[1] = 0x80 | (value & 0x3f); - return (2); - } - if (value < 0x10000) { - out[0] = 0xe0 | (value >> 12); - out[1] = 0x80 | ((value >> 6) & 0x3f); - out[2] = 0x80 | (value & 0x3f); - return (3); - } - out[0] = 0xf0 | (value >> 18); - out[1] = 0x80 | ((value >> 12) & 0x3f); - out[2] = 0x80 | ((value >> 6) & 0x3f); - out[3] = 0x80 | (value & 0x3f); - return (4); -} - static void kitty_delete(struct tty *tty, u_int id) { @@ -171,6 +157,49 @@ kitty_delete(struct tty *tty, u_int id) tty_puts(tty, s); } +static void +kitty_free_placements(struct kitty_image_cache *cache) +{ + struct kitty_placement_cache *placement, *next; + + for (placement = cache->placements; placement != NULL; + placement = next) { + next = placement->next; + free(placement); + } + cache->placements = NULL; +} + +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 *cache; + struct kitty_placement_cache **pp, *placement; + char s[64]; + + if (ko == NULL) + return; + for (cache = ko->images; cache != NULL; cache = cache->next) { + for (pp = &cache->placements; (placement = *pp) != NULL; ) { + if (placement->x >= x + width || + placement->x + placement->width <= x || + placement->y >= y + height || + placement->y + placement->height <= y) { + pp = &placement->next; + continue; + } + xsnprintf(s, sizeof s, + "\033_Ga=d,d=i,i=%u,p=%u,q=2\033\\", cache->kitty_id, + placement->id); + tty_puts(tty, s); + *pp = placement->next; + free(placement); + } + } +} + void kitty_free_output(struct tty *tty, int send) { @@ -183,6 +212,7 @@ kitty_free_output(struct tty *tty, int send) next = cache->next; if (send) kitty_delete(tty, cache->kitty_id); + kitty_free_placements(cache); free(cache); } free(ko); @@ -210,41 +240,45 @@ kitty_images_collect(struct tty *tty) } kitty_delete(tty, cache->kitty_id); *pp = cache->next; + kitty_free_placements(cache); free(cache); } } static void -kitty_place(struct tty *tty, struct image *im, u_int id) +kitty_place(struct tty *tty, struct kitty_image_cache *cache, + struct image *im, u_int source_x, u_int source_y, u_int width, + u_int height, u_int destination_x, u_int destination_y) { char control[192]; - u_int x, y, columns, rows, px, py, pwidth, pheight; - u_int placement = 1, sx, sy, canvas_width, canvas_height; + u_int px, py, pwidth, pheight, sx, sy; + u_int canvas_width, canvas_height; + struct kitty_placement_cache *placement; image_get_cell_dimensions(im, &sx, &sy); - for (y = 0; y < sy; y += nitems(kitty_diacritics)) { - rows = sy - y; - if (rows > nitems(kitty_diacritics)) - rows = nitems(kitty_diacritics); - for (x = 0; x < sx; x += nitems(kitty_diacritics)) { - columns = sx - x; - if (columns > nitems(kitty_diacritics)) - columns = nitems(kitty_diacritics); - image_get_canvas_dimensions(im, &canvas_width, - &canvas_height); - px = (uint64_t)x * canvas_width / sx; - py = (uint64_t)y * canvas_height / sy; - pwidth = ((uint64_t)(x + columns) * canvas_width + sx - 1) / - sx - px; - pheight = ((uint64_t)(y + rows) * canvas_height + sy - 1) / - sy - py; - xsnprintf(control, sizeof control, - "\033_Ga=p,U=1,i=%u,p=%u,x=%u,y=%u,w=%u,h=%u," - "c=%u,r=%u,q=2\033\\", id, placement++, px, py, - pwidth, pheight, columns, rows); - tty_puts(tty, control); - } - } + image_get_canvas_dimensions(im, &canvas_width, &canvas_height); + px = (uint64_t)source_x * canvas_width / sx; + py = (uint64_t)source_y * canvas_height / sy; + pwidth = ((uint64_t)(source_x + width) * canvas_width + sx - 1) / + sx - px; + pheight = ((uint64_t)(source_y + height) * canvas_height + sy - 1) / + sy - py; + placement = xcalloc(1, sizeof *placement); + do { + placement->id = ++cache->next_placement; + } while (placement->id == 0); + placement->x = destination_x; + placement->y = destination_y; + placement->width = width; + placement->height = height; + placement->next = cache->placements; + cache->placements = placement; + tty_cursor(tty, destination_x, destination_y); + xsnprintf(control, sizeof control, + "\033_Ga=p,i=%u,p=%llu,x=%u,y=%u,w=%u,h=%u,c=%u,r=%u,C=1," + "q=2\033\\", cache->kitty_id, (unsigned long long)placement->id, px, py, + pwidth, pheight, width, height); + tty_puts(tty, control); } static struct kitty_image_cache * @@ -268,6 +302,7 @@ kitty_upload(struct tty *tty, struct image *im) cache->ypixel == tty->ypixel) return (cache); kitty_delete(tty, cache->kitty_id); + kitty_free_placements(cache); cache->server_id = 0; break; } @@ -283,6 +318,7 @@ kitty_upload(struct tty *tty, struct image *im) cache->kitty_id = id; cache->xpixel = tty->xpixel; cache->ypixel = tty->ypixel; + cache->next_placement = 0; pixels = image_get_pixels(im, &stride, &image_size); image_get_dimensions(im, &width, &height); @@ -330,60 +366,16 @@ kitty_upload(struct tty *tty, struct image *im) tty_puts(tty, "\033\\"); } free(padded); - kitty_place(tty, im, id); return (cache); } -static u_int -kitty_placement(struct image *im, u_int x, u_int y) -{ - u_int across, sx; - - image_get_cell_dimensions(im, &sx, NULL); - across = (sx + nitems(kitty_diacritics) - 1) / - nitems(kitty_diacritics); - return ((y / nitems(kitty_diacritics)) * across + - x / nitems(kitty_diacritics) + 1); -} - -static void -kitty_placeholder(struct tty *tty, struct kitty_image_cache *cache, - struct image *im, u_int x, u_int y, u_int count) -{ - char buf[8192], sgr[96]; - size_t used = 0; - u_int localx, localy, placement, i; - - localx = x % nitems(kitty_diacritics); - localy = y % nitems(kitty_diacritics); - placement = kitty_placement(im, x, y); - xsnprintf(sgr, sizeof sgr, - "\033[38;2;%u;%u;%um\033[58;2;%u;%u;%um", - (cache->kitty_id >> 16) & 0xff, (cache->kitty_id >> 8) & 0xff, - cache->kitty_id & 0xff, (placement >> 16) & 0xff, - (placement >> 8) & 0xff, placement & 0xff); - tty_puts(tty, sgr); - for (i = 0; i < count; i++) { - used += kitty_utf8(buf + used, 0x10eeee); - if (i == 0) { - used += kitty_utf8(buf + used, - kitty_diacritics[localy]); - used += kitty_utf8(buf + used, - kitty_diacritics[localx]); - } - } - tty_putn(tty, buf, used, count); - tty_puts(tty, "\033[39;59m"); -} - void kitty_draw_rectangle(struct tty *tty, const struct image_rectangle *rectangle, - const struct tty_style_ctx *style_ctx) + __unused const struct tty_style_ctx *style_ctx) { - struct grid_cell draw_gc; struct kitty_image_cache *cache; struct image *im; - u_int x, y, run, available, source_x, source_y; + u_int source_x, source_y; u_int width, height, destination_x, destination_y; im = image_rectangle_get_image(rectangle); @@ -391,26 +383,10 @@ kitty_draw_rectangle(struct tty *tty, const struct image_rectangle *rectangle, cache = kitty_upload(tty, im); if (cache == NULL) return; - memcpy(&draw_gc, image_rectangle_get_cell(rectangle), sizeof draw_gc); image_rectangle_get_coordinates(rectangle, &source_x, &source_y, &width, &height, &destination_x, &destination_y); - draw_gc.flags &= ~(GRID_FLAG_IMAGE|GRID_FLAG_SELECTED); - utf8_set(&draw_gc.data, ' '); - for (y = 0; y < height; y++) { - for (x = 0; x < width; x += run) { - available = nitems(kitty_diacritics) - - (source_x + x) % - nitems(kitty_diacritics); - run = width - x; - if (run > available) - run = available; - tty_cursor(tty, destination_x + x, destination_y + y); - tty_attributes(tty, &draw_gc, style_ctx); - kitty_placeholder(tty, cache, im, source_x + x, - source_y + y, run); - tty_reset(tty); - } - } + kitty_place(tty, cache, im, source_x, source_y, width, height, + destination_x, destination_y); } static int diff --git a/image.c b/image.c index cb73fe902..d5cf6efec 100644 --- a/image.c +++ b/image.c @@ -139,6 +139,15 @@ image_tty_update(struct tty *tty) tty->client->name, backend->name); } +void +image_redraw_start(struct tty *tty, u_int x, u_int y, u_int width, + u_int height) +{ + image_tty_update(tty); + if (tty->image_backend == &image_backend_kitty) + kitty_redraw_start(tty, x, y, width, height); +} + int image_tty_is_graphical(struct tty *tty) { diff --git a/screen-redraw.c b/screen-redraw.c index 572164adf..e387c3fb0 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1583,6 +1583,30 @@ redraw_draw_pane_lines(struct redraw_draw_ctx *dctx, struct window_pane *wp, if (bottom > (int)scene->sy) bottom = scene->sy; +#ifdef ENABLE_IMAGES + if (flags & REDRAW_PANE) { + int left, right, image_y; + + left = wp->xoff - (int)scene->ox; + if (left < 0) + left = 0; + if (left > (int)scene->sx) + left = scene->sx; + right = wp->xoff + (int)wp->sx - (int)scene->ox; + if (right < 0) + right = 0; + if (right > (int)scene->sx) + right = scene->sx; + if (left < right && top < bottom) { + image_y = top; + if (dctx->flags & REDRAW_STATUS_TOP) + image_y += dctx->status_lines; + image_redraw_start(&scene->c->tty, left, image_y, + right - left, bottom - top); + } + } +#endif + for (y = top; y < bottom; y++) { line = &scene->lines[y]; if (dctx->flags & REDRAW_STATUS_TOP) @@ -1926,6 +1950,10 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags) } } tty_sync_start(tty); +#ifdef ENABLE_IMAGES + if (wp == NULL && (flags & REDRAW_PANE)) + image_redraw_start(tty, 0, 0, tty->sx, tty->sy); +#endif tty_update_mode(tty, tty->mode & ~CURSOR_MODES, NULL); if (wp != NULL) diff --git a/tmux.h b/tmux.h index 043bfd5c9..0a514b8d9 100644 --- a/tmux.h +++ b/tmux.h @@ -4266,6 +4266,7 @@ void image_redraw_area(struct screen_write_ctx *, u_int, u_int, 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); int image_tty_is_graphical(struct tty *); int image_tty_scrolls(struct tty *); void image_tty_update(struct tty *); @@ -4293,6 +4294,7 @@ int kitty_placeholder_to_cell(void *, struct grid_cell *, void kitty_free_state(void *); void kitty_draw_rectangle(struct tty *, const struct image_rectangle *, const struct tty_style_ctx *); +void kitty_redraw_start(struct tty *, u_int, u_int, u_int, u_int); void kitty_free_output(struct tty *, int); void kitty_geometry_changed(struct tty *); #endif