Annotation of src/usr.bin/tmux/tty.c, Revision 1.76
1.76 ! nicm 1: /* $OpenBSD: tty.c,v 1.75 2009/11/27 09:41:03 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.76 ! nicm 183: tty_putcode(tty, TTYC_RMKX);
1.1 nicm 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");
1.76 ! nicm 412: }
! 413: if (changed & MODE_KKEYPAD) {
! 414: if (mode & MODE_KKEYPAD)
! 415: tty_putcode(tty, TTYC_SMKX);
! 416: else
! 417: tty_putcode(tty, TTYC_RMKX);
1.1 nicm 418: }
419: tty->mode = mode;
420: }
421:
422: void
423: tty_emulate_repeat(
424: struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n)
425: {
426: if (tty_term_has(tty->term, code))
427: tty_putcode1(tty, code, n);
428: else {
429: while (n-- > 0)
430: tty_putcode(tty, code1);
431: }
432: }
433:
434: /*
435: * Redraw scroll region using data from screen (already updated). Used when
436: * CSR not supported, or window is a pane that doesn't take up the full
437: * width of the terminal.
438: */
439: void
1.24 nicm 440: tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 441: {
1.14 nicm 442: struct window_pane *wp = ctx->wp;
443: struct screen *s = wp->screen;
444: u_int i;
1.1 nicm 445:
446: /*
447: * If region is >= 50% of the screen, just schedule a window redraw. In
448: * most cases, this is likely to be followed by some more scrolling -
449: * without this, the entire pane ends up being redrawn many times which
450: * can be much more data.
451: */
1.14 nicm 452: if (ctx->orupper - ctx->orlower >= screen_size_y(s) / 2) {
1.1 nicm 453: wp->flags |= PANE_REDRAW;
454: return;
455: }
456:
1.14 nicm 457: if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) {
458: for (i = ctx->ocy; i < screen_size_y(s); i++)
1.1 nicm 459: tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
460: } else {
1.14 nicm 461: for (i = ctx->orupper; i <= ctx->orlower; i++)
1.1 nicm 462: tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
463: }
464: }
465:
466: void
467: tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy)
468: {
469: const struct grid_cell *gc;
1.46 nicm 470: struct grid_line *gl;
1.16 nicm 471: struct grid_cell tmpgc;
1.1 nicm 472: const struct grid_utf8 *gu;
473: u_int i, sx;
474:
1.35 nicm 475: tty_update_mode(tty, tty->mode & ~MODE_CURSOR);
476:
1.1 nicm 477: sx = screen_size_x(s);
1.19 nicm 478: if (sx > s->grid->linedata[s->grid->hsize + py].cellsize)
479: sx = s->grid->linedata[s->grid->hsize + py].cellsize;
1.1 nicm 480: if (sx > tty->sx)
481: sx = tty->sx;
482:
1.46 nicm 483: /*
484: * Don't move the cursor to the start permission if it will wrap there
1.50 nicm 485: * itself.
1.46 nicm 486: */
487: gl = NULL;
488: if (py != 0)
489: gl = &s->grid->linedata[s->grid->hsize + py - 1];
1.47 nicm 490: if (oy + py == 0 || (gl != NULL && !(gl->flags & GRID_LINE_WRAPPED)) ||
491: tty->cx < tty->sx || ox != 0 ||
492: (oy + py != tty->cy + 1 && tty->cy != s->rlower + oy))
1.46 nicm 493: tty_cursor(tty, ox, oy + py);
494:
1.1 nicm 495: for (i = 0; i < sx; i++) {
496: gc = grid_view_peek_cell(s->grid, i, py);
497:
498: gu = NULL;
499: if (gc->flags & GRID_FLAG_UTF8)
500: gu = grid_view_peek_utf8(s->grid, i, py);
501:
502: if (screen_check_selection(s, i, py)) {
1.16 nicm 503: memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc);
504: tmpgc.data = gc->data;
1.38 nicm 505: tmpgc.flags = gc->flags &
506: ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
507: tmpgc.flags |= s->sel.cell.flags &
508: (GRID_FLAG_FG256|GRID_FLAG_BG256);
1.16 nicm 509: tty_cell(tty, &tmpgc, gu);
1.1 nicm 510: } else
511: tty_cell(tty, gc, gu);
512: }
513:
1.35 nicm 514: if (sx >= tty->sx) {
515: tty_update_mode(tty, tty->mode);
1.1 nicm 516: return;
1.35 nicm 517: }
1.1 nicm 518: tty_reset(tty);
519:
1.41 nicm 520: tty_cursor(tty, ox + sx, oy + py);
1.1 nicm 521: if (screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL))
522: tty_putcode(tty, TTYC_EL);
523: else {
524: for (i = sx; i < screen_size_x(s); i++)
525: tty_putc(tty, ' ');
1.15 nicm 526: }
1.35 nicm 527: tty_update_mode(tty, tty->mode);
1.15 nicm 528: }
529:
530: void
1.24 nicm 531: tty_write(void (*cmdfn)(
532: struct tty *, const struct tty_ctx *), const struct tty_ctx *ctx)
1.15 nicm 533: {
534: struct window_pane *wp = ctx->wp;
535: struct client *c;
536: u_int i;
537:
1.75 nicm 538: /* wp can be NULL if updating the screen but not the terminal. */
1.15 nicm 539: if (wp == NULL)
540: return;
541:
542: if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
543: return;
544: if (wp->window->flags & WINDOW_HIDDEN || !window_pane_visible(wp))
545: return;
546:
547: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
548: c = ARRAY_ITEM(&clients, i);
549: if (c == NULL || c->session == NULL)
550: continue;
551: if (c->flags & CLIENT_SUSPENDED)
552: continue;
553:
554: if (c->session->curw->window == wp->window) {
555: if (c->tty.flags & TTY_FREEZE || c->tty.term == NULL)
556: continue;
557: cmdfn(&c->tty, ctx);
558: }
1.1 nicm 559: }
560: }
561:
562: void
1.24 nicm 563: tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 564: {
1.12 nicm 565: struct window_pane *wp = ctx->wp;
566: struct screen *s = wp->screen;
1.24 nicm 567: u_int i;
1.1 nicm 568:
569: if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
1.14 nicm 570: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 571: return;
572: }
573:
574: tty_reset(tty);
575:
1.41 nicm 576: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
577:
1.1 nicm 578: if (tty_term_has(tty->term, TTYC_ICH) ||
579: tty_term_has(tty->term, TTYC_ICH1))
1.12 nicm 580: tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
1.72 nicm 581: else if (tty_term_has(tty->term, TTYC_SMIR) &&
582: tty_term_has(tty->term, TTYC_RMIR)) {
1.1 nicm 583: tty_putcode(tty, TTYC_SMIR);
1.24 nicm 584: for (i = 0; i < ctx->num; i++)
1.1 nicm 585: tty_putc(tty, ' ');
586: tty_putcode(tty, TTYC_RMIR);
1.72 nicm 587: } else
588: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 589: }
590:
591: void
1.24 nicm 592: tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 593: {
1.12 nicm 594: struct window_pane *wp = ctx->wp;
595: struct screen *s = wp->screen;
1.1 nicm 596:
1.26 nicm 597: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
598: (!tty_term_has(tty->term, TTYC_DCH) &&
599: !tty_term_has(tty->term, TTYC_DCH1))) {
1.14 nicm 600: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 601: return;
602: }
603:
604: tty_reset(tty);
605:
1.41 nicm 606: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
607:
1.26 nicm 608: if (tty_term_has(tty->term, TTYC_DCH) ||
609: tty_term_has(tty->term, TTYC_DCH1))
610: tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
1.1 nicm 611: }
612:
613: void
1.24 nicm 614: tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 615: {
1.12 nicm 616: struct window_pane *wp = ctx->wp;
617: struct screen *s = wp->screen;
1.1 nicm 618:
619: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
1.72 nicm 620: !tty_term_has(tty->term, TTYC_CSR) ||
621: !tty_term_has(tty->term, TTYC_IL1)) {
1.14 nicm 622: tty_redraw_region(tty, ctx);
1.1 nicm 623: return;
624: }
625:
626: tty_reset(tty);
627:
1.39 nicm 628: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1.41 nicm 629: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 630:
1.12 nicm 631: tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
1.1 nicm 632: }
633:
634: void
1.24 nicm 635: tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 636: {
1.12 nicm 637: struct window_pane *wp = ctx->wp;
638: struct screen *s = wp->screen;
1.1 nicm 639:
640: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
1.72 nicm 641: !tty_term_has(tty->term, TTYC_CSR) ||
642: !tty_term_has(tty->term, TTYC_DL1)) {
1.14 nicm 643: tty_redraw_region(tty, ctx);
1.1 nicm 644: return;
645: }
646:
647: tty_reset(tty);
648:
1.39 nicm 649: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1.41 nicm 650: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 651:
1.12 nicm 652: tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
1.1 nicm 653: }
654:
655: void
1.24 nicm 656: tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 657: {
1.12 nicm 658: struct window_pane *wp = ctx->wp;
659: struct screen *s = wp->screen;
660: u_int i;
1.1 nicm 661:
662: tty_reset(tty);
663:
1.41 nicm 664: tty_cursor_pane(tty, ctx, 0, ctx->ocy);
665:
1.1 nicm 666: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
667: tty_term_has(tty->term, TTYC_EL)) {
668: tty_putcode(tty, TTYC_EL);
669: } else {
670: for (i = 0; i < screen_size_x(s); i++)
671: tty_putc(tty, ' ');
672: }
673: }
674:
675: void
1.24 nicm 676: tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 677: {
1.12 nicm 678: struct window_pane *wp = ctx->wp;
679: struct screen *s = wp->screen;
680: u_int i;
1.1 nicm 681:
682: tty_reset(tty);
683:
1.41 nicm 684: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
685:
1.1 nicm 686: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
687: tty_term_has(tty->term, TTYC_EL))
688: tty_putcode(tty, TTYC_EL);
689: else {
1.14 nicm 690: for (i = ctx->ocx; i < screen_size_x(s); i++)
1.1 nicm 691: tty_putc(tty, ' ');
692: }
693: }
694:
695: void
1.24 nicm 696: tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 697: {
1.12 nicm 698: struct window_pane *wp = ctx->wp;
699: u_int i;
1.1 nicm 700:
701: tty_reset(tty);
702:
703: if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) {
1.41 nicm 704: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 705: tty_putcode(tty, TTYC_EL1);
706: } else {
1.41 nicm 707: tty_cursor_pane(tty, ctx, 0, ctx->ocy);
1.14 nicm 708: for (i = 0; i < ctx->ocx + 1; i++)
1.1 nicm 709: tty_putc(tty, ' ');
710: }
711: }
712:
713: void
1.24 nicm 714: tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 715: {
1.12 nicm 716: struct window_pane *wp = ctx->wp;
717: struct screen *s = wp->screen;
1.1 nicm 718:
1.56 nicm 719: if (ctx->ocy != ctx->orupper)
720: return;
721:
1.1 nicm 722: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
1.70 nicm 723: !tty_term_has(tty->term, TTYC_CSR) ||
724: !tty_term_has(tty->term, TTYC_RI)) {
1.14 nicm 725: tty_redraw_region(tty, ctx);
1.1 nicm 726: return;
727: }
728:
1.56 nicm 729: tty_reset(tty);
730:
731: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
732: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
733:
734: tty_putcode(tty, TTYC_RI);
1.1 nicm 735: }
736:
737: void
1.24 nicm 738: tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 739: {
1.12 nicm 740: struct window_pane *wp = ctx->wp;
741: struct screen *s = wp->screen;
1.1 nicm 742:
1.56 nicm 743: if (ctx->ocy != ctx->orlower)
744: return;
745:
1.1 nicm 746: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
747: !tty_term_has(tty->term, TTYC_CSR)) {
1.14 nicm 748: tty_redraw_region(tty, ctx);
1.1 nicm 749: return;
750: }
1.52 nicm 751:
752: /*
753: * If this line wrapped naturally (ctx->num is nonzero), don't do
754: * anything - the cursor can just be moved to the last cell and wrap
755: * naturally.
756: */
757: if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP))
758: return;
1.1 nicm 759:
1.56 nicm 760: tty_reset(tty);
761:
762: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
763: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
764:
765: tty_putc(tty, '\n');
1.1 nicm 766: }
767:
768: void
1.24 nicm 769: tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 770: {
1.12 nicm 771: struct window_pane *wp = ctx->wp;
772: struct screen *s = wp->screen;
773: u_int i, j;
1.1 nicm 774:
775: tty_reset(tty);
776:
1.39 nicm 777: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.41 nicm 778: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.39 nicm 779:
1.1 nicm 780: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
781: tty_term_has(tty->term, TTYC_EL)) {
782: tty_putcode(tty, TTYC_EL);
1.14 nicm 783: if (ctx->ocy != screen_size_y(s) - 1) {
1.41 nicm 784: tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
1.14 nicm 785: for (i = ctx->ocy + 1; i < screen_size_y(s); i++) {
1.1 nicm 786: tty_putcode(tty, TTYC_EL);
787: if (i == screen_size_y(s) - 1)
788: continue;
789: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
790: tty->cy++;
791: }
792: }
793: } else {
1.14 nicm 794: for (i = ctx->ocx; i < screen_size_x(s); i++)
1.1 nicm 795: tty_putc(tty, ' ');
1.68 nicm 796: for (j = ctx->ocy + 1; j < screen_size_y(s); j++) {
1.41 nicm 797: tty_cursor_pane(tty, ctx, 0, j);
1.1 nicm 798: for (i = 0; i < screen_size_x(s); i++)
799: tty_putc(tty, ' ');
800: }
801: }
802: }
803:
804: void
1.24 nicm 805: tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 806: {
1.12 nicm 807: struct window_pane *wp = ctx->wp;
808: struct screen *s = wp->screen;
809: u_int i, j;
1.1 nicm 810:
811: tty_reset(tty);
812:
1.39 nicm 813: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.41 nicm 814: tty_cursor_pane(tty, ctx, 0, 0);
1.39 nicm 815:
1.1 nicm 816: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
817: tty_term_has(tty->term, TTYC_EL)) {
1.14 nicm 818: for (i = 0; i < ctx->ocy; i++) {
1.1 nicm 819: tty_putcode(tty, TTYC_EL);
820: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
821: tty->cy++;
822: }
823: } else {
1.14 nicm 824: for (j = 0; j < ctx->ocy; j++) {
1.41 nicm 825: tty_cursor_pane(tty, ctx, 0, j);
1.1 nicm 826: for (i = 0; i < screen_size_x(s); i++)
827: tty_putc(tty, ' ');
828: }
829: }
1.14 nicm 830: for (i = 0; i <= ctx->ocx; i++)
1.1 nicm 831: tty_putc(tty, ' ');
832: }
833:
834: void
1.24 nicm 835: tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 836: {
1.12 nicm 837: struct window_pane *wp = ctx->wp;
838: struct screen *s = wp->screen;
839: u_int i, j;
1.1 nicm 840:
841: tty_reset(tty);
842:
1.39 nicm 843: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.41 nicm 844: tty_cursor_pane(tty, ctx, 0, 0);
1.39 nicm 845:
1.1 nicm 846: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
847: tty_term_has(tty->term, TTYC_EL)) {
848: for (i = 0; i < screen_size_y(s); i++) {
849: tty_putcode(tty, TTYC_EL);
850: if (i != screen_size_y(s) - 1) {
851: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
852: tty->cy++;
853: }
854: }
855: } else {
856: for (j = 0; j < screen_size_y(s); j++) {
1.41 nicm 857: tty_cursor_pane(tty, ctx, 0, j);
1.1 nicm 858: for (i = 0; i < screen_size_x(s); i++)
859: tty_putc(tty, ' ');
860: }
1.4 nicm 861: }
862: }
863:
864: void
1.24 nicm 865: tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
1.4 nicm 866: {
1.12 nicm 867: struct window_pane *wp = ctx->wp;
868: struct screen *s = wp->screen;
869: u_int i, j;
1.4 nicm 870:
871: tty_reset(tty);
872:
1.39 nicm 873: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.4 nicm 874:
875: for (j = 0; j < screen_size_y(s); j++) {
1.41 nicm 876: tty_cursor_pane(tty, ctx, 0, j);
1.4 nicm 877: for (i = 0; i < screen_size_x(s); i++)
878: tty_putc(tty, 'E');
1.1 nicm 879: }
880: }
881:
882: void
1.24 nicm 883: tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 884: {
1.46 nicm 885: struct window_pane *wp = ctx->wp;
886: struct screen *s = wp->screen;
1.58 nicm 887: u_int cx;
1.46 nicm 888:
889: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
890:
1.57 nicm 891: /* Is the cursor in the very last position? */
892: if (ctx->ocx > wp->sx - ctx->last_width) {
893: if (wp->xoff != 0 || wp->sx != tty->sx) {
894: /*
895: * The pane doesn't fill the entire line, the linefeed
896: * will already have happened, so just move the cursor.
897: */
898: tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
899: } else if (tty->cx < tty->sx) {
900: /*
901: * The cursor isn't in the last position already, so
902: * move as far left as possinble and redraw the last
903: * cell to move into the last position.
904: */
1.50 nicm 905: cx = screen_size_x(s) - ctx->last_width;
906: tty_cursor_pane(tty, ctx, cx, ctx->ocy);
907: tty_cell(tty, &ctx->last_cell, &ctx->last_utf8);
908: }
909: } else
1.46 nicm 910: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 911:
1.12 nicm 912: tty_cell(tty, ctx->cell, ctx->utf8);
1.1 nicm 913: }
914:
915: void
1.24 nicm 916: tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 917: {
1.53 nicm 918: struct window_pane *wp = ctx->wp;
1.1 nicm 919:
1.53 nicm 920: /*
921: * Cannot rely on not being a partial character, so just redraw the
922: * whole line.
923: */
924: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 925: }
926:
927: void
928: tty_cell(
929: struct tty *tty, const struct grid_cell *gc, const struct grid_utf8 *gu)
930: {
931: u_int i;
932:
933: /* Skip last character if terminal is stupid. */
934: if (tty->term->flags & TERM_EARLYWRAP &&
935: tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1)
936: return;
937:
938: /* If this is a padding character, do nothing. */
939: if (gc->flags & GRID_FLAG_PADDING)
940: return;
941:
942: /* Set the attributes. */
943: tty_attributes(tty, gc);
944:
945: /* If not UTF-8, write directly. */
946: if (!(gc->flags & GRID_FLAG_UTF8)) {
947: if (gc->data < 0x20 || gc->data == 0x7f)
948: return;
949: tty_putc(tty, gc->data);
950: return;
951: }
952:
953: /* If the terminal doesn't support UTF-8, write underscores. */
954: if (!(tty->flags & TTY_UTF8)) {
955: for (i = 0; i < gu->width; i++)
956: tty_putc(tty, '_');
957: return;
958: }
959:
960: /* Otherwise, write UTF-8. */
1.5 nicm 961: tty_pututf8(tty, gu);
1.1 nicm 962: }
963:
964: void
965: tty_reset(struct tty *tty)
966: {
967: struct grid_cell *gc = &tty->cell;
968:
969: if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0)
970: return;
971:
972: if (tty_term_has(tty->term, TTYC_RMACS) && gc->attr & GRID_ATTR_CHARSET)
973: tty_putcode(tty, TTYC_RMACS);
974: tty_putcode(tty, TTYC_SGR0);
975: memcpy(gc, &grid_default_cell, sizeof *gc);
976: }
977:
1.40 nicm 978: /* Set region inside pane. */
1.1 nicm 979: void
1.39 nicm 980: tty_region_pane(
981: struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower)
1.1 nicm 982: {
1.39 nicm 983: struct window_pane *wp = ctx->wp;
984:
1.40 nicm 985: tty_region(tty, wp->yoff + rupper, wp->yoff + rlower);
1.39 nicm 986: }
987:
1.40 nicm 988: /* Set region at absolute position. */
1.39 nicm 989: void
1.40 nicm 990: tty_region(struct tty *tty, u_int rupper, u_int rlower)
1.39 nicm 991: {
992: if (tty->rlower == rlower && tty->rupper == rupper)
993: return;
1.1 nicm 994: if (!tty_term_has(tty->term, TTYC_CSR))
995: return;
1.39 nicm 996:
997: tty->rupper = rupper;
998: tty->rlower = rlower;
1.55 nicm 999:
1000: /*
1001: * Some terminals (such as PuTTY) do not correctly reset the cursor to
1002: * 0,0 if it is beyond the last column (they do not reset their wrap
1003: * flag so further output causes a line feed). As a workaround, do an
1004: * explicit move to 0 first.
1005: */
1006: if (tty->cx >= tty->sx)
1007: tty_cursor(tty, 0, tty->cy);
1.42 nicm 1008:
1.39 nicm 1009: tty->cx = 0;
1010: tty->cy = 0;
1011:
1012: tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
1.1 nicm 1013: }
1014:
1.42 nicm 1015: /* Move cursor inside pane. */
1.1 nicm 1016: void
1.41 nicm 1017: tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)
1018: {
1019: struct window_pane *wp = ctx->wp;
1020:
1021: tty_cursor(tty, wp->xoff + cx, wp->yoff + cy);
1022: }
1023:
1.42 nicm 1024: /* Move cursor to absolute position. */
1.41 nicm 1025: void
1026: tty_cursor(struct tty *tty, u_int cx, u_int cy)
1.1 nicm 1027: {
1.42 nicm 1028: struct tty_term *term = tty->term;
1029: u_int thisx, thisy;
1030: int change;
1031:
1032: if (cx > tty->sx - 1)
1033: cx = tty->sx - 1;
1034:
1035: thisx = tty->cx;
1036: thisy = tty->cy;
1037:
1038: /* No change. */
1039: if (cx == thisx && cy == thisy)
1040: return;
1041:
1.43 nicm 1042: /* Very end of the line, just use absolute movement. */
1043: if (thisx > tty->sx - 1)
1044: goto absolute;
1045:
1.42 nicm 1046: /* Move to home position (0, 0). */
1047: if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) {
1048: tty_putcode(tty, TTYC_HOME);
1049: goto out;
1050: }
1051:
1052: /* Zero on the next line. */
1.48 nicm 1053: if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower) {
1.1 nicm 1054: tty_putc(tty, '\r');
1.42 nicm 1055: tty_putc(tty, '\n');
1056: goto out;
1057: }
1058:
1.45 nicm 1059: /* Moving column or row. */
1.42 nicm 1060: if (cy == thisy) {
1.45 nicm 1061: /*
1062: * Moving column only, row staying the same.
1063: */
1064:
1.42 nicm 1065: /* To left edge. */
1066: if (cx == 0) {
1067: tty_putc(tty, '\r');
1068: goto out;
1069: }
1070:
1071: /* One to the left. */
1072: if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) {
1073: tty_putcode(tty, TTYC_CUB1);
1074: goto out;
1075: }
1076:
1077: /* One to the right. */
1078: if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) {
1079: tty_putcode(tty, TTYC_CUF1);
1080: goto out;
1081: }
1082:
1083: /* Calculate difference. */
1084: change = thisx - cx; /* +ve left, -ve right */
1085:
1086: /*
1087: * Use HPA if change is larger than absolute, otherwise move
1088: * the cursor with CUB/CUF.
1089: */
1090: if (abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
1091: tty_putcode1(tty, TTYC_HPA, cx);
1092: goto out;
1093: } else if (change > 0 && tty_term_has(term, TTYC_CUB)) {
1094: tty_putcode1(tty, TTYC_CUB, change);
1095: goto out;
1096: } else if (change < 0 && tty_term_has(term, TTYC_CUF)) {
1097: tty_putcode1(tty, TTYC_CUF, -change);
1098: goto out;
1099: }
1.45 nicm 1100: } else if (cx == thisx) {
1101: /*
1102: * Moving row only, column staying the same.
1103: */
1.42 nicm 1104:
1105: /* One above. */
1.49 nicm 1106: if (thisy != tty->rupper &&
1.42 nicm 1107: cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) {
1108: tty_putcode(tty, TTYC_CUU1);
1109: goto out;
1110: }
1111:
1112: /* One below. */
1.49 nicm 1113: if (thisy != tty->rlower &&
1.42 nicm 1114: cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) {
1115: tty_putcode(tty, TTYC_CUD1);
1116: goto out;
1117: }
1118:
1119: /* Calculate difference. */
1120: change = thisy - cy; /* +ve up, -ve down */
1121:
1122: /*
1.44 nicm 1123: * Try to use VPA if change is larger than absolute or if this change
1.42 nicm 1124: * would cross the scroll region, otherwise use CUU/CUD.
1125: */
1.44 nicm 1126: if (abs(change) > cy ||
1.42 nicm 1127: (change < 0 && cy - change > tty->rlower) ||
1.44 nicm 1128: (change > 0 && cy - change < tty->rupper)) {
1129: if (tty_term_has(term, TTYC_VPA)) {
1130: tty_putcode1(tty, TTYC_VPA, cy);
1131: goto out;
1132: }
1.42 nicm 1133: } else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
1134: tty_putcode1(tty, TTYC_CUU, change);
1135: goto out;
1136: } else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
1137: tty_putcode1(tty, TTYC_CUD, -change);
1138: goto out;
1139: }
1140: }
1141:
1.43 nicm 1142: absolute:
1.42 nicm 1143: /* Absolute movement. */
1144: tty_putcode2(tty, TTYC_CUP, cy, cx);
1145:
1146: out:
1147: tty->cx = cx;
1148: tty->cy = cy;
1.1 nicm 1149: }
1150:
1151: void
1152: tty_attributes(struct tty *tty, const struct grid_cell *gc)
1153: {
1.63 nicm 1154: struct grid_cell *tc = &tty->cell, gc2;
1.74 nicm 1155: u_char changed, new_attr;
1.61 nicm 1156:
1.63 nicm 1157: /* If the character is space, don't care about foreground. */
1158: if (gc->data == ' ' && !(gc->flags & GRID_FLAG_UTF8)) {
1159: memcpy(&gc2, gc, sizeof gc2);
1160: if (gc->attr & GRID_ATTR_REVERSE)
1161: gc2.bg = tc->bg;
1162: else
1163: gc2.fg = tc->fg;
1164: gc = &gc2;
1165: }
1166:
1.18 nicm 1167: /*
1168: * If no setab, try to use the reverse attribute as a best-effort for a
1169: * non-default background. This is a bit of a hack but it doesn't do
1170: * any serious harm and makes a couple of applications happier.
1171: */
1172: if (!tty_term_has(tty->term, TTYC_SETAB)) {
1.64 nicm 1173: if (gc != &gc2) {
1174: memcpy(&gc2, gc, sizeof gc2);
1175: gc = &gc2;
1176: }
1177:
1178: if (gc->attr & GRID_ATTR_REVERSE) {
1179: if (gc->fg != 7 && gc->fg != 8)
1180: gc2.attr &= ~GRID_ATTR_REVERSE;
1.18 nicm 1181: } else {
1.64 nicm 1182: if (gc->bg != 0 && gc->bg != 8)
1183: gc2.attr |= GRID_ATTR_REVERSE;
1.18 nicm 1184: }
1185: }
1.1 nicm 1186:
1.64 nicm 1187: /* If any bits are being cleared, reset everything. */
1188: if (tc->attr & ~gc->attr)
1189: tty_reset(tty);
1190:
1191: /*
1192: * Set the colours. This may call tty_reset() (so it comes next) and
1193: * may add to (NOT remove) the desired attributes by changing new_attr.
1194: */
1195: new_attr = gc->attr;
1196: tty_colours(tty, gc, &new_attr);
1197:
1.1 nicm 1198: /* Filter out attribute bits already set. */
1.64 nicm 1199: changed = new_attr & ~tc->attr;
1200: tc->attr = new_attr;
1.1 nicm 1201:
1202: /* Set the attributes. */
1203: if (changed & GRID_ATTR_BRIGHT)
1204: tty_putcode(tty, TTYC_BOLD);
1205: if (changed & GRID_ATTR_DIM)
1206: tty_putcode(tty, TTYC_DIM);
1207: if (changed & GRID_ATTR_ITALICS)
1208: tty_putcode(tty, TTYC_SMSO);
1209: if (changed & GRID_ATTR_UNDERSCORE)
1210: tty_putcode(tty, TTYC_SMUL);
1211: if (changed & GRID_ATTR_BLINK)
1212: tty_putcode(tty, TTYC_BLINK);
1213: if (changed & GRID_ATTR_REVERSE) {
1214: if (tty_term_has(tty->term, TTYC_REV))
1215: tty_putcode(tty, TTYC_REV);
1216: else if (tty_term_has(tty->term, TTYC_SMSO))
1217: tty_putcode(tty, TTYC_SMSO);
1218: }
1219: if (changed & GRID_ATTR_HIDDEN)
1220: tty_putcode(tty, TTYC_INVIS);
1221: if (changed & GRID_ATTR_CHARSET)
1222: tty_putcode(tty, TTYC_SMACS);
1223: }
1224:
1.61 nicm 1225: void
1.74 nicm 1226: tty_colours(struct tty *tty, const struct grid_cell *gc, u_char *attr)
1.1 nicm 1227: {
1.61 nicm 1228: struct grid_cell *tc = &tty->cell;
1.62 nicm 1229: u_char fg = gc->fg, bg = gc->bg, flags = gc->flags;
1230: int have_ax, fg_default, bg_default;
1.61 nicm 1231:
1232: /* No changes? Nothing is necessary. */
1.62 nicm 1233: if (fg == tc->fg && bg == tc->bg &&
1234: ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256)) == 0)
1.63 nicm 1235: return;
1.1 nicm 1236:
1.61 nicm 1237: /*
1238: * Is either the default colour? This is handled specially because the
1239: * best solution might be to reset both colours to default, in which
1240: * case if only one is default need to fall onward to set the other
1241: * colour.
1242: */
1.62 nicm 1243: fg_default = (fg == 8 && !(flags & GRID_FLAG_FG256));
1244: bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256));
1.61 nicm 1245: if (fg_default || bg_default) {
1246: /*
1247: * If don't have AX but do have op, send sgr0 (op can't
1248: * actually be used because it is sometimes the same as sgr0
1249: * and sometimes isn't). This resets both colours to default.
1250: *
1251: * Otherwise, try to set the default colour only as needed.
1252: */
1253: have_ax = tty_term_has(tty->term, TTYC_AX);
1254: if (!have_ax && tty_term_has(tty->term, TTYC_OP))
1255: tty_reset(tty);
1256: else {
1257: if (fg_default &&
1258: fg != tc->fg && !(tc->flags & GRID_FLAG_FG256)) {
1259: if (have_ax)
1260: tty_puts(tty, "\033[39m");
1261: else if (tc->fg != 7)
1262: tty_putcode1(tty, TTYC_SETAF, 7);
1263: tc->fg = 8;
1264: tc->flags &= ~GRID_FLAG_FG256;
1265: }
1266: if (bg_default &&
1267: bg != tc->bg && !(tc->flags & GRID_FLAG_BG256)) {
1268: if (have_ax)
1269: tty_puts(tty, "\033[49m");
1270: else if (tc->bg != 0)
1271: tty_putcode1(tty, TTYC_SETAB, 0);
1272: tc->bg = 8;
1273: tc->flags &= ~GRID_FLAG_BG256;
1274: }
1275: }
1276: }
1.1 nicm 1277:
1.61 nicm 1278: /* Set the foreground colour. */
1279: if (!fg_default && (fg != tc->fg ||
1.62 nicm 1280: ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256))))
1.61 nicm 1281: tty_colours_fg(tty, gc, attr);
1.1 nicm 1282:
1.61 nicm 1283: /*
1284: * Set the background colour. This must come after the foreground as
1285: * tty_colour_fg() can call tty_reset().
1286: */
1287: if (!bg_default && (bg != tc->bg ||
1.62 nicm 1288: ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256))))
1.73 nicm 1289: tty_colours_bg(tty, gc);
1.1 nicm 1290: }
1291:
1292: void
1.74 nicm 1293: tty_colours_fg(struct tty *tty, const struct grid_cell *gc, u_char *attr)
1.1 nicm 1294: {
1.61 nicm 1295: struct grid_cell *tc = &tty->cell;
1296: u_char fg = gc->fg;
1.1 nicm 1297:
1.61 nicm 1298: /* Is this a 256-colour colour? */
1.1 nicm 1299: if (gc->flags & GRID_FLAG_FG256) {
1.61 nicm 1300: /* Try as 256 colours or translating to 88. */
1.1 nicm 1301: if (tty_try_256(tty, fg, "38") == 0)
1.61 nicm 1302: goto save_fg;
1.1 nicm 1303: if (tty_try_88(tty, fg, "38") == 0)
1.61 nicm 1304: goto save_fg;
1305:
1306: /* Translate to 16-colour palette, updating bold if needed. */
1.1 nicm 1307: fg = colour_256to16(fg);
1308: if (fg & 8) {
1309: fg &= 7;
1.61 nicm 1310: (*attr) |= GRID_ATTR_BRIGHT;
1311: } else
1312: tty_reset(tty); /* turn off bold */
1.1 nicm 1313: }
1314:
1.61 nicm 1315: /* Otherwise set the foreground colour. */
1316: tty_putcode1(tty, TTYC_SETAF, fg);
1317:
1318: save_fg:
1319: /* Save the new values in the terminal current cell. */
1320: tc->fg = fg;
1321: tc->flags &= ~GRID_FLAG_FG256;
1322: tc->flags |= gc->flags & GRID_FLAG_FG256;
1.1 nicm 1323: }
1324:
1325: void
1.73 nicm 1326: tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
1.1 nicm 1327: {
1.61 nicm 1328: struct grid_cell *tc = &tty->cell;
1329: u_char bg = gc->bg;
1.1 nicm 1330:
1.61 nicm 1331: /* Is this a 256-colour colour? */
1.1 nicm 1332: if (gc->flags & GRID_FLAG_BG256) {
1.61 nicm 1333: /* Try as 256 colours or translating to 88. */
1.1 nicm 1334: if (tty_try_256(tty, bg, "48") == 0)
1.61 nicm 1335: goto save_bg;
1.1 nicm 1336: if (tty_try_88(tty, bg, "48") == 0)
1.61 nicm 1337: goto save_bg;
1338:
1339: /*
1340: * Translate to 16-colour palette. Bold background doesn't
1341: * exist portably, so just discard the bold bit if set.
1342: */
1.1 nicm 1343: bg = colour_256to16(bg);
1344: if (bg & 8)
1345: bg &= 7;
1346: }
1347:
1.61 nicm 1348: /* Otherwise set the background colour. */
1349: tty_putcode1(tty, TTYC_SETAB, bg);
1350:
1351: save_bg:
1352: /* Save the new values in the terminal current cell. */
1353: tc->bg = bg;
1354: tc->flags &= ~GRID_FLAG_BG256;
1355: tc->flags |= gc->flags & GRID_FLAG_BG256;
1356: }
1357:
1358: int
1359: tty_try_256(struct tty *tty, u_char colour, const char *type)
1360: {
1361: char s[32];
1362:
1363: if (!(tty->term->flags & TERM_256COLOURS) &&
1364: !(tty->term_flags & TERM_256COLOURS))
1365: return (-1);
1366:
1367: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1368: tty_puts(tty, s);
1369: return (0);
1370: }
1371:
1372: int
1373: tty_try_88(struct tty *tty, u_char colour, const char *type)
1374: {
1375: char s[32];
1376:
1377: if (!(tty->term->flags & TERM_88COLOURS) &&
1378: !(tty->term_flags & TERM_88COLOURS))
1379: return (-1);
1380: colour = colour_256to88(colour);
1381:
1382: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1383: tty_puts(tty, s);
1384: return (0);
1.1 nicm 1385: }