/* $OpenBSD$ */ /* * Copyright (c) 2026 Nicholas Marriott * * 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 #include #include #include #include "tmux.h" /* Type of span in the scene. */ enum redraw_span_type { REDRAW_SPAN_PANE, /* inside a pane */ REDRAW_SPAN_OUTSIDE, /* outside the window */ REDRAW_SPAN_EMPTY, /* inside the window but nothing visible */ REDRAW_SPAN_STATUS, /* pane status line */ REDRAW_SPAN_BORDER, /* pane border */ REDRAW_SPAN_SCROLLBAR, /* pane scrollbar */ }; #define REDRAW_SPAN_TYPES 6 /* Border connections to adjacent cells. */ #define REDRAW_BORDER_L 0x1 #define REDRAW_BORDER_R 0x2 #define REDRAW_BORDER_U 0x4 #define REDRAW_BORDER_D 0x8 /* Span flags. */ #define REDRAW_BORDER_IS_ARROW 0x1 #define REDRAW_SCROLLBAR_LEFT 0x2 #define REDRAW_SCROLLBAR_RIGHT 0x4 /* Draw operations. */ #define REDRAW_DRAW_PANE 0x1 #define REDRAW_DRAW_BORDER 0x2 #define REDRAW_DRAW_STATUS 0x4 #define REDRAW_DRAW_SCROLLBAR 0x8 #define REDRAW_DRAW_ALL 0xff /* Data for a span. */ struct redraw_span_data { enum redraw_span_type type; union { struct { /* Pane this span belongs to. */ struct window_pane *wp; /* Position of span inside the pane. */ u_int px; u_int py; } p; struct { /* Adjacent panes on each side. */ struct window_pane *top_wp; struct window_pane *bottom_wp; struct window_pane *left_wp; struct window_pane *right_wp; /* * The pane this span belongs if that is known when the * scene is built. This is used for the half coloured * active pane indicator. */ struct window_pane *style_wp; /* Cell type for this span. */ int cell_type; int cell_mask; /* Line styles for this span. */ enum pane_lines top_lines; enum pane_lines bottom_lines; enum pane_lines left_lines; enum pane_lines right_lines; /* Flags for this span. */ int flags; } b; struct { /* The pane and the offset into the status line. */ struct window_pane *wp; u_int offset; } st; struct { /* Pane this span belongs to. */ struct window_pane *wp; /* Line within the scrollbar. */ u_int y; /* Full height of scrollbar. */ u_int height; /* Flags for this span. */ int flags; } sb; }; }; /* A span of cells of the same type inside a line. */ struct redraw_span { u_int x; u_int width; struct redraw_span_data data; TAILQ_ENTRY(redraw_span) entry; }; TAILQ_HEAD(redraw_spans, redraw_span); /* A visible line on the client. */ struct redraw_line { struct redraw_spans spans[REDRAW_SPAN_TYPES]; }; /* A scene representing all the spans on the client. */ struct redraw_scene { struct client *c; struct window *w; u_int sx; u_int sy; u_int ox; u_int oy; struct redraw_line *lines; }; /* Cell for building the scene. */ struct redraw_build_cell { struct redraw_span_data data; }; /* Context for building the scene. */ struct redraw_build_ctx { struct client *c; struct window *w; u_int ox; u_int oy; u_int sx; u_int sy; int sb; int sbp; int ind; struct redraw_build_cell *cells; }; /* Initialize the context for building scene. */ static void screen_redraw_set_context(struct client *c, struct redraw_build_ctx *bctx) { struct session *s = c->session; struct window *w = s->curw->window; struct options *oo = w->options; memset(bctx, 0, sizeof *bctx); bctx->c = c; bctx->w = w; tty_window_offset(&c->tty, &bctx->ox, &bctx->oy, &bctx->sx, &bctx->sy); bctx->sb = options_get_number(oo, "pane-scrollbars"); bctx->sbp = options_get_number(oo, "pane-scrollbars-position"); bctx->ind = options_get_number(oo, "pane-border-indicators"); } /* Return a cell. */ static struct redraw_build_cell * screen_redraw_cell(struct redraw_build_ctx *bctx, u_int x, u_int y) { return (&bctx->cells[(y * bctx->sx) + x]); } /* Reset cell to either empty or outside the window. */ static void screen_redraw_reset_cell(struct redraw_build_ctx *bctx, u_int x, u_int y) { struct redraw_build_cell *bc = screen_redraw_cell(bctx, x, y); struct window *w = bctx->w; memset(bc, 0, sizeof *bc); if (bctx->ox + x <= w->sx && bctx->oy + y <= w->sy) bc->data.type = REDRAW_SPAN_EMPTY; else bc->data.type = REDRAW_SPAN_OUTSIDE; } /* Convert window position to scene position. */ static int screen_redraw_window_scene(struct redraw_build_ctx *bctx, int wx, int wy, u_int *x, u_int *y) { if (wx < 0 || wy < 0) return (0); if ((u_int)wx > bctx->w->sx || (u_int)wy > bctx->w->sy) return (0); if (wx < (int)bctx->ox || wy < (int)bctx->oy) return (0); wx -= bctx->ox; wy -= bctx->oy; if (wx < 0 || wy < 0) return (0); if ((u_int)wx >= bctx->sx || (u_int)wy >= bctx->sy) return (0); *x = wx; *y = wy; return (1); } /* Convert pane position to scene. */ static int screen_redraw_pane_scene(struct redraw_build_ctx *bctx, struct window_pane *wp, int px, int py, u_int *x, u_int *y) { int wx = wp->xoff + px, wy = wp->yoff + py; return (screen_redraw_window_scene(bctx, wx, wy, x, y)); } /* Convert redraw border mask to a cell type. */ static int screen_redraw_get_cell_type(int mask) { switch (mask) { case REDRAW_BORDER_L|REDRAW_BORDER_R|REDRAW_BORDER_U|REDRAW_BORDER_D: return (CELL_JOIN); case REDRAW_BORDER_L|REDRAW_BORDER_R|REDRAW_BORDER_U: return (CELL_BOTTOMJOIN); case REDRAW_BORDER_L|REDRAW_BORDER_R|REDRAW_BORDER_D: return (CELL_TOPJOIN); case REDRAW_BORDER_L|REDRAW_BORDER_R: case REDRAW_BORDER_L: case REDRAW_BORDER_R: return (CELL_LEFTRIGHT); case REDRAW_BORDER_L|REDRAW_BORDER_U|REDRAW_BORDER_D: return (CELL_RIGHTJOIN); case REDRAW_BORDER_L|REDRAW_BORDER_U: return (CELL_BOTTOMRIGHT); case REDRAW_BORDER_L|REDRAW_BORDER_D: return (CELL_TOPRIGHT); case REDRAW_BORDER_R|REDRAW_BORDER_U|REDRAW_BORDER_D: return (CELL_LEFTJOIN); case REDRAW_BORDER_R|REDRAW_BORDER_U: return (CELL_BOTTOMLEFT); case REDRAW_BORDER_R|REDRAW_BORDER_D: return (CELL_TOPLEFT); case REDRAW_BORDER_U|REDRAW_BORDER_D: case REDRAW_BORDER_U: case REDRAW_BORDER_D: return (CELL_TOPBOTTOM); } return (CELL_OUTSIDE); } /* Return if this cell has exactly two panes with a shared border. */ static int screen_redraw_two_panes(struct window *w, enum layout_type *type) { struct window_pane *wp; u_int count = 0; TAILQ_FOREACH(wp, &w->panes, entry) { if (window_pane_is_floating(wp) || wp->layout_cell == NULL) continue; count++; if (count > 2 || wp->layout_cell->parent == NULL) return (0); *type = wp->layout_cell->parent->type; } return (count == 2); } /* Clear the cells covered by a floating pane. */ static void screen_redraw_clear_floating_pane(struct redraw_build_ctx *bctx, struct window_pane *wp, int sb_w, int sb_left) { enum pane_lines pane_lines; u_int x, y; int left, right, top, bottom, wx, wy; if (!window_pane_is_floating(wp)) return; pane_lines = window_pane_get_pane_lines(wp); if (pane_lines == PANE_LINES_NONE) { left = wp->xoff; right = wp->xoff + (int)wp->sx - 1; top = wp->yoff; bottom = wp->yoff + (int)wp->sy - 1; } else { left = wp->xoff - 1; right = wp->xoff + (int)wp->sx; top = wp->yoff - 1; bottom = wp->yoff + (int)wp->sy; } if (sb_left) left -= sb_w; else right += sb_w; for (wy = top; wy <= bottom; wy++) { for (wx = left; wx <= right; wx++) { if (screen_redraw_window_scene(bctx, wx, wy, &x, &y)) screen_redraw_reset_cell(bctx, x, y); } } } /* Mark pane inside data. */ static void screen_redraw_mark_pane_inside(struct redraw_build_ctx *bctx, struct window_pane *wp) { struct redraw_build_cell *bc; u_int px, py, x, y; for (py = 0; py < wp->sy; py++) { for (px = 0; px < wp->sx; px++) { if (!screen_redraw_pane_scene(bctx, wp, px, py, &x, &y)) continue; bc = screen_redraw_cell(bctx, x, y); memset(bc, 0, sizeof *bc); bc->data.type = REDRAW_SPAN_PANE; bc->data.p.wp = wp; bc->data.p.px = px; bc->data.p.py = py; } } } /* Mark scrollbar data. */ static void screen_redraw_mark_pane_scrollbar(struct redraw_build_ctx *bctx, struct window_pane *wp, int sb_w, int sb_left) { struct redraw_build_cell *bc; u_int x, y; int wx, wy, sx, ex; u_int sy; if (sb_w == 0) return; if (sb_left) { sx = wp->xoff - sb_w; ex = wp->xoff - 1; } else { sx = wp->xoff + (int)wp->sx; ex = sx + sb_w - 1; } for (sy = 0; sy < wp->sy; sy++) { wy = wp->yoff + (int)sy; for (wx = sx; wx <= ex; wx++) { if (!screen_redraw_window_scene(bctx, wx, wy, &x, &y)) continue; bc = screen_redraw_cell(bctx, x, y); memset(bc, 0, sizeof *bc); bc->data.type = REDRAW_SPAN_SCROLLBAR; bc->data.sb.wp = wp; bc->data.sb.y = sy; bc->data.sb.height = wp->sy; if (sb_left) bc->data.sb.flags |= REDRAW_SCROLLBAR_LEFT; else bc->data.sb.flags |= REDRAW_SCROLLBAR_RIGHT; } } } /* * Mark one border cell. If a non-border cell is being marked as a border, * replace it. If it is already a border, merge it. */ static void screen_redraw_mark_border_cell(struct redraw_build_ctx *bctx, int wx, int wy, struct window_pane *wp, int top_owner, int bottom_owner, int mask) { struct redraw_build_cell *bc; enum pane_lines pane_lines; u_int x, y; if (!screen_redraw_window_scene(bctx, wx, wy, &x, &y)) return; bc = screen_redraw_cell(bctx, x, y); if (bc->data.type != REDRAW_SPAN_BORDER) { if (bc->data.type != REDRAW_SPAN_EMPTY) return; memset(bc, 0, sizeof *bc); bc->data.type = REDRAW_SPAN_BORDER; } pane_lines = window_pane_get_pane_lines(wp); if (top_owner) { bc->data.b.top_wp = wp; bc->data.b.top_lines = pane_lines; } if (bottom_owner) { bc->data.b.bottom_wp = wp; bc->data.b.bottom_lines = pane_lines; } if (mask & (REDRAW_BORDER_U|REDRAW_BORDER_D)) { if (wx < wp->xoff) { bc->data.b.right_wp = wp; bc->data.b.right_lines = pane_lines; } else if (wx >= wp->xoff + (int)wp->sx) { bc->data.b.left_wp = wp; bc->data.b.left_lines = pane_lines; } } mask |= bc->data.b.cell_mask; bc->data.b.cell_mask = mask; bc->data.b.cell_type = screen_redraw_get_cell_type(mask); } /* Mark area available for pane status line. */ static void screen_redraw_mark_border_status(struct redraw_build_ctx *bctx, struct window_pane *wp, int left, int right, int top, int bottom) { struct redraw_build_cell *bc; u_int x, y, offset = 0; int pane_status, wy, sx, ex, wx; pane_status = window_pane_get_pane_status(wp); if (pane_status == PANE_STATUS_OFF) return; if (pane_status == PANE_STATUS_TOP) wy = top; else wy = bottom; sx = left + 1; ex = right - 1; if (bctx->ind == PANE_BORDER_ARROWS || bctx->ind == PANE_BORDER_BOTH) { wx = wp->xoff + 1; if (wx >= sx && wx <= ex) sx = wx + 1; } if (sx > ex) return; for (wx = sx; wx <= ex; wx++, offset++) { if (!screen_redraw_window_scene(bctx, wx, wy, &x, &y)) continue; bc = screen_redraw_cell(bctx, x, y); if (bc->data.type != REDRAW_SPAN_BORDER) continue; bc->data.type = REDRAW_SPAN_STATUS; bc->data.st.wp = wp; bc->data.st.offset = offset; } } /* Mark where indicator arrows will go, if enabled. */ static void screen_redraw_mark_border_arrows(struct redraw_build_ctx *bctx, struct window_pane *wp, int left, int right, int top, int bottom) { struct redraw_build_cell *bc; u_int x, y; int wx, wy; if (bctx->ind != PANE_BORDER_ARROWS && bctx->ind != PANE_BORDER_BOTH) return; wx = wp->xoff + 1; if (wx >= left && wx <= right) { wy = top; if (screen_redraw_window_scene(bctx, wx, wy, &x, &y)) { bc = screen_redraw_cell(bctx, x, y); if (bc->data.type == REDRAW_SPAN_BORDER) bc->data.b.flags |= REDRAW_BORDER_IS_ARROW; } wy = bottom; if (screen_redraw_window_scene(bctx, wx, wy, &x, &y)) { bc = screen_redraw_cell(bctx, x, y); if (bc->data.type == REDRAW_SPAN_BORDER) bc->data.b.flags |= REDRAW_BORDER_IS_ARROW; } } wy = wp->yoff + 1; if (wy >= top && wy <= bottom) { wx = left; if (screen_redraw_window_scene(bctx, wx, wy, &x, &y)) { bc = screen_redraw_cell(bctx, x, y); if (bc->data.type == REDRAW_SPAN_BORDER) bc->data.b.flags |= REDRAW_BORDER_IS_ARROW; } wx = right; if (screen_redraw_window_scene(bctx, wx, wy, &x, &y)) { bc = screen_redraw_cell(bctx, x, y); if (bc->data.type == REDRAW_SPAN_BORDER) bc->data.b.flags |= REDRAW_BORDER_IS_ARROW; } } } /* Mark pane borders. */ static void screen_redraw_mark_pane_borders(struct redraw_build_ctx *bctx, struct window_pane *wp, int sb_w, int sb_left) { enum pane_lines pane_lines = window_pane_get_pane_lines(wp); int pane_status, left, right, top, bottom, wx, wy; int draw_top, draw_bottom, draw_left, draw_right, mask = 0; if (window_pane_is_floating(wp) && pane_lines == PANE_LINES_NONE) return; pane_status = window_pane_get_pane_status(wp); left = wp->xoff - 1; right = wp->xoff + (int)wp->sx; if (sb_w != 0) { if (sb_left) left -= sb_w; else right += sb_w; } top = wp->yoff - 1; bottom = wp->yoff + (int)wp->sy; draw_left = (left >= 0); draw_right = (right <= (int)bctx->w->sx); draw_top = (top >= 0); draw_bottom = (bottom <= (int)bctx->w->sy); if (pane_status == PANE_STATUS_TOP) draw_bottom = 0; else if (pane_status == PANE_STATUS_BOTTOM) draw_top = 0; if (draw_top) { for (wx = left; wx <= right; wx++) { mask = 0; if (wx > left) mask |= REDRAW_BORDER_L; if (wx < right) mask |= REDRAW_BORDER_R; screen_redraw_mark_border_cell(bctx, wx, top, wp, 0, 1, mask); } } if (draw_bottom) { for (wx = left; wx <= right; wx++) { mask = 0; if (wx > left) mask |= REDRAW_BORDER_L; if (wx < right) mask |= REDRAW_BORDER_R; screen_redraw_mark_border_cell(bctx, wx, bottom, wp, 1, 0, ((wx > left) ? REDRAW_BORDER_L : 0) | ((wx < right) ? REDRAW_BORDER_R : 0)); } } if (draw_left) { for (wy = top; wy <= bottom; wy++) { mask = 0; if (wy > top) mask |= REDRAW_BORDER_U; if (wy < bottom) mask |= REDRAW_BORDER_D; screen_redraw_mark_border_cell(bctx, left, wy, wp, 0, 0, mask); } } if (draw_right) { for (wy = top; wy <= bottom; wy++) { mask = 0; if (wy > top) mask |= REDRAW_BORDER_U; if (wy < bottom) mask |= REDRAW_BORDER_D; screen_redraw_mark_border_cell(bctx, right, wy, wp, 0, 0, mask); } } screen_redraw_mark_border_status(bctx, wp, left, right, top, bottom); screen_redraw_mark_border_arrows(bctx, wp, left, right, top, bottom); } /* * Mark an entire pane in the build grid. Floating panes overwrite anything * already below them. */ static void screen_redraw_mark_pane(struct redraw_build_ctx *bctx, struct window_pane *wp) { int sb_w = 0, sb_left = 0; if (!window_pane_is_visible(wp)) return; if (window_pane_show_scrollbar(wp, bctx->sb)) sb_w = wp->scrollbar_style.width + wp->scrollbar_style.pad; if (sb_w != 0 && bctx->sbp == PANE_SCROLLBARS_LEFT) sb_left = 1; screen_redraw_clear_floating_pane(bctx, wp, sb_w, sb_left); screen_redraw_mark_pane_inside(bctx, wp); screen_redraw_mark_pane_borders(bctx, wp, sb_w, sb_left); screen_redraw_mark_pane_scrollbar(bctx, wp, sb_w, sb_left); } /* Mark the indicator. */ static void screen_redraw_mark_two_pane_colours(struct redraw_build_ctx *bctx) { struct redraw_build_cell *bc; struct redraw_span_data *sd; enum layout_type type; u_int x, y, wx, wy; if (bctx->ind != PANE_BORDER_COLOUR && bctx->ind != PANE_BORDER_BOTH) return; if (!screen_redraw_two_panes(bctx->w, &type)) return; for (y = 0; y < bctx->sy; y++) { for (x = 0; x < bctx->sx; x++) { bc = screen_redraw_cell(bctx, x, y); if (bc->data.type != REDRAW_SPAN_BORDER) continue; sd = &bc->data; wx = bctx->ox + x; wy = bctx->oy + y; if (type == LAYOUT_LEFTRIGHT && sd->b.left_wp != NULL && sd->b.right_wp != NULL) { if (wy <= bctx->w->sy / 2) sd->b.style_wp = sd->b.left_wp; else sd->b.style_wp = sd->b.right_wp; } else if (type == LAYOUT_TOPBOTTOM && sd->b.top_wp != NULL && sd->b.bottom_wp != NULL) { if (wx <= bctx->w->sx / 2) sd->b.style_wp = sd->b.top_wp; else sd->b.style_wp = sd->b.bottom_wp; } } } } /* Return true if two adjacent build data can be coalesced into one span. */ static int screen_redraw_data_cmp(struct redraw_build_cell *a, struct redraw_build_cell *b) { struct redraw_span_data *ad = &a->data, *bd = &b->data; if (ad->type != bd->type) return (0); switch (ad->type) { case REDRAW_SPAN_PANE: if (ad->p.wp != bd->p.wp || ad->p.py != bd->p.py || ad->p.px + 1 != bd->p.px) return (0); return (1); case REDRAW_SPAN_BORDER: if (ad->b.top_wp != bd->b.top_wp || ad->b.bottom_wp != bd->b.bottom_wp || ad->b.left_wp != bd->b.left_wp || ad->b.right_wp != bd->b.right_wp || ad->b.style_wp != bd->b.style_wp || ad->b.top_lines != bd->b.top_lines || ad->b.bottom_lines != bd->b.bottom_lines || ad->b.left_lines != bd->b.left_lines || ad->b.right_lines != bd->b.right_lines || ad->b.cell_type != bd->b.cell_type || ad->b.cell_mask != bd->b.cell_mask || ad->b.flags != bd->b.flags) return (0); if (ad->b.flags & REDRAW_BORDER_IS_ARROW) return (0); return (1); case REDRAW_SPAN_STATUS: if (ad->st.wp != bd->st.wp || ad->st.offset + 1 != bd->st.offset) return (0); return (1); case REDRAW_SPAN_SCROLLBAR: if (ad->sb.wp != bd->sb.wp || ad->sb.y != bd->sb.y || ad->sb.height != bd->sb.height || ad->sb.flags != bd->sb.flags) return (0); return (1); case REDRAW_SPAN_OUTSIDE: case REDRAW_SPAN_EMPTY: return (1); } return (0); } /* Convert the cells into spans. */ static void screen_redraw_finish_scene(struct redraw_build_ctx *bctx, struct redraw_scene *scene) { struct redraw_build_cell *bc, *last; struct redraw_line *line; struct redraw_span *span; enum redraw_span_type type; u_int x, y, x0; for (y = 0; y < bctx->sy; y++) { line = &scene->lines[y]; x = 0; while (x < bctx->sx) { x0 = x; last = screen_redraw_cell(bctx, x, y); x++; while (x < bctx->sx) { bc = screen_redraw_cell(bctx, x, y); if (!screen_redraw_data_cmp(last, bc)) break; last = bc; x++; } bc = screen_redraw_cell(bctx, x0, y); type = bc->data.type; span = xcalloc(1, sizeof *span); span->x = x0; span->width = x - x0; span->data = bc->data; TAILQ_INSERT_TAIL(&line->spans[type], span, entry); } } } /* * Build and return a redraw scene for c. The caller owns the scene and must * free it with screen_redraw_free_scene(). */ static struct redraw_scene * screen_redraw_make_scene(struct client *c) { struct redraw_build_ctx bctx; struct redraw_scene *scene; struct window_pane *wp; u_int x, y, type; size_t ncells; if (c->flags & CLIENT_SUSPENDED) return (NULL); screen_redraw_set_context(c, &bctx); scene = xcalloc(1, sizeof *scene); scene->c = c; scene->w = bctx.w; scene->sx = bctx.sx; scene->sy = bctx.sy; scene->ox = bctx.ox; scene->oy = bctx.oy; scene->lines = xcalloc(scene->sy, sizeof *scene->lines); for (y = 0; y < scene->sy; y++) { for (type = 0; type < REDRAW_SPAN_TYPES; type++) TAILQ_INIT(&scene->lines[y].spans[type]); } if (bctx.sx != 0 && bctx.sy > SIZE_MAX / bctx.sx) fatalx("%s: too many cells", __func__); ncells = (size_t)bctx.sx * bctx.sy; if (ncells > SIZE_MAX / sizeof *bctx.cells) fatalx("%s: too many cells", __func__); bctx.cells = xmalloc(ncells * sizeof *bctx.cells); for (y = 0; y < bctx.sy; y++) { for (x = 0; x < bctx.sx; x++) screen_redraw_reset_cell(&bctx, x, y); } TAILQ_FOREACH_REVERSE(wp, &bctx.w->z_index, window_panes_zindex, zentry) screen_redraw_mark_pane(&bctx, wp); screen_redraw_mark_two_pane_colours(&bctx); screen_redraw_finish_scene(&bctx, scene); free(bctx.cells); return (scene); } /* Free a scene. */ static void screen_redraw_free_scene(struct redraw_scene *scene) { struct redraw_spans *spans; struct redraw_span *span, *span1; u_int y, type; for (y = 0; y < scene->sy; y++) { for (type = 0; type < REDRAW_SPAN_TYPES; type++) { spans = &scene->lines[y].spans[type]; TAILQ_FOREACH_SAFE(span, spans, entry, span1) { TAILQ_REMOVE(spans, span, entry); free(span); } } } free(scene->lines); free(scene); } /* Is this span adjacent to this pane? */ static int screen_redraw_span_has_pane(struct redraw_span *span, struct window_pane *wp) { if (span->data.b.top_wp == wp) return (1); if (span->data.b.bottom_wp == wp) return (1); if (span->data.b.left_wp == wp) return (1); if (span->data.b.right_wp == wp) return (1); return (0); } /* Draw a pane span. */ static void screen_redraw_draw_pane_span(struct redraw_scene *scene, struct redraw_span *span, u_int x, u_int y, u_int n) { struct client *c = scene->c; struct tty *tty = &c->tty; struct window_pane *wp = span->data.p.wp; struct screen *s = wp->screen; struct grid_cell defaults; struct tty_style_ctx style_ctx; u_int px, py; //XXX sync? tty_default_colours(&defaults, wp); style_ctx.defaults = &defaults; style_ctx.palette = &wp->palette; style_ctx.hyperlinks = s->hyperlinks; px = span->data.p.px + (x - span->x); py = span->data.p.py; tty_draw_line(tty, s, px, py, n, x, y, &style_ctx); } /* Draw a border span. */ static void screen_redraw_draw_border_span(struct redraw_scene *scene, struct redraw_span *span, u_int x, u_int y, u_int n) { struct client *c = scene->c; struct tty *tty = &c->tty; tty_cursor(tty, x, y); for (x = 0; x < n; x++) tty_putc(tty, '-'); } /* Draw a status span. */ static void screen_redraw_draw_status_span(struct redraw_scene *scene, struct redraw_span *span, u_int x, u_int y, u_int n) { struct client *c = scene->c; struct tty *tty = &c->tty; tty_cursor(tty, x, y); for (x = 0; x < n; x++) tty_putc(tty, 's'); } /* Draw a scrollbar span. */ static void screen_redraw_draw_scrollbar_span(struct redraw_scene *scene, struct redraw_span *span, u_int x, u_int y, u_int n) { struct window_pane *wp = span->data.sb.wp; struct screen *s = wp->screen; struct tty *tty = &scene->c->tty; struct style *sb_style = &wp->scrollbar_style; struct grid_cell gc, slgc, *gcp; double pct_view; u_int total_height, slider_h, slider_y; u_int sb_h = span->data.sb.height; u_int sb_y = span->data.sb.y; u_int i, off, sb_w, sb_pad; int cm_y, cm_size; if (window_pane_mode(wp) == WINDOW_PANE_NO_MODE) { total_height = screen_size_y(s) + screen_hsize(s); if (total_height == 0) return; pct_view = (double)sb_h / total_height; slider_h = (double)sb_h * pct_view; slider_y = sb_h - slider_h; } else { if (TAILQ_FIRST(&wp->modes) == NULL) return; if (window_copy_get_current_offset(wp, &cm_y, &cm_size) == 0) return; total_height = cm_size + sb_h; if (total_height == 0) return; pct_view = (double)sb_h / total_height; slider_h = (double)sb_h * pct_view; slider_y = (sb_h + 1) * ((double)cm_y / total_height); } if (slider_h < 1) slider_h = 1; if (slider_y >= sb_h) slider_y = sb_h - 1; wp->sb_slider_y = slider_y; wp->sb_slider_h = slider_h; gc = sb_style->gc; memcpy(&slgc, &gc, sizeof slgc); slgc.fg = gc.bg; slgc.bg = gc.fg; sb_w = sb_style->width; sb_pad = sb_style->pad; off = x - span->x; tty_cursor(tty, x, y); for (i = 0; i < n; i++) { if (span->data.sb.flags & REDRAW_SCROLLBAR_LEFT) { if (off + i >= sb_w && off + i < sb_w + sb_pad) { tty_cell(tty, &grid_default_cell, NULL); continue; } } else { if (off + i < sb_pad) { tty_cell(tty, &grid_default_cell, NULL); continue; } } if (sb_y >= slider_y && sb_y < slider_y + slider_h) gcp = &slgc; else gcp = &gc; tty_cell(tty, gcp, NULL); } } /* Draw a span. */ static void screen_redraw_draw_span(struct redraw_scene *scene, struct redraw_span *span, u_int y) { struct client *c = scene->c; struct tty *tty = &c->tty; struct visible_ranges *r; struct visible_range *rr; u_int i, x, n; r = tty_check_overlay_range(tty, span->x, y, span->width); for (i = 0; i < r->used; i++) { rr = &r->ranges[i]; if (rr->nx == 0) continue; x = rr->px; n = rr->nx; switch (span->data.type) { case REDRAW_SPAN_PANE: screen_redraw_draw_pane_span(scene, span, x, y, n); break; case REDRAW_SPAN_BORDER: case REDRAW_SPAN_EMPTY: case REDRAW_SPAN_OUTSIDE: screen_redraw_draw_border_span(scene, span, x, y, n); break; case REDRAW_SPAN_STATUS: screen_redraw_draw_status_span(scene, span, x, y, n); break; case REDRAW_SPAN_SCROLLBAR: screen_redraw_draw_scrollbar_span(scene, span, x, y, n); break; } } } /* Draw pane lines. */ static void screen_redraw_draw_pane_lines(struct redraw_scene *scene, u_int status_lines, struct window_pane *wp, int flags) { struct redraw_line *line; struct redraw_spans *spans; struct redraw_span *span; u_int y, cy, top, bottom; if (wp->yoff < (int)scene->oy) top = 0; else top = wp->yoff - scene->oy; if (wp->yoff + wp->sy <= scene->oy) bottom = 0; else bottom = wp->yoff + wp->sy - scene->oy; if (bottom > scene->sy) bottom = scene->sy; for (y = top; y < bottom; y++) { line = &scene->lines[y]; cy = status_lines + y; if (flags & REDRAW_DRAW_PANE) { spans = &line->spans[REDRAW_SPAN_PANE]; TAILQ_FOREACH(span, spans, entry) { if (span->data.p.wp != wp) continue; screen_redraw_draw_span(scene, span, cy); } } if (flags & REDRAW_DRAW_SCROLLBAR) { spans = &line->spans[REDRAW_SPAN_SCROLLBAR]; TAILQ_FOREACH(span, spans, entry) { if (span->data.sb.wp != wp) continue; cy = status_lines + y; screen_redraw_draw_span(scene, span, cy); } } } } /* Draw lines. */ static void screen_redraw_draw_lines(struct redraw_scene *scene, u_int status_lines, int flags) { struct redraw_line *line; struct redraw_spans *spans; struct redraw_span *span; u_int y, cy, type; for (y = 0; y < scene->sy; y++) { line = &scene->lines[y]; cy = status_lines + y; for (type = 0; type < REDRAW_SPAN_TYPES; type++) { if (flags != REDRAW_DRAW_ALL) { switch (type) { case REDRAW_SPAN_PANE: if (~flags & REDRAW_DRAW_PANE) continue; break; case REDRAW_SPAN_BORDER: if (~flags & REDRAW_DRAW_BORDER) continue; break; case REDRAW_SPAN_STATUS: if (~flags & REDRAW_DRAW_STATUS) continue; break; case REDRAW_SPAN_SCROLLBAR: if (~flags & REDRAW_DRAW_SCROLLBAR) continue; break; default: continue; } } spans = &line->spans[type]; TAILQ_FOREACH(span, spans, entry) screen_redraw_draw_span(scene, span, cy); } } } /* Draw scene to client. */ static void screen_redraw_draw(struct client *c, struct window_pane *wp, int flags) { struct session *s = c->session; struct tty *tty = &c->tty; struct redraw_scene *scene; u_int status_lines = 0; if (c->flags & CLIENT_SUSPENDED) return; scene = screen_redraw_make_scene(c); //XXX if (scene == NULL) return; if (options_get_number(s->options, "status-position") == 0) status_lines = status_line_size(c); tty_sync_start(tty); tty_update_mode(tty, 0, NULL); if (wp != NULL) screen_redraw_draw_pane_lines(scene, status_lines, wp, flags); else screen_redraw_draw_lines(scene, status_lines, flags); tty_reset(tty); tty_sync_end(tty); screen_redraw_free_scene(scene); //XXX } /* Draw screen. */ void screen_redraw_screen(struct client *c) { int flags = 0; if (c->flags & CLIENT_REDRAWWINDOW) screen_redraw_draw(c, NULL, REDRAW_DRAW_ALL); else { if (c->flags & CLIENT_REDRAWBORDERS) flags |= (REDRAW_DRAW_BORDER|REDRAW_DRAW_STATUS); if (c->flags & CLIENT_REDRAWSTATUS) flags |= REDRAW_DRAW_STATUS; screen_redraw_draw(c, NULL, flags); } //XXX client status line //XXX overlays } /* Draw a single pane. */ void screen_redraw_pane(struct client *c, struct window_pane *wp) { screen_redraw_draw(c, wp, REDRAW_DRAW_PANE|REDRAW_DRAW_SCROLLBAR); } /* Draw a pane's scrollbar. */ void screen_redraw_pane_scrollbar(struct client *c, struct window_pane *wp) { screen_redraw_draw(c, wp, REDRAW_DRAW_SCROLLBAR); }