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