Annotation of src/usr.bin/tmux/tty.c, Revision 1.15
1.15 ! nicm 1: /* $OpenBSD: tty.c,v 1.14 2009/07/22 20:53:38 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, ' ');
1.15 ! nicm 517: }
! 518: }
! 519:
! 520: void
! 521: tty_write(void (*cmdfn)(struct tty *, struct tty_ctx *), struct tty_ctx *ctx)
! 522: {
! 523: struct window_pane *wp = ctx->wp;
! 524: struct client *c;
! 525: u_int i;
! 526:
! 527: if (wp == NULL)
! 528: return;
! 529:
! 530: if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
! 531: return;
! 532: if (wp->window->flags & WINDOW_HIDDEN || !window_pane_visible(wp))
! 533: return;
! 534:
! 535: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
! 536: c = ARRAY_ITEM(&clients, i);
! 537: if (c == NULL || c->session == NULL)
! 538: continue;
! 539: if (c->flags & CLIENT_SUSPENDED)
! 540: continue;
! 541:
! 542: if (c->session->curw->window == wp->window) {
! 543: if (c->tty.flags & TTY_FREEZE || c->tty.term == NULL)
! 544: continue;
! 545: tty_update_mode(&c->tty, c->tty.mode & ~MODE_CURSOR);
! 546: cmdfn(&c->tty, ctx);
! 547: }
1.1 nicm 548: }
549: }
550:
551: void
1.12 nicm 552: tty_cmd_insertcharacter(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 553: {
1.12 nicm 554: struct window_pane *wp = ctx->wp;
555: struct screen *s = wp->screen;
1.1 nicm 556:
557: if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
1.14 nicm 558: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 559: return;
560: }
561:
562: tty_reset(tty);
563:
1.14 nicm 564: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 565: if (tty_term_has(tty->term, TTYC_ICH) ||
566: tty_term_has(tty->term, TTYC_ICH1))
1.12 nicm 567: tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
1.1 nicm 568: else {
569: tty_putcode(tty, TTYC_SMIR);
1.12 nicm 570: while (ctx->num-- > 0)
1.1 nicm 571: tty_putc(tty, ' ');
572: tty_putcode(tty, TTYC_RMIR);
573: }
574: }
575:
576: void
1.12 nicm 577: tty_cmd_deletecharacter(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 578: {
1.12 nicm 579: struct window_pane *wp = ctx->wp;
580: struct screen *s = wp->screen;
1.1 nicm 581:
582: if (wp->xoff != 0 || screen_size_x(s) < tty->sx) {
1.14 nicm 583: tty_draw_line(tty, wp->screen, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 584: return;
585: }
586:
587: tty_reset(tty);
588:
1.14 nicm 589: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.12 nicm 590: tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
1.1 nicm 591: }
592:
593: void
1.12 nicm 594: tty_cmd_insertline(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 595: {
1.12 nicm 596: struct window_pane *wp = ctx->wp;
597: struct screen *s = wp->screen;
1.1 nicm 598:
599: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
600: !tty_term_has(tty->term, TTYC_CSR)) {
1.14 nicm 601: tty_redraw_region(tty, ctx);
1.1 nicm 602: return;
603: }
604:
605: tty_reset(tty);
606:
1.14 nicm 607: tty_region(tty, ctx->orupper, ctx->orlower, wp->yoff);
1.1 nicm 608:
1.14 nicm 609: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.12 nicm 610: tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
1.1 nicm 611: }
612:
613: void
1.12 nicm 614: tty_cmd_deleteline(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 615: {
1.12 nicm 616: struct window_pane *wp = ctx->wp;
617: struct screen *s = wp->screen;
1.1 nicm 618:
619: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
620: !tty_term_has(tty->term, TTYC_CSR)) {
1.14 nicm 621: tty_redraw_region(tty, ctx);
1.1 nicm 622: return;
623: }
624:
625: tty_reset(tty);
626:
1.14 nicm 627: tty_region(tty, ctx->orupper, ctx->orlower, wp->yoff);
1.1 nicm 628:
1.14 nicm 629: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.12 nicm 630: tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
1.1 nicm 631: }
632:
633: void
1.12 nicm 634: tty_cmd_clearline(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 635: {
1.12 nicm 636: struct window_pane *wp = ctx->wp;
637: struct screen *s = wp->screen;
638: u_int i;
1.1 nicm 639:
640: tty_reset(tty);
641:
1.14 nicm 642: tty_cursor(tty, 0, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 643: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
644: tty_term_has(tty->term, TTYC_EL)) {
645: tty_putcode(tty, TTYC_EL);
646: } else {
647: for (i = 0; i < screen_size_x(s); i++)
648: tty_putc(tty, ' ');
649: }
650: }
651:
652: void
1.12 nicm 653: tty_cmd_clearendofline(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 654: {
1.12 nicm 655: struct window_pane *wp = ctx->wp;
656: struct screen *s = wp->screen;
657: u_int i;
1.1 nicm 658:
659: tty_reset(tty);
660:
1.14 nicm 661: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 662: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
663: tty_term_has(tty->term, TTYC_EL))
664: tty_putcode(tty, TTYC_EL);
665: else {
1.14 nicm 666: for (i = ctx->ocx; i < screen_size_x(s); i++)
1.1 nicm 667: tty_putc(tty, ' ');
668: }
669: }
670:
671: void
1.12 nicm 672: tty_cmd_clearstartofline(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 673: {
1.12 nicm 674: struct window_pane *wp = ctx->wp;
675: u_int i;
1.1 nicm 676:
677: tty_reset(tty);
678:
679: if (wp->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) {
1.14 nicm 680: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 681: tty_putcode(tty, TTYC_EL1);
682: } else {
1.14 nicm 683: tty_cursor(tty, 0, ctx->ocy, wp->xoff, wp->yoff);
684: for (i = 0; i < ctx->ocx + 1; i++)
1.1 nicm 685: tty_putc(tty, ' ');
686: }
687: }
688:
689: void
1.12 nicm 690: tty_cmd_reverseindex(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 691: {
1.12 nicm 692: struct window_pane *wp = ctx->wp;
693: struct screen *s = wp->screen;
1.1 nicm 694:
695: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
696: !tty_term_has(tty->term, TTYC_CSR)) {
1.14 nicm 697: tty_redraw_region(tty, ctx);
1.1 nicm 698: return;
699: }
700:
701: tty_reset(tty);
702:
1.14 nicm 703: tty_region(tty, ctx->orupper, ctx->orlower, wp->yoff);
1.1 nicm 704:
1.14 nicm 705: if (ctx->ocy == ctx->orupper) {
706: tty_cursor(tty, ctx->ocx, ctx->orupper, wp->xoff, wp->yoff);
1.1 nicm 707: tty_putcode(tty, TTYC_RI);
708: }
709: }
710:
711: void
1.12 nicm 712: tty_cmd_linefeed(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 713: {
1.12 nicm 714: struct window_pane *wp = ctx->wp;
715: struct screen *s = wp->screen;
1.1 nicm 716:
717: if (wp->xoff != 0 || screen_size_x(s) < tty->sx ||
718: !tty_term_has(tty->term, TTYC_CSR)) {
1.14 nicm 719: tty_redraw_region(tty, ctx);
1.1 nicm 720: return;
721: }
722:
723: tty_reset(tty);
724:
1.14 nicm 725: tty_region(tty, ctx->orupper, ctx->orlower, wp->yoff);
1.1 nicm 726:
1.14 nicm 727: if (ctx->ocy == ctx->orlower) {
728: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 729: tty_putc(tty, '\n');
730: }
731: }
732:
733: void
1.12 nicm 734: tty_cmd_clearendofscreen(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 735: {
1.12 nicm 736: struct window_pane *wp = ctx->wp;
737: struct screen *s = wp->screen;
738: u_int i, j;
1.1 nicm 739:
740: tty_reset(tty);
741:
742: tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
1.14 nicm 743: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 744: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
745: tty_term_has(tty->term, TTYC_EL)) {
746: tty_putcode(tty, TTYC_EL);
1.14 nicm 747: if (ctx->ocy != screen_size_y(s) - 1) {
748: tty_cursor(tty, 0, ctx->ocy + 1, wp->xoff, wp->yoff);
749: for (i = ctx->ocy + 1; i < screen_size_y(s); i++) {
1.1 nicm 750: tty_putcode(tty, TTYC_EL);
751: if (i == screen_size_y(s) - 1)
752: continue;
753: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
754: tty->cy++;
755: }
756: }
757: } else {
1.14 nicm 758: for (i = ctx->ocx; i < screen_size_x(s); i++)
1.1 nicm 759: tty_putc(tty, ' ');
1.14 nicm 760: for (j = ctx->ocy; j < screen_size_y(s); j++) {
1.1 nicm 761: tty_cursor(tty, 0, j, wp->xoff, wp->yoff);
762: for (i = 0; i < screen_size_x(s); i++)
763: tty_putc(tty, ' ');
764: }
765: }
766: }
767:
768: void
1.12 nicm 769: tty_cmd_clearstartofscreen(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 770: {
1.12 nicm 771: struct window_pane *wp = ctx->wp;
772: struct screen *s = wp->screen;
773: u_int i, j;
1.1 nicm 774:
775: tty_reset(tty);
776:
777: tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
778: tty_cursor(tty, 0, 0, wp->xoff, wp->yoff);
779: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
780: tty_term_has(tty->term, TTYC_EL)) {
1.14 nicm 781: for (i = 0; i < ctx->ocy; i++) {
1.1 nicm 782: tty_putcode(tty, TTYC_EL);
783: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
784: tty->cy++;
785: }
786: } else {
1.14 nicm 787: for (j = 0; j < ctx->ocy; j++) {
1.1 nicm 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: }
792: }
1.14 nicm 793: for (i = 0; i <= ctx->ocx; i++)
1.1 nicm 794: tty_putc(tty, ' ');
795: }
796:
797: void
1.12 nicm 798: tty_cmd_clearscreen(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 799: {
1.12 nicm 800: struct window_pane *wp = ctx->wp;
801: struct screen *s = wp->screen;
802: u_int i, j;
1.1 nicm 803:
804: tty_reset(tty);
805:
806: tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
807: tty_cursor(tty, 0, 0, wp->xoff, wp->yoff);
808: if (wp->xoff == 0 && screen_size_x(s) >= tty->sx &&
809: tty_term_has(tty->term, TTYC_EL)) {
810: for (i = 0; i < screen_size_y(s); i++) {
811: tty_putcode(tty, TTYC_EL);
812: if (i != screen_size_y(s) - 1) {
813: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
814: tty->cy++;
815: }
816: }
817: } else {
818: for (j = 0; j < screen_size_y(s); j++) {
819: tty_cursor(tty, 0, j, wp->xoff, wp->yoff);
820: for (i = 0; i < screen_size_x(s); i++)
821: tty_putc(tty, ' ');
822: }
1.4 nicm 823: }
824: }
825:
826: void
1.12 nicm 827: tty_cmd_alignmenttest(struct tty *tty, struct tty_ctx *ctx)
1.4 nicm 828: {
1.12 nicm 829: struct window_pane *wp = ctx->wp;
830: struct screen *s = wp->screen;
831: u_int i, j;
1.4 nicm 832:
833: tty_reset(tty);
834:
835: tty_region(tty, 0, screen_size_y(s) - 1, wp->yoff);
836:
837: for (j = 0; j < screen_size_y(s); j++) {
838: tty_cursor(tty, 0, j, wp->xoff, wp->yoff);
839: for (i = 0; i < screen_size_x(s); i++)
840: tty_putc(tty, 'E');
1.1 nicm 841: }
842: }
843:
844: void
1.12 nicm 845: tty_cmd_cell(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 846: {
1.12 nicm 847: struct window_pane *wp = ctx->wp;
1.1 nicm 848:
1.14 nicm 849: tty_cursor(tty, ctx->ocx, ctx->ocy, wp->xoff, wp->yoff);
1.1 nicm 850:
1.12 nicm 851: tty_cell(tty, ctx->cell, ctx->utf8);
1.1 nicm 852: }
853:
854: void
1.12 nicm 855: tty_cmd_utf8character(struct tty *tty, struct tty_ctx *ctx)
1.1 nicm 856: {
1.12 nicm 857: u_char *ptr = ctx->ptr;
1.11 nicm 858: size_t i;
1.1 nicm 859:
1.11 nicm 860: for (i = 0; i < UTF8_SIZE; i++) {
1.12 nicm 861: if (ptr[i] == 0xff)
1.11 nicm 862: break;
1.12 nicm 863: tty_putc(tty, ptr[i]);
1.11 nicm 864: }
1.1 nicm 865: }
866:
867: void
868: tty_cell(
869: struct tty *tty, const struct grid_cell *gc, const struct grid_utf8 *gu)
870: {
871: u_int i;
872:
873: /* Skip last character if terminal is stupid. */
874: if (tty->term->flags & TERM_EARLYWRAP &&
875: tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1)
876: return;
877:
878: /* If this is a padding character, do nothing. */
879: if (gc->flags & GRID_FLAG_PADDING)
880: return;
881:
882: /* Set the attributes. */
883: tty_attributes(tty, gc);
884:
885: /* If not UTF-8, write directly. */
886: if (!(gc->flags & GRID_FLAG_UTF8)) {
887: if (gc->data < 0x20 || gc->data == 0x7f)
888: return;
889: tty_putc(tty, gc->data);
890: return;
891: }
892:
893: /* If the terminal doesn't support UTF-8, write underscores. */
894: if (!(tty->flags & TTY_UTF8)) {
895: for (i = 0; i < gu->width; i++)
896: tty_putc(tty, '_');
897: return;
898: }
899:
900: /* Otherwise, write UTF-8. */
1.5 nicm 901: tty_pututf8(tty, gu);
1.1 nicm 902: }
903:
904: void
905: tty_reset(struct tty *tty)
906: {
907: struct grid_cell *gc = &tty->cell;
908:
909: if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0)
910: return;
911:
912: if (tty_term_has(tty->term, TTYC_RMACS) && gc->attr & GRID_ATTR_CHARSET)
913: tty_putcode(tty, TTYC_RMACS);
914: tty_putcode(tty, TTYC_SGR0);
915: memcpy(gc, &grid_default_cell, sizeof *gc);
916: }
917:
918: void
919: tty_region(struct tty *tty, u_int rupper, u_int rlower, u_int oy)
920: {
921: if (!tty_term_has(tty->term, TTYC_CSR))
922: return;
923: if (tty->rlower != oy + rlower || tty->rupper != oy + rupper) {
924: tty->rlower = oy + rlower;
925: tty->rupper = oy + rupper;
926: tty->cx = 0;
927: tty->cy = 0;
928: tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
929: }
930: }
931:
932: void
933: tty_cursor(struct tty *tty, u_int cx, u_int cy, u_int ox, u_int oy)
934: {
935: if (ox + cx == 0 && tty->cx != 0 && tty->cy == oy + cy) {
936: tty->cx = 0;
937: tty_putc(tty, '\r');
938: } else if (tty->cx != ox + cx || tty->cy != oy + cy) {
939: tty->cx = ox + cx;
940: tty->cy = oy + cy;
941: tty_putcode2(tty, TTYC_CUP, tty->cy, tty->cx);
942: }
943: }
944:
945: void
946: tty_attributes(struct tty *tty, const struct grid_cell *gc)
947: {
948: struct grid_cell *tc = &tty->cell;
949: u_char changed;
950: u_int fg, bg;
951:
952: /* If any bits are being cleared, reset everything. */
953: if (tc->attr & ~gc->attr)
954: tty_reset(tty);
955:
956: /* Filter out attribute bits already set. */
957: changed = gc->attr & ~tc->attr;
958: tc->attr = gc->attr;
959:
960: /* Set the attributes. */
961: fg = gc->fg;
962: bg = gc->bg;
963: if (changed & GRID_ATTR_BRIGHT)
964: tty_putcode(tty, TTYC_BOLD);
965: if (changed & GRID_ATTR_DIM)
966: tty_putcode(tty, TTYC_DIM);
967: if (changed & GRID_ATTR_ITALICS)
968: tty_putcode(tty, TTYC_SMSO);
969: if (changed & GRID_ATTR_UNDERSCORE)
970: tty_putcode(tty, TTYC_SMUL);
971: if (changed & GRID_ATTR_BLINK)
972: tty_putcode(tty, TTYC_BLINK);
973: if (changed & GRID_ATTR_REVERSE) {
974: if (tty_term_has(tty->term, TTYC_REV))
975: tty_putcode(tty, TTYC_REV);
976: else if (tty_term_has(tty->term, TTYC_SMSO))
977: tty_putcode(tty, TTYC_SMSO);
978: }
979: if (changed & GRID_ATTR_HIDDEN)
980: tty_putcode(tty, TTYC_INVIS);
981: if (changed & GRID_ATTR_CHARSET)
982: tty_putcode(tty, TTYC_SMACS);
983:
984: /* Set foreground colour. */
985: if (fg != tc->fg ||
986: (gc->flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256)) {
987: tty_attributes_fg(tty, gc);
988: tc->fg = fg;
1.8 nicm 989: tc->flags &= ~GRID_FLAG_FG256;
990: tc->flags |= gc->flags & GRID_FLAG_FG256;
1.1 nicm 991: }
992:
993: /* Set background colour. */
994: if (bg != tc->bg ||
995: (gc->flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256)) {
996: tty_attributes_bg(tty, gc);
997: tc->bg = bg;
1.8 nicm 998: tc->flags &= ~GRID_FLAG_BG256;
999: tc->flags |= gc->flags & GRID_FLAG_BG256;
1.1 nicm 1000: }
1001: }
1002:
1003: int
1004: tty_try_256(struct tty *tty, u_char colour, const char *type)
1005: {
1006: char s[32];
1007:
1008: if (!(tty->term->flags & TERM_256COLOURS) &&
1009: !(tty->term_flags & TERM_256COLOURS))
1010: return (-1);
1011:
1012: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1013: tty_puts(tty, s);
1014: return (0);
1015: }
1016:
1017: int
1018: tty_try_88(struct tty *tty, u_char colour, const char *type)
1019: {
1020: char s[32];
1021:
1022: if (!(tty->term->flags & TERM_88COLOURS) &&
1023: !(tty->term_flags & TERM_88COLOURS))
1024: return (-1);
1025: colour = colour_256to88(colour);
1026:
1027: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1028: tty_puts(tty, s);
1029: return (0);
1030: }
1031:
1032: void
1033: tty_attributes_fg(struct tty *tty, const struct grid_cell *gc)
1034: {
1035: u_char fg;
1036:
1037: fg = gc->fg;
1038: if (gc->flags & GRID_FLAG_FG256) {
1039: if (tty_try_256(tty, fg, "38") == 0)
1040: return;
1041: if (tty_try_88(tty, fg, "38") == 0)
1042: return;
1043: fg = colour_256to16(fg);
1044: if (fg & 8) {
1045: fg &= 7;
1046: tty_putcode(tty, TTYC_BOLD);
1047: tty->cell.attr |= GRID_ATTR_BRIGHT;
1048: } else if (tty->cell.attr & GRID_ATTR_BRIGHT)
1049: tty_reset(tty);
1050: }
1051:
1052: if (fg == 8 &&
1053: !(tty->term->flags & TERM_HASDEFAULTS) &&
1054: !(tty->term_flags & TERM_HASDEFAULTS))
1055: fg = 7;
1056: if (fg == 8)
1057: tty_puts(tty, "\033[39m");
1058: else
1059: tty_putcode1(tty, TTYC_SETAF, fg);
1060: }
1061:
1062: void
1063: tty_attributes_bg(struct tty *tty, const struct grid_cell *gc)
1064: {
1065: u_char bg;
1066:
1067: bg = gc->bg;
1068: if (gc->flags & GRID_FLAG_BG256) {
1069: if (tty_try_256(tty, bg, "48") == 0)
1070: return;
1071: if (tty_try_88(tty, bg, "48") == 0)
1072: return;
1073: bg = colour_256to16(bg);
1074: if (bg & 8)
1075: bg &= 7;
1076: }
1077:
1078: if (bg == 8 &&
1079: !(tty->term->flags & TERM_HASDEFAULTS) &&
1080: !(tty->term_flags & TERM_HASDEFAULTS))
1081: bg = 0;
1082: if (bg == 8)
1083: tty_puts(tty, "\033[49m");
1084: else
1085: tty_putcode1(tty, TTYC_SETAB, bg);
1086: }