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