1 /* $OpenBSD: screen-redraw.c,v 1.12 2009/10/12 09:29:58 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net> 5 * 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. 9 * 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. 17 */ 18 19 #include <sys/types.h> 20 21 #include <string.h> 22 23 #include "tmux.h" 24 25 int screen_redraw_cell_border(struct client *, u_int, u_int); 26 int screen_redraw_check_cell(struct client *, u_int, u_int); 27 void screen_redraw_draw_number(struct client *, struct window_pane *); 28 29 #define CELL_INSIDE 0 30 #define CELL_LEFTRIGHT 1 31 #define CELL_TOPBOTTOM 2 32 #define CELL_TOPLEFT 3 33 #define CELL_TOPRIGHT 4 34 #define CELL_BOTTOMLEFT 5 35 #define CELL_BOTTOMRIGHT 6 36 #define CELL_TOPJOIN 7 37 #define CELL_BOTTOMJOIN 8 38 #define CELL_LEFTJOIN 9 39 #define CELL_RIGHTJOIN 10 40 #define CELL_JOIN 11 41 #define CELL_OUTSIDE 12 42 43 /* Check if a cell is on the pane border. */ 44 int 45 screen_redraw_cell_border(struct client *c, u_int px, u_int py) 46 { 47 struct window *w = c->session->curw->window; 48 struct window_pane *wp; 49 50 /* Check all the panes. */ 51 TAILQ_FOREACH(wp, &w->panes, entry) { 52 if (!window_pane_visible(wp)) 53 continue; 54 55 /* Inside pane. */ 56 if (px >= wp->xoff && px < wp->xoff + wp->sx && 57 py >= wp->yoff && py < wp->yoff + wp->sy) 58 return (0); 59 60 /* Left/right borders. */ 61 if ((wp->yoff == 0 || py >= wp->yoff - 1) && 62 py <= wp->yoff + wp->sy) { 63 if (wp->xoff != 0 && px == wp->xoff - 1) 64 return (1); 65 if (px == wp->xoff + wp->sx) 66 return (1); 67 } 68 69 /* Top/bottom borders. */ 70 if ((wp->xoff == 0 || px >= wp->xoff - 1) && 71 px <= wp->xoff + wp->sx) { 72 if (wp->yoff != 0 && py == wp->yoff - 1) 73 return (1); 74 if (py == wp->yoff + wp->sy) 75 return (1); 76 } 77 } 78 79 return (0); 80 } 81 82 /* Check if cell inside a pane. */ 83 int 84 screen_redraw_check_cell(struct client *c, u_int px, u_int py) 85 { 86 struct window *w = c->session->curw->window; 87 struct window_pane *wp; 88 int borders; 89 90 if (px > w->sx || py > w->sy) 91 return (CELL_OUTSIDE); 92 93 TAILQ_FOREACH(wp, &w->panes, entry) { 94 if (!window_pane_visible(wp)) 95 continue; 96 97 /* If outside the pane and its border, skip it. */ 98 if ((wp->xoff != 0 && px < wp->xoff - 1) || 99 px > wp->xoff + wp->sx || 100 (wp->yoff != 0 && py < wp->yoff - 1) || 101 py > wp->yoff + wp->sy) 102 continue; 103 104 /* If definitely inside, return so. */ 105 if (!screen_redraw_cell_border(c, px, py)) 106 return (CELL_INSIDE); 107 108 /* 109 * Construct a bitmask of whether the cells to the left (bit 110 * 4), right, top, and bottom (bit 1) of this cell are borders. 111 */ 112 borders = 0; 113 if (px == 0 || screen_redraw_cell_border(c, px - 1, py)) 114 borders |= 8; 115 if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py)) 116 borders |= 4; 117 if (py == 0 || screen_redraw_cell_border(c, px, py - 1)) 118 borders |= 2; 119 if (py <= w->sy && screen_redraw_cell_border(c, px, py + 1)) 120 borders |= 1; 121 122 /* 123 * Figure out what kind of border this cell is. Only one bit 124 * set doesn't make sense (can't have a border cell with no 125 * others connected). 126 */ 127 switch (borders) { 128 case 15: /* 1111, left right top bottom */ 129 return (CELL_JOIN); 130 case 14: /* 1110, left right top */ 131 return (CELL_BOTTOMJOIN); 132 case 13: /* 1101, left right bottom */ 133 return (CELL_TOPJOIN); 134 case 12: /* 1100, left right */ 135 return (CELL_TOPBOTTOM); 136 case 11: /* 1011, left top bottom */ 137 return (CELL_RIGHTJOIN); 138 case 10: /* 1010, left top */ 139 return (CELL_BOTTOMRIGHT); 140 case 9: /* 1001, left bottom */ 141 return (CELL_TOPRIGHT); 142 case 7: /* 0111, right top bottom */ 143 return (CELL_LEFTJOIN); 144 case 6: /* 0110, right top */ 145 return (CELL_BOTTOMLEFT); 146 case 5: /* 0101, right bottom */ 147 return (CELL_TOPLEFT); 148 case 3: /* 0011, top bottom */ 149 return (CELL_LEFTRIGHT); 150 } 151 } 152 153 return (CELL_OUTSIDE); 154 } 155 156 /* Redraw entire screen. */ 157 void 158 screen_redraw_screen(struct client *c, int status_only) 159 { 160 struct window *w = c->session->curw->window; 161 struct tty *tty = &c->tty; 162 struct window_pane *wp; 163 u_int i, j, type; 164 int status; 165 const u_char *base, *ptr; 166 u_char ch, border[20]; 167 168 /* Get status line, er, status. */ 169 if (c->message_string != NULL || c->prompt_string != NULL) 170 status = 1; 171 else 172 status = options_get_number(&c->session->options, "status"); 173 174 /* If only drawing status and it is present, don't need the rest. */ 175 if (status_only && status) { 176 tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); 177 tty_reset(tty); 178 return; 179 } 180 181 /* Draw background and borders. */ 182 tty_reset(tty); 183 strlcpy(border, " |-....--||+.", sizeof border); 184 if (tty_term_has(tty->term, TTYC_ACSC)) { 185 base = " xqlkmjwvtun~"; 186 for (ptr = base; *ptr != '\0'; ptr++) { 187 if ((ch = tty_get_acs(tty, *ptr)) != '\0') 188 border[ptr - base] = ch; 189 } 190 tty_putcode(tty, TTYC_SMACS); 191 } 192 for (j = 0; j < tty->sy - status; j++) { 193 if (status_only && j != tty->sy - 1) 194 continue; 195 for (i = 0; i < tty->sx; i++) { 196 type = screen_redraw_check_cell(c, i, j); 197 if (type != CELL_INSIDE) { 198 tty_cursor(tty, i, j); 199 tty_putc(tty, border[type]); 200 } 201 } 202 } 203 tty_putcode(tty, TTYC_RMACS); 204 205 /* Draw the panes. */ 206 TAILQ_FOREACH(wp, &w->panes, entry) { 207 if (!window_pane_visible(wp)) 208 continue; 209 for (i = 0; i < wp->sy; i++) { 210 if (status_only && wp->yoff + i != tty->sy - 1) 211 continue; 212 tty_draw_line(tty, wp->screen, i, wp->xoff, wp->yoff); 213 } 214 if (c->flags & CLIENT_IDENTIFY) 215 screen_redraw_draw_number(c, wp); 216 } 217 218 /* Draw the status line. */ 219 if (status) 220 tty_draw_line(tty, &c->status, 0, 0, tty->sy - 1); 221 tty_reset(tty); 222 } 223 224 /* Draw a single pane. */ 225 void 226 screen_redraw_pane(struct client *c, struct window_pane *wp) 227 { 228 u_int i; 229 230 for (i = 0; i < wp->sy; i++) 231 tty_draw_line(&c->tty, wp->screen, i, wp->xoff, wp->yoff); 232 tty_reset(&c->tty); 233 } 234 235 /* Draw number on a pane. */ 236 void 237 screen_redraw_draw_number(struct client *c, struct window_pane *wp) 238 { 239 struct tty *tty = &c->tty; 240 struct session *s = c->session; 241 struct grid_cell gc; 242 u_int idx, px, py, i, j, xoff, yoff; 243 int colour; 244 char buf[16], *ptr; 245 size_t len; 246 247 idx = window_pane_index(wp->window, wp); 248 len = xsnprintf(buf, sizeof buf, "%u", idx); 249 250 if (wp->sx < len) 251 return; 252 colour = options_get_number(&s->options, "display-panes-colour"); 253 254 px = wp->sx / 2; py = wp->sy / 2; 255 xoff = wp->xoff; yoff = wp->yoff; 256 257 if (wp->sx < len * 6 || wp->sy < 5) { 258 tty_cursor(tty, xoff + px - len / 2, yoff + py); 259 memcpy(&gc, &grid_default_cell, sizeof gc); 260 colour_set_fg(&gc, colour); 261 tty_attributes(tty, &gc); 262 tty_puts(tty, buf); 263 return; 264 } 265 266 px -= len * 3; 267 py -= 2; 268 269 memcpy(&gc, &grid_default_cell, sizeof gc); 270 colour_set_bg(&gc, colour); 271 tty_attributes(tty, &gc); 272 for (ptr = buf; *ptr != '\0'; ptr++) { 273 if (*ptr < '0' || *ptr > '9') 274 continue; 275 idx = *ptr - '0'; 276 277 for (j = 0; j < 5; j++) { 278 for (i = px; i < px + 5; i++) { 279 tty_cursor(tty, xoff + i, yoff + py + j); 280 if (clock_table[idx][j][i - px]) 281 tty_putc(tty, ' '); 282 } 283 } 284 px += 6; 285 } 286 } 287