Annotation of src/usr.bin/tmux/tty.c, Revision 1.22
1.22 ! nicm 1: /* $OpenBSD: tty.c,v 1.21 2009/08/11 20:29:05 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>
24: #include <string.h>
25: #include <termios.h>
26: #include <unistd.h>
27:
28: #include "tmux.h"
29:
30: void tty_fill_acs(struct tty *);
31:
32: void tty_raw(struct tty *, const char *);
33:
34: int tty_try_256(struct tty *, u_char, const char *);
35: int tty_try_88(struct tty *, u_char, const char *);
36:
37: void tty_attributes(struct tty *, const struct grid_cell *);
38: void tty_attributes_fg(struct tty *, const struct grid_cell *);
39: void tty_attributes_bg(struct tty *, const struct grid_cell *);
40:
1.14 nicm 41: void tty_redraw_region(struct tty *, struct tty_ctx *);
1.13 nicm 42: void tty_emulate_repeat(
43: struct tty *, enum tty_code_code, enum tty_code_code, u_int);
1.14 nicm 44: void tty_cell(struct tty *,
45: const struct grid_cell *, const struct grid_utf8 *);
1.1 nicm 46:
47: void
1.22 ! nicm 48: tty_init(struct tty *tty, int fd, char *path, char *term)
1.1 nicm 49: {
50: tty->path = xstrdup(path);
1.22 ! nicm 51: tty->fd = fd;
! 52:
1.9 nicm 53: if (term == NULL || *term == '\0')
1.1 nicm 54: tty->termname = xstrdup("unknown");
55: else
56: tty->termname = xstrdup(term);
57: tty->flags = 0;
58: tty->term_flags = 0;
59: }
60:
61: int
1.17 nicm 62: tty_open(struct tty *tty, const char *overrides, char **cause)
1.1 nicm 63: {
1.22 ! nicm 64: int mode;
1.1 nicm 65:
66: if (tty->fd == -1) {
1.22 ! nicm 67: tty->fd = open(tty->path, O_RDWR|O_NONBLOCK);
! 68: if (tty->fd == -1) {
! 69: xasprintf(cause, "%s: %s", tty->path, strerror(errno));
! 70: return (-1);
! 71: }
1.1 nicm 72: }
73:
74: if ((mode = fcntl(tty->fd, F_GETFL)) == -1)
75: fatal("fcntl failed");
76: if (fcntl(tty->fd, F_SETFL, mode|O_NONBLOCK) == -1)
1.9 nicm 77: fatal("fcntl failed");
1.1 nicm 78: if (fcntl(tty->fd, F_SETFD, FD_CLOEXEC) == -1)
79: fatal("fcntl failed");
80:
81: if (debug_level > 3)
82: tty->log_fd = open("tmux.out", O_WRONLY|O_CREAT|O_TRUNC, 0644);
83: else
84: tty->log_fd = -1;
85:
1.17 nicm 86: tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause);
1.21 nicm 87: if (tty->term == NULL) {
88: tty_close(tty);
89: return (-1);
90: }
91: tty->flags |= TTY_OPENED;
1.1 nicm 92:
93: tty->in = buffer_create(BUFSIZ);
94: tty->out = buffer_create(BUFSIZ);
95:
96: tty->flags &= TTY_UTF8;
97:
98: tty_start_tty(tty);
99:
100: tty_keys_init(tty);
101:
102: tty_fill_acs(tty);
103:
104: return (0);
105: }
106:
107: void
108: tty_start_tty(struct tty *tty)
109: {
110: struct termios tio;
111: int what;
112:
1.2 nicm 113: #if 0
1.1 nicm 114: tty_detect_utf8(tty);
1.2 nicm 115: #endif
1.1 nicm 116:
117: if (tcgetattr(tty->fd, &tty->tio) != 0)
118: fatal("tcgetattr failed");
119: memcpy(&tio, &tty->tio, sizeof tio);
120: tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP);
121: tio.c_iflag |= IGNBRK;
122: tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
123: tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|
124: ECHOPRT|ECHOKE|ECHOCTL|ISIG);
125: tio.c_cc[VMIN] = 1;
126: tio.c_cc[VTIME] = 0;
127: if (tcsetattr(tty->fd, TCSANOW, &tio) != 0)
128: fatal("tcsetattr failed");
129:
130: what = 0;
131: if (ioctl(tty->fd, TIOCFLUSH, &what) != 0)
132: fatal("ioctl(TIOCFLUSH)");
133:
1.10 nicm 134: tty_putcode(tty, TTYC_SMCUP);
1.1 nicm 135:
136: tty_putcode(tty, TTYC_SMKX);
137: tty_putcode(tty, TTYC_ENACS);
138: tty_putcode(tty, TTYC_CLEAR);
139:
140: tty_putcode(tty, TTYC_CNORM);
141: if (tty_term_has(tty->term, TTYC_KMOUS))
142: tty_puts(tty, "\033[?1000l");
143:
144: memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
145:
146: tty->cx = UINT_MAX;
147: tty->cy = UINT_MAX;
148:
149: tty->rlower = UINT_MAX;
150: tty->rupper = UINT_MAX;
151:
152: tty->mode = MODE_CURSOR;
1.20 nicm 153:
154: tty->flags |= TTY_STARTED;
1.1 nicm 155: }
156:
157: void
158: tty_stop_tty(struct tty *tty)
159: {
160: struct winsize ws;
161:
1.20 nicm 162: if (!(tty->flags & TTY_STARTED))
163: return;
164: tty->flags &= ~TTY_STARTED;
165:
1.1 nicm 166: /*
167: * Be flexible about error handling and try not kill the server just
168: * because the fd is invalid. Things like ssh -t can easily leave us
169: * with a dead tty.
170: */
171: if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1)
172: return;
173: if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1)
174: return;
175:
176: tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
177: tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
178: tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
179: tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
180: tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
181:
182: tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
183: if (tty_term_has(tty->term, TTYC_KMOUS))
184: tty_raw(tty, "\033[?1000l");
1.10 nicm 185:
186: tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
1.1 nicm 187: }
188:
1.2 nicm 189: #if 0
1.1 nicm 190: void
191: tty_detect_utf8(struct tty *tty)
192: {
193: struct pollfd pfd;
194: char buf[7];
195: size_t len;
196: ssize_t n;
197: int nfds;
198: struct termios tio, old_tio;
199: int what;
200:
201: if (tty->flags & TTY_UTF8)
202: return;
203:
204: /*
205: * If the terminal looks reasonably likely to support this, try to
206: * write a three-byte UTF-8 wide character to the terminal, then read
207: * the cursor position.
208: *
209: * XXX This entire function is a hack.
210: */
211:
212: /* Check if the terminal looks sort of vt100. */
213: if (strstr(tty_term_string(tty->term, TTYC_CLEAR), "[2J") == NULL ||
214: strstr(tty_term_string(tty->term, TTYC_CUP), "H") == NULL)
215: return;
216:
217: if (tcgetattr(tty->fd, &old_tio) != 0)
218: fatal("tcgetattr failed");
219: cfmakeraw(&tio);
220: if (tcsetattr(tty->fd, TCSANOW, &tio) != 0)
221: fatal("tcsetattr failed");
222:
223: what = 0;
224: if (ioctl(tty->fd, TIOCFLUSH, &what) != 0)
225: fatal("ioctl(TIOCFLUSH)");
226:
227: #define UTF8_TEST_DATA "\033[H\357\277\246\033[6n"
228: if (write(tty->fd, UTF8_TEST_DATA, (sizeof UTF8_TEST_DATA) - 1) == -1)
229: fatal("write failed");
230: #undef UTF8_TEST_DATA
231:
232: len = 0;
233: for (;;) {
234: pfd.fd = tty->fd;
235: pfd.events = POLLIN;
236:
237: nfds = poll(&pfd, 1, 500);
238: if (nfds == -1) {
239: if (errno == EAGAIN || errno == EINTR)
240: continue;
241: fatal("poll failed");
242: }
243: if (nfds == 0)
244: break;
245: if (pfd.revents & (POLLERR|POLLNVAL|POLLHUP))
246: break;
247: if (!(pfd.revents & POLLIN))
248: continue;
249:
250: if ((n = read(tty->fd, buf + len, 1)) != 1)
251: break;
252: buf[++len] = '\0';
253:
254: if (len == (sizeof buf) - 1) {
255: if (strcmp(buf, "\033[1;3R") == 0)
256: tty->flags |= TTY_UTF8;
257: break;
258: }
259: }
260:
261: if (tcsetattr(tty->fd, TCSANOW, &old_tio) != 0)
262: fatal("tcsetattr failed");
263: }
1.2 nicm 264: #endif
1.1 nicm 265:
266: void
267: tty_fill_acs(struct tty *tty)
268: {
269: const char *ptr;
270:
271: memset(tty->acs, 0, sizeof tty->acs);
272: if (!tty_term_has(tty->term, TTYC_ACSC))
273: return;
274:
275: ptr = tty_term_string(tty->term, TTYC_ACSC);
276: if (strlen(ptr) % 2 != 0)
277: return;
278: for (; *ptr != '\0'; ptr += 2)
279: tty->acs[(u_char) ptr[0]] = ptr[1];
280: }
281:
282: u_char
283: tty_get_acs(struct tty *tty, u_char ch)
284: {
285: if (tty->acs[ch] != '\0')
286: return (tty->acs[ch]);
287: return (ch);
288: }
289:
290: void
1.20 nicm 291: tty_close(struct tty *tty)
1.1 nicm 292: {
293: if (tty->log_fd != -1) {
294: close(tty->log_fd);
295: tty->log_fd = -1;
296: }
297:
1.20 nicm 298: tty_stop_tty(tty);
1.1 nicm 299:
1.21 nicm 300: if (tty->flags & TTY_OPENED) {
301: tty_term_free(tty->term);
302: tty_keys_free(tty);
303:
304: buffer_destroy(tty->in);
305: buffer_destroy(tty->out);
1.1 nicm 306:
1.21 nicm 307: tty->flags &= ~TTY_OPENED;
308: }
1.1 nicm 309:
1.21 nicm 310: if (tty->fd != -1) {
311: close(tty->fd);
312: tty->fd = -1;
313: }
1.1 nicm 314: }
315:
316: void
1.20 nicm 317: tty_free(struct tty *tty)
1.1 nicm 318: {
1.20 nicm 319: tty_close(tty);
1.1 nicm 320:
321: if (tty->path != NULL)
322: xfree(tty->path);
323: if (tty->termname != NULL)
324: xfree(tty->termname);
325: }
326:
327: void
328: tty_raw(struct tty *tty, const char *s)
329: {
330: write(tty->fd, s, strlen(s));
331: }
332:
333: void
334: tty_putcode(struct tty *tty, enum tty_code_code code)
335: {
336: tty_puts(tty, tty_term_string(tty->term, code));
337: }
338:
339: void
340: tty_putcode1(struct tty *tty, enum tty_code_code code, int a)
341: {
342: if (a < 0)
343: return;
344: tty_puts(tty, tty_term_string1(tty->term, code, a));
345: }
346:
347: void
348: tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b)
349: {
350: if (a < 0 || b < 0)
351: return;
352: tty_puts(tty, tty_term_string2(tty->term, code, a, b));
353: }
354:
355: void
356: tty_puts(struct tty *tty, const char *s)
357: {
358: if (*s == '\0')
359: return;
360: buffer_write(tty->out, s, strlen(s));
361:
362: if (tty->log_fd != -1)
363: write(tty->log_fd, s, strlen(s));
364: }
365:
366: void
367: tty_putc(struct tty *tty, u_char ch)
368: {
369: u_int sx;
370:
371: if (tty->cell.attr & GRID_ATTR_CHARSET)
372: ch = tty_get_acs(tty, ch);
373: buffer_write8(tty->out, ch);
374:
375: if (ch >= 0x20 && ch != 0x7f) {
376: sx = tty->sx;
377: if (tty->term->flags & TERM_EARLYWRAP)
378: sx--;
379:
380: if (tty->cx == sx) {
381: tty->cx = 0;
382: tty->cy++;
383: } else
384: tty->cx++;
385: }
386:
387: if (tty->log_fd != -1)
388: write(tty->log_fd, &ch, 1);
389: }
390:
391: void
1.5 nicm 392: tty_pututf8(struct tty *tty, const struct grid_utf8 *gu)
393: {
394: u_int i, width;
395:
396: for (i = 0; i < UTF8_SIZE; i++) {
397: if (gu->data[i] == 0xff)
398: break;
399: buffer_write8(tty->out, gu->data[i]);
400: if (tty->log_fd != -1)
401: write(tty->log_fd, &gu->data[i], 1);
402: }
403:
404: width = utf8_width(gu->data);
405: tty->cx += width;
406: }
407:
408: void
1.1 nicm 409: tty_set_title(struct tty *tty, const char *title)
410: {
411: if (strstr(tty->termname, "xterm") == NULL &&
412: strstr(tty->termname, "rxvt") == NULL &&
413: strcmp(tty->termname, "screen") != 0)
414: return;
415:
416: tty_puts(tty, "\033]0;");
417: tty_puts(tty, title);
418: tty_putc(tty, '\007');
419: }
420:
421: void
422: tty_update_mode(struct tty *tty, int mode)
423: {
424: int changed;
425:
426: if (tty->flags & TTY_NOCURSOR)
427: mode &= ~MODE_CURSOR;
428:
429: changed = mode ^ tty->mode;
430: if (changed & MODE_CURSOR) {
431: if (mode & MODE_CURSOR)
432: tty_putcode(tty, TTYC_CNORM);
433: else
434: tty_putcode(tty, TTYC_CIVIS);
435: }
436: if (changed & MODE_MOUSE) {
437: if (mode & MODE_MOUSE)
438: tty_puts(tty, "\033[?1000h");
439: else
440: tty_puts(tty, "\033[?1000l");
441: }
442: tty->mode = mode;
443: }
444:
445: void
446: tty_emulate_repeat(
447: struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n)
448: {
449: if (tty_term_has(tty->term, code))
450: tty_putcode1(tty, code, n);
451: else {
452: while (n-- > 0)
453: tty_putcode(tty, code1);
454: }
455: }
456:
457: /*
458: * Redraw scroll region using data from screen (already updated). Used when
459: * CSR not supported, or window is a pane that doesn't take up the full
460: * width of the terminal.
461: */
462: void
1.14 nicm 463: tty_redraw_region(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 464: {
1.14 nicm 465: struct window_pane *wp = ctx->wp;
466: struct screen *s = wp->screen;
467: u_int i;
1.1 nicm 468:
469: /*
470: * If region is >= 50% of the screen, just schedule a window redraw. In
471: * most cases, this is likely to be followed by some more scrolling -
472: * without this, the entire pane ends up being redrawn many times which
473: * can be much more data.
474: */
1.14 nicm 475: if (ctx->orupper - ctx->orlower >= screen_size_y(s) / 2) {
1.1 nicm 476: wp->flags |= PANE_REDRAW;
477: return;
478: }
479:
1.14 nicm 480: if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) {
481: for (i = ctx->ocy; i < screen_size_y(s); i++)
1.1 nicm 482: tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
483: } else {
1.14 nicm 484: for (i = ctx->orupper; i <= ctx->orlower; i++)
1.1 nicm 485: tty_draw_line(tty, s, i, wp->xoff, wp->yoff);
486: }
487: }
488:
489: void
490: tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy)
491: {
492: const struct grid_cell *gc;
1.16 nicm 493: struct grid_cell tmpgc;
1.1 nicm 494: const struct grid_utf8 *gu;
495: u_int i, sx;
496:
497: sx = screen_size_x(s);
1.19 nicm 498: if (sx > s->grid->linedata[s->grid->hsize + py].cellsize)
499: sx = s->grid->linedata[s->grid->hsize + py].cellsize;
1.1 nicm 500: if (sx > tty->sx)
501: sx = tty->sx;
502:
503: tty_cursor(tty, 0, py, ox, oy);
504: for (i = 0; i < sx; i++) {
505: gc = grid_view_peek_cell(s->grid, i, py);
506:
507: gu = NULL;
508: if (gc->flags & GRID_FLAG_UTF8)
509: gu = grid_view_peek_utf8(s->grid, i, py);
510:
511: if (screen_check_selection(s, i, py)) {
1.16 nicm 512: memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc);
513: tmpgc.data = gc->data;
514: tmpgc.flags = gc->flags;
515: tty_cell(tty, &tmpgc, gu);
1.1 nicm 516: } else
517: tty_cell(tty, gc, gu);
518: }
519:
520: if (sx >= tty->sx)
521: return;
522: tty_reset(tty);
523:
524: tty_cursor(tty, sx, py, ox, oy);
525: if (screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL))
526: tty_putcode(tty, TTYC_EL);
527: else {
528: for (i = sx; i < screen_size_x(s); i++)
529: tty_putc(tty, ' ');
1.15 nicm 530: }
531: }
532:
533: void
534: tty_write(void (*cmdfn)(struct tty *, struct tty_ctx *), struct tty_ctx *ctx)
535: {
536: struct window_pane *wp = ctx->wp;
537: struct client *c;
538: u_int i;
539:
540: if (wp == NULL)
541: return;
542:
543: if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
544: return;
545: if (wp->window->flags & WINDOW_HIDDEN || !window_pane_visible(wp))
546: return;
547:
548: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
549: c = ARRAY_ITEM(&clients, i);
550: if (c == NULL || c->session == NULL)
551: continue;
552: if (c->flags & CLIENT_SUSPENDED)
553: continue;
554:
555: if (c->session->curw->window == wp->window) {
556: if (c->tty.flags & TTY_FREEZE || c->tty.term == NULL)
557: continue;
558: tty_update_mode(&c->tty, c->tty.mode & ~MODE_CURSOR);
559: cmdfn(&c->tty, ctx);
560: }
1.1 nicm 561: }
562: }
563:
564: void
1.12 nicm 565: tty_cmd_insertcharacter(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 566: {
1.12 nicm 567: struct window_pane *wp = ctx->wp;
568: struct screen *s = wp->screen;
1.1 nicm 569:
570: if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
1.14 nicm 571: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 572: return;
573: }
574:
575: tty_reset(tty);
576:
1.14 nicm 577: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 578: if (tty_term_has(tty->term, TTYC_ICH) ||
579: tty_term_has(tty->term, TTYC_ICH1))
1.12 nicm 580: tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
1.1 nicm 581: else {
582: tty_putcode(tty, TTYC_SMIR);
1.12 nicm 583: while (ctx->num-- > 0)
1.1 nicm 584: tty_putc(tty, ' ');
585: tty_putcode(tty, TTYC_RMIR);
586: }
587: }
588:
589: void
1.12 nicm 590: tty_cmd_deletecharacter(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 591: {
1.12 nicm 592: struct window_pane *wp = ctx->wp;
593: struct screen *s = wp->screen;
1.1 nicm 594:
595: if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
1.14 nicm 596: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 597: return;
598: }
599:
600: tty_reset(tty);
601:
1.14 nicm 602: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.12 nicm 603: tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
1.1 nicm 604: }
605:
606: void
1.12 nicm 607: tty_cmd_insertline(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 608: {
1.12 nicm 609: struct window_pane *wp = ctx->wp;
610: struct screen *s = wp->screen;
1.1 nicm 611:
612: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
613: !tty_term_has(tty->term, TTYC_CSR)) {
1.14 nicm 614: tty_redraw_region(tty, ctx);
1.1 nicm 615: return;
616: }
617:
618: tty_reset(tty);
619:
1.14 nicm 620: tty_region(tty, ctx->orupper, ctx->orlower, wp->yoff);
1.1 nicm 621:
1.14 nicm 622: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.12 nicm 623: tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
1.1 nicm 624: }
625:
626: void
1.12 nicm 627: tty_cmd_deleteline(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 628: {
1.12 nicm 629: struct window_pane *wp = ctx->wp;
630: struct screen *s = wp->screen;
1.1 nicm 631:
632: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
633: !tty_term_has(tty->term, TTYC_CSR)) {
1.14 nicm 634: tty_redraw_region(tty, ctx);
1.1 nicm 635: return;
636: }
637:
638: tty_reset(tty);
639:
1.14 nicm 640: tty_region(tty, ctx->orupper, ctx->orlower, wp->yoff);
1.1 nicm 641:
1.14 nicm 642: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.12 nicm 643: tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
1.1 nicm 644: }
645:
646: void
1.12 nicm 647: tty_cmd_clearline(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 648: {
1.12 nicm 649: struct window_pane *wp = ctx->wp;
650: struct screen *s = wp->screen;
651: u_int i;
1.1 nicm 652:
653: tty_reset(tty);
654:
1.14 nicm 655: tty_cursor(tty, 0, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 656: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
657: tty_term_has(tty->term, TTYC_EL)) {
658: tty_putcode(tty, TTYC_EL);
659: } else {
660: for (i = 0; i < screen_size_x(s); i++)
661: tty_putc(tty, ' ');
662: }
663: }
664:
665: void
1.12 nicm 666: tty_cmd_clearendofline(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 667: {
1.12 nicm 668: struct window_pane *wp = ctx->wp;
669: struct screen *s = wp->screen;
670: u_int i;
1.1 nicm 671:
672: tty_reset(tty);
673:
1.14 nicm 674: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 675: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
676: tty_term_has(tty->term, TTYC_EL))
677: tty_putcode(tty, TTYC_EL);
678: else {
1.14 nicm 679: for (i = ctx->ocx; i < screen_size_x(s); i++)
1.1 nicm 680: tty_putc(tty, ' ');
681: }
682: }
683:
684: void
1.12 nicm 685: tty_cmd_clearstartofline(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 686: {
1.12 nicm 687: struct window_pane *wp = ctx->wp;
688: u_int i;
1.1 nicm 689:
690: tty_reset(tty);
691:
692: if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) {
1.14 nicm 693: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 694: tty_putcode(tty, TTYC_EL1);
695: } else {
1.14 nicm 696: tty_cursor(tty, 0, ctx->ocy, wp->xoff, wp->yoff);
697: for (i = 0; i < ctx->ocx + 1; i++)
1.1 nicm 698: tty_putc(tty, ' ');
699: }
700: }
701:
702: void
1.12 nicm 703: tty_cmd_reverseindex(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 704: {
1.12 nicm 705: struct window_pane *wp = ctx->wp;
706: struct screen *s = wp->screen;
1.1 nicm 707:
708: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
709: !tty_term_has(tty->term, TTYC_CSR)) {
1.14 nicm 710: tty_redraw_region(tty, ctx);
1.1 nicm 711: return;
712: }
713:
714: tty_reset(tty);
715:
1.14 nicm 716: tty_region(tty, ctx->orupper, ctx->orlower, wp->yoff);
1.1 nicm 717:
1.14 nicm 718: if (ctx->ocy == ctx->orupper) {
719: tty_cursor(tty, ctx->ocx, ctx->orupper, wp->xoff, wp->yoff);
1.1 nicm 720: tty_putcode(tty, TTYC_RI);
721: }
722: }
723:
724: void
1.12 nicm 725: tty_cmd_linefeed(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 726: {
1.12 nicm 727: struct window_pane *wp = ctx->wp;
728: struct screen *s = wp->screen;
1.1 nicm 729:
730: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
731: !tty_term_has(tty->term, TTYC_CSR)) {
1.14 nicm 732: tty_redraw_region(tty, ctx);
1.1 nicm 733: return;
734: }
735:
736: tty_reset(tty);
737:
1.14 nicm 738: tty_region(tty, ctx->orupper, ctx->orlower, wp->yoff);
1.1 nicm 739:
1.14 nicm 740: if (ctx->ocy == ctx->orlower) {
741: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 742: tty_putc(tty, '\n');
743: }
744: }
745:
746: void
1.12 nicm 747: tty_cmd_clearendofscreen(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 748: {
1.12 nicm 749: struct window_pane *wp = ctx->wp;
750: struct screen *s = wp->screen;
751: u_int i, j;
1.1 nicm 752:
753: tty_reset(tty);
754:
755: tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
1.14 nicm 756: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 757: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
758: tty_term_has(tty->term, TTYC_EL)) {
759: tty_putcode(tty, TTYC_EL);
1.14 nicm 760: if (ctx->ocy != screen_size_y(s) - 1) {
761: tty_cursor(tty, 0, ctx->ocy + 1, wp->xoff, wp->yoff);
762: for (i = ctx->ocy + 1; i < screen_size_y(s); i++) {
1.1 nicm 763: tty_putcode(tty, TTYC_EL);
764: if (i == screen_size_y(s) - 1)
765: continue;
766: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
767: tty->cy++;
768: }
769: }
770: } else {
1.14 nicm 771: for (i = ctx->ocx; i < screen_size_x(s); i++)
1.1 nicm 772: tty_putc(tty, ' ');
1.14 nicm 773: for (j = ctx->ocy; j < screen_size_y(s); j++) {
1.1 nicm 774: tty_cursor(tty, 0, j, wp->xoff, wp->yoff);
775: for (i = 0; i < screen_size_x(s); i++)
776: tty_putc(tty, ' ');
777: }
778: }
779: }
780:
781: void
1.12 nicm 782: tty_cmd_clearstartofscreen(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 783: {
1.12 nicm 784: struct window_pane *wp = ctx->wp;
785: struct screen *s = wp->screen;
786: u_int i, j;
1.1 nicm 787:
788: tty_reset(tty);
789:
790: tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
791: tty_cursor(tty, 0, 0, wp->xoff, wp->yoff);
792: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
793: tty_term_has(tty->term, TTYC_EL)) {
1.14 nicm 794: for (i = 0; i < ctx->ocy; i++) {
1.1 nicm 795: tty_putcode(tty, TTYC_EL);
796: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
797: tty->cy++;
798: }
799: } else {
1.14 nicm 800: for (j = 0; j < ctx->ocy; j++) {
1.1 nicm 801: tty_cursor(tty, 0, j, wp->xoff, wp->yoff);
802: for (i = 0; i < screen_size_x(s); i++)
803: tty_putc(tty, ' ');
804: }
805: }
1.14 nicm 806: for (i = 0; i <= ctx->ocx; i++)
1.1 nicm 807: tty_putc(tty, ' ');
808: }
809:
810: void
1.12 nicm 811: tty_cmd_clearscreen(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 812: {
1.12 nicm 813: struct window_pane *wp = ctx->wp;
814: struct screen *s = wp->screen;
815: u_int i, j;
1.1 nicm 816:
817: tty_reset(tty);
818:
819: tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
820: tty_cursor(tty, 0, 0, wp->xoff, wp->yoff);
821: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
822: tty_term_has(tty->term, TTYC_EL)) {
823: for (i = 0; i < screen_size_y(s); i++) {
824: tty_putcode(tty, TTYC_EL);
825: if (i != screen_size_y(s) - 1) {
826: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
827: tty->cy++;
828: }
829: }
830: } else {
831: for (j = 0; j < screen_size_y(s); j++) {
832: tty_cursor(tty, 0, j, wp->xoff, wp->yoff);
833: for (i = 0; i < screen_size_x(s); i++)
834: tty_putc(tty, ' ');
835: }
1.4 nicm 836: }
837: }
838:
839: void
1.12 nicm 840: tty_cmd_alignmenttest(struct tty *tty, struct tty_ctx *ctx)
1.4 nicm 841: {
1.12 nicm 842: struct window_pane *wp = ctx->wp;
843: struct screen *s = wp->screen;
844: u_int i, j;
1.4 nicm 845:
846: tty_reset(tty);
847:
848: tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
849:
850: for (j = 0; j < screen_size_y(s); j++) {
851: tty_cursor(tty, 0, j, wp->xoff, wp->yoff);
852: for (i = 0; i < screen_size_x(s); i++)
853: tty_putc(tty, 'E');
1.1 nicm 854: }
855: }
856:
857: void
1.12 nicm 858: tty_cmd_cell(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 859: {
1.12 nicm 860: struct window_pane *wp = ctx->wp;
1.1 nicm 861:
1.14 nicm 862: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 863:
1.12 nicm 864: tty_cell(tty, ctx->cell, ctx->utf8);
1.1 nicm 865: }
866:
867: void
1.12 nicm 868: tty_cmd_utf8character(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 869: {
1.12 nicm 870: u_char *ptr = ctx->ptr;
1.11 nicm 871: size_t i;
1.1 nicm 872:
1.11 nicm 873: for (i = 0; i < UTF8_SIZE; i++) {
1.12 nicm 874: if (ptr[i] == 0xff)
1.11 nicm 875: break;
1.12 nicm 876: tty_putc(tty, ptr[i]);
1.11 nicm 877: }
1.1 nicm 878: }
879:
880: void
881: tty_cell(
882: struct tty *tty, const struct grid_cell *gc, const struct grid_utf8 *gu)
883: {
884: u_int i;
885:
886: /* Skip last character if terminal is stupid. */
887: if (tty->term->flags & TERM_EARLYWRAP &&
888: tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1)
889: return;
890:
891: /* If this is a padding character, do nothing. */
892: if (gc->flags & GRID_FLAG_PADDING)
893: return;
894:
895: /* Set the attributes. */
896: tty_attributes(tty, gc);
897:
898: /* If not UTF-8, write directly. */
899: if (!(gc->flags & GRID_FLAG_UTF8)) {
900: if (gc->data < 0x20 || gc->data == 0x7f)
901: return;
902: tty_putc(tty, gc->data);
903: return;
904: }
905:
906: /* If the terminal doesn't support UTF-8, write underscores. */
907: if (!(tty->flags & TTY_UTF8)) {
908: for (i = 0; i < gu->width; i++)
909: tty_putc(tty, '_');
910: return;
911: }
912:
913: /* Otherwise, write UTF-8. */
1.5 nicm 914: tty_pututf8(tty, gu);
1.1 nicm 915: }
916:
917: void
918: tty_reset(struct tty *tty)
919: {
920: struct grid_cell *gc = &tty->cell;
921:
922: if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0)
923: return;
924:
925: if (tty_term_has(tty->term, TTYC_RMACS) && gc->attr & GRID_ATTR_CHARSET)
926: tty_putcode(tty, TTYC_RMACS);
927: tty_putcode(tty, TTYC_SGR0);
928: memcpy(gc, &grid_default_cell, sizeof *gc);
929: }
930:
931: void
932: tty_region(struct tty *tty, u_int rupper, u_int rlower, u_int oy)
933: {
934: if (!tty_term_has(tty->term, TTYC_CSR))
935: return;
936: if (tty->rlower != oy + rlower || tty->rupper != oy + rupper) {
937: tty->rlower = oy + rlower;
938: tty->rupper = oy + rupper;
939: tty->cx = 0;
940: tty->cy = 0;
941: tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
942: }
943: }
944:
945: void
946: tty_cursor(struct tty *tty, u_int cx, u_int cy, u_int ox, u_int oy)
947: {
948: if (ox + cx == 0 && tty->cx != 0 && tty->cy == oy + cy) {
949: tty->cx = 0;
950: tty_putc(tty, '\r');
951: } else if (tty->cx != ox + cx || tty->cy != oy + cy) {
952: tty->cx = ox + cx;
953: tty->cy = oy + cy;
954: tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx);
955: }
956: }
957:
958: void
959: tty_attributes(struct tty *tty, const struct grid_cell *gc)
960: {
961: struct grid_cell *tc = &tty->cell;
962: u_char changed;
1.18 nicm 963: u_int fg, bg, attr;
964:
965: /*
966: * If no setab, try to use the reverse attribute as a best-effort for a
967: * non-default background. This is a bit of a hack but it doesn't do
968: * any serious harm and makes a couple of applications happier.
969: */
970: fg = gc->fg; bg = gc->bg; attr = gc->attr;
971: if (!tty_term_has(tty->term, TTYC_SETAB)) {
972: if (attr & GRID_ATTR_REVERSE) {
973: if (fg != 7 && fg != 8)
974: attr &= ~GRID_ATTR_REVERSE;
975: } else {
976: if (bg != 0 && bg != 8)
977: attr |= GRID_ATTR_REVERSE;
978: }
979: }
1.1 nicm 980:
981: /* If any bits are being cleared, reset everything. */
1.18 nicm 982: if (tc->attr & ~attr)
1.1 nicm 983: tty_reset(tty);
984:
985: /* Filter out attribute bits already set. */
1.18 nicm 986: changed = attr & ~tc->attr;
987: tc->attr = attr;
1.1 nicm 988:
989: /* Set the attributes. */
990: if (changed & GRID_ATTR_BRIGHT)
991: tty_putcode(tty, TTYC_BOLD);
992: if (changed & GRID_ATTR_DIM)
993: tty_putcode(tty, TTYC_DIM);
994: if (changed & GRID_ATTR_ITALICS)
995: tty_putcode(tty, TTYC_SMSO);
996: if (changed & GRID_ATTR_UNDERSCORE)
997: tty_putcode(tty, TTYC_SMUL);
998: if (changed & GRID_ATTR_BLINK)
999: tty_putcode(tty, TTYC_BLINK);
1000: if (changed & GRID_ATTR_REVERSE) {
1001: if (tty_term_has(tty->term, TTYC_REV))
1002: tty_putcode(tty, TTYC_REV);
1003: else if (tty_term_has(tty->term, TTYC_SMSO))
1004: tty_putcode(tty, TTYC_SMSO);
1005: }
1006: if (changed & GRID_ATTR_HIDDEN)
1007: tty_putcode(tty, TTYC_INVIS);
1008: if (changed & GRID_ATTR_CHARSET)
1009: tty_putcode(tty, TTYC_SMACS);
1010:
1011: /* Set foreground colour. */
1012: if (fg != tc->fg ||
1013: (gc->flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)) {
1014: tty_attributes_fg(tty, gc);
1015: tc->fg = fg;
1.8 nicm 1016: tc->flags &= ~GRID_FLAG_FG256;
1017: tc->flags |= gc->flags & GRID_FLAG_FG256;
1.1 nicm 1018: }
1019:
1020: /* Set background colour. */
1021: if (bg != tc->bg ||
1022: (gc->flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)) {
1023: tty_attributes_bg(tty, gc);
1024: tc->bg = bg;
1.8 nicm 1025: tc->flags &= ~GRID_FLAG_BG256;
1026: tc->flags |= gc->flags & GRID_FLAG_BG256;
1.1 nicm 1027: }
1028: }
1029:
1030: int
1031: tty_try_256(struct tty *tty, u_char colour, const char *type)
1032: {
1033: char s[32];
1034:
1035: if (!(tty->term->flags & TERM_256COLOURS) &&
1036: !(tty->term_flags & TERM_256COLOURS))
1037: return (-1);
1038:
1039: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1040: tty_puts(tty, s);
1041: return (0);
1042: }
1043:
1044: int
1045: tty_try_88(struct tty *tty, u_char colour, const char *type)
1046: {
1047: char s[32];
1048:
1049: if (!(tty->term->flags & TERM_88COLOURS) &&
1050: !(tty->term_flags & TERM_88COLOURS))
1051: return (-1);
1052: colour = colour_256to88(colour);
1053:
1054: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1055: tty_puts(tty, s);
1056: return (0);
1057: }
1058:
1059: void
1060: tty_attributes_fg(struct tty *tty, const struct grid_cell *gc)
1061: {
1062: u_char fg;
1063:
1064: fg = gc->fg;
1065: if (gc->flags & GRID_FLAG_FG256) {
1066: if (tty_try_256(tty, fg, "38") == 0)
1067: return;
1068: if (tty_try_88(tty, fg, "38") == 0)
1069: return;
1070: fg = colour_256to16(fg);
1071: if (fg & 8) {
1072: fg &= 7;
1073: tty_putcode(tty, TTYC_BOLD);
1074: tty->cell.attr |= GRID_ATTR_BRIGHT;
1075: } else if (tty->cell.attr & GRID_ATTR_BRIGHT)
1076: tty_reset(tty);
1077: }
1078:
1079: if (fg == 8 &&
1080: !(tty->term->flags & TERM_HASDEFAULTS) &&
1081: !(tty->term_flags & TERM_HASDEFAULTS))
1082: fg = 7;
1083: if (fg == 8)
1084: tty_puts(tty, "\033[39m");
1085: else
1086: tty_putcode1(tty, TTYC_SETAF, fg);
1087: }
1088:
1089: void
1090: tty_attributes_bg(struct tty *tty, const struct grid_cell *gc)
1091: {
1092: u_char bg;
1093:
1094: bg = gc->bg;
1095: if (gc->flags & GRID_FLAG_BG256) {
1096: if (tty_try_256(tty, bg, "48") == 0)
1097: return;
1098: if (tty_try_88(tty, bg, "48") == 0)
1099: return;
1100: bg = colour_256to16(bg);
1101: if (bg & 8)
1102: bg &= 7;
1103: }
1104:
1105: if (bg == 8 &&
1106: !(tty->term->flags & TERM_HASDEFAULTS) &&
1107: !(tty->term_flags & TERM_HASDEFAULTS))
1108: bg = 0;
1109: if (bg == 8)
1110: tty_puts(tty, "\033[49m");
1111: else
1112: tty_putcode1(tty, TTYC_SETAB, bg);
1113: }