diff --git a/menu.c b/menu.c index 901e0197..7b18772a 100644 --- a/menu.c +++ b/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 visible_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); } diff --git a/popup.c b/popup.c index 5422809c..adebb912 100644 --- a/popup.c +++ b/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,50 +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 visible_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; - static struct visible_ranges or[2] = { { NULL, 0, 0 }, { NULL, 0, 0 } }; - u_int i, j, k = 0; + struct popup_data *pd = data; + 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 < r->used; i++) { - server_client_overlay_range(pd->px, pd->py, pd->sx, - pd->sy, r->ranges[i].px, py, r->ranges[i].nx, &or[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"); - /* Caller must free when no longer used. */ - if (r->size == 0) { - r->ranges = xcalloc(2, sizeof(struct visible_range)); - r->size = 2; - r->used = 0; + /* + * 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->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 < r->used; i++) { - /* Each or[i] only has up to 2 ranges. */ - for (j = 0; j < or[i].used; j++) { - if (or[i].ranges[j].nx > 0) { - r->ranges[k].px = or[i].ranges[j].px; - r->ranges[k].nx = or[i].ranges[j].nx; - 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++; } } - return; + return (r); } - server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, - px, py, nx, r); - - return; + server_client_overlay_range(pd->px, pd->py, pd->sx, pd->sy, px, py, nx, + r); + return (r); } static void @@ -288,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); @@ -551,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)) { diff --git a/screen-redraw.c b/screen-redraw.c index 15ca76b7..8449f02f 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -808,23 +808,23 @@ screen_redraw_draw_border_arrows(struct screen_redraw_ctx *ctx, u_int i, static void screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j) { - struct client *c = ctx->c; - struct session *s = c->session; - struct window *w = s->curw->window; - struct options *oo = w->options; - struct tty *tty = &c->tty; - struct format_tree *ft; - struct window_pane *wp, *active = server_client_get_pane(c); - struct grid_cell gc; - const struct grid_cell *tmp; - static struct visible_ranges r = { NULL, 0, 0 }; - u_int cell_type; - u_int x = ctx->ox + i, y = ctx->oy + j; - int isolates; + struct client *c = ctx->c; + struct session *s = c->session; + struct window *w = s->curw->window; + struct options *oo = w->options; + struct tty *tty = &c->tty; + struct format_tree *ft; + struct window_pane *wp, *active = server_client_get_pane(c); + struct grid_cell gc; + const struct grid_cell *tmp; + 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.ranges[0].nx + r.ranges[1].nx == 0) + r = c->overlay_check(c, c->overlay_data, x, y, 1); + if (server_client_ranges_is_empty(r)) return; } @@ -938,14 +938,15 @@ screen_redraw_draw_status(struct screen_redraw_ctx *ctx) static void screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) { - struct client *c = ctx->c; - struct window *w = c->session->curw->window; - struct tty *tty = &c->tty; - struct screen *s = wp->screen; - struct colour_palette *palette = &wp->palette; - struct grid_cell defaults; - static struct visible_ranges vr; - u_int i, j, top, x, y, px, width, r; + struct client *c = ctx->c; + struct window *w = c->session->curw->window; + struct tty *tty = &c->tty; + struct screen *s = wp->screen; + struct colour_palette *palette = &wp->palette; + struct grid_cell defaults; + struct visible_ranges *r; + struct visible_range *rr; + u_int i, j, k, top, x, y, width; if (wp->base.mode & MODE_SYNC) screen_write_stop_sync(wp); @@ -989,22 +990,15 @@ screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp) log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u", __func__, c->name, wp->id, i, j, x, y, width); - /* xxxx Breaking up the tty_draw_line like this isn't fully working. */ - tty_check_overlay_range(tty, x, y, width, &vr); - tty_default_colours(&defaults, wp); - for (r=0; r < vr.used; r++) { - if (vr.ranges[r].nx == 0) - continue; - /* Convert window coordinates to tty coordinates. */ - px = vr.ranges[r].px; - /* 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.ranges[r].px - wp->xoff, j, - vr.ranges[r].nx, px, y, &defaults, palette); + r = tty_check_overlay_range(tty, x, y, width); + for (k = 0; k < r->used; k++) { + rr = &r->ranges[k]; + if (rr->nx != 0) { + tty_draw_line(tty, s, rr->px - wp->xoff, j, + rr->nx, rr->px, y, &defaults, palette); + } } } diff --git a/server-client.c b/server-client.c index c4952a92..f9c938cc 100644 --- a/server-client.c +++ b/server-client.c @@ -161,6 +161,29 @@ 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. @@ -169,22 +192,17 @@ 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 visible_ranges *r) { - u_int ox, onx; - - /* Caller must free when no longer used. */ - if (r->size == 0) { - r->ranges = xcalloc(2, sizeof(struct visible_range)); - r->size = 2; - r->used = 0; - } + u_int ox, onx; /* Trivial case of no overlap in the y direction. */ if (py < y || py > y + sy - 1) { + 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) { diff --git a/tmux.h b/tmux.h index 4544411c..247eced0 100644 --- a/tmux.h +++ b/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 */ @@ -1532,6 +1532,19 @@ struct key_event { size_t len; }; +/* 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 */ +}; + /* Terminal definition. */ struct tty_term { char *name; @@ -1599,6 +1612,7 @@ struct tty { size_t discarded; struct termios tio; + struct visible_ranges r; struct grid_cell cell; struct grid_cell last_cell; @@ -1915,28 +1929,15 @@ struct client_window { }; RB_HEAD(client_windows, client_window); -/* 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 */ - size_t used; /* number of entries in ranges */ - size_t size; /* allocated capacity of 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 visible_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 *); @@ -2559,8 +2560,8 @@ void tty_default_attributes(struct tty *, const struct grid_cell *, void tty_update_mode(struct tty *, int, struct screen *); const struct grid_cell *tty_check_codeset(struct tty *, const struct grid_cell *); -void tty_check_overlay_range(struct tty *, u_int, u_int, u_int, - struct visible_ranges *); +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, @@ -2577,7 +2578,6 @@ void tty_close(struct tty *); void tty_free(struct tty *); void tty_update_features(struct tty *); void tty_set_selection(struct tty *, const char *, const char *, size_t); -u_int tty_cell_width(const struct grid_cell *, u_int); void tty_write(void (*)(struct tty *, const struct tty_ctx *), struct tty_ctx *); void tty_cmd_alignmenttest(struct tty *, const struct tty_ctx *); @@ -2920,6 +2920,8 @@ 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 visible_ranges *); void server_client_set_key_table(struct client *, const char *); @@ -3623,8 +3625,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 visible_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 *); diff --git a/tty.c b/tty.c index 081b56de..d1c98d03 100644 --- a/tty.c +++ b/tty.c @@ -66,8 +66,6 @@ static void tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code, u_int); static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); static int tty_check_overlay(struct tty *, u_int, u_int); -void tty_check_overlay_range(struct tty *, u_int, u_int, u_int, - struct visible_ranges *); #ifdef ENABLE_SIXEL static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *), @@ -523,6 +521,8 @@ void tty_free(struct tty *tty) { tty_close(tty); + + free(tty->r.ranges); } void @@ -1411,66 +1411,34 @@ tty_check_codeset(struct tty *tty, const struct grid_cell *gc) return (&new); } -/* - * Compute the effective display width (in terminal columns) of a grid cell - * when it will be drawn at terminal column atcol. - */ -u_int -tty_cell_width(const struct grid_cell *gcp, u_int atcol) -{ - /* Tabs expand to the next tab stop (tab width = 8). */ - if (gcp->flags & GRID_FLAG_TAB) - return (8 - (atcol % 8)); - /* Normal characters: width stored in cell (1 or 2 usually). */ - return (gcp->data.width); -} - -/* - * 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) { - static struct visible_ranges r = { NULL, 0, 0 }; + 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. */ - if (r.size == 0) { - r.ranges = xcalloc(2, sizeof(struct visible_range)); - r.size = 2; - } - - tty_check_overlay_range(tty, px, py, 1, &r); - if (r.ranges[0].nx + r.ranges[1].nx == 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. */ -void -tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx, - struct visible_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 (r->size == 0) { - r->ranges = xcalloc(2, sizeof(struct visible_range)); - r->size = 2; - } - if (c->overlay_check == NULL) { - r->ranges[0].px = px; - r->ranges[0].nx = nx; - r->used = 1; - 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); - return; + return (c->overlay_check(c, c->overlay_data, px, py, nx)); } #ifdef ENABLE_SIXEL @@ -1998,7 +1966,7 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) { const struct grid_cell *gcp = ctx->cell; struct screen *s = ctx->s; - static struct visible_ranges r = { NULL, 0, 0 }; + struct visible_ranges *r; u_int px, py, i, vis = 0; px = ctx->xoff + ctx->ocx - ctx->wox; @@ -2015,9 +1983,9 @@ tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) /* Handle partially obstructed wide characters. */ if (gcp->data.width > 1) { - tty_check_overlay_range(tty, px, py, gcp->data.width, &r); - for (i = 0; i < r.used; i++) - vis += r.ranges[i].nx; + 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); @@ -2043,7 +2011,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 visible_ranges r = { NULL, 0, 0 }; + struct visible_ranges *r; + struct visible_range *rr; u_int i, px, py, cx; char *cp = ctx->ptr; @@ -2068,20 +2037,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 < r.used; i++) { - if (r.ranges[i].nx == 0) - continue; - /* Convert back to pane position for printing. */ - cx = r.ranges[i].px - ctx->xoff + ctx->wox; - tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy); - tty_putn(tty, cp + r.ranges[i].px - px, r.ranges[i].nx, r.ranges[i].nx); + r = tty_check_overlay_range(tty, px, py, ctx->num); + for (i = 0; i < r->used; i++) { + rr = &r->ranges[i]; + if (rr->nx != 0) { + cx = rr->px - ctx->xoff + ctx->wox; + tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy); + tty_putn(tty, cp + rr->px - px, rr->nx, rr->nx); + } } }