GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/tty.c Lines: 0 738 0.0 %
Date: 2016-12-06 Branches: 0 668 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: tty.c,v 1.205 2016/07/15 00:49:08 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
#include <sys/ioctl.h>
21
22
#include <netinet/in.h>
23
24
#include <errno.h>
25
#include <fcntl.h>
26
#include <resolv.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include <termios.h>
30
#include <unistd.h>
31
32
#include "tmux.h"
33
34
static int tty_log_fd = -1;
35
36
void	tty_read_callback(struct bufferevent *, void *);
37
void	tty_error_callback(struct bufferevent *, short, void *);
38
39
static int tty_client_ready(struct client *, struct window_pane *);
40
41
void	tty_set_italics(struct tty *);
42
int	tty_try_colour(struct tty *, int, const char *);
43
44
void	tty_colours(struct tty *, const struct grid_cell *);
45
void	tty_check_fg(struct tty *, struct grid_cell *);
46
void	tty_check_bg(struct tty *, struct grid_cell *);
47
void	tty_colours_fg(struct tty *, const struct grid_cell *);
48
void	tty_colours_bg(struct tty *, const struct grid_cell *);
49
50
int	tty_large_region(struct tty *, const struct tty_ctx *);
51
int	tty_fake_bce(const struct tty *, const struct window_pane *);
52
void	tty_redraw_region(struct tty *, const struct tty_ctx *);
53
void	tty_emulate_repeat(struct tty *, enum tty_code_code, enum tty_code_code,
54
	    u_int);
55
void	tty_repeat_space(struct tty *, u_int);
56
void	tty_cell(struct tty *, const struct grid_cell *,
57
	    const struct window_pane *);
58
void	tty_default_colours(struct grid_cell *, const struct window_pane *);
59
60
#define tty_use_acs(tty) \
61
	(tty_term_has((tty)->term, TTYC_ACSC) && !((tty)->flags & TTY_UTF8))
62
63
#define tty_pane_full_width(tty, ctx) \
64
	((ctx)->xoff == 0 && screen_size_x((ctx)->wp->screen) >= (tty)->sx)
65
66
void
67
tty_create_log(void)
68
{
69
	char	name[64];
70
71
	xsnprintf(name, sizeof name, "tmux-out-%ld.log", (long)getpid());
72
73
	tty_log_fd = open(name, O_WRONLY|O_CREAT|O_TRUNC, 0644);
74
	if (tty_log_fd != -1 && fcntl(tty_log_fd, F_SETFD, FD_CLOEXEC) == -1)
75
		fatal("fcntl failed");
76
}
77
78
int
79
tty_init(struct tty *tty, struct client *c, int fd, char *term)
80
{
81
	char	*path;
82
83
	if (!isatty(fd))
84
		return (-1);
85
86
	memset(tty, 0, sizeof *tty);
87
88
	if (term == NULL || *term == '\0')
89
		tty->termname = xstrdup("unknown");
90
	else
91
		tty->termname = xstrdup(term);
92
	tty->fd = fd;
93
	tty->client = c;
94
95
	if ((path = ttyname(fd)) == NULL)
96
		return (-1);
97
	tty->path = xstrdup(path);
98
	tty->cstyle = 0;
99
	tty->ccolour = xstrdup("");
100
101
	tty->flags = 0;
102
	tty->term_flags = 0;
103
104
	return (0);
105
}
106
107
int
108
tty_resize(struct tty *tty)
109
{
110
	struct winsize	ws;
111
	u_int		sx, sy;
112
113
	if (ioctl(tty->fd, TIOCGWINSZ, &ws) != -1) {
114
		sx = ws.ws_col;
115
		if (sx == 0)
116
			sx = 80;
117
		sy = ws.ws_row;
118
		if (sy == 0)
119
			sy = 24;
120
	} else {
121
		sx = 80;
122
		sy = 24;
123
	}
124
	if (!tty_set_size(tty, sx, sy))
125
		return (0);
126
127
	tty->cx = UINT_MAX;
128
	tty->cy = UINT_MAX;
129
130
	tty->rupper = UINT_MAX;
131
	tty->rlower = UINT_MAX;
132
133
	/*
134
	 * If the terminal has been started, reset the actual scroll region and
135
	 * cursor position, as this may not have happened.
136
	 */
137
	if (tty->flags & TTY_STARTED) {
138
		tty_cursor(tty, 0, 0);
139
		tty_region(tty, 0, tty->sy - 1);
140
	}
141
142
	return (1);
143
}
144
145
int
146
tty_set_size(struct tty *tty, u_int sx, u_int sy) {
147
	if (sx == tty->sx && sy == tty->sy)
148
		return (0);
149
	tty->sx = sx;
150
	tty->sy = sy;
151
	return (1);
152
}
153
154
int
155
tty_open(struct tty *tty, char **cause)
156
{
157
	tty->term = tty_term_find(tty->termname, tty->fd, cause);
158
	if (tty->term == NULL) {
159
		tty_close(tty);
160
		return (-1);
161
	}
162
	tty->flags |= TTY_OPENED;
163
164
	tty->flags &= ~(TTY_NOCURSOR|TTY_FREEZE|TTY_TIMER);
165
166
	tty->event = bufferevent_new(tty->fd, tty_read_callback, NULL,
167
	    tty_error_callback, tty);
168
169
	tty_start_tty(tty);
170
171
	tty_keys_build(tty);
172
173
	return (0);
174
}
175
176
void
177
tty_read_callback(__unused struct bufferevent *bufev, void *data)
178
{
179
	struct tty	*tty = data;
180
181
	while (tty_keys_next(tty))
182
		;
183
}
184
185
void
186
tty_error_callback(__unused struct bufferevent *bufev, __unused short what,
187
    __unused void *data)
