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 void tty_set_italics(struct tty
*);
38 static int tty_try_colour(struct tty
*, int, const char *);
39 static void tty_force_cursor_colour(struct tty
*, int);
40 static void tty_cursor_pane(struct tty
*, const struct tty_ctx
*, u_int
,
42 static void tty_cursor_pane_unless_wrap(struct tty
*,
43 const struct tty_ctx
*, u_int
, u_int
);
44 static void tty_invalidate(struct tty
*);
45 static void tty_colours(struct tty
*, const struct grid_cell
*);
46 static void tty_check_fg(struct tty
*, struct colour_palette
*,
48 static void tty_check_bg(struct tty
*, struct colour_palette
*,
50 static void tty_check_us(struct tty
*, struct colour_palette
*,
52 static void tty_colours_fg(struct tty
*, const struct grid_cell
*);
53 static void tty_colours_bg(struct tty
*, const struct grid_cell
*);
54 static void tty_colours_us(struct tty
*, const struct grid_cell
*);
56 static void tty_region_pane(struct tty
*, const struct tty_ctx
*, u_int
,
58 static void tty_region(struct tty
*, u_int
, u_int
);
59 static void tty_margin_pane(struct tty
*, const struct tty_ctx
*);
60 static void tty_margin(struct tty
*, u_int
, u_int
);
61 static int tty_large_region(struct tty
*, const struct tty_ctx
*);
62 static int tty_fake_bce(const struct tty
*, const struct grid_cell
*,
64 static void tty_redraw_region(struct tty
*, const struct tty_ctx
*);
65 static void tty_emulate_repeat(struct tty
*, enum tty_code_code
,
66 enum tty_code_code
, u_int
);
67 static void tty_repeat_space(struct tty
*, u_int
);
68 static void tty_draw_pane(struct tty
*, const struct tty_ctx
*, u_int
);
69 static void tty_default_attributes(struct tty
*, const struct grid_cell
*,
70 struct colour_palette
*, u_int
, struct hyperlinks
*);
71 static int tty_check_overlay(struct tty
*, u_int
, u_int
);
72 static void tty_check_overlay_range(struct tty
*, u_int
, u_int
, u_int
,
73 struct overlay_ranges
*);
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)
84 #define TTY_QUERY_TIMEOUT 5
85 #define TTY_REQUEST_LIMIT 30
92 xsnprintf(name
, sizeof name
, "tmux-out-%ld.log", (long)getpid());
94 tty_log_fd
= open(name
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0644);
95 if (tty_log_fd
!= -1 && fcntl(tty_log_fd
, F_SETFD
, FD_CLOEXEC
) == -1)
96 fatal("fcntl failed");
100 tty_init(struct tty
*tty
, struct client
*c
)
105 memset(tty
, 0, sizeof *tty
);
108 tty
->cstyle
= SCREEN_CURSOR_DEFAULT
;
110 tty
->fg
= tty
->bg
= -1;
112 if (tcgetattr(c
->fd
, &tty
->tio
) != 0)
118 tty_resize(struct tty
*tty
)
120 struct client
*c
= tty
->client
;
122 u_int sx
, sy
, xpixel
, ypixel
;
124 if (ioctl(c
->fd
, TIOCGWINSZ
, &ws
) != -1) {
130 xpixel
= ws
.ws_xpixel
/ sx
;
136 ypixel
= ws
.ws_ypixel
/ sy
;
143 log_debug("%s: %s now %ux%u (%ux%u)", __func__
, c
->name
, sx
, sy
,
145 tty_set_size(tty
, sx
, sy
, xpixel
, ypixel
);
150 tty_set_size(struct tty
*tty
, u_int sx
, u_int sy
, u_int xpixel
, u_int ypixel
)
154 tty
->xpixel
= xpixel
;
155 tty
->ypixel
= ypixel
;
159 tty_read_callback(__unused
int fd
, __unused
short events
, void *data
)
161 struct tty
*tty
= data
;
162 struct client
*c
= tty
->client
;
163 const char *name
= c
->name
;
164 size_t size
= EVBUFFER_LENGTH(tty
->in
);
167 nread
= evbuffer_read(tty
->in
, c
->fd
, -1);
168 if (nread
== 0 || nread
== -1) {
170 log_debug("%s: read closed", name
);
172 log_debug("%s: read error: %s", name
, strerror(errno
));
173 event_del(&tty
->event_in
);
174 server_client_lost(tty
->client
);
177 log_debug("%s: read %d bytes (already %zu)", name
, nread
, size
);
179 while (tty_keys_next(tty
))
184 tty_timer_callback(__unused
int fd
, __unused
short events
, void *data
)
186 struct tty
*tty
= data
;
187 struct client
*c
= tty
->client
;
188 struct timeval tv
= { .tv_usec
= TTY_BLOCK_INTERVAL
};
190 log_debug("%s: %zu discarded", c
->name
, tty
->discarded
);
192 c
->flags
|= CLIENT_ALLREDRAWFLAGS
;
193 c
->discarded
+= tty
->discarded
;
195 if (tty
->discarded
< TTY_BLOCK_STOP(tty
)) {
196 tty
->flags
&= ~TTY_BLOCK
;
201 evtimer_add(&tty
->timer
, &tv
);
205 tty_block_maybe(struct tty
*tty
)
207 struct client
*c
= tty
->client
;
208 size_t size
= EVBUFFER_LENGTH(tty
->out
);
209 struct timeval tv
= { .tv_usec
= TTY_BLOCK_INTERVAL
};
212 tty
->flags
&= ~TTY_NOBLOCK
;
213 else if (tty
->flags
& TTY_NOBLOCK
)
216 if (size
< TTY_BLOCK_START(tty
))
219 if (tty
->flags
& TTY_BLOCK
)
221 tty
->flags
|= TTY_BLOCK
;
223 log_debug("%s: can't keep up, %zu discarded", c
->name
, size
);
225 evbuffer_drain(tty
->out
, size
);
226 c
->discarded
+= size
;
229 evtimer_add(&tty
->timer
, &tv
);
234 tty_write_callback(__unused
int fd
, __unused
short events
, void *data
)
236 struct tty
*tty
= data
;
237 struct client
*c
= tty
->client
;
238 size_t size
= EVBUFFER_LENGTH(tty
->out
);
241 nwrite
= evbuffer_write(tty
->out
, c
->fd
);
244 log_debug("%s: wrote %d bytes (of %zu)", c
->name
, nwrite
, size
);
247 if ((size_t)nwrite
>= c
->redraw
)
251 log_debug("%s: waiting for redraw, %zu bytes left", c
->name
,
253 } else if (tty_block_maybe(tty
))
256 if (EVBUFFER_LENGTH(tty
->out
) != 0)
257 event_add(&tty
->event_out
, NULL
);
261 tty_open(struct tty
*tty
, char **cause
)
263 struct client
*c
= tty
->client
;
265 tty
->term
= tty_term_create(tty
, c
->term_name
, c
->term_caps
,
266 c
->term_ncaps
, &c
->term_features
, cause
);
267 if (tty
->term
== NULL
) {
271 tty
->flags
|= TTY_OPENED
;
273 tty
->flags
&= ~(TTY_NOCURSOR
|TTY_FREEZE
|TTY_BLOCK
|TTY_TIMER
);
275 event_set(&tty
->event_in
, c
->fd
, EV_PERSIST
|EV_READ
,
276 tty_read_callback
, tty
);
277 tty
->in
= evbuffer_new();
279 fatal("out of memory");
281 event_set(&tty
->event_out
, c
->fd
, EV_WRITE
, tty_write_callback
, tty
);
282 tty
->out
= evbuffer_new();
283 if (tty
->out
== NULL
)
284 fatal("out of memory");
286 evtimer_set(&tty
->timer
, tty_timer_callback
, tty
);
295 tty_start_timer_callback(__unused
int fd
, __unused
short events
, void *data
)
297 struct tty
*tty
= data
;
298 struct client
*c
= tty
->client
;
300 log_debug("%s: start timer fired", c
->name
);
301 if ((tty
->flags
& (TTY_HAVEDA
|TTY_HAVEDA2
|TTY_HAVEXDA
)) == 0)
302 tty_update_features(tty
);
303 tty
->flags
|= TTY_ALL_REQUEST_FLAGS
;
307 tty_start_tty(struct tty
*tty
)
309 struct client
*c
= tty
->client
;
311 struct timeval tv
= { .tv_sec
= TTY_QUERY_TIMEOUT
};
313 setblocking(c
->fd
, 0);
314 event_add(&tty
->event_in
, NULL
);
316 memcpy(&tio
, &tty
->tio
, sizeof tio
);
317 tio
.c_iflag
&= ~(IXON
|IXOFF
|ICRNL
|INLCR
|IGNCR
|IMAXBEL
|ISTRIP
);
318 tio
.c_iflag
|= IGNBRK
;
319 tio
.c_oflag
&= ~(OPOST
|ONLCR
|OCRNL
|ONLRET
);
320 tio
.c_lflag
&= ~(IEXTEN
|ICANON
|ECHO
|ECHOE
|ECHONL
|ECHOCTL
|ECHOPRT
|
324 if (tcsetattr(c
->fd
, TCSANOW
, &tio
) == 0)
325 tcflush(c
->fd
, TCOFLUSH
);
327 tty_putcode(tty
, TTYC_SMCUP
);
329 tty_putcode(tty
, TTYC_SMKX
);
330 tty_putcode(tty
, TTYC_CLEAR
);
332 if (tty_acs_needed(tty
)) {
333 log_debug("%s: using capabilities for ACS", c
->name
);
334 tty_putcode(tty
, TTYC_ENACS
);
336 log_debug("%s: using UTF-8 for ACS", c
->name
);
338 tty_putcode(tty
, TTYC_CNORM
);
339 if (tty_term_has(tty
->term
, TTYC_KMOUS
)) {
340 tty_puts(tty
, "\033[?1000l\033[?1002l\033[?1003l");
341 tty_puts(tty
, "\033[?1006l\033[?1005l");
343 if (tty_term_has(tty
->term
, TTYC_ENBP
))
344 tty_putcode(tty
, TTYC_ENBP
);
346 evtimer_set(&tty
->start_timer
, tty_start_timer_callback
, tty
);
347 evtimer_add(&tty
->start_timer
, &tv
);
349 tty
->flags
|= TTY_STARTED
;
352 if (tty
->ccolour
!= -1)
353 tty_force_cursor_colour(tty
, -1);
355 tty
->mouse_drag_flag
= 0;
356 tty
->mouse_drag_update
= NULL
;
357 tty
->mouse_drag_release
= NULL
;
361 tty_send_requests(struct tty
*tty
)
363 if (~tty
->flags
& TTY_STARTED
)
366 if (tty
->term
->flags
& TERM_VT100LIKE
) {
367 if (~tty
->term
->flags
& TTY_HAVEDA
)
368 tty_puts(tty
, "\033[c");
369 if (~tty
->flags
& TTY_HAVEDA2
)
370 tty_puts(tty
, "\033[>c");
371 if (~tty
->flags
& TTY_HAVEXDA
)
372 tty_puts(tty
, "\033[>q");
373 tty_puts(tty
, "\033]10;?\033\\");
374 tty_puts(tty
, "\033]11;?\033\\");
376 tty
->flags
|= TTY_ALL_REQUEST_FLAGS
;
377 tty
->last_requests
= time (NULL
);
381 tty_repeat_requests(struct tty
*tty
)
383 time_t t
= time (NULL
);
385 if (~tty
->flags
& TTY_STARTED
)
388 if (t
- tty
->last_requests
<= TTY_REQUEST_LIMIT
)
390 tty
->last_requests
= t
;
392 if (tty
->term
->flags
& TERM_VT100LIKE
) {
393 tty_puts(tty
, "\033]10;?\033\\");
394 tty_puts(tty
, "\033]11;?\033\\");
399 tty_stop_tty(struct tty
*tty
)
401 struct client
*c
= tty
->client
;
404 if (!(tty
->flags
& TTY_STARTED
))
406 tty
->flags
&= ~TTY_STARTED
;
408 evtimer_del(&tty
->start_timer
);
410 event_del(&tty
->timer
);
411 tty
->flags
&= ~TTY_BLOCK
;
413 event_del(&tty
->event_in
);
414 event_del(&tty
->event_out
);
417 * Be flexible about error handling and try not kill the server just
418 * because the fd is invalid. Things like ssh -t can easily leave us
421 if (ioctl(c
->fd
, TIOCGWINSZ
, &ws
) == -1)
423 if (tcsetattr(c
->fd
, TCSANOW
, &tty
->tio
) == -1)
426 tty_raw(tty
, tty_term_string_ii(tty
->term
, TTYC_CSR
, 0, ws
.ws_row
- 1));
427 if (tty_acs_needed(tty
))
428 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMACS
));
429 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_SGR0
));
430 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMKX
));
431 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CLEAR
));
432 if (tty
->cstyle
!= SCREEN_CURSOR_DEFAULT
) {
433 if (tty_term_has(tty
->term
, TTYC_SE
))
434 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_SE
));
435 else if (tty_term_has(tty
->term
, TTYC_SS
))
436 tty_raw(tty
, tty_term_string_i(tty
->term
, TTYC_SS
, 0));
438 if (tty
->ccolour
!= -1)
439 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CR
));
441 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_CNORM
));
442 if (tty_term_has(tty
->term
, TTYC_KMOUS
)) {
443 tty_raw(tty
, "\033[?1000l\033[?1002l\033[?1003l");
444 tty_raw(tty
, "\033[?1006l\033[?1005l");
446 if (tty_term_has(tty
->term
, TTYC_DSBP
))
447 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSBP
));
449 if (tty
->term
->flags
& TERM_VT100LIKE
)
450 tty_raw(tty
, "\033[?7727l");
451 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSFCS
));
452 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSEKS
));
454 if (tty_use_margin(tty
))
455 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_DSMG
));
456 tty_raw(tty
, tty_term_string(tty
->term
, TTYC_RMCUP
));
458 setblocking(c
->fd
, 1);
462 tty_close(struct tty
*tty
)
464 if (event_initialized(&tty
->key_timer
))
465 evtimer_del(&tty
->key_timer
);
468 if (tty
->flags
& TTY_OPENED
) {
469 evbuffer_free(tty
->in
);
470 event_del(&tty
->event_in
);
471 evbuffer_free(tty
->out
);
472 event_del(&tty
->event_out
);
474 tty_term_free(tty
->term
);
477 tty
->flags
&= ~TTY_OPENED
;
482 tty_free(struct tty
*tty
)
488 tty_update_features(struct tty
*tty
)
490 struct client
*c
= tty
->client
;
492 if (tty_apply_features(tty
->term
, c
->term_features
))
493 tty_term_apply_overrides(tty
->term
);
495 if (tty_use_margin(tty
))
496 tty_putcode(tty
, TTYC_ENMG
);
497 if (options_get_number(global_options
, "extended-keys"))
498 tty_puts(tty
, tty_term_string(tty
->term
, TTYC_ENEKS
));
499 if (options_get_number(global_options
, "focus-events"))
500 tty_puts(tty
, tty_term_string(tty
->term
, TTYC_ENFCS
));
501 if (tty
->term
->flags
& TERM_VT100LIKE
)
502 tty_puts(tty
, "\033[?7727h");
505 * Features might have changed since the first draw during attach. For
506 * example, this happens when DA responses are received.
508 server_redraw_client(c
);
514 tty_raw(struct tty
*tty
, const char *s
)
516 struct client
*c
= tty
->client
;
521 for (i
= 0; i
< 5; i
++) {
522 n
= write(c
->fd
, s
, slen
);
528 } else if (n
== -1 && errno
!= EAGAIN
)
535 tty_putcode(struct tty
*tty
, enum tty_code_code code
)
537 tty_puts(tty
, tty_term_string(tty
->term
, code
));
541 tty_putcode_i(struct tty
*tty
, enum tty_code_code code
, int a
)
545 tty_puts(tty
, tty_term_string_i(tty
->term
, code
, a
));
549 tty_putcode_ii(struct tty
*tty
, enum tty_code_code code
, int a
, int b
)
553 tty_puts(tty
, tty_term_string_ii(tty
->term
, code
, a
, b
));
557 tty_putcode_iii(struct tty
*tty
, enum tty_code_code code
, int a
, int b
, int c
)
559 if (a
< 0 || b
< 0 || c
< 0)
561 tty_puts(tty
, tty_term_string_iii(tty
->term
, code
, a
, b
, c
));
565 tty_putcode_s(struct tty
*tty
, enum tty_code_code code
, const char *a
)
568 tty_puts(tty
, tty_term_string_s(tty
->term
, code
, a
));
572 tty_putcode_ss(struct tty
*tty
, enum tty_code_code code
, const char *a
,
575 if (a
!= NULL
&& b
!= NULL
)
576 tty_puts(tty
, tty_term_string_ss(tty
->term
, code
, a
, b
));
580 tty_add(struct tty
*tty
, const char *buf
, size_t len
)
582 struct client
*c
= tty
->client
;
584 if (tty
->flags
& TTY_BLOCK
) {
585 tty
->discarded
+= len
;
589 evbuffer_add(tty
->out
, buf
, len
);
590 log_debug("%s: %.*s", c
->name
, (int)len
, buf
);
593 if (tty_log_fd
!= -1)
594 write(tty_log_fd
, buf
, len
);
595 if (tty
->flags
& TTY_STARTED
)
596 event_add(&tty
->event_out
, NULL
);
600 tty_puts(struct tty
*tty
, const char *s
)
603 tty_add(tty
, s
, strlen(s
));
607 tty_putc(struct tty
*tty
, u_char ch
)
611 if ((tty
->term
->flags
& TERM_NOAM
) &&
612 ch
>= 0x20 && ch
!= 0x7f &&
613 tty
->cy
== tty
->sy
- 1 &&
614 tty
->cx
+ 1 >= tty
->sx
)
617 if (tty
->cell
.attr
& GRID_ATTR_CHARSET
) {
618 acs
= tty_acs_get(tty
, ch
);
620 tty_add(tty
, acs
, strlen(acs
));
622 tty_add(tty
, &ch
, 1);
624 tty_add(tty
, &ch
, 1);
626 if (ch
>= 0x20 && ch
!= 0x7f) {
627 if (tty
->cx
>= tty
->sx
) {
629 if (tty
->cy
!= tty
->rlower
)
633 * On !am terminals, force the cursor position to where
634 * we think it should be after a line wrap - this means
635 * it works on sensible terminals as well.
637 if (tty
->term
->flags
& TERM_NOAM
)
638 tty_putcode_ii(tty
, TTYC_CUP
, tty
->cy
, tty
->cx
);
645 tty_putn(struct tty
*tty
, const void *buf
, size_t len
, u_int width
)
647 if ((tty
->term
->flags
& TERM_NOAM
) &&
648 tty
->cy
== tty
->sy
- 1 &&
649 tty
->cx
+ len
>= tty
->sx
)
650 len
= tty
->sx
- tty
->cx
- 1;
652 tty_add(tty
, buf
, len
);
653 if (tty
->cx
+ width
> tty
->sx
) {
654 tty
->cx
= (tty
->cx
+ width
) - tty
->sx
;
655 if (tty
->cx
<= tty
->sx
)
658 tty
->cx
= tty
->cy
= UINT_MAX
;
664 tty_set_italics(struct tty
*tty
)
668 if (tty_term_has(tty
->term
, TTYC_SITM
)) {
669 s
= options_get_string(global_options
, "default-terminal");
670 if (strcmp(s
, "screen") != 0 && strncmp(s
, "screen-", 7) != 0) {
671 tty_putcode(tty
, TTYC_SITM
);
675 tty_putcode(tty
, TTYC_SMSO
);
679 tty_set_title(struct tty
*tty
, const char *title
)
681 if (!tty_term_has(tty
->term
, TTYC_TSL
) ||
682 !tty_term_has(tty
->term
, TTYC_FSL
))
685 tty_putcode(tty
, TTYC_TSL
);
686 tty_puts(tty
, title
);
687 tty_putcode(tty
, TTYC_FSL
);
691 tty_set_path(struct tty
*tty
, const char *title
)
693 if (!tty_term_has(tty
->term
, TTYC_SWD
) ||
694 !tty_term_has(tty
->term
, TTYC_FSL
))
697 tty_putcode(tty
, TTYC_SWD
);
698 tty_puts(tty
, title
);
699 tty_putcode(tty
, TTYC_FSL
);
703 tty_force_cursor_colour(struct tty
*tty
, int c
)
709 c
= colour_force_rgb(c
);
710 if (c
== tty
->ccolour
)
713 tty_putcode(tty
, TTYC_CR
);
715 colour_split_rgb(c
, &r
, &g
, &b
);
716 xsnprintf(s
, sizeof s
, "rgb:%02hhx/%02hhx/%02hhx", r
, g
, b
);
717 tty_putcode_s(tty
, TTYC_CS
, s
);
723 tty_update_cursor(struct tty
*tty
, int mode
, struct screen
*s
)
725 enum screen_cursor_style cstyle
;
726 int ccolour
, changed
, cmode
= mode
;
728 /* Set cursor colour if changed. */
730 ccolour
= s
->ccolour
;
731 if (s
->ccolour
== -1)
732 ccolour
= s
->default_ccolour
;
733 tty_force_cursor_colour(tty
, ccolour
);
736 /* If cursor is off, set as invisible. */
737 if (~cmode
& MODE_CURSOR
) {
738 if (tty
->mode
& MODE_CURSOR
)
739 tty_putcode(tty
, TTYC_CIVIS
);
743 /* Check if blinking or very visible flag changed or style changed. */
745 cstyle
= tty
->cstyle
;
748 if (cstyle
== SCREEN_CURSOR_DEFAULT
) {
749 if (~cmode
& MODE_CURSOR_BLINKING_SET
) {
750 if (s
->default_mode
& MODE_CURSOR_BLINKING
)
751 cmode
|= MODE_CURSOR_BLINKING
;
753 cmode
&= ~MODE_CURSOR_BLINKING
;
755 cstyle
= s
->default_cstyle
;
759 /* If nothing changed, do nothing. */
760 changed
= cmode
^ tty
->mode
;
761 if ((changed
& CURSOR_MODES
) == 0 && cstyle
== tty
->cstyle
)
765 * Set cursor style. If an explicit style has been set with DECSCUSR,
766 * set it if supported, otherwise send cvvis for blinking styles.
768 * If no style, has been set (SCREEN_CURSOR_DEFAULT), then send cvvis
769 * if either the blinking or very visible flags are set.
771 tty_putcode(tty
, TTYC_CNORM
);
773 case SCREEN_CURSOR_DEFAULT
:
774 if (tty
->cstyle
!= SCREEN_CURSOR_DEFAULT
) {
775 if (tty_term_has(tty
->term
, TTYC_SE
))
776 tty_putcode(tty
, TTYC_SE
);
778 tty_putcode_i(tty
, TTYC_SS
, 0);
780 if (cmode
& (MODE_CURSOR_BLINKING
|MODE_CURSOR_VERY_VISIBLE
))
781 tty_putcode(tty
, TTYC_CVVIS
);
783 case SCREEN_CURSOR_BLOCK
:
784 if (tty_term_has(tty
->term
, TTYC_SS
)) {
785 if (cmode
& MODE_CURSOR_BLINKING
)
786 tty_putcode_i(tty
, TTYC_SS
, 1);
788 tty_putcode_i(tty
, TTYC_SS
, 2);
789 } else if (cmode
& MODE_CURSOR_BLINKING
)
790 tty_putcode(tty
, TTYC_CVVIS
);
792 case SCREEN_CURSOR_UNDERLINE
:
793 if (tty_term_has(tty
->term
, TTYC_SS
)) {
794 if (cmode
& MODE_CURSOR_BLINKING
)
795 tty_putcode_i(tty
, TTYC_SS
, 3);
797 tty_putcode_i(tty
, TTYC_SS
, 4);
798 } else if (cmode
& MODE_CURSOR_BLINKING
)
799 tty_putcode(tty
, TTYC_CVVIS
);
801 case SCREEN_CURSOR_BAR
:
802 if (tty_term_has(tty
->term
, TTYC_SS
)) {
803 if (cmode
& MODE_CURSOR_BLINKING
)
804 tty_putcode_i(tty
, TTYC_SS
, 5);
806 tty_putcode_i(tty
, TTYC_SS
, 6);
807 } else if (cmode
& MODE_CURSOR_BLINKING
)
808 tty_putcode(tty
, TTYC_CVVIS
);
811 tty
->cstyle
= cstyle
;
816 tty_update_mode(struct tty
*tty
, int mode
, struct screen
*s
)
818 struct tty_term
*term
= tty
->term
;
819 struct client
*c
= tty
->client
;
822 if (tty
->flags
& TTY_NOCURSOR
)
823 mode
&= ~MODE_CURSOR
;
825 if (tty_update_cursor(tty
, mode
, s
) & MODE_CURSOR_BLINKING
)
826 mode
|= MODE_CURSOR_BLINKING
;
828 mode
&= ~MODE_CURSOR_BLINKING
;
830 changed
= mode
^ tty
->mode
;
831 if (log_get_level() != 0 && changed
!= 0) {
832 log_debug("%s: current mode %s", c
->name
,
833 screen_mode_to_string(tty
->mode
));
834 log_debug("%s: setting mode %s", c
->name
,
835 screen_mode_to_string(mode
));
838 if ((changed
& ALL_MOUSE_MODES
) && tty_term_has(term
, TTYC_KMOUS
)) {
840 * If the mouse modes have changed, clear then all and apply
841 * again. There are differences in how terminals track the
844 tty_puts(tty
, "\033[?1006l\033[?1000l\033[?1002l\033[?1003l");
845 if (mode
& ALL_MOUSE_MODES
)
846 tty_puts(tty
, "\033[?1006h");
847 if (mode
& MODE_MOUSE_ALL
)
848 tty_puts(tty
, "\033[?1000h\033[?1002h\033[?1003h");
849 else if (mode
& MODE_MOUSE_BUTTON
)
850 tty_puts(tty
, "\033[?1000h\033[?1002h");
851 else if (mode
& MODE_MOUSE_STANDARD
)
852 tty_puts(tty
, "\033[?1000h");
858 tty_emulate_repeat(struct tty
*tty
, enum tty_code_code code
,
859 enum tty_code_code code1
, u_int n
)
861 if (tty_term_has(tty
->term
, code
))
862 tty_putcode_i(tty
, code
, n
);
865 tty_putcode(tty
, code1
);
870 tty_repeat_space(struct tty
*tty
, u_int n
)
875 memset(s
, ' ', sizeof s
);
877 while (n
> sizeof s
) {
878 tty_putn(tty
, s
, sizeof s
, sizeof s
);
882 tty_putn(tty
, s
, n
, n
);
885 /* Is this window bigger than the terminal? */
887 tty_window_bigger(struct tty
*tty
)
889 struct client
*c
= tty
->client
;
890 struct window
*w
= c
->session
->curw
->window
;
892 return (tty
->sx
< w
->sx
|| tty
->sy
- status_line_size(c
) < w
->sy
);
895 /* What offset should this window be drawn at? */
897 tty_window_offset(struct tty
*tty
, u_int
*ox
, u_int
*oy
, u_int
*sx
, u_int
*sy
)
907 /* What offset should this window be drawn at? */
909 tty_window_offset1(struct tty
*tty
, u_int
*ox
, u_int
*oy
, u_int
*sx
, u_int
*sy
)
911 struct client
*c
= tty
->client
;
912 struct window
*w
= c
->session
->curw
->window
;
913 struct window_pane
*wp
= server_client_get_pane(c
);
916 lines
= status_line_size(c
);
918 if (tty
->sx
>= w
->sx
&& tty
->sy
- lines
>= w
->sy
) {
924 c
->pan_window
= NULL
;
929 *sy
= tty
->sy
- lines
;
931 if (c
->pan_window
== w
) {
934 else if (c
->pan_ox
+ *sx
> w
->sx
)
935 c
->pan_ox
= w
->sx
- *sx
;
939 else if (c
->pan_oy
+ *sy
> w
->sy
)
940 c
->pan_oy
= w
->sy
- *sy
;
945 if (~wp
->screen
->mode
& MODE_CURSOR
) {
949 cx
= wp
->xoff
+ wp
->screen
->cx
;
950 cy
= wp
->yoff
+ wp
->screen
->cy
;
954 else if (cx
> w
->sx
- *sx
)
961 else if (cy
> w
->sy
- *sy
)
967 c
->pan_window
= NULL
;
971 /* Update stored offsets for a window and redraw if necessary. */
973 tty_update_window_offset(struct window
*w
)
977 TAILQ_FOREACH(c
, &clients
, entry
) {
978 if (c
->session
!= NULL
&&
979 c
->session
->curw
!= NULL
&&
980 c
->session
->curw
->window
== w
)
981 tty_update_client_offset(c
);
985 /* Update stored offsets for a client and redraw if necessary. */
987 tty_update_client_offset(struct client
*c
)
989 u_int ox
, oy
, sx
, sy
;
991 if (~c
->flags
& CLIENT_TERMINAL
)
994 c
->tty
.oflag
= tty_window_offset1(&c
->tty
, &ox
, &oy
, &sx
, &sy
);
995 if (ox
== c
->tty
.oox
&&
1001 log_debug ("%s: %s offset has changed (%u,%u %ux%u -> %u,%u %ux%u)",
1002 __func__
, c
->name
, c
->tty
.oox
, c
->tty
.ooy
, c
->tty
.osx
, c
->tty
.osy
,
1010 c
->flags
|= (CLIENT_REDRAWWINDOW
|CLIENT_REDRAWSTATUS
);
1014 * Is the region large enough to be worth redrawing once later rather than
1015 * probably several times now? Currently yes if it is more than 50% of the
1019 tty_large_region(__unused
struct tty
*tty
, const struct tty_ctx
*ctx
)
1021 return (ctx
->orlower
- ctx
->orupper
>= ctx
->sy
/ 2);
1025 * Return if BCE is needed but the terminal doesn't have it - it'll need to be
1029 tty_fake_bce(const struct tty
*tty
, const struct grid_cell
*gc
, u_int bg
)
1031 if (tty_term_flag(tty
->term
, TTYC_BCE
))
1033 if (!COLOUR_DEFAULT(bg
) || !COLOUR_DEFAULT(gc
->bg
))
1039 * Redraw scroll region using data from screen (already updated). Used when
1040 * CSR not supported, or window is a pane that doesn't take up the full
1041 * width of the terminal.
1044 tty_redraw_region(struct tty
*tty
, const struct tty_ctx
*ctx
)
1046 struct client
*c
= tty
->client
;
1050 * If region is large, schedule a redraw. In most cases this is likely
1051 * to be followed by some more scrolling.
1053 if (tty_large_region(tty
, ctx
)) {
1054 log_debug("%s: %s large redraw", __func__
, c
->name
);
1055 ctx
->redraw_cb(ctx
);
1059 for (i
= ctx
->orupper
; i
<= ctx
->orlower
; i
++)
1060 tty_draw_pane(tty
, ctx
, i
);
1063 /* Is this position visible in the pane? */
1065 tty_is_visible(__unused
struct tty
*tty
, const struct tty_ctx
*ctx
, u_int px
,
1066 u_int py
, u_int nx
, u_int ny
)
1068 u_int xoff
= ctx
->rxoff
+ px
, yoff
= ctx
->ryoff
+ py
;
1073 if (xoff
+ nx
<= ctx
->wox
|| xoff
>= ctx
->wox
+ ctx
->wsx
||
1074 yoff
+ ny
<= ctx
->woy
|| yoff
>= ctx
->woy
+ ctx
->wsy
)
1079 /* Clamp line position to visible part of pane. */
1081 tty_clamp_line(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int px
, u_int py
,
1082 u_int nx
, u_int
*i
, u_int
*x
, u_int
*rx
, u_int
*ry
)
1084 u_int xoff
= ctx
->rxoff
+ px
;
1086 if (!tty_is_visible(tty
, ctx
, px
, py
, nx
, 1))
1088 *ry
= ctx
->yoff
+ py
- ctx
->woy
;
1090 if (xoff
>= ctx
->wox
&& xoff
+ nx
<= ctx
->wox
+ ctx
->wsx
) {
1093 *x
= ctx
->xoff
+ px
- ctx
->wox
;
1095 } else if (xoff
< ctx
->wox
&& xoff
+ nx
> ctx
->wox
+ ctx
->wsx
) {
1096 /* Both left and right not visible. */
1100 } else if (xoff
< ctx
->wox
) {
1101 /* Left not visible. */
1102 *i
= ctx
->wox
- (ctx
->xoff
+ px
);
1106 /* Right not visible. */
1108 *x
= (ctx
->xoff
+ px
) - ctx
->wox
;
1109 *rx
= ctx
->wsx
- *x
;
1112 fatalx("%s: x too big, %u > %u", __func__
, *rx
, nx
);
1119 tty_clear_line(struct tty
*tty
, const struct grid_cell
*defaults
, u_int py
,
1120 u_int px
, u_int nx
, u_int bg
)
1122 struct client
*c
= tty
->client
;
1123 struct overlay_ranges r
;
1126 log_debug("%s: %s, %u at %u,%u", __func__
, c
->name
, nx
, px
, py
);
1128 /* Nothing to clear. */
1132 /* If genuine BCE is available, can try escape sequences. */
1133 if (c
->overlay_check
== NULL
&& !tty_fake_bce(tty
, defaults
, bg
)) {
1134 /* Off the end of the line, use EL if available. */
1135 if (px
+ nx
>= tty
->sx
&& tty_term_has(tty
->term
, TTYC_EL
)) {
1136 tty_cursor(tty
, px
, py
);
1137 tty_putcode(tty
, TTYC_EL
);
1141 /* At the start of the line. Use EL1. */
1142 if (px
== 0 && tty_term_has(tty
->term
, TTYC_EL1
)) {
1143 tty_cursor(tty
, px
+ nx
- 1, py
);
1144 tty_putcode(tty
, TTYC_EL1
);
1148 /* Section of line. Use ECH if possible. */
1149 if (tty_term_has(tty
->term
, TTYC_ECH
)) {
1150 tty_cursor(tty
, px
, py
);
1151 tty_putcode_i(tty
, TTYC_ECH
, nx
);
1157 * Couldn't use an escape sequence, use spaces. Clear only the visible
1158 * bit if there is an overlay.
1160 tty_check_overlay_range(tty
, px
, py
, nx
, &r
);
1161 for (i
= 0; i
< OVERLAY_MAX_RANGES
; i
++) {
1164 tty_cursor(tty
, r
.px
[i
], py
);
1165 tty_repeat_space(tty
, r
.nx
[i
]);
1169 /* Clear a line, adjusting to visible part of pane. */
1171 tty_clear_pane_line(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int py
,
1172 u_int px
, u_int nx
, u_int bg
)
1174 struct client
*c
= tty
->client
;
1177 log_debug("%s: %s, %u at %u,%u", __func__
, c
->name
, nx
, px
, py
);
1179 if (tty_clamp_line(tty
, ctx
, px
, py
, nx
, &i
, &x
, &rx
, &ry
))
1180 tty_clear_line(tty
, &ctx
->defaults
, ry
, x
, rx
, bg
);
1183 /* Clamp area position to visible part of pane. */
1185 tty_clamp_area(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int px
, u_int py
,
1186 u_int nx
, u_int ny
, u_int
*i
, u_int
*j
, u_int
*x
, u_int
*y
, u_int
*rx
,
1189 u_int xoff
= ctx
->rxoff
+ px
, yoff
= ctx
->ryoff
+ py
;
1191 if (!tty_is_visible(tty
, ctx
, px
, py
, nx
, ny
))
1194 if (xoff
>= ctx
->wox
&& xoff
+ nx
<= ctx
->wox
+ ctx
->wsx
) {
1197 *x
= ctx
->xoff
+ px
- ctx
->wox
;
1199 } else if (xoff
< ctx
->wox
&& xoff
+ nx
> ctx
->wox
+ ctx
->wsx
) {
1200 /* Both left and right not visible. */
1204 } else if (xoff
< ctx
->wox
) {
1205 /* Left not visible. */
1206 *i
= ctx
->wox
- (ctx
->xoff
+ px
);
1210 /* Right not visible. */
1212 *x
= (ctx
->xoff
+ px
) - ctx
->wox
;
1213 *rx
= ctx
->wsx
- *x
;
1216 fatalx("%s: x too big, %u > %u", __func__
, *rx
, nx
);
1218 if (yoff
>= ctx
->woy
&& yoff
+ ny
<= ctx
->woy
+ ctx
->wsy
) {
1221 *y
= ctx
->yoff
+ py
- ctx
->woy
;
1223 } else if (yoff
< ctx
->woy
&& yoff
+ ny
> ctx
->woy
+ ctx
->wsy
) {
1224 /* Both top and bottom not visible. */
1228 } else if (yoff
< ctx
->woy
) {
1229 /* Top not visible. */
1230 *j
= ctx
->woy
- (ctx
->yoff
+ py
);
1234 /* Bottom not visible. */
1236 *y
= (ctx
->yoff
+ py
) - ctx
->woy
;
1237 *ry
= ctx
->wsy
- *y
;
1240 fatalx("%s: y too big, %u > %u", __func__
, *ry
, ny
);
1245 /* Clear an area, adjusting to visible part of pane. */
1247 tty_clear_area(struct tty
*tty
, const struct grid_cell
*defaults
, u_int py
,
1248 u_int ny
, u_int px
, u_int nx
, u_int bg
)
1250 struct client
*c
= tty
->client
;
1254 log_debug("%s: %s, %u,%u at %u,%u", __func__
, c
->name
, nx
, ny
, px
, py
);
1256 /* Nothing to clear. */
1257 if (nx
== 0 || ny
== 0)
1260 /* If genuine BCE is available, can try escape sequences. */
1261 if (c
->overlay_check
== NULL
&& !tty_fake_bce(tty
, defaults
, bg
)) {
1262 /* Use ED if clearing off the bottom of the terminal. */
1264 px
+ nx
>= tty
->sx
&&
1265 py
+ ny
>= tty
->sy
&&
1266 tty_term_has(tty
->term
, TTYC_ED
)) {
1267 tty_cursor(tty
, 0, py
);
1268 tty_putcode(tty
, TTYC_ED
);
1273 * On VT420 compatible terminals we can use DECFRA if the
1274 * background colour isn't default (because it doesn't work
1277 if ((tty
->term
->flags
& TERM_DECFRA
) && !COLOUR_DEFAULT(bg
)) {
1278 xsnprintf(tmp
, sizeof tmp
, "\033[32;%u;%u;%u;%u$x",
1279 py
+ 1, px
+ 1, py
+ ny
, px
+ nx
);
1284 /* Full lines can be scrolled away to clear them. */
1286 px
+ nx
>= tty
->sx
&&
1288 tty_term_has(tty
->term
, TTYC_CSR
) &&
1289 tty_term_has(tty
->term
, TTYC_INDN
)) {
1290 tty_region(tty
, py
, py
+ ny
- 1);
1291 tty_margin_off(tty
);
1292 tty_putcode_i(tty
, TTYC_INDN
, ny
);
1297 * If margins are supported, can just scroll the area off to
1302 tty_term_has(tty
->term
, TTYC_CSR
) &&
1303 tty_use_margin(tty
) &&
1304 tty_term_has(tty
->term
, TTYC_INDN
)) {
1305 tty_region(tty
, py
, py
+ ny
- 1);
1306 tty_margin(tty
, px
, px
+ nx
- 1);
1307 tty_putcode_i(tty
, TTYC_INDN
, ny
);
1312 /* Couldn't use an escape sequence, loop over the lines. */
1313 for (yy
= py
; yy
< py
+ ny
; yy
++)
1314 tty_clear_line(tty
, defaults
, yy
, px
, nx
, bg
);
1317 /* Clear an area in a pane. */
1319 tty_clear_pane_area(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int py
,
1320 u_int ny
, u_int px
, u_int nx
, u_int bg
)
1322 u_int i
, j
, x
, y
, rx
, ry
;
1324 if (tty_clamp_area(tty
, ctx
, px
, py
, nx
, ny
, &i
, &j
, &x
, &y
, &rx
, &ry
))
1325 tty_clear_area(tty
, &ctx
->defaults
, y
, ry
, x
, rx
, bg
);
1329 tty_draw_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int py
)
1331 struct screen
*s
= ctx
->s
;
1332 u_int nx
= ctx
->sx
, i
, x
, rx
, ry
;
1334 log_debug("%s: %s %u %d", __func__
, tty
->client
->name
, py
, ctx
->bigger
);
1337 tty_draw_line(tty
, s
, 0, py
, nx
, ctx
->xoff
, ctx
->yoff
+ py
,
1338 &ctx
->defaults
, ctx
->palette
);
1341 if (tty_clamp_line(tty
, ctx
, 0, py
, nx
, &i
, &x
, &rx
, &ry
)) {
1342 tty_draw_line(tty
, s
, i
, py
, rx
, x
, ry
, &ctx
->defaults
,
1347 static const struct grid_cell
*
1348 tty_check_codeset(struct tty
*tty
, const struct grid_cell
*gc
)
1350 static struct grid_cell
new;
1353 /* Characters less than 0x7f are always fine, no matter what. */
1354 if (gc
->data
.size
== 1 && *gc
->data
.data
< 0x7f)
1357 /* UTF-8 terminal and a UTF-8 character - fine. */
1358 if (tty
->client
->flags
& CLIENT_UTF8
)
1360 memcpy(&new, gc
, sizeof new);
1362 /* See if this can be mapped to an ACS character. */
1363 c
= tty_acs_reverse_get(tty
, gc
->data
.data
, gc
->data
.size
);
1365 utf8_set(&new.data
, c
);
1366 new.attr
|= GRID_ATTR_CHARSET
;
1370 /* Replace by the right number of underscores. */
1371 new.data
.size
= gc
->data
.width
;
1372 if (new.data
.size
> UTF8_SIZE
)
1373 new.data
.size
= UTF8_SIZE
;
1374 memset(new.data
.data
, '_', new.data
.size
);
1379 * Check if a single character is obstructed by the overlay and return a
1383 tty_check_overlay(struct tty
*tty
, u_int px
, u_int py
)
1385 struct overlay_ranges r
;
1388 * A unit width range will always return nx[2] == 0 from a check, even
1389 * with multiple overlays, so it's sufficient to check just the first
1392 tty_check_overlay_range(tty
, px
, py
, 1, &r
);
1393 if (r
.nx
[0] + r
.nx
[1] == 0)
1398 /* Return parts of the input range which are visible. */
1400 tty_check_overlay_range(struct tty
*tty
, u_int px
, u_int py
, u_int nx
,
1401 struct overlay_ranges
*r
)
1403 struct client
*c
= tty
->client
;
1405 if (c
->overlay_check
== NULL
) {
1415 c
->overlay_check(c
, c
->overlay_data
, px
, py
, nx
, r
);
1419 tty_draw_line(struct tty
*tty
, struct screen
*s
, u_int px
, u_int py
, u_int nx
,
1420 u_int atx
, u_int aty
, const struct grid_cell
*defaults
,
1421 struct colour_palette
*palette
)
1423 struct grid
*gd
= s
->grid
;
1424 struct grid_cell gc
, last
;
1425 const struct grid_cell
*gcp
;
1426 struct grid_line
*gl
;
1427 struct client
*c
= tty
->client
;
1428 struct overlay_ranges r
;
1429 u_int i
, j
, ux
, sx
, width
, hidden
, eux
, nxx
;
1431 int flags
, cleared
= 0, wrapped
= 0;
1435 log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__
,
1436 px
, py
, nx
, atx
, aty
);
1437 log_debug("%s: defaults: fg=%d, bg=%d", __func__
, defaults
->fg
,
1441 * py is the line in the screen to draw.
1442 * px is the start x and nx is the width to draw.
1443 * atx,aty is the line on the terminal to draw it.
1446 flags
= (tty
->flags
& TTY_NOCURSOR
);
1447 tty
->flags
|= TTY_NOCURSOR
;
1448 tty_update_mode(tty
, tty
->mode
, s
);
1450 tty_region_off(tty
);
1451 tty_margin_off(tty
);
1454 * Clamp the width to cellsize - note this is not cellused, because
1455 * there may be empty background cells after it (from BCE).
1457 sx
= screen_size_x(s
);
1460 cellsize
= grid_get_line(gd
, gd
->hsize
+ py
)->cellsize
;
1472 gl
= grid_get_line(gd
, gd
->hsize
+ py
- 1);
1474 (~gl
->flags
& GRID_LINE_WRAPPED
) ||
1476 tty
->cx
< tty
->sx
||
1481 tty_term_has(tty
->term
, TTYC_EL1
) &&
1482 !tty_fake_bce(tty
, defaults
, 8) &&
1483 c
->overlay_check
== NULL
) {
1484 tty_default_attributes(tty
, defaults
, palette
, 8,
1486 tty_cursor(tty
, nx
- 1, aty
);
1487 tty_putcode(tty
, TTYC_EL1
);
1491 log_debug("%s: wrapped line %u", __func__
, aty
);
1495 memcpy(&last
, &grid_default_cell
, sizeof last
);
1499 for (i
= 0; i
< sx
; i
++) {
1500 grid_view_get_cell(gd
, px
+ i
, py
, &gc
);
1501 gcp
= tty_check_codeset(tty
, &gc
);
1503 (!tty_check_overlay(tty
, atx
+ ux
+ width
, aty
) ||
1504 (gcp
->attr
& GRID_ATTR_CHARSET
) ||
1505 gcp
->flags
!= last
.flags
||
1506 gcp
->attr
!= last
.attr
||
1507 gcp
->fg
!= last
.fg
||
1508 gcp
->bg
!= last
.bg
||
1509 gcp
->us
!= last
.us
||
1510 gcp
->link
!= last
.link
||
1511 ux
+ width
+ gcp
->data
.width
> nx
||
1512 (sizeof buf
) - len
< gcp
->data
.size
)) {
1513 tty_attributes(tty
, &last
, defaults
, palette
,
1515 if (last
.flags
& GRID_FLAG_CLEARED
) {
1516 log_debug("%s: %zu cleared", __func__
, len
);
1517 tty_clear_line(tty
, defaults
, aty
, atx
+ ux
,
1520 if (!wrapped
|| atx
!= 0 || ux
!= 0)
1521 tty_cursor(tty
, atx
+ ux
, aty
);
1522 tty_putn(tty
, buf
, len
, width
);
1531 if (gcp
->flags
& GRID_FLAG_SELECTED
)
1532 screen_select_cell(s
, &last
, gcp
);
1534 memcpy(&last
, gcp
, sizeof last
);
1536 tty_check_overlay_range(tty
, atx
+ ux
, aty
, gcp
->data
.width
,
1539 for (j
= 0; j
< OVERLAY_MAX_RANGES
; j
++)
1541 hidden
= gcp
->data
.width
- hidden
;
1542 if (hidden
!= 0 && hidden
== gcp
->data
.width
) {
1543 if (~gcp
->flags
& GRID_FLAG_PADDING
)
1544 ux
+= gcp
->data
.width
;
1545 } else if (hidden
!= 0 || ux
+ gcp
->data
.width
> nx
) {
1546 if (~gcp
->flags
& GRID_FLAG_PADDING
) {
1547 tty_attributes(tty
, &last
, defaults
, palette
,
1549 for (j
= 0; j
< OVERLAY_MAX_RANGES
; j
++) {
1552 /* Effective width drawn so far. */
1553 eux
= r
.px
[j
] - atx
;
1555 tty_cursor(tty
, r
.px
[j
], aty
);
1559 tty_repeat_space(tty
, r
.nx
[j
]);
1564 } else if (gcp
->attr
& GRID_ATTR_CHARSET
) {
1565 tty_attributes(tty
, &last
, defaults
, palette
,
1567 tty_cursor(tty
, atx
+ ux
, aty
);
1568 for (j
= 0; j
< gcp
->data
.size
; j
++)
1569 tty_putc(tty
, gcp
->data
.data
[j
]);
1570 ux
+= gcp
->data
.width
;
1571 } else if (~gcp
->flags
& GRID_FLAG_PADDING
) {
1572 memcpy(buf
+ len
, gcp
->data
.data
, gcp
->data
.size
);
1573 len
+= gcp
->data
.size
;
1574 width
+= gcp
->data
.width
;
1577 if (len
!= 0 && ((~last
.flags
& GRID_FLAG_CLEARED
) || last
.bg
!= 8)) {
1578 tty_attributes(tty
, &last
, defaults
, palette
, s
->hyperlinks
);
1579 if (last
.flags
& GRID_FLAG_CLEARED
) {
1580 log_debug("%s: %zu cleared (end)", __func__
, len
);
1581 tty_clear_line(tty
, defaults
, aty
, atx
+ ux
, width
,
1584 if (!wrapped
|| atx
!= 0 || ux
!= 0)
1585 tty_cursor(tty
, atx
+ ux
, aty
);
1586 tty_putn(tty
, buf
, len
, width
);
1591 if (!cleared
&& ux
< nx
) {
1592 log_debug("%s: %u to end of line (%zu cleared)", __func__
,
1594 tty_default_attributes(tty
, defaults
, palette
, 8,
1596 tty_clear_line(tty
, defaults
, aty
, atx
+ ux
, nx
- ux
, 8);
1599 tty
->flags
= (tty
->flags
& ~TTY_NOCURSOR
) | flags
;
1600 tty_update_mode(tty
, tty
->mode
, s
);
1604 tty_sync_start(struct tty
*tty
)
1606 if (tty
->flags
& TTY_BLOCK
)
1608 if (tty
->flags
& TTY_SYNCING
)
1610 tty
->flags
|= TTY_SYNCING
;
1612 if (tty_term_has(tty
->term
, TTYC_SYNC
)) {
1613 log_debug("%s sync start", tty
->client
->name
);
1614 tty_putcode_i(tty
, TTYC_SYNC
, 1);
1619 tty_sync_end(struct tty
*tty
)
1621 if (tty
->flags
& TTY_BLOCK
)
1623 if (~tty
->flags
& TTY_SYNCING
)
1625 tty
->flags
&= ~TTY_SYNCING
;
1627 if (tty_term_has(tty
->term
, TTYC_SYNC
)) {
1628 log_debug("%s sync end", tty
->client
->name
);
1629 tty_putcode_i(tty
, TTYC_SYNC
, 2);
1634 tty_client_ready(const struct tty_ctx
*ctx
, struct client
*c
)
1636 if (c
->session
== NULL
|| c
->tty
.term
== NULL
)
1638 if (c
->flags
& CLIENT_SUSPENDED
)
1642 * If invisible panes are allowed (used for passthrough), don't care if
1643 * redrawing or frozen.
1645 if (ctx
->allow_invisible_panes
)
1648 if (c
->flags
& CLIENT_REDRAWWINDOW
)
1650 if (c
->tty
.flags
& TTY_FREEZE
)
1656 tty_write(void (*cmdfn
)(struct tty
*, const struct tty_ctx
*),
1657 struct tty_ctx
*ctx
)
1662 if (ctx
->set_client_cb
== NULL
)
1664 TAILQ_FOREACH(c
, &clients
, entry
) {
1665 if (tty_client_ready(ctx
, c
)) {
1666 state
= ctx
->set_client_cb(ctx
, c
);
1671 cmdfn(&c
->tty
, ctx
);
1677 tty_cmd_insertcharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
1679 struct client
*c
= tty
->client
;
1682 !tty_full_width(tty
, ctx
) ||
1683 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1684 (!tty_term_has(tty
->term
, TTYC_ICH
) &&
1685 !tty_term_has(tty
->term
, TTYC_ICH1
)) ||
1686 c
->overlay_check
!= NULL
) {
1687 tty_draw_pane(tty
, ctx
, ctx
->ocy
);
1691 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1692 ctx
->s
->hyperlinks
);
1694 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1696 tty_emulate_repeat(tty
, TTYC_ICH
, TTYC_ICH1
, ctx
->num
);
1700 tty_cmd_deletecharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
1702 struct client
*c
= tty
->client
;
1705 !tty_full_width(tty
, ctx
) ||
1706 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1707 (!tty_term_has(tty
->term
, TTYC_DCH
) &&
1708 !tty_term_has(tty
->term
, TTYC_DCH1
)) ||
1709 c
->overlay_check
!= NULL
) {
1710 tty_draw_pane(tty
, ctx
, ctx
->ocy
);
1714 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1715 ctx
->s
->hyperlinks
);
1717 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1719 tty_emulate_repeat(tty
, TTYC_DCH
, TTYC_DCH1
, ctx
->num
);
1723 tty_cmd_clearcharacter(struct tty
*tty
, const struct tty_ctx
*ctx
)
1725 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1726 ctx
->s
->hyperlinks
);
1728 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, ctx
->ocx
, ctx
->num
, ctx
->bg
);
1732 tty_cmd_insertline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1734 struct client
*c
= tty
->client
;
1737 !tty_full_width(tty
, ctx
) ||
1738 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1739 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1740 !tty_term_has(tty
->term
, TTYC_IL1
) ||
1743 c
->overlay_check
!= NULL
) {
1744 tty_redraw_region(tty
, ctx
);
1748 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1749 ctx
->s
->hyperlinks
);
1751 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1752 tty_margin_off(tty
);
1753 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1755 tty_emulate_repeat(tty
, TTYC_IL
, TTYC_IL1
, ctx
->num
);
1756 tty
->cx
= tty
->cy
= UINT_MAX
;
1760 tty_cmd_deleteline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1762 struct client
*c
= tty
->client
;
1765 !tty_full_width(tty
, ctx
) ||
1766 tty_fake_bce(tty
, &ctx
->defaults
, ctx
->bg
) ||
1767 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1768 !tty_term_has(tty
->term
, TTYC_DL1
) ||
1771 c
->overlay_check
!= NULL
) {
1772 tty_redraw_region(tty
, ctx
);
1776 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1777 ctx
->s
->hyperlinks
);
1779 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1780 tty_margin_off(tty
);
1781 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1783 tty_emulate_repeat(tty
, TTYC_DL
, TTYC_DL1
, ctx
->num
);
1784 tty
->cx
= tty
->cy
= UINT_MAX
;
1788 tty_cmd_clearline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1790 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1791 ctx
->s
->hyperlinks
);
1793 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, 0, ctx
->sx
, ctx
->bg
);
1797 tty_cmd_clearendofline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1799 u_int nx
= ctx
->sx
- ctx
->ocx
;
1801 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1802 ctx
->s
->hyperlinks
);
1804 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, ctx
->ocx
, nx
, ctx
->bg
);
1808 tty_cmd_clearstartofline(struct tty
*tty
, const struct tty_ctx
*ctx
)
1810 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1811 ctx
->s
->hyperlinks
);
1813 tty_clear_pane_line(tty
, ctx
, ctx
->ocy
, 0, ctx
->ocx
+ 1, ctx
->bg
);
1817 tty_cmd_reverseindex(struct tty
*tty
, const struct tty_ctx
*ctx
)
1819 struct client
*c
= tty
->client
;
1821 if (ctx
->ocy
!= ctx
->orupper
)
1825 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1826 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1827 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1828 (!tty_term_has(tty
->term
, TTYC_RI
) &&
1829 !tty_term_has(tty
->term
, TTYC_RIN
)) ||
1832 c
->overlay_check
!= NULL
) {
1833 tty_redraw_region(tty
, ctx
);
1837 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1838 ctx
->s
->hyperlinks
);
1840 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1841 tty_margin_pane(tty
, ctx
);
1842 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->orupper
);
1844 if (tty_term_has(tty
->term
, TTYC_RI
))
1845 tty_putcode(tty
, TTYC_RI
);
1847 tty_putcode_i(tty
, TTYC_RIN
, 1);
1851 tty_cmd_linefeed(struct tty
*tty
, const struct tty_ctx
*ctx
)
1853 struct client
*c
= tty
->client
;
1855 if (ctx
->ocy
!= ctx
->orlower
)
1859 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1860 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1861 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1864 c
->overlay_check
!= NULL
) {
1865 tty_redraw_region(tty
, ctx
);
1869 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1870 ctx
->s
->hyperlinks
);
1872 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1873 tty_margin_pane(tty
, ctx
);
1876 * If we want to wrap a pane while using margins, the cursor needs to
1877 * be exactly on the right of the region. If the cursor is entirely off
1878 * the edge - move it back to the right. Some terminals are funny about
1879 * this and insert extra spaces, so only use the right if margins are
1882 if (ctx
->xoff
+ ctx
->ocx
> tty
->rright
) {
1883 if (!tty_use_margin(tty
))
1884 tty_cursor(tty
, 0, ctx
->yoff
+ ctx
->ocy
);
1886 tty_cursor(tty
, tty
->rright
, ctx
->yoff
+ ctx
->ocy
);
1888 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
1890 tty_putc(tty
, '\n');
1894 tty_cmd_scrollup(struct tty
*tty
, const struct tty_ctx
*ctx
)
1896 struct client
*c
= tty
->client
;
1900 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1901 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1902 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1905 c
->overlay_check
!= NULL
) {
1906 tty_redraw_region(tty
, ctx
);
1910 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1911 ctx
->s
->hyperlinks
);
1913 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1914 tty_margin_pane(tty
, ctx
);
1916 if (ctx
->num
== 1 || !tty_term_has(tty
->term
, TTYC_INDN
)) {
1917 if (!tty_use_margin(tty
))
1918 tty_cursor(tty
, 0, tty
->rlower
);
1920 tty_cursor(tty
, tty
->rright
, tty
->rlower
);
1921 for (i
= 0; i
< ctx
->num
; i
++)
1922 tty_putc(tty
, '\n');
1924 if (tty
->cy
== UINT_MAX
)
1925 tty_cursor(tty
, 0, 0);
1927 tty_cursor(tty
, 0, tty
->cy
);
1928 tty_putcode_i(tty
, TTYC_INDN
, ctx
->num
);
1933 tty_cmd_scrolldown(struct tty
*tty
, const struct tty_ctx
*ctx
)
1936 struct client
*c
= tty
->client
;
1939 (!tty_full_width(tty
, ctx
) && !tty_use_margin(tty
)) ||
1940 tty_fake_bce(tty
, &ctx
->defaults
, 8) ||
1941 !tty_term_has(tty
->term
, TTYC_CSR
) ||
1942 (!tty_term_has(tty
->term
, TTYC_RI
) &&
1943 !tty_term_has(tty
->term
, TTYC_RIN
)) ||
1946 c
->overlay_check
!= NULL
) {
1947 tty_redraw_region(tty
, ctx
);
1951 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1952 ctx
->s
->hyperlinks
);
1954 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
1955 tty_margin_pane(tty
, ctx
);
1956 tty_cursor_pane(tty
, ctx
, ctx
->ocx
, ctx
->orupper
);
1958 if (tty_term_has(tty
->term
, TTYC_RIN
))
1959 tty_putcode_i(tty
, TTYC_RIN
, ctx
->num
);
1961 for (i
= 0; i
< ctx
->num
; i
++)
1962 tty_putcode(tty
, TTYC_RI
);
1967 tty_cmd_clearendofscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
1969 u_int px
, py
, nx
, ny
;
1971 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1972 ctx
->s
->hyperlinks
);
1974 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
1975 tty_margin_off(tty
);
1980 ny
= ctx
->sy
- ctx
->ocy
- 1;
1982 tty_clear_pane_area(tty
, ctx
, py
, ny
, px
, nx
, ctx
->bg
);
1985 nx
= ctx
->sx
- ctx
->ocx
;
1988 tty_clear_pane_line(tty
, ctx
, py
, px
, nx
, ctx
->bg
);
1992 tty_cmd_clearstartofscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
1994 u_int px
, py
, nx
, ny
;
1996 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
1997 ctx
->s
->hyperlinks
);
1999 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
2000 tty_margin_off(tty
);
2007 tty_clear_pane_area(tty
, ctx
, py
, ny
, px
, nx
, ctx
->bg
);
2013 tty_clear_pane_line(tty
, ctx
, py
, px
, nx
, ctx
->bg
);
2017 tty_cmd_clearscreen(struct tty
*tty
, const struct tty_ctx
*ctx
)
2019 u_int px
, py
, nx
, ny
;
2021 tty_default_attributes(tty
, &ctx
->defaults
, ctx
->palette
, ctx
->bg
,
2022 ctx
->s
->hyperlinks
);
2024 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
2025 tty_margin_off(tty
);
2032 tty_clear_pane_area(tty
, ctx
, py
, ny
, px
, nx
, ctx
->bg
);
2036 tty_cmd_alignmenttest(struct tty
*tty
, const struct tty_ctx
*ctx
)
2041 ctx
->redraw_cb(ctx
);
2045 tty_attributes(tty
, &grid_default_cell
, &ctx
->defaults
, ctx
->palette
,
2046 ctx
->s
->hyperlinks
);
2048 tty_region_pane(tty
, ctx
, 0, ctx
->sy
- 1);
2049 tty_margin_off(tty
);
2051 for (j
= 0; j
< ctx
->sy
; j
++) {
2052 tty_cursor_pane(tty
, ctx
, 0, j
);
2053 for (i
= 0; i
< ctx
->sx
; i
++)
2059 tty_cmd_cell(struct tty
*tty
, const struct tty_ctx
*ctx
)
2061 const struct grid_cell
*gcp
= ctx
->cell
;
2062 struct screen
*s
= ctx
->s
;
2063 struct overlay_ranges r
;
2064 u_int px
, py
, i
, vis
= 0;
2066 px
= ctx
->xoff
+ ctx
->ocx
- ctx
->wox
;
2067 py
= ctx
->yoff
+ ctx
->ocy
- ctx
->woy
;
2068 if (!tty_is_visible(tty
, ctx
, ctx
->ocx
, ctx
->ocy
, 1, 1) ||
2069 (gcp
->data
.width
== 1 && !tty_check_overlay(tty
, px
, py
)))
2072 /* Handle partially obstructed wide characters. */
2073 if (gcp
->data
.width
> 1) {
2074 tty_check_overlay_range(tty
, px
, py
, gcp
->data
.width
, &r
);
2075 for (i
= 0; i
< OVERLAY_MAX_RANGES
; i
++)
2077 if (vis
< gcp
->data
.width
) {
2078 tty_draw_line(tty
, s
, s
->cx
, s
->cy
, gcp
->data
.width
,
2079 px
, py
, &ctx
->defaults
, ctx
->palette
);
2084 if (ctx
->xoff
+ ctx
->ocx
- ctx
->wox
> tty
->sx
- 1 &&
2085 ctx
->ocy
== ctx
->orlower
&&
2086 tty_full_width(tty
, ctx
))
2087 tty_region_pane(tty
, ctx
, ctx
->orupper
, ctx
->orlower
);
2089 tty_margin_off(tty
);
2090 tty_cursor_pane_unless_wrap(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
2092 tty_cell(tty
, ctx
->cell
, &ctx
->defaults
, ctx
->palette
,
2093 ctx
->s
->hyperlinks
);
2097 tty_cmd_cells(struct tty
*tty
, const struct tty_ctx
*ctx
)
2099 struct overlay_ranges r
;
2100 u_int i
, px
, py
, cx
;
2101 char *cp
= ctx
->ptr
;
2103 if (!tty_is_visible(tty
, ctx
, ctx
->ocx
, ctx
->ocy
, ctx
->num
, 1))
2107 (ctx
->xoff
+ ctx
->ocx
< ctx
->wox
||
2108 ctx
->xoff
+ ctx
->ocx
+ ctx
->num
> ctx
->wox
+ ctx
->wsx
)) {
2109 if (!ctx
->wrapped
||
2110 !tty_full_width(tty
, ctx
) ||
2111 (tty
->term
->flags
& TERM_NOAM
) ||
2112 ctx
->xoff
+ ctx
->ocx
!= 0 ||
2113 ctx
->yoff
+ ctx
->ocy
!= tty
->cy
+ 1 ||
2114 tty
->cx
< tty
->sx
||
2115 tty
->cy
== tty
->rlower
)
2116 tty_draw_pane(tty
, ctx
, ctx
->ocy
);
2118 ctx
->redraw_cb(ctx
);
2122 tty_margin_off(tty
);
2123 tty_cursor_pane_unless_wrap(tty
, ctx
, ctx
->ocx
, ctx
->ocy
);
2124 tty_attributes(tty
, ctx
->cell
, &ctx
->defaults
, ctx
->palette
, ctx
->s
->hyperlinks
);
2126 /* Get tty position from pane position for overlay check. */
2127 px
= ctx
->xoff
+ ctx
->ocx
- ctx
->wox
;
2128 py
= ctx
->yoff
+ ctx
->ocy
- ctx
->woy
;
2130 tty_check_overlay_range(tty
, px
, py
, ctx
->num
, &r
);
2131 for (i
= 0; i
< OVERLAY_MAX_RANGES
; i
++) {
2134 /* Convert back to pane position for printing. */
2135 cx
= r
.px
[i
] - ctx
->xoff
+ ctx
->wox
;
2136 tty_cursor_pane_unless_wrap(tty
, ctx
, cx
, ctx
->ocy
);
2137 tty_putn(tty
, cp
+ r
.px
[i
] - px
, r
.nx
[i
], r
.nx
[i
]);
2142 tty_cmd_setselection(struct tty
*tty
, const struct tty_ctx
*ctx
)
2144 tty_set_selection(tty
, ctx
->ptr2
, ctx
->ptr
, ctx
->num
);
2148 tty_set_selection(struct tty
*tty
, const char *flags
, const char *buf
,
2154 if (~tty
->flags
& TTY_STARTED
)
2156 if (!tty_term_has(tty
->term
, TTYC_MS
))
2159 size
= 4 * ((len
+ 2) / 3) + 1; /* storage for base64 */
2160 encoded
= xmalloc(size
);
2162 b64_ntop(buf
, len
, encoded
, size
);
2163 tty
->flags
|= TTY_NOBLOCK
;
2164 tty_putcode_ss(tty
, TTYC_MS
, flags
, encoded
);
2170 tty_cmd_rawstring(struct tty
*tty
, const struct tty_ctx
*ctx
)
2172 tty
->flags
|= TTY_NOBLOCK
;
2173 tty_add(tty
, ctx
->ptr
, ctx
->num
);
2174 tty_invalidate(tty
);
2178 tty_cmd_syncstart(struct tty
*tty
, const struct tty_ctx
*ctx
)
2180 if (ctx
->num
== 0x11) {
2182 * This is an overlay and a command that moves the cursor so
2183 * start synchronized updates.
2185 tty_sync_start(tty
);
2186 } else if (~ctx
->num
& 0x10) {
2188 * This is a pane. If there is an overlay, always start;
2189 * otherwise, only if requested.
2191 if (ctx
->num
|| tty
->client
->overlay_draw
!= NULL
)
2192 tty_sync_start(tty
);
2197 tty_cell(struct tty
*tty
, const struct grid_cell
*gc
,
2198 const struct grid_cell
*defaults
, struct colour_palette
*palette
,
2199 struct hyperlinks
*hl
)
2201 const struct grid_cell
*gcp
;
2203 /* Skip last character if terminal is stupid. */
2204 if ((tty
->term
->flags
& TERM_NOAM
) &&
2205 tty
->cy
== tty
->sy
- 1 &&
2206 tty
->cx
== tty
->sx
- 1)
2209 /* If this is a padding character, do nothing. */
2210 if (gc
->flags
& GRID_FLAG_PADDING
)
2213 /* Check the output codeset and apply attributes. */
2214 gcp
= tty_check_codeset(tty
, gc
);
2215 tty_attributes(tty
, gcp
, defaults
, palette
, hl
);
2217 /* If it is a single character, write with putc to handle ACS. */
2218 if (gcp
->data
.size
== 1) {
2219 tty_attributes(tty
, gcp
, defaults
, palette
, hl
);
2220 if (*gcp
->data
.data
< 0x20 || *gcp
->data
.data
== 0x7f)
2222 tty_putc(tty
, *gcp
->data
.data
);
2226 /* Write the data. */
2227 tty_putn(tty
, gcp
->data
.data
, gcp
->data
.size
, gcp
->data
.width
);
2231 tty_reset(struct tty
*tty
)
2233 struct grid_cell
*gc
= &tty
->cell
;
2235 if (!grid_cells_equal(gc
, &grid_default_cell
)) {
2237 tty_putcode_ss(tty
, TTYC_HLS
, "", "");
2238 if ((gc
->attr
& GRID_ATTR_CHARSET
) && tty_acs_needed(tty
))
2239 tty_putcode(tty
, TTYC_RMACS
);
2240 tty_putcode(tty
, TTYC_SGR0
);
2241 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
2243 memcpy(&tty
->last_cell
, &grid_default_cell
, sizeof tty
->last_cell
);
2247 tty_invalidate(struct tty
*tty
)
2249 memcpy(&tty
->cell
, &grid_default_cell
, sizeof tty
->cell
);
2250 memcpy(&tty
->last_cell
, &grid_default_cell
, sizeof tty
->last_cell
);
2252 tty
->cx
= tty
->cy
= UINT_MAX
;
2253 tty
->rupper
= tty
->rleft
= UINT_MAX
;
2254 tty
->rlower
= tty
->rright
= UINT_MAX
;
2256 if (tty
->flags
& TTY_STARTED
) {
2257 if (tty_use_margin(tty
))
2258 tty_putcode(tty
, TTYC_ENMG
);
2259 tty_putcode(tty
, TTYC_SGR0
);
2261 tty
->mode
= ALL_MODES
;
2262 tty_update_mode(tty
, MODE_CURSOR
, NULL
);
2264 tty_cursor(tty
, 0, 0);
2265 tty_region_off(tty
);
2266 tty_margin_off(tty
);
2268 tty
->mode
= MODE_CURSOR
;
2271 /* Turn off margin. */
2273 tty_region_off(struct tty
*tty
)
2275 tty_region(tty
, 0, tty
->sy
- 1);
2278 /* Set region inside pane. */
2280 tty_region_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int rupper
,
2283 tty_region(tty
, ctx
->yoff
+ rupper
- ctx
->woy
,
2284 ctx
->yoff
+ rlower
- ctx
->woy
);
2287 /* Set region at absolute position. */
2289 tty_region(struct tty
*tty
, u_int rupper
, u_int rlower
)
2291 if (tty
->rlower
== rlower
&& tty
->rupper
== rupper
)
2293 if (!tty_term_has(tty
->term
, TTYC_CSR
))
2296 tty
->rupper
= rupper
;
2297 tty
->rlower
= rlower
;
2300 * Some terminals (such as PuTTY) do not correctly reset the cursor to
2301 * 0,0 if it is beyond the last column (they do not reset their wrap
2302 * flag so further output causes a line feed). As a workaround, do an
2303 * explicit move to 0 first.
2305 if (tty
->cx
>= tty
->sx
) {
2306 if (tty
->cy
== UINT_MAX
)
2307 tty_cursor(tty
, 0, 0);
2309 tty_cursor(tty
, 0, tty
->cy
);
2312 tty_putcode_ii(tty
, TTYC_CSR
, tty
->rupper
, tty
->rlower
);
2313 tty
->cx
= tty
->cy
= UINT_MAX
;
2316 /* Turn off margin. */
2318 tty_margin_off(struct tty
*tty
)
2320 tty_margin(tty
, 0, tty
->sx
- 1);
2323 /* Set margin inside pane. */
2325 tty_margin_pane(struct tty
*tty
, const struct tty_ctx
*ctx
)
2327 tty_margin(tty
, ctx
->xoff
- ctx
->wox
,
2328 ctx
->xoff
+ ctx
->sx
- 1 - ctx
->wox
);
2331 /* Set margin at absolute position. */
2333 tty_margin(struct tty
*tty
, u_int rleft
, u_int rright
)
2335 if (!tty_use_margin(tty
))
2337 if (tty
->rleft
== rleft
&& tty
->rright
== rright
)
2340 tty_putcode_ii(tty
, TTYC_CSR
, tty
->rupper
, tty
->rlower
);
2343 tty
->rright
= rright
;
2345 if (rleft
== 0 && rright
== tty
->sx
- 1)
2346 tty_putcode(tty
, TTYC_CLMG
);
2348 tty_putcode_ii(tty
, TTYC_CMG
, rleft
, rright
);
2349 tty
->cx
= tty
->cy
= UINT_MAX
;
2353 * Move the cursor, unless it would wrap itself when the next character is
2357 tty_cursor_pane_unless_wrap(struct tty
*tty
, const struct tty_ctx
*ctx
,
2360 if (!ctx
->wrapped
||
2361 !tty_full_width(tty
, ctx
) ||
2362 (tty
->term
->flags
& TERM_NOAM
) ||
2363 ctx
->xoff
+ cx
!= 0 ||
2364 ctx
->yoff
+ cy
!= tty
->cy
+ 1 ||
2365 tty
->cx
< tty
->sx
||
2366 tty
->cy
== tty
->rlower
)
2367 tty_cursor_pane(tty
, ctx
, cx
, cy
);
2369 log_debug("%s: will wrap at %u,%u", __func__
, tty
->cx
, tty
->cy
);
2372 /* Move cursor inside pane. */
2374 tty_cursor_pane(struct tty
*tty
, const struct tty_ctx
*ctx
, u_int cx
, u_int cy
)
2376 tty_cursor(tty
, ctx
->xoff
+ cx
- ctx
->wox
, ctx
->yoff
+ cy
- ctx
->woy
);
2379 /* Move cursor to absolute position. */
2381 tty_cursor(struct tty
*tty
, u_int cx
, u_int cy
)
2383 struct tty_term
*term
= tty
->term
;
2387 if (tty
->flags
& TTY_BLOCK
)
2394 * If in the automargin space, and want to be there, do not move.
2395 * Otherwise, force the cursor to be in range (and complain).
2397 if (cx
== thisx
&& cy
== thisy
&& cx
== tty
->sx
)
2399 if (cx
> tty
->sx
- 1) {
2400 log_debug("%s: x too big %u > %u", __func__
, cx
, tty
->sx
- 1);
2405 if (cx
== thisx
&& cy
== thisy
)
2408 /* Currently at the very end of the line - use absolute movement. */
2409 if (thisx
> tty
->sx
- 1)
2412 /* Move to home position (0, 0). */
2413 if (cx
== 0 && cy
== 0 && tty_term_has(term
, TTYC_HOME
)) {
2414 tty_putcode(tty
, TTYC_HOME
);
2418 /* Zero on the next line. */
2419 if (cx
== 0 && cy
== thisy
+ 1 && thisy
!= tty
->rlower
&&
2420 (!tty_use_margin(tty
) || tty
->rleft
== 0)) {
2421 tty_putc(tty
, '\r');
2422 tty_putc(tty
, '\n');
2426 /* Moving column or row. */
2429 * Moving column only, row staying the same.
2433 if (cx
== 0 && (!tty_use_margin(tty
) || tty
->rleft
== 0)) {
2434 tty_putc(tty
, '\r');
2438 /* One to the left. */
2439 if (cx
== thisx
- 1 && tty_term_has(term
, TTYC_CUB1
)) {
2440 tty_putcode(tty
, TTYC_CUB1
);
2444 /* One to the right. */
2445 if (cx
== thisx
+ 1 && tty_term_has(term
, TTYC_CUF1
)) {
2446 tty_putcode(tty
, TTYC_CUF1
);
2450 /* Calculate difference. */
2451 change
= thisx
- cx
; /* +ve left, -ve right */
2454 * Use HPA if change is larger than absolute, otherwise move
2455 * the cursor with CUB/CUF.
2457 if ((u_int
) abs(change
) > cx
&& tty_term_has(term
, TTYC_HPA
)) {
2458 tty_putcode_i(tty
, TTYC_HPA
, cx
);
2460 } else if (change
> 0 &&
2461 tty_term_has(term
, TTYC_CUB
) &&
2462 !tty_use_margin(tty
)) {
2463 if (change
== 2 && tty_term_has(term
, TTYC_CUB1
)) {
2464 tty_putcode(tty
, TTYC_CUB1
);
2465 tty_putcode(tty
, TTYC_CUB1
);
2468 tty_putcode_i(tty
, TTYC_CUB
, change
);
2470 } else if (change
< 0 &&
2471 tty_term_has(term
, TTYC_CUF
) &&
2472 !tty_use_margin(tty
)) {
2473 tty_putcode_i(tty
, TTYC_CUF
, -change
);
2476 } else if (cx
== thisx
) {
2478 * Moving row only, column staying the same.
2482 if (thisy
!= tty
->rupper
&&
2483 cy
== thisy
- 1 && tty_term_has(term
, TTYC_CUU1
)) {
2484 tty_putcode(tty
, TTYC_CUU1
);
2489 if (thisy
!= tty
->rlower
&&
2490 cy
== thisy
+ 1 && tty_term_has(term
, TTYC_CUD1
)) {
2491 tty_putcode(tty
, TTYC_CUD1
);
2495 /* Calculate difference. */
2496 change
= thisy
- cy
; /* +ve up, -ve down */
2499 * Try to use VPA if change is larger than absolute or if this
2500 * change would cross the scroll region, otherwise use CUU/CUD.
2502 if ((u_int
) abs(change
) > cy
||
2503 (change
< 0 && cy
- change
> tty
->rlower
) ||
2504 (change
> 0 && cy
- change
< tty
->rupper
)) {
2505 if (tty_term_has(term
, TTYC_VPA
)) {
2506 tty_putcode_i(tty
, TTYC_VPA
, cy
);
2509 } else if (change
> 0 && tty_term_has(term
, TTYC_CUU
)) {
2510 tty_putcode_i(tty
, TTYC_CUU
, change
);
2512 } else if (change
< 0 && tty_term_has(term
, TTYC_CUD
)) {
2513 tty_putcode_i(tty
, TTYC_CUD
, -change
);
2519 /* Absolute movement. */
2520 tty_putcode_ii(tty
, TTYC_CUP
, cy
, cx
);
2528 tty_hyperlink(struct tty
*tty
, const struct grid_cell
*gc
,
2529 struct hyperlinks
*hl
)
2531 const char *uri
, *id
;
2533 if (gc
->link
== tty
->cell
.link
)
2535 tty
->cell
.link
= gc
->link
;
2540 if (gc
->link
== 0 || !hyperlinks_get(hl
, gc
->link
, &uri
, NULL
, &id
))
2541 tty_putcode_ss(tty
, TTYC_HLS
, "", "");
2543 tty_putcode_ss(tty
, TTYC_HLS
, id
, uri
);
2547 tty_attributes(struct tty
*tty
, const struct grid_cell
*gc
,
2548 const struct grid_cell
*defaults
, struct colour_palette
*palette
,
2549 struct hyperlinks
*hl
)
2551 struct grid_cell
*tc
= &tty
->cell
, gc2
;
2554 /* Copy cell and update default colours. */
2555 memcpy(&gc2
, gc
, sizeof gc2
);
2556 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2558 gc2
.fg
= defaults
->fg
;
2560 gc2
.bg
= defaults
->bg
;
2563 /* Ignore cell if it is the same as the last one. */
2564 if (gc2
.attr
== tty
->last_cell
.attr
&&
2565 gc2
.fg
== tty
->last_cell
.fg
&&
2566 gc2
.bg
== tty
->last_cell
.bg
&&
2567 gc2
.us
== tty
->last_cell
.us
&&
2568 gc2
.link
== tty
->last_cell
.link
)
2572 * If no setab, try to use the reverse attribute as a best-effort for a
2573 * non-default background. This is a bit of a hack but it doesn't do
2574 * any serious harm and makes a couple of applications happier.
2576 if (!tty_term_has(tty
->term
, TTYC_SETAB
)) {
2577 if (gc2
.attr
& GRID_ATTR_REVERSE
) {
2578 if (gc2
.fg
!= 7 && !COLOUR_DEFAULT(gc2
.fg
))
2579 gc2
.attr
&= ~GRID_ATTR_REVERSE
;
2581 if (gc2
.bg
!= 0 && !COLOUR_DEFAULT(gc2
.bg
))
2582 gc2
.attr
|= GRID_ATTR_REVERSE
;
2586 /* Fix up the colours if necessary. */
2587 tty_check_fg(tty
, palette
, &gc2
);
2588 tty_check_bg(tty
, palette
, &gc2
);
2589 tty_check_us(tty
, palette
, &gc2
);
2592 * If any bits are being cleared or the underline colour is now default,
2595 if ((tc
->attr
& ~gc2
.attr
) || (tc
->us
!= gc2
.us
&& gc2
.us
== 0))
2599 * Set the colours. This may call tty_reset() (so it comes next) and
2600 * may add to (NOT remove) the desired attributes.
2602 tty_colours(tty
, &gc2
);
2604 /* Filter out attribute bits already set. */
2605 changed
= gc2
.attr
& ~tc
->attr
;
2606 tc
->attr
= gc2
.attr
;
2608 /* Set the attributes. */
2609 if (changed
& GRID_ATTR_BRIGHT
)
2610 tty_putcode(tty
, TTYC_BOLD
);
2611 if (changed
& GRID_ATTR_DIM
)
2612 tty_putcode(tty
, TTYC_DIM
);
2613 if (changed
& GRID_ATTR_ITALICS
)
2614 tty_set_italics(tty
);
2615 if (changed
& GRID_ATTR_ALL_UNDERSCORE
) {
2616 if ((changed
& GRID_ATTR_UNDERSCORE
) ||
2617 !tty_term_has(tty
->term
, TTYC_SMULX
))
2618 tty_putcode(tty
, TTYC_SMUL
);
2619 else if (changed
& GRID_ATTR_UNDERSCORE_2
)
2620 tty_putcode_i(tty
, TTYC_SMULX
, 2);
2621 else if (changed
& GRID_ATTR_UNDERSCORE_3
)
2622 tty_putcode_i(tty
, TTYC_SMULX
, 3);
2623 else if (changed
& GRID_ATTR_UNDERSCORE_4
)
2624 tty_putcode_i(tty
, TTYC_SMULX
, 4);
2625 else if (changed
& GRID_ATTR_UNDERSCORE_5
)
2626 tty_putcode_i(tty
, TTYC_SMULX
, 5);
2628 if (changed
& GRID_ATTR_BLINK
)
2629 tty_putcode(tty
, TTYC_BLINK
);
2630 if (changed
& GRID_ATTR_REVERSE
) {
2631 if (tty_term_has(tty
->term
, TTYC_REV
))
2632 tty_putcode(tty
, TTYC_REV
);
2633 else if (tty_term_has(tty
->term
, TTYC_SMSO
))
2634 tty_putcode(tty
, TTYC_SMSO
);
2636 if (changed
& GRID_ATTR_HIDDEN
)
2637 tty_putcode(tty
, TTYC_INVIS
);
2638 if (changed
& GRID_ATTR_STRIKETHROUGH
)
2639 tty_putcode(tty
, TTYC_SMXX
);
2640 if (changed
& GRID_ATTR_OVERLINE
)
2641 tty_putcode(tty
, TTYC_SMOL
);
2642 if ((changed
& GRID_ATTR_CHARSET
) && tty_acs_needed(tty
))
2643 tty_putcode(tty
, TTYC_SMACS
);
2645 /* Set hyperlink if any. */
2646 tty_hyperlink(tty
, gc
, hl
);
2648 memcpy(&tty
->last_cell
, &gc2
, sizeof tty
->last_cell
);
2652 tty_colours(struct tty
*tty
, const struct grid_cell
*gc
)
2654 struct grid_cell
*tc
= &tty
->cell
;
2657 /* No changes? Nothing is necessary. */
2658 if (gc
->fg
== tc
->fg
&& gc
->bg
== tc
->bg
&& gc
->us
== tc
->us
)
2662 * Is either the default colour? This is handled specially because the
2663 * best solution might be to reset both colours to default, in which
2664 * case if only one is default need to fall onward to set the other
2667 if (COLOUR_DEFAULT(gc
->fg
) || COLOUR_DEFAULT(gc
->bg
)) {
2669 * If don't have AX but do have op, send sgr0 (op can't
2670 * actually be used because it is sometimes the same as sgr0
2671 * and sometimes isn't). This resets both colours to default.
2673 * Otherwise, try to set the default colour only as needed.
2675 have_ax
= tty_term_flag(tty
->term
, TTYC_AX
);
2676 if (!have_ax
&& tty_term_has(tty
->term
, TTYC_OP
))
2679 if (COLOUR_DEFAULT(gc
->fg
) && !COLOUR_DEFAULT(tc
->fg
)) {
2681 tty_puts(tty
, "\033[39m");
2682 else if (tc
->fg
!= 7)
2683 tty_putcode_i(tty
, TTYC_SETAF
, 7);
2686 if (COLOUR_DEFAULT(gc
->bg
) && !COLOUR_DEFAULT(tc
->bg
)) {
2688 tty_puts(tty
, "\033[49m");
2689 else if (tc
->bg
!= 0)
2690 tty_putcode_i(tty
, TTYC_SETAB
, 0);
2696 /* Set the foreground colour. */
2697 if (!COLOUR_DEFAULT(gc
->fg
) && gc
->fg
!= tc
->fg
)
2698 tty_colours_fg(tty
, gc
);
2701 * Set the background colour. This must come after the foreground as
2702 * tty_colour_fg() can call tty_reset().
2704 if (!COLOUR_DEFAULT(gc
->bg
) && gc
->bg
!= tc
->bg
)
2705 tty_colours_bg(tty
, gc
);
2707 /* Set the underscore colour. */
2708 if (gc
->us
!= tc
->us
)
2709 tty_colours_us(tty
, gc
);
2713 tty_check_fg(struct tty
*tty
, struct colour_palette
*palette
,
2714 struct grid_cell
*gc
)
2721 * Perform substitution if this pane has a palette. If the bright
2722 * attribute is set and Nobr is not present, use the bright entry in
2723 * the palette by changing to the aixterm colour
2725 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2728 gc
->attr
& GRID_ATTR_BRIGHT
&&
2729 !tty_term_has(tty
->term
, TTYC_NOBR
))
2731 if ((c
= colour_palette_get(palette
, c
)) != -1)
2735 /* Is this a 24-bit colour? */
2736 if (gc
->fg
& COLOUR_FLAG_RGB
) {
2737 /* Not a 24-bit terminal? Translate to 256-colour palette. */
2738 if (tty
->term
->flags
& TERM_RGBCOLOURS
)
2740 colour_split_rgb(gc
->fg
, &r
, &g
, &b
);
2741 gc
->fg
= colour_find_rgb(r
, g
, b
);
2744 /* How many colours does this terminal have? */
2745 if (tty
->term
->flags
& TERM_256COLOURS
)
2748 colours
= tty_term_number(tty
->term
, TTYC_COLORS
);
2750 /* Is this a 256-colour colour? */
2751 if (gc
->fg
& COLOUR_FLAG_256
) {
2752 /* And not a 256 colour mode? */
2753 if (colours
< 256) {
2754 gc
->fg
= colour_256to16(gc
->fg
);
2764 /* Is this an aixterm colour? */
2765 if (gc
->fg
>= 90 && gc
->fg
<= 97 && colours
< 16) {
2767 gc
->attr
|= GRID_ATTR_BRIGHT
;
2772 tty_check_bg(struct tty
*tty
, struct colour_palette
*palette
,
2773 struct grid_cell
*gc
)
2779 /* Perform substitution if this pane has a palette. */
2780 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2781 if ((c
= colour_palette_get(palette
, gc
->bg
)) != -1)
2785 /* Is this a 24-bit colour? */
2786 if (gc
->bg
& COLOUR_FLAG_RGB
) {
2787 /* Not a 24-bit terminal? Translate to 256-colour palette. */
2788 if (tty
->term
->flags
& TERM_RGBCOLOURS
)
2790 colour_split_rgb(gc
->bg
, &r
, &g
, &b
);
2791 gc
->bg
= colour_find_rgb(r
, g
, b
);
2794 /* How many colours does this terminal have? */
2795 if (tty
->term
->flags
& TERM_256COLOURS
)
2798 colours
= tty_term_number(tty
->term
, TTYC_COLORS
);
2800 /* Is this a 256-colour colour? */
2801 if (gc
->bg
& COLOUR_FLAG_256
) {
2803 * And not a 256 colour mode? Translate to 16-colour
2804 * palette. Bold background doesn't exist portably, so just
2805 * discard the bold bit if set.
2807 if (colours
< 256) {
2808 gc
->bg
= colour_256to16(gc
->bg
);
2818 /* Is this an aixterm colour? */
2819 if (gc
->bg
>= 90 && gc
->bg
<= 97 && colours
< 16)
2824 tty_check_us(__unused
struct tty
*tty
, struct colour_palette
*palette
,
2825 struct grid_cell
*gc
)
2829 /* Perform substitution if this pane has a palette. */
2830 if (~gc
->flags
& GRID_FLAG_NOPALETTE
) {
2831 if ((c
= colour_palette_get(palette
, gc
->us
)) != -1)
2835 /* Convert underscore colour if only RGB can be supported. */
2836 if (!tty_term_has(tty
->term
, TTYC_SETULC1
)) {
2837 if ((c
= colour_force_rgb (gc
->us
)) == -1)
2845 tty_colours_fg(struct tty
*tty
, const struct grid_cell
*gc
)
2847 struct grid_cell
*tc
= &tty
->cell
;
2850 /* Is this a 24-bit or 256-colour colour? */
2851 if (gc
->fg
& COLOUR_FLAG_RGB
|| gc
->fg
& COLOUR_FLAG_256
) {
2852 if (tty_try_colour(tty
, gc
->fg
, "38") == 0)
2854 /* Should not get here, already converted in tty_check_fg. */
2858 /* Is this an aixterm bright colour? */
2859 if (gc
->fg
>= 90 && gc
->fg
<= 97) {
2860 if (tty
->term
->flags
& TERM_256COLOURS
) {
2861 xsnprintf(s
, sizeof s
, "\033[%dm", gc
->fg
);
2864 tty_putcode_i(tty
, TTYC_SETAF
, gc
->fg
- 90 + 8);
2868 /* Otherwise set the foreground colour. */
2869 tty_putcode_i(tty
, TTYC_SETAF
, gc
->fg
);
2872 /* Save the new values in the terminal current cell. */
2877 tty_colours_bg(struct tty
*tty
, const struct grid_cell
*gc
)
2879 struct grid_cell
*tc
= &tty
->cell
;
2882 /* Is this a 24-bit or 256-colour colour? */
2883 if (gc
->bg
& COLOUR_FLAG_RGB
|| gc
->bg
& COLOUR_FLAG_256
) {
2884 if (tty_try_colour(tty
, gc
->bg
, "48") == 0)
2886 /* Should not get here, already converted in tty_check_bg. */
2890 /* Is this an aixterm bright colour? */
2891 if (gc
->bg
>= 90 && gc
->bg
<= 97) {
2892 if (tty
->term
->flags
& TERM_256COLOURS
) {
2893 xsnprintf(s
, sizeof s
, "\033[%dm", gc
->bg
+ 10);
2896 tty_putcode_i(tty
, TTYC_SETAB
, gc
->bg
- 90 + 8);
2900 /* Otherwise set the background colour. */
2901 tty_putcode_i(tty
, TTYC_SETAB
, gc
->bg
);
2904 /* Save the new values in the terminal current cell. */
2909 tty_colours_us(struct tty
*tty
, const struct grid_cell
*gc
)
2911 struct grid_cell
*tc
= &tty
->cell
;
2915 /* Clear underline colour. */
2916 if (COLOUR_DEFAULT(gc
->us
)) {
2917 tty_putcode(tty
, TTYC_OL
);
2922 * If this is not an RGB colour, use Setulc1 if it exists, otherwise
2925 if (~gc
->us
& COLOUR_FLAG_RGB
) {
2927 if ((~c
& COLOUR_FLAG_256
) && (c
>= 90 && c
<= 97))
2929 tty_putcode_i(tty
, TTYC_SETULC1
, c
& ~COLOUR_FLAG_256
);
2934 * Setulc and setal follows the ncurses(3) one argument "direct colour"
2935 * capability format. Calculate the colour value.
2937 colour_split_rgb(gc
->us
, &r
, &g
, &b
);
2938 c
= (65536 * r
) + (256 * g
) + b
;
2941 * Write the colour. Only use setal if the RGB flag is set because the
2942 * non-RGB version may be wrong.
2944 if (tty_term_has(tty
->term
, TTYC_SETULC
))
2945 tty_putcode_i(tty
, TTYC_SETULC
, c
);
2946 else if (tty_term_has(tty
->term
, TTYC_SETAL
) &&
2947 tty_term_has(tty
->term
, TTYC_RGB
))
2948 tty_putcode_i(tty
, TTYC_SETAL
, c
);
2951 /* Save the new values in the terminal current cell. */
2956 tty_try_colour(struct tty
*tty
, int colour
, const char *type
)
2960 if (colour
& COLOUR_FLAG_256
) {
2961 if (*type
== '3' && tty_term_has(tty
->term
, TTYC_SETAF
))
2962 tty_putcode_i(tty
, TTYC_SETAF
, colour
& 0xff);
2963 else if (tty_term_has(tty
->term
, TTYC_SETAB
))
2964 tty_putcode_i(tty
, TTYC_SETAB
, colour
& 0xff);
2968 if (colour
& COLOUR_FLAG_RGB
) {
2969 colour_split_rgb(colour
& 0xffffff, &r
, &g
, &b
);
2970 if (*type
== '3' && tty_term_has(tty
->term
, TTYC_SETRGBF
))
2971 tty_putcode_iii(tty
, TTYC_SETRGBF
, r
, g
, b
);
2972 else if (tty_term_has(tty
->term
, TTYC_SETRGBB
))
2973 tty_putcode_iii(tty
, TTYC_SETRGBB
, r
, g
, b
);
2981 tty_window_default_style(struct grid_cell
*gc
, struct window_pane
*wp
)
2983 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
2984 gc
->fg
= wp
->palette
.fg
;
2985 gc
->bg
= wp
->palette
.bg
;
2989 tty_default_colours(struct grid_cell
*gc
, struct window_pane
*wp
)
2991 struct options
*oo
= wp
->options
;
2992 struct format_tree
*ft
;
2994 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
2996 if (wp
->flags
& PANE_STYLECHANGED
) {
2997 log_debug("%%%u: style changed", wp
->id
);
2998 wp
->flags
&= ~PANE_STYLECHANGED
;
3000 ft
= format_create(NULL
, NULL
, FORMAT_PANE
|wp
->id
,
3002 format_defaults(ft
, NULL
, NULL
, NULL
, wp
);
3003 tty_window_default_style(&wp
->cached_active_gc
, wp
);
3004 style_add(&wp
->cached_active_gc
, oo
, "window-active-style", ft
);
3005 tty_window_default_style(&wp
->cached_gc
, wp
);
3006 style_add(&wp
->cached_gc
, oo
, "window-style", ft
);
3011 if (wp
== wp
->window
->active
&& wp
->cached_active_gc
.fg
!= 8)
3012 gc
->fg
= wp
->cached_active_gc
.fg
;
3014 gc
->fg
= wp
->cached_gc
.fg
;
3018 if (wp
== wp
->window
->active
&& wp
->cached_active_gc
.bg
!= 8)
3019 gc
->bg
= wp
->cached_active_gc
.bg
;
3021 gc
->bg
= wp
->cached_gc
.bg
;
3026 tty_default_attributes(struct tty
*tty
, const struct grid_cell
*defaults
,
3027 struct colour_palette
*palette
, u_int bg
, struct hyperlinks
*hl
)
3029 struct grid_cell gc
;
3031 memcpy(&gc
, &grid_default_cell
, sizeof gc
);
3033 tty_attributes(tty
, &gc
, defaults
, palette
, hl
);
3037 tty_clipboard_query_callback(__unused
int fd
, __unused
short events
, void *data
)
3039 struct tty
*tty
= data
;
3040 struct client
*c
= tty
->client
;
3042 c
->flags
&= ~CLIENT_CLIPBOARDBUFFER
;
3043 free(c
->clipboard_panes
);
3044 c
->clipboard_panes
= NULL
;
3045 c
->clipboard_npanes
= 0;
3047 tty
->flags
&= ~TTY_OSC52QUERY
;
3051 tty_clipboard_query(struct tty
*tty
)
3053 struct timeval tv
= { .tv_sec
= TTY_QUERY_TIMEOUT
};
3055 if ((~tty
->flags
& TTY_STARTED
) || (tty
->flags
& TTY_OSC52QUERY
))
3057 tty_putcode_ss(tty
, TTYC_MS
, "", "?");
3059 tty
->flags
|= TTY_OSC52QUERY
;
3060 evtimer_set(&tty
->clipboard_timer
, tty_clipboard_query_callback
, tty
);
3061 evtimer_add(&tty
->clipboard_timer
, &tv
);