mirror of
https://github.com/tmux/tmux.git
synced 2026-08-29 10:01:33 +00:00
The two Chawan symptoms now have matching fixes:
- Layout corruption: C=1 is respected.
- Lingering remnants: tmux now tracks Kitty’s (image ID, placement ID) identity, replaces moved placements, and honors
placement-specific d=i,...,p=... deletion.
This commit is contained in:
136
image-kitty.c
136
image-kitty.c
@@ -81,6 +81,7 @@ struct kitty_state {
|
||||
u_int columns;
|
||||
u_int rows;
|
||||
u_int image_id;
|
||||
u_int placement_id;
|
||||
u_int quiet;
|
||||
int no_cursor;
|
||||
int virtual;
|
||||
@@ -91,10 +92,17 @@ struct kitty_state {
|
||||
size_t encodedlen;
|
||||
};
|
||||
|
||||
struct kitty_placement {
|
||||
u_int placement_id;
|
||||
u_int server_id;
|
||||
struct kitty_placement *next;
|
||||
};
|
||||
|
||||
struct kitty_source {
|
||||
u_int app_id;
|
||||
u_int server_id;
|
||||
u_int virtual_id;
|
||||
struct kitty_placement *placements;
|
||||
struct kitty_source *next;
|
||||
};
|
||||
|
||||
@@ -441,6 +449,7 @@ kitty_control(struct kitty_state *ks, const u_char *buf, size_t len)
|
||||
case 'c':
|
||||
case 'r':
|
||||
case 'i':
|
||||
case 'p':
|
||||
case 'q':
|
||||
case 'm':
|
||||
case 'S':
|
||||
@@ -460,6 +469,7 @@ kitty_control(struct kitty_state *ks, const u_char *buf, size_t len)
|
||||
case 'c': ks->columns = number; break;
|
||||
case 'r': ks->rows = number; break;
|
||||
case 'i': ks->image_id = number; break;
|
||||
case 'p': ks->placement_id = number; break;
|
||||
case 'q': ks->quiet = number; break;
|
||||
case 'm': ks->more = (number != 0); break;
|
||||
case 'S': ks->data_size = number; break;
|
||||
@@ -481,6 +491,29 @@ kitty_state_free(struct kitty_state *ks)
|
||||
free(ks);
|
||||
}
|
||||
|
||||
static void
|
||||
kitty_placements_free(struct kitty_source *source)
|
||||
{
|
||||
struct kitty_placement *placement, *next;
|
||||
|
||||
for (placement = source->placements; placement != NULL;
|
||||
placement = next) {
|
||||
next = placement->next;
|
||||
image_free(placement->server_id);
|
||||
free(placement);
|
||||
}
|
||||
source->placements = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
kitty_placements_free_all(struct kitty_context *kc)
|
||||
{
|
||||
struct kitty_source *source;
|
||||
|
||||
for (source = kc->sources; source != NULL; source = source->next)
|
||||
kitty_placements_free(source);
|
||||
}
|
||||
|
||||
void
|
||||
kitty_free_state(void *state)
|
||||
{
|
||||
@@ -492,6 +525,7 @@ kitty_free_state(void *state)
|
||||
kitty_state_free(kc->transfer);
|
||||
for (source = kc->sources; source != NULL; source = next) {
|
||||
next = source->next;
|
||||
kitty_placements_free(source);
|
||||
if (source->virtual_id != 0)
|
||||
image_free(source->virtual_id);
|
||||
image_free(source->server_id);
|
||||
@@ -512,13 +546,14 @@ kitty_source_find(struct kitty_context *kc, u_int id)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
static u_int
|
||||
kitty_source_set(struct kitty_context *kc, u_int id, struct image *im)
|
||||
{
|
||||
struct kitty_source *source;
|
||||
u_int old_id = 0;
|
||||
|
||||
if (id == 0)
|
||||
return;
|
||||
return (0);
|
||||
source = kitty_source_find(kc, id);
|
||||
if (source == NULL) {
|
||||
source = xcalloc(1, sizeof *source);
|
||||
@@ -526,6 +561,8 @@ kitty_source_set(struct kitty_context *kc, u_int id, struct image *im)
|
||||
source->next = kc->sources;
|
||||
kc->sources = source;
|
||||
} else {
|
||||
old_id = source->server_id;
|
||||
kitty_placements_free(source);
|
||||
if (source->virtual_id != 0) {
|
||||
image_free(source->virtual_id);
|
||||
source->virtual_id = 0;
|
||||
@@ -534,6 +571,65 @@ kitty_source_set(struct kitty_context *kc, u_int id, struct image *im)
|
||||
}
|
||||
image_ref(im->id);
|
||||
source->server_id = im->id;
|
||||
return (old_id);
|
||||
}
|
||||
|
||||
static u_int
|
||||
kitty_placement_set(struct kitty_context *kc, u_int image_id,
|
||||
u_int placement_id, struct image *im)
|
||||
{
|
||||
struct kitty_source *source;
|
||||
struct kitty_placement *placement;
|
||||
u_int old_id;
|
||||
|
||||
if (placement_id == 0)
|
||||
return (0);
|
||||
source = kitty_source_find(kc, image_id);
|
||||
if (source == NULL)
|
||||
return (0);
|
||||
for (placement = source->placements; placement != NULL;
|
||||
placement = placement->next) {
|
||||
if (placement->placement_id == placement_id)
|
||||
break;
|
||||
}
|
||||
if (placement == NULL) {
|
||||
placement = xcalloc(1, sizeof *placement);
|
||||
placement->placement_id = placement_id;
|
||||
placement->next = source->placements;
|
||||
source->placements = placement;
|
||||
}
|
||||
old_id = placement->server_id;
|
||||
image_ref(im->id);
|
||||
placement->server_id = im->id;
|
||||
if (old_id != 0)
|
||||
image_free(old_id);
|
||||
return (old_id);
|
||||
}
|
||||
|
||||
static struct image *
|
||||
kitty_placement_remove(struct kitty_context *kc, u_int image_id,
|
||||
u_int placement_id)
|
||||
{
|
||||
struct kitty_source *source;
|
||||
struct kitty_placement **pp, *placement;
|
||||
struct image *im;
|
||||
|
||||
source = kitty_source_find(kc, image_id);
|
||||
if (source == NULL)
|
||||
return (NULL);
|
||||
for (pp = &source->placements; (placement = *pp) != NULL;
|
||||
pp = &placement->next) {
|
||||
if (placement->placement_id != placement_id)
|
||||
continue;
|
||||
im = image_find(placement->server_id);
|
||||
if (im != NULL)
|
||||
image_ref(im->id);
|
||||
*pp = placement->next;
|
||||
image_free(placement->server_id);
|
||||
free(placement);
|
||||
return (im);
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -563,6 +659,7 @@ kitty_source_remove(struct kitty_context *kc, u_int id)
|
||||
if (im != NULL)
|
||||
image_ref(im->id);
|
||||
*pp = source->next;
|
||||
kitty_placements_free(source);
|
||||
if (source->virtual_id != 0)
|
||||
image_free(source->virtual_id);
|
||||
image_free(source->server_id);
|
||||
@@ -733,7 +830,8 @@ kitty_place_image(struct image *source, struct kitty_state *ks, u_int xpixel,
|
||||
*/
|
||||
struct image *
|
||||
kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
u_int ypixel, u_int *image_id, u_int *quiet, char *action, int *status)
|
||||
u_int ypixel, u_int *image_id, u_int *replace_id, u_int *quiet,
|
||||
char *action, int *status)
|
||||
{
|
||||
struct kitty_context *kc = *state;
|
||||
struct kitty_state *ks;
|
||||
@@ -745,12 +843,14 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
u_int sx, sy, cell_width, cell_height;
|
||||
uint64_t canvas_width, canvas_height;
|
||||
struct image *im = NULL, *source;
|
||||
struct kitty_source *stored;
|
||||
|
||||
if (kc == NULL) {
|
||||
kc = xcalloc(1, sizeof *kc);
|
||||
*state = kc;
|
||||
}
|
||||
*image_id = 0;
|
||||
*replace_id = 0;
|
||||
*quiet = 0;
|
||||
*action = '\0';
|
||||
*status = KITTY_PARSE_ERROR;
|
||||
@@ -802,18 +902,33 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
} else {
|
||||
im = kitty_place_image(source, ks, xpixel, ypixel);
|
||||
image_free(source->id);
|
||||
if (im != NULL)
|
||||
if (im != NULL) {
|
||||
*replace_id = kitty_placement_set(kc, ks->image_id,
|
||||
ks->placement_id, im);
|
||||
*status = KITTY_PARSE_OK;
|
||||
}
|
||||
}
|
||||
kitty_state_free(ks);
|
||||
return (im);
|
||||
}
|
||||
if (ks->action == 'd') {
|
||||
if (ks->delete == 'a' || ks->delete == 'A')
|
||||
if (ks->delete == 'a' || ks->delete == 'A') {
|
||||
kitty_placements_free_all(kc);
|
||||
im = NULL;
|
||||
else if (ks->delete == 'i')
|
||||
im = kitty_source_get(kc, ks->image_id);
|
||||
else if (ks->delete == 'I')
|
||||
} else if (ks->delete == 'i') {
|
||||
if (ks->placement_id != 0)
|
||||
im = kitty_placement_remove(kc, ks->image_id,
|
||||
ks->placement_id);
|
||||
else {
|
||||
source = kitty_source_get(kc, ks->image_id);
|
||||
if (source != NULL) {
|
||||
stored = kitty_source_find(kc,
|
||||
ks->image_id);
|
||||
kitty_placements_free(stored);
|
||||
}
|
||||
im = source;
|
||||
}
|
||||
} else if (ks->delete == 'I')
|
||||
im = kitty_source_remove(kc, ks->image_id);
|
||||
else
|
||||
goto fail;
|
||||
@@ -885,11 +1000,14 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
else {
|
||||
*status = KITTY_PARSE_OK;
|
||||
if (ks->action != 'q')
|
||||
kitty_source_set(kc, ks->image_id, source);
|
||||
*replace_id = kitty_source_set(kc, ks->image_id, source);
|
||||
if (ks->action == 'T' && !ks->virtual) {
|
||||
im = kitty_place_image(source, ks, xpixel, ypixel);
|
||||
if (im == NULL)
|
||||
*status = KITTY_PARSE_ERROR;
|
||||
else if (ks->placement_id != 0)
|
||||
(void)kitty_placement_set(kc, ks->image_id,
|
||||
ks->placement_id, im);
|
||||
} else if (ks->virtual) {
|
||||
im = kitty_place_image(source, ks, xpixel, ypixel);
|
||||
if (im == NULL)
|
||||
|
||||
7
input.c
7
input.c
@@ -2817,7 +2817,7 @@ 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, quiet = 0;
|
||||
u_int image_id = 0, replace_id = 0, quiet = 0;
|
||||
char action = '\0';
|
||||
int status;
|
||||
|
||||
@@ -2825,7 +2825,8 @@ input_handle_kitty(struct input_ctx *ictx, const u_char *buf, size_t len)
|
||||
return (0);
|
||||
im = kitty_parse_image(&ictx->kitty_state, buf, len,
|
||||
wp->window->xpixel,
|
||||
wp->window->ypixel, &image_id, &quiet, &action, &status);
|
||||
wp->window->ypixel, &image_id, &replace_id, &quiet, &action,
|
||||
&status);
|
||||
if (status == KITTY_PARSE_MORE)
|
||||
return (1);
|
||||
if (status != KITTY_PARSE_OK) {
|
||||
@@ -2839,6 +2840,8 @@ input_handle_kitty(struct input_ctx *ictx, const u_char *buf, size_t len)
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
if (replace_id != 0)
|
||||
image_clear(sctx, replace_id);
|
||||
if (im != NULL) {
|
||||
if (action == 'd')
|
||||
image_clear(sctx, im->id);
|
||||
|
||||
@@ -192,6 +192,32 @@ $TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP | wc -c)" = 61 ] || exit 1
|
||||
[ -z "$(sed -n 2p $TMP)" ] || exit 1
|
||||
|
||||
# A nonzero Kitty placement ID identifies one placement of an image. Reusing
|
||||
# the same image and placement IDs moves it rather than leaving the old cells.
|
||||
PLACEMENT_WINDOW=$($TMUX2 new-window -dP -F '#{window_id}' "
|
||||
printf '\033_Ga=t,q=2,f=32,s=1,v=1,i=11;/////w==\033\\'
|
||||
printf '\033[2;2H\033_Ga=p,q=2,C=1,i=11,p=7,c=2,r=1\033\\'
|
||||
printf '\033[4;6H\033_Ga=p,q=2,C=1,i=11,p=7,c=2,r=1\033\\'
|
||||
sleep 10") || exit 1
|
||||
$TMUX2 select-window -t"$PLACEMENT_WINDOW" || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ -z "$(sed -n 2p $TMP)" ] || exit 1
|
||||
[ "$(sed -n 4p $TMP)" = " @@" ] || exit 1
|
||||
|
||||
# Deleting one placement ID leaves other placements of the image intact.
|
||||
PLACEMENT_WINDOW=$($TMUX2 new-window -dP -F '#{window_id}' "
|
||||
printf '\033_Ga=t,q=2,f=32,s=1,v=1,i=12;/////w==\033\\'
|
||||
printf '\033[2;2H\033_Ga=p,q=2,C=1,i=12,p=7,c=2,r=1\033\\'
|
||||
printf '\033[4;6H\033_Ga=p,q=2,C=1,i=12,p=8,c=2,r=1\033\\'
|
||||
printf '\033_Ga=d,d=i,q=2,i=12,p=7\033\\'
|
||||
sleep 10") || exit 1
|
||||
$TMUX2 select-window -t"$PLACEMENT_WINDOW" || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ -z "$(sed -n 2p $TMP)" ] || exit 1
|
||||
[ "$(sed -n 4p $TMP)" = " @@" ] || exit 1
|
||||
|
||||
# A weighted median at the maximum channel level must still leave colours on
|
||||
# both sides of the split. This skewed black, grey and white image used to stop
|
||||
# palette generation after one colour instead of producing three.
|
||||
|
||||
2
tmux.h
2
tmux.h
@@ -4307,7 +4307,7 @@ void image_clear(struct screen_write_ctx *, u_int);
|
||||
#define KITTY_PARSE_MORE 1
|
||||
#define KITTY_PARSE_MISSING 2
|
||||
struct image *kitty_parse_image(void **, const u_char *, size_t, u_int,
|
||||
u_int, u_int *, u_int *, char *, int *);
|
||||
u_int, u_int *, u_int *, u_int *, char *, int *);
|
||||
int kitty_placeholder_to_cell(void *, struct grid_cell *,
|
||||
const struct grid_cell *);
|
||||
void kitty_free_state(void *);
|
||||
|
||||
Reference in New Issue
Block a user