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>
36 static int tty_log_fd
= -1;
38 static void tty_set_italics(struct tty
*);
39 static int tty_try_colour(struct tty
*, int, const char *);
40 static void tty_force_cursor_colour(struct tty
*, int);
41 static void tty_cursor_pane(struct tty
*, const struct tty_ctx
*, u_int
,
43 static void tty_cursor_pane_unless_wrap(struct tty
*,
44 const struct tty_ctx
*, u_int
, u_int
);
45 static void tty_invalidate(struct tty
*);
46 static void tty_colours(struct tty
*, const struct grid_cell
*);
47 static void tty_check_fg(struct tty
*, struct colour_palette
*,
49 static void tty_check_bg(struct tty
*, struct colour_palette
*,
51 static void tty_check_us(struct tty
*, struct colour_palette
*,
53 static void tty_colours_fg(struct tty
*, const struct grid_cell
*);
54 static void tty_colours_bg(struct tty
*, const struct grid_cell
*);
55 static void tty_colours_us(struct tty
*, const struct grid_cell
*);
57 static void tty_region_pane(struct tty
*, const struct tty_ctx
*, u_int
,
59 static void tty_region(struct tty
*, u_int
, u_int
);
60 static void tty_margin_pane(struct tty
*, const struct tty_ctx
*);
61 static void tty_margin(struct tty
*, u_int
, u_int
);
62 static int tty_large_region(struct tty
*, const struct tty_ctx
*);
63 static int tty_fake_bce(const struct tty
*, const struct grid_cell
*,
65 static void tty_redraw_region(struct tty
*, const struct tty_ctx
*);
66 static void tty_emulate_repeat(struct tty
*, enum tty_code_code
,
67 enum tty_code_code
, u_int
);
68 static void tty_repeat_space(struct tty
*, u_int
);
69 static void tty_draw_pane(struct tty
*, const struct tty_ctx
*, u_int
);
70 static void tty_default_attributes(struct tty
*, const struct grid_cell
*,
71 struct colour_palette
*, u_int
, struct hyperlinks
*);
72 static int tty_check_overlay(struct tty
*, u_int
, u_int
);
73 static void tty_check_overlay_range(struct tty
*, u_int
, u_int
, u_int
,
74 struct overlay_ranges
*);
76 #define tty_use_margin(tty) \
77 (tty->term->flags & TERM_DECSLRM)
78 #define tty_full_width(tty, ctx) \
79 ((ctx)->xoff == 0 && (ctx)->sx >= (tty)->sx)
81 #define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */)
82 #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8)
83 #define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8)
85 #define TTY_QUERY_TIMEOUT 5
86 #define TTY_REQUEST_LIMIT 30
93 xsnprintf(name
, sizeof name
, "tmux-out-%ld.log", (long)getpid());
95 tty_log_fd
= open(name
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0644);
96 if (tty_log_fd
!= -1 && fcntl(tty_log_fd
, F_SETFD
, FD_CLOEXEC
) == -1)
97 fatal("fcntl failed");
101 tty_init(struct tty
*tty
, struct client
*c
)
106 memset(tty
, 0, sizeof *tty
);
109 tty
->cstyle
= SCREEN_CURSOR_DEFAULT
;
111 tty
->fg
= tty
->bg
= -1;
113 if (tcgetattr(c
->fd
, &tty
->tio
) != 0)
119 tty_resize(struct tty
*tty
)
121 struct client
*c
= tty
->client
;
123 u_int sx
, sy
, xpixel
, ypixel
;
125 if (ioctl(c
->fd
, TIOCGWINSZ
, &ws
) != -1) {
131 xpixel
= ws
.ws_xpixel
/ sx
;
137 ypixel
= ws
.ws_ypixel
/ sy
;
144 log_debug("%s: %s now %ux%u (%ux%u)", __func__
, c
->name
, sx
, sy
,
146 tty_set_size(tty
, sx
, sy
, xpixel
, ypixel
);
151 tty_set_size(struct tty
*tty
, u_int sx
, u_int sy
, u_int xpixel
, u_int ypixel
)
155 tty
->xpixel
= xpixel
;
156 tty
->ypixel
= ypixel
;
160 tty_read_callback(__unused
int fd
, __unused
short events
, void *data
)
162 struct tty
*tty
= data
;
163 struct client
*c
= tty
->client
;
164 const char *name
= c
->name
;
165 size_t size
= EVBUFFER_LENGTH(tty
->in
);
168 nread
= evbuffer_read(tty
->in
, c
->fd
, -1);
169 if (nread
== 0 || nread
== -1) {
171 log_debug("%s: read closed", name
);
173 log_debug("%s: read error: %s", name
, strerror(errno
));
174 event_del(&tty
->event_in
);
175 server_client_lost(tty
->client
);
178 log_debug("%s: read %d bytes (already %zu)", name
, nread
, size
);
180 while (tty_keys_next(tty
))
185 tty_timer_callback(__unused
int fd
, __unused
short events
, void *data
)
187 struct tty
*tty
= data
;
188 struct client
*c
= tty
->client
;
189 struct timeval tv
= { .tv_usec
= TTY_BLOCK_INTERVAL
};
191 log_debug("%s: %zu discarded", c
->name
, tty
->discarded
);
193 c
->flags
|= CLIENT_ALLREDRAWFLAGS
;
194 c
->discarded
+= tty
->discarded
;
196 if (tty
->discarded
< TTY_BLOCK_STOP(tty
)) {
197 tty
->flags
&= ~TTY_BLOCK
;
202 evtimer_add(&tty
->timer
, &tv
);
206 tty_block_maybe(struct tty
*tty
)
208 struct client
*c
= tty
->client
;
209 size_t size
= EVBUFFER_LENGTH(tty
->out
);
210 struct timeval tv
= { .tv_usec
= TTY_BLOCK_INTERVAL
};
213 tty
->flags
&= ~TTY_NOBLOCK
;
214 else if (tty
->flags
& TTY_NOBLOCK
)
217 if (size
< TTY_BLOCK_START(tty
))
220 if (tty
->flags
& TTY_BLOCK
)
222 tty
->flags
|= TTY_BLOCK
;
224 log_debug("%s: can't keep up, %zu discarded", c
->name
, size
);
226 evbuffer_drain(tty
->out
, size
);
227 c
->discarded
+= size
;
230 evtimer_add(&tty
->timer
, &tv
);
235 tty_write_callback(__unused
int fd
, __unused
short events
, void *data
)
237 struct tty
*tty
= data
;
238 struct client
*c
= tty
->client
;
239 size_t size
= EVBUFFER_LENGTH(tty
->out
);
242 nwrite
= evbuffer_write(tty
->out
, c
->fd
);
245 log_debug("%s: wrote %d bytes (of %zu)", c
->name
, nwrite
, size
);
248 if ((size_t)nwrite
>= c
->redraw
)
252 log_debug("%s: waiting for redraw, %zu bytes left", c
->name
,
254 } else if (tty_block_maybe(tty
))
257 if (EVBUFFER_LENGTH(tty
->out
) != 0)
258 event_add(&tty
->event_out
, NULL
);
262 tty_open(struct tty
*tty
, char **cause
)
264 struct client
*c
= tty
->client
;
266 tty
->term
= tty_term_create(tty
, c
->term_name
, c
->term_caps
,
267 c
->term_ncaps
, &c
->term_features
, cause
);
268 if (tty
->term
== NULL
) {
272 tty
->flags
|= TTY_OPENED
;
274 tty
->flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
|TTY_BLOCK
|TTY_TIMER
);
276 event_set(&tty
->event_in
, c
->fd
, EV_PERSIST
|EV_READ
,
277 tty_read_callback
, tty
);
278 tty
->in
= evbuffer_new();
280 fatal("out of memory");
282 event_set(&tty
->event_out
, c
->fd
, EV_WRITE
, tty_write_callback
, tty
);
283 tty
->out
= evbuffer_new();
284 if (tty
->out
== NULL
)
285 fatal("out of memory");
287 evtimer_set(&tty
->timer
, tty_timer_callback
, tty
);
296 tty_start_timer_callback(__unused
int fd
, __unused
short events
, void *data
)
298 struct tty
*tty
= data
;
299 struct client
*c
= tty
->client
;
301 log_debug("%s: start timer fired", c
->name
);
302 if ((tty
->flags
& (TTY_HAVEDA
|TTY_HAVEDA2
|TTY_HAVEXDA
)) == 0)
303 tty_update_features(tty
);
304 tty
->flags
|= TTY_ALL_REQUEST_FLAGS
;
308 tty_start_tty(struct tty
*tty
)
310 struct client
*c
= tty
->client
;
312 struct timeval tv
= { .tv_sec
= TTY_QUERY_TIMEOUT
};
314 setblocking(c
->fd
, 0);
315 event_add(&tty
->event_in
, NULL
);
317 memcpy(&tio
, &tty
->tio
, sizeof tio
);
318 tio
.c_iflag
&= ~(IXON
|IXOFF
|ICRNL
|INLCR
|IGNCR
|IMAXBEL
|ISTRIP
);
319 tio
.c_iflag
|= IGNBRK
;
320 tio
.c_oflag
&= ~(OPOST
|ONLCR
|OCRNL
|ONLRET
);
321 tio
.c_lflag
&= ~(IEXTEN
|ICANON
|ECHO
|ECHOE
|ECHONL
|ECHOCTL
|ECHOPRT
|
325 if (tcsetattr(c
->fd
, TCSANOW
, &tio
) == 0)
326 tcflush(c
->fd
, TCOFLUSH
);
328 tty_putcode(tty
, TTYC_SMCUP
);
330 tty_putcode(tty
, TTYC_SMKX
);
331 tty_putcode(tty
, TTYC_CLEAR
);
333 if (tty_acs_needed(tty
)) {
334 log_debug("%s: using capabilities for ACS", c
->name
);
335 tty_putcode(tty
, TTYC_ENACS
);
337 log_debug("%s: using UTF-8 for ACS", c
->name
);
339 tty_putcode(tty
, TTYC_CNORM
);
340 if (tty_term_has(tty
->term
, TTYC_KMOUS
)) {
341 tty_puts(tty
, "\033[?1000l\033[?1002l\033[?1003l");
342 tty_puts(tty
, "\033[?1006l\033[?1005l");
344 if (tty_term_has(tty
->term
, TTYC_ENBP
))
345 tty_putcode(tty
, TTYC_ENBP
);
347 evtimer_set(&tty
->start_timer
, tty_start_timer_callback
, tty
);
348 evtimer_add(&tty
->start_timer
, &tv
);
350 tty
->flags
|= TTY_STARTED
;
353 if (tty
->ccolour
!= -1)
354 tty_force_cursor_colour(tty
, -1);
356 tty
->mouse_drag_flag
= 0;
357 tty
->mouse_drag_update
= NULL
;
358 tty
->mouse_drag_release
= NULL
;
362 tty_send_requests(struct tty
*tty
)
364 if (~tty
->flags
& TTY_STARTED
)
367 if (tty
->term
->flags
& TERM_VT100LIKE
) {
368 if (~tty
->term
->flags
& TTY_HAVEDA
)
369 tty_puts(tty
, "\033[c");
370 if (~tty
->flags
& TTY_HAVEDA2
)
371 tty_puts(tty
, "\033[>c");
372 if (~tty
->flags
& TTY_HAVEXDA
)
373 tty_puts(tty
, "\033[>q");
374 tty_puts(tty
, "\033]10;?\033\\");
375 tty_puts(tty
, "\033]11;?\033\\");
377 tty
->flags
|= TTY_ALL_REQUEST_FLAGS
;
378 tty
->last_requests
= time(NULL
);
382 tty_repeat_requests(struct tty
*tty
)
384 time_t t
= time(NULL
);
386 if (~tty
->flags
& TTY_STARTED
)
389 if (t
- tty
->last_requests
<= TTY_REQUEST_LIMIT
)
391 tty
->last_requests
= t
;
393 if (tty
->term
->flags
& TERM_VT100LIKE
) {
394 tty_puts(tty
, "\033]10;?\033\\");
395 tty_puts(tty
, "\033]11;?\033\\");
400 tty_stop_tty(struct tty
*tty
)
402 struct client
*c
= tty
->client
;
405 if (!(tty
->flags
& TTY_STARTED
))
407 tty
->flags
&= ~TTY_STARTED
;
409 evtimer_del(&tty
->start_timer
);
411 event_del(&tty
->timer
);
412 tty
->flags
&= ~TTY_BLOCK
;
414 event_del(&tty
->event_in
);
415 event_del(&tty
->event_out
);
418 * Be flexible about error handling and try not kill the server just
419 * because the fd is invalid. Things like ssh -t can easily leave us
422 if (ioctl(c
->fd
, TIOCGWINSZ
, &ws
) == -1)
424 if (tcsetattr(c
->fd
, TCSANOW
, &tty
->tio
) == -1)
427 tty_raw(tty
, tty_term_string_ii(tty
->term
, TTYC_CSR
, 0, ws
.ws_row
- 1));
428 if (tty_acs_needed(tty
))
429 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMACS
));
430 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_SGR0
));
431 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMKX
));
432 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CLEAR
));
433 if (tty
->cstyle
!= SCREEN_CURSOR_DEFAULT
) {
434 if (tty_term_has(tty
->term
, TTYC_SE
))
435 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_SE
));
436 else if (tty_term_has(tty
->term
, TTYC_SS
))
437 tty_raw(tty
, tty_term_string_i(tty
->term
, TTYC_SS
, 0));
439 if (tty
->ccolour
!= -1)
440 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CR
));
442 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CNORM
));
443 if (tty_term_has(tty
->term
, TTYC_KMOUS
)) {
444 tty_raw(tty
, "\033[?1000l\033[?1002l\033[?1003l");
445 tty_raw(tty
, "\033[?1006l\033[?1005l");
447 if (tty_term_has(tty
->term
, TTYC_DSBP
))
448 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSBP
));
450 if (tty
->term
->flags
& TERM_VT100LIKE
)
451 tty_raw(tty
, "\033[?7727l");
452 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSFCS
));
453 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSEKS
));
455 if (tty_use_margin(tty
))
456 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSMG
));
457 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMCUP
));
459 setblocking(c
->fd
, 1);
463 tty_close(struct tty
*tty
)
465 if (event_initialized(&tty
->key_timer
))
466 evtimer_del(&tty
->key_timer
);
469 if (tty
->flags
& TTY_OPENED
) {
470 evbuffer_free(tty
->in
);
471 event_del(&tty
->event_in
);
472 evbuffer_free(tty
->out
);
473 event_del(&tty
->event_out
);
475 tty_term_free(tty
->term
);
478 tty
->flags
&= ~TTY_OPENED
;
483 tty_free(struct tty
*tty
)
489 tty_update_features(struct tty
*tty
)
491 struct client
*c
= tty
->client
;
493 if (tty_apply_features(tty
->term
, c
->term_features
))
494 tty_term_apply_overrides(tty
->term
);
496 if (tty_use_margin(tty
))
497 tty_putcode(tty
, TTYC_ENMG
);
498 if (options_get_number(global_options
, "extended-keys"))
499 tty_puts(tty
, tty_term_string(tty
->term
, TTYC_ENEKS
));
500 if (options_get_number(global_options
, "focus-events"))
501 tty_puts(tty
, tty_term_string(tty
->term
, TTYC_ENFCS
));
502 if (tty
->term
->flags
& TERM_VT100LIKE
)
503 tty_puts(tty
, "\033[?7727h");
506 * Features might have changed since the first draw during attach. For
507 * example, this happens when DA responses are received.
509 server_redraw_client(c
);
515 tty_raw(struct tty
*tty
, const char *s
)
517 struct client
*c
= tty
->client
;
522 for (i
= 0; i
< 5; i
++) {
523 n
= write(c
->fd
, s
, slen
);
529 } else if (n
== -1 && errno
!= EAGAIN
)
536 tty_putcode(struct tty
*tty
, enum tty_code_code code
)
538 tty_puts(tty
, tty_term_string(tty
->term
, code
));
542 tty_putcode_i(struct tty
*tty
, enum tty_code_code code
, int a
)
546 tty_puts(tty
, tty_term_string_i(tty
->term
, code
, a
));
550 tty_putcode_ii(struct tty
*tty
, enum tty_code_code code
, int a
, int b
)
554 tty_puts(tty
, tty_term_string_ii(tty
->term
, code
, a
, b
));
558 tty_putcode_iii(struct tty
*tty
, enum tty_code_code code
, int a
, int b
, int c
)
560 if (a
< 0 || b
< 0 || c
< 0)
562 tty_puts(tty
, tty_term_string_iii(tty
->term
, code
, a
, b
, c
));
566 tty_putcode_s(struct tty
*tty
, enum tty_code_code code
, const char *a
)
569 tty_puts(tty
, tty_term_string_s(tty
->term
, code
, a
));
573 tty_putcode_ss(struct tty
*tty
, enum tty_code_code code
, const char *a
,
576 if (a
!= NULL
&& b
!= NULL
)
577 tty_puts(tty
, tty_term_string_ss(tty
->term
, code
, a
, b
));
581 tty_add(struct tty
*tty
, const char *buf
, size_t len
)
583 struct client
*c
= tty
->client
;
585 if (tty
->flags
& TTY_BLOCK
) {
586 tty
->discarded
+= len
;
590 evbuffer_add(tty
->out
, buf
, len
);
591 log_debug("%s: %.*s", c
->name
, (int)len
, buf
);
594 if (tty_log_fd
!= -1)
595 write(tty_log_fd
, buf
, len
);
596 if (tty
->flags
& TTY_STARTED
)
597 event_add(&tty
->event_out
, NULL
);
601 tty_puts(struct tty
*tty
, const char *s
)
604 tty_add(tty
, s
, strlen(s
));
608 tty_putc(struct tty
*tty
, u_char ch
)
612 if ((tty
->term
->flags
& TERM_NOAM
) &&
613 ch
>= 0x20 && ch
!= 0x7f &&
614 tty
->cy
== tty
->sy
- 1 &&
615 tty
->cx
+ 1 >= tty
->sx
)
618 if (tty
->cell
.attr
& GRID_ATTR_CHARSET
) {
619 acs
= tty_acs_get(tty
, ch
);
621 tty_add(tty
, acs
, strlen(acs
));
623 tty_add(tty
, &ch
, 1);
625 tty_add(tty
, &ch
, 1);
627 if (ch
>= 0x20 && ch
!= 0x7f) {
628 if (tty
->cx
>= tty
->sx
) {
630 if (tty
->cy
!= tty
->rlower
)
634 * On !am terminals, force the cursor position to where
635 * we think it should be after a line wrap - this means
636 * it works on sensible terminals as well.
638 if (tty
->term
->flags
& TERM_NOAM
)
639 tty_putcode_ii(tty
, TTYC_CUP
, tty
->cy
, tty
->cx
);
646 tty_putn(struct tty
*tty
, const void *buf
, size_t len
, u_int width
)
648 if ((tty
->term
->flags
& TERM_NOAM
) &&
649 tty
->cy
== tty
->sy
- 1 &&
650 tty
->cx
+ len
>= tty
->sx
)
651 len
= tty
->sx
- tty
->cx
- 1;
653 tty_add(tty
, buf
, len
);
654 if (tty
->cx
+ width
> tty
->sx
) {
655 tty
->cx
= (tty
->cx
+ width
) - tty
->sx
;
656 if (tty
->cx
<= tty
->sx
)
659 tty
->cx
= tty
->cy
= UINT_MAX
;
665 tty_set_italics(struct tty
*tty
)
669 if (tty_term_has(tty
->term
, TTYC_SITM
)) {
670 s
= options_get_string(global_options
, "default-terminal");
671 if (strcmp(s
, "screen") != 0 && strncmp(s
, "screen-", 7) != 0) {
672 tty_putcode(tty
, TTYC_SITM
);
676 tty_putcode(tty
, TTYC_SMSO
);
680 tty_set_title(struct tty
*tty
, const char *title
)
682 if (!tty_term_has(tty
->term
, TTYC_TSL
) ||
683 !tty_term_has(tty
->term
, TTYC_FSL
))
686 tty_putcode(tty
, TTYC_TSL
);
687 tty_puts(tty
, title
);
688 tty_putcode(tty
, TTYC_FSL
);
692 tty_set_path(struct tty
*tty
, const char *title
)
694 if (!tty_term_has(tty
->term
, TTYC_SWD
) ||
695 !tty_term_has(tty
->term
, TTYC_FSL
))
698 tty_putcode(tty
, TTYC_SWD
);
699 tty_puts(tty
, title
);
700 tty_putcode(tty
, TTYC_FSL
);
704 tty_force_cursor_colour(struct tty
*tty
, int c
)
710 c
= colour_force_rgb(c
);
711 if (c
== tty
->ccolour
)
714 tty_putcode(tty
, TTYC_CR
);
716 colour_split_rgb(c
, &r
, &g
, &b
);
717 xsnprintf(s
, sizeof s
, "rgb:%02hhx/%02hhx/%02hhx", r
, g
, b
);
718 tty_putcode_s(tty
, TTYC_CS
, s
);
724 tty_update_cursor(struct tty
*tty
, int mode
, struct screen
*s
)
726 enum screen_cursor_style cstyle
;
727 int ccolour
, changed
, cmode
= mode
;
729 /* Set cursor colour if changed. */
731 ccolour
= s
->ccolour
;
732 if (s
->ccolour
== -1)
733 ccolour
= s
->default_ccolour
;
734 tty_force_cursor_colour(tty
, ccolour
);
737 /* If cursor is off, set as invisible. */
738 if (~cmode
& MODE_CURSOR
) {
739 if (tty
->mode
& MODE_CURSOR
)
740 tty_putcode(tty
, TTYC_CIVIS
);
744 /* Check if blinking or very visible flag changed or style changed. */
746 cstyle
= tty
->cstyle
;
749 if (cstyle
== SCREEN_CURSOR_DEFAULT
) {
750 if (~cmode
& MODE_CURSOR_BLINKING_SET
) {
751 if (s
->default_mode
& MODE_CURSOR_BLINKING
)
752 cmode
|= MODE_CURSOR_BLINKING
;
754 cmode
&= ~MODE_CURSOR_BLINKING
;
756 cstyle
= s
->default_cstyle
;
760 /* If nothing changed, do nothing. */
761 changed
= cmode
^ tty
->mode
;
762 if ((changed
& CURSOR_MODES
) == 0 && cstyle
== tty
->cstyle
)
766 * Set cursor style. If an explicit style has been set with DECSCUSR,
767 * set it if supported, otherwise send cvvis for blinking styles.
769 * If no style, has been set (SCREEN_CURSOR_DEFAULT), then send cvvis
770 * if either the blinking or very visible flags are set.
772 tty_putcode(tty
, TTYC_CNORM
);
774 case SCREEN_CURSOR_DEFAULT
:
775 if (tty
->cstyle
!= SCREEN_CURSOR_DEFAULT
) {
776 if (tty_term_has(tty
->term
, TTYC_SE
))
777 tty_putcode(tty
, TTYC_SE
);
779 tty_putcode_i(tty
, TTYC_SS
, 0);
781 if (cmode
& (MODE_CURSOR_BLINKING
|MODE_CURSOR_VERY_VISIBLE
))
782 tty_putcode(tty
, TTYC_CVVIS
);
784 case SCREEN_CURSOR_BLOCK
:
785 if (tty_term_has(tty
->term
, TTYC_SS
)) {
786 if (cmode
& MODE_CURSOR_BLINKING
)
787 tty_putcode_i(tty
, TTYC_SS
, 1);
789 tty_putcode_i(tty
, TTYC_SS
, 2);
790 } else if (cmode
& MODE_CURSOR_BLINKING
)
791 tty_putcode(tty
, TTYC_CVVIS
);
793 case SCREEN_CURSOR_UNDERLINE
:
794 if (tty_term_has(tty
->term
, TTYC_SS
)) {
795 if (cmode
& MODE_CURSOR_BLINKING
)
796 tty_putcode_i(tty
, TTYC_SS
, 3);
798 tty_putcode_i(tty
, TTYC_SS
, 4);
799 } else if (cmode
& MODE_CURSOR_BLINKING
)
800 tty_putcode(tty
, TTYC_CVVIS
);
802 case SCREEN_CURSOR_BAR
:
803 if (tty_term_has(tty
->term
, TTYC_SS
)) {
804 if (cmode
& MODE_CURSOR_BLINKING
)
805 tty_putcode_i(tty
, TTYC_SS
, 5);
807 tty_putcode_i(tty
, TTYC_SS
, 6);
808 } else if (cmode
& MODE_CURSOR_BLINKING
)
809 tty_putcode(tty
, TTYC_CVVIS
);
812 tty
->cstyle
= cstyle
;
817 tty_update_mode(struct tty
*tty
, int mode
, struct screen
*s
)
819 struct tty_term
*term
= tty
->term
;
820 struct client
*c
= tty
->client
;
823 if (tty
->flags
& TTY_NOCURSOR
)
824 mode
&= ~MODE_CURSOR
;
826 if (tty_update_cursor(tty
, mode
, s
) & MODE_CURSOR_BLINKING
)
827 mode
|= MODE_CURSOR_BLINKING
;
829 mode
&= ~MODE_CURSOR_BLINKING
;
831 changed
= mode
^ tty
->mode
;
832 if (log_get_level() != 0 && changed
!= 0) {
833 log_debug("%s: current mode %s", c
->name
,
834 screen_mode_to_string(tty
->mode
));
835 log_debug("%s: setting mode %s", c
->name
,
836 screen_mode_to_string(mode
));
839 if ((changed
& ALL_MOUSE_MODES
) && tty_term_has(term
, TTYC_KMOUS
)) {
841 * If the mouse modes have changed, clear then all and apply
842 * again. There are differences in how terminals track the
845 tty_puts(tty
, "\033[?1006l\033[?1000l\033[?1002l\033[?1003l");
846 if (mode
& ALL_MOUSE_MODES
)
847 tty_puts(tty
, "\033[?1006h");
848 if (mode
& MODE_MOUSE_ALL
)
849 tty_puts(tty
, "\033[?1000h\033[?1002h\033[?1003h");
850 else if (mode
& MODE_MOUSE_BUTTON
)
851 tty_puts(tty
, "\033[?1000h\033[?1002h");
852 else if (mode
& MODE_MOUSE_STANDARD
)
853 tty_puts(tty
, "\033[?1000h");
859 tty_emulate_repeat(struct tty
*tty
, enum tty_code_code code
,
860 enum tty_code_code code1
, u_int n
)
862 if (tty_term_has(tty
->term
, code
))
863 tty_putcode_i(tty
, code
, n
);
866 tty_putcode(tty
, code1
);
871 tty_repeat_space(struct tty
*tty
, u_int n
)
876 memset(s
, ' ', sizeof s
);
878 while (n
> sizeof s
) {
879 tty_putn(tty
, s
, sizeof s
, sizeof s
);
883 tty_putn(tty
, s
, n
, n
);
886 /* Is this window bigger than the terminal? */
888 tty_window_bigger(struct tty
*tty
)
890 struct client
*c
= tty
->client
;
891 struct window
*w
= c
->session
->curw
->window
;
893 return (tty
->sx
< w
->sx
|| tty
->sy
- status_line_size(c
) < w
->sy
);
896 /* What offset should this window be drawn at? */
898 tty_window_offset(struct tty
*tty
, u_int
*ox
, u_int
*oy
, u_int
*sx
, u_int
*sy
)
908 /* What offset should this window be drawn at? */
910 tty_window_offset1(struct tty
*tty
, u_int
*ox
, u_int
*oy
, u_int
*sx
, u_int
*sy
)
912 struct client
*c
= tty
->client
;
913 struct window
*w
= c
->session
->curw
->window
;
914 struct window_pane
*wp
= server_client_get_pane(c
);
917 lines
= status_line_size(c
);
919 if (tty
->sx
>= w
->sx
&& tty
->sy
- lines
>= w
->sy
) {
925 c
->pan_window
= NULL
;
930 *sy
= tty
->sy
- lines
;
932 if (c
->pan_window
== w
) {
935 else if (c
->pan_ox
+ *sx
> w
->sx
)
936 c
->pan_ox
= w
->sx
- *sx
;
940 else if (c
->pan_oy
+ *sy
> w
->sy
)
941 c
->pan_oy
= w
->sy
- *sy
;
946 if (~wp
->screen
->mode
& MODE_CURSOR
) {
950 cx
= wp
->xoff
+ wp
->screen
->cx
;
951 cy
= wp
->yoff
+ wp
->screen
->cy
;
955 else if (cx
> w
->sx
- *sx
)
962 else if (cy
> w
->sy
- *sy
)
968 c
->pan_window
= NULL
;
972 /* Update stored offsets for a window and redraw if necessary. */
974 tty_update_window_offset(struct window
*w
)
978 TAILQ_FOREACH(c
, &clients
, entry
) {
979 if (c
->session
!= NULL
&&
980 c
->session
->curw
!= NULL
&&
981 c
->session
->curw
->window
== w
)
982 tty_update_client_offset(c
);
986 /* Update stored offsets for a client and redraw if necessary. */
988 tty_update_client_offset(struct client
*c
)
990 u_int ox
, oy
, sx
, sy
;
992 if (~c
->flags
& CLIENT_TERMINAL
)
995 c
->tty
.oflag
= tty_window_offset1(&c
->tty
, &ox
, &oy
, &sx
, &sy
);
996 if (ox
== c
->tty
.oox
&&
1002 log_debug ("%s: %s offset has changed (%u,%u %ux%u -> %u,%u %ux%u)",
1003 __func__
, c
->name
, c
->tty
.oox
, c
->tty
.ooy
, c
->tty
.osx
, c
->tty
.osy
,
1011 c
->flags
|= (CLIENT_REDRAWWINDOW
|CLIENT_REDRAWSTATUS
);
1015 * Is the region large enough to be worth redrawing once later rather than
1016 * probably several times now? Currently yes if it is more than 50% of the
1020 tty_large_region(__unused
struct tty
*tty
, const struct tty_ctx
*ctx
)
1022 return (ctx
->orlower
- ctx
->orupper
>= ctx
->sy
/ 2);
1026 * Return if BCE is needed but the terminal doesn't have it - it'll need to be
1030 tty_fake_bce(const struct tty
*tty
, const struct grid_cell
*gc
, u_int bg
)
1032 if (tty_term_flag(tty
->term
, TTYC_BCE
))
1034 if (!COLOUR_DEFAULT(bg
) || !COLOUR_DEFAULT(gc
->bg
))
1040 * Redraw scroll region using data from screen (already updated). Used when
1041 * CSR not supported, or window is a pane that doesn't take up the full
1042 * width of the terminal.
1045 tty_redraw_region(struct tty
*tty
, const struct tty_ctx
*ctx
)
1047 struct client
*c
= tty
->client
;
1051 * If region is large, schedule a redraw. In most cases this is likely
1052 * to be followed by some more scrolling.
1054 if (tty_large_region(tty
, ctx
)) {
1055 log_debug("%s: %s large redraw", __func__
, c
->name
);
1056 ctx
->redraw_cb(ctx
);
1060 for (i
= ctx
->orupper
; i
<= ctx
->orlower
; i
++)
1061 tty_draw_pane(tty
, ctx
, i
);
1064 /* Is this position visible in the pane? */
1066 tty_is_visible(__unused
struct tty
*tty
, const struct tty_ctx
*ctx
, u_int px
,
1067 u_int py
, u_int nx
, u_int ny
)
1069 u_int xoff
= ctx
->rxoff
+ px
, yoff
= ctx
->ryoff
+ py
;
1074 if (xoff
+ nx
<= ctx
->wox
|| xoff
>= ctx
->wox
+ ctx
->wsx
||
1075 yoff
+ ny
<= ctx
->woy
|| yoff
>= ctx
->woy
+ ctx
->wsy
)
1080 /* Clamp line position to visible part of pane. */
1082 tty_clamp_line(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int px
, u_int py
,
1083 u_int nx
, u_int
*i
, u_int
*x
, u_int
*rx
, u_int
*ry
)
1085 u_int xoff
= ctx
->rxoff
+ px
;
1087 if (!tty_is_visible(tty
, ctx
, px
, py
, nx
, 1))
1089 *ry
= ctx
->yoff
+ py
- ctx
->woy
;
1091 if (xoff
>= ctx
->wox
&& xoff
+ nx
<= ctx
->wox
+ ctx
->wsx
) {
1094 *x
= ctx
->xoff
+ px
- ctx
->wox
;
1096 } else if (xoff
< ctx
->wox
&& xoff
+ nx
> ctx
->wox
+ ctx
->wsx
) {
1097 /* Both left and right not visible. */
1101 } else if (xoff
< ctx
->wox
) {
1102 /* Left not visible. */
1103 *i
= ctx
->wox
- (ctx
->xoff
+ px
);
1107 /* Right not visible. */
1109 *x
= (ctx
->xoff
+ px
) - ctx
->wox
;
1110 *rx
= ctx
->wsx
- *x
;
1113 fatalx("%s: x too big, %u > %u", __func__
, *rx
, nx
);
1120 tty_clear_line(struct tty
*tty
, const struct grid_cell
*defaults
, u_int py
,
1121 u_int px
, u_int nx
, u_int bg
)
1123 struct client
*c
= tty
->client
;
1124 struct overlay_ranges r
;
1127 log_debug("%s: %s, %u at %u,%u", __func__
, c
->name
, nx
, px
, py
);
1129 /* Nothing to clear. */
1133 /* If genuine BCE is available, can try escape sequences. */
1134 if (c
->overlay_check
== NULL
&& !tty_fake_bce(tty
, defaults
, bg
)) {
1135 /* Off the end of the line, use EL if available. */
1136 if (px
+ nx
>= tty
->sx
&& tty_term_has(tty
->term
, TTYC_EL
)) {
1137 tty_cursor(tty
, px
, py
);
1138 tty_putcode(tty
, TTYC_EL
);
1142 /* At the start of the line. Use EL1. */
1143 if (px
== 0 && tty_term_has(tty
->term
, TTYC_EL1
)) {
1144 tty_cursor(tty
, px
+ nx
- 1, py
);
1145 tty_putcode(tty
, TTYC_EL1
);
1149 /* Section of line. Use ECH if possible. */
1150 if (tty_term_has(tty
->term
, TTYC_ECH
)) {
1151 tty_cursor(tty
, px
, py
);
1152 tty_putcode_i(tty
, TTYC_ECH
, nx
);
1158 * Couldn't use an escape sequence, use spaces. Clear only the visible
1159 * bit if there is an overlay.
1161 tty_check_overlay_range(tty
, px
, py
, nx
, &r
);
1162 for (i
= 0; i
< OVERLAY_MAX_RANGES
; i
++) {
1165 tty_cursor(tty
, r
.px
[i
], py
);
1166 tty_repeat_space(tty
, r
.nx
[i
]);
1170 /* Clear a line, adjusting to visible part of pane. */
1172 tty_clear_pane_line(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int py
,
1173 u_int px
, u_int nx
, u_int bg
)
1175 struct client
*c
= tty
->client
;
1178 log_debug("%s: %s, %u at %u,%u", __func__
, c
->name
, nx
, px
, py
);
1180 if (tty_clamp_line(tty
, ctx
, px
, py
, nx
, &i
, &x
, &rx
, &ry
))
1181 tty_clear_line(tty
, &ctx
->defaults
, ry
, x
, rx
, bg
);
1184 /* Clamp area position to visible part of pane. */
1186 tty_clamp_area(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int px
, u_int py
,
1187 u_int nx
, u_int ny
, u_int
*i
, u_int
*j
, u_int
*x
, u_int
*y
, u_int
*rx
,
1190 u_int xoff
= ctx
->rxoff
+ px
, yoff
= ctx
->ryoff
+ py
;
1192 if (!tty_is_visible(tty
, ctx
, px
, py
, nx
, ny
))
1195 if (xoff
>= ctx
->wox
&& xoff
+ nx
<= ctx
->wox
+ ctx
->wsx
) {
1198 *x
= ctx
->xoff
+ px
- ctx
->wox
;
1200 } else if (xoff
< ctx
->wox
&& xoff
+ nx
> ctx
->wox
+ ctx
->wsx
) {
1201 /* Both left and right not visible. */
1205 } else if (xoff
< ctx
->wox
) {
1206 /* Left not visible. */
1207 *i
= ctx
->wox
- (ctx
->xoff
+ px
);
1211 /* Right not visible. */
1213 *x
= (ctx
->xoff
+ px
) - ctx
->wox
;
1214 *rx
= ctx
->wsx
- *x
;
1217 fatalx("%s: x too big, %u > %u", __func__
, *rx
, nx
);
1219 if (yoff
>= ctx
->woy
&& yoff
+ ny
<= ctx
->woy
+ ctx
->wsy
) {
1222 *y
= ctx
->yoff
+ py
- ctx
->woy
;
1224 } else if (yoff
< ctx
->woy
&& yoff
+ ny
> ctx
->woy
+ ctx
->wsy
) {
1225 /* Both top and bottom not visible. */
1229 } else if (yoff
< ctx
->woy
) {
1230 /* Top not visible. */
1231 *j
= ctx
->woy
- (ctx
->yoff
+ py
);
1235 /* Bottom not visible. */
1237 *y
= (ctx
->yoff
+ py
) - ctx
->woy
;
1238 *ry
= ctx
->wsy
- *y
;
1241 fatalx("%s: y too big, %u > %u", __func__
, *ry
, ny
);
1246 /* Clear an area, adjusting to visible part of pane. */
1248 tty_clear_area(struct tty
*tty
, const struct grid_cell
*defaults
, u_int py
,
1249 u_int ny
, u_int px
, u_int nx
, u_int bg
)
1251 struct client
*c
= tty
->client
;
1255 log_debug("%s: %s, %u,%u at %u,%u", __func__
, c
->name
, nx
, ny
, px
, py
);
1257 /* Nothing to clear. */
1258 if (nx
== 0 || ny
== 0)
1261 /* If genuine BCE is available, can try escape sequences. */
1262 if (c
->overlay_check
== NULL
&& !tty_fake_bce(tty
, defaults
, bg
)) {
1263 /* Use ED if clearing off the bottom of the terminal. */
1265 px
+ nx
>= tty
->sx
&&
1266 py
+ ny
>= tty
->sy
&&
1267 tty_term_has(tty
->term
, TTYC_ED
)) {
1268 tty_cursor(tty
, 0, py
);
1269 tty_putcode(tty
, TTYC_ED
);
1274 * On VT420 compatible terminals we can use DECFRA if the
1275 * background colour isn't default (because it doesn't work
1278 if ((tty
->term
->flags
& TERM_DECFRA
) && !COLOUR_DEFAULT(bg
)) {
1279 xsnprintf(tmp
, sizeof tmp
, "\033[32;%u;%u;%u;%u$x",
1280 py
+ 1, px
+ 1, py
+ ny
, px
+ nx
);
1285 /* Full lines can be scrolled away to clear them. */
1287 px
+ nx
>= tty
->sx
&&
1289 tty_term_has(tty
->term
, TTYC_CSR
) &&
1290 tty_term_has(tty
->term
, TTYC_INDN
)) {
1291 tty_region(tty
, py
, py
+ ny
- 1);
1292 tty_margin_off(tty
);
1293 tty_putcode_i(tty
, TTYC_INDN
, ny
);
1298 * If margins are supported, can just scroll the area off to
1303 tty_term_has(tty
->term
, TTYC_CSR
) &&
1304 tty_use_margin(tty
) &&
1305 tty_term_has(tty
->term
, TTYC_INDN
)) {
1306 tty_region(tty
, py
, py
+ ny
- 1);
1307 tty_margin(tty
, px
, px
+ nx
- 1);
1308 tty_putcode_i(tty
, TTYC_INDN
, ny
);
1313 /* Couldn't use an escape sequence, loop over the lines. */
1314 for (yy
= py
; yy
< py
+ ny
; yy
++)
1315 tty_clear_line(tty
, defaults
, yy
, px
, nx
, bg
);
1318 /* Clear an area in a pane. */
1320 tty_clear_pane_area(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int py
,
1321 u_int ny
, u_int px
, u_int nx
, u_int bg
)
1323 u_int i
, j
, x
, y
, rx
, ry
;
1325 if (tty_clamp_area(tty
, ctx
, px
, py
, nx
, ny
, &i
, &j
, &x
, &y
, &rx
, &ry
))
1326 tty_clear_area(tty
, &ctx
->defaults
, y
, ry
, x
, rx
, bg
);
1330 tty_draw_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int py
)
1332 struct screen
*s
= ctx
->s
;
1333 u_int nx
= ctx
->sx
, i
, x
, rx
, ry
;
1335 log_debug("%s: %s %u %d", __func__
, tty
->client
->name
, py
, ctx
->bigger
);
1338 tty_draw_line(tty
, s
, 0, py
, nx
, ctx
->xoff
, ctx
->yoff
+ py
,
1339 &ctx
->defaults
, ctx
->palette
);
1342 if (tty_clamp_line(tty
, ctx
, 0, py
, nx
, &i
, &x
, &rx
, &ry
)) {
1343 tty_draw_line(tty
, s
, i
, py
, rx
, x
, ry
, &ctx
->defaults
,
1348 static const struct grid_cell
*
1349 tty_check_codeset(struct tty
*tty
, const struct grid_cell
*gc
)
1351 static struct grid_cell
new;
1354 /* Characters less than 0x7f are always fine, no matter what. */
1355 if (gc
->data
.size
== 1 && *gc
->data
.data
< 0x7f)
1358 /* UTF-8 terminal and a UTF-8 character - fine. */
1359 if (tty
->client
->flags
& CLIENT_UTF8
)
1361 memcpy(&new, gc
, sizeof new);
1363 /* See if this can be mapped to an ACS character. */
1364 c
= tty_acs_reverse_get(tty
, gc
->data
.data
, gc
->data
.size
);
1366 utf8_set(&new.data
, c
);
1367 new.attr
|= GRID_ATTR_CHARSET
;
1371 /* Replace by the right number of underscores. */
1372 new.data
.size
= gc
->data
.width
;
1373 if (new.data
.size
> UTF8_SIZE
)
1374 new.data
.size
= UTF8_SIZE
;
1375 memset(new.data
.data
, '_', new.data
.size
);
1380 * Check if a single character is obstructed by the overlay and return a
1384 tty_check_overlay(struct tty
*tty
, u_int px
, u_int py
)
1386 struct overlay_ranges r
;
1389 * A unit width range will always return nx[2] == 0 from a check, even
1390 * with multiple overlays, so it's sufficient to check just the first
1393 tty_check_overlay_range(tty
, px
, py
, 1, &r
);
1394 if (r
.nx
[0] + r
.nx
[1] == 0)
1399 /* Return parts of the input range which are visible. */
1401 tty_check_overlay_range(struct tty
*tty
, u_int px
, u_int py
, u_int nx
,
1402 struct overlay_ranges
*r
)
1404 struct client
*c
= tty
->client
;
1406 if (c
->overlay_check
== NULL
) {
1416 c
->overlay_check(c
, c
->overlay_data
, px
, py
, nx
, r
);
1420 tty_draw_line(struct tty
*tty
, struct screen
*s
, u_int px
, u_int py
, u_int nx
,
1421 u_int atx
, u_int aty
, const struct grid_cell
*defaults
,
1422 struct colour_palette
*palette
)
1424 struct grid
*gd
= s
->grid
;
1425 struct grid_cell gc
, last
;
1426 const struct grid_cell
*gcp
;
1427 struct grid_line
*gl
;
1428 struct client
*c
= tty
->client
;
1429 struct overlay_ranges r
;
1430 u_int i
, j
, ux
, sx
, width
, hidden
, eux
, nxx
;
1432 int flags
, cleared
= 0, wrapped
= 0;
1436 log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__
,
1437 px
, py
, nx
, atx
, aty
);
1438 log_debug("%s: defaults: fg=%d, bg=%d", __func__
, defaults
->fg
,
1442 * py is the line in the screen to draw.
1443 * px is the start x and nx is the width to draw.
1444 * atx,aty is the line on the terminal to draw it.
1447 flags
= (tty
->flags
& TTY_NOCURSOR
);
1448 tty
->flags
|= TTY_NOCURSOR
;
1449 tty_update_mode(tty
, tty
->mode
, s
);
1451 tty_region_off(tty
);
1452 tty_margin_off(tty
);
1455 * Clamp the width to cellsize - note this is not cellused, because
1456 * there may be empty background cells after it (from BCE).
1458 sx
= screen_size_x(s
);
1461 cellsize
= grid_get_line(gd
, gd
->hsize
+ py
)->cellsize
;
1473 gl
= grid_get_line(gd
, gd
->hsize
+ py
- 1);
1475 (~gl
->flags
& GRID_LINE_WRAPPED
) ||
1477 tty
->cx
< tty
->sx
||
1482 tty_term_has(tty
->term
, TTYC_EL1
) &&
1483 !tty_fake_bce(tty
, defaults
, 8) &&
1484 c
->overlay_check
== NULL
) {
1485 tty_default_attributes(tty
, defaults
, palette
, 8,
1487 tty_cursor(tty
, nx
- 1, aty
);
1488 tty_putcode(tty
, TTYC_EL1
);
1492 log_debug("%s: wrapped line %u", __func__
, aty
);
1496 memcpy(&last
, &grid_default_cell
, sizeof last
);
1500 for (i
= 0; i
< sx
; i
++) {
1501 grid_view_get_cell(gd
, px
+ i
, py
, &gc
);
1502 gcp
= tty_check_codeset(tty
, &gc
);
1504 (!tty_check_overlay(tty
, atx
+ ux
+ width
, aty
) ||
1505 (gcp
->attr
& GRID_ATTR_CHARSET
) ||
1506 gcp
->flags
!= last
.flags
||
1507 gcp
->attr
!= last
.attr
||
1508 gcp
->fg
!= last
.fg
||
1509 gcp
->bg
!= last
.bg
||
1510 gcp
->us
!= last
.us
||
1511 gcp
->link
!= last
.link
||
1512 ux
+ width
+ gcp
->data
.width
> nx
||
1513 (sizeof buf
) - len
< gcp
->data
.size
)) {
1514 tty_attributes(tty
, &last
, defaults
, palette
,
1516 if (last
.flags
& GRID_FLAG_CLEARED
) {
1517 log_debug("%s: %zu cleared", __func__
, len
);
1518 tty_clear_line(tty
, defaults
, aty
, atx
+ ux
,
1521 if (!wrapped
|| atx
!= 0 || ux
!= 0)
1522 tty_cursor(tty
, atx
+ ux
, aty
);
1523 tty_putn(tty
, buf
, len
, width
);
1532 if (gcp
->flags
& GRID_FLAG_SELECTED
)
1533 screen_select_cell(s
, &last
, gcp
);
1535 memcpy(&last
, gcp
, sizeof last
);
1537 tty_check_overlay_range(tty
, atx
+ ux
, aty
, gcp
->data
.width
,
1540 for (j
= 0; j
< OVERLAY_MAX_RANGES
; j
++)
1542 hidden
= gcp
->data
.width
- hidden
;
1543 if (hidden
!= 0 && hidden
== gcp
->data
.width
) {
1544 if (~gcp
->flags
& GRID_FLAG_PADDING
)
1545 ux
+= gcp
->data
.width
;
1546 } else if (hidden
!= 0 || ux
+ gcp
->data
.width
> nx
) {
1547 if (~gcp
->flags
& GRID_FLAG_PADDING
) {
1548 tty_attributes(tty
, &last
, defaults
, palette
,
1550 for (j
= 0; j
< OVERLAY_MAX_RANGES
; j
++) {
1553 /* Effective width drawn so far. */
1554 eux
= r
.px
[j
] - atx
;
1556 tty_cursor(tty
, r
.px
[j
], aty
);
1560 tty_repeat_space(tty
, r
.nx
[j
]);
1565 } else if (gcp
->attr
& GRID_ATTR_CHARSET
) {
1566 tty_attributes(tty
, &last
, defaults
, palette
,
1568 tty_cursor(tty
, atx
+ ux
, aty
);
1569 for (j
= 0; j
< gcp
->data
.size
; j
++)
1570 tty_putc(tty
, gcp
->data
.data
[j
]);
1571 ux
+= gcp
->data
.width
;
1572 } else if (~gcp
->flags
& GRID_FLAG_PADDING
) {
1573 memcpy(buf
+ len
, gcp
->data
.data
, gcp
->data
.size
);
1574 len
+= gcp
->data
.size
;
1575 width
+= gcp
->data
.width
;
1578 if (len
!= 0 && ((~last
.flags
& GRID_FLAG_CLEARED
) || last
.bg
!= 8)) {
1579 tty_attributes(tty
, &last
, defaults
, palette
, s
->hyperlinks
);
1580 if (last
.flags
& GRID_FLAG_CLEARED
) {
1581 log_debug("%s: %zu cleared (end)", __func__
, len
);
1582 tty_clear_line(tty
, defaults
, aty
, atx
+ ux
, width
,
1585 if (!wrapped
|| atx
!= 0 || ux
!= 0)
1586 tty_cursor(tty
, atx
+ ux
, aty
);
1587 tty_putn(tty
, buf
, len
, width
);
1592 if (!cleared
&& ux
< nx
) {
1593 log_debug("%s: %u to end of line (%zu cleared)", __func__
,
1595 tty_default_attributes(tty
, defaults
, palette
, 8,
1597 tty_clear_line(tty
, defaults
, aty
, atx
+ ux
, nx
- ux
, 8);
1600 tty
->flags
= (tty
->flags
& ~TTY_NOCURSOR
) | flags
;
1601 tty_update_mode(tty
, tty
->mode
, s
);
1605 tty_sync_start(struct tty
*tty
)
1607 if (tty
->flags
& TTY_BLOCK
)
1609 if (tty
->flags
& TTY_SYNCING
)
1611 tty
->flags
|= TTY_SYNCING
;
1613 if (tty_term_has(tty
->term
, TTYC_SYNC
)) {
1614 log_debug("%s sync start", tty
->client
->name
);
1615 tty_putcode_i(tty
, TTYC_SYNC
, 1);
1620 tty_sync_end(struct tty
*tty
)
1622 if (tty
->flags
& TTY_BLOCK
)
1624 if (~tty
->flags
& TTY_SYNCING
)
1626 tty
->flags
&= ~TTY_SYNCING
;
1628 if (tty_term_has(tty
->term
, TTYC_SYNC
)) {
1629 log_debug("%s sync end", tty
->client
->name
);
1630 tty_putcode_i(tty
, TTYC_SYNC
, 2);
1635 tty_client_ready(const struct tty_ctx
*ctx
, struct client
*c
)
1637 if (c
->session
== NULL
|| c
->tty
.term
== NULL
)
1639 if (c
->flags
& CLIENT_SUSPENDED
)
1643 * If invisible panes are allowed (used for passthrough), don't care if
1644 * redrawing or frozen.
1646 if (ctx
->allow_invisible_panes
)
1649 if (c
->flags
& CLIENT_REDRAWWINDOW
)
1651 if (c
->tty
.flags
& TTY_FREEZE
)
1657 tty_write(void (*cmdfn
)(struct tty
*, const struct tty_ctx
*),
1658 struct tty_ctx
*ctx
)
1663 if (ctx
->set_client_cb
== NULL
)
1665 TAILQ_FOREACH(c
, &clients
, entry
) {
1666 if (tty_client_ready(ctx
, c
)) {
1667 state
= ctx
->set_client_cb(ctx
, c
);
1672 cmdfn(&c
->tty
, ctx
);
1678 tty_cmd_insertcharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
1680 struct client
*c
= tty
->client
;
1683 !tty_full_width(tty
, ctx
) ||
1684 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1685 (!tty_term_has(tty
->term
, TTYC_ICH
) &&
1686 !tty_term_has(tty
->term
, TTYC_ICH1
)) ||
1687 c
->overlay_check
!= NULL
) {
1688 tty_draw_pane(tty
, ctx
, ctx
->ocy
);
1692 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1693 ctx
->s
->hyperlinks
);
1695 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1697 tty_emulate_repeat(tty
, TTYC_ICH
, TTYC_ICH1
, ctx
->num
);
1701 tty_cmd_deletecharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
1703 struct client
*c
= tty
->client
;
1706 !tty_full_width(tty
, ctx
) ||
1707 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1708 (!tty_term_has(tty
->term
, TTYC_DCH
) &&
1709 !tty_term_has(tty
->term
, TTYC_DCH1
)) ||
1710 c
->overlay_check
!= NULL
) {
1711 tty_draw_pane(tty
, ctx
, ctx
->ocy
);
1715 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1716 ctx
->s
->hyperlinks
);
1718 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1720 tty_emulate_repeat(tty
, TTYC_DCH
, TTYC_DCH1
, ctx
->num
);
1724 tty_cmd_clearcharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
1726 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1727 ctx
->s
->hyperlinks
);
1729 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, ctx
->ocx
, ctx
->num
, ctx
->bg
);
1733 tty_cmd_insertline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1735 struct client
*c
= tty
->client
;
1738 !tty_full_width(tty
, ctx
) ||
1739 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1740 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1741 !tty_term_has(tty
->term
, TTYC_IL1
) ||
1744 c
->overlay_check
!= NULL
) {
1745 tty_redraw_region(tty
, ctx
);
1749 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1750 ctx
->s
->hyperlinks
);
1752 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1753 tty_margin_off(tty
);
1754 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1756 tty_emulate_repeat(tty
, TTYC_IL
, TTYC_IL1
, ctx
->num
);
1757 tty
->cx
= tty
->cy
= UINT_MAX
;
1761 tty_cmd_deleteline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1763 struct client
*c
= tty
->client
;
1766 !tty_full_width(tty
, ctx
) ||
1767 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1768 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1769 !tty_term_has(tty
->term
, TTYC_DL1
) ||
1772 c
->overlay_check
!= NULL
) {
1773 tty_redraw_region(tty
, ctx
);
1777 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1778 ctx
->s
->hyperlinks
);
1780 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1781 tty_margin_off(tty
);
1782 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1784 tty_emulate_repeat(tty
, TTYC_DL
, TTYC_DL1
, ctx
->num
);
1785 tty
->cx
= tty
->cy
= UINT_MAX
;
1789 tty_cmd_clearline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1791 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1792 ctx
->s
->hyperlinks
);
1794 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, 0, ctx
->sx
, ctx
->bg
);
1798 tty_cmd_clearendofline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1800 u_int nx
= ctx
->sx
- ctx
->ocx
;
1802 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1803 ctx
->s
->hyperlinks
);
1805 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, ctx
->ocx
, nx
, ctx
->bg
);
1809 tty_cmd_clearstartofline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1811 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1812 ctx
->s
->hyperlinks
);
1814 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, 0, ctx
->ocx
+ 1, ctx
->bg
);
1818 tty_cmd_reverseindex(struct tty
*tty
, const struct tty_ctx
*ctx
)
1820 struct client
*c
= tty
->client
;
1822 if (ctx
->ocy
!= ctx
->orupper
)
1826 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1827 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1828 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1829 (!tty_term_has(tty
->term
, TTYC_RI
) &&
1830 !tty_term_has(tty
->term
, TTYC_RIN
)) ||
1833 c
->overlay_check
!= NULL
) {
1834 tty_redraw_region(tty
, ctx
);
1838 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1839 ctx
->s
->hyperlinks
);
1841 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1842 tty_margin_pane(tty
, ctx
);
1843 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->orupper
);
1845 if (tty_term_has(tty
->term
, TTYC_RI
))
1846 tty_putcode(tty
, TTYC_RI
);
1848 tty_putcode_i(tty
, TTYC_RIN
, 1);
1852 tty_cmd_linefeed(struct tty
*tty
, const struct tty_ctx
*ctx
)
1854 struct client
*c
= tty
->client
;
1856 if (ctx
->ocy
!= ctx
->orlower
)
1860 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1861 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1862 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1865 c
->overlay_check
!= NULL
) {
1866 tty_redraw_region(tty
, ctx
);
1870 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1871 ctx
->s
->hyperlinks
);
1873 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1874 tty_margin_pane(tty
, ctx
);
1877 * If we want to wrap a pane while using margins, the cursor needs to
1878 * be exactly on the right of the region. If the cursor is entirely off
1879 * the edge - move it back to the right. Some terminals are funny about
1880 * this and insert extra spaces, so only use the right if margins are
1883 if (ctx
->xoff
+ ctx
->ocx
> tty
->rright
) {
1884 if (!tty_use_margin(tty
))
1885 tty_cursor(tty
, 0, ctx
->yoff
+ ctx
->ocy
);
1887 tty_cursor(tty
, tty
->rright
, ctx
->yoff
+ ctx
->ocy
);
1889 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1891 tty_putc(tty
, '\n');
1895 tty_cmd_scrollup(struct tty
*tty
, const struct tty_ctx
*ctx
)
1897 struct client
*c
= tty
->client
;
1901 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1902 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1903 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1906 c
->overlay_check
!= NULL
) {
1907 tty_redraw_region(tty
, ctx
);
1911 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1912 ctx
->s
->hyperlinks
);
1914 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1915 tty_margin_pane(tty
, ctx
);
1917 if (ctx
->num
== 1 || !tty_term_has(tty
->term
, TTYC_INDN
)) {
1918 if (!tty_use_margin(tty
))
1919 tty_cursor(tty
, 0, tty
->rlower
);
1921 tty_cursor(tty
, tty
->rright
, tty
->rlower
);
1922 for (i
= 0; i
< ctx
->num
; i
++)
1923 tty_putc(tty
, '\n');
1925 if (tty
->cy
== UINT_MAX
)
1926 tty_cursor(tty
, 0, 0);
1928 tty_cursor(tty
, 0, tty
->cy
);
1929 tty_putcode_i(tty
, TTYC_INDN
, ctx
->num
);
1934 tty_cmd_scrolldown(struct tty
*tty
, const struct tty_ctx
*ctx
)
1937 struct client
*c
= tty
->client
;
1940 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1941 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1942 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1943 (!tty_term_has(tty
->term
, TTYC_RI
) &&
1944 !tty_term_has(tty
->term
, TTYC_RIN
)) ||
1947 c
->overlay_check
!= NULL
) {
1948 tty_redraw_region(tty
, ctx
);
1952 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1953 ctx
->s
->hyperlinks
);
1955 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1956 tty_margin_pane(tty
, ctx
);
1957 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->orupper
);
1959 if (tty_term_has(tty
->term
, TTYC_RIN
))
1960 tty_putcode_i(tty
, TTYC_RIN
, ctx
->num
);
1962 for (i
= 0; i
< ctx
->num
; i
++)
1963 tty_putcode(tty
, TTYC_RI
);
1968 tty_cmd_clearendofscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
1970 u_int px
, py
, nx
, ny
;
1972 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1973 ctx
->s
->hyperlinks
);
1975 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
1976 tty_margin_off(tty
);
1981 ny
= ctx
->sy
- ctx
->ocy
- 1;
1983 tty_clear_pane_area(tty
, ctx
, py
, ny
, px
, nx
, ctx
->bg
);
1986 nx
= ctx
->sx
- ctx
->ocx
;
1989 tty_clear_pane_line(tty
, ctx
, py
, px
, nx
, ctx
->bg
);
1993 tty_cmd_clearstartofscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
1995 u_int px
, py
, nx
, ny
;
1997 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1998 ctx
->s
->hyperlinks
);
2000 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
2001 tty_margin_off(tty
);
2008 tty_clear_pane_area(tty
, ctx
, py
, ny
, px
, nx
, ctx
->bg
);
2014 tty_clear_pane_line(tty
, ctx
, py
, px
, nx
, ctx
->bg
);
2018 tty_cmd_clearscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
2020 u_int px
, py
, nx
, ny
;
2022 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
2023 ctx
->s
->hyperlinks
);
2025 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
2026 tty_margin_off(tty
);
2033 tty_clear_pane_area(tty
, ctx
, py
, ny
, px
, nx
, ctx
->bg
);
2037 tty_cmd_alignmenttest(struct tty
*tty
, const struct tty_ctx
*ctx
)
2042 ctx
->redraw_cb(ctx
);
2046 tty_attributes(tty
, &grid_default_cell
, &ctx
->defaults
, ctx
->palette
,
2047 ctx
->s
->hyperlinks
);
2049 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
2050 tty_margin_off(tty
);
2052 for (j
= 0; j
< ctx
->sy
; j
++) {
2053 tty_cursor_pane(tty
, ctx
, 0, j
);
2054 for (i
= 0; i
< ctx
->sx
; i
++)
2060 tty_cmd_cell(struct tty
*tty
, const struct tty_ctx
*ctx
)
2062 const struct grid_cell
*gcp
= ctx
->cell
;
2063 struct screen
*s
= ctx
->s
;
2064 struct overlay_ranges r
;
2065 u_int px
, py
, i
, vis
= 0;
2067 px
= ctx
->xoff
+ ctx
->ocx
- ctx
->wox
;
2068 py
= ctx
->yoff
+ ctx
->ocy
- ctx
->woy
;
2069 if (!tty_is_visible(tty
, ctx
, ctx
->ocx
, ctx
->ocy
, 1, 1) ||
2070 (gcp
->data
.width
== 1 && !tty_check_overlay(tty
, px
, py
)))
2073 /* Handle partially obstructed wide characters. */
2074 if (gcp
->data
.width
> 1) {
2075 tty_check_overlay_range(tty
, px
, py
, gcp
->data
.width
, &r
);
2076 for (i
= 0; i
< OVERLAY_MAX_RANGES
; i
++)
2078 if (vis
< gcp
->data
.width
) {
2079 tty_draw_line(tty
, s
, s
->cx
, s
->cy
, gcp
->data
.width
,
2080 px
, py
, &ctx
->defaults
, ctx
->palette
);
2085 if (ctx
->xoff
+ ctx
->ocx
- ctx
->wox
> tty
->sx
- 1 &&
2086 ctx
->ocy
== ctx
->orlower
&&
2087 tty_full_width(tty
, ctx
))
2088 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
2090 tty_margin_off(tty
);
2091 tty_cursor_pane_unless_wrap(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
2093 tty_cell(tty
, ctx
->cell
, &ctx
->defaults
, ctx
->palette
,
2094 ctx
->s
->hyperlinks
);
2097 tty_invalidate(tty
);
2101 tty_cmd_cells(struct tty
*tty
, const struct tty_ctx
*ctx
)
2103 struct overlay_ranges r
;
2104 u_int i
, px
, py
, cx
;
2105 char *cp
= ctx
->ptr
;
2107 if (!tty_is_visible(tty
, ctx
, ctx
->ocx
, ctx
->ocy
, ctx
->num
, 1))
2111 (ctx
->xoff
+ ctx
->ocx
< ctx
->wox
||
2112 ctx
->xoff
+ ctx
->ocx
+ ctx
->num
> ctx
->wox
+ ctx
->wsx
)) {
2113 if (!ctx
->wrapped
||
2114 !tty_full_width(tty
, ctx
) ||
2115 (tty
->term
->flags
& TERM_NOAM
) ||
2116 ctx
->xoff
+ ctx
->ocx
!= 0 ||
2117 ctx
->yoff
+ ctx
->ocy
!= tty
->cy
+ 1 ||
2118 tty
->cx
< tty
->sx
||
2119 tty
->cy
== tty
->rlower
)
2120 tty_draw_pane(tty
, ctx
, ctx
->ocy
);
2122 ctx
->redraw_cb(ctx
);
2126 tty_margin_off(tty
);
2127 tty_cursor_pane_unless_wrap(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
2128 tty_attributes(tty
, ctx
->cell
, &ctx
->defaults
, ctx
->palette
, ctx
->s
->hyperlinks
);
2130 /* Get tty position from pane position for overlay check. */
2131 px
= ctx
->xoff
+ ctx
->ocx
- ctx
->wox
;
2132 py
= ctx
->yoff
+ ctx
->ocy
- ctx
->woy
;
2134 tty_check_overlay_range(tty
, px
, py
, ctx
->num
, &r
);
2135 for (i
= 0; i
< OVERLAY_MAX_RANGES
; i
++) {
2138 /* Convert back to pane position for printing. */
2139 cx
= r
.px
[i
] - ctx
->xoff
+ ctx
->wox
;
2140 tty_cursor_pane_unless_wrap(tty
, ctx
, cx
, ctx
->ocy
);
2141 tty_putn(tty
, cp
+ r
.px
[i
] - px
, r
.nx
[i
], r
.nx
[i
]);
2146 tty_cmd_setselection(struct tty
*tty
, const struct tty_ctx
*ctx
)
2148 tty_set_selection(tty
, ctx
->ptr2
, ctx
->ptr
, ctx
->num
);
2152 tty_set_selection(struct tty
*tty
, const char *flags
, const char *buf
,
2158 if (~tty
->flags
& TTY_STARTED
)
2160 if (!tty_term_has(tty
->term
, TTYC_MS
))
2163 size
= 4 * ((len
+ 2) / 3) + 1; /* storage for base64 */
2164 encoded
= xmalloc(size
);
2166 b64_ntop(buf
, len
, encoded
, size
);
2167 tty
->flags
|= TTY_NOBLOCK
;
2168 tty_putcode_ss(tty
, TTYC_MS
, flags
, encoded
);
2174 tty_cmd_rawstring(struct tty
*tty
, const struct tty_ctx
*ctx
)
2176 tty
->flags
|= TTY_NOBLOCK
;
2177 tty_add(tty
, ctx
->ptr
, ctx
->num
);
2178 tty_invalidate(tty
);
2182 tty_cmd_syncstart(struct tty
*tty
, const struct tty_ctx
*ctx
)
2184 if (ctx
->num
== 0x11) {
2186 * This is an overlay and a command that moves the cursor so
2187 * start synchronized updates.
2189 tty_sync_start(tty
);
2190 } else if (~ctx
->num
& 0x10) {
2192 * This is a pane. If there is an overlay, always start;
2193 * otherwise, only if requested.
2195 if (ctx
->num
|| tty
->client
->overlay_draw
!= NULL
)
2196 tty_sync_start(tty
);
2201 tty_cell(struct tty
*tty
, const struct grid_cell
*gc
,
2202 const struct grid_cell
*defaults
, struct colour_palette
*palette
,
2203 struct hyperlinks
*hl
)
2205 const struct grid_cell
*gcp
;
2207 /* Skip last character if terminal is stupid. */
2208 if ((tty
->term
->flags
& TERM_NOAM
) &&
2209 tty
->cy
== tty
->sy
- 1 &&
2210 tty
->cx
== tty
->sx
- 1)
2213 /* If this is a padding character, do nothing. */
2214 if (gc
->flags
& GRID_FLAG_PADDING
)
2217 /* Check the output codeset and apply attributes. */
2218 gcp
= tty_check_codeset(tty
, gc
);
2219 tty_attributes(tty
, gcp
, defaults
, palette
, hl
);
2221 /* If it is a single character, write with putc to handle ACS. */
2222 if (gcp
->data
.size
== 1) {
2223 tty_attributes(tty
, gcp
, defaults
, palette
, hl
);
2224 if (*gcp
->data
.data
< 0x20 || *gcp
->data
.data
== 0x7f)
2226 tty_putc(tty
, *gcp
->data
.data
);
2230 /* Write the data. */
2231 tty_putn(tty
, gcp
->data
.data
, gcp
->data
.size
, gcp
->data
.width
);
2235 tty_reset(struct tty
*tty
)
2237 struct grid_cell
*gc
= &tty
->cell
;
2239 if (!grid_cells_equal(gc
, &grid_default_cell
)) {
2241 tty_putcode_ss(tty
, TTYC_HLS
, "", "");
2242 if ((gc
->attr
& GRID_ATTR_CHARSET
) && tty_acs_needed(tty
))
2243 tty_putcode(tty
, TTYC_RMACS
);
2244 tty_putcode(tty
, TTYC_SGR0
);
2245 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
2247 memcpy(&tty
->last_cell
, &grid_default_cell
, sizeof tty
->last_cell
);
2251 tty_invalidate(struct tty
*tty
)
2253 memcpy(&tty
->cell
, &grid_default_cell
, sizeof tty
->cell
);
2254 memcpy(&tty
->last_cell
, &grid_default_cell
, sizeof tty
->last_cell
);
2256 tty
->cx
= tty
->cy
= UINT_MAX
;
2257 tty
->rupper
= tty
->rleft
= UINT_MAX
;
2258 tty
->rlower
= tty
->rright
= UINT_MAX
;
2260 if (tty
->flags
& TTY_STARTED
) {
2261 if (tty_use_margin(tty
))
2262 tty_putcode(tty
, TTYC_ENMG
);
2263 tty_putcode(tty
, TTYC_SGR0
);
2265 tty
->mode
= ALL_MODES
;
2266 tty_update_mode(tty
, MODE_CURSOR
, NULL
);
2268 tty_cursor(tty
, 0, 0);
2269 tty_region_off(tty
);
2270 tty_margin_off(tty
);
2272 tty
->mode
= MODE_CURSOR
;
2275 /* Turn off margin. */
2277 tty_region_off(struct tty
*tty
)
2279 tty_region(tty
, 0, tty
->sy
- 1);
2282 /* Set region inside pane. */
2284 tty_region_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int rupper
,
2287 tty_region(tty
, ctx
->yoff
+ rupper
- ctx
->woy
,
2288 ctx
->yoff
+ rlower
- ctx
->woy
);
2291 /* Set region at absolute position. */
2293 tty_region(struct tty
*tty
, u_int rupper
, u_int rlower
)
2295 if (tty
->rlower
== rlower
&& tty
->rupper
== rupper
)
2297 if (!tty_term_has(tty
->term
, TTYC_CSR
))
2300 tty
->rupper
= rupper
;
2301 tty
->rlower
= rlower
;
2304 * Some terminals (such as PuTTY) do not correctly reset the cursor to
2305 * 0,0 if it is beyond the last column (they do not reset their wrap
2306 * flag so further output causes a line feed). As a workaround, do an
2307 * explicit move to 0 first.
2309 if (tty
->cx
>= tty
->sx
) {
2310 if (tty
->cy
== UINT_MAX
)
2311 tty_cursor(tty
, 0, 0);
2313 tty_cursor(tty
, 0, tty
->cy
);
2316 tty_putcode_ii(tty
, TTYC_CSR
, tty
->rupper
, tty
->rlower
);
2317 tty
->cx
= tty
->cy
= UINT_MAX
;
2320 /* Turn off margin. */
2322 tty_margin_off(struct tty
*tty
)
2324 tty_margin(tty
, 0, tty
->sx
- 1);
2327 /* Set margin inside pane. */
2329 tty_margin_pane(struct tty
*tty
, const struct tty_ctx
*ctx
)
2331 tty_margin(tty
, ctx
->xoff
- ctx
->wox
,
2332 ctx
->xoff
+ ctx
->sx
- 1 - ctx
->wox
);
2335 /* Set margin at absolute position. */
2337 tty_margin(struct tty
*tty
, u_int rleft
, u_int rright
)
2339 if (!tty_use_margin(tty
))
2341 if (tty
->rleft
== rleft
&& tty
->rright
== rright
)
2344 tty_putcode_ii(tty
, TTYC_CSR
, tty
->rupper
, tty
->rlower
);
2347 tty
->rright
= rright
;
2349 if (rleft
== 0 && rright
== tty
->sx
- 1)
2350 tty_putcode(tty
, TTYC_CLMG
);
2352 tty_putcode_ii(tty
, TTYC_CMG
, rleft
, rright
);
2353 tty
->cx
= tty
->cy
= UINT_MAX
;
2357 * Move the cursor, unless it would wrap itself when the next character is
2361 tty_cursor_pane_unless_wrap(struct tty
*tty
, const struct tty_ctx
*ctx
,
2364 if (!ctx
->wrapped
||
2365 !tty_full_width(tty
, ctx
) ||
2366 (tty
->term
->flags
& TERM_NOAM
) ||
2367 ctx
->xoff
+ cx
!= 0 ||
2368 ctx
->yoff
+ cy
!= tty
->cy
+ 1 ||
2369 tty
->cx
< tty
->sx
||
2370 tty
->cy
== tty
->rlower
)
2371 tty_cursor_pane(tty
, ctx
, cx
, cy
);
2373 log_debug("%s: will wrap at %u,%u", __func__
, tty
->cx
, tty
->cy
);
2376 /* Move cursor inside pane. */
2378 tty_cursor_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int cx
, u_int cy
)
2380 tty_cursor(tty
, ctx
->xoff
+ cx
- ctx
->wox
, ctx
->yoff
+ cy
- ctx
->woy
);
2383 /* Move cursor to absolute position. */
2385 tty_cursor(struct tty
*tty
, u_int cx
, u_int cy
)
2387 struct tty_term
*term
= tty
->term
;
2391 if (tty
->flags
& TTY_BLOCK
)
2398 * If in the automargin space, and want to be there, do not move.
2399 * Otherwise, force the cursor to be in range (and complain).
2401 if (cx
== thisx
&& cy
== thisy
&& cx
== tty
->sx
)
2403 if (cx
> tty
->sx
- 1) {
2404 log_debug("%s: x too big %u > %u", __func__
, cx
, tty
->sx
- 1);
2409 if (cx
== thisx
&& cy
== thisy
)
2412 /* Currently at the very end of the line - use absolute movement. */
2413 if (thisx
> tty
->sx
- 1)
2416 /* Move to home position (0, 0). */
2417 if (cx
== 0 && cy
== 0 && tty_term_has(term
, TTYC_HOME
)) {
2418 tty_putcode(tty
, TTYC_HOME
);
2422 /* Zero on the next line. */
2423 if (cx
== 0 && cy
== thisy
+ 1 && thisy
!= tty
->rlower
&&
2424 (!tty_use_margin(tty
) || tty
->rleft
== 0)) {
2425 tty_putc(tty
, '\r');
2426 tty_putc(tty
, '\n');
2430 /* Moving column or row. */
2433 * Moving column only, row staying the same.
2437 if (cx
== 0 && (!tty_use_margin(tty
) || tty
->rleft
== 0)) {
2438 tty_putc(tty
, '\r');
2442 /* One to the left. */
2443 if (cx
== thisx
- 1 && tty_term_has(term
, TTYC_CUB1
)) {
2444 tty_putcode(tty
, TTYC_CUB1
);
2448 /* One to the right. */
2449 if (cx
== thisx
+ 1 && tty_term_has(term
, TTYC_CUF1
)) {
2450 tty_putcode(tty
, TTYC_CUF1
);
2454 /* Calculate difference. */
2455 change
= thisx
- cx
; /* +ve left, -ve right */
2458 * Use HPA if change is larger than absolute, otherwise move
2459 * the cursor with CUB/CUF.
2461 if ((u_int
) abs(change
) > cx
&& tty_term_has(term
, TTYC_HPA
)) {
2462 tty_putcode_i(tty
, TTYC_HPA
, cx
);
2464 } else if (change
> 0 &&
2465 tty_term_has(term
, TTYC_CUB
) &&
2466 !tty_use_margin(tty
)) {
2467 if (change
== 2 && tty_term_has(term
, TTYC_CUB1
)) {
2468 tty_putcode(tty
, TTYC_CUB1
);
2469 tty_putcode(tty
, TTYC_CUB1
);
2472 tty_putcode_i(tty
, TTYC_CUB
, change
);
2474 } else if (change
< 0 &&
2475 tty_term_has(term
, TTYC_CUF
) &&
2476 !tty_use_margin(tty
)) {
2477 tty_putcode_i(tty
, TTYC_CUF
, -change
);
2480 } else if (cx
== thisx
) {
2482 * Moving row only, column staying the same.
2486 if (thisy
!= tty
->rupper
&&
2487 cy
== thisy
- 1 && tty_term_has(term
, TTYC_CUU1
)) {
2488 tty_putcode(tty
, TTYC_CUU1
);
2493 if (thisy
!= tty
->rlower
&&
2494 cy
== thisy
+ 1 && tty_term_has(term
, TTYC_CUD1
)) {
2495 tty_putcode(tty
, TTYC_CUD1
);
2499 /* Calculate difference. */
2500 change
= thisy
- cy
; /* +ve up, -ve down */
2503 * Try to use VPA if change is larger than absolute or if this
2504 * change would cross the scroll region, otherwise use CUU/CUD.
2506 if ((u_int
) abs(change
) > cy
||
2507 (change
< 0 && cy
- change
> tty
->rlower
) ||
2508 (change
> 0 && cy
- change
< tty
->rupper
)) {
2509 if (tty_term_has(term
, TTYC_VPA
)) {
2510 tty_putcode_i(tty
, TTYC_VPA
, cy
);
2513 } else if (change
> 0 && tty_term_has(term
, TTYC_CUU
)) {
2514 tty_putcode_i(tty
, TTYC_CUU
, change
);
2516 } else if (change
< 0 && tty_term_has(term
, TTYC_CUD
)) {
2517 tty_putcode_i(tty
, TTYC_CUD
, -change
);
2523 /* Absolute movement. */
2524 tty_putcode_ii(tty
, TTYC_CUP
, cy
, cx
);
2532 tty_hyperlink(struct tty
*tty
, const struct grid_cell
*gc
,
2533 struct hyperlinks
*hl
)
2535 const char *uri
, *id
;
2537 if (gc
->link
== tty
->cell
.link
)
2539 tty
->cell
.link
= gc
->link
;
2544 if (gc
->link
== 0 || !hyperlinks_get(hl
, gc
->link
, &uri
, NULL
, &id
))
2545 tty_putcode_ss(tty
, TTYC_HLS
, "", "");
2547 tty_putcode_ss(tty
, TTYC_HLS
, id
, uri
);
2551 tty_attributes(struct tty
*tty
, const struct grid_cell
*gc
,
2552 const struct grid_cell
*defaults
, struct colour_palette
*palette
,
2553 struct hyperlinks
*hl
)
2555 struct grid_cell
*tc
= &tty
->cell
, gc2
;
2558 /* Copy cell and update default colours. */
2559 memcpy(&gc2
, gc
, sizeof gc2
);
2560 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2562 gc2
.fg
= defaults
->fg
;
2564 gc2
.bg
= defaults
->bg
;
2567 /* Ignore cell if it is the same as the last one. */
2568 if (gc2
.attr
== tty
->last_cell
.attr
&&
2569 gc2
.fg
== tty
->last_cell
.fg
&&
2570 gc2
.bg
== tty
->last_cell
.bg
&&
2571 gc2
.us
== tty
->last_cell
.us
&&
2572 gc2
.link
== tty
->last_cell
.link
)
2576 * If no setab, try to use the reverse attribute as a best-effort for a
2577 * non-default background. This is a bit of a hack but it doesn't do
2578 * any serious harm and makes a couple of applications happier.
2580 if (!tty_term_has(tty
->term
, TTYC_SETAB
)) {
2581 if (gc2
.attr
& GRID_ATTR_REVERSE
) {
2582 if (gc2
.fg
!= 7 && !COLOUR_DEFAULT(gc2
.fg
))
2583 gc2
.attr
&= ~GRID_ATTR_REVERSE
;
2585 if (gc2
.bg
!= 0 && !COLOUR_DEFAULT(gc2
.bg
))
2586 gc2
.attr
|= GRID_ATTR_REVERSE
;
2590 /* Fix up the colours if necessary. */
2591 tty_check_fg(tty
, palette
, &gc2
);
2592 tty_check_bg(tty
, palette
, &gc2
);
2593 tty_check_us(tty
, palette
, &gc2
);
2596 * If any bits are being cleared or the underline colour is now default,
2599 if ((tc
->attr
& ~gc2
.attr
) || (tc
->us
!= gc2
.us
&& gc2
.us
== 0))
2603 * Set the colours. This may call tty_reset() (so it comes next) and
2604 * may add to (NOT remove) the desired attributes.
2606 tty_colours(tty
, &gc2
);
2608 /* Filter out attribute bits already set. */
2609 changed
= gc2
.attr
& ~tc
->attr
;
2610 tc
->attr
= gc2
.attr
;
2612 /* Set the attributes. */
2613 if (changed
& GRID_ATTR_BRIGHT
)
2614 tty_putcode(tty
, TTYC_BOLD
);
2615 if (changed
& GRID_ATTR_DIM
)
2616 tty_putcode(tty
, TTYC_DIM
);
2617 if (changed
& GRID_ATTR_ITALICS
)
2618 tty_set_italics(tty
);
2619 if (changed
& GRID_ATTR_ALL_UNDERSCORE
) {
2620 if ((changed
& GRID_ATTR_UNDERSCORE
) ||
2621 !tty_term_has(tty
->term
, TTYC_SMULX
))
2622 tty_putcode(tty
, TTYC_SMUL
);
2623 else if (changed
& GRID_ATTR_UNDERSCORE_2
)
2624 tty_putcode_i(tty
, TTYC_SMULX
, 2);
2625 else if (changed
& GRID_ATTR_UNDERSCORE_3
)
2626 tty_putcode_i(tty
, TTYC_SMULX
, 3);
2627 else if (changed
& GRID_ATTR_UNDERSCORE_4
)
2628 tty_putcode_i(tty
, TTYC_SMULX
, 4);
2629 else if (changed
& GRID_ATTR_UNDERSCORE_5
)
2630 tty_putcode_i(tty
, TTYC_SMULX
, 5);
2632 if (changed
& GRID_ATTR_BLINK
)
2633 tty_putcode(tty
, TTYC_BLINK
);
2634 if (changed
& GRID_ATTR_REVERSE
) {
2635 if (tty_term_has(tty
->term
, TTYC_REV
))
2636 tty_putcode(tty
, TTYC_REV
);
2637 else if (tty_term_has(tty
->term
, TTYC_SMSO
))
2638 tty_putcode(tty
, TTYC_SMSO
);
2640 if (changed
& GRID_ATTR_HIDDEN
)
2641 tty_putcode(tty
, TTYC_INVIS
);
2642 if (changed
& GRID_ATTR_STRIKETHROUGH
)
2643 tty_putcode(tty
, TTYC_SMXX
);
2644 if (changed
& GRID_ATTR_OVERLINE
)
2645 tty_putcode(tty
, TTYC_SMOL
);
2646 if ((changed
& GRID_ATTR_CHARSET
) && tty_acs_needed(tty
))
2647 tty_putcode(tty
, TTYC_SMACS
);
2649 /* Set hyperlink if any. */
2650 tty_hyperlink(tty
, gc
, hl
);
2652 memcpy(&tty
->last_cell
, &gc2
, sizeof tty
->last_cell
);
2656 tty_colours(struct tty
*tty
, const struct grid_cell
*gc
)
2658 struct grid_cell
*tc
= &tty
->cell
;
2661 /* No changes? Nothing is necessary. */
2662 if (gc
->fg
== tc
->fg
&& gc
->bg
== tc
->bg
&& gc
->us
== tc
->us
)
2666 * Is either the default colour? This is handled specially because the
2667 * best solution might be to reset both colours to default, in which
2668 * case if only one is default need to fall onward to set the other
2671 if (COLOUR_DEFAULT(gc
->fg
) || COLOUR_DEFAULT(gc
->bg
)) {
2673 * If don't have AX but do have op, send sgr0 (op can't
2674 * actually be used because it is sometimes the same as sgr0
2675 * and sometimes isn't). This resets both colours to default.
2677 * Otherwise, try to set the default colour only as needed.
2679 have_ax
= tty_term_flag(tty
->term
, TTYC_AX
);
2680 if (!have_ax
&& tty_term_has(tty
->term
, TTYC_OP
))
2683 if (COLOUR_DEFAULT(gc
->fg
) && !COLOUR_DEFAULT(tc
->fg
)) {
2685 tty_puts(tty
, "\033[39m");
2686 else if (tc
->fg
!= 7)
2687 tty_putcode_i(tty
, TTYC_SETAF
, 7);
2690 if (COLOUR_DEFAULT(gc
->bg
) && !COLOUR_DEFAULT(tc
->bg
)) {
2692 tty_puts(tty
, "\033[49m");
2693 else if (tc
->bg
!= 0)
2694 tty_putcode_i(tty
, TTYC_SETAB
, 0);
2700 /* Set the foreground colour. */
2701 if (!COLOUR_DEFAULT(gc
->fg
) && gc
->fg
!= tc
->fg
)
2702 tty_colours_fg(tty
, gc
);
2705 * Set the background colour. This must come after the foreground as
2706 * tty_colour_fg() can call tty_reset().
2708 if (!COLOUR_DEFAULT(gc
->bg
) && gc
->bg
!= tc
->bg
)
2709 tty_colours_bg(tty
, gc
);
2711 /* Set the underscore colour. */
2712 if (gc
->us
!= tc
->us
)
2713 tty_colours_us(tty
, gc
);
2717 tty_check_fg(struct tty
*tty
, struct colour_palette
*palette
,
2718 struct grid_cell
*gc
)
2725 * Perform substitution if this pane has a palette. If the bright
2726 * attribute is set and Nobr is not present, use the bright entry in
2727 * the palette by changing to the aixterm colour
2729 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2732 gc
->attr
& GRID_ATTR_BRIGHT
&&
2733 !tty_term_has(tty
->term
, TTYC_NOBR
))
2735 if ((c
= colour_palette_get(palette
, c
)) != -1)
2739 /* Is this a 24-bit colour? */
2740 if (gc
->fg
& COLOUR_FLAG_RGB
) {
2741 /* Not a 24-bit terminal? Translate to 256-colour palette. */
2742 if (tty
->term
->flags
& TERM_RGBCOLOURS
)
2744 colour_split_rgb(gc
->fg
, &r
, &g
, &b
);
2745 gc
->fg
= colour_find_rgb(r
, g
, b
);
2748 /* How many colours does this terminal have? */
2749 if (tty
->term
->flags
& TERM_256COLOURS
)
2752 colours
= tty_term_number(tty
->term
, TTYC_COLORS
);
2754 /* Is this a 256-colour colour? */
2755 if (gc
->fg
& COLOUR_FLAG_256
) {
2756 /* And not a 256 colour mode? */
2757 if (colours
< 256) {
2758 gc
->fg
= colour_256to16(gc
->fg
);
2768 /* Is this an aixterm colour? */
2769 if (gc
->fg
>= 90 && gc
->fg
<= 97 && colours
< 16) {
2771 gc
->attr
|= GRID_ATTR_BRIGHT
;
2776 tty_check_bg(struct tty
*tty
, struct colour_palette
*palette
,
2777 struct grid_cell
*gc
)
2783 /* Perform substitution if this pane has a palette. */
2784 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2785 if ((c
= colour_palette_get(palette
, gc
->bg
)) != -1)
2789 /* Is this a 24-bit colour? */
2790 if (gc
->bg
& COLOUR_FLAG_RGB
) {
2791 /* Not a 24-bit terminal? Translate to 256-colour palette. */
2792 if (tty
->term
->flags
& TERM_RGBCOLOURS
)
2794 colour_split_rgb(gc
->bg
, &r
, &g
, &b
);
2795 gc
->bg
= colour_find_rgb(r
, g
, b
);
2798 /* How many colours does this terminal have? */
2799 if (tty
->term
->flags
& TERM_256COLOURS
)
2802 colours
= tty_term_number(tty
->term
, TTYC_COLORS
);
2804 /* Is this a 256-colour colour? */
2805 if (gc
->bg
& COLOUR_FLAG_256
) {
2807 * And not a 256 colour mode? Translate to 16-colour
2808 * palette. Bold background doesn't exist portably, so just
2809 * discard the bold bit if set.
2811 if (colours
< 256) {
2812 gc
->bg
= colour_256to16(gc
->bg
);
2822 /* Is this an aixterm colour? */
2823 if (gc
->bg
>= 90 && gc
->bg
<= 97 && colours
< 16)
2828 tty_check_us(__unused
struct tty
*tty
, struct colour_palette
*palette
,
2829 struct grid_cell
*gc
)
2833 /* Perform substitution if this pane has a palette. */
2834 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2835 if ((c
= colour_palette_get(palette
, gc
->us
)) != -1)
2839 /* Convert underscore colour if only RGB can be supported. */
2840 if (!tty_term_has(tty
->term
, TTYC_SETULC1
)) {
2841 if ((c
= colour_force_rgb (gc
->us
)) == -1)
2849 tty_colours_fg(struct tty
*tty
, const struct grid_cell
*gc
)
2851 struct grid_cell
*tc
= &tty
->cell
;
2854 /* Is this a 24-bit or 256-colour colour? */
2855 if (gc
->fg
& COLOUR_FLAG_RGB
|| gc
->fg
& COLOUR_FLAG_256
) {
2856 if (tty_try_colour(tty
, gc
->fg
, "38") == 0)
2858 /* Should not get here, already converted in tty_check_fg. */
2862 /* Is this an aixterm bright colour? */
2863 if (gc
->fg
>= 90 && gc
->fg
<= 97) {
2864 if (tty
->term
->flags
& TERM_256COLOURS
) {
2865 xsnprintf(s
, sizeof s
, "\033[%dm", gc
->fg
);
2868 tty_putcode_i(tty
, TTYC_SETAF
, gc
->fg
- 90 + 8);
2872 /* Otherwise set the foreground colour. */
2873 tty_putcode_i(tty
, TTYC_SETAF
, gc
->fg
);
2876 /* Save the new values in the terminal current cell. */
2881 tty_colours_bg(struct tty
*tty
, const struct grid_cell
*gc
)
2883 struct grid_cell
*tc
= &tty
->cell
;
2886 /* Is this a 24-bit or 256-colour colour? */
2887 if (gc
->bg
& COLOUR_FLAG_RGB
|| gc
->bg
& COLOUR_FLAG_256
) {
2888 if (tty_try_colour(tty
, gc
->bg
, "48") == 0)
2890 /* Should not get here, already converted in tty_check_bg. */
2894 /* Is this an aixterm bright colour? */
2895 if (gc
->bg
>= 90 && gc
->bg
<= 97) {
2896 if (tty
->term
->flags
& TERM_256COLOURS
) {
2897 xsnprintf(s
, sizeof s
, "\033[%dm", gc
->bg
+ 10);
2900 tty_putcode_i(tty
, TTYC_SETAB
, gc
->bg
- 90 + 8);
2904 /* Otherwise set the background colour. */
2905 tty_putcode_i(tty
, TTYC_SETAB
, gc
->bg
);
2908 /* Save the new values in the terminal current cell. */
2913 tty_colours_us(struct tty
*tty
, const struct grid_cell
*gc
)
2915 struct grid_cell
*tc
= &tty
->cell
;
2919 /* Clear underline colour. */
2920 if (COLOUR_DEFAULT(gc
->us
)) {
2921 tty_putcode(tty
, TTYC_OL
);
2926 * If this is not an RGB colour, use Setulc1 if it exists, otherwise
2929 if (~gc
->us
& COLOUR_FLAG_RGB
) {
2931 if ((~c
& COLOUR_FLAG_256
) && (c
>= 90 && c
<= 97))
2933 tty_putcode_i(tty
, TTYC_SETULC1
, c
& ~COLOUR_FLAG_256
);
2938 * Setulc and setal follows the ncurses(3) one argument "direct colour"
2939 * capability format. Calculate the colour value.
2941 colour_split_rgb(gc
->us
, &r
, &g
, &b
);
2942 c
= (65536 * r
) + (256 * g
) + b
;
2945 * Write the colour. Only use setal if the RGB flag is set because the
2946 * non-RGB version may be wrong.
2948 if (tty_term_has(tty
->term
, TTYC_SETULC
))
2949 tty_putcode_i(tty
, TTYC_SETULC
, c
);
2950 else if (tty_term_has(tty
->term
, TTYC_SETAL
) &&
2951 tty_term_has(tty
->term
, TTYC_RGB
))
2952 tty_putcode_i(tty
, TTYC_SETAL
, c
);
2955 /* Save the new values in the terminal current cell. */
2960 tty_try_colour(struct tty
*tty
, int colour
, const char *type
)
2964 if (colour
& COLOUR_FLAG_256
) {
2965 if (*type
== '3' && tty_term_has(tty
->term
, TTYC_SETAF
))
2966 tty_putcode_i(tty
, TTYC_SETAF
, colour
& 0xff);
2967 else if (tty_term_has(tty
->term
, TTYC_SETAB
))
2968 tty_putcode_i(tty
, TTYC_SETAB
, colour
& 0xff);
2972 if (colour
& COLOUR_FLAG_RGB
) {
2973 colour_split_rgb(colour
& 0xffffff, &r
, &g
, &b
);
2974 if (*type
== '3' && tty_term_has(tty
->term
, TTYC_SETRGBF
))
2975 tty_putcode_iii(tty
, TTYC_SETRGBF
, r
, g
, b
);
2976 else if (tty_term_has(tty
->term
, TTYC_SETRGBB
))
2977 tty_putcode_iii(tty
, TTYC_SETRGBB
, r
, g
, b
);
2985 tty_window_default_style(struct grid_cell
*gc
, struct window_pane
*wp
)
2987 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
2988 gc
->fg
= wp
->palette
.fg
;
2989 gc
->bg
= wp
->palette
.bg
;
2993 tty_default_colours(struct grid_cell
*gc
, struct window_pane
*wp
)
2995 struct options
*oo
= wp
->options
;
2996 struct format_tree
*ft
;
2998 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
3000 if (wp
->flags
& PANE_STYLECHANGED
) {
3001 log_debug("%%%u: style changed", wp
->id
);
3002 wp
->flags
&= ~PANE_STYLECHANGED
;
3004 ft
= format_create(NULL
, NULL
, FORMAT_PANE
|wp
->id
,
3006 format_defaults(ft
, NULL
, NULL
, NULL
, wp
);
3007 tty_window_default_style(&wp
->cached_active_gc
, wp
);
3008 style_add(&wp
->cached_active_gc
, oo
, "window-active-style", ft
);
3009 tty_window_default_style(&wp
->cached_gc
, wp
);
3010 style_add(&wp
->cached_gc
, oo
, "window-style", ft
);
3015 if (wp
== wp
->window
->active
&& wp
->cached_active_gc
.fg
!= 8)
3016 gc
->fg
= wp
->cached_active_gc
.fg
;
3018 gc
->fg
= wp
->cached_gc
.fg
;
3022 if (wp
== wp
->window
->active
&& wp
->cached_active_gc
.bg
!= 8)
3023 gc
->bg
= wp
->cached_active_gc
.bg
;
3025 gc
->bg
= wp
->cached_gc
.bg
;
3030 tty_default_attributes(struct tty
*tty
, const struct grid_cell
*defaults
,
3031 struct colour_palette
*palette
, u_int bg
, struct hyperlinks
*hl
)
3033 struct grid_cell gc
;
3035 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
3037 tty_attributes(tty
, &gc
, defaults
, palette
, hl
);
3041 tty_clipboard_query_callback(__unused
int fd
, __unused
short events
, void *data
)
3043 struct tty
*tty
= data
;
3044 struct client
*c
= tty
->client
;
3046 c
->flags
&= ~CLIENT_CLIPBOARDBUFFER
;
3047 free(c
->clipboard_panes
);
3048 c
->clipboard_panes
= NULL
;
3049 c
->clipboard_npanes
= 0;
3051 tty
->flags
&= ~TTY_OSC52QUERY
;
3055 tty_clipboard_query(struct tty
*tty
)
3057 struct timeval tv
= { .tv_sec
= TTY_QUERY_TIMEOUT
};
3059 if ((~tty
->flags
& TTY_STARTED
) || (tty
->flags
& TTY_OSC52QUERY
))
3061 tty_putcode_ss(tty
, TTYC_MS
, "", "?");
3063 tty
->flags
|= TTY_OSC52QUERY
;
3064 evtimer_set(&tty
->clipboard_timer
, tty_clipboard_query_callback
, tty
);
3065 evtimer_add(&tty
->clipboard_timer
, &tv
);