mirror of
https://github.com/tmux/tmux.git
synced 2026-03-06 07:45:35 +00:00
Refactor kitty images to use unified image cache API
The original kitty implementation used a passthrough approach where images were forwarded directly to the outer terminal without being stored in tmux's image cache. This refactors kitty images to work like sixel images.
This commit is contained in:
@@ -234,6 +234,11 @@ endif
|
|||||||
# Enable sixel support.
|
# Enable sixel support.
|
||||||
if ENABLE_SIXEL_IMAGES
|
if ENABLE_SIXEL_IMAGES
|
||||||
dist_tmux_SOURCES += image.c image-sixel.c
|
dist_tmux_SOURCES += image.c image-sixel.c
|
||||||
|
else
|
||||||
|
# If not sixel, still need image.c for kitty.
|
||||||
|
if ENABLE_KITTY_IMAGES
|
||||||
|
dist_tmux_SOURCES += image.c
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Enable kitty graphics protocol support.
|
# Enable kitty graphics protocol support.
|
||||||
|
|||||||
@@ -190,6 +190,49 @@ kitty_free(struct kitty_image *ki)
|
|||||||
free(ki);
|
free(ki);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Serialize a kitty_image back into an APC escape sequence for transmission
|
||||||
|
* to the terminal. This recreates the original command that was parsed.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
kitty_print(struct kitty_image *ki, size_t *outlen)
|
||||||
|
{
|
||||||
|
char *out;
|
||||||
|
size_t total, pos;
|
||||||
|
|
||||||
|
if (ki == NULL || ki->ctrl == NULL)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
/* Calculate total length: ESC _ G + ctrl + ; + encoded + ESC \ */
|
||||||
|
total = 3 + ki->ctrllen; /* \033_G + ctrl */
|
||||||
|
if (ki->encoded != NULL && ki->encodedlen > 0) {
|
||||||
|
total += 1 + ki->encodedlen; /* ; + encoded */
|
||||||
|
}
|
||||||
|
total += 2; /* \033\\ */
|
||||||
|
|
||||||
|
out = xmalloc(total + 1);
|
||||||
|
*outlen = total;
|
||||||
|
|
||||||
|
/* Build the sequence */
|
||||||
|
pos = 0;
|
||||||
|
memcpy(out + pos, "\033_G", 3);
|
||||||
|
pos += 3;
|
||||||
|
memcpy(out + pos, ki->ctrl, ki->ctrllen);
|
||||||
|
pos += ki->ctrllen;
|
||||||
|
|
||||||
|
if (ki->encoded != NULL && ki->encodedlen > 0) {
|
||||||
|
out[pos++] = ';';
|
||||||
|
memcpy(out + pos, ki->encoded, ki->encodedlen);
|
||||||
|
pos += ki->encodedlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(out + pos, "\033\\", 2);
|
||||||
|
pos += 2;
|
||||||
|
out[pos] = '\0';
|
||||||
|
|
||||||
|
return (out);
|
||||||
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
kitty_delete_all(size_t *outlen)
|
kitty_delete_all(size_t *outlen)
|
||||||
{
|
{
|
||||||
|
|||||||
141
image.c
141
image.c
@@ -61,7 +61,22 @@ image_free(struct image *im)
|
|||||||
all_images_count--;
|
all_images_count--;
|
||||||
|
|
||||||
TAILQ_REMOVE(&s->images, im, entry);
|
TAILQ_REMOVE(&s->images, im, entry);
|
||||||
sixel_free(im->data);
|
|
||||||
|
switch (im->type) {
|
||||||
|
#ifdef ENABLE_SIXEL_IMAGES
|
||||||
|
case IMAGE_SIXEL:
|
||||||
|
sixel_free(im->data.sixel);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_KITTY_IMAGES
|
||||||
|
case IMAGE_KITTY:
|
||||||
|
kitty_free(im->data.kitty);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
free(im->fallback);
|
free(im->fallback);
|
||||||
free(im);
|
free(im);
|
||||||
}
|
}
|
||||||
@@ -81,13 +96,30 @@ image_free_all(struct screen *s)
|
|||||||
|
|
||||||
/* Create text placeholder for an image. */
|
/* Create text placeholder for an image. */
|
||||||
static void
|
static void
|
||||||
image_fallback(char **ret, u_int sx, u_int sy)
|
image_fallback(char **ret, enum image_type type, u_int sx, u_int sy)
|
||||||
{
|
{
|
||||||
char *buf, *label;
|
char *buf, *label;
|
||||||
u_int py, size, lsize;
|
u_int py, size, lsize;
|
||||||
|
const char *type_name;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
#ifdef ENABLE_SIXEL_IMAGES
|
||||||
|
case IMAGE_SIXEL:
|
||||||
|
type_name = "SIXEL";
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_KITTY_IMAGES
|
||||||
|
case IMAGE_KITTY:
|
||||||
|
type_name = "KITTY";
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
type_name = "UNKNOWN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Allocate first line. */
|
/* Allocate first line. */
|
||||||
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
|
lsize = xasprintf(&label, "%s IMAGE (%ux%u)\r\n", type_name, sx, sy) + 1;
|
||||||
if (sx < lsize - 3)
|
if (sx < lsize - 3)
|
||||||
size = lsize - 1;
|
size = lsize - 1;
|
||||||
else
|
else
|
||||||
@@ -122,19 +154,62 @@ image_fallback(char **ret, u_int sx, u_int sy)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct image*
|
struct image*
|
||||||
image_store(struct screen *s, struct sixel_image *si)
|
image_store(struct screen *s, enum image_type type, void *data)
|
||||||
{
|
{
|
||||||
struct image *im;
|
struct image *im;
|
||||||
|
|
||||||
im = xcalloc(1, sizeof *im);
|
im = xcalloc(1, sizeof *im);
|
||||||
|
|
||||||
|
im->type = type;
|
||||||
im->s = s;
|
im->s = s;
|
||||||
im->data = si;
|
|
||||||
|
|
||||||
im->px = s->cx;
|
im->px = s->cx;
|
||||||
im->py = s->cy;
|
im->py = s->cy;
|
||||||
sixel_size_in_cells(si, &im->sx, &im->sy);
|
|
||||||
|
|
||||||
image_fallback(&im->fallback, im->sx, im->sy);
|
switch (type) {
|
||||||
|
#ifdef ENABLE_SIXEL_IMAGES
|
||||||
|
case IMAGE_SIXEL:
|
||||||
|
im->data.sixel = data;
|
||||||
|
sixel_size_in_cells(im->data.sixel, &im->sx, &im->sy);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_KITTY_IMAGES
|
||||||
|
case IMAGE_KITTY:
|
||||||
|
im->data.kitty = data;
|
||||||
|
|
||||||
|
/* Kitty images have size info in the structure */
|
||||||
|
im->sx = im->data.kitty->cols;
|
||||||
|
im->sy = im->data.kitty->rows;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If cols/rows are 0, they mean "auto" - calculate from
|
||||||
|
* pixel dimensions. The terminal will figure out the actual
|
||||||
|
* size, but we need a reasonable estimate for our cache.
|
||||||
|
*/
|
||||||
|
if (im->sx == 0 && im->data.kitty->pixel_w > 0 &&
|
||||||
|
im->data.kitty->xpixel > 0) {
|
||||||
|
im->sx = (im->data.kitty->pixel_w +
|
||||||
|
im->data.kitty->xpixel - 1) /
|
||||||
|
im->data.kitty->xpixel;
|
||||||
|
}
|
||||||
|
if (im->sy == 0 && im->data.kitty->pixel_h > 0 &&
|
||||||
|
im->data.kitty->ypixel > 0) {
|
||||||
|
im->sy = (im->data.kitty->pixel_h +
|
||||||
|
im->data.kitty->ypixel - 1) / im->data.kitty->ypixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If still 0, use a reasonable default */
|
||||||
|
if (im->sx == 0)
|
||||||
|
im->sx = 10;
|
||||||
|
if (im->sy == 0)
|
||||||
|
im->sy = 10;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
image_fallback(&im->fallback, type, im->sx, im->sy);
|
||||||
|
|
||||||
image_log(im, __func__, NULL);
|
image_log(im, __func__, NULL);
|
||||||
TAILQ_INSERT_TAIL(&s->images, im, entry);
|
TAILQ_INSERT_TAIL(&s->images, im, entry);
|
||||||
@@ -188,8 +263,10 @@ image_scroll_up(struct screen *s, u_int lines)
|
|||||||
{
|
{
|
||||||
struct image *im, *im1;
|
struct image *im, *im1;
|
||||||
int redraw = 0;
|
int redraw = 0;
|
||||||
u_int sx, sy;
|
#ifdef ENABLE_SIXEL_IMAGES
|
||||||
struct sixel_image *new;
|
struct sixel_image *new;
|
||||||
|
u_int sx, sy;
|
||||||
|
#endif
|
||||||
|
|
||||||
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||||
if (im->py >= lines) {
|
if (im->py >= lines) {
|
||||||
@@ -204,20 +281,46 @@ image_scroll_up(struct screen *s, u_int lines)
|
|||||||
redraw = 1;
|
redraw = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sx = im->sx;
|
|
||||||
sy = (im->py + im->sy) - lines;
|
|
||||||
image_log(im, __func__, "3, lines=%u, sy=%u", lines, sy);
|
|
||||||
|
|
||||||
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
|
/* Image is partially scrolled off - need to crop it */
|
||||||
sixel_free(im->data);
|
switch (im->type) {
|
||||||
im->data = new;
|
#ifdef ENABLE_SIXEL_IMAGES
|
||||||
|
case IMAGE_SIXEL:
|
||||||
|
sx = im->sx;
|
||||||
|
sy = (im->py + im->sy) - lines;
|
||||||
|
image_log(im, __func__, "3 sixel, lines=%u, sy=%u",
|
||||||
|
lines, sy);
|
||||||
|
|
||||||
im->py = 0;
|
new = sixel_scale(im->data.sixel, 0, 0, 0, im->sy - sy,
|
||||||
sixel_size_in_cells(im->data, &im->sx, &im->sy);
|
sx, sy, 1);
|
||||||
|
sixel_free(im->data.sixel);
|
||||||
|
im->data.sixel = new;
|
||||||
|
|
||||||
free(im->fallback);
|
im->py = 0;
|
||||||
image_fallback(&im->fallback, im->sx, im->sy);
|
sixel_size_in_cells(im->data.sixel, &im->sx, &im->sy);
|
||||||
redraw = 1;
|
|
||||||
|
free(im->fallback);
|
||||||
|
image_fallback(&im->fallback, im->type, im->sx, im->sy);
|
||||||
|
redraw = 1;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_KITTY_IMAGES
|
||||||
|
case IMAGE_KITTY:
|
||||||
|
/*
|
||||||
|
* For kitty images, we can't rescale - the terminal
|
||||||
|
* owns the placement. Just adjust position and let
|
||||||
|
* the terminal handle clipping.
|
||||||
|
*/
|
||||||
|
image_log(im, __func__,
|
||||||
|
"3 kitty, lines=%u (no rescale)", lines);
|
||||||
|
im->py = 0;
|
||||||
|
/* Height remains the same - terminal will clip */
|
||||||
|
redraw = 1;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (redraw);
|
return (redraw);
|
||||||
}
|
}
|
||||||
|
|||||||
52
input.c
52
input.c
@@ -1561,12 +1561,31 @@ input_csi_dispatch(struct input_ctx *ictx)
|
|||||||
case -1:
|
case -1:
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Report sixel support only if outer terminal supports it.
|
||||||
|
* This ensures applications choose the right protocol.
|
||||||
|
*/
|
||||||
|
int has_sixel = 0;
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_SIXEL_IMAGES
|
||||||
input_reply(ictx, 1, "\033[?1;2;4c");
|
if (ictx->wp != NULL) {
|
||||||
#else
|
struct client *c;
|
||||||
input_reply(ictx, 1, "\033[?1;2c");
|
TAILQ_FOREACH(c, &clients, entry) {
|
||||||
|
if (c->session != NULL &&
|
||||||
|
c->session->curw->window == ictx->wp->window &&
|
||||||
|
(c->tty.term->flags & TERM_SIXEL)) {
|
||||||
|
has_sixel = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (has_sixel)
|
||||||
|
input_reply(ictx, 1, "\033[?1;2;4c");
|
||||||
|
else
|
||||||
|
input_reply(ictx, 1, "\033[?1;2c");
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
log_debug("%s: unknown '%c'", __func__, ictx->ch);
|
||||||
break;
|
break;
|
||||||
@@ -2729,6 +2748,7 @@ input_exit_apc(struct input_ctx *ictx)
|
|||||||
struct window_pane *wp = ictx->wp;
|
struct window_pane *wp = ictx->wp;
|
||||||
#ifdef ENABLE_KITTY_IMAGES
|
#ifdef ENABLE_KITTY_IMAGES
|
||||||
struct kitty_image *ki;
|
struct kitty_image *ki;
|
||||||
|
struct window *w;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (ictx->flags & INPUT_DISCARD)
|
if (ictx->flags & INPUT_DISCARD)
|
||||||
@@ -2740,6 +2760,7 @@ input_exit_apc(struct input_ctx *ictx)
|
|||||||
if (wp == NULL)
|
if (wp == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
w = wp->window;
|
||||||
/*
|
/*
|
||||||
* Intercept only a=q (query) to reply OK ourselves.
|
* Intercept only a=q (query) to reply OK ourselves.
|
||||||
* Everything else — including a=T (transmit+display),
|
* Everything else — including a=T (transmit+display),
|
||||||
@@ -2749,8 +2770,12 @@ input_exit_apc(struct input_ctx *ictx)
|
|||||||
* scrolling; tmux must not interfere.
|
* scrolling; tmux must not interfere.
|
||||||
*/
|
*/
|
||||||
ki = kitty_parse(ictx->input_buf + 1,
|
ki = kitty_parse(ictx->input_buf + 1,
|
||||||
ictx->input_len - 1, 0, 0);
|
ictx->input_len - 1, w->xpixel, w->ypixel);
|
||||||
if (ki != NULL && ki->action == 'q') {
|
if (ki == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Handle query commands */
|
||||||
|
if (ki->action == 'q') {
|
||||||
if (ki->image_id != 0)
|
if (ki->image_id != 0)
|
||||||
input_reply(ictx, 0,
|
input_reply(ictx, 0,
|
||||||
"\033_Gi=%u;OK\033\\",
|
"\033_Gi=%u;OK\033\\",
|
||||||
@@ -2761,26 +2786,31 @@ input_exit_apc(struct input_ctx *ictx)
|
|||||||
kitty_free(ki);
|
kitty_free(ki);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
kitty_free(ki);
|
|
||||||
|
|
||||||
/* Reconstruct APC and pass through verbatim. */
|
/*
|
||||||
{
|
* Store the image and trigger a redraw.
|
||||||
|
* Similar to sixel, we cache the image and let the
|
||||||
|
* redraw mechanism handle sending it to terminals.
|
||||||
|
*/
|
||||||
|
if (ki->action == 'T' || ki->action == 't' || ki->action == 'p') {
|
||||||
|
screen_write_kittyimage(sctx, ki);
|
||||||
|
} else {
|
||||||
|
/* For other actions (delete, etc.), pass through */
|
||||||
char *apc;
|
char *apc;
|
||||||
size_t apclen;
|
size_t apclen;
|
||||||
|
|
||||||
/* input_buf already starts with 'G'. */
|
|
||||||
apclen = 2 + ictx->input_len + 2;
|
apclen = 2 + ictx->input_len + 2;
|
||||||
apc = xmalloc(apclen + 1);
|
apc = xmalloc(apclen + 1);
|
||||||
apc[0] = '\033';
|
apc[0] = '\033';
|
||||||
apc[1] = '_';
|
apc[1] = '_';
|
||||||
memcpy(apc + 2, ictx->input_buf,
|
memcpy(apc + 2, ictx->input_buf, ictx->input_len);
|
||||||
ictx->input_len);
|
|
||||||
apc[2 + ictx->input_len] = '\033';
|
apc[2 + ictx->input_len] = '\033';
|
||||||
apc[2 + ictx->input_len + 1] = '\\';
|
apc[2 + ictx->input_len + 1] = '\\';
|
||||||
apc[apclen] = '\0';
|
apc[apclen] = '\0';
|
||||||
tty_kitty_passthrough(wp, apc, apclen,
|
tty_kitty_passthrough(wp, apc, apclen,
|
||||||
sctx->s->cx, sctx->s->cy);
|
sctx->s->cx, sctx->s->cy);
|
||||||
free(apc);
|
free(apc);
|
||||||
|
kitty_free(ki);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2450,7 +2450,7 @@ screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
|
|||||||
screen_write_collect_flush(ctx, 0, __func__);
|
screen_write_collect_flush(ctx, 0, __func__);
|
||||||
|
|
||||||
screen_write_initctx(ctx, &ttyctx, 0);
|
screen_write_initctx(ctx, &ttyctx, 0);
|
||||||
ttyctx.ptr = image_store(s, si);
|
ttyctx.ptr = image_store(s, IMAGE_SIXEL, si);
|
||||||
|
|
||||||
tty_write(tty_cmd_sixelimage, &ttyctx);
|
tty_write(tty_cmd_sixelimage, &ttyctx);
|
||||||
|
|
||||||
@@ -2458,6 +2458,38 @@ screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_KITTY_IMAGES
|
||||||
|
void
|
||||||
|
screen_write_kittyimage(struct screen_write_ctx *ctx, struct kitty_image *ki)
|
||||||
|
{
|
||||||
|
struct screen *s = ctx->s;
|
||||||
|
struct tty_ctx ttyctx;
|
||||||
|
struct image *im;
|
||||||
|
|
||||||
|
if (ki == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Store the image in the cache */
|
||||||
|
im = image_store(s, IMAGE_KITTY, ki);
|
||||||
|
|
||||||
|
/* Trigger a tty write to send to all terminals */
|
||||||
|
if (im != NULL && ctx->wp != NULL) {
|
||||||
|
screen_write_collect_flush(ctx, 0, __func__);
|
||||||
|
screen_write_initctx(ctx, &ttyctx, 0);
|
||||||
|
ttyctx.ptr = im;
|
||||||
|
ttyctx.arg = ctx->wp;
|
||||||
|
ttyctx.ocx = s->cx;
|
||||||
|
ttyctx.ocy = s->cy;
|
||||||
|
ttyctx.set_client_cb = tty_set_client_cb;
|
||||||
|
tty_write(tty_cmd_kittyimage, &ttyctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Move cursor past the image */
|
||||||
|
if (ki->rows > 0)
|
||||||
|
screen_write_cursormove(ctx, 0, s->cy + ki->rows, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Turn alternate screen on. */
|
/* Turn alternate screen on. */
|
||||||
void
|
void
|
||||||
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
|
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
|
||||||
|
|||||||
12
screen.c
12
screen.c
@@ -89,7 +89,7 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
|
|||||||
s->tabs = NULL;
|
s->tabs = NULL;
|
||||||
s->sel = NULL;
|
s->sel = NULL;
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
TAILQ_INIT(&s->images);
|
TAILQ_INIT(&s->images);
|
||||||
TAILQ_INIT(&s->saved_images);
|
TAILQ_INIT(&s->saved_images);
|
||||||
#endif
|
#endif
|
||||||
@@ -127,7 +127,7 @@ screen_reinit(struct screen *s)
|
|||||||
screen_clear_selection(s);
|
screen_clear_selection(s);
|
||||||
screen_free_titles(s);
|
screen_free_titles(s);
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
image_free_all(s);
|
image_free_all(s);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ screen_free(struct screen *s)
|
|||||||
hyperlinks_free(s->hyperlinks);
|
hyperlinks_free(s->hyperlinks);
|
||||||
screen_free_titles(s);
|
screen_free_titles(s);
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
image_free_all(s);
|
image_free_all(s);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -324,7 +324,7 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
|
|||||||
if (sy != screen_size_y(s))
|
if (sy != screen_size_y(s))
|
||||||
screen_resize_y(s, sy, eat_empty, &cy);
|
screen_resize_y(s, sy, eat_empty, &cy);
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
image_free_all(s);
|
image_free_all(s);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -649,7 +649,7 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
|
|||||||
}
|
}
|
||||||
memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
|
memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
TAILQ_CONCAT(&s->saved_images, &s->images, entry);
|
TAILQ_CONCAT(&s->saved_images, &s->images, entry);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -707,7 +707,7 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
|||||||
grid_destroy(s->saved_grid);
|
grid_destroy(s->saved_grid);
|
||||||
s->saved_grid = NULL;
|
s->saved_grid = NULL;
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
image_free_all(s);
|
image_free_all(s);
|
||||||
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
|
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
41
tmux.h
41
tmux.h
@@ -68,6 +68,11 @@ struct screen_write_cline;
|
|||||||
struct screen_write_ctx;
|
struct screen_write_ctx;
|
||||||
struct session;
|
struct session;
|
||||||
|
|
||||||
|
/* Convenience macro: defined if any image protocol is compiled in. */
|
||||||
|
#if defined(ENABLE_SIXEL_IMAGES) || defined(ENABLE_KITTY_IMAGES)
|
||||||
|
#define ENABLE_IMAGES
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_SIXEL_IMAGES
|
||||||
struct sixel_image;
|
struct sixel_image;
|
||||||
#endif
|
#endif
|
||||||
@@ -930,11 +935,23 @@ struct style {
|
|||||||
enum style_default_type default_type;
|
enum style_default_type default_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
|
/* Image types. */
|
||||||
|
enum image_type {
|
||||||
|
IMAGE_SIXEL,
|
||||||
|
IMAGE_KITTY
|
||||||
|
};
|
||||||
|
|
||||||
/* Image. */
|
/* Image. */
|
||||||
struct image {
|
struct image {
|
||||||
|
enum image_type type;
|
||||||
struct screen *s;
|
struct screen *s;
|
||||||
struct sixel_image *data;
|
union {
|
||||||
|
struct sixel_image *sixel;
|
||||||
|
#ifdef ENABLE_KITTY_IMAGES
|
||||||
|
struct kitty_image *kitty;
|
||||||
|
#endif
|
||||||
|
} data;
|
||||||
char *fallback;
|
char *fallback;
|
||||||
|
|
||||||
u_int px;
|
u_int px;
|
||||||
@@ -1027,7 +1044,7 @@ struct screen {
|
|||||||
bitstr_t *tabs;
|
bitstr_t *tabs;
|
||||||
struct screen_sel *sel;
|
struct screen_sel *sel;
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
struct images images;
|
struct images images;
|
||||||
struct images saved_images;
|
struct images saved_images;
|
||||||
#endif
|
#endif
|
||||||
@@ -2664,7 +2681,7 @@ struct visible_ranges *tty_check_overlay_range(struct tty *, u_int, u_int,
|
|||||||
void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int,
|
void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int,
|
||||||
u_int, u_int, const struct grid_cell *, struct colour_palette *);
|
u_int, u_int, const struct grid_cell *, struct colour_palette *);
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
void tty_draw_images(struct client *, struct window_pane *, struct screen *);
|
void tty_draw_images(struct client *, struct window_pane *, struct screen *);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -2698,11 +2715,16 @@ void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *);
|
|||||||
void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
|
void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
|
||||||
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
|
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
|
||||||
|
|
||||||
|
#ifdef ENABLE_IMAGES
|
||||||
|
int tty_set_client_cb(struct tty_ctx *, struct client *);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_SIXEL_IMAGES
|
||||||
void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *);
|
void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_KITTY_IMAGES
|
#ifdef ENABLE_KITTY_IMAGES
|
||||||
|
void tty_cmd_kittyimage(struct tty *, const struct tty_ctx *);
|
||||||
void tty_kitty_delete_all(struct tty *);
|
void tty_kitty_delete_all(struct tty *);
|
||||||
void tty_kitty_delete_all_pane(struct window_pane *);
|
void tty_kitty_delete_all_pane(struct window_pane *);
|
||||||
void tty_kitty_passthrough(struct window_pane *, const char *, size_t,
|
void tty_kitty_passthrough(struct window_pane *, const char *, size_t,
|
||||||
@@ -3315,6 +3337,10 @@ void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
|
|||||||
void screen_write_sixelimage(struct screen_write_ctx *,
|
void screen_write_sixelimage(struct screen_write_ctx *,
|
||||||
struct sixel_image *, u_int);
|
struct sixel_image *, u_int);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_KITTY_IMAGES
|
||||||
|
void screen_write_kittyimage(struct screen_write_ctx *,
|
||||||
|
struct kitty_image *);
|
||||||
|
#endif
|
||||||
|
|
||||||
void screen_write_alternateon(struct screen_write_ctx *,
|
void screen_write_alternateon(struct screen_write_ctx *,
|
||||||
struct grid_cell *, int);
|
struct grid_cell *, int);
|
||||||
@@ -3779,14 +3805,16 @@ struct window_pane *spawn_pane(struct spawn_context *, char **);
|
|||||||
/* regsub.c */
|
/* regsub.c */
|
||||||
char *regsub(const char *, const char *, const char *, int);
|
char *regsub(const char *, const char *, const char *, int);
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
/* image.c */
|
/* image.c */
|
||||||
int image_free_all(struct screen *);
|
int image_free_all(struct screen *);
|
||||||
struct image *image_store(struct screen *, struct sixel_image *);
|
struct image *image_store(struct screen *, enum image_type, void *);
|
||||||
int image_check_line(struct screen *, u_int, u_int);
|
int image_check_line(struct screen *, u_int, u_int);
|
||||||
int image_check_area(struct screen *, u_int, u_int, u_int, u_int);
|
int image_check_area(struct screen *, u_int, u_int, u_int, u_int);
|
||||||
int image_scroll_up(struct screen *, u_int);
|
int image_scroll_up(struct screen *, u_int);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIXEL_IMAGES
|
||||||
/* image-sixel.c */
|
/* image-sixel.c */
|
||||||
#define SIXEL_COLOUR_REGISTERS 1024
|
#define SIXEL_COLOUR_REGISTERS 1024
|
||||||
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
|
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
|
||||||
@@ -3804,6 +3832,7 @@ struct screen *sixel_to_screen(struct sixel_image *);
|
|||||||
/* image-kitty.c */
|
/* image-kitty.c */
|
||||||
struct kitty_image *kitty_parse(const u_char *, size_t, u_int, u_int);
|
struct kitty_image *kitty_parse(const u_char *, size_t, u_int, u_int);
|
||||||
void kitty_free(struct kitty_image *);
|
void kitty_free(struct kitty_image *);
|
||||||
|
char *kitty_print(struct kitty_image *, size_t *);
|
||||||
char *kitty_delete_all(size_t *);
|
char *kitty_delete_all(size_t *);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
84
tty.c
84
tty.c
@@ -67,7 +67,7 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code,
|
|||||||
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
|
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
|
||||||
static int tty_check_overlay(struct tty *, u_int, u_int);
|
static int tty_check_overlay(struct tty *, u_int, u_int);
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
|
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
|
||||||
struct client *, struct tty_ctx *);
|
struct client *, struct tty_ctx *);
|
||||||
#endif
|
#endif
|
||||||
@@ -1459,9 +1459,9 @@ tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
|
|||||||
return (c->overlay_check(c, c->overlay_data, px, py, nx));
|
return (c->overlay_check(c, c->overlay_data, px, py, nx));
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
/* Update context for client. */
|
/* Update context for client. */
|
||||||
static int
|
int
|
||||||
tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
||||||
{
|
{
|
||||||
struct window_pane *wp = ttyctx->arg;
|
struct window_pane *wp = ttyctx->arg;
|
||||||
@@ -1485,7 +1485,6 @@ tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
|
|||||||
void
|
void
|
||||||
tty_draw_images(struct client *c, struct window_pane *wp, struct screen *s)
|
tty_draw_images(struct client *c, struct window_pane *wp, struct screen *s)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
|
||||||
struct image *im;
|
struct image *im;
|
||||||
struct tty_ctx ttyctx;
|
struct tty_ctx ttyctx;
|
||||||
|
|
||||||
@@ -1507,11 +1506,23 @@ tty_draw_images(struct client *c, struct window_pane *wp, struct screen *s)
|
|||||||
ttyctx.arg = wp;
|
ttyctx.arg = wp;
|
||||||
ttyctx.set_client_cb = tty_set_client_cb;
|
ttyctx.set_client_cb = tty_set_client_cb;
|
||||||
ttyctx.allow_invisible_panes = 1;
|
ttyctx.allow_invisible_panes = 1;
|
||||||
tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
|
|
||||||
}
|
/* Call the appropriate rendering function based on image type */
|
||||||
#else
|
switch (im->type) {
|
||||||
(void)c; (void)wp; (void)s;
|
#ifdef ENABLE_SIXEL_IMAGES
|
||||||
|
case IMAGE_SIXEL:
|
||||||
|
tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
|
||||||
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_KITTY_IMAGES
|
||||||
|
case IMAGE_KITTY:
|
||||||
|
tty_write_one(tty_cmd_kittyimage, c, &ttyctx);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1588,7 +1599,7 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ENABLE_SIXEL_IMAGES
|
#ifdef ENABLE_IMAGES
|
||||||
/* Only write to the incoming tty instead of every client. */
|
/* Only write to the incoming tty instead of every client. */
|
||||||
static void
|
static void
|
||||||
tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
|
tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
|
||||||
@@ -2118,7 +2129,7 @@ void
|
|||||||
tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
|
tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct image *im = ctx->ptr;
|
struct image *im = ctx->ptr;
|
||||||
struct sixel_image *si = im->data;
|
struct sixel_image *si = im->data.sixel;
|
||||||
struct sixel_image *new;
|
struct sixel_image *new;
|
||||||
char *data;
|
char *data;
|
||||||
size_t size;
|
size_t size;
|
||||||
@@ -2173,6 +2184,59 @@ tty_has_kitty(struct tty *tty)
|
|||||||
tty_term_has(tty->term, TTYC_KTY));
|
tty_term_has(tty->term, TTYC_KTY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tty_cmd_kittyimage(struct tty *tty, const struct tty_ctx *ctx)
|
||||||
|
{
|
||||||
|
struct image *im = ctx->ptr;
|
||||||
|
struct kitty_image *ki;
|
||||||
|
char *data;
|
||||||
|
size_t size;
|
||||||
|
u_int cx = ctx->ocx, cy = ctx->ocy;
|
||||||
|
int fallback = 0;
|
||||||
|
|
||||||
|
log_debug("%s: called, im=%p", __func__, im);
|
||||||
|
|
||||||
|
if (im == NULL) {
|
||||||
|
log_debug("%s: NULL image pointer", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ki = im->data.kitty;
|
||||||
|
log_debug("%s: ki=%p, type=%d", __func__, ki, im->type);
|
||||||
|
|
||||||
|
if (ki == NULL) {
|
||||||
|
log_debug("%s: NULL kitty_image pointer", __func__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if this terminal supports kitty graphics */
|
||||||
|
if (!tty_has_kitty(tty))
|
||||||
|
fallback = 1;
|
||||||
|
|
||||||
|
log_debug("%s: image at %u,%u (fallback=%d)", __func__, cx, cy, fallback);
|
||||||
|
|
||||||
|
if (fallback == 1) {
|
||||||
|
/* Use text fallback for non-kitty terminals */
|
||||||
|
data = xstrdup(im->fallback);
|
||||||
|
size = strlen(data);
|
||||||
|
} else {
|
||||||
|
/* Re-serialize the kitty image command */
|
||||||
|
data = kitty_print(ki, &size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != NULL) {
|
||||||
|
log_debug("%s: %zu bytes", __func__, size);
|
||||||
|
tty_region_off(tty);
|
||||||
|
tty_margin_off(tty);
|
||||||
|
tty_cursor(tty, cx + ctx->xoff, cy + ctx->yoff);
|
||||||
|
|
||||||
|
tty->flags |= TTY_NOBLOCK;
|
||||||
|
tty_add(tty, data, size);
|
||||||
|
tty_invalidate(tty);
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pass a kitty APC sequence directly to all attached kitty-capable clients
|
* Pass a kitty APC sequence directly to all attached kitty-capable clients
|
||||||
* showing the given pane. The outer terminal's cursor is first moved to
|
* showing the given pane. The outer terminal's cursor is first moved to
|
||||||
|
|||||||
Reference in New Issue
Block a user