Some of SIXEL output path.

This commit is contained in:
Nicholas Marriott
2026-10-14 11:04:00 +01:00
parent 9a37890133
commit d68e56f130
7 changed files with 307 additions and 9 deletions

View File

@@ -34,7 +34,14 @@ static const struct utf8_data fallback_half_lower = {
"\342\226\204", 3, 3, 1 "\342\226\204", 3, 3, 1
}; };
/* Return the average colour and brightness of an area. */ /* Fallback output is available on every terminal. */
int
fallback_select(__unused struct tty *tty)
{
return (1);
}
/* Get the average colour and brightness of an area. */
static int static int
fallback_sample(struct image *im, uint64_t px, uint64_t py, uint64_t pw, fallback_sample(struct image *im, uint64_t px, uint64_t py, uint64_t pw,
uint64_t ph, u_char *brightness) uint64_t ph, u_char *brightness)
@@ -142,7 +149,7 @@ fallback_get_cell(struct tty *tty, struct image *im, u_int x, u_int y,
out->attr |= GRID_ATTR_BRIGHT; out->attr |= GRID_ATTR_BRIGHT;
} }
/* Return a text cell for an image grid cell, if there is one. */ /* Get a text cell for an image grid cell, if there is one. */
const struct grid_cell * const struct grid_cell *
fallback_map_cell(struct tty *tty, struct grid *gd, u_int x, u_int y, fallback_map_cell(struct tty *tty, struct grid *gd, u_int x, u_int y,
const struct grid_cell *gc, struct grid_cell *out, int *empty) const struct grid_cell *gc, struct grid_cell *out, int *empty)

View File

