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