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>
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
*);
33 static void screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx
*);
34 static void screen_redraw_draw_scrollbar(struct screen_redraw_ctx
*,
35 struct window_pane
*, int, int, int, u_int
, u_int
, u_int
);
36 static void screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx
*,
37 struct window_pane
*);
39 #define START_ISOLATE "\342\201\246"
40 #define END_ISOLATE "\342\201\251"
42 /* Border in relation to a pane. */
43 enum screen_redraw_border_type
{
44 SCREEN_REDRAW_OUTSIDE
,
46 SCREEN_REDRAW_BORDER_LEFT
,
47 SCREEN_REDRAW_BORDER_RIGHT
,
48 SCREEN_REDRAW_BORDER_TOP
,
49 SCREEN_REDRAW_BORDER_BOTTOM
51 #define BORDER_MARKERS " +,.-"
53 /* Get cell border character. */
55 screen_redraw_border_set(struct window
*w
, struct window_pane
*wp
,
56 enum pane_lines pane_lines
, int cell_type
, struct grid_cell
*gc
)
60 if (cell_type
== CELL_OUTSIDE
&& w
->fill_character
!= NULL
) {
61 utf8_copy(&gc
->data
, &w
->fill_character
[0]);
66 case PANE_LINES_NUMBER
:
67 if (cell_type
== CELL_OUTSIDE
) {
68 gc
->attr
|= GRID_ATTR_CHARSET
;
69 utf8_set(&gc
->data
, CELL_BORDERS
[CELL_OUTSIDE
]);
72 gc
->attr
&= ~GRID_ATTR_CHARSET
;
73 if (wp
!= NULL
&& window_pane_index(wp
, &idx
) == 0)
74 utf8_set(&gc
->data
, '0' + (idx
% 10));
76 utf8_set(&gc
->data
, '*');
78 case PANE_LINES_DOUBLE
:
79 gc
->attr
&= ~GRID_ATTR_CHARSET
;
80 utf8_copy(&gc
->data
, tty_acs_double_borders(cell_type
));
82 case PANE_LINES_HEAVY
:
83 gc
->attr
&= ~GRID_ATTR_CHARSET
;
84 utf8_copy(&gc
->data
, tty_acs_heavy_borders(cell_type
));
86 case PANE_LINES_SIMPLE
:
87 gc
->attr
&= ~GRID_ATTR_CHARSET
;
88 utf8_set(&gc
->data
, SIMPLE_BORDERS
[cell_type
]);
91 gc
->attr
|= GRID_ATTR_CHARSET
;
92 utf8_set(&gc
->data
, CELL_BORDERS
[cell_type
]);
97 /* Return if window has only two panes. */
99 screen_redraw_two_panes(struct window
*w
, int direction
)
101 struct window_pane
*wp
;
103 wp
= TAILQ_NEXT(TAILQ_FIRST(&w
->panes
), entry
);
105 return (0); /* one pane */
106 if (TAILQ_NEXT(wp
, entry
) != NULL
)
107 return (0); /* more than two panes */
108 if (direction
== 0 && wp
->xoff
== 0)
110 if (direction
== 1 && wp
->yoff
== 0)
115 /* Check if cell is on the border of a pane. */
116 static enum screen_redraw_border_type
117 screen_redraw_pane_border(struct screen_redraw_ctx
*ctx
, struct window_pane
*wp
,
120 struct options
*oo
= wp
->window
->options
;
121 u_int ex
= wp
->xoff
+ wp
->sx
, ey
= wp
->yoff
+ wp
->sy
;
122 int hsplit
= 0, vsplit
= 0, pane_status
= ctx
->pane_status
;
123 int pane_scrollbars
= ctx
->pane_scrollbars
, sb_w
= 0;
124 int sb_pos
= ctx
->pane_scrollbars_pos
;
127 if (px
>= wp
->xoff
&& px
< ex
&& py
>= wp
->yoff
&& py
< ey
)
128 return (SCREEN_REDRAW_INSIDE
);
130 /* Get pane indicator. */
131 switch (options_get_number(oo
, "pane-border-indicators")) {
132 case PANE_BORDER_COLOUR
:
133 case PANE_BORDER_BOTH
:
134 hsplit
= screen_redraw_two_panes(wp
->window
, 0);
135 vsplit
= screen_redraw_two_panes(wp
->window
, 1);
139 /* Are scrollbars enabled? */
140 if (pane_scrollbars
== PANE_SCROLLBARS_ALWAYS
||
141 (pane_scrollbars
== PANE_SCROLLBARS_MODAL
&&
142 window_pane_mode(wp
) != WINDOW_PANE_NO_MODE
))
143 sb_w
= PANE_SCROLLBARS_WIDTH
;
146 * Left/right borders. The wp->sy / 2 test is to colour only half the
147 * active window's border when there are two panes.
149 if ((wp
->yoff
== 0 || py
>= wp
->yoff
- 1) && py
<= ey
) {
150 if (sb_pos
== PANE_SCROLLBARS_LEFT
) {
151 if (wp
->xoff
- sb_w
== 0 && px
== wp
->sx
+ sb_w
)
152 if (!hsplit
|| (hsplit
&& py
<= wp
->sy
/ 2))
153 return (SCREEN_REDRAW_BORDER_RIGHT
);
154 if (wp
->xoff
- sb_w
!= 0 && px
== wp
->xoff
- sb_w
- 1)
155 if (!hsplit
|| (hsplit
&& py
> wp
->sy
/ 2))
156 return (SCREEN_REDRAW_BORDER_LEFT
);
157 } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
158 if (wp
->xoff
== 0 && px
== wp
->sx
+ sb_w
)
159 if (!hsplit
|| (hsplit
&& py
<= wp
->sy
/ 2))
160 return (SCREEN_REDRAW_BORDER_RIGHT
);
161 if (wp
->xoff
!= 0 && px
== wp
->xoff
- 1)
162 if (!hsplit
|| (hsplit
&& py
> wp
->sy
/ 2))
163 return (SCREEN_REDRAW_BORDER_LEFT
);
167 /* Top/bottom borders. */
168 if (vsplit
&& pane_status
== PANE_STATUS_OFF
&& sb_w
== 0) {
169 if (wp
->yoff
== 0 && py
== wp
->sy
&& px
<= wp
->sx
/ 2)
170 return (SCREEN_REDRAW_BORDER_BOTTOM
);
171 if (wp
->yoff
!= 0 && py
== wp
->yoff
- 1 && px
> wp
->sx
/ 2)
172 return (SCREEN_REDRAW_BORDER_TOP
);
174 if (sb_pos
== PANE_SCROLLBARS_LEFT
) {
175 if ((wp
->xoff
- sb_w
== 0 || px
>= wp
->xoff
- sb_w
) &&
176 (px
<= ex
|| (sb_w
!= 0 && px
- 1 == ex
))) {
177 if (wp
->yoff
!= 0 && py
== wp
->yoff
- 1)
178 return (SCREEN_REDRAW_BORDER_TOP
);
180 return (SCREEN_REDRAW_BORDER_BOTTOM
);
182 } else { /* sb_pos == PANE_SCROLLBARS_RIGHT */
183 if ((wp
->xoff
== 0 || px
>= wp
->xoff
) &&
184 (px
<= ex
|| (sb_w
!= 0 && px
- 1 == ex
))) {
185 if (wp
->yoff
!= 0 && py
== wp
->yoff
- 1)
186 return (SCREEN_REDRAW_BORDER_TOP
);
188 return (SCREEN_REDRAW_BORDER_BOTTOM
);
194 return (SCREEN_REDRAW_OUTSIDE
);
197 /* Check if a cell is on a border. */
199 screen_redraw_cell_border(struct screen_redraw_ctx
*ctx
, u_int px
, u_int py
)
201 struct client
*c
= ctx
->c
;
202 struct window
*w
= c
->session
->curw
->window
;
203 struct window_pane
*wp
;
206 if (ctx
->pane_status
== PANE_STATUS_BOTTOM
)
209 /* Outside the window? */
210 if (px
> w
->sx
|| py
> sy
)
213 /* On the window border? */
214 if (px
== w
->sx
|| py
== sy
)
217 /* Check all the panes. */
218 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
219 if (!window_pane_visible(wp
))
221 switch (screen_redraw_pane_border(ctx
, wp
, px
, py
)) {
222 case SCREEN_REDRAW_INSIDE
:
224 case SCREEN_REDRAW_OUTSIDE
:
234 /* Work out type of border cell from surrounding cells. */
236 screen_redraw_type_of_cell(struct screen_redraw_ctx
*ctx
, u_int px
, u_int py
)
238 struct client
*c
= ctx
->c
;
239 int pane_status
= ctx
->pane_status
;
240 struct window
*w
= c
->session
->curw
->window
;
241 u_int sx
= w
->sx
, sy
= w
->sy
;
244 if (pane_status
== PANE_STATUS_BOTTOM
)
247 /* Is this outside the window? */
248 if (px
> sx
|| py
> sy
)
249 return (CELL_OUTSIDE
);
252 * Construct a bitmask of whether the cells to the left (bit 8), right,
253 * top, and bottom (bit 1) of this cell are borders.
259 if (px
== 0 || screen_redraw_cell_border(ctx
, px
- 1, py
))
261 if (px
<= sx
&& screen_redraw_cell_border(ctx
, px
+ 1, py
))
263 if (pane_status
== PANE_STATUS_TOP
) {
265 screen_redraw_cell_border(ctx
, px
, py
- 1))
267 if (screen_redraw_cell_border(ctx
, px
, py
+ 1))
269 } else if (pane_status
== PANE_STATUS_BOTTOM
) {
271 screen_redraw_cell_border(ctx
, px
, py
- 1))
274 screen_redraw_cell_border(ctx
, px
, py
+ 1))
278 screen_redraw_cell_border(ctx
, px
, py
- 1))
280 if (screen_redraw_cell_border(ctx
, px
, py
+ 1))
285 * Figure out what kind of border this cell is. Only one bit set
286 * doesn't make sense (can't have a border cell with no others
290 case 15: /* 1111, left right top bottom */
292 case 14: /* 1110, left right top */
293 return (CELL_BOTTOMJOIN
);
294 case 13: /* 1101, left right bottom */
295 return (CELL_TOPJOIN
);
296 case 12: /* 1100, left right */
297 return (CELL_LEFTRIGHT
);
298 case 11: /* 1011, left top bottom */
299 return (CELL_RIGHTJOIN
);
300 case 10: /* 1010, left top */
301 return (CELL_BOTTOMRIGHT
);
302 case 9: /* 1001, left bottom */
303 return (CELL_TOPRIGHT
);
304 case 7: /* 0111, right top bottom */
305 return (CELL_LEFTJOIN
);
306 case 6: /* 0110, right top */
307 return (CELL_BOTTOMLEFT
);
308 case 5: /* 0101, right bottom */
309 return (CELL_TOPLEFT
);
310 case 3: /* 0011, top bottom */
311 return (CELL_TOPBOTTOM
);
313 return (CELL_OUTSIDE
);
316 /* Check if cell inside a pane. */
318 screen_redraw_check_cell(struct screen_redraw_ctx
*ctx
, u_int px
, u_int py
,
319 struct window_pane
**wpp
)
321 struct client
*c
= ctx
->c
;
322 struct window
*w
= c
->session
->curw
->window
;
323 struct window_pane
*wp
, *active
;
324 int pane_status
= ctx
->pane_status
;
325 u_int sx
= w
->sx
, sy
= w
->sy
;
326 int border
, pane_scrollbars
= ctx
->pane_scrollbars
;
328 int sb_pos
= ctx
->pane_scrollbars_pos
;
329 int sb_w
= PANE_SCROLLBARS_WIDTH
;
333 if (px
> sx
|| py
> sy
)
334 return (CELL_OUTSIDE
);
335 if (px
== sx
|| py
== sy
) /* window border */
336 return (screen_redraw_type_of_cell(ctx
, px
, py
));
338 if (pane_status
!= PANE_STATUS_OFF
) {
339 active
= wp
= server_client_get_pane(c
);
341 if (!window_pane_visible(wp
))
344 if (pane_status
== PANE_STATUS_TOP
)
347 line
= wp
->yoff
+ sy
;
348 right
= wp
->xoff
+ 2 + wp
->status_size
- 1;
350 if (py
== line
&& px
>= wp
->xoff
+ 2 && px
<= right
)
351 return (CELL_INSIDE
);
354 wp
= TAILQ_NEXT(wp
, entry
);
356 wp
= TAILQ_FIRST(&w
->panes
);
357 } while (wp
!= active
);
360 active
= wp
= server_client_get_pane(c
);
362 if (!window_pane_visible(wp
))
366 /* Check if CELL_SCROLLBAR */
367 if (pane_scrollbars
== PANE_SCROLLBARS_ALWAYS
||
368 (pane_scrollbars
== PANE_SCROLLBARS_MODAL
&&
369 window_pane_mode(wp
) != WINDOW_PANE_NO_MODE
)) {
371 if (pane_status
== PANE_STATUS_TOP
)
374 line
= wp
->yoff
+ wp
->sy
;
377 * Check if py could lie within a scrollbar. If the
378 * pane is at the top then py == 0 to sy; if the pane
379 * is not at the top, then yoff to yoff + sy.
381 if ((pane_status
&& py
!= line
) ||
382 (wp
->yoff
== 0 && py
< wp
->sy
) ||
383 (py
>= wp
->yoff
&& py
< wp
->yoff
+ wp
->sy
)) {
384 /* Check if px lies within a scrollbar. */
385 if ((sb_pos
== PANE_SCROLLBARS_RIGHT
&&
386 (px
>= wp
->xoff
+ wp
->sx
&&
387 px
< wp
->xoff
+ wp
->sx
+ sb_w
)) ||
388 (sb_pos
== PANE_SCROLLBARS_LEFT
&&
389 (px
>= wp
->xoff
- sb_w
&&
391 return (CELL_SCROLLBAR
);
396 * If definitely inside, return. If not on border, skip.
397 * Otherwise work out the cell.
399 border
= screen_redraw_pane_border(ctx
, wp
, px
, py
);
400 if (border
== SCREEN_REDRAW_INSIDE
)
401 return (CELL_INSIDE
);
402 if (border
== SCREEN_REDRAW_OUTSIDE
)
404 return (screen_redraw_type_of_cell(ctx
, px
, py
));
407 wp
= TAILQ_NEXT(wp
, entry
);
409 wp
= TAILQ_FIRST(&w
->panes
);
410 } while (wp
!= active
);
412 return (CELL_OUTSIDE
);
415 /* Check if the border of a particular pane. */
417 screen_redraw_check_is(struct screen_redraw_ctx
*ctx
, u_int px
, u_int py
,
418 struct window_pane
*wp
)
420 enum screen_redraw_border_type border
;
422 border
= screen_redraw_pane_border(ctx
, wp
, px
, py
);
423 if (border
!= SCREEN_REDRAW_INSIDE
&& border
!= SCREEN_REDRAW_OUTSIDE
)
428 /* Update pane status. */
430 screen_redraw_make_pane_status(struct client
*c
, struct window_pane
*wp
,
431 struct screen_redraw_ctx
*rctx
, enum pane_lines pane_lines
)
433 struct window
*w
= wp
->window
;
436 struct format_tree
*ft
;
438 int pane_status
= rctx
->pane_status
;
439 u_int width
, i
, cell_type
, px
, py
;
440 struct screen_write_ctx ctx
;
443 ft
= format_create(c
, NULL
, FORMAT_PANE
|wp
->id
, FORMAT_STATUS
);
444 format_defaults(ft
, c
, c
->session
, c
->session
->curw
, wp
);
446 if (wp
== server_client_get_pane(c
))
447 style_apply(&gc
, w
->options
, "pane-active-border-style", ft
);
449 style_apply(&gc
, w
->options
, "pane-border-style", ft
);
450 fmt
= options_get_string(wp
->options
, "pane-border-format");
452 expanded
= format_expand_time(ft
, fmt
);
454 wp
->status_size
= width
= 0;
456 wp
->status_size
= width
= wp
->sx
- 4;
458 memcpy(&old
, &wp
->status_screen
, sizeof old
);
459 screen_init(&wp
->status_screen
, width
, 1, 0);
460 wp
->status_screen
.mode
= 0;
462 screen_write_start(&ctx
, &wp
->status_screen
);
464 for (i
= 0; i
< width
; i
++) {
465 px
= wp
->xoff
+ 2 + i
;
466 if (pane_status
== PANE_STATUS_TOP
)
469 py
= wp
->yoff
+ wp
->sy
;
470 cell_type
= screen_redraw_type_of_cell(rctx
, px
, py
);
471 screen_redraw_border_set(w
, wp
, pane_lines
, cell_type
, &gc
);
472 screen_write_cell(&ctx
, &gc
);
474 gc
.attr
&= ~GRID_ATTR_CHARSET
;
476 screen_write_cursormove(&ctx
, 0, 0, 0);
477 format_draw(&ctx
, &gc
, width
, expanded
, NULL
, 0);
478 screen_write_stop(&ctx
);
483 if (grid_compare(wp
->status_screen
.grid
, old
.grid
) == 0) {
491 /* Draw pane status. */
493 screen_redraw_draw_pane_status(struct screen_redraw_ctx
*ctx
)
495 struct client
*c
= ctx
->c
;
496 struct window
*w
= c
->session
->curw
->window
;
497 struct tty
*tty
= &c
->tty
;
498 struct window_pane
*wp
;
500 u_int i
, x
, width
, xoff
, yoff
, size
;
502 log_debug("%s: %s @%u", __func__
, c
->name
, w
->id
);
504 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
505 if (!window_pane_visible(wp
))
507 s
= &wp
->status_screen
;
509 size
= wp
->status_size
;
510 if (ctx
->pane_status
== PANE_STATUS_TOP
)
513 yoff
= wp
->yoff
+ wp
->sy
;
516 if (xoff
+ size
<= ctx
->ox
||
517 xoff
>= ctx
->ox
+ ctx
->sx
||
519 yoff
>= ctx
->oy
+ ctx
->sy
)
522 if (xoff
>= ctx
->ox
&& xoff
+ size
<= ctx
->ox
+ ctx
->sx
) {
527 } else if (xoff
< ctx
->ox
&& xoff
+ size
> ctx
->ox
+ ctx
->sx
) {
528 /* Both left and right not visible. */
532 } else if (xoff
< ctx
->ox
) {
533 /* Left not visible. */
538 /* Right not visible. */
545 yoff
+= ctx
->statuslines
;
546 tty_draw_line(tty
, s
, i
, 0, width
, x
, yoff
- ctx
->oy
,
547 &grid_default_cell
, NULL
);
549 tty_cursor(tty
, 0, 0);
552 /* Update status line and change flags if unchanged. */
554 screen_redraw_update(struct screen_redraw_ctx
*ctx
, uint64_t flags
)
556 struct client
*c
= ctx
->c
;
557 struct window
*w
= c
->session
->curw
->window
;
558 struct window_pane
*wp
;
560 enum pane_lines lines
;
562 if (c
->message_string
!= NULL
)
563 redraw
= status_message_redraw(c
);
564 else if (c
->prompt_string
!= NULL
)
565 redraw
= status_prompt_redraw(c
);
567 redraw
= status_redraw(c
);
568 if (!redraw
&& (~flags
& CLIENT_REDRAWSTATUSALWAYS
))
569 flags
&= ~CLIENT_REDRAWSTATUS
;
571 if (c
->overlay_draw
!= NULL
)
572 flags
|= CLIENT_REDRAWOVERLAY
;
574 if (ctx
->pane_status
!= PANE_STATUS_OFF
) {
575 lines
= ctx
->pane_lines
;
577 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
578 if (screen_redraw_make_pane_status(c
, wp
, ctx
, lines
))
582 flags
|= CLIENT_REDRAWBORDERS
;
588 /* Set up redraw context. */
590 screen_redraw_set_context(struct client
*c
, struct screen_redraw_ctx
*ctx
)
592 struct session
*s
= c
->session
;
593 struct options
*oo
= s
->options
;
594 struct window
*w
= s
->curw
->window
;
595 struct options
*wo
= w
->options
;
598 memset(ctx
, 0, sizeof *ctx
);
601 lines
= status_line_size(c
);
602 if (c
->message_string
!= NULL
|| c
->prompt_string
!= NULL
)
603 lines
= (lines
== 0) ? 1 : lines
;
604 if (lines
!= 0 && options_get_number(oo
, "status-position") == 0)
606 ctx
->statuslines
= lines
;
608 ctx
->pane_status
= options_get_number(wo
, "pane-border-status");
609 ctx
->pane_lines
= options_get_number(wo
, "pane-border-lines");
611 ctx
->pane_scrollbars
= options_get_number(wo
, "pane-scrollbars");
612 ctx
->pane_scrollbars_pos
= options_get_number(wo
,
613 "pane-scrollbars-position");
615 tty_window_offset(&c
->tty
, &ctx
->ox
, &ctx
->oy
, &ctx
->sx
, &ctx
->sy
);
617 log_debug("%s: %s @%u ox=%u oy=%u sx=%u sy=%u %u/%d", __func__
, c
->name
,
618 w
->id
, ctx
->ox
, ctx
->oy
, ctx
->sx
, ctx
->sy
, ctx
->statuslines
,
622 /* Redraw entire screen. */
624 screen_redraw_screen(struct client
*c
)
626 struct screen_redraw_ctx ctx
;
629 if (c
->flags
& CLIENT_SUSPENDED
)
632 screen_redraw_set_context(c
, &ctx
);
634 flags
= screen_redraw_update(&ctx
, c
->flags
);
635 if ((flags
& CLIENT_ALLREDRAWFLAGS
) == 0)
638 tty_sync_start(&c
->tty
);
639 tty_update_mode(&c
->tty
, c
->tty
.mode
, NULL
);
641 if (flags
& (CLIENT_REDRAWWINDOW
|CLIENT_REDRAWBORDERS
)) {
642 log_debug("%s: redrawing borders", c
->name
);
643 screen_redraw_draw_borders(&ctx
);
644 if (ctx
.pane_status
!= PANE_STATUS_OFF
)
645 screen_redraw_draw_pane_status(&ctx
);
646 screen_redraw_draw_pane_scrollbars(&ctx
);
648 if (flags
& CLIENT_REDRAWWINDOW
) {
649 log_debug("%s: redrawing panes", c
->name
);
650 screen_redraw_draw_panes(&ctx
);
651 screen_redraw_draw_pane_scrollbars(&ctx
);
653 if (ctx
.statuslines
!= 0 &&
654 (flags
& (CLIENT_REDRAWSTATUS
|CLIENT_REDRAWSTATUSALWAYS
))) {
655 log_debug("%s: redrawing status", c
->name
);
656 screen_redraw_draw_status(&ctx
);
658 if (c
->overlay_draw
!= NULL
&& (flags
& CLIENT_REDRAWOVERLAY
)) {
659 log_debug("%s: redrawing overlay", c
->name
);
660 c
->overlay_draw(c
, c
->overlay_data
, &ctx
);
666 /* Redraw a single pane and its scrollbar. */
668 screen_redraw_pane(struct client
*c
, struct window_pane
*wp
,
669 int redraw_scrollbar_only
)
671 struct screen_redraw_ctx ctx
;
672 int pane_scrollbars
, mode
;
674 if (!window_pane_visible(wp
))
676 mode
= window_pane_mode(wp
);
678 screen_redraw_set_context(c
, &ctx
);
679 tty_sync_start(&c
->tty
);
680 tty_update_mode(&c
->tty
, c
->tty
.mode
, NULL
);
682 if (!redraw_scrollbar_only
)
683 screen_redraw_draw_pane(&ctx
, wp
);
686 * Redraw scrollbar if needed. Always redraw scrollbar in a mode because
687 * if redrawing a pane, it's because pane has scrolled.
689 pane_scrollbars
= ctx
.pane_scrollbars
;
690 if (pane_scrollbars
== PANE_SCROLLBARS_MODAL
&&
691 mode
== WINDOW_PANE_NO_MODE
)
692 pane_scrollbars
= PANE_SCROLLBARS_OFF
;
693 if (pane_scrollbars
!= PANE_SCROLLBARS_OFF
)
694 screen_redraw_draw_pane_scrollbar(&ctx
, wp
);
699 /* Get border cell style. */
700 static const struct grid_cell
*
701 screen_redraw_draw_borders_style(struct screen_redraw_ctx
*ctx
, u_int x
,
702 u_int y
, struct window_pane
*wp
)
704 struct client
*c
= ctx
->c
;
705 struct session
*s
= c
->session
;
706 struct window
*w
= s
->curw
->window
;
707 struct window_pane
*active
= server_client_get_pane(c
);
708 struct options
*oo
= w
->options
;
709 struct format_tree
*ft
;
711 if (wp
->border_gc_set
)
712 return (&wp
->border_gc
);
713 wp
->border_gc_set
= 1;
715 ft
= format_create_defaults(NULL
, c
, s
, s
->curw
, wp
);
716 if (screen_redraw_check_is(ctx
, x
, y
, active
))
717 style_apply(&wp
->border_gc
, oo
, "pane-active-border-style", ft
);
719 style_apply(&wp
->border_gc
, oo
, "pane-border-style", ft
);
722 return (&wp
->border_gc
);
725 /* Draw a border cell. */
727 screen_redraw_draw_borders_cell(struct screen_redraw_ctx
*ctx
, u_int i
, u_int j
)
729 struct client
*c
= ctx
->c
;
730 struct session
*s
= c
->session
;
731 struct window
*w
= s
->curw
->window
;
732 struct options
*oo
= w
->options
;
733 struct tty
*tty
= &c
->tty
;
734 struct format_tree
*ft
;
735 struct window_pane
*wp
, *active
= server_client_get_pane(c
);
737 const struct grid_cell
*tmp
;
738 struct overlay_ranges r
;
739 u_int cell_type
, x
= ctx
->ox
+ i
, y
= ctx
->oy
+ j
;
740 int arrows
= 0, border
, isolates
;
742 if (c
->overlay_check
!= NULL
) {
743 c
->overlay_check(c
, c
->overlay_data
, x
, y
, 1, &r
);
744 if (r
.nx
[0] + r
.nx
[1] == 0)
748 cell_type
= screen_redraw_check_cell(ctx
, x
, y
, &wp
);
749 if (cell_type
== CELL_INSIDE
|| cell_type
== CELL_SCROLLBAR
)
753 if (!ctx
->no_pane_gc_set
) {
754 ft
= format_create_defaults(NULL
, c
, s
, s
->curw
, NULL
);
755 memcpy(&ctx
->no_pane_gc
, &grid_default_cell
, sizeof gc
);
756 style_add(&ctx
->no_pane_gc
, oo
, "pane-border-style",
759 ctx
->no_pane_gc_set
= 1;
761 memcpy(&gc
, &ctx
->no_pane_gc
, sizeof gc
);
763 tmp
= screen_redraw_draw_borders_style(ctx
, x
, y
, wp
);
766 memcpy(&gc
, tmp
, sizeof gc
);
768 if (server_is_marked(s
, s
->curw
, marked_pane
.wp
) &&
769 screen_redraw_check_is(ctx
, x
, y
, marked_pane
.wp
))
770 gc
.attr
^= GRID_ATTR_REVERSE
;
772 screen_redraw_border_set(w
, wp
, ctx
->pane_lines
, cell_type
, &gc
);
774 if (cell_type
== CELL_TOPBOTTOM
&&
775 (c
->flags
& CLIENT_UTF8
) &&
776 tty_term_has(tty
->term
, TTYC_BIDI
))
782 tty_cursor(tty
, i
, ctx
->statuslines
+ j
);
784 tty_cursor(tty
, i
, j
);
786 tty_puts(tty
, END_ISOLATE
);
788 switch (options_get_number(oo
, "pane-border-indicators")) {
789 case PANE_BORDER_ARROWS
:
790 case PANE_BORDER_BOTH
:
795 if (wp
!= NULL
&& arrows
) {
796 border
= screen_redraw_pane_border(ctx
, active
, x
, y
);
797 if (((i
== wp
->xoff
+ 1 &&
798 (cell_type
== CELL_LEFTRIGHT
||
799 (cell_type
== CELL_TOPJOIN
&&
800 border
== SCREEN_REDRAW_BORDER_BOTTOM
) ||
801 (cell_type
== CELL_BOTTOMJOIN
&&
802 border
== SCREEN_REDRAW_BORDER_TOP
))) ||
803 (j
== wp
->yoff
+ 1 &&
804 (cell_type
== CELL_TOPBOTTOM
||
805 (cell_type
== CELL_LEFTJOIN
&&
806 border
== SCREEN_REDRAW_BORDER_RIGHT
) ||
807 (cell_type
== CELL_RIGHTJOIN
&&
808 border
== SCREEN_REDRAW_BORDER_LEFT
)))) &&
809 screen_redraw_check_is(ctx
, x
, y
, active
)) {
810 gc
.attr
|= GRID_ATTR_CHARSET
;
811 utf8_set(&gc
.data
, BORDER_MARKERS
[border
]);
815 tty_cell(tty
, &gc
, &grid_default_cell
, NULL
, NULL
);
817 tty_puts(tty
, START_ISOLATE
);
820 /* Draw the borders. */
822 screen_redraw_draw_borders(struct screen_redraw_ctx
*ctx
)
824 struct client
*c
= ctx
->c
;
825 struct session
*s
= c
->session
;
826 struct window
*w
= s
->curw
->window
;
827 struct window_pane
*wp
;
830 log_debug("%s: %s @%u", __func__
, c
->name
, w
->id
);
832 TAILQ_FOREACH(wp
, &w
->panes
, entry
)
833 wp
->border_gc_set
= 0;
835 for (j
= 0; j
< c
->tty
.sy
- ctx
->statuslines
; j
++) {
836 for (i
= 0; i
< c
->tty
.sx
; i
++)
837 screen_redraw_draw_borders_cell(ctx
, i
, j
);
841 /* Draw the panes. */
843 screen_redraw_draw_panes(struct screen_redraw_ctx
*ctx
)
845 struct client
*c
= ctx
->c
;
846 struct window
*w
= c
->session
->curw
->window
;
847 struct window_pane
*wp
;
849 log_debug("%s: %s @%u", __func__
, c
->name
, w
->id
);
851 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
852 if (window_pane_visible(wp
))
853 screen_redraw_draw_pane(ctx
, wp
);
857 /* Draw the status line. */
859 screen_redraw_draw_status(struct screen_redraw_ctx
*ctx
)
861 struct client
*c
= ctx
->c
;
862 struct window
*w
= c
->session
->curw
->window
;
863 struct tty
*tty
= &c
->tty
;
864 struct screen
*s
= c
->status
.active
;
867 log_debug("%s: %s @%u", __func__
, c
->name
, w
->id
);
872 y
= c
->tty
.sy
- ctx
->statuslines
;
873 for (i
= 0; i
< ctx
->statuslines
; i
++) {
874 tty_draw_line(tty
, s
, 0, i
, UINT_MAX
, 0, y
+ i
,
875 &grid_default_cell
, NULL
);
881 screen_redraw_draw_pane(struct screen_redraw_ctx
*ctx
, struct window_pane
*wp
)
883 struct client
*c
= ctx
->c
;
884 struct window
*w
= c
->session
->curw
->window
;
885 struct tty
*tty
= &c
->tty
;
886 struct screen
*s
= wp
->screen
;
887 struct colour_palette
*palette
= &wp
->palette
;
888 struct grid_cell defaults
;
889 u_int i
, j
, top
, x
, y
, width
;
891 log_debug("%s: %s @%u %%%u", __func__
, c
->name
, w
->id
, wp
->id
);
893 if (wp
->xoff
+ wp
->sx
<= ctx
->ox
|| wp
->xoff
>= ctx
->ox
+ ctx
->sx
)
896 top
= ctx
->statuslines
;
899 for (j
= 0; j
< wp
->sy
; j
++) {
900 if (wp
->yoff
+ j
< ctx
->oy
|| wp
->yoff
+ j
>= ctx
->oy
+ ctx
->sy
)
902 y
= top
+ wp
->yoff
+ j
- ctx
->oy
;
904 if (wp
->xoff
>= ctx
->ox
&&
905 wp
->xoff
+ wp
->sx
<= ctx
->ox
+ ctx
->sx
) {
908 x
= wp
->xoff
- ctx
->ox
;
910 } else if (wp
->xoff
< ctx
->ox
&&
911 wp
->xoff
+ wp
->sx
> ctx
->ox
+ ctx
->sx
) {
912 /* Both left and right not visible. */
916 } else if (wp
->xoff
< ctx
->ox
) {
917 /* Left not visible. */
918 i
= ctx
->ox
- wp
->xoff
;
922 /* Right not visible. */
924 x
= wp
->xoff
- ctx
->ox
;
927 log_debug("%s: %s %%%u line %u,%u at %u,%u, width %u",
928 __func__
, c
->name
, wp
->id
, i
, j
, x
, y
, width
);
930 tty_default_colours(&defaults
, wp
);
931 tty_draw_line(tty
, s
, i
, j
, width
, x
, y
, &defaults
, palette
);
935 tty_draw_images(c
, wp
, s
);
939 /* Draw the panes scrollbars */
941 screen_redraw_draw_pane_scrollbars(struct screen_redraw_ctx
*ctx
)
943 struct client
*c
= ctx
->c
;
944 struct window
*w
= c
->session
->curw
->window
;
945 struct window_pane
*wp
;
947 log_debug("%s: %s @%u", __func__
, c
->name
, w
->id
);
949 TAILQ_FOREACH(wp
, &w
->panes
, entry
) {
950 switch (ctx
->pane_scrollbars
) {
951 case PANE_SCROLLBARS_OFF
:
953 case PANE_SCROLLBARS_MODAL
:
954 if (window_pane_mode(wp
) == WINDOW_PANE_NO_MODE
)
957 case PANE_SCROLLBARS_ALWAYS
:
960 if (window_pane_visible(wp
))
961 screen_redraw_draw_pane_scrollbar(ctx
, wp
);
965 /* Draw pane scrollbar. */
967 screen_redraw_draw_pane_scrollbar(struct screen_redraw_ctx
*ctx
,
968 struct window_pane
*wp
)
970 struct screen
*s
= wp
->screen
;
972 u_int sb
= ctx
->pane_scrollbars
, total_height
, sb_h
= wp
->sy
;
973 u_int sb_pos
= ctx
->pane_scrollbars_pos
, slider_h
, slider_y
;
974 u_int sb_w
= PANE_SCROLLBARS_WIDTH
, cm_y
, cm_size
;
975 int sb_x
, sb_y
= (int)(wp
->yoff
- ctx
->oy
); /* sb top */
977 if (window_pane_mode(wp
) == WINDOW_PANE_NO_MODE
) {
978 if (sb
== PANE_SCROLLBARS_MODAL
)
980 /* Show slider at the bottom of the scrollbar. */
981 total_height
= screen_size_y(s
) + screen_hsize(s
);
982 percent_view
= (double)sb_h
/ total_height
;
983 slider_h
= (double)sb_h
* percent_view
;
984 slider_y
= sb_h
- slider_h
;
986 if (TAILQ_FIRST(&wp
->modes
) == NULL
)
988 if (window_copy_get_current_offset(wp
, &cm_y
, &cm_size
) == 0)
990 total_height
= cm_size
+ sb_h
;
991 percent_view
= (double)sb_h
/ (cm_size
+ sb_h
);
992 slider_h
= (double)sb_h
* percent_view
;
993 slider_y
= (sb_h
+ 1) * ((double)cm_y
/ total_height
);
996 if (sb_pos
== PANE_SCROLLBARS_LEFT
)
997 sb_x
= (int)wp
->xoff
- sb_w
- ctx
->ox
;
999 sb_x
= (int)wp
->xoff
+ wp
->sx
- ctx
->ox
;
1003 if (slider_y
>= sb_h
)
1004 slider_y
= sb_h
- 1;
1006 screen_redraw_draw_scrollbar(ctx
, wp
, sb_pos
, sb_x
, sb_y
, sb_h
,
1007 slider_h
, slider_y
);
1011 screen_redraw_draw_scrollbar(struct screen_redraw_ctx
*ctx
,
1012 struct window_pane
*wp
, int sb_pos
, int sb_x
, int sb_y
, u_int sb_h
,
1013 u_int slider_h
, u_int slider_y
)
1015 struct client
*c
= ctx
->c
;
1016 struct window
*w
= wp
->window
;
1017 struct tty
*tty
= &c
->tty
;
1018 struct grid_cell gc
, slgc
, *gcp
;
1019 u_int i
, j
, sb_w
= PANE_SCROLLBARS_WIDTH
;
1021 int px
, py
, ox
= ctx
->ox
, oy
= ctx
->oy
;
1022 int sb_pad
= PANE_SCROLLBARS_PADDING
, sx
= ctx
->sx
;
1023 int sy
= ctx
->sy
, xoff
= wp
->xoff
, yoff
= wp
->yoff
;
1025 /* Set up default style. */
1026 style_apply(&gc
, w
->options
, "pane-scrollbars-style", NULL
);
1027 utf8_set(&gc
.data
, ' ');
1029 /* Set up style for slider. */
1030 memcpy(&slgc
, &gc
, sizeof slgc
);
1034 if (sb_pos
== PANE_SCROLLBARS_RIGHT
)
1040 for (i
= 0; i
< sb_w
; i
++) {
1041 for (j
= 0; j
< sb_h
; j
++) {
1044 if (px
< xoff
- ox
- 1 || px
>= sx
|| px
< 0 ||
1045 py
< yoff
- oy
- 1 || py
>= sy
|| py
< 0)
1047 tty_cursor(tty
, px
, py
);
1048 if (sb_pad
&& i
== pad_col
) {
1049 tty_cell(tty
, &grid_default_cell
,
1050 &grid_default_cell
, NULL
, NULL
);
1052 if (j
>= slider_y
&& j
< slider_y
+ slider_h
)
1056 tty_cell(tty
, gcp
, &grid_default_cell
, NULL
,