mirror of
https://github.com/tmux/tmux.git
synced 2026-10-08 10:54:28 +00:00
Implement generalised image support
Support Kitty and SIXEL input and output with grid-resident image layers, history and copy-mode integration, text fallback rendering, and damage-rectangle redraws. Consolidate the image work through 250f96b4b onto the redraw-damage-rectangles-new base, excluding the superseded redraw implementation and its historical merges. The original history is preserved in backup/4902-image-support-before-master-rebase-20261003.
This commit is contained in:
105
input.c
105
input.c
@@ -144,6 +144,9 @@ struct input_ctx {
|
||||
*/
|
||||
struct evbuffer *since_ground;
|
||||
struct event ground_timer;
|
||||
#ifdef ENABLE_IMAGES
|
||||
void *kitty_state;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Helper functions. */
|
||||
@@ -182,6 +185,9 @@ static void input_enter_osc(struct input_ctx *);
|
||||
static void input_exit_osc(struct input_ctx *);
|
||||
static void input_enter_apc(struct input_ctx *);
|
||||
static void input_exit_apc(struct input_ctx *);
|
||||
#ifdef ENABLE_IMAGES
|
||||
static int input_handle_kitty(struct input_ctx *, const u_char *, size_t);
|
||||
#endif
|
||||
static void input_enter_rename(struct input_ctx *);
|
||||
static void input_exit_rename(struct input_ctx *);
|
||||
|
||||
@@ -914,6 +920,9 @@ input_free(struct input_ctx *ictx)
|
||||
event_del(&ictx->request_timer);
|
||||
|
||||
free(ictx->input_buf);
|
||||
#ifdef ENABLE_IMAGES
|
||||
kitty_free_state(ictx->kitty_state);
|
||||
#endif
|
||||
evbuffer_free(ictx->since_ground);
|
||||
event_del(&ictx->ground_timer);
|
||||
|
||||
@@ -936,6 +945,7 @@ input_reset(struct input_ctx *ictx, int clear)
|
||||
screen_write_start_pane(sctx, wp, &wp->base);
|
||||
else
|
||||
screen_write_start(sctx, &wp->base);
|
||||
sctx->flags |= SCREEN_WRITE_INPUT;
|
||||
screen_write_reset(sctx);
|
||||
screen_write_stop(sctx);
|
||||
}
|
||||
@@ -1064,6 +1074,7 @@ input_parse_buffer(struct window_pane *wp, const u_char *buf, size_t len)
|
||||
screen_write_start_pane(sctx, wp, &wp->base);
|
||||
else
|
||||
screen_write_start(sctx, &wp->base);
|
||||
sctx->flags |= SCREEN_WRITE_INPUT;
|
||||
|
||||
log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id,
|
||||
ictx->state->name, len, (int)len, buf);
|
||||
@@ -1083,6 +1094,7 @@ input_parse_screen(struct input_ctx *ictx, struct screen *s,
|
||||
return;
|
||||
|
||||
screen_write_start_callback(sctx, s, cb, arg);
|
||||
sctx->flags |= SCREEN_WRITE_INPUT;
|
||||
input_parse(ictx, buf, len);
|
||||
screen_write_stop(sctx);
|
||||
}
|
||||
@@ -1582,7 +1594,7 @@ input_csi_dispatch(struct input_ctx *ictx)
|
||||
case -1:
|
||||
break;
|
||||
case 0:
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
input_reply(ictx, 1, "\033[?1;2;4c");
|
||||
#else
|
||||
input_reply(ictx, 1, "\033[?1;2c");
|
||||
@@ -2100,7 +2112,7 @@ input_csi_dispatch_sm_private(struct input_ctx *ictx)
|
||||
static void
|
||||
input_csi_dispatch_sm_graphics(__unused struct input_ctx *ictx)
|
||||
{
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
int n, m, o;
|
||||
|
||||
if (ictx->param_list_len > 3)
|
||||
@@ -2625,10 +2637,10 @@ input_dcs_dispatch(struct input_ctx *ictx)
|
||||
const char prefix[] = "tmux;";
|
||||
const u_int prefixlen = (sizeof prefix) - 1;
|
||||
long long allow_passthrough = 0;
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct window *w;
|
||||
struct sixel_image *si;
|
||||
int p2;
|
||||
int p1, p2;
|
||||
#endif
|
||||
|
||||
if (wp == NULL)
|
||||
@@ -2641,15 +2653,18 @@ input_dcs_dispatch(struct input_ctx *ictx)
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (wp != NULL && buf[0] == 'q' && ictx->interm_len == 0) {
|
||||
w = wp->window;
|
||||
if (input_split(ictx) != 0)
|
||||
return (0);
|
||||
p1 = input_get(ictx, 0, 0, 0);
|
||||
if (p1 == -1)
|
||||
p1 = 0;
|
||||
p2 = input_get(ictx, 1, 0, 0);
|
||||
if (p2 == -1)
|
||||
p2 = 0;
|
||||
si = sixel_parse(buf, len, p2, w->xpixel, w->ypixel);
|
||||
si = sixel_parse(buf, len, p1, p2, w->xpixel, w->ypixel);
|
||||
if (si != NULL)
|
||||
screen_write_sixelimage(sctx, si, ictx->cell.cell.bg);
|
||||
}
|
||||
@@ -2788,6 +2803,57 @@ input_enter_apc(struct input_ctx *ictx)
|
||||
}
|
||||
|
||||
/* APC terminator (ST) received. */
|
||||
#ifdef ENABLE_IMAGES
|
||||
static int
|
||||
input_handle_kitty(struct input_ctx *ictx, const u_char *buf, size_t len)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct image *im;
|
||||
u_int image_id = 0, replace_id = 0, quiet = 0;
|
||||
u_int placement_id = 0;
|
||||
int32_t z = 0;
|
||||
char action = '\0', delete = '\0';
|
||||
int status;
|
||||
|
||||
if (wp == NULL)
|
||||
return (0);
|
||||
im = kitty_parse_image(&ictx->kitty_state, buf, len,
|
||||
wp->window->xpixel,
|
||||
wp->window->ypixel, &image_id, &replace_id, &quiet, &action,
|
||||
&delete, &placement_id, &z, &status);
|
||||
if (status == KITTY_PARSE_MORE)
|
||||
return (1);
|
||||
if (status != KITTY_PARSE_OK) {
|
||||
if (quiet < 2 && action != '\0') {
|
||||
if (status == KITTY_PARSE_MISSING)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;ENOENT\033\\",
|
||||
image_id);
|
||||
else
|
||||
input_reply(ictx, 0, "\033_Gi=%u;EINVAL\033\\",
|
||||
image_id);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
if (replace_id != 0)
|
||||
image_clear(sctx, replace_id);
|
||||
if (im != NULL) {
|
||||
if (action == 'd')
|
||||
image_clear_kitty(sctx, delete, image_id, placement_id, z);
|
||||
else
|
||||
image_write_kitty(sctx, im, ictx->cell.cell.bg, image_id,
|
||||
placement_id, z);
|
||||
image_free(image_get_id(im));
|
||||
if (quiet == 0 && image_id != 0)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\", image_id);
|
||||
} else if (action == 'd')
|
||||
image_clear_kitty(sctx, delete, image_id, placement_id, z);
|
||||
else if ((action == 't' || action == 'q' || action == 'u') && quiet == 0)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\", image_id);
|
||||
return (1);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
input_exit_apc(struct input_ctx *ictx)
|
||||
{
|
||||
@@ -2798,6 +2864,13 @@ input_exit_apc(struct input_ctx *ictx)
|
||||
return;
|
||||
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (ictx->input_len > 1 && ictx->input_buf[0] == 'G' &&
|
||||
input_handle_kitty(ictx, ictx->input_buf + 1,
|
||||
ictx->input_len - 1))
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (wp != NULL &&
|
||||
options_get_number(wp->options, "allow-set-title") &&
|
||||
screen_set_title(sctx->s, ictx->input_buf, 1)) {
|
||||
@@ -2858,6 +2931,12 @@ input_top_bit_set(struct input_ctx *ictx)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct utf8_data *ud = &ictx->utf8data;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell gc;
|
||||
struct image *im;
|
||||
u_int x, source_x, source_y, image_id, placement_id;
|
||||
int32_t z;
|
||||
#endif
|
||||
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
|
||||
@@ -2885,6 +2964,20 @@ input_top_bit_set(struct input_ctx *ictx)
|
||||
utf8_copy(&ictx->cell.cell.data, ud);
|
||||
screen_write_collect_add(sctx, &ictx->cell.cell);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (sctx->s->cx != 0) {
|
||||
x = sctx->s->cx - 1; /* cx-1 is the cell just written. */
|
||||
grid_view_get_cell(sctx->s->grid, x, sctx->s->cy, &gc);
|
||||
if (kitty_placeholder_to_image(ictx->kitty_state,
|
||||
sctx->s->grid, &gc, x, sctx->s->cy, &im, &source_x,
|
||||
&source_y, &image_id, &placement_id, &z)) {
|
||||
grid_view_set_cell(sctx->s->grid, x, sctx->s->cy, &gc);
|
||||
image_place_cell_kitty(sctx, im, x, sctx->s->cy, source_x,
|
||||
source_y, image_id, placement_id, z);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
utf8_copy(&ictx->last, &ictx->cell.cell.data);
|
||||
ictx->flags |= INPUT_LAST;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user