15494e770Schristos /* $OpenBSD$ */ 2698d5317Sjmmv 3698d5317Sjmmv /* 4ed4e6cd4Schristos * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5698d5317Sjmmv * 6698d5317Sjmmv * Permission to use, copy, modify, and distribute this software for any 7698d5317Sjmmv * purpose with or without fee is hereby granted, provided that the above 8698d5317Sjmmv * copyright notice and this permission notice appear in all copies. 9698d5317Sjmmv * 10698d5317Sjmmv * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11698d5317Sjmmv * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12698d5317Sjmmv * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13698d5317Sjmmv * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14698d5317Sjmmv * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15698d5317Sjmmv * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16698d5317Sjmmv * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17698d5317Sjmmv */ 18698d5317Sjmmv 19698d5317Sjmmv #include <sys/types.h> 20698d5317Sjmmv #include <sys/ioctl.h> 21698d5317Sjmmv 22d530c4d0Sjmmv #include <netinet/in.h> 23d530c4d0Sjmmv 244e179ddaSchristos #include <curses.h> 25698d5317Sjmmv #include <errno.h> 26698d5317Sjmmv #include <fcntl.h> 27d530c4d0Sjmmv #include <resolv.h> 28698d5317Sjmmv #include <stdlib.h> 29698d5317Sjmmv #include <string.h> 30698d5317Sjmmv #include <termios.h> 31*890b6d91Swiz #include <time.h> 32698d5317Sjmmv #include <unistd.h> 33698d5317Sjmmv 34698d5317Sjmmv #include "tmux.h" 35698d5317Sjmmv 36ed4e6cd4Schristos static int tty_log_fd = -1; 37ed4e6cd4Schristos 384e179ddaSchristos static void tty_set_italics(struct tty *); 394e179ddaSchristos static int tty_try_colour(struct tty *, int, const char *); 4046548964Swiz static void tty_force_cursor_colour(struct tty *, int); 414e179ddaSchristos static void tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int, 42ed4e6cd4Schristos u_int); 434e179ddaSchristos static void tty_cursor_pane_unless_wrap(struct tty *, 444e179ddaSchristos const struct tty_ctx *, u_int, u_int); 454e179ddaSchristos static void tty_invalidate(struct tty *); 464e179ddaSchristos static void tty_colours(struct tty *, const struct grid_cell *); 4746548964Swiz static void tty_check_fg(struct tty *, struct colour_palette *, 4846548964Swiz struct grid_cell *); 4946548964Swiz static void tty_check_bg(struct tty *, struct colour_palette *, 5046548964Swiz struct grid_cell *); 5146548964Swiz static void tty_check_us(struct tty *, struct colour_palette *, 5246548964Swiz struct grid_cell *); 534e179ddaSchristos static void tty_colours_fg(struct tty *, const struct grid_cell *); 544e179ddaSchristos static void tty_colours_bg(struct tty *, const struct grid_cell *); 5530744affSchristos static void tty_colours_us(struct tty *, const struct grid_cell *); 564e179ddaSchristos 574e179ddaSchristos static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int, 584e179ddaSchristos u_int); 594e179ddaSchristos static void tty_region(struct tty *, u_int, u_int); 604e179ddaSchristos static void tty_margin_pane(struct tty *, const struct tty_ctx *); 614e179ddaSchristos static void tty_margin(struct tty *, u_int, u_int); 624e179ddaSchristos static int tty_large_region(struct tty *, const struct tty_ctx *); 63e271dbb8Schristos static int tty_fake_bce(const struct tty *, const struct grid_cell *, 64e271dbb8Schristos u_int); 654e179ddaSchristos static void tty_redraw_region(struct tty *, const struct tty_ctx *); 664e179ddaSchristos static void tty_emulate_repeat(struct tty *, enum tty_code_code, 674e179ddaSchristos enum tty_code_code, u_int); 684e179ddaSchristos static void tty_repeat_space(struct tty *, u_int); 690a274e86Schristos static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int); 70e271dbb8Schristos static void tty_default_attributes(struct tty *, const struct grid_cell *, 71f844e94eSwiz struct colour_palette *, u_int, struct hyperlinks *); 7246548964Swiz static int tty_check_overlay(struct tty *, u_int, u_int); 7346548964Swiz static void tty_check_overlay_range(struct tty *, u_int, u_int, u_int, 7446548964Swiz struct overlay_ranges *); 75698d5317Sjmmv 76f844e94eSwiz #ifdef ENABLE_SIXEL 77f844e94eSwiz static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *), 78f844e94eSwiz struct client *, struct tty_ctx *); 79f844e94eSwiz #endif 80f844e94eSwiz 814e179ddaSchristos #define tty_use_margin(tty) \ 82e271dbb8Schristos (tty->term->flags & TERM_DECSLRM) 83e271dbb8Schristos #define tty_full_width(tty, ctx) \ 84e271dbb8Schristos ((ctx)->xoff == 0 && (ctx)->sx >= (tty)->sx) 85698d5317Sjmmv 86fe99a117Schristos #define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */) 87fe99a117Schristos #define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8) 88fe99a117Schristos #define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8) 89fe99a117Schristos 9046548964Swiz #define TTY_QUERY_TIMEOUT 5 91f844e94eSwiz #define TTY_REQUEST_LIMIT 30 9246548964Swiz 93ed4e6cd4Schristos void 94ed4e6cd4Schristos tty_create_log(void) 95ed4e6cd4Schristos { 96ed4e6cd4Schristos char name[64]; 97ed4e6cd4Schristos 98ed4e6cd4Schristos xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid()); 99ed4e6cd4Schristos 100ed4e6cd4Schristos tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644); 101ed4e6cd4Schristos if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1) 102ed4e6cd4Schristos fatal("fcntl failed"); 103ed4e6cd4Schristos } 104ed4e6cd4Schristos 1055494e770Schristos int 106e271dbb8Schristos tty_init(struct tty *tty, struct client *c) 107698d5317Sjmmv { 108e271dbb8Schristos if (!isatty(c->fd)) 1095494e770Schristos return (-1); 1105494e770Schristos 111698d5317Sjmmv memset(tty, 0, sizeof *tty); 112928fc495Schristos tty->client = c; 113698d5317Sjmmv 11446548964Swiz tty->cstyle = SCREEN_CURSOR_DEFAULT; 11546548964Swiz tty->ccolour = -1; 116f844e94eSwiz tty->fg = tty->bg = -1; 117698d5317Sjmmv 118e271dbb8Schristos if (tcgetattr(c->fd, &tty->tio) != 0) 119e271dbb8Schristos return (-1); 1205494e770Schristos return (0); 121698d5317Sjmmv } 122698d5317Sjmmv 123fe99a117Schristos void 124698d5317Sjmmv tty_resize(struct tty *tty) 125698d5317Sjmmv { 1264e179ddaSchristos struct client *c = tty->client; 127698d5317Sjmmv struct winsize ws; 12868e6ba84Schristos u_int sx, sy, xpixel, ypixel; 129698d5317Sjmmv 130e271dbb8Schristos if (ioctl(c->fd, TIOCGWINSZ, &ws) != -1) { 131698d5317Sjmmv sx = ws.ws_col; 13268e6ba84Schristos if (sx == 0) { 133698d5317Sjmmv sx = 80; 13468e6ba84Schristos xpixel = 0; 13568e6ba84Schristos } else 13668e6ba84Schristos xpixel = ws.ws_xpixel / sx; 137698d5317Sjmmv sy = ws.ws_row; 13868e6ba84Schristos if (sy == 0) { 139698d5317Sjmmv sy = 24; 14068e6ba84Schristos ypixel = 0; 14168e6ba84Schristos } else 14268e6ba84Schristos ypixel = ws.ws_ypixel / sy; 143698d5317Sjmmv } else { 144698d5317Sjmmv sx = 80; 145698d5317Sjmmv sy = 24; 14668e6ba84Schristos xpixel = 0; 14768e6ba84Schristos ypixel = 0; 148698d5317Sjmmv } 14968e6ba84Schristos log_debug("%s: %s now %ux%u (%ux%u)", __func__, c->name, sx, sy, 15068e6ba84Schristos xpixel, ypixel); 15168e6ba84Schristos tty_set_size(tty, sx, sy, xpixel, ypixel); 1524e179ddaSchristos tty_invalidate(tty); 153698d5317Sjmmv } 154698d5317Sjmmv 155fe99a117Schristos void 15668e6ba84Schristos tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel) 1574e179ddaSchristos { 158928fc495Schristos tty->sx = sx; 159928fc495Schristos tty->sy = sy; 16068e6ba84Schristos tty->xpixel = xpixel; 16168e6ba84Schristos tty->ypixel = ypixel; 162928fc495Schristos } 163928fc495Schristos 1644e179ddaSchristos static void 1654e179ddaSchristos tty_read_callback(__unused int fd, __unused short events, void *data) 1664e179ddaSchristos { 1674e179ddaSchristos struct tty *tty = data; 1684e179ddaSchristos struct client *c = tty->client; 169e271dbb8Schristos const char *name = c->name; 1704e179ddaSchristos size_t size = EVBUFFER_LENGTH(tty->in); 1714e179ddaSchristos int nread; 1724e179ddaSchristos 173e271dbb8Schristos nread = evbuffer_read(tty->in, c->fd, -1); 174fe99a117Schristos if (nread == 0 || nread == -1) { 175e271dbb8Schristos if (nread == 0) 176e271dbb8Schristos log_debug("%s: read closed", name); 177e271dbb8Schristos else 178e271dbb8Schristos log_debug("%s: read error: %s", name, strerror(errno)); 179fe99a117Schristos event_del(&tty->event_in); 180fe99a117Schristos server_client_lost(tty->client); 1814e179ddaSchristos return; 182fe99a117Schristos } 183e271dbb8Schristos log_debug("%s: read %d bytes (already %zu)", name, nread, size); 1844e179ddaSchristos 1854e179ddaSchristos while (tty_keys_next(tty)) 1864e179ddaSchristos ; 1874e179ddaSchristos } 1884e179ddaSchristos 1894e179ddaSchristos static void 190fe99a117Schristos tty_timer_callback(__unused int fd, __unused short events, void *data) 191fe99a117Schristos { 192fe99a117Schristos struct tty *tty = data; 193fe99a117Schristos struct client *c = tty->client; 194fe99a117Schristos struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; 195fe99a117Schristos 196fe99a117Schristos log_debug("%s: %zu discarded", c->name, tty->discarded); 197fe99a117Schristos 1980a274e86Schristos c->flags |= CLIENT_ALLREDRAWFLAGS; 199fe99a117Schristos c->discarded += tty->discarded; 200fe99a117Schristos 201fe99a117Schristos if (tty->discarded < TTY_BLOCK_STOP(tty)) { 202fe99a117Schristos tty->flags &= ~TTY_BLOCK; 203fe99a117Schristos tty_invalidate(tty); 204fe99a117Schristos return; 205fe99a117Schristos } 206fe99a117Schristos tty->discarded = 0; 207fe99a117Schristos evtimer_add(&tty->timer, &tv); 208fe99a117Schristos } 209fe99a117Schristos 210fe99a117Schristos static int 211fe99a117Schristos tty_block_maybe(struct tty *tty) 212fe99a117Schristos { 213fe99a117Schristos struct client *c = tty->client; 214fe99a117Schristos size_t size = EVBUFFER_LENGTH(tty->out); 215fe99a117Schristos struct timeval tv = { .tv_usec = TTY_BLOCK_INTERVAL }; 216fe99a117Schristos 21746548964Swiz if (size == 0) 21846548964Swiz tty->flags &= ~TTY_NOBLOCK; 21946548964Swiz else if (tty->flags & TTY_NOBLOCK) 22046548964Swiz return (0); 22146548964Swiz 222fe99a117Schristos if (size < TTY_BLOCK_START(tty)) 223fe99a117Schristos return (0); 224fe99a117Schristos 225fe99a117Schristos if (tty->flags & TTY_BLOCK) 226fe99a117Schristos return (1); 227fe99a117Schristos tty->flags |= TTY_BLOCK; 228fe99a117Schristos 229fe99a117Schristos log_debug("%s: can't keep up, %zu discarded", c->name, size); 230fe99a117Schristos 231fe99a117Schristos evbuffer_drain(tty->out, size); 232fe99a117Schristos c->discarded += size; 233fe99a117Schristos 234fe99a117Schristos tty->discarded = 0; 235fe99a117Schristos evtimer_add(&tty->timer, &tv); 236fe99a117Schristos return (1); 237fe99a117Schristos } 238fe99a117Schristos 239fe99a117Schristos static void 2404e179ddaSchristos tty_write_callback(__unused int fd, __unused short events, void *data) 2414e179ddaSchristos { 2424e179ddaSchristos struct tty *tty = data; 2434e179ddaSchristos struct client *c = tty->client; 2444e179ddaSchristos size_t size = EVBUFFER_LENGTH(tty->out); 2454e179ddaSchristos int nwrite; 2464e179ddaSchristos 247e271dbb8Schristos nwrite = evbuffer_write(tty->out, c->fd); 2484e179ddaSchristos if (nwrite == -1) 2494e179ddaSchristos return; 2504e179ddaSchristos log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size); 2514e179ddaSchristos 252fe99a117Schristos if (c->redraw > 0) { 253fe99a117Schristos if ((size_t)nwrite >= c->redraw) 254fe99a117Schristos c->redraw = 0; 255fe99a117Schristos else 256fe99a117Schristos c->redraw -= nwrite; 257fe99a117Schristos log_debug("%s: waiting for redraw, %zu bytes left", c->name, 258fe99a117Schristos c->redraw); 259fe99a117Schristos } else if (tty_block_maybe(tty)) 260fe99a117Schristos return; 261fe99a117Schristos 2624e179ddaSchristos if (EVBUFFER_LENGTH(tty->out) != 0) 2634e179ddaSchristos event_add(&tty->event_out, NULL); 2644e179ddaSchristos } 2654e179ddaSchristos 266928fc495Schristos int 2675494e770Schristos tty_open(struct tty *tty, char **cause) 268698d5317Sjmmv { 269e271dbb8Schristos struct client *c = tty->client; 270e271dbb8Schristos 271e271dbb8Schristos tty->term = tty_term_create(tty, c->term_name, c->term_caps, 272e271dbb8Schristos c->term_ncaps, &c->term_features, cause); 273698d5317Sjmmv if (tty->term == NULL) { 274698d5317Sjmmv tty_close(tty); 275698d5317Sjmmv return (-1); 276698d5317Sjmmv } 277698d5317Sjmmv tty->flags |= TTY_OPENED; 278698d5317Sjmmv 279fe99a117Schristos tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER); 280698d5317Sjmmv 281e271dbb8Schristos event_set(&tty->event_in, c->fd, EV_PERSIST|EV_READ, 2824e179ddaSchristos tty_read_callback, tty); 2834e179ddaSchristos tty->in = evbuffer_new(); 2840a274e86Schristos if (tty->in == NULL) 2850a274e86Schristos fatal("out of memory"); 2864e179ddaSchristos 287e271dbb8Schristos event_set(&tty->event_out, c->fd, EV_WRITE, tty_write_callback, tty); 2884e179ddaSchristos tty->out = evbuffer_new(); 2890a274e86Schristos if (tty->out == NULL) 2900a274e86Schristos fatal("out of memory"); 291698d5317Sjmmv 292fe99a117Schristos evtimer_set(&tty->timer, tty_timer_callback, tty); 293fe99a117Schristos 294698d5317Sjmmv tty_start_tty(tty); 295928fc495Schristos tty_keys_build(tty); 296698d5317Sjmmv 297698d5317Sjmmv return (0); 298698d5317Sjmmv } 299698d5317Sjmmv 30068e6ba84Schristos static void 30168e6ba84Schristos tty_start_timer_callback(__unused int fd, __unused short events, void *data) 30268e6ba84Schristos { 30368e6ba84Schristos struct tty *tty = data; 30468e6ba84Schristos struct client *c = tty->client; 30568e6ba84Schristos 30668e6ba84Schristos log_debug("%s: start timer fired", c->name); 307f844e94eSwiz if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0) 308e271dbb8Schristos tty_update_features(tty); 309f844e94eSwiz tty->flags |= TTY_ALL_REQUEST_FLAGS; 31068e6ba84Schristos } 31168e6ba84Schristos 312698d5317Sjmmv void 3134e179ddaSchristos tty_start_tty(struct tty *tty) 314698d5317Sjmmv { 315fe99a117Schristos struct client *c = tty->client; 316698d5317Sjmmv struct termios tio; 31746548964Swiz struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; 318698d5317Sjmmv 319e271dbb8Schristos setblocking(c->fd, 0); 3204e179ddaSchristos event_add(&tty->event_in, NULL); 321698d5317Sjmmv 3224e179ddaSchristos memcpy(&tio, &tty->tio, sizeof tio); 323698d5317Sjmmv tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP); 324698d5317Sjmmv tio.c_iflag |= IGNBRK; 325698d5317Sjmmv tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET); 326e271dbb8Schristos tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|ECHOPRT| 327e271dbb8Schristos ECHOKE|ISIG); 328698d5317Sjmmv tio.c_cc[VMIN] = 1; 329698d5317Sjmmv tio.c_cc[VTIME] = 0; 330e271dbb8Schristos if (tcsetattr(c->fd, TCSANOW, &tio) == 0) 33146548964Swiz tcflush(c->fd, TCOFLUSH); 332928fc495Schristos 333698d5317Sjmmv tty_putcode(tty, TTYC_SMCUP); 334698d5317Sjmmv 3354e179ddaSchristos tty_putcode(tty, TTYC_SMKX); 336698d5317Sjmmv tty_putcode(tty, TTYC_CLEAR); 337698d5317Sjmmv 338fe99a117Schristos if (tty_acs_needed(tty)) { 339fe99a117Schristos log_debug("%s: using capabilities for ACS", c->name); 340fe99a117Schristos tty_putcode(tty, TTYC_ENACS); 341fe99a117Schristos } else 342fe99a117Schristos log_debug("%s: using UTF-8 for ACS", c->name); 343fe99a117Schristos 344698d5317Sjmmv tty_putcode(tty, TTYC_CNORM); 34568e6ba84Schristos if (tty_term_has(tty->term, TTYC_KMOUS)) { 34668e6ba84Schristos tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l"); 34768e6ba84Schristos tty_puts(tty, "\033[?1006l\033[?1005l"); 34868e6ba84Schristos } 349f844e94eSwiz if (tty_term_has(tty->term, TTYC_ENBP)) 350f844e94eSwiz tty_putcode(tty, TTYC_ENBP); 351928fc495Schristos 35268e6ba84Schristos evtimer_set(&tty->start_timer, tty_start_timer_callback, tty); 35368e6ba84Schristos evtimer_add(&tty->start_timer, &tv); 354698d5317Sjmmv 355698d5317Sjmmv tty->flags |= TTY_STARTED; 3564e179ddaSchristos tty_invalidate(tty); 357d530c4d0Sjmmv 35846548964Swiz if (tty->ccolour != -1) 35946548964Swiz tty_force_cursor_colour(tty, -1); 360698d5317Sjmmv 3615494e770Schristos tty->mouse_drag_flag = 0; 3625494e770Schristos tty->mouse_drag_update = NULL; 3635494e770Schristos tty->mouse_drag_release = NULL; 364928fc495Schristos } 365928fc495Schristos 366928fc495Schristos void 367e271dbb8Schristos tty_send_requests(struct tty *tty) 368e271dbb8Schristos { 369e271dbb8Schristos if (~tty->flags & TTY_STARTED) 370e271dbb8Schristos return; 371e271dbb8Schristos 372e271dbb8Schristos if (tty->term->flags & TERM_VT100LIKE) { 373f844e94eSwiz if (~tty->term->flags & TTY_HAVEDA) 374f844e94eSwiz tty_puts(tty, "\033[c"); 375f844e94eSwiz if (~tty->flags & TTY_HAVEDA2) 376e271dbb8Schristos tty_puts(tty, "\033[>c"); 377e271dbb8Schristos if (~tty->flags & TTY_HAVEXDA) 378e271dbb8Schristos tty_puts(tty, "\033[>q"); 379f844e94eSwiz tty_puts(tty, "\033]10;?\033\\"); 380f844e94eSwiz tty_puts(tty, "\033]11;?\033\\"); 381e271dbb8Schristos } else 382f844e94eSwiz tty->flags |= TTY_ALL_REQUEST_FLAGS; 383f844e94eSwiz tty->last_requests = time(NULL); 384f844e94eSwiz } 385f844e94eSwiz 386f844e94eSwiz void 387f844e94eSwiz tty_repeat_requests(struct tty *tty) 388f844e94eSwiz { 389f844e94eSwiz time_t t = time(NULL); 390f844e94eSwiz 391f844e94eSwiz if (~tty->flags & TTY_STARTED) 392f844e94eSwiz return; 393f844e94eSwiz 394f844e94eSwiz if (t - tty->last_requests <= TTY_REQUEST_LIMIT) 395f844e94eSwiz return; 396f844e94eSwiz tty->last_requests = t; 397f844e94eSwiz 398f844e94eSwiz if (tty->term->flags & TERM_VT100LIKE) { 399f844e94eSwiz tty_puts(tty, "\033]10;?\033\\"); 400f844e94eSwiz tty_puts(tty, "\033]11;?\033\\"); 401f844e94eSwiz } 402e271dbb8Schristos } 403e271dbb8Schristos 404e271dbb8Schristos void 405698d5317Sjmmv tty_stop_tty(struct tty *tty) 406698d5317Sjmmv { 407e271dbb8Schristos struct client *c = tty->client; 408698d5317Sjmmv struct winsize ws; 409698d5317Sjmmv 410698d5317Sjmmv if (!(tty->flags & TTY_STARTED)) 411698d5317Sjmmv return; 412698d5317Sjmmv tty->flags &= ~TTY_STARTED; 413698d5317Sjmmv 41468e6ba84Schristos evtimer_del(&tty->start_timer); 41568e6ba84Schristos 416fe99a117Schristos event_del(&tty->timer); 417fe99a117Schristos tty->flags &= ~TTY_BLOCK; 418fe99a117Schristos 4194e179ddaSchristos event_del(&tty->event_in); 4204e179ddaSchristos event_del(&tty->event_out); 421698d5317Sjmmv 422698d5317Sjmmv /* 423698d5317Sjmmv * Be flexible about error handling and try not kill the server just 424698d5317Sjmmv * because the fd is invalid. Things like ssh -t can easily leave us 425698d5317Sjmmv * with a dead tty. 426698d5317Sjmmv */ 427e271dbb8Schristos if (ioctl(c->fd, TIOCGWINSZ, &ws) == -1) 428698d5317Sjmmv return; 429e271dbb8Schristos if (tcsetattr(c->fd, TCSANOW, &tty->tio) == -1) 430698d5317Sjmmv return; 431698d5317Sjmmv 432f844e94eSwiz tty_raw(tty, tty_term_string_ii(tty->term, TTYC_CSR, 0, ws.ws_row - 1)); 433fe99a117Schristos if (tty_acs_needed(tty)) 434698d5317Sjmmv tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS)); 435698d5317Sjmmv tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0)); 436698d5317Sjmmv tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX)); 437698d5317Sjmmv tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR)); 43846548964Swiz if (tty->cstyle != SCREEN_CURSOR_DEFAULT) { 439928fc495Schristos if (tty_term_has(tty->term, TTYC_SE)) 440928fc495Schristos tty_raw(tty, tty_term_string(tty->term, TTYC_SE)); 44146548964Swiz else if (tty_term_has(tty->term, TTYC_SS)) 442f844e94eSwiz tty_raw(tty, tty_term_string_i(tty->term, TTYC_SS, 0)); 443d530c4d0Sjmmv } 44446548964Swiz if (tty->ccolour != -1) 445d530c4d0Sjmmv tty_raw(tty, tty_term_string(tty->term, TTYC_CR)); 446698d5317Sjmmv 447698d5317Sjmmv tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM)); 44868e6ba84Schristos if (tty_term_has(tty->term, TTYC_KMOUS)) { 44968e6ba84Schristos tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l"); 45068e6ba84Schristos tty_raw(tty, "\033[?1006l\033[?1005l"); 45168e6ba84Schristos } 452f844e94eSwiz if (tty_term_has(tty->term, TTYC_DSBP)) 453f844e94eSwiz tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP)); 454928fc495Schristos 455e271dbb8Schristos if (tty->term->flags & TERM_VT100LIKE) 456e271dbb8Schristos tty_raw(tty, "\033[?7727l"); 457e271dbb8Schristos tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS)); 458e271dbb8Schristos tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS)); 459698d5317Sjmmv 4604e179ddaSchristos if (tty_use_margin(tty)) 461e271dbb8Schristos tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG)); 462698d5317Sjmmv tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP)); 463928fc495Schristos 464e271dbb8Schristos setblocking(c->fd, 1); 465698d5317Sjmmv } 466698d5317Sjmmv 467698d5317Sjmmv void 468698d5317Sjmmv tty_close(struct tty *tty) 469698d5317Sjmmv { 470928fc495Schristos if (event_initialized(&tty->key_timer)) 471698d5317Sjmmv evtimer_del(&tty->key_timer); 472698d5317Sjmmv tty_stop_tty(tty); 473698d5317Sjmmv 474698d5317Sjmmv if (tty->flags & TTY_OPENED) { 4754e179ddaSchristos evbuffer_free(tty->in); 4764e179ddaSchristos event_del(&tty->event_in); 4774e179ddaSchristos evbuffer_free(tty->out); 4784e179ddaSchristos event_del(&tty->event_out); 479698d5317Sjmmv 480698d5317Sjmmv tty_term_free(tty->term); 481698d5317Sjmmv tty_keys_free(tty); 482698d5317Sjmmv 483698d5317Sjmmv tty->flags &= ~TTY_OPENED; 484698d5317Sjmmv } 485698d5317Sjmmv } 486698d5317Sjmmv 487698d5317Sjmmv void 488698d5317Sjmmv tty_free(struct tty *tty) 489698d5317Sjmmv { 490698d5317Sjmmv tty_close(tty); 4914e179ddaSchristos } 4924e179ddaSchristos 4934e179ddaSchristos void 494e271dbb8Schristos tty_update_features(struct tty *tty) 4954e179ddaSchristos { 496e271dbb8Schristos struct client *c = tty->client; 497e271dbb8Schristos 498e271dbb8Schristos if (tty_apply_features(tty->term, c->term_features)) 499e271dbb8Schristos tty_term_apply_overrides(tty->term); 5004e179ddaSchristos 5014e179ddaSchristos if (tty_use_margin(tty)) 502e271dbb8Schristos tty_putcode(tty, TTYC_ENMG); 503e271dbb8Schristos if (options_get_number(global_options, "extended-keys")) 504e271dbb8Schristos tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS)); 505e271dbb8Schristos if (options_get_number(global_options, "focus-events")) 506e271dbb8Schristos tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS)); 507e271dbb8Schristos if (tty->term->flags & TERM_VT100LIKE) 508e271dbb8Schristos tty_puts(tty, "\033[?7727h"); 509f844e94eSwiz 510f844e94eSwiz /* 511f844e94eSwiz * Features might have changed since the first draw during attach. For 512f844e94eSwiz * example, this happens when DA responses are received. 513f844e94eSwiz */ 514f844e94eSwiz server_redraw_client(c); 515f844e94eSwiz 516f844e94eSwiz tty_invalidate(tty); 517698d5317Sjmmv } 518698d5317Sjmmv 519698d5317Sjmmv void 520698d5317Sjmmv tty_raw(struct tty *tty, const char *s) 521698d5317Sjmmv { 522e271dbb8Schristos struct client *c = tty->client; 523928fc495Schristos ssize_t n, slen; 524928fc495Schristos u_int i; 525928fc495Schristos 526928fc495Schristos slen = strlen(s); 527928fc495Schristos for (i = 0; i < 5; i++) { 528e271dbb8Schristos n = write(c->fd, s, slen); 529928fc495Schristos if (n >= 0) { 530928fc495Schristos s += n; 531928fc495Schristos slen -= n; 532928fc495Schristos if (slen == 0) 533928fc495Schristos break; 534928fc495Schristos } else if (n == -1 && errno != EAGAIN) 535928fc495Schristos break; 536928fc495Schristos usleep(100); 537928fc495Schristos } 538698d5317Sjmmv } 539698d5317Sjmmv 540698d5317Sjmmv void 541698d5317Sjmmv tty_putcode(struct tty *tty, enum tty_code_code code) 542698d5317Sjmmv { 543698d5317Sjmmv tty_puts(tty, tty_term_string(tty->term, code)); 544698d5317Sjmmv } 545698d5317Sjmmv 546698d5317Sjmmv void 547f844e94eSwiz tty_putcode_i(struct tty *tty, enum tty_code_code code, int a) 548698d5317Sjmmv { 549698d5317Sjmmv if (a < 0) 550698d5317Sjmmv return; 551f844e94eSwiz tty_puts(tty, tty_term_string_i(tty->term, code, a)); 552698d5317Sjmmv } 553698d5317Sjmmv 554698d5317Sjmmv void 555f844e94eSwiz tty_putcode_ii(struct tty *tty, enum tty_code_code code, int a, int b) 556698d5317Sjmmv { 557698d5317Sjmmv if (a < 0 || b < 0) 558698d5317Sjmmv return; 559f844e94eSwiz tty_puts(tty, tty_term_string_ii(tty->term, code, a, b)); 560698d5317Sjmmv } 561698d5317Sjmmv 562698d5317Sjmmv void 563f844e94eSwiz tty_putcode_iii(struct tty *tty, enum tty_code_code code, int a, int b, int c) 564fe99a117Schristos { 565fe99a117Schristos if (a < 0 || b < 0 || c < 0) 566fe99a117Schristos return; 567f844e94eSwiz tty_puts(tty, tty_term_string_iii(tty->term, code, a, b, c)); 568fe99a117Schristos } 569fe99a117Schristos 570fe99a117Schristos void 571f844e94eSwiz tty_putcode_s(struct tty *tty, enum tty_code_code code, const char *a) 572d530c4d0Sjmmv { 573d530c4d0Sjmmv if (a != NULL) 574f844e94eSwiz tty_puts(tty, tty_term_string_s(tty->term, code, a)); 575d530c4d0Sjmmv } 576d530c4d0Sjmmv 577d530c4d0Sjmmv void 578f844e94eSwiz tty_putcode_ss(struct tty *tty, enum tty_code_code code, const char *a, 579f844e94eSwiz const char *b) 580d530c4d0Sjmmv { 581d530c4d0Sjmmv if (a != NULL && b != NULL) 582f844e94eSwiz tty_puts(tty, tty_term_string_ss(tty->term, code, a, b)); 583d530c4d0Sjmmv } 584d530c4d0Sjmmv 5854e179ddaSchristos static void 5864e179ddaSchristos tty_add(struct tty *tty, const char *buf, size_t len) 5874e179ddaSchristos { 5884e179ddaSchristos struct client *c = tty->client; 5894e179ddaSchristos 590fe99a117Schristos if (tty->flags & TTY_BLOCK) { 591fe99a117Schristos tty->discarded += len; 592fe99a117Schristos return; 593fe99a117Schristos } 594fe99a117Schristos 5954e179ddaSchristos evbuffer_add(tty->out, buf, len); 596fe99a117Schristos log_debug("%s: %.*s", c->name, (int)len, buf); 597fe99a117Schristos c->written += len; 5984e179ddaSchristos 5994e179ddaSchristos if (tty_log_fd != -1) 6004e179ddaSchristos write(tty_log_fd, buf, len); 6014e179ddaSchristos if (tty->flags & TTY_STARTED) 6024e179ddaSchristos event_add(&tty->event_out, NULL); 6034e179ddaSchristos } 6044e179ddaSchristos 605d530c4d0Sjmmv void 606698d5317Sjmmv tty_puts(struct tty *tty, const char *s) 607698d5317Sjmmv { 6084e179ddaSchristos if (*s != '\0') 6094e179ddaSchristos tty_add(tty, s, strlen(s)); 610698d5317Sjmmv } 611698d5317Sjmmv 612698d5317Sjmmv void 613698d5317Sjmmv tty_putc(struct tty *tty, u_char ch) 614698d5317Sjmmv { 615698d5317Sjmmv const char *acs; 616698d5317Sjmmv 617e271dbb8Schristos if ((tty->term->flags & TERM_NOAM) && 61830744affSchristos ch >= 0x20 && ch != 0x7f && 61930744affSchristos tty->cy == tty->sy - 1 && 62030744affSchristos tty->cx + 1 >= tty->sx) 62130744affSchristos return; 62230744affSchristos 623698d5317Sjmmv if (tty->cell.attr & GRID_ATTR_CHARSET) { 624698d5317Sjmmv acs = tty_acs_get(tty, ch); 625698d5317Sjmmv if (acs != NULL) 6264e179ddaSchristos tty_add(tty, acs, strlen(acs)); 627698d5317Sjmmv else 628e9a2d6faSchristos tty_add(tty, (char *)&ch, 1); 629698d5317Sjmmv } else 630e9a2d6faSchristos tty_add(tty, (char *)&ch, 1); 631698d5317Sjmmv 632698d5317Sjmmv if (ch >= 0x20 && ch != 0x7f) { 6334e179ddaSchristos if (tty->cx >= tty->sx) { 634698d5317Sjmmv tty->cx = 1; 635698d5317Sjmmv if (tty->cy != tty->rlower) 636698d5317Sjmmv tty->cy++; 6374e179ddaSchristos 6384e179ddaSchristos /* 639e271dbb8Schristos * On !am terminals, force the cursor position to where 640e271dbb8Schristos * we think it should be after a line wrap - this means 641e271dbb8Schristos * it works on sensible terminals as well. 6424e179ddaSchristos */ 643e271dbb8Schristos if (tty->term->flags & TERM_NOAM) 644f844e94eSwiz tty_putcode_ii(tty, TTYC_CUP, tty->cy, tty->cx); 645698d5317Sjmmv } else 646698d5317Sjmmv tty->cx++; 647698d5317Sjmmv } 648698d5317Sjmmv } 649698d5317Sjmmv 650698d5317Sjmmv void 651928fc495Schristos tty_putn(struct tty *tty, const void *buf, size_t len, u_int width) 652698d5317Sjmmv { 653e271dbb8Schristos if ((tty->term->flags & TERM_NOAM) && 65430744affSchristos tty->cy == tty->sy - 1 && 65530744affSchristos tty->cx + len >= tty->sx) 65630744affSchristos len = tty->sx - tty->cx - 1; 65730744affSchristos 6584e179ddaSchristos tty_add(tty, buf, len); 659fe99a117Schristos if (tty->cx + width > tty->sx) { 660fe99a117Schristos tty->cx = (tty->cx + width) - tty->sx; 661fe99a117Schristos if (tty->cx <= tty->sx) 662fe99a117Schristos tty->cy++; 6634e179ddaSchristos else 664fe99a117Schristos tty->cx = tty->cy = UINT_MAX; 665fe99a117Schristos } else 666928fc495Schristos tty->cx += width; 667698d5317Sjmmv } 668698d5317Sjmmv 6694e179ddaSchristos static void 6705494e770Schristos tty_set_italics(struct tty *tty) 6715494e770Schristos { 6725494e770Schristos const char *s; 6735494e770Schristos 6745494e770Schristos if (tty_term_has(tty->term, TTYC_SITM)) { 675ed4e6cd4Schristos s = options_get_string(global_options, "default-terminal"); 6765494e770Schristos if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) { 6775494e770Schristos tty_putcode(tty, TTYC_SITM); 6785494e770Schristos return; 6795494e770Schristos } 6805494e770Schristos } 6815494e770Schristos tty_putcode(tty, TTYC_SMSO); 6825494e770Schristos } 6835494e770Schristos 6845494e770Schristos void 685698d5317Sjmmv tty_set_title(struct tty *tty, const char *title) 686698d5317Sjmmv { 687d530c4d0Sjmmv if (!tty_term_has(tty->term, TTYC_TSL) || 688d530c4d0Sjmmv !tty_term_has(tty->term, TTYC_FSL)) 689698d5317Sjmmv return; 690698d5317Sjmmv 691d530c4d0Sjmmv tty_putcode(tty, TTYC_TSL); 692698d5317Sjmmv tty_puts(tty, title); 693d530c4d0Sjmmv tty_putcode(tty, TTYC_FSL); 694698d5317Sjmmv } 695698d5317Sjmmv 69646548964Swiz void 69746548964Swiz tty_set_path(struct tty *tty, const char *title) 698d530c4d0Sjmmv { 69946548964Swiz if (!tty_term_has(tty->term, TTYC_SWD) || 70046548964Swiz !tty_term_has(tty->term, TTYC_FSL)) 70146548964Swiz return; 70246548964Swiz 70346548964Swiz tty_putcode(tty, TTYC_SWD); 70446548964Swiz tty_puts(tty, title); 70546548964Swiz tty_putcode(tty, TTYC_FSL); 70646548964Swiz } 70746548964Swiz 70846548964Swiz static void 70946548964Swiz tty_force_cursor_colour(struct tty *tty, int c) 71046548964Swiz { 71146548964Swiz u_char r, g, b; 712f844e94eSwiz char s[13]; 71346548964Swiz 71446548964Swiz if (c != -1) 71546548964Swiz c = colour_force_rgb(c); 71646548964Swiz if (c == tty->ccolour) 71746548964Swiz return; 71846548964Swiz if (c == -1) 719d530c4d0Sjmmv tty_putcode(tty, TTYC_CR); 72046548964Swiz else { 72146548964Swiz colour_split_rgb(c, &r, &g, &b); 72246548964Swiz xsnprintf(s, sizeof s, "rgb:%02hhx/%02hhx/%02hhx", r, g, b); 723f844e94eSwiz tty_putcode_s(tty, TTYC_CS, s); 72446548964Swiz } 72546548964Swiz tty->ccolour = c; 72646548964Swiz } 72746548964Swiz 72846548964Swiz static int 72946548964Swiz tty_update_cursor(struct tty *tty, int mode, struct screen *s) 73046548964Swiz { 73146548964Swiz enum screen_cursor_style cstyle; 73246548964Swiz int ccolour, changed, cmode = mode; 73346548964Swiz 73446548964Swiz /* Set cursor colour if changed. */ 73546548964Swiz if (s != NULL) { 73646548964Swiz ccolour = s->ccolour; 73746548964Swiz if (s->ccolour == -1) 73846548964Swiz ccolour = s->default_ccolour; 73946548964Swiz tty_force_cursor_colour(tty, ccolour); 74046548964Swiz } 74146548964Swiz 74246548964Swiz /* If cursor is off, set as invisible. */ 74346548964Swiz if (~cmode & MODE_CURSOR) { 74446548964Swiz if (tty->mode & MODE_CURSOR) 74546548964Swiz tty_putcode(tty, TTYC_CIVIS); 74646548964Swiz return (cmode); 74746548964Swiz } 74846548964Swiz 74946548964Swiz /* Check if blinking or very visible flag changed or style changed. */ 75046548964Swiz if (s == NULL) 75146548964Swiz cstyle = tty->cstyle; 75246548964Swiz else { 75346548964Swiz cstyle = s->cstyle; 75446548964Swiz if (cstyle == SCREEN_CURSOR_DEFAULT) { 75546548964Swiz if (~cmode & MODE_CURSOR_BLINKING_SET) { 75646548964Swiz if (s->default_mode & MODE_CURSOR_BLINKING) 75746548964Swiz cmode |= MODE_CURSOR_BLINKING; 758d530c4d0Sjmmv else 75946548964Swiz cmode &= ~MODE_CURSOR_BLINKING; 76046548964Swiz } 76146548964Swiz cstyle = s->default_cstyle; 76246548964Swiz } 76346548964Swiz } 76446548964Swiz 76546548964Swiz /* If nothing changed, do nothing. */ 76646548964Swiz changed = cmode ^ tty->mode; 76746548964Swiz if ((changed & CURSOR_MODES) == 0 && cstyle == tty->cstyle) 76846548964Swiz return (cmode); 76946548964Swiz 77046548964Swiz /* 77146548964Swiz * Set cursor style. If an explicit style has been set with DECSCUSR, 77246548964Swiz * set it if supported, otherwise send cvvis for blinking styles. 77346548964Swiz * 77446548964Swiz * If no style, has been set (SCREEN_CURSOR_DEFAULT), then send cvvis 77546548964Swiz * if either the blinking or very visible flags are set. 77646548964Swiz */ 77746548964Swiz tty_putcode(tty, TTYC_CNORM); 77846548964Swiz switch (cstyle) { 77946548964Swiz case SCREEN_CURSOR_DEFAULT: 78046548964Swiz if (tty->cstyle != SCREEN_CURSOR_DEFAULT) { 78146548964Swiz if (tty_term_has(tty->term, TTYC_SE)) 78246548964Swiz tty_putcode(tty, TTYC_SE); 78346548964Swiz else 784f844e94eSwiz tty_putcode_i(tty, TTYC_SS, 0); 78546548964Swiz } 78646548964Swiz if (cmode & (MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE)) 78746548964Swiz tty_putcode(tty, TTYC_CVVIS); 78846548964Swiz break; 78946548964Swiz case SCREEN_CURSOR_BLOCK: 79046548964Swiz if (tty_term_has(tty->term, TTYC_SS)) { 79146548964Swiz if (cmode & MODE_CURSOR_BLINKING) 792f844e94eSwiz tty_putcode_i(tty, TTYC_SS, 1); 79346548964Swiz else 794f844e94eSwiz tty_putcode_i(tty, TTYC_SS, 2); 79546548964Swiz } else if (cmode & MODE_CURSOR_BLINKING) 79646548964Swiz tty_putcode(tty, TTYC_CVVIS); 79746548964Swiz break; 79846548964Swiz case SCREEN_CURSOR_UNDERLINE: 79946548964Swiz if (tty_term_has(tty->term, TTYC_SS)) { 80046548964Swiz if (cmode & MODE_CURSOR_BLINKING) 801f844e94eSwiz tty_putcode_i(tty, TTYC_SS, 3); 80246548964Swiz else 803f844e94eSwiz tty_putcode_i(tty, TTYC_SS, 4); 80446548964Swiz } else if (cmode & MODE_CURSOR_BLINKING) 80546548964Swiz tty_putcode(tty, TTYC_CVVIS); 80646548964Swiz break; 80746548964Swiz case SCREEN_CURSOR_BAR: 80846548964Swiz if (tty_term_has(tty->term, TTYC_SS)) { 80946548964Swiz if (cmode & MODE_CURSOR_BLINKING) 810f844e94eSwiz tty_putcode_i(tty, TTYC_SS, 5); 81146548964Swiz else 812f844e94eSwiz tty_putcode_i(tty, TTYC_SS, 6); 81346548964Swiz } else if (cmode & MODE_CURSOR_BLINKING) 81446548964Swiz tty_putcode(tty, TTYC_CVVIS); 81546548964Swiz break; 81646548964Swiz } 81746548964Swiz tty->cstyle = cstyle; 81846548964Swiz return (cmode); 819d530c4d0Sjmmv } 820d530c4d0Sjmmv 821d530c4d0Sjmmv void 822d530c4d0Sjmmv tty_update_mode(struct tty *tty, int mode, struct screen *s) 823698d5317Sjmmv { 82446548964Swiz struct tty_term *term = tty->term; 825e271dbb8Schristos struct client *c = tty->client; 826698d5317Sjmmv int changed; 827698d5317Sjmmv 828698d5317Sjmmv if (tty->flags & TTY_NOCURSOR) 829698d5317Sjmmv mode &= ~MODE_CURSOR; 830698d5317Sjmmv 83146548964Swiz if (tty_update_cursor(tty, mode, s) & MODE_CURSOR_BLINKING) 83246548964Swiz mode |= MODE_CURSOR_BLINKING; 833698d5317Sjmmv else 83446548964Swiz mode &= ~MODE_CURSOR_BLINKING; 83546548964Swiz 83646548964Swiz changed = mode ^ tty->mode; 83746548964Swiz if (log_get_level() != 0 && changed != 0) { 83846548964Swiz log_debug("%s: current mode %s", c->name, 83946548964Swiz screen_mode_to_string(tty->mode)); 84046548964Swiz log_debug("%s: setting mode %s", c->name, 84146548964Swiz screen_mode_to_string(mode)); 842d530c4d0Sjmmv } 843e271dbb8Schristos 84446548964Swiz if ((changed & ALL_MOUSE_MODES) && tty_term_has(term, TTYC_KMOUS)) { 845928fc495Schristos /* 84646548964Swiz * If the mouse modes have changed, clear then all and apply 84746548964Swiz * again. There are differences in how terminals track the 84846548964Swiz * various bits. 849928fc495Schristos */ 85046548964Swiz tty_puts(tty, "\033[?1006l\033[?1000l\033[?1002l\033[?1003l"); 851e271dbb8Schristos if (mode & ALL_MOUSE_MODES) 852e271dbb8Schristos tty_puts(tty, "\033[?1006h"); 853e271dbb8Schristos if (mode & MODE_MOUSE_ALL) 85446548964Swiz tty_puts(tty, "\033[?1000h\033[?1002h\033[?1003h"); 85546548964Swiz else if (mode & MODE_MOUSE_BUTTON) 85646548964Swiz tty_puts(tty, "\033[?1000h\033[?1002h"); 85746548964Swiz else if (mode & MODE_MOUSE_STANDARD) 85846548964Swiz tty_puts(tty, "\033[?1000h"); 859698d5317Sjmmv } 860698d5317Sjmmv tty->mode = mode; 861698d5317Sjmmv } 862698d5317Sjmmv 8634e179ddaSchristos static void 8645494e770Schristos tty_emulate_repeat(struct tty *tty, enum tty_code_code code, 8655494e770Schristos enum tty_code_code code1, u_int n) 866698d5317Sjmmv { 867698d5317Sjmmv if (tty_term_has(tty->term, code)) 868f844e94eSwiz tty_putcode_i(tty, code, n); 869698d5317Sjmmv else { 870698d5317Sjmmv while (n-- > 0) 871698d5317Sjmmv tty_putcode(tty, code1); 872698d5317Sjmmv } 873698d5317Sjmmv } 874698d5317Sjmmv 8754e179ddaSchristos static void 876928fc495Schristos tty_repeat_space(struct tty *tty, u_int n) 877928fc495Schristos { 8784e179ddaSchristos static char s[500]; 8794e179ddaSchristos 8804e179ddaSchristos if (*s != ' ') 8814e179ddaSchristos memset(s, ' ', sizeof s); 8824e179ddaSchristos 8834e179ddaSchristos while (n > sizeof s) { 8844e179ddaSchristos tty_putn(tty, s, sizeof s, sizeof s); 8854e179ddaSchristos n -= sizeof s; 8864e179ddaSchristos } 8874e179ddaSchristos if (n != 0) 8884e179ddaSchristos tty_putn(tty, s, n, n); 889928fc495Schristos } 890928fc495Schristos 8910a274e86Schristos /* Is this window bigger than the terminal? */ 8920a274e86Schristos int 8930a274e86Schristos tty_window_bigger(struct tty *tty) 8940a274e86Schristos { 8950a274e86Schristos struct client *c = tty->client; 8960a274e86Schristos struct window *w = c->session->curw->window; 8970a274e86Schristos 8980a274e86Schristos return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy); 8990a274e86Schristos } 9000a274e86Schristos 9010a274e86Schristos /* What offset should this window be drawn at? */ 9020a274e86Schristos int 9030a274e86Schristos tty_window_offset(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) 9040a274e86Schristos { 9050a274e86Schristos *ox = tty->oox; 9060a274e86Schristos *oy = tty->ooy; 9070a274e86Schristos *sx = tty->osx; 9080a274e86Schristos *sy = tty->osy; 9090a274e86Schristos 9100a274e86Schristos return (tty->oflag); 9110a274e86Schristos } 9120a274e86Schristos 9130a274e86Schristos /* What offset should this window be drawn at? */ 9140a274e86Schristos static int 9150a274e86Schristos tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) 9160a274e86Schristos { 9170a274e86Schristos struct client *c = tty->client; 9180a274e86Schristos struct window *w = c->session->curw->window; 919e271dbb8Schristos struct window_pane *wp = server_client_get_pane(c); 9200a274e86Schristos u_int cx, cy, lines; 9210a274e86Schristos 9220a274e86Schristos lines = status_line_size(c); 9230a274e86Schristos 9240a274e86Schristos if (tty->sx >= w->sx && tty->sy - lines >= w->sy) { 9250a274e86Schristos *ox = 0; 9260a274e86Schristos *oy = 0; 9270a274e86Schristos *sx = w->sx; 9280a274e86Schristos *sy = w->sy; 9290a274e86Schristos 9300a274e86Schristos c->pan_window = NULL; 9310a274e86Schristos return (0); 9320a274e86Schristos } 9330a274e86Schristos 9340a274e86Schristos *sx = tty->sx; 9350a274e86Schristos *sy = tty->sy - lines; 9360a274e86Schristos 9370a274e86Schristos if (c->pan_window == w) { 9380a274e86Schristos if (*sx >= w->sx) 9390a274e86Schristos c->pan_ox = 0; 9400a274e86Schristos else if (c->pan_ox + *sx > w->sx) 9410a274e86Schristos c->pan_ox = w->sx - *sx; 9420a274e86Schristos *ox = c->pan_ox; 9430a274e86Schristos if (*sy >= w->sy) 9440a274e86Schristos c->pan_oy = 0; 9450a274e86Schristos else if (c->pan_oy + *sy > w->sy) 9460a274e86Schristos c->pan_oy = w->sy - *sy; 9470a274e86Schristos *oy = c->pan_oy; 9480a274e86Schristos return (1); 9490a274e86Schristos } 9500a274e86Schristos 9510a274e86Schristos if (~wp->screen->mode & MODE_CURSOR) { 9520a274e86Schristos *ox = 0; 9530a274e86Schristos *oy = 0; 9540a274e86Schristos } else { 9550a274e86Schristos cx = wp->xoff + wp->screen->cx; 9560a274e86Schristos cy = wp->yoff + wp->screen->cy; 9570a274e86Schristos 9580a274e86Schristos if (cx < *sx) 9590a274e86Schristos *ox = 0; 9600a274e86Schristos else if (cx > w->sx - *sx) 9610a274e86Schristos *ox = w->sx - *sx; 9620a274e86Schristos else 9630a274e86Schristos *ox = cx - *sx / 2; 9640a274e86Schristos 9650a274e86Schristos if (cy < *sy) 9660a274e86Schristos *oy = 0; 9670a274e86Schristos else if (cy > w->sy - *sy) 9680a274e86Schristos *oy = w->sy - *sy; 9690a274e86Schristos else 9700a274e86Schristos *oy = cy - *sy / 2; 9710a274e86Schristos } 9720a274e86Schristos 9730a274e86Schristos c->pan_window = NULL; 9740a274e86Schristos return (1); 9750a274e86Schristos } 9760a274e86Schristos 9770a274e86Schristos /* Update stored offsets for a window and redraw if necessary. */ 9780a274e86Schristos void 9790a274e86Schristos tty_update_window_offset(struct window *w) 9800a274e86Schristos { 9810a274e86Schristos struct client *c; 9820a274e86Schristos 9830a274e86Schristos TAILQ_FOREACH(c, &clients, entry) { 98446548964Swiz if (c->session != NULL && 98546548964Swiz c->session->curw != NULL && 98646548964Swiz c->session->curw->window == w) 9870a274e86Schristos tty_update_client_offset(c); 9880a274e86Schristos } 9890a274e86Schristos } 9900a274e86Schristos 9910a274e86Schristos /* Update stored offsets for a client and redraw if necessary. */ 9920a274e86Schristos void 9930a274e86Schristos tty_update_client_offset(struct client *c) 9940a274e86Schristos { 9950a274e86Schristos u_int ox, oy, sx, sy; 9960a274e86Schristos 9970a274e86Schristos if (~c->flags & CLIENT_TERMINAL) 9980a274e86Schristos return; 9990a274e86Schristos 10000a274e86Schristos c->tty.oflag = tty_window_offset1(&c->tty, &ox, &oy, &sx, &sy); 10010a274e86Schristos if (ox == c->tty.oox && 10020a274e86Schristos oy == c->tty.ooy && 10030a274e86Schristos sx == c->tty.osx && 10040a274e86Schristos sy == c->tty.osy) 10050a274e86Schristos return; 10060a274e86Schristos 10070a274e86Schristos log_debug ("%s: %s offset has changed (%u,%u %ux%u -> %u,%u %ux%u)", 10080a274e86Schristos __func__, c->name, c->tty.oox, c->tty.ooy, c->tty.osx, c->tty.osy, 10090a274e86Schristos ox, oy, sx, sy); 10100a274e86Schristos 10110a274e86Schristos c->tty.oox = ox; 10120a274e86Schristos c->tty.ooy = oy; 10130a274e86Schristos c->tty.osx = sx; 10140a274e86Schristos c->tty.osy = sy; 10150a274e86Schristos 10160a274e86Schristos c->flags |= (CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS); 10170a274e86Schristos } 10180a274e86Schristos 1019928fc495Schristos /* 1020928fc495Schristos * Is the region large enough to be worth redrawing once later rather than 1021928fc495Schristos * probably several times now? Currently yes if it is more than 50% of the 1022928fc495Schristos * pane. 1023928fc495Schristos */ 10244e179ddaSchristos static int 1025ed4e6cd4Schristos tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx) 1026928fc495Schristos { 1027e271dbb8Schristos return (ctx->orlower - ctx->orupper >= ctx->sy / 2); 1028928fc495Schristos } 1029928fc495Schristos 1030698d5317Sjmmv /* 10315494e770Schristos * Return if BCE is needed but the terminal doesn't have it - it'll need to be 10325494e770Schristos * emulated. 10335494e770Schristos */ 10344e179ddaSchristos static int 1035e271dbb8Schristos tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg) 10365494e770Schristos { 10374e179ddaSchristos if (tty_term_flag(tty->term, TTYC_BCE)) 10384e179ddaSchristos return (0); 1039e271dbb8Schristos if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc->bg)) 10404e179ddaSchristos return (1); 10415494e770Schristos return (0); 10425494e770Schristos } 10435494e770Schristos 10445494e770Schristos /* 1045698d5317Sjmmv * Redraw scroll region using data from screen (already updated). Used when 1046698d5317Sjmmv * CSR not supported, or window is a pane that doesn't take up the full 1047698d5317Sjmmv * width of the terminal. 1048698d5317Sjmmv */ 10494e179ddaSchristos static void 1050698d5317Sjmmv tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx) 1051698d5317Sjmmv { 1052e271dbb8Schristos struct client *c = tty->client; 1053698d5317Sjmmv u_int i; 1054698d5317Sjmmv 1055698d5317Sjmmv /* 1056e271dbb8Schristos * If region is large, schedule a redraw. In most cases this is likely 1057e271dbb8Schristos * to be followed by some more scrolling. 1058698d5317Sjmmv */ 1059928fc495Schristos if (tty_large_region(tty, ctx)) { 1060e271dbb8Schristos log_debug("%s: %s large redraw", __func__, c->name); 1061e271dbb8Schristos ctx->redraw_cb(ctx); 1062698d5317Sjmmv return; 1063698d5317Sjmmv } 1064698d5317Sjmmv 1065698d5317Sjmmv for (i = ctx->orupper; i <= ctx->orlower; i++) 10660a274e86Schristos tty_draw_pane(tty, ctx, i); 1067698d5317Sjmmv } 1068698d5317Sjmmv 10690a274e86Schristos /* Is this position visible in the pane? */ 10700a274e86Schristos static int 1071e271dbb8Schristos tty_is_visible(__unused struct tty *tty, const struct tty_ctx *ctx, u_int px, 1072e271dbb8Schristos u_int py, u_int nx, u_int ny) 1073fe99a117Schristos { 1074e271dbb8Schristos u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py; 10750a274e86Schristos 10760a274e86Schristos if (!ctx->bigger) 10770a274e86Schristos return (1); 10780a274e86Schristos 1079e271dbb8Schristos if (xoff + nx <= ctx->wox || xoff >= ctx->wox + ctx->wsx || 1080e271dbb8Schristos yoff + ny <= ctx->woy || yoff >= ctx->woy + ctx->wsy) 10810a274e86Schristos return (0); 10820a274e86Schristos return (1); 10830a274e86Schristos } 10840a274e86Schristos 10850a274e86Schristos /* Clamp line position to visible part of pane. */ 10860a274e86Schristos static int 10870a274e86Schristos tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, 10880a274e86Schristos u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry) 10890a274e86Schristos { 1090e271dbb8Schristos u_int xoff = ctx->rxoff + px; 10910a274e86Schristos 10920a274e86Schristos if (!tty_is_visible(tty, ctx, px, py, nx, 1)) 10930a274e86Schristos return (0); 1094e271dbb8Schristos *ry = ctx->yoff + py - ctx->woy; 10950a274e86Schristos 1096e271dbb8Schristos if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) { 10970a274e86Schristos /* All visible. */ 10980a274e86Schristos *i = 0; 1099e271dbb8Schristos *x = ctx->xoff + px - ctx->wox; 11000a274e86Schristos *rx = nx; 1101e271dbb8Schristos } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) { 11020a274e86Schristos /* Both left and right not visible. */ 1103e271dbb8Schristos *i = ctx->wox; 11040a274e86Schristos *x = 0; 1105e271dbb8Schristos *rx = ctx->wsx; 1106e271dbb8Schristos } else if (xoff < ctx->wox) { 11070a274e86Schristos /* Left not visible. */ 1108e271dbb8Schristos *i = ctx->wox - (ctx->xoff + px); 11090a274e86Schristos *x = 0; 11100a274e86Schristos *rx = nx - *i; 11110a274e86Schristos } else { 11120a274e86Schristos /* Right not visible. */ 11130a274e86Schristos *i = 0; 1114e271dbb8Schristos *x = (ctx->xoff + px) - ctx->wox; 1115e271dbb8Schristos *rx = ctx->wsx - *x; 11160a274e86Schristos } 11170a274e86Schristos if (*rx > nx) 11180a274e86Schristos fatalx("%s: x too big, %u > %u", __func__, *rx, nx); 11190a274e86Schristos 11200a274e86Schristos return (1); 11210a274e86Schristos } 11220a274e86Schristos 11230a274e86Schristos /* Clear a line. */ 11240a274e86Schristos static void 1125e271dbb8Schristos tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py, 1126e271dbb8Schristos u_int px, u_int nx, u_int bg) 11270a274e86Schristos { 11280a274e86Schristos struct client *c = tty->client; 112946548964Swiz struct overlay_ranges r; 113046548964Swiz u_int i; 11310a274e86Schristos 11320a274e86Schristos log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); 1133fe99a117Schristos 1134fe99a117Schristos /* Nothing to clear. */ 1135fe99a117Schristos if (nx == 0) 1136fe99a117Schristos return; 1137fe99a117Schristos 1138fe99a117Schristos /* If genuine BCE is available, can try escape sequences. */ 113946548964Swiz if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) { 1140fe99a117Schristos /* Off the end of the line, use EL if available. */ 1141fe99a117Schristos if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) { 1142fe99a117Schristos tty_cursor(tty, px, py); 1143fe99a117Schristos tty_putcode(tty, TTYC_EL); 1144fe99a117Schristos return; 1145fe99a117Schristos } 1146fe99a117Schristos 1147fe99a117Schristos /* At the start of the line. Use EL1. */ 1148fe99a117Schristos if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) { 1149fe99a117Schristos tty_cursor(tty, px + nx - 1, py); 1150fe99a117Schristos tty_putcode(tty, TTYC_EL1); 1151fe99a117Schristos return; 1152fe99a117Schristos } 1153fe99a117Schristos 1154fe99a117Schristos /* Section of line. Use ECH if possible. */ 1155fe99a117Schristos if (tty_term_has(tty->term, TTYC_ECH)) { 1156fe99a117Schristos tty_cursor(tty, px, py); 1157f844e94eSwiz tty_putcode_i(tty, TTYC_ECH, nx); 1158fe99a117Schristos return; 1159fe99a117Schristos } 1160fe99a117Schristos } 1161fe99a117Schristos 116246548964Swiz /* 116346548964Swiz * Couldn't use an escape sequence, use spaces. Clear only the visible 116446548964Swiz * bit if there is an overlay. 116546548964Swiz */ 116646548964Swiz tty_check_overlay_range(tty, px, py, nx, &r); 116746548964Swiz for (i = 0; i < OVERLAY_MAX_RANGES; i++) { 116846548964Swiz if (r.nx[i] == 0) 116946548964Swiz continue; 117046548964Swiz tty_cursor(tty, r.px[i], py); 117146548964Swiz tty_repeat_space(tty, r.nx[i]); 117246548964Swiz } 1173fe99a117Schristos } 1174fe99a117Schristos 11750a274e86Schristos /* Clear a line, adjusting to visible part of pane. */ 1176fe99a117Schristos static void 11770a274e86Schristos tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py, 11780a274e86Schristos u_int px, u_int nx, u_int bg) 1179fe99a117Schristos { 11800a274e86Schristos struct client *c = tty->client; 11810a274e86Schristos u_int i, x, rx, ry; 11820a274e86Schristos 11830a274e86Schristos log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py); 11840a274e86Schristos 11850a274e86Schristos if (tty_clamp_line(tty, ctx, px, py, nx, &i, &x, &rx, &ry)) 1186e271dbb8Schristos tty_clear_line(tty, &ctx->defaults, ry, x, rx, bg); 11870a274e86Schristos } 11880a274e86Schristos 11890a274e86Schristos /* Clamp area position to visible part of pane. */ 11900a274e86Schristos static int 11910a274e86Schristos tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py, 11920a274e86Schristos u_int nx, u_int ny, u_int *i, u_int *j, u_int *x, u_int *y, u_int *rx, 11930a274e86Schristos u_int *ry) 11940a274e86Schristos { 1195e271dbb8Schristos u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py; 11960a274e86Schristos 11970a274e86Schristos if (!tty_is_visible(tty, ctx, px, py, nx, ny)) 11980a274e86Schristos return (0); 11990a274e86Schristos 1200e271dbb8Schristos if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) { 12010a274e86Schristos /* All visible. */ 12020a274e86Schristos *i = 0; 1203e271dbb8Schristos *x = ctx->xoff + px - ctx->wox; 12040a274e86Schristos *rx = nx; 1205e271dbb8Schristos } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) { 12060a274e86Schristos /* Both left and right not visible. */ 1207e271dbb8Schristos *i = ctx->wox; 12080a274e86Schristos *x = 0; 1209e271dbb8Schristos *rx = ctx->wsx; 1210e271dbb8Schristos } else if (xoff < ctx->wox) { 12110a274e86Schristos /* Left not visible. */ 1212e271dbb8Schristos *i = ctx->wox - (ctx->xoff + px); 12130a274e86Schristos *x = 0; 12140a274e86Schristos *rx = nx - *i; 12150a274e86Schristos } else { 12160a274e86Schristos /* Right not visible. */ 12170a274e86Schristos *i = 0; 1218e271dbb8Schristos *x = (ctx->xoff + px) - ctx->wox; 1219e271dbb8Schristos *rx = ctx->wsx - *x; 12200a274e86Schristos } 12210a274e86Schristos if (*rx > nx) 12220a274e86Schristos fatalx("%s: x too big, %u > %u", __func__, *rx, nx); 12230a274e86Schristos 1224e271dbb8Schristos if (yoff >= ctx->woy && yoff + ny <= ctx->woy + ctx->wsy) { 12250a274e86Schristos /* All visible. */ 12260a274e86Schristos *j = 0; 1227e271dbb8Schristos *y = ctx->yoff + py - ctx->woy; 12280a274e86Schristos *ry = ny; 1229e271dbb8Schristos } else if (yoff < ctx->woy && yoff + ny > ctx->woy + ctx->wsy) { 123030744affSchristos /* Both top and bottom not visible. */ 1231e271dbb8Schristos *j = ctx->woy; 12320a274e86Schristos *y = 0; 1233e271dbb8Schristos *ry = ctx->wsy; 1234e271dbb8Schristos } else if (yoff < ctx->woy) { 123530744affSchristos /* Top not visible. */ 1236e271dbb8Schristos *j = ctx->woy - (ctx->yoff + py); 12370a274e86Schristos *y = 0; 12380a274e86Schristos *ry = ny - *j; 12390a274e86Schristos } else { 124030744affSchristos /* Bottom not visible. */ 12410a274e86Schristos *j = 0; 1242e271dbb8Schristos *y = (ctx->yoff + py) - ctx->woy; 1243e271dbb8Schristos *ry = ctx->wsy - *y; 12440a274e86Schristos } 12450a274e86Schristos if (*ry > ny) 12460a274e86Schristos fatalx("%s: y too big, %u > %u", __func__, *ry, ny); 12470a274e86Schristos 12480a274e86Schristos return (1); 12490a274e86Schristos } 12500a274e86Schristos 12510a274e86Schristos /* Clear an area, adjusting to visible part of pane. */ 12520a274e86Schristos static void 1253e271dbb8Schristos tty_clear_area(struct tty *tty, const struct grid_cell *defaults, u_int py, 1254e271dbb8Schristos u_int ny, u_int px, u_int nx, u_int bg) 12550a274e86Schristos { 12560a274e86Schristos struct client *c = tty->client; 1257fe99a117Schristos u_int yy; 1258fe99a117Schristos char tmp[64]; 1259fe99a117Schristos 12600a274e86Schristos log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py); 1261fe99a117Schristos 1262fe99a117Schristos /* Nothing to clear. */ 1263fe99a117Schristos if (nx == 0 || ny == 0) 1264fe99a117Schristos return; 1265fe99a117Schristos 1266fe99a117Schristos /* If genuine BCE is available, can try escape sequences. */ 126746548964Swiz if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) { 1268fe99a117Schristos /* Use ED if clearing off the bottom of the terminal. */ 1269fe99a117Schristos if (px == 0 && 1270fe99a117Schristos px + nx >= tty->sx && 1271fe99a117Schristos py + ny >= tty->sy && 1272fe99a117Schristos tty_term_has(tty->term, TTYC_ED)) { 1273fe99a117Schristos tty_cursor(tty, 0, py); 1274fe99a117Schristos tty_putcode(tty, TTYC_ED); 1275fe99a117Schristos return; 1276fe99a117Schristos } 1277fe99a117Schristos 1278fe99a117Schristos /* 1279fe99a117Schristos * On VT420 compatible terminals we can use DECFRA if the 1280fe99a117Schristos * background colour isn't default (because it doesn't work 1281fe99a117Schristos * after SGR 0). 1282fe99a117Schristos */ 1283e271dbb8Schristos if ((tty->term->flags & TERM_DECFRA) && !COLOUR_DEFAULT(bg)) { 1284fe99a117Schristos xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x", 1285fe99a117Schristos py + 1, px + 1, py + ny, px + nx); 1286fe99a117Schristos tty_puts(tty, tmp); 1287fe99a117Schristos return; 1288fe99a117Schristos } 1289fe99a117Schristos 1290fe99a117Schristos /* Full lines can be scrolled away to clear them. */ 1291fe99a117Schristos if (px == 0 && 1292fe99a117Schristos px + nx >= tty->sx && 1293fe99a117Schristos ny > 2 && 1294fe99a117Schristos tty_term_has(tty->term, TTYC_CSR) && 1295fe99a117Schristos tty_term_has(tty->term, TTYC_INDN)) { 1296fe99a117Schristos tty_region(tty, py, py + ny - 1); 1297fe99a117Schristos tty_margin_off(tty); 1298f844e94eSwiz tty_putcode_i(tty, TTYC_INDN, ny); 1299fe99a117Schristos return; 1300fe99a117Schristos } 1301fe99a117Schristos 1302fe99a117Schristos /* 1303fe99a117Schristos * If margins are supported, can just scroll the area off to 1304fe99a117Schristos * clear it. 1305fe99a117Schristos */ 1306fe99a117Schristos if (nx > 2 && 1307fe99a117Schristos ny > 2 && 1308fe99a117Schristos tty_term_has(tty->term, TTYC_CSR) && 1309fe99a117Schristos tty_use_margin(tty) && 1310fe99a117Schristos tty_term_has(tty->term, TTYC_INDN)) { 1311fe99a117Schristos tty_region(tty, py, py + ny - 1); 1312fe99a117Schristos tty_margin(tty, px, px + nx - 1); 1313f844e94eSwiz tty_putcode_i(tty, TTYC_INDN, ny); 1314fe99a117Schristos return; 1315fe99a117Schristos } 1316fe99a117Schristos } 1317fe99a117Schristos 1318fe99a117Schristos /* Couldn't use an escape sequence, loop over the lines. */ 1319fe99a117Schristos for (yy = py; yy < py + ny; yy++) 1320e271dbb8Schristos tty_clear_line(tty, defaults, yy, px, nx, bg); 1321fe99a117Schristos } 1322fe99a117Schristos 13230a274e86Schristos /* Clear an area in a pane. */ 13240a274e86Schristos static void 13250a274e86Schristos tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py, 13260a274e86Schristos u_int ny, u_int px, u_int nx, u_int bg) 13275494e770Schristos { 13280a274e86Schristos u_int i, j, x, y, rx, ry; 13290a274e86Schristos 13300a274e86Schristos if (tty_clamp_area(tty, ctx, px, py, nx, ny, &i, &j, &x, &y, &rx, &ry)) 1331e271dbb8Schristos tty_clear_area(tty, &ctx->defaults, y, ry, x, rx, bg); 13320a274e86Schristos } 13330a274e86Schristos 13340a274e86Schristos static void 13350a274e86Schristos tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py) 13360a274e86Schristos { 1337e271dbb8Schristos struct screen *s = ctx->s; 1338e271dbb8Schristos u_int nx = ctx->sx, i, x, rx, ry; 13390a274e86Schristos 13400a274e86Schristos log_debug("%s: %s %u %d", __func__, tty->client->name, py, ctx->bigger); 13410a274e86Schristos 13420a274e86Schristos if (!ctx->bigger) { 1343e271dbb8Schristos tty_draw_line(tty, s, 0, py, nx, ctx->xoff, ctx->yoff + py, 1344e271dbb8Schristos &ctx->defaults, ctx->palette); 13450a274e86Schristos return; 13460a274e86Schristos } 1347e271dbb8Schristos if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) { 1348e271dbb8Schristos tty_draw_line(tty, s, i, py, rx, x, ry, &ctx->defaults, 1349e271dbb8Schristos ctx->palette); 1350e271dbb8Schristos } 13515494e770Schristos } 13525494e770Schristos 1353c7e17de0Schristos static const struct grid_cell * 1354c7e17de0Schristos tty_check_codeset(struct tty *tty, const struct grid_cell *gc) 1355c7e17de0Schristos { 1356c7e17de0Schristos static struct grid_cell new; 1357e271dbb8Schristos int c; 1358c7e17de0Schristos 1359c7e17de0Schristos /* Characters less than 0x7f are always fine, no matter what. */ 1360c7e17de0Schristos if (gc->data.size == 1 && *gc->data.data < 0x7f) 1361c7e17de0Schristos return (gc); 1362c7e17de0Schristos 1363c7e17de0Schristos /* UTF-8 terminal and a UTF-8 character - fine. */ 1364e271dbb8Schristos if (tty->client->flags & CLIENT_UTF8) 1365c7e17de0Schristos return (gc); 1366c7e17de0Schristos memcpy(&new, gc, sizeof new); 1367e271dbb8Schristos 1368e271dbb8Schristos /* See if this can be mapped to an ACS character. */ 1369e271dbb8Schristos c = tty_acs_reverse_get(tty, (const char *)gc->data.data, gc->data.size); 1370e271dbb8Schristos if (c != -1) { 1371e271dbb8Schristos utf8_set(&new.data, c); 1372e271dbb8Schristos new.attr |= GRID_ATTR_CHARSET; 1373c7e17de0Schristos return (&new); 1374c7e17de0Schristos } 1375c7e17de0Schristos 1376e271dbb8Schristos /* Replace by the right number of underscores. */ 1377e271dbb8Schristos new.data.size = gc->data.width; 1378e271dbb8Schristos if (new.data.size > UTF8_SIZE) 1379e271dbb8Schristos new.data.size = UTF8_SIZE; 1380e271dbb8Schristos memset(new.data.data, '_', new.data.size); 1381e271dbb8Schristos return (&new); 1382e271dbb8Schristos } 1383e271dbb8Schristos 138446548964Swiz /* 138546548964Swiz * Check if a single character is obstructed by the overlay and return a 138646548964Swiz * boolean. 138746548964Swiz */ 1388e271dbb8Schristos static int 1389e271dbb8Schristos tty_check_overlay(struct tty *tty, u_int px, u_int py) 1390e271dbb8Schristos { 139146548964Swiz struct overlay_ranges r; 139246548964Swiz 139346548964Swiz /* 139446548964Swiz * A unit width range will always return nx[2] == 0 from a check, even 139546548964Swiz * with multiple overlays, so it's sufficient to check just the first 139646548964Swiz * two entries. 139746548964Swiz */ 139846548964Swiz tty_check_overlay_range(tty, px, py, 1, &r); 139946548964Swiz if (r.nx[0] + r.nx[1] == 0) 140046548964Swiz return (0); 140146548964Swiz return (1); 140246548964Swiz } 140346548964Swiz 140446548964Swiz /* Return parts of the input range which are visible. */ 140546548964Swiz static void 140646548964Swiz tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx, 140746548964Swiz struct overlay_ranges *r) 140846548964Swiz { 1409e271dbb8Schristos struct client *c = tty->client; 1410e271dbb8Schristos 141146548964Swiz if (c->overlay_check == NULL) { 141246548964Swiz r->px[0] = px; 141346548964Swiz r->nx[0] = nx; 141446548964Swiz r->px[1] = 0; 141546548964Swiz r->nx[1] = 0; 141646548964Swiz r->px[2] = 0; 141746548964Swiz r->nx[2] = 0; 141846548964Swiz return; 141946548964Swiz } 142046548964Swiz 142146548964Swiz c->overlay_check(c, c->overlay_data, px, py, nx, r); 1422e271dbb8Schristos } 1423e271dbb8Schristos 14245494e770Schristos void 1425e271dbb8Schristos tty_draw_line(struct tty *tty, struct screen *s, u_int px, u_int py, u_int nx, 142646548964Swiz u_int atx, u_int aty, const struct grid_cell *defaults, 142746548964Swiz struct colour_palette *palette) 1428698d5317Sjmmv { 1429c7e17de0Schristos struct grid *gd = s->grid; 14304e179ddaSchristos struct grid_cell gc, last; 1431c7e17de0Schristos const struct grid_cell *gcp; 14320a274e86Schristos struct grid_line *gl; 143346548964Swiz struct client *c = tty->client; 143446548964Swiz struct overlay_ranges r; 143546548964Swiz u_int i, j, ux, sx, width, hidden, eux, nxx; 143646548964Swiz u_int cellsize; 143730744affSchristos int flags, cleared = 0, wrapped = 0; 14384e179ddaSchristos char buf[512]; 14390a274e86Schristos size_t len; 1440698d5317Sjmmv 14410a274e86Schristos log_debug("%s: px=%u py=%u nx=%u atx=%u aty=%u", __func__, 14420a274e86Schristos px, py, nx, atx, aty); 144346548964Swiz log_debug("%s: defaults: fg=%d, bg=%d", __func__, defaults->fg, 144446548964Swiz defaults->bg); 14450a274e86Schristos 14460a274e86Schristos /* 14470a274e86Schristos * py is the line in the screen to draw. 14480a274e86Schristos * px is the start x and nx is the width to draw. 14490a274e86Schristos * atx,aty is the line on the terminal to draw it. 14500a274e86Schristos */ 14510a274e86Schristos 14524e179ddaSchristos flags = (tty->flags & TTY_NOCURSOR); 14535494e770Schristos tty->flags |= TTY_NOCURSOR; 14545494e770Schristos tty_update_mode(tty, tty->mode, s); 1455698d5317Sjmmv 14564e179ddaSchristos tty_region_off(tty); 14574e179ddaSchristos tty_margin_off(tty); 14584e179ddaSchristos 1459fe99a117Schristos /* 1460fe99a117Schristos * Clamp the width to cellsize - note this is not cellused, because 1461fe99a117Schristos * there may be empty background cells after it (from BCE). 1462fe99a117Schristos */ 1463698d5317Sjmmv sx = screen_size_x(s); 14640a274e86Schristos if (nx > sx) 14650a274e86Schristos nx = sx; 1466c7e17de0Schristos cellsize = grid_get_line(gd, gd->hsize + py)->cellsize; 1467c7e17de0Schristos if (sx > cellsize) 1468c7e17de0Schristos sx = cellsize; 1469698d5317Sjmmv if (sx > tty->sx) 1470698d5317Sjmmv sx = tty->sx; 14710a274e86Schristos if (sx > nx) 14720a274e86Schristos sx = nx; 1473fe99a117Schristos ux = 0; 1474698d5317Sjmmv 14750a274e86Schristos if (py == 0) 14760a274e86Schristos gl = NULL; 14770a274e86Schristos else 14780a274e86Schristos gl = grid_get_line(gd, gd->hsize + py - 1); 1479e271dbb8Schristos if (gl == NULL || 14800a274e86Schristos (~gl->flags & GRID_LINE_WRAPPED) || 14810a274e86Schristos atx != 0 || 1482fe99a117Schristos tty->cx < tty->sx || 14830a274e86Schristos nx < tty->sx) { 14840a274e86Schristos if (nx < tty->sx && 14850a274e86Schristos atx == 0 && 14860a274e86Schristos px + sx != nx && 14874e179ddaSchristos tty_term_has(tty->term, TTYC_EL1) && 148846548964Swiz !tty_fake_bce(tty, defaults, 8) && 148946548964Swiz c->overlay_check == NULL) { 1490f844e94eSwiz tty_default_attributes(tty, defaults, palette, 8, 1491f844e94eSwiz s->hyperlinks); 14920a274e86Schristos tty_cursor(tty, nx - 1, aty); 14934e179ddaSchristos tty_putcode(tty, TTYC_EL1); 14944e179ddaSchristos cleared = 1; 14954e179ddaSchristos } 149630744affSchristos } else { 14970a274e86Schristos log_debug("%s: wrapped line %u", __func__, aty); 149830744affSchristos wrapped = 1; 149930744affSchristos } 1500698d5317Sjmmv 15014e179ddaSchristos memcpy(&last, &grid_default_cell, sizeof last); 15024e179ddaSchristos len = 0; 15034e179ddaSchristos width = 0; 15044e179ddaSchristos 1505698d5317Sjmmv for (i = 0; i < sx; i++) { 15060a274e86Schristos grid_view_get_cell(gd, px + i, py, &gc); 1507c7e17de0Schristos gcp = tty_check_codeset(tty, &gc); 15084e179ddaSchristos if (len != 0 && 1509e271dbb8Schristos (!tty_check_overlay(tty, atx + ux + width, aty) || 1510e271dbb8Schristos (gcp->attr & GRID_ATTR_CHARSET) || 1511c7e17de0Schristos gcp->flags != last.flags || 1512c7e17de0Schristos gcp->attr != last.attr || 1513c7e17de0Schristos gcp->fg != last.fg || 1514c7e17de0Schristos gcp->bg != last.bg || 151530744affSchristos gcp->us != last.us || 1516f844e94eSwiz gcp->link != last.link || 15170a274e86Schristos ux + width + gcp->data.width > nx || 1518c7e17de0Schristos (sizeof buf) - len < gcp->data.size)) { 1519f844e94eSwiz tty_attributes(tty, &last, defaults, palette, 1520f844e94eSwiz s->hyperlinks); 15210a274e86Schristos if (last.flags & GRID_FLAG_CLEARED) { 15220a274e86Schristos log_debug("%s: %zu cleared", __func__, len); 1523e271dbb8Schristos tty_clear_line(tty, defaults, aty, atx + ux, 1524e271dbb8Schristos width, last.bg); 15250a274e86Schristos } else { 152630744affSchristos if (!wrapped || atx != 0 || ux != 0) 15270a274e86Schristos tty_cursor(tty, atx + ux, aty); 15284e179ddaSchristos tty_putn(tty, buf, len, width); 15290a274e86Schristos } 1530fe99a117Schristos ux += width; 15314e179ddaSchristos 15324e179ddaSchristos len = 0; 15334e179ddaSchristos width = 0; 153430744affSchristos wrapped = 0; 1535698d5317Sjmmv } 1536698d5317Sjmmv 1537c7e17de0Schristos if (gcp->flags & GRID_FLAG_SELECTED) 1538c7e17de0Schristos screen_select_cell(s, &last, gcp); 15394e179ddaSchristos else 1540c7e17de0Schristos memcpy(&last, gcp, sizeof last); 154146548964Swiz 154246548964Swiz tty_check_overlay_range(tty, atx + ux, aty, gcp->data.width, 154346548964Swiz &r); 154446548964Swiz hidden = 0; 154546548964Swiz for (j = 0; j < OVERLAY_MAX_RANGES; j++) 154646548964Swiz hidden += r.nx[j]; 154746548964Swiz hidden = gcp->data.width - hidden; 154846548964Swiz if (hidden != 0 && hidden == gcp->data.width) { 1549e271dbb8Schristos if (~gcp->flags & GRID_FLAG_PADDING) 1550e271dbb8Schristos ux += gcp->data.width; 155146548964Swiz } else if (hidden != 0 || ux + gcp->data.width > nx) { 155246548964Swiz if (~gcp->flags & GRID_FLAG_PADDING) { 1553f844e94eSwiz tty_attributes(tty, &last, defaults, palette, 1554f844e94eSwiz s->hyperlinks); 155546548964Swiz for (j = 0; j < OVERLAY_MAX_RANGES; j++) { 155646548964Swiz if (r.nx[j] == 0) 155746548964Swiz continue; 155846548964Swiz /* Effective width drawn so far. */ 155946548964Swiz eux = r.px[j] - atx; 156046548964Swiz if (eux < nx) { 156146548964Swiz tty_cursor(tty, r.px[j], aty); 156246548964Swiz nxx = nx - eux; 156346548964Swiz if (r.nx[j] > nxx) 156446548964Swiz r.nx[j] = nxx; 156546548964Swiz tty_repeat_space(tty, r.nx[j]); 156646548964Swiz ux = eux + r.nx[j]; 156746548964Swiz } 156846548964Swiz } 15694e179ddaSchristos } 1570c7e17de0Schristos } else if (gcp->attr & GRID_ATTR_CHARSET) { 1571f844e94eSwiz tty_attributes(tty, &last, defaults, palette, 1572f844e94eSwiz s->hyperlinks); 15730a274e86Schristos tty_cursor(tty, atx + ux, aty); 1574c7e17de0Schristos for (j = 0; j < gcp->data.size; j++) 1575c7e17de0Schristos tty_putc(tty, gcp->data.data[j]); 1576e271dbb8Schristos ux += gcp->data.width; 1577e271dbb8Schristos } else if (~gcp->flags & GRID_FLAG_PADDING) { 1578c7e17de0Schristos memcpy(buf + len, gcp->data.data, gcp->data.size); 1579c7e17de0Schristos len += gcp->data.size; 1580c7e17de0Schristos width += gcp->data.width; 15814e179ddaSchristos } 15824e179ddaSchristos } 15830a274e86Schristos if (len != 0 && ((~last.flags & GRID_FLAG_CLEARED) || last.bg != 8)) { 1584f844e94eSwiz tty_attributes(tty, &last, defaults, palette, s->hyperlinks); 15850a274e86Schristos if (last.flags & GRID_FLAG_CLEARED) { 15860a274e86Schristos log_debug("%s: %zu cleared (end)", __func__, len); 1587e271dbb8Schristos tty_clear_line(tty, defaults, aty, atx + ux, width, 1588e271dbb8Schristos last.bg); 15890a274e86Schristos } else { 159030744affSchristos if (!wrapped || atx != 0 || ux != 0) 15910a274e86Schristos tty_cursor(tty, atx + ux, aty); 15924e179ddaSchristos tty_putn(tty, buf, len, width); 15930a274e86Schristos } 1594fe99a117Schristos ux += width; 1595fe99a117Schristos } 1596698d5317Sjmmv 15970a274e86Schristos if (!cleared && ux < nx) { 15980a274e86Schristos log_debug("%s: %u to end of line (%zu cleared)", __func__, 15990a274e86Schristos nx - ux, len); 1600f844e94eSwiz tty_default_attributes(tty, defaults, palette, 8, 1601f844e94eSwiz s->hyperlinks); 1602e271dbb8Schristos tty_clear_line(tty, defaults, aty, atx + ux, nx - ux, 8); 16035494e770Schristos } 16045494e770Schristos 16055494e770Schristos tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags; 1606d530c4d0Sjmmv tty_update_mode(tty, tty->mode, s); 1607698d5317Sjmmv } 1608698d5317Sjmmv 1609f844e94eSwiz #ifdef ENABLE_SIXEL 1610f844e94eSwiz /* Update context for client. */ 1611f844e94eSwiz static int 1612f844e94eSwiz tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c) 1613f844e94eSwiz { 1614f844e94eSwiz struct window_pane *wp = ttyctx->arg; 1615f844e94eSwiz 1616f844e94eSwiz if (c->session->curw->window != wp->window) 1617f844e94eSwiz return (0); 1618f844e94eSwiz if (wp->layout_cell == NULL) 1619f844e94eSwiz return (0); 1620f844e94eSwiz 1621f844e94eSwiz /* Set the properties relevant to the current client. */ 1622f844e94eSwiz ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, 1623f844e94eSwiz &ttyctx->wsx, &ttyctx->wsy); 1624f844e94eSwiz 1625f844e94eSwiz ttyctx->yoff = ttyctx->ryoff = wp->yoff; 1626f844e94eSwiz if (status_at_line(c) == 0) 1627f844e94eSwiz ttyctx->yoff += status_line_size(c); 1628f844e94eSwiz 1629f844e94eSwiz return (1); 1630f844e94eSwiz } 1631f844e94eSwiz 1632f844e94eSwiz void 1633f844e94eSwiz tty_draw_images(struct client *c, struct window_pane *wp, struct screen *s) 1634f844e94eSwiz { 1635f844e94eSwiz struct image *im; 1636f844e94eSwiz struct tty_ctx ttyctx; 1637f844e94eSwiz 1638f844e94eSwiz TAILQ_FOREACH(im, &s->images, entry) { 1639f844e94eSwiz memset(&ttyctx, 0, sizeof ttyctx); 1640f844e94eSwiz 1641f844e94eSwiz /* Set the client independent properties. */ 1642f844e94eSwiz ttyctx.ocx = im->px; 1643f844e94eSwiz ttyctx.ocy = im->py; 1644f844e94eSwiz 1645f844e94eSwiz ttyctx.orlower = s->rlower; 1646f844e94eSwiz ttyctx.orupper = s->rupper; 1647f844e94eSwiz 1648f844e94eSwiz ttyctx.xoff = ttyctx.rxoff = wp->xoff; 1649f844e94eSwiz ttyctx.sx = wp->sx; 1650f844e94eSwiz ttyctx.sy = wp->sy; 1651f844e94eSwiz 1652f844e94eSwiz ttyctx.ptr = im; 1653f844e94eSwiz ttyctx.arg = wp; 1654f844e94eSwiz ttyctx.set_client_cb = tty_set_client_cb; 1655f844e94eSwiz ttyctx.allow_invisible_panes = 1; 1656f844e94eSwiz tty_write_one(tty_cmd_sixelimage, c, &ttyctx); 1657f844e94eSwiz } 1658f844e94eSwiz } 1659f844e94eSwiz #endif 1660f844e94eSwiz 1661e271dbb8Schristos void 1662e271dbb8Schristos tty_sync_start(struct tty *tty) 1663e271dbb8Schristos { 1664e271dbb8Schristos if (tty->flags & TTY_BLOCK) 1665e271dbb8Schristos return; 1666e271dbb8Schristos if (tty->flags & TTY_SYNCING) 1667e271dbb8Schristos return; 1668e271dbb8Schristos tty->flags |= TTY_SYNCING; 1669e271dbb8Schristos 1670e271dbb8Schristos if (tty_term_has(tty->term, TTYC_SYNC)) { 1671e271dbb8Schristos log_debug("%s sync start", tty->client->name); 1672f844e94eSwiz tty_putcode_i(tty, TTYC_SYNC, 1); 1673e271dbb8Schristos } 1674e271dbb8Schristos } 1675e271dbb8Schristos 1676e271dbb8Schristos void 1677e271dbb8Schristos tty_sync_end(struct tty *tty) 1678e271dbb8Schristos { 1679e271dbb8Schristos if (tty->flags & TTY_BLOCK) 1680e271dbb8Schristos return; 1681e271dbb8Schristos if (~tty->flags & TTY_SYNCING) 1682e271dbb8Schristos return; 1683e271dbb8Schristos tty->flags &= ~TTY_SYNCING; 1684e271dbb8Schristos 1685e271dbb8Schristos if (tty_term_has(tty->term, TTYC_SYNC)) { 1686e271dbb8Schristos log_debug("%s sync end", tty->client->name); 1687f844e94eSwiz tty_putcode_i(tty, TTYC_SYNC, 2); 1688e271dbb8Schristos } 1689e271dbb8Schristos } 1690e271dbb8Schristos 16914e179ddaSchristos static int 1692f844e94eSwiz tty_client_ready(const struct tty_ctx *ctx, struct client *c) 16935494e770Schristos { 16945494e770Schristos if (c->session == NULL || c->tty.term == NULL) 16955494e770Schristos return (0); 1696f844e94eSwiz if (c->flags & CLIENT_SUSPENDED) 1697f844e94eSwiz return (0); 1698f844e94eSwiz 1699f844e94eSwiz /* 1700f844e94eSwiz * If invisible panes are allowed (used for passthrough), don't care if 1701f844e94eSwiz * redrawing or frozen. 1702f844e94eSwiz */ 1703f844e94eSwiz if (ctx->allow_invisible_panes) 1704f844e94eSwiz return (1); 1705f844e94eSwiz 1706f844e94eSwiz if (c->flags & CLIENT_REDRAWWINDOW) 17075494e770Schristos return (0); 17085494e770Schristos if (c->tty.flags & TTY_FREEZE) 17095494e770Schristos return (0); 17105494e770Schristos return (1); 17115494e770Schristos } 17125494e770Schristos 1713698d5317Sjmmv void 17145494e770Schristos tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *), 17155494e770Schristos struct tty_ctx *ctx) 1716698d5317Sjmmv { 1717698d5317Sjmmv struct client *c; 1718e271dbb8Schristos int state; 1719698d5317Sjmmv 1720e271dbb8Schristos if (ctx->set_client_cb == NULL) 1721698d5317Sjmmv return; 17225494e770Schristos TAILQ_FOREACH(c, &clients, entry) { 1723f844e94eSwiz if (tty_client_ready(ctx, c)) { 1724e271dbb8Schristos state = ctx->set_client_cb(ctx, c); 1725e271dbb8Schristos if (state == -1) 1726e271dbb8Schristos break; 1727e271dbb8Schristos if (state == 0) 1728e271dbb8Schristos continue; 1729698d5317Sjmmv cmdfn(&c->tty, ctx); 1730698d5317Sjmmv } 1731698d5317Sjmmv } 1732f844e94eSwiz } 1733f844e94eSwiz 1734f844e94eSwiz #ifdef ENABLE_SIXEL 1735f844e94eSwiz /* Only write to the incoming tty instead of every client. */ 1736f844e94eSwiz static void 1737f844e94eSwiz tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *), 1738f844e94eSwiz struct client *c, struct tty_ctx *ctx) 1739f844e94eSwiz { 1740f844e94eSwiz if (ctx->set_client_cb == NULL) 1741f844e94eSwiz return; 1742f844e94eSwiz if ((ctx->set_client_cb(ctx, c)) == 1) 1743f844e94eSwiz cmdfn(&c->tty, ctx); 1744f844e94eSwiz } 1745f844e94eSwiz #endif 1746698d5317Sjmmv 1747698d5317Sjmmv void 1748698d5317Sjmmv tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx) 1749698d5317Sjmmv { 175046548964Swiz struct client *c = tty->client; 175146548964Swiz 17520a274e86Schristos if (ctx->bigger || 1753e271dbb8Schristos !tty_full_width(tty, ctx) || 1754e271dbb8Schristos tty_fake_bce(tty, &ctx->defaults, ctx->bg) || 17554e179ddaSchristos (!tty_term_has(tty->term, TTYC_ICH) && 175646548964Swiz !tty_term_has(tty->term, TTYC_ICH1)) || 175746548964Swiz c->overlay_check != NULL) { 17580a274e86Schristos tty_draw_pane(tty, ctx, ctx->ocy); 1759698d5317Sjmmv return; 1760698d5317Sjmmv } 1761698d5317Sjmmv 1762f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1763f844e94eSwiz ctx->s->hyperlinks); 1764698d5317Sjmmv 1765698d5317Sjmmv tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 1766698d5317Sjmmv 1767698d5317Sjmmv tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num); 1768698d5317Sjmmv } 1769698d5317Sjmmv 1770698d5317Sjmmv void 1771698d5317Sjmmv tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx) 1772698d5317Sjmmv { 177346548964Swiz struct client *c = tty->client; 177446548964Swiz 17750a274e86Schristos if (ctx->bigger || 1776e271dbb8Schristos !tty_full_width(tty, ctx) || 1777e271dbb8Schristos tty_fake_bce(tty, &ctx->defaults, ctx->bg) || 1778698d5317Sjmmv (!tty_term_has(tty->term, TTYC_DCH) && 177946548964Swiz !tty_term_has(tty->term, TTYC_DCH1)) || 178046548964Swiz c->overlay_check != NULL) { 17810a274e86Schristos tty_draw_pane(tty, ctx, ctx->ocy); 1782698d5317Sjmmv return; 1783698d5317Sjmmv } 1784698d5317Sjmmv 1785f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1786f844e94eSwiz ctx->s->hyperlinks); 1787698d5317Sjmmv 1788698d5317Sjmmv tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 1789698d5317Sjmmv 1790698d5317Sjmmv tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num); 1791698d5317Sjmmv } 1792698d5317Sjmmv 1793698d5317Sjmmv void 1794928fc495Schristos tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx) 1795928fc495Schristos { 1796f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1797f844e94eSwiz ctx->s->hyperlinks); 17980a274e86Schristos 1799e271dbb8Schristos tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->num, ctx->bg); 1800928fc495Schristos } 1801928fc495Schristos 1802928fc495Schristos void 1803698d5317Sjmmv tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx) 1804698d5317Sjmmv { 180546548964Swiz struct client *c = tty->client; 180646548964Swiz 18070a274e86Schristos if (ctx->bigger || 1808e271dbb8Schristos !tty_full_width(tty, ctx) || 1809e271dbb8Schristos tty_fake_bce(tty, &ctx->defaults, ctx->bg) || 1810698d5317Sjmmv !tty_term_has(tty->term, TTYC_CSR) || 18110a274e86Schristos !tty_term_has(tty->term, TTYC_IL1) || 1812e271dbb8Schristos ctx->sx == 1 || 181346548964Swiz ctx->sy == 1 || 181446548964Swiz c->overlay_check != NULL) { 1815698d5317Sjmmv tty_redraw_region(tty, ctx); 1816698d5317Sjmmv return; 1817698d5317Sjmmv } 1818698d5317Sjmmv 1819f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1820f844e94eSwiz ctx->s->hyperlinks); 1821698d5317Sjmmv 1822698d5317Sjmmv tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 18234e179ddaSchristos tty_margin_off(tty); 1824698d5317Sjmmv tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 1825698d5317Sjmmv 1826698d5317Sjmmv tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num); 1827fe99a117Schristos tty->cx = tty->cy = UINT_MAX; 1828698d5317Sjmmv } 1829698d5317Sjmmv 1830698d5317Sjmmv void 1831698d5317Sjmmv tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx) 1832698d5317Sjmmv { 183346548964Swiz struct client *c = tty->client; 183446548964Swiz 18350a274e86Schristos if (ctx->bigger || 1836e271dbb8Schristos !tty_full_width(tty, ctx) || 1837e271dbb8Schristos tty_fake_bce(tty, &ctx->defaults, ctx->bg) || 1838698d5317Sjmmv !tty_term_has(tty->term, TTYC_CSR) || 18390a274e86Schristos !tty_term_has(tty->term, TTYC_DL1) || 1840e271dbb8Schristos ctx->sx == 1 || 184146548964Swiz ctx->sy == 1 || 184246548964Swiz c->overlay_check != NULL) { 1843698d5317Sjmmv tty_redraw_region(tty, ctx); 1844698d5317Sjmmv return; 1845698d5317Sjmmv } 1846698d5317Sjmmv 1847f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1848f844e94eSwiz ctx->s->hyperlinks); 1849698d5317Sjmmv 1850698d5317Sjmmv tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 18514e179ddaSchristos tty_margin_off(tty); 1852698d5317Sjmmv tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 1853698d5317Sjmmv 1854698d5317Sjmmv tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num); 1855fe99a117Schristos tty->cx = tty->cy = UINT_MAX; 1856698d5317Sjmmv } 1857698d5317Sjmmv 1858698d5317Sjmmv void 1859698d5317Sjmmv tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx) 1860698d5317Sjmmv { 1861f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1862f844e94eSwiz ctx->s->hyperlinks); 1863698d5317Sjmmv 1864e271dbb8Schristos tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg); 1865698d5317Sjmmv } 1866698d5317Sjmmv 1867698d5317Sjmmv void 1868698d5317Sjmmv tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx) 1869698d5317Sjmmv { 1870e271dbb8Schristos u_int nx = ctx->sx - ctx->ocx; 1871698d5317Sjmmv 1872f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1873f844e94eSwiz ctx->s->hyperlinks); 1874698d5317Sjmmv 18750a274e86Schristos tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg); 1876698d5317Sjmmv } 1877698d5317Sjmmv 1878698d5317Sjmmv void 1879698d5317Sjmmv tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx) 1880698d5317Sjmmv { 1881f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1882f844e94eSwiz ctx->s->hyperlinks); 18834e179ddaSchristos 18840a274e86Schristos tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg); 1885698d5317Sjmmv } 1886698d5317Sjmmv 1887698d5317Sjmmv void 1888698d5317Sjmmv tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) 1889698d5317Sjmmv { 189046548964Swiz struct client *c = tty->client; 189146548964Swiz 1892698d5317Sjmmv if (ctx->ocy != ctx->orupper) 1893698d5317Sjmmv return; 1894698d5317Sjmmv 18950a274e86Schristos if (ctx->bigger || 1896e271dbb8Schristos (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || 1897e271dbb8Schristos tty_fake_bce(tty, &ctx->defaults, 8) || 1898698d5317Sjmmv !tty_term_has(tty->term, TTYC_CSR) || 189930744affSchristos (!tty_term_has(tty->term, TTYC_RI) && 190030744affSchristos !tty_term_has(tty->term, TTYC_RIN)) || 1901e271dbb8Schristos ctx->sx == 1 || 190246548964Swiz ctx->sy == 1 || 190346548964Swiz c->overlay_check != NULL) { 1904698d5317Sjmmv tty_redraw_region(tty, ctx); 1905698d5317Sjmmv return; 1906698d5317Sjmmv } 1907698d5317Sjmmv 1908f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1909f844e94eSwiz ctx->s->hyperlinks); 1910698d5317Sjmmv 1911698d5317Sjmmv tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 191230744affSchristos tty_margin_pane(tty, ctx); 1913698d5317Sjmmv tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); 1914698d5317Sjmmv 191530744affSchristos if (tty_term_has(tty->term, TTYC_RI)) 1916698d5317Sjmmv tty_putcode(tty, TTYC_RI); 191730744affSchristos else 1918f844e94eSwiz tty_putcode_i(tty, TTYC_RIN, 1); 1919698d5317Sjmmv } 1920698d5317Sjmmv 1921698d5317Sjmmv void 1922698d5317Sjmmv tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) 1923698d5317Sjmmv { 192446548964Swiz struct client *c = tty->client; 192546548964Swiz 1926698d5317Sjmmv if (ctx->ocy != ctx->orlower) 1927698d5317Sjmmv return; 1928698d5317Sjmmv 19290a274e86Schristos if (ctx->bigger || 1930e271dbb8Schristos (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || 1931e271dbb8Schristos tty_fake_bce(tty, &ctx->defaults, 8) || 19320a274e86Schristos !tty_term_has(tty->term, TTYC_CSR) || 1933e271dbb8Schristos ctx->sx == 1 || 193446548964Swiz ctx->sy == 1 || 193546548964Swiz c->overlay_check != NULL) { 1936698d5317Sjmmv tty_redraw_region(tty, ctx); 1937698d5317Sjmmv return; 1938698d5317Sjmmv } 1939698d5317Sjmmv 1940f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1941f844e94eSwiz ctx->s->hyperlinks); 19424e179ddaSchristos 19434e179ddaSchristos tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 19444e179ddaSchristos tty_margin_pane(tty, ctx); 19454e179ddaSchristos 1946698d5317Sjmmv /* 1947c7e17de0Schristos * If we want to wrap a pane while using margins, the cursor needs to 1948c7e17de0Schristos * be exactly on the right of the region. If the cursor is entirely off 1949c7e17de0Schristos * the edge - move it back to the right. Some terminals are funny about 1950c7e17de0Schristos * this and insert extra spaces, so only use the right if margins are 1951c7e17de0Schristos * enabled. 1952698d5317Sjmmv */ 1953c7e17de0Schristos if (ctx->xoff + ctx->ocx > tty->rright) { 1954c7e17de0Schristos if (!tty_use_margin(tty)) 1955c7e17de0Schristos tty_cursor(tty, 0, ctx->yoff + ctx->ocy); 19564e179ddaSchristos else 1957c7e17de0Schristos tty_cursor(tty, tty->rright, ctx->yoff + ctx->ocy); 1958c7e17de0Schristos } else 19594e179ddaSchristos tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); 19604e179ddaSchristos 19614e179ddaSchristos tty_putc(tty, '\n'); 19624e179ddaSchristos } 19634e179ddaSchristos 19644e179ddaSchristos void 19654e179ddaSchristos tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx) 19664e179ddaSchristos { 196746548964Swiz struct client *c = tty->client; 19684e179ddaSchristos u_int i; 19694e179ddaSchristos 19700a274e86Schristos if (ctx->bigger || 1971e271dbb8Schristos (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || 1972e271dbb8Schristos tty_fake_bce(tty, &ctx->defaults, 8) || 19730a274e86Schristos !tty_term_has(tty->term, TTYC_CSR) || 1974e271dbb8Schristos ctx->sx == 1 || 197546548964Swiz ctx->sy == 1 || 197646548964Swiz c->overlay_check != NULL) { 19774e179ddaSchristos tty_redraw_region(tty, ctx); 1978698d5317Sjmmv return; 19794e179ddaSchristos } 1980698d5317Sjmmv 1981f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 1982f844e94eSwiz ctx->s->hyperlinks); 1983698d5317Sjmmv 1984698d5317Sjmmv tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 19854e179ddaSchristos tty_margin_pane(tty, ctx); 1986698d5317Sjmmv 19874e179ddaSchristos if (ctx->num == 1 || !tty_term_has(tty->term, TTYC_INDN)) { 1988c7e17de0Schristos if (!tty_use_margin(tty)) 1989c7e17de0Schristos tty_cursor(tty, 0, tty->rlower); 1990c7e17de0Schristos else 19914e179ddaSchristos tty_cursor(tty, tty->rright, tty->rlower); 19924e179ddaSchristos for (i = 0; i < ctx->num; i++) 1993698d5317Sjmmv tty_putc(tty, '\n'); 1994c7e17de0Schristos } else { 1995e271dbb8Schristos if (tty->cy == UINT_MAX) 1996e271dbb8Schristos tty_cursor(tty, 0, 0); 1997e271dbb8Schristos else 1998c7e17de0Schristos tty_cursor(tty, 0, tty->cy); 1999f844e94eSwiz tty_putcode_i(tty, TTYC_INDN, ctx->num); 2000698d5317Sjmmv } 2001c7e17de0Schristos } 2002698d5317Sjmmv 2003698d5317Sjmmv void 200430744affSchristos tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx) 200530744affSchristos { 200630744affSchristos u_int i; 200746548964Swiz struct client *c = tty->client; 200830744affSchristos 200930744affSchristos if (ctx->bigger || 2010e271dbb8Schristos (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) || 2011e271dbb8Schristos tty_fake_bce(tty, &ctx->defaults, 8) || 201230744affSchristos !tty_term_has(tty->term, TTYC_CSR) || 201330744affSchristos (!tty_term_has(tty->term, TTYC_RI) && 201430744affSchristos !tty_term_has(tty->term, TTYC_RIN)) || 2015e271dbb8Schristos ctx->sx == 1 || 201646548964Swiz ctx->sy == 1 || 201746548964Swiz c->overlay_check != NULL) { 201830744affSchristos tty_redraw_region(tty, ctx); 201930744affSchristos return; 202030744affSchristos } 202130744affSchristos 2022f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 2023f844e94eSwiz ctx->s->hyperlinks); 202430744affSchristos 202530744affSchristos tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 202630744affSchristos tty_margin_pane(tty, ctx); 202730744affSchristos tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); 202830744affSchristos 202930744affSchristos if (tty_term_has(tty->term, TTYC_RIN)) 2030f844e94eSwiz tty_putcode_i(tty, TTYC_RIN, ctx->num); 203130744affSchristos else { 203230744affSchristos for (i = 0; i < ctx->num; i++) 203330744affSchristos tty_putcode(tty, TTYC_RI); 203430744affSchristos } 203530744affSchristos } 203630744affSchristos 203730744affSchristos void 2038698d5317Sjmmv tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx) 2039698d5317Sjmmv { 2040fe99a117Schristos u_int px, py, nx, ny; 2041698d5317Sjmmv 2042f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 2043f844e94eSwiz ctx->s->hyperlinks); 2044698d5317Sjmmv 2045e271dbb8Schristos tty_region_pane(tty, ctx, 0, ctx->sy - 1); 20464e179ddaSchristos tty_margin_off(tty); 2047698d5317Sjmmv 20480a274e86Schristos px = 0; 2049e271dbb8Schristos nx = ctx->sx; 20500a274e86Schristos py = ctx->ocy + 1; 2051e271dbb8Schristos ny = ctx->sy - ctx->ocy - 1; 2052fe99a117Schristos 20530a274e86Schristos tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); 2054fe99a117Schristos 20550a274e86Schristos px = ctx->ocx; 2056e271dbb8Schristos nx = ctx->sx - ctx->ocx; 20570a274e86Schristos py = ctx->ocy; 2058fe99a117Schristos 20590a274e86Schristos tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg); 2060698d5317Sjmmv } 2061698d5317Sjmmv 2062698d5317Sjmmv void 2063698d5317Sjmmv tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx) 2064698d5317Sjmmv { 2065fe99a117Schristos u_int px, py, nx, ny; 2066698d5317Sjmmv 2067f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 2068f844e94eSwiz ctx->s->hyperlinks); 2069698d5317Sjmmv 2070e271dbb8Schristos tty_region_pane(tty, ctx, 0, ctx->sy - 1); 20714e179ddaSchristos tty_margin_off(tty); 2072698d5317Sjmmv 20730a274e86Schristos px = 0; 2074e271dbb8Schristos nx = ctx->sx; 20750a274e86Schristos py = 0; 20760a274e86Schristos ny = ctx->ocy; 2077fe99a117Schristos 20780a274e86Schristos tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); 2079fe99a117Schristos 20800a274e86Schristos px = 0; 2081fe99a117Schristos nx = ctx->ocx + 1; 20820a274e86Schristos py = ctx->ocy; 2083fe99a117Schristos 20840a274e86Schristos tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg); 2085698d5317Sjmmv } 2086698d5317Sjmmv 2087698d5317Sjmmv void 2088698d5317Sjmmv tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx) 2089698d5317Sjmmv { 2090fe99a117Schristos u_int px, py, nx, ny; 2091698d5317Sjmmv 2092f844e94eSwiz tty_default_attributes(tty, &ctx->defaults, ctx->palette, ctx->bg, 2093f844e94eSwiz ctx->s->hyperlinks); 2094698d5317Sjmmv 2095e271dbb8Schristos tty_region_pane(tty, ctx, 0, ctx->sy - 1); 20964e179ddaSchristos tty_margin_off(tty); 2097698d5317Sjmmv 20980a274e86Schristos px = 0; 2099e271dbb8Schristos nx = ctx->sx; 21000a274e86Schristos py = 0; 2101e271dbb8Schristos ny = ctx->sy; 2102fe99a117Schristos 21030a274e86Schristos tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg); 2104698d5317Sjmmv } 2105698d5317Sjmmv 2106698d5317Sjmmv void 2107698d5317Sjmmv tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx) 2108698d5317Sjmmv { 2109698d5317Sjmmv u_int i, j; 2110698d5317Sjmmv 21110a274e86Schristos if (ctx->bigger) { 2112e271dbb8Schristos ctx->redraw_cb(ctx); 21130a274e86Schristos return; 21140a274e86Schristos } 21150a274e86Schristos 2116f844e94eSwiz tty_attributes(tty, &grid_default_cell, &ctx->defaults, ctx->palette, 2117f844e94eSwiz ctx->s->hyperlinks); 2118698d5317Sjmmv 2119e271dbb8Schristos tty_region_pane(tty, ctx, 0, ctx->sy - 1); 21204e179ddaSchristos tty_margin_off(tty); 2121698d5317Sjmmv 2122e271dbb8Schristos for (j = 0; j < ctx->sy; j++) { 2123698d5317Sjmmv tty_cursor_pane(tty, ctx, 0, j); 2124e271dbb8Schristos for (i = 0; i < ctx->sx; i++) 2125698d5317Sjmmv tty_putc(tty, 'E'); 2126698d5317Sjmmv } 2127698d5317Sjmmv } 2128698d5317Sjmmv 2129698d5317Sjmmv void 2130698d5317Sjmmv tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) 2131698d5317Sjmmv { 213246548964Swiz const struct grid_cell *gcp = ctx->cell; 213346548964Swiz struct screen *s = ctx->s; 213446548964Swiz struct overlay_ranges r; 213546548964Swiz u_int px, py, i, vis = 0; 213646548964Swiz 213746548964Swiz px = ctx->xoff + ctx->ocx - ctx->wox; 213846548964Swiz py = ctx->yoff + ctx->ocy - ctx->woy; 213946548964Swiz if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1) || 214046548964Swiz (gcp->data.width == 1 && !tty_check_overlay(tty, px, py))) 21410a274e86Schristos return; 2142698d5317Sjmmv 214346548964Swiz /* Handle partially obstructed wide characters. */ 214446548964Swiz if (gcp->data.width > 1) { 214546548964Swiz tty_check_overlay_range(tty, px, py, gcp->data.width, &r); 214646548964Swiz for (i = 0; i < OVERLAY_MAX_RANGES; i++) 214746548964Swiz vis += r.nx[i]; 214846548964Swiz if (vis < gcp->data.width) { 214946548964Swiz tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width, 215046548964Swiz px, py, &ctx->defaults, ctx->palette); 215146548964Swiz return; 215246548964Swiz } 215346548964Swiz } 215446548964Swiz 2155e271dbb8Schristos if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 && 21560a274e86Schristos ctx->ocy == ctx->orlower && 2157e271dbb8Schristos tty_full_width(tty, ctx)) 21580a274e86Schristos tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); 21590a274e86Schristos 21600a274e86Schristos tty_margin_off(tty); 21614e179ddaSchristos tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); 21624e179ddaSchristos 2163f844e94eSwiz tty_cell(tty, ctx->cell, &ctx->defaults, ctx->palette, 2164f844e94eSwiz ctx->s->hyperlinks); 2165f844e94eSwiz 2166f844e94eSwiz if (ctx->num == 1) 2167f844e94eSwiz tty_invalidate(tty); 2168698d5317Sjmmv } 2169698d5317Sjmmv 2170698d5317Sjmmv void 21714e179ddaSchristos tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx) 2172698d5317Sjmmv { 217346548964Swiz struct overlay_ranges r; 217446548964Swiz u_int i, px, py, cx; 217546548964Swiz char *cp = ctx->ptr; 217646548964Swiz 21770a274e86Schristos if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, ctx->num, 1)) 21780a274e86Schristos return; 21790a274e86Schristos 21800a274e86Schristos if (ctx->bigger && 2181e271dbb8Schristos (ctx->xoff + ctx->ocx < ctx->wox || 2182e271dbb8Schristos ctx->xoff + ctx->ocx + ctx->num > ctx->wox + ctx->wsx)) { 21830a274e86Schristos if (!ctx->wrapped || 2184e271dbb8Schristos !tty_full_width(tty, ctx) || 2185e271dbb8Schristos (tty->term->flags & TERM_NOAM) || 21860a274e86Schristos ctx->xoff + ctx->ocx != 0 || 21870a274e86Schristos ctx->yoff + ctx->ocy != tty->cy + 1 || 21880a274e86Schristos tty->cx < tty->sx || 21890a274e86Schristos tty->cy == tty->rlower) 21900a274e86Schristos tty_draw_pane(tty, ctx, ctx->ocy); 21910a274e86Schristos else 2192e271dbb8Schristos ctx->redraw_cb(ctx); 21930a274e86Schristos return; 21940a274e86Schristos } 21950a274e86Schristos 21960a274e86Schristos tty_margin_off(tty); 21974e179ddaSchristos tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy); 2198f844e94eSwiz tty_attributes(tty, ctx->cell, &ctx->defaults, ctx->palette, ctx->s->hyperlinks); 219946548964Swiz 220046548964Swiz /* Get tty position from pane position for overlay check. */ 220146548964Swiz px = ctx->xoff + ctx->ocx - ctx->wox; 220246548964Swiz py = ctx->yoff + ctx->ocy - ctx->woy; 220346548964Swiz 220446548964Swiz tty_check_overlay_range(tty, px, py, ctx->num, &r); 220546548964Swiz for (i = 0; i < OVERLAY_MAX_RANGES; i++) { 220646548964Swiz if (r.nx[i] == 0) 220746548964Swiz continue; 220846548964Swiz /* Convert back to pane position for printing. */ 220946548964Swiz cx = r.px[i] - ctx->xoff + ctx->wox; 221046548964Swiz tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy); 221146548964Swiz tty_putn(tty, cp + r.px[i] - px, r.nx[i], r.nx[i]); 221246548964Swiz } 2213698d5317Sjmmv } 2214698d5317Sjmmv 2215698d5317Sjmmv void 2216d530c4d0Sjmmv tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx) 2217d530c4d0Sjmmv { 2218f844e94eSwiz tty_set_selection(tty, ctx->ptr2, ctx->ptr, ctx->num); 2219e271dbb8Schristos } 2220d530c4d0Sjmmv 2221e271dbb8Schristos void 2222f844e94eSwiz tty_set_selection(struct tty *tty, const char *flags, const char *buf, 2223f844e94eSwiz size_t len) 2224e271dbb8Schristos { 2225e271dbb8Schristos char *encoded; 2226e271dbb8Schristos size_t size; 2227e271dbb8Schristos 2228e271dbb8Schristos if (~tty->flags & TTY_STARTED) 2229e271dbb8Schristos return; 2230d530c4d0Sjmmv if (!tty_term_has(tty->term, TTYC_MS)) 2231d530c4d0Sjmmv return; 2232d530c4d0Sjmmv 2233e271dbb8Schristos size = 4 * ((len + 2) / 3) + 1; /* storage for base64 */ 2234e271dbb8Schristos encoded = xmalloc(size); 2235d530c4d0Sjmmv 223646548964Swiz b64_ntop(buf, len, encoded, size); 223746548964Swiz tty->flags |= TTY_NOBLOCK; 2238f844e94eSwiz tty_putcode_ss(tty, TTYC_MS, flags, encoded); 2239d530c4d0Sjmmv 2240e271dbb8Schristos free(encoded); 2241d530c4d0Sjmmv } 2242d530c4d0Sjmmv 2243d530c4d0Sjmmv void 2244d530c4d0Sjmmv tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx) 2245d530c4d0Sjmmv { 224646548964Swiz tty->flags |= TTY_NOBLOCK; 22474e179ddaSchristos tty_add(tty, ctx->ptr, ctx->num); 22484e179ddaSchristos tty_invalidate(tty); 2249d530c4d0Sjmmv } 2250d530c4d0Sjmmv 2251f844e94eSwiz #ifdef ENABLE_SIXEL 2252f844e94eSwiz void 2253f844e94eSwiz tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx) 2254f844e94eSwiz { 2255f844e94eSwiz struct image *im = ctx->ptr; 2256f844e94eSwiz struct sixel_image *si = im->data; 2257f844e94eSwiz struct sixel_image *new; 2258f844e94eSwiz char *data; 2259f844e94eSwiz size_t size; 2260f844e94eSwiz u_int cx = ctx->ocx, cy = ctx->ocy, sx, sy; 2261f844e94eSwiz u_int i, j, x, y, rx, ry; 2262f844e94eSwiz int fallback = 0; 2263f844e94eSwiz 2264f844e94eSwiz if ((~tty->term->flags & TERM_SIXEL) && 2265f844e94eSwiz !tty_term_has(tty->term, TTYC_SXL)) 2266f844e94eSwiz fallback = 1; 2267f844e94eSwiz if (tty->xpixel == 0 || tty->ypixel == 0) 2268f844e94eSwiz fallback = 1; 2269f844e94eSwiz 2270f844e94eSwiz sixel_size_in_cells(si, &sx, &sy); 2271f844e94eSwiz log_debug("%s: image is %ux%u", __func__, sx, sy); 2272f844e94eSwiz if (!tty_clamp_area(tty, ctx, cx, cy, sx, sy, &i, &j, &x, &y, &rx, &ry)) 2273f844e94eSwiz return; 2274f844e94eSwiz log_debug("%s: clamping to %u,%u-%u,%u", __func__, i, j, rx, ry); 2275f844e94eSwiz 2276f844e94eSwiz if (fallback == 1) { 2277f844e94eSwiz data = xstrdup(im->fallback); 2278f844e94eSwiz size = strlen(data); 2279f844e94eSwiz } else { 2280f844e94eSwiz new = sixel_scale(si, tty->xpixel, tty->ypixel, i, j, rx, ry, 0); 2281f844e94eSwiz if (new == NULL) 2282f844e94eSwiz return; 2283f844e94eSwiz 2284f844e94eSwiz data = sixel_print(new, si, &size); 2285f844e94eSwiz } 2286f844e94eSwiz if (data != NULL) { 2287f844e94eSwiz log_debug("%s: %zu bytes: %s", __func__, size, data); 2288f844e94eSwiz tty_region_off(tty); 2289f844e94eSwiz tty_margin_off(tty); 2290f844e94eSwiz tty_cursor(tty, x, y); 2291f844e94eSwiz 2292f844e94eSwiz tty->flags |= TTY_NOBLOCK; 2293f844e94eSwiz tty_add(tty, data, size); 2294f844e94eSwiz tty_invalidate(tty); 2295f844e94eSwiz free(data); 2296f844e94eSwiz } 2297f844e94eSwiz 2298f844e94eSwiz if (fallback == 0) 2299f844e94eSwiz sixel_free(new); 2300f844e94eSwiz } 2301f844e94eSwiz #endif 2302f844e94eSwiz 2303e271dbb8Schristos void 230446548964Swiz tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx) 2305e271dbb8Schristos { 230646548964Swiz if (ctx->num == 0x11) { 230746548964Swiz /* 230846548964Swiz * This is an overlay and a command that moves the cursor so 230946548964Swiz * start synchronized updates. 231046548964Swiz */ 2311e271dbb8Schristos tty_sync_start(tty); 231246548964Swiz } else if (~ctx->num & 0x10) { 231346548964Swiz /* 231446548964Swiz * This is a pane. If there is an overlay, always start; 231546548964Swiz * otherwise, only if requested. 231646548964Swiz */ 231746548964Swiz if (ctx->num || tty->client->overlay_draw != NULL) 231846548964Swiz tty_sync_start(tty); 231946548964Swiz } 2320e271dbb8Schristos } 2321e271dbb8Schristos 2322e271dbb8Schristos void 2323e271dbb8Schristos tty_cell(struct tty *tty, const struct grid_cell *gc, 2324f844e94eSwiz const struct grid_cell *defaults, struct colour_palette *palette, 2325f844e94eSwiz struct hyperlinks *hl) 2326698d5317Sjmmv { 2327c7e17de0Schristos const struct grid_cell *gcp; 2328698d5317Sjmmv 2329698d5317Sjmmv /* Skip last character if terminal is stupid. */ 2330e271dbb8Schristos if ((tty->term->flags & TERM_NOAM) && 23314e179ddaSchristos tty->cy == tty->sy - 1 && 23324e179ddaSchristos tty->cx == tty->sx - 1) 2333698d5317Sjmmv return; 2334698d5317Sjmmv 2335698d5317Sjmmv /* If this is a padding character, do nothing. */ 2336698d5317Sjmmv if (gc->flags & GRID_FLAG_PADDING) 2337698d5317Sjmmv return; 2338698d5317Sjmmv 2339e271dbb8Schristos /* Check the output codeset and apply attributes. */ 2340c7e17de0Schristos gcp = tty_check_codeset(tty, gc); 2341f844e94eSwiz tty_attributes(tty, gcp, defaults, palette, hl); 2342e271dbb8Schristos 2343e271dbb8Schristos /* If it is a single character, write with putc to handle ACS. */ 2344c7e17de0Schristos if (gcp->data.size == 1) { 2345f844e94eSwiz tty_attributes(tty, gcp, defaults, palette, hl); 2346c7e17de0Schristos if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f) 2347698d5317Sjmmv return; 2348c7e17de0Schristos tty_putc(tty, *gcp->data.data); 2349698d5317Sjmmv return; 2350698d5317Sjmmv } 2351698d5317Sjmmv 2352928fc495Schristos /* Write the data. */ 2353c7e17de0Schristos tty_putn(tty, gcp->data.data, gcp->data.size, gcp->data.width); 2354698d5317Sjmmv } 2355698d5317Sjmmv 2356698d5317Sjmmv void 2357698d5317Sjmmv tty_reset(struct tty *tty) 2358698d5317Sjmmv { 2359698d5317Sjmmv struct grid_cell *gc = &tty->cell; 2360698d5317Sjmmv 23614e179ddaSchristos if (!grid_cells_equal(gc, &grid_default_cell)) { 2362f844e94eSwiz if (gc->link != 0) 2363f844e94eSwiz tty_putcode_ss(tty, TTYC_HLS, "", ""); 2364fe99a117Schristos if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) 2365698d5317Sjmmv tty_putcode(tty, TTYC_RMACS); 2366698d5317Sjmmv tty_putcode(tty, TTYC_SGR0); 2367698d5317Sjmmv memcpy(gc, &grid_default_cell, sizeof *gc); 2368698d5317Sjmmv } 23694e179ddaSchristos memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); 23704e179ddaSchristos } 23714e179ddaSchristos 23724e179ddaSchristos static void 23734e179ddaSchristos tty_invalidate(struct tty *tty) 23744e179ddaSchristos { 23754e179ddaSchristos memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell); 23764e179ddaSchristos memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell); 23774e179ddaSchristos 23784e179ddaSchristos tty->cx = tty->cy = UINT_MAX; 23794e179ddaSchristos tty->rupper = tty->rleft = UINT_MAX; 23804e179ddaSchristos tty->rlower = tty->rright = UINT_MAX; 23814e179ddaSchristos 23824e179ddaSchristos if (tty->flags & TTY_STARTED) { 238330744affSchristos if (tty_use_margin(tty)) 2384e271dbb8Schristos tty_putcode(tty, TTYC_ENMG); 23854e179ddaSchristos tty_putcode(tty, TTYC_SGR0); 23864e179ddaSchristos 23874e179ddaSchristos tty->mode = ALL_MODES; 23884e179ddaSchristos tty_update_mode(tty, MODE_CURSOR, NULL); 23894e179ddaSchristos 23904e179ddaSchristos tty_cursor(tty, 0, 0); 23914e179ddaSchristos tty_region_off(tty); 23924e179ddaSchristos tty_margin_off(tty); 23934e179ddaSchristos } else 23944e179ddaSchristos tty->mode = MODE_CURSOR; 23954e179ddaSchristos } 23964e179ddaSchristos 23974e179ddaSchristos /* Turn off margin. */ 2398698d5317Sjmmv void 23994e179ddaSchristos tty_region_off(struct tty *tty) 24004e179ddaSchristos { 24014e179ddaSchristos tty_region(tty, 0, tty->sy - 1); 24024e179ddaSchristos } 24034e179ddaSchristos 24044e179ddaSchristos /* Set region inside pane. */ 24054e179ddaSchristos static void 2406ed4e6cd4Schristos tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper, 2407ed4e6cd4Schristos u_int rlower) 2408698d5317Sjmmv { 2409e271dbb8Schristos tty_region(tty, ctx->yoff + rupper - ctx->woy, 2410e271dbb8Schristos ctx->yoff + rlower - ctx->woy); 2411698d5317Sjmmv } 2412698d5317Sjmmv 2413698d5317Sjmmv /* Set region at absolute position. */ 24144e179ddaSchristos static void 2415698d5317Sjmmv tty_region(struct tty *tty, u_int rupper, u_int rlower) 2416698d5317Sjmmv { 2417698d5317Sjmmv if (tty->rlower == rlower && tty->rupper == rupper) 2418698d5317Sjmmv return; 2419698d5317Sjmmv if (!tty_term_has(tty->term, TTYC_CSR)) 2420698d5317Sjmmv return; 2421698d5317Sjmmv 2422698d5317Sjmmv tty->rupper = rupper; 2423698d5317Sjmmv tty->rlower = rlower; 2424698d5317Sjmmv 2425698d5317Sjmmv /* 2426698d5317Sjmmv * Some terminals (such as PuTTY) do not correctly reset the cursor to 2427698d5317Sjmmv * 0,0 if it is beyond the last column (they do not reset their wrap 2428698d5317Sjmmv * flag so further output causes a line feed). As a workaround, do an 2429698d5317Sjmmv * explicit move to 0 first. 2430698d5317Sjmmv */ 2431e271dbb8Schristos if (tty->cx >= tty->sx) { 2432e271dbb8Schristos if (tty->cy == UINT_MAX) 2433e271dbb8Schristos tty_cursor(tty, 0, 0); 2434e271dbb8Schristos else 2435698d5317Sjmmv tty_cursor(tty, 0, tty->cy); 2436e271dbb8Schristos } 2437698d5317Sjmmv 2438f844e94eSwiz tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower); 24394e179ddaSchristos tty->cx = tty->cy = UINT_MAX; 24404e179ddaSchristos } 24414e179ddaSchristos 24424e179ddaSchristos /* Turn off margin. */ 24434e179ddaSchristos void 24444e179ddaSchristos tty_margin_off(struct tty *tty) 24454e179ddaSchristos { 24464e179ddaSchristos tty_margin(tty, 0, tty->sx - 1); 24474e179ddaSchristos } 24484e179ddaSchristos 24494e179ddaSchristos /* Set margin inside pane. */ 24504e179ddaSchristos static void 24514e179ddaSchristos tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx) 24524e179ddaSchristos { 2453e271dbb8Schristos tty_margin(tty, ctx->xoff - ctx->wox, 2454e271dbb8Schristos ctx->xoff + ctx->sx - 1 - ctx->wox); 24554e179ddaSchristos } 24564e179ddaSchristos 24574e179ddaSchristos /* Set margin at absolute position. */ 24584e179ddaSchristos static void 24594e179ddaSchristos tty_margin(struct tty *tty, u_int rleft, u_int rright) 24604e179ddaSchristos { 24614e179ddaSchristos if (!tty_use_margin(tty)) 24624e179ddaSchristos return; 24634e179ddaSchristos if (tty->rleft == rleft && tty->rright == rright) 24644e179ddaSchristos return; 24654e179ddaSchristos 2466f844e94eSwiz tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower); 24674e179ddaSchristos 24684e179ddaSchristos tty->rleft = rleft; 24694e179ddaSchristos tty->rright = rright; 24704e179ddaSchristos 24714e179ddaSchristos if (rleft == 0 && rright == tty->sx - 1) 2472e271dbb8Schristos tty_putcode(tty, TTYC_CLMG); 24734e179ddaSchristos else 2474f844e94eSwiz tty_putcode_ii(tty, TTYC_CMG, rleft, rright); 24754e179ddaSchristos tty->cx = tty->cy = UINT_MAX; 24764e179ddaSchristos } 24774e179ddaSchristos 24784e179ddaSchristos /* 24794e179ddaSchristos * Move the cursor, unless it would wrap itself when the next character is 24804e179ddaSchristos * printed. 24814e179ddaSchristos */ 24824e179ddaSchristos static void 24834e179ddaSchristos tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx, 24844e179ddaSchristos u_int cx, u_int cy) 24854e179ddaSchristos { 2486fe99a117Schristos if (!ctx->wrapped || 2487e271dbb8Schristos !tty_full_width(tty, ctx) || 2488e271dbb8Schristos (tty->term->flags & TERM_NOAM) || 24894e179ddaSchristos ctx->xoff + cx != 0 || 24904e179ddaSchristos ctx->yoff + cy != tty->cy + 1 || 24914e179ddaSchristos tty->cx < tty->sx || 24924e179ddaSchristos tty->cy == tty->rlower) 24934e179ddaSchristos tty_cursor_pane(tty, ctx, cx, cy); 24944e179ddaSchristos else 24954e179ddaSchristos log_debug("%s: will wrap at %u,%u", __func__, tty->cx, tty->cy); 2496698d5317Sjmmv } 2497698d5317Sjmmv 2498698d5317Sjmmv /* Move cursor inside pane. */ 24994e179ddaSchristos static void 2500698d5317Sjmmv tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy) 2501698d5317Sjmmv { 2502e271dbb8Schristos tty_cursor(tty, ctx->xoff + cx - ctx->wox, ctx->yoff + cy - ctx->woy); 2503698d5317Sjmmv } 2504698d5317Sjmmv 2505698d5317Sjmmv /* Move cursor to absolute position. */ 2506698d5317Sjmmv void 2507698d5317Sjmmv tty_cursor(struct tty *tty, u_int cx, u_int cy) 2508698d5317Sjmmv { 2509698d5317Sjmmv struct tty_term *term = tty->term; 2510698d5317Sjmmv u_int thisx, thisy; 2511698d5317Sjmmv int change; 2512698d5317Sjmmv 2513e271dbb8Schristos if (tty->flags & TTY_BLOCK) 2514e271dbb8Schristos return; 2515e271dbb8Schristos 2516698d5317Sjmmv thisx = tty->cx; 2517698d5317Sjmmv thisy = tty->cy; 2518698d5317Sjmmv 251946548964Swiz /* 252046548964Swiz * If in the automargin space, and want to be there, do not move. 252146548964Swiz * Otherwise, force the cursor to be in range (and complain). 252246548964Swiz */ 252346548964Swiz if (cx == thisx && cy == thisy && cx == tty->sx) 252446548964Swiz return; 252546548964Swiz if (cx > tty->sx - 1) { 252646548964Swiz log_debug("%s: x too big %u > %u", __func__, cx, tty->sx - 1); 252746548964Swiz cx = tty->sx - 1; 252846548964Swiz } 252946548964Swiz 2530698d5317Sjmmv /* No change. */ 2531698d5317Sjmmv if (cx == thisx && cy == thisy) 2532698d5317Sjmmv return; 2533698d5317Sjmmv 253446548964Swiz /* Currently at the very end of the line - use absolute movement. */ 2535698d5317Sjmmv if (thisx > tty->sx - 1) 2536698d5317Sjmmv goto absolute; 2537698d5317Sjmmv 2538698d5317Sjmmv /* Move to home position (0, 0). */ 2539698d5317Sjmmv if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) { 2540698d5317Sjmmv tty_putcode(tty, TTYC_HOME); 2541698d5317Sjmmv goto out; 2542698d5317Sjmmv } 2543698d5317Sjmmv 2544698d5317Sjmmv /* Zero on the next line. */ 2545fe99a117Schristos if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower && 2546fe99a117Schristos (!tty_use_margin(tty) || tty->rleft == 0)) { 2547698d5317Sjmmv tty_putc(tty, '\r'); 2548698d5317Sjmmv tty_putc(tty, '\n'); 2549698d5317Sjmmv goto out; 2550698d5317Sjmmv } 2551698d5317Sjmmv 2552698d5317Sjmmv /* Moving column or row. */ 2553698d5317Sjmmv if (cy == thisy) { 2554698d5317Sjmmv /* 2555698d5317Sjmmv * Moving column only, row staying the same. 2556698d5317Sjmmv */ 2557698d5317Sjmmv 2558698d5317Sjmmv /* To left edge. */ 2559fe99a117Schristos if (cx == 0 && (!tty_use_margin(tty) || tty->rleft == 0)) { 2560698d5317Sjmmv tty_putc(tty, '\r'); 2561698d5317Sjmmv goto out; 2562698d5317Sjmmv } 2563698d5317Sjmmv 2564698d5317Sjmmv /* One to the left. */ 2565698d5317Sjmmv if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) { 2566698d5317Sjmmv tty_putcode(tty, TTYC_CUB1); 2567698d5317Sjmmv goto out; 2568698d5317Sjmmv } 2569698d5317Sjmmv 2570698d5317Sjmmv /* One to the right. */ 2571698d5317Sjmmv if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) { 2572698d5317Sjmmv tty_putcode(tty, TTYC_CUF1); 2573698d5317Sjmmv goto out; 2574698d5317Sjmmv } 2575698d5317Sjmmv 2576698d5317Sjmmv /* Calculate difference. */ 2577698d5317Sjmmv change = thisx - cx; /* +ve left, -ve right */ 2578698d5317Sjmmv 2579698d5317Sjmmv /* 2580698d5317Sjmmv * Use HPA if change is larger than absolute, otherwise move 2581698d5317Sjmmv * the cursor with CUB/CUF. 2582698d5317Sjmmv */ 2583698d5317Sjmmv if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) { 2584f844e94eSwiz tty_putcode_i(tty, TTYC_HPA, cx); 2585698d5317Sjmmv goto out; 258668e6ba84Schristos } else if (change > 0 && 258768e6ba84Schristos tty_term_has(term, TTYC_CUB) && 258868e6ba84Schristos !tty_use_margin(tty)) { 25894e179ddaSchristos if (change == 2 && tty_term_has(term, TTYC_CUB1)) { 25904e179ddaSchristos tty_putcode(tty, TTYC_CUB1); 25914e179ddaSchristos tty_putcode(tty, TTYC_CUB1); 25924e179ddaSchristos goto out; 25934e179ddaSchristos } 2594f844e94eSwiz tty_putcode_i(tty, TTYC_CUB, change); 2595698d5317Sjmmv goto out; 259668e6ba84Schristos } else if (change < 0 && 259768e6ba84Schristos tty_term_has(term, TTYC_CUF) && 259868e6ba84Schristos !tty_use_margin(tty)) { 2599f844e94eSwiz tty_putcode_i(tty, TTYC_CUF, -change); 2600698d5317Sjmmv goto out; 2601698d5317Sjmmv } 2602698d5317Sjmmv } else if (cx == thisx) { 2603698d5317Sjmmv /* 2604698d5317Sjmmv * Moving row only, column staying the same. 2605698d5317Sjmmv */ 2606698d5317Sjmmv 2607698d5317Sjmmv /* One above. */ 2608698d5317Sjmmv if (thisy != tty->rupper && 2609698d5317Sjmmv cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) { 2610698d5317Sjmmv tty_putcode(tty, TTYC_CUU1); 2611698d5317Sjmmv goto out; 2612698d5317Sjmmv } 2613698d5317Sjmmv 2614698d5317Sjmmv /* One below. */ 2615698d5317Sjmmv if (thisy != tty->rlower && 2616698d5317Sjmmv cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) { 2617698d5317Sjmmv tty_putcode(tty, TTYC_CUD1); 2618698d5317Sjmmv goto out; 2619698d5317Sjmmv } 2620698d5317Sjmmv 2621698d5317Sjmmv /* Calculate difference. */ 2622698d5317Sjmmv change = thisy - cy; /* +ve up, -ve down */ 2623698d5317Sjmmv 2624698d5317Sjmmv /* 2625698d5317Sjmmv * Try to use VPA if change is larger than absolute or if this 2626698d5317Sjmmv * change would cross the scroll region, otherwise use CUU/CUD. 2627698d5317Sjmmv */ 2628698d5317Sjmmv if ((u_int) abs(change) > cy || 2629698d5317Sjmmv (change < 0 && cy - change > tty->rlower) || 2630698d5317Sjmmv (change > 0 && cy - change < tty->rupper)) { 2631698d5317Sjmmv if (tty_term_has(term, TTYC_VPA)) { 2632f844e94eSwiz tty_putcode_i(tty, TTYC_VPA, cy); 2633698d5317Sjmmv goto out; 2634698d5317Sjmmv } 2635698d5317Sjmmv } else if (change > 0 && tty_term_has(term, TTYC_CUU)) { 2636f844e94eSwiz tty_putcode_i(tty, TTYC_CUU, change); 2637698d5317Sjmmv goto out; 2638698d5317Sjmmv } else if (change < 0 && tty_term_has(term, TTYC_CUD)) { 2639f844e94eSwiz tty_putcode_i(tty, TTYC_CUD, -change); 2640698d5317Sjmmv goto out; 2641698d5317Sjmmv } 2642698d5317Sjmmv } 2643698d5317Sjmmv 2644698d5317Sjmmv absolute: 2645698d5317Sjmmv /* Absolute movement. */ 2646f844e94eSwiz tty_putcode_ii(tty, TTYC_CUP, cy, cx); 2647698d5317Sjmmv 2648698d5317Sjmmv out: 2649698d5317Sjmmv tty->cx = cx; 2650698d5317Sjmmv tty->cy = cy; 2651698d5317Sjmmv } 2652698d5317Sjmmv 2653f844e94eSwiz static void 2654f844e94eSwiz tty_hyperlink(struct tty *tty, const struct grid_cell *gc, 2655f844e94eSwiz struct hyperlinks *hl) 2656f844e94eSwiz { 2657f844e94eSwiz const char *uri, *id; 2658f844e94eSwiz 2659f844e94eSwiz if (gc->link == tty->cell.link) 2660f844e94eSwiz return; 2661f844e94eSwiz tty->cell.link = gc->link; 2662f844e94eSwiz 2663f844e94eSwiz if (hl == NULL) 2664f844e94eSwiz return; 2665f844e94eSwiz 2666f844e94eSwiz if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, NULL, &id)) 2667f844e94eSwiz tty_putcode_ss(tty, TTYC_HLS, "", ""); 2668f844e94eSwiz else 2669f844e94eSwiz tty_putcode_ss(tty, TTYC_HLS, id, uri); 2670f844e94eSwiz } 2671f844e94eSwiz 2672698d5317Sjmmv void 26735494e770Schristos tty_attributes(struct tty *tty, const struct grid_cell *gc, 2674f844e94eSwiz const struct grid_cell *defaults, struct colour_palette *palette, 2675f844e94eSwiz struct hyperlinks *hl) 2676698d5317Sjmmv { 2677698d5317Sjmmv struct grid_cell *tc = &tty->cell, gc2; 26784e179ddaSchristos int changed; 2679698d5317Sjmmv 26804e179ddaSchristos /* Copy cell and update default colours. */ 2681698d5317Sjmmv memcpy(&gc2, gc, sizeof gc2); 268246548964Swiz if (~gc->flags & GRID_FLAG_NOPALETTE) { 2683e271dbb8Schristos if (gc2.fg == 8) 2684e271dbb8Schristos gc2.fg = defaults->fg; 2685e271dbb8Schristos if (gc2.bg == 8) 2686e271dbb8Schristos gc2.bg = defaults->bg; 268746548964Swiz } 2688e271dbb8Schristos 2689e271dbb8Schristos /* Ignore cell if it is the same as the last one. */ 2690e271dbb8Schristos if (gc2.attr == tty->last_cell.attr && 2691e271dbb8Schristos gc2.fg == tty->last_cell.fg && 2692e271dbb8Schristos gc2.bg == tty->last_cell.bg && 2693f844e94eSwiz gc2.us == tty->last_cell.us && 2694f844e94eSwiz gc2.link == tty->last_cell.link) 2695e271dbb8Schristos return; 2696698d5317Sjmmv 2697698d5317Sjmmv /* 2698698d5317Sjmmv * If no setab, try to use the reverse attribute as a best-effort for a 2699698d5317Sjmmv * non-default background. This is a bit of a hack but it doesn't do 2700698d5317Sjmmv * any serious harm and makes a couple of applications happier. 2701698d5317Sjmmv */ 2702698d5317Sjmmv if (!tty_term_has(tty->term, TTYC_SETAB)) { 2703698d5317Sjmmv if (gc2.attr & GRID_ATTR_REVERSE) { 27040a274e86Schristos if (gc2.fg != 7 && !COLOUR_DEFAULT(gc2.fg)) 2705698d5317Sjmmv gc2.attr &= ~GRID_ATTR_REVERSE; 2706698d5317Sjmmv } else { 27070a274e86Schristos if (gc2.bg != 0 && !COLOUR_DEFAULT(gc2.bg)) 2708698d5317Sjmmv gc2.attr |= GRID_ATTR_REVERSE; 2709698d5317Sjmmv } 2710698d5317Sjmmv } 2711698d5317Sjmmv 2712698d5317Sjmmv /* Fix up the colours if necessary. */ 2713e271dbb8Schristos tty_check_fg(tty, palette, &gc2); 2714e271dbb8Schristos tty_check_bg(tty, palette, &gc2); 2715e271dbb8Schristos tty_check_us(tty, palette, &gc2); 2716698d5317Sjmmv 271730744affSchristos /* 271830744affSchristos * If any bits are being cleared or the underline colour is now default, 271930744affSchristos * reset everything. 272030744affSchristos */ 272130744affSchristos if ((tc->attr & ~gc2.attr) || (tc->us != gc2.us && gc2.us == 0)) 2722698d5317Sjmmv tty_reset(tty); 2723698d5317Sjmmv 2724698d5317Sjmmv /* 2725698d5317Sjmmv * Set the colours. This may call tty_reset() (so it comes next) and 272630744affSchristos * may add to (NOT remove) the desired attributes. 2727698d5317Sjmmv */ 2728698d5317Sjmmv tty_colours(tty, &gc2); 2729698d5317Sjmmv 2730698d5317Sjmmv /* Filter out attribute bits already set. */ 2731698d5317Sjmmv changed = gc2.attr & ~tc->attr; 2732698d5317Sjmmv tc->attr = gc2.attr; 2733698d5317Sjmmv 2734698d5317Sjmmv /* Set the attributes. */ 2735698d5317Sjmmv if (changed & GRID_ATTR_BRIGHT) 2736698d5317Sjmmv tty_putcode(tty, TTYC_BOLD); 2737698d5317Sjmmv if (changed & GRID_ATTR_DIM) 2738698d5317Sjmmv tty_putcode(tty, TTYC_DIM); 27395494e770Schristos if (changed & GRID_ATTR_ITALICS) 27405494e770Schristos tty_set_italics(tty); 27410a274e86Schristos if (changed & GRID_ATTR_ALL_UNDERSCORE) { 27420a274e86Schristos if ((changed & GRID_ATTR_UNDERSCORE) || 27430a274e86Schristos !tty_term_has(tty->term, TTYC_SMULX)) 2744698d5317Sjmmv tty_putcode(tty, TTYC_SMUL); 27450a274e86Schristos else if (changed & GRID_ATTR_UNDERSCORE_2) 2746f844e94eSwiz tty_putcode_i(tty, TTYC_SMULX, 2); 27470a274e86Schristos else if (changed & GRID_ATTR_UNDERSCORE_3) 2748f844e94eSwiz tty_putcode_i(tty, TTYC_SMULX, 3); 27490a274e86Schristos else if (changed & GRID_ATTR_UNDERSCORE_4) 2750f844e94eSwiz tty_putcode_i(tty, TTYC_SMULX, 4); 27510a274e86Schristos else if (changed & GRID_ATTR_UNDERSCORE_5) 2752f844e94eSwiz tty_putcode_i(tty, TTYC_SMULX, 5); 27530a274e86Schristos } 2754698d5317Sjmmv if (changed & GRID_ATTR_BLINK) 2755698d5317Sjmmv tty_putcode(tty, TTYC_BLINK); 2756698d5317Sjmmv if (changed & GRID_ATTR_REVERSE) { 2757698d5317Sjmmv if (tty_term_has(tty->term, TTYC_REV)) 2758698d5317Sjmmv tty_putcode(tty, TTYC_REV); 2759698d5317Sjmmv else if (tty_term_has(tty->term, TTYC_SMSO)) 2760698d5317Sjmmv tty_putcode(tty, TTYC_SMSO); 2761698d5317Sjmmv } 2762698d5317Sjmmv if (changed & GRID_ATTR_HIDDEN) 2763698d5317Sjmmv tty_putcode(tty, TTYC_INVIS); 27644e179ddaSchristos if (changed & GRID_ATTR_STRIKETHROUGH) 27654e179ddaSchristos tty_putcode(tty, TTYC_SMXX); 276630744affSchristos if (changed & GRID_ATTR_OVERLINE) 276730744affSchristos tty_putcode(tty, TTYC_SMOL); 2768fe99a117Schristos if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty)) 2769698d5317Sjmmv tty_putcode(tty, TTYC_SMACS); 2770e271dbb8Schristos 2771f844e94eSwiz /* Set hyperlink if any. */ 2772f844e94eSwiz tty_hyperlink(tty, gc, hl); 2773f844e94eSwiz 2774e271dbb8Schristos memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell); 2775698d5317Sjmmv } 2776698d5317Sjmmv 27774e179ddaSchristos static void 2778698d5317Sjmmv tty_colours(struct tty *tty, const struct grid_cell *gc) 2779698d5317Sjmmv { 2780698d5317Sjmmv struct grid_cell *tc = &tty->cell; 2781698d5317Sjmmv 2782698d5317Sjmmv /* No changes? Nothing is necessary. */ 278330744affSchristos if (gc->fg == tc->fg && gc->bg == tc->bg && gc->us == tc->us) 2784698d5317Sjmmv return; 2785698d5317Sjmmv 2786698d5317Sjmmv /* 2787698d5317Sjmmv * Is either the default colour? This is handled specially because the 2788698d5317Sjmmv * best solution might be to reset both colours to default, in which 2789698d5317Sjmmv * case if only one is default need to fall onward to set the other 2790698d5317Sjmmv * colour. 2791698d5317Sjmmv */ 27920a274e86Schristos if (COLOUR_DEFAULT(gc->fg) || COLOUR_DEFAULT(gc->bg)) { 2793698d5317Sjmmv /* 2794*890b6d91Swiz * If don't have AX, send sgr0. This resets both colours to default. 2795698d5317Sjmmv * Otherwise, try to set the default colour only as needed. 2796698d5317Sjmmv */ 2797*890b6d91Swiz if (!tty_term_flag(tty->term, TTYC_AX)) 2798698d5317Sjmmv tty_reset(tty); 2799698d5317Sjmmv else { 28000a274e86Schristos if (COLOUR_DEFAULT(gc->fg) && !COLOUR_DEFAULT(tc->fg)) { 2801698d5317Sjmmv tty_puts(tty, "\033[39m"); 28020a274e86Schristos tc->fg = gc->fg; 2803698d5317Sjmmv } 28040a274e86Schristos if (COLOUR_DEFAULT(gc->bg) && !COLOUR_DEFAULT(tc->bg)) { 2805698d5317Sjmmv tty_puts(tty, "\033[49m"); 28060a274e86Schristos tc->bg = gc->bg; 2807698d5317Sjmmv } 2808698d5317Sjmmv } 2809698d5317Sjmmv } 2810698d5317Sjmmv 2811698d5317Sjmmv /* Set the foreground colour. */ 28120a274e86Schristos if (!COLOUR_DEFAULT(gc->fg) && gc->fg != tc->fg) 2813698d5317Sjmmv tty_colours_fg(tty, gc); 2814698d5317Sjmmv 2815698d5317Sjmmv /* 2816698d5317Sjmmv * Set the background colour. This must come after the foreground as 2817*890b6d91Swiz * tty_colours_fg() can call tty_reset(). 2818698d5317Sjmmv */ 28190a274e86Schristos if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg) 2820698d5317Sjmmv tty_colours_bg(tty, gc); 282130744affSchristos 282246548964Swiz /* Set the underscore colour. */ 282330744affSchristos if (gc->us != tc->us) 282430744affSchristos tty_colours_us(tty, gc); 2825698d5317Sjmmv } 2826698d5317Sjmmv 28274e179ddaSchristos static void 282846548964Swiz tty_check_fg(struct tty *tty, struct colour_palette *palette, 282946548964Swiz struct grid_cell *gc) 2830698d5317Sjmmv { 28314e179ddaSchristos u_char r, g, b; 2832698d5317Sjmmv u_int colours; 28334e179ddaSchristos int c; 28344e179ddaSchristos 2835fe99a117Schristos /* 2836fe99a117Schristos * Perform substitution if this pane has a palette. If the bright 2837f844e94eSwiz * attribute is set and Nobr is not present, use the bright entry in 2838f844e94eSwiz * the palette by changing to the aixterm colour 2839fe99a117Schristos */ 2840fe99a117Schristos if (~gc->flags & GRID_FLAG_NOPALETTE) { 2841fe99a117Schristos c = gc->fg; 2842f844e94eSwiz if (c < 8 && 2843f844e94eSwiz gc->attr & GRID_ATTR_BRIGHT && 2844f844e94eSwiz !tty_term_has(tty->term, TTYC_NOBR)) 2845fe99a117Schristos c += 90; 284646548964Swiz if ((c = colour_palette_get(palette, c)) != -1) 28474e179ddaSchristos gc->fg = c; 2848fe99a117Schristos } 2849698d5317Sjmmv 2850ed4e6cd4Schristos /* Is this a 24-bit colour? */ 28514e179ddaSchristos if (gc->fg & COLOUR_FLAG_RGB) { 2852ed4e6cd4Schristos /* Not a 24-bit terminal? Translate to 256-colour palette. */ 2853e271dbb8Schristos if (tty->term->flags & TERM_RGBCOLOURS) 285468e6ba84Schristos return; 28554e179ddaSchristos colour_split_rgb(gc->fg, &r, &g, &b); 28564e179ddaSchristos gc->fg = colour_find_rgb(r, g, b); 2857ed4e6cd4Schristos } 28584e179ddaSchristos 28594e179ddaSchristos /* How many colours does this terminal have? */ 2860e271dbb8Schristos if (tty->term->flags & TERM_256COLOURS) 28614e179ddaSchristos colours = 256; 28624e179ddaSchristos else 28635494e770Schristos colours = tty_term_number(tty->term, TTYC_COLORS); 28645494e770Schristos 2865698d5317Sjmmv /* Is this a 256-colour colour? */ 28664e179ddaSchristos if (gc->fg & COLOUR_FLAG_256) { 2867698d5317Sjmmv /* And not a 256 colour mode? */ 2868e271dbb8Schristos if (colours < 256) { 2869698d5317Sjmmv gc->fg = colour_256to16(gc->fg); 2870698d5317Sjmmv if (gc->fg & 8) { 2871698d5317Sjmmv gc->fg &= 7; 28725494e770Schristos if (colours >= 16) 28735494e770Schristos gc->fg += 90; 287468e6ba84Schristos } 2875698d5317Sjmmv } 2876698d5317Sjmmv return; 2877698d5317Sjmmv } 2878698d5317Sjmmv 2879698d5317Sjmmv /* Is this an aixterm colour? */ 2880698d5317Sjmmv if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) { 2881698d5317Sjmmv gc->fg -= 90; 2882698d5317Sjmmv gc->attr |= GRID_ATTR_BRIGHT; 2883698d5317Sjmmv } 2884698d5317Sjmmv } 2885698d5317Sjmmv 28864e179ddaSchristos static void 288746548964Swiz tty_check_bg(struct tty *tty, struct colour_palette *palette, 288846548964Swiz struct grid_cell *gc) 2889698d5317Sjmmv { 28904e179ddaSchristos u_char r, g, b; 2891698d5317Sjmmv u_int colours; 28924e179ddaSchristos int c; 28934e179ddaSchristos 2894fe99a117Schristos /* Perform substitution if this pane has a palette. */ 2895fe99a117Schristos if (~gc->flags & GRID_FLAG_NOPALETTE) { 289646548964Swiz if ((c = colour_palette_get(palette, gc->bg)) != -1) 28974e179ddaSchristos gc->bg = c; 2898fe99a117Schristos } 2899698d5317Sjmmv 2900ed4e6cd4Schristos /* Is this a 24-bit colour? */ 29014e179ddaSchristos if (gc->bg & COLOUR_FLAG_RGB) { 2902ed4e6cd4Schristos /* Not a 24-bit terminal? Translate to 256-colour palette. */ 2903e271dbb8Schristos if (tty->term->flags & TERM_RGBCOLOURS) 290468e6ba84Schristos return; 29054e179ddaSchristos colour_split_rgb(gc->bg, &r, &g, &b); 29064e179ddaSchristos gc->bg = colour_find_rgb(r, g, b); 2907ed4e6cd4Schristos } 29084e179ddaSchristos 29094e179ddaSchristos /* How many colours does this terminal have? */ 2910e271dbb8Schristos if (tty->term->flags & TERM_256COLOURS) 29114e179ddaSchristos colours = 256; 29124e179ddaSchristos else 29135494e770Schristos colours = tty_term_number(tty->term, TTYC_COLORS); 29145494e770Schristos 2915698d5317Sjmmv /* Is this a 256-colour colour? */ 29164e179ddaSchristos if (gc->bg & COLOUR_FLAG_256) { 2917698d5317Sjmmv /* 2918698d5317Sjmmv * And not a 256 colour mode? Translate to 16-colour 2919698d5317Sjmmv * palette. Bold background doesn't exist portably, so just 2920698d5317Sjmmv * discard the bold bit if set. 2921698d5317Sjmmv */ 2922e271dbb8Schristos if (colours < 256) { 2923698d5317Sjmmv gc->bg = colour_256to16(gc->bg); 29245494e770Schristos if (gc->bg & 8) { 2925698d5317Sjmmv gc->bg &= 7; 29265494e770Schristos if (colours >= 16) 292730744affSchristos gc->bg += 90; 29285494e770Schristos } 2929698d5317Sjmmv } 2930698d5317Sjmmv return; 2931698d5317Sjmmv } 2932698d5317Sjmmv 2933698d5317Sjmmv /* Is this an aixterm colour? */ 29345494e770Schristos if (gc->bg >= 90 && gc->bg <= 97 && colours < 16) 2935698d5317Sjmmv gc->bg -= 90; 2936698d5317Sjmmv } 2937698d5317Sjmmv 29384e179ddaSchristos static void 293946548964Swiz tty_check_us(__unused struct tty *tty, struct colour_palette *palette, 294046548964Swiz struct grid_cell *gc) 294130744affSchristos { 294230744affSchristos int c; 294330744affSchristos 294430744affSchristos /* Perform substitution if this pane has a palette. */ 294530744affSchristos if (~gc->flags & GRID_FLAG_NOPALETTE) { 294646548964Swiz if ((c = colour_palette_get(palette, gc->us)) != -1) 294730744affSchristos gc->us = c; 294830744affSchristos } 294930744affSchristos 2950f844e94eSwiz /* Convert underscore colour if only RGB can be supported. */ 2951f844e94eSwiz if (!tty_term_has(tty->term, TTYC_SETULC1)) { 2952f844e94eSwiz if ((c = colour_force_rgb (gc->us)) == -1) 2953f844e94eSwiz gc->us = 8; 2954f844e94eSwiz else 2955f844e94eSwiz gc->us = c; 2956f844e94eSwiz } 295730744affSchristos } 295830744affSchristos 295930744affSchristos static void 2960698d5317Sjmmv tty_colours_fg(struct tty *tty, const struct grid_cell *gc) 2961698d5317Sjmmv { 2962698d5317Sjmmv struct grid_cell *tc = &tty->cell; 2963698d5317Sjmmv char s[32]; 2964698d5317Sjmmv 2965*890b6d91Swiz /* 2966*890b6d91Swiz * If the current colour is an aixterm bright colour and the new is not, 2967*890b6d91Swiz * reset because some terminals do not clear bright correctly. 2968*890b6d91Swiz */ 2969*890b6d91Swiz if (tty->cell.fg >= 90 && 2970*890b6d91Swiz tty->cell.bg <= 97 && 2971*890b6d91Swiz (gc->fg < 90 || gc->fg > 97)) 2972*890b6d91Swiz tty_reset(tty); 2973*890b6d91Swiz 29744e179ddaSchristos /* Is this a 24-bit or 256-colour colour? */ 2975c7e17de0Schristos if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) { 29764e179ddaSchristos if (tty_try_colour(tty, gc->fg, "38") == 0) 2977e271dbb8Schristos goto save; 2978ed4e6cd4Schristos /* Should not get here, already converted in tty_check_fg. */ 2979698d5317Sjmmv return; 2980698d5317Sjmmv } 2981698d5317Sjmmv 2982698d5317Sjmmv /* Is this an aixterm bright colour? */ 29834e179ddaSchristos if (gc->fg >= 90 && gc->fg <= 97) { 2984e271dbb8Schristos if (tty->term->flags & TERM_256COLOURS) { 29854e179ddaSchristos xsnprintf(s, sizeof s, "\033[%dm", gc->fg); 2986698d5317Sjmmv tty_puts(tty, s); 298730744affSchristos } else 2988f844e94eSwiz tty_putcode_i(tty, TTYC_SETAF, gc->fg - 90 + 8); 2989e271dbb8Schristos goto save; 2990698d5317Sjmmv } 2991698d5317Sjmmv 2992698d5317Sjmmv /* Otherwise set the foreground colour. */ 2993f844e94eSwiz tty_putcode_i(tty, TTYC_SETAF, gc->fg); 2994698d5317Sjmmv 2995e271dbb8Schristos save: 2996698d5317Sjmmv /* Save the new values in the terminal current cell. */ 29974e179ddaSchristos tc->fg = gc->fg; 2998698d5317Sjmmv } 2999698d5317Sjmmv 30004e179ddaSchristos static void 3001698d5317Sjmmv tty_colours_bg(struct tty *tty, const struct grid_cell *gc) 3002698d5317Sjmmv { 3003698d5317Sjmmv struct grid_cell *tc = &tty->cell; 3004698d5317Sjmmv char s[32]; 3005698d5317Sjmmv 30064e179ddaSchristos /* Is this a 24-bit or 256-colour colour? */ 3007c7e17de0Schristos if (gc->bg & COLOUR_FLAG_RGB || gc->bg & COLOUR_FLAG_256) { 30084e179ddaSchristos if (tty_try_colour(tty, gc->bg, "48") == 0) 3009e271dbb8Schristos goto save; 3010ed4e6cd4Schristos /* Should not get here, already converted in tty_check_bg. */ 3011698d5317Sjmmv return; 3012698d5317Sjmmv } 3013698d5317Sjmmv 3014698d5317Sjmmv /* Is this an aixterm bright colour? */ 30154e179ddaSchristos if (gc->bg >= 90 && gc->bg <= 97) { 3016e271dbb8Schristos if (tty->term->flags & TERM_256COLOURS) { 30174e179ddaSchristos xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10); 3018698d5317Sjmmv tty_puts(tty, s); 301930744affSchristos } else 3020f844e94eSwiz tty_putcode_i(tty, TTYC_SETAB, gc->bg - 90 + 8); 3021e271dbb8Schristos goto save; 3022698d5317Sjmmv } 3023698d5317Sjmmv 3024698d5317Sjmmv /* Otherwise set the background colour. */ 3025f844e94eSwiz tty_putcode_i(tty, TTYC_SETAB, gc->bg); 3026698d5317Sjmmv 3027e271dbb8Schristos save: 3028698d5317Sjmmv /* Save the new values in the terminal current cell. */ 30294e179ddaSchristos tc->bg = gc->bg; 3030698d5317Sjmmv } 3031698d5317Sjmmv 303230744affSchristos static void 303330744affSchristos tty_colours_us(struct tty *tty, const struct grid_cell *gc) 303430744affSchristos { 303530744affSchristos struct grid_cell *tc = &tty->cell; 303630744affSchristos u_int c; 303730744affSchristos u_char r, g, b; 303830744affSchristos 3039e271dbb8Schristos /* Clear underline colour. */ 3040f844e94eSwiz if (COLOUR_DEFAULT(gc->us)) { 3041e271dbb8Schristos tty_putcode(tty, TTYC_OL); 3042e271dbb8Schristos goto save; 3043e271dbb8Schristos } 3044e271dbb8Schristos 3045f844e94eSwiz /* 3046f844e94eSwiz * If this is not an RGB colour, use Setulc1 if it exists, otherwise 3047f844e94eSwiz * convert. 3048f844e94eSwiz */ 3049f844e94eSwiz if (~gc->us & COLOUR_FLAG_RGB) { 3050f844e94eSwiz c = gc->us; 3051f844e94eSwiz if ((~c & COLOUR_FLAG_256) && (c >= 90 && c <= 97)) 3052f844e94eSwiz c -= 82; 3053f844e94eSwiz tty_putcode_i(tty, TTYC_SETULC1, c & ~COLOUR_FLAG_256); 305430744affSchristos return; 3055f844e94eSwiz } 305630744affSchristos 305730744affSchristos /* 3058e271dbb8Schristos * Setulc and setal follows the ncurses(3) one argument "direct colour" 305930744affSchristos * capability format. Calculate the colour value. 306030744affSchristos */ 306130744affSchristos colour_split_rgb(gc->us, &r, &g, &b); 306230744affSchristos c = (65536 * r) + (256 * g) + b; 306330744affSchristos 3064e271dbb8Schristos /* 3065e271dbb8Schristos * Write the colour. Only use setal if the RGB flag is set because the 3066e271dbb8Schristos * non-RGB version may be wrong. 3067e271dbb8Schristos */ 3068e271dbb8Schristos if (tty_term_has(tty->term, TTYC_SETULC)) 3069f844e94eSwiz tty_putcode_i(tty, TTYC_SETULC, c); 3070e271dbb8Schristos else if (tty_term_has(tty->term, TTYC_SETAL) && 3071e271dbb8Schristos tty_term_has(tty->term, TTYC_RGB)) 3072f844e94eSwiz tty_putcode_i(tty, TTYC_SETAL, c); 307330744affSchristos 3074e271dbb8Schristos save: 307530744affSchristos /* Save the new values in the terminal current cell. */ 307630744affSchristos tc->us = gc->us; 307730744affSchristos } 307830744affSchristos 30794e179ddaSchristos static int 30804e179ddaSchristos tty_try_colour(struct tty *tty, int colour, const char *type) 3081698d5317Sjmmv { 30824e179ddaSchristos u_char r, g, b; 3083698d5317Sjmmv 30844e179ddaSchristos if (colour & COLOUR_FLAG_256) { 3085e271dbb8Schristos if (*type == '3' && tty_term_has(tty->term, TTYC_SETAF)) 3086f844e94eSwiz tty_putcode_i(tty, TTYC_SETAF, colour & 0xff); 3087e271dbb8Schristos else if (tty_term_has(tty->term, TTYC_SETAB)) 3088f844e94eSwiz tty_putcode_i(tty, TTYC_SETAB, colour & 0xff); 3089928fc495Schristos return (0); 3090928fc495Schristos } 3091698d5317Sjmmv 30924e179ddaSchristos if (colour & COLOUR_FLAG_RGB) { 309368e6ba84Schristos colour_split_rgb(colour & 0xffffff, &r, &g, &b); 3094e271dbb8Schristos if (*type == '3' && tty_term_has(tty->term, TTYC_SETRGBF)) 3095f844e94eSwiz tty_putcode_iii(tty, TTYC_SETRGBF, r, g, b); 3096e271dbb8Schristos else if (tty_term_has(tty->term, TTYC_SETRGBB)) 3097f844e94eSwiz tty_putcode_iii(tty, TTYC_SETRGBB, r, g, b); 3098ed4e6cd4Schristos return (0); 3099ed4e6cd4Schristos } 3100ed4e6cd4Schristos 31014e179ddaSchristos return (-1); 31024e179ddaSchristos } 31034e179ddaSchristos 31044e179ddaSchristos static void 3105e271dbb8Schristos tty_window_default_style(struct grid_cell *gc, struct window_pane *wp) 3106e271dbb8Schristos { 3107e271dbb8Schristos memcpy(gc, &grid_default_cell, sizeof *gc); 310846548964Swiz gc->fg = wp->palette.fg; 310946548964Swiz gc->bg = wp->palette.bg; 3110e271dbb8Schristos } 3111e271dbb8Schristos 3112e271dbb8Schristos void 31130a274e86Schristos tty_default_colours(struct grid_cell *gc, struct window_pane *wp) 31145494e770Schristos { 311530744affSchristos struct options *oo = wp->options; 311646548964Swiz struct format_tree *ft; 3117e271dbb8Schristos 3118e271dbb8Schristos memcpy(gc, &grid_default_cell, sizeof *gc); 31195494e770Schristos 312030744affSchristos if (wp->flags & PANE_STYLECHANGED) { 312146548964Swiz log_debug("%%%u: style changed", wp->id); 312230744affSchristos wp->flags &= ~PANE_STYLECHANGED; 312330744affSchristos 312446548964Swiz ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, 312546548964Swiz FORMAT_NOJOBS); 312646548964Swiz format_defaults(ft, NULL, NULL, NULL, wp); 3127e271dbb8Schristos tty_window_default_style(&wp->cached_active_gc, wp); 312846548964Swiz style_add(&wp->cached_active_gc, oo, "window-active-style", ft); 3129e271dbb8Schristos tty_window_default_style(&wp->cached_gc, wp); 313046548964Swiz style_add(&wp->cached_gc, oo, "window-style", ft); 313146548964Swiz format_free(ft); 31324e179ddaSchristos } 31335494e770Schristos 31344e179ddaSchristos if (gc->fg == 8) { 3135e271dbb8Schristos if (wp == wp->window->active && wp->cached_active_gc.fg != 8) 3136e271dbb8Schristos gc->fg = wp->cached_active_gc.fg; 31374e179ddaSchristos else 3138e271dbb8Schristos gc->fg = wp->cached_gc.fg; 31390a274e86Schristos } 31404e179ddaSchristos 31414e179ddaSchristos if (gc->bg == 8) { 3142e271dbb8Schristos if (wp == wp->window->active && wp->cached_active_gc.bg != 8) 3143e271dbb8Schristos gc->bg = wp->cached_active_gc.bg; 31444e179ddaSchristos else 3145e271dbb8Schristos gc->bg = wp->cached_gc.bg; 3146928fc495Schristos } 31470a274e86Schristos } 3148698d5317Sjmmv 31494e179ddaSchristos static void 3150e271dbb8Schristos tty_default_attributes(struct tty *tty, const struct grid_cell *defaults, 3151f844e94eSwiz struct colour_palette *palette, u_int bg, struct hyperlinks *hl) 31524e179ddaSchristos { 3153e271dbb8Schristos struct grid_cell gc; 31544e179ddaSchristos 31554e179ddaSchristos memcpy(&gc, &grid_default_cell, sizeof gc); 31564e179ddaSchristos gc.bg = bg; 3157f844e94eSwiz tty_attributes(tty, &gc, defaults, palette, hl); 3158698d5317Sjmmv } 315946548964Swiz 316046548964Swiz static void 316146548964Swiz tty_clipboard_query_callback(__unused int fd, __unused short events, void *data) 316246548964Swiz { 316346548964Swiz struct tty *tty = data; 316446548964Swiz struct client *c = tty->client; 316546548964Swiz 316646548964Swiz c->flags &= ~CLIENT_CLIPBOARDBUFFER; 316746548964Swiz free(c->clipboard_panes); 316846548964Swiz c->clipboard_panes = NULL; 316946548964Swiz c->clipboard_npanes = 0; 317046548964Swiz 317146548964Swiz tty->flags &= ~TTY_OSC52QUERY; 317246548964Swiz } 317346548964Swiz 317446548964Swiz void 317546548964Swiz tty_clipboard_query(struct tty *tty) 317646548964Swiz { 317746548964Swiz struct timeval tv = { .tv_sec = TTY_QUERY_TIMEOUT }; 317846548964Swiz 317946548964Swiz if ((~tty->flags & TTY_STARTED) || (tty->flags & TTY_OSC52QUERY)) 318046548964Swiz return; 3181f844e94eSwiz tty_putcode_ss(tty, TTYC_MS, "", "?"); 318246548964Swiz 318346548964Swiz tty->flags |= TTY_OSC52QUERY; 318446548964Swiz evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty); 318546548964Swiz evtimer_add(&tty->clipboard_timer, &tv); 318646548964Swiz } 3187