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