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-protocol.h \
tty-acs.c \
tty-draw.c \
tty-features.c \
tty-keys.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
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)
return (0);

10
menu.c
View File

@@ -34,6 +34,7 @@ struct menu_data {
struct cmd_find_state fs;
struct screen s;
struct visible_ranges r;
u_int px;
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. */
void
struct visible_ranges *
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 *menu = md->menu;
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
@@ -232,7 +234,9 @@ menu_free_cb(__unused struct client *c, void *data)
if (md->cb != NULL)
md->cb(md->menu, UINT_MAX, KEYC_NONE, md->data);
free(md->r.ranges);
screen_free(&md->s);
menu_free(md->menu);
free(md);
}

69
popup.c
View File

@@ -39,6 +39,9 @@ struct popup_data {
struct grid_cell defaults;
struct colour_palette palette;
struct visible_ranges r;
struct visible_ranges or[2];
struct job *job;
struct input_ctx *ictx;
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. */
static void
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx,
struct overlay_ranges *r)
static struct visible_ranges *
popup_check_cb(struct client* c, void *data, u_int px, u_int py, u_int nx)
{
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;
if (pd->md != NULL) {
/* Check each returned range for the menu against the popup. */
menu_check_cb(c, pd->md, px, py, nx, r);
for (i = 0; i < 2; i++) {
/*
* Work out the visible ranges for the menu (that is, the
* 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,
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,
* ordered from left to right. Collect them in the output.
* We now have nonoverlapping ranges from left to right.
* Combine them together into the output.
*/
for (i = 0; i < 2; i++) {
/* Each or[i] only has 2 ranges. */
for (j = 0; j < 2; j++) {
if (or[i].nx[j] > 0) {
r->px[k] = or[i].px[j];
r->nx[k] = or[i].nx[j];
k++;
}
server_client_ensure_ranges(r, 3);
for (i = 0; i < mr->used; i++) {
for (j = 0; j < pd->or[i].used; j++) {
if (pd->or[i].ranges[j].nx == 0)
continue;
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++;
}
}
/* Zero remaining ranges if any. */
for (i = k; i < OVERLAY_MAX_RANGES; i++) {
r->px[i] = 0;
r->nx[i] = 0;
}
return;
return (r);
}
server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx,
r);
return (r);
}
static void
@@ -286,6 +298,9 @@ popup_free_cb(struct client *c, void *data)
job_free(pd->job);
input_free(pd->ictx);
free(pd->or[0].ranges);
free(pd->or[1].ranges);
free(pd->r.ranges);
screen_free(&pd->s);
colour_palette_free(&pd->palette);
@@ -549,7 +564,7 @@ popup_key_cb(struct client *c, void *data, struct key_event *event)
(event->key == '\033' || event->key == ('c'|KEYC_CTRL)))
return (1);
if (pd->job == NULL && (pd->flags & POPUP_CLOSEANYKEY) &&
!KEYC_IS_MOUSE(event->key) && !KEYC_IS_PASTE(event->key))
!KEYC_IS_MOUSE(event->key) && !KEYC_IS_PASTE(event->key))
return (1);
if (pd->job != NULL) {
if (KEYC_IS_MOUSE(event->key)) {

View File

@@ -674,8 +674,9 @@ screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
struct tty *tty = &c->tty;
struct window_pane *wp;
struct screen *s;
struct visible_ranges *vr;
u_int i, x, width, size, r;
struct visible_ranges *r;
struct visible_range *ri;
u_int i, l, x, width, size;
int xoff, yoff;
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) {
/* All visible. */
i = 0;
l = 0;
x = xoff - ctx->ox;
width = size;
} else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) {
/* Both left and right not visible. */
i = ctx->ox;
l = ctx->ox;
x = 0;
width = ctx->sx;
} else if (xoff < ctx->ox) {
/* Left not visible. */
i = ctx->ox - xoff;
l = ctx->ox - xoff;
x = 0;
width = size - i;
} else {
/* Right not visible. */
i = 0;
l = 0;
x = xoff - ctx->ox;
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)
yoff += ctx->statuslines;
for (r=0; r < vr->used; r++) {
if (vr->nx[r] == 0)
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
tty_draw_line(tty, s, i + (vr->px[r] - x), 0, vr->nx[r],
vr->px[r], yoff - ctx->oy,
tty_draw_line(tty, s, l + (ri->px - x), 0, ri->nx,
ri->px, yoff - ctx->oy,
&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 grid_cell gc;
const struct grid_cell *tmp;
struct overlay_ranges r;
u_int cell_type, x = ctx->ox + i, y = ctx->oy + j;
u_int cell_type;
u_int x = ctx->ox + i, y = ctx->oy + j;
int isolates;
struct visible_ranges *r;
if (c->overlay_check != NULL) {
c->overlay_check(c, c->overlay_data, x, y, 1, &r);
if (r.nx[0] + r.nx[1] == 0)
r = c->overlay_check(c, c->overlay_data, x, y, 1);
if (server_client_ranges_is_empty(r))
return;
}
@@ -1106,18 +1110,20 @@ screen_redraw_draw_status(struct screen_redraw_ctx *ctx)
* floating window pane). Returns a boolean.
*/
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. */
if (vr == NULL)
if (r == NULL)
return (1);
for (r=0; r < vr->used; r++) {
if (vr->nx[r] == 0)
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
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 (0);
@@ -1127,33 +1133,26 @@ screen_redraw_is_visible(struct visible_ranges *vr, u_int px)
cells of base_wp that are unobsructed. */
struct visible_ranges *
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 *w;
static struct visible_ranges vr = {NULL, NULL, 0, 0};
struct visible_range *ri;
int found_self, sb_w, sb_pos;
u_int lb, rb, tb, bb;
u_int r, s;
u_int i, s;
/* For efficiency vr is static and space reused. */
if (vr.size == 0) {
vr.px = xcalloc(1, sizeof(u_int));
vr.nx = xcalloc(1, sizeof(u_int));
vr.size = 1;
if (r == NULL) {
server_client_ensure_ranges(&base_wp->r, 1);
r = &base_wp->r;
/* Start with the entire width of the range. */
r->ranges[0].px = px;
r->ranges[0].nx = width;
r->used = 1;
}
/* debugging:
* display *vr.px@vr.used
* display *vr.nx@vr.used
*/
/* Start with the entire width of the range. */
vr.px[0] = px;
vr.nx[0] = width;
vr.used = 1;
if (base_wp == NULL)
return (&vr);
return (r);
w = base_wp->window;
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;
}
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 (wp->xoff > sb_w)
lb = wp->xoff - 1 - sb_w;
@@ -1204,60 +1204,54 @@ screen_redraw_get_visible_ranges(struct window_pane *base_wp, u_int px,
rb = w->sx - 1;
/* If the left edge of floating wp
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. */
if (lb > vr.px[r] &&
lb < vr.px[r] + vr.nx[r] &&
rb >= vr.px[r] + vr.nx[r]) {
vr.nx[r] = lb - vr.px[r];
if (lb > ri->px &&
lb < ri->px + ri->nx &&
rb >= ri->px + ri->nx) {
ri->nx = lb - ri->px;
}
/* Else if the right edge of floating wp
falls inside of this range and left
edge covers the left of range,
then move px forward to right edge of wp. */
else if (rb >= vr.px[r] &&
rb < vr.px[r] + vr.nx[r] &&
lb <= vr.px[r]) {
vr.nx[r] = vr.nx[r] - (rb + 1 - vr.px[r]);
vr.px[r] = vr.px[r] + (rb + 1 - vr.px[r]);
else if (rb >= ri->px &&
rb < ri->px + ri->nx &&
lb <= ri->px) {
ri->nx = ri->nx - (rb + 1 - ri->px);
ri->px = ri->px + (rb + 1 - ri->px);
}
/* Else if wp fully inside range
then split range into 2 ranges. */
else if (lb > vr.px[r] &&
rb < vr.px[r] + vr.nx[r]) {
if (vr.size == vr.used) {
vr.size++;
vr.px = xreallocarray(vr.px,
vr.size, sizeof (u_int));
vr.nx = xreallocarray(vr.nx,
vr.size, sizeof (u_int));
}
for (s=vr.used; s>r; s--) {
vr.px[s] = vr.px[s-1];
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++;
else if (lb > ri->px &&
rb < ri->px + ri->nx) {
server_client_ensure_ranges(r, r->size + 1);
for (s=r->used; s>i; s--)
memcpy(&r->ranges[s-1], &r->ranges[s],
sizeof (struct visible_range));
r->ranges[i+1].px = rb + 1;
r->ranges[i+1].nx = ri->px + ri->nx - (rb + 1);
/* ri->px was copied, unchanged. */
ri->nx = lb - ri->px;
r->used++;
}
/* If floating wp completely covers this range
* then delete it (make it 0 length). */
else if (lb <= vr.px[r] &&
rb >= vr.px[r] + vr.nx[r]) {
vr.nx[r] = 0;
else if (lb <= ri->px &&
rb >= ri->px + ri->nx) {
ri->nx = 0;
}
/* Else the range is already obscured, do nothing. */
}
}
for (r=0; r<vr.used; r++) {
log_debug("%s: %%%u visible_range py=%u %u: [px=%u nx=%u]",
__func__, base_wp->id, py, r, vr.px[r], vr.nx[r]);
for (i=0; i<r->used; i++) {
ri = &r->ranges[i];
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 colour_palette *palette = &wp->palette;
struct grid_cell defaults;
struct visible_ranges *vr;
u_int i, j, woy, wx, wy, px, py, width, r;
u_int i, j, woy, wx, wy, px, py, width;
struct visible_ranges *r;
struct visible_range *ri;
if (wp->base.mode & MODE_SYNC)
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);
/* 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);
for (r=0; r < vr->used; r++) {
if (vr->nx[r] == 0)
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
/* Convert window coordinates to tty coordinates. */
px = vr->px[r];
/* i is px of cell, add px of region, sub the
* 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);
px = ri->px;
tty_draw_line(tty, s, ri->px - wp->xoff, j,
ri->nx, 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 xoff = wp->xoff;
int yoff = wp->yoff;
struct visible_ranges *vr;
struct visible_ranges *r;
/*
* 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++) {
py = sb_y + j; /* tty 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++) {
px = sb_x + ox + i; /* tty 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 ||
wy < yoff - 1 ||
py >= sy || py < 0 ||
! screen_redraw_is_visible(vr, wx))
! screen_redraw_is_visible(r, wx))
continue;
tty_cursor(tty, px, py);
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_cell gc;
u_int xx, yy, cx = s->cx, cy = s->cy;
struct visible_ranges *r;
if (nx == 0 || ny == 0)
return;
@@ -584,15 +585,19 @@ screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
s->cx = cx;
if (wp != NULL)
screen_write_initctx(ctx, &ttyctx, 0);
r = screen_redraw_get_visible_ranges(wp, px, py, nx, NULL);
for (xx = px; xx < px + nx; xx++) {
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;
grid_get_cell(gd, xx, yy, &gc);
if (xx + gc.data.width > px + nx)
break;
grid_view_set_cell(ctx->s->grid, s->cx, s->cy, &gc);
if (wp != NULL) {
if (! screen_redraw_is_visible(r, px))
break;
ttyctx.cell = &gc;
tty_write(tty_cmd_cell, &ttyctx);
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_write_citem *ci, *tmp;
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;
int r_start, r_end, ci_start, ci_end;
int xoff, yoff;
struct tty_ctx ttyctx;
struct visible_ranges *vr;
struct visible_ranges *r;
struct visible_range *ri;
struct window_pane *wp = ctx->wp;
if (s->mode & MODE_SYNC) {
@@ -1879,8 +1885,8 @@ screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
continue;
cl = &ctx->s->write_list[y];
vr = screen_redraw_get_visible_ranges(wp, 0, y + yoff,
wsx);
r = screen_redraw_get_visible_ranges(wp, 0, y + yoff, wsx,
NULL);
last = UINT_MAX;
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);
}
wr_length = 0;
for (r = 0; r < vr->used; r++) {
if (vr->nx[r] == 0) continue;
r_start = vr->px[r];
r_end = vr->px[r] + vr->nx[r];
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0) continue;
r_start = ri->px;
r_end = ri->px + ri->nx;
ci_start = ci->x;
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;
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
wr_start = ci_start;
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
wr_end = ci_end;
wr_length = wr_end - wr_start;
@@ -2101,6 +2111,7 @@ void
screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
{
struct screen *s = ctx->s;
struct window_pane *wp = ctx->wp;
struct grid *gd = s->grid;
const struct utf8_data *ud = &gc->data;
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 tty_ctx ttyctx;
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;
struct visible_ranges *r;
struct visible_range *ri;
/* Ignore padding cells. */
if (gc->flags & GRID_FLAG_PADDING)
@@ -2228,11 +2241,33 @@ screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
if (!skip && !(s->mode & MODE_SYNC)) {
if (selected) {
screen_select_cell(s, &tmp_gc, gc);
ttyctx.cell = &tmp_gc;
} else
ttyctx.cell = gc;
memcpy(&tmp_gc, gc, sizeof tmp_gc);
ttyctx.cell = &tmp_gc;
ttyctx.num = redraw ? 2 : 0;
tty_write(tty_cmd_cell, &ttyctx);
/* 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);
}
}
} else {
tty_write(tty_cmd_cell, &ttyctx);
}
}
}
@@ -2241,12 +2276,14 @@ static int
screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
{
struct screen *s = ctx->s;
struct window_pane *wp = ctx->wp;
struct grid *gd = s->grid;
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 tty_ctx ttyctx;
int force_wide = 0, zero_width = 0;
struct visible_ranges *r;
/* Ignore U+3164 HANGUL_FILLER entirely. */
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)
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
* 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. */
void
int
screen_select_cell(struct screen *s, struct grid_cell *dst,
const struct grid_cell *src)
{
if (s->sel == NULL || s->sel->hidden)
return;
return (0);
memcpy(dst, &s->sel->cell, sizeof *dst);
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);
else
dst->attr |= src->attr;
return (1);
}
/* Reflow wrapped lines. */
@@ -770,3 +771,72 @@ screen_mode_to_string(int mode)
tmp[strlen(tmp) - 1] = '\0';
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);
}
/* 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
* are visible.
*/
void
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;
/* Return up to 2 ranges. */
r->px[2] = 0;
r->nx[2] = 0;
/* Trivial case of no overlap in the y direction. */
if (py < y || py > y + sy - 1) {
r->px[0] = px;
r->nx[0] = nx;
r->px[1] = 0;
r->nx[1] = 0;
server_client_ensure_ranges(r, 1);
r->ranges[0].px = px;
r->ranges[0].nx = nx;
r->used = 1;
return;
}
server_client_ensure_ranges(r, 2);
/* Visible bit to the left of the popup. */
if (px < x) {
r->px[0] = px;
r->nx[0] = x - px;
if (r->nx[0] > nx)
r->nx[0] = nx;
r->ranges[0].px = px;
r->ranges[0].nx = x - px;
if (r->ranges[0].nx > nx)
r->ranges[0].nx = nx;
} else {
r->px[0] = 0;
r->nx[0] = 0;
r->ranges[0].px = 0;
r->ranges[0].nx = 0;
}
/* 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;
onx = px + nx;
if (onx > ox) {
r->px[1] = ox;
r->nx[1] = onx - ox;
r->ranges[1].px = ox;
r->ranges[1].nx = onx - ox;
} else {
r->px[1] = 0;
r->nx[1] = 0;
r->ranges[1].px = 0;
r->ranges[1].nx = 0;
}
r->used = 2;
}
/* Check if this client is inside this server. */
@@ -3023,7 +3044,7 @@ server_client_reset_state(struct client *c)
cy += status_line_size(c);
}
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;
if (!cursor)

64
tmux.h
View File

@@ -956,7 +956,7 @@ struct screen_sel;
struct screen_titles;
struct screen {
char *title;
char *path;
char *path;
struct screen_titles *titles;
struct grid *grid; /* grid data */
@@ -1177,6 +1177,19 @@ enum client_theme {
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. */
struct window_pane {
u_int id;
@@ -1268,6 +1281,8 @@ struct window_pane {
struct style scrollbar_style;
struct visible_ranges r;
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) zentry; /* z-index link in list of all panes */
@@ -1606,6 +1621,7 @@ struct tty {
size_t discarded;
struct termios tio;
struct visible_ranges r;
struct grid_cell cell;
struct grid_cell last_cell;
@@ -1922,22 +1938,15 @@ struct 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. */
typedef int (*prompt_input_cb)(struct client *, void *, const char *, int);
typedef void (*prompt_free_cb)(void *);
typedef void (*overlay_check_cb)(struct client*, void *, u_int, u_int, u_int,
struct overlay_ranges *);
typedef struct visible_ranges *(*overlay_check_cb)(struct client*, void *,
u_int, u_int, 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 *,
struct screen_redraw_ctx *);
struct screen_redraw_ctx *);
typedef int (*overlay_key_cb)(struct client *, void *, struct key_event *);
typedef void (*overlay_free_cb)(struct client *, void *);
typedef void (*overlay_resize_cb)(struct client *, void *);
@@ -2269,14 +2278,6 @@ struct mode_tree_sort_criteria {
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 */
extern struct options *global_options;
extern struct options *global_s_options;
@@ -2538,6 +2539,8 @@ void tty_reset(struct tty *);
void tty_region_off(struct tty *);
void tty_margin_off(struct tty *);
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_putcode(struct tty *, enum tty_code_code);
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_set_title(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 *);
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,
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_free_cb, overlay_resize_cb, void *);
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,
u_int, struct overlay_ranges *);
u_int, struct visible_ranges *);
void server_client_set_key_table(struct client *, const char *);
const char *server_client_get_key_table(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);
int screen_redraw_is_visible(struct visible_ranges *, u_int px);
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 */
@@ -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_hide_selection(struct screen *);
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 *);
void screen_alternate_on(struct screen *, struct grid_cell *, int);
void screen_alternate_off(struct screen *, struct grid_cell *, int);
const char *screen_mode_to_string(int);
__unused char * screen_print(struct screen *s);
/* window.c */
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 *,
menu_choice_cb, void *);
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 overlay_ranges *);
struct visible_ranges *menu_check_cb(struct client *, void *, u_int, u_int,
u_int);
void menu_draw_cb(struct client *, void *,
struct screen_redraw_ctx *);
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);
}

