GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/screen-redraw.c Lines: 0 296 0.0 %
Date: 2017-11-07 Branches: 0 260 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: screen-redraw.c,v 1.46 2017/05/01 12:20:55 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
21
#include <string.h>
22
23
#include "tmux.h"
24
25
static int	screen_redraw_cell_border1(struct window_pane *, u_int, u_int);
26
static int	screen_redraw_cell_border(struct client *, u_int, u_int);
27
static int	screen_redraw_check_cell(struct client *, u_int, u_int, int,
28
		    struct window_pane **);
29
static int	screen_redraw_check_is(u_int, u_int, int, int, struct window *,
30
		    struct window_pane *, struct window_pane *);
31
32
static int 	screen_redraw_make_pane_status(struct client *, struct window *,
33
		    struct window_pane *);
34
static void	screen_redraw_draw_pane_status(struct client *, int);
35
36
static void	screen_redraw_draw_borders(struct client *, int, int, u_int);
37
static void	screen_redraw_draw_panes(struct client *, u_int);
38
static void	screen_redraw_draw_status(struct client *, u_int);
39
static void	screen_redraw_draw_number(struct client *, struct window_pane *,
40
		    u_int);
41
42
#define CELL_INSIDE 0
43
#define CELL_LEFTRIGHT 1
44
#define CELL_TOPBOTTOM 2
45
#define CELL_TOPLEFT 3
46
#define CELL_TOPRIGHT 4
47
#define CELL_BOTTOMLEFT 5
48
#define CELL_BOTTOMRIGHT 6
49
#define CELL_TOPJOIN 7
50
#define CELL_BOTTOMJOIN 8
51
#define CELL_LEFTJOIN 9
52
#define CELL_RIGHTJOIN 10
53
#define CELL_JOIN 11
54
#define CELL_OUTSIDE 12
55
56
#define CELL_BORDERS " xqlkmjwvtun~"
57
58
#define CELL_STATUS_OFF 0
59
#define CELL_STATUS_TOP 1
60
#define CELL_STATUS_BOTTOM 2
61
62
/* Check if cell is on the border of a particular pane. */
63
static int
64
screen_redraw_cell_border1(struct window_pane *wp, u_int px, u_int py)
65
{
66
	/* Inside pane. */
67
	if (px >= wp->xoff && px < wp->xoff + wp->sx &&
68
	    py >= wp->yoff && py < wp->yoff + wp->sy)
69
		return (0);
70
71
	/* Left/right borders. */
72
	if ((wp->yoff == 0 || py >= wp->yoff - 1) && py <= wp->yoff + wp->sy) {
73
		if (wp->xoff != 0 && px == wp->xoff - 1)
74
			return (1);
75
		if (px == wp->xoff + wp->sx)
76
			return (2);
77
	}
78
79
	/* Top/bottom borders. */
80
	if ((wp->xoff == 0 || px >= wp->xoff - 1) && px <= wp->xoff + wp->sx) {
81
		if (wp->yoff != 0 && py == wp->yoff - 1)
82
			return (3);
83
		if (py == wp->yoff + wp->sy)
84
			return (4);
85
	}
86
87
	/* Outside pane. */
88
	return (-1);
89
}
90
91
/* Check if a cell is on the pane border. */
92
static int
93
screen_redraw_cell_border(struct client *c, u_int px, u_int py)
94
{
95
	struct window		*w = c->session->curw->window;
96
	struct window_pane	*wp;
97
	int			 retval;
98
99
	/* Check all the panes. */
100
	TAILQ_FOREACH(wp, &w->panes, entry) {
101
		if (!window_pane_visible(wp))
102
			continue;
103
		if ((retval = screen_redraw_cell_border1(wp, px, py)) != -1)
104
			return (!!retval);
105
	}
106
107
	return (0);
108
}
109
110
/* Check if cell inside a pane. */
111
static int
112
screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status,
113
    struct window_pane **wpp)