@@ -83,6 +83,105 @@ struct image_recover_row {
u_int source_y; u_int source_y;
}; };
/* A backend to draw an image. */
struct image_backend {
const char *name;
int flags;
int (*select)(struct tty *);
void (*draw_rect)(struct tty *, struct image_rect *);
void (*start_draw)(struct tty *, u_int, u_int, u_int, u_int);
void (*flush)(struct tty *);
void (*free)(struct tty *);
};
/* Backend definitions. */
static struct image_backend image_backend_fallback = {
"fallback",
IMAGE_BACKEND_SCROLLS,
fallback_select,
NULL,
NULL,
NULL,
NULL
};
static struct image_backend image_backend_sixel = {
"sixel",
IMAGE_BACKEND_GRAPHICAL,
sixel_select,
sixel_draw_rect,
sixel_start_draw,
sixel_flush_pending,
sixel_free_pending
};
/* Backends in order of preference. */
static struct image_backend *image_backends[] = {
&image_backend_sixel,
&image_backend_fallback
};
/* Update the image backend. */
int
image_tty_update(struct tty *tty)
{
struct image_backend *backend;
u_int i;
for (i = 0; i < nitems(image_backends); i++) {
backend = image_backends[i];
if (backend->select(tty))
break;
}
if (tty->image_backend == backend)
return (0);
if (tty->image_backend != NULL && tty->image_backend->free != NULL)
tty->image_backend->free(tty);
tty->image_data = NULL;
tty->image_backend = backend;
return (1);
}
/* Clear old image pixels before redrawing a terminal area. */
void
image_redraw_start(struct tty *tty, u_int x, u_int y, u_int w, u_int h)
{
image_tty_update(tty);
if (tty->image_backend->start_draw != NULL)
tty->image_backend->start_draw(tty, x, y, w, h);
}
/* Write pending image output to the terminal. */
void
image_draw_flush(struct tty *tty)
{
image_tty_update(tty);
if (tty->image_backend->flush != NULL)
tty->image_backend->flush(tty);
}
/* Discard image backend state after a terminal size change. */
void
image_tty_size_changed(struct tty *tty)
{
if (!image_tty_update(tty)) {
if (tty->image_backend->free != NULL)
tty->image_backend->free(tty);
}
}
/* Free image backend state for a terminal. */
void
image_tty_free(struct tty *tty)
{
if (tty->image_backend != NULL) {
if (tty->image_backend->free != NULL)
tty->image_backend->free(tty);
tty->image_backend = NULL;
tty->image_data = NULL;
}
}
/* Get the placement store for a grid. */ /* Get the placement store for a grid. */
static struct image_store * static struct image_store *
image_store_get(struct grid *gd) image_store_get(struct grid *gd)
@@ -401,6 +500,7 @@ image_grid_copy_area(struct grid *dst, u_int dx, u_int dy, struct grid *src,
struct image_copy_map *maps = NULL; struct image_copy_map *maps = NULL;
struct image_placement *plmt; struct image_placement *plmt;
struct image_line *src_line, *dst_line; struct image_line *src_line, *dst_line;
struct grid_line *gl;
struct image_span *span; struct image_span *span;
u_int nmaps = 0, row, start, end, span_end; u_int nmaps = 0, row, start, end, span_end;
@@ -428,8 +528,10 @@ image_grid_copy_area(struct grid *dst, u_int dx, u_int dy, struct grid *src,
continue; continue;
if (span_end > end) if (span_end > end)
span_end = end; span_end = end;
if (dst_line == NULL) if (dst_line == NULL) {
dst_line = image_line_get(&dst->linedata[dy + row]); gl = &dst->linedata[dy + row];
dst_line = image_line_get(gl);
}
plmt = image_copy_placement(dst, span->placement, &maps, plmt = image_copy_placement(dst, span->placement, &maps,
&nmaps); &nmaps);
plmt->recover_from = 0; plmt->recover_from = 0;

View File

@@ -100,6 +100,23 @@ struct sixel_plane {
char *stream; char *stream;
}; };
/* Adjacent placement rows buffered for one SIXEL output sequence. */
struct sixel_pending {
struct image *image;
/* Source in image in cells. */
u_int src_x;
u_int src_y;
/* Size in cells. */
u_int w;
u_int h;
/* Destination on terminal in cells. */
u_int dst_x;
u_int dst_y;
};
/* RGB histogram entry. */ /* RGB histogram entry. */
struct sixel_hgram { struct sixel_hgram {
u_int count; u_int count;
@@ -1272,3 +1289,115 @@ sixel_from_image(struct image *im, u_int x, u_int y, u_int sx, u_int sy,
sixel_free(si); sixel_free(si);
return (data); return (data);
} }
/* Return whether the terminal supports SIXEL. */
int
sixel_select(struct tty *tty)
{
if (tty->term != NULL &&
(tty->term->flags & TERM_SIXEL) &&
tty->xpixel != 0 &&
tty->ypixel != 0)
return (1);
return (0);
}
/* Get pending SIXEL rows for a terminal. */
static struct sixel_pending *
sixel_get_pending(struct tty *tty)
{
struct sixel_pending *sp = tty->image_data;
if (sp == NULL) {
sp = xcalloc(1, sizeof *sp);
tty->image_data = sp;
}
return (sp);
}
/* Free pending SIXEL rows for a terminal. */
void
sixel_free_pending(struct tty *tty)
{
struct sixel_pending *sp = tty->image_data;
if (sp == NULL)
return;
if (sp->image != NULL)
image_free(sp->image);
free(sp);
tty->image_data = NULL;
}
/* Write the rows held in the pending run as a single SIXEL. */
void
sixel_flush_pending(struct tty *tty)
{
struct sixel_pending *sp = tty->image_data;
struct image *im;
char *data;
size_t size;
if (sp == NULL || sp->image == NULL)
return;
im = sp->image;
sp->image = NULL;
data = sixel_from_image(im, sp->src_x, sp->src_y, sp->w, sp->h,
tty->xpixel, tty->ypixel, &size);
if (data != NULL) {
tty_region_off(tty);
tty_margin_off(tty);
tty_cursor(tty, sp->dst_x, sp->dst_y);
tty->flags |= TTY_NOBLOCK;
tty_putn(tty, data, size, 0);
tty->cx = tty->cy = UINT_MAX;
free(data);
}
image_free(im);
}
/* Queue adjacent placement rows for one SIXEL output sequence. */
void
sixel_draw_rect(struct tty *tty, struct image_rect *rect)
{
struct sixel_pending *sp = sixel_get_pending(tty);
if (sp->image == rect->image &&
sp->src_x == rect->src_x &&
sp->w == rect->sx &&
sp->dst_x == rect->dst_x &&
sp->src_y + sp->h == rect->src_y &&
sp->dst_y + sp->h == rect->dst_y) {
sp->h += rect->sy;
return;
}
image_ref(rect->image);
sixel_flush_pending(tty);
sp->image = rect->image;
sp->src_x = rect->src_x;
sp->src_y = rect->src_y;
sp->w = rect->sx;
sp->h = rect->sy;
sp->dst_x = rect->dst_x;
sp->dst_y = rect->dst_y;
}
/* Remove old SIXEL pixels before drawing an image area. */
void
sixel_start_draw(struct tty *tty, u_int x, u_int y, u_int sx, u_int sy)
{
u_int yy;
for (yy = y; yy < y + sy; yy++) {
tty_cursor(tty, x, yy);
if (tty_term_has(tty->term, TTYC_ECH))
tty_putcode_i(tty, TTYC_ECH, sx);
else
tty_repeat_space(tty, sx);
}
}

View File

@@ -21,12 +21,13 @@
#define TMUX_IMAGE_H #define TMUX_IMAGE_H
#ifdef ENABLE_IMAGES #ifdef ENABLE_IMAGES
#error image support is not ready yet //#error image support is not ready yet
struct grid; struct grid;
struct grid_cell; struct grid_cell;
struct grid_line; struct grid_line;
struct image; struct image;
struct image_backend;
struct image_line; struct image_line;
struct image_store; struct image_store;
struct screen; struct screen;
@@ -37,6 +38,12 @@ struct tty;
/* Maximum image size in bytes. */ /* Maximum image size in bytes. */
#define IMAGE_SIZE_LIMIT (64 * 1024 * 1024) #define IMAGE_SIZE_LIMIT (64 * 1024 * 1024)
/* Backend draws images as graphics. */
#define IMAGE_BACKEND_GRAPHICAL 0x1
/* Backend images move with terminal scrolling. */
#define IMAGE_BACKEND_SCROLLS 0x2
/* View of all or part of an image. */ /* View of all or part of an image. */
struct image_view { struct image_view {
/* Position and size in the image, in pixels. */ /* Position and size in the image, in pixels. */
@@ -60,6 +67,23 @@ struct image_view {
u_int sy; u_int sy;
}; };
/* A part of an image to draw. */
struct image_rect {
struct image *image;
/* Size in cells. */
u_int sx;
u_int sy;
/* Source in image in cells. */
u_int src_x;
u_int src_y;
/* Destination on terminal in cells. */
u_int dst_x;
u_int dst_y;
};
/* image.c */ /* image.c */
struct image *image_create(u_int, u_int, u_int, u_int, u_int, u_int, struct image *image_create(u_int, u_int, u_int, u_int, u_int, u_int,
u_char *); u_char *);
@@ -102,13 +126,20 @@ void image_damage_screen(struct screen_write_ctx *, u_int, u_int,
void image_write(struct screen_write_ctx *, struct image *, u_int); void image_write(struct screen_write_ctx *, struct image *, u_int);
int image_grid_get_at(struct grid *, u_int, u_int, struct image **, int image_grid_get_at(struct grid *, u_int, u_int, struct image **,
u_int *, u_int *); u_int *, u_int *);
void image_redraw_start(struct tty *, u_int, u_int, u_int, u_int);
void image_draw_flush(struct tty *);
int image_tty_update(struct tty *);
void image_tty_size_changed(struct tty *);
void image_tty_free(struct tty *);
/* image-fallback.c */ /* image-fallback.c */
int fallback_select(struct tty *);
const struct grid_cell *fallback_map_cell(struct tty *, struct grid *, u_int, const struct grid_cell *fallback_map_cell(struct tty *, struct grid *, u_int,
u_int, const struct grid_cell *, struct grid_cell *, int *); u_int, const struct grid_cell *, struct grid_cell *, int *);
/* image-sixel.c */ /* image-sixel.c */
#define SIXEL_COLOUR_REGISTERS 1024 #define SIXEL_COLOUR_REGISTERS 1024
int sixel_select(struct tty *);
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,
u_int); u_int);
void sixel_free(struct sixel_image *); void sixel_free(struct sixel_image *);
@@ -118,6 +149,10 @@ void sixel_write(struct screen_write_ctx *, struct sixel_image *,
u_int); u_int);
char *sixel_from_image(struct image *, u_int, u_int, u_int, u_int, char *sixel_from_image(struct image *, u_int, u_int, u_int, u_int,
u_int, u_int, size_t *); u_int, u_int, size_t *);
void sixel_draw_rect(struct tty *, struct image_rect *);
void sixel_start_draw(struct tty *, u_int, u_int, u_int, u_int);
void sixel_free_pending(struct tty *);
void sixel_flush_pending(struct tty *);
#endif /* ENABLE_IMAGES */ #endif /* ENABLE_IMAGES */

7
tmux.h
View File

@@ -1852,6 +1852,11 @@ struct tty {
struct event key_timer; struct event key_timer;
struct tty_key *key_tree; struct tty_key *key_tree;
#ifdef ENABLE_IMAGES
struct image_backend *image_backend;
void *image_data;
#endif
}; };
/* Terminal command context. */ /* Terminal command context. */
@@ -2941,7 +2946,7 @@ void tty_cell(struct tty *, const struct grid_cell *,
const struct tty_style_ctx *); const struct tty_style_ctx *);
int tty_init(struct tty *, struct client *); int tty_init(struct tty *, struct client *);
void tty_resize(struct tty *); void tty_resize(struct tty *);
void tty_set_size(struct tty *, u_int, u_int, u_int, u_int); int tty_set_size(struct tty *, u_int, u_int, u_int, u_int);
void tty_invalidate(struct tty *); void tty_invalidate(struct tty *);
void tty_start_tty(struct tty *); void tty_start_tty(struct tty *);
void tty_send_requests(struct tty *); void tty_send_requests(struct tty *);

