mirror of
https://github.com/tmux/tmux.git
synced 2026-03-06 15:55:33 +00:00
Kitty-capable terminals (kitty, ghostty, and others) can display inline images via APC escape sequences. Kitty's image support uses a passthrough model: APC sequences from programs running inside panes are relayed verbatim to the outer terminal with cursor positioning adjusted for the pane offset. The outer terminal handles all image rendering and therefore itself must be kitty-aware.
204 lines
4.4 KiB
C
204 lines
4.4 KiB
C
/* $OpenBSD$ */
|
|
|
|
/*
|
|
* Copyright (c) 2026 Thomas Adam <thomas@xteddy.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "tmux.h"
|
|
|
|
/*
|
|
* Parse control-data key=value pairs from a kitty APC sequence.
|
|
* Format: key=value,key=value,...
|
|
*/
|
|
static int
|
|
kitty_parse_control(const char *ctrl, size_t ctrllen, struct kitty_image *ki)
|
|
{
|
|
const char *p = ctrl, *end = ctrl + ctrllen, *errstr;
|
|
char key[4], val[32];
|
|
size_t klen, vlen;
|
|
|
|
while (p < end) {
|
|
klen = 0;
|
|
while (p < end && *p != '=' && klen < sizeof(key) - 1)
|
|
key[klen++] = *p++;
|
|
key[klen] = '\0';
|
|
if (p >= end || *p != '=')
|
|
return (-1);
|
|
p++;
|
|
|
|
vlen = 0;
|
|
while (p < end && *p != ',' && vlen < sizeof(val) - 1)
|
|
val[vlen++] = *p++;
|
|
val[vlen] = '\0';
|
|
if (p < end && *p == ',')
|
|
p++;
|
|
|
|
if (klen != 1)
|
|
continue;
|
|
|
|
switch (key[0]) {
|
|
case 'a':
|
|
ki->action = val[0];
|
|
break;
|
|
case 'f':
|
|
ki->format = strtonum(val, 0, UINT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 't':
|
|
ki->medium = val[0];
|
|
break;
|
|
case 's':
|
|
ki->pixel_w = strtonum(val, 0, UINT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 'v':
|
|
ki->pixel_h = strtonum(val, 0, UINT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 'c':
|
|
ki->cols = strtonum(val, 0, UINT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 'r':
|
|
ki->rows = strtonum(val, 0, UINT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 'i':
|
|
ki->image_id = strtonum(val, 0, UINT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 'I':
|
|
ki->image_num = strtonum(val, 0, UINT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 'p':
|
|
ki->placement_id = strtonum(val, 0, UINT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 'm':
|
|
ki->more = strtonum(val, 0, UINT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 'q':
|
|
ki->quiet = strtonum(val, 0, UINT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 'z':
|
|
ki->z_index = strtonum(val, INT_MIN, INT_MAX, &errstr);
|
|
if (errstr != NULL)
|
|
return (-1);
|
|
break;
|
|
case 'o':
|
|
ki->compression = val[0];
|
|
break;
|
|
case 'd':
|
|
ki->delete_what = val[0];
|
|
break;
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Parse a kitty APC body (after the leading 'G').
|
|
* Stores the original control string and base64 payload verbatim for
|
|
* pass-through re-emission to the outer terminal.
|
|
*/
|
|
struct kitty_image *
|
|
kitty_parse(const u_char *buf, size_t len, u_int xpixel, u_int ypixel)
|
|
{
|
|
struct kitty_image *ki;
|
|
const u_char *semi;
|
|
const char *ctrl;
|
|
size_t ctrllen, paylen;
|
|
|
|
if (len == 0)
|
|
return (NULL);
|
|
|
|
semi = memchr(buf, ';', len);
|
|
if (semi != NULL) {
|
|
ctrl = (const char *)buf;
|
|
ctrllen = semi - buf;
|
|
paylen = len - ctrllen - 1;
|
|
} else {
|
|
ctrl = (const char *)buf;
|
|
ctrllen = len;
|
|
paylen = 0;
|
|
}
|
|
|
|
ki = xcalloc(1, sizeof *ki);
|
|
ki->xpixel = xpixel;
|
|
ki->ypixel = ypixel;
|
|
ki->action = 'T';
|
|
ki->format = 32;
|
|
ki->medium = 'd';
|
|
|
|
if (kitty_parse_control(ctrl, ctrllen, ki) != 0) {
|
|
free(ki);
|
|
return (NULL);
|
|
}
|
|
|
|
if (paylen > 0) {
|
|
ki->encoded = xmalloc(paylen + 1);
|
|
memcpy(ki->encoded, semi + 1, paylen);
|
|
ki->encoded[paylen] = '\0';
|
|
ki->encodedlen = paylen;
|
|
}
|
|
|
|
ki->ctrl = xmalloc(ctrllen + 1);
|
|
memcpy(ki->ctrl, ctrl, ctrllen);
|
|
ki->ctrl[ctrllen] = '\0';
|
|
ki->ctrllen = ctrllen;
|
|
|
|
return (ki);
|
|
}
|
|
|
|
void
|
|
kitty_free(struct kitty_image *ki)
|
|
{
|
|
if (ki == NULL)
|
|
return;
|
|
free(ki->encoded);
|
|
free(ki->ctrl);
|
|
free(ki);
|
|
}
|
|
|
|
char *
|
|
kitty_delete_all(size_t *outlen)
|
|
{
|
|
char *out;
|
|
|
|
*outlen = 3 + 7 + 2;
|
|
out = xmalloc(*outlen + 1);
|
|
memcpy(out, "\033_Ga=d,d=a\033\\", *outlen);
|
|
out[*outlen] = '\0';
|
|
return (out);
|
|
}
|