114
{
115
	struct window		*w = c->session->curw->window;
116
	struct window_pane	*wp;
117
	int			 borders;
118
	u_int			 right, line;
119
120
	*wpp = NULL;
121
122
	if (px > w->sx || py > w->sy)
123
		return (CELL_OUTSIDE);
124
125
	if (pane_status != CELL_STATUS_OFF) {
126
		TAILQ_FOREACH(wp, &w->panes, entry) {
127
			if (!window_pane_visible(wp))
128
				continue;
129
130
			if (pane_status == CELL_STATUS_TOP)
131
				line = wp->yoff - 1;
132
			else
133
				line = wp->yoff + wp->sy;
134
			right = wp->xoff + 2 + wp->status_size - 1;
135
136
			if (py == line && px >= wp->xoff + 2 && px <= right)
137
				return (CELL_INSIDE);
138
		}
139
	}
140
141
	TAILQ_FOREACH(wp, &w->panes, entry) {
142
		if (!window_pane_visible(wp))
143
			continue;
144
		*wpp = wp;
145
146
		/* If outside the pane and its border, skip it. */
147
		if ((wp->xoff != 0 && px < wp->xoff - 1) ||
148
		    px > wp->xoff + wp->sx ||
149
		    (wp->yoff != 0 && py < wp->yoff - 1) ||
150
		    py > wp->yoff + wp->sy)
151
			continue;
152
153
		/* If definitely inside, return so. */
154
		if (!screen_redraw_cell_border(c, px, py))
155
			return (CELL_INSIDE);
156
157
		/*
158
		 * Construct a bitmask of whether the cells to the left (bit
159
		 * 4), right, top, and bottom (bit 1) of this cell are borders.
160
		 */
161
		borders = 0;
162
		if (px == 0 || screen_redraw_cell_border(c, px - 1, py))
163
			borders |= 8;
164
		if (px <= w->sx && screen_redraw_cell_border(c, px + 1, py))
165
			borders |= 4;
166
		if (pane_status == CELL_STATUS_TOP) {
167
			if (py != 0 && screen_redraw_cell_border(c, px, py - 1))
168
				borders |= 2;
169
		} else {
170
			if (py == 0 || screen_redraw_cell_border(c, px, py - 1))
171
				borders |= 2;
172
		}
173
		if (py <= w->sy && screen_redraw_cell_border(c, px, py + 1))
174
			borders |= 1;
175
176
		/*
177
		 * Figure out what kind of border this cell is. Only one bit
178
		 * set doesn't make sense (can't have a border cell with no
179
		 * others connected).
180
		 */
181
		switch (borders) {
182
		case 15:	/* 1111, left right top bottom */
183
			return (CELL_JOIN);
184
		case 14:	/* 1110, left right top */
185
			return (CELL_BOTTOMJOIN);
186
		case 13:	/* 1101, left right bottom */
187
			return (CELL_TOPJOIN);
188
		case 12:	/* 1100, left right */
189
			return (CELL_TOPBOTTOM);
190
		case 11:	/* 1011, left top bottom */
191
			return (CELL_RIGHTJOIN);
192
		case 10:	/* 1010, left top */
193
			return (CELL_BOTTOMRIGHT);
194
		case 9:		/* 1001, left bottom */
195
			return (CELL_TOPRIGHT);
196
		case 7:		/* 0111, right top bottom */
197
			return (CELL_LEFTJOIN);
198
		case 6:		/* 0110, right top */
199
			return (CELL_BOTTOMLEFT);
200
		case 5:		/* 0101, right bottom */
201
			return (CELL_TOPLEFT);
202
		case 3:		/* 0011, top bottom */
203
			return (CELL_LEFTRIGHT);
204
		}
205
	}
206
207
	return (CELL_OUTSIDE);
208
}
209
210
/* Check if the border of a particular pane. */
211
static int
212
screen_redraw_check_is(u_int px, u_int py, int type, int pane_status,
213
    struct window *w, struct window_pane *wantwp, struct window_pane *wp)
