Coverage Report

Created: 2026-08-31 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/tty.c
Line
Count
Source
1
/* $OpenBSD: tty.c,v 1.480 2026/08/25 08:37:08 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/ioctl.h>
21
22
#include <netinet/in.h>
23
24
#include <curses.h>
25
#include <errno.h>
26
#include <fcntl.h>
27
#include <resolv.h>
28
#include <stdlib.h>
29
#include <string.h>
30
#include <termios.h>
31
#include <time.h>
32
#include <unistd.h>
33
34
#include "tmux.h"
35
36
static int  tty_log_fd = -1;
37
38
static void tty_start_timer_callback(int, short, void *);
39
static void tty_clipboard_query_callback(int, short, void *);
40
static void tty_set_italics(struct tty *);
41
static int  tty_try_colour(struct tty *, int, const char *);
42
static void tty_force_cursor_colour(struct tty *, int);
43
static void tty_cursor_pane(struct tty *, const struct tty_ctx *, u_int,
44
        u_int);
45
static void tty_cursor_pane_unless_wrap(struct tty *,
46
        const struct tty_ctx *, u_int, u_int);
47
static void tty_colours(struct tty *, const struct grid_cell *);
48
static void tty_check_fg(struct tty *, struct colour_palette *,
49
        struct grid_cell *);
50
static void tty_check_bg(struct tty *, struct colour_palette *,
51
        struct grid_cell *);
52
static void tty_check_us(struct tty *, struct colour_palette *,
53
        struct grid_cell *);
54
static int  tty_map_theme_colour(struct tty *, int);
55
static void tty_colours_fg(struct tty *, const struct grid_cell *);
56
static void tty_colours_bg(struct tty *, const struct grid_cell *);
57
static void tty_colours_us(struct tty *, const struct grid_cell *);
58
59
static void tty_region_pane(struct tty *, const struct tty_ctx *, u_int,
60
        u_int);
61
static void tty_region(struct tty *, u_int, u_int);
62
static void tty_margin_pane(struct tty *, const struct tty_ctx *);
63
static void tty_margin(struct tty *, u_int, u_int);
64
static int  tty_large_region(struct tty *, const struct tty_ctx *);
65
static void tty_redraw_region(struct tty *, const struct tty_ctx *);
66
static void tty_emulate_repeat(struct tty *, enum tty_code_code,
67
        enum tty_code_code, u_int);
68
static void tty_draw_pane(struct tty *, const struct tty_ctx *, u_int);
69
static int  tty_check_overlay(struct tty *, u_int, u_int);
70
71
#ifdef ENABLE_SIXEL
72
static void tty_write_one(void (*)(struct tty *, const struct tty_ctx *),
73
        struct client *, struct tty_ctx *);
74
#endif
75
76
#define tty_use_margin(tty) \
77
0
  (tty->term->flags & TERM_DECSLRM)
78
#define tty_full_width(tty, ctx) \
79
0
  ((ctx)->xoff == 0 && (ctx)->sx >= (tty)->sx)
80
81
0
#define TTY_BLOCK_INTERVAL (100000 /* 100 milliseconds */)
82
0
#define TTY_BLOCK_START(tty) (1 + ((tty)->sx * (tty)->sy) * 8)
83
0
#define TTY_BLOCK_STOP(tty) (1 + ((tty)->sx * (tty)->sy) / 8)
84
85
0
#define TTY_QUERY_TIMEOUT 5
86
0
#define TTY_REQUEST_LIMIT 30
87
88
static struct tty_style_ctx tty_default_style_ctx = {
89
  &grid_default_cell, NULL, 0, NULL
90
};
91
92
void
93
tty_create_log(void)
94
0
{
95
0
  char  name[64];
96
97
0
  xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid());
98
99
0
  tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644);
100
0
  if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1)
101
0
    fatal("fcntl failed");
102
0
}
103
104
int
105
tty_init(struct tty *tty, struct client *c)
106
0
{
107
0
  if (!isatty(c->fd))
108
0
    return (-1);
109
110
0
  memset(tty, 0, sizeof *tty);
111
0
  tty->client = c;
112
113
0
  tty->cstyle = SCREEN_CURSOR_DEFAULT;
114
0
  tty->ccolour = -1;
115
0
  tty->fg = tty->bg = -1;
116
0
  tty->mouse_last_pane = -1;
117
118
0
  if (tcgetattr(c->fd, &tty->tio) != 0)
119
0
    return (-1);
120
0
  return (0);
121
0
}
122
123
void
124
tty_resize(struct tty *tty)
125
0
{
126
0
  struct client *c = tty->client;
127
0
  struct winsize   ws;
128
0
  u_int    sx, sy, xpixel, ypixel;
129
130
0
  if (ioctl(c->fd, TIOCGWINSZ, &ws) != -1) {
131
0
    sx = ws.ws_col;
132
0
    if (sx == 0) {
133
0
      sx = 80;
134
0
      xpixel = 0;
135
0
    } else
136
0
      xpixel = ws.ws_xpixel / sx;
137
0
    sy = ws.ws_row;
138
0
    if (sy == 0) {
139
0
      sy = 24;
140
0
      ypixel = 0;
141
0
    } else
142
0
      ypixel = ws.ws_ypixel / sy;
143
144
0
    if ((xpixel == 0 || ypixel == 0) &&
145
0
        tty->out != NULL &&
146
0
        !(tty->flags & TTY_WINSIZEQUERY) &&
147
0
        (tty->term->flags & TERM_VT100LIKE)) {
148
0
      tty_puts(tty, "\033[18t\033[14t");
149
0
      tty->flags |= TTY_WINSIZEQUERY;
150
0
    }
151
0
  } else {
152
0
    sx = 80;
153
0
    sy = 24;
154
0
    xpixel = 0;
155
0
    ypixel = 0;
156
0
  }
157
0
  log_debug("%s: %s now %ux%u (%ux%u)", __func__, c->name, sx, sy,
158
0
      xpixel, ypixel);
159
0
  tty_set_size(tty, sx, sy, xpixel, ypixel);
160
0
  tty_invalidate(tty);
161
0
}
162
163
void
164
tty_set_size(struct tty *tty, u_int sx, u_int sy, u_int xpixel, u_int ypixel)
165
0
{
166
0
  tty->sx = sx;
167
0
  tty->sy = sy;
168
0
  tty->xpixel = xpixel;
169
0
  tty->ypixel = ypixel;
170
0
}
171
172
static void
173
tty_read_callback(__unused int fd, __unused short events, void *data)
174
0
{
175
0
  struct tty  *tty = data;
176
0
  struct client *c = tty->client;
177
0
  const char  *name = c->name;
178
0
  size_t     size = EVBUFFER_LENGTH(tty->in);
179
0
  int    nread;
180
181
0
  nread = evbuffer_read(tty->in, c->fd, -1);
182
0
  if (nread == 0 || nread == -1) {
183
0
    if (nread == 0)
184
0
      log_debug("%s: read closed", name);
185
0
    else
186
0
      log_debug("%s: read error: %s", name, strerror(errno));
187
0
    event_del(&tty->event_in);
188
0
    server_client_lost(tty->client);
189
0
    return;
190
0
  }
191
0
  log_debug("%s: read %d bytes (already %zu)", name, nread, size);
192
193
0
  while (tty_keys_next(tty))
194
0
    ;
195
0
}
196
197
static void
198
tty_timer_callback(__unused int fd, __unused short events, void *data)
199
0
{
200
0
  struct tty  *tty = data;
201
0
  struct client *c = tty->client;
202
0
  struct timeval   tv = { .tv_usec = TTY_BLOCK_INTERVAL };
203
204
0
  log_debug("%s: %zu discarded", c->name, tty->discarded);
205
206
0
  c->flags |= CLIENT_ALLREDRAWFLAGS;
207
0
  c->discarded += tty->discarded;
208
209
0
  if (tty->discarded < TTY_BLOCK_STOP(tty)) {
210
0
    tty->flags &= ~TTY_BLOCK;
211
0
    tty_invalidate(tty);
212
0
    return;
213
0
  }
214
0
  tty->discarded = 0;
215
0
  evtimer_add(&tty->timer, &tv);
216
0
}
217
218
static int
219
tty_block_maybe(struct tty *tty)
220
0
{
221
0
  struct client *c = tty->client;
222
0
  size_t     size = EVBUFFER_LENGTH(tty->out);
223
0
  struct timeval   tv = { .tv_usec = TTY_BLOCK_INTERVAL };
224
225
0
  if (size == 0)
226
0
    tty->flags &= ~TTY_NOBLOCK;
227
0
  else if (tty->flags & TTY_NOBLOCK)
228
0
    return (0);
229
230
0
  if (size < TTY_BLOCK_START(tty))
231
0
    return (0);
232
233
0
  if (tty->flags & TTY_BLOCK)
234
0
    return (1);
235
0
  tty->flags |= TTY_BLOCK;
236
237
0
  log_debug("%s: can't keep up, %zu discarded", c->name, size);
238
239
0
  evbuffer_drain(tty->out, size);
240
0
  c->discarded += size;
241
242
0
  tty->discarded = 0;
243
0
  evtimer_add(&tty->timer, &tv);
244
0
  return (1);
245
0
}
246
247
static void
248
tty_write_callback(__unused int fd, __unused short events, void *data)
249
0
{
250
0
  struct tty  *tty = data;
251
0
  struct client *c = tty->client;
252
0
  size_t     size = EVBUFFER_LENGTH(tty->out);
253
0
  int    nwrite;
254
255
0
  nwrite = evbuffer_write(tty->out, c->fd);
256
0
  if (nwrite == -1)
257
0
    return;
258
0
  log_debug("%s: wrote %d bytes (of %zu)", c->name, nwrite, size);
259
260
0
  if (c->redraw > 0) {
261
0
    if ((size_t)nwrite >= c->redraw)
262
0
      c->redraw = 0;
263
0
    else
264
0
      c->redraw -= nwrite;
265
0
    log_debug("%s: waiting for redraw, %zu bytes left", c->name,
266
0
        c->redraw);
267
0
  } else if (tty_block_maybe(tty))
268
0
    return;
269
270
0
  if (EVBUFFER_LENGTH(tty->out) != 0)
271
0
    event_add(&tty->event_out, NULL);
272
0
}
273
274
int
275
tty_open(struct tty *tty, char **cause)
276
0
{
277
0
  struct client *c = tty->client;
278
279
0
  tty->term = tty_term_create(tty, c->term_name, c->term_caps,
280
0
      c->term_ncaps, cause);
281
0
  if (tty->term == NULL) {
282
0
    tty_close(tty);
283
0
    return (-1);
284
0
  }
285
0
  tty->flags |= TTY_OPENED;
286
287
0
  tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_BLOCK|TTY_TIMER);
288
289
0
  event_set(&tty->event_in, c->fd, EV_PERSIST|EV_READ,
290
0
      tty_read_callback, tty);
291
0
  tty->in = evbuffer_new();
292
0
  if (tty->in == NULL)
293
0
    fatal("out of memory");
294
295
0
  event_set(&tty->event_out, c->fd, EV_WRITE, tty_write_callback, tty);
296
0
  tty->out = evbuffer_new();
297
0
  if (tty->out == NULL)
298
0
    fatal("out of memory");
299
300
0
  evtimer_set(&tty->clipboard_timer, tty_clipboard_query_callback, tty);
301
0
  evtimer_set(&tty->start_timer, tty_start_timer_callback, tty);
302
0
  evtimer_set(&tty->timer, tty_timer_callback, tty);
303
304
0
  tty_start_tty(tty);
305
0
  tty_keys_build(tty);
306
307
0
  return (0);
308
0
}
309
310
static void
311
tty_start_timer_callback(__unused int fd, __unused short events, void *data)
312
0
{
313
0
  struct tty  *tty = data;
314
0
  struct client *c = tty->client;
315
316
0
  log_debug("%s: start timer fired", c->name);
317
318
0
  if ((tty->flags & (TTY_HAVEDA|TTY_HAVEDA2|TTY_HAVEXDA)) == 0)
319
0
    tty_update_features(tty);
320
0
  tty->flags |= TTY_ALL_REQUEST_FLAGS;
321
322
0
  tty->flags &= ~(TTY_WAITBG|TTY_WAITFG);
323
0
}
324
325
static void
326
tty_start_start_timer(struct tty *tty)
327
0
{
328
0
  struct client *c = tty->client;
329
0
  struct timeval   tv = { .tv_sec = TTY_QUERY_TIMEOUT };
330
331
0
  log_debug("%s: start timer started", c->name);
332
0
  evtimer_del(&tty->start_timer);
333
0
  evtimer_add(&tty->start_timer, &tv);
334
0
}
335
336
void
337
tty_start_tty(struct tty *tty)
338
0
{
339
0
  struct client *c = tty->client;
340
0
  struct termios   tio;
341
0
  u_int    i;
342
343
0
  setblocking(c->fd, 0);
344
0
  event_add(&tty->event_in, NULL);
345
346
0
  memcpy(&tio, &tty->tio, sizeof tio);
347
0
  tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP);
348
0
  tio.c_iflag |= IGNBRK;
349
0
  tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
350
0
  tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|ECHOPRT|
351
0
      ECHOKE|ISIG);
352
0
  tio.c_cc[VMIN] = 1;
353
0
  tio.c_cc[VTIME] = 0;
354
0
  if (tcsetattr(c->fd, TCSANOW, &tio) == 0)
355
0
    tcflush(c->fd, TCOFLUSH);
356
357
0
  if (options_get_number(global_options, "clear-on-attach")) {
358
0
    tty_putcode(tty, TTYC_SMCUP);
359
0
    tty_putcode(tty, TTYC_CLEAR);
360
0
  } else {
361
0
    tty_putcode_ii(tty, TTYC_CSR, 0, tty->sy - 1);
362
0
    tty_putcode_ii(tty, TTYC_CUP, 0, tty->sy - 1);
363
0
    if (tty_term_has(tty->term, TTYC_INDN))
364
0
      tty_putcode_i(tty, TTYC_INDN, tty->sy + 1);
365
0
    else if (tty_term_has(tty->term, TTYC_IND)) {
366
0
      for (i = 0; i < tty->sy + 1; i++)
367
0
        tty_putcode(tty, TTYC_IND);
368
0
    } else
369
0
      tty_putcode(tty, TTYC_CLEAR);
370
0
  }
371
0
  tty_putcode(tty, TTYC_SMKX);
372
373
0
  if (tty_acs_needed(tty)) {
374
0
    log_debug("%s: using capabilities for ACS", c->name);
375
0
    tty_putcode(tty, TTYC_ENACS);
376
0
  } else
377
0
    log_debug("%s: using UTF-8 for ACS", c->name);
378
379
0
  tty_putcode(tty, TTYC_CNORM);
380
0
  if (tty_term_has(tty->term, TTYC_KMOUS)) {
381
0
    tty_puts(tty, "\033[?1000l\033[?1002l\033[?1003l");
382
0
    tty_puts(tty, "\033[?1006l\033[?1005l");
383
0
  }
384
0
  if (tty_term_has(tty->term, TTYC_ENBP))
385
0
    tty_putcode(tty, TTYC_ENBP);
386
387
0
  if (tty->term->flags & TERM_VT100LIKE) {
388
    /* Subscribe to theme changes and request theme now. */
389
0
    tty_puts(tty, "\033[?2031h\033[?996n");
390
0
  }
391
392
0
  tty_start_start_timer(tty);
393
394
0
  tty->flags |= TTY_STARTED;
395
0
  tty_invalidate(tty);
396
397
0
  if (tty->ccolour != -1)
398
0
    tty_force_cursor_colour(tty, -1);
399
400
0
  tty->mouse_drag_flag = 0;
401
0
  tty->mouse_drag_update = NULL;
402
0
  tty->mouse_drag_release = NULL;
