mirror of
https://github.com/tmux/tmux.git
synced 2026-08-02 21:48:59 +00:00
Initial commit. Implement generalised image support.
This commit is contained in:
@@ -246,9 +246,10 @@ if HAVE_UTF8PROC
|
||||
nodist_tmux_SOURCES += compat/utf8proc.c
|
||||
endif
|
||||
|
||||
# Enable sixel support.
|
||||
if ENABLE_SIXEL
|
||||
dist_tmux_SOURCES += image.c image-sixel.c
|
||||
# Enable image support.
|
||||
if ENABLE_IMAGES
|
||||
dist_tmux_SOURCES += image.c image-sixel.c image-kitty.c image-ascii.c \
|
||||
image-diacritics.h
|
||||
endif
|
||||
|
||||
# Add bits for fuzzing if enabled.
|
||||
|
||||
26
configure.ac
26
configure.ac
@@ -482,15 +482,35 @@ if test "x$enable_cgroups" = xyes; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Enable sixel support.
|
||||
# Enable protocol-neutral image support.
|
||||
AC_ARG_WITH(
|
||||
image-support,
|
||||
AS_HELP_STRING(--with-image-support, enable Kitty/SIXEL/ASCII images)
|
||||
)
|
||||
|
||||
# Keep the old option as an alias for one compatibility cycle.
|
||||
AC_ARG_ENABLE(
|
||||
sixel,
|
||||
AS_HELP_STRING(--enable-sixel, enable sixel images)
|
||||
AS_HELP_STRING(--enable-sixel, deprecated alias for --with-image-support)
|
||||
)
|
||||
if test "x$enable_sixel" = xyes; then
|
||||
with_image_support=yes
|
||||
fi
|
||||
if test "x$with_image_support" = xyes; then
|
||||
PKG_CHECK_MODULES(
|
||||
IMAGE,
|
||||
[libpng >= 1.6 zlib],
|
||||
[
|
||||
AM_CPPFLAGS="$IMAGE_CFLAGS $AM_CPPFLAGS"
|
||||
CPPFLAGS="$AM_CPPFLAGS $SAVED_CPPFLAGS"
|
||||
LIBS="$IMAGE_LIBS $LIBS"
|
||||
],
|
||||
AC_MSG_ERROR("image support requires libpng 1.6 and zlib")
|
||||
)
|
||||
AC_DEFINE(ENABLE_IMAGES)
|
||||
AC_DEFINE(ENABLE_SIXEL)
|
||||
fi
|
||||
AM_CONDITIONAL(ENABLE_SIXEL, [test "x$enable_sixel" = xyes])
|
||||
AM_CONDITIONAL(ENABLE_IMAGES, [test "x$with_image_support" = xyes])
|
||||
|
||||
# Check for b64_ntop. If we have b64_ntop, we assume b64_pton as well.
|
||||
AC_MSG_CHECKING(for b64_ntop)
|
||||
|
||||
14
format.c
14
format.c
@@ -2951,6 +2951,17 @@ format_cb_sixel_support(__unused struct format_tree *ft)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Callback for image_support. */
|
||||
static void *
|
||||
format_cb_image_support(__unused struct format_tree *ft)
|
||||
{
|
||||
#ifdef ENABLE_IMAGES
|
||||
return (xstrdup("1"));
|
||||
#else
|
||||
return (xstrdup("0"));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Callback for active_window_index. */
|
||||
static void *
|
||||
format_cb_active_window_index(struct format_tree *ft)
|
||||
@@ -3614,6 +3625,9 @@ static const struct format_table_entry format_table[] = {
|
||||
{ "host_short", FORMAT_TABLE_STRING,
|
||||
format_cb_host_short
|
||||
},
|
||||
{ "image_support", FORMAT_TABLE_STRING,
|
||||
format_cb_image_support
|
||||
},
|
||||
{ "insert_flag", FORMAT_TABLE_STRING,
|
||||
format_cb_insert_flag
|
||||
},
|
||||
|
||||
139
grid.c
139
grid.c
@@ -41,6 +41,9 @@
|
||||
/* Default grid cell data. */
|
||||
const struct grid_cell grid_default_cell = {
|
||||
{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
|
||||
#ifdef ENABLE_IMAGES
|
||||
, 0, 0, 0
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -49,16 +52,38 @@ const struct grid_cell grid_default_cell = {
|
||||
*/
|
||||
static const struct grid_cell grid_padding_cell = {
|
||||
{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
|
||||
#ifdef ENABLE_IMAGES
|
||||
, 0, 0, 0
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Cleared grid cell data. */
|
||||
static const struct grid_cell grid_cleared_cell = {
|
||||
{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
|
||||
#ifdef ENABLE_IMAGES
|
||||
, 0, 0, 0
|
||||
#endif
|
||||
};
|
||||
static const struct grid_cell_entry grid_cleared_entry = {
|
||||
{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
|
||||
};
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Return the image referenced by a stored cell, or zero. */
|
||||
static u_int
|
||||
grid_entry_image(struct grid_line *gl, struct grid_cell_entry *gce)
|
||||
{
|
||||
struct grid_extd_entry *gee;
|
||||
|
||||
if (~gce->flags & GRID_FLAG_EXTENDED || gce->offset >= gl->extdsize)
|
||||
return (0);
|
||||
gee = &gl->extddata[gce->offset];
|
||||
if (~gee->flags & GRID_FLAG_IMAGE)
|
||||
return (0);
|
||||
return (gee->image_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
void
|
||||
grid_check_is_clear(struct grid *gd)
|
||||
@@ -134,6 +159,10 @@ grid_need_extended_cell(const struct grid_cell_entry *gce,
|
||||
return (1);
|
||||
if (gc->flags & GRID_FLAG_TAB)
|
||||
return (1);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (gc->flags & GRID_FLAG_IMAGE)
|
||||
return (1);
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -181,6 +210,11 @@ grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
|
||||
gee->bg = gc->bg;
|
||||
gee->us = gc->us;
|
||||
gee->link = gc->link;
|
||||
#ifdef ENABLE_IMAGES
|
||||
gee->image_id = gc->image_id;
|
||||
gee->image_x = gc->image_x;
|
||||
gee->image_y = gc->image_y;
|
||||
#endif
|
||||
return (gee);
|
||||
}
|
||||
|
||||
@@ -249,6 +283,12 @@ grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg, int moved)
|
||||
struct grid_extd_entry *gee;
|
||||
u_int old_offset = gce->offset;
|
||||
int had_extd = (gce->flags & GRID_FLAG_EXTENDED);
|
||||
#ifdef ENABLE_IMAGES
|
||||
u_int image_id = grid_entry_image(gl, gce);
|
||||
|
||||
if (!moved && image_id != 0)
|
||||
image_free(image_id);
|
||||
#endif
|
||||
|
||||
memcpy(gce, &grid_cleared_entry, sizeof *gce);
|
||||
if (!moved && had_extd && old_offset < gl->extdsize) {
|
||||
@@ -295,6 +335,13 @@ grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
|
||||
return (0);
|
||||
if (gc1->link != gc2->link)
|
||||
return (0);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if ((gc1->flags & GRID_FLAG_IMAGE) &&
|
||||
(gc1->image_id != gc2->image_id ||
|
||||
gc1->image_x != gc2->image_x ||
|
||||
gc1->image_y != gc2->image_y))
|
||||
return (0);
|
||||
#endif
|
||||
return (1);
|
||||
}
|
||||
|
||||
@@ -327,6 +374,10 @@ static void
|
||||
grid_free_line(struct grid *gd, u_int py)
|
||||
{
|
||||
struct grid_line *gl = &gd->linedata[py];
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell_entry *gce;
|
||||
u_int image_id, px;
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
assert(gl->cellused <= gl->cellsize);
|
||||
@@ -334,6 +385,14 @@ grid_free_line(struct grid *gd, u_int py)
|
||||
assert(gl->cellsize == 0 || gl->celldata != NULL);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
for (px = 0; px < gl->cellsize; px++) {
|
||||
gce = &gl->celldata[px];
|
||||
image_id = grid_entry_image(gl, gce);
|
||||
if (image_id != 0)
|
||||
image_free(image_id);
|
||||
}
|
||||
#endif
|
||||
free(gl->celldata);
|
||||
free(gl->extddata);
|
||||
memset(gl, 0, sizeof *gl);
|
||||
@@ -605,6 +664,11 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
|
||||
gc->bg = gee->bg;
|
||||
gc->us = gee->us;
|
||||
gc->link = gee->link;
|
||||
#ifdef ENABLE_IMAGES
|
||||
gc->image_id = gee->image_id;
|
||||
gc->image_x = gee->image_x;
|
||||
gc->image_y = gee->image_y;
|
||||
#endif
|
||||
|
||||
if (gc->flags & GRID_FLAG_TAB)
|
||||
grid_set_tab(gc, gee->data);
|
||||
@@ -625,6 +689,11 @@ grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
|
||||
gc->us = 8;
|
||||
utf8_set(&gc->data, gce->data.data);
|
||||
gc->link = 0;
|
||||
#ifdef ENABLE_IMAGES
|
||||
gc->image_id = 0;
|
||||
gc->image_x = 0;
|
||||
gc->image_y = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Get cell for reading. */
|
||||
@@ -644,6 +713,9 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
|
||||
{
|
||||
struct grid_line *gl;
|
||||
struct grid_cell_entry *gce;
|
||||
#ifdef ENABLE_IMAGES
|
||||
u_int old_id, new_id = 0;
|
||||
#endif
|
||||
|
||||
if (grid_check_y(gd, __func__, py) != 0)
|
||||
return;
|
||||
@@ -655,6 +727,17 @@ grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
|
||||
gl->cellused = px + 1;
|
||||
|
||||
gce = &gl->celldata[px];
|
||||
#ifdef ENABLE_IMAGES
|
||||
old_id = grid_entry_image(gl, gce);
|
||||
if (gc->flags & GRID_FLAG_IMAGE)
|
||||
new_id = gc->image_id;
|
||||
if (old_id != new_id) {
|
||||
if (old_id != 0)
|
||||
image_free(old_id);
|
||||
if (new_id != 0)
|
||||
image_ref(new_id);
|
||||
}
|
||||
#endif
|
||||
if (grid_need_extended_cell(gce, gc))
|
||||
grid_extended_cell(gl, gce, gc);
|
||||
else
|
||||
@@ -689,6 +772,20 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
|
||||
|
||||
for (i = 0; i < slen; i++) {
|
||||
gce = &gl->celldata[px + i];
|
||||
#ifdef ENABLE_IMAGES
|
||||
{
|
||||
u_int old_id = grid_entry_image(gl, gce);
|
||||
u_int new_id = (gc->flags & GRID_FLAG_IMAGE) ?
|
||||
gc->image_id : 0;
|
||||
|
||||
if (old_id != new_id) {
|
||||
if (old_id != 0)
|
||||
image_free(old_id);
|
||||
if (new_id != 0)
|
||||
image_ref(new_id);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (grid_need_extended_cell(gce, gc)) {
|
||||
gee = grid_extended_cell(gl, gce, gc);
|
||||
gee->data = utf8_build_one(s[i]);
|
||||
@@ -808,6 +905,9 @@ grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
|
||||
{
|
||||
struct grid_line *gl;
|
||||
u_int xx;
|
||||
#ifdef ENABLE_IMAGES
|
||||
u_int image_id;
|
||||
#endif
|
||||
|
||||
if (nx == 0 || px == dx)
|
||||
return;
|
||||
@@ -818,6 +918,16 @@ 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, dx + nx, 8);
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* References in the source range are transferred, not duplicated. */
|
||||
for (xx = dx; xx < dx + nx; xx++) {
|
||||
if (xx >= px && xx < px + nx)
|
||||
continue;
|
||||
image_id = grid_entry_image(gl, &gl->celldata[xx]);
|
||||
if (image_id != 0)
|
||||
image_free(image_id);
|
||||
}
|
||||
#endif
|
||||
memmove(&gl->celldata[dx], &gl->celldata[px],
|
||||
nx * sizeof *gl->celldata);
|
||||
if (dx + nx > gl->cellused)
|
||||
@@ -1190,6 +1300,13 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
|
||||
grid_get_cell(gd, xx, py, &gc);
|
||||
if (gc.flags & GRID_FLAG_PADDING)
|
||||
continue;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (gc.flags & GRID_FLAG_IMAGE) {
|
||||
utf8_set(&gc.data, ' ');
|
||||
gc.flags &= ~GRID_FLAG_IMAGE;
|
||||
gc.image_id = gc.image_x = gc.image_y = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (lastgc != NULL && (flags & GRID_STRING_WITH_SEQUENCES)) {
|
||||
grid_string_cells_code(*lastgc, &gc, code, sizeof code,
|
||||
@@ -1257,6 +1374,9 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
||||
{
|
||||
struct grid_line *dstl, *srcl;
|
||||
u_int yy;
|
||||
#ifdef ENABLE_IMAGES
|
||||
u_int px, image_id;
|
||||
#endif
|
||||
|
||||
if (dy + ny > dst->hsize + dst->sy)
|
||||
ny = dst->hsize + dst->sy - dy;
|
||||
@@ -1285,6 +1405,13 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
||||
} else
|
||||
dstl->extddata = NULL;
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
for (px = 0; px < dstl->cellsize; px++) {
|
||||
image_id = grid_entry_image(dstl, &dstl->celldata[px]);
|
||||
if (image_id != 0)
|
||||
image_ref(image_id);
|
||||
}
|
||||
#endif
|
||||
sy++;
|
||||
dy++;
|
||||
}
|
||||
@@ -1420,8 +1547,7 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
|
||||
/* Remove the lines that were completely consumed. */
|
||||
for (i = yy + 1; i < yy + 1 + lines; i++) {
|
||||
free(gd->linedata[i].celldata);
|
||||
free(gd->linedata[i].extddata);
|
||||
grid_free_line(gd, i);
|
||||
grid_reflow_dead(&gd->linedata[i]);
|
||||
}
|
||||
|
||||
@@ -1477,6 +1603,15 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
}
|
||||
width += gc.data.width;
|
||||
grid_set_cell(target, xx, line, &gc);
|
||||
#ifdef ENABLE_IMAGES
|
||||
/*
|
||||
* The tail of the original line is discarded below. Transfer
|
||||
* its image reference to the new cell rather than duplicating
|
||||
* it.
|
||||
*/
|
||||
if (gc.flags & GRID_FLAG_IMAGE)
|
||||
image_free(gc.image_id);
|
||||
#endif
|
||||
xx++;
|
||||
}
|
||||
if (flags & GRID_LINE_WRAPPED)
|
||||
|
||||
199
image-sixel.c
199
image-sixel.c
@@ -397,6 +397,10 @@ sixel_log(struct sixel_image *si)
|
||||
void
|
||||
sixel_size_in_cells(struct sixel_image *si, u_int *x, u_int *y)
|
||||
{
|
||||
if (si->xpixel == 0)
|
||||
si->xpixel = 8;
|
||||
if (si->ypixel == 0)
|
||||
si->ypixel = 16;
|
||||
if ((si->x % si->xpixel) == 0)
|
||||
*x = (si->x / si->xpixel);
|
||||
else
|
||||
@@ -407,6 +411,92 @@ sixel_size_in_cells(struct sixel_image *si, u_int *x, u_int *y)
|
||||
*y = 1 + (si->y / si->ypixel);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
static double
|
||||
sixel_hue(double p, double q, double t)
|
||||
{
|
||||
if (t < 0)
|
||||
t += 1;
|
||||
if (t > 1)
|
||||
t -= 1;
|
||||
if (t < 1.0 / 6)
|
||||
return (p + (q - p) * 6 * t);
|
||||
if (t < 1.0 / 2)
|
||||
return (q);
|
||||
if (t < 2.0 / 3)
|
||||
return (p + (q - p) * (2.0 / 3 - t) * 6);
|
||||
return (p);
|
||||
}
|
||||
|
||||
static void
|
||||
sixel_colour_to_rgb(u_int colour, u_char *r, u_char *g, u_char *b)
|
||||
{
|
||||
u_int type = colour >> 25;
|
||||
double h, l, s, p, q;
|
||||
|
||||
if (type == 2) {
|
||||
*r = ((colour >> 16) & 0x1ff) * 255 / 100;
|
||||
*g = ((colour >> 8) & 0xff) * 255 / 100;
|
||||
*b = (colour & 0xff) * 255 / 100;
|
||||
return;
|
||||
}
|
||||
if (type != 1) {
|
||||
*r = *g = *b = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
h = ((colour >> 16) & 0x1ff) / 360.0;
|
||||
l = ((colour >> 8) & 0xff) / 100.0;
|
||||
s = (colour & 0xff) / 100.0;
|
||||
if (s == 0) {
|
||||
*r = *g = *b = l * 255;
|
||||
return;
|
||||
}
|
||||
q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
p = 2 * l - q;
|
||||
*r = sixel_hue(p, q, h + 1.0 / 3) * 255;
|
||||
*g = sixel_hue(p, q, h) * 255;
|
||||
*b = sixel_hue(p, q, h - 1.0 / 3) * 255;
|
||||
}
|
||||
|
||||
/* Convert decoded SIXEL data into the protocol-neutral immutable image. */
|
||||
struct image *
|
||||
sixel_to_image(struct sixel_image *si)
|
||||
{
|
||||
u_char *pixels, *pixel, r, g, b;
|
||||
u_int x, y, c, sx, sy;
|
||||
struct image *im;
|
||||
|
||||
if ((uint64_t)si->x * si->y * 4 > SIZE_MAX)
|
||||
return (NULL);
|
||||
pixels = xcalloc(si->x * si->y, 4);
|
||||
for (y = 0; y < si->y; y++) {
|
||||
for (x = 0; x < si->x; x++) {
|
||||
c = sixel_get_pixel(si, x, y);
|
||||
pixel = pixels + ((size_t)y * si->x + x) * 4;
|
||||
if (c == 0) {
|
||||
pixel[3] = si->p2 == 1 ? 0 : 255;
|
||||
continue;
|
||||
}
|
||||
c--;
|
||||
if (c < si->ncolours)
|
||||
sixel_colour_to_rgb(si->colours[c], &r, &g, &b);
|
||||
else
|
||||
r = g = b = 0;
|
||||
pixel[0] = r;
|
||||
pixel[1] = g;
|
||||
pixel[2] = b;
|
||||
pixel[3] = 255;
|
||||
}
|
||||
}
|
||||
sixel_size_in_cells(si, &sx, &sy);
|
||||
im = image_create(si->x, si->y, sx, sy, pixels);
|
||||
if (im == NULL)
|
||||
free(pixels);
|
||||
return (im);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct sixel_image *
|
||||
sixel_scale(struct sixel_image *si, u_int xpixel, u_int ypixel, u_int ox,
|
||||
u_int oy, u_int sx, u_int sy, int colours)
|
||||
@@ -649,6 +739,115 @@ sixel_print(struct sixel_image *si, struct sixel_image *map, size_t *size)
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static struct sixel_image *
|
||||
sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells,
|
||||
u_int xpixel, u_int ypixel)
|
||||
{
|
||||
struct sixel_image *si;
|
||||
const u_char *pixel;
|
||||
u_int x, y, sx, sy, sourcex, sourcey, sourcey0;
|
||||
u_int sourcey1;
|
||||
u_int r, g, b, colour, i;
|
||||
|
||||
sx = cells * xpixel;
|
||||
sy = ypixel;
|
||||
if (sx == 0 || sy == 0 || sx > SIXEL_WIDTH_LIMIT ||
|
||||
sy > SIXEL_HEIGHT_LIMIT)
|
||||
return (NULL);
|
||||
|
||||
si = xcalloc(1, sizeof *si);
|
||||
si->xpixel = xpixel;
|
||||
si->ypixel = ypixel;
|
||||
si->p2 = 1;
|
||||
si->set_ra = 1;
|
||||
si->ra_x = sx;
|
||||
si->ra_y = sy;
|
||||
si->ncolours = si->used_colours = 216;
|
||||
si->colours = xcalloc(si->ncolours, sizeof *si->colours);
|
||||
for (i = 0; i < si->ncolours; i++) {
|
||||
r = (i / 36) * 20;
|
||||
g = ((i / 6) % 6) * 20;
|
||||
b = (i % 6) * 20;
|
||||
si->colours[i] = (2U << 25)|(r << 16)|(g << 8)|b;
|
||||
}
|
||||
|
||||
for (y = 0; y < sy; y++) {
|
||||
sourcey0 = (uint64_t)oy * im->height / im->sy;
|
||||
sourcey1 = (uint64_t)(oy + 1) * im->height / im->sy;
|
||||
sourcey = sourcey0 +
|
||||
(uint64_t)y * (sourcey1 - sourcey0) / sy;
|
||||
if (sourcey >= im->height)
|
||||
sourcey = im->height - 1;
|
||||
for (x = 0; x < sx; x++) {
|
||||
sourcex = ((uint64_t)ox * im->width / im->sx) +
|
||||
(uint64_t)x * im->width * cells /
|
||||
((uint64_t)im->sx * sx);
|
||||
if (sourcex >= im->width)
|
||||
sourcex = im->width - 1;
|
||||
pixel = im->pixels + sourcey * im->stride + sourcex * 4;
|
||||
if (pixel[3] < 128)
|
||||
continue;
|
||||
r = (pixel[0] * 5 + 127) / 255;
|
||||
g = (pixel[1] * 5 + 127) / 255;
|
||||
b = (pixel[2] * 5 + 127) / 255;
|
||||
colour = r * 36 + g * 6 + b + 1;
|
||||
if (sixel_set_pixel(si, x, y, colour) != 0) {
|
||||
sixel_free(si);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (si);
|
||||
}
|
||||
|
||||
void
|
||||
sixel_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py,
|
||||
u_int nx, u_int atx, u_int aty)
|
||||
{
|
||||
struct grid_cell gc, next;
|
||||
struct image *im;
|
||||
struct sixel_image *si;
|
||||
char *data;
|
||||
size_t size;
|
||||
u_int i, run;
|
||||
|
||||
for (i = 0; i < nx; i += run) {
|
||||
grid_view_get_cell(s->grid, px + i, py, &gc);
|
||||
if (~gc.flags & GRID_FLAG_IMAGE) {
|
||||
run = 1;
|
||||
continue;
|
||||
}
|
||||
im = image_find(gc.image_id);
|
||||
if (im == NULL) {
|
||||
run = 1;
|
||||
continue;
|
||||
}
|
||||
for (run = 1; i + run < nx; run++) {
|
||||
grid_view_get_cell(s->grid, px + i + run, py, &next);
|
||||
if (~next.flags & GRID_FLAG_IMAGE ||
|
||||
next.image_id != gc.image_id ||
|
||||
next.image_y != gc.image_y ||
|
||||
next.image_x != gc.image_x + run)
|
||||
break;
|
||||
}
|
||||
si = sixel_from_image(im, gc.image_x, gc.image_y, run,
|
||||
tty->xpixel, tty->ypixel);
|
||||
if (si == NULL)
|
||||
continue;
|
||||
data = sixel_print(si, NULL, &size);
|
||||
sixel_free(si);
|
||||
if (data == NULL)
|
||||
continue;
|
||||
tty_region_off(tty);
|
||||
tty_margin_off(tty);
|
||||
tty_cursor(tty, atx + i, aty);
|
||||
tty->flags |= TTY_NOBLOCK;
|
||||
tty_putn(tty, data, size, 0);
|
||||
tty_invalidate(tty);
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
struct screen *
|
||||
sixel_to_screen(struct sixel_image *si)
|
||||
{
|
||||
|
||||
333
image.c
333
image.c
@@ -1,7 +1,7 @@
|
||||
/* $OpenBSD$ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
* Copyright (c) 2026 Nicholas Marriott <nicholas@roaima.co.uk>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -23,200 +23,201 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
static struct images all_images = TAILQ_HEAD_INITIALIZER(all_images);
|
||||
static u_int all_images_count;
|
||||
#define MAX_IMAGE_COUNT 20
|
||||
static struct images images = RB_INITIALIZER(&images);
|
||||
static u_int image_next_id;
|
||||
|
||||
static void printflike(3, 4)
|
||||
image_log(struct image *im, const char* from, const char* fmt, ...)
|
||||
static int
|
||||
image_cmp(struct image *a, struct image *b)
|
||||
{
|
||||
va_list ap;
|
||||
char s[128];
|
||||
if (a->id < b->id)
|
||||
return (-1);
|
||||
if (a->id > b->id)
|
||||
return (1);
|
||||
return (0);
|
||||
}
|
||||
RB_GENERATE_STATIC(images, image, entry, image_cmp);
|
||||
|
||||
if (log_get_level() == 0)
|
||||
return;
|
||||
struct image *
|
||||
image_find(u_int id)
|
||||
{
|
||||
struct image find;
|
||||
|
||||
if (fmt == NULL) {
|
||||
log_debug("%s: %p (%ux%u %u,%u)", from, im, im->sx, im->sy,
|
||||
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);
|
||||
find.id = id;
|
||||
return (RB_FIND(images, &images, &find));
|
||||
}
|
||||
|
||||
static void
|
||||
image_free(struct image *im)
|
||||
{
|
||||
image_log(im, __func__, NULL);
|
||||
|
||||
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
|
||||
image_free_all(struct screen *s)
|
||||
{
|
||||
struct image *im, *im1;
|
||||
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;
|
||||
|
||||
/* 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 *
|
||||
image_create(u_int width, u_int height, u_int sx, u_int sy, u_char *pixels)
|
||||
{
|
||||
struct image *im;
|
||||
|
||||
if (width == 0 || height == 0 || sx == 0 || sy == 0 || pixels == NULL)
|
||||
return (NULL);
|
||||
if ((uint64_t)width * height * 4 > SIZE_MAX)
|
||||
return (NULL);
|
||||
if ((uint64_t)sx * sy > SIZE_MAX)
|
||||
return (NULL);
|
||||
|
||||
im = xcalloc(1, sizeof *im);
|
||||
im->s = s;
|
||||
im->data = si;
|
||||
do {
|
||||
if (++image_next_id == 0)
|
||||
image_next_id++;
|
||||
im->id = image_next_id;
|
||||
} while (image_find(im->id) != NULL);
|
||||
|
||||
im->px = s->cx;
|
||||
im->py = s->cy;
|
||||
sixel_size_in_cells(si, &im->sx, &im->sy);
|
||||
|
||||
image_fallback(&im->fallback, im->sx, im->sy);
|
||||
|
||||
image_log(im, __func__, NULL);
|
||||
im->list = &s->images;
|
||||
TAILQ_INSERT_TAIL(im->list, im, entry);
|
||||
|
||||
TAILQ_INSERT_TAIL(&all_images, im, all_entry);
|
||||
if (++all_images_count == MAX_IMAGE_COUNT)
|
||||
image_free(TAILQ_FIRST(&all_images));
|
||||
im->references = 1;
|
||||
im->width = width;
|
||||
im->height = height;
|
||||
im->sx = sx;
|
||||
im->sy = sy;
|
||||
im->stride = (size_t)width * 4;
|
||||
im->size = im->stride * height;
|
||||
im->pixels = pixels;
|
||||
image_make_ascii(im);
|
||||
|
||||
RB_INSERT(images, &images, im);
|
||||
log_debug("%s: image %u is %ux%u pixels, %ux%u cells", __func__,
|
||||
im->id, width, height, sx, sy);
|
||||
return (im);
|
||||
}
|
||||
|
||||
int
|
||||
image_check_line(struct screen *s, u_int py, u_int ny)
|
||||
void
|
||||
image_ref(u_int id)
|
||||
{
|
||||
struct image *im, *im1;
|
||||
int redraw = 0, in;
|
||||
struct image *im = image_find(id);
|
||||
|
||||
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||
in = (py + ny > im->py && py < im->py + im->sy);
|
||||
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in);
|
||||
if (in) {
|
||||
image_free(im);
|
||||
redraw = 1;
|
||||
if (im == NULL)
|
||||
fatalx("reference to missing image %u", id);
|
||||
if (im->references == UINT_MAX)
|
||||
fatalx("too many references to image %u", id);
|
||||
im->references++;
|
||||
}
|
||||
|
||||
void
|
||||
image_free(u_int id)
|
||||
{
|
||||
struct image *im = image_find(id);
|
||||
|
||||
if (im == NULL)
|
||||
fatalx("free of missing image %u", id);
|
||||
if (--im->references != 0)
|
||||
return;
|
||||
|
||||
log_debug("%s: freeing image %u", __func__, id);
|
||||
RB_REMOVE(images, &images, im);
|
||||
free(im->pixels);
|
||||
free(im->ascii);
|
||||
free(im);
|
||||
}
|
||||
|
||||
void
|
||||
image_set_cell(struct grid_cell *gc, struct image *im, u_int x, u_int y)
|
||||
{
|
||||
memcpy(gc, &grid_default_cell, sizeof *gc);
|
||||
gc->flags |= GRID_FLAG_IMAGE;
|
||||
gc->image_id = im->id;
|
||||
gc->image_x = x;
|
||||
gc->image_y = y;
|
||||
}
|
||||
|
||||
void
|
||||
image_clear(struct screen *s, u_int id)
|
||||
{
|
||||
struct grid *gd = s->grid;
|
||||
struct grid_cell gc;
|
||||
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))
|
||||
grid_set_cell(gd, x, y, &grid_default_cell);
|
||||
}
|
||||
}
|
||||
return (redraw);
|
||||
}
|
||||
|
||||
int
|
||||
image_check_area(struct screen *s, u_int px, u_int py, u_int nx, u_int ny)
|
||||
{
|
||||
struct image *im, *im1;
|
||||
int redraw = 0, in;
|
||||
struct grid_cell gc;
|
||||
u_int x, y, ex, ey;
|
||||
|
||||
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||
in = (py < im->py + im->sy &&
|
||||
py + ny > im->py &&
|
||||
px < im->px + im->sx &&
|
||||
px + nx > im->px);
|
||||
image_log(im, __func__, "py=%u, ny=%u, in=%d", py, ny, in);
|
||||
if (in) {
|
||||
image_free(im);
|
||||
redraw = 1;
|
||||
ex = px + nx;
|
||||
if (ex > screen_size_x(s))
|
||||
ex = screen_size_x(s);
|
||||
ey = py + ny;
|
||||
if (ey > screen_size_y(s))
|
||||
ey = screen_size_y(s);
|
||||
for (y = py; y < ey; y++) {
|
||||
for (x = px; x < ex; x++) {
|
||||
grid_view_get_cell(s->grid, x, y, &gc);
|
||||
if (gc.flags & GRID_FLAG_IMAGE)
|
||||
return (1);
|
||||
}
|
||||
}
|
||||
return (redraw);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
image_scroll_up(struct screen *s, u_int lines)
|
||||
image_check_line(struct screen *s, u_int py, u_int ny)
|
||||
{
|
||||
struct image *im, *im1;
|
||||
int redraw = 0;
|
||||
u_int sx, sy;
|
||||
struct sixel_image *new;
|
||||
|
||||
TAILQ_FOREACH_SAFE(im, &s->images, entry, im1) {
|
||||
if (im->py >= lines) {
|
||||
image_log(im, __func__, "1, lines=%u", lines);
|
||||
im->py -= lines;
|
||||
redraw = 1;
|
||||
continue;
|
||||
}
|
||||
if (im->py + im->sy <= lines) {
|
||||
image_log(im, __func__, "2, lines=%u", lines);
|
||||
image_free(im);
|
||||
redraw = 1;
|
||||
continue;
|
||||
}
|
||||
sx = im->sx;
|
||||
sy = (im->py + im->sy) - lines;
|
||||
image_log(im, __func__, "3, lines=%u, sy=%u", lines, sy);
|
||||
|
||||
new = sixel_scale(im->data, 0, 0, 0, im->sy - sy, sx, sy, 1);
|
||||
sixel_free(im->data);
|
||||
im->data = new;
|
||||
|
||||
im->py = 0;
|
||||
sixel_size_in_cells(im->data, &im->sx, &im->sy);
|
||||
|
||||
free(im->fallback);
|
||||
image_fallback(&im->fallback, im->sx, im->sy);
|
||||
redraw = 1;
|
||||
}
|
||||
return (redraw);
|
||||
return (image_check_area(s, 0, py, screen_size_x(s), ny));
|
||||
}
|
||||
|
||||
int
|
||||
image_free_all(struct screen *s)
|
||||
{
|
||||
return (image_check_area(s, 0, 0, screen_size_x(s),
|
||||
screen_size_y(s)));
|
||||
}
|
||||
|
||||
int
|
||||
image_scroll_up(struct screen *s, __unused u_int lines)
|
||||
{
|
||||
return (image_free_all(s));
|
||||
}
|
||||
|
||||
/*
|
||||
* Put image marker cells at the cursor. The pixel object is immutable; only
|
||||
* the marker rectangle is clipped to the available pane cells.
|
||||
*/
|
||||
void
|
||||
image_write(struct screen_write_ctx *ctx, struct image *im, u_int bg)
|
||||
{
|
||||
struct screen *s = ctx->s;
|
||||
struct grid *gd = s->grid;
|
||||
struct grid_cell gc;
|
||||
u_int cx = s->cx, cy = s->cy;
|
||||
u_int x, y, sx, sy, lines;
|
||||
|
||||
sx = im->sx;
|
||||
if (sx > screen_size_x(s) - cx)
|
||||
sx = screen_size_x(s) - cx;
|
||||
sy = im->sy;
|
||||
if (sy > screen_size_y(s) - 1)
|
||||
sy = screen_size_y(s) - 1;
|
||||
if (sx == 0 || sy == 0)
|
||||
return;
|
||||
|
||||
if (screen_size_y(s) - cy <= sy) {
|
||||
lines = sy - (screen_size_y(s) - cy) + 1;
|
||||
screen_write_scrollup(ctx, lines, bg);
|
||||
if (lines > cy)
|
||||
screen_write_cursormove(ctx, -1, 0, 0);
|
||||
else
|
||||
screen_write_cursormove(ctx, -1, cy - lines, 0);
|
||||
cy = s->cy;
|
||||
}
|
||||
|
||||
for (y = 0; y < sy; y++) {
|
||||
for (x = 0; x < sx; x++) {
|
||||
image_set_cell(&gc, im, x, y);
|
||||
gc.bg = bg;
|
||||
grid_view_set_cell(gd, cx + x, cy + y, &gc);
|
||||
}
|
||||
}
|
||||
if (ctx->wp != NULL)
|
||||
ctx->wp->flags |= PANE_REDRAW;
|
||||
screen_write_cursormove(ctx, 0, cy + sy, 0);
|
||||
}
|
||||
|
||||
49
input.c
49
input.c
@@ -145,6 +145,9 @@ struct input_ctx {
|
||||
*/
|
||||
struct evbuffer *since_ground;
|
||||
struct event ground_timer;
|
||||
#ifdef ENABLE_IMAGES
|
||||
void *kitty_state;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Helper functions. */
|
||||
@@ -917,6 +920,9 @@ input_free(struct input_ctx *ictx)
|
||||
event_del(&ictx->request_timer);
|
||||
|
||||
free(ictx->input_buf);
|
||||
#ifdef ENABLE_IMAGES
|
||||
kitty_free_state(ictx->kitty_state);
|
||||
#endif
|
||||
evbuffer_free(ictx->since_ground);
|
||||
event_del(&ictx->ground_timer);
|
||||
|
||||
@@ -2795,11 +2801,54 @@ input_exit_apc(struct input_ctx *ictx)
|
||||
{
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct window_pane *wp = ictx->wp;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image *im;
|
||||
u_int image_id = 0, quiet = 0;
|
||||
char action = '\0';
|
||||
int status;
|
||||
#endif
|
||||
|
||||
if (ictx->flags & INPUT_DISCARD)
|
||||
return;
|
||||
log_debug("%s: \"%s\"", __func__, ictx->input_buf);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (wp != NULL && ictx->input_len > 1 && ictx->input_buf[0] == 'G') {
|
||||
im = kitty_parse_image(&ictx->kitty_state, ictx->input_buf + 1,
|
||||
ictx->input_len - 1, wp->window->xpixel,
|
||||
wp->window->ypixel, &image_id, &quiet, &action, &status);
|
||||
if (status == KITTY_PARSE_MORE)
|
||||
return;
|
||||
if (status != KITTY_PARSE_OK) {
|
||||
if (quiet < 2 && action != '\0') {
|
||||
if (status == KITTY_PARSE_MISSING)
|
||||
input_reply(ictx, 0,
|
||||
"\033_Gi=%u;ENOENT\033\\",
|
||||
image_id);
|
||||
else
|
||||
input_reply(ictx, 0,
|
||||
"\033_Gi=%u;EINVAL\033\\",
|
||||
image_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (im != NULL) {
|
||||
if (action == 'd')
|
||||
image_clear(sctx->s, im->id);
|
||||
else
|
||||
image_write(sctx, im, ictx->cell.cell.bg);
|
||||
image_free(im->id);
|
||||
if (quiet == 0 && image_id != 0)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\",
|
||||
image_id);
|
||||
} else if (action == 'd' && image_id == 0) {
|
||||
image_clear(sctx->s, 0);
|
||||
} else if ((action == 't' || action == 'q') && quiet == 0)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\", image_id);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (wp != NULL &&
|
||||
options_get_number(wp->options, "allow-set-title") &&
|
||||
screen_set_title(sctx->s, ictx->input_buf, 1)) {
|
||||
|
||||
@@ -541,6 +541,7 @@ const struct options_table_entry options_table[] = {
|
||||
.scope = OPTIONS_TABLE_SERVER,
|
||||
.flags = OPTIONS_TABLE_IS_ARRAY,
|
||||
.default_str = "xterm*:clipboard:ccolour:cstyle:focus:title,"
|
||||
"xterm-kitty*:kitty,"
|
||||
"screen*:title,"
|
||||
"rxvt*:ignorefkeys",
|
||||
.separator = ",",
|
||||
|
||||
@@ -1122,6 +1122,13 @@ redraw_draw_pane_span(struct redraw_draw_ctx *dctx,
|
||||
px = span->data.p.px + (x - span->x);
|
||||
py = span->data.p.py;
|
||||
tty_draw_line(tty, s, px, py, n, x, y, &style_ctx);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (tty->term->flags & TERM_KITTY)
|
||||
kitty_draw_line(tty, s, px, py, n, x, y);
|
||||
else if ((tty->term->flags & TERM_SIXEL) &&
|
||||
tty->xpixel != 0 && tty->ypixel != 0)
|
||||
sixel_draw_line(tty, s, px, py, n, x, y);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Get default border style for spans without a pane. */
|
||||
@@ -1806,15 +1813,6 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
||||
tty_reset(tty);
|
||||
tty_sync_end(tty);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -3043,57 +3043,14 @@ 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;
|
||||
struct image *im;
|
||||
|
||||
if (screen_size_y(s) == 1)
|
||||
im = sixel_to_image(si);
|
||||
sixel_free(si);
|
||||
if (im == NULL)
|
||||
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);
|
||||
image_write(ctx, im, bg);
|
||||
image_free(im->id);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
45
screen.c
45
screen.c
@@ -91,11 +91,6 @@ screen_init(struct screen *s, u_int sx, u_int sy, u_int hlimit)
|
||||
s->tabs = NULL;
|
||||
s->sel = NULL;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
TAILQ_INIT(&s->images);
|
||||
TAILQ_INIT(&s->saved_images);
|
||||
#endif
|
||||
|
||||
s->write_list = NULL;
|
||||
s->hyperlinks = NULL;
|
||||
|
||||
@@ -152,10 +147,6 @@ screen_reset_hyperlinks(struct screen *s)
|
||||
void
|
||||
screen_free(struct screen *s)
|
||||
{
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct image *im;
|
||||
#endif
|
||||
|
||||
free(s->sel);
|
||||
free(s->tabs);
|
||||
free(s->path);
|
||||
@@ -172,17 +163,6 @@ screen_free(struct screen *s)
|
||||
hyperlinks_free(s->hyperlinks);
|
||||
screen_free_titles(s);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
/*
|
||||
* 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. */
|
||||
@@ -375,10 +355,6 @@ screen_resize_cursor(struct screen *s, u_int sx, u_int sy, int reflow,
|
||||
if (sy != screen_size_y(s))
|
||||
screen_resize_y(s, sy, eat_empty, &cy);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
image_free_all(s);
|
||||
#endif
|
||||
|
||||
if (reflow)
|
||||
screen_reflow(s, sx, &cx, &cy, cursor);
|
||||
|
||||
@@ -690,10 +666,6 @@ int
|
||||
screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
{
|
||||
u_int sx, sy;
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct image *im;
|
||||
#endif
|
||||
|
||||
if (SCREEN_IS_ALTERNATE(s))
|
||||
return 0;
|
||||
sx = screen_size_x(s);
|
||||
@@ -707,12 +679,6 @@ screen_alternate_on(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
}
|
||||
memcpy(&s->saved_cell, gc, sizeof s->saved_cell);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
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);
|
||||
|
||||
s->saved_flags = s->grid->flags;
|
||||
@@ -726,10 +692,6 @@ int
|
||||
screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
{
|
||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct image *im;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* If the current size is different, temporarily resize to the old size
|
||||
* before copying back.
|
||||
@@ -772,13 +734,6 @@ screen_alternate_off(struct screen *s, struct grid_cell *gc, int cursor)
|
||||
grid_destroy(s->saved_grid);
|
||||
s->saved_grid = NULL;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
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)
|
||||
s->cx = screen_size_x(s) - 1;
|
||||
if (s->cy > screen_size_y(s) - 1)
|
||||
|
||||
6
style.c
6
style.c
@@ -30,7 +30,11 @@
|
||||
|
||||
/* Default style. */
|
||||
static struct style style_default = {
|
||||
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 },
|
||||
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0
|
||||
#ifdef ENABLE_IMAGES
|
||||
, 0, 0, 0
|
||||
#endif
|
||||
},
|
||||
0,
|
||||
0,
|
||||
|
||||
|
||||
88
tmux.h
88
tmux.h
@@ -75,6 +75,10 @@ struct screen_write_cline;
|
||||
struct screen_write_ctx;
|
||||
struct session;
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image;
|
||||
struct tty_image_cache;
|
||||
#endif
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct sixel_image;
|
||||
#endif
|
||||
@@ -803,6 +807,7 @@ struct colour_palette {
|
||||
#define GRID_FLAG_NOPALETTE 0x20
|
||||
#define GRID_FLAG_CLEARED 0x40
|
||||
#define GRID_FLAG_TAB 0x80
|
||||
#define GRID_FLAG_IMAGE 0x100
|
||||
|
||||
/* Grid line flags. */
|
||||
#define GRID_LINE_WRAPPED 0x1
|
||||
@@ -847,22 +852,32 @@ struct colour_palette {
|
||||
struct grid_cell {
|
||||
struct utf8_data data;
|
||||
u_short attr;
|
||||
u_char flags;
|
||||
u_short flags;
|
||||
int fg;
|
||||
int bg;
|
||||
int us;
|
||||
u_int link;
|
||||
#ifdef ENABLE_IMAGES
|
||||
u_int image_id;
|
||||
u_int image_x;
|
||||
u_int image_y;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Grid extended cell entry. */
|
||||
struct grid_extd_entry {
|
||||
utf8_char data;
|
||||
u_short attr;
|
||||
u_char flags;
|
||||
u_short flags;
|
||||
int fg;
|
||||
int bg;
|
||||
int us;
|
||||
u_int link;
|
||||
#ifdef ENABLE_IMAGES
|
||||
u_int image_id;
|
||||
u_int image_x;
|
||||
u_int image_y;
|
||||
#endif
|
||||
} __packed;
|
||||
|
||||
/* Grid cell entry. */
|
||||
@@ -876,7 +891,7 @@ struct grid_cell_entry {
|
||||
u_char data;
|
||||
} data;
|
||||
};
|
||||
u_char flags;
|
||||
u_short flags;
|
||||
} __packed;
|
||||
|
||||
/* Grid line. */
|
||||
@@ -1000,24 +1015,23 @@ struct style {
|
||||
u_int link;
|
||||
};
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
/* Image. */
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Immutable protocol-neutral image placement. */
|
||||
struct image {
|
||||
struct screen *s;
|
||||
struct sixel_image *data;
|
||||
char *fallback;
|
||||
|
||||
u_int px;
|
||||
u_int py;
|
||||
u_int id;
|
||||
u_int references;
|
||||
u_int width;
|
||||
u_int height;
|
||||
u_int sx;
|
||||
u_int sy;
|
||||
size_t stride;
|
||||
size_t size;
|
||||
u_char *pixels;
|
||||
char *ascii;
|
||||
|
||||
struct images *list;
|
||||
TAILQ_ENTRY (image) entry;
|
||||
|
||||
TAILQ_ENTRY (image) all_entry;
|
||||
RB_ENTRY(image) entry;
|
||||
};
|
||||
TAILQ_HEAD(images, image);
|
||||
RB_HEAD(images, image);
|
||||
#endif
|
||||
|
||||
/* Cursor style. */
|
||||
@@ -1076,11 +1090,6 @@ struct screen {
|
||||
bitstr_t *tabs;
|
||||
struct screen_sel *sel;
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
struct images images;
|
||||
struct images saved_images;
|
||||
#endif
|
||||
|
||||
struct screen_write_cline *write_list;
|
||||
|
||||
struct hyperlinks *hyperlinks;
|
||||
@@ -1706,6 +1715,7 @@ struct tty_term {
|
||||
#define TERM_RGBCOLOURS 0x10
|
||||
#define TERM_VT100LIKE 0x20
|
||||
#define TERM_SIXEL 0x40
|
||||
#define TERM_KITTY 0x80
|
||||
int flags;
|
||||
|
||||
LIST_ENTRY(tty_term) entry;
|
||||
@@ -1807,6 +1817,10 @@ struct tty {
|
||||
|
||||
struct event key_timer;
|
||||
struct tty_key *key_tree;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct tty_image_cache *images;
|
||||
u_int image_next_id;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Terminal command context. */
|
||||
@@ -2951,10 +2965,6 @@ void tty_cmd_scrolldown(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_rawstring(struct tty *, const struct tty_ctx *);
|
||||
#ifdef ENABLE_SIXEL
|
||||
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_default_colours(struct grid_cell *, struct window_pane *, u_int *);
|
||||
|
||||
@@ -4173,14 +4183,37 @@ void spawn_editor_finish(struct window_pane *);
|
||||
/* regsub.c */
|
||||
char *regsub(const char *, const char *, const char *, int);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* image.c */
|
||||
struct image *image_create(u_int, u_int, u_int, u_int, u_char *);
|
||||
struct image *image_find(u_int);
|
||||
void image_ref(u_int);
|
||||
void image_free(u_int);
|
||||
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);
|
||||
void image_set_cell(struct grid_cell *, struct image *, u_int,
|
||||
u_int);
|
||||
void image_write(struct screen_write_ctx *, struct image *, u_int);
|
||||
void image_make_ascii(struct image *);
|
||||
const char *image_get_ascii(struct image *, u_int, u_int);
|
||||
void image_clear(struct screen *, u_int);
|
||||
#define KITTY_PARSE_ERROR -1
|
||||
#define KITTY_PARSE_OK 0
|
||||
#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 *);
|
||||
void kitty_free_state(void *);
|
||||
void kitty_draw_line(struct tty *, struct screen *, u_int, u_int,
|
||||
u_int, u_int, u_int);
|
||||
void kitty_images_free(struct tty *, int);
|
||||
void sixel_draw_line(struct tty *, struct screen *, u_int, u_int,
|
||||
u_int, u_int, u_int);
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
/* image-sixel.c */
|
||||
#define SIXEL_COLOUR_REGISTERS 1024
|
||||
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int);
|
||||
@@ -4192,6 +4225,7 @@ struct sixel_image *sixel_scale(struct sixel_image *, u_int, u_int, u_int,
|
||||
char *sixel_print(struct sixel_image *, struct sixel_image *,
|
||||
size_t *);
|
||||
struct screen *sixel_to_screen(struct sixel_image *);
|
||||
struct image *sixel_to_image(struct sixel_image *);
|
||||
#endif
|
||||
|
||||
/* server-acl.c */
|
||||
|
||||
33
tty-draw.c
33
tty-draw.c
@@ -121,6 +121,11 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
struct grid *gd = s->grid;
|
||||
const struct grid_cell *gcp;
|
||||
struct grid_cell gc, ngc, last;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell image_gc;
|
||||
struct image *im;
|
||||
const char *ascii;
|
||||
#endif
|
||||
struct grid_line *gl;
|
||||
u_int i, j, last_i, cx, ex, width;
|
||||
u_int cellsize, bg;
|
||||
@@ -251,14 +256,36 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
/* Get the current cell. */
|
||||
grid_view_get_cell(gd, px + i, py, &gc);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Text terminals render image blocks as ASCII. */
|
||||
if (gc.flags & GRID_FLAG_IMAGE) {
|
||||
memcpy(&image_gc, &gc, sizeof image_gc);
|
||||
im = image_find(gc.image_id);
|
||||
if ((tty->term->flags & TERM_KITTY) ||
|
||||
((tty->term->flags & TERM_SIXEL) &&
|
||||
tty->xpixel != 0 &&
|
||||
tty->ypixel != 0))
|
||||
ascii = " ";
|
||||
else
|
||||
ascii = image_get_ascii(im,
|
||||
gc.image_x, gc.image_y);
|
||||
utf8_set(&image_gc.data, *ascii);
|
||||
image_gc.flags &= ~GRID_FLAG_IMAGE;
|
||||
gcp = &image_gc;
|
||||
} else
|
||||
gcp = &gc;
|
||||
#else
|
||||
gcp = &gc;
|
||||
#endif
|
||||
|
||||
/* Work out empty cells. */
|
||||
empty = tty_draw_line_get_empty(&gc, &last,
|
||||
empty = tty_draw_line_get_empty(gcp, &last,
|
||||
nx - i);
|
||||
if (empty != 0)
|
||||
gcp = &gc;
|
||||
;
|
||||
else {
|
||||
/* Update for codeset if needed. */
|
||||
gcp = tty_check_codeset(tty, &gc);
|
||||
gcp = tty_check_codeset(tty, gcp);
|
||||
|
||||
/* And for selection. */
|
||||
if (gcp->flags & GRID_FLAG_SELECTED) {
|
||||
|
||||
@@ -357,6 +357,17 @@ static const struct tty_feature tty_feature_sixel = {
|
||||
TERM_SIXEL
|
||||
};
|
||||
|
||||
/* Terminal has Kitty graphics protocol capability. */
|
||||
static const char *const tty_feature_kitty_capabilities[] = {
|
||||
"Kty",
|
||||
NULL
|
||||
};
|
||||
static const struct tty_feature tty_feature_kitty = {
|
||||
"kitty",
|
||||
tty_feature_kitty_capabilities,
|
||||
TERM_KITTY
|
||||
};
|
||||
|
||||
/* Terminal supports the OSC 9;4 progress bar. */
|
||||
static const char *const tty_feature_progressbar_capabilities[] = {
|
||||
"Spb=\\E]9;4;%p1%d;%p2%d\\E\\\\",
|
||||
@@ -379,6 +390,7 @@ static const struct tty_feature *const tty_features[] = {
|
||||
&tty_feature_extkeys,
|
||||
&tty_feature_focus,
|
||||
&tty_feature_ignorefkeys,
|
||||
&tty_feature_kitty,
|
||||
&tty_feature_margins,
|
||||
&tty_feature_mouse,
|
||||
&tty_feature_osc7,
|
||||
@@ -594,7 +606,20 @@ tty_default_features(int *feat, const char *name, u_int version)
|
||||
"osc7,"
|
||||
"sync,"
|
||||
"usstyle,"
|
||||
"progressbar"
|
||||
"progressbar,"
|
||||
"kitty"
|
||||
},
|
||||
{ .name = "kitty",
|
||||
.features = TTY_FEATURES_BASE_MODERN_XTERM ","
|
||||
"ccolour,"
|
||||
"cstyle,"
|
||||
"extkeys,"
|
||||
"focus,"
|
||||
"hyperlinks,"
|
||||
"osc7,"
|
||||
"sync,"
|
||||
"usstyle,"
|
||||
"kitty"
|
||||
},
|
||||
{ .name = "XTerm",
|
||||
/*
|
||||
|
||||
133
tty.c
133
tty.c
@@ -68,11 +68,6 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code,
|
||||
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
|
||||
static int tty_check_overlay(struct tty *, u_int, u_int);
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
|
||||
struct client *, struct tty_ctx *);
|
||||
#endif
|
||||
|
||||
#define tty_use_margin(tty) \
|
||||
(tty->term->flags & TERM_DECSLRM)
|
||||
#define tty_full_width(tty, ctx) \
|
||||
@@ -114,6 +109,9 @@ tty_init(struct tty *tty, struct client *c)
|
||||
tty->ccolour = -1;
|
||||
tty->fg = tty->bg = -1;
|
||||
tty->mouse_last_pane = -1;
|
||||
#ifdef ENABLE_IMAGES
|
||||
tty->image_next_id = arc4random_uniform(0xffff00) + 1;
|
||||
#endif
|
||||
|
||||
if (tcgetattr(c->fd, &tty->tio) != 0)
|
||||
return (-1);
|
||||
@@ -511,6 +509,9 @@ tty_close(struct tty *tty)
|
||||
tty_stop_tty(tty);
|
||||
|
||||
if (tty->flags & TTY_OPENED) {
|
||||
#ifdef ENABLE_IMAGES
|
||||
kitty_images_free(tty, 1);
|
||||
#endif
|
||||
evbuffer_free(tty->in);
|
||||
event_del(&tty->event_in);
|
||||
evbuffer_free(tty->out);
|
||||
@@ -527,6 +528,9 @@ void
|
||||
tty_free(struct tty *tty)
|
||||
{
|
||||
tty_close(tty);
|
||||
#ifdef ENABLE_IMAGES
|
||||
kitty_images_free(tty, 0);
|
||||
#endif
|
||||
|
||||
free(tty->r.ranges);
|
||||
}
|
||||
@@ -1522,60 +1526,6 @@ tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
|
||||
return (c->overlay_check(c, c->overlay_data, px, py, nx));
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
/* 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
|
||||
tty_sync_start(struct tty *tty)
|
||||
{
|
||||
@@ -1649,19 +1599,6 @@ tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
/* 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
|
||||
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
@@ -2155,58 +2092,6 @@ tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
|
||||
tty_invalidate(tty);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SIXEL
|
||||
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
|
||||
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
|
||||
{
|
||||
|
||||
@@ -6121,7 +6121,9 @@ window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
|
||||
grid_get_cell(gd, i, sy, &gc);
|
||||
if (gc.flags & GRID_FLAG_PADDING)
|
||||
continue;
|
||||
if (gc.flags & GRID_FLAG_TAB)
|
||||
if (gc.flags & GRID_FLAG_IMAGE)
|
||||
utf8_set(&ud, ' ');
|
||||
else if (gc.flags & GRID_FLAG_TAB)
|
||||
utf8_set(&ud, '\t');
|
||||
else
|
||||
utf8_copy(&ud, &gc.data);
|
||||
|
||||
Reference in New Issue
Block a user