Merge with visible_ranges banch. Convert visible_ranges to new style.

This commit is contained in:
Michael Grant
2026-01-28 15:26:00 +00:00
11 changed files with 758 additions and 475 deletions

View File

@@ -198,6 +198,7 @@ dist_tmux_SOURCES = \
tmux.h \ tmux.h \
tmux-protocol.h \ tmux-protocol.h \
tty-acs.c \ tty-acs.c \
tty-draw.c \
tty-features.c \ tty-features.c \
tty-keys.c \ tty-keys.c \
tty-term.c \ tty-term.c \

2
grid.c
View File

@@ -235,7 +235,7 @@ grid_check_y(struct grid *gd, const char *from, u_int py)
int int
grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
{ {
int flags1 = gc1->flags, flags2 = gc2->flags;; int flags1 = gc1->flags, flags2 = gc2->flags;
if (gc1->fg != gc2->fg || gc1->bg != gc2->bg) if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
return (0); return (0);

10
menu.c
View File

@@ -34,6 +34,7 @@ struct menu_data {
struct cmd_find_state fs; struct cmd_find_state fs;
struct screen s; struct screen s;
struct visible_ranges r;
u_int px; u_int px;
u_int py; u_int py;
@@ -181,15 +182,16 @@ menu_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
} }
/* Return parts of the input range which are not obstructed by the menu. */ /* Return parts of the input range which are not obstructed by the menu. */
void struct visible_ranges *
menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py, menu_check_cb(__unused struct client *c, void *data, u_int px, u_int py,
u_int nx, struct overlay_ranges *r) u_int nx)
{ {
struct menu_data *md = data; struct menu_data *md = data;
struct menu *menu = md->menu; struct menu *menu = md->menu;
server_client_overlay_range(md->px, md->py, menu->width + 4, server_client_overlay_range(md->px, md->py, menu->width + 4,
menu->count + 2, px, py, nx, r); menu->count + 2, px, py, nx, &md->r);
return (&md->r);
} }
void void
@@ -232,7 +234,9 @@ menu_free_cb(__unused struct client *c, void *data)
if (md->cb != NULL) if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data); md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
free(md->r.ranges);
screen_free(&md->s); screen_free(&md->s);
menu_free(md->menu); menu_free(md->menu);
free(md); free(md);
} }

65
popup.c
View File

@@ -39,6 +39,9 @@ struct popup_data {
struct grid_cell defaults; struct grid_cell defaults;
struct colour_palette palette; struct colour_palette palette;
struct visible_ranges r;
struct visible_ranges or[2];
struct job *job; struct job *job;
struct input_ctx *ictx; struct input_ctx *ictx;
int status; int status;
@@ -164,48 +167,57 @@ popup_mode_cb(__unused struct client *c, void *data, u_int *cx, u_int *cy)
} }
/* Return parts of the input range which are not obstructed by the popup. */ /* Return parts of the input range which are not obstructed by the popup. */
static void static struct visible_ranges *
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx, popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
struct overlay_ranges *r)
{ {
struct popup_data *pd = data; struct popup_data *pd = data;
struct overlay_ranges or[2]; struct visible_ranges *r = &pd->r;
struct visible_ranges *mr;
u_int i, j, k = 0; u_int i, j, k = 0;
if (pd->md != NULL) { if (pd->md != NULL) {
/* Check each returned range for the menu against the popup. */ /*
menu_check_cb(c, pd->md, px, py, nx, r); * Work out the visible ranges for the menu (that is, the
for (i = 0; i < 2; i++) { * ranges not covered by the menu). A menu should have at most
* two ranges and we rely on this being the case.
*/
mr = menu_check_cb(c, pd->md, px, py, nx);
if (mr->used > 2)
fatalx("too many menu ranges");
/*
* Walk the ranges still visible under the menu and check if
* each is visible under the popup as well. At most there can be
* three total ranges if popup and menu do not intersect.
*/
for (i = 0; i < mr->used; i++) {
server_client_overlay_range(pd->px, pd->py, pd->sx, server_client_overlay_range(pd->px, pd->py, pd->sx,
pd->sy, r->px[i], py, r->nx[i], &or[i]); pd->sy, r->ranges[i].px, py, r->ranges[i].nx,
&pd->or[i]);
} }
/* /*
* or has up to OVERLAY_MAX_RANGES non-overlapping ranges, * We now have nonoverlapping ranges from left to right.
* ordered from left to right. Collect them in the output. * Combine them together into the output.
*/ */
for (i = 0; i < 2; i++) { server_client_ensure_ranges(r, 3);
/* Each or[i] only has 2 ranges. */ for (i = 0; i < mr->used; i++) {
for (j = 0; j < 2; j++) { for (j = 0; j < pd->or[i].used; j++) {
if (or[i].nx[j] > 0) { if (pd->or[i].ranges[j].nx == 0)
r->px[k] = or[i].px[j]; continue;
r->nx[k] = or[i].nx[j]; if (k >= 3)
fatalx("too many popup & menu ranges");
r->ranges[k].px = pd->or[i].ranges[j].px;
r->ranges[k].nx = pd->or[i].ranges[j].nx;
k++; k++;
} }
} }
} return (r);
/* Zero remaining ranges if any. */
for (i = k; i < OVERLAY_MAX_RANGES; i++) {
r->px[i] = 0;
r->nx[i] = 0;
}
return;
} }
server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx, server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx,
r); r);
return (r);
} }
static void static void
@@ -286,6 +298,9 @@ popup_free_cb(struct client *c, void *data)
job_free(pd->job); job_free(pd->job);
input_free(pd->ictx); input_free(pd->ictx);
free(pd->or[0].ranges);
free(pd->or[1].ranges);
free(pd->r.ranges);
screen_free(&pd->s); screen_free(&pd->s);
colour_palette_free(&pd->palette); colour_palette_free(&pd->palette);

View File

