mirror of
https://github.com/tmux/tmux.git
synced 2026-03-06 15:55:33 +00:00
Merge with visible_ranges banch. Convert visible_ranges to new style.
This commit is contained in:
@@ -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
2
grid.c
@@ -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
10
menu.c
@@ -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
69
popup.c
@@ -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)) {
|
||||
|
||||
177
screen-redraw.c
177
screen-redraw.c
@@ -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 &&
|
||||
|
||||
@@ -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
|
||||
|
||||
74
screen.c
74
screen.c
@@ -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
|
||||
|
||||
@@ -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
64
tmux.h
@@ -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
319
tty-draw.c
Normal 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
370
tty.c
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user