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
*);
40 void tty_check_fg(struct tty
*, struct grid_cell
*);
41 void tty_check_bg(struct tty
*, struct grid_cell
*);
42 void tty_colours_fg(struct tty
*, const struct grid_cell
*);
43 void tty_colours_bg(struct tty
*, const struct grid_cell
*);
45 void tty_redraw_region(struct tty
*, const struct tty_ctx
*);
46 void tty_emulate_repeat(
47 struct tty
*, enum tty_code_code
, enum tty_code_code
, u_int
);
48 void tty_cell(struct tty
*,
49 const struct grid_cell
*, const struct grid_utf8
*);
52 tty_init(struct tty
*tty
, int fd
, char *term
)
56 memset(tty
, 0, sizeof *tty
);
59 if (term
== NULL
|| *term
== '\0')
60 tty
->termname
= xstrdup("unknown");
62 tty
->termname
= xstrdup(term
);
64 if (fcntl(fd
, F_SETFD
, FD_CLOEXEC
) == -1)
65 fatal("fcntl failed");
68 if ((path
= ttyname(fd
)) == NULL
)
69 fatalx("ttyname failed");
70 tty
->path
= xstrdup(path
);
77 tty_resize(struct tty
*tty
)
82 if (ioctl(tty
->fd
, TIOCGWINSZ
, &ws
) != -1) {
93 if (sx
== tty
->sx
&& sy
== tty
->sy
)
101 tty
->rupper
= UINT_MAX
;
102 tty
->rlower
= UINT_MAX
;
105 * If the terminal has been started, reset the actual scroll region and
106 * cursor position, as this may not have happened.
108 if (tty
->flags
& TTY_STARTED
) {
109 tty_cursor(tty
, 0, 0);
110 tty_region(tty
, 0, tty
->sy
- 1);
117 tty_open(struct tty
*tty
, const char *overrides
, char **cause
)
122 if (debug_level
> 3) {
123 xsnprintf(out
, sizeof out
, "tmux-out-%ld.log", (long) getpid());
124 fd
= open(out
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0644);
125 if (fd
!= -1 && fcntl(fd
, F_SETFD
, FD_CLOEXEC
) == -1)
126 fatal("fcntl failed");
130 tty
->term
= tty_term_find(tty
->termname
, tty
->fd
, overrides
, cause
);
131 if (tty
->term
== NULL
) {
135 tty
->flags
|= TTY_OPENED
;
137 tty
->flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
|TTY_ESCAPE
);
139 tty
->event
= bufferevent_new(
140 tty
->fd
, tty_read_callback
, NULL
, tty_error_callback
, tty
);
153 tty_read_callback(unused
struct bufferevent
*bufev
, void *data
)
155 struct tty
*tty
= data
;
157 while (tty_keys_next(tty
))
164 unused
struct bufferevent
*bufev
, unused
short what
, unused
void *data
)
169 tty_start_tty(struct tty
*tty
)
177 if ((mode
= fcntl(tty
->fd
, F_GETFL
)) == -1)
178 fatal("fcntl failed");
179 if (fcntl(tty
->fd
, F_SETFL
, mode
|O_NONBLOCK
) == -1)
180 fatal("fcntl failed");
182 bufferevent_enable(tty
->event
, EV_READ
|EV_WRITE
);
184 if (tcgetattr(tty
->fd
, &tty
->tio
) != 0)
185 fatal("tcgetattr failed");
186 memcpy(&tio
, &tty
->tio
, sizeof tio
);
187 tio
.c_iflag
&= ~(IXON
|IXOFF
|ICRNL
|INLCR
|IGNCR
|IMAXBEL
|ISTRIP
);
188 tio
.c_iflag
|= IGNBRK
;
189 tio
.c_oflag
&= ~(OPOST
|ONLCR
|OCRNL
|ONLRET
);
190 tio
.c_lflag
&= ~(IEXTEN
|ICANON
|ECHO
|ECHOE
|ECHONL
|ECHOCTL
|
191 ECHOPRT
|ECHOKE
|ECHOCTL
|ISIG
);
194 if (tcsetattr(tty
->fd
, TCSANOW
, &tio
) != 0)
195 fatal("tcsetattr failed");
196 tcflush(tty
->fd
, TCIOFLUSH
);
198 tty_putcode(tty
, TTYC_SMCUP
);
200 tty_putcode(tty
, TTYC_SGR0
);
201 memcpy(&tty
->cell
, &grid_default_cell
, sizeof tty
->cell
);
203 tty_putcode(tty
, TTYC_RMKX
);
204 tty_putcode(tty
, TTYC_ENACS
);
205 tty_putcode(tty
, TTYC_CLEAR
);
207 tty_putcode(tty
, TTYC_CNORM
);
208 if (tty_term_has(tty
->term
, TTYC_KMOUS
))
209 tty_puts(tty
, "\033[?1000l");
214 tty
->rlower
= UINT_MAX
;
215 tty
->rupper
= UINT_MAX
;
217 tty
->mode
= MODE_CURSOR
;
219 tty
->flags
|= TTY_STARTED
;
223 tty_stop_tty(struct tty
*tty
)
228 if (!(tty
->flags
& TTY_STARTED
))
230 tty
->flags
&= ~TTY_STARTED
;
232 bufferevent_disable(tty
->event
, EV_READ
|EV_WRITE
);
235 * Be flexible about error handling and try not kill the server just
236 * because the fd is invalid. Things like ssh -t can easily leave us
239 if (ioctl(tty
->fd
, TIOCGWINSZ
, &ws
) == -1)
241 if (tcsetattr(tty
->fd
, TCSANOW
, &tty
->tio
) == -1)
244 tty_raw(tty
, tty_term_string2(tty
->term
, TTYC_CSR
, 0, ws
.ws_row
- 1));
245 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMACS
));
246 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_SGR0
));
247 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMKX
));
248 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CLEAR
));
250 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CNORM
));
251 if (tty_term_has(tty
->term
, TTYC_KMOUS
))
252 tty_raw(tty
, "\033[?1000l");
254 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMCUP
));
256 if ((mode
= fcntl(tty
->fd
, F_GETFL
)) != -1)
257 fcntl(tty
->fd
, F_SETFL
, mode
& ~O_NONBLOCK
);
261 tty_fill_acs(struct tty
*tty
)
265 memset(tty
->acs
, 0, sizeof tty
->acs
);
266 if (!tty_term_has(tty
->term
, TTYC_ACSC
))
269 ptr
= tty_term_string(tty
->term
, TTYC_ACSC
);
270 if (strlen(ptr
) % 2 != 0)
272 for (; *ptr
!= '\0'; ptr
+= 2)
273 tty
->acs
[(u_char
) ptr
[0]] = ptr
[1];
277 tty_get_acs(struct tty
*tty
, u_char ch
)
279 if (tty
->acs
[ch
] != '\0')
280 return (tty
->acs
[ch
]);
285 tty_close(struct tty
*tty
)
287 if (tty
->log_fd
!= -1) {
292 evtimer_del(&tty
->key_timer
);
295 if (tty
->flags
& TTY_OPENED
) {
296 bufferevent_free(tty
->event
);
298 tty_term_free(tty
->term
);
301 tty
->flags
&= ~TTY_OPENED
;
311 tty_free(struct tty
*tty
)
315 if (tty
->path
!= NULL
)
317 if (tty
->termname
!= NULL
)
318 xfree(tty
->termname
);
322 tty_raw(struct tty
*tty
, const char *s
)
324 write(tty
->fd
, s
, strlen(s
));
328 tty_putcode(struct tty
*tty
, enum tty_code_code code
)
330 tty_puts(tty
, tty_term_string(tty
->term
, code
));
334 tty_putcode1(struct tty
*tty
, enum tty_code_code code
, int a
)
338 tty_puts(tty
, tty_term_string1(tty
->term
, code
, a
));
342 tty_putcode2(struct tty
*tty
, enum tty_code_code code
, int a
, int b
)
346 tty_puts(tty
, tty_term_string2(tty
->term
, code
, a
, b
));
350 tty_puts(struct tty
*tty
, const char *s
)
354 bufferevent_write(tty
->event
, s
, strlen(s
));
356 if (tty
->log_fd
!= -1)
357 write(tty
->log_fd
, s
, strlen(s
));
361 tty_putc(struct tty
*tty
, u_char ch
)
365 if (tty
->cell
.attr
& GRID_ATTR_CHARSET
)
366 ch
= tty_get_acs(tty
, ch
);
367 bufferevent_write(tty
->event
, &ch
, 1);
369 if (ch
>= 0x20 && ch
!= 0x7f) {
371 if (tty
->term
->flags
& TERM_EARLYWRAP
)
376 if (tty
->cy
!= tty
->rlower
)
382 if (tty
->log_fd
!= -1)
383 write(tty
->log_fd
, &ch
, 1);
387 tty_pututf8(struct tty
*tty
, const struct grid_utf8
*gu
)
391 size
= grid_utf8_size(gu
);
392 bufferevent_write(tty
->event
, gu
->data
, size
);
393 if (tty
->log_fd
!= -1)
394 write(tty
->log_fd
, gu
->data
, size
);
395 tty
->cx
+= gu
->width
;
399 tty_set_title(struct tty
*tty
, const char *title
)
401 if (strstr(tty
->termname
, "xterm") == NULL
&&
402 strstr(tty
->termname
, "rxvt") == NULL
&&
403 strcmp(tty
->termname
, "screen") != 0)
406 tty_puts(tty
, "\033]0;");
407 tty_puts(tty
, title
);
408 tty_putc(tty
, '\007');
412 tty_update_mode(struct tty
*tty
, int mode
)
416 if (tty
->flags
& TTY_NOCURSOR
)
417 mode
&= ~MODE_CURSOR
;
419 changed
= mode
^ tty
->mode
;
420 if (changed
& MODE_CURSOR
) {
421 if (mode
& MODE_CURSOR
)
422 tty_putcode(tty
, TTYC_CNORM
);
424 tty_putcode(tty
, TTYC_CIVIS
);
426 if (changed
& (MODE_MOUSE
|MODE_MOUSEMOTION
)) {
427 if (mode
& MODE_MOUSE
) {
428 if (mode
& MODE_MOUSEMOTION
)
429 tty_puts(tty
, "\033[?1003h");
431 tty_puts(tty
, "\033[?1000h");
433 if (mode
& MODE_MOUSEMOTION
)
434 tty_puts(tty
, "\033[?1003l");
436 tty_puts(tty
, "\033[?1000l");
439 if (changed
& MODE_KKEYPAD
) {
440 if (mode
& MODE_KKEYPAD
)
441 tty_putcode(tty
, TTYC_SMKX
);
443 tty_putcode(tty
, TTYC_RMKX
);
450 struct tty
*tty
, enum tty_code_code code
, enum tty_code_code code1
, u_int n
)
452 if (tty_term_has(tty
->term
, code
))
453 tty_putcode1(tty
, code
, n
);
456 tty_putcode(tty
, code1
);
461 * Redraw scroll region using data from screen (already updated). Used when
462 * CSR not supported, or window is a pane that doesn't take up the full
463 * width of the terminal.
466 tty_redraw_region(struct tty
*tty
, const struct tty_ctx
*ctx
)
468 struct window_pane
*wp
= ctx
->wp
;
469 struct screen
*s
= wp
->screen
;
473 * If region is >= 50% of the screen, just schedule a window redraw. In
474 * most cases, this is likely to be followed by some more scrolling -
475 * without this, the entire pane ends up being redrawn many times which
476 * can be much more data.
478 if (ctx
->orupper
- ctx
->orlower
>= screen_size_y(s
) / 2) {
479 wp
->flags
|= PANE_REDRAW
;
483 if (ctx
->ocy
< ctx
->orupper
|| ctx
->ocy
> ctx
->orlower
) {
484 for (i
= ctx
->ocy
; i
< screen_size_y(s
); i
++)
485 tty_draw_line(tty
, s
, i
, wp
->xoff
, wp
->yoff
);
487 for (i
= ctx
->orupper
; i
<= ctx
->orlower
; i
++)
488 tty_draw_line(tty
, s
, i
, wp
->xoff
, wp
->yoff
);
493 tty_draw_line(struct tty
*tty
, struct screen
*s
, u_int py
, u_int ox
, u_int oy
)
495 const struct grid_cell
*gc
;
496 struct grid_line
*gl
;
497 struct grid_cell tmpgc
;
498 const struct grid_utf8
*gu
;
501 tty_update_mode(tty
, tty
->mode
& ~MODE_CURSOR
);
503 sx
= screen_size_x(s
);
504 if (sx
> s
->grid
->linedata
[s
->grid
->hsize
+ py
].cellsize
)
505 sx
= s
->grid
->linedata
[s
->grid
->hsize
+ py
].cellsize
;
510 * Don't move the cursor to the start permission if it will wrap there
515 gl
= &s
->grid
->linedata
[s
->grid
->hsize
+ py
- 1];
516 if (oy
+ py
== 0 || gl
== NULL
|| !(gl
->flags
& GRID_LINE_WRAPPED
) ||
517 tty
->cx
< tty
->sx
|| ox
!= 0 ||
518 (oy
+ py
!= tty
->cy
+ 1 && tty
->cy
!= s
->rlower
+ oy
))
519 tty_cursor(tty
, ox
, oy
+ py
);
521 for (i
= 0; i
< sx
; i
++) {
522 gc
= grid_view_peek_cell(s
->grid
, i
, py
);
525 if (gc
->flags
& GRID_FLAG_UTF8
)
526 gu
= grid_view_peek_utf8(s
->grid
, i
, py
);
528 if (screen_check_selection(s
, i
, py
)) {
529 memcpy(&tmpgc
, &s
->sel
.cell
, sizeof tmpgc
);
530 tmpgc
.data
= gc
->data
;
531 tmpgc
.flags
= gc
->flags
&
532 ~(GRID_FLAG_FG256
|GRID_FLAG_BG256
);
533 tmpgc
.flags
|= s
->sel
.cell
.flags
&
534 (GRID_FLAG_FG256
|GRID_FLAG_BG256
);
535 tty_cell(tty
, &tmpgc
, gu
);
537 tty_cell(tty
, gc
, gu
);
541 tty_update_mode(tty
, tty
->mode
);
546 tty_cursor(tty
, ox
+ sx
, oy
+ py
);
547 if (screen_size_x(s
) >= tty
->sx
&& tty_term_has(tty
->term
, TTYC_EL
))
548 tty_putcode(tty
, TTYC_EL
);
550 for (i
= sx
; i
< screen_size_x(s
); i
++)
553 tty_update_mode(tty
, tty
->mode
);
557 tty_write(void (*cmdfn
)(
558 struct tty
*, const struct tty_ctx
*), const struct tty_ctx
*ctx
)
560 struct window_pane
*wp
= ctx
->wp
;
564 /* wp can be NULL if updating the screen but not the terminal. */
568 if (wp
->window
->flags
& WINDOW_REDRAW
|| wp
->flags
& PANE_REDRAW
)
570 if (wp
->window
->flags
& WINDOW_HIDDEN
|| !window_pane_visible(wp
))
573 for (i
= 0; i
< ARRAY_LENGTH(&clients
); i
++) {
574 c
= ARRAY_ITEM(&clients
, i
);
575 if (c
== NULL
|| c
->session
== NULL
)
577 if (c
->flags
& CLIENT_SUSPENDED
)
580 if (c
->session
->curw
->window
== wp
->window
) {
581 if (c
->tty
.flags
& TTY_FREEZE
|| c
->tty
.term
== NULL
)
589 tty_cmd_insertcharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
591 struct window_pane
*wp
= ctx
->wp
;
592 struct screen
*s
= wp
->screen
;
595 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
) {
596 tty_draw_line(tty
, wp
->screen
, ctx
->ocy
, wp
->xoff
, wp
->yoff
);
602 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
604 if (tty_term_has(tty
->term
, TTYC_ICH
) ||
605 tty_term_has(tty
->term
, TTYC_ICH1
))
606 tty_emulate_repeat(tty
, TTYC_ICH
, TTYC_ICH1
, ctx
->num
);
607 else if (tty_term_has(tty
->term
, TTYC_SMIR
) &&
608 tty_term_has(tty
->term
, TTYC_RMIR
)) {
609 tty_putcode(tty
, TTYC_SMIR
);
610 for (i
= 0; i
< ctx
->num
; i
++)
612 tty_putcode(tty
, TTYC_RMIR
);
614 tty_draw_line(tty
, wp
->screen
, ctx
->ocy
, wp
->xoff
, wp
->yoff
);
618 tty_cmd_deletecharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
620 struct window_pane
*wp
= ctx
->wp
;
621 struct screen
*s
= wp
->screen
;
623 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
||
624 (!tty_term_has(tty
->term
, TTYC_DCH
) &&
625 !tty_term_has(tty
->term
, TTYC_DCH1
))) {
626 tty_draw_line(tty
, wp
->screen
, ctx
->ocy
, wp
->xoff
, wp
->yoff
);
632 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
634 if (tty_term_has(tty
->term
, TTYC_DCH
) ||
635 tty_term_has(tty
->term
, TTYC_DCH1
))
636 tty_emulate_repeat(tty
, TTYC_DCH
, TTYC_DCH1
, ctx
->num
);
640 tty_cmd_insertline(struct tty
*tty
, const struct tty_ctx
*ctx
)
642 struct window_pane
*wp
= ctx
->wp
;
643 struct screen
*s
= wp
->screen
;
645 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
||
646 !tty_term_has(tty
->term
, TTYC_CSR
) ||
647 !tty_term_has(tty
->term
, TTYC_IL1
)) {
648 tty_redraw_region(tty
, ctx
);
654 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
655 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
657 tty_emulate_repeat(tty
, TTYC_IL
, TTYC_IL1
, ctx
->num
);
661 tty_cmd_deleteline(struct tty
*tty
, const struct tty_ctx
*ctx
)
663 struct window_pane
*wp
= ctx
->wp
;
664 struct screen
*s
= wp
->screen
;
666 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
||
667 !tty_term_has(tty
->term
, TTYC_CSR
) ||
668 !tty_term_has(tty
->term
, TTYC_DL1
)) {
669 tty_redraw_region(tty
, ctx
);
675 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
676 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
678 tty_emulate_repeat(tty
, TTYC_DL
, TTYC_DL1
, ctx
->num
);
682 tty_cmd_clearline(struct tty
*tty
, const struct tty_ctx
*ctx
)
684 struct window_pane
*wp
= ctx
->wp
;
685 struct screen
*s
= wp
->screen
;
690 tty_cursor_pane(tty
, ctx
, 0, ctx
->ocy
);
692 if (wp
->xoff
== 0 && screen_size_x(s
) >= tty
->sx
&&
693 tty_term_has(tty
->term
, TTYC_EL
)) {
694 tty_putcode(tty
, TTYC_EL
);
696 for (i
= 0; i
< screen_size_x(s
); i
++)
702 tty_cmd_clearendofline(struct tty
*tty
, const struct tty_ctx
*ctx
)
704 struct window_pane
*wp
= ctx
->wp
;
705 struct screen
*s
= wp
->screen
;
710 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
712 if (wp
->xoff
== 0 && screen_size_x(s
) >= tty
->sx
&&
713 tty_term_has(tty
->term
, TTYC_EL
))
714 tty_putcode(tty
, TTYC_EL
);
716 for (i
= ctx
->ocx
; i
< screen_size_x(s
); i
++)
722 tty_cmd_clearstartofline(struct tty
*tty
, const struct tty_ctx
*ctx
)
724 struct window_pane
*wp
= ctx
->wp
;
729 if (wp
->xoff
== 0 && tty_term_has(tty
->term
, TTYC_EL1
)) {
730 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
731 tty_putcode(tty
, TTYC_EL1
);
733 tty_cursor_pane(tty
, ctx
, 0, ctx
->ocy
);
734 for (i
= 0; i
< ctx
->ocx
+ 1; i
++)
740 tty_cmd_reverseindex(struct tty
*tty
, const struct tty_ctx
*ctx
)
742 struct window_pane
*wp
= ctx
->wp
;
743 struct screen
*s
= wp
->screen
;
745 if (ctx
->ocy
!= ctx
->orupper
)
748 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
||
749 !tty_term_has(tty
->term
, TTYC_CSR
) ||
750 !tty_term_has(tty
->term
, TTYC_RI
)) {
751 tty_redraw_region(tty
, ctx
);
757 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
758 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->orupper
);
760 tty_putcode(tty
, TTYC_RI
);
764 tty_cmd_linefeed(struct tty
*tty
, const struct tty_ctx
*ctx
)
766 struct window_pane
*wp
= ctx
->wp
;
767 struct screen
*s
= wp
->screen
;
769 if (ctx
->ocy
!= ctx
->orlower
)
772 if (wp
->xoff
!= 0 || screen_size_x(s
) < tty
->sx
||
773 !tty_term_has(tty
->term
, TTYC_CSR
)) {
774 tty_redraw_region(tty
, ctx
);
779 * If this line wrapped naturally (ctx->num is nonzero), don't do
780 * anything - the cursor can just be moved to the last cell and wrap
783 if (ctx
->num
&& !(tty
->term
->flags
& TERM_EARLYWRAP
))
788 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
789 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
795 tty_cmd_clearendofscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
797 struct window_pane
*wp
= ctx
->wp
;
798 struct screen
*s
= wp
->screen
;
803 tty_region_pane(tty
, ctx
, 0, screen_size_y(s
) - 1);
804 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
806 if (wp
->xoff
== 0 && screen_size_x(s
) >= tty
->sx
&&
807 tty_term_has(tty
->term
, TTYC_EL
)) {
808 tty_putcode(tty
, TTYC_EL
);
809 if (ctx
->ocy
!= screen_size_y(s
) - 1) {
810 tty_cursor_pane(tty
, ctx
, 0, ctx
->ocy
+ 1);
811 for (i
= ctx
->ocy
+ 1; i
< screen_size_y(s
); i
++) {
812 tty_putcode(tty
, TTYC_EL
);
813 if (i
== screen_size_y(s
) - 1)
815 tty_emulate_repeat(tty
, TTYC_CUD
, TTYC_CUD1
, 1);
820 for (i
= ctx
->ocx
; i
< screen_size_x(s
); i
++)
822 for (j
= ctx
->ocy
+ 1; j
< screen_size_y(s
); j
++) {
823 tty_cursor_pane(tty
, ctx
, 0, j
);
824 for (i
= 0; i
< screen_size_x(s
); i
++)
831 tty_cmd_clearstartofscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
833 struct window_pane
*wp
= ctx
->wp
;
834 struct screen
*s
= wp
->screen
;
839 tty_region_pane(tty
, ctx
, 0, screen_size_y(s
) - 1);
840 tty_cursor_pane(tty
, ctx
, 0, 0);
842 if (wp
->xoff
== 0 && screen_size_x(s
) >= tty
->sx
&&
843 tty_term_has(tty
->term
, TTYC_EL
)) {
844 for (i
= 0; i
< ctx
->ocy
; i
++) {
845 tty_putcode(tty
, TTYC_EL
);
846 tty_emulate_repeat(tty
, TTYC_CUD
, TTYC_CUD1
, 1);
850 for (j
= 0; j
< ctx
->ocy
; j
++) {
851 tty_cursor_pane(tty
, ctx
, 0, j
);
852 for (i
= 0; i
< screen_size_x(s
); i
++)
856 for (i
= 0; i
<= ctx
->ocx
; i
++)
861 tty_cmd_clearscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
863 struct window_pane
*wp
= ctx
->wp
;
864 struct screen
*s
= wp
->screen
;
869 tty_region_pane(tty
, ctx
, 0, screen_size_y(s
) - 1);
870 tty_cursor_pane(tty
, ctx
, 0, 0);
872 if (wp
->xoff
== 0 && screen_size_x(s
) >= tty
->sx
&&
873 tty_term_has(tty
->term
, TTYC_EL
)) {
874 for (i
= 0; i
< screen_size_y(s
); i
++) {
875 tty_putcode(tty
, TTYC_EL
);
876 if (i
!= screen_size_y(s
) - 1) {
877 tty_emulate_repeat(tty
, TTYC_CUD
, TTYC_CUD1
, 1);
882 for (j
= 0; j
< screen_size_y(s
); j
++) {
883 tty_cursor_pane(tty
, ctx
, 0, j
);
884 for (i
= 0; i
< screen_size_x(s
); i
++)
891 tty_cmd_alignmenttest(struct tty
*tty
, const struct tty_ctx
*ctx
)
893 struct window_pane
*wp
= ctx
->wp
;
894 struct screen
*s
= wp
->screen
;
899 tty_region_pane(tty
, ctx
, 0, screen_size_y(s
) - 1);
901 for (j
= 0; j
< screen_size_y(s
); j
++) {
902 tty_cursor_pane(tty
, ctx
, 0, j
);
903 for (i
= 0; i
< screen_size_x(s
); i
++)
909 tty_cmd_cell(struct tty
*tty
, const struct tty_ctx
*ctx
)
911 struct window_pane
*wp
= ctx
->wp
;
912 struct screen
*s
= wp
->screen
;
915 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
917 /* Is the cursor in the very last position? */
918 if (ctx
->ocx
> wp
->sx
- ctx
->last_width
) {
919 if (wp
->xoff
!= 0 || wp
->sx
!= tty
->sx
) {
921 * The pane doesn't fill the entire line, the linefeed
922 * will already have happened, so just move the cursor.
924 tty_cursor_pane(tty
, ctx
, 0, ctx
->ocy
+ 1);
925 } else if (tty
->cx
< tty
->sx
) {
927 * The cursor isn't in the last position already, so
928 * move as far left as possinble and redraw the last
929 * cell to move into the last position.
931 cx
= screen_size_x(s
) - ctx
->last_width
;
932 tty_cursor_pane(tty
, ctx
, cx
, ctx
->ocy
);
933 tty_cell(tty
, &ctx
->last_cell
, &ctx
->last_utf8
);
936 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
938 tty_cell(tty
, ctx
->cell
, ctx
->utf8
);
942 tty_cmd_utf8character(struct tty
*tty
, const struct tty_ctx
*ctx
)
944 struct window_pane
*wp
= ctx
->wp
;
947 * Cannot rely on not being a partial character, so just redraw the
950 tty_draw_line(tty
, wp
->screen
, ctx
->ocy
, wp
->xoff
, wp
->yoff
);
955 struct tty
*tty
, const struct grid_cell
*gc
, const struct grid_utf8
*gu
)
959 /* Skip last character if terminal is stupid. */
960 if (tty
->term
->flags
& TERM_EARLYWRAP
&&
961 tty
->cy
== tty
->sy
- 1 && tty
->cx
== tty
->sx
- 1)
964 /* If this is a padding character, do nothing. */
965 if (gc
->flags
& GRID_FLAG_PADDING
)
968 /* Set the attributes. */
969 tty_attributes(tty
, gc
);
971 /* If not UTF-8, write directly. */
972 if (!(gc
->flags
& GRID_FLAG_UTF8
)) {
973 if (gc
->data
< 0x20 || gc
->data
== 0x7f)
975 tty_putc(tty
, gc
->data
);
979 /* If the terminal doesn't support UTF-8, write underscores. */
980 if (!(tty
->flags
& TTY_UTF8
)) {
981 for (i
= 0; i
< gu
->width
; i
++)
986 /* Otherwise, write UTF-8. */
987 tty_pututf8(tty
, gu
);
991 tty_reset(struct tty
*tty
)
993 struct grid_cell
*gc
= &tty
->cell
;
995 if (memcmp(gc
, &grid_default_cell
, sizeof *gc
) == 0)
998 if (tty_term_has(tty
->term
, TTYC_RMACS
) && gc
->attr
& GRID_ATTR_CHARSET
)
999 tty_putcode(tty
, TTYC_RMACS
);
1000 tty_putcode(tty
, TTYC_SGR0
);
1001 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
1004 /* Set region inside pane. */
1007 struct tty
*tty
, const struct tty_ctx
*ctx
, u_int rupper
, u_int rlower
)
1009 struct window_pane
*wp
= ctx
->wp
;
1011 tty_region(tty
, wp
->yoff
+ rupper
, wp
->yoff
+ rlower
);
1014 /* Set region at absolute position. */
1016 tty_region(struct tty
*tty
, u_int rupper
, u_int rlower
)
1018 if (tty
->rlower
== rlower
&& tty
->rupper
== rupper
)
1020 if (!tty_term_has(tty
->term
, TTYC_CSR
))
1023 tty
->rupper
= rupper
;
1024 tty
->rlower
= rlower
;
1027 * Some terminals (such as PuTTY) do not correctly reset the cursor to
1028 * 0,0 if it is beyond the last column (they do not reset their wrap
1029 * flag so further output causes a line feed). As a workaround, do an
1030 * explicit move to 0 first.
1032 if (tty
->cx
>= tty
->sx
)
1033 tty_cursor(tty
, 0, tty
->cy
);
1035 tty_putcode2(tty
, TTYC_CSR
, tty
->rupper
, tty
->rlower
);
1036 tty_cursor(tty
, 0, 0);
1039 /* Move cursor inside pane. */
1041 tty_cursor_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int cx
, u_int cy
)
1043 struct window_pane
*wp
= ctx
->wp
;
1045 tty_cursor(tty
, wp
->xoff
+ cx
, wp
->yoff
+ cy
);
1048 /* Move cursor to absolute position. */
1050 tty_cursor(struct tty
*tty
, u_int cx
, u_int cy
)
1052 struct tty_term
*term
= tty
->term
;
1056 if (cx
> tty
->sx
- 1)
1063 if (cx
== thisx
&& cy
== thisy
)
1066 /* Very end of the line, just use absolute movement. */
1067 if (thisx
> tty
->sx
- 1)
1070 /* Move to home position (0, 0). */
1071 if (cx
== 0 && cy
== 0 && tty_term_has(term
, TTYC_HOME
)) {
1072 tty_putcode(tty
, TTYC_HOME
);
1076 /* Zero on the next line. */
1077 if (cx
== 0 && cy
== thisy
+ 1 && thisy
!= tty
->rlower
) {
1078 tty_putc(tty
, '\r');
1079 tty_putc(tty
, '\n');
1083 /* Moving column or row. */
1086 * Moving column only, row staying the same.
1091 tty_putc(tty
, '\r');
1095 /* One to the left. */
1096 if (cx
== thisx
- 1 && tty_term_has(term
, TTYC_CUB1
)) {
1097 tty_putcode(tty
, TTYC_CUB1
);
1101 /* One to the right. */
1102 if (cx
== thisx
+ 1 && tty_term_has(term
, TTYC_CUF1
)) {
1103 tty_putcode(tty
, TTYC_CUF1
);
1107 /* Calculate difference. */
1108 change
= thisx
- cx
; /* +ve left, -ve right */
1111 * Use HPA if change is larger than absolute, otherwise move
1112 * the cursor with CUB/CUF.
1114 if ((u_int
) abs(change
) > cx
&& tty_term_has(term
, TTYC_HPA
)) {
1115 tty_putcode1(tty
, TTYC_HPA
, cx
);
1117 } else if (change
> 0 && tty_term_has(term
, TTYC_CUB
)) {
1118 tty_putcode1(tty
, TTYC_CUB
, change
);
1120 } else if (change
< 0 && tty_term_has(term
, TTYC_CUF
)) {
1121 tty_putcode1(tty
, TTYC_CUF
, -change
);
1124 } else if (cx
== thisx
) {
1126 * Moving row only, column staying the same.
1130 if (thisy
!= tty
->rupper
&&
1131 cy
== thisy
- 1 && tty_term_has(term
, TTYC_CUU1
)) {
1132 tty_putcode(tty
, TTYC_CUU1
);
1137 if (thisy
!= tty
->rlower
&&
1138 cy
== thisy
+ 1 && tty_term_has(term
, TTYC_CUD1
)) {
1139 tty_putcode(tty
, TTYC_CUD1
);
1143 /* Calculate difference. */
1144 change
= thisy
- cy
; /* +ve up, -ve down */
1147 * Try to use VPA if change is larger than absolute or if this
1148 * change would cross the scroll region, otherwise use CUU/CUD.
1150 if ((u_int
) abs(change
) > cy
||
1151 (change
< 0 && cy
- change
> tty
->rlower
) ||
1152 (change
> 0 && cy
- change
< tty
->rupper
)) {
1153 if (tty_term_has(term
, TTYC_VPA
)) {
1154 tty_putcode1(tty
, TTYC_VPA
, cy
);
1157 } else if (change
> 0 && tty_term_has(term
, TTYC_CUU
)) {
1158 tty_putcode1(tty
, TTYC_CUU
, change
);
1160 } else if (change
< 0 && tty_term_has(term
, TTYC_CUD
)) {
1161 tty_putcode1(tty
, TTYC_CUD
, -change
);
1167 /* Absolute movement. */
1168 tty_putcode2(tty
, TTYC_CUP
, cy
, cx
);
1176 tty_attributes(struct tty
*tty
, const struct grid_cell
*gc
)
1178 struct grid_cell
*tc
= &tty
->cell
, gc2
;
1181 memcpy(&gc2
, gc
, sizeof gc2
);
1184 * If no setab, try to use the reverse attribute as a best-effort for a
1185 * non-default background. This is a bit of a hack but it doesn't do
1186 * any serious harm and makes a couple of applications happier.
1188 if (!tty_term_has(tty
->term
, TTYC_SETAB
)) {
1189 if (gc2
.attr
& GRID_ATTR_REVERSE
) {
1190 if (gc2
.fg
!= 7 && gc2
.fg
!= 8)
1191 gc2
.attr
&= ~GRID_ATTR_REVERSE
;
1193 if (gc2
.bg
!= 0 && gc2
.bg
!= 8)
1194 gc2
.attr
|= GRID_ATTR_REVERSE
;
1198 /* Fix up the colours if necessary. */
1199 tty_check_fg(tty
, &gc2
);
1200 tty_check_bg(tty
, &gc2
);
1202 /* If any bits are being cleared, reset everything. */
1203 if (tc
->attr
& ~gc2
.attr
)
1207 * Set the colours. This may call tty_reset() (so it comes next) and
1208 * may add to (NOT remove) the desired attributes by changing new_attr.
1210 tty_colours(tty
, &gc2
);
1212 /* Filter out attribute bits already set. */
1213 changed
= gc2
.attr
& ~tc
->attr
;
1214 tc
->attr
= gc2
.attr
;
1216 /* Set the attributes. */
1217 if (changed
& GRID_ATTR_BRIGHT
)
1218 tty_putcode(tty
, TTYC_BOLD
);
1219 if (changed
& GRID_ATTR_DIM
)
1220 tty_putcode(tty
, TTYC_DIM
);
1221 if (changed
& GRID_ATTR_ITALICS
)
1222 tty_putcode(tty
, TTYC_SMSO
);
1223 if (changed
& GRID_ATTR_UNDERSCORE
)
1224 tty_putcode(tty
, TTYC_SMUL
);
1225 if (changed
& GRID_ATTR_BLINK
)
1226 tty_putcode(tty
, TTYC_BLINK
);
1227 if (changed
& GRID_ATTR_REVERSE
) {
1228 if (tty_term_has(tty
->term
, TTYC_REV
))
1229 tty_putcode(tty
, TTYC_REV
);
1230 else if (tty_term_has(tty
->term
, TTYC_SMSO
))
1231 tty_putcode(tty
, TTYC_SMSO
);
1233 if (changed
& GRID_ATTR_HIDDEN
)
1234 tty_putcode(tty
, TTYC_INVIS
);
1235 if (changed
& GRID_ATTR_CHARSET
)
1236 tty_putcode(tty
, TTYC_SMACS
);
1240 tty_colours(struct tty
*tty
, const struct grid_cell
*gc
)
1242 struct grid_cell
*tc
= &tty
->cell
;
1243 u_char fg
= gc
->fg
, bg
= gc
->bg
, flags
= gc
->flags
;
1244 int have_ax
, fg_default
, bg_default
;
1246 /* No changes? Nothing is necessary. */
1247 if (fg
== tc
->fg
&& bg
== tc
->bg
&&
1248 ((flags
^ tc
->flags
) & (GRID_FLAG_FG256
|GRID_FLAG_BG256
)) == 0)
1252 * Is either the default colour? This is handled specially because the
1253 * best solution might be to reset both colours to default, in which
1254 * case if only one is default need to fall onward to set the other
1257 fg_default
= (fg
== 8 && !(flags
& GRID_FLAG_FG256
));
1258 bg_default
= (bg
== 8 && !(flags
& GRID_FLAG_BG256
));
1259 if (fg_default
|| bg_default
) {
1261 * If don't have AX but do have op, send sgr0 (op can't
1262 * actually be used because it is sometimes the same as sgr0
1263 * and sometimes isn't). This resets both colours to default.
1265 * Otherwise, try to set the default colour only as needed.
1267 have_ax
= tty_term_has(tty
->term
, TTYC_AX
);
1268 if (!have_ax
&& tty_term_has(tty
->term
, TTYC_OP
))
1272 (tc
->fg
!= 8 || tc
->flags
& GRID_FLAG_FG256
)) {
1274 tty_puts(tty
, "\033[39m");
1275 else if (tc
->fg
!= 7 ||
1276 tc
->flags
& GRID_FLAG_FG256
)
1277 tty_putcode1(tty
, TTYC_SETAF
, 7);
1279 tc
->flags
&= ~GRID_FLAG_FG256
;
1282 (tc
->bg
!= 8 || tc
->flags
& GRID_FLAG_BG256
)) {
1284 tty_puts(tty
, "\033[49m");
1285 else if (tc
->bg
!= 0 ||
1286 tc
->flags
& GRID_FLAG_BG256
)
1287 tty_putcode1(tty
, TTYC_SETAB
, 0);
1289 tc
->flags
&= ~GRID_FLAG_BG256
;
1294 /* Set the foreground colour. */
1295 if (!fg_default
&& (fg
!= tc
->fg
||
1296 ((flags
& GRID_FLAG_FG256
) != (tc
->flags
& GRID_FLAG_FG256
))))
1297 tty_colours_fg(tty
, gc
);
1300 * Set the background colour. This must come after the foreground as
1301 * tty_colour_fg() can call tty_reset().
1303 if (!bg_default
&& (bg
!= tc
->bg
||
1304 ((flags
& GRID_FLAG_BG256
) != (tc
->flags
& GRID_FLAG_BG256
))))
1305 tty_colours_bg(tty
, gc
);
1309 tty_check_fg(struct tty
*tty
, struct grid_cell
*gc
)
1313 /* Is this a 256-colour colour? */
1314 if (gc
->flags
& GRID_FLAG_FG256
) {
1315 /* And not a 256 colour mode? */
1316 if (!(tty
->term
->flags
& TERM_88COLOURS
) &&
1317 !(tty
->term_flags
& TERM_88COLOURS
) &&
1318 !(tty
->term
->flags
& TERM_256COLOURS
) &&
1319 !(tty
->term_flags
& TERM_256COLOURS
)) {
1320 gc
->fg
= colour_256to16(gc
->fg
);
1323 gc
->attr
|= GRID_ATTR_BRIGHT
;
1325 gc
->attr
&= ~GRID_ATTR_BRIGHT
;
1326 gc
->flags
&= ~GRID_FLAG_FG256
;
1331 /* Is this an aixterm colour? */
1332 colours
= tty_term_number(tty
->term
, TTYC_COLORS
);
1333 if (gc
->fg
>= 90 && gc
->fg
<= 97 && colours
< 16) {
1335 gc
->attr
|= GRID_ATTR_BRIGHT
;
1340 tty_check_bg(struct tty
*tty
, struct grid_cell
*gc
)
1344 /* Is this a 256-colour colour? */
1345 if (gc
->flags
& GRID_FLAG_BG256
) {
1347 * And not a 256 colour mode? Translate to 16-colour
1348 * palette. Bold background doesn't exist portably, so just
1349 * discard the bold bit if set.
1351 if (!(tty
->term
->flags
& TERM_88COLOURS
) &&
1352 !(tty
->term_flags
& TERM_88COLOURS
) &&
1353 !(tty
->term
->flags
& TERM_256COLOURS
) &&
1354 !(tty
->term_flags
& TERM_256COLOURS
)) {
1355 gc
->bg
= colour_256to16(gc
->bg
);
1358 gc
->attr
&= ~GRID_ATTR_BRIGHT
;
1359 gc
->flags
&= ~GRID_FLAG_BG256
;
1364 /* Is this an aixterm colour? */
1365 colours
= tty_term_number(tty
->term
, TTYC_COLORS
);
1366 if (gc
->bg
>= 100 && gc
->bg
<= 107 && colours
< 16) {
1368 gc
->attr
|= GRID_ATTR_BRIGHT
;
1373 tty_colours_fg(struct tty
*tty
, const struct grid_cell
*gc
)
1375 struct grid_cell
*tc
= &tty
->cell
;
1379 /* Is this a 256-colour colour? */
1380 if (gc
->flags
& GRID_FLAG_FG256
) {
1381 /* Try as 256 colours or translating to 88. */
1382 if (tty_try_256(tty
, fg
, "38") == 0)
1384 if (tty_try_88(tty
, fg
, "38") == 0)
1386 /* Else already handled by tty_check_fg. */
1390 /* Is this an aixterm bright colour? */
1391 if (fg
>= 90 && fg
<= 97) {
1392 xsnprintf(s
, sizeof s
, "\033[%dm", fg
);
1397 /* Otherwise set the foreground colour. */
1398 tty_putcode1(tty
, TTYC_SETAF
, fg
);
1401 /* Save the new values in the terminal current cell. */
1403 tc
->flags
&= ~GRID_FLAG_FG256
;
1404 tc
->flags
|= gc
->flags
& GRID_FLAG_FG256
;
1408 tty_colours_bg(struct tty
*tty
, const struct grid_cell
*gc
)
1410 struct grid_cell
*tc
= &tty
->cell
;
1414 /* Is this a 256-colour colour? */
1415 if (gc
->flags
& GRID_FLAG_BG256
) {
1416 /* Try as 256 colours or translating to 88. */
1417 if (tty_try_256(tty
, bg
, "48") == 0)
1419 if (tty_try_88(tty
, bg
, "48") == 0)
1421 /* Else already handled by tty_check_bg. */
1425 /* Is this an aixterm bright colour? */
1426 if (bg
>= 100 && bg
<= 107) {
1427 /* 16 colour terminals or above only. */
1428 if (tty_term_number(tty
->term
, TTYC_COLORS
) >= 16) {
1429 xsnprintf(s
, sizeof s
, "\033[%dm", bg
);
1434 /* no such thing as a bold background */
1437 /* Otherwise set the background colour. */
1438 tty_putcode1(tty
, TTYC_SETAB
, bg
);
1441 /* Save the new values in the terminal current cell. */
1443 tc
->flags
&= ~GRID_FLAG_BG256
;
1444 tc
->flags
|= gc
->flags
& GRID_FLAG_BG256
;
1448 tty_try_256(struct tty
*tty
, u_char colour
, const char *type
)
1452 if (!(tty
->term
->flags
& TERM_256COLOURS
) &&
1453 !(tty
->term_flags
& TERM_256COLOURS
))
1456 xsnprintf(s
, sizeof s
, "\033[%s;5;%hhum", type
, colour
);
1462 tty_try_88(struct tty
*tty
, u_char colour
, const char *type
)
1466 if (!(tty
->term
->flags
& TERM_88COLOURS
) &&
1467 !(tty
->term_flags
& TERM_88COLOURS
))
1469 colour
= colour_256to88(colour
);
1471 xsnprintf(s
, sizeof s
, "\033[%s;5;%hhum", type
, colour
);