188
{
189
}
190
191
void
192
tty_init_termios(int fd, struct termios *orig_tio, struct bufferevent *bufev)
193
{
194
	struct termios	tio;
195
196
	if (fd == -1 || tcgetattr(fd, orig_tio) != 0)
197
		return;
198
199
	setblocking(fd, 0);
200
201
	if (bufev != NULL)
202
		bufferevent_enable(bufev, EV_READ|EV_WRITE);
203
204
	memcpy(&tio, orig_tio, sizeof tio);
205
	tio.c_iflag &= ~(IXON|IXOFF|ICRNL|INLCR|IGNCR|IMAXBEL|ISTRIP);
206
	tio.c_iflag |= IGNBRK;
207
	tio.c_oflag &= ~(OPOST|ONLCR|OCRNL|ONLRET);
208
	tio.c_lflag &= ~(IEXTEN|ICANON|ECHO|ECHOE|ECHONL|ECHOCTL|
209
	    ECHOPRT|ECHOKE|ISIG);
210
	tio.c_cc[VMIN] = 1;
211
	tio.c_cc[VTIME] = 0;
212
	if (tcsetattr(fd, TCSANOW, &tio) == 0)
213
		tcflush(fd, TCIOFLUSH);
214
}
215
216
void
217
tty_start_tty(struct tty *tty)
218
{
219
	tty_init_termios(tty->fd, &tty->tio, tty->event);
220
221
	tty_putcode(tty, TTYC_SMCUP);
222
223
	tty_putcode(tty, TTYC_SGR0);
224
	memcpy(&tty->cell, &grid_default_cell, sizeof tty->cell);
225
226
	tty_putcode(tty, TTYC_RMKX);
227
	if (tty_use_acs(tty))
228
		tty_putcode(tty, TTYC_ENACS);
229
	tty_putcode(tty, TTYC_CLEAR);
230
231
	tty_putcode(tty, TTYC_CNORM);
232
	if (tty_term_has(tty->term, TTYC_KMOUS))
233
		tty_puts(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l");
234
235
	if (tty_term_flag(tty->term, TTYC_XT)) {
236
		if (options_get_number(global_options, "focus-events")) {
237
			tty->flags |= TTY_FOCUS;
238
			tty_puts(tty, "\033[?1004h");
239
		}
240
	}
241
242
	tty->cx = UINT_MAX;
243
	tty->cy = UINT_MAX;
244
245
	tty->rlower = UINT_MAX;
246
	tty->rupper = UINT_MAX;
247
248
	tty->mode = MODE_CURSOR;
249
250
	tty->flags |= TTY_STARTED;
251
252
	tty_force_cursor_colour(tty, "");
253
254
	tty->mouse_drag_flag = 0;
255
	tty->mouse_drag_update = NULL;
256
	tty->mouse_drag_release = NULL;
257
}
258
259
void
260
tty_stop_tty(struct tty *tty)
261
{
262
	struct winsize	ws;
263
264
	if (!(tty->flags & TTY_STARTED))
265
		return;
266
	tty->flags &= ~TTY_STARTED;
267
268
	bufferevent_disable(tty->event, EV_READ|EV_WRITE);
269
270
	/*
271
	 * Be flexible about error handling and try not kill the server just
272
	 * because the fd is invalid. Things like ssh -t can easily leave us
273
	 * with a dead tty.
274
	 */
275
	if (ioctl(tty->fd, TIOCGWINSZ, &ws) == -1)
276
		return;
277
	if (tcsetattr(tty->fd, TCSANOW, &tty->tio) == -1)
278
		return;
279
280
	tty_raw(tty, tty_term_string2(tty->term, TTYC_CSR, 0, ws.ws_row - 1));
281
	if (tty_use_acs(tty))
282
		tty_raw(tty, tty_term_string(tty->term, TTYC_RMACS));
283
	tty_raw(tty, tty_term_string(tty->term, TTYC_SGR0));
284
	tty_raw(tty, tty_term_string(tty->term, TTYC_RMKX));
285
	tty_raw(tty, tty_term_string(tty->term, TTYC_CLEAR));
286
	if (tty_term_has(tty->term, TTYC_SS) && tty->cstyle != 0) {
287
		if (tty_term_has(tty->term, TTYC_SE))
288
			tty_raw(tty, tty_term_string(tty->term, TTYC_SE));
289
		else
290
			tty_raw(tty, tty_term_string1(tty->term, TTYC_SS, 0));
291
	}
292
	if (tty->mode & MODE_BRACKETPASTE)
293
		tty_raw(tty, "\033[?2004l");
294
	tty_raw(tty, tty_term_string(tty->term, TTYC_CR));
295
296
	tty_raw(tty, tty_term_string(tty->term, TTYC_CNORM));
297
	if (tty_term_has(tty->term, TTYC_KMOUS))
298
		tty_raw(tty, "\033[?1000l\033[?1002l\033[?1006l\033[?1005l");
299
300
	if (tty_term_flag(tty->term, TTYC_XT)) {
301
		if (tty->flags & TTY_FOCUS) {
302
			tty->flags &= ~TTY_FOCUS;
303
			tty_raw(tty, "\033[?1004l");
304
		}
305
	}
306
307
	tty_raw(tty, tty_term_string(tty->term, TTYC_RMCUP));
308
309
	setblocking(tty->fd, 1);
310
}
311
312
void
313
tty_close(struct tty *tty)
314
{
315
	if (event_initialized(&tty->key_timer))
316
		evtimer_del(&tty->key_timer);
317
	tty_stop_tty(tty);
318
319
	if (tty->flags & TTY_OPENED) {
320
		bufferevent_free(tty->event);
321
322
		tty_term_free(tty->term);
323
		tty_keys_free(tty);
324
325
		tty->flags &= ~TTY_OPENED;
326
	}
327
328
	if (tty->fd != -1) {
329
		close(tty->fd);
330
		tty->fd = -1;
331
	}
332
}
333
334
void
335
tty_free(struct tty *tty)
336
{
337
	tty_close(tty);
338
339
	free(tty->ccolour);
340
	free(tty->path);
341
	free(tty->termname);
342
}
343
344
void
345
tty_raw(struct tty *tty, const char *s)
346
{
347
	ssize_t	n, slen;
348
	u_int	i;
349
350
	slen = strlen(s);
351
	for (i = 0; i < 5; i++) {
352
		n = write(tty->fd, s, slen);
353
		if (n >= 0) {
354
			s += n;
355
			slen -= n;
356
			if (slen == 0)
357
				break;
358
		} else if (n == -1 && errno != EAGAIN)
359
			break;
360
		usleep(100);
361
	}
362
}
363
364
void
365
tty_putcode(struct tty *tty, enum tty_code_code code)
366
{
367
	tty_puts(tty, tty_term_string(tty->term, code));
368
}
369
370
void
371
tty_putcode1(struct tty *tty, enum tty_code_code code, int a)
372
{
373
	if (a < 0)
374
		return;
375
	tty_puts(tty, tty_term_string1(tty->term, code, a));
376
}
377
378
void
379
tty_putcode2(struct tty *tty, enum tty_code_code code, int a, int b)
380
{
381
	if (a < 0 || b < 0)
382
		return;
383
	tty_puts(tty, tty_term_string2(tty->term, code, a, b));
384
}
385
386
void
387
tty_putcode_ptr1(struct tty *tty, enum tty_code_code code, const void *a)
388
{
389
	if (a != NULL)
390
		tty_puts(tty, tty_term_ptr1(tty->term, code, a));
391
}
392
393
void
394
tty_putcode_ptr2(struct tty *tty, enum tty_code_code code, const void *a,
395
    const void *b)
396
{
397
	if (a != NULL && b != NULL)
398
		tty_puts(tty, tty_term_ptr2(tty->term, code, a, b));
399
}
400
401
void
402
tty_puts(struct tty *tty, const char *s)
403
{
404
	if (*s == '\0')
405
		return;
406
	bufferevent_write(tty->event, s, strlen(s));
407
408
	if (tty_log_fd != -1)
409
		write(tty_log_fd, s, strlen(s));
410
}
411
412
void
413
tty_putc(struct tty *tty, u_char ch)
414
{
415
	const char	*acs;
416
	u_int		 sx;
417
418
	if (tty->cell.attr & GRID_ATTR_CHARSET) {
419
		acs = tty_acs_get(tty, ch);
420
		if (acs != NULL)
421
			bufferevent_write(tty->event, acs, strlen(acs));
422
		else
423
			bufferevent_write(tty->event, &ch, 1);
424
	} else
425
		bufferevent_write(tty->event, &ch, 1);
426
427
	if (ch >= 0x20 && ch != 0x7f) {
428
		sx = tty->sx;
429
		if (tty->term->flags & TERM_EARLYWRAP)
430
			sx--;
431
432
		if (tty->cx >= sx) {
433
			tty->cx = 1;
434
			if (tty->cy != tty->rlower)
435
				tty->cy++;
436
		} else
437
			tty->cx++;
438
	}
439
440
	if (tty_log_fd != -1)
441
		write(tty_log_fd, &ch, 1);
442
}
443
444
void
445
tty_putn(struct tty *tty, const void *buf, size_t len, u_int width)
446
{
447
	bufferevent_write(tty->event, buf, len);
448
	if (tty_log_fd != -1)
449
		write(tty_log_fd, buf, len);
450
	tty->cx += width;
451
}
452
453
void
454
tty_set_italics(struct tty *tty)
455
{
456
	const char	*s;
457
458
	if (tty_term_has(tty->term, TTYC_SITM)) {
459
		s = options_get_string(global_options, "default-terminal");
460
		if (strcmp(s, "screen") != 0 && strncmp(s, "screen-", 7) != 0) {
461
			tty_putcode(tty, TTYC_SITM);
462
			return;
463
		}
464
	}
465
	tty_putcode(tty, TTYC_SMSO);
466
}
467
468
void
469
tty_set_title(struct tty *tty, const char *title)
470
{
471
	if (!tty_term_has(tty->term, TTYC_TSL) ||
472
	    !tty_term_has(tty->term, TTYC_FSL))
473
		return;
474
475
	tty_putcode(tty, TTYC_TSL);
476
	tty_puts(tty, title);
477
	tty_putcode(tty, TTYC_FSL);
478
}
479
480
void
481
tty_force_cursor_colour(struct tty *tty, const char *ccolour)
482
{
483
	if (*ccolour == '\0')
484
		tty_putcode(tty, TTYC_CR);
485
	else
486
		tty_putcode_ptr1(tty, TTYC_CS, ccolour);
487
	free(tty->ccolour);
488
	tty->ccolour = xstrdup(ccolour);
489
}
490
491
void
492
tty_update_mode(struct tty *tty, int mode, struct screen *s)
493
{
494
	int	changed;
495
496
	if (s != NULL && strcmp(s->ccolour, tty->ccolour) != 0)
497
		tty_force_cursor_colour(tty, s->ccolour);
498
499
	if (tty->flags & TTY_NOCURSOR)
500
		mode &= ~MODE_CURSOR;
501
502
	changed = mode ^ tty->mode;
503
	if (changed & MODE_BLINKING) {
504
		if (tty_term_has(tty->term, TTYC_CVVIS))
505
			tty_putcode(tty, TTYC_CVVIS);
506
		else
507
			tty_putcode(tty, TTYC_CNORM);
508
		changed |= MODE_CURSOR;
509
	}
510
	if (changed & MODE_CURSOR) {
511
		if (mode & MODE_CURSOR)
512
			tty_putcode(tty, TTYC_CNORM);
513
		else
514
			tty_putcode(tty, TTYC_CIVIS);
515
	}
516
	if (s != NULL && tty->cstyle != s->cstyle) {
517
		if (tty_term_has(tty->term, TTYC_SS)) {
518
			if (s->cstyle == 0 &&
519
			    tty_term_has(tty->term, TTYC_SE))
520
				tty_putcode(tty, TTYC_SE);
521
			else
522
				tty_putcode1(tty, TTYC_SS, s->cstyle);
523
		}
524
		tty->cstyle = s->cstyle;
525
	}
526
	if (changed & ALL_MOUSE_MODES) {
527
		if (mode & ALL_MOUSE_MODES) {
528
			/*
529
			 * Enable the SGR (1006) extension unconditionally, as
530
			 * this is safe from misinterpretation. Do it in this
531
			 * order, because in some terminals it's the last one
532
			 * that takes effect and SGR is the preferred one.
533
			 */
534
			tty_puts(tty, "\033[?1006h");
535
			if (mode & MODE_MOUSE_BUTTON)
536
				tty_puts(tty, "\033[?1002h");
537
			else if (mode & MODE_MOUSE_STANDARD)
538
				tty_puts(tty, "\033[?1000h");
539
		} else {
540
			if (tty->mode & MODE_MOUSE_BUTTON)
541
				tty_puts(tty, "\033[?1002l");
542
			else if (tty->mode & MODE_MOUSE_STANDARD)
543
				tty_puts(tty, "\033[?1000l");
544
			tty_puts(tty, "\033[?1006l");
545
		}
546
	}
547
	if (changed & MODE_KKEYPAD) {
548
		if (mode & MODE_KKEYPAD)
549
			tty_putcode(tty, TTYC_SMKX);
550
		else
551
			tty_putcode(tty, TTYC_RMKX);
552
	}
553
	if (changed & MODE_BRACKETPASTE) {
554
		if (mode & MODE_BRACKETPASTE)
555
			tty_puts(tty, "\033[?2004h");
556
		else
557
			tty_puts(tty, "\033[?2004l");
558
	}
559
	tty->mode = mode;
560
}
561
562
void
563
tty_emulate_repeat(struct tty *tty, enum tty_code_code code,
564
    enum tty_code_code code1, u_int n)
565
{
566
	if (tty_term_has(tty->term, code))
567
		tty_putcode1(tty, code, n);
568
	else {
569
		while (n-- > 0)
570
			tty_putcode(tty, code1);
571
	}
572
}
573
574
void
575
tty_repeat_space(struct tty *tty, u_int n)
576
{
577
	while (n-- > 0)
578
		tty_putc(tty, ' ');
579
}
580
581
/*
582
 * Is the region large enough to be worth redrawing once later rather than
583
 * probably several times now? Currently yes if it is more than 50% of the
584
 * pane.
585
 */
586
int
587
tty_large_region(__unused struct tty *tty, const struct tty_ctx *ctx)
588
{
589
	struct window_pane	*wp = ctx->wp;
590
591
	return (ctx->orlower - ctx->orupper >= screen_size_y(wp->screen) / 2);
592
}
593
594
/*
595
 * Return if BCE is needed but the terminal doesn't have it - it'll need to be
596
 * emulated.
597
 */
598
int
599
tty_fake_bce(const struct tty *tty, const struct window_pane *wp)
600
{
601
	struct grid_cell	gc;
602
603
	memcpy(&gc, &grid_default_cell, sizeof gc);
604
	if (wp != NULL)
605
		tty_default_colours(&gc, wp);
606
607
	if (gc.bg == 8)
608
		return (0);
609
	return (!tty_term_flag(tty->term, TTYC_BCE));
610
}
611
612
/*
613
 * Redraw scroll region using data from screen (already updated). Used when
614
 * CSR not supported, or window is a pane that doesn't take up the full
615
 * width of the terminal.
616
 */
617
void
618
tty_redraw_region(struct tty *tty, const struct tty_ctx *ctx)
619
{
620
	struct window_pane	*wp = ctx->wp;
621
	struct screen		*s = wp->screen;
622
	u_int			 i;
623
624
	/*
625
	 * If region is large, schedule a window redraw. In most cases this is
626
	 * likely to be followed by some more scrolling.
627
	 */
628
	if (tty_large_region(tty, ctx)) {
629
		wp->flags |= PANE_REDRAW;
630
		return;
631
	}
632
633
	if (ctx->ocy < ctx->orupper || ctx->ocy > ctx->orlower) {
634
		for (i = ctx->ocy; i < screen_size_y(s); i++)
635
			tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff);
636
	} else {
637
		for (i = ctx->orupper; i <= ctx->orlower; i++)
638
			tty_draw_pane(tty, wp, i, ctx->xoff, ctx->yoff);
639
	}
640
}
641
642
void
643
tty_draw_pane(struct tty *tty, const struct window_pane *wp, u_int py, u_int ox,
644
    u_int oy)