403
0
}
404
405
void
406
tty_send_requests(struct tty *tty)
407
0
{
408
0
  if (~tty->flags & TTY_STARTED)
409
0
    return;
410
411
0
  if (tty->term->flags & TERM_VT100LIKE) {
412
0
    if (~tty->flags & TTY_HAVEDA)
413
0
      tty_puts(tty, "\033[c");
414
0
    if (~tty->flags & TTY_HAVEDA2)
415
0
      tty_puts(tty, "\033[>c");
416
0
    if (~tty->flags & TTY_HAVEXDA)
417
0
      tty_puts(tty, "\033[>q");
418
0
    if (~tty->flags & TTY_HAVESYNC)
419
0
      tty_puts(tty, "\033[?2026$p");
420
0
    tty_puts(tty, "\033]10;?\033\\\033]11;?\033\\");
421
0
    tty->flags |= (TTY_WAITBG|TTY_WAITFG);
422
0
  } else
423
0
    tty->flags |= TTY_ALL_REQUEST_FLAGS;
424
0
  tty->last_requests = time(NULL);
425
0
}
426
427
void
428
tty_repeat_requests(struct tty *tty, int force)
429
0
{
430
0
  struct client *c = tty->client;
431
0
  time_t     t = time(NULL);
432
0
  u_int    n = t - tty->last_requests;
433
434
0
  if (~tty->flags & TTY_STARTED)
435
0
    return;
436
437
0
  if (!force && n <= TTY_REQUEST_LIMIT) {
438
0
    log_debug("%s: not repeating requests (%u seconds)", c->name,
439
0
        n);
440
0
    return;
441
0
  }
442
0
  log_debug("%s: %srepeating requests (%u seconds)", c->name,
443
0
      force ? "(force) " : "" , n);
444
0
  tty->last_requests = t;
445
446
0
  if (tty->term->flags & TERM_VT100LIKE) {
447
0
    tty_puts(tty, "\033]10;?\033\\\033]11;?\033\\");
448
0
    tty->flags |= (TTY_WAITBG|TTY_WAITFG);
449
0
  }
450
0
  tty_start_start_timer(tty);
451
0
}
452
453
void
454
tty_stop_tty(struct tty *tty)
455
0
{
456
0
  struct client *c = tty->client;
457
0
  struct winsize   ws;
458
459
0
  if (!(tty->flags & TTY_STARTED))
460
0
    return;
461
0
  tty->flags &= ~TTY_STARTED;
462
463
0
  evtimer_del(&tty->start_timer);
464
0
  evtimer_del(&tty->clipboard_timer);
465
466
0
  event_del(&tty->timer);
467
0
  tty->flags &= ~TTY_BLOCK;
468
469
0
  event_del(&tty->event_in);
470
0
  event_del(&tty->event_out);
471
472
  /*
473
   * Be flexible about error handling and try not kill the server just
474
   * because the fd is invalid. Things like ssh -t can easily leave us
475
   * with a dead tty.
476
   */
477
0
  if (ioctl(c->fd, TIOCGWINSZ, &ws) == -1)
478
0
    return;
479
0
  if (tcsetattr(c->fd, TCSANOW, &tty->tio) == -1)
480
0
    return;
481
482
0
  tty_raw(tty, tty_term_string_ii(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
483
0
  if (tty_acs_needed(tty))
484
0
    tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
485
0
  tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
486
0
  tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
487
0
  if (options_get_number(global_options, "clear-on-attach"))
488
0
    tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
489
0
  if (tty->cstyle != SCREEN_CURSOR_DEFAULT) {
490
0
    if (tty_term_has(tty->term, TTYC_SE))
491
0
      tty_raw(tty, tty_term_string(tty->term, TTYC_SE));
492
0
    else if (tty_term_has(tty->term, TTYC_SS))
493
0
      tty_raw(tty, tty_term_string_i(tty->term, TTYC_SS, 0));
494
0
  }
495
0
  if (tty->ccolour != -1)
496
0
    tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
497
498
0
  tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
499
0
  if (tty_term_has(tty->term, TTYC_KMOUS)) {
500
0
    tty_raw(tty, "\033[?1000l\033[?1002l\033[?1003l");
501
0
    tty_raw(tty, "\033[?1006l\033[?1005l");
502
0
  }
503
0
  if (tty_term_has(tty->term, TTYC_DSBP))
504
0
    tty_raw(tty, tty_term_string(tty->term, TTYC_DSBP));
505
506
0
  if (tty->term->flags & TERM_VT100LIKE)
507
0
    tty_raw(tty, "\033[?7727l");
508
0
  tty_raw(tty, tty_term_string(tty->term, TTYC_DSFCS));
509
0
  tty_raw(tty, tty_term_string(tty->term, TTYC_DSEKS));
510
511
0
  if (tty_use_margin(tty))
512
0
    tty_raw(tty, tty_term_string(tty->term, TTYC_DSMG));
513
0
  if (options_get_number(global_options, "clear-on-attach"))
514
0
    tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
515
0
  else
516
0
    tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
517
518
0
  if (tty->term->flags & TERM_VT100LIKE)
519
0
    tty_raw(tty, "\033[?2031l");
520
521
0
  setblocking(c->fd, 1);
522
0
}
523
524
void
525
tty_close(struct tty *tty)
526
0
{
527
0
  if (event_initialized(&tty->key_timer))
528
0
    evtimer_del(&tty->key_timer);
529
0
  tty_stop_tty(tty);
530
531
0
  if (tty->flags & TTY_OPENED) {
532
0
    evbuffer_free(tty->in);
533
0
    event_del(&tty->event_in);
534
0
    evbuffer_free(tty->out);
535
0
    event_del(&tty->event_out);
536
537
0
    tty_term_free(tty->term);
538
0
    tty_keys_free(tty);
539
540
0
    tty->flags &= ~TTY_OPENED;
541
0
  }
542
0
}
543
544
void
545
tty_free(struct tty *tty)
546
0
{
547
0
  tty_close(tty);
548
549
0
  free(tty->r.ranges);
550
0
}
551
552
void
553
tty_update_features(struct tty *tty)
554
0
{
555
0
  struct client *c = tty->client;
556
557
0
  if (tty_apply_features(tty->term))
558
0
    tty_term_apply_overrides(tty->term);
559
560
0
  if (tty_use_margin(tty))
561
0
    tty_putcode(tty, TTYC_ENMG);
562
0
  if (options_get_number(global_options, "extended-keys"))
563
0
    tty_puts(tty, tty_term_string(tty->term, TTYC_ENEKS));
564
0
  if (options_get_number(global_options, "focus-events"))
565
0
    tty_puts(tty, tty_term_string(tty->term, TTYC_ENFCS));
566
0
  if (tty->term->flags & TERM_VT100LIKE)
567
0
    tty_puts(tty, "\033[?7727h");
568
569
  /*
570
   * Features might have changed since the first draw during attach. For
571
   * example, this happens when DA responses are received.
572
   */
573
0
  server_redraw_client(c);
574
575
0
  tty_invalidate(tty);
576
0
}
577
578
void
579
tty_raw(struct tty *tty, const char *s)
580
0
{
581
0
  struct client *c = tty->client;
582
0
  ssize_t    n, slen;
583
0
  u_int    i;
584
585
0
  slen = strlen(s);
586
0
  for (i = 0; i < 5; i++) {
587
0
    n = write(c->fd, s, slen);
588
0
    if (n >= 0) {
589
0
      s += n;
590
0
      slen -= n;
591
0
      if (slen == 0)
592
0
        break;
593
0
    } else if (n == -1 && errno != EAGAIN)
594
0
      break;
595
0
    usleep(100);
596
0
  }
597
0
}
598
599
void
600
tty_putcode(struct tty *tty, enum tty_code_code code)
601
0
{
602
0
  tty_puts(tty, tty_term_string(tty->term, code));
603
0
}
604
605
void
606
tty_putcode_i(struct tty *tty, enum tty_code_code code, int a)
607
0
{
608
0
  if (a < 0)
609
0
    return;
610
0
  tty_puts(tty, tty_term_string_i(tty->term, code, a));
611
0
}
612
613
void
614
tty_putcode_ii(struct tty *tty, enum tty_code_code code, int a, int b)
615
0
{
616
0
  if (a < 0 || b < 0)
617
0
    return;
618
0
  tty_puts(tty, tty_term_string_ii(tty->term, code, a, b));
619
0
}
620
621
void
622
tty_putcode_iii(struct tty *tty, enum tty_code_code code, int a, int b, int c)
623
0
{
624
0
  if (a < 0 || b < 0 || c < 0)
625
0
    return;
626
0
  tty_puts(tty, tty_term_string_iii(tty->term, code, a, b, c));
627
0
}
628
629
void
630
tty_putcode_s(struct tty *tty, enum tty_code_code code, const char *a)
631
0
{
632
0
  if (a != NULL)
633
0
    tty_puts(tty, tty_term_string_s(tty->term, code, a));
634
0
}
635
636
void
637
tty_putcode_ss(struct tty *tty, enum tty_code_code code, const char *a,
638
    const char *b)
639
0
{
640
0
  if (a != NULL && b != NULL)
641
0
    tty_puts(tty, tty_term_string_ss(tty->term, code, a, b));
642
0
}
643
644
static void
645
tty_add(struct tty *tty, const char *buf, size_t len)
646
0
{
647
0
  struct client *c = tty->client;
648
649
0
  if (tty->flags & TTY_BLOCK) {
650
0
    tty->discarded += len;
651
0
    return;
652
0
  }
653
654
0
  evbuffer_add(tty->out, buf, len);
655
0
  log_debug("%s: %.*s", c->name, (int)len, buf);
656
0
  c->written += len;
657
658
0
  if (tty_log_fd != -1)
659
0
    write(tty_log_fd, buf, len);
660
0
  if ((tty->flags & TTY_STARTED) &&
661
0
      !event_pending(&tty->event_out, EV_WRITE, NULL))
662
0
    event_add(&tty->event_out, NULL);
663
0
}
664
665
void
666
tty_puts(struct tty *tty, const char *s)
667
0
{
668
0
  if (*s != '\0')
669
0
    tty_add(tty, s, strlen(s));
670
0
}
671
672
void
673
tty_putc(struct tty *tty, u_char ch)
674
0
{
675
0
  const char  *acs;
676
677
0
  if ((tty->term->flags & TERM_NOAM) &&
678
0
      ch >= 0x20 && ch != 0x7f &&
679
0
      tty->cy == tty->sy - 1 &&
680
0
      tty->cx + 1 >= tty->sx)
681
0
    return;
682
683
0
  if (tty->cell.attr & GRID_ATTR_CHARSET) {
684
0
    acs = tty_acs_get(tty, ch);
685
0
    if (acs != NULL)
686
0
      tty_add(tty, acs, strlen(acs));
687
0
    else
688
0
      tty_add(tty, &ch, 1);
689
0
  } else
690
0
    tty_add(tty, &ch, 1);
691
692
0
  if (ch >= 0x20 && ch != 0x7f) {
693
0
    if (tty->cx >= tty->sx) {
694
0
      tty->cx = 1;
695
0
      if (tty->cy != tty->rlower)
696
0
        tty->cy++;
697
698
      /*
699
       * On !am terminals, force the cursor position to where
700
       * we think it should be after a line wrap - this means
701
       * it works on sensible terminals as well.
702
       */
703
0
      if (tty->term->flags & TERM_NOAM)
704
0
        tty_putcode_ii(tty, TTYC_CUP, tty->cy, tty->cx);
705
0
    } else
706
0
      tty->cx++;
707
0
  }
708
0
}
709
710
void
711
tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
712
0
{
713
0
  if ((tty->term->flags & TERM_NOAM) &&
714
0
      tty->cy == tty->sy - 1 &&
715
0
      tty->cx + len >= tty->sx)
716
0
    len = tty->sx - tty->cx - 1;
717
718
0
  tty_add(tty, buf, len);
719
0
  if (tty->cx + width > tty->sx) {
720
0
    tty->cx = (tty->cx + width) - tty->sx;
721
0
    if (tty->cx <= tty->sx)
722
0
      tty->cy++;
723
0
    else
724
0
      tty->cx = tty->cy = UINT_MAX;
725
0
  } else
726
0
    tty->cx += width;
727
0
}
728
729
static void
730
tty_set_italics(struct tty *tty)
731
0
{
732
0
  const char  *s;
733
734
0
  if (tty_term_has(tty->term, TTYC_SITM)) {
735
0
    s = options_get_string(global_options, "default-terminal");
736
0
    if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) {
737
0
      tty_putcode(tty, TTYC_SITM);
738
0
      return;
739
0
    }
740
0
  }
741
0
  tty_putcode(tty, TTYC_SMSO);
742
0
}
743
744
void
745
tty_set_title(struct tty *tty, const char *title)
746
0
{
747
0
  if (!tty_term_has(tty->term, TTYC_TSL) ||
748
0
      !tty_term_has(tty->term, TTYC_FSL))
749
0
    return;
750
751
0
  tty_putcode(tty, TTYC_TSL);
752
0
  tty_puts(tty, title);
753
0
  tty_putcode(tty, TTYC_FSL);
754
0
}
755
756
void
757
tty_set_path(struct tty *tty, const char *title)
758
0
{
759
0
  if (!tty_term_has(tty->term, TTYC_SWD) ||
760
0
      !tty_term_has(tty->term, TTYC_FSL))
761
0
    return;
762
763
0
  tty_putcode(tty, TTYC_SWD);
764
0
  tty_puts(tty, title);
765
0
  tty_putcode(tty, TTYC_FSL);
766
0
}
767
768
static void
769
tty_force_cursor_colour(struct tty *tty, int c)
770
0
{
771
0
  u_char  r, g, b;
772
0
  char  s[13];
773
774
0
  if (c != -1) {
775
0
    c = tty_map_theme_colour(tty, c);
776
0
    c = colour_force_rgb(c);
777
0
  }
778
0
  if (c == tty->ccolour)
779
0
    return;
780
0
  if (c == -1)
781
0
    tty_putcode(tty, TTYC_CR);
782
0
  else {
783
0
    colour_split_rgb(c, &r, &g, &b);
784
0
    xsnprintf(s, sizeof s, "rgb:%02hhx/%02hhx/%02hhx", r, g, b);
785
0
    tty_putcode_s(tty, TTYC_CS, s);
786
0
  }
787
0
  tty->ccolour = c;
788
0
}
789
790
static int
791
tty_update_cursor(struct tty *tty, int mode, struct screen *s)
792
0
{
793
0
  enum screen_cursor_style  cstyle;
794
0
  int       ccolour, changed, cmode = mode;
795
796
  /* Set cursor colour if changed. */
797
0
  if (s != NULL) {
798
0
    ccolour = s->ccolour;
799
0
    if (s->ccolour == -1)
800
0
      ccolour = s->default_ccolour;
801
0
    tty_force_cursor_colour(tty, ccolour);
802
0
  }
803
804
  /* If cursor is off, set as invisible. */
805
0
  if (~cmode & MODE_CURSOR) {
806
0
    if (tty->mode & MODE_CURSOR)
807
0
      tty_putcode(tty, TTYC_CIVIS);
808
0
    return (cmode);
809
0
  }
810
811
  /* Check if blinking or very visible flag changed or style changed. */
812
0
  if (s == NULL)
813
0
    cstyle = tty->cstyle;
814
0
  else {
815
0
    cstyle = s->cstyle;
816
0
    if (cstyle == SCREEN_CURSOR_DEFAULT) {
817
0
      if (~cmode & MODE_CURSOR_BLINKING_SET) {
818
0
        if (s->default_mode & MODE_CURSOR_BLINKING)
819
0
          cmode |= MODE_CURSOR_BLINKING;
820
0
        else
821
0
          cmode &= ~MODE_CURSOR_BLINKING;
822
0
      }
823
0
      cstyle = s->default_cstyle;
824
0
    }
825
0
  }
826
827
  /* If nothing changed, do nothing. */
828
0
  changed = cmode ^ tty->mode;
829
0
  if ((changed & CURSOR_MODES) == 0 && cstyle == tty->cstyle)
830
0
    return (cmode);
831
832
  /*
833
   * Set cursor style. If an explicit style has been set with DECSCUSR,
834
   * set it if supported, otherwise send cvvis for blinking styles.
835
   *
836
   * If no style, has been set (SCREEN_CURSOR_DEFAULT), then send cvvis
837
   * if either the blinking or very visible flags are set.
838
   */
839
0
  tty_putcode(tty, TTYC_CNORM);
840
0
  switch (cstyle) {
841
0
  case SCREEN_CURSOR_DEFAULT:
842
0
    if (tty->cstyle != SCREEN_CURSOR_DEFAULT) {
843
0
      if (tty_term_has(tty->term, TTYC_SE))
844
0
        tty_putcode(tty, TTYC_SE);
845
0
      else
846
0
        tty_putcode_i(tty, TTYC_SS, 0);
847
0
    }
848
0
    if (cmode & (MODE_CURSOR_BLINKING|MODE_CURSOR_VERY_VISIBLE))
849
0
      tty_putcode(tty, TTYC_CVVIS);
850
0
    break;
851
0
  case SCREEN_CURSOR_BLOCK:
852
0
    if (tty_term_has(tty->term, TTYC_SS)) {
853
0
      if (cmode & MODE_CURSOR_BLINKING)
854
0
        tty_putcode_i(tty, TTYC_SS, 1);
855
0
      else
856
0
        tty_putcode_i(tty, TTYC_SS, 2);
857
0
    } else if (cmode & MODE_CURSOR_BLINKING)
858
0
      tty_putcode(tty, TTYC_CVVIS);
859
0
    break;
860
0
  case SCREEN_CURSOR_UNDERLINE:
861
0
    if (tty_term_has(tty->term, TTYC_SS)) {
862
0
      if (cmode & MODE_CURSOR_BLINKING)
863
0
        tty_putcode_i(tty, TTYC_SS, 3);
864
0
      else
865
0
        tty_putcode_i(tty, TTYC_SS, 4);
866
0
    } else if (cmode & MODE_CURSOR_BLINKING)
867
0
      tty_putcode(tty, TTYC_CVVIS);
868
0
    break;
869
0
  case SCREEN_CURSOR_BAR:
870
0
    if (tty_term_has(tty->term, TTYC_SS)) {
871
0
      if (cmode & MODE_CURSOR_BLINKING)
872
0
        tty_putcode_i(tty, TTYC_SS, 5);
873
0
      else
874
0
        tty_putcode_i(tty, TTYC_SS, 6);
875
0
    } else if (cmode & MODE_CURSOR_BLINKING)
876
0
      tty_putcode(tty, TTYC_CVVIS);
877
0
    break;
878
0
  }
879
0
  tty->cstyle = cstyle;
880
0
  return (cmode);
881
0
 }
882
883
void
884
tty_update_mode(struct tty *tty, int mode, struct screen *s)
885
0
{
886
0
  struct tty_term *term = tty->term;
887
0
  struct client *c = tty->client;
888
0
  int    changed;
889
890
0
  if (tty->flags & TTY_NOCURSOR)
891
0
    mode &= ~MODE_CURSOR;
892
893
0
  if (tty_update_cursor(tty, mode, s) & MODE_CURSOR_BLINKING)
894
0
    mode |= MODE_CURSOR_BLINKING;
895
0
  else
896
0
    mode &= ~MODE_CURSOR_BLINKING;
897
898
0
  changed = mode ^ tty->mode;
899
0
  if (log_get_level() != 0 && changed != 0) {
900
0
    log_debug("%s: current mode %s", c->name,
901
0
        screen_mode_to_string(tty->mode));
902
0
    log_debug("%s: setting mode %s", c->name,
903
0
        screen_mode_to_string(mode));
904
0
  }
905
906
0
  if ((changed & ALL_MOUSE_MODES) && tty_term_has(term, TTYC_KMOUS)) {
907
    /*
908
     * If the mouse modes have changed, clear then all and apply
909
     * again. There are differences in how terminals track the
910
     * various bits.
911
     */
912
0
    tty_puts(tty, "\033[?1006l\033[?1000l\033[?1002l\033[?1003l");
913
0
    if (mode & ALL_MOUSE_MODES)
914
0
      tty_puts(tty, "\033[?1006h");
915
0
    if (mode & MODE_MOUSE_ALL)
916
0
      tty_puts(tty, "\033[?1000h\033[?1002h\033[?1003h");
917
0
    else if (mode & MODE_MOUSE_BUTTON)
918
0
      tty_puts(tty, "\033[?1000h\033[?1002h");
919
0
    else if (mode & MODE_MOUSE_STANDARD)
920
0
      tty_puts(tty, "\033[?1000h");
921
0
  }
922
0
  tty->mode = mode;
923
0
}
924
925
static void
926
tty_emulate_repeat(struct tty *tty, enum tty_code_code code,
927
    enum tty_code_code code1, u_int n)
928
0
{
929
0
  if (tty_term_has(tty->term, code))
930
0
    tty_putcode_i(tty, code, n);
931
0
  else {
932
0
    while (n-- > 0)
933
0
      tty_putcode(tty, code1);
934
0
  }
935
0
}
936
937
void
938
tty_repeat_space(struct tty *tty, u_int n)
939
0
{
940
0
  static char s[500];
941
942
0
  if (*s != ' ')
943
0
    memset(s, ' ', sizeof s);
944
945
0
  while (n > sizeof s) {
946
0
    tty_putn(tty, s, sizeof s, sizeof s);
947
0
    n -= sizeof s;
948
0
  }
949
0
  if (n != 0)
950
0
    tty_putn(tty, s, n, n);
951
0
}
952
953
/* Is this window bigger than the terminal? */
954
int
955
tty_window_bigger(struct tty *tty)
956
0
{
957
0
  struct client *c = tty->client;
958
0
  struct window *w = c->session->curw->window;
959
960
0
  return (tty->sx < w->sx || tty->sy - status_line_size(c) < w->sy);
961
0
}
962
963
/* What offset should this window be drawn at? */
964
int
965
tty_window_offset(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
966
0
{
967
0
  *ox = tty->oox;
968
0
  *oy = tty->ooy;
969
0
  *sx = tty->osx;
970
0
  *sy = tty->osy;
971
972
0
  return (tty->oflag);
973
0
}
974
975
/* What offset should this window be drawn at? */
976
static int
977
tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy)
978
0
{
979
0
  struct client   *c = tty->client;
980
0
  struct window   *w = c->session->curw->window;
981
0
  struct window_pane  *wp = w->active;
982
0
  u_int      cx, cy, lines;
983
984
0
  lines = status_line_size(c);
985
986
0
  if (tty->sx >= w->sx && tty->sy - lines >= w->sy) {
987
0
    *ox = 0;
988
0
    *oy = 0;
989
0
    *sx = w->sx;
990
0
    *sy = w->sy;
991
992
0
    c->pan_window = NULL;
993
0
    return (0);
994
0
  }
995
996
0
  *sx = tty->sx;
997
0
  *sy = tty->sy - lines;
998
999
0
  if (c->pan_window == w) {
1000
0
    if (*sx >= w->sx)
1001
0
      c->pan_ox = 0;
1002
0
    else if (c->pan_ox + *sx > w->sx)
1003
0
      c->pan_ox = w->sx - *sx;
1004
0
    *ox = c->pan_ox;
1005
0
    if (*sy >= w->sy)
1006
0
      c->pan_oy = 0;
1007
0
    else if (c->pan_oy + *sy > w->sy)
1008
0
      c->pan_oy = w->sy - *sy;
1009
0
    *oy = c->pan_oy;
1010
0
    return (1);
1011
0
  }
1012
1013
0
  if (~wp->screen->mode & MODE_CURSOR) {
1014
0
    *ox = 0;
1015
0
    *oy = 0;
1016
0
  } else {
1017
0
    cx = wp->xoff + wp->screen->cx;
1018
0
    cy = wp->yoff + wp->screen->cy;
1019
1020
0
    if (cx < *sx)
1021
0
      *ox = 0;
1022
0
    else if (cx > w->sx - *sx)
1023
0
      *ox = w->sx - *sx;
1024
0
    else
1025
0
      *ox = cx - *sx / 2;
1026
1027
0
    if (cy < *sy)
1028
0
      *oy = 0;
1029
0
    else if (cy > w->sy - *sy)
1030
0
      *oy = w->sy - *sy;
1031
0
    else
1032
0
      *oy = cy - *sy + 1;
1033
0
  }
1034
1035
0
  c->pan_window = NULL;
1036
0
  return (1);
1037
0
}
1038
1039
/* Update stored offsets for a window and redraw if necessary. */
1040
void
1041
tty_update_window_offset(struct window *w)
1042
0
{
1043
0
  struct client *c;
1044
1045
0
  TAILQ_FOREACH(c, &clients, entry) {
1046
0
    if (c->session != NULL &&
1047
0
        c->session->curw != NULL &&
1048
0
        c->session->curw->window == w)
1049
0
      tty_update_client_offset(c);
1050
0
  }
1051
0
}
1052
1053
/* Update stored offsets for a client and redraw if necessary. */
1054
void
1055
tty_update_client_offset(struct client *c)
1056
0
{
1057
0
  u_int ox, oy, sx, sy;
1058
1059
0
  if (~c->flags & CLIENT_TERMINAL)
1060
0
    return;
1061
1062
0
  c->tty.oflag = tty_window_offset1(&c->tty, &ox, &oy, &sx, &sy);
1063
0
  if (ox == c->tty.oox &&
1064
0
      oy == c->tty.ooy &&
1065
0
      sx == c->tty.osx &&
1066
0
      sy == c->tty.osy)
1067
0
    return;
1068
1069
0
  log_debug ("%s: %s offset has changed (%u,%u %ux%u -> %u,%u %ux%u)",
1070
0
      __func__, c->name, c->tty.oox, c->tty.ooy, c->tty.osx, c->tty.osy,
1071
0
      ox, oy, sx, sy);
1072
1073
0
  c->tty.oox = ox;
1074
0
  c->tty.ooy = oy;
1075
0
  c->tty.osx = sx;
1076
0
  c->tty.osy = sy;
1077
1078
0
  c->flags |= (CLIENT_REDRAWWINDOW|CLIENT_REDRAWSTATUS);
1079
0
}
1080
1081
/*
1082
 * Is the region large enough to be worth redrawing once later rather than
1083
 * probably several times now? Currently yes if it is more than 50% of the
1084
 * pane.
1085
 */
1086
static int
1087
tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx)
1088
0
{
1089
0
  return (ctx->orlower - ctx->orupper >= ctx->sy / 2);
1090
0
}
1091
1092
/*
1093
 * Return if BCE is needed but the terminal doesn't have it - it'll need to be
1094
 * emulated.
1095
 */
