Coverage Report

Created: 2026-10-05 06:03

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