mirror of
https://github.com/tmux/tmux.git
synced 2026-08-28 01:21:33 +00:00
image: retain ordered placements in the grid
Replace the per-cell image marker with sparse placement spans attached to grid lines. A placement owns all of its spans and records the input protocol, application image and placement IDs, z-index, and creation order. This retains overlapping image layers without storing a list in every grid cell. Grid operations move, split, clip, and remove only the affected spans. Use the input protocol to determine image/text interaction: later text damages SIXEL spans, while Kitty placements remain and are ordered by their z-index. Rendering then adapts that one logical scene for each client, rather than changing its semantics according to whether the outer terminal uses Kitty or SIXEL.
This commit is contained in:
203
grid.c
203
grid.c
@@ -41,9 +41,6 @@
|
||||
/* 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
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -52,38 +49,16 @@ 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 (image_get_id_by_grid_id(gee->image_id));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
void
|
||||
grid_check_is_clear(struct grid *gd)
|
||||
@@ -111,6 +86,9 @@ grid_check_is_clear(struct grid *gd)
|
||||
assert(gl->extdsize == 0);
|
||||
assert(gl->flags == 0);
|
||||
assert(gl->time == 0);
|
||||
#ifdef ENABLE_IMAGES
|
||||
assert(gl->images == NULL);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -159,10 +137,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -210,11 +184,6 @@ 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 = image_get_grid_id(gc->image_id);
|
||||
gee->image_x = gc->image_x;
|
||||
gee->image_y = gc->image_y;
|
||||
#endif
|
||||
return (gee);
|
||||
}
|
||||
|
||||
@@ -302,12 +271,6 @@ 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) {
|
||||
@@ -354,13 +317,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -393,10 +349,6 @@ 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);
|
||||
@@ -405,12 +357,7 @@ grid_free_line(struct grid *gd, u_int py)
|
||||
#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);
|
||||
}
|
||||
image_grid_free_line(gd, gl);
|
||||
#endif
|
||||
free(gl->celldata);
|
||||
free(gl->extddata);
|
||||
@@ -456,6 +403,9 @@ void
|
||||
grid_destroy(struct grid *gd)
|
||||
{
|
||||
grid_free_lines(gd, 0, gd->hsize + gd->sy);
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_grid_free(gd);
|
||||
#endif
|
||||
free(gd->linedata);
|
||||
free(gd);
|
||||
}
|
||||
@@ -683,11 +633,6 @@ 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 = image_get_id_by_grid_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);
|
||||
@@ -708,11 +653,6 @@ 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. */
|
||||
@@ -732,9 +672,6 @@ 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;
|
||||
@@ -746,17 +683,6 @@ 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
|
||||
@@ -782,10 +708,6 @@ grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
|
||||
struct grid_line *gl;
|
||||
struct grid_cell_entry *gce;
|
||||
struct grid_extd_entry *gee;
|
||||
const struct grid_cell *new_gc;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell old_gc, image_gc;
|
||||
#endif
|
||||
u_int i;
|
||||
|
||||
if (grid_check_y(gd, __func__, py) != 0)
|
||||
@@ -799,39 +721,11 @@ 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];
|
||||
new_gc = gc;
|
||||
#ifdef ENABLE_IMAGES
|
||||
{
|
||||
u_int old_id = grid_entry_image(gl, gce);
|
||||
u_int new_id;
|
||||
|
||||
if (old_id != 0 && (~gc->flags & GRID_FLAG_IMAGE)) {
|
||||
grid_get_cell1(gl, px + i, &old_gc);
|
||||
memcpy(&image_gc, gc, sizeof image_gc);
|
||||
image_gc.flags |= GRID_FLAG_IMAGE|GRID_FLAG_IMAGE_DAMAGED;
|
||||
image_gc.image_id = old_gc.image_id;
|
||||
image_gc.image_x = old_gc.image_x;
|
||||
image_gc.image_y = old_gc.image_y;
|
||||
new_gc = &image_gc;
|
||||
}
|
||||
if (new_gc->flags & GRID_FLAG_IMAGE)
|
||||
new_id = new_gc->image_id;
|
||||
else
|
||||
new_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, new_gc)) {
|
||||
gee = grid_extended_cell(gl, gce, new_gc);
|
||||
if (grid_need_extended_cell(gce, gc)) {
|
||||
gee = grid_extended_cell(gl, gce, gc);
|
||||
gee->data = utf8_build_one(s[i]);
|
||||
} else
|
||||
grid_store_cell(gce, new_gc, s[i]);
|
||||
grid_store_cell(gce, gc, s[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -844,6 +738,9 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
|
||||
|
||||
if (nx == 0 || ny == 0)
|
||||
return;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_grid_damage(gd, px, py, nx, ny);
|
||||
#endif
|
||||
|
||||
if (px == 0 && nx == gd->sx) {
|
||||
grid_clear_lines(gd, py, ny, bg);
|
||||
@@ -879,6 +776,10 @@ grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
|
||||
void
|
||||
grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
|
||||
{
|
||||
struct grid_line *gl;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image_line *images;
|
||||
#endif
|
||||
u_int yy;
|
||||
|
||||
if (ny == 0)
|
||||
@@ -890,8 +791,18 @@ grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
|
||||
return;
|
||||
|
||||
for (yy = py; yy < py + ny; yy++) {
|
||||
grid_free_line(gd, yy);
|
||||
gl = &gd->linedata[yy];
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_grid_damage(gd, 0, yy, gd->sx, 1);
|
||||
images = gl->images;
|
||||
#endif
|
||||
free(gl->celldata);
|
||||
free(gl->extddata);
|
||||
memset(gl, 0, sizeof *gl);
|
||||
grid_empty_line(gd, yy, bg);
|
||||
#ifdef ENABLE_IMAGES
|
||||
gl->images = images;
|
||||
#endif
|
||||
}
|
||||
if (py != 0)
|
||||
gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
|
||||
@@ -946,9 +857,6 @@ 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;
|
||||
@@ -960,14 +868,7 @@ 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);
|
||||
}
|
||||
image_grid_move_cells(gd, dx, px, py, nx);
|
||||
#endif
|
||||
memmove(&gl->celldata[dx], &gl->celldata[px],
|
||||
nx * sizeof *gl->celldata);
|
||||
@@ -1341,14 +1242,6 @@ 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|GRID_FLAG_IMAGE_DAMAGED);
|
||||
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,
|
||||
flags, s, &has_link);
|
||||
@@ -1416,13 +1309,17 @@ 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;
|
||||
u_int original_dy, original_sy;
|
||||
#endif
|
||||
|
||||
if (dy + ny > dst->hsize + dst->sy)
|
||||
ny = dst->hsize + dst->sy - dy;
|
||||
if (sy + ny > src->hsize + src->sy)
|
||||
ny = src->hsize + src->sy - sy;
|
||||
#ifdef ENABLE_IMAGES
|
||||
original_dy = dy;
|
||||
original_sy = sy;
|
||||
#endif
|
||||
grid_free_lines(dst, dy, ny);
|
||||
|
||||
for (yy = 0; yy < ny; yy++) {
|
||||
@@ -1430,6 +1327,9 @@ grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
|
||||
dstl = &dst->linedata[dy];
|
||||
|
||||
memcpy(dstl, srcl, sizeof *dstl);
|
||||
#ifdef ENABLE_IMAGES
|
||||
dstl->images = NULL;
|
||||
#endif
|
||||
if (srcl->cellsize != 0) {
|
||||
dstl->celldata = xreallocarray(NULL,
|
||||
srcl->cellsize, sizeof *dstl->celldata);
|
||||
@@ -1446,16 +1346,12 @@ 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++;
|
||||
}
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_grid_duplicate_lines(dst, original_dy, src, original_sy, ny);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Mark line as dead. */
|
||||
@@ -1473,14 +1369,14 @@ grid_reflow_has_image(struct grid_line *gl)
|
||||
struct grid_cell gc;
|
||||
u_int i;
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (image_grid_line_has_images(gl))
|
||||
return (1);
|
||||
#endif
|
||||
if (~gl->flags & GRID_LINE_EXTENDED)
|
||||
return (0);
|
||||
for (i = 0; i < gl->cellused; i++) {
|
||||
grid_get_cell1(gl, i, &gc);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (gc.flags & GRID_FLAG_IMAGE)
|
||||
return (1);
|
||||
#endif
|
||||
/* Kitty Unicode placeholder base character (U+10EEEE). */
|
||||
if (gc.data.size >= 4 && gc.data.data[0] == 0xf4 &&
|
||||
gc.data.data[1] == 0x8e && gc.data.data[2] == 0xbb &&
|
||||
@@ -1672,15 +1568,6 @@ 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)
|
||||
@@ -1727,7 +1614,7 @@ grid_reflow(struct grid *gd, u_int sx)
|
||||
if (gl->flags & GRID_LINE_DEAD)
|
||||
continue;
|
||||
|
||||
/* Keep image markers at their original cell coordinates. */
|
||||
/* Keep image layers at their original cell coordinates. */
|
||||
if (grid_reflow_has_image(gl)) {
|
||||
grid_reflow_move(target, gl);
|
||||
continue;
|
||||
|
||||
169
image-kitty.c
169
image-kitty.c
@@ -82,6 +82,7 @@ struct kitty_state {
|
||||
u_int rows;
|
||||
u_int image_id;
|
||||
u_int placement_id;
|
||||
int32_t z;
|
||||
u_int quiet;
|
||||
int no_cursor;
|
||||
int virtual;
|
||||
@@ -95,6 +96,7 @@ struct kitty_state {
|
||||
struct kitty_placement {
|
||||
u_int placement_id;
|
||||
u_int server_id;
|
||||
int32_t z;
|
||||
struct kitty_placement *next;
|
||||
};
|
||||
|
||||
@@ -256,7 +258,7 @@ kitty_images_collect(struct tty *tty)
|
||||
static void
|
||||
kitty_place(struct tty *tty, struct kitty_image_cache *cache,
|
||||
struct image *im, u_int source_x, u_int source_y, u_int width,
|
||||
u_int height, u_int destination_x, u_int destination_y)
|
||||
u_int height, u_int destination_x, u_int destination_y, int32_t z)
|
||||
{
|
||||
char control[192];
|
||||
u_int px, py, pwidth, pheight, sx, sy;
|
||||
@@ -271,6 +273,9 @@ kitty_place(struct tty *tty, struct kitty_image_cache *cache,
|
||||
sx - px;
|
||||
pheight = ((uint64_t)(source_y + height) * canvas_height + sy - 1) /
|
||||
sy - py;
|
||||
/* Account for the duplicate-pixel border added by kitty_upload(). */
|
||||
px++;
|
||||
py++;
|
||||
placement = xcalloc(1, sizeof *placement);
|
||||
do {
|
||||
placement->id = ++cache->next_placement;
|
||||
@@ -283,9 +288,10 @@ kitty_place(struct tty *tty, struct kitty_image_cache *cache,
|
||||
cache->placements = placement;
|
||||
tty_cursor(tty, destination_x, destination_y);
|
||||
xsnprintf(control, sizeof control,
|
||||
"\033_Ga=p,i=%u,p=%llu,x=%u,y=%u,w=%u,h=%u,c=%u,r=%u,C=1,"
|
||||
"q=2\033\\", cache->kitty_id, (unsigned long long)placement->id, px, py,
|
||||
pwidth, pheight, width, height);
|
||||
"\033_Ga=p,i=%u,p=%llu,x=%u,y=%u,w=%u,h=%u,c=%u,r=%u,z=%d,"
|
||||
"C=1,q=2\033\\", cache->kitty_id,
|
||||
(unsigned long long)placement->id, px, py, pwidth, pheight, width,
|
||||
height, z);
|
||||
tty_puts(tty, control);
|
||||
}
|
||||
|
||||
@@ -303,6 +309,7 @@ kitty_upload(struct tty *tty, struct image *im)
|
||||
size_t image_size;
|
||||
int encodedlen;
|
||||
u_int id, width, height, canvas_width, canvas_height;
|
||||
u_int upload_width, upload_height;
|
||||
|
||||
for (cache = ko->images; cache != NULL; cache = cache->next) {
|
||||
if (cache->server_id != image_get_id(im))
|
||||
@@ -332,15 +339,36 @@ kitty_upload(struct tty *tty, struct image *im)
|
||||
pixels = image_get_pixels(im, &stride, &image_size);
|
||||
image_get_size(im, &width, &height);
|
||||
image_get_canvas_size(im, &canvas_width, &canvas_height);
|
||||
if ((uint64_t)canvas_width * canvas_height * 4 > IMAGE_SIZE_LIMIT)
|
||||
if (canvas_width > UINT_MAX - 2 || canvas_height > UINT_MAX - 2)
|
||||
return (NULL);
|
||||
padded = xcalloc((size_t)canvas_width * canvas_height, 4);
|
||||
upload_width = canvas_width + 2;
|
||||
upload_height = canvas_height + 2;
|
||||
if ((uint64_t)upload_width * upload_height * 4 > IMAGE_SIZE_LIMIT)
|
||||
return (NULL);
|
||||
/*
|
||||
* Pad the upload with duplicate edge pixels. Kitty linearly filters scaled
|
||||
* textures against transparent border pixels, which otherwise darkens the
|
||||
* outermost pixels of an opaque image.
|
||||
*/
|
||||
padded = xcalloc((size_t)upload_width * upload_height, 4);
|
||||
for (row = 0; row < height; row++)
|
||||
memcpy(padded + (size_t)row * canvas_width * 4,
|
||||
memcpy(padded + ((size_t)(row + 1) * upload_width + 1) * 4,
|
||||
pixels + row * stride, (size_t)width * 4);
|
||||
for (row = 1; row <= canvas_height; row++) {
|
||||
memcpy(padded + (size_t)row * upload_width * 4,
|
||||
padded + ((size_t)row * upload_width + 1) * 4, 4);
|
||||
memcpy(padded + ((size_t)row * upload_width + upload_width - 1) * 4,
|
||||
padded + ((size_t)row * upload_width + upload_width - 2) * 4,
|
||||
4);
|
||||
}
|
||||
memcpy(padded, padded + (size_t)upload_width * 4,
|
||||
(size_t)upload_width * 4);
|
||||
memcpy(padded + (size_t)(upload_height - 1) * upload_width * 4,
|
||||
padded + (size_t)(upload_height - 2) * upload_width * 4,
|
||||
(size_t)upload_width * 4);
|
||||
pixels = padded;
|
||||
width = canvas_width;
|
||||
height = canvas_height;
|
||||
width = upload_width;
|
||||
height = upload_height;
|
||||
image_size = (size_t)width * height * 4;
|
||||
stride = (size_t)width * 4;
|
||||
for (offset = 0; offset < image_size; offset += size) {
|
||||
@@ -387,6 +415,7 @@ kitty_draw_rect(struct tty *tty, const struct image_rect *rectangle,
|
||||
struct image *im;
|
||||
u_int source_x, source_y;
|
||||
u_int width, height, destination_x, destination_y;
|
||||
int32_t z;
|
||||
|
||||
im = image_rect_get_image(rectangle);
|
||||
kitty_images_collect(tty);
|
||||
@@ -395,8 +424,9 @@ kitty_draw_rect(struct tty *tty, const struct image_rect *rectangle,
|
||||
return;
|
||||
image_rect_get_coords(rectangle, &source_x, &source_y, &width,
|
||||
&height, &destination_x, &destination_y);
|
||||
z = image_rect_get_z(rectangle);
|
||||
kitty_place(tty, cache, im, source_x, source_y, width, height,
|
||||
destination_x, destination_y);
|
||||
destination_x, destination_y, z);
|
||||
}
|
||||
|
||||
/* Parse an unsigned Kitty graphics control value. */
|
||||
@@ -418,6 +448,25 @@ kitty_number(const char *s, size_t len, u_int *value)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Parse a signed Kitty graphics control value. */
|
||||
static int
|
||||
kitty_signed_number(const char *s, size_t len, int32_t *value)
|
||||
{
|
||||
char copy[32];
|
||||
const char *errstr;
|
||||
long long ll;
|
||||
|
||||
if (len == 0 || len >= sizeof copy)
|
||||
return (-1);
|
||||
memcpy(copy, s, len);
|
||||
copy[len] = '\0';
|
||||
ll = strtonum(copy, INT32_MIN, INT32_MAX, &errstr);
|
||||
if (errstr != NULL)
|
||||
return (-1);
|
||||
*value = ll;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Parse a Kitty graphics control string. */
|
||||
static int
|
||||
kitty_control(struct kitty_state *ks, const u_char *buf, size_t len)
|
||||
@@ -456,6 +505,11 @@ kitty_control(struct kitty_state *ks, const u_char *buf, size_t len)
|
||||
case 'o':
|
||||
ks->compression = value[0];
|
||||
break;
|
||||
case 'z':
|
||||
if (kitty_signed_number((const char *)value, valuelen,
|
||||
&ks->z) != 0)
|
||||
return (-1);
|
||||
break;
|
||||
case 'f':
|
||||
case 's':
|
||||
case 'v':
|
||||
@@ -534,6 +588,26 @@ kitty_placements_free_all(struct kitty_context *kc)
|
||||
kitty_placements_free(source);
|
||||
}
|
||||
|
||||
/* Remove all parser-side placements at a Kitty z-index. */
|
||||
static void
|
||||
kitty_placements_remove_z(struct kitty_context *kc, int32_t z)
|
||||
{
|
||||
struct kitty_source *source;
|
||||
struct kitty_placement **pp, *placement;
|
||||
|
||||
for (source = kc->sources; source != NULL; source = source->next) {
|
||||
for (pp = &source->placements; (placement = *pp) != NULL; ) {
|
||||
if (placement->z != z) {
|
||||
pp = &placement->next;
|
||||
continue;
|
||||
}
|
||||
*pp = placement->next;
|
||||
image_free(placement->server_id);
|
||||
free(placement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Free Kitty graphics parser state. */
|
||||
void
|
||||
kitty_free_state(void *state)
|
||||
@@ -600,7 +674,7 @@ kitty_source_set(struct kitty_context *kc, u_int id, struct image *im)
|
||||
/* Associate a placement ID with an image. */
|
||||
static u_int
|
||||
kitty_placement_set(struct kitty_context *kc, u_int image_id,
|
||||
u_int placement_id, struct image *im)
|
||||
u_int placement_id, int32_t z, struct image *im)
|
||||
{
|
||||
struct kitty_source *source;
|
||||
struct kitty_placement *placement;
|
||||
@@ -623,6 +697,7 @@ kitty_placement_set(struct kitty_context *kc, u_int image_id,
|
||||
source->placements = placement;
|
||||
}
|
||||
old_id = placement->server_id;
|
||||
placement->z = z;
|
||||
image_ref(image_get_id(im));
|
||||
placement->server_id = image_get_id(im);
|
||||
if (old_id != 0)
|
||||
@@ -864,7 +939,7 @@ kitty_place_image(struct image *source, struct kitty_state *ks, u_int xpixel,
|
||||
struct image *
|
||||
kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
u_int ypixel, u_int *image_id, u_int *replace_id, u_int *quiet,
|
||||
char *action, int *status)
|
||||
char *action, char *delete, u_int *placement_id, int32_t *z, int *status)
|
||||
{
|
||||
struct kitty_context *kc = *state;
|
||||
struct kitty_state *ks;
|
||||
@@ -886,6 +961,9 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
*replace_id = 0;
|
||||
*quiet = 0;
|
||||
*action = '\0';
|
||||
*delete = '\0';
|
||||
*placement_id = 0;
|
||||
*z = 0;
|
||||
*status = KITTY_PARSE_ERROR;
|
||||
ks = kc->transfer;
|
||||
semi = memchr(buf, ';', len);
|
||||
@@ -902,6 +980,9 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
*image_id = ks->image_id;
|
||||
*quiet = ks->quiet;
|
||||
*action = ks->action;
|
||||
*delete = ks->delete;
|
||||
*placement_id = ks->placement_id;
|
||||
*z = ks->z;
|
||||
if (kitty_control(ks, buf, controllen) != 0 ||
|
||||
ks->medium != 'd' ||
|
||||
(payloadlen != 0 &&
|
||||
@@ -911,6 +992,9 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
*image_id = ks->image_id;
|
||||
*quiet = ks->quiet;
|
||||
*action = ks->action;
|
||||
*delete = ks->delete;
|
||||
*placement_id = ks->placement_id;
|
||||
*z = ks->z;
|
||||
if (ks->more) {
|
||||
kc->transfer = ks;
|
||||
*status = KITTY_PARSE_MORE;
|
||||
@@ -937,7 +1021,7 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
image_free(image_get_id(source));
|
||||
if (im != NULL) {
|
||||
*replace_id = kitty_placement_set(kc, ks->image_id,
|
||||
ks->placement_id, im);
|
||||
ks->placement_id, ks->z, im);
|
||||
*status = KITTY_PARSE_OK;
|
||||
}
|
||||
}
|
||||
@@ -963,7 +1047,10 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
}
|
||||
} else if (ks->delete == 'I')
|
||||
im = kitty_source_remove(kc, ks->image_id);
|
||||
else
|
||||
else if (ks->delete == 'z' || ks->delete == 'Z') {
|
||||
kitty_placements_remove_z(kc, ks->z);
|
||||
im = NULL;
|
||||
} else
|
||||
goto fail;
|
||||
if ((ks->delete == 'i' || ks->delete == 'I') &&
|
||||
im == NULL)
|
||||
@@ -1040,7 +1127,7 @@ kitty_parse_image(void **state, const u_char *buf, size_t len, u_int xpixel,
|
||||
*status = KITTY_PARSE_ERROR;
|
||||
else if (ks->placement_id != 0)
|
||||
(void)kitty_placement_set(kc, ks->image_id,
|
||||
ks->placement_id, im);
|
||||
ks->placement_id, ks->z, im);
|
||||
} else if (ks->virtual) {
|
||||
im = kitty_place_image(source, ks, xpixel, ypixel);
|
||||
if (im == NULL)
|
||||
@@ -1119,10 +1206,11 @@ kitty_placeholder_index(uint32_t value, u_int *index)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Convert a Kitty Unicode placeholder cell into an image marker. */
|
||||
/* Resolve a Kitty Unicode placeholder to an image and source cell. */
|
||||
int
|
||||
kitty_placeholder_to_cell(void *state, struct grid_cell *gc,
|
||||
const struct grid_cell *left)
|
||||
kitty_placeholder_to_image(void *state, struct grid *gd, struct grid_cell *gc,
|
||||
u_int grid_x, u_int grid_y, struct image **image, u_int *source_x,
|
||||
u_int *source_y, u_int *image_id, u_int *placement_id, int32_t *z)
|
||||
{
|
||||
struct kitty_context *kc = state;
|
||||
struct kitty_source *source;
|
||||
@@ -1130,8 +1218,7 @@ kitty_placeholder_to_cell(void *state, struct grid_cell *gc,
|
||||
uint32_t value;
|
||||
size_t offset = 0;
|
||||
u_int values[3], nvalues = 0, id, x, y, sx, sy;
|
||||
struct utf8_data data;
|
||||
int fg, bg, us;
|
||||
u_int left_x, left_y;
|
||||
|
||||
if (kc == NULL ||
|
||||
!kitty_placeholder_character(gc->data.data, gc->data.size, &offset,
|
||||
@@ -1166,29 +1253,33 @@ kitty_placeholder_to_cell(void *state, struct grid_cell *gc,
|
||||
|
||||
if (nvalues >= 1)
|
||||
y = values[0];
|
||||
else if (left != NULL && left->flags & GRID_FLAG_IMAGE &&
|
||||
left->image_id == image_get_id(im))
|
||||
y = left->image_y;
|
||||
else
|
||||
return (0);
|
||||
else {
|
||||
if (grid_x == 0 || !image_grid_get_source(gd, grid_x - 1,
|
||||
gd->hsize + grid_y, im, &left_x, &left_y))
|
||||
return (0);
|
||||
y = left_y;
|
||||
}
|
||||
if (nvalues >= 2)
|
||||
x = values[1];
|
||||
else if (left != NULL && left->flags & GRID_FLAG_IMAGE &&
|
||||
left->image_id == image_get_id(im) && left->image_x != UINT_MAX)
|
||||
x = left->image_x + 1;
|
||||
else
|
||||
return (0);
|
||||
else {
|
||||
if (grid_x == 0 || !image_grid_get_source(gd, grid_x - 1,
|
||||
gd->hsize + grid_y, im, &left_x, &left_y) ||
|
||||
left_x == UINT_MAX)
|
||||
return (0);
|
||||
x = left_x + 1;
|
||||
}
|
||||
if (x >= sx || y >= sy)
|
||||
return (0);
|
||||
|
||||
utf8_copy(&data, &gc->data);
|
||||
fg = gc->fg;
|
||||
bg = gc->bg;
|
||||
us = gc->us;
|
||||
image_set_cell(gc, im, x, y);
|
||||
utf8_copy(&gc->data, &data);
|
||||
gc->fg = fg;
|
||||
gc->bg = bg;
|
||||
gc->us = us;
|
||||
*image = im;
|
||||
*source_x = x;
|
||||
*source_y = y;
|
||||
*image_id = id;
|
||||
if (gc->us & COLOUR_FLAG_RGB)
|
||||
*placement_id = gc->us & 0xffffff;
|
||||
else
|
||||
*placement_id = 0;
|
||||
*z = 0;
|
||||
utf8_set(&gc->data, ' ');
|
||||
return (1);
|
||||
}
|
||||
|
||||
@@ -603,6 +603,7 @@ sixel_scale(struct sixel_image *si, u_int cell_w, u_int cell_h, u_int ox,
|
||||
{
|
||||
struct sixel_image *new;
|
||||
u_int cx, cy, pox, poy, psx, psy, tsx, tsy, px, py;
|
||||
uint64_t x0, x1, y0, y1;
|
||||
u_int x, y, i;
|
||||
|
||||
/*
|
||||
@@ -625,10 +626,19 @@ sixel_scale(struct sixel_image *si, u_int cell_w, u_int cell_h, u_int ox,
|
||||
if (cell_h == 0)
|
||||
cell_h = si->cell_h;
|
||||
|
||||
pox = ox * si->cell_w;
|
||||
poy = oy * si->cell_h;
|
||||
psx = sx * si->cell_w;
|
||||
psy = sy * si->cell_h;
|
||||
/*
|
||||
* Map cell boundaries over the actual raster, not the rounded-up cell
|
||||
* canvas. Otherwise a raster shorter than its last cell row produces an
|
||||
* empty strip when it is scaled for output.
|
||||
*/
|
||||
x0 = (uint64_t)ox * si->sx / cx;
|
||||
x1 = (uint64_t)(ox + sx) * si->sx / cx;
|
||||
y0 = (uint64_t)oy * si->sy / cy;
|
||||
y1 = (uint64_t)(oy + sy) * si->sy / cy;
|
||||
pox = x0;
|
||||
poy = y0;
|
||||
psx = x1 - x0;
|
||||
psy = y1 - y0;
|
||||
|
||||
tsx = sx * cell_w;
|
||||
tsy = sy * cell_h;
|
||||
@@ -1445,6 +1455,21 @@ sixel_draw_rect(struct tty *tty, const struct image_rect *rectangle,
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* Remove old SIXEL pixels before replaying a dirty image area. */
|
||||
void
|
||||
sixel_redraw_start(struct tty *tty, u_int x, u_int y, u_int sx, u_int sy)
|
||||
{
|
||||
u_int yy;
|
||||
|
||||
for (yy = y; yy < y + sy; yy++) {
|
||||
tty_cursor(tty, x, yy);
|
||||
if (tty_term_has(tty->term, TTYC_ECH))
|
||||
tty_putcode_i(tty, TTYC_ECH, sx);
|
||||
else
|
||||
tty_repeat_space(tty, sx);
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert a SIXEL image to a fallback screen. */
|
||||
struct screen *
|
||||
sixel_to_screen(struct sixel_image *si)
|
||||
|
||||
41
input.c
41
input.c
@@ -947,6 +947,7 @@ input_reset(struct input_ctx *ictx, int clear)
|
||||
screen_write_start_pane(sctx, wp, &wp->base);
|
||||
else
|
||||
screen_write_start(sctx, &wp->base);
|
||||
sctx->flags |= SCREEN_WRITE_INPUT;
|
||||
screen_write_reset(sctx);
|
||||
screen_write_stop(sctx);
|
||||
}
|
||||
@@ -1074,6 +1075,7 @@ input_parse_buffer(struct window_pane *wp, const u_char *buf, size_t len)
|
||||
screen_write_start_pane(sctx, wp, &wp->base);
|
||||
else
|
||||
screen_write_start(sctx, &wp->base);
|
||||
sctx->flags |= SCREEN_WRITE_INPUT;
|
||||
|
||||
log_debug("%s: %%%u %s, %zu bytes: %.*s", __func__, wp->id,
|
||||
ictx->state->name, len, (int)len, buf);
|
||||
@@ -1093,6 +1095,7 @@ input_parse_screen(struct input_ctx *ictx, struct screen *s,
|
||||
return;
|
||||
|
||||
screen_write_start_callback(sctx, s, cb, arg);
|
||||
sctx->flags |= SCREEN_WRITE_INPUT;
|
||||
input_parse(ictx, buf, len);
|
||||
screen_write_stop(sctx);
|
||||
}
|
||||
@@ -2809,7 +2812,9 @@ input_handle_kitty(struct input_ctx *ictx, const u_char *buf, size_t len)
|
||||
struct window_pane *wp = ictx->wp;
|
||||
struct image *im;
|
||||
u_int image_id = 0, replace_id = 0, quiet = 0;
|
||||
char action = '\0';
|
||||
u_int placement_id = 0;
|
||||
int32_t z = 0;
|
||||
char action = '\0', delete = '\0';
|
||||
int status;
|
||||
|
||||
if (wp == NULL)
|
||||
@@ -2817,7 +2822,7 @@ input_handle_kitty(struct input_ctx *ictx, const u_char *buf, size_t len)
|
||||
im = kitty_parse_image(&ictx->kitty_state, buf, len,
|
||||
wp->window->xpixel,
|
||||
wp->window->ypixel, &image_id, &replace_id, &quiet, &action,
|
||||
&status);
|
||||
&delete, &placement_id, &z, &status);
|
||||
if (status == KITTY_PARSE_MORE)
|
||||
return (1);
|
||||
if (status != KITTY_PARSE_OK) {
|
||||
@@ -2835,14 +2840,15 @@ input_handle_kitty(struct input_ctx *ictx, const u_char *buf, size_t len)
|
||||
image_clear(sctx, replace_id);
|
||||
if (im != NULL) {
|
||||
if (action == 'd')
|
||||
image_clear(sctx, image_get_id(im));
|
||||
image_clear_kitty(sctx, delete, image_id, placement_id, z);
|
||||
else
|
||||
image_write(sctx, im, ictx->cell.cell.bg);
|
||||
image_write_kitty(sctx, im, ictx->cell.cell.bg, image_id,
|
||||
placement_id, z);
|
||||
image_free(image_get_id(im));
|
||||
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, 0);
|
||||
} else if (action == 'd')
|
||||
image_clear_kitty(sctx, delete, image_id, placement_id, z);
|
||||
else if ((action == 't' || action == 'q' || action == 'u') && quiet == 0)
|
||||
input_reply(ictx, 0, "\033_Gi=%u;OK\033\\", image_id);
|
||||
return (1);
|
||||
@@ -2927,9 +2933,10 @@ input_top_bit_set(struct input_ctx *ictx)
|
||||
struct screen_write_ctx *sctx = &ictx->ctx;
|
||||
struct utf8_data *ud = &ictx->utf8data;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell gc, left;
|
||||
const struct grid_cell *gcl;
|
||||
u_int x;
|
||||
struct grid_cell gc;
|
||||
struct image *im;
|
||||
u_int x, source_x, source_y, image_id, placement_id;
|
||||
int32_t z;
|
||||
#endif
|
||||
|
||||
ictx->flags &= ~INPUT_LAST;
|
||||
@@ -2962,18 +2969,12 @@ input_top_bit_set(struct input_ctx *ictx)
|
||||
if (sctx->s->cx != 0) {
|
||||
x = sctx->s->cx - 1; /* cx-1 is the cell just written. */
|
||||
grid_view_get_cell(sctx->s->grid, x, sctx->s->cy, &gc);
|
||||
/* The preceding cell provides context for implicit coordinates. */
|
||||
if (x == 0) {
|
||||
gcl = NULL;
|
||||
} else {
|
||||
grid_view_get_cell(sctx->s->grid, x - 1, sctx->s->cy, &left);
|
||||
gcl = &left;
|
||||
}
|
||||
|
||||
/* Convert a recognized Kitty placeholder in gc to an image marker. */
|
||||
if (kitty_placeholder_to_cell(ictx->kitty_state, &gc, gcl)) {
|
||||
if (kitty_placeholder_to_image(ictx->kitty_state,
|
||||
sctx->s->grid, &gc, x, sctx->s->cy, &im, &source_x,
|
||||
&source_y, &image_id, &placement_id, &z)) {
|
||||
grid_view_set_cell(sctx->s->grid, x, sctx->s->cy, &gc);
|
||||
image_redraw_area(sctx, x, sctx->s->cy, 1, 1);
|
||||
image_place_cell_kitty(sctx, im, x, sctx->s->cy, source_x,
|
||||
source_y, image_id, placement_id, z);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Grid-resident image markers: Kitty input, history, copy and overwrite.
|
||||
# Grid-resident image layers: Kitty input, history, copy and overwrite.
|
||||
|
||||
PATH=/bin:/usr/bin
|
||||
TERM=screen
|
||||
@@ -30,7 +30,7 @@ sleep 1
|
||||
$TMUX capture-pane -pS- >$TMP || exit 1
|
||||
grep '_G' $TMP >/dev/null && exit 1
|
||||
|
||||
# Copy mode duplicates the backing grid, including image marker references.
|
||||
# Copy mode duplicates the backing grid, including image layers.
|
||||
$TMUX copy-mode || exit 1
|
||||
$TMUX send-keys -X history-top || exit 1
|
||||
$TMUX capture-pane -p >$TMP || exit 1
|
||||
@@ -63,7 +63,7 @@ $TMUX new-window -d "
|
||||
sleep 1
|
||||
[ "$($TMUX display-message -pt:3 '#{cursor_y}')" = 1 ] || exit 1
|
||||
|
||||
# SIXEL input reaches the same grid marker and copy-mode paths.
|
||||
# SIXEL input reaches the same grid layer and copy-mode paths.
|
||||
$TMUX new-window -d "cat '$FIXTURE'; sleep 10"
|
||||
sleep 1
|
||||
[ "$($TMUX display-message -pt:4 '#{cursor_y}')" -gt 0 ] || exit 1
|
||||
@@ -145,7 +145,7 @@ $TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = "*" ] || exit 1
|
||||
[ "$(sed -n 3p $TMP)" = "@" ] || exit 1
|
||||
|
||||
# Image marker rows remain cell-aligned when a narrower terminal causes text
|
||||
# Image rows remain cell-aligned when a narrower terminal causes text
|
||||
# reflow. The ten-column rows are clipped to five columns, not split into four
|
||||
# wrapped rows.
|
||||
$TMUX2 new-window -d "
|
||||
@@ -225,7 +225,7 @@ $TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ -z "$(sed -n 2p $TMP)" ] || exit 1
|
||||
[ "$(sed -n 4p $TMP)" = " @@" ] || exit 1
|
||||
|
||||
# Image markers retain the cells beneath them. Deleting a transparent image
|
||||
# Image layers retain the cells beneath them. Deleting a transparent image
|
||||
# must reveal the original text rather than replacing it with spaces.
|
||||
PLACEMENT_WINDOW=$($TMUX2 new-window -dP -F '#{window_id}' "
|
||||
printf 'XY'
|
||||
@@ -259,6 +259,44 @@ $TMUX capture-pane -pS0 -E1 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "test" ] || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = "test" ] || exit 1
|
||||
|
||||
# Kitty z-indexes and SIXEL's temporal plane are properties of the input, not
|
||||
# of the outer terminal. Exercise the logical scene through the fallback
|
||||
# backend: higher nonnegative z-indexes cover lower ones and text, ordinary
|
||||
# negative z-indexes cover backgrounds but not text, and very negative ones
|
||||
# remain below the background.
|
||||
Z_WINDOW=$($TMUX2 new-window -dP -F '#{window_id}' "
|
||||
printf '\033_Ga=t,q=2,f=32,s=1,v=1,i=21;/wAA/w==\033\\'
|
||||
printf '\033_Ga=t,q=2,f=32,s=1,v=1,i=22;/////w==\033\\'
|
||||
printf 'X\r'
|
||||
printf '\033_Ga=p,q=2,C=1,i=21,p=1,z=2,c=1,r=1\033\\'
|
||||
printf '\033_Ga=p,q=2,C=1,i=22,p=2,z=1,c=1,r=1\033\\'
|
||||
printf '\033[2;1H\033[41m \033[0m\r'
|
||||
printf '\033_Ga=p,q=2,C=1,i=21,p=3,z=-1,c=1,r=1\033\\'
|
||||
printf '\033[3;1HY\r'
|
||||
printf '\033_Ga=p,q=2,C=1,i=21,p=4,z=-1,c=1,r=1\033\\'
|
||||
printf '\033[4;1H\033[41m \033[0m\r'
|
||||
printf '\033_Ga=p,q=2,C=1,i=21,p=5,z=-1073741825,c=1,r=1\033\\'
|
||||
$TEST_TMUX wait-for image-z-top-$$
|
||||
printf '\033_Ga=d,d=z,q=2,z=2\033\\'
|
||||
$TEST_TMUX wait-for image-z-lower-$$
|
||||
printf '\033_Ga=d,d=z,q=2,z=1\033\\'
|
||||
sleep 10") || exit 1
|
||||
$TMUX2 select-window -t"$Z_WINDOW" || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E3 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "." ] || exit 1
|
||||
[ "$(sed -n 2p $TMP)" = "." ] || exit 1
|
||||
[ "$(sed -n 3p $TMP)" = "Y" ] || exit 1
|
||||
[ -z "$(sed -n 4p $TMP)" ] || exit 1
|
||||
$TMUX2 wait-for -S image-z-top-$$ || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "@" ] || exit 1
|
||||
$TMUX2 wait-for -S image-z-lower-$$ || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -pS0 -E0 >$TMP || exit 1
|
||||
[ "$(sed -n 1p $TMP)" = "X" ] || exit 1
|
||||
|
||||
# A weighted median at the maximum channel level must still leave colours on
|
||||
# both sides of the split. This skewed black, grey and white image used to stop
|
||||
# palette generation after one colour instead of producing three.
|
||||
@@ -277,4 +315,22 @@ $TMUX send-keys Enter || exit 1
|
||||
sleep 1
|
||||
grep -a '#2;2;' $TMP >/dev/null || exit 1
|
||||
|
||||
# A pane redraw on a SIXEL terminal must not clear the status line, which is
|
||||
# outside the window scene and may not be redrawn after the pane scrolls.
|
||||
$TMUX kill-server 2>/dev/null
|
||||
$TMUX2 kill-server 2>/dev/null
|
||||
$TMUX2 new-session -d -x 20 -y 8 || exit 1
|
||||
$TMUX2 set -g 'status-format[0]' 'STATUS' || exit 1
|
||||
$TMUX2 set -as terminal-features ',*:sixel' || exit 1
|
||||
$TMUX new-session -d -x 20 -y 8 || exit 1
|
||||
$TMUX set -g status off || exit 1
|
||||
$TMUX send-keys -l "$TMUX2 attach-session" || exit 1
|
||||
$TMUX send-keys Enter || exit 1
|
||||
sleep 1
|
||||
$TMUX2 send-keys -l 'seq 30' || exit 1
|
||||
$TMUX2 send-keys Enter || exit 1
|
||||
sleep 1
|
||||
$TMUX capture-pane -p >$TMP || exit 1
|
||||
[ "$(tail -n 1 $TMP)" = "STATUS" ] || exit 1
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -1969,8 +1969,12 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
|
||||
}
|
||||
tty_sync_start(tty);
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (wp == NULL && (flags & REDRAW_PANE))
|
||||
image_redraw_start(tty, 0, 0, tty->sx, tty->sy);
|
||||
if (wp == NULL && (flags & REDRAW_PANE)) {
|
||||
y = 0;
|
||||
if (dctx.flags & REDRAW_STATUS_TOP)
|
||||
y = dctx.status_lines;
|
||||
image_redraw_start(tty, 0, y, scene->sx, scene->sy);
|
||||
}
|
||||
#endif
|
||||
tty_update_mode(tty, tty->mode & ~CURSOR_MODES, NULL);
|
||||
|
||||
|
||||
@@ -34,6 +34,10 @@ static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
|
||||
static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
|
||||
static void screen_write_collect_flush(struct screen_write_ctx *, int,
|
||||
const char *);
|
||||
#ifdef ENABLE_IMAGES
|
||||
static void screen_write_image_damage(struct screen_write_ctx *, u_int,
|
||||
u_int, u_int, u_int);
|
||||
#endif
|
||||
static int screen_write_overwrite(struct screen_write_ctx *,
|
||||
struct grid_cell *, u_int);
|
||||
static int screen_write_combine(struct screen_write_ctx *,
|
||||
@@ -377,6 +381,19 @@ screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
|
||||
ctx->bg = 8;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Damage temporal images when application input writes text cells. */
|
||||
static void
|
||||
screen_write_image_damage(struct screen_write_ctx *ctx, u_int x, u_int y,
|
||||
u_int sx, u_int sy)
|
||||
{
|
||||
image_redraw_area(ctx, x, y, sx, sy);
|
||||
if (ctx->flags & SCREEN_WRITE_INPUT)
|
||||
image_grid_damage(ctx->s->grid, x, ctx->s->grid->hsize + y,
|
||||
sx, sy);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialize writing with a pane. */
|
||||
void
|
||||
screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp,
|
||||
@@ -1339,6 +1356,9 @@ screen_write_alignmenttest(struct screen_write_ctx *ctx)
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_all(ctx);
|
||||
if (ctx->flags & SCREEN_WRITE_INPUT)
|
||||
image_grid_damage(s->grid, 0, s->grid->hsize,
|
||||
screen_size_x(s), screen_size_y(s));
|
||||
#endif
|
||||
|
||||
for (yy = 0; yy < screen_size_y(s); yy++) {
|
||||
@@ -2580,7 +2600,7 @@ screen_write_collect_end(struct screen_write_ctx *ctx)
|
||||
}
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_redraw_area(ctx, s->cx, s->cy, ci->used, 1);
|
||||
screen_write_image_damage(ctx, s->cx, s->cy, ci->used, 1);
|
||||
#endif
|
||||
|
||||
grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
|
||||
@@ -2666,9 +2686,6 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
struct grid_line *gl;
|
||||
struct grid_cell_entry *gce;
|
||||
struct grid_cell tmp_gc, now_gc;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell image_gc;
|
||||
#endif
|
||||
struct tty_ctx ttyctx;
|
||||
u_int sx = screen_size_x(s), sy = screen_size_y(s);
|
||||
u_int width = ud->width, xx, not_wrap, i, n, vis;
|
||||
@@ -2714,18 +2731,8 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
screen_write_initctx(ctx, &ttyctx, 0, 0);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Update the text underlay without removing an image placement. */
|
||||
grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
|
||||
if ((now_gc.flags & GRID_FLAG_IMAGE) &&
|
||||
(~gc->flags & GRID_FLAG_IMAGE)) {
|
||||
image_redraw_area(ctx, s->cx, s->cy, width, 1);
|
||||
memcpy(&image_gc, gc, sizeof image_gc);
|
||||
image_gc.flags |= GRID_FLAG_IMAGE|GRID_FLAG_IMAGE_DAMAGED;
|
||||
image_gc.image_id = now_gc.image_id;
|
||||
image_gc.image_x = now_gc.image_x;
|
||||
image_gc.image_y = now_gc.image_y;
|
||||
gc = &image_gc;
|
||||
}
|
||||
/* Input semantics decide which image layers this text damages. */
|
||||
screen_write_image_damage(ctx, s->cx, s->cy, width, 1);
|
||||
#endif
|
||||
|
||||
/* Handle overwriting of UTF-8 characters. */
|
||||
@@ -2955,6 +2962,9 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
|
||||
force_wide = 0;
|
||||
|
||||
/* Set the new cell. */
|
||||
#ifdef ENABLE_IMAGES
|
||||
screen_write_image_damage(ctx, cx - n, cy, n, 1);
|
||||
#endif
|
||||
grid_view_set_cell(gd, cx - n, cy, &last);
|
||||
if (force_wide)
|
||||
grid_view_set_padding(gd, cx - 1, cy, last.bg);
|
||||
@@ -3103,7 +3113,7 @@ screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
|
||||
sixel_free(si);
|
||||
return;
|
||||
}
|
||||
image_write(ctx, im, bg);
|
||||
image_write_sixel(ctx, im, bg);
|
||||
image_free(image_get_id(im));
|
||||
}
|
||||
#endif
|
||||
|
||||
3
style.c
3
style.c
@@ -31,9 +31,6 @@
|
||||
/* Default style. */
|
||||
static struct style style_default = {
|
||||
{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0
|
||||
#ifdef ENABLE_IMAGES
|
||||
, 0, 0, 0
|
||||
#endif
|
||||
},
|
||||
0,
|
||||
0,
|
||||
|
||||
66
tmux.h
66
tmux.h
@@ -80,8 +80,8 @@ struct session;
|
||||
struct image;
|
||||
struct image_backend;
|
||||
struct image_rect;
|
||||
#endif
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image_line;
|
||||
struct image_store;
|
||||
struct sixel_image;
|
||||
#endif
|
||||
|
||||
@@ -809,8 +809,6 @@ struct colour_palette {
|
||||
#define GRID_FLAG_NOPALETTE 0x20
|
||||
#define GRID_FLAG_CLEARED 0x40
|
||||
#define GRID_FLAG_TAB 0x80
|
||||
#define GRID_FLAG_IMAGE 0x100
|
||||
#define GRID_FLAG_IMAGE_DAMAGED 0x200
|
||||
|
||||
/* Grid line flags. */
|
||||
#define GRID_LINE_WRAPPED 0x1
|
||||
@@ -871,11 +869,6 @@ struct grid_cell {
|
||||
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. */
|
||||
@@ -887,11 +880,6 @@ struct grid_extd_entry {
|
||||
int bg;
|
||||
int us;
|
||||
u_int link;
|
||||
#ifdef ENABLE_IMAGES
|
||||
u_short image_id;
|
||||
u_short image_x;
|
||||
u_short image_y;
|
||||
#endif
|
||||
} __packed;
|
||||
|
||||
/* Grid cell entry. */
|
||||
@@ -929,6 +917,9 @@ struct grid_line {
|
||||
u_int time;
|
||||
struct osc133_data osc133_data;
|
||||
u_short flags;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image_line *images;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Entire grid of cells. */
|
||||
@@ -948,6 +939,9 @@ struct grid {
|
||||
u_int scroll_generation;
|
||||
|
||||
struct grid_line *linedata;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct image_store *images;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Virtual cursor in a grid. */
|
||||
@@ -1116,6 +1110,7 @@ struct screen_write_ctx {
|
||||
#define SCREEN_WRITE_SYNC 0x1
|
||||
#define SCREEN_WRITE_OBSCURED 0x2
|
||||
#define SCREEN_WRITE_CHECKED_IF_OBSCURED 0x4
|
||||
#define SCREEN_WRITE_INPUT 0x8
|
||||
|
||||
screen_write_init_ctx_cb init_ctx_cb;
|
||||
void *arg;
|
||||
@@ -4238,7 +4233,6 @@ char *regsub(const char *, const char *, const char *, int);
|
||||
/* image.c */
|
||||
#define IMAGE_BACKEND_GRAPHICAL 0x1
|
||||
#define IMAGE_BACKEND_SCROLLS 0x2
|
||||
#define IMAGE_BACKEND_TEMPORAL 0x4
|
||||
|
||||
struct image *image_create(u_int, u_int, u_int, u_int, u_int, u_int,
|
||||
u_char *);
|
||||
@@ -4246,8 +4240,6 @@ struct image *image_create_view(struct image *, u_int, u_int, u_int,
|
||||
u_int, u_int, u_int, u_int, u_int);
|
||||
struct image *image_find(u_int);
|
||||
u_int image_get_id(const struct image *);
|
||||
u_short image_get_grid_id(u_int);
|
||||
u_int image_get_id_by_grid_id(u_short);
|
||||
void image_get_size(const struct image *, u_int *, u_int *);
|
||||
void image_get_canvas_size(const struct image *, u_int *, u_int *);
|
||||
void image_get_size_in_cells(const struct image *, u_int *, u_int *);
|
||||
@@ -4257,9 +4249,10 @@ struct sixel_image *image_get_sixel(const struct image *);
|
||||
void image_set_sixel(struct image *, struct sixel_image *);
|
||||
void image_ref(u_int);
|
||||
void image_free(u_int);
|
||||
void image_set_cell(struct grid_cell *, struct image *, u_int,
|
||||
void image_write_sixel(struct screen_write_ctx *, struct image *,
|
||||
u_int);
|
||||
void image_write(struct screen_write_ctx *, struct image *, u_int);
|
||||
void image_write_kitty(struct screen_write_ctx *, struct image *,
|
||||
u_int, u_int, u_int, int32_t);
|
||||
void image_get_pixel_rect(const struct image *, u_int, u_int,
|
||||
u_int, u_int, u_int *, u_int *, u_int *, u_int *);
|
||||
void image_size_in_cells(u_int, u_int, u_int, u_int, u_int *,
|
||||
@@ -4273,8 +4266,6 @@ void image_redraw_all(struct screen_write_ctx *);
|
||||
void image_redraw_scroll(struct screen_write_ctx *, u_int);
|
||||
void image_redraw_start(struct tty *, u_int, u_int, u_int, u_int);
|
||||
int image_backend_flags(struct tty *);
|
||||
int image_get_draw_cell(struct tty *, const struct grid_cell *,
|
||||
struct grid_cell *, const struct tty_style_ctx *);
|
||||
void image_tty_update(struct tty *);
|
||||
void image_tty_geometry_changed(struct tty *);
|
||||
void image_tty_free(struct tty *, int);
|
||||
@@ -4283,20 +4274,44 @@ void image_draw_line(struct tty *, struct screen *, u_int, u_int,
|
||||
void image_get_fallback_cell(struct tty *, struct image *, u_int,
|
||||
u_int, const struct grid_cell *, struct grid_cell *,
|
||||
const struct tty_style_ctx *);
|
||||
int image_get_fallback_at(struct tty *, struct screen *, u_int,
|
||||
u_int, const struct grid_cell *, struct grid_cell *,
|
||||
const struct tty_style_ctx *);
|
||||
struct image *image_rect_get_image(const struct image_rect *);
|
||||
const struct grid_cell *image_rect_get_cell(
|
||||
const struct image_rect *);
|
||||
void image_rect_get_coords(const struct image_rect *,
|
||||
u_int *, u_int *, u_int *, u_int *, u_int *, u_int *);
|
||||
int32_t image_rect_get_z(const struct image_rect *);
|
||||
void image_clear(struct screen_write_ctx *, u_int);
|
||||
void image_clear_kitty(struct screen_write_ctx *, char, u_int,
|
||||
u_int, int32_t);
|
||||
void image_grid_damage(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);
|
||||
void image_grid_copy_area(struct grid *, u_int, u_int, struct grid *,
|
||||
u_int, u_int, u_int, u_int);
|
||||
int image_grid_line_has_images(const struct grid_line *);
|
||||
int image_grid_check_area(struct grid *, u_int, u_int, u_int,
|
||||
u_int);
|
||||
int image_grid_get_source(struct grid *, u_int, u_int,
|
||||
struct image *, u_int *, u_int *);
|
||||
void image_place_cell_kitty(struct screen_write_ctx *, struct image *,
|
||||
u_int, u_int, u_int, u_int, u_int, u_int, int32_t);
|
||||
#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 *, u_int *, char *, int *);
|
||||
int kitty_placeholder_to_cell(void *, struct grid_cell *,
|
||||
const struct grid_cell *);
|
||||
u_int, u_int *, u_int *, u_int *, char *, char *, u_int *,
|
||||
int32_t *, int *);
|
||||
int kitty_placeholder_to_image(void *, struct grid *,
|
||||
struct grid_cell *, u_int, u_int, struct image **, u_int *,
|
||||
u_int *, u_int *, u_int *, int32_t *);
|
||||
void kitty_free_state(void *);
|
||||
void kitty_draw_rect(struct tty *,
|
||||
const struct image_rect *, const struct tty_style_ctx *);
|
||||
@@ -4309,7 +4324,8 @@ void kitty_geometry_changed(struct tty *);
|
||||
/* image-sixel.c */
|
||||
#define SIXEL_COLOUR_REGISTERS 1024
|
||||
void sixel_draw_rect(struct tty *,
|
||||
const struct image_rect *, const struct tty_style_ctx *);
|
||||
const struct image_rect *, const struct tty_style_ctx *);
|
||||
void sixel_redraw_start(struct tty *, u_int, u_int, u_int, u_int);
|
||||
void sixel_free_output(struct tty *, int);
|
||||
void sixel_geometry_changed(struct tty *);
|
||||
struct sixel_image *sixel_parse(const char *, size_t, u_int, u_int, u_int,
|
||||
|
||||
26
tty-draw.c
26
tty-draw.c
@@ -125,6 +125,7 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
struct grid_cell gc, ngc, last;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell image_gc;
|
||||
int image_status;
|
||||
#endif
|
||||
struct grid_line *gl;
|
||||
u_int i, j, last_i, cx, ex, width;
|
||||
@@ -250,21 +251,26 @@ tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
|
||||
|
||||
if (px >= ex || i >= ex - px) {
|
||||
/* Outside the area being drawn. */
|
||||
empty = nx - i;
|
||||
gcp = &grid_default_cell;
|
||||
#ifdef ENABLE_IMAGES
|
||||
image_status = image_get_fallback_at(tty, s, px + i,
|
||||
py, gcp, &image_gc, style_ctx);
|
||||
if (image_status == 1) {
|
||||
gcp = &image_gc;
|
||||
empty = 0;
|
||||
} else if (image_status == -1)
|
||||
empty = 1;
|
||||
else
|
||||
#endif
|
||||
empty = nx - i;
|
||||
} else {
|
||||
/* Get the current cell. */
|
||||
grid_view_get_cell(gd, px + i, py, &gc);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (gc.flags & GRID_FLAG_IMAGE) {
|
||||
(void)image_get_draw_cell(tty, &gc, &image_gc,
|
||||
style_ctx);
|
||||
gcp = &image_gc;
|
||||
} else
|
||||
gcp = &gc;
|
||||
#else
|
||||
gcp = &gc;
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (image_get_fallback_at(tty, s, px + i, py, &gc,
|
||||
&image_gc, style_ctx) == 1)
|
||||
gcp = &image_gc;
|
||||
#endif
|
||||
|
||||
/* Work out empty cells. */
|
||||
|
||||
11
tty.c
11
tty.c
@@ -2132,9 +2132,6 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
|
||||
const struct tty_style_ctx *style_ctx)
|
||||
{
|
||||
const struct grid_cell *gcp;
|
||||
#ifdef ENABLE_IMAGES
|
||||
struct grid_cell image_gc;
|
||||
#endif
|
||||
|
||||
/* Skip last character if terminal is stupid. */
|
||||
if ((tty->term->flags & TERM_NOAM) &&
|
||||
@@ -2150,14 +2147,6 @@ tty_cell(struct tty *tty, const struct grid_cell *gc,
|
||||
if (!tty_check_overlay(tty, tty->cx, tty->cy))
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
if (gc->flags & GRID_FLAG_IMAGE) {
|
||||
if (image_get_draw_cell(tty, gc, &image_gc, style_ctx))
|
||||
return;
|
||||
gc = &image_gc;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Check the output codeset and apply attributes. */
|
||||
gcp = tty_check_codeset(tty, gc);
|
||||
tty_attributes(tty, gcp, style_ctx);
|
||||
|
||||
@@ -5408,6 +5408,14 @@ window_copy_write_line(struct window_mode_entry *wme,
|
||||
window_copy_write_one(wme, ctx, width, py, hsize - data->oy + py,
|
||||
content_sx, &mgc, &cgc, &mkgc, &clgc);
|
||||
|
||||
#ifdef ENABLE_IMAGES
|
||||
/* Copy the backing line's image layers separately from its text cells. */
|
||||
image_grid_free_line(s->grid,
|
||||
&s->grid->linedata[s->grid->hsize + py]);
|
||||
image_grid_copy_area(s->grid, width, s->grid->hsize + py,
|
||||
data->backing->grid, 0, hsize - data->oy + py, content_sx, 1);
|
||||
#endif
|
||||
|
||||
if (py == 0 && s->rupper < s->rlower && !data->hide_position) {
|
||||
value = options_get_string(oo, "copy-mode-position-format");
|
||||
if (*value != '\0') {
|
||||
@@ -6188,9 +6196,7 @@ 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_IMAGE)
|
||||
utf8_set(&ud, ' ');
|
||||
else if (gc.flags & GRID_FLAG_TAB)
|
||||
if (gc.flags & GRID_FLAG_TAB)
|
||||
utf8_set(&ud, '\t');
|
||||
else
|
||||
utf8_copy(&ud, &gc.data);
|
||||
|
||||
Reference in New Issue
Block a user