Annotation of src/usr.bin/tmux/tty.c, Revision 1.74
1.74 ! nicm 1: /* $OpenBSD: tty.c,v 1.73 2009/11/26 21:37:13 nicm Exp $ */
1.1 nicm 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: #include <sys/ioctl.h>
21:
22: #include <errno.h>
23: #include <fcntl.h>
1.42 nicm 24: #include <stdlib.h>
1.1 nicm 25: #include <string.h>
26: #include <termios.h>
27: #include <unistd.h>
28:
29: #include "tmux.h"
30:
1.67 nicm 31: void tty_read_callback(struct bufferevent *, void *);
1.66 nicm 32: void tty_error_callback(struct bufferevent *, short, void *);
33:
1.1 nicm 34: void tty_fill_acs(struct tty *);
35:
36: int tty_try_256(struct tty *, u_char, const char *);
37: int tty_try_88(struct tty *, u_char, const char *);
38:
1.74 ! nicm 39: void tty_colours(struct tty *, const struct grid_cell *, u_char *);
! 40: void tty_colours_fg(struct tty *, const struct grid_cell *, u_char *);
1.73 nicm 41: void tty_colours_bg(struct tty *, const struct grid_cell *);
1.1 nicm 42:
1.24 nicm 43: void tty_redraw_region(struct tty *, const struct tty_ctx *);
1.13 nicm 44: void tty_emulate_repeat(
45: struct tty *, enum tty_code_code, enum tty_code_code, u_int);
1.14 nicm 46: void tty_cell(struct tty *,
47: const struct grid_cell *, const struct grid_utf8 *);
1.1 nicm 48:
49: void
1.30 nicm 50: tty_init(struct tty *tty, int fd, char *term)
1.1 nicm 51: {
1.30 nicm 52: char *path;
53:
54: memset(tty, 0, sizeof *tty);
1.23 nicm 55: tty->log_fd = -1;
1.22 nicm 56:
1.9 nicm 57: if (term == NULL || *term == '\0')
1.1 nicm 58: tty->termname = xstrdup("unknown");
59: else
60: tty->termname = xstrdup(term);
1.30 nicm 61:
62: if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
63: fatal("fcntl failed");
64: tty->fd = fd;
65:
66: if ((path = ttyname(fd)) == NULL)
67: fatalx("ttyname failed");
68: tty->path = xstrdup(path);
69:
1.1 nicm 70: tty->flags = 0;
71: tty->term_flags = 0;
1.31 nicm 72: }
73:
74: void
75: tty_resize(struct tty *tty)
76: {
77: struct winsize ws;
78:
79: if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) {
80: tty->sx = ws.ws_col;
81: tty->sy = ws.ws_row;
82: }
83: if (tty->sx == 0)
84: tty->sx = 80;
85: if (tty->sy == 0)
86: tty->sy = 24;
87:
88: tty->cx = UINT_MAX;
89: tty->cy = UINT_MAX;
90:
91: tty->rupper = UINT_MAX;
92: tty->rlower = UINT_MAX;
1.1 nicm 93: }
94:
95: int
1.17 nicm 96: tty_open(struct tty *tty, const char *overrides, char **cause)
1.1 nicm 97: {
1.30 nicm 98: int fd;
1.1 nicm 99:
1.30 nicm 100: if (debug_level > 3) {
101: fd = open("tmux.out", O_WRONLY|O_CREAT|O_TRUNC, 0644);
102: if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
103: fatal("fcntl failed");
104: tty->log_fd = fd;
1.1 nicm 105: }
106:
1.17 nicm 107: tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause);
1.21 nicm 108: if (tty->term == NULL) {
109: tty_close(tty);
110: return (-1);
111: }
112: tty->flags |= TTY_OPENED;
1.1 nicm 113:
1.66 nicm 114: tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_ESCAPE);
1.1 nicm 115:
1.66 nicm 116: tty->event = bufferevent_new(
1.67 nicm 117: tty->fd, tty_read_callback, NULL, tty_error_callback, tty);
1.1 nicm 118:
119: tty_start_tty(tty);
120:
121: tty_keys_init(tty);
122:
123: tty_fill_acs(tty);
124:
125: return (0);
126: }
127:
1.73 nicm 128: /* ARGSUSED */
1.1 nicm 129: void
1.67 nicm 130: tty_read_callback(unused struct bufferevent *bufev, void *data)
131: {
132: struct tty *tty = data;
133:
134: while (tty_keys_next(tty))
135: ;
136: }
137:
1.73 nicm 138: /* ARGSUSED */
1.67 nicm 139: void
1.66 nicm 140: tty_error_callback(
141: unused struct bufferevent *bufev, unused short what, unused void *data)
142: {
143: }
144:
145: void
1.1 nicm 146: tty_start_tty(struct tty *tty)
147: {
148: struct termios tio;
1.34 nicm 149: int what, mode;
1.33 nicm 150:
151: if (tty->fd == -1)
152: return;
1.1 nicm 153:
1.34 nicm 154: if ((mode = fcntl(tty->fd, F_GETFL)) == -1)
155: fatal("fcntl failed");
156: if (fcntl(tty->fd, F_SETFL, mode|O_NONBLOCK) == -1)
157: fatal("fcntl failed");
158:
1.66 nicm 159: bufferevent_enable(tty->event, EV_READ|EV_WRITE);
160:
1.1 nicm 161: if (tcgetattr(tty->fd, &tty->tio) != 0)
162: fatal("tcgetattr failed");
163: memcpy(&tio, &tty->tio, sizeof tio);
164: tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP);
165: tio.c_iflag |= IGNBRK;
166: tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
167: tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|
168: ECHOPRT|ECHOKE|ECHOCTL|ISIG);
169: tio.c_cc[VMIN] = 1;
1.60 deraadt 170: tio.c_cc[VTIME] = 0;
1.1 nicm 171: if (tcsetattr(tty->fd, TCSANOW, &tio) != 0)
172: fatal("tcsetattr failed");
173:
174: what = 0;
175: if (ioctl(tty->fd, TIOCFLUSH, &what) != 0)
176: fatal("ioctl(TIOCFLUSH)");
177:
1.10 nicm 178: tty_putcode(tty, TTYC_SMCUP);
1.1 nicm 179:
1.25 nicm 180: tty_putcode(tty, TTYC_SGR0);
181: memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
182:
1.1 nicm 183: tty_putcode(tty, TTYC_SMKX);
184: tty_putcode(tty, TTYC_ENACS);
185: tty_putcode(tty, TTYC_CLEAR);
186:
187: tty_putcode(tty, TTYC_CNORM);
188: if (tty_term_has(tty->term, TTYC_KMOUS))
189: tty_puts(tty, "\033[?1000l");
190:
191: tty->cx = UINT_MAX;
192: tty->cy = UINT_MAX;
193:
194: tty->rlower = UINT_MAX;
195: tty->rupper = UINT_MAX;
196:
197: tty->mode = MODE_CURSOR;
1.20 nicm 198:
199: tty->flags |= TTY_STARTED;
1.1 nicm 200: }
201:
202: void
203: tty_stop_tty(struct tty *tty)
204: {
205: struct winsize ws;
1.34 nicm 206: int mode;
1.1 nicm 207:
1.20 nicm 208: if (!(tty->flags & TTY_STARTED))
209: return;
210: tty->flags &= ~TTY_STARTED;
211:
1.66 nicm 212: bufferevent_disable(tty->event, EV_READ|EV_WRITE);
213:
1.1 nicm 214: /*
215: * Be flexible about error handling and try not kill the server just
216: * because the fd is invalid. Things like ssh -t can easily leave us
217: * with a dead tty.
218: */
1.34 nicm 219: if ((mode = fcntl(tty->fd, F_GETFL)) == -1)
220: return;
221: if (fcntl(tty->fd, F_SETFL, mode & ~O_NONBLOCK) == -1)
222: return;
1.1 nicm 223: if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1)
224: return;
225: if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1)
226: return;
227:
228: tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
229: tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
230: tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
231: tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
232: tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
233:
234: tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
235: if (tty_term_has(tty->term, TTYC_KMOUS))
236: tty_raw(tty, "\033[?1000l");
1.10 nicm 237:
238: tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
1.1 nicm 239: }
240:
241: void
242: tty_fill_acs(struct tty *tty)
243: {
244: const char *ptr;
245:
246: memset(tty->acs, 0, sizeof tty->acs);
247: if (!tty_term_has(tty->term, TTYC_ACSC))
248: return;
249:
250: ptr = tty_term_string(tty->term, TTYC_ACSC);
251: if (strlen(ptr) % 2 != 0)
252: return;
253: for (; *ptr != '\0'; ptr += 2)
254: tty->acs[(u_char) ptr[0]] = ptr[1];
255: }
256:
257: u_char
258: tty_get_acs(struct tty *tty, u_char ch)
259: {
260: if (tty->acs[ch] != '\0')
261: return (tty->acs[ch]);
262: return (ch);
263: }
264:
265: void
1.20 nicm 266: tty_close(struct tty *tty)
1.1 nicm 267: {
268: if (tty->log_fd != -1) {
269: close(tty->log_fd);
270: tty->log_fd = -1;
271: }
272:
1.67 nicm 273: evtimer_del(&tty->key_timer);
1.20 nicm 274: tty_stop_tty(tty);
1.1 nicm 275:
1.21 nicm 276: if (tty->flags & TTY_OPENED) {
1.66 nicm 277: bufferevent_free(tty->event);
278:
1.21 nicm 279: tty_term_free(tty->term);
280: tty_keys_free(tty);
281:
282: tty->flags &= ~TTY_OPENED;
283: }
1.1 nicm 284:
1.21 nicm 285: if (tty->fd != -1) {
286: close(tty->fd);
287: tty->fd = -1;
288: }
1.1 nicm 289: }
290:
291: void
1.20 nicm 292: tty_free(struct tty *tty)
1.1 nicm 293: {
1.20 nicm 294: tty_close(tty);
1.1 nicm 295:
296: if (tty->path != NULL)
297: xfree(tty->path);
298: if (tty->termname != NULL)
299: xfree(tty->termname);
300: }
301:
302: void
303: tty_raw(struct tty *tty, const char *s)
304: {
305: write(tty->fd, s, strlen(s));
306: }
307:
308: void
309: tty_putcode(struct tty *tty, enum tty_code_code code)
310: {
311: tty_puts(tty, tty_term_string(tty->term, code));
312: }
313:
314: void
315: tty_putcode1(struct tty *tty, enum tty_code_code code, int a)
316: {
317: if (a < 0)
318: return;
319: tty_puts(tty, tty_term_string1(tty->term, code, a));
320: }
321:
322: void
323: tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b)
324: {
325: if (a < 0 || b < 0)
326: return;
327: tty_puts(tty, tty_term_string2(tty->term, code, a, b));
328: }
329:
330: void
331: tty_puts(struct tty *tty, const char *s)
332: {
333: if (*s == '\0')
334: return;
1.66 nicm 335: bufferevent_write(tty->event, s, strlen(s));
1.1 nicm 336:
337: if (tty->log_fd != -1)
338: write(tty->log_fd, s, strlen(s));
339: }
340:
341: void
342: tty_putc(struct tty *tty, u_char ch)
343: {
344: u_int sx;
345:
346: if (tty->cell.attr & GRID_ATTR_CHARSET)
347: ch = tty_get_acs(tty, ch);
1.66 nicm 348: bufferevent_write(tty->event, &ch, 1);
1.1 nicm 349:
350: if (ch >= 0x20 && ch != 0x7f) {
351: sx = tty->sx;
352: if (tty->term->flags & TERM_EARLYWRAP)
353: sx--;
354:
1.46 nicm 355: if (tty->cx >= sx) {
356: tty->cx = 1;
357: if (tty->cy != tty->rlower)
358: tty->cy++;
1.1 nicm 359: } else
360: tty->cx++;
361: }
362:
363: if (tty->log_fd != -1)
364: write(tty->log_fd, &ch, 1);
365: }
366:
367: void
1.5 nicm 368: tty_pututf8(struct tty *tty, const struct grid_utf8 *gu)
369: {
1.71 nicm 370: size_t size;
1.5 nicm 371:
1.71 nicm 372: size = grid_utf8_size(gu);
373: bufferevent_write(tty->event, gu->data, size);
374: if (tty->log_fd != -1)
375: write(tty->log_fd, gu->data, size);
1.54 nicm 376: tty->cx += gu->width;
1.5 nicm 377: }
378:
379: void
1.1 nicm 380: tty_set_title(struct tty *tty, const char *title)
381: {
382: if (strstr(tty->termname, "xterm") == NULL &&
383: strstr(tty->termname, "rxvt") == NULL &&
384: strcmp(tty->termname, "screen") != 0)
385: return;
386:
387: tty_puts(tty, "\033]0;");
388: tty_puts(tty, title);
389: tty_putc(tty, '\007');
390: }
391:
392: void
393: tty_update_mode(struct tty *tty, int mode)
394: {
395: int changed;
396:
397: if (tty->flags & TTY_NOCURSOR)
398: mode &= ~MODE_CURSOR;
399:
400: changed = mode ^ tty->mode;
401: if (changed & MODE_CURSOR) {
402: if (mode & MODE_CURSOR)
403: tty_putcode(tty, TTYC_CNORM);
404: else
405: tty_putcode(tty, TTYC_CIVIS);
406: }
407: if (changed & MODE_MOUSE) {
408: if (mode & MODE_MOUSE)
409: tty_puts(tty, "\033[?1000h");
410: else
411: tty_puts(tty, "\033[?1000l");
412: }
413: tty->mode = mode;
414: }
415:
416: void
417: tty_emulate_repeat(
418: struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n)
419: {
420: if (tty_term_has(tty->term, code))
421: tty_putcode1(tty, code, n);
422: else {
423: while (n-- > 0)
424: tty_putcode(tty, code1);
425: }
426: }
427:
428: /*
429: * Redraw scroll region using data from screen (already updated). Used when
430: * CSR not supported, or window is a pane that doesn't take up the full
431: * width of the terminal.
432: */
433: void
1.24 nicm 434: tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 435: {
1.14 nicm 436: struct window_pane *wp = ctx->wp;
437: struct screen *s = wp->screen;
438: u_int i;
1.1 nicm 439:
440: /*
441: * If region is >= 50% of the screen, just schedule a window redraw. In
442: * most cases, this is likely to be followed by some more scrolling -
443: * without this, the entire pane ends up being redrawn many times which
444: * can be much more data.
445: */
1.14 nicm 446: if (ctx->orupper - ctx->orlower >= screen_size_y(s) / 2) {
1.1 nicm 447: wp->flags |= PANE_REDRAW;
448: return;
449: }
450:
1.14 nicm 451: if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) {
452: for (i = ctx->ocy; i < screen_size_y(s); i++)
1.1 nicm 453: tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
454: } else {
1.14 nicm 455: for (i = ctx->orupper; i <= ctx->orlower; i++)
1.1 nicm 456: tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
457: }
458: }
459:
460: void
461: tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy)
462: {
463: const struct grid_cell *gc;
1.46 nicm 464: struct grid_line *gl;
1.16 nicm 465: struct grid_cell tmpgc;
1.1 nicm 466: const struct grid_utf8 *gu;
467: u_int i, sx;
468:
1.35 nicm 469: tty_update_mode(tty, tty->mode & ~MODE_CURSOR);
470:
1.1 nicm 471: sx = screen_size_x(s);
1.19 nicm 472: if (sx > s->grid->linedata[s->grid->hsize + py].cellsize)
473: sx = s->grid->linedata[s->grid->hsize + py].cellsize;
1.1 nicm 474: if (sx > tty->sx)
475: sx = tty->sx;
476:
1.46 nicm 477: /*
478: * Don't move the cursor to the start permission if it will wrap there
1.50 nicm 479: * itself.
1.46 nicm 480: */
481: gl = NULL;
482: if (py != 0)
483: gl = &s->grid->linedata[s->grid->hsize + py - 1];
1.47 nicm 484: if (oy + py == 0 || (gl != NULL && !(gl->flags & GRID_LINE_WRAPPED)) ||
485: tty->cx < tty->sx || ox != 0 ||
486: (oy + py != tty->cy + 1 && tty->cy != s->rlower + oy))
1.46 nicm 487: tty_cursor(tty, ox, oy + py);
488:
1.1 nicm 489: for (i = 0; i < sx; i++) {
490: gc = grid_view_peek_cell(s->grid, i, py);
491:
492: gu = NULL;
493: if (gc->flags & GRID_FLAG_UTF8)
494: gu = grid_view_peek_utf8(s->grid, i, py);
495:
496: if (screen_check_selection(s, i, py)) {
1.16 nicm 497: memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc);
498: tmpgc.data = gc->data;
1.38 nicm 499: tmpgc.flags = gc->flags &
500: ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
501: tmpgc.flags |= s->sel.cell.flags &
502: (GRID_FLAG_FG256|GRID_FLAG_BG256);
1.16 nicm 503: tty_cell(tty, &tmpgc, gu);
1.1 nicm 504: } else
505: tty_cell(tty, gc, gu);
506: }
507:
1.35 nicm 508: if (sx >= tty->sx) {
509: tty_update_mode(tty, tty->mode);
1.1 nicm 510: return;
1.35 nicm 511: }
1.1 nicm 512: tty_reset(tty);
513:
1.41 nicm 514: tty_cursor(tty, ox + sx, oy + py);
1.1 nicm 515: if (screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL))
516: tty_putcode(tty, TTYC_EL);
517: else {
518: for (i = sx; i < screen_size_x(s); i++)
519: tty_putc(tty, ' ');
1.15 nicm 520: }
1.35 nicm 521: tty_update_mode(tty, tty->mode);
1.15 nicm 522: }
523:
524: void
1.24 nicm 525: tty_write(void (*cmdfn)(
526: struct tty *, const struct tty_ctx *), const struct tty_ctx *ctx)
1.15 nicm 527: {
528: struct window_pane *wp = ctx->wp;
529: struct client *c;
530: u_int i;
531:
532: if (wp == NULL)
533: return;
534:
535: if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
536: return;
537: if (wp->window->flags & WINDOW_HIDDEN || !window_pane_visible(wp))
538: return;
539:
540: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
541: c = ARRAY_ITEM(&clients, i);
542: if (c == NULL || c->session == NULL)
543: continue;
544: if (c->flags & CLIENT_SUSPENDED)
545: continue;
546:
547: if (c->session->curw->window == wp->window) {
548: if (c->tty.flags & TTY_FREEZE || c->tty.term == NULL)
549: continue;
550: cmdfn(&c->tty, ctx);
551: }
1.1 nicm 552: }
553: }
554:
555: void
1.24 nicm 556: tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 557: {
1.12 nicm 558: struct window_pane *wp = ctx->wp;
559: struct screen *s = wp->screen;
1.24 nicm 560: u_int i;
1.1 nicm 561:
562: if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
1.14 nicm 563: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 564: return;
565: }
566:
567: tty_reset(tty);
568:
1.41 nicm 569: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
570:
1.1 nicm 571: if (tty_term_has(tty->term, TTYC_ICH) ||
572: tty_term_has(tty->term, TTYC_ICH1))
1.12 nicm 573: tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
1.72 nicm 574: else if (tty_term_has(tty->term, TTYC_SMIR) &&
575: tty_term_has(tty->term, TTYC_RMIR)) {
1.1 nicm 576: tty_putcode(tty, TTYC_SMIR);
1.24 nicm 577: for (i = 0; i < ctx->num; i++)
1.1 nicm 578: tty_putc(tty, ' ');
579: tty_putcode(tty, TTYC_RMIR);
1.72 nicm 580: } else
581: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 582: }
583:
584: void
1.24 nicm 585: tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 586: {
1.12 nicm 587: struct window_pane *wp = ctx->wp;
588: struct screen *s = wp->screen;
1.1 nicm 589:
1.26 nicm 590: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
591: (!tty_term_has(tty->term, TTYC_DCH) &&
592: !tty_term_has(tty->term, TTYC_DCH1))) {
1.14 nicm 593: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 594: return;
595: }
596:
597: tty_reset(tty);
598:
1.41 nicm 599: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
600:
1.26 nicm 601: if (tty_term_has(tty->term, TTYC_DCH) ||
602: tty_term_has(tty->term, TTYC_DCH1))
603: tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
1.1 nicm 604: }
605:
606: void
1.24 nicm 607: tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 608: {
1.12 nicm 609: struct window_pane *wp = ctx->wp;
610: struct screen *s = wp->screen;
1.1 nicm 611:
612: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
1.72 nicm 613: !tty_term_has(tty->term, TTYC_CSR) ||
614: !tty_term_has(tty->term, TTYC_IL1)) {
1.14 nicm 615: tty_redraw_region(tty, ctx);
1.1 nicm 616: return;
617: }
618:
619: tty_reset(tty);
620:
1.39 nicm 621: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1.41 nicm 622: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 623:
1.12 nicm 624: tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
1.1 nicm 625: }
626:
627: void
1.24 nicm 628: tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 629: {
1.12 nicm 630: struct window_pane *wp = ctx->wp;
631: struct screen *s = wp->screen;
1.1 nicm 632:
633: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
1.72 nicm 634: !tty_term_has(tty->term, TTYC_CSR) ||
635: !tty_term_has(tty->term, TTYC_DL1)) {
1.14 nicm 636: tty_redraw_region(tty, ctx);
1.1 nicm 637: return;
638: }
639:
640: tty_reset(tty);
641:
1.39 nicm 642: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1.41 nicm 643: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 644:
1.12 nicm 645: tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
1.1 nicm 646: }
647:
648: void
1.24 nicm 649: tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 650: {
1.12 nicm 651: struct window_pane *wp = ctx->wp;
652: struct screen *s = wp->screen;
653: u_int i;
1.1 nicm 654:
655: tty_reset(tty);
656:
1.41 nicm 657: tty_cursor_pane(tty, ctx, 0, ctx->ocy);
658:
1.1 nicm 659: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
660: tty_term_has(tty->term, TTYC_EL)) {
661: tty_putcode(tty, TTYC_EL);
662: } else {
663: for (i = 0; i < screen_size_x(s); i++)
664: tty_putc(tty, ' ');
665: }
666: }
667:
668: void
1.24 nicm 669: tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 670: {
1.12 nicm 671: struct window_pane *wp = ctx->wp;
672: struct screen *s = wp->screen;
673: u_int i;
1.1 nicm 674:
675: tty_reset(tty);
676:
1.41 nicm 677: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
678:
1.1 nicm 679: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
680: tty_term_has(tty->term, TTYC_EL))
681: tty_putcode(tty, TTYC_EL);
682: else {
1.14 nicm 683: for (i = ctx->ocx; i < screen_size_x(s); i++)
1.1 nicm 684: tty_putc(tty, ' ');
685: }
686: }
687:
688: void
1.24 nicm 689: tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 690: {
1.12 nicm 691: struct window_pane *wp = ctx->wp;
692: u_int i;
1.1 nicm 693:
694: tty_reset(tty);
695:
696: if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) {
1.41 nicm 697: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 698: tty_putcode(tty, TTYC_EL1);
699: } else {
1.41 nicm 700: tty_cursor_pane(tty, ctx, 0, ctx->ocy);
1.14 nicm 701: for (i = 0; i < ctx->ocx + 1; i++)
1.1 nicm 702: tty_putc(tty, ' ');
703: }
704: }
705:
706: void
1.24 nicm 707: tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 708: {
1.12 nicm 709: struct window_pane *wp = ctx->wp;
710: struct screen *s = wp->screen;
1.1 nicm 711:
1.56 nicm 712: if (ctx->ocy != ctx->orupper)
713: return;
714:
1.1 nicm 715: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
1.70 nicm 716: !tty_term_has(tty->term, TTYC_CSR) ||
717: !tty_term_has(tty->term, TTYC_RI)) {
1.14 nicm 718: tty_redraw_region(tty, ctx);
1.1 nicm 719: return;
720: }
721:
1.56 nicm 722: tty_reset(tty);
723:
724: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
725: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
726:
727: tty_putcode(tty, TTYC_RI);
1.1 nicm 728: }
729:
730: void
1.24 nicm 731: tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 732: {
1.12 nicm 733: struct window_pane *wp = ctx->wp;
734: struct screen *s = wp->screen;
1.1 nicm 735:
1.56 nicm 736: if (ctx->ocy != ctx->orlower)
737: return;
738:
1.1 nicm 739: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
740: !tty_term_has(tty->term, TTYC_CSR)) {
1.14 nicm 741: tty_redraw_region(tty, ctx);
1.1 nicm 742: return;
743: }
1.52 nicm 744:
745: /*
746: * If this line wrapped naturally (ctx->num is nonzero), don't do
747: * anything - the cursor can just be moved to the last cell and wrap
748: * naturally.
749: */
750: if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP))
751: return;
1.1 nicm 752:
1.56 nicm 753: tty_reset(tty);
754:
755: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
756: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
757:
758: tty_putc(tty, '\n');
1.1 nicm 759: }
760:
761: void
1.24 nicm 762: tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 763: {
1.12 nicm 764: struct window_pane *wp = ctx->wp;
765: struct screen *s = wp->screen;
766: u_int i, j;
1.1 nicm 767:
768: tty_reset(tty);
769:
1.39 nicm 770: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.41 nicm 771: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.39 nicm 772:
1.1 nicm 773: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
774: tty_term_has(tty->term, TTYC_EL)) {
775: tty_putcode(tty, TTYC_EL);
1.14 nicm 776: if (ctx->ocy != screen_size_y(s) - 1) {
1.41 nicm 777: tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
1.14 nicm 778: for (i = ctx->ocy + 1; i < screen_size_y(s); i++) {
1.1 nicm 779: tty_putcode(tty, TTYC_EL);
780: if (i == screen_size_y(s) - 1)
781: continue;
782: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
783: tty->cy++;
784: }
785: }
786: } else {
1.14 nicm 787: for (i = ctx->ocx; i < screen_size_x(s); i++)
1.1 nicm 788: tty_putc(tty, ' ');
1.68 nicm 789: for (j = ctx->ocy + 1; j < screen_size_y(s); j++) {
1.41 nicm 790: tty_cursor_pane(tty, ctx, 0, j);
1.1 nicm 791: for (i = 0; i < screen_size_x(s); i++)
792: tty_putc(tty, ' ');
793: }
794: }
795: }
796:
797: void
1.24 nicm 798: tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 799: {
1.12 nicm 800: struct window_pane *wp = ctx->wp;
801: struct screen *s = wp->screen;
802: u_int i, j;
1.1 nicm 803:
804: tty_reset(tty);
805:
1.39 nicm 806: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.41 nicm 807: tty_cursor_pane(tty, ctx, 0, 0);
1.39 nicm 808:
1.1 nicm 809: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
810: tty_term_has(tty->term, TTYC_EL)) {
1.14 nicm 811: for (i = 0; i < ctx->ocy; i++) {
1.1 nicm 812: tty_putcode(tty, TTYC_EL);
813: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
814: tty->cy++;
815: }
816: } else {
1.14 nicm 817: for (j = 0; j < ctx->ocy; j++) {
1.41 nicm 818: tty_cursor_pane(tty, ctx, 0, j);
1.1 nicm 819: for (i = 0; i < screen_size_x(s); i++)
820: tty_putc(tty, ' ');
821: }
822: }
1.14 nicm 823: for (i = 0; i <= ctx->ocx; i++)
1.1 nicm 824: tty_putc(tty, ' ');
825: }
826:
827: void
1.24 nicm 828: tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 829: {
1.12 nicm 830: struct window_pane *wp = ctx->wp;
831: struct screen *s = wp->screen;
832: u_int i, j;
1.1 nicm 833:
834: tty_reset(tty);
835:
1.39 nicm 836: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.41 nicm 837: tty_cursor_pane(tty, ctx, 0, 0);
1.39 nicm 838:
1.1 nicm 839: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
840: tty_term_has(tty->term, TTYC_EL)) {
841: for (i = 0; i < screen_size_y(s); i++) {
842: tty_putcode(tty, TTYC_EL);
843: if (i != screen_size_y(s) - 1) {
844: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
845: tty->cy++;
846: }
847: }
848: } else {
849: for (j = 0; j < screen_size_y(s); j++) {
1.41 nicm 850: tty_cursor_pane(tty, ctx, 0, j);
1.1 nicm 851: for (i = 0; i < screen_size_x(s); i++)
852: tty_putc(tty, ' ');
853: }
1.4 nicm 854: }
855: }
856:
857: void
1.24 nicm 858: tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
1.4 nicm 859: {
1.12 nicm 860: struct window_pane *wp = ctx->wp;
861: struct screen *s = wp->screen;
862: u_int i, j;
1.4 nicm 863:
864: tty_reset(tty);
865:
1.39 nicm 866: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.4 nicm 867:
868: for (j = 0; j < screen_size_y(s); j++) {
1.41 nicm 869: tty_cursor_pane(tty, ctx, 0, j);
1.4 nicm 870: for (i = 0; i < screen_size_x(s); i++)
871: tty_putc(tty, 'E');
1.1 nicm 872: }
873: }
874:
875: void
1.24 nicm 876: tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 877: {
1.46 nicm 878: struct window_pane *wp = ctx->wp;
879: struct screen *s = wp->screen;
1.58 nicm 880: u_int cx;
1.46 nicm 881:
882: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
883:
1.57 nicm 884: /* Is the cursor in the very last position? */
885: if (ctx->ocx > wp->sx - ctx->last_width) {
886: if (wp->xoff != 0 || wp->sx != tty->sx) {
887: /*
888: * The pane doesn't fill the entire line, the linefeed
889: * will already have happened, so just move the cursor.
890: */
891: tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
892: } else if (tty->cx < tty->sx) {
893: /*
894: * The cursor isn't in the last position already, so
895: * move as far left as possinble and redraw the last
896: * cell to move into the last position.
897: */
1.50 nicm 898: cx = screen_size_x(s) - ctx->last_width;
899: tty_cursor_pane(tty, ctx, cx, ctx->ocy);
900: tty_cell(tty, &ctx->last_cell, &ctx->last_utf8);
901: }
902: } else
1.46 nicm 903: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 904:
1.12 nicm 905: tty_cell(tty, ctx->cell, ctx->utf8);
1.1 nicm 906: }
907:
908: void
1.24 nicm 909: tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 910: {
1.53 nicm 911: struct window_pane *wp = ctx->wp;
1.1 nicm 912:
1.53 nicm 913: /*
914: * Cannot rely on not being a partial character, so just redraw the
915: * whole line.
916: */
917: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 918: }
919:
920: void
921: tty_cell(
922: struct tty *tty, const struct grid_cell *gc, const struct grid_utf8 *gu)
923: {
924: u_int i;
925:
926: /* Skip last character if terminal is stupid. */
927: if (tty->term->flags & TERM_EARLYWRAP &&
928: tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1)
929: return;
930:
931: /* If this is a padding character, do nothing. */
932: if (gc->flags & GRID_FLAG_PADDING)
933: return;
934:
935: /* Set the attributes. */
936: tty_attributes(tty, gc);
937:
938: /* If not UTF-8, write directly. */
939: if (!(gc->flags & GRID_FLAG_UTF8)) {
940: if (gc->data < 0x20 || gc->data == 0x7f)
941: return;
942: tty_putc(tty, gc->data);
943: return;
944: }
945:
946: /* If the terminal doesn't support UTF-8, write underscores. */
947: if (!(tty->flags & TTY_UTF8)) {
948: for (i = 0; i < gu->width; i++)
949: tty_putc(tty, '_');
950: return;
951: }
952:
953: /* Otherwise, write UTF-8. */
1.5 nicm 954: tty_pututf8(tty, gu);
1.1 nicm 955: }
956:
957: void
958: tty_reset(struct tty *tty)
959: {
960: struct grid_cell *gc = &tty->cell;
961:
962: if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0)
963: return;
964:
965: if (tty_term_has(tty->term, TTYC_RMACS) && gc->attr & GRID_ATTR_CHARSET)
966: tty_putcode(tty, TTYC_RMACS);
967: tty_putcode(tty, TTYC_SGR0);
968: memcpy(gc, &grid_default_cell, sizeof *gc);
969: }
970:
1.40 nicm 971: /* Set region inside pane. */
1.1 nicm 972: void
1.39 nicm 973: tty_region_pane(
974: struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower)
1.1 nicm 975: {
1.39 nicm 976: struct window_pane *wp = ctx->wp;
977:
1.40 nicm 978: tty_region(tty, wp->yoff + rupper, wp->yoff + rlower);
1.39 nicm 979: }
980:
1.40 nicm 981: /* Set region at absolute position. */
1.39 nicm 982: void
1.40 nicm 983: tty_region(struct tty *tty, u_int rupper, u_int rlower)
1.39 nicm 984: {
985: if (tty->rlower == rlower && tty->rupper == rupper)
986: return;
1.1 nicm 987: if (!tty_term_has(tty->term, TTYC_CSR))
988: return;
1.39 nicm 989:
990: tty->rupper = rupper;
991: tty->rlower = rlower;
1.55 nicm 992:
993: /*
994: * Some terminals (such as PuTTY) do not correctly reset the cursor to
995: * 0,0 if it is beyond the last column (they do not reset their wrap
996: * flag so further output causes a line feed). As a workaround, do an
997: * explicit move to 0 first.
998: */
999: if (tty->cx >= tty->sx)
1000: tty_cursor(tty, 0, tty->cy);
1.42 nicm 1001:
1.39 nicm 1002: tty->cx = 0;
1003: tty->cy = 0;
1004:
1005: tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
1.1 nicm 1006: }
1007:
1.42 nicm 1008: /* Move cursor inside pane. */
1.1 nicm 1009: void
1.41 nicm 1010: tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)
1011: {
1012: struct window_pane *wp = ctx->wp;
1013:
1014: tty_cursor(tty, wp->xoff + cx, wp->yoff + cy);
1015: }
1016:
1.42 nicm 1017: /* Move cursor to absolute position. */
1.41 nicm 1018: void
1019: tty_cursor(struct tty *tty, u_int cx, u_int cy)
1.1 nicm 1020: {
1.42 nicm 1021: struct tty_term *term = tty->term;
1022: u_int thisx, thisy;
1023: int change;
1024:
1025: if (cx > tty->sx - 1)
1026: cx = tty->sx - 1;
1027:
1028: thisx = tty->cx;
1029: thisy = tty->cy;
1030:
1031: /* No change. */
1032: if (cx == thisx && cy == thisy)
1033: return;
1034:
1.43 nicm 1035: /* Very end of the line, just use absolute movement. */
1036: if (thisx > tty->sx - 1)
1037: goto absolute;
1038:
1.42 nicm 1039: /* Move to home position (0, 0). */
1040: if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) {
1041: tty_putcode(tty, TTYC_HOME);
1042: goto out;
1043: }
1044:
1045: /* Zero on the next line. */
1.48 nicm 1046: if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower) {
1.1 nicm 1047: tty_putc(tty, '\r');
1.42 nicm 1048: tty_putc(tty, '\n');
1049: goto out;
1050: }
1051:
1.45 nicm 1052: /* Moving column or row. */
1.42 nicm 1053: if (cy == thisy) {
1.45 nicm 1054: /*
1055: * Moving column only, row staying the same.
1056: */
1057:
1.42 nicm 1058: /* To left edge. */
1059: if (cx == 0) {
1060: tty_putc(tty, '\r');
1061: goto out;
1062: }
1063:
1064: /* One to the left. */
1065: if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) {
1066: tty_putcode(tty, TTYC_CUB1);
1067: goto out;
1068: }
1069:
1070: /* One to the right. */
1071: if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) {
1072: tty_putcode(tty, TTYC_CUF1);
1073: goto out;
1074: }
1075:
1076: /* Calculate difference. */
1077: change = thisx - cx; /* +ve left, -ve right */
1078:
1079: /*
1080: * Use HPA if change is larger than absolute, otherwise move
1081: * the cursor with CUB/CUF.
1082: */
1083: if (abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
1084: tty_putcode1(tty, TTYC_HPA, cx);
1085: goto out;
1086: } else if (change > 0 && tty_term_has(term, TTYC_CUB)) {
1087: tty_putcode1(tty, TTYC_CUB, change);
1088: goto out;
1089: } else if (change < 0 && tty_term_has(term, TTYC_CUF)) {
1090: tty_putcode1(tty, TTYC_CUF, -change);
1091: goto out;
1092: }
1.45 nicm 1093: } else if (cx == thisx) {
1094: /*
1095: * Moving row only, column staying the same.
1096: */
1.42 nicm 1097:
1098: /* One above. */
1.49 nicm 1099: if (thisy != tty->rupper &&
1.42 nicm 1100: cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) {
1101: tty_putcode(tty, TTYC_CUU1);
1102: goto out;
1103: }
1104:
1105: /* One below. */
1.49 nicm 1106: if (thisy != tty->rlower &&
1.42 nicm 1107: cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) {
1108: tty_putcode(tty, TTYC_CUD1);
1109: goto out;
1110: }
1111:
1112: /* Calculate difference. */
1113: change = thisy - cy; /* +ve up, -ve down */
1114:
1115: /*
1.44 nicm 1116: * Try to use VPA if change is larger than absolute or if this change
1.42 nicm 1117: * would cross the scroll region, otherwise use CUU/CUD.
1118: */
1.44 nicm 1119: if (abs(change) > cy ||
1.42 nicm 1120: (change < 0 && cy - change > tty->rlower) ||
1.44 nicm 1121: (change > 0 && cy - change < tty->rupper)) {
1122: if (tty_term_has(term, TTYC_VPA)) {
1123: tty_putcode1(tty, TTYC_VPA, cy);
1124: goto out;
1125: }
1.42 nicm 1126: } else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
1127: tty_putcode1(tty, TTYC_CUU, change);
1128: goto out;
1129: } else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
1130: tty_putcode1(tty, TTYC_CUD, -change);
1131: goto out;
1132: }
1133: }
1134:
1.43 nicm 1135: absolute:
1.42 nicm 1136: /* Absolute movement. */
1137: tty_putcode2(tty, TTYC_CUP, cy, cx);
1138:
1139: out:
1140: tty->cx = cx;
1141: tty->cy = cy;
1.1 nicm 1142: }
1143:
1144: void
1145: tty_attributes(struct tty *tty, const struct grid_cell *gc)
1146: {
1.63 nicm 1147: struct grid_cell *tc = &tty->cell, gc2;
1.74 ! nicm 1148: u_char changed, new_attr;
1.61 nicm 1149:
1.63 nicm 1150: /* If the character is space, don't care about foreground. */
1151: if (gc->data == ' ' && !(gc->flags & GRID_FLAG_UTF8)) {
1152: memcpy(&gc2, gc, sizeof gc2);
1153: if (gc->attr & GRID_ATTR_REVERSE)
1154: gc2.bg = tc->bg;
1155: else
1156: gc2.fg = tc->fg;
1157: gc = &gc2;
1158: }
1159:
1.18 nicm 1160: /*
1161: * If no setab, try to use the reverse attribute as a best-effort for a
1162: * non-default background. This is a bit of a hack but it doesn't do
1163: * any serious harm and makes a couple of applications happier.
1164: */
1165: if (!tty_term_has(tty->term, TTYC_SETAB)) {
1.64 nicm 1166: if (gc != &gc2) {
1167: memcpy(&gc2, gc, sizeof gc2);
1168: gc = &gc2;
1169: }
1170:
1171: if (gc->attr & GRID_ATTR_REVERSE) {
1172: if (gc->fg != 7 && gc->fg != 8)
1173: gc2.attr &= ~GRID_ATTR_REVERSE;
1.18 nicm 1174: } else {
1.64 nicm 1175: if (gc->bg != 0 && gc->bg != 8)
1176: gc2.attr |= GRID_ATTR_REVERSE;
1.18 nicm 1177: }
1178: }
1.1 nicm 1179:
1.64 nicm 1180: /* If any bits are being cleared, reset everything. */
1181: if (tc->attr & ~gc->attr)
1182: tty_reset(tty);
1183:
1184: /*
1185: * Set the colours. This may call tty_reset() (so it comes next) and
1186: * may add to (NOT remove) the desired attributes by changing new_attr.
1187: */
1188: new_attr = gc->attr;
1189: tty_colours(tty, gc, &new_attr);
1190:
1.1 nicm 1191: /* Filter out attribute bits already set. */
1.64 nicm 1192: changed = new_attr & ~tc->attr;
1193: tc->attr = new_attr;
1.1 nicm 1194:
1195: /* Set the attributes. */
1196: if (changed & GRID_ATTR_BRIGHT)
1197: tty_putcode(tty, TTYC_BOLD);
1198: if (changed & GRID_ATTR_DIM)
1199: tty_putcode(tty, TTYC_DIM);
1200: if (changed & GRID_ATTR_ITALICS)
1201: tty_putcode(tty, TTYC_SMSO);
1202: if (changed & GRID_ATTR_UNDERSCORE)
1203: tty_putcode(tty, TTYC_SMUL);
1204: if (changed & GRID_ATTR_BLINK)
1205: tty_putcode(tty, TTYC_BLINK);
1206: if (changed & GRID_ATTR_REVERSE) {
1207: if (tty_term_has(tty->term, TTYC_REV))
1208: tty_putcode(tty, TTYC_REV);
1209: else if (tty_term_has(tty->term, TTYC_SMSO))
1210: tty_putcode(tty, TTYC_SMSO);
1211: }
1212: if (changed & GRID_ATTR_HIDDEN)
1213: tty_putcode(tty, TTYC_INVIS);
1214: if (changed & GRID_ATTR_CHARSET)
1215: tty_putcode(tty, TTYC_SMACS);
1216: }
1217:
1.61 nicm 1218: void
1.74 ! nicm 1219: tty_colours(struct tty *tty, const struct grid_cell *gc, u_char *attr)
1.1 nicm 1220: {
1.61 nicm 1221: struct grid_cell *tc = &tty->cell;
1.62 nicm 1222: u_char fg = gc->fg, bg = gc->bg, flags = gc->flags;
1223: int have_ax, fg_default, bg_default;
1.61 nicm 1224:
1225: /* No changes? Nothing is necessary. */
1.62 nicm 1226: if (fg == tc->fg && bg == tc->bg &&
1227: ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256)) == 0)
1.63 nicm 1228: return;
1.1 nicm 1229:
1.61 nicm 1230: /*
1231: * Is either the default colour? This is handled specially because the
1232: * best solution might be to reset both colours to default, in which
1233: * case if only one is default need to fall onward to set the other
1234: * colour.
1235: */
1.62 nicm 1236: fg_default = (fg == 8 && !(flags & GRID_FLAG_FG256));
1237: bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256));
1.61 nicm 1238: if (fg_default || bg_default) {
1239: /*
1240: * If don't have AX but do have op, send sgr0 (op can't
1241: * actually be used because it is sometimes the same as sgr0
1242: * and sometimes isn't). This resets both colours to default.
1243: *
1244: * Otherwise, try to set the default colour only as needed.
1245: */
1246: have_ax = tty_term_has(tty->term, TTYC_AX);
1247: if (!have_ax && tty_term_has(tty->term, TTYC_OP))
1248: tty_reset(tty);
1249: else {
1250: if (fg_default &&
1251: fg != tc->fg && !(tc->flags & GRID_FLAG_FG256)) {
1252: if (have_ax)
1253: tty_puts(tty, "\033[39m");
1254: else if (tc->fg != 7)
1255: tty_putcode1(tty, TTYC_SETAF, 7);
1256: tc->fg = 8;
1257: tc->flags &= ~GRID_FLAG_FG256;
1258: }
1259: if (bg_default &&
1260: bg != tc->bg && !(tc->flags & GRID_FLAG_BG256)) {
1261: if (have_ax)
1262: tty_puts(tty, "\033[49m");
1263: else if (tc->bg != 0)
1264: tty_putcode1(tty, TTYC_SETAB, 0);
1265: tc->bg = 8;
1266: tc->flags &= ~GRID_FLAG_BG256;
1267: }
1268: }
1269: }
1.1 nicm 1270:
1.61 nicm 1271: /* Set the foreground colour. */
1272: if (!fg_default && (fg != tc->fg ||
1.62 nicm 1273: ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256))))
1.61 nicm 1274: tty_colours_fg(tty, gc, attr);
1.1 nicm 1275:
1.61 nicm 1276: /*
1277: * Set the background colour. This must come after the foreground as
1278: * tty_colour_fg() can call tty_reset().
1279: */
1280: if (!bg_default && (bg != tc->bg ||
1.62 nicm 1281: ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256))))
1.73 nicm 1282: tty_colours_bg(tty, gc);
1.1 nicm 1283: }
1284:
1285: void
1.74 ! nicm 1286: tty_colours_fg(struct tty *tty, const struct grid_cell *gc, u_char *attr)
1.1 nicm 1287: {
1.61 nicm 1288: struct grid_cell *tc = &tty->cell;
1289: u_char fg = gc->fg;
1.1 nicm 1290:
1.61 nicm 1291: /* Is this a 256-colour colour? */
1.1 nicm 1292: if (gc->flags & GRID_FLAG_FG256) {
1.61 nicm 1293: /* Try as 256 colours or translating to 88. */
1.1 nicm 1294: if (tty_try_256(tty, fg, "38") == 0)
1.61 nicm 1295: goto save_fg;
1.1 nicm 1296: if (tty_try_88(tty, fg, "38") == 0)
1.61 nicm 1297: goto save_fg;
1298:
1299: /* Translate to 16-colour palette, updating bold if needed. */
1.1 nicm 1300: fg = colour_256to16(fg);
1301: if (fg & 8) {
1302: fg &= 7;
1.61 nicm 1303: (*attr) |= GRID_ATTR_BRIGHT;
1304: } else
1305: tty_reset(tty); /* turn off bold */
1.1 nicm 1306: }
1307:
1.61 nicm 1308: /* Otherwise set the foreground colour. */
1309: tty_putcode1(tty, TTYC_SETAF, fg);
1310:
1311: save_fg:
1312: /* Save the new values in the terminal current cell. */
1313: tc->fg = fg;
1314: tc->flags &= ~GRID_FLAG_FG256;
1315: tc->flags |= gc->flags & GRID_FLAG_FG256;
1.1 nicm 1316: }
1317:
1318: void
1.73 nicm 1319: tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
1.1 nicm 1320: {
1.61 nicm 1321: struct grid_cell *tc = &tty->cell;
1322: u_char bg = gc->bg;
1.1 nicm 1323:
1.61 nicm 1324: /* Is this a 256-colour colour? */
1.1 nicm 1325: if (gc->flags & GRID_FLAG_BG256) {
1.61 nicm 1326: /* Try as 256 colours or translating to 88. */
1.1 nicm 1327: if (tty_try_256(tty, bg, "48") == 0)
1.61 nicm 1328: goto save_bg;
1.1 nicm 1329: if (tty_try_88(tty, bg, "48") == 0)
1.61 nicm 1330: goto save_bg;
1331:
1332: /*
1333: * Translate to 16-colour palette. Bold background doesn't
1334: * exist portably, so just discard the bold bit if set.
1335: */
1.1 nicm 1336: bg = colour_256to16(bg);
1337: if (bg & 8)
1338: bg &= 7;
1339: }
1340:
1.61 nicm 1341: /* Otherwise set the background colour. */
1342: tty_putcode1(tty, TTYC_SETAB, bg);
1343:
1344: save_bg:
1345: /* Save the new values in the terminal current cell. */
1346: tc->bg = bg;
1347: tc->flags &= ~GRID_FLAG_BG256;
1348: tc->flags |= gc->flags & GRID_FLAG_BG256;
1349: }
1350:
1351: int
1352: tty_try_256(struct tty *tty, u_char colour, const char *type)
1353: {
1354: char s[32];
1355:
1356: if (!(tty->term->flags & TERM_256COLOURS) &&
1357: !(tty->term_flags & TERM_256COLOURS))
1358: return (-1);
1359:
1360: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1361: tty_puts(tty, s);
1362: return (0);
1363: }
1364:
1365: int
1366: tty_try_88(struct tty *tty, u_char colour, const char *type)
1367: {
1368: char s[32];
1369:
1370: if (!(tty->term->flags & TERM_88COLOURS) &&
1371: !(tty->term_flags & TERM_88COLOURS))
1372: return (-1);
1373: colour = colour_256to88(colour);
1374:
1375: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1376: tty_puts(tty, s);
1377: return (0);
1.1 nicm 1378: }