370
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(struct tty *, u_int, u_int);
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_emulate_repeat(struct tty *, enum tty_code_code,
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_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 void tty_check_overlay_range(struct tty *, u_int, u_int, u_int,
struct overlay_ranges *);
#ifdef ENABLE_SIXEL
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
@@ -528,6 +521,8 @@ void
tty_free(struct tty *tty)
{
tty_close(tty);
free(tty->r.ranges);
}
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)
{
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
* emulated.
*/
static int
int
tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg)
{
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)
{
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);
@@ -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
* bit if there is an overlay.
*/
tty_check_overlay_range(tty, px, py, nx, &r);
for (i = 0; i < OVERLAY_MAX_RANGES; i++) {
if (r.nx[i] == 0)
continue;
tty_cursor(tty, r.px[i], py);
tty_repeat_space(tty, r.nx[i]);
}
tty_cursor(tty, px, py);
tty_repeat_space(tty, nx);
}
/* 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 window_pane *wp = ctx->arg;
struct visible_ranges *vr = NULL;
u_int r, i, x, rx, ry;
struct visible_ranges *r;
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);
if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry)) {
vr = screen_redraw_get_visible_ranges(wp, x, ry, rx);
for (r=0; r < vr->used; r++) {
if (vr->nx[r] == 0)
if (tty_clamp_line(tty, ctx, px, py, nx, &l, &x, &rx, &ry)) {
r = tty_check_overlay_range(tty, x, ry, rx);
r = screen_redraw_get_visible_ranges(wp, x, ry, rx, r);
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
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;
struct window *w = c->session->curw->window;
struct window_pane *wpl, *wp = ctx->arg;
struct visible_ranges *vr = NULL;
u_int r, yy, overlap = 0, oy = 0;
struct visible_ranges *r;
struct visible_range *ri;
u_int i, yy, overlap = 0, oy = 0;
char tmp[64];
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. */
for (yy = py; yy < py + ny; yy++) {
vr = screen_redraw_get_visible_ranges(wp, px, yy - oy, nx);
for (r=0; r < vr->used; r++) {
if (vr->nx[r] == 0)
continue;
tty_clear_line(tty, defaults, yy, vr->px[r], vr->nx[r], bg);
r = tty_check_overlay_range(tty, px, yy - oy, nx);
r = screen_redraw_get_visible_ranges(wp, px, yy - oy, nx, r);
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
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 window_pane *wp = ctx->arg;
struct visible_ranges *vr = NULL;
u_int nx = ctx->sx, i, x, rx, ry, r;
struct visible_ranges *r = NULL;
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);
if (!ctx->bigger) {
if (wp) {
vr = screen_redraw_get_visible_ranges(wp, 0, ctx->yoff + py, nx);
for (r=0; r < vr->used; r++) {
if (vr->nx[r] == 0)
continue;
tty_draw_line(tty, s, vr->px[r], py, vr->nx[r],
ctx->xoff + vr->px[r], ctx->yoff + py,
r = tty_check_overlay_range(tty, 0, ctx->yoff + py, nx);
r = screen_redraw_get_visible_ranges(wp, 0, ctx->yoff +
py, nx, r);
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
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);
}
} else {
@@ -1437,24 +1434,26 @@ tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
}
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) {
vr = screen_redraw_get_visible_ranges(wp, i, py, rx);
for (r=0; r < vr->used; r++) {
if (vr->nx[r] == 0)
r = tty_check_overlay_range(tty, i, py, rx);
r = screen_redraw_get_visible_ranges(wp, i, py, rx, r);
for (i=0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx == 0)
continue;
tty_draw_line(tty, s, i, py, vr->nx[r],
x + vr->px[r], ry, &ctx->defaults,
tty_draw_line(tty, s, i, py, ri->nx,
x + ri->px, ry, &ctx->defaults,
ctx->palette);
}
} 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);
}
}
}
static const struct grid_cell *
const struct grid_cell *
tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
{
static struct grid_cell new;
@@ -1487,233 +1486,34 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
return (&new);
}
/*
* Check if a single character is obstructed by the overlay and return a
* boolean.
*/
/* Check if a single character is covered by the overlay. */
static int
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 multiple overlays, so it's sufficient to check just the first
* two entries.
* With a single character, if there is anything visible (that is, the
* range is not empty), it must be that character.
*/
tty_check_overlay_range(tty, px, py, 1, &r);
if (r.nx[0] + r.nx[1] == 0)
return (0);
return (1);
r = tty_check_overlay_range(tty, px, py, 1);
return (!server_client_ranges_is_empty(r));
}
/* Return parts of the input range which are visible. */
static void
tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx,
struct overlay_ranges *r)
struct visible_ranges *
tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
{
struct client *c = tty->client;
if (c->overlay_check == NULL) {
r->px[0] = px;
r->nx[0] = nx;
r->px[1] = 0;
r->nx[1] = 0;
r->px[2] = 0;
r->nx[2] = 0;
return;
server_client_ensure_ranges(&tty->r, 1);
tty->r.ranges[0].px = px;
tty->r.ranges[0].nx = nx;
tty->r.used = 1;
return (&tty->r);
}
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);
return (c->overlay_check(c, c->overlay_data, px, py, nx));
}
#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;
struct screen *s = ctx->s;
struct overlay_ranges r;
struct window_pane *wp = ctx->arg;
struct visible_ranges *vr;
u_int px, py, i, vis = 0, vis2 = 0;
struct visible_ranges *r;
u_int px, py, i, vis = 0;
px = ctx->xoff + ctx->ocx - ctx->wox;
py = ctx->yoff + ctx->ocy - ctx->woy;
@@ -2286,11 +2084,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
(gcp->data.width == 1 && !tty_check_overlay(tty, px, py)))
return;
vr = screen_redraw_get_visible_ranges(wp, px, py,
gcp->data.width);
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),
ctx->xoff - ctx->wox, py, &ctx->defaults, ctx->palette);
return;
@@ -2298,13 +2093,10 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
/* Handle partially obstructed wide characters. */
if (gcp->data.width > 1) {
for (i = 0; i < vr->used; i++)
vis2 += vr->nx[i];
tty_check_overlay_range(tty, px, py, gcp->data.width, &r);
for (i = 0; i < OVERLAY_MAX_RANGES; i++)
vis += r.nx[i];
if (vis < gcp->data.width ||
vis2 < gcp->data.width) { /* xxxx need to check visible range */
r = tty_check_overlay_range(tty, px, py, gcp->data.width);
for (i = 0; i < r->used; i++)
vis += r->ranges[i].nx;
if (vis < gcp->data.width) {
tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width,
px, py, &ctx->defaults, ctx->palette);
return;
@@ -2319,7 +2111,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty);
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,
ctx->s->hyperlinks);
@@ -2330,7 +2122,8 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
void
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;
char *cp = ctx->ptr;
@@ -2355,20 +2148,21 @@ tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
tty_margin_off(tty);
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. */
px = ctx->xoff + ctx->ocx - ctx->wox;
py = ctx->yoff + ctx->ocy - ctx->woy;
tty_check_overlay_range(tty, px, py, ctx->num, &r);
for (i = 0; i < OVERLAY_MAX_RANGES; i++) {
if (r.nx[i] == 0)
continue;
/* Convert back to pane position for printing. */
cx = r.px[i] - ctx->xoff + ctx->wox;
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
tty_putn(tty, cp + r.px[i] - px, r.nx[i], r.nx[i]);
r = tty_check_overlay_range(tty, px, py, ctx->num);
for (i = 0; i < r->used; i++) {
ri = &r->ranges[i];
if (ri->nx != 0) {
cx = ri->px - ctx->xoff + ctx->wox;
tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
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,
struct colour_palette *palette, u_int bg, struct hyperlinks *hl)
{