214
{
215
	int	border;
216
217
	/* Is this off the active pane border? */
218
	border = screen_redraw_cell_border1(wantwp, px, py);
219
	if (border == 0 || border == -1)
220
		return (0);
221
	if (pane_status == CELL_STATUS_TOP && border == 4)
222
		return (0);
223
	if (pane_status == CELL_STATUS_BOTTOM && border == 3)
224
		return (0);
225
226
	/* If there are more than two panes, that's enough. */
227
	if (window_count_panes(w) != 2)
228
		return (1);
229
230
	/* Else if the cell is not a border cell, forget it. */
231
	if (wp == NULL || (type == CELL_OUTSIDE || type == CELL_INSIDE))
232
		return (1);
233
234
	/* With status lines mark the entire line. */
235
	if (pane_status != CELL_STATUS_OFF)
236
		return (1);
237
238
	/* Check if the pane covers the whole width. */
239
	if (wp->xoff == 0 && wp->sx == w->sx) {
240
		/* This can either be the top pane or the bottom pane. */
241
		if (wp->yoff == 0) { /* top pane */
242
			if (wp == wantwp)
243
				return (px <= wp->sx / 2);
244
			return (px > wp->sx / 2);
245
		}
246
		return (0);
247
	}
248
249
	/* Check if the pane covers the whole height. */
250
	if (wp->yoff == 0 && wp->sy == w->sy) {
251
		/* This can either be the left pane or the right pane. */
252
		if (wp->xoff == 0) { /* left pane */
253
			if (wp == wantwp)
254
				return (py <= wp->sy / 2);
255
			return (py > wp->sy / 2);
256
		}
257
		return (0);
258
	}
259
260
	return (1);
261
}
262
263
/* Update pane status. */
264
static int
265
screen_redraw_make_pane_status(struct client *c, struct window *w,
266
    struct window_pane *wp)
267
{
268
	struct grid_cell	 gc;
269
	const char		*fmt;
270
	struct format_tree	*ft;
271
	char			*out;
272
	size_t			 outlen;
273
	struct screen_write_ctx	 ctx;
274
	struct screen		 old;
275
276
	if (wp == w->active)
277
		style_apply(&gc, w->options, "pane-active-border-style");
278
	else
279
		style_apply(&gc, w->options, "pane-border-style");
280
281
	fmt = options_get_string(w->options, "pane-border-format");
282
283
	ft = format_create(c, NULL, FORMAT_PANE|wp->id, 0);
284
	format_defaults(ft, c, NULL, NULL, wp);
285
286
	memcpy(&old, &wp->status_screen, sizeof old);
287
	screen_init(&wp->status_screen, wp->sx, 1, 0);
288
	wp->status_screen.mode = 0;
289
290
	out = format_expand(ft, fmt);
291
	outlen = screen_write_cstrlen("%s", out);
292
	if (outlen > wp->sx - 4)
293
		outlen = wp->sx - 4;
294
	screen_resize(&wp->status_screen, outlen, 1, 0);
295
296
	screen_write_start(&ctx, NULL, &wp->status_screen);
297
	screen_write_cursormove(&ctx, 0, 0);
298
	screen_write_clearline(&ctx, 8);
299
	screen_write_cnputs(&ctx, outlen, &gc, "%s", out);
300
	screen_write_stop(&ctx);
301
302
	format_free(ft);
303
304
	wp->status_size = outlen;
305
306
	if (grid_compare(wp->status_screen.grid, old.grid) == 0) {
307
		screen_free(&old);
308
		return (0);
309
	}
310
	screen_free(&old);
311
	return (1);
312
}
313
314
/* Draw pane status. */
315
static void
316
screen_redraw_draw_pane_status(struct client *c, int pane_status)
317
{
318
	struct window		*w = c->session->curw->window;
319
	struct options		*oo = c->session->options;
320
	struct tty		*tty = &c->tty;
321
	struct window_pane	*wp;
322
	int			 spos;
323
	u_int			 yoff;
324
325
	spos = options_get_number(oo, "status-position");
326
	TAILQ_FOREACH(wp, &w->panes, entry) {
327
		if (!window_pane_visible(wp))
328
			continue;
329
		if (pane_status == CELL_STATUS_TOP)
330
			yoff = wp->yoff - 1;
331
		else
332
			yoff = wp->yoff + wp->sy;
333
		if (spos == 0)
334
			yoff += 1;
335
336
		tty_draw_line(tty, NULL, &wp->status_screen, 0, wp->xoff + 2,
337
		    yoff);
338
	}
339
	tty_cursor(tty, 0, 0);
340
}
341
342
/* Update status line and change flags if unchanged. */
343
void
344
screen_redraw_update(struct client *c)
345
{
346
	struct window		*w = c->session->curw->window;
347
	struct window_pane	*wp;
348
	struct options		*wo = w->options;
349
	int			 redraw;
350
351
	if (c->message_string != NULL)
352
		redraw = status_message_redraw(c);
353
	else if (c->prompt_string != NULL)
354
		redraw = status_prompt_redraw(c);
355
	else
356
		redraw = status_redraw(c);
357
	if (!redraw)
358
		c->flags &= ~CLIENT_STATUS;
359
360
	if (options_get_number(wo, "pane-border-status") != CELL_STATUS_OFF) {
361
		redraw = 0;
362
		TAILQ_FOREACH(wp, &w->panes, entry) {
363
			if (screen_redraw_make_pane_status(c, w, wp))
364
				redraw = 1;
365
		}
366
		if (redraw)
367
			c->flags |= CLIENT_BORDERS;
368
	}
369
}
370
371
/* Redraw entire screen. */
372
void
373
screen_redraw_screen(struct client *c, int draw_panes, int draw_status,
374
    int draw_borders)