645
{
646
	tty_draw_line(tty, wp, wp->screen, py, ox, oy);
647
}
648
649
void
650
tty_draw_line(struct tty *tty, const struct window_pane *wp,
651
    struct screen *s, u_int py, u_int ox, u_int oy)
652
{
653
	struct grid_cell	 gc;
654
	struct grid_line	*gl;
655
	u_int			 i, sx;
656
	int			 flags;
657
658
	flags = tty->flags & TTY_NOCURSOR;
659
	tty->flags |= TTY_NOCURSOR;
660
	tty_update_mode(tty, tty->mode, s);
661
662
	sx = screen_size_x(s);
663
	if (sx > s->grid->linedata[s->grid->hsize + py].cellsize)
664
		sx = s->grid->linedata[s->grid->hsize + py].cellsize;
665
	if (sx > tty->sx)
666
		sx = tty->sx;
667
668
	/*
669
	 * Don't move the cursor to the start position if it will wrap there
670
	 * itself.
671
	 */
672
	gl = NULL;
673
	if (py != 0)
674
		gl = &s->grid->linedata[s->grid->hsize + py - 1];
675
	if (oy + py == 0 || gl == NULL || !(gl->flags & GRID_LINE_WRAPPED) ||
676
	    tty->cx < tty->sx || ox != 0 ||
677
	    (oy + py != tty->cy + 1 && tty->cy != s->rlower + oy))
678
		tty_cursor(tty, ox, oy + py);
679
680
	for (i = 0; i < sx; i++) {
681
		grid_view_get_cell(s->grid, i, py, &gc);
682
		tty_cell(tty, &gc, wp);
683
	}
684
685
	if (sx < tty->sx) {
686
		tty_attributes(tty, &grid_default_cell, wp);
687
688
		tty_cursor(tty, ox + sx, oy + py);
689
		if (sx != screen_size_x(s) &&
690
		    ox + screen_size_x(s) >= tty->sx &&
691
		    tty_term_has(tty->term, TTYC_EL) &&
692
		    !tty_fake_bce(tty, wp))
693
			tty_putcode(tty, TTYC_EL);
694
		else
695
			tty_repeat_space(tty, screen_size_x(s) - sx);
696
	}
697
698
	tty->flags = (tty->flags & ~TTY_NOCURSOR) | flags;
699
	tty_update_mode(tty, tty->mode, s);
700
}
701
702
static int
703
tty_client_ready(struct client *c, struct window_pane *wp)
704
{
705
	if (c->session == NULL || c->tty.term == NULL)
706
		return (0);
707
	if (c->flags & CLIENT_SUSPENDED)
708
		return (0);
709
	if (c->tty.flags & TTY_FREEZE)
710
		return (0);
711
	if (c->session->curw->window != wp->window)
712
		return (0);
713
	return (1);
714
}
715
716
void
717
tty_write(void (*cmdfn)(struct tty *, const struct tty_ctx *),
718
    struct tty_ctx *ctx)
