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