GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/screen-redraw.c Lines: 0 276 0.0 %
Date: 2016-12-06 Branches: 0 256 0.0 %

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