4 * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
31 void tty_read_callback(struct bufferevent
*, void *);
32 void tty_error_callback(struct bufferevent
*, short, void *);
34 void tty_fill_acs(struct tty
*);
36 int tty_try_256(struct tty
*, u_char
, const char *);
37 int tty_try_88(struct tty
*, u_char
, const char *);
39 void tty_colours(struct tty
*, const struct grid_cell
*, u_char
*);
40 void tty_colours_fg(struct tty
*, const struct grid_cell
*, u_char
*);
41 void tty_colours_bg(struct tty
*, const struct grid_cell
*);
43 void tty_redraw_region(struct tty
*, const struct tty_ctx
*);
44 void tty_emulate_repeat(
45 struct tty
*, enum tty_code_code
, enum tty_code_code
, u_int
);
46 void tty_cell(struct tty
*,
47 const struct grid_cell
*, const struct grid_utf8
*);
50 tty_init(struct tty
*tty
, int fd
, char *term
)
54 memset(tty
, 0, sizeof *tty
);
57 if (term
== NULL
|| *term
== '\0')
58 tty
->termname
= xstrdup("unknown");
60 tty
->termname
= xstrdup(term
);
62 if (fcntl(fd
, F_SETFD
, FD_CLOEXEC
) == -1)
63 fatal("fcntl failed");
66 if ((path
= ttyname(fd
)) == NULL
)
67 fatalx("ttyname failed");
68 tty
->path
= xstrdup(path
);
75 tty_resize(struct tty
*tty
)
79 if (ioctl(tty
->fd
, TIOCGWINSZ
, &ws
) != -1) {
91 tty
->rupper
= UINT_MAX
;
92 tty
->rlower
= UINT_MAX
;
96 tty_open(struct tty
*tty
, const char *overrides
, char **cause
)
100 if (debug_level
> 3) {
101 fd
= open("tmux.out", O_WRONLY
|O_CREAT
|O_TRUNC
, 0644);
102 if (fd
!= -1 && fcntl(fd
, F_SETFD
, FD_CLOEXEC
) == -1)
103 fatal("fcntl failed");
107 tty
->term
= tty_term_find(tty
->termname
, tty
->fd
, overrides
, cause
);
108 if (tty
->term
== NULL
) {
112 tty
->flags
|= TTY_OPENED
;
114 tty
->flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
|TTY_ESCAPE
);
116 tty
->event
= bufferevent_new(
117 tty
->fd
, tty_read_callback
, NULL
, tty_error_callback
, tty
);
130 tty_read_callback(unused
struct bufferevent
*bufev
, void *data
)
132 struct tty
*tty
= data
;
134 while (tty_keys_next(tty
))
141 unused
struct bufferevent
*bufev
, unused
short what
, unused
void *data
)
146 tty_start_tty(struct tty
*tty
)
154 if ((mode
= fcntl(tty
->fd
, F_GETFL
)) == -1)
155 fatal("fcntl failed");
156 if (fcntl(tty
->fd
, F_SETFL
, mode
|O_NONBLOCK
) == -1)
157 fatal("fcntl failed");
159 bufferevent_enable(tty
->event
, EV_READ
|EV_WRITE
);
161 if (tcgetattr(tty
->fd
, &tty
->tio
) != 0)
162 fatal("tcgetattr failed");
163 memcpy(&tio
, &tty
->tio
, sizeof tio
);
164 tio
.c_iflag
&= ~(IXON
|IXOFF
|ICRNL
|INLCR
|IGNCR
|IMAXBEL
|ISTRIP
);
165 tio
.c_iflag
|= IGNBRK
;
166 tio
.c_oflag
&= ~(OPOST
|ONLCR
|OCRNL
|ONLRET
);
167 tio
.c_lflag
&= ~(IEXTEN
|ICANON
|ECHO
|ECHOE
|ECHONL
|ECHOCTL
|
168 ECHOPRT
|ECHOKE
|ECHOCTL
|ISIG
);
171 if (tcsetattr(tty
->fd
, TCSANOW
, &tio
) != 0)
172 fatal("tcsetattr failed");
173 tcflush(tty
->fd
, TCIOFLUSH
);
175 tty_putcode(tty
, TTYC_SMCUP
);
177 tty_putcode(tty
, TTYC_SGR0
);
178 memcpy(&tty
->cell
, &grid_default_cell
, sizeof tty
->cell
);
180 tty_putcode(tty
, TTYC_RMKX
);
181 tty_putcode(tty
, TTYC_ENACS
);
182 tty_putcode(tty
, TTYC_CLEAR
);
184 tty_putcode(tty
, TTYC_CNORM
);
185 if (tty_term_has(tty
->term
, TTYC_KMOUS
))
186 tty_puts(tty
, "\033[?1000l");
191 tty
->rlower
= UINT_MAX
;
192 tty
->rupper
= UINT_MAX
;
194 tty
->mode
= MODE_CURSOR
;
196 tty
->flags
|= TTY_STARTED
;
200 tty_stop_tty(struct tty
*tty
)
205 if (!(tty
->flags
& TTY_STARTED
))
207 tty
->flags
&= ~TTY_STARTED
;
209 bufferevent_disable(tty
->event
, EV_READ
|EV_WRITE
);
212 * Be flexible about error handling and try not kill the server just
213 * because the fd is invalid. Things like ssh -t can easily leave us
216 if ((mode
= fcntl(tty
->fd
, F_GETFL
)) == -1)
218 if (fcntl(tty
->fd
, F_SETFL
, mode
& ~O_NONBLOCK
) == -1)
220 if (ioctl(tty
->fd
, TIOCGWINSZ
, &ws
) == -1)
222 if (tcsetattr(tty
->fd
, TCSANOW
, &tty
->tio
) == -1)
225 tty_raw(tty
, tty_term_string2(tty
->term
, TTYC_CSR
, 0, ws
.ws_row
- 1));
226 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMACS
));
227 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_SGR0
));
228 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMKX
));
229 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CLEAR
));
231 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CNORM
));
232 if (tty_term_has(tty
->term
, TTYC_KMOUS
))
233 tty_raw(tty
, "\033[?1000l");
235 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMCUP
));
239 tty_fill_acs(struct tty
*tty
)
243 memset(tty
->acs
, 0, sizeof tty
->acs
);
244 if (!tty_term_has(tty
->term
, TTYC_ACSC
))
247 ptr
= tty_term_string(tty
->term
, TTYC_ACSC
);
248 if (strlen(ptr
) % 2 != 0)
250 for (; *ptr
!= '\0'; ptr
+= 2)
251 tty
->acs
[(u_char
) ptr
[0]] = ptr
[1];
255 tty_get_acs(struct tty
*tty
, u_char ch
)
257 if (tty
->acs
[ch
] != '\0')
258 return (tty
->acs
[ch
]);
263 tty_close(struct tty
*tty
)
265 if (tty
->log_fd
!= -1) {
270 evtimer_del(&tty
->key_timer
);
273 if (tty
->flags
& TTY_OPENED
) {
274 bufferevent_free(tty
->event
);
276 tty_term_free(tty
->term
);
279 tty
->flags
&= ~TTY_OPENED
;
289 tty_free(struct tty
*tty
)
293 if (tty
->path
!= NULL
)
295 if (tty
->termname
!= NULL
)
296 xfree(tty
->termname
);
300 tty_raw(struct tty
*tty
, const char *s
)
302 write(tty
->fd
, s
, strlen(s
));
306 tty_putcode(struct tty
*tty
, enum tty_code_code code
)
308 tty_puts(tty
, tty_term_string(tty
->term
, code
));
312 tty_putcode1(struct tty
*tty
, enum tty_code_code code
, int a
)
316 tty_puts(tty
, tty_term_string1(tty
->term
, code
, a
));
320 tty_putcode2(struct tty
*tty
, enum tty_code_code code
, int a
, int b
)
324 tty_puts(tty
, tty_term_string2(tty
->term
, code
, a
, b
));
328 tty_puts(struct tty
*tty
, const char *s
)
332 bufferevent_write(tty
->event
, s
, strlen(s
));
334 if (tty
->log_fd
!= -1)
335 write(tty
->log_fd
, s
, strlen(s
));
339 tty_putc(struct tty
*tty
, u_char ch
)
343 if (tty
->cell
.attr
& GRID_ATTR_CHARSET
)
344 ch
= tty_get_acs(tty
, ch
);
345 bufferevent_write(tty
->event
, &ch
, 1);
347 if (ch
>= 0x20 && ch
!= 0x7f) {
349 if (tty
->term
->flags
& TERM_EARLYWRAP
)
354 if (tty
->cy
!= tty
->rlower
)
360 if (tty
->log_fd
!= -1)
361 write(tty
->log_fd
, &ch
, 1);
365 tty_pututf8(struct tty
*tty
, const struct grid_utf8
*gu
)
369 size
= grid_utf8_size(gu
);
370 bufferevent_write(tty
->event
, gu
->data
, size
);
371 if (tty
->log_fd
!= -1)
372 write(tty
->log_fd
, gu
->data
, size
);
373 tty
->cx
+= gu
->width
;
377 tty_set_title(struct tty
*tty
, const char *title
)
379 if (strstr(tty
->termname
, "xterm") == NULL
&&
380 strstr(tty
->termname
, "rxvt") == NULL
&&
381 strcmp(tty
->termname
, "screen") != 0)
384 tty_puts(tty
, "\033]0;");
385 tty_puts(tty
, title
);
386 tty_putc(tty
, '\007');
390 tty_update_mode(struct tty
*tty
, int mode
)
394 if (tty
->flags
& TTY_NOCURSOR
)
395 mode
&= ~MODE_CURSOR
;
397 changed
= mode
^ tty
->mode
;
398 if (changed
& MODE_CURSOR
) {
399 if (mode
& MODE_CURSOR
)
400 tty_putcode(tty
, TTYC_CNORM
);
402 tty_putcode(tty
, TTYC_CIVIS
);
404 if (changed
& MODE_MOUSE
) {
405 if (mode
& MODE_MOUSE
)
406 tty_puts(tty
, "\033[?1000h");
408 tty_puts(tty
, "\033[?1000l");
410 if (changed
& MODE_KKEYPAD
) {
411 if (mode
& MODE_KKEYPAD
)
412 tty_putcode(tty
, TTYC_SMKX
);
414 tty_putcode(tty
, TTYC_RMKX
);
421 struct tty
*tty
, enum tty_code_code code
, enum tty_code_code code1
, u_int n
)
423 if (tty_term_has(tty
->term
, code
))
424 tty_putcode1(tty
, code
, n
);
427 tty_putcode(tty
, code1
);
432 * Redraw scroll region using data from screen (already updated). Used when
433 * CSR not supported, or window is a pane that doesn't take up the full
434 * width of the terminal.
437 tty_redraw_region(struct tty
*tty
, const struct tty_ctx
*ctx
)
439 struct window_pane
*wp
= ctx
->wp
;
440 struct screen
*s
= wp
->screen
;
444 * If region is >= 50% of the screen, just schedule a window redraw. In
445 * most cases, this is likely to be followed by some more scrolling -
446 * without this, the entire pane ends up being redrawn many times which
447 * can be much more data.
449 if (ctx
->orupper
- ctx
->orlower
>= screen_size_y(s
) / 2) {
450 wp
->flags
|= PANE_REDRAW
;
454 if (ctx
->ocy
< ctx
->orupper
|| ctx
->ocy
> ctx
->orlower
) {
455 for (i
= ctx
->ocy
; i
< screen_size_y(s
); i
++)
456 tty_draw_line(tty
, s
, i
, wp
->xoff
, wp
->yoff
);
458 for (i
= ctx
->orupper
; i
<= ctx
->orlower
; i
++)
459 tty_draw_line(tty
, s
, i
, wp
->xoff
, wp
->yoff
);
464 tty_draw_line(struct tty
*tty
, struct screen
*s
, u_int py
, u_int ox
, u_int oy
)
466 const struct grid_cell
*gc
;
467 struct grid_line
*gl
;
468 struct grid_cell tmpgc
;
469 const struct grid_utf8
*gu
;
472 tty_update_mode(tty
, tty
->mode
& ~MODE_CURSOR
);
474 sx
= screen_size_x(s
);
475 if (sx
> s
->grid
->linedata
[s
->grid
->hsize
+ py
].cellsize
)
476 sx
= s
->grid
->linedata
[s
->grid
->hsize
+ py
].cellsize
;
481 * Don't move the cursor to the start permission if it will wrap there
486 gl
= &s
->grid
->linedata
[s
->grid
->hsize
+ py
- 1];
487 if (oy
+ py
== 0 || gl
== NULL
|| !(gl
->flags
& GRID_LINE_WRAPPED
) ||
488 tty
->cx
< tty
->sx
|| ox
!= 0 ||
489 (oy
+ py
!= tty
->cy
+ 1 && tty
->cy
!= s
->rlower
+ oy
))
490 tty_cursor(tty
, ox
, oy
+ py
);
492 for (i
= 0; i
< sx
; i
++) {
493 gc
= grid_view_peek_cell(s
->grid
, i
, py
);
496 if (gc
->flags
& GRID_FLAG_UTF8
)
497 gu
= grid_view_peek_utf8(s
->grid
, i
, py
);
499 if (screen_check_selection(s
, i
, py
)) {
500 memcpy(&tmpgc
, &s
->sel
.cell
, sizeof tmpgc
);
501 tmpgc
.data
= gc
->data
;
502 tmpgc
.flags
= gc
->flags
&
503 ~(GRID_FLAG_FG256
|GRID_FLAG_BG256
);
504 tmpgc
.flags
|= s
->sel
.cell
.flags
&
505 (GRID_FLAG_FG256
|GRID_FLAG_BG256
);
506 tty_cell(tty
, &tmpgc
, gu
);
508 tty_cell(tty
, gc
, gu
);
512 tty_update_mode(tty
, tty
->mode
);
517 tty_cursor(tty
, ox
+ sx
, oy
+ py
);
518 if (screen_size_x(s
) >= tty
->sx
&& tty_term_has(tty
->term
, TTYC_EL
))
519 tty_putcode(tty
, TTYC_EL
);
521 for (i
= sx
; i
< screen_size_x(s
); i
++)
524 tty_update_mode(tty
, tty
->mode
);
528 tty_write(void (*cmdfn
)(
529 struct tty
*, const struct tty_ctx
*), const struct tty_ctx
*ctx
)
531 struct window_pane
*wp
= ctx
->wp
;
535 /* wp can be NULL if updating the screen but not the terminal. */
539 if (wp
->window
->flags
& WINDOW_REDRAW
|| wp
->flags
& PANE_REDRAW
)
541 if (wp
->window
->flags
& WINDOW_HIDDEN
|| !window_pane_visible(wp
))
544 for (i
= 0; i
< ARRAY_LENGTH(&clients
); i
++) {
545 c
= ARRAY_ITEM(&clients
, i
);
546 if (c
== NULL
|| c
->session
== NULL
)
548 if (c
->flags
& CLIENT_SUSPENDED
)
551 if (c
->session
->curw
->window
== wp
->window
) {
552 if (c
->tty
.flags
& TTY_FREEZE
|| c
->tty
.term
== NULL
)
560 tty_cmd_insertcharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
562 struct window_pane
*wp
= ctx
->wp
;
563 struct screen
*s
= wp
->screen
;
566 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
) {
567 tty_draw_line(tty
, wp
->screen
, ctx
->ocy
, wp
->xoff
, wp
->yoff
);
573 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
575 if (tty_term_has(tty
->term
, TTYC_ICH
) ||
576 tty_term_has(tty
->term
, TTYC_ICH1
))
577 tty_emulate_repeat(tty
, TTYC_ICH
, TTYC_ICH1
, ctx
->num
);
578 else if (tty_term_has(tty
->term
, TTYC_SMIR
) &&
579 tty_term_has(tty
->term
, TTYC_RMIR
)) {
580 tty_putcode(tty
, TTYC_SMIR
);
581 for (i
= 0; i
< ctx
->num
; i
++)
583 tty_putcode(tty
, TTYC_RMIR
);
585 tty_draw_line(tty
, wp
->screen
, ctx
->ocy
, wp
->xoff
, wp
->yoff
);
589 tty_cmd_deletecharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
591 struct window_pane
*wp
= ctx
->wp
;
592 struct screen
*s
= wp
->screen
;
594 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
||
595 (!tty_term_has(tty
->term
, TTYC_DCH
) &&
596 !tty_term_has(tty
->term
, TTYC_DCH1
))) {
597 tty_draw_line(tty
, wp
->screen
, ctx
->ocy
, wp
->xoff
, wp
->yoff
);
603 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
605 if (tty_term_has(tty
->term
, TTYC_DCH
) ||
606 tty_term_has(tty
->term
, TTYC_DCH1
))
607 tty_emulate_repeat(tty
, TTYC_DCH
, TTYC_DCH1
, ctx
->num
);
611 tty_cmd_insertline(struct tty
*tty
, const struct tty_ctx
*ctx
)
613 struct window_pane
*wp
= ctx
->wp
;
614 struct screen
*s
= wp
->screen
;
616 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
||
617 !tty_term_has(tty
->term
, TTYC_CSR
) ||
618 !tty_term_has(tty
->term
, TTYC_IL1
)) {
619 tty_redraw_region(tty
, ctx
);
625 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
626 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
628 tty_emulate_repeat(tty
, TTYC_IL
, TTYC_IL1
, ctx
->num
);
632 tty_cmd_deleteline(struct tty
*tty
, const struct tty_ctx
*ctx
)
634 struct window_pane
*wp
= ctx
->wp
;
635 struct screen
*s
= wp
->screen
;
637 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
||
638 !tty_term_has(tty
->term
, TTYC_CSR
) ||
639 !tty_term_has(tty
->term
, TTYC_DL1
)) {
640 tty_redraw_region(tty
, ctx
);
646 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
647 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
649 tty_emulate_repeat(tty
, TTYC_DL
, TTYC_DL1
, ctx
->num
);
653 tty_cmd_clearline(struct tty
*tty
, const struct tty_ctx
*ctx
)
655 struct window_pane
*wp
= ctx
->wp
;
656 struct screen
*s
= wp
->screen
;
661 tty_cursor_pane(tty
, ctx
, 0, ctx
->ocy
);
663 if (wp
->xoff
== 0 && screen_size_x(s
) >= tty
->sx
&&
664 tty_term_has(tty
->term
, TTYC_EL
)) {
665 tty_putcode(tty
, TTYC_EL
);
667 for (i
= 0; i
< screen_size_x(s
); i
++)
673 tty_cmd_clearendofline(struct tty
*tty
, const struct tty_ctx
*ctx
)
675 struct window_pane
*wp
= ctx
->wp
;
676 struct screen
*s
= wp
->screen
;
681 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
683 if (wp
->xoff
== 0 && screen_size_x(s
) >= tty
->sx
&&
684 tty_term_has(tty
->term
, TTYC_EL
))
685 tty_putcode(tty
, TTYC_EL
);
687 for (i
= ctx
->ocx
; i
< screen_size_x(s
); i
++)
693 tty_cmd_clearstartofline(struct tty
*tty
, const struct tty_ctx
*ctx
)
695 struct window_pane
*wp
= ctx
->wp
;
700 if (wp
->xoff
== 0 && tty_term_has(tty
->term
, TTYC_EL1
)) {
701 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
702 tty_putcode(tty
, TTYC_EL1
);
704 tty_cursor_pane(tty
, ctx
, 0, ctx
->ocy
);
705 for (i
= 0; i
< ctx
->ocx
+ 1; i
++)
711 tty_cmd_reverseindex(struct tty
*tty
, const struct tty_ctx
*ctx
)
713 struct window_pane
*wp
= ctx
->wp
;
714 struct screen
*s
= wp
->screen
;
716 if (ctx
->ocy
!= ctx
->orupper
)
719 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
||
720 !tty_term_has(tty
->term
, TTYC_CSR
) ||
721 !tty_term_has(tty
->term
, TTYC_RI
)) {
722 tty_redraw_region(tty
, ctx
);
728 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
729 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->orupper
);
731 tty_putcode(tty
, TTYC_RI
);
735 tty_cmd_linefeed(struct tty
*tty
, const struct tty_ctx
*ctx
)
737 struct window_pane
*wp
= ctx
->wp
;
738 struct screen
*s
= wp
->screen
;
740 if (ctx
->ocy
!= ctx
->orlower
)
743 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
||
744 !tty_term_has(tty
->term
, TTYC_CSR
)) {
745 tty_redraw_region(tty
, ctx
);
750 * If this line wrapped naturally (ctx->num is nonzero), don't do
751 * anything - the cursor can just be moved to the last cell and wrap
754 if (ctx
->num
&& !(tty
->term
->flags
& TERM_EARLYWRAP
))
759 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
760 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
766 tty_cmd_clearendofscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
768 struct window_pane
*wp
= ctx
->wp
;
769 struct screen
*s
= wp
->screen
;
774 tty_region_pane(tty
, ctx
, 0, screen_size_y(s
) - 1);
775 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
777 if (wp
->xoff
== 0 && screen_size_x(s
) >= tty
->sx
&&
778 tty_term_has(tty
->term
, TTYC_EL
)) {
779 tty_putcode(tty
, TTYC_EL
);
780 if (ctx
->ocy
!= screen_size_y(s
) - 1) {
781 tty_cursor_pane(tty
, ctx
, 0, ctx
->ocy
+ 1);
782 for (i
= ctx
->ocy
+ 1; i
< screen_size_y(s
); i
++) {
783 tty_putcode(tty
, TTYC_EL
);
784 if (i
== screen_size_y(s
) - 1)
786 tty_emulate_repeat(tty
, TTYC_CUD
, TTYC_CUD1
, 1);
791 for (i
= ctx
->ocx
; i
< screen_size_x(s
); i
++)
793 for (j
= ctx
->ocy
+ 1; j
< screen_size_y(s
); j
++) {
794 tty_cursor_pane(tty
, ctx
, 0, j
);
795 for (i
= 0; i
< screen_size_x(s
); i
++)
802 tty_cmd_clearstartofscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
804 struct window_pane
*wp
= ctx
->wp
;
805 struct screen
*s
= wp
->screen
;
810 tty_region_pane(tty
, ctx
, 0, screen_size_y(s
) - 1);
811 tty_cursor_pane(tty
, ctx
, 0, 0);
813 if (wp
->xoff
== 0 && screen_size_x(s
) >= tty
->sx
&&
814 tty_term_has(tty
->term
, TTYC_EL
)) {
815 for (i
= 0; i
< ctx
->ocy
; i
++) {
816 tty_putcode(tty
, TTYC_EL
);
817 tty_emulate_repeat(tty
, TTYC_CUD
, TTYC_CUD1
, 1);
821 for (j
= 0; j
< ctx
->ocy
; j
++) {
822 tty_cursor_pane(tty
, ctx
, 0, j
);
823 for (i
= 0; i
< screen_size_x(s
); i
++)
827 for (i
= 0; i
<= ctx
->ocx
; i
++)
832 tty_cmd_clearscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
834 struct window_pane
*wp
= ctx
->wp
;
835 struct screen
*s
= wp
->screen
;
840 tty_region_pane(tty
, ctx
, 0, screen_size_y(s
) - 1);
841 tty_cursor_pane(tty
, ctx
, 0, 0);
843 if (wp
->xoff
== 0 && screen_size_x(s
) >= tty
->sx
&&
844 tty_term_has(tty
->term
, TTYC_EL
)) {
845 for (i
= 0; i
< screen_size_y(s
); i
++) {
846 tty_putcode(tty
, TTYC_EL
);
847 if (i
!= screen_size_y(s
) - 1) {
848 tty_emulate_repeat(tty
, TTYC_CUD
, TTYC_CUD1
, 1);
853 for (j
= 0; j
< screen_size_y(s
); j
++) {
854 tty_cursor_pane(tty
, ctx
, 0, j
);
855 for (i
= 0; i
< screen_size_x(s
); i
++)
862 tty_cmd_alignmenttest(struct tty
*tty
, const struct tty_ctx
*ctx
)
864 struct window_pane
*wp
= ctx
->wp
;
865 struct screen
*s
= wp
->screen
;
870 tty_region_pane(tty
, ctx
, 0, screen_size_y(s
) - 1);
872 for (j
= 0; j
< screen_size_y(s
); j
++) {
873 tty_cursor_pane(tty
, ctx
, 0, j
);
874 for (i
= 0; i
< screen_size_x(s
); i
++)
880 tty_cmd_cell(struct tty
*tty
, const struct tty_ctx
*ctx
)
882 struct window_pane
*wp
= ctx
->wp
;
883 struct screen
*s
= wp
->screen
;
886 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
888 /* Is the cursor in the very last position? */
889 if (ctx
->ocx
> wp
->sx
- ctx
->last_width
) {
890 if (wp
->xoff
!= 0 || wp
->sx
!= tty
->sx
) {
892 * The pane doesn't fill the entire line, the linefeed
893 * will already have happened, so just move the cursor.
895 tty_cursor_pane(tty
, ctx
, 0, ctx
->ocy
+ 1);
896 } else if (tty
->cx
< tty
->sx
) {
898 * The cursor isn't in the last position already, so
899 * move as far left as possinble and redraw the last
900 * cell to move into the last position.
902 cx
= screen_size_x(s
) - ctx
->last_width
;
903 tty_cursor_pane(tty
, ctx
, cx
, ctx
->ocy
);
904 tty_cell(tty
, &ctx
->last_cell
, &ctx
->last_utf8
);
907 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
909 tty_cell(tty
, ctx
->cell
, ctx
->utf8
);
913 tty_cmd_utf8character(struct tty
*tty
, const struct tty_ctx
*ctx
)
915 struct window_pane
*wp
= ctx
->wp
;
918 * Cannot rely on not being a partial character, so just redraw the
921 tty_draw_line(tty
, wp
->screen
, ctx
->ocy
, wp
->xoff
, wp
->yoff
);
926 struct tty
*tty
, const struct grid_cell
*gc
, const struct grid_utf8
*gu
)
930 /* Skip last character if terminal is stupid. */
931 if (tty
->term
->flags
& TERM_EARLYWRAP
&&
932 tty
->cy
== tty
->sy
- 1 && tty
->cx
== tty
->sx
- 1)
935 /* If this is a padding character, do nothing. */
936 if (gc
->flags
& GRID_FLAG_PADDING
)
939 /* Set the attributes. */
940 tty_attributes(tty
, gc
);
942 /* If not UTF-8, write directly. */
943 if (!(gc
->flags
& GRID_FLAG_UTF8
)) {
944 if (gc
->data
< 0x20 || gc
->data
== 0x7f)
946 tty_putc(tty
, gc
->data
);
950 /* If the terminal doesn't support UTF-8, write underscores. */
951 if (!(tty
->flags
& TTY_UTF8
)) {
952 for (i
= 0; i
< gu
->width
; i
++)
957 /* Otherwise, write UTF-8. */
958 tty_pututf8(tty
, gu
);
962 tty_reset(struct tty
*tty
)
964 struct grid_cell
*gc
= &tty
->cell
;
966 if (memcmp(gc
, &grid_default_cell
, sizeof *gc
) == 0)
969 if (tty_term_has(tty
->term
, TTYC_RMACS
) && gc
->attr
& GRID_ATTR_CHARSET
)
970 tty_putcode(tty
, TTYC_RMACS
);
971 tty_putcode(tty
, TTYC_SGR0
);
972 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
975 /* Set region inside pane. */
978 struct tty
*tty
, const struct tty_ctx
*ctx
, u_int rupper
, u_int rlower
)
980 struct window_pane
*wp
= ctx
->wp
;
982 tty_region(tty
, wp
->yoff
+ rupper
, wp
->yoff
+ rlower
);
985 /* Set region at absolute position. */
987 tty_region(struct tty
*tty
, u_int rupper
, u_int rlower
)
989 if (tty
->rlower
== rlower
&& tty
->rupper
== rupper
)
991 if (!tty_term_has(tty
->term
, TTYC_CSR
))
994 tty
->rupper
= rupper
;
995 tty
->rlower
= rlower
;
998 * Some terminals (such as PuTTY) do not correctly reset the cursor to
999 * 0,0 if it is beyond the last column (they do not reset their wrap
1000 * flag so further output causes a line feed). As a workaround, do an
1001 * explicit move to 0 first.
1003 if (tty
->cx
>= tty
->sx
)
1004 tty_cursor(tty
, 0, tty
->cy
);
1006 tty_putcode2(tty
, TTYC_CSR
, tty
->rupper
, tty
->rlower
);
1007 tty_cursor(tty
, 0, 0);
1010 /* Move cursor inside pane. */
1012 tty_cursor_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int cx
, u_int cy
)
1014 struct window_pane
*wp
= ctx
->wp
;
1016 tty_cursor(tty
, wp
->xoff
+ cx
, wp
->yoff
+ cy
);
1019 /* Move cursor to absolute position. */
1021 tty_cursor(struct tty
*tty
, u_int cx
, u_int cy
)
1023 struct tty_term
*term
= tty
->term
;
1027 if (cx
> tty
->sx
- 1)
1034 if (cx
== thisx
&& cy
== thisy
)
1037 /* Very end of the line, just use absolute movement. */
1038 if (thisx
> tty
->sx
- 1)
1041 /* Move to home position (0, 0). */
1042 if (cx
== 0 && cy
== 0 && tty_term_has(term
, TTYC_HOME
)) {
1043 tty_putcode(tty
, TTYC_HOME
);
1047 /* Zero on the next line. */
1048 if (cx
== 0 && cy
== thisy
+ 1 && thisy
!= tty
->rlower
) {
1049 tty_putc(tty
, '\r');
1050 tty_putc(tty
, '\n');
1054 /* Moving column or row. */
1057 * Moving column only, row staying the same.
1062 tty_putc(tty
, '\r');
1066 /* One to the left. */
1067 if (cx
== thisx
- 1 && tty_term_has(term
, TTYC_CUB1
)) {
1068 tty_putcode(tty
, TTYC_CUB1
);
1072 /* One to the right. */
1073 if (cx
== thisx
+ 1 && tty_term_has(term
, TTYC_CUF1
)) {
1074 tty_putcode(tty
, TTYC_CUF1
);
1078 /* Calculate difference. */
1079 change
= thisx
- cx
; /* +ve left, -ve right */
1082 * Use HPA if change is larger than absolute, otherwise move
1083 * the cursor with CUB/CUF.
1085 if (abs(change
) > cx
&& tty_term_has(term
, TTYC_HPA
)) {
1086 tty_putcode1(tty
, TTYC_HPA
, cx
);
1088 } else if (change
> 0 && tty_term_has(term
, TTYC_CUB
)) {
1089 tty_putcode1(tty
, TTYC_CUB
, change
);
1091 } else if (change
< 0 && tty_term_has(term
, TTYC_CUF
)) {
1092 tty_putcode1(tty
, TTYC_CUF
, -change
);
1095 } else if (cx
== thisx
) {
1097 * Moving row only, column staying the same.
1101 if (thisy
!= tty
->rupper
&&
1102 cy
== thisy
- 1 && tty_term_has(term
, TTYC_CUU1
)) {
1103 tty_putcode(tty
, TTYC_CUU1
);
1108 if (thisy
!= tty
->rlower
&&
1109 cy
== thisy
+ 1 && tty_term_has(term
, TTYC_CUD1
)) {
1110 tty_putcode(tty
, TTYC_CUD1
);
1114 /* Calculate difference. */
1115 change
= thisy
- cy
; /* +ve up, -ve down */
1118 * Try to use VPA if change is larger than absolute or if this
1119 * change would cross the scroll region, otherwise use CUU/CUD.
1121 if (abs(change
) > cy
||
1122 (change
< 0 && cy
- change
> tty
->rlower
) ||
1123 (change
> 0 && cy
- change
< tty
->rupper
)) {
1124 if (tty_term_has(term
, TTYC_VPA
)) {
1125 tty_putcode1(tty
, TTYC_VPA
, cy
);
1128 } else if (change
> 0 && tty_term_has(term
, TTYC_CUU
)) {
1129 tty_putcode1(tty
, TTYC_CUU
, change
);
1131 } else if (change
< 0 && tty_term_has(term
, TTYC_CUD
)) {
1132 tty_putcode1(tty
, TTYC_CUD
, -change
);
1138 /* Absolute movement. */
1139 tty_putcode2(tty
, TTYC_CUP
, cy
, cx
);
1147 tty_attributes(struct tty
*tty
, const struct grid_cell
*gc
)
1149 struct grid_cell
*tc
= &tty
->cell
, gc2
;
1150 u_char changed
, new_attr
;
1152 /* If the character is space, don't care about foreground. */
1153 if (gc
->data
== ' ' && !(gc
->flags
& GRID_FLAG_UTF8
)) {
1154 memcpy(&gc2
, gc
, sizeof gc2
);
1155 if (gc
->attr
& GRID_ATTR_REVERSE
)
1163 * If no setab, try to use the reverse attribute as a best-effort for a
1164 * non-default background. This is a bit of a hack but it doesn't do
1165 * any serious harm and makes a couple of applications happier.
1167 if (!tty_term_has(tty
->term
, TTYC_SETAB
)) {
1169 memcpy(&gc2
, gc
, sizeof gc2
);
1173 if (gc
->attr
& GRID_ATTR_REVERSE
) {
1174 if (gc
->fg
!= 7 && gc
->fg
!= 8)
1175 gc2
.attr
&= ~GRID_ATTR_REVERSE
;
1177 if (gc
->bg
!= 0 && gc
->bg
!= 8)
1178 gc2
.attr
|= GRID_ATTR_REVERSE
;
1182 /* If any bits are being cleared, reset everything. */
1183 if (tc
->attr
& ~gc
->attr
)
1187 * Set the colours. This may call tty_reset() (so it comes next) and
1188 * may add to (NOT remove) the desired attributes by changing new_attr.
1190 new_attr
= gc
->attr
;
1191 tty_colours(tty
, gc
, &new_attr
);
1193 /* Filter out attribute bits already set. */
1194 changed
= new_attr
& ~tc
->attr
;
1195 tc
->attr
= new_attr
;
1197 /* Set the attributes. */
1198 if (changed
& GRID_ATTR_BRIGHT
)
1199 tty_putcode(tty
, TTYC_BOLD
);
1200 if (changed
& GRID_ATTR_DIM
)
1201 tty_putcode(tty
, TTYC_DIM
);
1202 if (changed
& GRID_ATTR_ITALICS
)
1203 tty_putcode(tty
, TTYC_SMSO
);
1204 if (changed
& GRID_ATTR_UNDERSCORE
)
1205 tty_putcode(tty
, TTYC_SMUL
);
1206 if (changed
& GRID_ATTR_BLINK
)
1207 tty_putcode(tty
, TTYC_BLINK
);
1208 if (changed
& GRID_ATTR_REVERSE
) {
1209 if (tty_term_has(tty
->term
, TTYC_REV
))
1210 tty_putcode(tty
, TTYC_REV
);
1211 else if (tty_term_has(tty
->term
, TTYC_SMSO
))
1212 tty_putcode(tty
, TTYC_SMSO
);
1214 if (changed
& GRID_ATTR_HIDDEN
)
1215 tty_putcode(tty
, TTYC_INVIS
);
1216 if (changed
& GRID_ATTR_CHARSET
)
1217 tty_putcode(tty
, TTYC_SMACS
);
1221 tty_colours(struct tty
*tty
, const struct grid_cell
*gc
, u_char
*attr
)
1223 struct grid_cell
*tc
= &tty
->cell
;
1224 u_char fg
= gc
->fg
, bg
= gc
->bg
, flags
= gc
->flags
;
1225 int have_ax
, fg_default
, bg_default
;
1227 /* No changes? Nothing is necessary. */
1228 if (fg
== tc
->fg
&& bg
== tc
->bg
&&
1229 ((flags
^ tc
->flags
) & (GRID_FLAG_FG256
|GRID_FLAG_BG256
)) == 0)
1233 * Is either the default colour? This is handled specially because the
1234 * best solution might be to reset both colours to default, in which
1235 * case if only one is default need to fall onward to set the other
1238 fg_default
= (fg
== 8 && !(flags
& GRID_FLAG_FG256
));
1239 bg_default
= (bg
== 8 && !(flags
& GRID_FLAG_BG256
));
1240 if (fg_default
|| bg_default
) {
1242 * If don't have AX but do have op, send sgr0 (op can't
1243 * actually be used because it is sometimes the same as sgr0
1244 * and sometimes isn't). This resets both colours to default.
1246 * Otherwise, try to set the default colour only as needed.
1248 have_ax
= tty_term_has(tty
->term
, TTYC_AX
);
1249 if (!have_ax
&& tty_term_has(tty
->term
, TTYC_OP
))
1253 (tc
->fg
!= 8 || tc
->flags
& GRID_FLAG_FG256
)) {
1255 tty_puts(tty
, "\033[39m");
1256 else if (tc
->fg
!= 7 ||
1257 tc
->flags
& GRID_FLAG_FG256
)
1258 tty_putcode1(tty
, TTYC_SETAF
, 7);
1260 tc
->flags
&= ~GRID_FLAG_FG256
;
1263 (tc
->bg
!= 8 || tc
->flags
& GRID_FLAG_BG256
)) {
1265 tty_puts(tty
, "\033[49m");
1266 else if (tc
->bg
!= 0 ||
1267 tc
->flags
& GRID_FLAG_BG256
)
1268 tty_putcode1(tty
, TTYC_SETAB
, 0);
1270 tc
->flags
&= ~GRID_FLAG_BG256
;
1275 /* Set the foreground colour. */
1276 if (!fg_default
&& (fg
!= tc
->fg
||
1277 ((flags
& GRID_FLAG_FG256
) != (tc
->flags
& GRID_FLAG_FG256
))))
1278 tty_colours_fg(tty
, gc
, attr
);
1281 * Set the background colour. This must come after the foreground as
1282 * tty_colour_fg() can call tty_reset().
1284 if (!bg_default
&& (bg
!= tc
->bg
||
1285 ((flags
& GRID_FLAG_BG256
) != (tc
->flags
& GRID_FLAG_BG256
))))
1286 tty_colours_bg(tty
, gc
);
1290 tty_colours_fg(struct tty
*tty
, const struct grid_cell
*gc
, u_char
*attr
)
1292 struct grid_cell
*tc
= &tty
->cell
;
1296 /* Is this a 256-colour colour? */
1297 if (gc
->flags
& GRID_FLAG_FG256
) {
1298 /* Try as 256 colours or translating to 88. */
1299 if (tty_try_256(tty
, fg
, "38") == 0)
1301 if (tty_try_88(tty
, fg
, "38") == 0)
1304 /* Translate to 16-colour palette, updating bold if needed. */
1305 fg
= colour_256to16(fg
);
1308 (*attr
) |= GRID_ATTR_BRIGHT
;
1310 tty_reset(tty
); /* turn off bold */
1313 /* Is this an aixterm bright colour? */
1314 if (fg
>= 90 && fg
<= 97) {
1315 /* 16 colour terminals or above only. */
1316 if (tty_term_number(tty
->term
, TTYC_COLORS
) >= 16) {
1317 xsnprintf(s
, sizeof s
, "\033[%dm", fg
);
1322 (*attr
) |= GRID_ATTR_BRIGHT
;
1325 /* Otherwise set the foreground colour. */
1326 tty_putcode1(tty
, TTYC_SETAF
, fg
);
1329 /* Save the new values in the terminal current cell. */
1331 tc
->flags
&= ~GRID_FLAG_FG256
;
1332 tc
->flags
|= gc
->flags
& GRID_FLAG_FG256
;
1336 tty_colours_bg(struct tty
*tty
, const struct grid_cell
*gc
)
1338 struct grid_cell
*tc
= &tty
->cell
;
1342 /* Is this a 256-colour colour? */
1343 if (gc
->flags
& GRID_FLAG_BG256
) {
1344 /* Try as 256 colours or translating to 88. */
1345 if (tty_try_256(tty
, bg
, "48") == 0)
1347 if (tty_try_88(tty
, bg
, "48") == 0)
1351 * Translate to 16-colour palette. Bold background doesn't
1352 * exist portably, so just discard the bold bit if set.
1354 bg
= colour_256to16(bg
);
1359 /* Is this an aixterm bright colour? */
1360 if (bg
>= 100 && bg
<= 107) {
1361 /* 16 colour terminals or above only. */
1362 if (tty_term_number(tty
->term
, TTYC_COLORS
) >= 16) {
1363 xsnprintf(s
, sizeof s
, "\033[%dm", bg
);
1368 /* no such thing as a bold background */
1371 /* Otherwise set the background colour. */
1372 tty_putcode1(tty
, TTYC_SETAB
, bg
);
1375 /* Save the new values in the terminal current cell. */
1377 tc
->flags
&= ~GRID_FLAG_BG256
;
1378 tc
->flags
|= gc
->flags
& GRID_FLAG_BG256
;
1382 tty_try_256(struct tty
*tty
, u_char colour
, const char *type
)
1386 if (!(tty
->term
->flags
& TERM_256COLOURS
) &&
1387 !(tty
->term_flags
& TERM_256COLOURS
))
1390 xsnprintf(s
, sizeof s
, "\033[%s;5;%hhum", type
, colour
);
1396 tty_try_88(struct tty
*tty
, u_char colour
, const char *type
)
1400 if (!(tty
->term
->flags
& TERM_88COLOURS
) &&
1401 !(tty
->term_flags
& TERM_88COLOURS
))
1403 colour
= colour_256to88(colour
);
1405 xsnprintf(s
, sizeof s
, "\033[%s;5;%hhum", type
, colour
);