@@ -674,8 +674,9 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
struct tty *tty = &c->tty; struct tty *tty = &c->tty;
struct window_pane *wp; struct window_pane *wp;
struct screen *s; struct screen *s;
struct visible_ranges *vr; struct visible_ranges *r;
u_int i, x, width, size, r; struct visible_range *ri;
u_int i, l, x, width, size;
int xoff, yoff; int xoff, yoff;
log_debug("%s: %s @%u", __func__, c->name, w->id); log_debug("%s: %s @%u", __func__, c->name, w->id);
@@ -700,36 +701,38 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) { if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) {
/* All visible. */ /* All visible. */
i = 0; l = 0;
x = xoff - ctx->ox; x = xoff - ctx->ox;
width = size; width = size;
} else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) { } else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) {
/* Both left and right not visible. */ /* Both left and right not visible. */
i = ctx->ox; l = ctx->ox;
x = 0; x = 0;
width = ctx->sx; width = ctx->sx;
} else if (xoff < ctx->ox) { } else if (xoff < ctx->ox) {
/* Left not visible. */ /* Left not visible. */
i = ctx->ox - xoff; l = ctx->ox - xoff;
x = 0; x = 0;
width = size - i; width = size - i;
} else { } else {
/* Right not visible. */ /* Right not visible. */
i = 0; l = 0;
x = xoff - ctx->ox; x = xoff - ctx->ox;
width = size - x; width = size - x;
} }
vr = screen_redraw_get_visible_ranges(wp, x, yoff, width); r = tty_check_overlay_range(&c->tty, x, yoff, width);
r = screen_redraw_get_visible_ranges(wp, x, yoff, width, r);
if (ctx->statustop) if (ctx->statustop)
yoff += ctx->statuslines; yoff += ctx->statuslines;
for (r=0; r < vr->used; r++) { for (i=0; i < r->used; i++) {
if (vr->nx[r] == 0) ri = &r->ranges[i];
if (ri->nx == 0)
continue; continue;
tty_draw_line(tty, s, i + (vr->px[r] - x), 0, vr->nx[r], tty_draw_line(tty, s, l + (ri->px - x), 0, ri->nx,
vr->px[r], yoff - ctx->oy, ri->px, yoff - ctx->oy,
&grid_default_cell, NULL); &grid_default_cell, NULL);
} }
} }
@@ -986,13 +989,14 @@ screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
struct window_pane *wp, *active = server_client_get_pane(c); struct window_pane *wp, *active = server_client_get_pane(c);
struct grid_cell gc; struct grid_cell gc;
const struct grid_cell *tmp; const struct grid_cell *tmp;
struct overlay_ranges r; u_int cell_type;
u_int cell_type, x = ctx->ox + i, y = ctx->oy + j; u_int x = ctx->ox + i, y = ctx->oy + j;
int isolates; int isolates;
struct visible_ranges *r;
if (c->overlay_check != NULL) { if (c->overlay_check != NULL) {
c->overlay_check(c, c->overlay_data, x, y, 1, &r); r = c->overlay_check(c, c->overlay_data, x, y, 1);
if (r.nx[0] + r.nx[1] == 0) if (server_client_ranges_is_empty(r))
return; return;
} }
@@ -1106,18 +1110,20 @@ screen_redraw_draw_status(struct screen_redraw_ctx *ctx)
* floating window pane). Returns a boolean. * floating window pane). Returns a boolean.
*/ */
int int
screen_redraw_is_visible(struct visible_ranges *vr, u_int px) screen_redraw_is_visible(struct visible_ranges *r, u_int px)
{ {
u_int r; u_int i;
struct visible_range *ri;
/* No visible_ranges if called from a popup or menu. Always visible. */ /* No visible_ranges if called from a popup or menu. Always visible. */
if (vr == NULL) if (r == NULL)
return (1); return (1);
for (r=0; r < vr->used; r++) { for (i=0; i < r->used; i++) {
if (vr->nx[r] == 0) ri = &r->ranges[i];
if (ri->nx == 0)
continue; continue;
if ((px >= vr->px[r]) && (px <= vr->px[r] + vr->nx[r])) if ((px >= ri->px) && (px <= ri->px + ri->nx))
return (1); return (1);
} }
return (0); return (0);
@@ -1127,33 +1133,26 @@ screen_redraw_is_visible(struct visible_ranges *vr, u_int px)
cells of base_wp that are unobsructed. */ cells of base_wp that are unobsructed. */
struct visible_ranges * struct visible_ranges *
screen_redraw_get_visible_ranges(struct window_pane *base_wp, u_int px, screen_redraw_get_visible_ranges(struct window_pane *base_wp, u_int px,
u_int py, u_int width) { u_int py, u_int width, struct visible_ranges *r) {
struct window_pane *wp; struct window_pane *wp;
struct window *w; struct window *w;
static struct visible_ranges vr = {NULL, NULL, 0, 0}; struct visible_range *ri;
int found_self, sb_w, sb_pos; int found_self, sb_w, sb_pos;
u_int lb, rb, tb, bb; u_int lb, rb, tb, bb;
u_int r, s; u_int i, s;
/* For efficiency vr is static and space reused. */ if (r == NULL) {
if (vr.size == 0) { server_client_ensure_ranges(&base_wp->r, 1);
vr.px = xcalloc(1, sizeof(u_int)); r = &base_wp->r;
vr.nx = xcalloc(1, sizeof(u_int));
vr.size = 1;
}
/* debugging:
* display *vr.px@vr.used
* display *vr.nx@vr.used
*/
/* Start with the entire width of the range. */ /* Start with the entire width of the range. */
vr.px[0] = px; r->ranges[0].px = px;
vr.nx[0] = width; r->ranges[0].nx = width;
vr.used = 1; r->used = 1;
}
if (base_wp == NULL) if (base_wp == NULL)
return (&vr); return (r);
w = base_wp->window; w = base_wp->window;
sb_pos = options_get_number(w->options, "pane-scrollbars-position"); sb_pos = options_get_number(w->options, "pane-scrollbars-position");
@@ -1184,7 +1183,8 @@ screen_redraw_get_visible_ranges(struct window_pane *base_wp, u_int px,
sb_pos = 0; sb_pos = 0;
} }
for (r=0; r < vr.used; r++) { for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (sb_pos == PANE_SCROLLBARS_LEFT) { if (sb_pos == PANE_SCROLLBARS_LEFT) {
if (wp->xoff > sb_w) if (wp->xoff > sb_w)
lb = wp->xoff - 1 - sb_w; lb = wp->xoff - 1 - sb_w;
@@ -1206,58 +1206,52 @@ screen_redraw_get_visible_ranges(struct window_pane *base_wp, u_int px,
falls inside this range and right falls inside this range and right
edge covers up to right of range, edge covers up to right of range,
then shrink left edge of range. */ then shrink left edge of range. */
if (lb > vr.px[r] && if (lb > ri->px &&
lb < vr.px[r] + vr.nx[r] && lb < ri->px + ri->nx &&
rb >= vr.px[r] + vr.nx[r]) { rb >= ri->px + ri->nx) {
vr.nx[r] = lb - vr.px[r]; ri->nx = lb - ri->px;
} }
/* Else if the right edge of floating wp /* Else if the right edge of floating wp
falls inside of this range and left falls inside of this range and left
edge covers the left of range, edge covers the left of range,
then move px forward to right edge of wp. */ then move px forward to right edge of wp. */
else if (rb >= vr.px[r] && else if (rb >= ri->px &&
rb < vr.px[r] + vr.nx[r] && rb < ri->px + ri->nx &&
lb <= vr.px[r]) { lb <= ri->px) {
vr.nx[r] = vr.nx[r] - (rb + 1 - vr.px[r]); ri->nx = ri->nx - (rb + 1 - ri->px);
vr.px[r] = vr.px[r] + (rb + 1 - vr.px[r]); ri->px = ri->px + (rb + 1 - ri->px);
} }
/* Else if wp fully inside range /* Else if wp fully inside range
then split range into 2 ranges. */ then split range into 2 ranges. */
else if (lb > vr.px[r] && else if (lb > ri->px &&
rb < vr.px[r] + vr.nx[r]) { rb < ri->px + ri->nx) {
if (vr.size == vr.used) { server_client_ensure_ranges(r, r->size + 1);
vr.size++; for (s=r->used; s>i; s--)
vr.px = xreallocarray(vr.px, memcpy(&r->ranges[s-1], &r->ranges[s],
vr.size, sizeof (u_int)); sizeof (struct visible_range));
vr.nx = xreallocarray(vr.nx, r->ranges[i+1].px = rb + 1;
vr.size, sizeof (u_int)); r->ranges[i+1].nx = ri->px + ri->nx - (rb + 1);
} /* ri->px was copied, unchanged. */
for (s=vr.used; s>r; s--) { ri->nx = lb - ri->px;
vr.px[s] = vr.px[s-1]; r->used++;
vr.nx[s] = vr.nx[s-1];
}
vr.px[r+1] = rb + 1;
vr.nx[r+1] = (vr.px[r] + vr.nx[r]) - (rb + 1);
/* vr.px[r] was copied, unchanged. */
vr.nx[r] = lb - vr.px[r];
vr.used++;
} }
/* If floating wp completely covers this range /* If floating wp completely covers this range
* then delete it (make it 0 length). */ * then delete it (make it 0 length). */
else if (lb <= vr.px[r] && else if (lb <= ri->px &&
rb >= vr.px[r] + vr.nx[r]) { rb >= ri->px + ri->nx) {
vr.nx[r] = 0; ri->nx = 0;
} }
/* Else the range is already obscured, do nothing. */ /* Else the range is already obscured, do nothing. */
} }
} }
for (r=0; r<vr.used; r++) { for (i=0; i<r->used; i++) {
log_debug("%s: %%%u visible_range py=%u %u: [px=%u nx=%u]", ri = &r->ranges[i];
__func__, base_wp->id, py, r, vr.px[r], vr.nx[r]); log_debug("%s: %%%u visible_range py=%u r[%u]: [px=%u nx=%u]",
__func__, base_wp->id, py, i, ri->px, ri->nx);
} }
return (&vr); return (r);
} }
@@ -1271,8 +1265,9 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
struct screen *s = wp->screen; struct screen *s = wp->screen;
struct colour_palette *palette = &wp->palette; struct colour_palette *palette = &wp->palette;
struct grid_cell defaults; struct grid_cell defaults;
struct visible_ranges *vr; u_int i, j, woy, wx, wy, px, py, width;
u_int i, j, woy, wx, wy, px, py, width, r; struct visible_ranges *r;
struct visible_range *ri;
if (wp->base.mode & MODE_SYNC) if (wp->base.mode & MODE_SYNC)
screen_write_stop_sync(wp); screen_write_stop_sync(wp);
@@ -1329,21 +1324,18 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
__func__, c->name, wp->id, i, j, wx, wy, width); __func__, c->name, wp->id, i, j, wx, wy, width);
/* Get visible ranges of line before we draw it. */ /* Get visible ranges of line before we draw it. */
vr = screen_redraw_get_visible_ranges(wp, wx, wy, width); r = tty_check_overlay_range(tty, px, py, width);
r = screen_redraw_get_visible_ranges(wp, wx, wy, width, r);
tty_default_colours(&defaults, wp); tty_default_colours(&defaults, wp);
for (r=0; r < vr->used; r++) { for (i=0; i < r->used; i++) {
if (vr->nx[r] == 0) ri = &r->ranges[i];
if (ri->nx == 0)
continue; continue;
/* Convert window coordinates to tty coordinates. */ px = ri->px;
px = vr->px[r]; tty_draw_line(tty, s, ri->px - wp->xoff, j,
/* i is px of cell, add px of region, sub the ri->nx, px, py, &defaults, palette);
* pane offset. If you don't sub offset,
* contents of pane shifted. note: i apparently unnec.
*/
tty_draw_line(tty, s, /* i + */ vr->px[r] - wp->xoff, j,
vr->nx[r], px, py, &defaults, palette);
} }
} }
@@ -1435,7 +1427,7 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
int px, py, wx, wy, ox, oy, sx, sy; int px, py, wx, wy, ox, oy, sx, sy;
int xoff = wp->xoff; int xoff = wp->xoff;
int yoff = wp->yoff; int yoff = wp->yoff;
struct visible_ranges *vr; struct visible_ranges *r;
/* /*
* Size and offset of window relative to tty. * Size and offset of window relative to tty.
@@ -1487,7 +1479,8 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
for (j = jmin; j < jmax; j++) { for (j = jmin; j < jmax; j++) {
py = sb_y + j; /* tty y coordinate. */ py = sb_y + j; /* tty y coordinate. */
wy = sb_y + j + oy; /* window y coordinate. */ wy = sb_y + j + oy; /* window y coordinate. */
vr = screen_redraw_get_visible_ranges(wp, sb_x, wy, imax); r = tty_check_overlay_range(tty, sb_x, wy, imax);
r = screen_redraw_get_visible_ranges(wp, sb_x, wy, imax, r);
for (i = imin; i < imax; i++) { for (i = imin; i < imax; i++) {
px = sb_x + ox + i; /* tty x coordinate. */ px = sb_x + ox + i; /* tty x coordinate. */
wx = sb_x + i; /* window x coordinate. */ wx = sb_x + i; /* window x coordinate. */
@@ -1495,7 +1488,7 @@ screen_redraw_draw_scrollbar(struct screen_redraw_ctx *ctx,
px >= sx || px < 0 || px >= sx || px < 0 ||
wy < yoff - 1 || wy < yoff - 1 ||
py >= sy || py < 0 || py >= sy || py < 0 ||
! screen_redraw_is_visible(vr, wx)) ! screen_redraw_is_visible(r, wx))
continue; continue;
tty_cursor(tty, px, py); tty_cursor(tty, px, py);
if ((sb_pos == PANE_SCROLLBARS_LEFT && if ((sb_pos == PANE_SCROLLBARS_LEFT &&

View File

@@ -574,6 +574,7 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
struct grid *gd = src->grid; struct grid *gd = src->grid;
struct grid_cell gc; struct grid_cell gc;
u_int xx, yy, cx = s->cx, cy = s->cy; u_int xx, yy, cx = s->cx, cy = s->cy;
struct visible_ranges *r;
if (nx == 0 || ny == 0) if (nx == 0 || ny == 0)
return; return;
@@ -584,15 +585,19 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
s->cx = cx; s->cx = cx;
if (wp != NULL) if (wp != NULL)
screen_write_initctx(ctx, &ttyctx, 0); screen_write_initctx(ctx, &ttyctx, 0);
r = screen_redraw_get_visible_ranges(wp, px, py, nx, NULL);
for (xx = px; xx < px + nx; xx++) { for (xx = px; xx < px + nx; xx++) {
if (xx >= grid_get_line(gd, yy)->cellsize && if (xx >= grid_get_line(gd, yy)->cellsize &&
s->cx >= grid_get_line(ctx->s->grid, s->cy)->cellsize) s->cx >= grid_get_line(ctx->s->grid,
s->cy)->cellsize)
break; break;
grid_get_cell(gd, xx, yy, &gc); grid_get_cell(gd, xx, yy, &gc);
if (xx + gc.data.width > px + nx) if (xx + gc.data.width > px + nx)
break; break;
grid_view_set_cell(ctx->s->grid, s->cx, s->cy, &gc); grid_view_set_cell(ctx->s->grid, s->cx, s->cy, &gc);
if (wp != NULL) { if (wp != NULL) {
if (! screen_redraw_is_visible(r, px))
break;
ttyctx.cell = &gc; ttyctx.cell = &gc;
tty_write(tty_cmd_cell, &ttyctx); tty_write(tty_cmd_cell, &ttyctx);
ttyctx.ocx++; ttyctx.ocx++;
@@ -1817,12 +1822,13 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct screen_write_citem *ci, *tmp; struct screen_write_citem *ci, *tmp;
struct screen_write_cline *cl; struct screen_write_cline *cl;
u_int y, cx, cy, last, items = 0, r; u_int y, cx, cy, last, items = 0, i;
u_int wr_start, wr_end, wr_length, wsx, wsy; u_int wr_start, wr_end, wr_length, wsx, wsy;
int r_start, r_end, ci_start, ci_end; int r_start, r_end, ci_start, ci_end;
int xoff, yoff; int xoff, yoff;
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
struct visible_ranges *vr; struct visible_ranges *r;
struct visible_range *ri;
struct window_pane *wp = ctx->wp; struct window_pane *wp = ctx->wp;
if (s->mode & MODE_SYNC) { if (s->mode & MODE_SYNC) {
@@ -1879,8 +1885,8 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
continue; continue;
cl = &ctx->s->write_list[y]; cl = &ctx->s->write_list[y];
vr = screen_redraw_get_visible_ranges(wp, 0, y + yoff, r = screen_redraw_get_visible_ranges(wp, 0, y + yoff, wsx,
wsx); NULL);
last = UINT_MAX; last = UINT_MAX;
TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
@@ -1891,22 +1897,26 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
ci->x, last); ci->x, last);
} }
wr_length = 0; wr_length = 0;
for (r = 0; r < vr->used; r++) { for (i = 0; i < r->used; i++) {
if (vr->nx[r] == 0) continue; ri = &r->ranges[i];
r_start = vr->px[r]; if (ri->nx == 0) continue;
r_end = vr->px[r] + vr->nx[r]; r_start = ri->px;
r_end = ri->px + ri->nx;
ci_start = ci->x; ci_start = ci->x;
ci_end = ci->x + ci->used; ci_end = ci->x + ci->used;
if (ci_start + xoff > r_end || ci_end + xoff < r_start) if (ci_start + xoff > r_end ||
ci_end + xoff < r_start)
continue; continue;
if (r_start > ci_start + xoff) if (r_start > ci_start + xoff)
wr_start = ci_start + (r_start - (ci_start + xoff)); wr_start = ci_start +
(r_start - (ci_start + xoff));
else else
wr_start = ci_start; wr_start = ci_start;
if (ci_end + xoff > r_end) if (ci_end + xoff > r_end)
wr_end = ci_end - ((ci_end + xoff) - r_end); wr_end = ci_end -
((ci_end + xoff) - r_end);
else else
wr_end = ci_end; wr_end = ci_end;
wr_length = wr_end - wr_start; wr_length = wr_end - wr_start;
@@ -2101,6 +2111,7 @@ void
screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct window_pane *wp = ctx->wp;
struct grid *gd = s->grid; struct grid *gd = s->grid;
const struct utf8_data *ud = &gc->data; const struct utf8_data *ud = &gc->data;
struct grid_line *gl; struct grid_line *gl;
@@ -2108,8 +2119,10 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
struct grid_cell tmp_gc, now_gc; struct grid_cell tmp_gc, now_gc;
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
u_int sx = screen_size_x(s), sy = screen_size_y(s); u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int width = ud->width, xx, not_wrap; u_int width = ud->width, xx, not_wrap, i, n, vis;
int selected, skip = 1, redraw = 0; int selected, skip = 1, redraw = 0;
struct visible_ranges *r;
struct visible_range *ri;
/* Ignore padding cells. */ /* Ignore padding cells. */
if (gc->flags & GRID_FLAG_PADDING) if (gc->flags & GRID_FLAG_PADDING)
@@ -2228,25 +2241,49 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
if (!skip && !(s->mode & MODE_SYNC)) { if (!skip && !(s->mode & MODE_SYNC)) {
if (selected) { if (selected) {
screen_select_cell(s, &tmp_gc, gc); screen_select_cell(s, &tmp_gc, gc);
ttyctx.cell = &tmp_gc;
} else } else
ttyctx.cell = gc; memcpy(&tmp_gc, gc, sizeof tmp_gc);
ttyctx.cell = &tmp_gc;
ttyctx.num = redraw ? 2 : 0; ttyctx.num = redraw ? 2 : 0;
/* xxx to be cached in wp */
r = screen_redraw_get_visible_ranges(wp, s->cx, s->cy, width,
NULL);
for (i=0; i < r->used; i++) vis += r->ranges[i].nx;
if (vis < width) {
/* Wide character or tab partly obscured. Write
* spaces one by one in unobscured region(s).
*/
*tmp_gc.data.data = ' ';
tmp_gc.data.width = tmp_gc.data.size =
tmp_gc.data.have = 1;
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0) continue;
for (n = 0; n < ri->nx; n++) {
screen_write_set_cursor(ctx, ri->px + n,
-1);
tty_write(tty_cmd_cell, &ttyctx); tty_write(tty_cmd_cell, &ttyctx);
} }
} }
} else {
tty_write(tty_cmd_cell, &ttyctx);
}
}
}
/* Combine a UTF-8 zero-width character onto the previous if necessary. */ /* Combine a UTF-8 zero-width character onto the previous if necessary. */
static int static int
screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc) screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct window_pane *wp = ctx->wp;
struct grid *gd = s->grid; struct grid *gd = s->grid;
const struct utf8_data *ud = &gc->data; const struct utf8_data *ud = &gc->data;
u_int n, cx = s->cx, cy = s->cy; u_int i, n, cx = s->cx, cy = s->cy, vis;
struct grid_cell last; struct grid_cell last;
struct tty_ctx ttyctx; struct tty_ctx ttyctx;
int force_wide = 0, zero_width = 0; int force_wide = 0, zero_width = 0;
struct visible_ranges *r;
/* Ignore U+3164 HANGUL_FILLER entirely. */ /* Ignore U+3164 HANGUL_FILLER entirely. */
if (utf8_is_hangul_filler(ud)) if (utf8_is_hangul_filler(ud))
@@ -2334,6 +2371,21 @@ screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
if (force_wide) if (force_wide)
grid_view_set_padding(gd, cx - 1, cy); grid_view_set_padding(gd, cx - 1, cy);
/*
* Check if all of this character is visible. No character will
* be obscured in the middle, only on left or right, but there
* could be an empty range in the visible ranges so we add them all up.
*/
r = screen_redraw_get_visible_ranges(wp, cx - n, cy, n, NULL);
for (i=0; i < r->used; i++) vis += r->ranges[i].nx;
if (vis < n) {
/*
* Part of this character is obscured. Return 1
* and let screen_write_cell write a space.
*/
return (1);
}
/* /*
* Redraw the combined cell. If forcing the cell to width 2, reset the * Redraw the combined cell. If forcing the cell to width 2, reset the
* cached cursor position in the tty, since we don't really know * cached cursor position in the tty, since we don't really know

View File

@@ -581,12 +581,12 @@ screen_check_selection(struct screen *s, u_int px, u_int py)
} }
/* Get selected grid cell. */ /* Get selected grid cell. */
void int
screen_select_cell(struct screen *s, struct grid_cell *dst, screen_select_cell(struct screen *s, struct grid_cell *dst,
const struct grid_cell *src) const struct grid_cell *src)
{ {
if (s->sel == NULL || s->sel->hidden) if (s->sel == NULL || s->sel->hidden)
return; return (0);
memcpy(dst, &s->sel->cell, sizeof *dst); memcpy(dst, &s->sel->cell, sizeof *dst);
if (COLOUR_DEFAULT(dst->fg)) if (COLOUR_DEFAULT(dst->fg))
@@ -600,6 +600,7 @@ screen_select_cell(struct screen *s, struct grid_cell *dst,
dst->attr |= (src->attr & GRID_ATTR_CHARSET); dst->attr |= (src->attr & GRID_ATTR_CHARSET);
else else
dst->attr |= src->attr; dst->attr |= src->attr;
return (1);
} }
/* Reflow wrapped lines. */ /* Reflow wrapped lines. */
@@ -770,3 +771,72 @@ screen_mode_to_string(int mode)
tmp[strlen(tmp) - 1] = '\0'; tmp[strlen(tmp) - 1] = '\0';
return (tmp); return (tmp);
} }
#ifdef DEBUG
/* Debug function. Usage in gdb:
* printf "%S",screen_print(s)
*/
__unused char *
screen_print(struct screen *s) {
static char buf[16384];
const char *acs;
u_int x, y;
size_t last = 0, n;
struct utf8_data ud;
struct grid_line *gl;
struct grid_cell_entry *gce;
for (y = 0; y < screen_hsize(s)+s->grid->sy; y++) {
if (last + 6 > sizeof buf) goto out;
n = snprintf(buf + last, sizeof buf - last, "%.4d ", y);
last += n;
buf[last++] = '"';
gl = &s->grid->linedata[y];
for (x = 0; x < gl->cellused; x++) {
gce = &gl->celldata[x];
if (gce->flags & GRID_FLAG_PADDING)
continue;
if (~gce->flags & GRID_FLAG_EXTENDED) {
/* single-byte cell stored inline */
if (last + 2 > sizeof buf) goto out;
buf[last++] = gce->data.data;
} else if (gce->flags & GRID_FLAG_TAB) {
if (last + 2 > sizeof buf) goto out;
buf[last++] = '\t';
} else if ((gce->data.data & 0xff) &&
(gce->flags & GRID_ATTR_CHARSET)) {
/* single-byte ACS inline: try to map */
acs = tty_acs_get(NULL, gce->data.data);
if (acs != NULL) {
n = strlen(acs);
if (last + n + 1 > sizeof buf) goto out;
memcpy(buf + last, acs, n);
last += n;
continue;
}
buf[last++] = gce->data.data;
} else {
/* extended cell: convert utf8_char -> bytes */
utf8_to_data(gl->extddata[gce->offset].data,
&ud);
if (ud.size > 0) {
if (last + ud.size + 1 > sizeof buf)
goto out;
memcpy(buf + last, ud.data, ud.size);
last += ud.size;
}
}
}
if (last + 3 > sizeof buf) goto out;
buf[last++] = '"';
buf[last++] = '\n';
}
out: buf[last] = '\0';
return (buf);
}
#endif

View File

@@ -161,38 +161,58 @@ server_client_clear_overlay(struct client *c)
server_redraw_client(c); server_redraw_client(c);
} }
/* Are these ranges empty? That is, nothing is visible. */
int
server_client_ranges_is_empty(struct visible_ranges *r)
{
u_int i;
for (i = 0; i < r->used; i++) {
if (r->ranges[i].nx != 0)
return (0);
}
return (1);
}
/* Ensure we have space for at least n ranges. */
void
server_client_ensure_ranges(struct visible_ranges *r, u_int n)
{
if (r->size >= n)
return;
r->ranges = xrecallocarray(r->ranges, r->size, n, sizeof *r->ranges);
r->size = n;
}
/* /*
* Given overlay position and dimensions, return parts of the input range which * Given overlay position and dimensions, return parts of the input range which
* are visible. * are visible.
*/ */
void void
server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px, server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px,
u_int py, u_int nx, struct overlay_ranges *r) u_int py, u_int nx, struct visible_ranges *r)
{ {
u_int ox, onx; u_int ox, onx;
/* Return up to 2 ranges. */
r->px[2] = 0;
r->nx[2] = 0;
/* Trivial case of no overlap in the y direction. */ /* Trivial case of no overlap in the y direction. */
if (py < y || py > y + sy - 1) { if (py < y || py > y + sy - 1) {
r->px[0] = px; server_client_ensure_ranges(r, 1);
r->nx[0] = nx; r->ranges[0].px = px;
r->px[1] = 0; r->ranges[0].nx = nx;
r->nx[1] = 0; r->used = 1;
return; return;
} }
server_client_ensure_ranges(r, 2);
/* Visible bit to the left of the popup. */ /* Visible bit to the left of the popup. */
if (px < x) { if (px < x) {
r->px[0] = px; r->ranges[0].px = px;
r->nx[0] = x - px; r->ranges[0].nx = x - px;
if (r->nx[0] > nx) if (r->ranges[0].nx > nx)
r->nx[0] = nx; r->ranges[0].nx = nx;
} else { } else {
r->px[0] = 0; r->ranges[0].px = 0;
r->nx[0] = 0; r->ranges[0].nx = 0;
} }
/* Visible bit to the right of the popup. */ /* Visible bit to the right of the popup. */
@@ -201,12 +221,13 @@ server_client_overlay_range(u_int x, u_int y, u_int sx, u_int sy, u_int px,
ox = px; ox = px;
onx = px + nx; onx = px + nx;
if (onx > ox) { if (onx > ox) {
r->px[1] = ox; r->ranges[1].px = ox;
r->nx[1] = onx - ox; r->ranges[1].nx = onx - ox;
} else { } else {
r->px[1] = 0; r->ranges[1].px = 0;
r->nx[1] = 0; r->ranges[1].nx = 0;
} }
r->used = 2;
} }
/* Check if this client is inside this server. */ /* Check if this client is inside this server. */
@@ -3023,7 +3044,7 @@ server_client_reset_state(struct client *c)
cy += status_line_size(c); cy += status_line_size(c);
} }
if (!screen_redraw_is_visible( if (!screen_redraw_is_visible(
screen_redraw_get_visible_ranges(wp, cx, cy, 1), cx)) screen_redraw_get_visible_ranges(wp, cx, cy, 1, NULL), cx))
cursor = 0; cursor = 0;
if (!cursor) if (!cursor)

