mirror of
https://github.com/tmux/tmux.git
synced 2026-08-04 06:28:35 +00:00
Support Kitty source rectangle placements
This commit is contained in:
163
image-kitty.c
163
image-kitty.c
@@ -75,6 +75,10 @@ struct kitty_state {
|
||||
char compression;
|
||||
u_int width;
|
||||
u_int height;
|
||||
u_int source_x;
|
||||
u_int source_y;
|
||||
u_int source_width;
|
||||
u_int source_height;
|
||||
u_int columns;
|
||||
u_int rows;
|
||||
u_int image_id;
|
||||
@@ -232,7 +236,8 @@ kitty_upload(struct tty *tty, struct image *im)
|
||||
struct kitty_output *ko = kitty_get_output(tty);
|
||||
struct kitty_image_cache *cache;
|
||||
char control[128], encoded[4097];
|
||||
size_t offset, size;
|
||||
u_char raw[3072];
|
||||
size_t offset, size, copied, row, column, available;
|
||||
int encodedlen;
|
||||
u_int id;
|
||||
|
||||
@@ -261,9 +266,18 @@ kitty_upload(struct tty *tty, struct image *im)
|
||||
|
||||
for (offset = 0; offset < im->size; offset += size) {
|
||||
size = im->size - offset;
|
||||
if (size > 3072)
|
||||
size = 3072;
|
||||
encodedlen = b64_ntop(im->pixels + offset, size, encoded,
|
||||
if (size > sizeof raw)
|
||||
size = sizeof raw;
|
||||
for (copied = 0; copied < size; copied += available) {
|
||||
row = (offset + copied) / ((size_t)im->width * 4);
|
||||
column = (offset + copied) % ((size_t)im->width * 4);
|
||||
available = (size_t)im->width * 4 - column;
|
||||
if (available > size - copied)
|
||||
available = size - copied;
|
||||
memcpy(raw + copied, im->pixels + row * im->stride +
|
||||
column, available);
|
||||
}
|
||||
encodedlen = b64_ntop(raw, size, encoded,
|
||||
sizeof encoded);
|
||||
if (encodedlen < 0)
|
||||
return (NULL);
|
||||
@@ -418,6 +432,10 @@ kitty_control(struct kitty_state *ks, const u_char *buf, size_t len)
|
||||
case 'f':
|
||||
case 's':
|
||||
case 'v':
|
||||
case 'x':
|
||||
case 'y':
|
||||
case 'w':
|
||||
case 'h':
|
||||
case 'c':
|
||||
case 'r':
|
||||
case 'i':
|
||||
@@ -431,6 +449,10 @@ kitty_control(struct kitty_state *ks, const u_char *buf, size_t len)
|
||||
case 'f': ks->format = number; break;
|
||||
case 's': ks->width = number; break;
|
||||
case 'v': ks->height = number; break;
|
||||
case 'x': ks->source_x = number; break;
|
||||
case 'y': ks->source_y = number; break;
|
||||
case 'w': ks->source_width = number; break;
|
||||
case 'h': ks->source_height = number; break;
|
||||
case 'c': ks->columns = number; break;
|
||||
case 'r': ks->rows = number; break;
|
||||
case 'i': ks->image_id = number; break;
|
||||
@@ -594,9 +616,87 @@ kitty_raw(struct kitty_state *ks, u_char *data, size_t size)
|
||||
return (pixels);
|
||||
}
|
||||
|
||||
static struct image *
|
||||
kitty_place_image(struct image *source, struct kitty_state *ks, u_int xpixel,
|
||||
u_int ypixel)
|
||||
{
|
||||
uint64_t numerator, denominator, value;
|
||||
u_int x, y, width, height, sx, sy, canvas_width;
|
||||
u_int canvas_height, cell_width, cell_height;
|
||||
|
||||
x = ks->source_x;
|
||||
y = ks->source_y;
|
||||
if (x >= source->width || y >= source->height)
|
||||
return (NULL);
|
||||
width = ks->source_width;
|
||||
if (width == 0 || width > source->width - x)
|
||||
width = source->width - x;
|
||||
height = ks->source_height;
|
||||
if (height == 0 || height > source->height - y)
|
||||
height = source->height - y;
|
||||
|
||||
cell_width = (xpixel == 0 ? 8 : xpixel);
|
||||
cell_height = (ypixel == 0 ? 16 : ypixel);
|
||||
if (ks->columns == 0 && ks->rows == 0) {
|
||||
image_size_in_cells(width, height, cell_width, cell_height,
|
||||
&sx, &sy);
|
||||
value = (uint64_t)sx * cell_width;
|
||||
if (value > UINT_MAX)
|
||||
return (NULL);
|
||||
canvas_width = value;
|
||||
value = (uint64_t)sy * cell_height;
|
||||
if (value > UINT_MAX)
|
||||
return (NULL);
|
||||
canvas_height = value;
|
||||
} else if (ks->columns != 0 && ks->rows != 0) {
|
||||
sx = ks->columns;
|
||||
sy = ks->rows;
|
||||
canvas_width = width;
|
||||
canvas_height = height;
|
||||
} else if (ks->columns != 0) {
|
||||
sx = ks->columns;
|
||||
numerator = (uint64_t)height * sx * cell_width;
|
||||
denominator = (uint64_t)width * cell_height;
|
||||
value = (numerator + denominator - 1) / denominator;
|
||||
if (value == 0 || value > UINT_MAX)
|
||||
return (NULL);
|
||||
sy = value;
|
||||
canvas_width = width;
|
||||
numerator = (uint64_t)sy * cell_height * width;
|
||||
denominator = (uint64_t)sx * cell_width;
|
||||
value = (numerator + denominator - 1) / denominator;
|
||||
if (value < height)
|
||||
value = height;
|
||||
if (value > UINT_MAX)
|
||||
return (NULL);
|
||||
canvas_height = value;
|
||||
} else {
|
||||
sy = ks->rows;
|
||||
numerator = (uint64_t)width * sy * cell_height;
|
||||
denominator = (uint64_t)height * cell_width;
|
||||
value = (numerator + denominator - 1) / denominator;
|
||||
if (value == 0 || value > UINT_MAX)
|
||||
return (NULL);
|
||||
sx = value;
|
||||
canvas_height = height;
|
||||
numerator = (uint64_t)sx * cell_width * height;
|
||||
denominator = (uint64_t)sy * cell_height;
|
||||
value = (numerator + denominator - 1) / denominator;
|
||||
if (value < width)
|
||||
value = width;
|
||||
if (value > UINT_MAX)
|
||||
return (NULL);
|
||||
canvas_width = value;
|
||||
}
|
||||
|
||||
return (image_create_view(source, x, y, width, height, canvas_width,
|
||||
canvas_height, sx, sy));
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse one Kitty graphics APC body (without the leading G). Only direct
|
||||
* static images are accepted. The returned image owns decoded RGBA pixels.
|
||||
* static images are accepted. The returned image retains immutable RGBA
|
||||
* pixels for the lifetime of its placement.
|
||||
*/
|
||||
struct image *
|
||||
kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
@@ -609,8 +709,9 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
u_char *uncompressed;
|
||||
size_t controllen, payloadlen, decodedlen;
|
||||
uLongf uncompressedlen;
|
||||
u_int sx, sy;
|
||||
struct image *im;
|
||||
u_int sx, sy, cell_width, cell_height;
|
||||
uint64_t canvas_width, canvas_height;
|
||||
struct image *im = NULL, *source;
|
||||
|
||||
if (kc == NULL) {
|
||||
kc = xcalloc(1, sizeof *kc);
|
||||
@@ -650,9 +751,16 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
}
|
||||
kc->transfer = NULL;
|
||||
if (ks->action == 'p') {
|
||||
im = kitty_source_get(kc, ks->image_id);
|
||||
*status = (im == NULL ? KITTY_PARSE_MISSING :
|
||||
KITTY_PARSE_OK);
|
||||
source = kitty_source_get(kc, ks->image_id);
|
||||
if (source == NULL) {
|
||||
*status = KITTY_PARSE_MISSING;
|
||||
im = NULL;
|
||||
} else {
|
||||
im = kitty_place_image(source, ks, xpixel, ypixel);
|
||||
image_free(source->id);
|
||||
if (im != NULL)
|
||||
*status = KITTY_PARSE_OK;
|
||||
}
|
||||
kitty_state_free(ks);
|
||||
return (im);
|
||||
}
|
||||
@@ -717,21 +825,30 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
if (pixels == NULL)
|
||||
goto fail;
|
||||
|
||||
image_size_in_cells(ks->width, ks->height, xpixel, ypixel, &sx, &sy);
|
||||
if (ks->columns != 0)
|
||||
sx = ks->columns;
|
||||
if (ks->rows != 0)
|
||||
sy = ks->rows;
|
||||
im = image_create(ks->width, ks->height, sx, sy, pixels);
|
||||
if (im == NULL)
|
||||
free(pixels);
|
||||
cell_width = (xpixel == 0 ? 8 : xpixel);
|
||||
cell_height = (ypixel == 0 ? 16 : ypixel);
|
||||
image_size_in_cells(ks->width, ks->height, cell_width, cell_height,
|
||||
&sx, &sy);
|
||||
canvas_width = (uint64_t)sx * cell_width;
|
||||
canvas_height = (uint64_t)sy * cell_height;
|
||||
if (canvas_width > UINT_MAX || canvas_height > UINT_MAX)
|
||||
source = NULL;
|
||||
else
|
||||
source = image_create(ks->width, ks->height, canvas_width,
|
||||
canvas_height, sx, sy, pixels);
|
||||
if (source == NULL)
|
||||
free(pixels);
|
||||
else {
|
||||
*status = KITTY_PARSE_OK;
|
||||
if (im != NULL && ks->action != 'q')
|
||||
kitty_source_set(kc, ks->image_id, im);
|
||||
if (im != NULL && (ks->action == 'q' || ks->action == 't')) {
|
||||
image_free(im->id);
|
||||
im = NULL;
|
||||
if (ks->action != 'q')
|
||||
kitty_source_set(kc, ks->image_id, source);
|
||||
if (ks->action == 'T') {
|
||||
im = kitty_place_image(source, ks, xpixel, ypixel);
|
||||
if (im == NULL)
|
||||
*status = KITTY_PARSE_ERROR;
|
||||
} else
|
||||
im = NULL;
|
||||
image_free(source->id);
|
||||
}
|
||||
kitty_state_free(ks);
|
||||
return (im);
|
||||
|
||||
88
image.c
88
image.c
@@ -209,6 +209,38 @@ image_find(u_int id)
|
||||
return (RB_FIND(images, &images, &find));
|
||||
}
|
||||
|
||||
static struct image *
|
||||
image_create1(u_int width, u_int height, u_int canvas_width,
|
||||
u_int canvas_height, u_int sx, u_int sy, size_t stride, u_char *pixels)
|
||||
{
|
||||
struct image *im;
|
||||
|
||||
im = xcalloc(1, sizeof *im);
|
||||
do {
|
||||
if (++image_next_id == 0)
|
||||
image_next_id++;
|
||||
im->id = image_next_id;
|
||||
} while (image_find(im->id) != NULL);
|
||||
|
||||
im->references = 1;
|
||||
im->source_id = im->id;
|
||||
im->width = width;
|
||||
im->height = height;
|
||||
im->canvas_width = canvas_width;
|
||||
im->canvas_height = canvas_height;
|
||||
im->sx = sx;
|
||||
im->sy = sy;
|
||||
im->stride = stride;
|
||||
im->size = (size_t)width * height * 4;
|
||||
im->pixels = pixels;
|
||||
|
||||
RB_INSERT(images, &images, im);
|
||||
log_debug("%s: image %u is %ux%u pixels on %ux%u canvas, "
|
||||
"%ux%u cells", __func__, im->id, width, height, canvas_width,
|
||||
canvas_height, sx, sy);
|
||||
return (im);
|
||||
}
|
||||
|
||||
struct image *
|
||||
image_create(u_int width, u_int height, u_int canvas_width,
|
||||
u_int canvas_height, u_int sx, u_int sy, u_char *pixels)
|
||||
@@ -222,29 +254,32 @@ image_create(u_int width, u_int height, u_int canvas_width,
|
||||
return (NULL);
|
||||
if ((uint64_t)sx * sy > SIZE_MAX / sizeof *im->cells)
|
||||
return (NULL);
|
||||
im = image_create1(width, height, canvas_width, canvas_height, sx, sy,
|
||||
(size_t)width * 4, pixels);
|
||||
return (im);
|
||||
}
|
||||
|
||||
im = xcalloc(1, sizeof *im);
|
||||
do {
|
||||
if (++image_next_id == 0)
|
||||
image_next_id++;
|
||||
im->id = image_next_id;
|
||||
} while (image_find(im->id) != NULL);
|
||||
/* Create an immutable rectangular view without copying its source pixels. */
|
||||
struct image *
|
||||
image_create_view(struct image *source, u_int x, u_int y, u_int width,
|
||||
u_int height, u_int canvas_width, u_int canvas_height, u_int sx, u_int sy)
|
||||
{
|
||||
struct image *im;
|
||||
|
||||
im->references = 1;
|
||||
im->width = width;
|
||||
im->height = height;
|
||||
im->canvas_width = canvas_width;
|
||||
im->canvas_height = canvas_height;
|
||||
im->sx = sx;
|
||||
im->sy = sy;
|
||||
im->stride = (size_t)width * 4;
|
||||
im->size = im->stride * height;
|
||||
im->pixels = pixels;
|
||||
if (source == NULL || x >= source->width || y >= source->height ||
|
||||
width == 0 || width > source->width - x || height == 0 ||
|
||||
height > source->height - y || canvas_width < width ||
|
||||
canvas_height < height || sx == 0 || sy == 0)
|
||||
return (NULL);
|
||||
if ((uint64_t)sx * sy > SIZE_MAX / sizeof *im->cells)
|
||||
return (NULL);
|
||||
|
||||
RB_INSERT(images, &images, im);
|
||||
log_debug("%s: image %u is %ux%u pixels on %ux%u canvas, "
|
||||
"%ux%u cells", __func__, im->id, width, height, canvas_width,
|
||||
canvas_height, sx, sy);
|
||||
im = image_create1(width, height, canvas_width, canvas_height, sx, sy,
|
||||
source->stride, source->pixels + (size_t)y * source->stride +
|
||||
(size_t)x * 4);
|
||||
im->parent_id = source->id;
|
||||
im->source_id = source->source_id;
|
||||
image_ref(source->id);
|
||||
return (im);
|
||||
}
|
||||
|
||||
@@ -272,7 +307,10 @@ image_free(u_int id)
|
||||
|
||||
log_debug("%s: freeing image %u", __func__, id);
|
||||
RB_REMOVE(images, &images, im);
|
||||
free(im->pixels);
|
||||
if (im->parent_id == 0)
|
||||
free(im->pixels);
|
||||
else
|
||||
image_free(im->parent_id);
|
||||
free(im->cells);
|
||||
free(im);
|
||||
}
|
||||
@@ -413,13 +451,17 @@ image_clear(struct screen_write_ctx *ctx, u_int id)
|
||||
struct screen *s = ctx->s;
|
||||
struct grid *gd = s->grid;
|
||||
struct grid_cell gc;
|
||||
struct image *im;
|
||||
u_int x, y;
|
||||
|
||||
for (y = 0; y < gd->hsize + gd->sy; y++) {
|
||||
for (x = 0; x < gd->sx; x++) {
|
||||
grid_get_cell(gd, x, y, &gc);
|
||||
if ((gc.flags & GRID_FLAG_IMAGE) &&
|
||||
(id == 0 || gc.image_id == id)) {
|
||||
im = NULL;
|
||||
if (gc.flags & GRID_FLAG_IMAGE)
|
||||
im = image_find(gc.image_id);
|
||||
if (im != NULL && (id == 0 || gc.image_id == id ||
|
||||
im->source_id == id)) {
|
||||
if (y >= gd->hsize)
|
||||
image_damage_area(ctx, x, y - gd->hsize,
|
||||
1, 1);
|
||||
|
||||
@@ -96,4 +96,22 @@ sleep 1
|
||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
||||
grep -q '^#=' $TMP || exit 1
|
||||
|
||||
# A retained Kitty image may be placed repeatedly using source rectangles.
|
||||
# Crop the green and white right column from a red/green/blue/white image.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
$TMUX2 new-session -d -x 10 -y 4 "
|
||||
printf '\033_Ga=t,q=2,f=32,s=2,v=2,i=10;/wAA/wD/AP8AAP///////w==\033\\'
|
||||
printf '\033_Ga=p,q=2,i=10,x=1,y=0,w=1,h=2,c=1,r=2\033\\'
|
||||
sleep 10" || exit 1
|
||||
$TMUX2 set -g status off || exit 1
|
||||
$TMUX new-session -d -x 10 -y 4 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX send-keys -l "$TMUX2 attach-session" || exit 1
|
||||
$TMUX send-keys Enter || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E1 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "*" ] || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = "@" ] || exit 1
|
||||
|
||||
exit 0
|
||||
|
||||
4
tmux.h
4
tmux.h
@@ -1059,6 +1059,8 @@ struct image_cell {
|
||||
struct image {
|
||||
u_int id;
|
||||
u_int references;
|
||||
u_int parent_id;
|
||||
u_int source_id;
|
||||
u_int width;
|
||||
u_int height;
|
||||
u_int canvas_width;
|
||||
@@ -4259,6 +4261,8 @@ char *regsub(const char *, const char *, const char *, int);
|
||||
/* image.c */
|
||||
struct image *image_create(u_int, u_int, u_int, u_int, u_int, u_int,
|
||||
u_char *);
|
||||
struct image *image_create_view(struct image *, u_int, u_int, u_int,
|
||||
u_int, u_int, u_int, u_int, u_int);
|
||||
struct image *image_find(u_int);
|
||||
void image_ref(u_int);
|
||||
void image_free(u_int);
|
||||
|
||||
Reference in New Issue
Block a user