1096
int
1097
tty_fake_bce(const struct tty *tty, const struct grid_cell *gc, u_int bg)
1098
0
{
1099
0
  if (tty_term_flag(tty->term, TTYC_BCE))
1100
0
    return (0);
1101
0
  if (!COLOUR_DEFAULT(bg) || !COLOUR_DEFAULT(gc->bg))
1102
0
    return (1);
1103
0
  return (0);
1104
0
}
1105
1106
/*
1107
 * Redraw scroll region using data from screen (already updated). Used when
1108
 * CSR not supported, or window is a pane that doesn't take up the full
1109
 * width of the terminal.
1110
 */
1111
static void
1112
tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
1113
0
{
1114
0
  struct client   *c = tty->client;
1115
0
  u_int      i;
1116
1117
  /*
1118
   * If region is large, schedule a redraw. In most cases this is likely
1119
   * to be followed by some more scrolling.
1120
   */
1121
0
  if (tty_large_region(tty, ctx) || ctx->flags & TTY_CTX_PANE_OBSCURED) {
1122
0
    log_debug("%s: %s large region redraw", __func__, c->name);
1123
0
    ctx->redraw_cb(ctx);
1124
0
    return;
1125
0
  }
1126
1127
0
  log_debug("%s: %s small region redraw (%u-%u)", __func__, c->name,
1128
0
      ctx->orupper, ctx->orlower);
1129
0
  for (i = ctx->orupper; i <= ctx->orlower; i++)
1130
0
    tty_draw_pane(tty, ctx, i);
1131
0
}
1132
1133
/* Is this position visible in the pane? */
1134
static int
1135
tty_is_visible(__unused struct tty *tty, const struct tty_ctx *ctx, u_int px,
1136
    u_int py, u_int nx, u_int ny)
1137
0
{
1138
0
  u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py;
1139
1140
0
  if (~ctx->flags & TTY_CTX_WINDOW_BIGGER)
1141
0
    return (1);
1142
1143
0
  if (xoff + nx <= ctx->wox || xoff >= ctx->wox + ctx->wsx ||
1144
0
      yoff + ny <= ctx->woy || yoff >= ctx->woy + ctx->wsy)
1145
0
    return (0);
1146
0
  return (1);
1147
0
}
1148
1149
/* Clamp line position to visible part of pane. */
1150
static int
1151
tty_clamp_line(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
1152
    u_int nx, u_int *i, u_int *x, u_int *rx, u_int *ry)
1153
0
{
1154
0
  int xoff = ctx->rxoff + px;
1155
1156
  /*
1157
   * px = x position in pane
1158
   * py = y position in pane
1159
   * nx = width
1160
   *
1161
   * i = new x position in pane
1162
   * x = x position on terminal
1163
   * rx = new width
1164
   * ry = y position on terminal
1165
   */
1166
1167
0
  if (!tty_is_visible(tty, ctx, px, py, nx, 1))
1168
0
    return (0);
1169
0
  *ry = ctx->yoff + py - ctx->woy;
1170
1171
0
  if (xoff >= (int)ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) {
1172
    /* All visible. */
1173
0
    *i = 0;
1174
0
    *x = ctx->xoff + px - ctx->wox;
1175
0
    *rx = nx;
1176
0
  } else if (xoff < (int)ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
1177
    /* Both left and right not visible. */
1178
0
    *i = ctx->wox;
1179
0
    *x = 0;
1180
0
    *rx = ctx->wsx;
1181
0
  } else if (xoff < (int)ctx->wox) {
1182
    /* Left not visible. */
1183
0
    *i = ctx->wox - (ctx->xoff + px);
1184
0
    *x = 0;
1185
0
    *rx = nx - *i;
1186
0
  } else {
1187
    /* Right not visible. */
1188
0
    *i = 0;
1189
0
    *x = (ctx->xoff + px) - ctx->wox;
1190
0
    *rx = ctx->wsx - *x;
1191
0
  }
1192
0
  if (*rx > nx)
1193
0
    fatalx("%s: x too big, %u > %u", __func__, *rx, nx);
1194
1195
0
  return (1);
1196
0
}
1197
1198
/* Clear a line. */
1199
static void
1200
tty_clear_line(struct tty *tty, const struct grid_cell *defaults, u_int py,
1201
    u_int px, u_int nx, u_int bg)
1202
0
{
1203
0
  struct client   *c = tty->client;
1204
0
  struct visible_ranges *r;
1205
0
  struct visible_range  *rr;
1206
0
  u_int      i;
1207
1208
0
  log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
1209
1210
  /* Nothing to clear. */
1211
0
  if (nx == 0)
1212
0
    return;
1213
1214
  /* If genuine BCE is available, can try escape sequences. */
1215
0
  if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
1216
    /* Off the end of the line, use EL if available. */
1217
0
    if (px + nx >= tty->sx && tty_term_has(tty->term, TTYC_EL)) {
1218
0
      tty_cursor(tty, px, py);
1219
0
      tty_putcode(tty, TTYC_EL);
1220
0
      return;
1221
0
    }
1222
1223
    /* At the start of the line. Use EL1. */
1224
0
    if (px == 0 && tty_term_has(tty->term, TTYC_EL1)) {
1225
0
      tty_cursor(tty, px + nx - 1, py);
1226
0
      tty_putcode(tty, TTYC_EL1);
1227
0
      return;
1228
0
    }
1229
1230
    /* Section of line. Use ECH if possible. */
1231
0
    if (tty_term_has(tty->term, TTYC_ECH)) {
1232
0
      tty_cursor(tty, px, py);
1233
0
      tty_putcode_i(tty, TTYC_ECH, nx);
1234
0
      return;
1235
0
    }
1236
0
  }
1237
1238
  /*
1239
   * Couldn't use an escape sequence, use spaces. Clear only the visible
1240
   * bit if there is an overlay.
1241
   */
1242
0
  r = tty_check_overlay_range(tty, px, py, nx);