58
tmux.h
View File

@@ -1177,6 +1177,19 @@ enum client_theme {
THEME_DARK THEME_DARK
}; };
/* Visible range array element. */
struct visible_range {
u_int px; /* start */
u_int nx; /* length */
};
/* Visible areas not obstructed. */
struct visible_ranges {
struct visible_range *ranges; /* dynamically allocated array */
u_int used; /* number of entries in ranges */
u_int size; /* allocated capacity of ranges */
};
/* Child window structure. */ /* Child window structure. */
struct window_pane { struct window_pane {
u_int id; u_int id;
@@ -1268,6 +1281,8 @@ struct window_pane {
struct style scrollbar_style; struct style scrollbar_style;
struct visible_ranges r;
TAILQ_ENTRY(window_pane) entry; /* link in list of all panes */ TAILQ_ENTRY(window_pane) entry; /* link in list of all panes */
TAILQ_ENTRY(window_pane) sentry; /* link in list of last visited */ TAILQ_ENTRY(window_pane) sentry; /* link in list of last visited */
TAILQ_ENTRY(window_pane) zentry; /* z-index link in list of all panes */ TAILQ_ENTRY(window_pane) zentry; /* z-index link in list of all panes */
@@ -1606,6 +1621,7 @@ struct tty {
size_t discarded; size_t discarded;
struct termios tio; struct termios tio;
struct visible_ranges r;
struct grid_cell cell; struct grid_cell cell;
struct grid_cell last_cell; struct grid_cell last_cell;
@@ -1922,18 +1938,11 @@ struct client_window {
}; };
RB_HEAD(client_windows, client_window); RB_HEAD(client_windows, client_window);
/* Visible areas not obstructed by overlays. */
#define OVERLAY_MAX_RANGES 3
struct overlay_ranges {
u_int px[OVERLAY_MAX_RANGES];
u_int nx[OVERLAY_MAX_RANGES];
};
/* Client connection. */ /* Client connection. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *); typedef void (*prompt_free_cb)(void *);
typedef void (*overlay_check_cb)(struct client*, void *, u_int, u_int, u_int, typedef struct visible_ranges *(*overlay_check_cb)(struct client*, void *,
struct overlay_ranges *); u_int, u_int, u_int);
typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *, typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *,
u_int *); u_int *);
typedef void (*overlay_draw_cb)(struct client *, void *, typedef void (*overlay_draw_cb)(struct client *, void *,
@@ -2269,14 +2278,6 @@ struct mode_tree_sort_criteria {
int reversed; int reversed;
}; };
/* Visible range array element. nx==-1 is end of array mark. */
struct visible_ranges {
u_int *px; /* Start */
u_int *nx; /* Length */
size_t used;
size_t size;
};
/* tmux.c */ /* tmux.c */
extern struct options *global_options; extern struct options *global_options;
extern struct options *global_s_options; extern struct options *global_s_options;
@@ -2538,6 +2539,8 @@ void tty_reset(struct tty *);
void tty_region_off(struct tty *); void tty_region_off(struct tty *);
void tty_margin_off(struct tty *); void tty_margin_off(struct tty *);
void tty_cursor(struct tty *, u_int, u_int); void tty_cursor(struct tty *, u_int, u_int);
int tty_fake_bce(const struct tty *, const struct grid_cell *, u_int);
void tty_repeat_space(struct tty *, u_int);
void tty_clipboard_query(struct tty *); void tty_clipboard_query(struct tty *);
void tty_putcode(struct tty *, enum tty_code_code); void tty_putcode(struct tty *, enum tty_code_code);
void tty_putcode_i(struct tty *, enum tty_code_code, int); void tty_putcode_i(struct tty *, enum tty_code_code, int);
@@ -2562,7 +2565,15 @@ void tty_repeat_requests(struct tty *, int);
void tty_stop_tty(struct tty *); void tty_stop_tty(struct tty *);
void tty_set_title(struct tty *, const char *); void tty_set_title(struct tty *, const char *);
void tty_set_path(struct tty *, const char *); void tty_set_path(struct tty *, const char *);
void tty_default_attributes(struct tty *, const struct grid_cell *,
struct colour_palette *, u_int, struct hyperlinks *);
void tty_update_mode(struct tty *, int, struct screen *); void tty_update_mode(struct tty *, int, struct screen *);
const struct grid_cell *tty_check_codeset(struct tty *,
const struct grid_cell *);
struct visible_ranges *tty_check_overlay_range(struct tty *, u_int, u_int,
u_int);
/* tty-draw.c */
void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int, void tty_draw_line(struct tty *, struct screen *, u_int, u_int, u_int,
u_int, u_int, const struct grid_cell *, struct colour_palette *); u_int, u_int, const struct grid_cell *, struct colour_palette *);
@@ -2919,8 +2930,10 @@ void server_client_set_overlay(struct client *, u_int, overlay_check_cb,
overlay_mode_cb, overlay_draw_cb, overlay_key_cb, overlay_mode_cb, overlay_draw_cb, overlay_key_cb,
overlay_free_cb, overlay_resize_cb, void *); overlay_free_cb, overlay_resize_cb, void *);
void server_client_clear_overlay(struct client *); void server_client_clear_overlay(struct client *);
void server_client_ensure_ranges(struct visible_ranges *, u_int);
int server_client_ranges_is_empty(struct visible_ranges *);
void server_client_overlay_range(u_int, u_int, u_int, u_int, u_int, u_int, void server_client_overlay_range(u_int, u_int, u_int, u_int, u_int, u_int,
u_int, struct overlay_ranges *); u_int, struct visible_ranges *);
void server_client_set_key_table(struct client *, const char *); void server_client_set_key_table(struct client *, const char *);
const char *server_client_get_key_table(struct client *); const char *server_client_get_key_table(struct client *);
int server_client_check_nested(struct client *); int server_client_check_nested(struct client *);
@@ -3217,7 +3230,7 @@ void screen_redraw_screen(struct client *);
void screen_redraw_pane(struct client *, struct window_pane *, int); void screen_redraw_pane(struct client *, struct window_pane *, int);
int screen_redraw_is_visible(struct visible_ranges *, u_int px); int screen_redraw_is_visible(struct visible_ranges *, u_int px);
struct visible_ranges *screen_redraw_get_visible_ranges(struct window_pane *, struct visible_ranges *screen_redraw_get_visible_ranges(struct window_pane *,
u_int, u_int, u_int); u_int, u_int, u_int, struct visible_ranges *);
/* screen.c */ /* screen.c */
@@ -3240,11 +3253,12 @@ void screen_set_selection(struct screen *, u_int, u_int, u_int, u_int,
void screen_clear_selection(struct screen *); void screen_clear_selection(struct screen *);
void screen_hide_selection(struct screen *); void screen_hide_selection(struct screen *);
int screen_check_selection(struct screen *, u_int, u_int); int screen_check_selection(struct screen *, u_int, u_int);
void screen_select_cell(struct screen *, struct grid_cell *, int screen_select_cell(struct screen *, struct grid_cell *,
const struct grid_cell *); const struct grid_cell *);
void screen_alternate_on(struct screen *, struct grid_cell *, int); void screen_alternate_on(struct screen *, struct grid_cell *, int);
void screen_alternate_off(struct screen *, struct grid_cell *, int); void screen_alternate_off(struct screen *, struct grid_cell *, int);
const char *screen_mode_to_string(int); const char *screen_mode_to_string(int);
__unused char * screen_print(struct screen *s);
/* window.c */ /* window.c */
extern struct windows windows; extern struct windows windows;
@@ -3631,8 +3645,8 @@ int menu_display(struct menu *, int, int, struct cmdq_item *,
const char *, const char *, struct cmd_find_state *, const char *, const char *, struct cmd_find_state *,
menu_choice_cb, void *); menu_choice_cb, void *);
struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *); struct screen *menu_mode_cb(struct client *, void *, u_int *, u_int *);
void menu_check_cb(struct client *, void *, u_int, u_int, u_int, struct visible_ranges *menu_check_cb(struct client *, void *, u_int, u_int,
struct overlay_ranges *); u_int);
void menu_draw_cb(struct client *, void *, void menu_draw_cb(struct client *, void *,
struct screen_redraw_ctx *); struct screen_redraw_ctx *);
void menu_free_cb(struct client *, void *); void menu_free_cb(struct client *, void *);

