mirror of
https://github.com/tmux/tmux.git
synced 2026-03-06 07:45:35 +00:00
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.
247 lines
5.4 KiB
C
247 lines
5.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);
|
|
}
|
|
|
|
/*
|
|
* 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 *
|
|
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);
|
|
}
|