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