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