1243
0
  for (i = 0; i < r->used; i++) {
1244
0
    rr = &r->ranges[i];
1245
0
    if (rr->nx != 0) {
1246
0
      tty_cursor(tty, rr->px, py);
1247
0
      tty_repeat_space(tty, rr->nx);
1248
0
    }
1249
0
  }
1250
0
}
1251
1252
/* Clear a line, adjusting to visible part of pane. */
1253
static void
1254
tty_clear_pane_line(struct tty *tty, const struct tty_ctx *ctx, u_int py,
1255
    u_int px, u_int nx, u_int bg)
1256
0
{
1257
0
  struct client   *c = tty->client;
1258
0
  struct visible_ranges *r;
1259
0
  struct visible_range  *ri;
1260
0
  u_int      i, l, x, rx, ry;
1261
1262
0
  log_debug("%s: %s, %u at %u,%u", __func__, c->name, nx, px, py);
1263
1264
0
  if (tty_clamp_line(tty, ctx, px, py, nx, &l, &x, &rx, &ry)) {
1265
0
    r = tty_check_overlay_range(tty, x, ry, rx);
1266
0
    for (i = 0; i < r->used; i++) {
1267
0
      ri = &r->ranges[i];
1268
0
      if (ri->nx == 0)
1269
0
        continue;
1270
0
      tty_clear_line(tty, &ctx->defaults, ry, ri->px, ri->nx,
1271
0
          bg);
1272
0
    }
1273
0
  }
1274
0
}
1275
1276
/* Clamp area position to visible part of pane. */
1277
static int
1278
tty_clamp_area(struct tty *tty, const struct tty_ctx *ctx, u_int px, u_int py,
1279
    u_int nx, u_int ny, u_int *i, u_int *j, u_int *x, u_int *y, u_int *rx,
1280
    u_int *ry)
1281
0
{
1282
0
  u_int xoff = ctx->rxoff + px, yoff = ctx->ryoff + py;
1283
1284
0
  if (!tty_is_visible(tty, ctx, px, py, nx, ny))
1285
0
    return (0);
1286
1287
0
  if (xoff >= ctx->wox && xoff + nx <= ctx->wox + ctx->wsx) {
1288
    /* All visible. */
1289
0
    *i = 0;
1290
0
    *x = ctx->xoff + px - ctx->wox;
1291
0
    *rx = nx;
1292
0
  } else if (xoff < ctx->wox && xoff + nx > ctx->wox + ctx->wsx) {
1293
    /* Both left and right not visible. */
1294
0
    *i = ctx->wox;
1295
0
    *x = 0;
1296
0
    *rx = ctx->wsx;
1297
0
  } else if (xoff < ctx->wox) {
1298
    /* Left not visible. */
1299
0
    *i = ctx->wox - (ctx->xoff + px);
1300
0
    *x = 0;
1301
0
    *rx = nx - *i;
1302
0
  } else {
1303
    /* Right not visible. */
1304
0
    *i = 0;
1305
0
    *x = (ctx->xoff + px) - ctx->wox;
1306
0
    *rx = ctx->wsx - *x;
1307
0
  }
1308
0
  if (*rx > nx)
1309
0
    fatalx("%s: x too big, %u > %u", __func__, *rx, nx);
1310
1311
0
  if (yoff >= ctx->woy && yoff + ny <= ctx->woy + ctx->wsy) {
1312
    /* All visible. */
1313
0
    *j = 0;
1314
0
    *y = ctx->yoff + py - ctx->woy;
1315
0
    *ry = ny;
1316
0
  } else if (yoff < ctx->woy && yoff + ny > ctx->woy + ctx->wsy) {
1317
    /* Both top and bottom not visible. */
1318
0
    *j = ctx->woy;
1319
0
    *y = 0;
1320
0
    *ry = ctx->wsy;
1321
0
  } else if (yoff < ctx->woy) {
1322
    /* Top not visible. */
1323
0
    *j = ctx->woy - (ctx->yoff + py);
1324
0
    *y = 0;
1325
0
    *ry = ny - *j;
1326
0
  } else {
1327
    /* Bottom not visible. */
1328
0
    *j = 0;
1329
0
    *y = (ctx->yoff + py) - ctx->woy;
1330
0
    *ry = ctx->wsy - *y;
1331
0
  }
1332
0
  if (*ry > ny)
1333
0
    fatalx("%s: y too big, %u > %u", __func__, *ry, ny);
1334
1335
0
  return (1);
1336
0
}
1337
1338
/* Clear an area, adjusting to visible part of pane. */
1339
static void
1340
tty_clear_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
1341
    u_int ny, u_int px, u_int nx, u_int bg)
1342
0
{
1343
0
  struct client   *c = tty->client;
1344
0
  const struct grid_cell  *defaults = &ctx->defaults;
1345
0
  u_int      yy;
1346
0
  char       tmp[64];
1347
1348
0
  log_debug("%s: %s, %u,%u at %u,%u", __func__, c->name, nx, ny, px, py);
1349
1350
  /* Nothing to clear. */
1351
0
  if (nx == 0 || ny == 0)
1352
0
    return;
1353
1354
  /*
1355
   * If there is an overlay or BCE is not available, cannot clear as a
1356
   * region.
1357
   */
1358
0
  if (c->overlay_check == NULL && !tty_fake_bce(tty, defaults, bg)) {
1359
    /* Use ED if clearing off the bottom of the terminal. */
1360
0
    if (px == 0 &&
1361
0
        px + nx >= tty->sx &&
1362
0
        py + ny >= tty->sy &&
1363
0
        tty_term_has(tty->term, TTYC_ED)) {
1364
0
      tty_cursor(tty, 0, py);
1365
0
      tty_putcode(tty, TTYC_ED);
1366
0
      return;
1367
0
    }
1368
1369
    /*
1370
     * On VT420 compatible terminals we can use DECFRA if the
1371
     * background colour isn't default (because it doesn't work
1372
     * after SGR 0).
1373
     */
1374
0
    if ((tty->term->flags & TERM_DECFRA) && !COLOUR_DEFAULT(bg)) {
1375
0
      xsnprintf(tmp, sizeof tmp, "\033[32;%u;%u;%u;%u$x",
1376
0
          py + 1, px + 1, py + ny, px + nx);
1377
0
      tty_puts(tty, tmp);
1378
0
      return;
1379
0
    }
1380
1381
    /* Full lines can be scrolled away to clear them. */
1382
0
    if (px == 0 &&
1383
0
        px + nx >= tty->sx &&
1384
0
        ny > 2 &&
1385
0
        tty_term_has(tty->term, TTYC_CSR) &&
1386
0
        tty_term_has(tty->term, TTYC_INDN)) {
1387
0
      tty_region(tty, py, py + ny - 1);
1388
0
      tty_margin_off(tty);
1389
0
      tty_putcode_i(tty, TTYC_INDN, ny);
1390
0
      return;
1391
0
    }
1392
1393
    /*
1394
     * If margins are supported, can just scroll the area off to
1395
     * clear it.
1396
     */
1397
0
    if (nx > 2 &&
1398
0
        ny > 2 &&
1399
0
        tty_term_has(tty->term, TTYC_CSR) &&
1400
0
        tty_use_margin(tty) &&
1401
0
        tty_term_has(tty->term, TTYC_INDN)) {
1402
0
      tty_region(tty, py, py + ny - 1);
1403
0
      tty_margin(tty, px, px + nx - 1);
1404
0
      tty_putcode_i(tty, TTYC_INDN, ny);
1405
0
      return;
1406
0
    }
1407
0
  }
1408
1409
  /* Couldn't use an escape sequence, loop over the lines. */
1410
0
  for (yy = py; yy < py + ny; yy++)
1411
0
    tty_clear_line(tty, defaults, yy, px, nx, bg);
1412
0
}
1413
1414
/* Clear an area in a pane. */
1415
static void
1416
tty_clear_pane_area(struct tty *tty, const struct tty_ctx *ctx, u_int py,
1417
    u_int ny, u_int px, u_int nx, u_int bg)
1418
0
{
1419
0
  u_int i, j, x, y, rx, ry;
1420
1421
0
  if (tty_clamp_area(tty, ctx, px, py, nx, ny, &i, &j, &x, &y, &rx, &ry))
1422
0
    tty_clear_area(tty, ctx, y, ry, x, rx, bg);
1423
0
}
1424
1425
/* Redraw a line of a screen at py. */
1426
static void
1427
tty_draw_pane(struct tty *tty, const struct tty_ctx *ctx, u_int py)
1428
0
{
1429
0
  struct screen   *s = ctx->s;
1430
0
  u_int      nx = ctx->sx, i, x, rx, ry, j;
1431
0
  struct visible_ranges *r;
1432
0
  struct visible_range  *rr;
1433
1434
0
  log_debug("%s: %s %u", __func__, tty->client->name, py);
1435
1436
0
  if (~ctx->flags & TTY_CTX_WINDOW_BIGGER) {
1437
0
    r = tty_check_overlay_range(tty, ctx->xoff, ctx->yoff + py, nx);
1438
0
    for (j = 0; j < r->used; j++) {
1439
0
      rr = &r->ranges[j];
1440
0
      if (rr->nx == 0)
1441
0
        continue;
1442
0
      tty_draw_line(tty, s, rr->px - ctx->xoff, py, rr->nx,
1443
0
          rr->px, ctx->yoff + py, &ctx->style_ctx);
1444
0
    }
1445
0
    return;
1446
0
  }
1447
0
  if (tty_clamp_line(tty, ctx, 0, py, nx, &i, &x, &rx, &ry)) {
1448
0
    r = tty_check_overlay_range(tty, x, ry, rx);
1449
0
    for (j = 0; j < r->used; j++) {
1450
0
      rr = &r->ranges[j];
1451
0
      if (rr->nx == 0)
1452
0
        continue;
1453
0
      tty_draw_line(tty, s, i + rr->px - x, py, rr->nx,
1454
0
          rr->px, ry, &ctx->style_ctx);
1455
0
    }
1456
0
  }
1457
0
}
1458
1459
void
1460
tty_cmd_redrawline(struct tty *tty, const struct tty_ctx *ctx)
1461
0
{
1462
0
  u_int      i, x, rx, ry, j;
1463
0
  struct visible_ranges *r;
1464
0
  struct visible_range  *rr;
1465
1466
0
  if (tty_clamp_line(tty, ctx, ctx->ocx, ctx->ocy, ctx->n,
1467
0
      &i, &x, &rx, &ry)) {
1468
0
    r = tty_check_overlay_range(tty, x, ry, rx);
1469
0
    for (j = 0; j < r->used; j++) {
1470
0
      rr = &r->ranges[j];
1471
0
      if (rr->nx == 0)
1472
0
        continue;
1473
0
      tty_draw_line(tty, ctx->s, ctx->ocx + i + rr->px - x,
1474
0
          ctx->ocy, rr->nx, rr->px, ry, &ctx->style_ctx);
1475
0
    }
1476
0
  }
1477
0
}
1478
1479
/* Check if character needs to be mapped for codeset. */
1480
const struct grid_cell *
1481
tty_check_codeset(struct tty *tty, const struct grid_cell *gc)
1482
0
{
1483
0
  static struct grid_cell new;
1484
0
  int     c;
1485
1486
  /* Characters less than 0x7f are always fine, no matter what. */
1487
0
  if (gc->data.size == 1 && *gc->data.data < 0x7f)
1488
0
    return (gc);
1489
0
  if (gc->flags & GRID_FLAG_TAB)
1490
0
    return (gc);
1491
1492
  /* UTF-8 terminal and a UTF-8 character - fine. */
1493
0
  if (tty->client->flags & CLIENT_UTF8)
1494
0
    return (gc);
1495
0
  memcpy(&new, gc, sizeof new);
1496
1497
  /* See if this can be mapped to an ACS character. */
1498
0
  c = tty_acs_reverse_get(tty, gc->data.data, gc->data.size);
1499
0
  if (c != -1) {
1500
0
    utf8_set(&new.data, c);
1501
0
    new.attr |= GRID_ATTR_CHARSET;
1502
0
    return (&new);
1503
0
  }
1504
1505
  /* Replace by the right number of underscores. */
1506
0
  new.data.size = gc->data.width;
1507
0
  if (new.data.size > UTF8_SIZE)
1508
0
    new.data.size = UTF8_SIZE;
1509
0
  memset(new.data.data, '_', new.data.size);
1510
0
  return (&new);
1511
0
}
1512
1513
/* Check if a single character is covered by the overlay. */
1514
static int
1515
tty_check_overlay(struct tty *tty, u_int px, u_int py)
1516
0
{
1517
0
  struct visible_ranges *r;
1518
1519
  /*
1520
   * With a single character, if there is anything visible (that is, the
1521
   * range is not empty), it must be that character.
1522
   */
1523
0
  r = tty_check_overlay_range(tty, px, py, 1);
1524
0
  return (!server_client_ranges_is_empty(r));
1525
0
}
1526
1527
/* Return parts of the input range which are visible. */
1528
struct visible_ranges *
1529
tty_check_overlay_range(struct tty *tty, u_int px, u_int py, u_int nx)
1530
0
{
1531
0
  struct client *c = tty->client;
1532
1533
0
  if (c->overlay_check == NULL) {
1534
0
    server_client_ensure_ranges(&tty->r, 1);
1535
0
    tty->r.ranges[0].px = px;
1536
0
    tty->r.ranges[0].nx = nx;
1537
0
    tty->r.used = 1;
1538
0
    return (&tty->r);
1539
0
  }
1540
0
  return (c->overlay_check(c, c->overlay_data, px, py, nx));
1541
0
}
1542
1543
#ifdef ENABLE_SIXEL
1544
/* Update context for client. */
1545
static int
1546
tty_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
1547
{
1548
  struct window_pane  *wp = ttyctx->arg;
1549
1550
  if (c->session->curw->window != wp->window)
1551
    return (0);
1552
  if (wp->layout_cell == NULL)
1553
    return (0);
1554
1555
  if (tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, &ttyctx->wsx,
1556
      &ttyctx->wsy))
1557
    ttyctx->flags |= TTY_CTX_WINDOW_BIGGER;
1558
  else
1559
    ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER;
1560
1561
  ttyctx->yoff = ttyctx->ryoff = wp->yoff;
1562
  if (status_at_line(c) == 0)
1563
    ttyctx->yoff += status_line_size(c);
1564
1565
  return (1);
1566
}
1567
1568
void
1569
tty_draw_images(struct client *c, struct window_pane *wp)
1570
{
1571
  struct image  *im;
1572
  struct tty_ctx   ttyctx;
1573
1574
  TAILQ_FOREACH(im, &wp->screen->images, entry) {
1575
    memset(&ttyctx, 0, sizeof ttyctx);
1576
1577
    /* Set the client independent properties. */
1578
    ttyctx.ocx = im->px;
1579
    ttyctx.ocy = im->py;
1580
1581
    ttyctx.orlower = wp->screen->rlower;
1582
    ttyctx.orupper = wp->screen->rupper;
1583
1584
    ttyctx.xoff = ttyctx.rxoff = wp->xoff;
1585
    ttyctx.sx = wp->sx;
1586
    ttyctx.sy = wp->sy;
1587
1588
    ttyctx.image = im;
1589
    ttyctx.arg = wp;
1590
    ttyctx.set_client_cb = tty_set_client_cb;
1591
    ttyctx.flags |= TTY_CTX_INVISIBLE_PANES;
1592
    tty_write_one(tty_cmd_sixelimage, c, &ttyctx);
1593
  }
1594
}
1595
#endif
1596
1597
void
1598
tty_sync_start(struct tty *tty)
1599
0
{
1600
0
  if (tty->flags & TTY_BLOCK)
1601
0
    return;
1602
0
  if (tty->flags & TTY_SYNCING)
1603
0
    return;
1604
0
  tty->flags |= TTY_SYNCING;
1605
1606
0
  if (tty_term_has(tty->term, TTYC_SYNC)) {
1607
0
    log_debug("%s sync start", tty->client->name);
1608
0
    tty_putcode_i(tty, TTYC_SYNC, 1);
1609
0
  }
1610
0
}
1611
1612
void
1613
tty_sync_end(struct tty *tty)
1614
0
{
1615
0
  if (tty->flags & TTY_BLOCK)
1616
0
    return;
1617
0
  if (~tty->flags & TTY_SYNCING)
1618
0
    return;
1619
0
  tty->flags &= ~TTY_SYNCING;
1620
1621
0
  if (tty_term_has(tty->term, TTYC_SYNC)) {
1622
0
    log_debug("%s sync end", tty->client->name);
1623
0
    tty_putcode_i(tty, TTYC_SYNC, 2);
1624
0
  }
1625
0
}
1626
1627
static int
1628
tty_client_ready(const struct tty_ctx *ctx, struct client *c)
1629
0
{
1630
0
  if (c->session == NULL || c->tty.term == NULL)
1631
0
    return (0);
1632
0
  if (c->flags & CLIENT_SUSPENDED)
1633
0
    return (0);
1634
1635
  /*
1636
   * If invisible panes are allowed (used for passthrough), don't care if
1637
   * redrawing or frozen.
1638
   */
1639
0
  if (ctx->flags & TTY_CTX_INVISIBLE_PANES)
1640
0
    return (1);
1641
1642
0
  if (c->flags & CLIENT_REDRAWWINDOW)
1643
0
    return (0);
1644
0
  if (c->tty.flags & TTY_FREEZE)
1645
0
    return (0);
1646
0
  return (1);
1647
0
}
1648
1649
void
1650
tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
1651
    struct tty_ctx *ctx)
