screen-redraw.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  14. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  15. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <sys/types.h>
  18. #include <string.h>
  19. #include "tmux.h"
  20. int screen_redraw_cell_border1(struct window_pane *, u_int, u_int);
  21. int screen_redraw_cell_border(struct client *, u_int, u_int);
  22. int screen_redraw_check_cell(struct client *, u_int, u_int,
  23. struct window_pane **);
  24. int screen_redraw_check_is(u_int, u_int, int, struct window *,
  25. struct window_pane *, struct window_pane *);
  26. void screen_redraw_draw_borders(struct client *, int, u_int);
  27. void screen_redraw_draw_panes(struct client *, u_int);
  28. void screen_redraw_draw_status(struct client *, u_int);
  29. void screen_redraw_draw_number(struct client *, struct window_pane *, u_int);
  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. #define CELL_BORDERS " xqlkmjwvtun~"
  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. /* Left/right borders. */
  53. if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= wp->yoff + wp->sy) {
  54. if (wp->xoff != 0 && px == wp->xoff - 1)
  55. return (1);
  56. if (px == wp->xoff + wp->sx)
  57. return (1);
  58. }
  59. /* Top/bottom borders. */
  60. if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= wp->xoff + wp->sx) {
  61. if (wp->yoff != 0 && py == wp->yoff - 1)
  62. return (1);
  63. if (py == wp->yoff + wp->sy)
  64. return (1);
  65. }
  66. /* Outside pane. */
  67. return (-1);
  68. }
  69. /* Check if a cell is on the pane border. */
  70. int
  71. screen_redraw_cell_border(struct client *c, u_int px, u_int py)
  72. {
  73. struct window *w = c->session->curw->window;
  74. struct window_pane *wp;
  75. int retval;
  76. /* Check all the panes. */
  77. TAILQ_FOREACH(wp, &w->panes, entry) {
  78. if (!window_pane_visible(wp))
  79. continue;
  80. if ((retval = screen_redraw_cell_border1(wp, px, py)) != -1)
  81. return (retval);
  82. }
  83. return (0);
  84. }
  85. /* Check if cell inside a pane. */
  86. int
  87. screen_redraw_check_cell(struct client *c, u_int px, u_int py,
  88. struct window_pane **wpp)
  89. {
  90. struct window *w = c->session->curw->window;
  91. struct window_pane *wp;
  92. int borders;
  93. if (px > w->sx || py > w->sy)
  94. return (CELL_OUTSIDE);
  95. TAILQ_FOREACH(wp, &w->panes, entry) {
  96. if (!window_pane_visible(wp))
  97. continue;
  98. *wpp = wp;
  99. /* If outside the pane and its border, skip it. */
  100. if ((wp->xoff != 0 && px < wp->xoff - 1) ||
  101. px > wp->xoff + wp->sx ||
  102. (wp->yoff != 0 && py < wp->yoff - 1) ||
  103. py > wp->yoff + wp->sy)
  104. continue;
  105. /* If definitely inside, return so. */
  106. if (!screen_redraw_cell_border(c, px, py))
  107. return (CELL_INSIDE);
  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. * Figure out what kind of border this cell is. Only one bit
  123. * set doesn't make sense (can't have a border cell with no
  124. * others connected).
  125. */
  126. switch (borders) {
  127. case 15: /* 1111, left right top bottom */
  128. return (CELL_JOIN);
  129. case 14: /* 1110, left right top */
  130. return (CELL_BOTTOMJOIN);
  131. case 13: /* 1101, left right bottom */
  132. return (CELL_TOPJOIN);
  133. case 12: /* 1100, left right */
  134. return (CELL_TOPBOTTOM);
  135. case 11: /* 1011, left top bottom */
  136. return (CELL_RIGHTJOIN);
  137. case 10: /* 1010, left top */
  138. return (CELL_BOTTOMRIGHT);
  139. case 9: /* 1001, left bottom */
  140. return (CELL_TOPRIGHT);
  141. case 7: /* 0111, right top bottom */
  142. return (CELL_LEFTJOIN);
  143. case 6: /* 0110, right top */
  144. return (CELL_BOTTOMLEFT);
  145. case 5: /* 0101, right bottom */
  146. return (CELL_TOPLEFT);
  147. case 3: /* 0011, top bottom */
  148. return (CELL_LEFTRIGHT);
  149. }
  150. }
  151. *wpp = NULL;
  152. return (CELL_OUTSIDE);
  153. }
  154. /* Check if the border of a particular pane. */
  155. int
  156. screen_redraw_check_is(u_int px, u_int py, int type, struct window *w,
  157. struct window_pane *wantwp, struct window_pane *wp)
  158. {
  159. /* Is this off the active pane border? */
  160. if (screen_redraw_cell_border1(wantwp, px, py) != 1)
  161. return (0);
  162. /* If there are more than two panes, that's enough. */
  163. if (window_count_panes(w) != 2)
  164. return (1);
  165. /* Else if the cell is not a border cell, forget it. */
  166. if (wp == NULL || (type == CELL_OUTSIDE || type == CELL_INSIDE))
  167. return (1);
  168. /* Check if the pane covers the whole width. */
  169. if (wp->xoff == 0 && wp->sx == w->sx) {
  170. /* This can either be the top pane or the bottom pane. */
  171. if (wp->yoff == 0) { /* top pane */
  172. if (wp == wantwp)
  173. return (px <= wp->sx / 2);
  174. return (px > wp->sx / 2);
  175. }
  176. return (0);
  177. }
  178. /* Check if the pane covers the whole height. */
  179. if (wp->yoff == 0 && wp->sy == w->sy) {
  180. /* This can either be the left pane or the right pane. */
  181. if (wp->xoff == 0) { /* left pane */
  182. if (wp == wantwp)
  183. return (py <= wp->sy / 2);
  184. return (py > wp->sy / 2);
  185. }
  186. return (0);
  187. }
  188. return (type);
  189. }
  190. /* Redraw entire screen. */
  191. void
  192. screen_redraw_screen(struct client *c, int draw_panes, int draw_status,
  193. int draw_borders)
  194. {
  195. struct options *oo = c->session->options;
  196. struct tty *tty = &c->tty;
  197. u_int top;
  198. int status, spos;
  199. /* Suspended clients should not be updated. */
  200. if (c->flags & CLIENT_SUSPENDED)
  201. return;
  202. /* Get status line, er, status. */
  203. spos = options_get_number(oo, "status-position");
  204. if (c->message_string != NULL || c->prompt_string != NULL)
  205. status = 1;
  206. else
  207. status = options_get_number(oo, "status");
  208. top = 0;
  209. if (status && spos == 0)
  210. top = 1;
  211. if (!status)
  212. draw_status = 0;
  213. if (draw_borders)
  214. screen_redraw_draw_borders(c, status, top);
  215. if (draw_panes)
  216. screen_redraw_draw_panes(c, top);
  217. if (draw_status)
  218. screen_redraw_draw_status(c, top);
  219. tty_reset(tty);
  220. }
  221. /* Draw a single pane. */
  222. void
  223. screen_redraw_pane(struct client *c, struct window_pane *wp)
  224. {
  225. u_int i, yoff;
  226. if (!window_pane_visible(wp))
  227. return;
  228. yoff = wp->yoff;
  229. if (status_at_line(c) == 0)
  230. yoff++;
  231. for (i = 0; i < wp->sy; i++)
  232. tty_draw_pane(&c->tty, wp, i, wp->xoff, yoff);
  233. tty_reset(&c->tty);
  234. }
  235. /* Draw the borders. */
  236. void
  237. screen_redraw_draw_borders(struct client *c, int status, u_int top)
  238. {
  239. struct session *s = c->session;
  240. struct window *w = s->curw->window;
  241. struct options *oo = w->options;
  242. struct tty *tty = &c->tty;
  243. struct window_pane *wp;
  244. struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc;
  245. struct grid_cell msg_gc;
  246. u_int i, j, type, msgx = 0, msgy = 0;
  247. int active, small, flags;
  248. char msg[256];
  249. const char *tmp;
  250. size_t msglen = 0;
  251. small = (tty->sy - status + top > w->sy) || (tty->sx > w->sx);
  252. if (small) {
  253. flags = w->flags & (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT);
  254. if (flags == (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT))
  255. tmp = "force-width, force-height";
  256. else if (flags == WINDOW_FORCEWIDTH)
  257. tmp = "force-width";
  258. else if (flags == WINDOW_FORCEHEIGHT)
  259. tmp = "force-height";
  260. else
  261. tmp = "a smaller client";
  262. xsnprintf(msg, sizeof msg, "(size %ux%u from %s)",
  263. w->sx, w->sy, tmp);
  264. msglen = strlen(msg);
  265. if (tty->sy - 1 - status + top > w->sy && tty->sx >= msglen) {
  266. msgx = tty->sx - msglen;
  267. msgy = tty->sy - 1 - status + top;
  268. } else if (tty->sx - w->sx > msglen) {
  269. msgx = tty->sx - msglen;
  270. msgy = tty->sy - 1 - status + top;
  271. } else
  272. small = 0;
  273. }
  274. style_apply(&other_gc, oo, "pane-border-style");
  275. style_apply(&active_gc, oo, "pane-active-border-style");
  276. active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET;
  277. memcpy(&m_other_gc, &other_gc, sizeof m_other_gc);
  278. m_other_gc.attr ^= GRID_ATTR_REVERSE;
  279. memcpy(&m_active_gc, &active_gc, sizeof m_active_gc);
  280. m_active_gc.attr ^= GRID_ATTR_REVERSE;
  281. for (j = 0; j < tty->sy - status; j++) {
  282. for (i = 0; i < tty->sx; i++) {
  283. type = screen_redraw_check_cell(c, i, j, &wp);
  284. if (type == CELL_INSIDE)
  285. continue;
  286. if (type == CELL_OUTSIDE && small &&
  287. i > msgx && j == msgy)
  288. continue;
  289. active = screen_redraw_check_is(i, j, type, w,
  290. w->active, wp);
  291. if (server_is_marked(s, s->curw, marked_pane.wp) &&
  292. screen_redraw_check_is(i, j, type, w,
  293. marked_pane.wp, wp)) {
  294. if (active)
  295. tty_attributes(tty, &m_active_gc, NULL);
  296. else
  297. tty_attributes(tty, &m_other_gc, NULL);
  298. } else if (active)
  299. tty_attributes(tty, &active_gc, NULL);
  300. else
  301. tty_attributes(tty, &other_gc, NULL);
  302. tty_cursor(tty, i, top + j);
  303. tty_putc(tty, CELL_BORDERS[type]);
  304. }
  305. }
  306. if (small) {
  307. memcpy(&msg_gc, &grid_default_cell, sizeof msg_gc);
  308. tty_attributes(tty, &msg_gc, NULL);
  309. tty_cursor(tty, msgx, msgy);
  310. tty_puts(tty, msg);
  311. }
  312. }
  313. /* Draw the panes. */
  314. void
  315. screen_redraw_draw_panes(struct client *c, u_int top)
  316. {
  317. struct window *w = c->session->curw->window;
  318. struct tty *tty = &c->tty;
  319. struct window_pane *wp;
  320. u_int i;
  321. TAILQ_FOREACH(wp, &w->panes, entry) {
  322. if (!window_pane_visible(wp))
  323. continue;
  324. for (i = 0; i < wp->sy; i++)
  325. tty_draw_pane(tty, wp, i, wp->xoff, top + wp->yoff);
  326. if (c->flags & CLIENT_IDENTIFY)
  327. screen_redraw_draw_number(c, wp, top);
  328. }
  329. }
  330. /* Draw the status line. */
  331. void
  332. screen_redraw_draw_status(struct client *c, u_int top)
  333. {
  334. struct tty *tty = &c->tty;
  335. if (top)
  336. tty_draw_line(tty, NULL, &c->status, 0, 0, 0);
  337. else
  338. tty_draw_line(tty, NULL, &c->status, 0, 0, tty->sy - 1);
  339. }
  340. /* Draw number on a pane. */
  341. void
  342. screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top)
  343. {
  344. struct tty *tty = &c->tty;
  345. struct session *s = c->session;
  346. struct options *oo = s->options;
  347. struct window *w = wp->window;
  348. struct grid_cell gc;
  349. u_int idx, px, py, i, j, xoff, yoff;
  350. int colour, active_colour;
  351. char buf[16], *ptr;
  352. size_t len;
  353. if (window_pane_index(wp, &idx) != 0)
  354. fatalx("index not found");
  355. len = xsnprintf(buf, sizeof buf, "%u", idx);
  356. if (wp->sx < len)
  357. return;
  358. colour = options_get_number(oo, "display-panes-colour");
  359. active_colour = options_get_number(oo, "display-panes-active-colour");
  360. px = wp->sx / 2; py = wp->sy / 2;
  361. xoff = wp->xoff; yoff = wp->yoff;
  362. if (top)
  363. yoff++;
  364. if (wp->sx < len * 6 || wp->sy < 5) {
  365. tty_cursor(tty, xoff + px - len / 2, yoff + py);
  366. goto draw_text;
  367. }
  368. px -= len * 3;
  369. py -= 2;
  370. memcpy(&gc, &grid_default_cell, sizeof gc);
  371. if (w->active == wp)
  372. colour_set_bg(&gc, active_colour);
  373. else
  374. colour_set_bg(&gc, colour);
  375. tty_attributes(tty, &gc, wp);
  376. for (ptr = buf; *ptr != '\0'; ptr++) {
  377. if (*ptr < '0' || *ptr > '9')
  378. continue;
  379. idx = *ptr - '0';
  380. for (j = 0; j < 5; j++) {
  381. for (i = px; i < px + 5; i++) {
  382. tty_cursor(tty, xoff + i, yoff + py + j);
  383. if (window_clock_table[idx][j][i - px])
  384. tty_putc(tty, ' ');
  385. }
  386. }
  387. px += 6;
  388. }
  389. len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy);
  390. if (wp->sx < len || wp->sy < 6)
  391. return;
  392. tty_cursor(tty, xoff + wp->sx - len, yoff);
  393. draw_text:
  394. memcpy(&gc, &grid_default_cell, sizeof gc);
  395. if (w->active == wp)
  396. colour_set_fg(&gc, active_colour);
  397. else
  398. colour_set_fg(&gc, colour);
  399. tty_attributes(tty, &gc, wp);
  400. tty_puts(tty, buf);
  401. tty_cursor(tty, 0, 0);
  402. }