Coverage Report

Created: 2026-08-13 06:04

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