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