View File

@@ -1914,7 +1914,8 @@ tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size,
return (0); return (0);
/* Window size in characters. */ /* Window size in characters. */
tty_set_size(tty, sx, sy, tty->xpixel, tty->ypixel); if (tty_set_size(tty, sx, sy, tty->xpixel, tty->ypixel))
server_redraw_client(c);
return (0); return (0);
} else if (sscanf(tmp, "4;%u;%u", &ypixel, &xpixel) == 2) { } else if (sscanf(tmp, "4;%u;%u", &ypixel, &xpixel) == 2) {
*size = end + 1; *size = end + 1;
@@ -1924,7 +1925,8 @@ tty_keys_winsz(struct tty *tty, const char *buf, size_t len, size_t *size,
/* Window size in pixels. */ /* Window size in pixels. */
char_x = (xpixel && tty->sx) ? xpixel / tty->sx : 0; char_x = (xpixel && tty->sx) ? xpixel / tty->sx : 0;
char_y = (ypixel && tty->sy) ? ypixel / tty->sy : 0; char_y = (ypixel && tty->sy) ? ypixel / tty->sy : 0;
tty_set_size(tty, tty->sx, tty->sy, char_x, char_y); if (tty_set_size(tty, tty->sx, tty->sy, char_x, char_y))
server_redraw_client(c);
tty_invalidate(tty); tty_invalidate(tty);
tty->flags &= ~TTY_WINSIZEQUERY; tty->flags &= ~TTY_WINSIZEQUERY;

20
tty.c
View File

@@ -155,13 +155,22 @@ tty_resize(struct tty *tty)
tty_invalidate(tty); tty_invalidate(tty);
} }
void int
tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel) tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel)
{ {
if (tty->sx == sx &&
tty->sy == sy &&
tty->xpixel == xpixel &&
tty->ypixel == ypixel)
return (0);
tty->sx = sx; tty->sx = sx;
tty->sy = sy; tty->sy = sy;
tty->xpixel = xpixel; tty->xpixel = xpixel;
tty->ypixel = ypixel; tty->ypixel = ypixel;
#ifdef ENABLE_IMAGES
image_tty_size_changed(tty);
#endif
return (1);
} }
static void static void
@@ -523,6 +532,9 @@ tty_close(struct tty *tty)
tty_stop_tty(tty); tty_stop_tty(tty);
if (tty->flags & TTY_OPENED) { if (tty->flags & TTY_OPENED) {
#ifdef ENABLE_IMAGES
image_tty_free(tty);
#endif
evbuffer_free(tty->in); evbuffer_free(tty->in);
event_del(&tty->event_in); event_del(&tty->event_in);
evbuffer_free(tty->out); evbuffer_free(tty->out);
@@ -539,6 +551,9 @@ void
tty_free(struct tty *tty) tty_free(struct tty *tty)
{ {
tty_close(tty); tty_close(tty);
#ifdef ENABLE_IMAGES
image_tty_free(tty);
#endif
} }
void void
@@ -548,6 +563,9 @@ tty_update_features(struct tty *tty)
if (tty_apply_features(tty->term)) if (tty_apply_features(tty->term))
tty_term_apply_overrides(tty->term); tty_term_apply_overrides(tty->term);
#ifdef ENABLE_IMAGES
image_tty_update(tty);
#endif
if (tty_use_margin(tty)) if (tty_use_margin(tty))
tty_putcode(tty, TTYC_ENMG); tty_putcode(tty, TTYC_ENMG);