1652
87.5k
{
1653
87.5k
  struct client *c;
1654
87.5k
  int    state;
1655
1656
87.5k
  if (ctx->set_client_cb == NULL)
1657
0
    return;
1658
87.5k
  TAILQ_FOREACH(c, &clients, entry) {
1659
0
    if (tty_client_ready(ctx, c)) {
1660
0
      state = ctx->set_client_cb(ctx, c);
1661
0
      if (state == -1)
1662
0
        break;
1663
0
      if (state == 0)
1664
0
        continue;
1665
0
      cmdfn(&c->tty, ctx);
1666
0
    }
1667
0
  }
1668
87.5k
}
1669
1670
#ifdef ENABLE_SIXEL
1671
/* Only write to the incoming tty instead of every client. */
1672
static void
1673
tty_write_one(void (*cmdfn)(struct tty *, const struct tty_ctx *),
1674
    struct client *c, struct tty_ctx *ctx)
1675
{
1676
  if (ctx->set_client_cb == NULL)
1677
    return;
1678
  if ((ctx->set_client_cb(ctx, c)) == 1)
1679
    cmdfn(&c->tty, ctx);
1680
}
1681
#endif
1682
1683
void
1684
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
1685
0
{
1686
0
  struct client *c = tty->client;
1687
1688
0
  if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
1689
0
      !tty_full_width(tty, ctx) ||
1690
0
      tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1691
0
      (!tty_term_has(tty->term, TTYC_ICH) &&
1692
0
      !tty_term_has(tty->term, TTYC_ICH1)) ||
1693
0
      c->overlay_check != NULL) {
1694
0
    tty_draw_pane(tty, ctx, ctx->ocy);
1695
0
    return;
1696
0
  }
1697
1698
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1699
1700
0
  tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1701
1702
0
  tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->n);
1703
0
}
1704
1705
void
1706
tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
1707
0
{
1708
0
  struct client *c = tty->client;
1709
1710
0
  if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
1711
0
      !tty_full_width(tty, ctx) ||
1712
0
      tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1713
0
      (!tty_term_has(tty->term, TTYC_DCH) &&
1714
0
      !tty_term_has(tty->term, TTYC_DCH1)) ||
1715
0
      c->overlay_check != NULL) {
1716
0
    tty_draw_pane(tty, ctx, ctx->ocy);
1717
0
    return;
1718
0
  }
1719
1720
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1721
1722
0
  tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1723
1724
0
  tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->n);
1725
0
}
1726
1727
void
1728
tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
1729
0
{
1730
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1731
1732
0
  tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, ctx->n, ctx->bg);
1733
0
}
1734
1735
void
1736
tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
1737
0
{
1738
0
  struct client *c = tty->client;
1739
1740
0
  if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
1741
0
      !tty_full_width(tty, ctx) ||
1742
0
      tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1743
0
      !tty_term_has(tty->term, TTYC_CSR) ||
1744
0
      !tty_term_has(tty->term, TTYC_IL1) ||
1745
0
      ctx->sx == 1 ||
1746
0
      ctx->sy == 1 ||
1747
0
      c->overlay_check != NULL) {
1748
0
    tty_redraw_region(tty, ctx);
1749
0
    return;
1750
0
  }
1751
1752
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1753
1754
0
  tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1755
0
  tty_margin_off(tty);
1756
0
  tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1757
1758
0
  tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->n);
1759
0
  tty->cx = tty->cy = UINT_MAX;
1760
0
}
1761
1762
void
1763
tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
1764
0
{
1765
0
  struct client *c = tty->client;
1766
1767
0
  if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
1768
0
      !tty_full_width(tty, ctx) ||
1769
0
      tty_fake_bce(tty, &ctx->defaults, ctx->bg) ||
1770
0
      !tty_term_has(tty->term, TTYC_CSR) ||
1771
0
      !tty_term_has(tty->term, TTYC_DL1) ||
1772
0
      ctx->sx == 1 ||
1773
0
      ctx->sy == 1 ||
1774
0
      c->overlay_check != NULL) {
1775
0
    tty_redraw_region(tty, ctx);
1776
0
    return;
1777
0
  }
1778
1779
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1780
1781
0
  tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1782
0
  tty_margin_off(tty);
1783
0
  tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1784
1785
0
  tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->n);
1786
0
  tty->cx = tty->cy = UINT_MAX;
1787
0
}
1788
1789
void
1790
tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
1791
0
{
1792
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1793
1794
0
  tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->sx, ctx->bg);
1795
0
}
1796
1797
void
1798
tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
1799
0
{
1800
0
  u_int nx = ctx->sx - ctx->ocx;
1801
1802
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1803
1804
0
  tty_clear_pane_line(tty, ctx, ctx->ocy, ctx->ocx, nx, ctx->bg);
1805
0
}
1806
1807
void
1808
tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
1809
0
{
1810
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1811
1812
0
  tty_clear_pane_line(tty, ctx, ctx->ocy, 0, ctx->ocx + 1, ctx->bg);
1813
0
}
1814
1815
void
1816
tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
1817
0
{
1818
0
  struct client *c = tty->client;
1819
1820
0
  if (ctx->ocy != ctx->orupper)
1821
0
    return;
1822
1823
0
  if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
1824
0
      (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1825
0
      tty_fake_bce(tty, &ctx->defaults, 8) ||
1826
0
      !tty_term_has(tty->term, TTYC_CSR) ||
1827
0
      (!tty_term_has(tty->term, TTYC_RI) &&
1828
0
      !tty_term_has(tty->term, TTYC_RIN)) ||
1829
0
      ctx->sx == 1 ||
1830
0
      ctx->sy == 1 ||
1831
0
      c->overlay_check != NULL) {
1832
0
    tty_redraw_region(tty, ctx);
1833
0
    return;
1834
0
  }
1835
1836
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1837
1838
0
  tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1839
0
  tty_margin_pane(tty, ctx);
1840
0
  tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
1841
1842
0
  if (tty_term_has(tty->term, TTYC_RI))
1843
0
    tty_putcode(tty, TTYC_RI);
1844
0
  else
1845
0
    tty_putcode_i(tty, TTYC_RIN, 1);
1846
0
}
1847
1848
void
1849
tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
1850
0
{
1851
0
  struct client *c = tty->client;
1852
1853
0
  if (ctx->ocy != ctx->orlower)
1854
0
    return;
1855
1856
0
  if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
1857
0
      (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1858
0
      tty_fake_bce(tty, &ctx->defaults, 8) ||
1859
0
      !tty_term_has(tty->term, TTYC_CSR) ||
1860
0
      ctx->sx == 1 ||
1861
0
      ctx->sy == 1 ||
1862
0
      c->overlay_check != NULL) {
1863
0
    tty_redraw_region(tty, ctx);
1864
0
    return;
1865
0
  }
1866
1867
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1868
1869
0
  tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1870
0
  tty_margin_pane(tty, ctx);
1871
1872
  /*
1873
   * If we want to wrap a pane while using margins, the cursor needs to
1874
   * be exactly on the right of the region. If the cursor is entirely off
1875
   * the edge - move it back to the right. Some terminals are funny about
1876
   * this and insert extra spaces, so only use the right if margins are
1877
   * enabled.
1878
   */
1879
0
  if (ctx->xoff + ctx->ocx > tty->rright) {
1880
0
    if (!tty_use_margin(tty))
1881
0
      tty_cursor(tty, 0, ctx->yoff + ctx->ocy);
1882
0
    else
1883
0
      tty_cursor(tty, tty->rright, ctx->yoff + ctx->ocy);
1884
0
  } else
1885
0
    tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1886
1887
0
  tty_putc(tty, '\n');
1888
0
}
1889
1890
void
1891
tty_cmd_scrollup(struct tty *tty, const struct tty_ctx *ctx)
1892
0
{
1893
0
  struct client   *c = tty->client;
1894
0
  u_int      i;
1895
1896
0
  if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
1897
0
      (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1898
0
      tty_fake_bce(tty, &ctx->defaults, 8) ||
1899
0
      !tty_term_has(tty->term, TTYC_CSR) ||
1900
0
      ctx->sx == 1 ||
1901
0
      ctx->sy == 1 ||
1902
0
      c->overlay_check != NULL) {
1903
0
    tty_redraw_region(tty, ctx);
1904
0
    return;
1905
0
  }
1906
1907
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1908
1909
0
  tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1910
0
  tty_margin_pane(tty, ctx);
1911
1912
0
  if (ctx->n == 1 || !tty_term_has(tty->term, TTYC_INDN)) {
1913
0
    if (!tty_use_margin(tty))
1914
0
      tty_cursor(tty, 0, tty->rlower);
1915
0
    else
1916
0
      tty_cursor(tty, tty->rright, tty->rlower);
1917
0
    for (i = 0; i < ctx->n; i++)
1918
0
      tty_putc(tty, '\n');
1919
0
  } else {
1920
0
    if (tty->cy == UINT_MAX)
1921
0
      tty_cursor(tty, 0, 0);
1922
0
    else
1923
0
      tty_cursor(tty, 0, tty->cy);
1924
0
    tty_putcode_i(tty, TTYC_INDN, ctx->n);
1925
0
  }
1926
0
}
1927
1928
void
1929
tty_cmd_scrolldown(struct tty *tty, const struct tty_ctx *ctx)
1930
0
{
1931
0
  u_int    i;
1932
0
  struct client *c = tty->client;
1933
1934
0
  if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
1935
0
      (!tty_full_width(tty, ctx) && !tty_use_margin(tty)) ||
1936
0
      tty_fake_bce(tty, &ctx->defaults, 8) ||
1937
0
      !tty_term_has(tty->term, TTYC_CSR) ||
1938
0
      (!tty_term_has(tty->term, TTYC_RI) &&
1939
0
      !tty_term_has(tty->term, TTYC_RIN)) ||
1940
0
      ctx->sx == 1 ||
1941
0
      ctx->sy == 1 ||
1942
0
      c->overlay_check != NULL) {
1943
0
    tty_redraw_region(tty, ctx);
1944
0
    return;
1945
0
  }
1946
1947
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1948
1949
0
  tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1950
0
  tty_margin_pane(tty, ctx);
1951
0
  tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
1952
1953
0
  if (tty_term_has(tty->term, TTYC_RIN))
1954
0
    tty_putcode_i(tty, TTYC_RIN, ctx->n);
1955
0
  else {
1956
0
    for (i = 0; i < ctx->n; i++)
1957
0
      tty_putcode(tty, TTYC_RI);
1958
0
  }
1959
0
}
1960
1961
void
1962
tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
1963
0
{
1964
0
  u_int px, py, nx, ny;
1965
1966
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1967
1968
0
  tty_region_pane(tty, ctx, 0, ctx->sy - 1);
1969
0
  tty_margin_off(tty);
1970
1971
0
  px = 0;
1972
0
  nx = ctx->sx;
1973
0
  py = ctx->ocy + 1;
1974
0
  ny = ctx->sy - ctx->ocy - 1;
1975
1976
0
  tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg);
1977
1978
0
  px = ctx->ocx;
1979
0
  nx = ctx->sx - ctx->ocx;
1980
0
  py = ctx->ocy;
1981
1982
0
  tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg);
1983
0
}
1984
1985
void
1986
tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
1987
0
{
1988
0
  u_int px, py, nx, ny;
1989
1990
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
1991
1992
0
  tty_region_pane(tty, ctx, 0, ctx->sy - 1);
1993
0
  tty_margin_off(tty);
1994
1995
0
  px = 0;
1996
0
  nx = ctx->sx;
1997
0
  py = 0;
1998
0
  ny = ctx->ocy;
1999
2000
0
  tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg);
2001
2002
0
  px = 0;
2003
0
  nx = ctx->ocx + 1;
2004
0
  py = ctx->ocy;
2005
2006
0
  tty_clear_pane_line(tty, ctx, py, px, nx, ctx->bg);
2007
0
}
2008
2009
void
2010
tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
2011
0
{
2012
0
  u_int px, py, nx, ny;
2013
2014
0
  tty_default_attributes(tty, ctx->bg, &ctx->style_ctx);
2015
2016
0
  tty_region_pane(tty, ctx, 0, ctx->sy - 1);
2017
0
  tty_margin_off(tty);
2018
2019
0
  px = 0;
2020
0
  nx = ctx->sx;
2021
0
  py = 0;
2022
0
  ny = ctx->sy;
2023
2024
0
  tty_clear_pane_area(tty, ctx, py, ny, px, nx, ctx->bg);
2025
0
}
2026
2027
void
2028
tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
2029
0
{
2030
0
  struct client *c = tty->client;
2031
0
  u_int    i, j;
2032
2033
0
  if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) ||
2034
0
      c->overlay_check != NULL) {
2035
0
    ctx->redraw_cb(ctx);
2036
0
    return;
2037
0
  }
2038
2039
0
  tty_attributes(tty, &grid_default_cell, &ctx->style_ctx);
2040
2041
0
  tty_region_pane(tty, ctx, 0, ctx->sy - 1);
2042
0
  tty_margin_off(tty);
2043
2044
0
  for (j = 0; j < ctx->sy; j++) {
2045
0
    tty_cursor_pane(tty, ctx, 0, j);
2046
0
    for (i = 0; i < ctx->sx; i++)
2047
0
      tty_putc(tty, 'E');
2048
0
  }
2049
0
}
2050
2051
void
2052
tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
2053
0
{
2054
0
  const struct grid_cell  *gcp = ctx->cell;
2055
0
  struct screen   *s = ctx->s;
2056
0
  struct visible_ranges *r;
2057
0
  u_int      px, py, i, vis = 0;
2058
2059
0
  px = ctx->xoff + ctx->ocx - ctx->wox;
2060
0
  py = ctx->yoff + ctx->ocy - ctx->woy;
2061
0
  if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, 1, 1))
2062
0
    return;
2063
2064
0
  if (gcp->data.width == 1 && !tty_check_overlay(tty, px, py))
2065
0
    return;
2066
0
  if (gcp->data.width > 1) { /* could be partially obscured */
2067
0
    r = tty_check_overlay_range(tty, px, py, gcp->data.width);
2068
0
    for (i = 0; i < r->used; i++)
2069
0
      vis += r->ranges[i].nx;
2070
0
    if (vis < gcp->data.width) {
2071
0
      tty_draw_line(tty, s, s->cx, s->cy, gcp->data.width,
2072
0
          px, py, &ctx->style_ctx);
2073
0
      return;
2074
0
    }
2075
0
  }
2076
2077
0
  if (ctx->xoff + ctx->ocx - ctx->wox > tty->sx - 1 &&
2078
0
      ctx->ocy == ctx->orlower &&
2079
0
      tty_full_width(tty, ctx))
2080
0
    tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
2081
2082
0
  tty_margin_off(tty);
