Merge branch 'image_merge'

This commit is contained in:
Nicholas Marriott
2026-10-13 21:02:23 +01:00
11 changed files with 776 additions and 587 deletions

View File

@@ -210,6 +210,7 @@ dist_tmux_SOURCES = \
style.c \ style.c \
tmux-protocol.h \ tmux-protocol.h \
tmux.c \ tmux.c \
tmux-image.h \
tmux.h \ tmux.h \
tty-acs.c \ tty-acs.c \
tty-draw.c \ tty-draw.c \
@@ -251,7 +252,7 @@ endif
# Enable image support. # Enable image support.
if ENABLE_IMAGES if ENABLE_IMAGES
dist_tmux_SOURCES += image.c image-sixel.c dist_tmux_SOURCES += image.c image-grid.c image-sixel.c
endif endif
# Add bits for fuzzing if enabled. # Add bits for fuzzing if enabled.

33
grid.c
View File

@@ -308,6 +308,9 @@ grid_free_line(struct grid *gd, u_int py)
{ {
struct grid_line *gl = &gd->linedata[py]; struct grid_line *gl = &gd->linedata[py];
#ifdef ENABLE_IMAGES
image_grid_free_line(gd, gl);
#endif
free(gl->celldata); free(gl->celldata);
free(gl->extddata); free(gl->extddata);
memset(gl, 0, sizeof *gl); memset(gl, 0, sizeof *gl);
@@ -347,6 +350,9 @@ void
grid_destroy(struct grid *gd) grid_destroy(struct grid *gd)
{ {
grid_free_lines(gd, 0, gd->hsize + gd->sy); grid_free_lines(gd, 0, gd->hsize + gd->sy);
#ifdef ENABLE_IMAGES
image_grid_free(gd);
#endif
free(gd->linedata); free(gd->linedata);
free(gd); free(gd);
} }
@@ -692,6 +698,9 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
return; return;
if (grid_check_y(gd, __func__, py + ny - 1) != 0) if (grid_check_y(gd, __func__, py + ny - 1) != 0)
return; return;
#ifdef ENABLE_IMAGES
image_grid_remove(gd, px, py, nx, ny);
#endif
for (yy = py; yy < py + ny; yy++) { for (yy = py; yy < py + ny; yy++) {
gl = &gd->linedata[yy]; gl = &gd->linedata[yy];
@@ -794,6 +803,9 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
grid_expand_line(gd, py, px + nx, 8); grid_expand_line(gd, py, px + nx, 8);
grid_expand_line(gd, py, dx + nx, 8); grid_expand_line(gd, py, dx + nx, 8);
#ifdef ENABLE_IMAGES
image_grid_move_cells(gd, dx, px, py, nx);
#endif
memmove(&gl->celldata[dx], &gl->celldata[px], memmove(&gl->celldata[dx], &gl->celldata[px],
nx * sizeof *gl->celldata); nx * sizeof *gl->celldata);
if (dx + nx > gl->cellused) if (dx + nx > gl->cellused)
@@ -1233,6 +1245,9 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
{ {
struct grid_line *dstl, *srcl; struct grid_line *dstl, *srcl;
u_int yy; u_int yy;
#ifdef ENABLE_IMAGES
u_int ody = dy, osy = sy;
#endif
if (dy + ny > dst->hsize + dst->sy) if (dy + ny > dst->hsize + dst->sy)
ny = dst->hsize + dst->sy - dy; ny = dst->hsize + dst->sy - dy;
@@ -1245,6 +1260,9 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
dstl = &dst->linedata[dy]; dstl = &dst->linedata[dy];
memcpy(dstl, srcl, sizeof *dstl); memcpy(dstl, srcl, sizeof *dstl);
#ifdef ENABLE_IMAGES
dstl->images = NULL;
#endif
if (srcl->cellsize != 0) { if (srcl->cellsize != 0) {
dstl->celldata = xreallocarray(NULL, dstl->celldata = xreallocarray(NULL,
srcl->cellsize, sizeof *dstl->celldata); srcl->cellsize, sizeof *dstl->celldata);
@@ -1264,6 +1282,9 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
sy++; sy++;
dy++; dy++;
} }
#ifdef ENABLE_IMAGES
image_grid_duplicate_lines(dst, ody, src, osy, ny);
#endif
} }
/* Mark line as dead. */ /* Mark line as dead. */
@@ -1384,6 +1405,11 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
break; break;
line = yy + 1 + lines; line = yy + 1 + lines;
#ifdef ENABLE_IMAGES
if (image_grid_line_has_images(&gd->linedata[line]))
break;
#endif
/* Consume empty lines, including any markers. */ /* Consume empty lines, including any markers. */
if (~gd->linedata[line].flags & GRID_LINE_WRAPPED) if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
wrapped = 0; wrapped = 0;
@@ -1561,6 +1587,13 @@ grid_reflow(struct grid *gd, u_int sx)
if (gl->flags & GRID_LINE_DEAD) if (gl->flags & GRID_LINE_DEAD)
continue; continue;
#ifdef ENABLE_IMAGES
if (image_grid_line_has_images(gl)) {
grid_reflow_move(target, gl);
continue;
}
#endif
/* /*
* Work out the width of this line. at is the point at which * Work out the width of this line. at is the point at which
* the available width is hit, and width is the full line * the available width is hit, and width is the full line

382
image-grid.c Normal file
View File

@@ -0,0 +1,382 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Michael Grant <mgrant@grant.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 <limits.h>
#include <stdlib.h>
#include "tmux.h"
/* A run of cells on one grid line. */
struct image_span {
u_int x;
u_int sx;
u_int source_x;
u_int source_y;
struct image_line *line;
struct image_placement *placement;
TAILQ_ENTRY(image_span) line_entry;
TAILQ_ENTRY(image_span) placement_entry;
};
TAILQ_HEAD(image_spans, image_span);
/* Spans on one grid line. */
struct image_line {
struct image_spans spans;
};
/* A placement of an image, may be across multiple lines */
struct image_placement {
struct image_store *store;
struct image *image;
struct image_spans spans;
TAILQ_ENTRY(image_placement) entry;
};
TAILQ_HEAD(image_placements, image_placement);
/* Placements owned by one grid. */
struct image_store {
struct image_placements placements;
};
/* A shared placements copied when duplicating lines. */
struct image_copy_map {
struct image_placement *source;
struct image_placement *destination;
};
/* Part of a span waiting to be moved. */
struct image_move {
struct image_placement *placement;
u_int x;
u_int sx;
u_int source_x;
u_int source_y;
};
/* Get the placement store for a grid. */
static struct image_store *
image_store_get(struct grid *gd)
{
if (gd->images == NULL) {
gd->images = xcalloc(1, sizeof *gd->images);
TAILQ_INIT(&gd->images->placements);
}
return (gd->images);
}
/* Get the spans for a grid line. */
static struct image_line *
image_line_get(struct grid_line *gl)
{
if (gl->images == NULL) {
gl->images = xcalloc(1, sizeof *gl->images);
TAILQ_INIT(&gl->images->spans);
}
return (gl->images);
}
/* Create a placement. Takes a reference to the image. */
static struct image_placement *
image_placement_create(struct grid *gd, struct image *im)
{
struct image_store *store = image_store_get(gd);
struct image_placement *plmt;
plmt = xcalloc(1, sizeof *plmt);
plmt->store = store;
plmt->image = im;
TAILQ_INIT(&plmt->spans);
TAILQ_INSERT_TAIL(&store->placements, plmt, entry);
image_ref(im);
return (plmt);
}
/* Free a placement after all its spans have gone. */
static void
image_placement_free(struct image_placement *plmt)
{
TAILQ_REMOVE(&plmt->store->placements, plmt, entry);
image_free(plmt->image);
free(plmt);
}
/* Attach a span to both its grid line and its placement. */
static void
image_span_add(struct image_line *line, struct image_placement *plmt, u_int x,
u_int sx, u_int src_x, u_int src_y)
{
struct image_span *span;
if (sx == 0)
return;
span = xcalloc(1, sizeof *span);
span->x = x;
span->sx = sx;
span->source_x = src_x;
span->source_y = src_y;
span->line = line;
span->placement = plmt;
TAILQ_INSERT_TAIL(&line->spans, span, line_entry);
TAILQ_INSERT_TAIL(&plmt->spans, span, placement_entry);
}
/* Detach and free one span. */
static void
image_span_free(struct image_span *span)
{
TAILQ_REMOVE(&span->line->spans, span, line_entry);
TAILQ_REMOVE(&span->placement->spans, span, placement_entry);
free(span);
}
/* Free placements with no remaining spans. */
static void
image_store_prune(struct image_store *store)
{
struct image_placement *plmt, *next;
if (store == NULL)
return;
TAILQ_FOREACH_SAFE(plmt, &store->placements, entry, next) {
if (TAILQ_EMPTY(&plmt->spans))
image_placement_free(plmt);
}
}
/* Add a source rectangle at absolute grid coordinates. */
void
image_grid_add(struct grid *gd, struct image *im, u_int x, u_int y,
u_int src_x, u_int src_y, u_int sx, u_int sy)
{
struct image_placement *plmt;
struct image_line *line;
u_int rows = gd->hsize + gd->sy, row;
u_int image_sx, image_sy;
if (sx == 0 || sy == 0 || x >= gd->sx || y >= rows)
return;
image_get_size_in_cells(im, &image_sx, &image_sy);
if (src_x >= image_sx || src_y >= image_sy)
return;
if (sx > gd->sx - x)
sx = gd->sx - x;
if (sx > image_sx - src_x)
sx = image_sx - src_x;
if (sy > rows - y)
sy = rows - y;
if (sy > image_sy - src_y)
sy = image_sy - src_y;
plmt = image_placement_create(gd, im);
for (row = 0; row < sy; row++) {
line = image_line_get(&gd->linedata[y + row]);
image_span_add(line, plmt, x, sx, src_x, src_y + row);
}
}
/* Remove the covered part of each span, retaining either side. */
static void
image_line_remove(struct image_line *line, u_int x, u_int w)
{
struct image_span *span, *next;
u_int end, span_end, right;
if (line == NULL || w == 0)
return;
end = x + w;
if (end < x)
end = UINT_MAX;
TAILQ_FOREACH_SAFE(span, &line->spans, line_entry, next) {
span_end = span->x + span->sx;
if (span_end <= x || span->x >= end)
continue;
if (span->x < x && span_end > end) {
right = span_end - end;
span->sx = x - span->x;
image_span_add(line, span->placement, end, right,
span->source_x + end - span->x, span->source_y);
continue;
}
if (span->x < x) {
span->sx = x - span->x;
continue;
}
if (span_end > end) {
span->source_x += end - span->x;
span->sx = span_end - end;
span->x = end;
continue;
}
image_span_free(span);
}
}
/* Remove a rectangle of image spans from a grid. */
void
image_grid_remove(struct grid *gd, u_int x, u_int y, u_int sx, u_int sy)
{
u_int rows = gd->hsize + gd->sy;
u_int row;
if (gd->images == NULL || sx == 0 || sy == 0 || y >= rows)
return;
if (sy > rows - y)
sy = rows - y;
for (row = y; row < y + sy; row++)
image_line_remove(gd->linedata[row].images, x, sx);
image_store_prune(gd->images);
}
/* Free all spans on a grid line. */
void
image_grid_free_line(struct grid *gd, struct grid_line *gl)
{
struct image_line *line = gl->images;
struct image_span *span, *next;
if (line == NULL)
return;
TAILQ_FOREACH_SAFE(span, &line->spans, line_entry, next)
image_span_free(span);
free(line);
gl->images = NULL;
image_store_prune(gd->images);
}
/* Free a grid's empty placement store. */
void
image_grid_free(struct grid *gd)
{
if (gd->images != NULL) {
image_store_prune(gd->images);
free(gd->images);
gd->images = NULL;
}
}
/* Move the image spans belonging to a range of text cells. */
void
image_grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx)
{
struct image_line *line;
struct image_span *span;
struct image_move *moves = NULL;
size_t n = 0, i;
u_int start, end, span_end;
if (gd->images == NULL ||
nx == 0 ||
px == dx ||
py >= gd->hsize + gd->sy)
return;
line = gd->linedata[py].images;
if (line == NULL)
return;
end = px + nx;
TAILQ_FOREACH(span, &line->spans, line_entry) {
span_end = span->x + span->sx;
start = (span->x > px ? span->x : px);
if (start >= end || span_end <= px)
continue;
if (span_end > end)
span_end = end;
moves = xreallocarray(moves, n + 1, sizeof *moves);
moves[n].placement = span->placement;
moves[n].x = dx + start - px;
moves[n].sx = span_end - start;
moves[n].source_x = span->source_x + start - span->x;
moves[n].source_y = span->source_y;
n++;
}
image_line_remove(line, px, nx);
image_line_remove(line, dx, nx);
for (i = 0; i < n; i++) {
image_span_add(line, moves[i].placement, moves[i].x,
moves[i].sx, moves[i].source_x, moves[i].source_y);
}
free(moves);
image_store_prune(gd->images);
}
/* Map one source placement to one destination placement during a copy. */
static struct image_placement *
image_copy_placement(struct grid *dst, struct image_placement *src,
struct image_copy_map **maps, u_int *nmaps)
{
struct image_placement *plmt;
struct image_copy_map *map = *maps;
u_int i, n = *nmaps;
for (i = 0; i < n; i++) {
if (map[i].source == src)
return (map[i].destination);
}
plmt = image_placement_create(dst, src->image);
map = xreallocarray(map, n + 1, sizeof *map);
map[n].source = src;
map[n].destination = plmt;
*maps = map;
*nmaps = n + 1;
return (plmt);
}
/* Duplicate image spans and their placements between grid lines. */
void
image_grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src,
u_int sy, u_int ny)
{
struct image_copy_map *maps = NULL;
struct image_line *src_line, *dst_line;
struct image_span *span;
struct image_placement *plmt;
u_int nmaps = 0, row;
for (row = 0; row < ny; row++) {
src_line = src->linedata[sy + row].images;
if (src_line == NULL)
continue;
dst_line = image_line_get(&dst->linedata[dy + row]);
TAILQ_FOREACH(span, &src_line->spans, line_entry) {
plmt = image_copy_placement(dst, span->placement, &maps,
&nmaps);
image_span_add(dst_line, plmt, span->x, span->sx,
span->source_x, span->source_y);
}
}
free(maps);
}
/* Check whether a grid line has any image spans. */
int
image_grid_line_has_images(struct grid_line *gl)
{
return (gl->images != NULL && !TAILQ_EMPTY(&gl->images->spans));
}

422
image.c
View File

@@ -2,6 +2,7 @@
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
* Copyright (c) 2026 Michael Grant <mgrant@grant.org>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -18,210 +19,289 @@
#include <sys/types.h> #include <sys/types.h>
#include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include "tmux.h" #include "tmux.h"
static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images); /* Maximum image size in bytes. */
static u_int all_images_count; #define IMAGE_SIZE_LIMIT (64 * 1024 * 1024)
#define MAX_IMAGE_COUNT 20
static void printflike(3, 4) /* An image. */
image_log(struct image *im, const char* from, const char* fmt, ...) struct image {
u_int id;
u_int references;
u_int flags;
#define IMAGE_FLAG_OWN_PIXELS 0x1
struct image *parent;
u_int source_id;
u_int order_id;
/* Size in cells. */
u_int sx;
u_int sy;
/* Size in pixels. */
u_int width;
u_int height;
/* Canvas size in pixels. */
u_int canvas_width;
u_int canvas_height;
/*
* RGBA pixels use four bytes each. Stride is the number of bytes
* between the start of each row. Size is the number of bytes
* containing all of this image's pixels - it may include pixels that
* are part of the parent image but not part of this image.
*/
size_t stride;
size_t size;
u_char *pixels;
RB_ENTRY(image) entry;
};
RB_HEAD(images, image);
static struct images images = RB_INITIALIZER(&images);
static u_int image_next_id;
/* Compare images by IDs. */
static int
image_cmp(struct image *a, struct image *b)
{ {
va_list ap; if (a->id < b->id)
char s[128]; return (-1);
if (a->id > b->id)
return (1);
return (0);
}
RB_GENERATE_STATIC(images, image, entry, image_cmp);
if (log_get_level() == 0) /* Find an image by its ID. */
return; struct image *
image_find(u_int id)
{
struct image find;
if (fmt == NULL) { find.id = id;
log_debug("%s: %p (%ux%u %u,%u)", from, im, im->sx, im->sy, return (RB_FIND(images, &images, &find));
im->px, im->py);
return;
}
va_start(ap, fmt);
vsnprintf(s, sizeof s, fmt, ap);
va_end(ap);
log_debug("%s: %p (%ux%u %u,%u): %s", from, im, im->sx, im->sy,
im->px, im->py, s);
} }
static void /* Return the ID of an image. */
image_free(struct image *im) u_int
image_get_id(struct image *im)
{ {
image_log(im, __func__, NULL); return (im->id);
TAILQ_REMOVE(&all_images, im, all_entry);
all_images_count--;
TAILQ_REMOVE(im->list, im, entry);
sixel_free(im->data);
free(im->fallback);
free(im);
} }
int /* Allocate an image and assign a unique ID. */
image_free_all(struct screen *s) static struct image *
{ image_alloc(u_int w, u_int h, u_int cw, u_int ch, u_int sx, u_int sy,
struct image *im, *im1; size_t stride, u_char *pixels)
int redraw = !TAILQ_EMPTY(&s->images);
if (redraw)
log_debug ("%s", __func__);
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1)
image_free(im);
return (redraw);
}
/* Create text placeholder for an image. */
static void
image_fallback(char **ret, u_int sx, u_int sy)
{
char *buf, *label;
u_int py, size, lsize;
if (sy == 0) {
*ret = xstrdup("");
return;
}
/* Allocate first line. */
lsize = xasprintf(&label, "SIXEL IMAGE (%ux%u)\r\n", sx, sy) + 1;
if (sx < lsize - 3)
size = lsize - 1;
else
size = sx + 2;
/* Remaining lines. Every placeholder line has \r\n at the end. */
size += (sx + 2) * (sy - 1) + 1;
*ret = buf = xmalloc(size);
/* Render first line. */
if (sx < lsize - 3) {
memcpy(buf, label, lsize);
buf += lsize - 1;
} else {
memcpy(buf, label, lsize - 3);
buf += lsize - 3;
memset(buf, '+', sx - lsize + 3);
buf += sx - lsize + 3;
snprintf(buf, 3, "\r\n");
buf += 2;
}
/* Remaining lines. */
for (py = 1; py < sy; py++) {
memset(buf, '+', sx);
buf += sx;
snprintf(buf, 3, "\r\n");
buf += 2;
}
free(label);
}
struct image*
image_store(struct screen *s, struct sixel_image *si)
{ {
struct image *im; struct image *im;
u_int id;
do {
if (++image_next_id == 0)
image_next_id++;
id = image_next_id;
} while (image_find(id) != NULL);
im = xcalloc(1, sizeof *im); im = xcalloc(1, sizeof *im);
im->s = s; im->id = id;
im->data = si; im->references = 1;
im->source_id = im->order_id = id;
im->px = s->cx; im->width = w;
im->py = s->cy; im->height = h;
sixel_size_in_cells(si, &im->sx, &im->sy); im->canvas_width = cw;
im->canvas_height = ch;
image_fallback(&im->fallback, im->sx, im->sy); im->sx = sx;
im->sy = sy;
image_log(im, __func__, NULL); im->stride = stride;
im->list = &s->images; im->size = (h - 1) * stride + w * 4;
TAILQ_INSERT_TAIL(im->list, im, entry); im->pixels = pixels;
RB_INSERT(images, &images, im);
TAILQ_INSERT_TAIL(&all_images, im, all_entry);
if (++all_images_count == MAX_IMAGE_COUNT)
image_free(TAILQ_FIRST(&all_images));
return (im); return (im);
} }
int /* Create an image with the provided pixels. */
image_check_line(struct screen *s, u_int py, u_int ny) struct image *
image_create(u_int w, u_int h, u_int cw, u_int ch, u_int sx, u_int sy,
u_char *pixels)
{ {
struct image *im, *im1; struct image *im;
int redraw = 0, in;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) { if (w == 0 || h == 0 || cw < w || ch < h)
in = (py + ny > im->py && py < im->py + im->sy); return (NULL);
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in); if (sx == 0 || sy == 0 || pixels == NULL)
if (in) { return (NULL);
image_free(im); if ((uint64_t)w * h > IMAGE_SIZE_LIMIT / 4)
redraw = 1; return (NULL);
} if (sx > USHRT_MAX || sy > USHRT_MAX)
} return (NULL);
return (redraw); if (sx * sy > IMAGE_SIZE_LIMIT)
return (NULL);
im = image_alloc(w, h, cw, ch, sx, sy, w * 4, pixels);
im->flags |= IMAGE_FLAG_OWN_PIXELS;
return (im);
} }
int /* Resample the crop into transparent, padded RGBA pixels. */
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny) static u_char *
image_view_resample(struct image *src, struct image_view *view, u_int w,
u_int h)
{ {
struct image *im, *im1; u_char *pixels, *row, *src_row;
int redraw = 0, in; u_int src_x, src_y;
uint64_t x, y, off;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) { pixels = xcalloc(w * h, 4);
in = (py < im->py + im->sy && for (y = 0; y < view->scaled_height; y++) {
py + ny > im->py && src_y = view->y + y * view->height / view->scaled_height;
px < im->px + im->sx && src_row = src->pixels + src_y * src->stride + view->x * 4;
px + nx > im->px);
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in); off = ((y + view->canvas_yoff) * w + view->canvas_xoff) * 4;
if (in) { row = pixels + off;
image_free(im);
redraw = 1; if (view->scaled_width == view->width)
memcpy(row, src_row, view->width * 4);
else {
for (x = 0; x < view->scaled_width; x++) {
src_x = x * view->width / view->scaled_width;
memcpy(row + x * 4, src_row + src_x * 4, 4);
}
} }
} }
return (redraw); return (pixels);
} }
int /* Crop and resample an image. The view takes a reference to the image. */
image_scroll_up(struct screen *s, u_int lines) struct image *
image_create_view(struct image *src, struct image_view *view)
{ {
struct image *im, *im1; struct image *im;
int redraw = 0; u_char *pixels;
u_int sx, sy; u_int w, h;
struct sixel_image *new;
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) { if (src == NULL ||
if (im->py >= lines) { view == NULL ||
image_log(im, __func__, "1, lines=%u", lines); view->x >= src->width ||
im->py -= lines; view->y >= src->height ||
redraw = 1; view->width == 0 ||
continue; view->height == 0 ||
} view->scaled_width == 0 ||
if (im->py + im->sy <= lines) { view->scaled_height == 0 ||
image_log(im, __func__, "2, lines=%u", lines); view->sx == 0 ||
image_free(im); view->sy == 0 ||
redraw = 1; view->width > src->width - view->x ||
continue; view->height > src->height - view->y ||
} view->sx > USHRT_MAX ||
sx = im->sx; view->sy > USHRT_MAX ||
sy = (im->py + im->sy) - lines; view->sx * view->sy > IMAGE_SIZE_LIMIT ||
image_log(im, __func__, "3, lines=%u, sy=%u", lines, sy); view->canvas_xoff > view->canvas_width ||
view->scaled_width > view->canvas_width - view->canvas_xoff ||
view->canvas_yoff > view->canvas_height ||
view->scaled_height > view->canvas_height - view->canvas_yoff)
return (NULL);
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1); if (view->scaled_width == view->width &&
sixel_free(im->data); view->scaled_height == view->height &&
im->data = new; view->canvas_xoff == 0 &&
view->canvas_yoff == 0) {
im->py = 0; pixels = src->pixels + view->y * src->stride + view->x * 4;
sixel_size_in_cells(im->data, &im->sx, &im->sy); im = image_alloc(view->width, view->height, view->canvas_width,
view->canvas_height, view->sx, view->sy, src->stride,
free(im->fallback); pixels);
image_fallback(&im->fallback, im->sx, im->sy); } else {
redraw = 1; w = view->canvas_xoff + view->scaled_width;
h = view->canvas_yoff + view->scaled_height;
if ((uint64_t)w * h > IMAGE_SIZE_LIMIT / 4)
return (NULL);
pixels = image_view_resample(src, view, w, h);
im = image_alloc(w, h, view->canvas_width, view->canvas_height,
view->sx, view->sy, w * 4, pixels);
im->flags |= IMAGE_FLAG_OWN_PIXELS;
} }
return (redraw); im->parent = src;
im->source_id = src->source_id;
im->order_id = src->order_id;
image_ref(src);
return (im);
}
/* Add a reference to an image. */
void
image_ref(struct image *im)
{
if (im->references == UINT_MAX)
fatalx("invalid image reference %u", im->id);
im->references++;
}
/* Free an image. */
void
image_free(struct image *im)
{
if (im->references == 0)
fatalx("invalid image reference %u", im->id);
if (--im->references == 0) {
RB_REMOVE(images, &images, im);
if (im->flags & IMAGE_FLAG_OWN_PIXELS)
free(im->pixels);
if (im->parent != NULL)
image_free(im->parent);
free(im);
}
}
/* Return image size in pixels. */
void
image_get_size(struct image *im, u_int *w, u_int *h)
{
*w = im->width;
*h = im->height;
}
/* Return canvas size in pixels. */
void
image_get_canvas_size(struct image *im, u_int *w, u_int *h)
{
*w = im->canvas_width;
*h = im->canvas_height;
}
/* Return image size in cells. */
void
image_get_size_in_cells(struct image *im, u_int *sx, u_int *sy)
{
*sx = im->sx;
*sy = im->sy;
}
/* Return image pixels. */
const u_char *
image_get_pixels(struct image *im, size_t *stride, size_t *size)
{
if (stride != NULL)
*stride = im->stride;
if (size != NULL)
*size = im->size;
return (im->pixels);
}
/* Calculate the cell size. */
void
image_size_in_cells(u_int w, u_int h, u_int xpixel, u_int ypixel, u_int *sx,
u_int *sy)
{
if (xpixel == 0)
xpixel = 8;
if (ypixel == 0)
ypixel = 16;
*sx = w / xpixel + (w % xpixel != 0);
*sy = h / ypixel + (h % ypixel != 0);
} }