375
{
376
	struct options		*oo = c->session->options;
377
	struct tty		*tty = &c->tty;
378
	struct window		*w = c->session->curw->window;
379
	struct options		*wo = w->options;
380
	u_int			 top;
381
	int	 		 status, pane_status, spos;
382
383
	/* Suspended clients should not be updated. */
384
	if (c->flags & CLIENT_SUSPENDED)
385
		return;
386
387
	/* Get status line, er, status. */
388
	spos = options_get_number(oo, "status-position");
389
	if (c->message_string != NULL || c->prompt_string != NULL)
390
		status = 1;
391
	else
392
		status = options_get_number(oo, "status");
393
	top = 0;
394
	if (status && spos == 0)
395
		top = 1;
396
	if (!status)
397
		draw_status = 0;
398
399
	/* Draw the elements. */
400
	if (draw_borders) {
401
		pane_status = options_get_number(wo, "pane-border-status");
402
		screen_redraw_draw_borders(c, status, pane_status, top);
403
		if (pane_status != CELL_STATUS_OFF)
404
			screen_redraw_draw_pane_status(c, pane_status);
405
	}
406
	if (draw_panes)
407
		screen_redraw_draw_panes(c, top);
408
	if (draw_status)
409
		screen_redraw_draw_status(c, top);
410
	tty_reset(tty);
411
}
412
413
/* Draw a single pane. */
414
void
415
screen_redraw_pane(struct client *c, struct window_pane *wp)
416
{
417
	u_int	i, yoff;
418
419
	if (!window_pane_visible(wp))
420
		return;
421
422
	yoff = wp->yoff;
423
	if (status_at_line(c) == 0)
424
		yoff++;
425
426
	log_debug("%s: redraw pane %%%u (at %u,%u)", c->name, wp->id,
427
	    wp->xoff, yoff);
428
429
	for (i = 0; i < wp->sy; i++)
430
		tty_draw_pane(&c->tty, wp, i, wp->xoff, yoff);
431
	tty_reset(&c->tty);
432
}
433
434
/* Draw the borders. */
435
static void
436
screen_redraw_draw_borders(struct client *c, int status, int pane_status,
437
    u_int top)