719
{
720
	struct window_pane	*wp = ctx->wp;
721
	struct client		*c;
722
723
	/* wp can be NULL if updating the screen but not the terminal. */
724
	if (wp == NULL)
725
		return;
726
727
	if (wp->window->flags & WINDOW_REDRAW || wp->flags & PANE_REDRAW)
728
		return;
729
	if (!window_pane_visible(wp) || wp->flags & PANE_DROP)
730
		return;
731
732
	TAILQ_FOREACH(c, &clients, entry) {
733
		if (!tty_client_ready(c, wp))
734
			continue;
735
736
		ctx->xoff = wp->xoff;
737
		ctx->yoff = wp->yoff;
738
		if (status_at_line(c) == 0)
739
			ctx->yoff++;
740
741
		cmdfn(&c->tty, ctx);
742
	}
743
}
744
745
void
746
tty_cmd_insertcharacter(struct tty *tty, const struct tty_ctx *ctx)
747
{
748
	struct window_pane	*wp = ctx->wp;
749
750
	if (!tty_pane_full_width(tty, ctx)) {
751
		tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff);
752
		return;
753
	}
754
755
	tty_attributes(tty, &grid_default_cell, wp);
756
757
	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
758
759
	if (!tty_fake_bce(tty, wp) && (tty_term_has(tty->term, TTYC_ICH) ||
760
	    tty_term_has(tty->term, TTYC_ICH1)))
761
		tty_emulate_repeat(tty, TTYC_ICH, TTYC_ICH1, ctx->num);
762
	else
763
		tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff);
764
}
765
766
void
767
tty_cmd_deletecharacter(struct tty *tty, const struct tty_ctx *ctx)
768
{
769
	struct window_pane	*wp = ctx->wp;
770
771
	if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp) ||
772
	    (!tty_term_has(tty->term, TTYC_DCH) &&
773
	    !tty_term_has(tty->term, TTYC_DCH1))) {
774
		tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff);
775
		return;
776
	}
777
778
	tty_attributes(tty, &grid_default_cell, wp);
779
780
	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
781
782
	if (tty_term_has(tty->term, TTYC_DCH) ||
783
	    tty_term_has(tty->term, TTYC_DCH1))
784
		tty_emulate_repeat(tty, TTYC_DCH, TTYC_DCH1, ctx->num);
785
}
786
787
void
788
tty_cmd_clearcharacter(struct tty *tty, const struct tty_ctx *ctx)
789
{
790
	u_int	i;
791
792
	tty_attributes(tty, &grid_default_cell, ctx->wp);
793
794
	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
795
796
	if (tty_term_has(tty->term, TTYC_ECH) && !tty_fake_bce(tty, ctx->wp))
797
		tty_putcode1(tty, TTYC_ECH, ctx->num);
798
	else {
799
		for (i = 0; i < ctx->num; i++)
800
			tty_putc(tty, ' ');
801
	}
802
}
803
804
void
805
tty_cmd_insertline(struct tty *tty, const struct tty_ctx *ctx)
806
{
807
	if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) ||
808
	    !tty_term_has(tty->term, TTYC_CSR) ||
809
	    !tty_term_has(tty->term, TTYC_IL1)) {
810
		tty_redraw_region(tty, ctx);
811
		return;
812
	}
813
814
	tty_attributes(tty, &grid_default_cell, ctx->wp);
815
816
	tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
817
	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
818
819
	tty_emulate_repeat(tty, TTYC_IL, TTYC_IL1, ctx->num);
820
}
821
822
void
823
tty_cmd_deleteline(struct tty *tty, const struct tty_ctx *ctx)
824
{
825
	if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) ||
826
	    !tty_term_has(tty->term, TTYC_CSR) ||
827
	    !tty_term_has(tty->term, TTYC_DL1)) {
828
		tty_redraw_region(tty, ctx);
829
		return;
830
	}
831
832
	tty_attributes(tty, &grid_default_cell, ctx->wp);
833
834
	tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
835
	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
836
837
	tty_emulate_repeat(tty, TTYC_DL, TTYC_DL1, ctx->num);
838
}
839
840
void
841
tty_cmd_clearline(struct tty *tty, const struct tty_ctx *ctx)
842
{
843
	struct window_pane	*wp = ctx->wp;
844
	struct screen		*s = wp->screen;
845
846
	tty_attributes(tty, &grid_default_cell, wp);
847
848
	tty_cursor_pane(tty, ctx, 0, ctx->ocy);
849
850
	if (tty_pane_full_width(tty, ctx) && !tty_fake_bce(tty, wp) &&
851
	    tty_term_has(tty->term, TTYC_EL))
852
		tty_putcode(tty, TTYC_EL);
853
	else
854
		tty_repeat_space(tty, screen_size_x(s));
855
}
856
857
void
858
tty_cmd_clearendofline(struct tty *tty, const struct tty_ctx *ctx)
859
{
860
	struct window_pane	*wp = ctx->wp;
861
	struct screen		*s = wp->screen;
862
863
	tty_attributes(tty, &grid_default_cell, wp);
864
865
	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
866
867
	if (tty_pane_full_width(tty, ctx) &&
868
	    tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp))
869
		tty_putcode(tty, TTYC_EL);
870
	else
871
		tty_repeat_space(tty, screen_size_x(s) - ctx->ocx);
872
}
873
874
void
875
tty_cmd_clearstartofline(struct tty *tty, const struct tty_ctx *ctx)
876
{
877
	tty_attributes(tty, &grid_default_cell, ctx->wp);
878
879
	if (ctx->xoff == 0 && tty_term_has(tty->term, TTYC_EL1) &&
880
	    !tty_fake_bce(tty, ctx->wp)) {
881
		tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
882
		tty_putcode(tty, TTYC_EL1);
883
	} else {
884
		tty_cursor_pane(tty, ctx, 0, ctx->ocy);
885
		tty_repeat_space(tty, ctx->ocx + 1);
886
	}
887
}
888
889
void
890
tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx)
891
{
892
	if (ctx->ocy != ctx->orupper)
893
		return;
894
895
	if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, ctx->wp) ||
896
	    !tty_term_has(tty->term, TTYC_CSR) ||
897
	    !tty_term_has(tty->term, TTYC_RI)) {
898
		tty_redraw_region(tty, ctx);
899
		return;
900
	}
901
902
	tty_attributes(tty, &grid_default_cell, ctx->wp);
903
904
	tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
905
	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper);