2083
0
  if (ctx->flags & TTY_CTX_CELL_INVALIDATE)
2084
0
    tty_invalidate(tty);
2085
0
  tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
2086
2087
0
  tty_cell(tty, ctx->cell, &ctx->style_ctx);
2088
2089
0
  if (ctx->flags & TTY_CTX_CELL_INVALIDATE)
2090
0
    tty_invalidate(tty);
2091
0
}
2092
2093
void
2094
tty_cmd_cells(struct tty *tty, const struct tty_ctx *ctx)
2095
0
{
2096
0
  struct visible_ranges *r;
2097
0
  struct visible_range  *ri;
2098
0
  u_int      i, px, py, cx;
2099
0
  const char    *cp = ctx->data.data;
2100
0
  size_t       n = ctx->data.size;
2101
2102
0
  if (!tty_is_visible(tty, ctx, ctx->ocx, ctx->ocy, n, 1))
2103
0
    return;
2104
2105
0
  if ((ctx->flags & TTY_CTX_WINDOW_BIGGER) &&
2106
0
      (ctx->xoff + ctx->ocx < ctx->wox ||
2107
0
      ctx->xoff + ctx->ocx + n > ctx->wox + ctx->wsx)) {
2108
0
    if ((~ctx->flags & TTY_CTX_WRAPPED) ||
2109
0
        !tty_full_width(tty, ctx) ||
2110
0
        (tty->term->flags & TERM_NOAM) ||
2111
0
        ctx->xoff + ctx->ocx != 0 ||
2112
0
        ctx->yoff + ctx->ocy != tty->cy + 1 ||
2113
0
        tty->cx < tty->sx ||
2114
0
        tty->cy == tty->rlower)
2115
0
      tty_draw_pane(tty, ctx, ctx->ocy);
2116
0
    else
2117
0
      ctx->redraw_cb(ctx);
2118
0
    return;
2119
0
  }
2120
2121
0
  tty_margin_off(tty);
2122
0
  tty_cursor_pane_unless_wrap(tty, ctx, ctx->ocx, ctx->ocy);
2123
0
  tty_attributes(tty, ctx->cell, &ctx->style_ctx);
2124
2125
  /* Get tty position from pane position for overlay check. */
2126
0
  px = ctx->xoff + ctx->ocx - ctx->wox;
2127
0
  py = ctx->yoff + ctx->ocy - ctx->woy;
2128
2129
0
  r = tty_check_overlay_range(tty, px, py, n);
2130
0
  for (i = 0; i < r->used; i++) {
2131
0
    ri = &r->ranges[i];
2132
0
    if (ri->nx != 0) {
2133
0
      cx = ri->px - ctx->xoff + ctx->wox;
2134
0
      tty_cursor_pane_unless_wrap(tty, ctx, cx, ctx->ocy);
2135
0
      tty_putn(tty, cp + ri->px - px, ri->nx, ri->nx);
2136
0
    }
2137
0
  }
2138
0
}
2139
2140
void
2141
tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
2142
0
{
2143
0
  tty_set_selection(tty, ctx->sel.clip, ctx->sel.data, ctx->sel.size);
2144
0
}
2145
2146
void
2147
tty_set_selection(struct tty *tty, const char *clip, const char *buf,
2148
    size_t len)
2149
0
{
2150
0
  char  *encoded;
2151
0
  size_t   size;
2152
2153
0
  if (~tty->flags & TTY_STARTED)
2154
0
    return;
2155
0
  if (!tty_term_has(tty->term, TTYC_MS))
2156
0
    return;
2157
2158
0
  size = 4 * ((len + 2) / 3) + 1; /* storage for base64 */
2159
0
  encoded = xmalloc(size);
2160
2161
0
  b64_ntop(buf, len, encoded, size);
2162
0
  tty->flags |= TTY_NOBLOCK;
2163
0
  tty_putcode_ss(tty, TTYC_MS, clip, encoded);
2164
2165
0
  free(encoded);
2166
0
}
2167
2168
void
2169
tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
2170
0
{
2171
0
  tty->flags |= TTY_NOBLOCK;
2172
0
  tty_add(tty, ctx->data.data, ctx->data.size);
2173
0
  tty_invalidate(tty);
2174
0
}
2175
2176
#ifdef ENABLE_SIXEL
2177
void
2178
tty_cmd_sixelimage(struct tty *tty, const struct tty_ctx *ctx)
2179
{
2180
  struct image    *im = ctx->image;
2181
  struct sixel_image  *si = im->data;
2182
  struct sixel_image  *new;
2183
  char      *data;
2184
  size_t       size;
2185
  u_int      cx = ctx->ocx, cy = ctx->ocy, sx, sy;
2186
  u_int      i, j, x, y, rx, ry;
2187
  int      fallback = 0;
2188
2189
  if ((~tty->term->flags & TERM_SIXEL) &&
2190
            !tty_term_has(tty->term, TTYC_SXL))
2191
    fallback = 1;
2192
  if (tty->xpixel == 0 || tty->ypixel == 0)
2193
    fallback = 1;
2194
2195
  sixel_size_in_cells(si, &sx, &sy);
2196
  log_debug("%s: image is %ux%u", __func__, sx, sy);
2197
  if (!tty_clamp_area(tty, ctx, cx, cy, sx, sy, &i, &j, &x, &y, &rx, &ry))
2198
    return;
2199
  log_debug("%s: clamping to %u,%u-%u,%u", __func__, i, j, rx, ry);
2200
2201
  if (fallback == 1) {
2202
    data = xstrdup(im->fallback);
2203
    size = strlen(data);
2204
  } else {
2205
    new = sixel_scale(si, tty->xpixel, tty->ypixel, i, j, rx, ry, 0);
2206
    if (new == NULL)
2207
      return;
2208
2209
    data = sixel_print(new, si, &size);
2210
  }
2211
  if (data != NULL) {
2212
    log_debug("%s: %zu bytes: %s", __func__, size, data);
2213
    tty_region_off(tty);
2214
    tty_margin_off(tty);
2215
    tty_cursor(tty, x, y);
2216
2217
    tty->flags |= TTY_NOBLOCK;
2218
    tty_add(tty, data, size);
2219
    tty_invalidate(tty);
2220
    free(data);
2221
  }
2222
2223
  if (fallback == 0)
2224
    sixel_free(new);
2225
}
2226
#endif
2227
2228
void
2229
tty_cmd_syncstart(struct tty *tty, const struct tty_ctx *ctx)
2230
0
{
2231
0
  struct client *c = tty->client;
2232
2233
0
  if ((ctx->flags & TTY_CTX_OVERLAY_SYNC) &&
2234
0
      (ctx->flags & TTY_CTX_SYNC)) {
2235
    /*
2236
     * This is an overlay and a command that moves the cursor so
2237
     * start synchronized updates.
2238
     */
2239
0
    tty_sync_start(tty);
2240
0
  } else if (~ctx->flags & TTY_CTX_OVERLAY_SYNC) {
2241
    /*
2242
     * This is a pane. If there is an overlay, always start;
2243
     * otherwise, only if requested.
2244
     */
2245
0
    if ((ctx->flags & TTY_CTX_SYNC) || c->overlay_draw != NULL)
2246
0
      tty_sync_start(tty);
2247
0
  }
2248
0
}
2249
2250
void
2251
tty_cell(struct tty *tty, const struct grid_cell *gc,
2252
    const struct tty_style_ctx *style_ctx)
2253
0
{
2254
0
  const struct grid_cell  *gcp;
2255
2256
  /* Skip last character if terminal is stupid. */
2257
0
  if ((tty->term->flags & TERM_NOAM) &&
2258
0
      tty->cy == tty->sy - 1 &&
2259
0
      tty->cx == tty->sx - 1)
2260
0
    return;
2261
2262
  /* If this is a padding character, do nothing. */
2263
0
  if (gc->flags & GRID_FLAG_PADDING)
2264
0
    return;
2265
2266
  /* Check if character is covered by overlay or floating pane. */
2267
0
  if (!tty_check_overlay(tty, tty->cx, tty->cy))
2268
0
    return;
2269
2270
  /* Check the output codeset and apply attributes. */
2271
0
  gcp = tty_check_codeset(tty, gc);
2272
0
  tty_attributes(tty, gcp, style_ctx);
2273
2274
  /* If it is a single character, write with putc to handle ACS. */
2275
0
  if (gcp->data.size == 1) {
2276
0
    if (*gcp->data.data < 0x20 || *gcp->data.data == 0x7f)
2277
0
      return;
2278
0
    tty_putc(tty, *gcp->data.data);
2279
0
    return;
2280
0
  }
2281
2282
  /* Write the data. */
2283
0
  tty_putn(tty, gcp->data.data, gcp->data.size, gcp->data.width);
2284
0
}
2285
2286
void
2287
tty_reset(struct tty *tty)
2288
0
{
2289
0
  struct grid_cell  *gc = &tty->cell;
2290
2291
0
  if (!grid_cells_equal(gc, &grid_default_cell)) {
2292
0
    if (gc->link != 0)
2293
0
      tty_putcode_ss(tty, TTYC_HLS, "", "");
2294
0
    if ((gc->attr & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
2295
0
      tty_putcode(tty, TTYC_RMACS);
2296
0
    tty_putcode(tty, TTYC_SGR0);
2297
0
    memcpy(gc, &grid_default_cell, sizeof *gc);
2298
0
  }
2299
0
  memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell);
2300
0
}
2301
2302
void
2303
tty_invalidate(struct tty *tty)
2304
0
{
2305
0
  memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
2306
0
  memcpy(&tty->last_cell, &grid_default_cell, sizeof tty->last_cell);
2307
2308
0
  tty->cx = tty->cy = UINT_MAX;
2309
0
  tty->rupper = tty->rleft = UINT_MAX;
2310
0
  tty->rlower = tty->rright = UINT_MAX;
2311
2312
0
  if (tty->flags & TTY_STARTED) {
2313
0
    if (tty_use_margin(tty))
2314
0
      tty_putcode(tty, TTYC_ENMG);
2315
0
    tty_putcode(tty, TTYC_SGR0);
2316
2317
0
    tty->mode = ALL_MODES;
2318
0
    tty_update_mode(tty, MODE_CURSOR, NULL);
2319
2320
0
    tty_cursor(tty, 0, 0);
2321
0
    tty_region_off(tty);
2322
0
    tty_margin_off(tty);
2323
0
  } else
2324
0
    tty->mode = MODE_CURSOR;
2325
0
}
2326
2327
/* Turn off margin. */
2328
void
2329
tty_region_off(struct tty *tty)
2330
0
{
2331
0
  tty_region(tty, 0, tty->sy - 1);
2332
0
}
2333
2334
/* Set region inside pane. */
2335
static void
2336
tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper,
2337
    u_int rlower)
2338
0
{
2339
0
  tty_region(tty, ctx->yoff + rupper - ctx->woy,
2340
0
      ctx->yoff + rlower - ctx->woy);
2341
0
}
2342
2343
/* Set region at absolute position. */
2344
static void
2345
tty_region(struct tty *tty, u_int rupper, u_int rlower)
2346
0
{
2347
0
  if (tty->rlower == rlower && tty->rupper == rupper)
2348
0
    return;
2349
0
  if (!tty_term_has(tty->term, TTYC_CSR))
2350
0
    return;
2351
2352
0
  tty->rupper = rupper;
2353
0
  tty->rlower = rlower;
2354
2355
  /*
2356
   * Some terminals (such as PuTTY) do not correctly reset the cursor to
2357
   * 0,0 if it is beyond the last column (they do not reset their wrap
2358
   * flag so further output causes a line feed). As a workaround, do an
2359
   * explicit move to 0 first.
2360
   */
2361
0
  if (tty->cx >= tty->sx) {
2362
0
    if (tty->cy == UINT_MAX)
2363
0
      tty_cursor(tty, 0, 0);
2364
0
    else
2365
0
      tty_cursor(tty, 0, tty->cy);
2366
0
  }
2367
2368
0
  tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower);
2369
0
  tty->cx = tty->cy = UINT_MAX;
2370
0
}
2371
2372
/* Turn off margin. */
2373
void
2374
tty_margin_off(struct tty *tty)
2375
0
{
2376
0
  tty_margin(tty, 0, tty->sx - 1);
2377
0
}
2378
2379
/* Set margin inside pane. */
2380
static void
2381
tty_margin_pane(struct tty *tty, const struct tty_ctx *ctx)
2382
0
{
2383
0
  int l, r;
2384
2385
0
  l = ctx->xoff - ctx->wox;
2386
0
  r = ctx->xoff + ctx->sx - 1 - ctx->wox;
2387
2388
0
  if (l < 0)
2389
0
    l = 0;
2390
0
  if (l > (int)ctx->wsx)
2391
0
    l = ctx->wsx;
2392
0
  if (r < 0)
2393
0
    r = 0;
2394
0
  if (r > (int)ctx->wsx)
2395
0
    r = ctx->wsx;
2396
2397
0
  tty_margin(tty, l, r);
2398
0
}
2399
2400
/* Set margin at absolute position. */
2401
static void
2402
tty_margin(struct tty *tty, u_int rleft, u_int rright)
2403
0
{
2404
0
  if (!tty_use_margin(tty))
2405
0
    return;
2406
0
  if (tty->rleft == rleft && tty->rright == rright)
2407
0
    return;
2408
2409
0
  tty_putcode_ii(tty, TTYC_CSR, tty->rupper, tty->rlower);
2410
2411
0
  tty->rleft = rleft;
2412
0
  tty->rright = rright;
2413
2414
0
  if (rleft == 0 && rright == tty->sx - 1)
2415
0
    tty_putcode(tty, TTYC_CLMG);
2416
0
  else
2417
0
    tty_putcode_ii(tty, TTYC_CMG, rleft, rright);
2418
0
  tty->cx = tty->cy = UINT_MAX;
2419
0
}
2420
2421
/*
2422
 * Move the cursor, unless it would wrap itself when the next character is
2423
 * printed.
2424
 */
2425
static void
2426
tty_cursor_pane_unless_wrap(struct tty *tty, const struct tty_ctx *ctx,
2427
    u_int cx, u_int cy)
2428
0
{
2429
0
  if ((~ctx->flags & TTY_CTX_WRAPPED) ||
2430
0
      !tty_full_width(tty, ctx) ||
2431
0
      (tty->term->flags & TERM_NOAM) ||
2432
0
      ctx->xoff + cx != 0 ||
2433
0
      ctx->yoff + cy != tty->cy + 1 ||
2434
0
      tty->cx < tty->sx ||
2435
0
      tty->cy == tty->rlower)
2436
0
    tty_cursor_pane(tty, ctx, cx, cy);
2437
0
  else
2438
0
    log_debug("%s: will wrap at %u,%u", __func__, tty->cx, tty->cy);
2439
0
}
2440
2441
/* Move cursor inside pane. */
2442
static void
2443
tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)
2444
0
{
2445
0
  tty_cursor(tty, ctx->xoff + cx - ctx->wox, ctx->yoff + cy - ctx->woy);
2446
0
}
2447
2448
/* Move cursor to absolute position. */
2449
void
2450
tty_cursor(struct tty *tty, u_int cx, u_int cy)
2451
0
{
2452
0
  struct tty_term *term = tty->term;
2453
0
  u_int    thisx, thisy;
2454
0
  int    change;
2455
2456
0
  if (tty->flags & TTY_BLOCK)
2457
0
    return;
2458
2459
0
  thisx = tty->cx;
2460
0
  thisy = tty->cy;
2461
2462
  /*
2463
   * If in the automargin space, and want to be there, do not move.
2464
   * Otherwise, force the cursor to be in range (and complain).
2465
   */
2466
0
  if (cx == thisx && cy == thisy && cx == tty->sx)
2467
0
    return;
2468
0
  if (cx > tty->sx - 1) {
2469
0
    log_debug("%s: x too big %u > %u", __func__, cx, tty->sx - 1);
2470
0
    cx = tty->sx - 1;
2471
0
  }
2472
2473
  /* No change. */
2474
0
  if (cx == thisx && cy == thisy)
2475
0
    return;
2476
2477
  /* Currently at the very end of the line - use absolute movement. */
2478
0
  if (thisx > tty->sx - 1)
2479
0
    goto absolute;
2480
2481
  /* Move to home position (0, 0). */
2482
0
  if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) {
2483
0
    tty_putcode(tty, TTYC_HOME);
2484
0
    goto out;
2485
0
  }