438
{
439
	struct session		*s = c->session;
440
	struct window		*w = s->curw->window;
441
	struct options		*oo = w->options;
442
	struct tty		*tty = &c->tty;
443
	struct window_pane	*wp;
444
	struct grid_cell	 m_active_gc, active_gc, m_other_gc, other_gc;
445
	struct grid_cell	 msg_gc;
446
	u_int		 	 i, j, type, msgx = 0, msgy = 0;
447
	int			 active, small, flags;
448
	char			 msg[256];
449
	const char		*tmp;
450
	size_t			 msglen = 0;
451
452
	small = (tty->sy - status + top > w->sy) || (tty->sx > w->sx);
453
	if (small) {
454
		flags = w->flags & (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT);
455
		if (flags == (WINDOW_FORCEWIDTH|WINDOW_FORCEHEIGHT))
456
			tmp = "force-width, force-height";
457
		else if (flags == WINDOW_FORCEWIDTH)
458
			tmp = "force-width";
459
		else if (flags == WINDOW_FORCEHEIGHT)
460
			tmp = "force-height";
461
		else
462
			tmp = "a smaller client";
463
		xsnprintf(msg, sizeof msg, "(size %ux%u from %s)",
464
		    w->sx, w->sy, tmp);
465
		msglen = strlen(msg);
466
467
		if (tty->sy - 1 - status + top > w->sy && tty->sx >= msglen) {
468
			msgx = tty->sx - msglen;
469
			msgy = tty->sy - 1 - status + top;
470
		} else if (tty->sx - w->sx > msglen) {
471
			msgx = tty->sx - msglen;
472
			msgy = tty->sy - 1 - status + top;
473
		} else
474
			small = 0;
475
	}
476
477
	style_apply(&other_gc, oo, "pane-border-style");
478
	style_apply(&active_gc, oo, "pane-active-border-style");
479
	active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET;
480
481
	memcpy(&m_other_gc, &other_gc, sizeof m_other_gc);
482
	m_other_gc.attr ^= GRID_ATTR_REVERSE;
483
	memcpy(&m_active_gc, &active_gc, sizeof m_active_gc);
484
	m_active_gc.attr ^= GRID_ATTR_REVERSE;
485
486
	for (j = 0; j < tty->sy - status; j++) {
487
		for (i = 0; i < tty->sx; i++) {
488
			type = screen_redraw_check_cell(c, i, j, pane_status,
489
			    &wp);
490
			if (type == CELL_INSIDE)
491
				continue;
492
			if (type == CELL_OUTSIDE && small &&
493
			    i > msgx && j == msgy)
494
				continue;
495
			active = screen_redraw_check_is(i, j, type, pane_status,
496
			    w, w->active, wp);
497
			if (server_is_marked(s, s->curw, marked_pane.wp) &&
498
			    screen_redraw_check_is(i, j, type, pane_status, w,
499
			    marked_pane.wp, wp)) {
500
				if (active)
501
					tty_attributes(tty, &m_active_gc, NULL);
502
				else
503
					tty_attributes(tty, &m_other_gc, NULL);
504
			} else if (active)
505
				tty_attributes(tty, &active_gc, NULL);
506
			else
507
				tty_attributes(tty, &other_gc, NULL);
508
			tty_cursor(tty, i, top + j);
509
			tty_putc(tty, CELL_BORDERS[type]);
510
		}
511
	}
512
513
	if (small) {
514
		memcpy(&msg_gc, &grid_default_cell, sizeof msg_gc);
515
		tty_attributes(tty, &msg_gc, NULL);
516
		tty_cursor(tty, msgx, msgy);
517
		tty_puts(tty, msg);
518
	}
519
}
520
521
/* Draw the panes. */
522
static void
523
screen_redraw_draw_panes(struct client *c, u_int top)
524
{
525
	struct window		*w = c->session->curw->window;
526
	struct tty		*tty = &c->tty;
527
	struct window_pane	*wp;
528
	u_int		 	 i;
529
530
	TAILQ_FOREACH(wp, &w->panes, entry) {
531
		if (!window_pane_visible(wp))
532
			continue;
533
		for (i = 0; i < wp->sy; i++)
534
			tty_draw_pane(tty, wp, i, wp->xoff, top + wp->yoff);
535
		if (c->flags & CLIENT_IDENTIFY)
536
			screen_redraw_draw_number(c, wp, top);
537
	}
538
}
539
540
/* Draw the status line. */
541
static void
542
screen_redraw_draw_status(struct client *c, u_int top)
543
{
544
	struct tty	*tty = &c->tty;
545
546
	if (top)
547
		tty_draw_line(tty, NULL, &c->status, 0, 0, 0);
548
	else
549
		tty_draw_line(tty, NULL, &c->status, 0, 0, tty->sy - 1);
550
}
551
552
/* Draw number on a pane. */
553
static void
554
screen_redraw_draw_number(struct client *c, struct window_pane *wp, u_int top)
555
{
556
	struct tty		*tty = &c->tty;
557
	struct session		*s = c->session;
558
	struct options		*oo = s->options;
559
	struct window		*w = wp->window;
560
	struct grid_cell	 gc;
561
	u_int			 idx, px, py, i, j, xoff, yoff;
562
	int			 colour, active_colour;
563
	char			 buf[16], *ptr;
564
	size_t			 len;
565
566
	if (window_pane_index(wp, &idx) != 0)
567
		fatalx("index not found");
568
	len = xsnprintf(buf, sizeof buf, "%u", idx);
569
570
	if (wp->sx < len)
571
		return;
572
	colour = options_get_number(oo, "display-panes-colour");
573
	active_colour = options_get_number(oo, "display-panes-active-colour");
574
575
	px = wp->sx / 2; py = wp->sy / 2;
576
	xoff = wp->xoff; yoff = wp->yoff;
577
578
	if (top)
579
		yoff++;
580
581
	if (wp->sx < len * 6 || wp->sy < 5) {
582
		tty_cursor(tty, xoff + px - len / 2, yoff + py);
583
		goto draw_text;
584
	}
585
586
	px -= len * 3;
587
	py -= 2;
588
589
	memcpy(&gc, &grid_default_cell, sizeof gc);
590
	if (w->active == wp)
591
		gc.bg = active_colour;
592
	else
593
		gc.bg = colour;
594
	gc.flags |= GRID_FLAG_NOPALETTE;
595
596
	tty_attributes(tty, &gc, wp);
597
	for (ptr = buf; *ptr != '\0'; ptr++) {
598
		if (*ptr < '0' || *ptr > '9')
599
			continue;
600
		idx = *ptr - '0';
601
602
		for (j = 0; j < 5; j++) {
603
			for (i = px; i < px + 5; i++) {
604
				tty_cursor(tty, xoff + i, yoff + py + j);
605
				if (window_clock_table[idx][j][i - px])
606
					tty_putc(tty, ' ');
607
			}
608
		}
609
		px += 6;
610
	}
611
612
	len = xsnprintf(buf, sizeof buf, "%ux%u", wp->sx, wp->sy);
613
	if (wp->sx < len || wp->sy < 6)
614
		return;
615
	tty_cursor(tty, xoff + wp->sx - len, yoff);
616
617
draw_text:
618
	memcpy(&gc, &grid_default_cell, sizeof gc);
619
	if (w->active == wp)
620
		gc.fg = active_colour;
621
	else
622
		gc.fg = colour;
623
	gc.flags |= GRID_FLAG_NOPALETTE;
624
625
	tty_attributes(tty, &gc, wp);
626
	tty_puts(tty, buf);
627
628
	tty_cursor(tty, 0, 0);
629
}