19
input.c
View File

@@ -2629,11 +2629,6 @@ input_dcs_dispatch(struct input_ctx *ictx)
const char prefix[] = "tmux;"; const char prefix[] = "tmux;";
const u_int prefixlen = (sizeof prefix) - 1; const u_int prefixlen = (sizeof prefix) - 1;
long long allow_passthrough = 0; long long allow_passthrough = 0;
#ifdef ENABLE_IMAGES
struct window *w;
struct sixel_image *si;
int p2;
#endif
if (wp == NULL) if (wp == NULL)
oo = global_w_options; oo = global_w_options;
@@ -2645,20 +2640,6 @@ input_dcs_dispatch(struct input_ctx *ictx)
return (0); return (0);
} }
#ifdef ENABLE_IMAGES
if (wp != NULL && buf[0] == 'q' && ictx->interm_len == 0) {
w = wp->window;
if (input_split(ictx) != 0)
return (0);
p2 = input_get(ictx, 1, 0, 0);
if (p2 == -1)
p2 = 0;
si = sixel_parse(buf, len, p2, w->xpixel, w->ypixel);
if (si != NULL)
screen_write_sixelimage(sctx, si, ictx->cell.cell.bg);
}
#endif
/* DCS sequences with intermediate byte '$' (includes DECRQSS). */ /* DCS sequences with intermediate byte '$' (includes DECRQSS). */
if (ictx->interm_len == 1 && ictx->interm_buf[0] == '$') { if (ictx->interm_len == 1 && ictx->interm_buf[0] == '$') {
/* DECRQSS is DCS $ q Pt ST. */ /* DECRQSS is DCS $ q Pt ST. */

View File

@@ -1921,15 +1921,6 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
tty_reset(tty); tty_reset(tty);
#ifdef ENABLE_IMAGES
if (wp != NULL)
tty_draw_images(c, wp);
else {
TAILQ_FOREACH(loop, &scene->w->panes, entry)
tty_draw_images(c, loop);
}
#endif
log_debug("%s: finished @%u redraw", c->name, scene->w->id); log_debug("%s: finished @%u redraw", c->name, scene->w->id);
} }