906
907
	tty_putcode(tty, TTYC_RI);
908
}
909
910
void
911
tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx)
912
{
913
	struct window_pane	*wp = ctx->wp;
914
915
	if (ctx->ocy != ctx->orlower)
916
		return;
917
918
	if (!tty_pane_full_width(tty, ctx) || tty_fake_bce(tty, wp) ||
919
	    !tty_term_has(tty->term, TTYC_CSR)) {
920
		if (tty_large_region(tty, ctx))
921
			wp->flags |= PANE_REDRAW;
922
		else
923
			tty_redraw_region(tty, ctx);
924
		return;
925
	}
926
927
	/*
928
	 * If this line wrapped naturally (ctx->num is nonzero), don't do
929
	 * anything - the cursor can just be moved to the last cell and wrap
930
	 * naturally.
931
	 */
932
	if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP))
933
		return;
934
935
	tty_attributes(tty, &grid_default_cell, wp);
936
937
	tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
938
	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
939
940
	tty_putc(tty, '\n');
941
}
942
943
void
944
tty_cmd_clearendofscreen(struct tty *tty, const struct tty_ctx *ctx)
945
{
946
	struct window_pane	*wp = ctx->wp;
947
	struct screen		*s = wp->screen;
948
	u_int			 i, j;
949
950
	tty_attributes(tty, &grid_default_cell, wp);
951
952
	tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
953
	tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
954
955
	if (tty_pane_full_width(tty, ctx) &&
956
	    tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) {
957
		tty_putcode(tty, TTYC_EL);
958
		if (ctx->ocy != screen_size_y(s) - 1) {
959
			tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
960
			for (i = ctx->ocy + 1; i < screen_size_y(s); i++) {
961
				tty_putcode(tty, TTYC_EL);
962
				if (i == screen_size_y(s) - 1)
963
					continue;
964
				tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
965
				tty->cy++;
966
			}
967
		}
968
	} else {
969
		tty_repeat_space(tty, screen_size_x(s) - ctx->ocx);
970
		for (j = ctx->ocy + 1; j < screen_size_y(s); j++) {
971
			tty_cursor_pane(tty, ctx, 0, j);
972
			tty_repeat_space(tty, screen_size_x(s));
973
		}
974
	}
975
}
976
977
void
978
tty_cmd_clearstartofscreen(struct tty *tty, const struct tty_ctx *ctx)
979
{
980
	struct window_pane	*wp = ctx->wp;
981
	struct screen		*s = wp->screen;
982
	u_int			 i, j;
983
984
	tty_attributes(tty, &grid_default_cell, wp);
985
986
	tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
987
	tty_cursor_pane(tty, ctx, 0, 0);
988
989
	if (tty_pane_full_width(tty, ctx) &&
990
	    tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) {
991
		for (i = 0; i < ctx->ocy; i++) {
992
			tty_putcode(tty, TTYC_EL);
993
			tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
994
			tty->cy++;
995
		}
996
	} else {
997
		for (j = 0; j < ctx->ocy; j++) {
998
			tty_cursor_pane(tty, ctx, 0, j);
999
			tty_repeat_space(tty, screen_size_x(s));
1000
		}
1001
	}
1002
	tty_repeat_space(tty, ctx->ocx + 1);
1003
}
1004
1005
void
1006
tty_cmd_clearscreen(struct tty *tty, const struct tty_ctx *ctx)
1007
{
1008
	struct window_pane	*wp = ctx->wp;
1009
	struct screen		*s = wp->screen;
1010
	u_int			 i, j;
1011
1012
	tty_attributes(tty, &grid_default_cell, wp);
1013
1014
	tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1015
	tty_cursor_pane(tty, ctx, 0, 0);
1016
1017
	if (tty_pane_full_width(tty, ctx) &&
1018
	    tty_term_has(tty->term, TTYC_EL) && !tty_fake_bce(tty, wp)) {
1019
		for (i = 0; i < screen_size_y(s); i++) {
1020
			tty_putcode(tty, TTYC_EL);
1021
			if (i != screen_size_y(s) - 1) {
1022
				tty_emulate_repeat(tty, TTYC_CUD, TTYC_CUD1, 1);
1023
				tty->cy++;
1024
			}
1025
		}
1026
	} else {
1027
		for (j = 0; j < screen_size_y(s); j++) {
1028
			tty_cursor_pane(tty, ctx, 0, j);
1029
			tty_repeat_space(tty, screen_size_x(s));
1030
		}
1031
	}
1032
}
1033
1034
void
1035
tty_cmd_alignmenttest(struct tty *tty, const struct tty_ctx *ctx)
1036
{
1037
	struct window_pane	*wp = ctx->wp;
1038
	struct screen		*s = wp->screen;
1039
	u_int			 i, j;
1040
1041
	tty_attributes(tty, &grid_default_cell, wp);
1042
1043
	tty_region_pane(tty, ctx, 0, screen_size_y(s) - 1);
1044
1045
	for (j = 0; j < screen_size_y(s); j++) {
1046
		tty_cursor_pane(tty, ctx, 0, j);
1047
		for (i = 0; i < screen_size_x(s); i++)
1048
			tty_putc(tty, 'E');
1049
	}
1050
}
1051
1052
void
1053
tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx)
1054
{
1055
	struct window_pane	*wp = ctx->wp;
1056
	struct screen		*s = wp->screen;
1057
	u_int			 cx, width;
1058
1059
	if (ctx->ocy == ctx->orlower)
1060
		tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower);
1061
1062
	/* Is the cursor in the very last position? */
1063
	width = ctx->cell->data.width;
1064
	if (ctx->ocx > wp->sx - width) {
1065
		if (ctx->xoff != 0 || wp->sx != tty->sx) {
1066
			/*
1067
			 * The pane doesn't fill the entire line, the linefeed
1068
			 * will already have happened, so just move the cursor.
1069
			 */
1070
			if (ctx->ocy != wp->yoff + wp->screen->rlower)
1071
				tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1);
1072
			else
1073
				tty_cursor_pane(tty, ctx, 0, ctx->ocy);
1074
		} else if (tty->cx < tty->sx) {
1075
			/*
1076
			 * The cursor isn't in the last position already, so
1077
			 * move as far left as possible and redraw the last
1078
			 * cell to move into the last position.
1079
			 */
1080
			cx = screen_size_x(s) - ctx->last_cell.data.width;
1081
			tty_cursor_pane(tty, ctx, cx, ctx->ocy);
1082
			tty_cell(tty, &ctx->last_cell, wp);
1083
		}
1084
	} else
1085
		tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy);
1086
1087
	tty_cell(tty, ctx->cell, wp);
1088
}
1089
1090
void
1091
tty_cmd_utf8character(struct tty *tty, const struct tty_ctx *ctx)
1092
{
1093
	struct window_pane	*wp = ctx->wp;
1094
1095
	/*
1096
	 * Cannot rely on not being a partial character, so just redraw the
1097
	 * whole line.
1098
	 */
1099
	tty_draw_pane(tty, wp, ctx->ocy, ctx->xoff, ctx->yoff);
1100
}
1101
1102
void
1103
tty_cmd_setselection(struct tty *tty, const struct tty_ctx *ctx)
1104
{
1105
	char	*buf;
1106
	size_t	 off;
1107
1108
	if (!tty_term_has(tty->term, TTYC_MS))
1109
		return;
1110
1111
	off = 4 * ((ctx->num + 2) / 3) + 1; /* storage for base64 */
1112
	buf = xmalloc(off);
1113
1114
	b64_ntop(ctx->ptr, ctx->num, buf, off);
1115
	tty_putcode_ptr2(tty, TTYC_MS, "", buf);
1116
1117
	free(buf);
1118
}
1119
1120
void
1121
tty_cmd_rawstring(struct tty *tty, const struct tty_ctx *ctx)
1122
{
1123
	u_int	 i;
1124
	u_char	*str = ctx->ptr;
1125
1126
	for (i = 0; i < ctx->num; i++)
1127
		tty_putc(tty, str[i]);
1128
1129
	tty->cx = tty->cy = UINT_MAX;
1130
	tty->rupper = tty->rlower = UINT_MAX;
1131
1132
	tty_attributes(tty, &grid_default_cell, ctx->wp);
1133
	tty_cursor(tty, 0, 0);
1134
}
1135
1136
void
1137
tty_cell(struct tty *tty, const struct grid_cell *gc,
1138
    const struct window_pane *wp)
