Annotation of src/usr.bin/tmux/tty.c, Revision 1.152
1.152 ! nicm 1: /* $OpenBSD: tty.c,v 1.151 2013/03/22 10:30:04 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:
1.106 nicm 22: #include <netinet/in.h>
23:
1.1 nicm 24: #include <errno.h>
25: #include <fcntl.h>
1.106 nicm 26: #include <resolv.h>
1.42 nicm 27: #include <stdlib.h>
1.1 nicm 28: #include <string.h>
29: #include <termios.h>
30: #include <unistd.h>
31:
32: #include "tmux.h"
33:
1.67 nicm 34: void tty_read_callback(struct bufferevent *, void *);
1.66 nicm 35: void tty_error_callback(struct bufferevent *, short, void *);
36:
1.1 nicm 37: int tty_try_256(struct tty *, u_char, const char *);
38: int tty_try_88(struct tty *, u_char, const char *);
39:
1.85 nicm 40: void tty_colours(struct tty *, const struct grid_cell *);
41: void tty_check_fg(struct tty *, struct grid_cell *);
42: void tty_check_bg(struct tty *, struct grid_cell *);
43: void tty_colours_fg(struct tty *, const struct grid_cell *);
1.73 nicm 44: void tty_colours_bg(struct tty *, const struct grid_cell *);
1.1 nicm 45:
1.120 nicm 46: int tty_large_region(struct tty *, const struct tty_ctx *);
1.24 nicm 47: void tty_redraw_region(struct tty *, const struct tty_ctx *);
1.13 nicm 48: void tty_emulate_repeat(
49: struct tty *, enum tty_code_code, enum tty_code_code, u_int);
1.133 nicm 50: void tty_repeat_space(struct tty *, u_int);
1.147 nicm 51: void tty_cell(struct tty *, const struct grid_cell *);
1.1 nicm 52:
1.90 nicm 53: #define tty_use_acs(tty) \
1.131 nicm 54: (tty_term_has((tty)->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8))
1.90 nicm 55:
1.132 nicm 56: #define tty_pane_full_width(tty, ctx) \
57: ((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx)
58:
1.1 nicm 59: void
1.136 nicm 60: tty_init(struct tty *tty, struct client *c, int fd, char *term)
1.1 nicm 61: {
1.30 nicm 62: char *path;
63:
64: memset(tty, 0, sizeof *tty);
1.23 nicm 65: tty->log_fd = -1;
1.22 nicm 66:
1.9 nicm 67: if (term == NULL || *term == '\0')
1.1 nicm 68: tty->termname = xstrdup("unknown");
69: else
70: tty->termname = xstrdup(term);
1.30 nicm 71: tty->fd = fd;
1.136 nicm 72: tty->client = c;
1.30 nicm 73:
74: if ((path = ttyname(fd)) == NULL)
75: fatalx("ttyname failed");
76: tty->path = xstrdup(path);
1.108 nicm 77: tty->cstyle = 0;
1.107 nicm 78: tty->ccolour = xstrdup("");
1.30 nicm 79:
1.1 nicm 80: tty->flags = 0;
81: tty->term_flags = 0;
1.31 nicm 82: }
83:
1.88 nicm 84: int
1.31 nicm 85: tty_resize(struct tty *tty)
86: {
87: struct winsize ws;
1.88 nicm 88: u_int sx, sy;
1.31 nicm 89:
90: if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) {
1.88 nicm 91: sx = ws.ws_col;
92: if (sx == 0)
93: sx = 80;
94: sy = ws.ws_row;
95: if (sy == 0)
96: sy = 24;
97: } else {
98: sx = 80;
99: sy = 24;
1.31 nicm 100: }
1.114 nicm 101: if (!tty_set_size(tty, sx, sy))
1.88 nicm 102: return (0);
1.31 nicm 103:
104: tty->cx = UINT_MAX;
105: tty->cy = UINT_MAX;
106:
107: tty->rupper = UINT_MAX;
108: tty->rlower = UINT_MAX;
1.88 nicm 109:
110: /*
111: * If the terminal has been started, reset the actual scroll region and
112: * cursor position, as this may not have happened.
113: */
114: if (tty->flags & TTY_STARTED) {
115: tty_cursor(tty, 0, 0);
116: tty_region(tty, 0, tty->sy - 1);
117: }
118:
1.114 nicm 119: return (1);
120: }
121:
122: int
123: tty_set_size(struct tty *tty, u_int sx, u_int sy) {
124: if (sx == tty->sx && sy == tty->sy)
125: return (0);
126: tty->sx = sx;
127: tty->sy = sy;
1.88 nicm 128: return (1);
1.1 nicm 129: }
130:
131: int
1.17 nicm 132: tty_open(struct tty *tty, const char *overrides, char **cause)
1.1 nicm 133: {
1.88 nicm 134: char out[64];
1.30 nicm 135: int fd;
1.1 nicm 136:
1.30 nicm 137: if (debug_level > 3) {
1.88 nicm 138: xsnprintf(out, sizeof out, "tmux-out-%ld.log", (long) getpid());
139: fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644);
1.30 nicm 140: if (fd != -1 && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
141: fatal("fcntl failed");
142: tty->log_fd = fd;
1.1 nicm 143: }
144:
1.17 nicm 145: tty->term = tty_term_find(tty->termname, tty->fd, overrides, cause);
1.21 nicm 146: if (tty->term == NULL) {
147: tty_close(tty);
148: return (-1);
149: }
150: tty->flags |= TTY_OPENED;
1.1 nicm 151:
1.149 nicm 152: tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_TIMER);
1.1 nicm 153:
1.66 nicm 154: tty->event = bufferevent_new(
1.67 nicm 155: tty->fd, tty_read_callback, NULL, tty_error_callback, tty);
1.1 nicm 156:
157: tty_start_tty(tty);
158:
1.148 nicm 159: tty_keys_build(tty);
1.1 nicm 160:
161: return (0);
162: }
163:
164: void
1.67 nicm 165: tty_read_callback(unused struct bufferevent *bufev, void *data)
166: {
167: struct tty *tty = data;
168:
169: while (tty_keys_next(tty))
170: ;
171: }
172:
173: void
1.66 nicm 174: tty_error_callback(
175: unused struct bufferevent *bufev, unused short what, unused void *data)
176: {
177: }
178:
179: void
1.127 nicm 180: tty_init_termios(int fd, struct termios *orig_tio, struct bufferevent *bufev)
1.1 nicm 181: {
1.128 nicm 182: struct termios tio;
1.33 nicm 183:
1.127 nicm 184: if (fd == -1 || tcgetattr(fd, orig_tio) != 0)
1.33 nicm 185: return;
1.1 nicm 186:
1.127 nicm 187: setblocking(fd, 0);
1.34 nicm 188:
1.127 nicm 189: if (bufev != NULL)
190: bufferevent_enable(bufev, EV_READ|EV_WRITE);
1.66 nicm 191:
1.127 nicm 192: memcpy(&tio, orig_tio, sizeof tio);
1.1 nicm 193: tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP);
194: tio.c_iflag |= IGNBRK;
195: tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
196: tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|
197: ECHOPRT|ECHOKE|ECHOCTL|ISIG);
198: tio.c_cc[VMIN] = 1;
1.60 deraadt 199: tio.c_cc[VTIME] = 0;
1.127 nicm 200: if (tcsetattr(fd, TCSANOW, &tio) == 0)
201: tcflush(fd, TCIOFLUSH);
202: }
203:
204: void
205: tty_start_tty(struct tty *tty)
206: {
207: tty_init_termios(tty->fd, &tty->tio, tty->event);
1.1 nicm 208:
1.10 nicm 209: tty_putcode(tty, TTYC_SMCUP);
1.1 nicm 210:
1.25 nicm 211: tty_putcode(tty, TTYC_SGR0);
212: memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
213:
1.76 nicm 214: tty_putcode(tty, TTYC_RMKX);
1.90 nicm 215: if (tty_use_acs(tty))
216: tty_putcode(tty, TTYC_ENACS);
1.1 nicm 217: tty_putcode(tty, TTYC_CLEAR);
218:
219: tty_putcode(tty, TTYC_CNORM);
220: if (tty_term_has(tty->term, TTYC_KMOUS))
221: tty_puts(tty, "\033[?1000l");
1.121 nicm 222:
223: if (tty_term_has(tty->term, TTYC_XT))
1.151 nicm 224: tty_puts(tty, "\033[c\033[>4;1m");
1.1 nicm 225:
226: tty->cx = UINT_MAX;
227: tty->cy = UINT_MAX;
228:
229: tty->rlower = UINT_MAX;
230: tty->rupper = UINT_MAX;
231:
232: tty->mode = MODE_CURSOR;
1.20 nicm 233:
234: tty->flags |= TTY_STARTED;
1.107 nicm 235:
236: tty_force_cursor_colour(tty, "");
1.145 nicm 237: }
238:
239: void
240: tty_set_class(struct tty *tty, u_int class)
241: {
242: if (tty->class != 0)
243: return;
244: tty->class = class;
1.128 nicm 245: }
246:
247: void
1.1 nicm 248: tty_stop_tty(struct tty *tty)
249: {
250: struct winsize ws;
251:
1.20 nicm 252: if (!(tty->flags & TTY_STARTED))
253: return;
254: tty->flags &= ~TTY_STARTED;
255:
1.66 nicm 256: bufferevent_disable(tty->event, EV_READ|EV_WRITE);
257:
1.1 nicm 258: /*
259: * Be flexible about error handling and try not kill the server just
260: * because the fd is invalid. Things like ssh -t can easily leave us
261: * with a dead tty.
262: */
263: if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1)
264: return;
265: if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1)
266: return;
267:
268: tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
1.90 nicm 269: if (tty_use_acs(tty))
270: tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
1.1 nicm 271: tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
272: tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
273: tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
1.108 nicm 274: if (tty_term_has(tty->term, TTYC_CS1) && tty->cstyle != 0) {
275: if (tty_term_has(tty->term, TTYC_CSR1))
276: tty_raw(tty, tty_term_string(tty->term, TTYC_CSR1));
1.109 nicm 277: else
1.108 nicm 278: tty_raw(tty, tty_term_string1(tty->term, TTYC_CS1, 0));
279: }
1.107 nicm 280: tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
1.1 nicm 281:
282: tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
283: if (tty_term_has(tty->term, TTYC_KMOUS))
284: tty_raw(tty, "\033[?1000l");
1.151 nicm 285:
286: if (tty_term_has(tty->term, TTYC_XT))
287: tty_puts(tty, "\033[>4m");
1.10 nicm 288:
289: tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
1.150 nicm 290:
291: setblocking(tty->fd, 1);
1.1 nicm 292: }
293:
294: void
1.20 nicm 295: tty_close(struct tty *tty)
1.1 nicm 296: {
297: if (tty->log_fd != -1) {
298: close(tty->log_fd);
299: tty->log_fd = -1;
300: }
301:
1.123 nicm 302: if (event_initialized(&tty->key_timer))
303: evtimer_del(&tty->key_timer);
1.20 nicm 304: tty_stop_tty(tty);
1.1 nicm 305:
1.21 nicm 306: if (tty->flags & TTY_OPENED) {
1.66 nicm 307: bufferevent_free(tty->event);
308:
1.21 nicm 309: tty_term_free(tty->term);
310: tty_keys_free(tty);
311:
312: tty->flags &= ~TTY_OPENED;
313: }
1.1 nicm 314:
1.21 nicm 315: if (tty->fd != -1) {
316: close(tty->fd);
317: tty->fd = -1;
318: }
1.1 nicm 319: }
320:
321: void
1.20 nicm 322: tty_free(struct tty *tty)
1.1 nicm 323: {
1.20 nicm 324: tty_close(tty);
1.1 nicm 325:
1.138 nicm 326: free(tty->ccolour);
1.1 nicm 327: if (tty->path != NULL)
1.138 nicm 328: free(tty->path);
1.1 nicm 329: if (tty->termname != NULL)
1.138 nicm 330: free(tty->termname);
1.1 nicm 331: }
332:
333: void
334: tty_raw(struct tty *tty, const char *s)
335: {
1.150 nicm 336: ssize_t n, slen;
337: u_int i;
338:
339: slen = strlen(s);
340: for (i = 0; i < 5; i++) {
341: n = write(tty->fd, s, slen);
342: if (n >= 0) {
343: s += n;
344: slen -= n;
345: if (slen == 0)
346: break;
347: } else if (n == -1 && errno != EAGAIN)
348: break;
349: usleep(100);
350: }
1.1 nicm 351: }
352:
353: void
354: tty_putcode(struct tty *tty, enum tty_code_code code)
355: {
356: tty_puts(tty, tty_term_string(tty->term, code));
357: }
358:
359: void
360: tty_putcode1(struct tty *tty, enum tty_code_code code, int a)
361: {
362: if (a < 0)
363: return;
364: tty_puts(tty, tty_term_string1(tty->term, code, a));
365: }
366:
367: void
368: tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b)
369: {
370: if (a < 0 || b < 0)
371: return;
372: tty_puts(tty, tty_term_string2(tty->term, code, a, b));
373: }
374:
375: void
1.107 nicm 376: tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a)
377: {
378: if (a != NULL)
379: tty_puts(tty, tty_term_ptr1(tty->term, code, a));
380: }
381:
382: void
1.106 nicm 383: tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a, const void *b)
384: {
385: if (a != NULL && b != NULL)
386: tty_puts(tty, tty_term_ptr2(tty->term, code, a, b));
387: }
388:
389: void
1.1 nicm 390: tty_puts(struct tty *tty, const char *s)
391: {
392: if (*s == '\0')
393: return;
1.66 nicm 394: bufferevent_write(tty->event, s, strlen(s));
1.1 nicm 395:
396: if (tty->log_fd != -1)
397: write(tty->log_fd, s, strlen(s));
398: }
399:
400: void
401: tty_putc(struct tty *tty, u_char ch)
402: {
1.90 nicm 403: const char *acs;
404: u_int sx;
1.1 nicm 405:
1.90 nicm 406: if (tty->cell.attr & GRID_ATTR_CHARSET) {
407: acs = tty_acs_get(tty, ch);
408: if (acs != NULL)
409: bufferevent_write(tty->event, acs, strlen(acs));
410: else
411: bufferevent_write(tty->event, &ch, 1);
412: } else
413: bufferevent_write(tty->event, &ch, 1);
1.1 nicm 414:
415: if (ch >= 0x20 && ch != 0x7f) {
416: sx = tty->sx;
417: if (tty->term->flags & TERM_EARLYWRAP)
418: sx--;
419:
1.46 nicm 420: if (tty->cx >= sx) {
421: tty->cx = 1;
422: if (tty->cy != tty->rlower)
423: tty->cy++;
1.1 nicm 424: } else
425: tty->cx++;
426: }
427:
428: if (tty->log_fd != -1)
429: write(tty->log_fd, &ch, 1);
430: }
431:
432: void
1.147 nicm 433: tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
1.5 nicm 434: {
1.147 nicm 435: bufferevent_write(tty->event, buf, len);
1.71 nicm 436: if (tty->log_fd != -1)
1.147 nicm 437: write(tty->log_fd, buf, len);
438: tty->cx += width;
1.5 nicm 439: }
440:
441: void
1.1 nicm 442: tty_set_title(struct tty *tty, const char *title)
443: {
1.105 nicm 444: if (!tty_term_has(tty->term, TTYC_TSL) ||
445: !tty_term_has(tty->term, TTYC_FSL))
1.1 nicm 446: return;
447:
1.105 nicm 448: tty_putcode(tty, TTYC_TSL);
1.1 nicm 449: tty_puts(tty, title);
1.105 nicm 450: tty_putcode(tty, TTYC_FSL);
1.1 nicm 451: }
452:
453: void
1.107 nicm 454: tty_force_cursor_colour(struct tty *tty, const char *ccolour)
455: {
456: if (*ccolour == '\0')
457: tty_putcode(tty, TTYC_CR);
458: else
459: tty_putcode_ptr1(tty, TTYC_CC, ccolour);
1.138 nicm 460: free(tty->ccolour);
1.107 nicm 461: tty->ccolour = xstrdup(ccolour);
462: }
463:
464: void
465: tty_update_mode(struct tty *tty, int mode, struct screen *s)
1.1 nicm 466: {
467: int changed;
468:
1.107 nicm 469: if (strcmp(s->ccolour, tty->ccolour))
470: tty_force_cursor_colour(tty, s->ccolour);
471:
1.1 nicm 472: if (tty->flags & TTY_NOCURSOR)
473: mode &= ~MODE_CURSOR;
474:
475: changed = mode ^ tty->mode;
476: if (changed & MODE_CURSOR) {
477: if (mode & MODE_CURSOR)
478: tty_putcode(tty, TTYC_CNORM);
479: else
480: tty_putcode(tty, TTYC_CIVIS);
1.108 nicm 481: }
482: if (tty->cstyle != s->cstyle) {
483: if (tty_term_has(tty->term, TTYC_CS1)) {
484: if (s->cstyle == 0 &&
485: tty_term_has(tty->term, TTYC_CSR1))
486: tty_putcode(tty, TTYC_CSR1);
487: else
488: tty_putcode1(tty, TTYC_CS1, s->cstyle);
489: }
490: tty->cstyle = s->cstyle;
1.1 nicm 491: }
1.94 nicm 492: if (changed & ALL_MOUSE_MODES) {
493: if (mode & ALL_MOUSE_MODES) {
1.95 nicm 494: if (mode & MODE_MOUSE_UTF8)
495: tty_puts(tty, "\033[?1005h");
1.98 nicm 496: if (mode & MODE_MOUSE_ANY)
497: tty_puts(tty, "\033[?1003h");
1.94 nicm 498: else if (mode & MODE_MOUSE_BUTTON)
499: tty_puts(tty, "\033[?1002h");
1.98 nicm 500: else if (mode & MODE_MOUSE_STANDARD)
501: tty_puts(tty, "\033[?1000h");
1.86 nicm 502: } else {
1.98 nicm 503: if (tty->mode & MODE_MOUSE_ANY)
504: tty_puts(tty, "\033[?1003l");
1.94 nicm 505: else if (tty->mode & MODE_MOUSE_BUTTON)
506: tty_puts(tty, "\033[?1002l");
1.98 nicm 507: else if (tty->mode & MODE_MOUSE_STANDARD)
508: tty_puts(tty, "\033[?1000l");
1.95 nicm 509: if (tty->mode & MODE_MOUSE_UTF8)
510: tty_puts(tty, "\033[?1005l");
1.86 nicm 511: }
1.76 nicm 512: }
513: if (changed & MODE_KKEYPAD) {
514: if (mode & MODE_KKEYPAD)
515: tty_putcode(tty, TTYC_SMKX);
516: else
517: tty_putcode(tty, TTYC_RMKX);
1.115 nicm 518: }
519: if (changed & MODE_BRACKETPASTE) {
520: if (mode & MODE_BRACKETPASTE)
521: tty_puts(tty, "\033[?2004h");
522: else
523: tty_puts(tty, "\033[?2004l");
1.1 nicm 524: }
525: tty->mode = mode;
526: }
527:
528: void
529: tty_emulate_repeat(
530: struct tty *tty, enum tty_code_code code, enum tty_code_code code1, u_int n)
531: {
532: if (tty_term_has(tty->term, code))
533: tty_putcode1(tty, code, n);
534: else {
535: while (n-- > 0)
536: tty_putcode(tty, code1);
537: }
538: }
539:
1.133 nicm 540: void
541: tty_repeat_space(struct tty *tty, u_int n)
542: {
543: while (n-- > 0)
544: tty_putc(tty, ' ');
545: }
546:
1.1 nicm 547: /*
1.119 nicm 548: * Is the region large enough to be worth redrawing once later rather than
549: * probably several times now? Currently yes if it is more than 50% of the
550: * pane.
551: */
552: int
553: tty_large_region(unused struct tty *tty, const struct tty_ctx *ctx)
554: {
555: struct window_pane *wp = ctx->wp;
556:
557: return (ctx->orlower - ctx->orupper >= screen_size_y(wp->screen) / 2);
558: }
559:
560: /*
1.1 nicm 561: * Redraw scroll region using data from screen (already updated). Used when
562: * CSR not supported, or window is a pane that doesn't take up the full
563: * width of the terminal.
564: */
565: void
1.24 nicm 566: tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 567: {
1.14 nicm 568: struct window_pane *wp = ctx->wp;
569: struct screen *s = wp->screen;
570: u_int i;
1.1 nicm 571:
572: /*
1.119 nicm 573: * If region is large, schedule a window redraw. In most cases this is
574: * likely to be followed by some more scrolling.
1.1 nicm 575: */
1.119 nicm 576: if (tty_large_region(tty, ctx)) {
1.1 nicm 577: wp->flags |= PANE_REDRAW;
578: return;
579: }
580:
1.14 nicm 581: if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) {
582: for (i = ctx->ocy; i < screen_size_y(s); i++)
1.113 nicm 583: tty_draw_line(tty, s, i, ctx->xoff, ctx->yoff);
1.1 nicm 584: } else {
1.14 nicm 585: for (i = ctx->orupper; i <= ctx->orlower; i++)
1.113 nicm 586: tty_draw_line(tty, s, i, ctx->xoff, ctx->yoff);
1.1 nicm 587: }
588: }
589:
590: void
591: tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy)
592: {
593: const struct grid_cell *gc;
1.46 nicm 594: struct grid_line *gl;
1.16 nicm 595: struct grid_cell tmpgc;
1.147 nicm 596: struct utf8_data ud;
1.1 nicm 597: u_int i, sx;
598:
1.107 nicm 599: tty_update_mode(tty, tty->mode & ~MODE_CURSOR, s);
1.35 nicm 600:
1.1 nicm 601: sx = screen_size_x(s);
1.19 nicm 602: if (sx > s->grid->linedata[s->grid->hsize + py].cellsize)
603: sx = s->grid->linedata[s->grid->hsize + py].cellsize;
1.1 nicm 604: if (sx > tty->sx)
605: sx = tty->sx;
606:
1.46 nicm 607: /*
608: * Don't move the cursor to the start permission if it will wrap there
1.50 nicm 609: * itself.
1.46 nicm 610: */
611: gl = NULL;
612: if (py != 0)
613: gl = &s->grid->linedata[s->grid->hsize + py - 1];
1.83 nicm 614: if (oy + py == 0 || gl == NULL || !(gl->flags & GRID_LINE_WRAPPED) ||
1.47 nicm 615: tty->cx < tty->sx || ox != 0 ||
616: (oy + py != tty->cy + 1 && tty->cy != s->rlower + oy))
1.46 nicm 617: tty_cursor(tty, ox, oy + py);
618:
1.1 nicm 619: for (i = 0; i < sx; i++) {
620: gc = grid_view_peek_cell(s->grid, i, py);
621: if (screen_check_selection(s, i, py)) {
1.16 nicm 622: memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc);
1.147 nicm 623: grid_cell_get(gc, &ud);
624: grid_cell_set(&tmpgc, &ud);
1.77 nicm 625: tmpgc.flags = gc->flags &
1.38 nicm 626: ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
627: tmpgc.flags |= s->sel.cell.flags &
628: (GRID_FLAG_FG256|GRID_FLAG_BG256);
1.147 nicm 629: tty_cell(tty, &tmpgc);
1.1 nicm 630: } else
1.147 nicm 631: tty_cell(tty, gc);
1.1 nicm 632: }
633:
1.35 nicm 634: if (sx >= tty->sx) {
1.107 nicm 635: tty_update_mode(tty, tty->mode, s);
1.1 nicm 636: return;
1.35 nicm 637: }
1.1 nicm 638: tty_reset(tty);
639:
1.41 nicm 640: tty_cursor(tty, ox + sx, oy + py);
1.126 nicm 641: if (sx != screen_size_x(s) && ox + screen_size_x(s) >= tty->sx &&
1.117 nicm 642: tty_term_has(tty->term, TTYC_EL))
1.1 nicm 643: tty_putcode(tty, TTYC_EL);
1.133 nicm 644: else
645: tty_repeat_space(tty, screen_size_x(s) - sx);
1.107 nicm 646: tty_update_mode(tty, tty->mode, s);
1.15 nicm 647: }
648:
649: void
1.113 nicm 650: tty_write(
651: void (*cmdfn)(struct tty *, const struct tty_ctx *), struct tty_ctx *ctx)
1.15 nicm 652: {
653: struct window_pane *wp = ctx->wp;
654: struct client *c;
655: u_int i;
656:
1.75 nicm 657: /* wp can be NULL if updating the screen but not the terminal. */
1.15 nicm 658: if (wp == NULL)
659: return;
660:
661: if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
662: return;
1.129 nicm 663: if (!window_pane_visible(wp) || wp->flags & PANE_DROP)
1.15 nicm 664: return;
665:
666: for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
667: c = ARRAY_ITEM(&clients, i);
1.139 nicm 668: if (c == NULL || c->session == NULL || c->tty.term == NULL)
1.15 nicm 669: continue;
1.142 nicm 670: if (c->flags & CLIENT_SUSPENDED)
671: continue;
672: if (c->tty.flags & TTY_FREEZE)
1.139 nicm 673: continue;
674: if (c->session->curw->window != wp->window)
1.15 nicm 675: continue;
676:
1.139 nicm 677: ctx->xoff = wp->xoff;
678: ctx->yoff = wp->yoff;
679: if (status_at_line(c) == 0)
680: ctx->yoff++;
1.113 nicm 681:
1.139 nicm 682: cmdfn(&c->tty, ctx);
1.1 nicm 683: }
684: }
685:
686: void
1.24 nicm 687: tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 688: {
1.77 nicm 689: struct window_pane *wp = ctx->wp;
1.1 nicm 690:
1.135 nicm 691: if (!tty_pane_full_width(tty, ctx)) {
1.113 nicm 692: tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff);
1.1 nicm 693: return;
694: }
695:
696: tty_reset(tty);
697:
1.77 nicm 698: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.41 nicm 699:
1.1 nicm 700: if (tty_term_has(tty->term, TTYC_ICH) ||
701: tty_term_has(tty->term, TTYC_ICH1))
1.12 nicm 702: tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
1.134 nicm 703: else
1.113 nicm 704: tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff);
1.1 nicm 705: }
706:
707: void
1.24 nicm 708: tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 709: {
1.77 nicm 710: struct window_pane *wp = ctx->wp;
1.1 nicm 711:
1.135 nicm 712: if (!tty_pane_full_width(tty, ctx) ||
1.26 nicm 713: (!tty_term_has(tty->term, TTYC_DCH) &&
714: !tty_term_has(tty->term, TTYC_DCH1))) {
1.113 nicm 715: tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff);
1.1 nicm 716: return;
717: }
718:
719: tty_reset(tty);
720:
1.77 nicm 721: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.41 nicm 722:
1.26 nicm 723: if (tty_term_has(tty->term, TTYC_DCH) ||
724: tty_term_has(tty->term, TTYC_DCH1))
725: tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
1.146 nicm 726: }
727:
728: void
729: tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
730: {
731: u_int i;
732:
733: tty_reset(tty);
734:
735: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
736:
737: if (tty_term_has(tty->term, TTYC_ECH))
738: tty_putcode1(tty, TTYC_ECH, ctx->num);
739: else {
740: for (i = 0; i < ctx->num; i++)
741: tty_putc(tty, ' ');
742: }
1.1 nicm 743: }
744:
745: void
1.24 nicm 746: tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 747: {
1.132 nicm 748: if (!tty_pane_full_width(tty, ctx) ||
1.77 nicm 749: !tty_term_has(tty->term, TTYC_CSR) ||
1.72 nicm 750: !tty_term_has(tty->term, TTYC_IL1)) {
1.14 nicm 751: tty_redraw_region(tty, ctx);
1.1 nicm 752: return;
753: }
754:
755: tty_reset(tty);
756:
1.77 nicm 757: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
758: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 759:
1.12 nicm 760: tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
1.1 nicm 761: }
762:
763: void
1.24 nicm 764: tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 765: {
1.132 nicm 766: if (!tty_pane_full_width(tty, ctx) ||
1.72 nicm 767: !tty_term_has(tty->term, TTYC_CSR) ||
768: !tty_term_has(tty->term, TTYC_DL1)) {
1.14 nicm 769: tty_redraw_region(tty, ctx);
1.1 nicm 770: return;
771: }
772:
773: tty_reset(tty);
774:
1.77 nicm 775: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
776: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 777:
1.12 nicm 778: tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
1.1 nicm 779: }
780:
781: void
1.24 nicm 782: tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 783: {
1.77 nicm 784: struct window_pane *wp = ctx->wp;
1.12 nicm 785: struct screen *s = wp->screen;
1.1 nicm 786:
787: tty_reset(tty);
788:
1.77 nicm 789: tty_cursor_pane(tty, ctx, 0, ctx->ocy);
1.41 nicm 790:
1.133 nicm 791: if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL))
1.1 nicm 792: tty_putcode(tty, TTYC_EL);
1.133 nicm 793: else
794: tty_repeat_space(tty, screen_size_x(s));
1.1 nicm 795: }
796:
797: void
1.24 nicm 798: tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 799: {
1.77 nicm 800: struct window_pane *wp = ctx->wp;
1.12 nicm 801: struct screen *s = wp->screen;
1.1 nicm 802:
803: tty_reset(tty);
804:
1.41 nicm 805: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
806:
1.132 nicm 807: if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL))
1.1 nicm 808: tty_putcode(tty, TTYC_EL);
1.133 nicm 809: else
810: tty_repeat_space(tty, screen_size_x(s) - ctx->ocx);
1.1 nicm 811: }
812:
813: void
1.24 nicm 814: tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 815: {
816: tty_reset(tty);
817:
1.113 nicm 818: if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1)) {
1.41 nicm 819: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 820: tty_putcode(tty, TTYC_EL1);
821: } else {
1.41 nicm 822: tty_cursor_pane(tty, ctx, 0, ctx->ocy);
1.133 nicm 823: tty_repeat_space(tty, ctx->ocx + 1);
1.1 nicm 824: }
825: }
826:
827: void
1.24 nicm 828: tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 829: {
1.56 nicm 830: if (ctx->ocy != ctx->orupper)
831: return;
832:
1.132 nicm 833: if (!tty_pane_full_width(tty, ctx) ||
1.70 nicm 834: !tty_term_has(tty->term, TTYC_CSR) ||
835: !tty_term_has(tty->term, TTYC_RI)) {
1.14 nicm 836: tty_redraw_region(tty, ctx);
1.1 nicm 837: return;
838: }
839:
1.56 nicm 840: tty_reset(tty);
1.77 nicm 841:
1.56 nicm 842: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
843: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
1.77 nicm 844:
1.56 nicm 845: tty_putcode(tty, TTYC_RI);
1.1 nicm 846: }
847:
848: void
1.24 nicm 849: tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 850: {
1.77 nicm 851: struct window_pane *wp = ctx->wp;
1.1 nicm 852:
1.56 nicm 853: if (ctx->ocy != ctx->orlower)
854: return;
855:
1.132 nicm 856: if (!tty_pane_full_width(tty, ctx) ||
1.1 nicm 857: !tty_term_has(tty->term, TTYC_CSR)) {
1.128 nicm 858: if (tty_large_region(tty, ctx))
859: wp->flags |= PANE_REDRAW;
1.140 nicm 860: else
1.128 nicm 861: tty_redraw_region(tty, ctx);
1.1 nicm 862: return;
863: }
1.52 nicm 864:
865: /*
866: * If this line wrapped naturally (ctx->num is nonzero), don't do
867: * anything - the cursor can just be moved to the last cell and wrap
868: * naturally.
869: */
870: if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP))
871: return;
1.1 nicm 872:
1.56 nicm 873: tty_reset(tty);
1.77 nicm 874:
1.56 nicm 875: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
876: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.77 nicm 877:
1.56 nicm 878: tty_putc(tty, '\n');
1.1 nicm 879: }
880:
881: void
1.24 nicm 882: tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 883: {
1.77 nicm 884: struct window_pane *wp = ctx->wp;
1.12 nicm 885: struct screen *s = wp->screen;
886: u_int i, j;
1.1 nicm 887:
888: tty_reset(tty);
889:
1.39 nicm 890: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.41 nicm 891: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.39 nicm 892:
1.132 nicm 893: if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) {
1.1 nicm 894: tty_putcode(tty, TTYC_EL);
1.14 nicm 895: if (ctx->ocy != screen_size_y(s) - 1) {
1.41 nicm 896: tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
1.14 nicm 897: for (i = ctx->ocy + 1; i < screen_size_y(s); i++) {
1.1 nicm 898: tty_putcode(tty, TTYC_EL);
899: if (i == screen_size_y(s) - 1)
900: continue;
901: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
902: tty->cy++;
903: }
904: }
905: } else {
1.133 nicm 906: tty_repeat_space(tty, screen_size_x(s) - ctx->ocx);
1.68 nicm 907: for (j = ctx->ocy + 1; j < screen_size_y(s); j++) {
1.41 nicm 908: tty_cursor_pane(tty, ctx, 0, j);
1.133 nicm 909: tty_repeat_space(tty, screen_size_x(s));
1.1 nicm 910: }
911: }
912: }
913:
914: void
1.24 nicm 915: tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 916: {
1.77 nicm 917: struct window_pane *wp = ctx->wp;
1.12 nicm 918: struct screen *s = wp->screen;
919: u_int i, j;
1.1 nicm 920:
921: tty_reset(tty);
922:
1.39 nicm 923: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.41 nicm 924: tty_cursor_pane(tty, ctx, 0, 0);
1.39 nicm 925:
1.132 nicm 926: if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) {
1.14 nicm 927: for (i = 0; i < ctx->ocy; i++) {
1.1 nicm 928: tty_putcode(tty, TTYC_EL);
929: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
930: tty->cy++;
931: }
932: } else {
1.14 nicm 933: for (j = 0; j < ctx->ocy; j++) {
1.41 nicm 934: tty_cursor_pane(tty, ctx, 0, j);
1.133 nicm 935: tty_repeat_space(tty, screen_size_x(s));
1.1 nicm 936: }
937: }
1.133 nicm 938: tty_repeat_space(tty, ctx->ocx + 1);
1.1 nicm 939: }
940:
941: void
1.24 nicm 942: tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 943: {
1.77 nicm 944: struct window_pane *wp = ctx->wp;
1.12 nicm 945: struct screen *s = wp->screen;
946: u_int i, j;
1.1 nicm 947:
948: tty_reset(tty);
949:
1.39 nicm 950: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.41 nicm 951: tty_cursor_pane(tty, ctx, 0, 0);
1.39 nicm 952:
1.132 nicm 953: if (tty_pane_full_width(tty, ctx) && tty_term_has(tty->term, TTYC_EL)) {
1.1 nicm 954: for (i = 0; i < screen_size_y(s); i++) {
955: tty_putcode(tty, TTYC_EL);
956: if (i != screen_size_y(s) - 1) {
957: tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
958: tty->cy++;
959: }
960: }
961: } else {
962: for (j = 0; j < screen_size_y(s); j++) {
1.41 nicm 963: tty_cursor_pane(tty, ctx, 0, j);
1.133 nicm 964: tty_repeat_space(tty, screen_size_x(s));
1.1 nicm 965: }
1.4 nicm 966: }
967: }
968:
969: void
1.24 nicm 970: tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
1.4 nicm 971: {
1.12 nicm 972: struct window_pane *wp = ctx->wp;
973: struct screen *s = wp->screen;
974: u_int i, j;
1.4 nicm 975:
976: tty_reset(tty);
977:
1.39 nicm 978: tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1.4 nicm 979:
980: for (j = 0; j < screen_size_y(s); j++) {
1.41 nicm 981: tty_cursor_pane(tty, ctx, 0, j);
1.4 nicm 982: for (i = 0; i < screen_size_x(s); i++)
983: tty_putc(tty, 'E');
1.1 nicm 984: }
985: }
986:
987: void
1.24 nicm 988: tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 989: {
1.46 nicm 990: struct window_pane *wp = ctx->wp;
991: struct screen *s = wp->screen;
1.58 nicm 992: u_int cx;
1.102 nicm 993: u_int width;
1.46 nicm 994:
995: tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
996:
1.57 nicm 997: /* Is the cursor in the very last position? */
1.147 nicm 998: width = grid_cell_width(ctx->cell);
1.102 nicm 999: if (ctx->ocx > wp->sx - width) {
1.113 nicm 1000: if (ctx->xoff != 0 || wp->sx != tty->sx) {
1.57 nicm 1001: /*
1002: * The pane doesn't fill the entire line, the linefeed
1003: * will already have happened, so just move the cursor.
1004: */
1.125 nicm 1005: if (ctx->ocy != wp->yoff + wp->screen->rlower)
1.122 nicm 1006: tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
1007: else
1008: tty_cursor_pane(tty, ctx, 0, ctx->ocy);
1.57 nicm 1009: } else if (tty->cx < tty->sx) {
1010: /*
1011: * The cursor isn't in the last position already, so
1.102 nicm 1012: * move as far left as possible and redraw the last
1.57 nicm 1013: * cell to move into the last position.
1014: */
1.147 nicm 1015: cx = screen_size_x(s) - grid_cell_width(&ctx->last_cell);
1.50 nicm 1016: tty_cursor_pane(tty, ctx, cx, ctx->ocy);
1.147 nicm 1017: tty_cell(tty, &ctx->last_cell);
1.50 nicm 1018: }
1019: } else
1.46 nicm 1020: tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1.1 nicm 1021:
1.147 nicm 1022: tty_cell(tty, ctx->cell);
1.1 nicm 1023: }
1024:
1025: void
1.24 nicm 1026: tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx)
1.1 nicm 1027: {
1.53 nicm 1028: struct window_pane *wp = ctx->wp;
1.1 nicm 1029:
1.53 nicm 1030: /*
1031: * Cannot rely on not being a partial character, so just redraw the
1032: * whole line.
1033: */
1.113 nicm 1034: tty_draw_line(tty, wp->screen, ctx->ocy, ctx->xoff, ctx->yoff);
1.106 nicm 1035: }
1036:
1037: void
1038: tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
1039: {
1040: char *buf;
1041: size_t off;
1042:
1043: if (!tty_term_has(tty->term, TTYC_MS))
1044: return;
1045:
1046: off = 4 * ((ctx->num + 2) / 3) + 1; /* storage for base64 */
1047: buf = xmalloc(off);
1048:
1049: b64_ntop(ctx->ptr, ctx->num, buf, off);
1050: tty_putcode_ptr2(tty, TTYC_MS, "", buf);
1051:
1.138 nicm 1052: free(buf);
1.100 nicm 1053: }
1054:
1055: void
1056: tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
1057: {
1058: u_int i;
1059: u_char *str = ctx->ptr;
1060:
1061: for (i = 0; i < ctx->num; i++)
1062: tty_putc(tty, str[i]);
1.141 nicm 1063:
1064: tty->cx = tty->cy = UINT_MAX;
1065: tty->rupper = tty->rlower = UINT_MAX;
1066:
1067: tty_reset(tty);
1068: tty_cursor(tty, 0, 0);
1.1 nicm 1069: }
1070:
1071: void
1.147 nicm 1072: tty_cell(struct tty *tty, const struct grid_cell *gc)
1.1 nicm 1073: {
1.147 nicm 1074: struct utf8_data ud;
1075: u_int i;
1.1 nicm 1076:
1077: /* Skip last character if terminal is stupid. */
1078: if (tty->term->flags & TERM_EARLYWRAP &&
1079: tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1)
1080: return;
1081:
1082: /* If this is a padding character, do nothing. */
1083: if (gc->flags & GRID_FLAG_PADDING)
1084: return;
1085:
1086: /* Set the attributes. */
1087: tty_attributes(tty, gc);
1088:
1.147 nicm 1089: /* Get the cell and if ASCII write with putc to do ACS translation. */
1090: grid_cell_get(gc, &ud);
1091: if (ud.size == 1) {
1092: if (*ud.data < 0x20 || *ud.data == 0x7f)
1.1 nicm 1093: return;
1.147 nicm 1094: tty_putc(tty, *ud.data);
1.1 nicm 1095: return;
1096: }
1097:
1.147 nicm 1098: /* If not UTF-8, write _. */
1.1 nicm 1099: if (!(tty->flags & TTY_UTF8)) {
1.147 nicm 1100: for (i = 0; i < ud.width; i++)
1.1 nicm 1101: tty_putc(tty, '_');
1102: return;
1103: }
1104:
1.147 nicm 1105: /* Write the data. */
1106: tty_putn(tty, ud.data, ud.size, ud.width);
1.1 nicm 1107: }
1108:
1109: void
1110: tty_reset(struct tty *tty)
1111: {
1112: struct grid_cell *gc = &tty->cell;
1113:
1114: if (memcmp(gc, &grid_default_cell, sizeof *gc) == 0)
1115: return;
1116:
1.90 nicm 1117: if ((gc->attr & GRID_ATTR_CHARSET) && tty_use_acs(tty))
1.1 nicm 1118: tty_putcode(tty, TTYC_RMACS);
1119: tty_putcode(tty, TTYC_SGR0);
1120: memcpy(gc, &grid_default_cell, sizeof *gc);
1121: }
1122:
1.40 nicm 1123: /* Set region inside pane. */
1.1 nicm 1124: void
1.39 nicm 1125: tty_region_pane(
1126: struct tty *tty, const struct tty_ctx *ctx, u_int rupper, u_int rlower)
1.1 nicm 1127: {
1.113 nicm 1128: tty_region(tty, ctx->yoff + rupper, ctx->yoff + rlower);
1.39 nicm 1129: }
1130:
1.40 nicm 1131: /* Set region at absolute position. */
1.39 nicm 1132: void
1.40 nicm 1133: tty_region(struct tty *tty, u_int rupper, u_int rlower)
1.39 nicm 1134: {
1135: if (tty->rlower == rlower && tty->rupper == rupper)
1136: return;
1.1 nicm 1137: if (!tty_term_has(tty->term, TTYC_CSR))
1138: return;
1.39 nicm 1139:
1140: tty->rupper = rupper;
1141: tty->rlower = rlower;
1.55 nicm 1142:
1143: /*
1144: * Some terminals (such as PuTTY) do not correctly reset the cursor to
1145: * 0,0 if it is beyond the last column (they do not reset their wrap
1146: * flag so further output causes a line feed). As a workaround, do an
1147: * explicit move to 0 first.
1148: */
1149: if (tty->cx >= tty->sx)
1150: tty_cursor(tty, 0, tty->cy);
1.42 nicm 1151:
1.39 nicm 1152: tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
1.78 nicm 1153: tty_cursor(tty, 0, 0);
1.1 nicm 1154: }
1155:
1.42 nicm 1156: /* Move cursor inside pane. */
1.1 nicm 1157: void
1.41 nicm 1158: tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)
1159: {
1.113 nicm 1160: tty_cursor(tty, ctx->xoff + cx, ctx->yoff + cy);
1.41 nicm 1161: }
1162:
1.42 nicm 1163: /* Move cursor to absolute position. */
1.41 nicm 1164: void
1165: tty_cursor(struct tty *tty, u_int cx, u_int cy)
1.1 nicm 1166: {
1.42 nicm 1167: struct tty_term *term = tty->term;
1168: u_int thisx, thisy;
1169: int change;
1.77 nicm 1170:
1.42 nicm 1171: if (cx > tty->sx - 1)
1172: cx = tty->sx - 1;
1173:
1174: thisx = tty->cx;
1175: thisy = tty->cy;
1176:
1177: /* No change. */
1178: if (cx == thisx && cy == thisy)
1179: return;
1180:
1.43 nicm 1181: /* Very end of the line, just use absolute movement. */
1182: if (thisx > tty->sx - 1)
1183: goto absolute;
1184:
1.42 nicm 1185: /* Move to home position (0, 0). */
1186: if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) {
1187: tty_putcode(tty, TTYC_HOME);
1188: goto out;
1189: }
1190:
1191: /* Zero on the next line. */
1.48 nicm 1192: if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower) {
1.1 nicm 1193: tty_putc(tty, '\r');
1.42 nicm 1194: tty_putc(tty, '\n');
1195: goto out;
1196: }
1197:
1.45 nicm 1198: /* Moving column or row. */
1.42 nicm 1199: if (cy == thisy) {
1.45 nicm 1200: /*
1201: * Moving column only, row staying the same.
1202: */
1203:
1.42 nicm 1204: /* To left edge. */
1205: if (cx == 0) {
1206: tty_putc(tty, '\r');
1207: goto out;
1208: }
1209:
1210: /* One to the left. */
1211: if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) {
1212: tty_putcode(tty, TTYC_CUB1);
1213: goto out;
1214: }
1215:
1216: /* One to the right. */
1217: if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) {
1218: tty_putcode(tty, TTYC_CUF1);
1219: goto out;
1220: }
1221:
1222: /* Calculate difference. */
1223: change = thisx - cx; /* +ve left, -ve right */
1224:
1225: /*
1226: * Use HPA if change is larger than absolute, otherwise move
1227: * the cursor with CUB/CUF.
1228: */
1.87 nicm 1229: if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
1.42 nicm 1230: tty_putcode1(tty, TTYC_HPA, cx);
1231: goto out;
1232: } else if (change > 0 && tty_term_has(term, TTYC_CUB)) {
1233: tty_putcode1(tty, TTYC_CUB, change);
1234: goto out;
1235: } else if (change < 0 && tty_term_has(term, TTYC_CUF)) {
1236: tty_putcode1(tty, TTYC_CUF, -change);
1237: goto out;
1238: }
1.45 nicm 1239: } else if (cx == thisx) {
1240: /*
1241: * Moving row only, column staying the same.
1242: */
1.42 nicm 1243:
1244: /* One above. */
1.77 nicm 1245: if (thisy != tty->rupper &&
1.42 nicm 1246: cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) {
1247: tty_putcode(tty, TTYC_CUU1);
1248: goto out;
1249: }
1250:
1251: /* One below. */
1.49 nicm 1252: if (thisy != tty->rlower &&
1.42 nicm 1253: cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) {
1254: tty_putcode(tty, TTYC_CUD1);
1255: goto out;
1256: }
1257:
1258: /* Calculate difference. */
1259: change = thisy - cy; /* +ve up, -ve down */
1260:
1261: /*
1.77 nicm 1262: * Try to use VPA if change is larger than absolute or if this
1263: * change would cross the scroll region, otherwise use CUU/CUD.
1.42 nicm 1264: */
1.87 nicm 1265: if ((u_int) abs(change) > cy ||
1.42 nicm 1266: (change < 0 && cy - change > tty->rlower) ||
1.44 nicm 1267: (change > 0 && cy - change < tty->rupper)) {
1268: if (tty_term_has(term, TTYC_VPA)) {
1269: tty_putcode1(tty, TTYC_VPA, cy);
1270: goto out;
1271: }
1.42 nicm 1272: } else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
1273: tty_putcode1(tty, TTYC_CUU, change);
1274: goto out;
1275: } else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
1276: tty_putcode1(tty, TTYC_CUD, -change);
1277: goto out;
1278: }
1279: }
1280:
1.43 nicm 1281: absolute:
1.42 nicm 1282: /* Absolute movement. */
1283: tty_putcode2(tty, TTYC_CUP, cy, cx);
1284:
1285: out:
1286: tty->cx = cx;
1287: tty->cy = cy;
1.1 nicm 1288: }
1289:
1290: void
1291: tty_attributes(struct tty *tty, const struct grid_cell *gc)
1292: {
1.63 nicm 1293: struct grid_cell *tc = &tty->cell, gc2;
1.85 nicm 1294: u_char changed;
1.61 nicm 1295:
1.85 nicm 1296: memcpy(&gc2, gc, sizeof gc2);
1.63 nicm 1297:
1.18 nicm 1298: /*
1299: * If no setab, try to use the reverse attribute as a best-effort for a
1300: * non-default background. This is a bit of a hack but it doesn't do
1301: * any serious harm and makes a couple of applications happier.
1302: */
1303: if (!tty_term_has(tty->term, TTYC_SETAB)) {
1.85 nicm 1304: if (gc2.attr & GRID_ATTR_REVERSE) {
1305: if (gc2.fg != 7 && gc2.fg != 8)
1.64 nicm 1306: gc2.attr &= ~GRID_ATTR_REVERSE;
1.18 nicm 1307: } else {
1.85 nicm 1308: if (gc2.bg != 0 && gc2.bg != 8)
1.64 nicm 1309: gc2.attr |= GRID_ATTR_REVERSE;
1.18 nicm 1310: }
1311: }
1.1 nicm 1312:
1.85 nicm 1313: /* Fix up the colours if necessary. */
1314: tty_check_fg(tty, &gc2);
1315: tty_check_bg(tty, &gc2);
1316:
1.64 nicm 1317: /* If any bits are being cleared, reset everything. */
1.85 nicm 1318: if (tc->attr & ~gc2.attr)
1.64 nicm 1319: tty_reset(tty);
1320:
1321: /*
1322: * Set the colours. This may call tty_reset() (so it comes next) and
1323: * may add to (NOT remove) the desired attributes by changing new_attr.
1324: */
1.85 nicm 1325: tty_colours(tty, &gc2);
1.64 nicm 1326:
1.1 nicm 1327: /* Filter out attribute bits already set. */
1.85 nicm 1328: changed = gc2.attr & ~tc->attr;
1329: tc->attr = gc2.attr;
1.1 nicm 1330:
1331: /* Set the attributes. */
1332: if (changed & GRID_ATTR_BRIGHT)
1333: tty_putcode(tty, TTYC_BOLD);
1334: if (changed & GRID_ATTR_DIM)
1335: tty_putcode(tty, TTYC_DIM);
1336: if (changed & GRID_ATTR_ITALICS)
1.104 nicm 1337: {
1338: if (tty_term_has(tty->term, TTYC_SITM))
1339: tty_putcode(tty, TTYC_SITM);
1340: else
1341: tty_putcode(tty, TTYC_SMSO);
1342: }
1.1 nicm 1343: if (changed & GRID_ATTR_UNDERSCORE)
1344: tty_putcode(tty, TTYC_SMUL);
1345: if (changed & GRID_ATTR_BLINK)
1346: tty_putcode(tty, TTYC_BLINK);
1347: if (changed & GRID_ATTR_REVERSE) {
1348: if (tty_term_has(tty->term, TTYC_REV))
1349: tty_putcode(tty, TTYC_REV);
1350: else if (tty_term_has(tty->term, TTYC_SMSO))
1351: tty_putcode(tty, TTYC_SMSO);
1352: }
1353: if (changed & GRID_ATTR_HIDDEN)
1354: tty_putcode(tty, TTYC_INVIS);
1.90 nicm 1355: if ((changed & GRID_ATTR_CHARSET) && tty_use_acs(tty))
1.1 nicm 1356: tty_putcode(tty, TTYC_SMACS);
1357: }
1358:
1.61 nicm 1359: void
1.85 nicm 1360: tty_colours(struct tty *tty, const struct grid_cell *gc)
1.1 nicm 1361: {
1.61 nicm 1362: struct grid_cell *tc = &tty->cell;
1.62 nicm 1363: u_char fg = gc->fg, bg = gc->bg, flags = gc->flags;
1364: int have_ax, fg_default, bg_default;
1.61 nicm 1365:
1366: /* No changes? Nothing is necessary. */
1.62 nicm 1367: if (fg == tc->fg && bg == tc->bg &&
1368: ((flags ^ tc->flags) & (GRID_FLAG_FG256|GRID_FLAG_BG256)) == 0)
1.63 nicm 1369: return;
1.1 nicm 1370:
1.61 nicm 1371: /*
1372: * Is either the default colour? This is handled specially because the
1373: * best solution might be to reset both colours to default, in which
1374: * case if only one is default need to fall onward to set the other
1375: * colour.
1376: */
1.62 nicm 1377: fg_default = (fg == 8 && !(flags & GRID_FLAG_FG256));
1378: bg_default = (bg == 8 && !(flags & GRID_FLAG_BG256));
1.61 nicm 1379: if (fg_default || bg_default) {
1380: /*
1381: * If don't have AX but do have op, send sgr0 (op can't
1382: * actually be used because it is sometimes the same as sgr0
1383: * and sometimes isn't). This resets both colours to default.
1384: *
1385: * Otherwise, try to set the default colour only as needed.
1386: */
1387: have_ax = tty_term_has(tty->term, TTYC_AX);
1388: if (!have_ax && tty_term_has(tty->term, TTYC_OP))
1389: tty_reset(tty);
1390: else {
1391: if (fg_default &&
1.81 nicm 1392: (tc->fg != 8 || tc->flags & GRID_FLAG_FG256)) {
1.61 nicm 1393: if (have_ax)
1394: tty_puts(tty, "\033[39m");
1.81 nicm 1395: else if (tc->fg != 7 ||
1396: tc->flags & GRID_FLAG_FG256)
1.61 nicm 1397: tty_putcode1(tty, TTYC_SETAF, 7);
1398: tc->fg = 8;
1399: tc->flags &= ~GRID_FLAG_FG256;
1400: }
1401: if (bg_default &&
1.81 nicm 1402: (tc->bg != 8 || tc->flags & GRID_FLAG_BG256)) {
1.77 nicm 1403: if (have_ax)
1.61 nicm 1404: tty_puts(tty, "\033[49m");
1.81 nicm 1405: else if (tc->bg != 0 ||
1406: tc->flags & GRID_FLAG_BG256)
1.61 nicm 1407: tty_putcode1(tty, TTYC_SETAB, 0);
1408: tc->bg = 8;
1409: tc->flags &= ~GRID_FLAG_BG256;
1410: }
1411: }
1412: }
1.1 nicm 1413:
1.61 nicm 1414: /* Set the foreground colour. */
1415: if (!fg_default && (fg != tc->fg ||
1.62 nicm 1416: ((flags & GRID_FLAG_FG256) != (tc->flags & GRID_FLAG_FG256))))
1.85 nicm 1417: tty_colours_fg(tty, gc);
1.1 nicm 1418:
1.61 nicm 1419: /*
1420: * Set the background colour. This must come after the foreground as
1421: * tty_colour_fg() can call tty_reset().
1422: */
1423: if (!bg_default && (bg != tc->bg ||
1.62 nicm 1424: ((flags & GRID_FLAG_BG256) != (tc->flags & GRID_FLAG_BG256))))
1.73 nicm 1425: tty_colours_bg(tty, gc);
1.1 nicm 1426: }
1427:
1428: void
1.85 nicm 1429: tty_check_fg(struct tty *tty, struct grid_cell *gc)
1430: {
1431: u_int colours;
1432:
1433: /* Is this a 256-colour colour? */
1434: if (gc->flags & GRID_FLAG_FG256) {
1435: /* And not a 256 colour mode? */
1436: if (!(tty->term->flags & TERM_88COLOURS) &&
1437: !(tty->term_flags & TERM_88COLOURS) &&
1438: !(tty->term->flags & TERM_256COLOURS) &&
1439: !(tty->term_flags & TERM_256COLOURS)) {
1440: gc->fg = colour_256to16(gc->fg);
1441: if (gc->fg & 8) {
1442: gc->fg &= 7;
1443: gc->attr |= GRID_ATTR_BRIGHT;
1444: } else
1445: gc->attr &= ~GRID_ATTR_BRIGHT;
1446: gc->flags &= ~GRID_FLAG_FG256;
1447: }
1448: return;
1449: }
1450:
1451: /* Is this an aixterm colour? */
1452: colours = tty_term_number(tty->term, TTYC_COLORS);
1453: if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) {
1454: gc->fg -= 90;
1455: gc->attr |= GRID_ATTR_BRIGHT;
1456: }
1457: }
1458:
1459: void
1460: tty_check_bg(struct tty *tty, struct grid_cell *gc)
1461: {
1462: u_int colours;
1463:
1464: /* Is this a 256-colour colour? */
1465: if (gc->flags & GRID_FLAG_BG256) {
1466: /*
1467: * And not a 256 colour mode? Translate to 16-colour
1468: * palette. Bold background doesn't exist portably, so just
1469: * discard the bold bit if set.
1470: */
1471: if (!(tty->term->flags & TERM_88COLOURS) &&
1472: !(tty->term_flags & TERM_88COLOURS) &&
1473: !(tty->term->flags & TERM_256COLOURS) &&
1474: !(tty->term_flags & TERM_256COLOURS)) {
1475: gc->bg = colour_256to16(gc->bg);
1476: if (gc->bg & 8)
1477: gc->bg &= 7;
1478: gc->attr &= ~GRID_ATTR_BRIGHT;
1479: gc->flags &= ~GRID_FLAG_BG256;
1480: }
1481: return;
1482: }
1483:
1484: /* Is this an aixterm colour? */
1485: colours = tty_term_number(tty->term, TTYC_COLORS);
1.112 nicm 1486: if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) {
1.85 nicm 1487: gc->bg -= 90;
1488: gc->attr |= GRID_ATTR_BRIGHT;
1489: }
1490: }
1491:
1492: void
1493: tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
1.1 nicm 1494: {
1.61 nicm 1495: struct grid_cell *tc = &tty->cell;
1496: u_char fg = gc->fg;
1.79 nicm 1497: char s[32];
1.1 nicm 1498:
1.61 nicm 1499: /* Is this a 256-colour colour? */
1.1 nicm 1500: if (gc->flags & GRID_FLAG_FG256) {
1.61 nicm 1501: /* Try as 256 colours or translating to 88. */
1.1 nicm 1502: if (tty_try_256(tty, fg, "38") == 0)
1.61 nicm 1503: goto save_fg;
1.1 nicm 1504: if (tty_try_88(tty, fg, "38") == 0)
1.61 nicm 1505: goto save_fg;
1.85 nicm 1506: /* Else already handled by tty_check_fg. */
1507: return;
1.1 nicm 1508: }
1509:
1.79 nicm 1510: /* Is this an aixterm bright colour? */
1511: if (fg >= 90 && fg <= 97) {
1.85 nicm 1512: xsnprintf(s, sizeof s, "\033[%dm", fg);
1513: tty_puts(tty, s);
1514: goto save_fg;
1.79 nicm 1515: }
1516:
1.61 nicm 1517: /* Otherwise set the foreground colour. */
1518: tty_putcode1(tty, TTYC_SETAF, fg);
1519:
1.77 nicm 1520: save_fg:
1.61 nicm 1521: /* Save the new values in the terminal current cell. */
1522: tc->fg = fg;
1523: tc->flags &= ~GRID_FLAG_FG256;
1524: tc->flags |= gc->flags & GRID_FLAG_FG256;
1.1 nicm 1525: }
1526:
1527: void
1.73 nicm 1528: tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
1.1 nicm 1529: {
1.61 nicm 1530: struct grid_cell *tc = &tty->cell;
1531: u_char bg = gc->bg;
1.79 nicm 1532: char s[32];
1.1 nicm 1533:
1.61 nicm 1534: /* Is this a 256-colour colour? */
1.1 nicm 1535: if (gc->flags & GRID_FLAG_BG256) {
1.61 nicm 1536: /* Try as 256 colours or translating to 88. */
1.1 nicm 1537: if (tty_try_256(tty, bg, "48") == 0)
1.61 nicm 1538: goto save_bg;
1.1 nicm 1539: if (tty_try_88(tty, bg, "48") == 0)
1.61 nicm 1540: goto save_bg;
1.85 nicm 1541: /* Else already handled by tty_check_bg. */
1542: return;
1.79 nicm 1543: }
1544:
1545: /* Is this an aixterm bright colour? */
1.112 nicm 1546: if (bg >= 90 && bg <= 97) {
1.79 nicm 1547: /* 16 colour terminals or above only. */
1548: if (tty_term_number(tty->term, TTYC_COLORS) >= 16) {
1.112 nicm 1549: xsnprintf(s, sizeof s, "\033[%dm", bg + 10);
1.79 nicm 1550: tty_puts(tty, s);
1551: goto save_bg;
1552: }
1.112 nicm 1553: bg -= 90;
1.79 nicm 1554: /* no such thing as a bold background */
1.1 nicm 1555: }
1556:
1.61 nicm 1557: /* Otherwise set the background colour. */
1558: tty_putcode1(tty, TTYC_SETAB, bg);
1559:
1560: save_bg:
1561: /* Save the new values in the terminal current cell. */
1562: tc->bg = bg;
1563: tc->flags &= ~GRID_FLAG_BG256;
1564: tc->flags |= gc->flags & GRID_FLAG_BG256;
1565: }
1566:
1567: int
1568: tty_try_256(struct tty *tty, u_char colour, const char *type)
1569: {
1570: char s[32];
1571:
1572: if (!(tty->term->flags & TERM_256COLOURS) &&
1573: !(tty->term_flags & TERM_256COLOURS))
1574: return (-1);
1575:
1576: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1577: tty_puts(tty, s);
1578: return (0);
1579: }
1580:
1581: int
1582: tty_try_88(struct tty *tty, u_char colour, const char *type)
1583: {
1584: char s[32];
1585:
1586: if (!(tty->term->flags & TERM_88COLOURS) &&
1587: !(tty->term_flags & TERM_88COLOURS))
1588: return (-1);
1589: colour = colour_256to88(colour);
1590:
1591: xsnprintf(s, sizeof s, "\033[%s;5;%hhum", type, colour);
1592: tty_puts(tty, s);
1593: return (0);
1.110 nicm 1594: }
1595:
1596: void
1597: tty_bell(struct tty *tty)
1598: {
1599: tty_putcode(tty, TTYC_BEL);
1.1 nicm 1600: }