View File

@@ -1471,11 +1471,6 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
memcpy(&gc, &grid_default_cell, sizeof gc); memcpy(&gc, &grid_default_cell, sizeof gc);
utf8_set(&gc.data, 'E'); utf8_set(&gc.data, 'E');
#ifdef ENABLE_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
for (yy = 0; yy < screen_size_y(s); yy++) { for (yy = 0; yy < screen_size_y(s); yy++) {
for (xx = 0; xx < screen_size_x(s); xx++) for (xx = 0; xx < screen_size_x(s); xx++)
grid_view_set_cell(s->grid, xx, yy, &gc); grid_view_set_cell(s->grid, xx, yy, &gc);
@@ -1518,11 +1513,6 @@ screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1) if (s->cx > screen_size_x(s) - 1)
return; return;
#ifdef ENABLE_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 0, 1); screen_write_initctx(ctx, &ttyctx, 0, 1);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -1559,11 +1549,6 @@ screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1) if (s->cx > screen_size_x(s) - 1)
return; return;
#ifdef ENABLE_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 0, 1); screen_write_initctx(ctx, &ttyctx, 0, 1);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -1600,11 +1585,6 @@ screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
if (s->cx > screen_size_x(s) - 1) if (s->cx > screen_size_x(s) - 1)
return; return;
#ifdef ENABLE_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 0, 1); screen_write_initctx(ctx, &ttyctx, 0, 1);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -1635,11 +1615,6 @@ screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
if (ny == 0) if (ny == 0)
ny = 1; ny = 1;
#ifdef ENABLE_IMAGES
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
if (s->cy < s->rupper || s->cy > s->rlower) { if (s->cy < s->rupper || s->cy > s->rlower) {
if (ny > sy - s->cy) if (ny > sy - s->cy)
ny = sy - s->cy; ny = sy - s->cy;
@@ -1703,11 +1678,6 @@ screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
if (ny == 0) if (ny == 0)
ny = 1; ny = 1;
#ifdef ENABLE_IMAGES
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
if (s->cy < s->rupper || s->cy > s->rlower) { if (s->cy < s->rupper || s->cy > s->rlower) {
if (ny > sy - s->cy) if (ny > sy - s->cy)
ny = sy - s->cy; ny = sy - s->cy;
@@ -1776,11 +1746,6 @@ screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
return; return;
#ifdef ENABLE_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
flags = gl->flags & GRID_LINE_OSC133_FLAGS; flags = gl->flags & GRID_LINE_OSC133_FLAGS;
memcpy(&od, &gl->osc133_data, sizeof od); memcpy(&od, &gl->osc133_data, sizeof od);
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
@@ -1815,11 +1780,6 @@ screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
return; return;
#ifdef ENABLE_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
ci->x = s->cx; ci->x = s->cx;
@@ -1842,11 +1802,6 @@ screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
return; return;
} }
#ifdef ENABLE_IMAGES
if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
if (s->cx > sx - 1) if (s->cx > sx - 1)
grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
else else
@@ -1896,11 +1851,6 @@ screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
return; return;
} }
#ifdef ENABLE_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
screen_write_collect_flush(ctx, 0, __func__); screen_write_collect_flush(ctx, 0, __func__);
@@ -1948,9 +1898,6 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct grid *gd = s->grid; struct grid *gd = s->grid;
struct grid_line *gl; struct grid_line *gl;
#ifdef ENABLE_IMAGES
int redraw = 0;
#endif
u_int rupper = s->rupper, rlower = s->rlower; u_int rupper = s->rupper, rlower = s->rlower;
gl = grid_get_line(gd, gd->hsize + s->cy); gl = grid_get_line(gd, gd->hsize + s->cy);
@@ -1971,15 +1918,6 @@ screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
return; return;
} }
#ifdef ENABLE_IMAGES
if (rlower == screen_size_y(s) - 1)
redraw = image_scroll_up(s, 1);
else
redraw = image_check_line(s, rupper, rlower - rupper);
if (redraw && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
screen_write_collect_scroll(ctx, bg); screen_write_collect_scroll(ctx, bg);
ctx->scrolled++; ctx->scrolled++;
@@ -2003,11 +1941,6 @@ screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
ctx->bg = bg; ctx->bg = bg;
} }
#ifdef ENABLE_IMAGES
if (image_scroll_up(s, lines) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
for (i = 0; i < lines; i++) { for (i = 0; i < lines; i++) {
grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
screen_write_collect_scroll(ctx, bg); screen_write_collect_scroll(ctx, bg);
@@ -2032,11 +1965,6 @@ screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
else if (lines > s->rlower - s->rupper + 1) else if (lines > s->rlower - s->rupper + 1)
lines = s->rlower - s->rupper + 1; lines = s->rlower - s->rupper + 1;
#ifdef ENABLE_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
for (i = 0; i < lines; i++) for (i = 0; i < lines; i++)
grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg); grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
@@ -2073,11 +2001,6 @@ screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
struct visible_ranges *r; struct visible_ranges *r;
struct visible_range *ri; struct visible_range *ri;
#ifdef ENABLE_IMAGES
if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 1, 1); screen_write_initctx(ctx, &ttyctx, 1, 1);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -2154,11 +2077,6 @@ screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
struct visible_ranges *r; struct visible_ranges *r;
struct visible_range *ri; struct visible_range *ri;
#ifdef ENABLE_IMAGES
if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 1, 1); screen_write_initctx(ctx, &ttyctx, 1, 1);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -2227,11 +2145,6 @@ screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
struct visible_ranges *r; struct visible_ranges *r;
struct visible_range *ri; struct visible_range *ri;
#ifdef ENABLE_IMAGES
if (image_free_all(s) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
screen_write_initctx(ctx, &ttyctx, 1, 1); screen_write_initctx(ctx, &ttyctx, 1, 1);
ttyctx.bg = bg; ttyctx.bg = bg;
@@ -2736,11 +2649,6 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
} }
} }
#ifdef ENABLE_IMAGES
if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
#endif
grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x, grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
ci->used); ci->used);
if (bnx != 0) if (bnx != 0)
@@ -3240,66 +3148,6 @@ screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
tty_write(tty_cmd_rawstring, &ttyctx); tty_write(tty_cmd_rawstring, &ttyctx);
} }
#ifdef ENABLE_IMAGES
/* Write a SIXEL image. */
void
screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
u_int bg)
{
struct screen *s = ctx->s;
struct grid *gd = s->grid;
struct tty_ctx ttyctx;
u_int x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines;
struct sixel_image *new;
if (screen_size_y(s) == 1)
return;
sixel_size_in_cells(si, &x, &y);
if (x > screen_size_x(s) || y > screen_size_y(s) - 1) {
if (x > screen_size_x(s) - cx)
sx = screen_size_x(s) - cx;
else
sx = x;
if (y > screen_size_y(s) - 1)
sy = screen_size_y(s) - 1;
else
sy = y;
new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1);
sixel_free(si);
if (new == NULL)
return;
sixel_size_in_cells(new, &x, &y);
si = new;
}
sy = screen_size_y(s) - cy;
if (sy <= y) {
lines = y - sy + 1;
if (image_scroll_up(s, lines) && ctx->wp != NULL)
ctx->wp->flags |= PANE_REDRAW;
for (i = 0; i < lines; i++) {
grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1,
bg);
screen_write_collect_scroll(ctx, bg);
}
ctx->scrolled += lines;
if (lines > cy)
screen_write_cursormove(ctx, -1, 0, 0);
else
screen_write_cursormove(ctx, -1, cy - lines, 0);
}
screen_write_collect_flush(ctx, 0, __func__);
screen_write_initctx(ctx, &ttyctx, 0, 0);
ttyctx.image = image_store(s, si);
tty_write(tty_cmd_sixelimage, &ttyctx);
screen_write_cursormove(ctx, 0, cy + y, 0);
}
#endif
/* Turn alternate screen on. */ /* Turn alternate screen on. */
void void
screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,