1139
{
1140
	u_int	i;
1141
1142
	/* Skip last character if terminal is stupid. */
1143
	if (tty->term->flags & TERM_EARLYWRAP &&
1144
	    tty->cy == tty->sy - 1 && tty->cx == tty->sx - 1)
1145
		return;
1146
1147
	/* If this is a padding character, do nothing. */
1148
	if (gc->flags & GRID_FLAG_PADDING)
1149
		return;
1150
1151
	/* Set the attributes. */
1152
	tty_attributes(tty, gc, wp);
1153
1154
	/* Get the cell and if ASCII write with putc to do ACS translation. */
1155
	if (gc->data.size == 1) {
1156
		if (*gc->data.data < 0x20 || *gc->data.data == 0x7f)
1157
			return;
1158
		tty_putc(tty, *gc->data.data);
1159
		return;
1160
	}
1161
1162
	/* If not UTF-8, write _. */
1163
	if (!(tty->flags & TTY_UTF8)) {
1164
		for (i = 0; i < gc->data.width; i++)
1165
			tty_putc(tty, '_');
1166
		return;
1167
	}
1168
1169
	/* Write the data. */
1170
	tty_putn(tty, gc->data.data, gc->data.size, gc->data.width);
1171
}
1172
1173
void
1174
tty_reset(struct tty *tty)
1175
{
1176
	struct grid_cell	*gc = &tty->cell;
1177
1178
	if (grid_cells_equal(gc, &grid_default_cell))
1179
		return;
1180
1181
	if ((gc->attr & GRID_ATTR_CHARSET) && tty_use_acs(tty))
1182
		tty_putcode(tty, TTYC_RMACS);
1183
	tty_putcode(tty, TTYC_SGR0);
1184
	memcpy(gc, &grid_default_cell, sizeof *gc);
1185
}
1186
1187
/* Set region inside pane. */
1188
void
1189
tty_region_pane(struct tty *tty, const struct tty_ctx *ctx, u_int rupper,
1190
    u_int rlower)
1191
{
1192
	tty_region(tty, ctx->yoff + rupper, ctx->yoff + rlower);
1193
}
1194
1195
/* Set region at absolute position. */
1196
void
1197
tty_region(struct tty *tty, u_int rupper, u_int rlower)
1198
{
1199
	if (tty->rlower == rlower && tty->rupper == rupper)
1200
		return;
1201
	if (!tty_term_has(tty->term, TTYC_CSR))
1202
		return;
1203
1204
	tty->rupper = rupper;
1205
	tty->rlower = rlower;
1206
1207
	/*
1208
	 * Some terminals (such as PuTTY) do not correctly reset the cursor to
1209
	 * 0,0 if it is beyond the last column (they do not reset their wrap
1210
	 * flag so further output causes a line feed). As a workaround, do an
1211
	 * explicit move to 0 first.
1212
	 */
1213
	if (tty->cx >= tty->sx)
1214
		tty_cursor(tty, 0, tty->cy);
1215
1216
	tty_putcode2(tty, TTYC_CSR, tty->rupper, tty->rlower);
1217
	tty_cursor(tty, 0, 0);
1218
}
1219
1220
/* Move cursor inside pane. */
1221
void
1222
tty_cursor_pane(struct tty *tty, const struct tty_ctx *ctx, u_int cx, u_int cy)
1223
{
1224
	tty_cursor(tty, ctx->xoff + cx, ctx->yoff + cy);
1225
}
1226
1227
/* Move cursor to absolute position. */
1228
void
1229
tty_cursor(struct tty *tty, u_int cx, u_int cy)
1230
{
1231
	struct tty_term	*term = tty->term;
1232
	u_int		 thisx, thisy;
1233
	int		 change;
1234
1235
	if (cx > tty->sx - 1)
1236
		cx = tty->sx - 1;
1237
1238
	thisx = tty->cx;
1239
	thisy = tty->cy;
1240
1241
	/* No change. */
1242
	if (cx == thisx && cy == thisy)
1243
		return;
1244
1245
	/* Very end of the line, just use absolute movement. */
1246
	if (thisx > tty->sx - 1)
1247
		goto absolute;
1248
1249
	/* Move to home position (0, 0). */
1250
	if (cx == 0 && cy == 0 && tty_term_has(term, TTYC_HOME)) {
1251
		tty_putcode(tty, TTYC_HOME);
1252
		goto out;
1253
	}
1254
1255
	/* Zero on the next line. */
1256
	if (cx == 0 && cy == thisy + 1 && thisy != tty->rlower) {
1257
		tty_putc(tty, '\r');
1258
		tty_putc(tty, '\n');
1259
		goto out;
1260
	}
1261
1262
	/* Moving column or row. */
1263
	if (cy == thisy) {
1264
		/*
1265
		 * Moving column only, row staying the same.
1266
		 */
1267
1268
		/* To left edge. */
1269
		if (cx == 0) {
1270
			tty_putc(tty, '\r');
1271
			goto out;
1272
		}
1273
1274
		/* One to the left. */
1275
		if (cx == thisx - 1 && tty_term_has(term, TTYC_CUB1)) {
1276
			tty_putcode(tty, TTYC_CUB1);
1277
			goto out;
1278
		}
1279
1280
		/* One to the right. */
1281
		if (cx == thisx + 1 && tty_term_has(term, TTYC_CUF1)) {
1282
			tty_putcode(tty, TTYC_CUF1);
1283
			goto out;
1284
		}
1285
1286
		/* Calculate difference. */
1287
		change = thisx - cx;	/* +ve left, -ve right */
1288
1289
		/*
1290
		 * Use HPA if change is larger than absolute, otherwise move
1291
		 * the cursor with CUB/CUF.
1292
		 */
1293
		if ((u_int) abs(change) > cx && tty_term_has(term, TTYC_HPA)) {
1294
			tty_putcode1(tty, TTYC_HPA, cx);
1295
			goto out;
1296
		} else if (change > 0 && tty_term_has(term, TTYC_CUB)) {
1297
			if (change == 2 && tty_term_has(term, TTYC_CUB1)) {
1298
				tty_putcode(tty, TTYC_CUB1);
1299
				tty_putcode(tty, TTYC_CUB1);
1300
				goto out;
1301
			}
1302
			tty_putcode1(tty, TTYC_CUB, change);
1303
			goto out;
1304
		} else if (change < 0 && tty_term_has(term, TTYC_CUF)) {
1305
			tty_putcode1(tty, TTYC_CUF, -change);
1306
			goto out;
1307
		}
1308
	} else if (cx == thisx) {
1309
		/*
1310
		 * Moving row only, column staying the same.
1311
		 */
1312
1313
		/* One above. */
1314
		if (thisy != tty->rupper &&
1315
		    cy == thisy - 1 && tty_term_has(term, TTYC_CUU1)) {
1316
			tty_putcode(tty, TTYC_CUU1);
1317
			goto out;
1318
		}
1319
1320
		/* One below. */
1321
		if (thisy != tty->rlower &&
1322
		    cy == thisy + 1 && tty_term_has(term, TTYC_CUD1)) {
1323
			tty_putcode(tty, TTYC_CUD1);
1324
			goto out;
1325
		}
1326
1327
		/* Calculate difference. */
1328
		change = thisy - cy;	/* +ve up, -ve down */
1329
1330
		/*
1331
		 * Try to use VPA if change is larger than absolute or if this
1332
		 * change would cross the scroll region, otherwise use CUU/CUD.
1333
		 */
1334
		if ((u_int) abs(change) > cy ||
1335
		    (change < 0 && cy - change > tty->rlower) ||
1336
		    (change > 0 && cy - change < tty->rupper)) {
1337
			    if (tty_term_has(term, TTYC_VPA)) {
1338
				    tty_putcode1(tty, TTYC_VPA, cy);
1339
				    goto out;
1340
			    }
1341
		} else if (change > 0 && tty_term_has(term, TTYC_CUU)) {
1342
			tty_putcode1(tty, TTYC_CUU, change);
1343
			goto out;
1344
		} else if (change < 0 && tty_term_has(term, TTYC_CUD)) {
1345
			tty_putcode1(tty, TTYC_CUD, -change);
1346
			goto out;
1347
		}
1348
	}
1349
1350
absolute:
1351
	/* Absolute movement. */
1352
	tty_putcode2(tty, TTYC_CUP, cy, cx);
1353
1354
out:
1355
	tty->cx = cx;
1356
	tty->cy = cy;
1357
}
1358
1359
void
1360
tty_attributes(struct tty *tty, const struct grid_cell *gc,
1361
    const struct window_pane *wp)