2486
2487
  /* Zero on the next line. */
2488
0
  if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower &&
2489
0
      (!tty_use_margin(tty) || tty->rleft == 0)) {
2490
0
    tty_putc(tty, '\r');
2491
0
    tty_putc(tty, '\n');
2492
0
    goto out;
2493
0
  }
2494
2495
  /* Moving column or row. */
2496
0
  if (cy == thisy) {
2497
    /*
2498
     * Moving column only, row staying the same.
2499
     */
2500
2501
    /* To left edge. */
2502
0
    if (cx == 0 && (!tty_use_margin(tty) || tty->rleft == 0)) {
2503
0
      tty_putc(tty, '\r');
2504
0
      goto out;
2505
0
    }
2506
2507
    /* One to the left. */
2508
0
    if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) {
2509
0
      tty_putcode(tty, TTYC_CUB1);
2510
0
      goto out;
2511
0
    }
2512
2513
    /* One to the right. */
2514
0
    if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) {
2515
0
      tty_putcode(tty, TTYC_CUF1);
2516
0
      goto out;
2517
0
    }
2518
2519
    /* Calculate difference. */
2520
0
    change = thisx - cx;  /* +ve left, -ve right */
2521
2522
    /*
2523
     * Use HPA if change is larger than absolute, otherwise move
2524
     * the cursor with CUB/CUF.
2525
     */
2526
0
    if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
2527
0
      tty_putcode_i(tty, TTYC_HPA, cx);
2528
0
      goto out;
2529
0
    } else if (change > 0 &&
2530
0
        tty_term_has(term, TTYC_CUB) &&
2531
0
        !tty_use_margin(tty)) {
2532
0
      if (change == 2 && tty_term_has(term, TTYC_CUB1)) {
2533
0
        tty_putcode(tty, TTYC_CUB1);
2534
0
        tty_putcode(tty, TTYC_CUB1);
2535
0
        goto out;
2536
0
      }
2537
0
      tty_putcode_i(tty, TTYC_CUB, change);
2538
0
      goto out;
2539
0
    } else if (change < 0 &&
2540
0
        tty_term_has(term, TTYC_CUF) &&
2541
0
        !tty_use_margin(tty)) {
2542
0
      tty_putcode_i(tty, TTYC_CUF, -change);
2543
0
      goto out;
2544
0
    }
2545
0
  } else if (cx == thisx) {
2546
    /*
2547
     * Moving row only, column staying the same.
2548
     */
2549
2550
    /* One above. */
2551
0
    if (thisy != tty->rupper &&
2552
0
        cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) {
2553
0
      tty_putcode(tty, TTYC_CUU1);
2554
0
      goto out;
2555
0
    }
2556
2557
    /* One below. */
2558
0
    if (thisy != tty->rlower &&
2559
0
        cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) {
2560
0
      tty_putcode(tty, TTYC_CUD1);
2561
0
      goto out;
2562
0
    }
2563
2564
    /* Calculate difference. */
2565
0
    change = thisy - cy;  /* +ve up, -ve down */
2566
2567
    /*
2568
     * Try to use VPA if change is larger than absolute or if this
2569
     * change would cross the scroll region, otherwise use CUU/CUD.
2570
     */
2571
0
    if ((u_int) abs(change) > cy ||
2572
0
        (change < 0 && cy - change > tty->rlower) ||
2573
0
        (change > 0 && cy - change < tty->rupper)) {
2574
0
          if (tty_term_has(term, TTYC_VPA)) {
2575
0
            tty_putcode_i(tty, TTYC_VPA, cy);
2576
0
            goto out;
2577
0
          }
2578
0
    } else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
2579
0
      tty_putcode_i(tty, TTYC_CUU, change);
2580
0
      goto out;
2581
0
    } else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
2582
0
      tty_putcode_i(tty, TTYC_CUD, -change);
2583
0
      goto out;
2584
0
    }
2585
0
  }
2586
2587
0
absolute:
2588
  /* Absolute movement. */
2589
0
  tty_putcode_ii(tty, TTYC_CUP, cy, cx);
2590
2591
0
out:
2592
0
  tty->cx = cx;
2593
0
  tty->cy = cy;
2594
0
}
2595
2596
static void
2597
tty_hyperlink(struct tty *tty, const struct grid_cell *gc,
2598
    struct hyperlinks *hl)
2599
0
{
2600
0
  const char  *uri, *id;
2601
2602
0
  if (gc->link == tty->cell.link)
2603
0
    return;
2604
0
  tty->cell.link = gc->link;
2605
2606
0
  if (hl == NULL)
2607
0
    return;
2608
2609
0
  if (gc->link == 0 || !hyperlinks_get(hl, gc->link, &uri, NULL, &id))
2610
0
    tty_putcode_ss(tty, TTYC_HLS, "", "");
2611
0
  else
2612
0
    tty_putcode_ss(tty, TTYC_HLS, id, uri);
2613
0
}
2614
2615
static int
2616
tty_dim_default_colour(struct tty *tty, int c, int foreground)
2617
0
{
2618
0
  enum client_theme  theme;
2619
2620
0
  if (!COLOUR_DEFAULT(c))
2621
0
    return (c);
2622
2623
0
  if (foreground && tty->fg != -1)
2624
0
    return (tty->fg);
2625
0
  if (!foreground && tty->bg != -1)
2626
0
    return (tty->bg);
2627
2628
0
  theme = tty->client->theme;
2629
0
  if (theme == THEME_DARK)
2630
0
    return (foreground ? 7 : 0);
2631
0
  if (theme == THEME_LIGHT)
2632
0
    return (foreground ? 0 : 7);
2633
0
  return (c);
2634
0
}
2635
2636
void
2637
tty_attributes(struct tty *tty, const struct grid_cell *gc,
2638
    const struct tty_style_ctx *style_ctx)
2639
0
{
2640
0
  struct grid_cell  *tc = &tty->cell, gc2;
2641
0
  struct colour_palette *palette;
2642
0
  int      changed;
2643
2644
  /* Use default style if not given. */
2645
0
  if (style_ctx == NULL)
2646
0
    style_ctx = &tty_default_style_ctx;
2647
0
  palette = style_ctx->palette;
2648
2649
  /* Copy cell and update default colours. */
2650
0
  memcpy(&gc2, gc, sizeof gc2);
2651
0
  if (~gc->flags & GRID_FLAG_NOPALETTE) {
2652
0
    if (gc2.fg == 8)
2653
0
      gc2.fg = style_ctx->defaults->fg;
2654
0
    if (gc2.bg == 8)
2655
0
      gc2.bg = style_ctx->defaults->bg;
2656
0
    if (palette != NULL) {
2657
0
      changed = colour_palette_get(palette, gc2.fg);
2658
0
      if (changed != -1)
2659
0
        gc2.fg = changed;
2660
0
      changed = colour_palette_get(palette, gc2.bg);
2661
0
      if (changed != -1)
2662
0
        gc2.bg = changed;
2663
0
    }
2664
0
  }
2665
0
  gc2.fg = tty_map_theme_colour(tty, gc2.fg);
2666
0
  gc2.bg = tty_map_theme_colour(tty, gc2.bg);
2667
0
  gc2.us = tty_map_theme_colour(tty, gc2.us);
2668
0
  if (style_ctx->dim != 0) {
2669
0
    gc2.fg = tty_dim_default_colour(tty, gc2.fg, 1);
2670
0
    gc2.bg = tty_dim_default_colour(tty, gc2.bg, 0);
2671
0
    changed = colour_dim(gc2.fg, style_ctx->dim);
2672
0
    if (changed != -1)
2673
0
      gc2.fg = changed;
2674
0
    changed = colour_dim(gc2.bg, style_ctx->dim);
2675
0
    if (changed != -1)
2676
0
      gc2.bg = changed;
2677
0
  }
2678
2679
  /* Ignore cell if it is the same as the last one. */
2680
0
  if (gc2.attr == tty->last_cell.attr &&
2681
0
      gc2.fg == tty->last_cell.fg &&
2682
0
      gc2.bg == tty->last_cell.bg &&
2683
0
      gc2.us == tty->last_cell.us &&
2684
0
    gc2.link == tty->last_cell.link)
2685
0
    return;
2686
2687
  /*
2688
   * If no setab, try to use the reverse attribute as a best-effort for a
2689
   * non-default background. This is a bit of a hack but it doesn't do
2690
   * any serious harm and makes a couple of applications happier.
2691
   */
2692
0
  if (!tty_term_has(tty->term, TTYC_SETAB)) {
2693
0
    if (gc2.attr & GRID_ATTR_REVERSE) {
2694
0
      if (gc2.fg != 7 && !COLOUR_DEFAULT(gc2.fg))
2695
0
        gc2.attr &= ~GRID_ATTR_REVERSE;
2696
0
    } else {
2697
0
      if (gc2.bg != 0 && !COLOUR_DEFAULT(gc2.bg))
2698
0
        gc2.attr |= GRID_ATTR_REVERSE;
2699
0
    }
2700
0
  }
2701
2702
  /* Fix up the colours if necessary. */
2703
0
  tty_check_fg(tty, palette, &gc2);
2704
0
  tty_check_bg(tty, palette, &gc2);
2705
0
  tty_check_us(tty, palette, &gc2);
2706
2707
  /*
2708
   * If any bits are being cleared or the underline colour is now default,
2709
   * reset everything.
2710
   */
2711
0
  if ((tc->attr & ~gc2.attr) || (tc->us != gc2.us && gc2.us == 0))
2712
0
    tty_reset(tty);
2713
2714
  /*
2715
   * Set the colours. This may call tty_reset() (so it comes next) and
2716
   * may add to (NOT remove) the desired attributes.
2717
   */
2718
0
  tty_colours(tty, &gc2);
2719
2720
  /* Filter out attribute bits already set. */
2721
0
  changed = gc2.attr & ~tc->attr;
2722
0
  tc->attr = gc2.attr;
2723
2724
  /* Set the attributes. */
2725
0
  if (changed & GRID_ATTR_BRIGHT)
2726
0
    tty_putcode(tty, TTYC_BOLD);
2727
0
  if (changed & GRID_ATTR_DIM)
2728
0
    tty_putcode(tty, TTYC_DIM);
2729
0
  if (changed & GRID_ATTR_ITALICS)
2730
0
    tty_set_italics(tty);
2731
0
  if (changed & GRID_ATTR_ALL_UNDERSCORE) {
2732
0
    if (changed & GRID_ATTR_UNDERSCORE)
2733
0
      tty_putcode(tty, TTYC_SMUL);
2734
0
    else if (changed & GRID_ATTR_UNDERSCORE_2)
2735
0
      tty_putcode_i(tty, TTYC_SMULX, 2);
2736
0
    else if (changed & GRID_ATTR_UNDERSCORE_3)
2737
0
      tty_putcode_i(tty, TTYC_SMULX, 3);
2738
0
    else if (changed & GRID_ATTR_UNDERSCORE_4)
2739
0
      tty_putcode_i(tty, TTYC_SMULX, 4);
2740
0
    else if (changed & GRID_ATTR_UNDERSCORE_5)
2741
0
      tty_putcode_i(tty, TTYC_SMULX, 5);
2742
0
  }
2743
0
  if (changed & GRID_ATTR_BLINK)
2744
0
    tty_putcode(tty, TTYC_BLINK);
2745
0
  if (changed & GRID_ATTR_REVERSE) {
2746
0
    if (tty_term_has(tty->term, TTYC_REV))
2747
0
      tty_putcode(tty, TTYC_REV);
2748
0
    else if (tty_term_has(tty->term, TTYC_SMSO))
2749
0
      tty_putcode(tty, TTYC_SMSO);
2750
0
  }
2751
0
  if (changed & GRID_ATTR_HIDDEN)
2752
0
    tty_putcode(tty, TTYC_INVIS);
2753
0
  if (changed & GRID_ATTR_STRIKETHROUGH)
2754
0
    tty_putcode(tty, TTYC_SMXX);
2755
0
  if (changed & GRID_ATTR_OVERLINE)
2756
0
    tty_putcode(tty, TTYC_SMOL);
2757
0
  if ((changed & GRID_ATTR_CHARSET) && tty_acs_needed(tty))
2758
0
    tty_putcode(tty, TTYC_SMACS);
2759
2760
  /* Set hyperlink if any. */
2761
0
  tty_hyperlink(tty, gc, style_ctx->hyperlinks);
2762
2763
0
  memcpy(&tty->last_cell, &gc2, sizeof tty->last_cell);
2764
0
}
2765
2766
static void
2767
tty_colours(struct tty *tty, const struct grid_cell *gc)
2768
0
{
2769
0
  struct grid_cell  *tc = &tty->cell;
2770
2771
  /* No changes? Nothing is necessary. */
2772
0
  if (gc->fg == tc->fg && gc->bg == tc->bg && gc->us == tc->us)
2773
0
    return;
2774
2775
  /*
2776
   * Is either the default colour? This is handled specially because the
2777
   * best solution might be to reset both colours to default, in which
2778
   * case if only one is default need to fall onward to set the other
2779
   * colour.
2780
   */
2781
0
  if (COLOUR_DEFAULT(gc->fg) || COLOUR_DEFAULT(gc->bg)) {
2782
    /*
2783
     * If don't have AX, send sgr0. This resets both colours to
2784
     * default. Otherwise, try to set the default colour only as
2785
     * needed.
2786
     */
2787
0
    if (!tty_term_flag(tty->term, TTYC_AX))
2788
0
      tty_reset(tty);
2789
0
    else {
2790
0
      if (COLOUR_DEFAULT(gc->fg) && !COLOUR_DEFAULT(tc->fg)) {
2791
0
        tty_puts(tty, "\033[39m");
2792
0
        tc->fg = gc->fg;
2793
0
      }
2794
0
      if (COLOUR_DEFAULT(gc->bg) && !COLOUR_DEFAULT(tc->bg)) {
2795
0
        tty_puts(tty, "\033[49m");
2796
0
        tc->bg = gc->bg;
2797
0
      }
2798
0
    }
2799
0
  }
2800
2801
  /* Set the foreground colour. */
2802
0
  if (!COLOUR_DEFAULT(gc->fg) && gc->fg != tc->fg)
2803
0
    tty_colours_fg(tty, gc);
2804
2805
  /*
2806
   * Set the background colour. This must come after the foreground as
2807
   * tty_colours_fg() can call tty_reset().
2808
   */
2809
0
  if (!COLOUR_DEFAULT(gc->bg) && gc->bg != tc->bg)
2810
0
    tty_colours_bg(tty, gc);
2811
2812
  /* Set the underscore colour. */
2813
0
  if (gc->us != tc->us)
2814
0
    tty_colours_us(tty, gc);
2815
0
}
2816
2817
static int
2818
tty_map_theme_colour(struct tty *tty, int colour)
2819
0
{
2820
0
  struct client *c;
2821
0
  u_int    n;
2822
0
  int    m;
2823
2824
0
  if (~colour & COLOUR_FLAG_THEME)
2825
0
    return (colour);
2826
2827
0
  n = colour & 0xff;
2828
0
  if (n >= COLOUR_THEME_COUNT)
2829
0
    return (8);
2830
0
  if (tty == NULL || (c = tty->client) == NULL)
2831
0
    return (8);
2832
2833
0
  m = c->theme_colours[n];
2834
0
  if (m == -1 || (m & COLOUR_FLAG_THEME))
2835
0
    return (8);
2836
0
  return (m);
2837
0
}
2838
2839
static void
2840
tty_check_fg(struct tty *tty, struct colour_palette *palette,
2841
    struct grid_cell *gc)
