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