1362
{
1363
	struct grid_cell	*tc = &tty->cell, gc2;
1364
	u_char			 changed;
1365
1366
	memcpy(&gc2, gc, sizeof gc2);
1367
	if (wp != NULL)
1368
		tty_default_colours(&gc2, wp);
1369
1370
	/*
1371
	 * If no setab, try to use the reverse attribute as a best-effort for a
1372
	 * non-default background. This is a bit of a hack but it doesn't do
1373
	 * any serious harm and makes a couple of applications happier.
1374
	 */
1375
	if (!tty_term_has(tty->term, TTYC_SETAB)) {
1376
		if (gc2.attr & GRID_ATTR_REVERSE) {
1377
			if (gc2.fg != 7 && gc2.fg != 8)
1378
				gc2.attr &= ~GRID_ATTR_REVERSE;
1379
		} else {
1380
			if (gc2.bg != 0 && gc2.bg != 8)
1381
				gc2.attr |= GRID_ATTR_REVERSE;
1382
		}
1383
	}
1384
1385
	/* Fix up the colours if necessary. */
1386
	tty_check_fg(tty, &gc2);
1387
	tty_check_bg(tty, &gc2);
1388
1389
	/* If any bits are being cleared, reset everything. */
1390
	if (tc->attr & ~gc2.attr)
1391
		tty_reset(tty);
1392
1393
	/*
1394
	 * Set the colours. This may call tty_reset() (so it comes next) and
1395
	 * may add to (NOT remove) the desired attributes by changing new_attr.
1396
	 */
1397
	tty_colours(tty, &gc2);
1398
1399
	/* Filter out attribute bits already set. */
1400
	changed = gc2.attr & ~tc->attr;
1401
	tc->attr = gc2.attr;
1402
1403
	/* Set the attributes. */
1404
	if (changed & GRID_ATTR_BRIGHT)
1405
		tty_putcode(tty, TTYC_BOLD);
1406
	if (changed & GRID_ATTR_DIM)
1407
		tty_putcode(tty, TTYC_DIM);
1408
	if (changed & GRID_ATTR_ITALICS)
1409
		tty_set_italics(tty);
1410
	if (changed & GRID_ATTR_UNDERSCORE)
1411
		tty_putcode(tty, TTYC_SMUL);
1412
	if (changed & GRID_ATTR_BLINK)
1413
		tty_putcode(tty, TTYC_BLINK);
1414
	if (changed & GRID_ATTR_REVERSE) {
1415
		if (tty_term_has(tty->term, TTYC_REV))
1416
			tty_putcode(tty, TTYC_REV);
1417
		else if (tty_term_has(tty->term, TTYC_SMSO))
1418
			tty_putcode(tty, TTYC_SMSO);
1419
	}
1420
	if (changed & GRID_ATTR_HIDDEN)
1421
		tty_putcode(tty, TTYC_INVIS);
1422
	if ((changed & GRID_ATTR_CHARSET) && tty_use_acs(tty))
1423
		tty_putcode(tty, TTYC_SMACS);
1424
}
1425
1426
void
1427
tty_colours(struct tty *tty, const struct grid_cell *gc)
1428
{
1429
	struct grid_cell	*tc = &tty->cell;
1430
	int			 have_ax;
1431
1432
	/* No changes? Nothing is necessary. */
1433
	if (gc->fg == tc->fg && gc->bg == tc->bg)
1434
		return;
1435
1436
	/*
1437
	 * Is either the default colour? This is handled specially because the
1438
	 * best solution might be to reset both colours to default, in which
1439
	 * case if only one is default need to fall onward to set the other
1440
	 * colour.
1441
	 */
1442
	if (gc->fg == 8 || gc->bg == 8) {
1443
		/*
1444
		 * If don't have AX but do have op, send sgr0 (op can't
1445
		 * actually be used because it is sometimes the same as sgr0
1446
		 * and sometimes isn't). This resets both colours to default.
1447
		 *
1448
		 * Otherwise, try to set the default colour only as needed.
1449
		 */
1450
		have_ax = tty_term_flag(tty->term, TTYC_AX);
1451
		if (!have_ax && tty_term_has(tty->term, TTYC_OP))
1452
			tty_reset(tty);
1453
		else {
1454
			if (gc->fg == 8 && tc->fg != 8) {
1455
				if (have_ax)
1456
					tty_puts(tty, "\033[39m");
1457
				else if (tc->fg != 7)
1458
					tty_putcode1(tty, TTYC_SETAF, 7);
1459
				tc->fg = 8;
1460
			}
1461
			if (gc->bg == 8 && tc->bg != 8) {
1462
				if (have_ax)
1463
					tty_puts(tty, "\033[49m");
1464
				else if (tc->bg != 0)
1465
					tty_putcode1(tty, TTYC_SETAB, 0);
1466
				tc->bg = 8;
1467
			}
1468
		}
1469
	}
1470
1471
	/* Set the foreground colour. */
1472
	if (gc->fg != 8 && gc->fg != tc->fg)
1473
		tty_colours_fg(tty, gc);
1474
1475
	/*
1476
	 * Set the background colour. This must come after the foreground as
1477
	 * tty_colour_fg() can call tty_reset().
1478
	 */
1479
	if (gc->bg != 8 && gc->bg != tc->bg)
1480
		tty_colours_bg(tty, gc);
1481
}
1482
1483
void
1484
tty_check_fg(struct tty *tty, struct grid_cell *gc)
1485
{
1486
	u_char	r, g, b;
1487
	u_int	colours;
1488
1489
	/* Is this a 24-bit colour? */
1490
	if (gc->fg & COLOUR_FLAG_RGB) {
1491
		/* Not a 24-bit terminal? Translate to 256-colour palette. */
1492
		if (!tty_term_flag(tty->term, TTYC_TC)) {
1493
			colour_split_rgb(gc->fg, &r, &g, &b);
1494
			gc->fg = colour_find_rgb(r, g, b);
1495
		} else
1496
			return;
1497
	}
1498
	colours = tty_term_number(tty->term, TTYC_COLORS);
1499
1500
	/* Is this a 256-colour colour? */
1501
	if (gc->fg & COLOUR_FLAG_256) {
1502
		/* And not a 256 colour mode? */
1503
		if (!(tty->term->flags & TERM_256COLOURS) &&
1504
		    !(tty->term_flags & TERM_256COLOURS)) {
1505
			gc->fg = colour_256to16(gc->fg);
1506
			if (gc->fg & 8) {
1507
				gc->fg &= 7;
1508
				if (colours >= 16)
1509
					gc->fg += 90;
1510
				else
1511
					gc->attr |= GRID_ATTR_BRIGHT;
1512
			} else
1513
				gc->attr &= ~GRID_ATTR_BRIGHT;
1514
		}
1515
		return;
1516
	}
1517
1518
	/* Is this an aixterm colour? */
1519
	if (gc->fg >= 90 && gc->fg <= 97 && colours < 16) {
1520
		gc->fg -= 90;
1521
		gc->attr |= GRID_ATTR_BRIGHT;
1522
	}
1523
}
1524
1525
void
1526
tty_check_bg(struct tty *tty, struct grid_cell *gc)
1527
{
1528
	u_char	r, g, b;
1529
	u_int	colours;
1530
1531
	/* Is this a 24-bit colour? */
1532
	if (gc->bg & COLOUR_FLAG_RGB) {
1533
		/* Not a 24-bit terminal? Translate to 256-colour palette. */
1534
		if (!tty_term_flag(tty->term, TTYC_TC)) {
1535
			colour_split_rgb(gc->bg, &r, &g, &b);
1536
			gc->bg = colour_find_rgb(r, g, b);
1537
		} else
1538
			return;
1539
	}
1540
	colours = tty_term_number(tty->term, TTYC_COLORS);
1541
1542
	/* Is this a 256-colour colour? */
1543
	if (gc->bg & COLOUR_FLAG_256) {
1544
		/*
1545
		 * And not a 256 colour mode? Translate to 16-colour
1546
		 * palette. Bold background doesn't exist portably, so just
1547
		 * discard the bold bit if set.
1548
		 */
1549
		if (!(tty->term->flags & TERM_256COLOURS) &&
1550
		    !(tty->term_flags & TERM_256COLOURS)) {
1551
			gc->bg = colour_256to16(gc->bg);
1552
			if (gc->bg & 8) {
1553
				gc->bg &= 7;
1554
				if (colours >= 16)
1555
					gc->fg += 90;
1556
			}
1557
		}
1558
		return;
1559
	}
1560
1561
	/* Is this an aixterm colour? */
1562
	if (gc->bg >= 90 && gc->bg <= 97 && colours < 16)
1563
		gc->bg -= 90;
1564
}
1565
1566
void
1567
tty_colours_fg(struct tty *tty, const struct grid_cell *gc)
1568
{
1569
	struct grid_cell	*tc = &tty->cell;
1570
	char			 s[32];
1571
1572
	/* Is this a 24-bit or 256-colour colour? */
1573
	if (gc->fg & COLOUR_FLAG_RGB ||
1574
	    gc->fg & COLOUR_FLAG_256) {
1575
		if (tty_try_colour(tty, gc->fg, "38") == 0)
1576
			goto save_fg;
1577
		/* Should not get here, already converted in tty_check_fg. */
1578
		return;
1579
	}
1580
1581
	/* Is this an aixterm bright colour? */
1582
	if (gc->fg >= 90 && gc->fg <= 97) {
1583
		xsnprintf(s, sizeof s, "\033[%dm", gc->fg);
1584
		tty_puts(tty, s);
1585
		goto save_fg;
1586
	}
1587
1588
	/* Otherwise set the foreground colour. */
1589
	tty_putcode1(tty, TTYC_SETAF, gc->fg);
1590
1591
save_fg:
1592
	/* Save the new values in the terminal current cell. */
1593
	tc->fg = gc->fg;
1594
}
1595
1596
void
1597
tty_colours_bg(struct tty *tty, const struct grid_cell *gc)
1598
{
1599
	struct grid_cell	*tc = &tty->cell;
1600
	char			 s[32];
1601
1602
	/* Is this a 24-bit or 256-colour colour? */
1603
	if (gc->bg & COLOUR_FLAG_RGB ||
1604
	    gc->bg & COLOUR_FLAG_256) {
1605
		if (tty_try_colour(tty, gc->bg, "48") == 0)
1606
			goto save_bg;
1607
		/* Should not get here, already converted in tty_check_bg. */
1608
		return;
1609
	}
1610
1611
	/* Is this an aixterm bright colour? */
1612
	if (gc->bg >= 90 && gc->bg <= 97) {
1613
		xsnprintf(s, sizeof s, "\033[%dm", gc->bg + 10);
1614
		tty_puts(tty, s);
1615
		goto save_bg;
1616
	}
1617
1618
	/* Otherwise set the background colour. */
1619
	tty_putcode1(tty, TTYC_SETAB, gc->bg);
1620
1621
save_bg:
1622
	/* Save the new values in the terminal current cell. */
1623
	tc->bg = gc->bg;
1624
}
1625
1626
int
1627
tty_try_colour(struct tty *tty, int colour, const char *type)
1628
{
1629
	u_char	r, g, b;
1630
	char	s[32];
1631
1632
	if (colour & COLOUR_FLAG_256) {
1633
		/*
1634
		 * If the user has specified -2 to the client, setaf and setab
1635
		 * may not work (or they may not want to use them), so send the
1636
		 * usual sequence.
1637
		 */
1638
		if (tty->term_flags & TERM_256COLOURS)
1639
			goto fallback_256;
1640
1641
		/*
1642
		 * If the terminfo entry has 256 colours and setaf and setab
1643
		 * exist, assume that they work correctly.
1644
		 */
1645
		if (tty->term->flags & TERM_256COLOURS) {
1646
			if (*type == '3') {
1647
				if (!tty_term_has(tty->term, TTYC_SETAF))
1648
					goto fallback_256;
1649
				tty_putcode1(tty, TTYC_SETAF, colour & 0xff);
1650
			} else {
1651
				if (!tty_term_has(tty->term, TTYC_SETAB))
1652
					goto fallback_256;
1653
				tty_putcode1(tty, TTYC_SETAB, colour & 0xff);
1654
			}
1655
			return (0);
1656
		}
1657
		goto fallback_256;
1658
	}
1659
1660
	if (colour & COLOUR_FLAG_RGB) {
1661
		if (!tty_term_flag(tty->term, TTYC_TC))
1662
			return (-1);
1663
1664
		colour_split_rgb(colour & 0xffffff, &r, &g, &b);
1665
		xsnprintf(s, sizeof s, "\033[%s;2;%hhu;%hhu;%hhum", type,
1666
		    r, g, b);
1667
		tty_puts(tty, s);
1668
		return (0);
1669
	}
1670
1671
	return (-1);
1672
1673
fallback_256:
1674
	xsnprintf(s, sizeof s, "\033[%s;5;%dm", type, colour & 0xff);
1675
	tty_puts(tty, s);
1676
	return (0);
1677
}
1678
1679
void
1680
tty_default_colours(struct grid_cell *gc, const struct window_pane *wp)
1681
{
1682
	struct window		*w = wp->window;
1683
	struct options		*oo = w->options;
1684
	const struct grid_cell	*agc, *pgc, *wgc;
1685
1686
	if (w->flags & WINDOW_STYLECHANGED) {
1687
		w->flags &= ~WINDOW_STYLECHANGED;
1688
		agc = options_get_style(oo, "window-active-style");
1689
		memcpy(&w->active_style, agc, sizeof w->active_style);
1690
		wgc = options_get_style(oo, "window-style");
1691
		memcpy(&w->style, wgc, sizeof w->style);
1692
	} else {
1693
		agc = &w->active_style;
1694
		wgc = &w->style;
1695
	}
1696
	pgc = &wp->colgc;
1697
1698
	if (gc->fg == 8) {
1699
		if (pgc->fg != 8)
1700
			gc->fg = pgc->fg;
1701
		else if (wp == w->active && agc->fg != 8)
1702
			gc->fg = agc->fg;
1703
		else
1704
			gc->fg = wgc->fg;
1705
	}
1706
1707
	if (gc->bg == 8) {
1708
		if (pgc->bg != 8)
1709
			gc->bg = pgc->bg;
1710
		else if (wp == w->active && agc->bg != 8)
1711
			gc->bg = agc->bg;
1712
		else
1713
			gc->bg = wgc->bg;
1714
	}
1715
}