2842
0
{
2843
0
  u_char  r, g, b;
2844
0
  u_int colours;
2845
0
  int c;
2846
2847
  /*
2848
   * Perform substitution if this pane has a palette. If the bright
2849
   * attribute is set and Nobr is not present, use the bright entry in
2850
   * the palette by changing to the aixterm colour
2851
   */
2852
0
  if (~gc->flags & GRID_FLAG_NOPALETTE) {
2853
0
    c = gc->fg;
2854
0
    if (c < 8 &&
2855
0
        gc->attr & GRID_ATTR_BRIGHT &&
2856
0
        !tty_term_has(tty->term, TTYC_NOBR))
2857
0
      c += 90;
2858
0
    if ((c = colour_palette_get(palette, c)) != -1)
2859
0
      gc->fg = c;
2860
0
  }
2861
0
  gc->fg = tty_map_theme_colour(tty, gc->fg);
2862
2863
  /* Is this a 24-bit colour? */
2864
0
  if (gc->fg & COLOUR_FLAG_RGB) {
2865
    /* Not a 24-bit terminal? Translate to 256-colour palette. */
2866
0
    if (tty->term->flags & TERM_RGBCOLOURS)
2867
0
      return;
2868
0
    colour_split_rgb(gc->fg, &r, &g, &b);
2869
0
    gc->fg = colour_find_rgb(r, g, b);
2870
0
  }
2871
2872
  /* How many colours does this terminal have? */
2873
0
  if (tty->term->flags & TERM_256COLOURS)
2874
0
    colours = 256;
2875
0
  else
2876
0
    colours = tty_term_number(tty->term, TTYC_COLORS);
2877
2878
  /* Is this a 256-colour colour? */
2879
0
  if (gc->fg & COLOUR_FLAG_256) {
2880
    /* And not a 256 colour mode? */
2881
0
    if (colours >= 256)
2882
0
      return;
2883
0
    gc->fg = colour_256to16(gc->fg);
2884
0
    if (~gc->fg & 8)
2885
0
      return;
2886
0
    gc->fg &= 7;
2887
0
    if (colours >= 16)
2888
0
      gc->fg += 90;
2889
0
    else {
2890
      /*
2891
       * Mapping to black-on-black or white-on-white is not
2892
       * much use, so change the foreground.
2893
       */
2894
0
      if (gc->fg == 0 && gc->bg == 0)
2895
0
        gc->fg = 7;
2896
0
      else if (gc->fg == 7 && gc->bg == 7)
2897
0
        gc->fg = 0;
2898
0
    }
2899
0
    return;
2900
0
  }
2901
2902
  /* Is this an aixterm colour? */
2903
0
  if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) {
2904
0
    gc->fg -= 90;
2905
0
    gc->attr |= GRID_ATTR_BRIGHT;
2906
0
  }
2907
0
}
2908
2909
static void
2910
tty_check_bg(struct tty *tty, struct colour_palette *palette,
2911
    struct grid_cell *gc)
2912
0
{
2913
0
  u_char  r, g, b;
2914
0
  u_int colours;
2915
0
  int c;
2916
2917
  /* Perform substitution if this pane has a palette. */
2918
0
  if (~gc->flags & GRID_FLAG_NOPALETTE) {
2919
0
    if ((c = colour_palette_get(palette, gc->bg)) != -1)
2920
0
      gc->bg = c;
2921
0
  }
2922
0
  gc->bg = tty_map_theme_colour(tty, gc->bg);
2923
2924
  /* Is this a 24-bit colour? */
2925
0
  if (gc->bg & COLOUR_FLAG_RGB) {
2926
    /* Not a 24-bit terminal? Translate to 256-colour palette. */
2927
0
    if (tty->term->flags & TERM_RGBCOLOURS)
2928
0
      return;
2929
0
    colour_split_rgb(gc->bg, &r, &g, &b);
2930
0
    gc->bg = colour_find_rgb(r, g, b);
2931
0
  }
2932
2933
  /* How many colours does this terminal have? */
2934
0
  if (tty->term->flags & TERM_256COLOURS)
2935
0
    colours = 256;
2936
0
  else
2937
0
    colours = tty_term_number(tty->term, TTYC_COLORS);
2938
2939
  /* Is this a 256-colour colour? */
2940
0
  if (gc->bg & COLOUR_FLAG_256) {
2941
    /*
2942
     * And not a 256 colour mode? Translate to 16-colour
2943
     * palette. Bold background doesn't exist portably, so just
2944
     * discard the bold bit if set.
2945
     */
2946
0
    if (colours >= 256)
2947
0
      return;
2948
0
    gc->bg = colour_256to16(gc->bg);
2949
0
    if (~gc->bg & 8)
2950
0
      return;
2951
0
    gc->bg &= 7;
2952
0
    if (colours >= 16)
2953
0
      gc->bg += 90;
2954
0
    return;
2955
0
  }
2956
2957
  /* Is this an aixterm colour? */
2958
0
  if (gc->bg >= 90 && gc->bg <= 97 && colours < 16)
2959
0
    gc->bg -= 90;
2960
0
}
2961
2962
static void
2963
tty_check_us(__unused struct tty *tty, struct colour_palette *palette,
2964
    struct grid_cell *gc)
2965
0
{
2966
0
  int c;
2967
2968
  /* Perform substitution if this pane has a palette. */
2969
0
  if (~gc->flags & GRID_FLAG_NOPALETTE) {
2970
0
    if ((c = colour_palette_get(palette, gc->us)) != -1)
2971
0
      gc->us = c;
2972
0
  }
2973
0
  gc->us = tty_map_theme_colour(tty, gc->us);
2974
2975
  /* Convert underscore colour if only RGB can be supported. */
2976
0
  if (!tty_term_has(tty->term, TTYC_SETULC1)) {
2977
0
        if ((c = colour_force_rgb (gc->us)) == -1)
2978
0
          gc->us = 8;
2979
0
        else
2980
0
          gc->us = c;
2981
0
  }
2982
0
}
2983
2984
static void
2985
tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
2986
0
{
2987
0
  struct grid_cell  *tc = &tty->cell;
2988
0
  char       s[32];
2989
2990
  /*
2991
   * If the current colour is an aixterm bright colour and the new is not,
2992
   * reset because some terminals do not clear bright correctly.
2993
   */
2994
0
  if (tty->cell.fg >= 90 &&
2995
0
      tty->cell.bg <= 97 &&
2996
0
      (gc->fg < 90 || gc->fg > 97))
2997
0
    tty_reset(tty);
2998
2999
  /* Is this a 24-bit or 256-colour colour? */
3000
0
  if (gc->fg & COLOUR_FLAG_RGB || gc->fg & COLOUR_FLAG_256) {
3001
0
    if (tty_try_colour(tty, gc->fg, "38") == 0)
3002
0
      goto save;
3003
    /* Should not get here, already converted in tty_check_fg. */
3004
0
    return;
3005
0
  }
3006
3007
  /* Is this an aixterm bright colour? */
3008
0
  if (gc->fg >= 90 && gc->fg <= 97) {
3009
0
    if (tty->term->flags & TERM_256COLOURS) {
3010
0
      xsnprintf(s, sizeof s, "\033[%dm", gc->fg);
3011
0
      tty_puts(tty, s);
3012
0
    } else
3013
0
      tty_putcode_i(tty, TTYC_SETAF, gc->fg - 90 + 8);
3014
0
    goto save;
3015
0
  }
3016
3017
  /* Otherwise set the foreground colour. */
3018
0
  tty_putcode_i(tty, TTYC_SETAF, gc->fg);
3019
3020
0
save:
3021
  /* Save the new values in the terminal current cell. */
3022
0
  tc->fg = gc->fg;
3023
0
}
3024
3025
static void
3026
tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
3027
0
{
3028
0
  struct grid_cell  *tc = &tty->cell;
3029
0
  char       s[32];
3030
3031
  /* Is this a 24-bit or 256-colour colour? */
3032
0
  if (gc->bg & COLOUR_FLAG_RGB || gc->bg & COLOUR_FLAG_256) {
3033
0
    if (tty_try_colour(tty, gc->bg, "48") == 0)
3034
0
      goto save;
3035
    /* Should not get here, already converted in tty_check_bg. */
3036
0
    return;
3037
0
  }
3038
3039
  /* Is this an aixterm bright colour? */
3040
0
  if (gc->bg >= 90 && gc->bg <= 97) {
3041
0
    if (tty->term->flags & TERM_256COLOURS) {
3042
0
      xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10);
3043
0
      tty_puts(tty, s);
3044
0
    } else
3045
0
      tty_putcode_i(tty, TTYC_SETAB, gc->bg - 90 + 8);
3046
0
    goto save;
3047
0
  }
3048
3049
  /* Otherwise set the background colour. */
3050
0
  tty_putcode_i(tty, TTYC_SETAB, gc->bg);
3051
3052
0
save:
3053
  /* Save the new values in the terminal current cell. */
3054
0
  tc->bg = gc->bg;
3055
0
}
3056
3057
static void
3058
tty_colours_us(struct tty *tty, const struct grid_cell *gc)
3059
0
{
3060
0
  struct grid_cell  *tc = &tty->cell;
3061
0
  u_int      c;
3062
0
  u_char       r, g, b;
3063
3064
  /* Clear underline colour. */
3065
0
  if (COLOUR_DEFAULT(gc->us)) {
3066
0
    tty_putcode(tty, TTYC_OL);
3067
0
    goto save;
3068
0
  }
3069
3070
  /*
3071
   * If this is not an RGB colour, use Setulc1 if it exists, otherwise
3072
   * convert.
3073
   */
3074
0
  if (~gc->us & COLOUR_FLAG_RGB) {
3075
0
    c = gc->us;
3076
0
    if ((~c & COLOUR_FLAG_256) && (c >= 90 && c <= 97))
3077
0
      c -= 82;
3078
0
    tty_putcode_i(tty, TTYC_SETULC1, c & ~COLOUR_FLAG_256);
3079
0
    return;
3080
0
  }
3081
3082
  /*
3083
   * Setulc and setal follows the ncurses(3) one argument "direct colour"
3084
   * capability format. Calculate the colour value.
3085
   */
3086
0
  colour_split_rgb(gc->us, &r, &g, &b);
3087
0
  c = (65536 * r) + (256 * g) + b;
3088
3089
  /*
3090
   * Write the colour. Only use setal if the RGB flag is set because the
3091
   * non-RGB version may be wrong.
3092
   */
3093
0
  if (tty_term_has(tty->term, TTYC_SETULC))
3094
0
    tty_putcode_i(tty, TTYC_SETULC, c);
3095
0
  else if (tty_term_has(tty->term, TTYC_SETAL) &&
3096
0
      tty_term_has(tty->term, TTYC_RGB))
3097
0
    tty_putcode_i(tty, TTYC_SETAL, c);
3098
3099
0
save:
3100
  /* Save the new values in the terminal current cell. */
3101
0
  tc->us = gc->us;
3102
0
}
3103
3104
static int
3105
tty_try_colour(struct tty *tty, int colour, const char *type)
3106
0
{
3107
0
  u_char  r, g, b;
3108
3109
0
  if (colour & COLOUR_FLAG_256) {
3110
0
    if (*type == '3' && tty_term_has(tty->term, TTYC_SETAF))
3111
0
      tty_putcode_i(tty, TTYC_SETAF, colour & 0xff);
3112
0
    else if (tty_term_has(tty->term, TTYC_SETAB))
3113
0
      tty_putcode_i(tty, TTYC_SETAB, colour & 0xff);
3114
0
    return (0);
3115
0
  }
3116
3117
0
  if (colour & COLOUR_FLAG_RGB) {
3118
0
    colour_split_rgb(colour & 0xffffff, &r, &g, &b);
3119
0
    if (*type == '3' && tty_term_has(tty->term, TTYC_SETRGBF))
3120
0
      tty_putcode_iii(tty, TTYC_SETRGBF, r, g, b);
3121
0
    else if (tty_term_has(tty->term, TTYC_SETRGBB))
3122
0
      tty_putcode_iii(tty, TTYC_SETRGBB, r, g, b);
3123
0
    return (0);
3124
0
  }
3125
3126
0
  return (-1);
3127
0
}
3128
3129
static void
3130
tty_window_default_style(struct grid_cell *gc, struct window_pane *wp)
3131
8.18k
{
3132
8.18k
  memcpy(gc, &grid_default_cell, sizeof *gc);
3133
8.18k
  gc->fg = wp->palette.fg;
3134
8.18k
  gc->bg = wp->palette.bg;
3135
8.18k
}
3136
3137
static void
3138
tty_style_changed(struct window_pane *wp)
3139
4.09k
{
3140
4.09k
  struct options    *oo = wp->options;
3141
4.09k
  struct format_tree  *ft;
3142
4.09k
  struct style    *sy;
3143
3144
4.09k
  log_debug("%%%u: style changed", wp->id);
3145
4.09k
  wp->flags &= ~PANE_STYLECHANGED;
3146
3147
4.09k
  ft = format_create(NULL, NULL, FORMAT_PANE|wp->id, FORMAT_NOJOBS);
3148
4.09k
  format_defaults(ft, NULL, NULL, NULL, wp);
3149
3150
4.09k
  tty_window_default_style(&wp->cached_active_gc, wp);
3151
4.09k
  sy = style_add(&wp->cached_active_gc, oo, "window-active-style", ft);
3152
4.09k
  wp->cached_active_dim = sy->dim;
3153
3154
4.09k
  tty_window_default_style(&wp->cached_gc, wp);
3155
4.09k
  sy = style_add(&wp->cached_gc, oo, "window-style", ft);
3156
4.09k
  wp->cached_dim = sy->dim;
3157
3158
4.09k
  format_free(ft);
3159
4.09k
}
3160
3161
void
3162
tty_default_colours(struct grid_cell *gc, struct window_pane *wp, u_int *dim)
3163
74.2k
{
3164
74.2k
  if (wp->flags & PANE_STYLECHANGED)
3165
4.09k
    tty_style_changed (wp);
3166
3167
74.2k
  memcpy(gc, &grid_default_cell, sizeof *gc);
3168
74.2k
  if (wp == wp->window->active && wp->cached_active_gc.fg != 8)
3169
0
    gc->fg = wp->cached_active_gc.fg;
3170
74.2k
  else
3171
74.2k
    gc->fg = wp->cached_gc.fg;
3172
74.2k
  if (wp == wp->window->active && wp->cached_active_gc.bg != 8)
3173
0
    gc->bg = wp->cached_active_gc.bg;
3174
74.2k
  else
3175
74.2k
    gc->bg = wp->cached_gc.bg;
3176
3177
74.2k
  if (dim != NULL) {
3178
73.9k
    if (wp == wp->window->active)
3179
0
      *dim = wp->cached_active_dim;
3180
73.9k
    else
3181
73.9k
      *dim = wp->cached_dim;
3182
73.9k
  }
3183
74.2k
}
3184
3185
void
3186
tty_default_attributes(struct tty *tty, u_int bg,
3187
    const struct tty_style_ctx *style_ctx)
3188
0
{
3189
0
  struct grid_cell  gc;
3190
3191
0
  memcpy(&gc, &grid_default_cell, sizeof gc);
3192
0
  gc.bg = bg;
3193
0
  tty_attributes(tty, &gc, style_ctx);
3194
0
}
3195
3196
static void
3197
tty_clipboard_query_callback(__unused int fd, __unused short events, void *data)
3198
0
{
3199
0
  struct tty  *tty = data;
3200
3201
0
  tty->flags &= ~TTY_OSC52QUERY;
3202
0
}
3203
3204
void
3205
tty_clipboard_query(struct tty *tty)
3206
0
{
3207
0
  struct timeval   tv = { .tv_sec = TTY_QUERY_TIMEOUT };
3208
3209
0
  if ((tty->flags & TTY_STARTED) && (~tty->flags & TTY_OSC52QUERY)) {
3210
0
    tty_putcode_ss(tty, TTYC_MS, "", "?");
3211
0
    tty->flags |= TTY_OSC52QUERY;
3212
0
    evtimer_add(&tty->clipboard_timer, &tv);
3213
0
  }
3214
0
}
3215
3216
void
3217
tty_set_progress_bar(struct tty *tty, struct progress_bar *pb)
3218
0
{
3219
0
  if (tty_term_has(tty->term, TTYC_SPB))
3220
0
    tty_putcode_ii(tty, TTYC_SPB, pb->state, pb->progress);
3221
0
}