1 /* $OpenBSD: screen-redraw.c,v 1.32 2015/06/04 11:43:51 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 struct window_pane **); 29 int screen_redraw_check_is(u_int, u_int, int, struct window *, 30 struct window_pane *, struct window_pane *); 31 32 void screen_redraw_draw_borders(struct client *, int, u_int); 33 void screen_redraw_draw_panes(struct client *, u_int); 34 void screen_redraw_draw_status(struct client *, u_int); 35 void screen_redraw_draw_number(struct client *, struct window_pane *, u_int); 36 37 #define CELL_INSIDE 0 38 #define CELL_LEFTRIGHT 1 39 #define CELL_TOPBOTTOM 2 40 #define CELL_TOPLEFT 3 41 #define CELL_TOPRIGHT 4 42 #define CELL_BOTTOMLEFT 5 43 #define CELL_BOTTOMRIGHT 6 44 #define CELL_TOPJOIN 7 45 #define CELL_BOTTOMJOIN 8 46 #define CELL_LEFTJOIN 9 47 #define CELL_RIGHTJOIN 10 48 #define CELL_JOIN 11 49 #define CELL_OUTSIDE 12 50 51 #define CELL_BORDERS " xqlkmjwvtun~" 52 53 /* Check if cell is on the border of a particular pane. */ 54 int 55 screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py) 56 { 57 /* Inside pane. */ 58 if (px >= wp->xoff && px < wp->xoff + wp->sx && 59 py >= wp->yoff && py < wp->yoff + wp->sy) 60 return (0); 61 62 /* Left/right borders. */ 63 if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= wp->yoff + wp->sy) { 64 if (wp->xoff != 0 && px == wp->xoff - 1) 65 return (1); 66 if (px == wp->xoff + wp->sx) 67 return (1); 68 } 69 70 /* Top/bottom borders. */ 71 if ((wp->xoff == 0 || px >= wp->xoff - 1) && 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 /* Outside pane. */ 79 return (-1); 80 } 81 82 /* Check if a cell is on the pane border. */ 83 int 84 screen_redraw_cell_border(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 retval; 89 90 /* Check all the panes. */ 91 TAILQ_FOREACH(wp, &w->panes, entry) { 92 if (!window_pane_visible(wp)) 93 continue; 94 if ((retval = screen_redraw_cell_border1(wp, px, py)) != -1) 95 return (retval); 96 } 97 98 return (0); 99 } 100 101 /* Check if cell inside a pane. */ 102 int 103 screen_redraw_check_cell(struct client *c, u_int px, u_int py, 104 struct window_pane **wpp) 105 { 106 struct window *w = c->session->curw->window; 107 struct window_pane *wp; 108 int borders; 109 110 if (px > w->sx || py > w->sy) 111 return (CELL_OUTSIDE); 112 113 TAILQ_FOREACH(wp, &w->panes, entry) { 114 if (!window_pane_visible(wp)) 115 continue; 116 *wpp = wp; 117 118 /* If outside the pane and its border, skip it. */ 119 if ((wp->xoff != 0 && px < wp->xoff - 1) || 120 px > wp->xoff + wp->sx || 121 (wp->yoff != 0 && py < wp->yoff - 1) || 122 py > wp->yoff + wp->sy) 123 continue; 124 125 /* If definitely inside, return so. */ 126 if (!screen_redraw_cell_border(c, px, py)) 127 return (CELL_INSIDE); 128 129 /* 130 * Construct a bitmask of whether the cells to the left (bit 131 * 4), right, top, and bottom (bit 1) of this cell are borders. 132 */ 133 borders = 0; 134 if (px == 0 || screen_redraw_cell_border(c, px - 1, py)) 135 borders |= 8; 136 if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py)) 137 borders |= 4; 138 if (py == 0 || screen_redraw_cell_border(c, px, py - 1)) 139 borders |= 2; 140 if (py <= w->sy && screen_redraw_cell_border(c, px, py + 1)) 141 borders |= 1; 142 143 /* 144 * Figure out what kind of border this cell is. Only one bit 145 * set doesn't make sense (can't have a border cell with no 146 * others connected). 147 */ 148 switch (borders) { 149 case 15: /* 1111, left right top bottom */ 150 return (CELL_JOIN); 151 case 14: /* 1110, left right top */ 152 return (CELL_BOTTOMJOIN); 153 case 13: /* 1101, left right bottom */ 154 return (CELL_TOPJOIN); 155 case 12: /* 1100, left right */ 156 return (CELL_TOPBOTTOM); 157 case 11: /* 1011, left top bottom */ 158 return (CELL_RIGHTJOIN); 159 case 10: /* 1010, left top */ 160 return (CELL_BOTTOMRIGHT); 161 case 9: /* 1001, left bottom */ 162 return (CELL_TOPRIGHT); 163 case 7: /* 0111, right top bottom */ 164 return (CELL_LEFTJOIN); 165 case 6: /* 0110, right top */ 166 return (CELL_BOTTOMLEFT); 167 case 5: /* 0101, right bottom */ 168 return (CELL_TOPLEFT); 169 case 3: /* 0011, top bottom */ 170 return (CELL_LEFTRIGHT); 171 } 172 } 173 174 *wpp = NULL; 175 return (CELL_OUTSIDE); 176 } 177 178 /* Check if the border of a particular pane. */ 179 int 180 screen_redraw_check_is(u_int px, u_int py, int type, struct window *w, 181 struct window_pane *wantwp, struct window_pane *wp) 182 { 183 /* Is this off the active pane border? */ 184 if (screen_redraw_cell_border1(wantwp, px, py) != 1) 185 return (0); 186 187 /* If there are more than two panes, that's enough. */ 188 if (window_count_panes(w) != 2) 189 return (1); 190 191 /* Else if the cell is not a border cell, forget it. */ 192 if (wp == NULL || (type == CELL_OUTSIDE || type == CELL_INSIDE)) 193 return (1); 194 195 /* Check if the pane covers the whole width. */ 196 if (wp->xoff == 0 && wp->sx == w->sx) { 197 /* This can either be the top pane or the bottom pane. */ 198 if (wp->yoff == 0) { /* top pane */ 199 if (wp == wantwp) 200 return (px <= wp->sx / 2); 201 return (px > wp->sx / 2); 202 } 203 return (0); 204 } 205 206 /* Check if the pane covers the whole height. */ 207 if (wp->yoff == 0 && wp->sy == w->sy) { 208 /* This can either be the left pane or the right pane. */ 209 if (wp->xoff == 0) { /* left pane */ 210 if (wp == wantwp) 211 return (py <= wp->sy / 2); 212 return (py > wp->sy / 2); 213 } 214 return (0); 215 } 216 217 return (type); 218 } 219 220 /* Redraw entire screen. */ 221 void 222 screen_redraw_screen(struct client *c, int draw_panes, int draw_status, 223 int draw_borders) 224 { 225 struct options *oo = &c->session->options; 226 struct tty *tty = &c->tty; 227 u_int top; 228 int status, spos; 229 230 /* Suspended clients should not be updated. */ 231 if (c->flags & CLIENT_SUSPENDED) 232 return; 233 234 /* Get status line, er, status. */ 235 spos = options_get_number(oo, "status-position"); 236 if (c->message_string != NULL || c->prompt_string != NULL) 237 status = 1; 238 else 239 status = options_get_number(oo, "status"); 240 top = 0; 241 if (status && spos == 0) 242 top = 1; 243 if (!status) 244 draw_status = 0; 245 246 if (draw_borders) 247 screen_redraw_draw_borders(c, status, top); 248 if (draw_panes) 249 screen_redraw_draw_panes(c, top); 250 if (draw_status) 251 screen_redraw_draw_status(c, top); 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, yoff; 260 261 if (!window_pane_visible(wp)) 262 return; 263 264 yoff = wp->yoff; 265 if (status_at_line(c) == 0) 266 yoff++; 267 268 for (i = 0; i < wp->sy; i++) 269 tty_draw_pane(&c->tty, wp, i, wp->xoff, yoff); 270 tty_reset(&c->tty); 271 } 272 273 /* Draw the borders. */ 274 void 275 screen_redraw_draw_borders(struct client *c, int status, u_int top) 276 { 277 struct session *s = c->session; 278 struct window *w = s->curw->window; 279 struct options *oo = &w->options; 280 struct tty *tty = &c->tty; 281 struct window_pane *wp; 282 struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; 283 struct grid_cell msg_gc; 284 u_int i, j, type, msgx = 0, msgy = 0; 285 int active, small, flags; 286 char msg[256]; 287 const char *tmp; 288 size_t msglen = 0; 289 290 small = (tty->sy - status + top > w->sy) || (tty->sx > w->sx); 291 if (small) { 292 flags = w->flags & (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT); 293 if (flags == (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT)) 294 tmp = "force-width, force-height"; 295 else if (flags == WINDOW_FORCEWIDTH) 296 tmp = "force-width"; 297 else if (flags == WINDOW_FORCEHEIGHT) 298 tmp = "force-height"; 299 else 300 tmp = "a smaller client"; 301 xsnprintf(msg, sizeof msg, "(size %ux%u from %s)", 302 w->sx, w->sy, tmp); 303 msglen = strlen(msg); 304 305 if (tty->sy - 1 - status + top > w->sy && tty->sx >= msglen) { 306 msgx = tty->sx - msglen; 307 msgy = tty->sy - 1 - status + top; 308 } else if (tty->sx - w->sx > msglen) { 309 msgx = tty->sx - msglen; 310 msgy = tty->sy - 1 - status + top; 311 } else 312 small = 0; 313 } 314 315 style_apply(&other_gc, oo, "pane-border-style"); 316 style_apply(&active_gc, oo, "pane-active-border-style"); 317 active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; 318 319 memcpy(&m_other_gc, &other_gc, sizeof m_other_gc); 320 m_other_gc.attr ^= GRID_ATTR_REVERSE; 321 memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); 322 m_active_gc.attr ^= GRID_ATTR_REVERSE; 323 324 for (j = 0; j < tty->sy - status; j++) { 325 for (i = 0; i < tty->sx; i++) { 326 type = screen_redraw_check_cell(c, i, j, &wp); 327 if (type == CELL_INSIDE) 328 continue; 329 if (type == CELL_OUTSIDE && small && 330 i > msgx && j == msgy) 331 continue; 332 active = screen_redraw_check_is(i, j, type, w, 333 w->active, wp); 334 if (server_is_marked(s, s->curw, marked_window_pane) && 335 screen_redraw_check_is(i, j, type, w, 336 marked_window_pane, wp)) { 337 if (active) 338 tty_attributes(tty, &m_active_gc, NULL); 339 else 340 tty_attributes(tty, &m_other_gc, NULL); 341 } else if (active) 342 tty_attributes(tty, &active_gc, NULL); 343 else 344 tty_attributes(tty, &other_gc, NULL); 345 tty_cursor(tty, i, top + j); 346 tty_putc(tty, CELL_BORDERS[type]); 347 } 348 } 349 350 if (small) { 351 memcpy(&msg_gc, &grid_default_cell, sizeof msg_gc); 352 tty_attributes(tty, &msg_gc, NULL); 353 tty_cursor(tty, msgx, msgy); 354 tty_puts(tty, msg); 355 } 356 } 357 358 /* Draw the panes. */ 359 void 360 screen_redraw_draw_panes(struct client *c, u_int top) 361 { 362 struct window *w = c->session->curw->window; 363 struct tty *tty = &c->tty; 364 struct window_pane *wp; 365 u_int i; 366 367 TAILQ_FOREACH(wp, &w->panes, entry) { 368 if (!window_pane_visible(wp)) 369 continue; 370 for (i = 0; i < wp->sy; i++) 371 tty_draw_pane(tty, wp, i, wp->xoff, top + wp->yoff); 372 if (c->flags & CLIENT_IDENTIFY) 373 screen_redraw_draw_number(c, wp, top); 374 } 375 } 376 377 /* Draw the status line. */ 378 void 379 screen_redraw_draw_status(struct client *c, u_int top) 380 { 381 struct tty *tty = &c->tty; 382 383 if (top) 384 tty_draw_line(tty, NULL, &c->status, 0, 0, 0); 385 else 386 tty_draw_line(tty, NULL, &c->status, 0, 0, tty->sy - 1); 387 } 388 389 /* Draw number on a pane. */ 390 void 391 screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top) 392 { 393 struct tty *tty = &c->tty; 394 struct session *s = c->session; 395 struct options *oo = &s->options; 396 struct window *w = wp->window; 397 struct grid_cell gc; 398 u_int idx, px, py, i, j, xoff, yoff; 399 int colour, active_colour; 400 char buf[16], *ptr; 401 size_t len; 402 403 if (window_pane_index(wp, &idx) != 0) 404 fatalx("index not found"); 405 len = xsnprintf(buf, sizeof buf, "%u", idx); 406 407 if (wp->sx < len) 408 return; 409 colour = options_get_number(oo, "display-panes-colour"); 410 active_colour = options_get_number(oo, "display-panes-active-colour"); 411 412 px = wp->sx / 2; py = wp->sy / 2; 413 xoff = wp->xoff; yoff = wp->yoff; 414 415 if (top) 416 yoff++; 417 418 if (wp->sx < len * 6 || wp->sy < 5) { 419 tty_cursor(tty, xoff + px - len / 2, yoff + py); 420 goto draw_text; 421 } 422 423 px -= len * 3; 424 py -= 2; 425 426 memcpy(&gc, &grid_default_cell, sizeof gc); 427 if (w->active == wp) 428 colour_set_bg(&gc, active_colour); 429 else 430 colour_set_bg(&gc, colour); 431 tty_attributes(tty, &gc, wp); 432 for (ptr = buf; *ptr != '\0'; ptr++) { 433 if (*ptr < '0' || *ptr > '9') 434 continue; 435 idx = *ptr - '0'; 436 437 for (j = 0; j < 5; j++) { 438 for (i = px; i < px + 5; i++) { 439 tty_cursor(tty, xoff + i, yoff + py + j); 440 if (window_clock_table[idx][j][i - px]) 441 tty_putc(tty, ' '); 442 } 443 } 444 px += 6; 445 } 446 447 len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy); 448 if (wp->sx < len || wp->sy < 6) 449 return; 450 tty_cursor(tty, xoff + wp->sx - len, yoff); 451 452 draw_text: 453 memcpy(&gc, &grid_default_cell, sizeof gc); 454 if (w->active == wp) 455 colour_set_fg(&gc, active_colour); 456 else 457 colour_set_fg(&gc, colour); 458 tty_attributes(tty, &gc, wp); 459 tty_puts(tty, buf); 460 461 tty_cursor(tty, 0, 0); 462 } 463