View File

@@ -93,11 +93,6 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
s->tabs = NULL; s->tabs = NULL;
s->sel = NULL; s->sel = NULL;
#ifdef ENABLE_IMAGES
TAILQ_INIT(&s->images);
TAILQ_INIT(&s->saved_images);
#endif
s->write_list = NULL; s->write_list = NULL;
s->hyperlinks = NULL; s->hyperlinks = NULL;
@@ -129,11 +124,6 @@ screen_reinit(struct screen *s)
screen_clear_selection(s); screen_clear_selection(s);
screen_free_titles(s); screen_free_titles(s);
#ifdef ENABLE_IMAGES
image_free_all(s);
#endif
screen_set_progress_bar(s, PROGRESS_BAR_HIDDEN, 0); screen_set_progress_bar(s, PROGRESS_BAR_HIDDEN, 0);
screen_reset_hyperlinks(s); screen_reset_hyperlinks(s);
} }
@@ -152,10 +142,6 @@ screen_reset_hyperlinks(struct screen *s)
void void
screen_free(struct screen *s) screen_free(struct screen *s)
{ {
#ifdef ENABLE_IMAGES
struct image *im;
#endif
free(s->sel); free(s->sel);
free(s->tabs); free(s->tabs);
free(s->path); free(s->path);
@@ -172,17 +158,6 @@ screen_free(struct screen *s)
hyperlinks_free(s->hyperlinks); hyperlinks_free(s->hyperlinks);
screen_free_titles(s); screen_free_titles(s);
#ifdef ENABLE_IMAGES
/*
* Images saved when entering the alternate screen stay linked in the
* global list; move them back so they are freed and unlinked here, or
* a later eviction would write through this freed screen.
*/
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
TAILQ_FOREACH(im, &s->images, entry)
im->list = &s->images;
image_free_all(s);
#endif
} }
/* Reset tabs to default, eight spaces apart. */ /* Reset tabs to default, eight spaces apart. */
@@ -375,10 +350,6 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
if (sy != screen_size_y(s)) if (sy != screen_size_y(s))
screen_resize_y(s, sy, eat_empty, &cy); screen_resize_y(s, sy, eat_empty, &cy);
#ifdef ENABLE_IMAGES
image_free_all(s);
#endif
if (reflow) if (reflow)
screen_reflow(s, sx, &cx, &cy, cursor); screen_reflow(s, sx, &cx, &cy, cursor);
@@ -690,10 +661,6 @@ int
screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor) screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
{ {
u_int sx, sy; u_int sx, sy;
#ifdef ENABLE_IMAGES
struct image *im;
#endif
if (SCREEN_IS_ALTERNATE(s)) if (SCREEN_IS_ALTERNATE(s))
return 0; return 0;
sx = screen_size_x(s); sx = screen_size_x(s);
@@ -707,12 +674,6 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
} }
memcpy(&s->saved_cell, gc, sizeof s->saved_cell); memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
#ifdef ENABLE_IMAGES
TAILQ_CONCAT(&s->saved_images, &s->images, entry);
TAILQ_FOREACH(im, &s->saved_images, entry)
im->list = &s->saved_images;
#endif
grid_view_clear(s->grid, 0, 0, sx, sy, 8); grid_view_clear(s->grid, 0, 0, sx, sy, 8);
s->saved_flags = s->grid->flags; s->saved_flags = s->grid->flags;
@@ -726,10 +687,6 @@ int
screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor) screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
{ {
u_int sx = screen_size_x(s), sy = screen_size_y(s); u_int sx = screen_size_x(s), sy = screen_size_y(s);
#ifdef ENABLE_IMAGES
struct image *im;
#endif
/* /*
* If the current size is different, temporarily resize to the old size * If the current size is different, temporarily resize to the old size
* before copying back. * before copying back.
@@ -776,13 +733,6 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
grid_destroy(s->saved_grid); grid_destroy(s->saved_grid);
s->saved_grid = NULL; s->saved_grid = NULL;
#ifdef ENABLE_IMAGES
image_free_all(s);
TAILQ_CONCAT(&s->images, &s->saved_images, entry);
TAILQ_FOREACH(im, &s->images, entry)
im->list = &s->images;
#endif
if (s->cx > screen_size_x(s) - 1) if (s->cx > screen_size_x(s) - 1)
s->cx = screen_size_x(s) - 1; s->cx = screen_size_x(s) - 1;
if (s->cy > screen_size_y(s) - 1) if (s->cy > screen_size_y(s) - 1)

99
tmux-image.h Normal file
View File

@@ -0,0 +1,99 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
* Copyright (c) 2026 Michael Grant <mgrant@grant.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.
*/
#ifndef TMUX_IMAGE_H
#define TMUX_IMAGE_H
#ifdef ENABLE_IMAGES
#error image support is not ready yet
struct grid;
struct grid_line;
struct image;
struct image_line;
struct image_store;
struct screen;
struct screen_write_ctx;
struct sixel_image;
/* View of all or part of an image. */
struct image_view {
/* Position and size in the image, in pixels. */
u_int x;
u_int y;
u_int width;
u_int height;
/* Size after scaling, in pixels. */
u_int scaled_width;
u_int scaled_height;
/* Destination canvas size and offset in pixels. */
u_int canvas_width;
u_int canvas_height;
u_int canvas_xoff;
u_int canvas_yoff;
/* Destination size in terminal cells. */
u_int sx;
u_int sy;
};
/* 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 *, struct image_view *);
struct image *image_find(u_int);
u_int image_get_id(struct image *);
void image_ref(struct image *);
void image_free(struct image *);
void image_get_size(struct image *, u_int *, u_int *);
void image_get_canvas_size(struct image *, u_int *, u_int *);
void image_get_size_in_cells(struct image *, u_int *, u_int *);
const u_char *image_get_pixels(struct image *, size_t *, size_t *);
void image_size_in_cells(u_int, u_int, u_int, u_int, u_int *,
u_int *);
/* image-grid.c */
void image_grid_add(struct grid *, struct image *, u_int, u_int,
u_int, u_int, u_int, u_int);
void image_grid_remove(struct grid *, u_int, u_int, u_int, u_int);
void image_grid_free_line(struct grid *, struct grid_line *);
void image_grid_free(struct grid *);
void image_grid_move_cells(struct grid *, u_int, u_int, u_int,
u_int);
void image_grid_duplicate_lines(struct grid *, u_int, struct grid *,
u_int, u_int);
int image_grid_line_has_images(struct grid_line *);
/* image-sixel.c */
#define SIXEL_COLOUR_REGISTERS 1024
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
void sixel_free(struct sixel_image *);
void sixel_log(struct sixel_image *);
void sixel_size_in_cells(struct sixel_image *, u_int *, u_int *);
struct sixel_image *sixel_scale(struct sixel_image *, u_int, u_int, u_int,
u_int, u_int, u_int, int);
char *sixel_print(struct sixel_image *, struct sixel_image *,
size_t *);
struct screen *sixel_to_screen(struct sixel_image *);
#endif /* ENABLE_IMAGES */
#endif /* TMUX_IMAGE_H */

70
tmux.h
View File

@@ -19,11 +19,13 @@
#ifndef TMUX_H #ifndef TMUX_H
#define TMUX_H #define TMUX_H
#include <sys/types.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <limits.h> #include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h>
#include <stdio.h> #include <stdio.h>
#include <termios.h> #include <termios.h>
#include <wchar.h> #include <wchar.h>
@@ -33,6 +35,7 @@
#endif #endif
#include "compat.h" #include "compat.h"
#include "tmux-image.h"
#include "tmux-protocol.h" #include "tmux-protocol.h"
#include "xmalloc.h" #include "xmalloc.h"
@@ -77,10 +80,6 @@ struct screen_write_cline;
struct screen_write_ctx; struct screen_write_ctx;
struct session; struct session;
#ifdef ENABLE_IMAGES
struct sixel_image;
#endif
struct tty_ctx; struct tty_ctx;
struct tty_code; struct tty_code;
struct tty_key; struct tty_key;
@@ -927,6 +926,9 @@ struct grid_line {
u_int time; u_int time;
struct osc133_data osc133_data; struct osc133_data osc133_data;
u_short flags; u_short flags;
#ifdef ENABLE_IMAGES
struct image_line *images;
#endif
}; };
/* Entire grid of cells. */ /* Entire grid of cells. */
@@ -946,6 +948,9 @@ struct grid {
u_int scroll_generation; u_int scroll_generation;
struct grid_line *linedata; struct grid_line *linedata;
#ifdef ENABLE_IMAGES
struct image_store *images;
#endif
}; };
/* Virtual cursor in a grid. */ /* Virtual cursor in a grid. */
@@ -1037,26 +1042,6 @@ struct style {
u_int link; u_int link;
}; };
#ifdef ENABLE_IMAGES
/* Image. */
struct image {
struct screen *s;
struct sixel_image *data;
char *fallback;
u_int px;
u_int py;
u_int sx;
u_int sy;
struct images *list;
TAILQ_ENTRY (image) entry;
TAILQ_ENTRY (image) all_entry;
};
TAILQ_HEAD(images, image);
#endif
/* Cursor style. */ /* Cursor style. */
enum screen_cursor_style { enum screen_cursor_style {
SCREEN_CURSOR_DEFAULT, SCREEN_CURSOR_DEFAULT,
@@ -1113,11 +1098,6 @@ struct screen {
bitstr_t *tabs; bitstr_t *tabs;
struct screen_sel *sel; struct screen_sel *sel;
#ifdef ENABLE_IMAGES
struct images images;
struct images saved_images;
#endif
struct screen_write_cline *write_list; struct screen_write_cline *write_list;
struct hyperlinks *hyperlinks; struct hyperlinks *hyperlinks;
@@ -1907,9 +1887,6 @@ struct tty_ctx {
size_t size; size_t size;
} sel; } sel;
#ifdef ENABLE_IMAGES
struct image *image;
#endif
}; };
/* /*
@@ -3008,10 +2985,6 @@ void tty_cmd_scrolldown(struct tty *, const struct tty_ctx *);
void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *); void tty_cmd_reverseindex(struct tty *, const struct tty_ctx *);
void tty_cmd_setselection(struct tty *, const struct tty_ctx *); void tty_cmd_setselection(struct tty *, const struct tty_ctx *);
void tty_cmd_rawstring(struct tty *, const struct tty_ctx *); void tty_cmd_rawstring(struct tty *, const struct tty_ctx *);
#ifdef ENABLE_IMAGES
void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *);
void tty_draw_images(struct client *, struct window_pane *);
#endif
void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); void tty_cmd_syncstart(struct tty *, const struct tty_ctx *);
void tty_default_colours(struct grid_cell *, struct window_pane *, u_int *); void tty_default_colours(struct grid_cell *, struct window_pane *, u_int *);
@@ -3652,10 +3625,6 @@ void screen_write_setselection(struct screen_write_ctx *, const char *,
u_char *, u_int); u_char *, u_int);
void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int, void screen_write_rawstring(struct screen_write_ctx *, u_char *, u_int,
int); int);
#ifdef ENABLE_IMAGES
void screen_write_sixelimage(struct screen_write_ctx *,
struct sixel_image *, u_int);
#endif
void screen_write_alternateon(struct screen_write_ctx *, void screen_write_alternateon(struct screen_write_ctx *,
struct grid_cell *, int); struct grid_cell *, int);
void screen_write_alternateoff(struct screen_write_ctx *, void screen_write_alternateoff(struct screen_write_ctx *,
@@ -4240,27 +4209,6 @@ void spawn_editor_finish(struct window_pane *);
/* regsub.c */ /* regsub.c */
char *regsub(const char *, const char *, const char *, int); char *regsub(const char *, const char *, const char *, int);
#ifdef ENABLE_IMAGES
/* image.c */
int image_free_all(struct screen *);
struct image *image_store(struct screen *, struct sixel_image *);
int image_check_line(struct screen *, u_int, u_int);
int image_check_area(struct screen *, u_int, u_int, u_int, u_int);
int image_scroll_up(struct screen *, u_int);
/* image-sixel.c */
#define SIXEL_COLOUR_REGISTERS 1024
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
void sixel_free(struct sixel_image *);
void sixel_log(struct sixel_image *);
void sixel_size_in_cells(struct sixel_image *, u_int *, u_int *);
struct sixel_image *sixel_scale(struct sixel_image *, u_int, u_int, u_int,
u_int, u_int, u_int, int);
char *sixel_print(struct sixel_image *, struct sixel_image *,
size_t *);
struct screen *sixel_to_screen(struct sixel_image *);
#endif
/* server-acl.c */ /* server-acl.c */
#define SERVER_ACL_READONLY 0x1 #define SERVER_ACL_READONLY 0x1
#define SERVER_ACL_IS_GROUP 0x2 #define SERVER_ACL_IS_GROUP 0x2