319
tty-draw.c Normal file
View File

@@ -0,0 +1,319 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <string.h>
#include "tmux.h"
enum tty_draw_line_state {
TTY_DRAW_LINE_FIRST,
TTY_DRAW_LINE_FLUSH,
TTY_DRAW_LINE_NEW1,
TTY_DRAW_LINE_NEW2,
TTY_DRAW_LINE_EMPTY,
TTY_DRAW_LINE_SAME,
TTY_DRAW_LINE_PAD,
TTY_DRAW_LINE_DONE
};
static const char* tty_draw_line_states[] = {
"FIRST",
"FLUSH",
"NEW1",
"NEW2",
"EMPTY",
"SAME",
"PAD",
"DONE"
};
/* Clear part of the line. */
static void
tty_draw_line_clear(struct tty *tty, u_int px, u_int py, u_int nx,
const struct grid_cell *defaults, u_int bg, int wrapped)
{
/* Nothing to clear. */
if (nx == 0)
return;
/* If genuine BCE is available, can try escape sequences. */
if (!wrapped && nx >= 10 && !tty_fake_bce(tty, defaults, bg)) {
/* Off the end of the line, use EL if available. */
if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
tty_cursor(tty, px, py);
tty_putcode(tty, TTYC_EL);
return;
}
/* At the start of the line. Use EL1. */
if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) {
tty_cursor(tty, px + nx - 1, py);
tty_putcode(tty, TTYC_EL1);
return;
}
/* Section of line. Use ECH if possible. */
if (tty_term_has(tty->term, TTYC_ECH)) {
tty_cursor(tty, px, py);
tty_putcode_i(tty, TTYC_ECH, nx);
return;
}
}
/* Couldn't use an escape sequence, use spaces. */
if (px != 0 || !wrapped)
tty_cursor(tty, px, py);
if (nx == 1)
tty_putc(tty, ' ');
else if (nx == 2)
tty_putn(tty, " ", 2, 2);
else
tty_repeat_space(tty, nx);
}
/* Is this cell empty? */
static u_int
tty_draw_line_get_empty(const struct grid_cell *gc, u_int nx)
{
u_int empty = 0;
if (gc->data.width != 1 && gc->data.width > nx)
empty = nx;
else if (gc->attr == 0 && gc->link == 0) {
if (gc->flags & GRID_FLAG_CLEARED)
empty = 1;
else if (gc->flags & GRID_FLAG_TAB)
empty = gc->data.width;
else if (gc->data.size == 1 && *gc->data.data == ' ')
empty = 1;
}
return (empty);
}
/* Draw a line from screen to tty. */
void
tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
u_int atx, u_int aty, const struct grid_cell *defaults,
struct colour_palette *palette)
{
struct grid *gd = s->grid;
const struct grid_cell *gcp;
struct grid_cell gc, ngc, last;
struct grid_line *gl;
u_int i, j, last_i, cx, ex, width;
u_int cellsize, bg;
int flags, empty, wrapped = 0;
char buf[1000];
size_t len;
enum tty_draw_line_state current_state, next_state;
/*
* py is the line in the screen to draw. px is the start x and nx is
* the width to draw. atx,aty is the line on the terminal to draw it.
*/
log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__, px, py, nx,
atx, aty);
/*
* Clamp the width to cellsize - note this is not cellused, because
* there may be empty background cells after it (from BCE).
*/
cellsize = grid_get_line(gd, gd->hsize + py)->cellsize;
if (screen_size_x(s) > cellsize)
ex = cellsize;
else {
ex = screen_size_x(s);
if (px > ex)
return;
if (px + nx > ex)
nx = ex - px;
}
if (ex < nx)
ex = nx;
log_debug("%s: drawing %u-%u,%u (end %u) at %u,%u; defaults: fg=%d, "
"bg=%d", __func__, px, px + nx, py, ex, atx, aty, defaults->fg,
defaults->bg);
/*
* If there is padding at the start, we must have truncated a wide
* character. Clear it.
*/
cx = 0;
for (i = px; i < px + nx; i++) {
grid_view_get_cell(gd, i, py, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
cx++;
}
if (cx != 0) {
/* Find the previous cell for the background colour. */
for (i = px + 1; i > 0; i--) {
grid_view_get_cell(gd, i - 1, py, &gc);
if (~gc.flags & GRID_FLAG_PADDING)
break;
}
if (i == 0)
bg = defaults->bg;
else {
bg = gc.bg;
if (gc.flags & GRID_FLAG_SELECTED) {
memcpy(&ngc, &gc, sizeof ngc);
if (screen_select_cell(s, &ngc, &gc))
bg = ngc.bg;
}
}
tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
log_debug("%s: clearing %u padding cells", __func__, cx);
tty_draw_line_clear(tty, atx, aty, cx, defaults, bg, 0);
if (cx == ex)
return;
atx += cx;
px += cx;
nx -= cx;
ex -= cx;
}
/* Did the previous line wrap on to this one? */
if (py != 0 && atx == 0 && tty->cx >= tty->sx && nx == tty->sx) {
gl = grid_get_line(gd, gd->hsize + py - 1);
if (gl->flags & GRID_LINE_WRAPPED)
wrapped = 1;
}
/* Turn off cursor while redrawing and reset region and margins. */
flags = (tty->flags & TTY_NOCURSOR);
tty->flags |= TTY_NOCURSOR;
tty_update_mode(tty, tty->mode, s);
tty_region_off(tty);
tty_margin_off(tty);
/* Start with the default cell as the last cell. */
memcpy(&last, &grid_default_cell, sizeof last);
last.bg = defaults->bg;
tty_default_attributes(tty, defaults, palette, 8, s->hyperlinks);
/* Loop over each character in the range. */
last_i = i = 0;
len = 0;
width = 0;
current_state = TTY_DRAW_LINE_FIRST;
for (;;) {
/* Work out the next state. */
if (i == nx) {
/*
* If this is the last cell, we are done. But we need to
* go through the loop again to flush anything in
* the buffer.
*/
empty = 0;
next_state = TTY_DRAW_LINE_DONE;
} else {
/* Get the current cell. */
grid_view_get_cell(gd, px + i, py, &gc);
/* Update for codeset if needed. */
gcp = tty_check_codeset(tty, &gc);
/* And for selection. */
if (gcp->flags & GRID_FLAG_SELECTED) {
memcpy(&ngc, gcp, sizeof ngc);
if (screen_select_cell(s, &ngc, gcp))
gcp = &ngc;
}
/* Work out the the empty width. */
if (i >= ex)
empty = 1;
else
empty = tty_draw_line_get_empty(gcp, nx - i);
/* Work out the next state. */
if (empty != 0)
next_state = TTY_DRAW_LINE_EMPTY;
else if (current_state == TTY_DRAW_LINE_FIRST)
next_state = TTY_DRAW_LINE_SAME;
else if (gcp->flags & GRID_FLAG_PADDING)
next_state = TTY_DRAW_LINE_PAD;
else if (grid_cells_look_equal(gcp, &last)) {
if (gcp->data.size > (sizeof buf) - len)
next_state = TTY_DRAW_LINE_FLUSH;
else
next_state = TTY_DRAW_LINE_SAME;
} else if (current_state == TTY_DRAW_LINE_NEW1)
next_state = TTY_DRAW_LINE_NEW2;
else
next_state = TTY_DRAW_LINE_NEW1;
}
log_debug("%s: cell %u empty %u, bg %u; state: current %s, "
"next %s", __func__, px + i, empty, gcp->bg,
tty_draw_line_states[current_state],
tty_draw_line_states[next_state]);
/* If the state has changed, flush any collected data. */
if (next_state != current_state) {
if (current_state == TTY_DRAW_LINE_EMPTY) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
tty_draw_line_clear(tty, atx + last_i, aty,
i - last_i, defaults, last.bg, wrapped);
wrapped = 0;
} else if (next_state != TTY_DRAW_LINE_SAME &&
len != 0) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
if (atx + i - width != 0 || !wrapped)
tty_cursor(tty, atx + i - width, aty);
if (~last.attr & GRID_ATTR_CHARSET)
tty_putn(tty, buf, len, width);
else {
for (j = 0; j < len; j++)
tty_putc(tty, buf[j]);
}
len = 0;
width = 0;
wrapped = 0;
}
last_i = i;
}
/* Append the cell if it is not empty and not padding. */
if (next_state != TTY_DRAW_LINE_EMPTY &&
next_state != TTY_DRAW_LINE_PAD) {
memcpy(buf + len, gcp->data.data, gcp->data.size);
len += gcp->data.size;
width += gcp->data.width;
}
/* If this is the last cell, we are done. */
if (next_state == TTY_DRAW_LINE_DONE)
break;
/* Otherwise move to the next. */
current_state = next_state;
memcpy(&last, gcp, sizeof last);
if (empty != 0)
i += empty;
else
i += gcp->data.width;
}
tty->flags = (tty->flags & ~TTY_NOCURSOR)|flags;
tty_update_mode(tty, tty->mode, s);
}

