4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
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>
22 #include <netinet/in.h>
35 static int tty_log_fd
= -1;
37 static int tty_client_ready(struct client
*);
39 static void tty_set_italics(struct tty
*);
40 static int tty_try_colour(struct tty
*, int, const char *);
41 static void tty_force_cursor_colour(struct tty
*, const char *);
42 static void tty_cursor_pane(struct tty
*, const struct tty_ctx
*, u_int
,
44 static void tty_cursor_pane_unless_wrap(struct tty
*,
45 const struct tty_ctx
*, u_int
, u_int
);
46 static void tty_invalidate(struct tty
*);
47 static void tty_colours(struct tty
*, const struct grid_cell
*);
48 static void tty_check_fg(struct tty
*, struct colour_palette
*,
50 static void tty_check_bg(struct tty
*, struct colour_palette
*,
52 static void tty_check_us(struct tty
*, struct colour_palette
*,
54 static void tty_colours_fg(struct tty
*, const struct grid_cell
*);
55 static void tty_colours_bg(struct tty
*, const struct grid_cell
*);
56 static void tty_colours_us(struct tty
*, const struct grid_cell
*);
58 static void tty_region_pane(struct tty
*, const struct tty_ctx
*, u_int
,
60 static void tty_region(struct tty
*, u_int
, u_int
);
61 static void tty_margin_pane(struct tty
*, const struct tty_ctx
*);
62 static void tty_margin(struct tty
*, u_int
, u_int
);
63 static int tty_large_region(struct tty
*, const struct tty_ctx
*);
64 static int tty_fake_bce(const struct tty
*, const struct grid_cell
*,
66 static void tty_redraw_region(struct tty
*, const struct tty_ctx
*);
67 static void tty_emulate_repeat(struct tty
*, enum tty_code_code
,
68 enum tty_code_code
, u_int
);
69 static void tty_repeat_space(struct tty
*, u_int
);
70 static void tty_draw_pane(struct tty
*, const struct tty_ctx
*, u_int
);
71 static void tty_default_attributes(struct tty
*, const struct grid_cell
*,
72 struct colour_palette
*, u_int
);
73 static int tty_check_overlay(struct tty
*, u_int
, u_int
);
75 #define tty_use_margin(tty) \
76 (tty->term->flags & TERM_DECSLRM)
77 #define tty_full_width(tty, ctx) \
78 ((ctx)->xoff == 0 && (ctx)->sx >= (tty)->sx)
80 #define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */)
81 #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8)
82 #define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8)
89 xsnprintf(name
, sizeof name
, "tmux-out-%ld.log", (long)getpid());
91 tty_log_fd
= open(name
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0644);
92 if (tty_log_fd
!= -1 && fcntl(tty_log_fd
, F_SETFD
, FD_CLOEXEC
) == -1)
93 fatal("fcntl failed");
97 tty_init(struct tty
*tty
, struct client
*c
)
102 memset(tty
, 0, sizeof *tty
);
105 tty
->cstyle
= SCREEN_CURSOR_DEFAULT
;
106 tty
->ccolour
= xstrdup("");
108 if (tcgetattr(c
->fd
, &tty
->tio
) != 0)
114 tty_resize(struct tty
*tty
)
116 struct client
*c
= tty
->client
;
118 u_int sx
, sy
, xpixel
, ypixel
;
120 if (ioctl(c
->fd
, TIOCGWINSZ
, &ws
) != -1) {
126 xpixel
= ws
.ws_xpixel
/ sx
;
132 ypixel
= ws
.ws_ypixel
/ sy
;
139 log_debug("%s: %s now %ux%u (%ux%u)", __func__
, c
->name
, sx
, sy
,
141 tty_set_size(tty
, sx
, sy
, xpixel
, ypixel
);
146 tty_set_size(struct tty
*tty
, u_int sx
, u_int sy
, u_int xpixel
, u_int ypixel
)
150 tty
->xpixel
= xpixel
;
151 tty
->ypixel
= ypixel
;
155 tty_read_callback(__unused
int fd
, __unused
short events
, void *data
)
157 struct tty
*tty
= data
;
158 struct client
*c
= tty
->client
;
159 const char *name
= c
->name
;
160 size_t size
= EVBUFFER_LENGTH(tty
->in
);
163 nread
= evbuffer_read(tty
->in
, c
->fd
, -1);
164 if (nread
== 0 || nread
== -1) {
166 log_debug("%s: read closed", name
);
168 log_debug("%s: read error: %s", name
, strerror(errno
));
169 event_del(&tty
->event_in
);
170 server_client_lost(tty
->client
);
173 log_debug("%s: read %d bytes (already %zu)", name
, nread
, size
);
175 while (tty_keys_next(tty
))
180 tty_timer_callback(__unused
int fd
, __unused
short events
, void *data
)
182 struct tty
*tty
= data
;
183 struct client
*c
= tty
->client
;
184 struct timeval tv
= { .tv_usec
= TTY_BLOCK_INTERVAL
};
186 log_debug("%s: %zu discarded", c
->name
, tty
->discarded
);
188 c
->flags
|= CLIENT_ALLREDRAWFLAGS
;
189 c
->discarded
+= tty
->discarded
;
191 if (tty
->discarded
< TTY_BLOCK_STOP(tty
)) {
192 tty
->flags
&= ~TTY_BLOCK
;
197 evtimer_add(&tty
->timer
, &tv
);
201 tty_block_maybe(struct tty
*tty
)
203 struct client
*c
= tty
->client
;
204 size_t size
= EVBUFFER_LENGTH(tty
->out
);
205 struct timeval tv
= { .tv_usec
= TTY_BLOCK_INTERVAL
};
207 if (size
< TTY_BLOCK_START(tty
))
210 if (tty
->flags
& TTY_BLOCK
)
212 tty
->flags
|= TTY_BLOCK
;
214 log_debug("%s: can't keep up, %zu discarded", c
->name
, size
);
216 evbuffer_drain(tty
->out
, size
);
217 c
->discarded
+= size
;
220 evtimer_add(&tty
->timer
, &tv
);
225 tty_write_callback(__unused
int fd
, __unused
short events
, void *data
)
227 struct tty
*tty
= data
;
228 struct client
*c
= tty
->client
;
229 size_t size
= EVBUFFER_LENGTH(tty
->out
);
232 nwrite
= evbuffer_write(tty
->out
, c
->fd
);
235 log_debug("%s: wrote %d bytes (of %zu)", c
->name
, nwrite
, size
);
238 if ((size_t)nwrite
>= c
->redraw
)
242 log_debug("%s: waiting for redraw, %zu bytes left", c
->name
,
244 } else if (tty_block_maybe(tty
))
247 if (EVBUFFER_LENGTH(tty
->out
) != 0)
248 event_add(&tty
->event_out
, NULL
);
252 tty_open(struct tty
*tty
, char **cause
)
254 struct client
*c
= tty
->client
;
256 tty
->term
= tty_term_create(tty
, c
->term_name
, c
->term_caps
,
257 c
->term_ncaps
, &c
->term_features
, cause
);
258 if (tty
->term
== NULL
) {
262 tty
->flags
|= TTY_OPENED
;
264 tty
->flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
|TTY_BLOCK
|TTY_TIMER
);
266 event_set(&tty
->event_in
, c
->fd
, EV_PERSIST
|EV_READ
,
267 tty_read_callback
, tty
);
268 tty
->in
= evbuffer_new();
270 fatal("out of memory");
272 event_set(&tty
->event_out
, c
->fd
, EV_WRITE
, tty_write_callback
, tty
);
273 tty
->out
= evbuffer_new();
274 if (tty
->out
== NULL
)
275 fatal("out of memory");
277 evtimer_set(&tty
->timer
, tty_timer_callback
, tty
);
287 tty_start_timer_callback(__unused
int fd
, __unused
short events
, void *data
)
289 struct tty
*tty
= data
;
290 struct client
*c
= tty
->client
;
292 log_debug("%s: start timer fired", c
->name
);
293 if ((tty
->flags
& (TTY_HAVEDA
|TTY_HAVEXDA
)) == 0)
294 tty_update_features(tty
);
295 tty
->flags
|= (TTY_HAVEDA
|TTY_HAVEXDA
);
299 tty_start_tty(struct tty
*tty
)
301 struct client
*c
= tty
->client
;
303 struct timeval tv
= { .tv_sec
= 1 };
305 setblocking(c
->fd
, 0);
306 event_add(&tty
->event_in
, NULL
);
308 memcpy(&tio
, &tty
->tio
, sizeof tio
);
309 tio
.c_iflag
&= ~(IXON
|IXOFF
|ICRNL
|INLCR
|IGNCR
|IMAXBEL
|ISTRIP
);
310 tio
.c_iflag
|= IGNBRK
;
311 tio
.c_oflag
&= ~(OPOST
|ONLCR
|OCRNL
|ONLRET
);
312 tio
.c_lflag
&= ~(IEXTEN
|ICANON
|ECHO
|ECHOE
|ECHONL
|ECHOCTL
|ECHOPRT
|
316 if (tcsetattr(c
->fd
, TCSANOW
, &tio
) == 0)
317 tcflush(c
->fd
, TCIOFLUSH
);
319 tty_putcode(tty
, TTYC_SMCUP
);
321 tty_putcode(tty
, TTYC_SMKX
);
322 tty_putcode(tty
, TTYC_CLEAR
);
324 if (tty_acs_needed(tty
)) {
325 log_debug("%s: using capabilities for ACS", c
->name
);
326 tty_putcode(tty
, TTYC_ENACS
);
328 log_debug("%s: using UTF-8 for ACS", c
->name
);
330 tty_putcode(tty
, TTYC_CNORM
);
331 if (tty_term_has(tty
->term
, TTYC_KMOUS
)) {
332 tty_puts(tty
, "\033[?1000l\033[?1002l\033[?1003l");
333 tty_puts(tty
, "\033[?1006l\033[?1005l");
336 evtimer_set(&tty
->start_timer
, tty_start_timer_callback
, tty
);
337 evtimer_add(&tty
->start_timer
, &tv
);
339 tty
->flags
|= TTY_STARTED
;
342 if (*tty
->ccolour
!= '\0')
343 tty_force_cursor_colour(tty
, "");
345 tty
->mouse_drag_flag
= 0;
346 tty
->mouse_drag_update
= NULL
;
347 tty
->mouse_drag_release
= NULL
;
351 tty_send_requests(struct tty
*tty
)
353 if (~tty
->flags
& TTY_STARTED
)
356 if (tty
->term
->flags
& TERM_VT100LIKE
) {
357 if (~tty
->flags
& TTY_HAVEDA
)
358 tty_puts(tty
, "\033[>c");
359 if (~tty
->flags
& TTY_HAVEXDA
)
360 tty_puts(tty
, "\033[>q");
362 tty
->flags
|= (TTY_HAVEDA
|TTY_HAVEXDA
);
366 tty_stop_tty(struct tty
*tty
)
368 struct client
*c
= tty
->client
;
371 if (!(tty
->flags
& TTY_STARTED
))
373 tty
->flags
&= ~TTY_STARTED
;
375 evtimer_del(&tty
->start_timer
);
377 event_del(&tty
->timer
);
378 tty
->flags
&= ~TTY_BLOCK
;
380 event_del(&tty
->event_in
);
381 event_del(&tty
->event_out
);
384 * Be flexible about error handling and try not kill the server just
385 * because the fd is invalid. Things like ssh -t can easily leave us
388 if (ioctl(c
->fd
, TIOCGWINSZ
, &ws
) == -1)
390 if (tcsetattr(c
->fd
, TCSANOW
, &tty
->tio
) == -1)
393 tty_raw(tty
, tty_term_string2(tty
->term
, TTYC_CSR
, 0, ws
.ws_row
- 1));
394 if (tty_acs_needed(tty
))
395 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMACS
));
396 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_SGR0
));
397 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMKX
));
398 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CLEAR
));
399 if (tty
->cstyle
!= SCREEN_CURSOR_DEFAULT
) {
400 if (tty_term_has(tty
->term
, TTYC_SE
))
401 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_SE
));
402 else if (tty_term_has(tty
->term
, TTYC_SS
))
403 tty_raw(tty
, tty_term_string1(tty
->term
, TTYC_SS
, 0));
405 if (tty
->mode
& MODE_BRACKETPASTE
)
406 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSBP
));
407 if (*tty
->ccolour
!= '\0')
408 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CR
));
410 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CNORM
));
411 if (tty_term_has(tty
->term
, TTYC_KMOUS
)) {
412 tty_raw(tty
, "\033[?1000l\033[?1002l\033[?1003l");
413 tty_raw(tty
, "\033[?1006l\033[?1005l");
416 if (tty
->term
->flags
& TERM_VT100LIKE
)
417 tty_raw(tty
, "\033[?7727l");
418 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSFCS
));
419 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSEKS
));
421 if (tty_use_margin(tty
))
422 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSMG
));
423 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMCUP
));
425 setblocking(c
->fd
, 1);
429 tty_close(struct tty
*tty
)
431 if (event_initialized(&tty
->key_timer
))
432 evtimer_del(&tty
->key_timer
);
435 if (tty
->flags
& TTY_OPENED
) {
436 evbuffer_free(tty
->in
);
437 event_del(&tty
->event_in
);
438 evbuffer_free(tty
->out
);
439 event_del(&tty
->event_out
);
441 tty_term_free(tty
->term
);
444 tty
->flags
&= ~TTY_OPENED
;
449 tty_free(struct tty
*tty
)
456 tty_update_features(struct tty
*tty
)
458 struct client
*c
= tty
->client
;
460 if (tty_apply_features(tty
->term
, c
->term_features
))
461 tty_term_apply_overrides(tty
->term
);
463 if (tty_use_margin(tty
))
464 tty_putcode(tty
, TTYC_ENMG
);
465 if (options_get_number(global_options
, "extended-keys"))
466 tty_puts(tty
, tty_term_string(tty
->term
, TTYC_ENEKS
));
467 if (options_get_number(global_options
, "focus-events"))
468 tty_puts(tty
, tty_term_string(tty
->term
, TTYC_ENFCS
));
469 if (tty
->term
->flags
& TERM_VT100LIKE
)
470 tty_puts(tty
, "\033[?7727h");
474 tty_raw(struct tty
*tty
, const char *s
)
476 struct client
*c
= tty
->client
;
481 for (i
= 0; i
< 5; i
++) {
482 n
= write(c
->fd
, s
, slen
);
488 } else if (n
== -1 && errno
!= EAGAIN
)
495 tty_putcode(struct tty
*tty
, enum tty_code_code code
)
497 tty_puts(tty
, tty_term_string(tty
->term
, code
));
501 tty_putcode1(struct tty
*tty
, enum tty_code_code code
, int a
)
505 tty_puts(tty
, tty_term_string1(tty
->term
, code
, a
));
509 tty_putcode2(struct tty
*tty
, enum tty_code_code code
, int a
, int b
)
513 tty_puts(tty
, tty_term_string2(tty
->term
, code
, a
, b
));
517 tty_putcode3(struct tty
*tty
, enum tty_code_code code
, int a
, int b
, int c
)
519 if (a
< 0 || b
< 0 || c
< 0)
521 tty_puts(tty
, tty_term_string3(tty
->term
, code
, a
, b
, c
));
525 tty_putcode_ptr1(struct tty
*tty
, enum tty_code_code code
, const void *a
)
528 tty_puts(tty
, tty_term_ptr1(tty
->term
, code
, a
));
532 tty_putcode_ptr2(struct tty
*tty
, enum tty_code_code code
, const void *a
,
535 if (a
!= NULL
&& b
!= NULL
)
536 tty_puts(tty
, tty_term_ptr2(tty
->term
, code
, a
, b
));
540 tty_add(struct tty
*tty
, const char *buf
, size_t len
)
542 struct client
*c
= tty
->client
;
544 if (tty
->flags
& TTY_BLOCK
) {
545 tty
->discarded
+= len
;
549 evbuffer_add(tty
->out
, buf
, len
);
550 log_debug("%s: %.*s", c
->name
, (int)len
, buf
);
553 if (tty_log_fd
!= -1)
554 write(tty_log_fd
, buf
, len
);
555 if (tty
->flags
& TTY_STARTED
)
556 event_add(&tty
->event_out
, NULL
);
560 tty_puts(struct tty
*tty
, const char *s
)
563 tty_add(tty
, s
, strlen(s
));
567 tty_putc(struct tty
*tty
, u_char ch
)
571 if ((tty
->term
->flags
& TERM_NOAM
) &&
572 ch
>= 0x20 && ch
!= 0x7f &&
573 tty
->cy
== tty
->sy
- 1 &&
574 tty
->cx
+ 1 >= tty
->sx
)
577 if (tty
->cell
.attr
& GRID_ATTR_CHARSET
) {
578 acs
= tty_acs_get(tty
, ch
);
580 tty_add(tty
, acs
, strlen(acs
));
582 tty_add(tty
, &ch
, 1);
584 tty_add(tty
, &ch
, 1);
586 if (ch
>= 0x20 && ch
!= 0x7f) {
587 if (tty
->cx
>= tty
->sx
) {
589 if (tty
->cy
!= tty
->rlower
)
593 * On !am terminals, force the cursor position to where
594 * we think it should be after a line wrap - this means
595 * it works on sensible terminals as well.
597 if (tty
->term
->flags
& TERM_NOAM
)
598 tty_putcode2(tty
, TTYC_CUP
, tty
->cy
, tty
->cx
);
605 tty_putn(struct tty
*tty
, const void *buf
, size_t len
, u_int width
)
607 if ((tty
->term
->flags
& TERM_NOAM
) &&
608 tty
->cy
== tty
->sy
- 1 &&
609 tty
->cx
+ len
>= tty
->sx
)
610 len
= tty
->sx
- tty
->cx
- 1;
612 tty_add(tty
, buf
, len
);
613 if (tty
->cx
+ width
> tty
->sx
) {
614 tty
->cx
= (tty
->cx
+ width
) - tty
->sx
;
615 if (tty
->cx
<= tty
->sx
)
618 tty
->cx
= tty
->cy
= UINT_MAX
;
624 tty_set_italics(struct tty
*tty
)
628 if (tty_term_has(tty
->term
, TTYC_SITM
)) {
629 s
= options_get_string(global_options
, "default-terminal");
630 if (strcmp(s
, "screen") != 0 && strncmp(s
, "screen-", 7) != 0) {
631 tty_putcode(tty
, TTYC_SITM
);
635 tty_putcode(tty
, TTYC_SMSO
);
639 tty_set_title(struct tty
*tty
, const char *title
)
641 if (!tty_term_has(tty
->term
, TTYC_TSL
) ||
642 !tty_term_has(tty
->term
, TTYC_FSL
))
645 tty_putcode(tty
, TTYC_TSL
);
646 tty_puts(tty
, title
);
647 tty_putcode(tty
, TTYC_FSL
);
651 tty_force_cursor_colour(struct tty
*tty
, const char *ccolour
)
653 if (*ccolour
== '\0')
654 tty_putcode(tty
, TTYC_CR
);
656 tty_putcode_ptr1(tty
, TTYC_CS
, ccolour
);
658 tty
->ccolour
= xstrdup(ccolour
);
662 tty_update_mode(struct tty
*tty
, int mode
, struct screen
*s
)
664 struct client
*c
= tty
->client
;
666 enum screen_cursor_style cstyle
= tty
->cstyle
;
668 if (tty
->flags
& TTY_NOCURSOR
)
669 mode
&= ~MODE_CURSOR
;
671 changed
= mode
^ tty
->mode
;
672 if (log_get_level() != 0 && changed
!= 0) {
673 log_debug("%s: current mode %s", c
->name
,
674 screen_mode_to_string(tty
->mode
));
675 log_debug("%s: setting mode %s", c
->name
,
676 screen_mode_to_string(mode
));
680 if (strcmp(s
->ccolour
, tty
->ccolour
) != 0)
681 tty_force_cursor_colour(tty
, s
->ccolour
);
684 if (~mode
& MODE_CURSOR
) {
685 /* Cursor now off - set as invisible. */
686 if (changed
& MODE_CURSOR
)
687 tty_putcode(tty
, TTYC_CIVIS
);
688 } else if ((changed
& (MODE_CURSOR
|MODE_BLINKING
)) ||
689 cstyle
!= tty
->cstyle
) {
691 * Cursor now on, blinking flag changed or style changed. Start
692 * by setting the cursor to normal.
694 tty_putcode(tty
, TTYC_CNORM
);
696 case SCREEN_CURSOR_DEFAULT
:
698 * If the old style wasn't default, then reset it to
701 if (tty
->cstyle
!= SCREEN_CURSOR_DEFAULT
) {
702 if (tty_term_has(tty
->term
, TTYC_SE
))
703 tty_putcode(tty
, TTYC_SE
);
705 tty_putcode1(tty
, TTYC_SS
, 0);
708 /* Set the cursor as very visible if necessary. */
709 if (mode
& MODE_BLINKING
)
710 tty_putcode(tty
, TTYC_CVVIS
);
712 case SCREEN_CURSOR_BLOCK
:
714 * Set style to either block blinking (1) or steady (2)
715 * if supported, otherwise just check the blinking
718 if (tty_term_has(tty
->term
, TTYC_SS
)) {
719 if (mode
& MODE_BLINKING
)
720 tty_putcode1(tty
, TTYC_SS
, 1);
722 tty_putcode1(tty
, TTYC_SS
, 2);
723 } else if (mode
& MODE_BLINKING
)
724 tty_putcode(tty
, TTYC_CVVIS
);
726 case SCREEN_CURSOR_UNDERLINE
:
728 * Set style to either underline blinking (3) or steady
729 * (4) if supported, otherwise just check the blinking
732 if (tty_term_has(tty
->term
, TTYC_SS
)) {
733 if (mode
& MODE_BLINKING
)
734 tty_putcode1(tty
, TTYC_SS
, 3);
736 tty_putcode1(tty
, TTYC_SS
, 4);
737 } else if (mode
& MODE_BLINKING
)
738 tty_putcode(tty
, TTYC_CVVIS
);
740 case SCREEN_CURSOR_BAR
:
742 * Set style to either bar blinking (5) or steady (6)
743 * if supported, otherwise just check the blinking
746 if (tty_term_has(tty
->term
, TTYC_SS
)) {
747 if (mode
& MODE_BLINKING
)
748 tty_putcode1(tty
, TTYC_SS
, 5);
750 tty_putcode1(tty
, TTYC_SS
, 6);
751 } else if (mode
& MODE_BLINKING
)
752 tty_putcode(tty
, TTYC_CVVIS
);
755 tty
->cstyle
= cstyle
;
758 if ((changed
& ALL_MOUSE_MODES
) &&
759 tty_term_has(tty
->term
, TTYC_KMOUS
)) {
761 * If the mouse modes have changed, clear any that are set and
762 * apply again. There are differences in how terminals track
765 if (tty
->mode
& MODE_MOUSE_SGR
)
766 tty_puts(tty
, "\033[?1006l");
767 if (tty
->mode
& MODE_MOUSE_STANDARD
)
768 tty_puts(tty
, "\033[?1000l");
769 if (tty
->mode
& MODE_MOUSE_BUTTON
)
770 tty_puts(tty
, "\033[?1002l");
771 if (tty
->mode
& MODE_MOUSE_ALL
)
772 tty_puts(tty
, "\033[?1003l");
773 if (mode
& ALL_MOUSE_MODES
)
774 tty_puts(tty
, "\033[?1006h");
775 if (mode
& MODE_MOUSE_STANDARD
)
776 tty_puts(tty
, "\033[?1000h");
777 if (mode
& MODE_MOUSE_BUTTON
)
778 tty_puts(tty
, "\033[?1002h");
779 if (mode
& MODE_MOUSE_ALL
)
780 tty_puts(tty
, "\033[?1003h");
782 if (changed
& MODE_BRACKETPASTE
) {
783 if (mode
& MODE_BRACKETPASTE
)
784 tty_putcode(tty
, TTYC_ENBP
);
786 tty_putcode(tty
, TTYC_DSBP
);
792 tty_emulate_repeat(struct tty
*tty
, enum tty_code_code code
,
793 enum tty_code_code code1
, u_int n
)
795 if (tty_term_has(tty
->term
, code
))
796 tty_putcode1(tty
, code
, n
);
799 tty_putcode(tty
, code1
);
804 tty_repeat_space(struct tty
*tty
, u_int n
)
809 memset(s
, ' ', sizeof s
);
811 while (n
> sizeof s
) {
812 tty_putn(tty
, s
, sizeof s
, sizeof s
);
816 tty_putn(tty
, s
, n
, n
);
819 /* Is this window bigger than the terminal? */
821 tty_window_bigger(struct tty
*tty
)
823 struct client
*c
= tty
->client
;
824 struct window
*w
= c
->session
->curw
->window
;
826 return (tty
->sx
< w
->sx
|| tty
->sy
- status_line_size(c
) < w
->sy
);
829 /* What offset should this window be drawn at? */
831 tty_window_offset(struct tty
*tty
, u_int
*ox
, u_int
*oy
, u_int
*sx
, u_int
*sy
)
841 /* What offset should this window be drawn at? */
843 tty_window_offset1(struct tty
*tty
, u_int
*ox
, u_int
*oy
, u_int
*sx
, u_int
*sy
)
845 struct client
*c
= tty
->client
;
846 struct window
*w
= c
->session
->curw
->window
;
847 struct window_pane
*wp
= server_client_get_pane(c
);
850 lines
= status_line_size(c
);
852 if (tty
->sx
>= w
->sx
&& tty
->sy
- lines
>= w
->sy
) {
858 c
->pan_window
= NULL
;
863 *sy
= tty
->sy
- lines
;
865 if (c
->pan_window
== w
) {
868 else if (c
->pan_ox
+ *sx
> w
->sx
)
869 c
->pan_ox
= w
->sx
- *sx
;
873 else if (c
->pan_oy
+ *sy
> w
->sy
)
874 c
->pan_oy
= w
->sy
- *sy
;
879 if (~wp
->screen
->mode
& MODE_CURSOR
) {
883 cx
= wp
->xoff
+ wp
->screen
->cx
;
884 cy
= wp
->yoff
+ wp
->screen
->cy
;
888 else if (cx
> w
->sx
- *sx
)
895 else if (cy
> w
->sy
- *sy
)
901 c
->pan_window
= NULL
;
905 /* Update stored offsets for a window and redraw if necessary. */
907 tty_update_window_offset(struct window
*w
)
911 TAILQ_FOREACH(c
, &clients
, entry
) {
912 if (c
->session
!= NULL
&& c
->session
->curw
->window
== w
)
913 tty_update_client_offset(c
);
917 /* Update stored offsets for a client and redraw if necessary. */
919 tty_update_client_offset(struct client
*c
)
921 u_int ox
, oy
, sx
, sy
;
923 if (~c
->flags
& CLIENT_TERMINAL
)
926 c
->tty
.oflag
= tty_window_offset1(&c
->tty
, &ox
, &oy
, &sx
, &sy
);
927 if (ox
== c
->tty
.oox
&&
933 log_debug ("%s: %s offset has changed (%u,%u %ux%u -> %u,%u %ux%u)",
934 __func__
, c
->name
, c
->tty
.oox
, c
->tty
.ooy
, c
->tty
.osx
, c
->tty
.osy
,
942 c
->flags
|= (CLIENT_REDRAWWINDOW
|CLIENT_REDRAWSTATUS
);
946 * Is the region large enough to be worth redrawing once later rather than
947 * probably several times now? Currently yes if it is more than 50% of the
951 tty_large_region(__unused
struct tty
*tty
, const struct tty_ctx
*ctx
)
953 return (ctx
->orlower
- ctx
->orupper
>= ctx
->sy
/ 2);
957 * Return if BCE is needed but the terminal doesn't have it - it'll need to be
961 tty_fake_bce(const struct tty
*tty
, const struct grid_cell
*gc
, u_int bg
)
963 if (tty_term_flag(tty
->term
, TTYC_BCE
))
965 if (!COLOUR_DEFAULT(bg
) || !COLOUR_DEFAULT(gc
->bg
))
971 * Redraw scroll region using data from screen (already updated). Used when
972 * CSR not supported, or window is a pane that doesn't take up the full
973 * width of the terminal.
976 tty_redraw_region(struct tty
*tty
, const struct tty_ctx
*ctx
)
978 struct client
*c
= tty
->client
;
982 * If region is large, schedule a redraw. In most cases this is likely
983 * to be followed by some more scrolling.
985 if (tty_large_region(tty
, ctx
)) {
986 log_debug("%s: %s large redraw", __func__
, c
->name
);
991 for (i
= ctx
->orupper
; i
<= ctx
->orlower
; i
++)
992 tty_draw_pane(tty
, ctx
, i
);
995 /* Is this position visible in the pane? */
997 tty_is_visible(__unused
struct tty
*tty
, const struct tty_ctx
*ctx
, u_int px
,
998 u_int py
, u_int nx
, u_int ny
)
1000 u_int xoff
= ctx
->rxoff
+ px
, yoff
= ctx
->ryoff
+ py
;
1005 if (xoff
+ nx
<= ctx
->wox
|| xoff
>= ctx
->wox
+ ctx
->wsx
||
1006 yoff
+ ny
<= ctx
->woy
|| yoff
>= ctx
->woy
+ ctx
->wsy
)
1011 /* Clamp line position to visible part of pane. */
1013 tty_clamp_line(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int px
, u_int py
,
1014 u_int nx
, u_int
*i
, u_int
*x
, u_int
*rx
, u_int
*ry
)
1016 u_int xoff
= ctx
->rxoff
+ px
;
1018 if (!tty_is_visible(tty
, ctx
, px
, py
, nx
, 1))
1020 *ry
= ctx
->yoff
+ py
- ctx
->woy
;
1022 if (xoff
>= ctx
->wox
&& xoff
+ nx
<= ctx
->wox
+ ctx
->wsx
) {
1025 *x
= ctx
->xoff
+ px
- ctx
->wox
;
1027 } else if (xoff
< ctx
->wox
&& xoff
+ nx
> ctx
->wox
+ ctx
->wsx
) {
1028 /* Both left and right not visible. */
1032 } else if (xoff
< ctx
->wox
) {
1033 /* Left not visible. */
1034 *i
= ctx
->wox
- (ctx
->xoff
+ px
);
1038 /* Right not visible. */
1040 *x
= (ctx
->xoff
+ px
) - ctx
->wox
;
1041 *rx
= ctx
->wsx
- *x
;
1044 fatalx("%s: x too big, %u > %u", __func__
, *rx
, nx
);
1051 tty_clear_line(struct tty
*tty
, const struct grid_cell
*defaults
, u_int py
,
1052 u_int px
, u_int nx
, u_int bg
)
1054 struct client
*c
= tty
->client
;
1057 log_debug("%s: %s, %u at %u,%u", __func__
, c
->name
, nx
, px
, py
);
1059 /* Nothing to clear. */
1063 /* If genuine BCE is available, can try escape sequences. */
1064 if (c
->overlay_check
== NULL
&& !tty_fake_bce(tty
, defaults
, bg
)) {
1065 /* Off the end of the line, use EL if available. */
1066 if (px
+ nx
>= tty
->sx
&& tty_term_has(tty
->term
, TTYC_EL
)) {
1067 tty_cursor(tty
, px
, py
);
1068 tty_putcode(tty
, TTYC_EL
);
1072 /* At the start of the line. Use EL1. */
1073 if (px
== 0 && tty_term_has(tty
->term
, TTYC_EL1
)) {
1074 tty_cursor(tty
, px
+ nx
- 1, py
);
1075 tty_putcode(tty
, TTYC_EL1
);
1079 /* Section of line. Use ECH if possible. */
1080 if (tty_term_has(tty
->term
, TTYC_ECH
)) {
1081 tty_cursor(tty
, px
, py
);
1082 tty_putcode1(tty
, TTYC_ECH
, nx
);
1088 * Couldn't use an escape sequence, use spaces. Clear only the visible
1089 * bit if there is an overlay.
1091 for (i
= 0; i
< nx
; i
++) {
1092 if (!tty_check_overlay(tty
, px
+ i
, py
))
1095 tty_cursor(tty
, px
, py
);
1096 tty_repeat_space(tty
, i
);
1097 for (; i
< nx
; i
++) {
1098 if (tty_check_overlay(tty
, px
+ i
, py
))
1101 tty_cursor(tty
, px
+ i
, py
);
1102 tty_repeat_space(tty
, nx
- i
);
1105 /* Clear a line, adjusting to visible part of pane. */
1107 tty_clear_pane_line(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int py
,
1108 u_int px
, u_int nx
, u_int bg
)
1110 struct client
*c
= tty
->client
;
1113 log_debug("%s: %s, %u at %u,%u", __func__
, c
->name
, nx
, px
, py
);
1115 if (tty_clamp_line(tty
, ctx
, px
, py
, nx
, &i
, &x
, &rx
, &ry
))
1116 tty_clear_line(tty
, &ctx
->defaults
, ry
, x
, rx
, bg
);
1119 /* Clamp area position to visible part of pane. */
1121 tty_clamp_area(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int px
, u_int py
,
1122 u_int nx
, u_int ny
, u_int
*i
, u_int
*j
, u_int
*x
, u_int
*y
, u_int
*rx
,
1125 u_int xoff
= ctx
->rxoff
+ px
, yoff
= ctx
->ryoff
+ py
;
1127 if (!tty_is_visible(tty
, ctx
, px
, py
, nx
, ny
))
1130 if (xoff
>= ctx
->wox
&& xoff
+ nx
<= ctx
->wox
+ ctx
->wsx
) {
1133 *x
= ctx
->xoff
+ px
- ctx
->wox
;
1135 } else if (xoff
< ctx
->wox
&& xoff
+ nx
> ctx
->wox
+ ctx
->wsx
) {
1136 /* Both left and right not visible. */
1140 } else if (xoff
< ctx
->wox
) {
1141 /* Left not visible. */
1142 *i
= ctx
->wox
- (ctx
->xoff
+ px
);
1146 /* Right not visible. */
1148 *x
= (ctx
->xoff
+ px
) - ctx
->wox
;
1149 *rx
= ctx
->wsx
- *x
;
1152 fatalx("%s: x too big, %u > %u", __func__
, *rx
, nx
);
1154 if (yoff
>= ctx
->woy
&& yoff
+ ny
<= ctx
->woy
+ ctx
->wsy
) {
1157 *y
= ctx
->yoff
+ py
- ctx
->woy
;
1159 } else if (yoff
< ctx
->woy
&& yoff
+ ny
> ctx
->woy
+ ctx
->wsy
) {
1160 /* Both top and bottom not visible. */
1164 } else if (yoff
< ctx
->woy
) {
1165 /* Top not visible. */
1166 *j
= ctx
->woy
- (ctx
->yoff
+ py
);
1170 /* Bottom not visible. */
1172 *y
= (ctx
->yoff
+ py
) - ctx
->woy
;
1173 *ry
= ctx
->wsy
- *y
;
1176 fatalx("%s: y too big, %u > %u", __func__
, *ry
, ny
);
1181 /* Clear an area, adjusting to visible part of pane. */
1183 tty_clear_area(struct tty
*tty
, const struct grid_cell
*defaults
, u_int py
,
1184 u_int ny
, u_int px
, u_int nx
, u_int bg
)
1186 struct client
*c
= tty
->client
;
1190 log_debug("%s: %s, %u,%u at %u,%u", __func__
, c
->name
, nx
, ny
, px
, py
);
1192 /* Nothing to clear. */
1193 if (nx
== 0 || ny
== 0)
1196 /* If genuine BCE is available, can try escape sequences. */
1197 if (c
->overlay_check
== NULL
&& !tty_fake_bce(tty
, defaults
, bg
)) {
1198 /* Use ED if clearing off the bottom of the terminal. */
1200 px
+ nx
>= tty
->sx
&&
1201 py
+ ny
>= tty
->sy
&&
1202 tty_term_has(tty
->term
, TTYC_ED
)) {
1203 tty_cursor(tty
, 0, py
);
1204 tty_putcode(tty
, TTYC_ED
);
1209 * On VT420 compatible terminals we can use DECFRA if the
1210 * background colour isn't default (because it doesn't work
1213 if ((tty
->term
->flags
& TERM_DECFRA
) && !COLOUR_DEFAULT(bg
)) {
1214 xsnprintf(tmp
, sizeof tmp
, "\033[32;%u;%u;%u;%u$x",
1215 py
+ 1, px
+ 1, py
+ ny
, px
+ nx
);
1220 /* Full lines can be scrolled away to clear them. */
1222 px
+ nx
>= tty
->sx
&&
1224 tty_term_has(tty
->term
, TTYC_CSR
) &&
1225 tty_term_has(tty
->term
, TTYC_INDN
)) {
1226 tty_region(tty
, py
, py
+ ny
- 1);
1227 tty_margin_off(tty
);
1228 tty_putcode1(tty
, TTYC_INDN
, ny
);
1233 * If margins are supported, can just scroll the area off to
1238 tty_term_has(tty
->term
, TTYC_CSR
) &&
1239 tty_use_margin(tty
) &&
1240 tty_term_has(tty
->term
, TTYC_INDN
)) {
1241 tty_region(tty
, py
, py
+ ny
- 1);
1242 tty_margin(tty
, px
, px
+ nx
- 1);
1243 tty_putcode1(tty
, TTYC_INDN
, ny
);
1248 /* Couldn't use an escape sequence, loop over the lines. */
1249 for (yy
= py
; yy
< py
+ ny
; yy
++)
1250 tty_clear_line(tty
, defaults
, yy
, px
, nx
, bg
);
1253 /* Clear an area in a pane. */
1255 tty_clear_pane_area(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int py
,
1256 u_int ny
, u_int px
, u_int nx
, u_int bg
)
1258 u_int i
, j
, x
, y
, rx
, ry
;
1260 if (tty_clamp_area(tty
, ctx
, px
, py
, nx
, ny
, &i
, &j
, &x
, &y
, &rx
, &ry
))
1261 tty_clear_area(tty
, &ctx
->defaults
, y
, ry
, x
, rx
, bg
);
1265 tty_draw_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int py
)
1267 struct screen
*s
= ctx
->s
;
1268 u_int nx
= ctx
->sx
, i
, x
, rx
, ry
;
1270 log_debug("%s: %s %u %d", __func__
, tty
->client
->name
, py
, ctx
->bigger
);
1273 tty_draw_line(tty
, s
, 0, py
, nx
, ctx
->xoff
, ctx
->yoff
+ py
,
1274 &ctx
->defaults
, ctx
->palette
);
1277 if (tty_clamp_line(tty
, ctx
, 0, py
, nx
, &i
, &x
, &rx
, &ry
)) {
1278 tty_draw_line(tty
, s
, i
, py
, rx
, x
, ry
, &ctx
->defaults
,
1283 static const struct grid_cell
*
1284 tty_check_codeset(struct tty
*tty
, const struct grid_cell
*gc
)
1286 static struct grid_cell
new;
1289 /* Characters less than 0x7f are always fine, no matter what. */
1290 if (gc
->data
.size
== 1 && *gc
->data
.data
< 0x7f)
1293 /* UTF-8 terminal and a UTF-8 character - fine. */
1294 if (tty
->client
->flags
& CLIENT_UTF8
)
1296 memcpy(&new, gc
, sizeof new);
1298 /* See if this can be mapped to an ACS character. */
1299 c
= tty_acs_reverse_get(tty
, gc
->data
.data
, gc
->data
.size
);
1301 utf8_set(&new.data
, c
);
1302 new.attr
|= GRID_ATTR_CHARSET
;
1306 /* Replace by the right number of underscores. */
1307 new.data
.size
= gc
->data
.width
;
1308 if (new.data
.size
> UTF8_SIZE
)
1309 new.data
.size
= UTF8_SIZE
;
1310 memset(new.data
.data
, '_', new.data
.size
);
1315 tty_check_overlay(struct tty
*tty
, u_int px
, u_int py
)
1317 struct client
*c
= tty
->client
;
1319 if (c
->overlay_check
== NULL
)
1321 return (c
->overlay_check(c
, c
->overlay_data
, px
, py
));
1325 tty_draw_line(struct tty
*tty
, struct screen
*s
, u_int px
, u_int py
, u_int nx
,
1326 u_int atx
, u_int aty
, const struct grid_cell
*defaults
,
1327 struct colour_palette
*palette
)
1329 struct grid
*gd
= s
->grid
;
1330 struct grid_cell gc
, last
;
1331 const struct grid_cell
*gcp
;
1332 struct grid_line
*gl
;
1333 struct client
*c
= tty
->client
;
1334 u_int i
, j
, ux
, sx
, width
, hidden
;
1335 int flags
, cleared
= 0, wrapped
= 0;
1340 log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__
,
1341 px
, py
, nx
, atx
, aty
);
1342 log_debug("%s: defaults: fg=%d, bg=%d", __func__
, defaults
->fg
,
1346 * py is the line in the screen to draw.
1347 * px is the start x and nx is the width to draw.
1348 * atx,aty is the line on the terminal to draw it.
1351 flags
= (tty
->flags
& TTY_NOCURSOR
);
1352 tty
->flags
|= TTY_NOCURSOR
;
1353 tty_update_mode(tty
, tty
->mode
, s
);
1355 tty_region_off(tty
);
1356 tty_margin_off(tty
);
1359 * Clamp the width to cellsize - note this is not cellused, because
1360 * there may be empty background cells after it (from BCE).
1362 sx
= screen_size_x(s
);
1365 cellsize
= grid_get_line(gd
, gd
->hsize
+ py
)->cellsize
;
1377 gl
= grid_get_line(gd
, gd
->hsize
+ py
- 1);
1379 (~gl
->flags
& GRID_LINE_WRAPPED
) ||
1381 tty
->cx
< tty
->sx
||
1386 tty_term_has(tty
->term
, TTYC_EL1
) &&
1387 !tty_fake_bce(tty
, defaults
, 8) &&
1388 c
->overlay_check
== NULL
) {
1389 tty_default_attributes(tty
, defaults
, palette
, 8);
1390 tty_cursor(tty
, nx
- 1, aty
);
1391 tty_putcode(tty
, TTYC_EL1
);
1395 log_debug("%s: wrapped line %u", __func__
, aty
);
1399 memcpy(&last
, &grid_default_cell
, sizeof last
);
1403 for (i
= 0; i
< sx
; i
++) {
1404 grid_view_get_cell(gd
, px
+ i
, py
, &gc
);
1405 gcp
= tty_check_codeset(tty
, &gc
);
1407 (!tty_check_overlay(tty
, atx
+ ux
+ width
, aty
) ||
1408 (gcp
->attr
& GRID_ATTR_CHARSET
) ||
1409 gcp
->flags
!= last
.flags
||
1410 gcp
->attr
!= last
.attr
||
1411 gcp
->fg
!= last
.fg
||
1412 gcp
->bg
!= last
.bg
||
1413 gcp
->us
!= last
.us
||
1414 ux
+ width
+ gcp
->data
.width
> nx
||
1415 (sizeof buf
) - len
< gcp
->data
.size
)) {
1416 tty_attributes(tty
, &last
, defaults
, palette
);
1417 if (last
.flags
& GRID_FLAG_CLEARED
) {
1418 log_debug("%s: %zu cleared", __func__
, len
);
1419 tty_clear_line(tty
, defaults
, aty
, atx
+ ux
,
1422 if (!wrapped
|| atx
!= 0 || ux
!= 0)
1423 tty_cursor(tty
, atx
+ ux
, aty
);
1424 tty_putn(tty
, buf
, len
, width
);
1433 if (gcp
->flags
& GRID_FLAG_SELECTED
)
1434 screen_select_cell(s
, &last
, gcp
);
1436 memcpy(&last
, gcp
, sizeof last
);
1439 for (j
= 0; j
< gcp
->data
.width
; j
++) {
1440 if (!tty_check_overlay(tty
, atx
+ ux
+ j
, aty
))
1443 if (hidden
!= 0 && hidden
== gcp
->data
.width
) {
1444 if (~gcp
->flags
& GRID_FLAG_PADDING
)
1445 ux
+= gcp
->data
.width
;
1446 } else if (hidden
!= 0 || ux
+ gcp
->data
.width
> nx
) {
1447 if (~gcp
->flags
& GRID_FLAG_PADDING
) {
1448 tty_attributes(tty
, &last
, defaults
, palette
);
1449 tty_cursor(tty
, atx
+ ux
, aty
);
1450 for (j
= 0; j
< gcp
->data
.width
; j
++) {
1453 if (tty_check_overlay(tty
, atx
+ ux
,
1457 tty_cursor(tty
, atx
+ ux
+ 1,
1463 } else if (gcp
->attr
& GRID_ATTR_CHARSET
) {
1464 tty_attributes(tty
, &last
, defaults
, palette
);
1465 tty_cursor(tty
, atx
+ ux
, aty
);
1466 for (j
= 0; j
< gcp
->data
.size
; j
++)
1467 tty_putc(tty
, gcp
->data
.data
[j
]);
1468 ux
+= gcp
->data
.width
;
1469 } else if (~gcp
->flags
& GRID_FLAG_PADDING
) {
1470 memcpy(buf
+ len
, gcp
->data
.data
, gcp
->data
.size
);
1471 len
+= gcp
->data
.size
;
1472 width
+= gcp
->data
.width
;
1475 if (len
!= 0 && ((~last
.flags
& GRID_FLAG_CLEARED
) || last
.bg
!= 8)) {
1476 tty_attributes(tty
, &last
, defaults
, palette
);
1477 if (last
.flags
& GRID_FLAG_CLEARED
) {
1478 log_debug("%s: %zu cleared (end)", __func__
, len
);
1479 tty_clear_line(tty
, defaults
, aty
, atx
+ ux
, width
,
1482 if (!wrapped
|| atx
!= 0 || ux
!= 0)
1483 tty_cursor(tty
, atx
+ ux
, aty
);
1484 tty_putn(tty
, buf
, len
, width
);
1489 if (!cleared
&& ux
< nx
) {
1490 log_debug("%s: %u to end of line (%zu cleared)", __func__
,
1492 tty_default_attributes(tty
, defaults
, palette
, 8);
1493 tty_clear_line(tty
, defaults
, aty
, atx
+ ux
, nx
- ux
, 8);
1496 tty
->flags
= (tty
->flags
& ~TTY_NOCURSOR
) | flags
;
1497 tty_update_mode(tty
, tty
->mode
, s
);
1501 tty_sync_start(struct tty
*tty
)
1503 if (tty
->flags
& TTY_BLOCK
)
1505 if (tty
->flags
& TTY_SYNCING
)
1507 tty
->flags
|= TTY_SYNCING
;
1509 if (tty_term_has(tty
->term
, TTYC_SYNC
)) {
1510 log_debug("%s sync start", tty
->client
->name
);
1511 tty_putcode1(tty
, TTYC_SYNC
, 1);
1516 tty_sync_end(struct tty
*tty
)
1518 if (tty
->flags
& TTY_BLOCK
)
1520 if (~tty
->flags
& TTY_SYNCING
)
1522 tty
->flags
&= ~TTY_SYNCING
;
1524 if (tty_term_has(tty
->term
, TTYC_SYNC
)) {
1525 log_debug("%s sync end", tty
->client
->name
);
1526 tty_putcode1(tty
, TTYC_SYNC
, 2);
1531 tty_client_ready(struct client
*c
)
1533 if (c
->session
== NULL
|| c
->tty
.term
== NULL
)
1535 if (c
->flags
& (CLIENT_REDRAWWINDOW
|CLIENT_SUSPENDED
))
1537 if (c
->tty
.flags
& TTY_FREEZE
)
1543 tty_write(void (*cmdfn
)(struct tty
*, const struct tty_ctx
*),
1544 struct tty_ctx
*ctx
)
1549 if (ctx
->set_client_cb
== NULL
)
1551 TAILQ_FOREACH(c
, &clients
, entry
) {
1552 if (!tty_client_ready(c
))
1554 state
= ctx
->set_client_cb(ctx
, c
);
1559 cmdfn(&c
->tty
, ctx
);
1564 tty_cmd_insertcharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
1566 struct client
*c
= tty
->client
;
1569 !tty_full_width(tty
, ctx
) ||
1570 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1571 (!tty_term_has(tty
->term
, TTYC_ICH
) &&
1572 !tty_term_has(tty
->term
, TTYC_ICH1
)) ||
1573 c
->overlay_check
!= NULL
) {
1574 tty_draw_pane(tty
, ctx
, ctx
->ocy
);
1578 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1580 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1582 tty_emulate_repeat(tty
, TTYC_ICH
, TTYC_ICH1
, ctx
->num
);
1586 tty_cmd_deletecharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
1588 struct client
*c
= tty
->client
;
1591 !tty_full_width(tty
, ctx
) ||
1592 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1593 (!tty_term_has(tty
->term
, TTYC_DCH
) &&
1594 !tty_term_has(tty
->term
, TTYC_DCH1
)) ||
1595 c
->overlay_check
!= NULL
) {
1596 tty_draw_pane(tty
, ctx
, ctx
->ocy
);
1600 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1602 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1604 tty_emulate_repeat(tty
, TTYC_DCH
, TTYC_DCH1
, ctx
->num
);
1608 tty_cmd_clearcharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
1610 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1612 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, ctx
->ocx
, ctx
->num
, ctx
->bg
);
1616 tty_cmd_insertline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1618 struct client
*c
= tty
->client
;
1621 !tty_full_width(tty
, ctx
) ||
1622 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1623 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1624 !tty_term_has(tty
->term
, TTYC_IL1
) ||
1627 c
->overlay_check
!= NULL
) {
1628 tty_redraw_region(tty
, ctx
);
1632 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1634 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1635 tty_margin_off(tty
);
1636 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1638 tty_emulate_repeat(tty
, TTYC_IL
, TTYC_IL1
, ctx
->num
);
1639 tty
->cx
= tty
->cy
= UINT_MAX
;
1643 tty_cmd_deleteline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1645 struct client
*c
= tty
->client
;
1648 !tty_full_width(tty
, ctx
) ||
1649 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1650 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1651 !tty_term_has(tty
->term
, TTYC_DL1
) ||
1654 c
->overlay_check
!= NULL
) {
1655 tty_redraw_region(tty
, ctx
);
1659 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1661 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1662 tty_margin_off(tty
);
1663 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1665 tty_emulate_repeat(tty
, TTYC_DL
, TTYC_DL1
, ctx
->num
);
1666 tty
->cx
= tty
->cy
= UINT_MAX
;
1670 tty_cmd_clearline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1672 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1674 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, 0, ctx
->sx
, ctx
->bg
);
1678 tty_cmd_clearendofline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1680 u_int nx
= ctx
->sx
- ctx
->ocx
;
1682 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1684 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, ctx
->ocx
, nx
, ctx
->bg
);
1688 tty_cmd_clearstartofline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1690 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1692 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, 0, ctx
->ocx
+ 1, ctx
->bg
);
1696 tty_cmd_reverseindex(struct tty
*tty
, const struct tty_ctx
*ctx
)
1698 struct client
*c
= tty
->client
;
1700 if (ctx
->ocy
!= ctx
->orupper
)
1704 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1705 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1706 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1707 (!tty_term_has(tty
->term
, TTYC_RI
) &&
1708 !tty_term_has(tty
->term
, TTYC_RIN
)) ||
1711 c
->overlay_check
!= NULL
) {
1712 tty_redraw_region(tty
, ctx
);
1716 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1718 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1719 tty_margin_pane(tty
, ctx
);
1720 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->orupper
);
1722 if (tty_term_has(tty
->term
, TTYC_RI
))
1723 tty_putcode(tty
, TTYC_RI
);
1725 tty_putcode1(tty
, TTYC_RIN
, 1);
1729 tty_cmd_linefeed(struct tty
*tty
, const struct tty_ctx
*ctx
)
1731 struct client
*c
= tty
->client
;
1733 if (ctx
->ocy
!= ctx
->orlower
)
1737 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1738 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1739 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1742 c
->overlay_check
!= NULL
) {
1743 tty_redraw_region(tty
, ctx
);
1747 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1749 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1750 tty_margin_pane(tty
, ctx
);
1753 * If we want to wrap a pane while using margins, the cursor needs to
1754 * be exactly on the right of the region. If the cursor is entirely off
1755 * the edge - move it back to the right. Some terminals are funny about
1756 * this and insert extra spaces, so only use the right if margins are
1759 if (ctx
->xoff
+ ctx
->ocx
> tty
->rright
) {
1760 if (!tty_use_margin(tty
))
1761 tty_cursor(tty
, 0, ctx
->yoff
+ ctx
->ocy
);
1763 tty_cursor(tty
, tty
->rright
, ctx
->yoff
+ ctx
->ocy
);
1765 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1767 tty_putc(tty
, '\n');
1771 tty_cmd_scrollup(struct tty
*tty
, const struct tty_ctx
*ctx
)
1773 struct client
*c
= tty
->client
;
1777 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1778 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1779 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1782 c
->overlay_check
!= NULL
) {
1783 tty_redraw_region(tty
, ctx
);
1787 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1789 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1790 tty_margin_pane(tty
, ctx
);
1792 if (ctx
->num
== 1 || !tty_term_has(tty
->term
, TTYC_INDN
)) {
1793 if (!tty_use_margin(tty
))
1794 tty_cursor(tty
, 0, tty
->rlower
);
1796 tty_cursor(tty
, tty
->rright
, tty
->rlower
);
1797 for (i
= 0; i
< ctx
->num
; i
++)
1798 tty_putc(tty
, '\n');
1800 if (tty
->cy
== UINT_MAX
)
1801 tty_cursor(tty
, 0, 0);
1803 tty_cursor(tty
, 0, tty
->cy
);
1804 tty_putcode1(tty
, TTYC_INDN
, ctx
->num
);
1809 tty_cmd_scrolldown(struct tty
*tty
, const struct tty_ctx
*ctx
)
1812 struct client
*c
= tty
->client
;
1815 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1816 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1817 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1818 (!tty_term_has(tty
->term
, TTYC_RI
) &&
1819 !tty_term_has(tty
->term
, TTYC_RIN
)) ||
1822 c
->overlay_check
!= NULL
) {
1823 tty_redraw_region(tty
, ctx
);
1827 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1829 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1830 tty_margin_pane(tty
, ctx
);
1831 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->orupper
);
1833 if (tty_term_has(tty
->term
, TTYC_RIN
))
1834 tty_putcode1(tty
, TTYC_RIN
, ctx
->num
);
1836 for (i
= 0; i
< ctx
->num
; i
++)
1837 tty_putcode(tty
, TTYC_RI
);
1842 tty_cmd_clearendofscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
1844 u_int px
, py
, nx
, ny
;
1846 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1848 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
1849 tty_margin_off(tty
);
1854 ny
= ctx
->sy
- ctx
->ocy
- 1;
1856 tty_clear_pane_area(tty
, ctx
, py
, ny
, px
, nx
, ctx
->bg
);
1859 nx
= ctx
->sx
- ctx
->ocx
;
1862 tty_clear_pane_line(tty
, ctx
, py
, px
, nx
, ctx
->bg
);
1866 tty_cmd_clearstartofscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
1868 u_int px
, py
, nx
, ny
;
1870 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1872 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
1873 tty_margin_off(tty
);
1880 tty_clear_pane_area(tty
, ctx
, py
, ny
, px
, nx
, ctx
->bg
);
1886 tty_clear_pane_line(tty
, ctx
, py
, px
, nx
, ctx
->bg
);
1890 tty_cmd_clearscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
1892 u_int px
, py
, nx
, ny
;
1894 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
);
1896 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
1897 tty_margin_off(tty
);
1904 tty_clear_pane_area(tty
, ctx
, py
, ny
, px
, nx
, ctx
->bg
);
1908 tty_cmd_alignmenttest(struct tty
*tty
, const struct tty_ctx
*ctx
)
1913 ctx
->redraw_cb(ctx
);
1917 tty_attributes(tty
, &grid_default_cell
, &ctx
->defaults
, ctx
->palette
);
1919 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
1920 tty_margin_off(tty
);
1922 for (j
= 0; j
< ctx
->sy
; j
++) {
1923 tty_cursor_pane(tty
, ctx
, 0, j
);
1924 for (i
= 0; i
< ctx
->sx
; i
++)
1930 tty_cmd_cell(struct tty
*tty
, const struct tty_ctx
*ctx
)
1932 const struct grid_cell
*gcp
= ctx
->cell
;
1933 struct screen
*s
= ctx
->s
;
1936 px
= ctx
->xoff
+ ctx
->ocx
- ctx
->wox
;
1937 py
= ctx
->yoff
+ ctx
->ocy
- ctx
->woy
;
1938 if (!tty_is_visible(tty
, ctx
, ctx
->ocx
, ctx
->ocy
, 1, 1) ||
1939 (gcp
->data
.width
== 1 && !tty_check_overlay(tty
, px
, py
)))
1942 /* Handle partially obstructed wide characters. */
1943 if (gcp
->data
.width
> 1) {
1944 for (i
= 0; i
< gcp
->data
.width
; i
++) {
1945 if (!tty_check_overlay(tty
, px
+ i
, py
)) {
1946 tty_draw_line(tty
, s
, s
->cx
, s
->cy
,
1947 gcp
->data
.width
, px
, py
, &ctx
->defaults
,
1954 if (ctx
->xoff
+ ctx
->ocx
- ctx
->wox
> tty
->sx
- 1 &&
1955 ctx
->ocy
== ctx
->orlower
&&
1956 tty_full_width(tty
, ctx
))
1957 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1959 tty_margin_off(tty
);
1960 tty_cursor_pane_unless_wrap(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1962 tty_cell(tty
, ctx
->cell
, &ctx
->defaults
, ctx
->palette
);
1966 tty_cmd_cells(struct tty
*tty
, const struct tty_ctx
*ctx
)
1970 if (!tty_is_visible(tty
, ctx
, ctx
->ocx
, ctx
->ocy
, ctx
->num
, 1))
1974 (ctx
->xoff
+ ctx
->ocx
< ctx
->wox
||
1975 ctx
->xoff
+ ctx
->ocx
+ ctx
->num
> ctx
->wox
+ ctx
->wsx
)) {
1976 if (!ctx
->wrapped
||
1977 !tty_full_width(tty
, ctx
) ||
1978 (tty
->term
->flags
& TERM_NOAM
) ||
1979 ctx
->xoff
+ ctx
->ocx
!= 0 ||
1980 ctx
->yoff
+ ctx
->ocy
!= tty
->cy
+ 1 ||
1981 tty
->cx
< tty
->sx
||
1982 tty
->cy
== tty
->rlower
)
1983 tty_draw_pane(tty
, ctx
, ctx
->ocy
);
1985 ctx
->redraw_cb(ctx
);
1989 tty_margin_off(tty
);
1990 tty_cursor_pane_unless_wrap(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1992 tty_attributes(tty
, ctx
->cell
, &ctx
->defaults
, ctx
->palette
);
1993 for (i
= 0; i
< ctx
->num
; i
++) {
1994 if (!tty_check_overlay(tty
, tty
->cx
+ i
, tty
->cy
))
1997 tty_putn(tty
, ctx
->ptr
, i
, i
);
1998 for (; i
< ctx
->num
; i
++) {
1999 if (tty_check_overlay(tty
, tty
->cx
+ hide
, tty
->cy
))
2003 tty_cursor(tty
, tty
->cx
+ hide
, tty
->cy
);
2004 tty_putn(tty
, (char *)ctx
->ptr
+ i
, ctx
->num
- i
, ctx
->num
- i
);
2008 tty_cmd_setselection(struct tty
*tty
, const struct tty_ctx
*ctx
)
2010 tty_set_selection(tty
, ctx
->ptr
, ctx
->num
);
2014 tty_set_selection(struct tty
*tty
, const char *buf
, size_t len
)
2019 if (~tty
->flags
& TTY_STARTED
)
2021 if (!tty_term_has(tty
->term
, TTYC_MS
))
2024 size
= 4 * ((len
+ 2) / 3) + 1; /* storage for base64 */
2025 encoded
= xmalloc(size
);
2027 b64_ntop(buf
, len
, encoded
, size
);
2028 tty_putcode_ptr2(tty
, TTYC_MS
, "", encoded
);
2029 tty
->client
->redraw
= EVBUFFER_LENGTH(tty
->out
);
2035 tty_cmd_rawstring(struct tty
*tty
, const struct tty_ctx
*ctx
)
2037 tty_add(tty
, ctx
->ptr
, ctx
->num
);
2038 tty_invalidate(tty
);
2042 tty_cmd_syncstart(struct tty
*tty
, const struct tty_ctx
*ctx
)
2044 if (ctx
->num
== 0x11) {
2046 * This is an overlay and a command that moves the cursor so
2047 * start synchronized updates.
2049 tty_sync_start(tty
);
2050 } else if (~ctx
->num
& 0x10) {
2052 * This is a pane. If there is an overlay, always start;
2053 * otherwise, only if requested.
2055 if (ctx
->num
|| tty
->client
->overlay_draw
!= NULL
)
2056 tty_sync_start(tty
);
2061 tty_cell(struct tty
*tty
, const struct grid_cell
*gc
,
2062 const struct grid_cell
*defaults
, struct colour_palette
*palette
)
2064 const struct grid_cell
*gcp
;
2066 /* Skip last character if terminal is stupid. */
2067 if ((tty
->term
->flags
& TERM_NOAM
) &&
2068 tty
->cy
== tty
->sy
- 1 &&
2069 tty
->cx
== tty
->sx
- 1)
2072 /* If this is a padding character, do nothing. */
2073 if (gc
->flags
& GRID_FLAG_PADDING
)
2076 /* Check the output codeset and apply attributes. */
2077 gcp
= tty_check_codeset(tty
, gc
);
2078 tty_attributes(tty
, gcp
, defaults
, palette
);
2080 /* If it is a single character, write with putc to handle ACS. */
2081 if (gcp
->data
.size
== 1) {
2082 tty_attributes(tty
, gcp
, defaults
, palette
);
2083 if (*gcp
->data
.data
< 0x20 || *gcp
->data
.data
== 0x7f)
2085 tty_putc(tty
, *gcp
->data
.data
);
2089 /* Write the data. */
2090 tty_putn(tty
, gcp
->data
.data
, gcp
->data
.size
, gcp
->data
.width
);
2094 tty_reset(struct tty
*tty
)
2096 struct grid_cell
*gc
= &tty
->cell
;
2098 if (!grid_cells_equal(gc
, &grid_default_cell
)) {
2099 if ((gc
->attr
& GRID_ATTR_CHARSET
) && tty_acs_needed(tty
))
2100 tty_putcode(tty
, TTYC_RMACS
);
2101 tty_putcode(tty
, TTYC_SGR0
);
2102 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
2104 memcpy(&tty
->last_cell
, &grid_default_cell
, sizeof tty
->last_cell
);
2108 tty_invalidate(struct tty
*tty
)
2110 memcpy(&tty
->cell
, &grid_default_cell
, sizeof tty
->cell
);
2111 memcpy(&tty
->last_cell
, &grid_default_cell
, sizeof tty
->last_cell
);
2113 tty
->cx
= tty
->cy
= UINT_MAX
;
2114 tty
->rupper
= tty
->rleft
= UINT_MAX
;
2115 tty
->rlower
= tty
->rright
= UINT_MAX
;
2117 if (tty
->flags
& TTY_STARTED
) {
2118 if (tty_use_margin(tty
))
2119 tty_putcode(tty
, TTYC_ENMG
);
2120 tty_putcode(tty
, TTYC_SGR0
);
2122 tty
->mode
= ALL_MODES
;
2123 tty_update_mode(tty
, MODE_CURSOR
, NULL
);
2125 tty_cursor(tty
, 0, 0);
2126 tty_region_off(tty
);
2127 tty_margin_off(tty
);
2129 tty
->mode
= MODE_CURSOR
;
2132 /* Turn off margin. */
2134 tty_region_off(struct tty
*tty
)
2136 tty_region(tty
, 0, tty
->sy
- 1);
2139 /* Set region inside pane. */
2141 tty_region_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int rupper
,
2144 tty_region(tty
, ctx
->yoff
+ rupper
- ctx
->woy
,
2145 ctx
->yoff
+ rlower
- ctx
->woy
);
2148 /* Set region at absolute position. */
2150 tty_region(struct tty
*tty
, u_int rupper
, u_int rlower
)
2152 if (tty
->rlower
== rlower
&& tty
->rupper
== rupper
)
2154 if (!tty_term_has(tty
->term
, TTYC_CSR
))
2157 tty
->rupper
= rupper
;
2158 tty
->rlower
= rlower
;
2161 * Some terminals (such as PuTTY) do not correctly reset the cursor to
2162 * 0,0 if it is beyond the last column (they do not reset their wrap
2163 * flag so further output causes a line feed). As a workaround, do an
2164 * explicit move to 0 first.
2166 if (tty
->cx
>= tty
->sx
) {
2167 if (tty
->cy
== UINT_MAX
)
2168 tty_cursor(tty
, 0, 0);
2170 tty_cursor(tty
, 0, tty
->cy
);
2173 tty_putcode2(tty
, TTYC_CSR
, tty
->rupper
, tty
->rlower
);
2174 tty
->cx
= tty
->cy
= UINT_MAX
;
2177 /* Turn off margin. */
2179 tty_margin_off(struct tty
*tty
)
2181 tty_margin(tty
, 0, tty
->sx
- 1);
2184 /* Set margin inside pane. */
2186 tty_margin_pane(struct tty
*tty
, const struct tty_ctx
*ctx
)
2188 tty_margin(tty
, ctx
->xoff
- ctx
->wox
,
2189 ctx
->xoff
+ ctx
->sx
- 1 - ctx
->wox
);
2192 /* Set margin at absolute position. */
2194 tty_margin(struct tty
*tty
, u_int rleft
, u_int rright
)
2196 if (!tty_use_margin(tty
))
2198 if (tty
->rleft
== rleft
&& tty
->rright
== rright
)
2201 tty_putcode2(tty
, TTYC_CSR
, tty
->rupper
, tty
->rlower
);
2204 tty
->rright
= rright
;
2206 if (rleft
== 0 && rright
== tty
->sx
- 1)
2207 tty_putcode(tty
, TTYC_CLMG
);
2209 tty_putcode2(tty
, TTYC_CMG
, rleft
, rright
);
2210 tty
->cx
= tty
->cy
= UINT_MAX
;
2214 * Move the cursor, unless it would wrap itself when the next character is
2218 tty_cursor_pane_unless_wrap(struct tty
*tty
, const struct tty_ctx
*ctx
,
2221 if (!ctx
->wrapped
||
2222 !tty_full_width(tty
, ctx
) ||
2223 (tty
->term
->flags
& TERM_NOAM
) ||
2224 ctx
->xoff
+ cx
!= 0 ||
2225 ctx
->yoff
+ cy
!= tty
->cy
+ 1 ||
2226 tty
->cx
< tty
->sx
||
2227 tty
->cy
== tty
->rlower
)
2228 tty_cursor_pane(tty
, ctx
, cx
, cy
);
2230 log_debug("%s: will wrap at %u,%u", __func__
, tty
->cx
, tty
->cy
);
2233 /* Move cursor inside pane. */
2235 tty_cursor_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int cx
, u_int cy
)
2237 tty_cursor(tty
, ctx
->xoff
+ cx
- ctx
->wox
, ctx
->yoff
+ cy
- ctx
->woy
);
2240 /* Move cursor to absolute position. */
2242 tty_cursor(struct tty
*tty
, u_int cx
, u_int cy
)
2244 struct tty_term
*term
= tty
->term
;
2248 if (tty
->flags
& TTY_BLOCK
)
2251 if (cx
> tty
->sx
- 1)
2258 if (cx
== thisx
&& cy
== thisy
)
2261 /* Very end of the line, just use absolute movement. */
2262 if (thisx
> tty
->sx
- 1)
2265 /* Move to home position (0, 0). */
2266 if (cx
== 0 && cy
== 0 && tty_term_has(term
, TTYC_HOME
)) {
2267 tty_putcode(tty
, TTYC_HOME
);
2271 /* Zero on the next line. */
2272 if (cx
== 0 && cy
== thisy
+ 1 && thisy
!= tty
->rlower
&&
2273 (!tty_use_margin(tty
) || tty
->rleft
== 0)) {
2274 tty_putc(tty
, '\r');
2275 tty_putc(tty
, '\n');
2279 /* Moving column or row. */
2282 * Moving column only, row staying the same.
2286 if (cx
== 0 && (!tty_use_margin(tty
) || tty
->rleft
== 0)) {
2287 tty_putc(tty
, '\r');
2291 /* One to the left. */
2292 if (cx
== thisx
- 1 && tty_term_has(term
, TTYC_CUB1
)) {
2293 tty_putcode(tty
, TTYC_CUB1
);
2297 /* One to the right. */
2298 if (cx
== thisx
+ 1 && tty_term_has(term
, TTYC_CUF1
)) {
2299 tty_putcode(tty
, TTYC_CUF1
);
2303 /* Calculate difference. */
2304 change
= thisx
- cx
; /* +ve left, -ve right */
2307 * Use HPA if change is larger than absolute, otherwise move
2308 * the cursor with CUB/CUF.
2310 if ((u_int
) abs(change
) > cx
&& tty_term_has(term
, TTYC_HPA
)) {
2311 tty_putcode1(tty
, TTYC_HPA
, cx
);
2313 } else if (change
> 0 &&
2314 tty_term_has(term
, TTYC_CUB
) &&
2315 !tty_use_margin(tty
)) {
2316 if (change
== 2 && tty_term_has(term
, TTYC_CUB1
)) {
2317 tty_putcode(tty
, TTYC_CUB1
);
2318 tty_putcode(tty
, TTYC_CUB1
);
2321 tty_putcode1(tty
, TTYC_CUB
, change
);
2323 } else if (change
< 0 &&
2324 tty_term_has(term
, TTYC_CUF
) &&
2325 !tty_use_margin(tty
)) {
2326 tty_putcode1(tty
, TTYC_CUF
, -change
);
2329 } else if (cx
== thisx
) {
2331 * Moving row only, column staying the same.
2335 if (thisy
!= tty
->rupper
&&
2336 cy
== thisy
- 1 && tty_term_has(term
, TTYC_CUU1
)) {
2337 tty_putcode(tty
, TTYC_CUU1
);
2342 if (thisy
!= tty
->rlower
&&
2343 cy
== thisy
+ 1 && tty_term_has(term
, TTYC_CUD1
)) {
2344 tty_putcode(tty
, TTYC_CUD1
);
2348 /* Calculate difference. */
2349 change
= thisy
- cy
; /* +ve up, -ve down */
2352 * Try to use VPA if change is larger than absolute or if this
2353 * change would cross the scroll region, otherwise use CUU/CUD.
2355 if ((u_int
) abs(change
) > cy
||
2356 (change
< 0 && cy
- change
> tty
->rlower
) ||
2357 (change
> 0 && cy
- change
< tty
->rupper
)) {
2358 if (tty_term_has(term
, TTYC_VPA
)) {
2359 tty_putcode1(tty
, TTYC_VPA
, cy
);
2362 } else if (change
> 0 && tty_term_has(term
, TTYC_CUU
)) {
2363 tty_putcode1(tty
, TTYC_CUU
, change
);
2365 } else if (change
< 0 && tty_term_has(term
, TTYC_CUD
)) {
2366 tty_putcode1(tty
, TTYC_CUD
, -change
);
2372 /* Absolute movement. */
2373 tty_putcode2(tty
, TTYC_CUP
, cy
, cx
);
2381 tty_attributes(struct tty
*tty
, const struct grid_cell
*gc
,
2382 const struct grid_cell
*defaults
, struct colour_palette
*palette
)
2384 struct grid_cell
*tc
= &tty
->cell
, gc2
;
2387 /* Copy cell and update default colours. */
2388 memcpy(&gc2
, gc
, sizeof gc2
);
2389 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2391 gc2
.fg
= defaults
->fg
;
2393 gc2
.bg
= defaults
->bg
;
2396 /* Ignore cell if it is the same as the last one. */
2397 if (gc2
.attr
== tty
->last_cell
.attr
&&
2398 gc2
.fg
== tty
->last_cell
.fg
&&
2399 gc2
.bg
== tty
->last_cell
.bg
&&
2400 gc2
.us
== tty
->last_cell
.us
)
2404 * If no setab, try to use the reverse attribute as a best-effort for a
2405 * non-default background. This is a bit of a hack but it doesn't do
2406 * any serious harm and makes a couple of applications happier.
2408 if (!tty_term_has(tty
->term
, TTYC_SETAB
)) {
2409 if (gc2
.attr
& GRID_ATTR_REVERSE
) {
2410 if (gc2
.fg
!= 7 && !COLOUR_DEFAULT(gc2
.fg
))
2411 gc2
.attr
&= ~GRID_ATTR_REVERSE
;
2413 if (gc2
.bg
!= 0 && !COLOUR_DEFAULT(gc2
.bg
))
2414 gc2
.attr
|= GRID_ATTR_REVERSE
;
2418 /* Fix up the colours if necessary. */
2419 tty_check_fg(tty
, palette
, &gc2
);
2420 tty_check_bg(tty
, palette
, &gc2
);
2421 tty_check_us(tty
, palette
, &gc2
);
2424 * If any bits are being cleared or the underline colour is now default,
2427 if ((tc
->attr
& ~gc2
.attr
) || (tc
->us
!= gc2
.us
&& gc2
.us
== 0))
2431 * Set the colours. This may call tty_reset() (so it comes next) and
2432 * may add to (NOT remove) the desired attributes.
2434 tty_colours(tty
, &gc2
);
2436 /* Filter out attribute bits already set. */
2437 changed
= gc2
.attr
& ~tc
->attr
;
2438 tc
->attr
= gc2
.attr
;
2440 /* Set the attributes. */
2441 if (changed
& GRID_ATTR_BRIGHT
)
2442 tty_putcode(tty
, TTYC_BOLD
);
2443 if (changed
& GRID_ATTR_DIM
)
2444 tty_putcode(tty
, TTYC_DIM
);
2445 if (changed
& GRID_ATTR_ITALICS
)
2446 tty_set_italics(tty
);
2447 if (changed
& GRID_ATTR_ALL_UNDERSCORE
) {
2448 if ((changed
& GRID_ATTR_UNDERSCORE
) ||
2449 !tty_term_has(tty
->term
, TTYC_SMULX
))
2450 tty_putcode(tty
, TTYC_SMUL
);
2451 else if (changed
& GRID_ATTR_UNDERSCORE_2
)
2452 tty_putcode1(tty
, TTYC_SMULX
, 2);
2453 else if (changed
& GRID_ATTR_UNDERSCORE_3
)
2454 tty_putcode1(tty
, TTYC_SMULX
, 3);
2455 else if (changed
& GRID_ATTR_UNDERSCORE_4
)
2456 tty_putcode1(tty
, TTYC_SMULX
, 4);
2457 else if (changed
& GRID_ATTR_UNDERSCORE_5
)
2458 tty_putcode1(tty
, TTYC_SMULX
, 5);
2460 if (changed
& GRID_ATTR_BLINK
)
2461 tty_putcode(tty
, TTYC_BLINK
);
2462 if (changed
& GRID_ATTR_REVERSE
) {
2463 if (tty_term_has(tty
->term
, TTYC_REV
))
2464 tty_putcode(tty
, TTYC_REV
);
2465 else if (tty_term_has(tty
->term
, TTYC_SMSO
))
2466 tty_putcode(tty
, TTYC_SMSO
);
2468 if (changed
& GRID_ATTR_HIDDEN
)
2469 tty_putcode(tty
, TTYC_INVIS
);
2470 if (changed
& GRID_ATTR_STRIKETHROUGH
)
2471 tty_putcode(tty
, TTYC_SMXX
);
2472 if (changed
& GRID_ATTR_OVERLINE
)
2473 tty_putcode(tty
, TTYC_SMOL
);
2474 if ((changed
& GRID_ATTR_CHARSET
) && tty_acs_needed(tty
))
2475 tty_putcode(tty
, TTYC_SMACS
);
2477 memcpy(&tty
->last_cell
, &gc2
, sizeof tty
->last_cell
);
2481 tty_colours(struct tty
*tty
, const struct grid_cell
*gc
)
2483 struct grid_cell
*tc
= &tty
->cell
;
2486 /* No changes? Nothing is necessary. */
2487 if (gc
->fg
== tc
->fg
&& gc
->bg
== tc
->bg
&& gc
->us
== tc
->us
)
2491 * Is either the default colour? This is handled specially because the
2492 * best solution might be to reset both colours to default, in which
2493 * case if only one is default need to fall onward to set the other
2496 if (COLOUR_DEFAULT(gc
->fg
) || COLOUR_DEFAULT(gc
->bg
)) {
2498 * If don't have AX but do have op, send sgr0 (op can't
2499 * actually be used because it is sometimes the same as sgr0
2500 * and sometimes isn't). This resets both colours to default.
2502 * Otherwise, try to set the default colour only as needed.
2504 have_ax
= tty_term_flag(tty
->term
, TTYC_AX
);
2505 if (!have_ax
&& tty_term_has(tty
->term
, TTYC_OP
))
2508 if (COLOUR_DEFAULT(gc
->fg
) && !COLOUR_DEFAULT(tc
->fg
)) {
2510 tty_puts(tty
, "\033[39m");
2511 else if (tc
->fg
!= 7)
2512 tty_putcode1(tty
, TTYC_SETAF
, 7);
2515 if (COLOUR_DEFAULT(gc
->bg
) && !COLOUR_DEFAULT(tc
->bg
)) {
2517 tty_puts(tty
, "\033[49m");
2518 else if (tc
->bg
!= 0)
2519 tty_putcode1(tty
, TTYC_SETAB
, 0);
2525 /* Set the foreground colour. */
2526 if (!COLOUR_DEFAULT(gc
->fg
) && gc
->fg
!= tc
->fg
)
2527 tty_colours_fg(tty
, gc
);
2530 * Set the background colour. This must come after the foreground as
2531 * tty_colour_fg() can call tty_reset().
2533 if (!COLOUR_DEFAULT(gc
->bg
) && gc
->bg
!= tc
->bg
)
2534 tty_colours_bg(tty
, gc
);
2536 /* Set the underscore colour. */
2537 if (gc
->us
!= tc
->us
)
2538 tty_colours_us(tty
, gc
);
2542 tty_check_fg(struct tty
*tty
, struct colour_palette
*palette
,
2543 struct grid_cell
*gc
)
2550 * Perform substitution if this pane has a palette. If the bright
2551 * attribute is set, use the bright entry in the palette by changing to
2552 * the aixterm colour.
2554 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2556 if (c
< 8 && gc
->attr
& GRID_ATTR_BRIGHT
)
2558 if ((c
= colour_palette_get(palette
, c
)) != -1)
2562 /* Is this a 24-bit colour? */
2563 if (gc
->fg
& COLOUR_FLAG_RGB
) {
2564 /* Not a 24-bit terminal? Translate to 256-colour palette. */
2565 if (tty
->term
->flags
& TERM_RGBCOLOURS
)
2567 colour_split_rgb(gc
->fg
, &r
, &g
, &b
);
2568 gc
->fg
= colour_find_rgb(r
, g
, b
);
2571 /* How many colours does this terminal have? */
2572 if (tty
->term
->flags
& TERM_256COLOURS
)
2575 colours
= tty_term_number(tty
->term
, TTYC_COLORS
);
2577 /* Is this a 256-colour colour? */
2578 if (gc
->fg
& COLOUR_FLAG_256
) {
2579 /* And not a 256 colour mode? */
2580 if (colours
< 256) {
2581 gc
->fg
= colour_256to16(gc
->fg
);
2591 /* Is this an aixterm colour? */
2592 if (gc
->fg
>= 90 && gc
->fg
<= 97 && colours
< 16) {
2594 gc
->attr
|= GRID_ATTR_BRIGHT
;
2599 tty_check_bg(struct tty
*tty
, struct colour_palette
*palette
,
2600 struct grid_cell
*gc
)
2606 /* Perform substitution if this pane has a palette. */
2607 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2608 if ((c
= colour_palette_get(palette
, gc
->bg
)) != -1)
2612 /* Is this a 24-bit colour? */
2613 if (gc
->bg
& COLOUR_FLAG_RGB
) {
2614 /* Not a 24-bit terminal? Translate to 256-colour palette. */
2615 if (tty
->term
->flags
& TERM_RGBCOLOURS
)
2617 colour_split_rgb(gc
->bg
, &r
, &g
, &b
);
2618 gc
->bg
= colour_find_rgb(r
, g
, b
);
2621 /* How many colours does this terminal have? */
2622 if (tty
->term
->flags
& TERM_256COLOURS
)
2625 colours
= tty_term_number(tty
->term
, TTYC_COLORS
);
2627 /* Is this a 256-colour colour? */
2628 if (gc
->bg
& COLOUR_FLAG_256
) {
2630 * And not a 256 colour mode? Translate to 16-colour
2631 * palette. Bold background doesn't exist portably, so just
2632 * discard the bold bit if set.
2634 if (colours
< 256) {
2635 gc
->bg
= colour_256to16(gc
->bg
);
2645 /* Is this an aixterm colour? */
2646 if (gc
->bg
>= 90 && gc
->bg
<= 97 && colours
< 16)
2651 tty_check_us(__unused
struct tty
*tty
, struct colour_palette
*palette
,
2652 struct grid_cell
*gc
)
2656 /* Perform substitution if this pane has a palette. */
2657 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2658 if ((c
= colour_palette_get(palette
, gc
->us
)) != -1)
2662 /* Underscore colour is set as RGB so convert a 256 colour to RGB. */
2663 if (gc
->us
& COLOUR_FLAG_256
)
2664 gc
->us
= colour_256toRGB (gc
->us
);
2668 tty_colours_fg(struct tty
*tty
, const struct grid_cell
*gc
)
2670 struct grid_cell
*tc
= &tty
->cell
;
2673 /* Is this a 24-bit or 256-colour colour? */
2674 if (gc
->fg
& COLOUR_FLAG_RGB
|| gc
->fg
& COLOUR_FLAG_256
) {
2675 if (tty_try_colour(tty
, gc
->fg
, "38") == 0)
2677 /* Should not get here, already converted in tty_check_fg. */
2681 /* Is this an aixterm bright colour? */
2682 if (gc
->fg
>= 90 && gc
->fg
<= 97) {
2683 if (tty
->term
->flags
& TERM_256COLOURS
) {
2684 xsnprintf(s
, sizeof s
, "\033[%dm", gc
->fg
);
2687 tty_putcode1(tty
, TTYC_SETAF
, gc
->fg
- 90 + 8);
2691 /* Otherwise set the foreground colour. */
2692 tty_putcode1(tty
, TTYC_SETAF
, gc
->fg
);
2695 /* Save the new values in the terminal current cell. */
2700 tty_colours_bg(struct tty
*tty
, const struct grid_cell
*gc
)
2702 struct grid_cell
*tc
= &tty
->cell
;
2705 /* Is this a 24-bit or 256-colour colour? */
2706 if (gc
->bg
& COLOUR_FLAG_RGB
|| gc
->bg
& COLOUR_FLAG_256
) {
2707 if (tty_try_colour(tty
, gc
->bg
, "48") == 0)
2709 /* Should not get here, already converted in tty_check_bg. */
2713 /* Is this an aixterm bright colour? */
2714 if (gc
->bg
>= 90 && gc
->bg
<= 97) {
2715 if (tty
->term
->flags
& TERM_256COLOURS
) {
2716 xsnprintf(s
, sizeof s
, "\033[%dm", gc
->bg
+ 10);
2719 tty_putcode1(tty
, TTYC_SETAB
, gc
->bg
- 90 + 8);
2723 /* Otherwise set the background colour. */
2724 tty_putcode1(tty
, TTYC_SETAB
, gc
->bg
);
2727 /* Save the new values in the terminal current cell. */
2732 tty_colours_us(struct tty
*tty
, const struct grid_cell
*gc
)
2734 struct grid_cell
*tc
= &tty
->cell
;
2738 /* Clear underline colour. */
2740 tty_putcode(tty
, TTYC_OL
);
2744 /* Must be an RGB colour - this should never happen. */
2745 if (~gc
->us
& COLOUR_FLAG_RGB
)
2749 * Setulc and setal follows the ncurses(3) one argument "direct colour"
2750 * capability format. Calculate the colour value.
2752 colour_split_rgb(gc
->us
, &r
, &g
, &b
);
2753 c
= (65536 * r
) + (256 * g
) + b
;
2756 * Write the colour. Only use setal if the RGB flag is set because the
2757 * non-RGB version may be wrong.
2759 if (tty_term_has(tty
->term
, TTYC_SETULC
))
2760 tty_putcode1(tty
, TTYC_SETULC
, c
);
2761 else if (tty_term_has(tty
->term
, TTYC_SETAL
) &&
2762 tty_term_has(tty
->term
, TTYC_RGB
))
2763 tty_putcode1(tty
, TTYC_SETAL
, c
);
2766 /* Save the new values in the terminal current cell. */
2771 tty_try_colour(struct tty
*tty
, int colour
, const char *type
)
2775 if (colour
& COLOUR_FLAG_256
) {
2776 if (*type
== '3' && tty_term_has(tty
->term
, TTYC_SETAF
))
2777 tty_putcode1(tty
, TTYC_SETAF
, colour
& 0xff);
2778 else if (tty_term_has(tty
->term
, TTYC_SETAB
))
2779 tty_putcode1(tty
, TTYC_SETAB
, colour
& 0xff);
2783 if (colour
& COLOUR_FLAG_RGB
) {
2784 colour_split_rgb(colour
& 0xffffff, &r
, &g
, &b
);
2785 if (*type
== '3' && tty_term_has(tty
->term
, TTYC_SETRGBF
))
2786 tty_putcode3(tty
, TTYC_SETRGBF
, r
, g
, b
);
2787 else if (tty_term_has(tty
->term
, TTYC_SETRGBB
))
2788 tty_putcode3(tty
, TTYC_SETRGBB
, r
, g
, b
);
2796 tty_window_default_style(struct grid_cell
*gc
, struct window_pane
*wp
)
2798 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
2799 gc
->fg
= wp
->palette
.fg
;
2800 gc
->bg
= wp
->palette
.bg
;
2804 tty_default_colours(struct grid_cell
*gc
, struct window_pane
*wp
)
2806 struct options
*oo
= wp
->options
;
2807 struct format_tree
*ft
;
2809 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
2811 if (wp
->flags
& PANE_STYLECHANGED
) {
2812 log_debug("%%%u: style changed", wp
->id
);
2813 wp
->flags
&= ~PANE_STYLECHANGED
;
2815 ft
= format_create(NULL
, NULL
, FORMAT_PANE
|wp
->id
,
2817 format_defaults(ft
, NULL
, NULL
, NULL
, wp
);
2818 tty_window_default_style(&wp
->cached_active_gc
, wp
);
2819 style_add(&wp
->cached_active_gc
, oo
, "window-active-style", ft
);
2820 tty_window_default_style(&wp
->cached_gc
, wp
);
2821 style_add(&wp
->cached_gc
, oo
, "window-style", ft
);
2826 if (wp
== wp
->window
->active
&& wp
->cached_active_gc
.fg
!= 8)
2827 gc
->fg
= wp
->cached_active_gc
.fg
;
2829 gc
->fg
= wp
->cached_gc
.fg
;
2833 if (wp
== wp
->window
->active
&& wp
->cached_active_gc
.bg
!= 8)
2834 gc
->bg
= wp
->cached_active_gc
.bg
;
2836 gc
->bg
= wp
->cached_gc
.bg
;
2841 tty_default_attributes(struct tty
*tty
, const struct grid_cell
*defaults
,
2842 struct colour_palette
*palette
, u_int bg
)
2844 struct grid_cell gc
;
2846 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
2848 tty_attributes(tty
, &gc
, defaults
, palette
);