Merge branch 'obsd-master'
[tmux.git] / screen-redraw.c
blob410bd78dcee4ab76338fb2c6ac39293d1e8201ee
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #include "tmux.h"
26 static void screen_redraw_draw_borders(struct screen_redraw_ctx *);
27 static void screen_redraw_draw_panes(struct screen_redraw_ctx *);
28 static void screen_redraw_draw_status(struct screen_redraw_ctx *);
29 static void screen_redraw_draw_pane(struct screen_redraw_ctx *,
30 struct window_pane *);
31 static void screen_redraw_set_context(struct client *,
32 struct screen_redraw_ctx *);
34 #define START_ISOLATE "\342\201\246"
35 #define END_ISOLATE "\342\201\251"
37 /* Border in relation to a pane. */
38 enum screen_redraw_border_type {
39 SCREEN_REDRAW_OUTSIDE,
40 SCREEN_REDRAW_INSIDE,
41 SCREEN_REDRAW_BORDER_LEFT,
42 SCREEN_REDRAW_BORDER_RIGHT,
43 SCREEN_REDRAW_BORDER_TOP,
44 SCREEN_REDRAW_BORDER_BOTTOM
46 #define BORDER_MARKERS " +,.-"
48 /* Get cell border character. */
49 static void
50 screen_redraw_border_set(struct window *w, struct window_pane *wp,
51 enum pane_lines pane_lines, int cell_type, struct grid_cell *gc)
53 u_int idx;
55 if (cell_type == CELL_OUTSIDE && w->fill_character != NULL) {
56 utf8_copy(&gc->data, &w->fill_character[0]);
57 return;
60 switch (pane_lines) {
61 case PANE_LINES_NUMBER:
62 if (cell_type == CELL_OUTSIDE) {
63 gc->attr |= GRID_ATTR_CHARSET;
64 utf8_set(&gc->data, CELL_BORDERS[CELL_OUTSIDE]);
65 break;
67 gc->attr &= ~GRID_ATTR_CHARSET;
68 if (wp != NULL && window_pane_index(wp, &idx) == 0)
69 utf8_set(&gc->data, '0' + (idx % 10));
70 else
71 utf8_set(&gc->data, '*');
72 break;
73 case PANE_LINES_DOUBLE:
74 gc->attr &= ~GRID_ATTR_CHARSET;
75 utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
76 break;
77 case PANE_LINES_HEAVY:
78 gc->attr &= ~GRID_ATTR_CHARSET;
79 utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
80 break;
81 case PANE_LINES_SIMPLE:
82 gc->attr &= ~GRID_ATTR_CHARSET;
83 utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
84 break;
85 default:
86 gc->attr |= GRID_ATTR_CHARSET;
87 utf8_set(&gc->data, CELL_BORDERS[cell_type]);
88 break;
92 /* Return if window has only two panes. */
93 static int
94 screen_redraw_two_panes(struct window *w, int direction)
96 struct window_pane *wp;
98 wp = TAILQ_NEXT(TAILQ_FIRST(&w->panes), entry);
99 if (wp == NULL)
100 return (0); /* one pane */
101 if (TAILQ_NEXT(wp, entry) != NULL)
102 return (0); /* more than two panes */
103 if (direction == 0 && wp->xoff == 0)
104 return (0);
105 if (direction == 1 && wp->yoff == 0)
106 return (0);
107 return (1);
110 /* Check if cell is on the border of a pane. */
111 static enum screen_redraw_border_type
112 screen_redraw_pane_border(struct screen_redraw_ctx *ctx, struct window_pane *wp,
113 u_int px, u_int py)
115 struct options *oo = wp->window->options;
116 int split = 0;
117 u_int ex = wp->xoff + wp->sx, ey = wp->yoff + wp->sy;
118 int pane_status = ctx->pane_status;
120 /* Inside pane. */
121 if (px >= wp->xoff && px < ex && py >= wp->yoff && py < ey)
122 return (SCREEN_REDRAW_INSIDE);
124 /* Get pane indicator. */
125 switch (options_get_number(oo, "pane-border-indicators")) {
126 case PANE_BORDER_COLOUR:
127 case PANE_BORDER_BOTH:
128 split = 1;
129 break;
132 /* Left/right borders. */
133 if (pane_status == PANE_STATUS_OFF) {
134 if (screen_redraw_two_panes(wp->window, 0) && split) {
135 if (wp->xoff == 0 && px == wp->sx && py <= wp->sy / 2)
136 return (SCREEN_REDRAW_BORDER_RIGHT);
137 if (wp->xoff != 0 &&
138 px == wp->xoff - 1 &&
139 py > wp->sy / 2)
140 return (SCREEN_REDRAW_BORDER_LEFT);
141 } else {
142 if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
143 if (wp->xoff != 0 && px == wp->xoff - 1)
144 return (SCREEN_REDRAW_BORDER_LEFT);
145 if (px == ex)
146 return (SCREEN_REDRAW_BORDER_RIGHT);
149 } else {
150 if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= ey) {
151 if (wp->xoff != 0 && px == wp->xoff - 1)
152 return (SCREEN_REDRAW_BORDER_LEFT);
153 if (px == ex)
154 return (SCREEN_REDRAW_BORDER_RIGHT);
158 /* Top/bottom borders. */
159 if (pane_status == PANE_STATUS_OFF) {
160 if (screen_redraw_two_panes(wp->window, 1) && split) {
161 if (wp->yoff == 0 && py == wp->sy && px <= wp->sx / 2)
162 return (SCREEN_REDRAW_BORDER_BOTTOM);
163 if (wp->yoff != 0 &&
164 py == wp->yoff - 1 &&
165 px > wp->sx / 2)
166 return (SCREEN_REDRAW_BORDER_TOP);
167 } else {
168 if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
169 if (wp->yoff != 0 && py == wp->yoff - 1)
170 return (SCREEN_REDRAW_BORDER_TOP);
171 if (py == ey)
172 return (SCREEN_REDRAW_BORDER_BOTTOM);
175 } else if (pane_status == PANE_STATUS_TOP) {
176 if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
177 if (wp->yoff != 0 && py == wp->yoff - 1)
178 return (SCREEN_REDRAW_BORDER_TOP);
180 } else {
181 if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= ex) {
182 if (py == ey)
183 return (SCREEN_REDRAW_BORDER_BOTTOM);
187 /* Outside pane. */
188 return (SCREEN_REDRAW_OUTSIDE);
191 /* Check if a cell is on a border. */
192 static int
193 screen_redraw_cell_border(struct screen_redraw_ctx *ctx, u_int px, u_int py)
195 struct client *c = ctx->c;
196 struct window *w = c->session->curw->window;
197 struct window_pane *wp;
198 u_int sy = w->sy;
200 if (ctx->pane_status == PANE_STATUS_BOTTOM)
201 sy--;
203 /* Outside the window? */
204 if (px > w->sx || py > sy)
205 return (0);
207 /* On the window border? */
208 if (px == w->sx || py == sy)
209 return (1);
211 /* Check all the panes. */
212 TAILQ_FOREACH(wp, &w->panes, entry) {
213 if (!window_pane_visible(wp))
214 continue;
215 switch (screen_redraw_pane_border(ctx, wp, px, py)) {
216 case SCREEN_REDRAW_INSIDE:
217 return (0);
218 case SCREEN_REDRAW_OUTSIDE:
219 break;
220 default:
221 return (1);
225 return (0);
228 /* Work out type of border cell from surrounding cells. */
229 static int
230 screen_redraw_type_of_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py)
232 struct client *c = ctx->c;
233 int pane_status = ctx->pane_status;
234 struct window *w = c->session->curw->window;
235 u_int sx = w->sx, sy = w->sy;
236 int borders = 0;
238 if (pane_status == PANE_STATUS_BOTTOM)
239 sy--;
241 /* Is this outside the window? */
242 if (px > sx || py > sy)
243 return (CELL_OUTSIDE);
246 * Construct a bitmask of whether the cells to the left (bit 4), right,
247 * top, and bottom (bit 1) of this cell are borders.
249 if (px == 0 || screen_redraw_cell_border(ctx, px - 1, py))
250 borders |= 8;
251 if (px <= sx && screen_redraw_cell_border(ctx, px + 1, py))
252 borders |= 4;
253 if (pane_status == PANE_STATUS_TOP) {
254 if (py != 0 &&
255 screen_redraw_cell_border(ctx, px, py - 1))
256 borders |= 2;
257 if (screen_redraw_cell_border(ctx, px, py + 1))
258 borders |= 1;
259 } else if (pane_status == PANE_STATUS_BOTTOM) {
260 if (py == 0 ||
261 screen_redraw_cell_border(ctx, px, py - 1))
262 borders |= 2;
263 if (py != sy &&
264 screen_redraw_cell_border(ctx, px, py + 1))
265 borders |= 1;
266 } else {
267 if (py == 0 ||
268 screen_redraw_cell_border(ctx, px, py - 1))
269 borders |= 2;
270 if (screen_redraw_cell_border(ctx, px, py + 1))
271 borders |= 1;
275 * Figure out what kind of border this cell is. Only one bit set
276 * doesn't make sense (can't have a border cell with no others
277 * connected).
279 switch (borders) {
280 case 15: /* 1111, left right top bottom */
281 return (CELL_JOIN);
282 case 14: /* 1110, left right top */
283 return (CELL_BOTTOMJOIN);
284 case 13: /* 1101, left right bottom */
285 return (CELL_TOPJOIN);
286 case 12: /* 1100, left right */
287 return (CELL_LEFTRIGHT);
288 case 11: /* 1011, left top bottom */
289 return (CELL_RIGHTJOIN);
290 case 10: /* 1010, left top */
291 return (CELL_BOTTOMRIGHT);
292 case 9: /* 1001, left bottom */
293 return (CELL_TOPRIGHT);
294 case 7: /* 0111, right top bottom */
295 return (CELL_LEFTJOIN);
296 case 6: /* 0110, right top */
297 return (CELL_BOTTOMLEFT);
298 case 5: /* 0101, right bottom */
299 return (CELL_TOPLEFT);
300 case 3: /* 0011, top bottom */
301 return (CELL_TOPBOTTOM);
303 return (CELL_OUTSIDE);
306 /* Check if cell inside a pane. */
307 static int
308 screen_redraw_check_cell(struct screen_redraw_ctx *ctx, u_int px, u_int py,
309 struct window_pane **wpp)
311 struct client *c = ctx->c;
312 struct window *w = c->session->curw->window;
313 struct window_pane *wp, *active;
314 int pane_status = ctx->pane_status;
315 u_int sx = w->sx, sy = w->sy;
316 int border;
317 u_int right, line;
319 *wpp = NULL;
321 if (px > sx || py > sy)
322 return (CELL_OUTSIDE);
323 if (px == sx || py == sy) /* window border */
324 return (screen_redraw_type_of_cell(ctx, px, py));
326 if (pane_status != PANE_STATUS_OFF) {
327 active = wp = server_client_get_pane(c);
328 do {
329 if (!window_pane_visible(wp))
330 goto next1;
332 if (pane_status == PANE_STATUS_TOP)
333 line = wp->yoff - 1;
334 else
335 line = wp->yoff + sy;
336 right = wp->xoff + 2 + wp->status_size - 1;
338 if (py == line && px >= wp->xoff + 2 && px <= right)
339 return (CELL_INSIDE);
341 next1:
342 wp = TAILQ_NEXT(wp, entry);
343 if (wp == NULL)
344 wp = TAILQ_FIRST(&w->panes);
345 } while (wp != active);
348 active = wp = server_client_get_pane(c);
349 do {
350 if (!window_pane_visible(wp))
351 goto next2;
352 *wpp = wp;
355 * If definitely inside, return. If not on border, skip.
356 * Otherwise work out the cell.
358 border = screen_redraw_pane_border(ctx, wp, px, py);
359 if (border == SCREEN_REDRAW_INSIDE)
360 return (CELL_INSIDE);
361 if (border == SCREEN_REDRAW_OUTSIDE)
362 goto next2;
363 return (screen_redraw_type_of_cell(ctx, px, py));
365 next2:
366 wp = TAILQ_NEXT(wp, entry);
367 if (wp == NULL)
368 wp = TAILQ_FIRST(&w->panes);
369 } while (wp != active);
371 return (CELL_OUTSIDE);
374 /* Check if the border of a particular pane. */
375 static int
376 screen_redraw_check_is(struct screen_redraw_ctx *ctx, u_int px, u_int py,
377 struct window_pane *wp)
379 enum screen_redraw_border_type border;
381 border = screen_redraw_pane_border(ctx, wp, px, py);
382 if (border != SCREEN_REDRAW_INSIDE && border != SCREEN_REDRAW_OUTSIDE)
383 return (1);
384 return (0);
387 /* Update pane status. */
388 static int
389 screen_redraw_make_pane_status(struct client *c, struct window_pane *wp,
390 struct screen_redraw_ctx *rctx, enum pane_lines pane_lines)
392 struct window *w = wp->window;
393 struct grid_cell gc;
394 const char *fmt;
395 struct format_tree *ft;
396 char *expanded;
397 int pane_status = rctx->pane_status;
398 u_int width, i, cell_type, px, py;
399 struct screen_write_ctx ctx;
400 struct screen old;
402 ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS);
403 format_defaults(ft, c, c->session, c->session->curw, wp);
405 if (wp == server_client_get_pane(c))
406 style_apply(&gc, w->options, "pane-active-border-style", ft);
407 else
408 style_apply(&gc, w->options, "pane-border-style", ft);
409 fmt = options_get_string(wp->options, "pane-border-format");
411 expanded = format_expand_time(ft, fmt);
412 if (wp->sx < 4)
413 wp->status_size = width = 0;
414 else
415 wp->status_size = width = wp->sx - 4;
417 memcpy(&old, &wp->status_screen, sizeof old);
418 screen_init(&wp->status_screen, width, 1, 0);
419 wp->status_screen.mode = 0;
421 screen_write_start(&ctx, &wp->status_screen);
423 for (i = 0; i < width; i++) {
424 px = wp->xoff + 2 + i;
425 if (pane_status == PANE_STATUS_TOP)
426 py = wp->yoff - 1;
427 else
428 py = wp->yoff + wp->sy;
429 cell_type = screen_redraw_type_of_cell(rctx, px, py);
430 screen_redraw_border_set(w, wp, pane_lines, cell_type, &gc);
431 screen_write_cell(&ctx, &gc);
433 gc.attr &= ~GRID_ATTR_CHARSET;
435 screen_write_cursormove(&ctx, 0, 0, 0);
436 format_draw(&ctx, &gc, width, expanded, NULL, 0);
437 screen_write_stop(&ctx);
439 free(expanded);
440 format_free(ft);
442 if (grid_compare(wp->status_screen.grid, old.grid) == 0) {
443 screen_free(&old);
444 return (0);
446 screen_free(&old);
447 return (1);
450 /* Draw pane status. */
451 static void
452 screen_redraw_draw_pane_status(struct screen_redraw_ctx *ctx)
454 struct client *c = ctx->c;
455 struct window *w = c->session->curw->window;
456 struct tty *tty = &c->tty;
457 struct window_pane *wp;
458 struct screen *s;
459 u_int i, x, width, xoff, yoff, size;
461 log_debug("%s: %s @%u", __func__, c->name, w->id);
463 TAILQ_FOREACH(wp, &w->panes, entry) {
464 if (!window_pane_visible(wp))
465 continue;
466 s = &wp->status_screen;
468 size = wp->status_size;
469 if (ctx->pane_status == PANE_STATUS_TOP)
470 yoff = wp->yoff - 1;
471 else
472 yoff = wp->yoff + wp->sy;
473 xoff = wp->xoff + 2;
475 if (xoff + size <= ctx->ox ||
476 xoff >= ctx->ox + ctx->sx ||
477 yoff < ctx->oy ||
478 yoff >= ctx->oy + ctx->sy)
479 continue;
481 if (xoff >= ctx->ox && xoff + size <= ctx->ox + ctx->sx) {
482 /* All visible. */
483 i = 0;
484 x = xoff - ctx->ox;
485 width = size;
486 } else if (xoff < ctx->ox && xoff + size > ctx->ox + ctx->sx) {
487 /* Both left and right not visible. */
488 i = ctx->ox;
489 x = 0;
490 width = ctx->sx;
491 } else if (xoff < ctx->ox) {
492 /* Left not visible. */
493 i = ctx->ox - xoff;
494 x = 0;
495 width = size - i;
496 } else {
497 /* Right not visible. */
498 i = 0;
499 x = xoff - ctx->ox;
500 width = size - x;
503 if (ctx->statustop)
504 yoff += ctx->statuslines;
505 tty_draw_line(tty, s, i, 0, width, x, yoff - ctx->oy,
506 &grid_default_cell, NULL);
508 tty_cursor(tty, 0, 0);
511 /* Update status line and change flags if unchanged. */
512 static uint64_t
513 screen_redraw_update(struct client *c, uint64_t flags)
515 struct window *w = c->session->curw->window;
516 struct window_pane *wp;
517 struct options *wo = w->options;
518 int redraw;
519 enum pane_lines lines;
520 struct screen_redraw_ctx ctx;
522 if (c->message_string != NULL)
523 redraw = status_message_redraw(c);
524 else if (c->prompt_string != NULL)
525 redraw = status_prompt_redraw(c);
526 else
527 redraw = status_redraw(c);
528 if (!redraw && (~flags & CLIENT_REDRAWSTATUSALWAYS))
529 flags &= ~CLIENT_REDRAWSTATUS;
531 if (c->overlay_draw != NULL)
532 flags |= CLIENT_REDRAWOVERLAY;
534 if (options_get_number(wo, "pane-border-status") != PANE_STATUS_OFF) {
535 screen_redraw_set_context(c, &ctx);
536 lines = options_get_number(wo, "pane-border-lines");
537 redraw = 0;
538 TAILQ_FOREACH(wp, &w->panes, entry) {
539 if (screen_redraw_make_pane_status(c, wp, &ctx, lines))
540 redraw = 1;
542 if (redraw)
543 flags |= CLIENT_REDRAWBORDERS;
545 return (flags);
548 /* Set up redraw context. */
549 static void
550 screen_redraw_set_context(struct client *c, struct screen_redraw_ctx *ctx)
552 struct session *s = c->session;
553 struct options *oo = s->options;
554 struct window *w = s->curw->window;
555 struct options *wo = w->options;
556 u_int lines;
558 memset(ctx, 0, sizeof *ctx);
559 ctx->c = c;
561 lines = status_line_size(c);
562 if (c->message_string != NULL || c->prompt_string != NULL)
563 lines = (lines == 0) ? 1 : lines;
564 if (lines != 0 && options_get_number(oo, "status-position") == 0)
565 ctx->statustop = 1;
566 ctx->statuslines = lines;
568 ctx->pane_status = options_get_number(wo, "pane-border-status");
569 ctx->pane_lines = options_get_number(wo, "pane-border-lines");
571 tty_window_offset(&c->tty, &ctx->ox, &ctx->oy, &ctx->sx, &ctx->sy);
573 log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__, c->name,
574 w->id, ctx->ox, ctx->oy, ctx->sx, ctx->sy, ctx->statuslines,
575 ctx->statustop);
578 /* Redraw entire screen. */
579 void
580 screen_redraw_screen(struct client *c)
582 struct screen_redraw_ctx ctx;
583 uint64_t flags;
585 if (c->flags & CLIENT_SUSPENDED)
586 return;
588 flags = screen_redraw_update(c, c->flags);
589 if ((flags & CLIENT_ALLREDRAWFLAGS) == 0)
590 return;
592 screen_redraw_set_context(c, &ctx);
593 tty_sync_start(&c->tty);
594 tty_update_mode(&c->tty, c->tty.mode, NULL);
596 if (flags & (CLIENT_REDRAWWINDOW|CLIENT_REDRAWBORDERS)) {
597 log_debug("%s: redrawing borders", c->name);
598 screen_redraw_draw_borders(&ctx);
599 if (ctx.pane_status != PANE_STATUS_OFF)
600 screen_redraw_draw_pane_status(&ctx);
602 if (flags & CLIENT_REDRAWWINDOW) {
603 log_debug("%s: redrawing panes", c->name);
604 screen_redraw_draw_panes(&ctx);
606 if (ctx.statuslines != 0 &&
607 (flags & (CLIENT_REDRAWSTATUS|CLIENT_REDRAWSTATUSALWAYS))) {
608 log_debug("%s: redrawing status", c->name);
609 screen_redraw_draw_status(&ctx);
611 if (c->overlay_draw != NULL && (flags & CLIENT_REDRAWOVERLAY)) {
612 log_debug("%s: redrawing overlay", c->name);
613 c->overlay_draw(c, c->overlay_data, &ctx);
616 tty_reset(&c->tty);
619 /* Redraw a single pane. */
620 void
621 screen_redraw_pane(struct client *c, struct window_pane *wp)
623 struct screen_redraw_ctx ctx;
625 if (!window_pane_visible(wp))
626 return;
628 screen_redraw_set_context(c, &ctx);
629 tty_sync_start(&c->tty);
630 tty_update_mode(&c->tty, c->tty.mode, NULL);
632 screen_redraw_draw_pane(&ctx, wp);
634 tty_reset(&c->tty);
637 /* Get border cell style. */
638 static const struct grid_cell *
639 screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x,
640 u_int y, struct window_pane *wp)
642 struct client *c = ctx->c;
643 struct session *s = c->session;
644 struct window *w = s->curw->window;
645 struct window_pane *active = server_client_get_pane(c);
646 struct options *oo = w->options;
647 struct format_tree *ft;
649 if (wp->border_gc_set)
650 return (&wp->border_gc);
651 wp->border_gc_set = 1;
653 ft = format_create_defaults(NULL, c, s, s->curw, wp);
654 if (screen_redraw_check_is(ctx, x, y, active))
655 style_apply(&wp->border_gc, oo, "pane-active-border-style", ft);
656 else
657 style_apply(&wp->border_gc, oo, "pane-border-style", ft);
658 format_free(ft);
660 return (&wp->border_gc);
663 /* Draw a border cell. */
664 static void
665 screen_redraw_draw_borders_cell(struct screen_redraw_ctx *ctx, u_int i, u_int j)
667 struct client *c = ctx->c;
668 struct session *s = c->session;
669 struct window *w = s->curw->window;
670 struct options *oo = w->options;
671 struct tty *tty = &c->tty;
672 struct format_tree *ft;
673 struct window_pane *wp, *active = server_client_get_pane(c);
674 struct grid_cell gc;
675 const struct grid_cell *tmp;
676 struct overlay_ranges r;
677 u_int cell_type, x = ctx->ox + i, y = ctx->oy + j;
678 int arrows = 0, border;
679 int isolates;
681 if (c->overlay_check != NULL) {
682 c->overlay_check(c, c->overlay_data, x, y, 1, &r);
683 if (r.nx[0] + r.nx[1] == 0)
684 return;
687 cell_type = screen_redraw_check_cell(ctx, x, y, &wp);
688 if (cell_type == CELL_INSIDE)
689 return;
691 if (wp == NULL) {
692 if (!ctx->no_pane_gc_set) {
693 ft = format_create_defaults(NULL, c, s, s->curw, NULL);
694 memcpy(&ctx->no_pane_gc, &grid_default_cell, sizeof gc);
695 style_add(&ctx->no_pane_gc, oo, "pane-border-style",
696 ft);
697 format_free(ft);
698 ctx->no_pane_gc_set = 1;
700 memcpy(&gc, &ctx->no_pane_gc, sizeof gc);
701 } else {
702 tmp = screen_redraw_draw_borders_style(ctx, x, y, wp);
703 if (tmp == NULL)
704 return;
705 memcpy(&gc, tmp, sizeof gc);
707 if (server_is_marked(s, s->curw, marked_pane.wp) &&
708 screen_redraw_check_is(ctx, x, y, marked_pane.wp))
709 gc.attr ^= GRID_ATTR_REVERSE;
711 screen_redraw_border_set(w, wp, ctx->pane_lines, cell_type, &gc);
713 if (cell_type == CELL_TOPBOTTOM &&
714 (c->flags & CLIENT_UTF8) &&
715 tty_term_has(tty->term, TTYC_BIDI))
716 isolates = 1;
717 else
718 isolates = 0;
720 if (ctx->statustop)
721 tty_cursor(tty, i, ctx->statuslines + j);
722 else
723 tty_cursor(tty, i, j);
724 if (isolates)
725 tty_puts(tty, END_ISOLATE);
727 switch (options_get_number(oo, "pane-border-indicators")) {
728 case PANE_BORDER_ARROWS:
729 case PANE_BORDER_BOTH:
730 arrows = 1;
731 break;
734 if (wp != NULL && arrows) {
735 border = screen_redraw_pane_border(ctx, active, x, y);
736 if (((i == wp->xoff + 1 &&
737 (cell_type == CELL_LEFTRIGHT ||
738 (cell_type == CELL_TOPJOIN &&
739 border == SCREEN_REDRAW_BORDER_BOTTOM) ||
740 (cell_type == CELL_BOTTOMJOIN &&
741 border == SCREEN_REDRAW_BORDER_TOP))) ||
742 (j == wp->yoff + 1 &&
743 (cell_type == CELL_TOPBOTTOM ||
744 (cell_type == CELL_LEFTJOIN &&
745 border == SCREEN_REDRAW_BORDER_RIGHT) ||
746 (cell_type == CELL_RIGHTJOIN &&
747 border == SCREEN_REDRAW_BORDER_LEFT)))) &&
748 screen_redraw_check_is(ctx, x, y, active)) {
749 gc.attr |= GRID_ATTR_CHARSET;
750 utf8_set(&gc.data, BORDER_MARKERS[border]);
754 tty_cell(tty, &gc, &grid_default_cell, NULL, NULL);
755 if (isolates)
756 tty_puts(tty, START_ISOLATE);
759 /* Draw the borders. */
760 static void
761 screen_redraw_draw_borders(struct screen_redraw_ctx *ctx)
763 struct client *c = ctx->c;
764 struct session *s = c->session;
765 struct window *w = s->curw->window;
766 struct window_pane *wp;
767 u_int i, j;
769 log_debug("%s: %s @%u", __func__, c->name, w->id);
771 TAILQ_FOREACH(wp, &w->panes, entry)
772 wp->border_gc_set = 0;
774 for (j = 0; j < c->tty.sy - ctx->statuslines; j++) {
775 for (i = 0; i < c->tty.sx; i++)
776 screen_redraw_draw_borders_cell(ctx, i, j);
780 /* Draw the panes. */
781 static void
782 screen_redraw_draw_panes(struct screen_redraw_ctx *ctx)
784 struct client *c = ctx->c;
785 struct window *w = c->session->curw->window;
786 struct window_pane *wp;
788 log_debug("%s: %s @%u", __func__, c->name, w->id);
790 TAILQ_FOREACH(wp, &w->panes, entry) {
791 if (window_pane_visible(wp))
792 screen_redraw_draw_pane(ctx, wp);
796 /* Draw the status line. */
797 static void
798 screen_redraw_draw_status(struct screen_redraw_ctx *ctx)
800 struct client *c = ctx->c;
801 struct window *w = c->session->curw->window;
802 struct tty *tty = &c->tty;
803 struct screen *s = c->status.active;
804 u_int i, y;
806 log_debug("%s: %s @%u", __func__, c->name, w->id);
808 if (ctx->statustop)
809 y = 0;
810 else
811 y = c->tty.sy - ctx->statuslines;
812 for (i = 0; i < ctx->statuslines; i++) {
813 tty_draw_line(tty, s, 0, i, UINT_MAX, 0, y + i,
814 &grid_default_cell, NULL);
818 /* Draw one pane. */
819 static void
820 screen_redraw_draw_pane(struct screen_redraw_ctx *ctx, struct window_pane *wp)
822 struct client *c = ctx->c;
823 struct window *w = c->session->curw->window;
824 struct tty *tty = &c->tty;
825 struct screen *s = wp->screen;
826 struct colour_palette *palette = &wp->palette;
827 struct grid_cell defaults;
828 u_int i, j, top, x, y, width;
830 log_debug("%s: %s @%u %%%u", __func__, c->name, w->id, wp->id);
832 if (wp->xoff + wp->sx <= ctx->ox || wp->xoff >= ctx->ox + ctx->sx)
833 return;
834 if (ctx->statustop)
835 top = ctx->statuslines;
836 else
837 top = 0;
838 for (j = 0; j < wp->sy; j++) {
839 if (wp->yoff + j < ctx->oy || wp->yoff + j >= ctx->oy + ctx->sy)
840 continue;
841 y = top + wp->yoff + j - ctx->oy;
843 if (wp->xoff >= ctx->ox &&
844 wp->xoff + wp->sx <= ctx->ox + ctx->sx) {
845 /* All visible. */
846 i = 0;
847 x = wp->xoff - ctx->ox;
848 width = wp->sx;
849 } else if (wp->xoff < ctx->ox &&
850 wp->xoff + wp->sx > ctx->ox + ctx->sx) {
851 /* Both left and right not visible. */
852 i = ctx->ox;
853 x = 0;
854 width = ctx->sx;
855 } else if (wp->xoff < ctx->ox) {
856 /* Left not visible. */
857 i = ctx->ox - wp->xoff;
858 x = 0;
859 width = wp->sx - i;
860 } else {
861 /* Right not visible. */
862 i = 0;
863 x = wp->xoff - ctx->ox;
864 width = ctx->sx - x;
866 log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u",
867 __func__, c->name, wp->id, i, j, x, y, width);
869 tty_default_colours(&defaults, wp);
870 tty_draw_line(tty, s, i, j, width, x, y, &defaults, palette);
873 #ifdef ENABLE_SIXEL
874 tty_draw_images(c, wp, s);
875 #endif