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