366
tty.c
View File

@@ -61,18 +61,11 @@ static void tty_region(struct tty *, u_int, u_int);
static void tty_margin_pane(struct tty *, const struct tty_ctx *); static void tty_margin_pane(struct tty *, const struct tty_ctx *);
static void tty_margin(struct tty *, u_int, u_int); static void tty_margin(struct tty *, u_int, u_int);
static int tty_large_region(struct tty *, const struct tty_ctx *); static int tty_large_region(struct tty *, const struct tty_ctx *);
static int tty_fake_bce(const struct tty *, const struct grid_cell *,
u_int);
static void tty_redraw_region(struct tty *, const struct tty_ctx *); static void tty_redraw_region(struct tty *, const struct tty_ctx *);
static void tty_emulate_repeat(struct tty *, enum tty_code_code, static void tty_emulate_repeat(struct tty *, enum tty_code_code,
enum tty_code_code, u_int); enum tty_code_code, u_int);
static void tty_repeat_space(struct tty *, u_int);
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
static void tty_default_attributes(struct tty *, const struct grid_cell *,
struct colour_palette *, u_int, struct hyperlinks *);
static int tty_check_overlay(struct tty *, u_int, u_int); static int tty_check_overlay(struct tty *, u_int, u_int);
static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int,
struct overlay_ranges *);
#ifdef ENABLE_SIXEL #ifdef ENABLE_SIXEL
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *), static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
@@ -528,6 +521,8 @@ void
tty_free(struct tty *tty) tty_free(struct tty *tty)
{ {
tty_close(tty); tty_close(tty);
free(tty->r.ranges);
} }
void void
@@ -912,7 +907,7 @@ tty_emulate_repeat(struct tty *tty, enum tty_code_code code,
} }
} }
static void void
tty_repeat_space(struct tty *tty, u_int n) tty_repeat_space(struct tty *tty, u_int n)
{ {
static char s[500]; static char s[500];
@@ -1074,7 +1069,7 @@ tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx)
* Return if BCE is needed but the terminal doesn't have it - it'll need to be * Return if BCE is needed but the terminal doesn't have it - it'll need to be
* emulated. * emulated.
*/ */
static int int
tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg) tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg)
{ {
if (tty_term_flag(tty->term, TTYC_BCE)) if (tty_term_flag(tty->term, TTYC_BCE))
@@ -1169,8 +1164,6 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
u_int px, u_int nx, u_int bg) u_int px, u_int nx, u_int bg)
{ {
struct client *c = tty->client; struct client *c = tty->client;
struct overlay_ranges r;
u_int i;
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
@@ -1206,13 +1199,8 @@ tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
* Couldn't use an escape sequence, use spaces. Clear only the visible * Couldn't use an escape sequence, use spaces. Clear only the visible
* bit if there is an overlay. * bit if there is an overlay.
*/ */
tty_check_overlay_range(tty, px, py, nx, &r); tty_cursor(tty, px, py);
for (i = 0; i < OVERLAY_MAX_RANGES; i++) { tty_repeat_space(tty, nx);
if (r.nx[i] == 0)
continue;
tty_cursor(tty, r.px[i], py);
tty_repeat_space(tty, r.nx[i]);
}
} }
/* Clear a line, adjusting to visible part of pane. */ /* Clear a line, adjusting to visible part of pane. */
@@ -1222,17 +1210,21 @@ tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py,
{ {
struct client *c = tty->client; struct client *c = tty->client;
struct window_pane *wp = ctx->arg; struct window_pane *wp = ctx->arg;
struct visible_ranges *vr = NULL; struct visible_ranges *r;
u_int r, i, x, rx, ry; struct visible_range *ri;
u_int i, l, x, rx, ry;
log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry)) { if (tty_clamp_line(tty, ctx, px, py, nx, &l, &x, &rx, &ry)) {
vr = screen_redraw_get_visible_ranges(wp, x, ry, rx); r = tty_check_overlay_range(tty, x, ry, rx);
for (r=0; r < vr->used; r++) { r = screen_redraw_get_visible_ranges(wp, x, ry, rx, r);
if (vr->nx[r] == 0) for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue; continue;
tty_clear_line(tty, &ctx->defaults, ry, vr->px[r], vr->nx[r], bg); tty_clear_line(tty, &ctx->defaults, ry, ri->px, ri->nx,
bg);
} }
} }
} }
@@ -1308,8 +1300,9 @@ tty_clear_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
const struct grid_cell *defaults = &ctx->defaults; const struct grid_cell *defaults = &ctx->defaults;
struct window *w = c->session->curw->window; struct window *w = c->session->curw->window;
struct window_pane *wpl, *wp = ctx->arg; struct window_pane *wpl, *wp = ctx->arg;
struct visible_ranges *vr = NULL; struct visible_ranges *r;
u_int r, yy, overlap = 0, oy = 0; struct visible_range *ri;
u_int i, yy, overlap = 0, oy = 0;
char tmp[64]; char tmp[64];
log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py); log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py);
@@ -1388,11 +1381,12 @@ tty_clear_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
/* Couldn't use an escape sequence, loop over the lines. */ /* Couldn't use an escape sequence, loop over the lines. */
for (yy = py; yy < py + ny; yy++) { for (yy = py; yy < py + ny; yy++) {
vr = screen_redraw_get_visible_ranges(wp, px, yy - oy, nx); r = tty_check_overlay_range(tty, px, yy - oy, nx);
for (r=0; r < vr->used; r++) { r = screen_redraw_get_visible_ranges(wp, px, yy - oy, nx, r);
if (vr->nx[r] == 0) for (i=0; i < r->used; i++) {
continue; ri = &r->ranges[i];
tty_clear_line(tty, defaults, yy, vr->px[r], vr->nx[r], bg); if (ri->nx == 0) continue;
tty_clear_line(tty, defaults, yy, ri->px, ri->nx, bg);
} }
} }
} }
@@ -1416,19 +1410,22 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
{ {
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct window_pane *wp = ctx->arg; struct window_pane *wp = ctx->arg;
struct visible_ranges *vr = NULL; struct visible_ranges *r = NULL;
u_int nx = ctx->sx, i, x, rx, ry, r; struct visible_range *ri;
u_int nx = ctx->sx, i, px, x, rx, ry;
log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger); log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger);
if (!ctx->bigger) { if (!ctx->bigger) {
if (wp) { if (wp) {
vr = screen_redraw_get_visible_ranges(wp, 0, ctx->yoff + py, nx); r = tty_check_overlay_range(tty, 0, ctx->yoff + py, nx);
for (r=0; r < vr->used; r++) { r = screen_redraw_get_visible_ranges(wp, 0, ctx->yoff +
if (vr->nx[r] == 0) py, nx, r);
continue; for (i=0; i < r->used; i++) {
tty_draw_line(tty, s, vr->px[r], py, vr->nx[r], ri = &r->ranges[i];
ctx->xoff + vr->px[r], ctx->yoff + py, if (ri->nx == 0) continue;
tty_draw_line(tty, s, ri->px, py, ri->nx,
ctx->xoff + ri->px, ctx->yoff + py,
&ctx->defaults, ctx->palette); &ctx->defaults, ctx->palette);
} }
} else { } else {
@@ -1437,24 +1434,26 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
} }
return; return;
} }
if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) { if (tty_clamp_line(tty, ctx, 0, py, nx, &px, &x, &rx, &ry)) {
if (wp) { if (wp) {
vr = screen_redraw_get_visible_ranges(wp, i, py, rx); r = tty_check_overlay_range(tty, i, py, rx);
for (r=0; r < vr->used; r++) { r = screen_redraw_get_visible_ranges(wp, i, py, rx, r);
if (vr->nx[r] == 0) for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue; continue;
tty_draw_line(tty, s, i, py, vr->nx[r], tty_draw_line(tty, s, i, py, ri->nx,
x + vr->px[r], ry, &ctx->defaults, x + ri->px, ry, &ctx->defaults,
ctx->palette); ctx->palette);
} }
} else { } else {
tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->defaults, tty_draw_line(tty, s, px, py, rx, x, ry, &ctx->defaults,
ctx->palette); ctx->palette);
} }
} }
} }
static const struct grid_cell * const struct grid_cell *
tty_check_codeset(struct tty *tty, const struct grid_cell *gc) tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
{ {
static struct grid_cell new; static struct grid_cell new;
@@ -1487,233 +1486,34 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
return (&new); return (&new);
} }
/* /* Check if a single character is covered by the overlay. */
* Check if a single character is obstructed by the overlay and return a
* boolean.
*/
static int static int
tty_check_overlay(struct tty *tty, u_int px, u_int py) tty_check_overlay(struct tty *tty, u_int px, u_int py)
{ {
struct overlay_ranges r; struct visible_ranges *r;
/* /*
* A unit width range will always return nx[2] == 0 from a check, even * With a single character, if there is anything visible (that is, the
* with multiple overlays, so it's sufficient to check just the first * range is not empty), it must be that character.
* two entries.
*/ */
tty_check_overlay_range(tty, px, py, 1, &r); r = tty_check_overlay_range(tty, px, py, 1);
if (r.nx[0] + r.nx[1] == 0) return (!server_client_ranges_is_empty(r));
return (0);
return (1);
} }
/* Return parts of the input range which are visible. */ /* Return parts of the input range which are visible. */
static void struct visible_ranges *
tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx, tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
struct overlay_ranges *r)
{ {
struct client *c = tty->client; struct client *c = tty->client;
if (c->overlay_check == NULL) { if (c->overlay_check == NULL) {
r->px[0] = px; server_client_ensure_ranges(&tty->r, 1);
r->nx[0] = nx; tty->r.ranges[0].px = px;
r->px[1] = 0; tty->r.ranges[0].nx = nx;
r->nx[1] = 0; tty->r.used = 1;
r->px[2] = 0; return (&tty->r);
r->nx[2] = 0;
return;
} }
return (c->overlay_check(c, c->overlay_data, px, py, nx));
c->overlay_check(c, c->overlay_data, px, py, nx, r);
}
void
tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx,
u_int atx, u_int aty, const struct grid_cell *defaults,
struct colour_palette *palette)
{
struct grid *gd = s->grid;
struct grid_cell gc, last;
const struct grid_cell *gcp;
struct grid_line *gl;
struct client *c = tty->client;
struct overlay_ranges r;
u_int i, j, ux, sx, width, hidden, eux, nxx;
u_int cellsize;
int flags, cleared = 0, wrapped = 0;
char buf[512];
size_t len;
log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__,
px, py, nx, atx, aty);
log_debug("%s: defaults: fg=%d, bg=%d", __func__, defaults->fg,
defaults->bg);
/*
* py is the line in the screen to draw.
* px is the start x and nx is the width to draw.
* atx,aty is the line on the terminal to draw it.
*/
flags = (tty->flags & TTY_NOCURSOR);
tty->flags |= TTY_NOCURSOR;
tty_update_mode(tty, tty->mode, s);
tty_region_off(tty);
tty_margin_off(tty);
/*
* Clamp the width to cellsize - note this is not cellused, because
* there may be empty background cells after it (from BCE).
*/
sx = screen_size_x(s);
if (nx > sx)
nx = sx;
cellsize = grid_get_line(gd, gd->hsize + py)->cellsize;
if (sx > cellsize)
sx = cellsize;
if (sx > tty->sx)
sx = tty->sx;
if (sx > nx)
sx = nx;
ux = 0;
if (py == 0)
gl = NULL;
else
gl = grid_get_line(gd, gd->hsize + py - 1);
if (gl == NULL ||
(~gl->flags & GRID_LINE_WRAPPED) ||
atx != 0 ||
tty->cx < tty->sx ||
nx < tty->sx) {
/* Do I need to check
!tty_is_obstructed(c->session->curw->window->active
here too? It's not certain that the active pane is
the one being drawn in. (delete this comment) */
if (nx < tty->sx &&
atx == 0 &&
px + sx != nx &&
tty_term_has(tty->term, TTYC_EL1) &&
!tty_fake_bce(tty, defaults, 8) &&
c->overlay_check == NULL) {
tty_default_attributes(tty, defaults, palette, 8,
s->hyperlinks);
tty_cursor(tty, nx - 1, aty);
tty_putcode(tty, TTYC_EL1);
cleared = 1;
}
} else {
log_debug("%s: wrapped line %u", __func__, aty);
wrapped = 1;
}
memcpy(&last, &grid_default_cell, sizeof last);
len = 0;
width = 0;
for (i = 0; i < sx; i++) {
grid_view_get_cell(gd, px + i, py, &gc);
gcp = tty_check_codeset(tty, &gc);
if (len != 0 &&
(!tty_check_overlay(tty, atx + ux + width, aty) ||
(gcp->attr & GRID_ATTR_CHARSET) ||
gcp->flags != last.flags ||
gcp->attr != last.attr ||
gcp->fg != last.fg ||
gcp->bg != last.bg ||
gcp->us != last.us ||
gcp->link != last.link ||
ux + width + gcp->data.width > nx ||
(sizeof buf) - len < gcp->data.size)) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
if (last.flags & GRID_FLAG_CLEARED) {
log_debug("%s: %zu cleared", __func__, len);
tty_clear_line(tty, defaults, aty, atx + ux,
width, last.bg);
} else {
if (!wrapped || atx != 0 || ux != 0)
tty_cursor(tty, atx + ux, aty);
tty_putn(tty, buf, len, width);
}
ux += width;
len = 0;
width = 0;
wrapped = 0;
}
if (gcp->flags & GRID_FLAG_SELECTED)
screen_select_cell(s, &last, gcp);
else
memcpy(&last, gcp, sizeof last);
tty_check_overlay_range(tty, atx + ux, aty, gcp->data.width,
&r);
hidden = 0; /* need to check visible_ranges too? xxxx */
for (j = 0; j < OVERLAY_MAX_RANGES; j++)
hidden += r.nx[j];
hidden = gcp->data.width - hidden;
if (hidden != 0 && hidden == gcp->data.width) {
if (~gcp->flags & GRID_FLAG_PADDING)
ux += gcp->data.width;
} else if (hidden != 0 || ux + gcp->data.width > nx) {
if (~gcp->flags & GRID_FLAG_PADDING) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
for (j = 0; j < OVERLAY_MAX_RANGES; j++) {
if (r.nx[j] == 0)
continue;
/* Effective width drawn so far. */
eux = r.px[j] - atx;
if (eux < nx) {
tty_cursor(tty, r.px[j], aty);
nxx = nx - eux;
if (r.nx[j] > nxx)
r.nx[j] = nxx;
tty_repeat_space(tty, r.nx[j]);
ux = eux + r.nx[j];
}
}
}
} else if (gcp->attr & GRID_ATTR_CHARSET) {
tty_attributes(tty, &last, defaults, palette,
s->hyperlinks);
tty_cursor(tty, atx + ux, aty);
for (j = 0; j < gcp->data.size; j++)
tty_putc(tty, gcp->data.data[j]);
ux += gcp->data.width;
} else if (~gcp->flags & GRID_FLAG_PADDING) {
memcpy(buf + len, gcp->data.data, gcp->data.size);
len += gcp->data.size;
width += gcp->data.width;
}
}
if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) {
tty_attributes(tty, &last, defaults, palette, s->hyperlinks);
if (last.flags & GRID_FLAG_CLEARED) {
log_debug("%s: %zu cleared (end)", __func__, len);
tty_clear_line(tty, defaults, aty, atx + ux, width,
last.bg);
} else {
if (!wrapped || atx != 0 || ux != 0)
tty_cursor(tty, atx + ux, aty);
tty_putn(tty, buf, len, width);
}
ux += width;
}
if (!cleared && ux < nx) {
log_debug("%s: %u to end of line (%zu cleared)", __func__,
nx - ux, len);
tty_default_attributes(tty, defaults, palette, 8,
s->hyperlinks);
tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8);
}
tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags;
tty_update_mode(tty, tty->mode, s);
} }
#ifdef ENABLE_SIXEL #ifdef ENABLE_SIXEL
@@ -2275,10 +2075,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
{ {
const struct grid_cell *gcp = ctx->cell; const struct grid_cell *gcp = ctx->cell;
struct screen *s = ctx->s; struct screen *s = ctx->s;
struct overlay_ranges r; struct visible_ranges *r;
struct window_pane *wp = ctx->arg; u_int px, py, i, vis = 0;
struct visible_ranges *vr;
u_int px, py, i, vis = 0, vis2 = 0;
px = ctx->xoff + ctx->ocx - ctx->wox; px = ctx->xoff + ctx->ocx - ctx->wox;
py = ctx->yoff + ctx->ocy - ctx->woy; py = ctx->yoff + ctx->ocy - ctx->woy;
@@ -2286,9 +2084,6 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
(gcp->data.width == 1 && !tty_check_overlay(tty, px, py))) (gcp->data.width == 1 && !tty_check_overlay(tty, px, py)))
return; return;
vr = screen_redraw_get_visible_ranges(wp, px, py,
gcp->data.width);
if (ctx->num == 2) { if (ctx->num == 2) {
/* xxxx need to check visible range */ /* xxxx need to check visible range */
tty_draw_line(tty, s, 0, s->cy, screen_size_x(s), tty_draw_line(tty, s, 0, s->cy, screen_size_x(s),
@@ -2298,13 +2093,10 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
/* Handle partially obstructed wide characters. */ /* Handle partially obstructed wide characters. */
if (gcp->data.width > 1) { if (gcp->data.width > 1) {
for (i = 0; i < vr->used; i++) r = tty_check_overlay_range(tty, px, py, gcp->data.width);
vis2 += vr->nx[i]; for (i = 0; i < r->used; i++)
tty_check_overlay_range(tty, px, py, gcp->data.width, &r); vis += r->ranges[i].nx;
for (i = 0; i < OVERLAY_MAX_RANGES; i++) if (vis < gcp->data.width) {
vis += r.nx[i];
if (vis < gcp->data.width ||
vis2 < gcp->data.width) { /* xxxx need to check visible range */
tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width, tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width,
px, py, &ctx->defaults, ctx->palette); px, py, &ctx->defaults, ctx->palette);
return; return;
@@ -2319,7 +2111,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty); tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
if (screen_redraw_is_visible(vr, px)) if (screen_redraw_is_visible(r, px))
tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette, tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette,
ctx->s->hyperlinks); ctx->s->hyperlinks);
@@ -2330,7 +2122,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
void void
tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
{ {
struct overlay_ranges r; struct visible_ranges *r;
struct visible_range *ri;
u_int i, px, py, cx; u_int i, px, py, cx;
char *cp = ctx->ptr; char *cp = ctx->ptr;
@@ -2355,20 +2148,21 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty); tty_margin_off(tty);
tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks); tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette,
ctx->s->hyperlinks);
/* Get tty position from pane position for overlay check. */ /* Get tty position from pane position for overlay check. */
px = ctx->xoff + ctx->ocx - ctx->wox; px = ctx->xoff + ctx->ocx - ctx->wox;
py = ctx->yoff + ctx->ocy - ctx->woy; py = ctx->yoff + ctx->ocy - ctx->woy;
tty_check_overlay_range(tty, px, py, ctx->num, &r); r = tty_check_overlay_range(tty, px, py, ctx->num);
for (i = 0; i < OVERLAY_MAX_RANGES; i++) { for (i = 0; i < r->used; i++) {
if (r.nx[i] == 0) ri = &r->ranges[i];
continue; if (ri->nx != 0) {
/* Convert back to pane position for printing. */ cx = ri->px - ctx->xoff + ctx->wox;
cx = r.px[i] - ctx->xoff + ctx->wox;
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy); tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
tty_putn(tty, cp + r.px[i] - px, r.nx[i], r.nx[i]); tty_putn(tty, cp + ri->px - px, ri->nx, ri->nx);
}
} }
} }
@@ -3324,7 +3118,7 @@ tty_default_colours(struct grid_cell *gc, struct window_pane *wp)
} }
} }
static void void
tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, tty_default_attributes(struct tty *tty, const struct grid_cell *defaults,
struct colour_palette *palette, u_int bg, struct hyperlinks *hl) struct colour_palette *palette, u_int bg, struct hyperlinks *hl)
{ {