124
tty.c
View File

@@ -68,11 +68,6 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code,
enum tty_code_code, u_int); enum tty_code_code, u_int);
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
#ifdef ENABLE_IMAGES
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
struct client *, struct tty_ctx *);
#endif
#define tty_use_margin(tty) \ #define tty_use_margin(tty) \
(tty->term->flags & TERM_DECSLRM) (tty->term->flags & TERM_DECSLRM)
#define tty_full_width(tty, ctx) \ #define tty_full_width(tty, ctx) \
@@ -1460,60 +1455,6 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
return (&new); return (&new);
} }
#ifdef ENABLE_IMAGES
/* Update context for client. */
static int
tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
{
struct window_pane *wp = ttyctx->arg;
if (c->session->curw->window != wp->window)
return (0);
if (wp->layout_cell == NULL)
return (0);
if (tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, &ttyctx->wsx,
&ttyctx->wsy))
ttyctx->flags |= TTY_CTX_WINDOW_BIGGER;
else
ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
ttyctx->yoff = ttyctx->ryoff = wp->yoff;
if (status_at_line(c) == 0)
ttyctx->yoff += status_line_size(c);
return (1);
}
void
tty_draw_images(struct client *c, struct window_pane *wp)
{
struct image *im;
struct tty_ctx ttyctx;
TAILQ_FOREACH(im, &wp->screen->images, entry) {
memset(&ttyctx, 0, sizeof ttyctx);
/* Set the client independent properties. */
ttyctx.ocx = im->px;
ttyctx.ocy = im->py;
ttyctx.orlower = wp->screen->rlower;
ttyctx.orupper = wp->screen->rupper;
ttyctx.xoff = ttyctx.rxoff = wp->xoff;
ttyctx.sx = wp->sx;
ttyctx.sy = wp->sy;
ttyctx.image = im;
ttyctx.arg = wp;
ttyctx.set_client_cb = tty_set_client_cb;
ttyctx.flags |= TTY_CTX_INVISIBLE_PANES;
tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
}
}
#endif
void void
tty_sync_start(struct tty *tty) tty_sync_start(struct tty *tty)
{ {
@@ -1588,19 +1529,6 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
} }
} }
#ifdef ENABLE_IMAGES
/* Only write to the incoming tty instead of every client. */
static void
tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
struct client *c, struct tty_ctx *ctx)
{
if (ctx->set_client_cb == NULL)
return;
if ((ctx->set_client_cb(ctx, c)) == 1)
cmdfn(&c->tty, ctx);
}
#endif
void void
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
{ {
@@ -2034,58 +1962,6 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
tty_invalidate(tty); tty_invalidate(tty);
} }
#ifdef ENABLE_IMAGES
void
tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
{
struct image *im = ctx->image;
struct sixel_image *si = im->data;
struct sixel_image *new;
char *data;
size_t size;
u_int cx = ctx->ocx, cy = ctx->ocy, sx, sy;
u_int i, j, x, y, rx, ry;
int fallback = 0;
if ((~tty->term->flags & TERM_SIXEL) &&
!tty_term_has(tty->term, TTYC_SXL))
fallback = 1;
if (tty->xpixel == 0 || tty->ypixel == 0)
fallback = 1;
sixel_size_in_cells(si, &sx, &sy);
log_debug("%s: image is %ux%u", __func__, sx, sy);
if (!tty_clamp_area(tty, ctx, cx, cy, sx, sy, &i, &j, &x, &y, &rx, &ry))
return;
log_debug("%s: clamping to %u,%u-%u,%u", __func__, i, j, rx, ry);
if (fallback == 1) {
data = xstrdup(im->fallback);
size = strlen(data);
} else {
new = sixel_scale(si, tty->xpixel, tty->ypixel, i, j, rx, ry, 0);
if (new == NULL)
return;
data = sixel_print(new, si, &size);
}
if (data != NULL) {
log_debug("%s: %zu bytes: %s", __func__, size, data);
tty_region_off(tty);
tty_margin_off(tty);
tty_cursor(tty, x, y);
tty->flags |= TTY_NOBLOCK;
tty_add(tty, data, size);
tty_invalidate(tty);
free(data);
}
if (fallback == 0)
sixel_free(new);
}
#endif
void void
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
{ {