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