xref: /openbsd-src/usr.bin/tmux/screen-write.c (revision d62c220e1d6a1cf43d0b3dbfc8766135b5f7886e)
1*d62c220eSnicm /* $OpenBSD: screen-write.c,v 1.232 2024/11/16 16:49:50 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm 
217d053cf9Snicm #include <stdlib.h>
22311827fbSnicm #include <string.h>
23311827fbSnicm 
24311827fbSnicm #include "tmux.h"
25311827fbSnicm 
262612f635Snicm static struct screen_write_citem *screen_write_collect_trim(
272612f635Snicm 		    struct screen_write_ctx *, u_int, u_int, u_int, int *);
288248eab7Snicm static void	screen_write_collect_clear(struct screen_write_ctx *, u_int,
298248eab7Snicm 		    u_int);
302612f635Snicm static void	screen_write_collect_scroll(struct screen_write_ctx *, u_int);
319b53212eSnicm static void	screen_write_collect_flush(struct screen_write_ctx *, int,
329b53212eSnicm 		    const char *);
338e39655fSnicm static int	screen_write_overwrite(struct screen_write_ctx *,
348e39655fSnicm 		    struct grid_cell *, u_int);
352af49740Snicm static int	screen_write_combine(struct screen_write_ctx *,
362af49740Snicm 		    const struct grid_cell *);
37311827fbSnicm 
382612f635Snicm struct screen_write_citem {
398248eab7Snicm 	u_int				x;
40566401c6Snicm 	int				wrapped;
418248eab7Snicm 
422612f635Snicm 	enum { TEXT, CLEAR }		type;
438248eab7Snicm 	u_int				used;
448b17a0f3Snicm 	u_int				bg;
458248eab7Snicm 
468248eab7Snicm 	struct grid_cell		gc;
478248eab7Snicm 
482612f635Snicm 	TAILQ_ENTRY(screen_write_citem) entry;
498248eab7Snicm };
502612f635Snicm struct screen_write_cline {
51977b6338Snicm 	char				*data;
522612f635Snicm 	TAILQ_HEAD(, screen_write_citem) items;
538248eab7Snicm };
542612f635Snicm TAILQ_HEAD(, screen_write_citem)  screen_write_citem_freelist =
552612f635Snicm     TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist);
562612f635Snicm 
572612f635Snicm static struct screen_write_citem *
582612f635Snicm screen_write_get_citem(void)
592612f635Snicm {
602612f635Snicm     struct screen_write_citem	*ci;
612612f635Snicm 
622612f635Snicm     ci = TAILQ_FIRST(&screen_write_citem_freelist);
632612f635Snicm     if (ci != NULL) {
642612f635Snicm         TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
652612f635Snicm         memset(ci, 0, sizeof *ci);
662612f635Snicm         return (ci);
672612f635Snicm     }
682612f635Snicm     return (xcalloc(1, sizeof *ci));
692612f635Snicm }
702612f635Snicm 
712612f635Snicm static void
722612f635Snicm screen_write_free_citem(struct screen_write_citem *ci)
732612f635Snicm {
742612f635Snicm     TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
752612f635Snicm }
765fd00eceSnicm 
777b470e93Snicm static void
787b470e93Snicm screen_write_offset_timer(__unused int fd, __unused short events, void *data)
797b470e93Snicm {
807b470e93Snicm 	struct window	*w = data;
817b470e93Snicm 
827b470e93Snicm 	tty_update_window_offset(w);
837b470e93Snicm }
847b470e93Snicm 
857b470e93Snicm /* Set cursor position. */
867b470e93Snicm static void
877b470e93Snicm screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy)
887b470e93Snicm {
897b470e93Snicm 	struct window_pane	*wp = ctx->wp;
907b470e93Snicm 	struct window		*w;
917b470e93Snicm 	struct screen		*s = ctx->s;
927b470e93Snicm 	struct timeval		 tv = { .tv_usec = 10000 };
937b470e93Snicm 
947b470e93Snicm 	if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy)
957b470e93Snicm 		return;
967b470e93Snicm 
97ad47aa1bSnicm 	if (cx != -1) {
9894b5b5e9Snicm 		if ((u_int)cx > screen_size_x(s)) /* allow last column */
99ad47aa1bSnicm 			cx = screen_size_x(s) - 1;
1007b470e93Snicm 		s->cx = cx;
101ad47aa1bSnicm 	}
102ad47aa1bSnicm 	if (cy != -1) {
103ad47aa1bSnicm 		if ((u_int)cy > screen_size_y(s) - 1)
104ad47aa1bSnicm 			cy = screen_size_y(s) - 1;
1057b470e93Snicm 		s->cy = cy;
106ad47aa1bSnicm 	}
1077b470e93Snicm 
1087b470e93Snicm 	if (wp == NULL)
1097b470e93Snicm 		return;
1107b470e93Snicm 	w = wp->window;
1117b470e93Snicm 
1127b470e93Snicm 	if (!event_initialized(&w->offset_timer))
1137b470e93Snicm 		evtimer_set(&w->offset_timer, screen_write_offset_timer, w);
1147b470e93Snicm 	if (!evtimer_pending(&w->offset_timer, NULL))
1157b470e93Snicm 		evtimer_add(&w->offset_timer, &tv);
1167b470e93Snicm }
1177b470e93Snicm 
11883e83a91Snicm /* Do a full redraw. */
11983e83a91Snicm static void
12083e83a91Snicm screen_write_redraw_cb(const struct tty_ctx *ttyctx)
12183e83a91Snicm {
12283e83a91Snicm 	struct window_pane	*wp = ttyctx->arg;
12383e83a91Snicm 
1243f192e30Snicm 	if (wp != NULL)
12583e83a91Snicm 		wp->flags |= PANE_REDRAW;
12683e83a91Snicm }
12783e83a91Snicm 
12883e83a91Snicm /* Update context for client. */
12983e83a91Snicm static int
13083e83a91Snicm screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
13183e83a91Snicm {
13283e83a91Snicm 	struct window_pane	*wp = ttyctx->arg;
13383e83a91Snicm 
134fa3d3b2eSnicm 	if (ttyctx->allow_invisible_panes) {
135fa3d3b2eSnicm 		if (session_has(c->session, wp->window))
136fa3d3b2eSnicm 			return (1);
137fa3d3b2eSnicm 		return (0);
138fa3d3b2eSnicm 	}
139fa3d3b2eSnicm 
14083e83a91Snicm 	if (c->session->curw->window != wp->window)
14183e83a91Snicm 		return (0);
14283e83a91Snicm 	if (wp->layout_cell == NULL)
14383e83a91Snicm 		return (0);
14483e83a91Snicm 
14583e83a91Snicm 	if (wp->flags & (PANE_REDRAW|PANE_DROP))
14683e83a91Snicm 		return (-1);
14783e83a91Snicm 	if (c->flags & CLIENT_REDRAWPANES) {
14883e83a91Snicm 		/*
14983e83a91Snicm 		 * Redraw is already deferred to redraw another pane - redraw
15083e83a91Snicm 		 * this one also when that happens.
15183e83a91Snicm 		 */
1522612f635Snicm 		log_debug("%s: adding %%%u to deferred redraw", __func__,
1532612f635Snicm 		    wp->id);
154d2117533Snicm 		wp->flags |= (PANE_REDRAW|PANE_REDRAWSCROLLBAR);
15583e83a91Snicm 		return (-1);
15683e83a91Snicm 	}
15783e83a91Snicm 
15883e83a91Snicm 	ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy,
15983e83a91Snicm 	    &ttyctx->wsx, &ttyctx->wsy);
16083e83a91Snicm 
16183e83a91Snicm 	ttyctx->xoff = ttyctx->rxoff = wp->xoff;
16283e83a91Snicm 	ttyctx->yoff = ttyctx->ryoff = wp->yoff;
16383e83a91Snicm 
16483e83a91Snicm 	if (status_at_line(c) == 0)
16583e83a91Snicm 		ttyctx->yoff += status_line_size(c);
16683e83a91Snicm 
16783e83a91Snicm 	return (1);
16883e83a91Snicm }
16983e83a91Snicm 
1707f9a0536Snicm /* Set up context for TTY command. */
1717f9a0536Snicm static void
1727f9a0536Snicm screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
1737f9a0536Snicm     int sync)
1747f9a0536Snicm {
1757f9a0536Snicm 	struct screen	*s = ctx->s;
1767f9a0536Snicm 
1777f9a0536Snicm 	memset(ttyctx, 0, sizeof *ttyctx);
1787f9a0536Snicm 
17983e83a91Snicm 	ttyctx->s = s;
18010b45510Snicm 	ttyctx->sx = screen_size_x(s);
18110b45510Snicm 	ttyctx->sy = screen_size_y(s);
18210b45510Snicm 
1837f9a0536Snicm 	ttyctx->ocx = s->cx;
1847f9a0536Snicm 	ttyctx->ocy = s->cy;
1857f9a0536Snicm 	ttyctx->orlower = s->rlower;
1867f9a0536Snicm 	ttyctx->orupper = s->rupper;
1877f9a0536Snicm 
18833a1e283Snicm 	memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
18933a1e283Snicm 	if (ctx->init_ctx_cb != NULL) {
19083e83a91Snicm 		ctx->init_ctx_cb(ctx, ttyctx);
19133a1e283Snicm 		if (ttyctx->palette != NULL) {
192ffda99cdSnicm 			if (ttyctx->defaults.fg == 8)
19333a1e283Snicm 				ttyctx->defaults.fg = ttyctx->palette->fg;
194ffda99cdSnicm 			if (ttyctx->defaults.bg == 8)
19533a1e283Snicm 				ttyctx->defaults.bg = ttyctx->palette->bg;
19633a1e283Snicm 		}
19733a1e283Snicm 	} else {
19883e83a91Snicm 		ttyctx->redraw_cb = screen_write_redraw_cb;
19933a1e283Snicm 		if (ctx->wp != NULL) {
20033a1e283Snicm 			tty_default_colours(&ttyctx->defaults, ctx->wp);
20133a1e283Snicm 			ttyctx->palette = &ctx->wp->palette;
20283e83a91Snicm 			ttyctx->set_client_cb = screen_write_set_client_cb;
20383e83a91Snicm 			ttyctx->arg = ctx->wp;
20483e83a91Snicm 		}
20533a1e283Snicm 	}
20683e83a91Snicm 
207290f3047Snicm 	if (~ctx->flags & SCREEN_WRITE_SYNC) {
208290f3047Snicm 		/*
209290f3047Snicm 		 * For the active pane or for an overlay (no pane), we want to
210290f3047Snicm 		 * only use synchronized updates if requested (commands that
211290f3047Snicm 		 * move the cursor); for other panes, always use it, since the
212290f3047Snicm 		 * cursor will have to move.
213290f3047Snicm 		 */
214290f3047Snicm 		if (ctx->wp != NULL) {
215290f3047Snicm 			if (ctx->wp != ctx->wp->window->active)
216290f3047Snicm 				ttyctx->num = 1;
217290f3047Snicm 			else
218290f3047Snicm 				ttyctx->num = sync;
219290f3047Snicm 		} else
220290f3047Snicm 			ttyctx->num = 0x10|sync;
2217f9a0536Snicm 		tty_write(tty_cmd_syncstart, ttyctx);
2222b07bb4fSnicm 		ctx->flags |= SCREEN_WRITE_SYNC;
2237f9a0536Snicm 	}
2247f9a0536Snicm }
2257f9a0536Snicm 
226977b6338Snicm /* Make write list. */
227977b6338Snicm void
228977b6338Snicm screen_write_make_list(struct screen *s)
229977b6338Snicm {
230977b6338Snicm 	u_int	y;
231977b6338Snicm 
232977b6338Snicm 	s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list);
233977b6338Snicm 	for (y = 0; y < screen_size_y(s); y++)
234977b6338Snicm 		TAILQ_INIT(&s->write_list[y].items);
235977b6338Snicm }
236977b6338Snicm 
237977b6338Snicm /* Free write list. */
238977b6338Snicm void
239977b6338Snicm screen_write_free_list(struct screen *s)
240977b6338Snicm {
241977b6338Snicm 	u_int	y;
242977b6338Snicm 
243977b6338Snicm 	for (y = 0; y < screen_size_y(s); y++)
244977b6338Snicm 		free(s->write_list[y].data);
245977b6338Snicm 	free(s->write_list);
246977b6338Snicm }
247977b6338Snicm 
24883e83a91Snicm /* Set up for writing. */
24983e83a91Snicm static void
25083e83a91Snicm screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
251311827fbSnicm {
2528248eab7Snicm 	memset(ctx, 0, sizeof *ctx);
2535fd00eceSnicm 
254311827fbSnicm 	ctx->s = s;
2555fd00eceSnicm 
256977b6338Snicm 	if (ctx->s->write_list == NULL)
257977b6338Snicm 		screen_write_make_list(ctx->s);
2582612f635Snicm 	ctx->item = screen_write_get_citem();
2595fd00eceSnicm 
2606d5c64a0Snicm 	ctx->scrolled = 0;
2616d5c64a0Snicm 	ctx->bg = 8;
26283e83a91Snicm }
26383e83a91Snicm 
26483e83a91Snicm /* Initialize writing with a pane. */
26583e83a91Snicm void
26683e83a91Snicm screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp,
26783e83a91Snicm     struct screen *s)
26883e83a91Snicm {
26983e83a91Snicm 	if (s == NULL)
27083e83a91Snicm 		s = wp->screen;
27183e83a91Snicm 	screen_write_init(ctx, s);
27283e83a91Snicm 	ctx->wp = wp;
2736d5c64a0Snicm 
274aadd6faaSnicm 	if (log_get_level() != 0) {
275aadd6faaSnicm 		log_debug("%s: size %ux%u, pane %%%u (at %u,%u)",
27683e83a91Snicm 		    __func__, screen_size_x(ctx->s), screen_size_y(ctx->s),
27783e83a91Snicm 		    wp->id, wp->xoff, wp->yoff);
2787b470e93Snicm 	}
279aadd6faaSnicm }
28083e83a91Snicm 
28183e83a91Snicm /* Initialize writing with a callback. */
28283e83a91Snicm void
28383e83a91Snicm screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s,
28483e83a91Snicm     screen_write_init_ctx_cb cb, void *arg)
28583e83a91Snicm {
28683e83a91Snicm 	screen_write_init(ctx, s);
28783e83a91Snicm 
28883e83a91Snicm 	ctx->init_ctx_cb = cb;
28983e83a91Snicm 	ctx->arg = arg;
29083e83a91Snicm 
29183e83a91Snicm 	if (log_get_level() != 0) {
29283e83a91Snicm 		log_debug("%s: size %ux%u, with callback", __func__,
29383e83a91Snicm 		    screen_size_x(ctx->s), screen_size_y(ctx->s));
29483e83a91Snicm 	}
29583e83a91Snicm }
29683e83a91Snicm 
29783e83a91Snicm /* Initialize writing. */
29883e83a91Snicm void
29983e83a91Snicm screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
30083e83a91Snicm {
30183e83a91Snicm 	screen_write_init(ctx, s);
30283e83a91Snicm 
30383e83a91Snicm 	if (log_get_level() != 0) {
30483e83a91Snicm 		log_debug("%s: size %ux%u, no pane", __func__,
30583e83a91Snicm 		    screen_size_x(ctx->s), screen_size_y(ctx->s));
30683e83a91Snicm 	}
307311827fbSnicm }
308311827fbSnicm 
309311827fbSnicm /* Finish writing. */
310311827fbSnicm void
3115fd00eceSnicm screen_write_stop(struct screen_write_ctx *ctx)
312311827fbSnicm {
3138248eab7Snicm 	screen_write_collect_end(ctx);
3149b53212eSnicm 	screen_write_collect_flush(ctx, 0, __func__);
3155fd00eceSnicm 
3162612f635Snicm 	screen_write_free_citem(ctx->item);
317311827fbSnicm }
318311827fbSnicm 
31952e27826Snicm /* Reset screen state. */
32052e27826Snicm void
32152e27826Snicm screen_write_reset(struct screen_write_ctx *ctx)
32252e27826Snicm {
3233699ed24Snicm 	struct screen	*s = ctx->s;
32452e27826Snicm 
3253699ed24Snicm 	screen_reset_tabs(s);
3263699ed24Snicm 	screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
3276c0777c5Snicm 
328d97b4537Snicm 	s->mode = MODE_CURSOR|MODE_WRAP;
329719f5715Snicm 
330b514c1a1Snicm 	if (options_get_number(global_options, "extended-keys") == 2)
331719f5715Snicm 		s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
33252e27826Snicm 
333ae4624e7Snicm 	screen_write_clearscreen(ctx, 8);
334ad47aa1bSnicm 	screen_write_set_cursor(ctx, 0, 0);
33552e27826Snicm }
33652e27826Snicm 
337311827fbSnicm /* Write character. */
338311827fbSnicm void
33919d1250cSnicm screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
340bfcd10e2Snicm     u_char ch)
341311827fbSnicm {
34219d1250cSnicm 	struct grid_cell	gc;
34319d1250cSnicm 
34419d1250cSnicm 	memcpy(&gc, gcp, sizeof gc);
34519d1250cSnicm 
34619d1250cSnicm 	utf8_set(&gc.data, ch);
34719d1250cSnicm 	screen_write_cell(ctx, &gc);
348311827fbSnicm }
349311827fbSnicm 
350f89fbc24Snicm /* Calculate string length. */
35174d4b937Snicm size_t
352f650d6adSnicm screen_write_strlen(const char *fmt, ...)
353f89fbc24Snicm {
354f89fbc24Snicm 	va_list			ap;
355f89fbc24Snicm 	char   	       	       *msg;
3569b3c9bc5Snicm 	struct utf8_data	ud;
35740cac527Snicm 	u_char 	      	       *ptr;
358f89fbc24Snicm 	size_t			left, size = 0;
35939d4fc02Snicm 	enum utf8_state		more;
360f89fbc24Snicm 
361f89fbc24Snicm 	va_start(ap, fmt);
362f89fbc24Snicm 	xvasprintf(&msg, fmt, ap);
363f89fbc24Snicm 	va_end(ap);
364f89fbc24Snicm 
365f89fbc24Snicm 	ptr = msg;
366f89fbc24Snicm 	while (*ptr != '\0') {
36739d4fc02Snicm 		if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) {
36840cac527Snicm 			ptr++;
369f89fbc24Snicm 
370f89fbc24Snicm 			left = strlen(ptr);
371e931849fSnicm 			if (left < (size_t)ud.size - 1)
37240cac527Snicm 				break;
37339d4fc02Snicm 			while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE)
374f89fbc24Snicm 				ptr++;
37540cac527Snicm 			ptr++;
37640cac527Snicm 
37739d4fc02Snicm 			if (more == UTF8_DONE)
3789b3c9bc5Snicm 				size += ud.width;
379f89fbc24Snicm 		} else {
380c851f995Snicm 			if (*ptr == '\t' || (*ptr > 0x1f && *ptr < 0x7f))
381f89fbc24Snicm 				size++;
382f89fbc24Snicm 			ptr++;
383f89fbc24Snicm 		}
384f89fbc24Snicm 	}
385f89fbc24Snicm 
3867d053cf9Snicm 	free(msg);
387f89fbc24Snicm 	return (size);
388f89fbc24Snicm }
389f89fbc24Snicm 
39067c16a7cSnicm /* Write string wrapped over lines. */
39167c16a7cSnicm int
39267c16a7cSnicm screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width,
39367c16a7cSnicm     u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...)
39467c16a7cSnicm {
39567c16a7cSnicm 	struct screen		*s = ctx->s;
39667c16a7cSnicm 	va_list			 ap;
39767c16a7cSnicm 	char			*tmp;
39867c16a7cSnicm 	u_int			 cy = s->cy, i, end, next, idx = 0, at, left;
39967c16a7cSnicm 	struct utf8_data	*text;
40067c16a7cSnicm 	struct grid_cell	 gc;
40167c16a7cSnicm 
40267c16a7cSnicm 	memcpy(&gc, gcp, sizeof gc);
40367c16a7cSnicm 
40467c16a7cSnicm 	va_start(ap, fmt);
40567c16a7cSnicm 	xvasprintf(&tmp, fmt, ap);
40667c16a7cSnicm 	va_end(ap);
40767c16a7cSnicm 
40867c16a7cSnicm 	text = utf8_fromcstr(tmp);
40967c16a7cSnicm 	free(tmp);
41067c16a7cSnicm 
41167c16a7cSnicm 	left = (cx + width) - s->cx;
41267c16a7cSnicm 	for (;;) {
41367c16a7cSnicm 		/* Find the end of what can fit on the line. */
41467c16a7cSnicm 		at = 0;
41567c16a7cSnicm 		for (end = idx; text[end].size != 0; end++) {
41667c16a7cSnicm 			if (text[end].size == 1 && text[end].data[0] == '\n')
41767c16a7cSnicm 				break;
41867c16a7cSnicm 			if (at + text[end].width > left)
41967c16a7cSnicm 				break;
42067c16a7cSnicm 			at += text[end].width;
42167c16a7cSnicm 		}
42267c16a7cSnicm 
42367c16a7cSnicm 		/*
42467c16a7cSnicm 		 * If we're on a space, that's the end. If not, walk back to
42567c16a7cSnicm 		 * try and find one.
42667c16a7cSnicm 		 */
42767c16a7cSnicm 		if (text[end].size == 0)
42867c16a7cSnicm 			next = end;
42967c16a7cSnicm 		else if (text[end].size == 1 && text[end].data[0] == '\n')
43067c16a7cSnicm 			next = end + 1;
43167c16a7cSnicm 		else if (text[end].size == 1 && text[end].data[0] == ' ')
43267c16a7cSnicm 			next = end + 1;
43367c16a7cSnicm 		else {
43467c16a7cSnicm 			for (i = end; i > idx; i--) {
43567c16a7cSnicm 				if (text[i].size == 1 && text[i].data[0] == ' ')
43667c16a7cSnicm 					break;
43767c16a7cSnicm 			}
43867c16a7cSnicm 			if (i != idx) {
43967c16a7cSnicm 				next = i + 1;
44067c16a7cSnicm 				end = i;
44167c16a7cSnicm 			} else
44267c16a7cSnicm 				next = end;
44367c16a7cSnicm 		}
44467c16a7cSnicm 
44567c16a7cSnicm 		/* Print the line. */
44667c16a7cSnicm 		for (i = idx; i < end; i++) {
44767c16a7cSnicm 			utf8_copy(&gc.data, &text[i]);
44867c16a7cSnicm 			screen_write_cell(ctx, &gc);
44967c16a7cSnicm 		}
45067c16a7cSnicm 
45167c16a7cSnicm 		/* If at the bottom, stop. */
45267c16a7cSnicm 		idx = next;
45367c16a7cSnicm 		if (s->cy == cy + lines - 1 || text[idx].size == 0)
45467c16a7cSnicm 			break;
45567c16a7cSnicm 
45667c16a7cSnicm 		screen_write_cursormove(ctx, cx, s->cy + 1, 0);
45767c16a7cSnicm 		left = width;
45867c16a7cSnicm 	}
45967c16a7cSnicm 
46067c16a7cSnicm 	/*
46167c16a7cSnicm 	 * Fail if on the last line and there is more to come or at the end, or
46267c16a7cSnicm 	 * if the text was not entirely consumed.
46367c16a7cSnicm 	 */
46467c16a7cSnicm 	if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) ||
46567c16a7cSnicm 	    text[idx].size != 0) {
46667c16a7cSnicm 		free(text);
46767c16a7cSnicm 		return (0);
46867c16a7cSnicm 	}
46967c16a7cSnicm 	free(text);
47067c16a7cSnicm 
47167c16a7cSnicm 	/*
47267c16a7cSnicm 	 * If no more to come, move to the next line. Otherwise, leave on
47367c16a7cSnicm 	 * the same line (except if at the end).
47467c16a7cSnicm 	 */
47567c16a7cSnicm 	if (!more || s->cx == cx + width)
47667c16a7cSnicm 		screen_write_cursormove(ctx, cx, s->cy + 1, 0);
47767c16a7cSnicm 	return (1);
47867c16a7cSnicm }
47967c16a7cSnicm 
48067c16a7cSnicm /* Write simple string (no maximum length). */
48174d4b937Snicm void
48219d1250cSnicm screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
48374d4b937Snicm     const char *fmt, ...)
484311827fbSnicm {
485311827fbSnicm 	va_list	ap;
486311827fbSnicm 
487311827fbSnicm 	va_start(ap, fmt);
48819d1250cSnicm 	screen_write_vnputs(ctx, -1, gcp, fmt, ap);
489311827fbSnicm 	va_end(ap);
490f89fbc24Snicm }
491311827fbSnicm 
492f89fbc24Snicm /* Write string with length limit (-1 for unlimited). */
49374d4b937Snicm void
49474d4b937Snicm screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen,
49519d1250cSnicm     const struct grid_cell *gcp, const char *fmt, ...)
496f89fbc24Snicm {
497f89fbc24Snicm 	va_list	ap;
498f89fbc24Snicm 
499f89fbc24Snicm 	va_start(ap, fmt);
50019d1250cSnicm 	screen_write_vnputs(ctx, maxlen, gcp, fmt, ap);
501f89fbc24Snicm 	va_end(ap);
502f89fbc24Snicm }
503f89fbc24Snicm 
504f89fbc24Snicm void
505653bc467Snicm screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
50619d1250cSnicm     const struct grid_cell *gcp, const char *fmt, va_list ap)
507f89fbc24Snicm {
50819d1250cSnicm 	struct grid_cell	gc;
50919d1250cSnicm 	struct utf8_data       *ud = &gc.data;
510f89fbc24Snicm 	char   		       *msg;
51140cac527Snicm 	u_char 		       *ptr;
512f89fbc24Snicm 	size_t		 	left, size = 0;
51339d4fc02Snicm 	enum utf8_state		more;
514f89fbc24Snicm 
51519d1250cSnicm 	memcpy(&gc, gcp, sizeof gc);
516f89fbc24Snicm 	xvasprintf(&msg, fmt, ap);
517f89fbc24Snicm 
518f89fbc24Snicm 	ptr = msg;
519f89fbc24Snicm 	while (*ptr != '\0') {
52019d1250cSnicm 		if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) {
52140cac527Snicm 			ptr++;
522f89fbc24Snicm 
523f89fbc24Snicm 			left = strlen(ptr);
52419d1250cSnicm 			if (left < (size_t)ud->size - 1)
52540cac527Snicm 				break;
52619d1250cSnicm 			while ((more = utf8_append(ud, *ptr)) == UTF8_MORE)
527f89fbc24Snicm 				ptr++;
52840cac527Snicm 			ptr++;
529f89fbc24Snicm 
53019d1250cSnicm 			if (more != UTF8_DONE)
53119d1250cSnicm 				continue;
53219d1250cSnicm 			if (maxlen > 0 && size + ud->width > (size_t)maxlen) {
533f89fbc24Snicm 				while (size < (size_t)maxlen) {
53419d1250cSnicm 					screen_write_putc(ctx, &gc, ' ');
535f89fbc24Snicm 					size++;
536f89fbc24Snicm 				}
537f89fbc24Snicm 				break;
538f89fbc24Snicm 			}
53919d1250cSnicm 			size += ud->width;
54019d1250cSnicm 			screen_write_cell(ctx, &gc);
541f89fbc24Snicm 		} else {
5425af50dcaSnicm 			if (maxlen > 0 && size + 1 > (size_t)maxlen)
543f89fbc24Snicm 				break;
544f89fbc24Snicm 
545be573e22Snicm 			if (*ptr == '\001')
54619d1250cSnicm 				gc.attr ^= GRID_ATTR_CHARSET;
5476523adafSnicm 			else if (*ptr == '\n') {
5486523adafSnicm 				screen_write_linefeed(ctx, 0, 8);
5496523adafSnicm 				screen_write_carriagereturn(ctx);
550c851f995Snicm 			} else if (*ptr == '\t' || (*ptr > 0x1f && *ptr < 0x7f)) {
551f89fbc24Snicm 				size++;
55219d1250cSnicm 				screen_write_putc(ctx, &gc, *ptr);
553be573e22Snicm 			}
554f89fbc24Snicm 			ptr++;
555f89fbc24Snicm 		}
556f89fbc24Snicm 	}
557311827fbSnicm 
5587d053cf9Snicm 	free(msg);
559311827fbSnicm }
560311827fbSnicm 
5616edce865Snicm /*
5624d951239Snicm  * Copy from another screen but without the selection stuff. Assumes the target
5634d951239Snicm  * region is already big enough.
5646edce865Snicm  */
5656edce865Snicm void
5666edce865Snicm screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
5676edce865Snicm     u_int px, u_int py, u_int nx, u_int ny)
5686edce865Snicm {
5696edce865Snicm 	struct screen		*s = ctx->s;
57043ac8c28Snicm 	struct window_pane	*wp = ctx->wp;
57143ac8c28Snicm 	struct tty_ctx	 	 ttyctx;
5726edce865Snicm 	struct grid		*gd = src->grid;
5736edce865Snicm 	struct grid_cell	 gc;
57443ac8c28Snicm 	u_int		 	 xx, yy, cx = s->cx, cy = s->cy;
5756edce865Snicm 
5766edce865Snicm 	if (nx == 0 || ny == 0)
5776edce865Snicm 		return;
5786edce865Snicm 
5796edce865Snicm 	cy = s->cy;
5806edce865Snicm 	for (yy = py; yy < py + ny; yy++) {
58163c9949dSnicm 		if (yy >= gd->hsize + gd->sy)
58263c9949dSnicm 			break;
58343ac8c28Snicm 		s->cx = cx;
58443ac8c28Snicm 		if (wp != NULL)
58543ac8c28Snicm 			screen_write_initctx(ctx, &ttyctx, 0);
5866edce865Snicm 		for (xx = px; xx < px + nx; xx++) {
5877621e88fSnicm 			if (xx >= grid_get_line(gd, yy)->cellsize)
5886edce865Snicm 				break;
5896edce865Snicm 			grid_get_cell(gd, xx, yy, &gc);
590414c61adSnicm 			if (xx + gc.data.width > px + nx)
591414c61adSnicm 				break;
59243ac8c28Snicm 			grid_view_set_cell(ctx->s->grid, s->cx, s->cy, &gc);
59343ac8c28Snicm 			if (wp != NULL) {
59443ac8c28Snicm 				ttyctx.cell = &gc;
59543ac8c28Snicm 				tty_write(tty_cmd_cell, &ttyctx);
59643ac8c28Snicm 				ttyctx.ocx++;
5976edce865Snicm 			}
59843ac8c28Snicm 			s->cx++;
5996edce865Snicm 		}
60043ac8c28Snicm 		s->cy++;
60143ac8c28Snicm 	}
60243ac8c28Snicm 
60343ac8c28Snicm 	s->cx = cx;
60443ac8c28Snicm 	s->cy = cy;
6056edce865Snicm }
6066edce865Snicm 
607e340e6ccSnicm /* Select character set for drawing border lines. */
608e340e6ccSnicm static void
609e340e6ccSnicm screen_write_box_border_set(enum box_lines lines, int cell_type,
610e340e6ccSnicm     struct grid_cell *gc)
611e340e6ccSnicm {
612e340e6ccSnicm 	switch (lines) {
613e340e6ccSnicm         case BOX_LINES_NONE:
614e340e6ccSnicm 		break;
615e340e6ccSnicm         case BOX_LINES_DOUBLE:
616e340e6ccSnicm                 gc->attr &= ~GRID_ATTR_CHARSET;
617e340e6ccSnicm                 utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
618e340e6ccSnicm 		break;
619e340e6ccSnicm         case BOX_LINES_HEAVY:
620e340e6ccSnicm                 gc->attr &= ~GRID_ATTR_CHARSET;
621e340e6ccSnicm                 utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
622e340e6ccSnicm 		break;
623e340e6ccSnicm         case BOX_LINES_ROUNDED:
624e340e6ccSnicm                 gc->attr &= ~GRID_ATTR_CHARSET;
625e340e6ccSnicm                 utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
626e340e6ccSnicm 		break;
627e340e6ccSnicm         case BOX_LINES_SIMPLE:
628e340e6ccSnicm                 gc->attr &= ~GRID_ATTR_CHARSET;
629e340e6ccSnicm                 utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
630e340e6ccSnicm                 break;
631e340e6ccSnicm         case BOX_LINES_PADDED:
632e340e6ccSnicm                 gc->attr &= ~GRID_ATTR_CHARSET;
633e340e6ccSnicm                 utf8_set(&gc->data, PADDED_BORDERS[cell_type]);
634e340e6ccSnicm                 break;
635e340e6ccSnicm 	case BOX_LINES_SINGLE:
636e340e6ccSnicm 	case BOX_LINES_DEFAULT:
637e340e6ccSnicm 		gc->attr |= GRID_ATTR_CHARSET;
638e340e6ccSnicm 		utf8_set(&gc->data, CELL_BORDERS[cell_type]);
639e340e6ccSnicm 		break;
640e340e6ccSnicm 	}
641e340e6ccSnicm }
642e340e6ccSnicm 
643367b65a4Snicm /* Draw a horizontal line on screen. */
644a42faf7dSnicm void
645e340e6ccSnicm screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right,
646e340e6ccSnicm    enum box_lines lines, const struct grid_cell *border_gc)
647a42faf7dSnicm {
648a42faf7dSnicm 	struct screen		*s = ctx->s;
649a42faf7dSnicm 	struct grid_cell	 gc;
650a42faf7dSnicm 	u_int			 cx, cy, i;
651a42faf7dSnicm 
652a42faf7dSnicm 	cx = s->cx;
653a42faf7dSnicm 	cy = s->cy;
654a42faf7dSnicm 
655e340e6ccSnicm 	if (border_gc != NULL)
656e340e6ccSnicm 		memcpy(&gc, border_gc, sizeof gc);
657e340e6ccSnicm 	else
658a42faf7dSnicm 		memcpy(&gc, &grid_default_cell, sizeof gc);
659a42faf7dSnicm 	gc.attr |= GRID_ATTR_CHARSET;
660a42faf7dSnicm 
661e340e6ccSnicm 	if (left)
662e340e6ccSnicm 		screen_write_box_border_set(lines, CELL_LEFTJOIN, &gc);
663e340e6ccSnicm 	else
664e340e6ccSnicm 		screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
665e340e6ccSnicm 	screen_write_cell(ctx, &gc);
666e340e6ccSnicm 
667e340e6ccSnicm 	screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
668a42faf7dSnicm 	for (i = 1; i < nx - 1; i++)
669e340e6ccSnicm 		screen_write_cell(ctx, &gc);
670e340e6ccSnicm 
671e340e6ccSnicm 	if (right)
672e340e6ccSnicm 		screen_write_box_border_set(lines, CELL_RIGHTJOIN, &gc);
673e340e6ccSnicm 	else
674e340e6ccSnicm 		screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
675e340e6ccSnicm 	screen_write_cell(ctx, &gc);
676a42faf7dSnicm 
677ad47aa1bSnicm 	screen_write_set_cursor(ctx, cx, cy);
678a42faf7dSnicm }
679a42faf7dSnicm 
6807e55262dSnicm /* Draw a vertical line on screen. */
681367b65a4Snicm void
682367b65a4Snicm screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
683367b65a4Snicm {
684367b65a4Snicm 	struct screen		*s = ctx->s;
685367b65a4Snicm 	struct grid_cell	 gc;
686367b65a4Snicm 	u_int			 cx, cy, i;
687367b65a4Snicm 
688367b65a4Snicm 	cx = s->cx;
689367b65a4Snicm 	cy = s->cy;
690367b65a4Snicm 
691367b65a4Snicm 	memcpy(&gc, &grid_default_cell, sizeof gc);
692367b65a4Snicm 	gc.attr |= GRID_ATTR_CHARSET;
693367b65a4Snicm 
694367b65a4Snicm 	screen_write_putc(ctx, &gc, top ? 'w' : 'x');
695367b65a4Snicm 	for (i = 1; i < ny - 1; i++) {
696ad47aa1bSnicm 		screen_write_set_cursor(ctx, cx, cy + i);
697367b65a4Snicm 		screen_write_putc(ctx, &gc, 'x');
698367b65a4Snicm 	}
699ad47aa1bSnicm 	screen_write_set_cursor(ctx, cx, cy + ny - 1);
700367b65a4Snicm 	screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
701367b65a4Snicm 
702ad47aa1bSnicm 	screen_write_set_cursor(ctx, cx, cy);
703367b65a4Snicm }
704367b65a4Snicm 
7053f2ec672Snicm /* Draw a menu on screen. */
7063f2ec672Snicm void
707e340e6ccSnicm screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice,
708e340e6ccSnicm     enum box_lines lines, const struct grid_cell *menu_gc,
709e340e6ccSnicm     const struct grid_cell *border_gc, const struct grid_cell *choice_gc)
7103f2ec672Snicm {
7113f2ec672Snicm 	struct screen		*s = ctx->s;
71248a3a827Snicm 	struct grid_cell	 default_gc;
71348a3a827Snicm 	const struct grid_cell	*gc = &default_gc;
714e340e6ccSnicm 	u_int			 cx, cy, i, j, width = menu->width;
715f05a5af3Snicm 	const char		*name;
7163f2ec672Snicm 
7173f2ec672Snicm 	cx = s->cx;
7183f2ec672Snicm 	cy = s->cy;
7193f2ec672Snicm 
720e340e6ccSnicm 	memcpy(&default_gc, menu_gc, sizeof default_gc);
7213f2ec672Snicm 
722e340e6ccSnicm 	screen_write_box(ctx, menu->width + 4, menu->count + 2, lines,
723e340e6ccSnicm 	    border_gc, menu->title);
7243f2ec672Snicm 
7253f2ec672Snicm 	for (i = 0; i < menu->count; i++) {
726f05a5af3Snicm 		name = menu->items[i].name;
727f05a5af3Snicm 		if (name == NULL) {
7283f2ec672Snicm 			screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
72917d7ce67Snicm 			screen_write_hline(ctx, width + 4, 1, 1, lines,
73017d7ce67Snicm 			    border_gc);
731e340e6ccSnicm 			continue;
732e340e6ccSnicm 		}
733e340e6ccSnicm 
734f05a5af3Snicm 		if (choice >= 0 && i == (u_int)choice && *name != '-')
73548a3a827Snicm 			gc = choice_gc;
736e340e6ccSnicm 
73717d7ce67Snicm 		screen_write_cursormove(ctx, cx + 1, cy + 1 + i, 0);
73817d7ce67Snicm 		for (j = 0; j < width + 2; j++)
73948a3a827Snicm 			screen_write_putc(ctx, gc, ' ');
740e340e6ccSnicm 
7413f2ec672Snicm 		screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
742f05a5af3Snicm 		if (*name == '-') {
74348a3a827Snicm 			default_gc.attr |= GRID_ATTR_DIM;
744e340e6ccSnicm 			format_draw(ctx, gc, width, name + 1, NULL, 0);
74548a3a827Snicm 			default_gc.attr &= ~GRID_ATTR_DIM;
746e340e6ccSnicm 			continue;
7473f2ec672Snicm 		}
748e340e6ccSnicm 
74919c94b00Snicm 		format_draw(ctx, gc, width, name, NULL, 0);
750e340e6ccSnicm 		gc = &default_gc;
7513f2ec672Snicm 	}
7523f2ec672Snicm 
7533f2ec672Snicm 	screen_write_set_cursor(ctx, cx, cy);
7543f2ec672Snicm }
7553f2ec672Snicm 
756a42faf7dSnicm /* Draw a box on screen. */
757a42faf7dSnicm void
758ff8fb92bSnicm screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny,
759dad8e5c0Snicm     enum box_lines lines, const struct grid_cell *gcp, const char *title)
760a42faf7dSnicm {
761a42faf7dSnicm 	struct screen		*s = ctx->s;
762a42faf7dSnicm 	struct grid_cell         gc;
763a42faf7dSnicm 	u_int			 cx, cy, i;
764a42faf7dSnicm 
765a42faf7dSnicm 	cx = s->cx;
766a42faf7dSnicm 	cy = s->cy;
767a42faf7dSnicm 
768ff8fb92bSnicm 	if (gcp != NULL)
769ff8fb92bSnicm 		memcpy(&gc, gcp, sizeof gc);
770ff8fb92bSnicm 	else
771a42faf7dSnicm 		memcpy(&gc, &grid_default_cell, sizeof gc);
772dad8e5c0Snicm 
773a42faf7dSnicm 	gc.attr |= GRID_ATTR_CHARSET;
77433a1e283Snicm 	gc.flags |= GRID_FLAG_NOPALETTE;
775a42faf7dSnicm 
776ccb627cdSnicm 	/* Draw top border */
777dad8e5c0Snicm 	screen_write_box_border_set(lines, CELL_TOPLEFT, &gc);
778ccb627cdSnicm 	screen_write_cell(ctx, &gc);
779dad8e5c0Snicm 	screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
780a42faf7dSnicm 	for (i = 1; i < nx - 1; i++)
781ccb627cdSnicm 		screen_write_cell(ctx, &gc);
782dad8e5c0Snicm 	screen_write_box_border_set(lines, CELL_TOPRIGHT, &gc);
783ccb627cdSnicm 	screen_write_cell(ctx, &gc);
784a42faf7dSnicm 
785ccb627cdSnicm 	/* Draw bottom border */
786ad47aa1bSnicm 	screen_write_set_cursor(ctx, cx, cy + ny - 1);
787dad8e5c0Snicm 	screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc);
788ccb627cdSnicm 	screen_write_cell(ctx, &gc);
789dad8e5c0Snicm 	screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
790a42faf7dSnicm 	for (i = 1; i < nx - 1; i++)
791ccb627cdSnicm 		screen_write_cell(ctx, &gc);
792dad8e5c0Snicm 	screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc);
793ccb627cdSnicm 	screen_write_cell(ctx, &gc);
794a42faf7dSnicm 
795ccb627cdSnicm 	/* Draw sides */
796dad8e5c0Snicm 	screen_write_box_border_set(lines, CELL_TOPBOTTOM, &gc);
797a42faf7dSnicm 	for (i = 1; i < ny - 1; i++) {
798ccb627cdSnicm 		/* left side */
799ad47aa1bSnicm 		screen_write_set_cursor(ctx, cx, cy + i);
800ccb627cdSnicm 		screen_write_cell(ctx, &gc);
801ccb627cdSnicm 		/* right side */
802ad47aa1bSnicm 		screen_write_set_cursor(ctx, cx + nx - 1, cy + i);
803ccb627cdSnicm 		screen_write_cell(ctx, &gc);
804a42faf7dSnicm 	}
805a42faf7dSnicm 
806dad8e5c0Snicm 	if (title != NULL) {
807dad8e5c0Snicm 		gc.attr &= ~GRID_ATTR_CHARSET;
808dad8e5c0Snicm 		screen_write_cursormove(ctx, cx + 2, cy, 0);
809173e8225Snicm 		format_draw(ctx, &gc, nx - 4, title, NULL, 0);
810dad8e5c0Snicm 	}
811dad8e5c0Snicm 
812ad47aa1bSnicm 	screen_write_set_cursor(ctx, cx, cy);
813a42faf7dSnicm }
814a42faf7dSnicm 
8156edce865Snicm /*
8166edce865Snicm  * Write a preview version of a window. Assumes target area is big enough and
8176edce865Snicm  * already cleared.
8186edce865Snicm  */
819a42faf7dSnicm void
820a42faf7dSnicm screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
821a42faf7dSnicm     u_int ny)
822a42faf7dSnicm {
823a42faf7dSnicm 	struct screen		*s = ctx->s;
824a42faf7dSnicm 	struct grid_cell	 gc;
825a42faf7dSnicm 	u_int			 cx, cy, px, py;
826a42faf7dSnicm 
827a42faf7dSnicm 	cx = s->cx;
828a42faf7dSnicm 	cy = s->cy;
829a42faf7dSnicm 
830a42faf7dSnicm 	/*
831a42faf7dSnicm 	 * If the cursor is on, pick the area around the cursor, otherwise use
832a42faf7dSnicm 	 * the top left.
833a42faf7dSnicm 	 */
834a42faf7dSnicm 	if (src->mode & MODE_CURSOR) {
835a42faf7dSnicm 		px = src->cx;
836a42faf7dSnicm 		if (px < nx / 3)
837a42faf7dSnicm 			px = 0;
838a42faf7dSnicm 		else
839a42faf7dSnicm 			px = px - nx / 3;
840a42faf7dSnicm 		if (px + nx > screen_size_x(src)) {
841a42faf7dSnicm 			if (nx > screen_size_x(src))
842a42faf7dSnicm 				px = 0;
843a42faf7dSnicm 			else
844a42faf7dSnicm 				px = screen_size_x(src) - nx;
845a42faf7dSnicm 		}
846a42faf7dSnicm 		py = src->cy;
847a42faf7dSnicm 		if (py < ny / 3)
848a42faf7dSnicm 			py = 0;
849a42faf7dSnicm 		else
850a42faf7dSnicm 			py = py - ny / 3;
851a42faf7dSnicm 		if (py + ny > screen_size_y(src)) {
852a42faf7dSnicm 			if (ny > screen_size_y(src))
853a42faf7dSnicm 				py = 0;
854a42faf7dSnicm 			else
855a42faf7dSnicm 				py = screen_size_y(src) - ny;
856a42faf7dSnicm 		}
857a42faf7dSnicm 	} else {
858a42faf7dSnicm 		px = 0;
859a42faf7dSnicm 		py = 0;
860a42faf7dSnicm 	}
861a42faf7dSnicm 
8626edce865Snicm 	screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
863a42faf7dSnicm 
864a42faf7dSnicm 	if (src->mode & MODE_CURSOR) {
865a42faf7dSnicm 		grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
866a42faf7dSnicm 		gc.attr |= GRID_ATTR_REVERSE;
867ad47aa1bSnicm 		screen_write_set_cursor(ctx, cx + (src->cx - px),
868a42faf7dSnicm 		    cy + (src->cy - py));
869a42faf7dSnicm 		screen_write_cell(ctx, &gc);
870a42faf7dSnicm 	}
871a42faf7dSnicm }
872a42faf7dSnicm 
8733699ed24Snicm /* Set a mode. */
8743699ed24Snicm void
8753699ed24Snicm screen_write_mode_set(struct screen_write_ctx *ctx, int mode)
8763699ed24Snicm {
8773699ed24Snicm 	struct screen	*s = ctx->s;
8783699ed24Snicm 
8793699ed24Snicm 	s->mode |= mode;
88026a875eaSnicm 
88126a875eaSnicm 	if (log_get_level() != 0)
88226a875eaSnicm 		log_debug("%s: %s", __func__, screen_mode_to_string(mode));
8833699ed24Snicm }
8843699ed24Snicm 
8853699ed24Snicm /* Clear a mode. */
8863699ed24Snicm void
8873699ed24Snicm screen_write_mode_clear(struct screen_write_ctx *ctx, int mode)
8883699ed24Snicm {
8893699ed24Snicm 	struct screen	*s = ctx->s;
8903699ed24Snicm 
8913699ed24Snicm 	s->mode &= ~mode;
89226a875eaSnicm 
89326a875eaSnicm 	if (log_get_level() != 0)
89426a875eaSnicm 		log_debug("%s: %s", __func__, screen_mode_to_string(mode));
8953699ed24Snicm }
8963699ed24Snicm 
897311827fbSnicm /* Cursor up by ny. */
898311827fbSnicm void
899311827fbSnicm screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
900311827fbSnicm {
901311827fbSnicm 	struct screen	*s = ctx->s;
9027b470e93Snicm 	u_int		 cx = s->cx, cy = s->cy;
903311827fbSnicm 
904311827fbSnicm 	if (ny == 0)
905311827fbSnicm 		ny = 1;
906311827fbSnicm 
9077b470e93Snicm 	if (cy < s->rupper) {
9082a42fa53Snicm 		/* Above region. */
9097b470e93Snicm 		if (ny > cy)
9107b470e93Snicm 			ny = cy;
9112a42fa53Snicm 	} else {
9122a42fa53Snicm 		/* Below region. */
9137b470e93Snicm 		if (ny > cy - s->rupper)
9147b470e93Snicm 			ny = cy - s->rupper;
9152a42fa53Snicm 	}
9167b470e93Snicm 	if (cx == screen_size_x(s))
9177b470e93Snicm 		cx--;
918311827fbSnicm 
9197b470e93Snicm 	cy -= ny;
9207b470e93Snicm 
9217b470e93Snicm 	screen_write_set_cursor(ctx, cx, cy);
922311827fbSnicm }
923311827fbSnicm 
924311827fbSnicm /* Cursor down by ny. */
925311827fbSnicm void
926311827fbSnicm screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
927311827fbSnicm {
928311827fbSnicm 	struct screen	*s = ctx->s;
9297b470e93Snicm 	u_int		 cx = s->cx, cy = s->cy;
930311827fbSnicm 
931311827fbSnicm 	if (ny == 0)
932311827fbSnicm 		ny = 1;
933311827fbSnicm 
9347b470e93Snicm 	if (cy > s->rlower) {
9352a42fa53Snicm 		/* Below region. */
9367b470e93Snicm 		if (ny > screen_size_y(s) - 1 - cy)
9377b470e93Snicm 			ny = screen_size_y(s) - 1 - cy;
9382a42fa53Snicm 	} else {
9392a42fa53Snicm 		/* Above region. */
9407b470e93Snicm 		if (ny > s->rlower - cy)
9417b470e93Snicm 			ny = s->rlower - cy;
9422a42fa53Snicm 	}
9437b470e93Snicm 	if (cx == screen_size_x(s))
9447b470e93Snicm 	    cx--;
9457b470e93Snicm 	else if (ny == 0)
946311827fbSnicm 		return;
947311827fbSnicm 
9487b470e93Snicm 	cy += ny;
9497b470e93Snicm 
9507b470e93Snicm 	screen_write_set_cursor(ctx, cx, cy);
951311827fbSnicm }
952311827fbSnicm 
953311827fbSnicm /* Cursor right by nx. */
954311827fbSnicm void
955311827fbSnicm screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
956311827fbSnicm {
957311827fbSnicm 	struct screen	*s = ctx->s;
9587b470e93Snicm 	u_int		 cx = s->cx, cy = s->cy;
959311827fbSnicm 
960311827fbSnicm 	if (nx == 0)
961311827fbSnicm 		nx = 1;
962311827fbSnicm 
9637b470e93Snicm 	if (nx > screen_size_x(s) - 1 - cx)
9647b470e93Snicm 		nx = screen_size_x(s) - 1 - cx;
965311827fbSnicm 	if (nx == 0)
966311827fbSnicm 		return;
967311827fbSnicm 
9687b470e93Snicm 	cx += nx;
9697b470e93Snicm 
9707b470e93Snicm 	screen_write_set_cursor(ctx, cx, cy);
971311827fbSnicm }
972311827fbSnicm 
973311827fbSnicm /* Cursor left by nx. */
974311827fbSnicm void
975311827fbSnicm screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
976311827fbSnicm {
977311827fbSnicm 	struct screen	*s = ctx->s;
9787b470e93Snicm 	u_int		 cx = s->cx, cy = s->cy;
979311827fbSnicm 
980311827fbSnicm 	if (nx == 0)
981311827fbSnicm 		nx = 1;
982311827fbSnicm 
9837b470e93Snicm 	if (nx > cx)
9847b470e93Snicm 		nx = cx;
985311827fbSnicm 	if (nx == 0)
986311827fbSnicm 		return;
987311827fbSnicm 
9887b470e93Snicm 	cx -= nx;
9897b470e93Snicm 
9907b470e93Snicm 	screen_write_set_cursor(ctx, cx, cy);
991311827fbSnicm }
992311827fbSnicm 
9936d20045fSnicm /* Backspace; cursor left unless at start of wrapped line when can move up. */
9946d20045fSnicm void
9956d20045fSnicm screen_write_backspace(struct screen_write_ctx *ctx)
9966d20045fSnicm {
9976d20045fSnicm 	struct screen		*s = ctx->s;
9986d20045fSnicm 	struct grid_line	*gl;
9997b470e93Snicm 	u_int			 cx = s->cx, cy = s->cy;
10006d20045fSnicm 
10017b470e93Snicm 	if (cx == 0) {
10027b470e93Snicm 		if (cy == 0)
10036d20045fSnicm 			return;
10047b470e93Snicm 		gl = grid_get_line(s->grid, s->grid->hsize + cy - 1);
10056d20045fSnicm 		if (gl->flags & GRID_LINE_WRAPPED) {
10067b470e93Snicm 			cy--;
10077b470e93Snicm 			cx = screen_size_x(s) - 1;
10086d20045fSnicm 		}
10096d20045fSnicm 	} else
10107b470e93Snicm 		cx--;
10117b470e93Snicm 
10127b470e93Snicm 	screen_write_set_cursor(ctx, cx, cy);
10136d20045fSnicm }
10146d20045fSnicm 
1015b18cc82cSnicm /* VT100 alignment test. */
1016b18cc82cSnicm void
1017b18cc82cSnicm screen_write_alignmenttest(struct screen_write_ctx *ctx)
1018b18cc82cSnicm {
1019b18cc82cSnicm 	struct screen		*s = ctx->s;
1020900b44f5Snicm 	struct tty_ctx	 	 ttyctx;
1021b18cc82cSnicm 	struct grid_cell       	 gc;
1022b18cc82cSnicm 	u_int			 xx, yy;
1023b18cc82cSnicm 
1024b18cc82cSnicm 	memcpy(&gc, &grid_default_cell, sizeof gc);
1025e931849fSnicm 	utf8_set(&gc.data, 'E');
1026b18cc82cSnicm 
1027b18cc82cSnicm 	for (yy = 0; yy < screen_size_y(s); yy++) {
1028b18cc82cSnicm 		for (xx = 0; xx < screen_size_x(s); xx++)
1029b18cc82cSnicm 			grid_view_set_cell(s->grid, xx, yy, &gc);
1030b18cc82cSnicm 	}
1031b18cc82cSnicm 
10327b470e93Snicm 	screen_write_set_cursor(ctx, 0, 0);
1033b18cc82cSnicm 
1034b18cc82cSnicm 	s->rupper = 0;
1035b18cc82cSnicm 	s->rlower = screen_size_y(s) - 1;
1036b18cc82cSnicm 
10377f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 1);
1038e2755cc9Snicm 
10398248eab7Snicm 	screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
1040900b44f5Snicm 	tty_write(tty_cmd_alignmenttest, &ttyctx);
1041b18cc82cSnicm }
1042b18cc82cSnicm 
1043311827fbSnicm /* Insert nx characters. */
1044311827fbSnicm void
1045ae4624e7Snicm screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1046311827fbSnicm {
1047311827fbSnicm 	struct screen	*s = ctx->s;
1048900b44f5Snicm 	struct tty_ctx	 ttyctx;
1049311827fbSnicm 
1050311827fbSnicm 	if (nx == 0)
1051311827fbSnicm 		nx = 1;
1052311827fbSnicm 
1053e12ac7a4Snicm 	if (nx > screen_size_x(s) - s->cx)
1054e12ac7a4Snicm 		nx = screen_size_x(s) - s->cx;
1055311827fbSnicm 	if (nx == 0)
1056311827fbSnicm 		return;
1057311827fbSnicm 
10589c8da6a8Snicm 	if (s->cx > screen_size_x(s) - 1)
10599c8da6a8Snicm 		return;
10609c8da6a8Snicm 
10617f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 0);
10629c8da6a8Snicm 	ttyctx.bg = bg;
1063311827fbSnicm 
1064ae4624e7Snicm 	grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg);
1065311827fbSnicm 
10669b53212eSnicm 	screen_write_collect_flush(ctx, 0, __func__);
1067900b44f5Snicm 	ttyctx.num = nx;
1068900b44f5Snicm 	tty_write(tty_cmd_insertcharacter, &ttyctx);
1069311827fbSnicm }
1070311827fbSnicm 
1071311827fbSnicm /* Delete nx characters. */
1072311827fbSnicm void
1073ae4624e7Snicm screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1074311827fbSnicm {
1075311827fbSnicm 	struct screen	*s = ctx->s;
1076900b44f5Snicm 	struct tty_ctx	 ttyctx;
1077311827fbSnicm 
1078311827fbSnicm 	if (nx == 0)
1079311827fbSnicm 		nx = 1;
1080311827fbSnicm 
1081e12ac7a4Snicm 	if (nx > screen_size_x(s) - s->cx)
1082e12ac7a4Snicm 		nx = screen_size_x(s) - s->cx;
1083311827fbSnicm 	if (nx == 0)
1084311827fbSnicm 		return;
1085311827fbSnicm 
10869c8da6a8Snicm 	if (s->cx > screen_size_x(s) - 1)
10879c8da6a8Snicm 		return;
10889c8da6a8Snicm 
10897f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 0);
10909c8da6a8Snicm 	ttyctx.bg = bg;
1091311827fbSnicm 
1092ae4624e7Snicm 	grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg);
1093311827fbSnicm 
10949b53212eSnicm 	screen_write_collect_flush(ctx, 0, __func__);
1095900b44f5Snicm 	ttyctx.num = nx;
1096900b44f5Snicm 	tty_write(tty_cmd_deletecharacter, &ttyctx);
1097311827fbSnicm }
1098311827fbSnicm 
1099f752bf02Snicm /* Clear nx characters. */
1100f752bf02Snicm void
1101cf8e7221Snicm screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1102f752bf02Snicm {
1103f752bf02Snicm 	struct screen	*s = ctx->s;
1104f752bf02Snicm 	struct tty_ctx	 ttyctx;
1105f752bf02Snicm 
1106f752bf02Snicm 	if (nx == 0)
1107f752bf02Snicm 		nx = 1;
1108f752bf02Snicm 
1109f752bf02Snicm 	if (nx > screen_size_x(s) - s->cx)
1110f752bf02Snicm 		nx = screen_size_x(s) - s->cx;
1111f752bf02Snicm 	if (nx == 0)
1112f752bf02Snicm 		return;
1113f752bf02Snicm 
11149c8da6a8Snicm 	if (s->cx > screen_size_x(s) - 1)
11159c8da6a8Snicm 		return;
11169c8da6a8Snicm 
11177f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 0);
1118cf8e7221Snicm 	ttyctx.bg = bg;
1119f752bf02Snicm 
11205278aeaaSnicm 	grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg);
1121f752bf02Snicm 
11229b53212eSnicm 	screen_write_collect_flush(ctx, 0, __func__);
1123f752bf02Snicm 	ttyctx.num = nx;
1124f752bf02Snicm 	tty_write(tty_cmd_clearcharacter, &ttyctx);
1125f752bf02Snicm }
1126f752bf02Snicm 
1127311827fbSnicm /* Insert ny lines. */
1128311827fbSnicm void
1129ae4624e7Snicm screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1130311827fbSnicm {
1131311827fbSnicm 	struct screen	*s = ctx->s;
11329c8da6a8Snicm 	struct grid	*gd = s->grid;
1133900b44f5Snicm 	struct tty_ctx	 ttyctx;
1134311827fbSnicm 
1135311827fbSnicm 	if (ny == 0)
1136311827fbSnicm 		ny = 1;
1137311827fbSnicm 
1138a6133b88Snicm 	if (s->cy < s->rupper || s->cy > s->rlower) {
1139e12ac7a4Snicm 		if (ny > screen_size_y(s) - s->cy)
1140e12ac7a4Snicm 			ny = screen_size_y(s) - s->cy;
1141311827fbSnicm 		if (ny == 0)
1142311827fbSnicm 			return;
1143311827fbSnicm 
11447f9a0536Snicm 		screen_write_initctx(ctx, &ttyctx, 1);
11459c8da6a8Snicm 		ttyctx.bg = bg;
1146311827fbSnicm 
11479c8da6a8Snicm 		grid_view_insert_lines(gd, s->cy, ny, bg);
1148a6133b88Snicm 
11499b53212eSnicm 		screen_write_collect_flush(ctx, 0, __func__);
1150900b44f5Snicm 		ttyctx.num = ny;
1151900b44f5Snicm 		tty_write(tty_cmd_insertline, &ttyctx);
1152a6133b88Snicm 		return;
1153a6133b88Snicm 	}
1154a6133b88Snicm 
1155a6133b88Snicm 	if (ny > s->rlower + 1 - s->cy)
1156a6133b88Snicm 		ny = s->rlower + 1 - s->cy;
1157a6133b88Snicm 	if (ny == 0)
1158a6133b88Snicm 		return;
1159a6133b88Snicm 
11607f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 1);
11619c8da6a8Snicm 	ttyctx.bg = bg;
1162a6133b88Snicm 
1163311827fbSnicm 	if (s->cy < s->rupper || s->cy > s->rlower)
11649c8da6a8Snicm 		grid_view_insert_lines(gd, s->cy, ny, bg);
11659c8da6a8Snicm 	else
11669c8da6a8Snicm 		grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg);
1167311827fbSnicm 
11689b53212eSnicm 	screen_write_collect_flush(ctx, 0, __func__);
11699b53212eSnicm 
1170900b44f5Snicm 	ttyctx.num = ny;
1171900b44f5Snicm 	tty_write(tty_cmd_insertline, &ttyctx);
1172311827fbSnicm }
1173311827fbSnicm 
1174311827fbSnicm /* Delete ny lines. */
1175311827fbSnicm void
1176ae4624e7Snicm screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1177311827fbSnicm {
1178311827fbSnicm 	struct screen	*s = ctx->s;
11799c8da6a8Snicm 	struct grid	*gd = s->grid;
1180900b44f5Snicm 	struct tty_ctx	 ttyctx;
1181d2117533Snicm 	u_int		 sy = screen_size_y(s);
1182311827fbSnicm 
1183311827fbSnicm 	if (ny == 0)
1184311827fbSnicm 		ny = 1;
1185311827fbSnicm 
1186a6133b88Snicm 	if (s->cy < s->rupper || s->cy > s->rlower) {
1187d2117533Snicm 		if (ny > sy - s->cy)
1188d2117533Snicm 			ny = sy - s->cy;
1189311827fbSnicm 		if (ny == 0)
1190311827fbSnicm 			return;
1191311827fbSnicm 
11927f9a0536Snicm 		screen_write_initctx(ctx, &ttyctx, 1);
11939c8da6a8Snicm 		ttyctx.bg = bg;
1194311827fbSnicm 
11959c8da6a8Snicm 		grid_view_delete_lines(gd, s->cy, ny, bg);
1196a6133b88Snicm 
11979b53212eSnicm 		screen_write_collect_flush(ctx, 0, __func__);
1198900b44f5Snicm 		ttyctx.num = ny;
1199900b44f5Snicm 		tty_write(tty_cmd_deleteline, &ttyctx);
1200a6133b88Snicm 		return;
1201a6133b88Snicm 	}
1202a6133b88Snicm 
1203a6133b88Snicm 	if (ny > s->rlower + 1 - s->cy)
1204a6133b88Snicm 		ny = s->rlower + 1 - s->cy;
1205a6133b88Snicm 	if (ny == 0)
1206a6133b88Snicm 		return;
1207a6133b88Snicm 
12087f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 1);
12099c8da6a8Snicm 	ttyctx.bg = bg;
1210a6133b88Snicm 
1211311827fbSnicm 	if (s->cy < s->rupper || s->cy > s->rlower)
12129c8da6a8Snicm 		grid_view_delete_lines(gd, s->cy, ny, bg);
12139c8da6a8Snicm 	else
12149c8da6a8Snicm 		grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg);
1215311827fbSnicm 
12169b53212eSnicm 	screen_write_collect_flush(ctx, 0, __func__);
1217900b44f5Snicm 	ttyctx.num = ny;
1218900b44f5Snicm 	tty_write(tty_cmd_deleteline, &ttyctx);
1219311827fbSnicm }
1220311827fbSnicm 
1221311827fbSnicm /* Clear line at cursor. */
1222311827fbSnicm void
1223ae4624e7Snicm screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
1224311827fbSnicm {
1225311827fbSnicm 	struct screen			*s = ctx->s;
12265fd00eceSnicm 	struct grid_line		*gl;
12275fd00eceSnicm 	u_int				 sx = screen_size_x(s);
12282612f635Snicm 	struct screen_write_citem	*ci = ctx->item;
1229311827fbSnicm 
12307621e88fSnicm 	gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1231d78fd057Snicm 	if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
12325fd00eceSnicm 		return;
1233311827fbSnicm 
1234ae4624e7Snicm 	grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1235ae4624e7Snicm 
12368248eab7Snicm 	screen_write_collect_clear(ctx, s->cy, 1);
12372612f635Snicm 	ci->x = 0;
12382612f635Snicm 	ci->used = sx;
12392612f635Snicm 	ci->type = CLEAR;
12402612f635Snicm 	ci->bg = bg;
12412612f635Snicm 	TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
12422612f635Snicm 	ctx->item = screen_write_get_citem();
1243311827fbSnicm }
1244311827fbSnicm 
1245311827fbSnicm /* Clear to end of line from cursor. */
1246311827fbSnicm void
1247ae4624e7Snicm screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
1248311827fbSnicm {
1249311827fbSnicm 	struct screen			*s = ctx->s;
12505fd00eceSnicm 	struct grid_line		*gl;
12515fd00eceSnicm 	u_int				 sx = screen_size_x(s);
12522612f635Snicm 	struct screen_write_citem	*ci = ctx->item, *before;
12538b17a0f3Snicm 
12548b17a0f3Snicm 	if (s->cx == 0) {
12558b17a0f3Snicm 		screen_write_clearline(ctx, bg);
12568b17a0f3Snicm 		return;
12578b17a0f3Snicm 	}
1258311827fbSnicm 
12597621e88fSnicm 	gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1260d78fd057Snicm 	if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
12615fd00eceSnicm 		return;
1262311827fbSnicm 
1263ae4624e7Snicm 	grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
1264ae4624e7Snicm 
12652612f635Snicm  	before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
12668b17a0f3Snicm 	ci->x = s->cx;
12672612f635Snicm 	ci->used = sx - s->cx;
12682612f635Snicm 	ci->type = CLEAR;
12698b17a0f3Snicm 	ci->bg = bg;
12702612f635Snicm 	if (before == NULL)
127150376775Snicm 		TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
12722612f635Snicm 	else
12732612f635Snicm 		TAILQ_INSERT_BEFORE(before, ci, entry);
12742612f635Snicm 	ctx->item = screen_write_get_citem();
12758b17a0f3Snicm }
1276311827fbSnicm 
1277311827fbSnicm /* Clear to start of line from cursor. */
1278311827fbSnicm void
1279ae4624e7Snicm screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
1280311827fbSnicm {
1281311827fbSnicm 	struct screen			 *s = ctx->s;
12825fd00eceSnicm 	u_int				 sx = screen_size_x(s);
12832612f635Snicm 	struct screen_write_citem	*ci = ctx->item, *before;
12848b17a0f3Snicm 
12858b17a0f3Snicm 	if (s->cx >= sx - 1) {
12868b17a0f3Snicm 		screen_write_clearline(ctx, bg);
12878b17a0f3Snicm 		return;
12888b17a0f3Snicm 	}
1289311827fbSnicm 
12908248eab7Snicm 	if (s->cx > sx - 1)
1291ae4624e7Snicm 		grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
12928248eab7Snicm 	else
1293ae4624e7Snicm 		grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1294311827fbSnicm 
12952612f635Snicm 	before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
12962612f635Snicm 	ci->x = 0;
12972612f635Snicm 	ci->used = s->cx + 1;
12982612f635Snicm 	ci->type = CLEAR;
12998b17a0f3Snicm 	ci->bg = bg;
13002612f635Snicm 	if (before == NULL)
130150376775Snicm 		TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
13022612f635Snicm 	else
13032612f635Snicm 		TAILQ_INSERT_BEFORE(before, ci, entry);
13042612f635Snicm 	ctx->item = screen_write_get_citem();
13058b17a0f3Snicm }
1306311827fbSnicm 
1307311827fbSnicm /* Move cursor to px,py. */
1308311827fbSnicm void
13095c6c7001Snicm screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
13105c6c7001Snicm     int origin)
1311311827fbSnicm {
1312311827fbSnicm 	struct screen	*s = ctx->s;
1313311827fbSnicm 
13145c6c7001Snicm 	if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
1315c15b2347Snicm 		if ((u_int)py > s->rlower - s->rupper)
1316ad47aa1bSnicm 			py = s->rlower;
1317ad47aa1bSnicm 		else
1318ad47aa1bSnicm 			py += s->rupper;
1319ad47aa1bSnicm 	}
1320311827fbSnicm 
1321c15b2347Snicm 	if (px != -1 && (u_int)px > screen_size_x(s) - 1)
132294b5b5e9Snicm 		px = screen_size_x(s) - 1;
1323c15b2347Snicm 	if (py != -1 && (u_int)py > screen_size_y(s) - 1)
132494b5b5e9Snicm 		py = screen_size_y(s) - 1;
132594b5b5e9Snicm 
13262612f635Snicm 	log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
13277b470e93Snicm 	screen_write_set_cursor(ctx, px, py);
1328311827fbSnicm }
1329311827fbSnicm 
1330311827fbSnicm /* Reverse index (up with scroll). */
1331311827fbSnicm void
13326d5c64a0Snicm screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
1333311827fbSnicm {
1334311827fbSnicm 	struct screen	*s = ctx->s;
1335900b44f5Snicm 	struct tty_ctx	 ttyctx;
1336311827fbSnicm 
13378b17a0f3Snicm 	if (s->cy == s->rupper) {
13388b17a0f3Snicm 		grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
13398b17a0f3Snicm 		screen_write_collect_flush(ctx, 0, __func__);
13408b17a0f3Snicm 
13417f9a0536Snicm 		screen_write_initctx(ctx, &ttyctx, 1);
13426d5c64a0Snicm 		ttyctx.bg = bg;
1343311827fbSnicm 
13448b17a0f3Snicm 		tty_write(tty_cmd_reverseindex, &ttyctx);
13458b17a0f3Snicm 	} else if (s->cy > 0)
13467b470e93Snicm 		screen_write_set_cursor(ctx, -1, s->cy - 1);
1347311827fbSnicm 
1348311827fbSnicm }
1349311827fbSnicm 
1350311827fbSnicm /* Set scroll region. */
1351311827fbSnicm void
1352d5221700Snicm screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
1353d5221700Snicm     u_int rlower)
1354311827fbSnicm {
1355311827fbSnicm 	struct screen	*s = ctx->s;
1356311827fbSnicm 
1357311827fbSnicm 	if (rupper > screen_size_y(s) - 1)
1358311827fbSnicm 		rupper = screen_size_y(s) - 1;
1359311827fbSnicm 	if (rlower > screen_size_y(s) - 1)
1360311827fbSnicm 		rlower = screen_size_y(s) - 1;
13613a129772Snicm 	if (rupper >= rlower)	/* cannot be one line */
1362311827fbSnicm 		return;
1363311827fbSnicm 
13649b53212eSnicm 	screen_write_collect_flush(ctx, 0, __func__);
1365f0063641Snicm 
1366311827fbSnicm 	/* Cursor moves to top-left. */
13677b470e93Snicm 	screen_write_set_cursor(ctx, 0, 0);
1368311827fbSnicm 
1369311827fbSnicm 	s->rupper = rupper;
1370311827fbSnicm 	s->rlower = rlower;
1371311827fbSnicm }
1372311827fbSnicm 
1373cb3fa4d4Snicm /* Line feed. */
1374311827fbSnicm void
13756d5c64a0Snicm screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
1376311827fbSnicm {
1377311827fbSnicm 	struct screen		*s = ctx->s;
13788248eab7Snicm 	struct grid		*gd = s->grid;
137917022a89Snicm 	struct grid_line	*gl;
1380d2117533Snicm 	u_int			 rupper = s->rupper, rlower = s->rlower;
1381cb3fa4d4Snicm 
13827621e88fSnicm 	gl = grid_get_line(gd, gd->hsize + s->cy);
138317022a89Snicm 	if (wrapped)
138417022a89Snicm 		gl->flags |= GRID_LINE_WRAPPED;
138517022a89Snicm 
1386ddeb5b35Snicm 	log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1387d2117533Snicm 	    rupper, rlower);
1388ddeb5b35Snicm 
13896d5c64a0Snicm 	if (bg != ctx->bg) {
13909b53212eSnicm 		screen_write_collect_flush(ctx, 1, __func__);
13916d5c64a0Snicm 		ctx->bg = bg;
13926d5c64a0Snicm 	}
13936d5c64a0Snicm 
13945fd00eceSnicm 	if (s->cy == s->rlower) {
13956d5c64a0Snicm 		grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
13962612f635Snicm 		screen_write_collect_scroll(ctx, bg);
1397f0063641Snicm 		ctx->scrolled++;
13988248eab7Snicm 	} else if (s->cy < screen_size_y(s) - 1)
13997b470e93Snicm 		screen_write_set_cursor(ctx, -1, s->cy + 1);
1400311827fbSnicm }
1401311827fbSnicm 
1402f0063641Snicm /* Scroll up. */
1403f0063641Snicm void
14046d5c64a0Snicm screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1405f0063641Snicm {
1406f0063641Snicm 	struct screen	*s = ctx->s;
1407f0063641Snicm 	struct grid	*gd = s->grid;
1408f0063641Snicm 	u_int		 i;
1409f0063641Snicm 
1410f0063641Snicm 	if (lines == 0)
1411f0063641Snicm 		lines = 1;
1412f0063641Snicm 	else if (lines > s->rlower - s->rupper + 1)
1413f0063641Snicm 		lines = s->rlower - s->rupper + 1;
1414f0063641Snicm 
14156d5c64a0Snicm 	if (bg != ctx->bg) {
14169b53212eSnicm 		screen_write_collect_flush(ctx, 1, __func__);
14176d5c64a0Snicm 		ctx->bg = bg;
14186d5c64a0Snicm 	}
14196d5c64a0Snicm 
1420f0063641Snicm 	for (i = 0; i < lines; i++) {
14216d5c64a0Snicm 		grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
14222612f635Snicm 		screen_write_collect_scroll(ctx, bg);
1423f0063641Snicm 	}
1424f0063641Snicm 	ctx->scrolled += lines;
1425f0063641Snicm }
1426f0063641Snicm 
1427fa32c33cSnicm /* Scroll down. */
1428fa32c33cSnicm void
1429fa32c33cSnicm screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1430fa32c33cSnicm {
1431fa32c33cSnicm 	struct screen	*s = ctx->s;
1432fa32c33cSnicm 	struct grid	*gd = s->grid;
1433fa32c33cSnicm 	struct tty_ctx	 ttyctx;
1434fa32c33cSnicm 	u_int		 i;
1435fa32c33cSnicm 
14367f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 1);
1437fa32c33cSnicm 	ttyctx.bg = bg;
1438fa32c33cSnicm 
1439fa32c33cSnicm 	if (lines == 0)
1440fa32c33cSnicm 		lines = 1;
1441fa32c33cSnicm 	else if (lines > s->rlower - s->rupper + 1)
1442fa32c33cSnicm 		lines = s->rlower - s->rupper + 1;
1443fa32c33cSnicm 
1444fa32c33cSnicm 	for (i = 0; i < lines; i++)
1445fa32c33cSnicm 		grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
1446fa32c33cSnicm 
14479b53212eSnicm 	screen_write_collect_flush(ctx, 0, __func__);
1448fa32c33cSnicm 	ttyctx.num = lines;
1449fa32c33cSnicm 	tty_write(tty_cmd_scrolldown, &ttyctx);
1450fa32c33cSnicm }
1451fa32c33cSnicm 
1452311827fbSnicm /* Carriage return (cursor to start of line). */
1453311827fbSnicm void
1454311827fbSnicm screen_write_carriagereturn(struct screen_write_ctx *ctx)
1455311827fbSnicm {
14567b470e93Snicm 	screen_write_set_cursor(ctx, 0, -1);
1457311827fbSnicm }
1458311827fbSnicm 
1459311827fbSnicm /* Clear to end of screen from cursor. */
1460311827fbSnicm void
1461ae4624e7Snicm screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
1462311827fbSnicm {
1463311827fbSnicm 	struct screen	*s = ctx->s;
14649c8da6a8Snicm 	struct grid	*gd = s->grid;
1465900b44f5Snicm 	struct tty_ctx	 ttyctx;
14665fd00eceSnicm 	u_int		 sx = screen_size_x(s), sy = screen_size_y(s);
1467311827fbSnicm 
14687f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 1);
1469ae4624e7Snicm 	ttyctx.bg = bg;
1470311827fbSnicm 
14710864dd21Snicm 	/* Scroll into history if it is enabled and clearing entire screen. */
14725df43dd6Snicm 	if (s->cx == 0 &&
14735df43dd6Snicm 	    s->cy == 0 &&
14745df43dd6Snicm 	    (gd->flags & GRID_HISTORY) &&
14755df43dd6Snicm 	    ctx->wp != NULL &&
14765df43dd6Snicm 	    options_get_number(ctx->wp->options, "scroll-on-clear"))
14779c8da6a8Snicm 		grid_view_clear_history(gd, bg);
14788248eab7Snicm 	else {
14798248eab7Snicm 		if (s->cx <= sx - 1)
14809c8da6a8Snicm 			grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg);
14819c8da6a8Snicm 		grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg);
14820864dd21Snicm 	}
1483311827fbSnicm 
14848248eab7Snicm 	screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
14859b53212eSnicm 	screen_write_collect_flush(ctx, 0, __func__);
1486900b44f5Snicm 	tty_write(tty_cmd_clearendofscreen, &ttyctx);
1487311827fbSnicm }
1488311827fbSnicm 
1489311827fbSnicm /* Clear to start of screen. */
1490311827fbSnicm void
149192483283Snicm screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
1492311827fbSnicm {
1493311827fbSnicm 	struct screen	*s = ctx->s;
1494900b44f5Snicm 	struct tty_ctx	 ttyctx;
14955fd00eceSnicm 	u_int		 sx = screen_size_x(s);
1496311827fbSnicm 
14977f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 1);
149892483283Snicm 	ttyctx.bg = bg;
1499311827fbSnicm 
15008248eab7Snicm 	if (s->cy > 0)
150192483283Snicm 		grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
15028248eab7Snicm 	if (s->cx > sx - 1)
1503cd8b1929Snicm 		grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
15048248eab7Snicm 	else
1505cd8b1929Snicm 		grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1506311827fbSnicm 
15078248eab7Snicm 	screen_write_collect_clear(ctx, 0, s->cy);
15089b53212eSnicm 	screen_write_collect_flush(ctx, 0, __func__);
1509900b44f5Snicm 	tty_write(tty_cmd_clearstartofscreen, &ttyctx);
1510311827fbSnicm }
1511311827fbSnicm 
1512311827fbSnicm /* Clear entire screen. */
1513311827fbSnicm void
1514ae4624e7Snicm screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
1515311827fbSnicm {
1516311827fbSnicm 	struct screen	*s = ctx->s;
1517900b44f5Snicm 	struct tty_ctx	 ttyctx;
15185fd00eceSnicm 	u_int		 sx = screen_size_x(s), sy = screen_size_y(s);
1519311827fbSnicm 
15207f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 1);
1521ae4624e7Snicm 	ttyctx.bg = bg;
1522311827fbSnicm 
15230864dd21Snicm 	/* Scroll into history if it is enabled. */
152408dcff4bSnicm 	if ((s->grid->flags & GRID_HISTORY) &&
152508dcff4bSnicm 	    ctx->wp != NULL &&
152608dcff4bSnicm 	    options_get_number(ctx->wp->options, "scroll-on-clear"))
1527ae4624e7Snicm 		grid_view_clear_history(s->grid, bg);
1528d5221700Snicm 	else
1529ae4624e7Snicm 		grid_view_clear(s->grid, 0, 0, sx, sy, bg);
1530311827fbSnicm 
15318248eab7Snicm 	screen_write_collect_clear(ctx, 0, sy);
1532900b44f5Snicm 	tty_write(tty_cmd_clearscreen, &ttyctx);
1533311827fbSnicm }
1534311827fbSnicm 
1535786c44dcSnicm /* Clear entire history. */
1536786c44dcSnicm void
1537786c44dcSnicm screen_write_clearhistory(struct screen_write_ctx *ctx)
1538786c44dcSnicm {
15399c5e7fb6Snicm 	grid_clear_history(ctx->s->grid);
1540786c44dcSnicm }
1541786c44dcSnicm 
154233a1e283Snicm /* Force a full redraw. */
154333a1e283Snicm void
154433a1e283Snicm screen_write_fullredraw(struct screen_write_ctx *ctx)
154533a1e283Snicm {
154633a1e283Snicm 	struct tty_ctx	 ttyctx;
154733a1e283Snicm 
154833a1e283Snicm 	screen_write_collect_flush(ctx, 0, __func__);
154933a1e283Snicm 
155033a1e283Snicm 	screen_write_initctx(ctx, &ttyctx, 1);
1551924a158aSnicm 	if (ttyctx.redraw_cb != NULL)
155233a1e283Snicm 		ttyctx.redraw_cb(&ttyctx);
155333a1e283Snicm }
155433a1e283Snicm 
15552612f635Snicm /* Trim collected items. */
15562612f635Snicm static struct screen_write_citem *
15572612f635Snicm screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
15582612f635Snicm     u_int used, int *wrapped)
15598b17a0f3Snicm {
15602612f635Snicm 	struct screen_write_cline	*cl = &ctx->s->write_list[y];
15612612f635Snicm 	struct screen_write_citem	*ci, *ci2, *tmp, *before = NULL;
15622612f635Snicm 	u_int				 sx = x, ex = x + used - 1;
15632612f635Snicm 	u_int				 csx, cex;
15648b17a0f3Snicm 
15652612f635Snicm 	if (TAILQ_EMPTY(&cl->items))
15662612f635Snicm 		return (NULL);
15672612f635Snicm 	TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
15682612f635Snicm 		csx = ci->x;
15692612f635Snicm 		cex = ci->x + ci->used - 1;
15702612f635Snicm 
15712612f635Snicm 		/* Item is entirely before. */
15722612f635Snicm 		if (cex < sx) {
15732612f635Snicm 			log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
15742612f635Snicm 			    csx, cex, sx, ex);
15758b17a0f3Snicm 			continue;
15768b17a0f3Snicm 		}
15778b17a0f3Snicm 
15782612f635Snicm 		/* Item is entirely after. */
15792612f635Snicm 		if (csx > ex) {
15802612f635Snicm 			log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
15812612f635Snicm 			    csx, cex, sx, ex);
15822612f635Snicm 			before = ci;
15832612f635Snicm 			break;
15842612f635Snicm 		}
15858b17a0f3Snicm 
15862612f635Snicm 		/* Item is entirely inside. */
15872612f635Snicm 		if (csx >= sx && cex <= ex) {
15882612f635Snicm 			log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
15892612f635Snicm 			    csx, cex, sx, ex);
15902612f635Snicm 			TAILQ_REMOVE(&cl->items, ci, entry);
15912612f635Snicm 			screen_write_free_citem(ci);
15922612f635Snicm 			if (csx == 0 && ci->wrapped && wrapped != NULL)
15932612f635Snicm 				*wrapped = 1;
15948b17a0f3Snicm 			continue;
15952612f635Snicm 		}
15962612f635Snicm 
15972612f635Snicm 		/* Item under the start. */
15982612f635Snicm 		if (csx < sx && cex >= sx && cex <= ex) {
15992612f635Snicm 			log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
16002612f635Snicm 			    csx, cex, sx, ex);
16012612f635Snicm 			ci->used = sx - csx;
16022612f635Snicm 			log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
16032612f635Snicm 			    ci->x + ci->used + 1);
16048b17a0f3Snicm 			continue;
16052612f635Snicm 		}
16062612f635Snicm 
16072612f635Snicm 		/* Item covers the end. */
16082612f635Snicm 		if (cex > ex && csx >= sx && csx <= ex) {
16092612f635Snicm 			log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
16102612f635Snicm 			    csx, cex, sx, ex);
16112612f635Snicm 			ci->x = ex + 1;
16122612f635Snicm 			ci->used = cex - ex;
16132612f635Snicm 			log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
16142612f635Snicm 			    ci->x + ci->used + 1);
16152612f635Snicm 			before = ci;
16168b17a0f3Snicm 			break;
16178b17a0f3Snicm 		}
16182612f635Snicm 
16192612f635Snicm 		/* Item must cover both sides. */
16202612f635Snicm 		log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
16212612f635Snicm 		    csx, cex, sx, ex);
16222612f635Snicm 		ci2 = screen_write_get_citem();
16232612f635Snicm 		ci2->type = ci->type;
16242612f635Snicm 		ci2->bg = ci->bg;
16252612f635Snicm 		memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
16262612f635Snicm 		TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
16272612f635Snicm 
16282612f635Snicm 		ci->used = sx - csx;
16292612f635Snicm 		ci2->x = ex + 1;
16302612f635Snicm 		ci2->used = cex - ex;
16312612f635Snicm 
16322612f635Snicm 		log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
16332612f635Snicm 		    ci->x, ci->x + ci->used - 1, ci, ci2->x,
16342612f635Snicm 		    ci2->x + ci2->used - 1, ci2);
16352612f635Snicm 		before = ci2;
16362612f635Snicm 		break;
16378b17a0f3Snicm 	}
16382612f635Snicm 	return (before);
16398b17a0f3Snicm }
16408b17a0f3Snicm 
1641977b6338Snicm /* Clear collected lines. */
16428248eab7Snicm static void
16438248eab7Snicm screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
16448248eab7Snicm {
16452612f635Snicm 	struct screen_write_cline	*cl;
16462612f635Snicm 	u_int				 i;
16478248eab7Snicm 
16488248eab7Snicm 	for (i = y; i < y + n; i++) {
164950376775Snicm 		cl = &ctx->s->write_list[i];
16502612f635Snicm 		TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
16518248eab7Snicm 	}
16528248eab7Snicm }
16538248eab7Snicm 
16548248eab7Snicm /* Scroll collected lines up. */
16558248eab7Snicm static void
16562612f635Snicm screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
16578248eab7Snicm {
16588248eab7Snicm 	struct screen			*s = ctx->s;
16592612f635Snicm 	struct screen_write_cline	*cl;
16608248eab7Snicm 	u_int				 y;
1661977b6338Snicm 	char				*saved;
16622612f635Snicm 	struct screen_write_citem	*ci;
16638248eab7Snicm 
1664ddeb5b35Snicm 	log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1665ddeb5b35Snicm 	    s->rupper, s->rlower);
1666ddeb5b35Snicm 
16678248eab7Snicm 	screen_write_collect_clear(ctx, s->rupper, 1);
166850376775Snicm 	saved = ctx->s->write_list[s->rupper].data;
16698248eab7Snicm 	for (y = s->rupper; y < s->rlower; y++) {
167050376775Snicm 		cl = &ctx->s->write_list[y + 1];
167150376775Snicm 		TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
167250376775Snicm 		ctx->s->write_list[y].data = cl->data;
16738248eab7Snicm 	}
1674208fe07fSnicm 	ctx->s->write_list[s->rlower].data = saved;
16752612f635Snicm 
16762612f635Snicm 	ci = screen_write_get_citem();
16772612f635Snicm 	ci->x = 0;
16782612f635Snicm 	ci->used = screen_size_x(s);
16792612f635Snicm 	ci->type = CLEAR;
16802612f635Snicm 	ci->bg = bg;
16812612f635Snicm 	TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
16828248eab7Snicm }
16838248eab7Snicm 
16848248eab7Snicm /* Flush collected lines. */
16858248eab7Snicm static void
16869b53212eSnicm screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
16879b53212eSnicm     const char *from)
16888248eab7Snicm {
16898248eab7Snicm 	struct screen			*s = ctx->s;
16902612f635Snicm 	struct screen_write_citem	*ci, *tmp;
16912612f635Snicm 	struct screen_write_cline	*cl;
16922612f635Snicm 	u_int				 y, cx, cy, last, items = 0;
16938248eab7Snicm 	struct tty_ctx			 ttyctx;
16948248eab7Snicm 
1695f0063641Snicm 	if (ctx->scrolled != 0) {
1696f0063641Snicm 		log_debug("%s: scrolled %u (region %u-%u)", __func__,
1697f0063641Snicm 		    ctx->scrolled, s->rupper, s->rlower);
1698f0063641Snicm 		if (ctx->scrolled > s->rlower - s->rupper + 1)
1699f0063641Snicm 			ctx->scrolled = s->rlower - s->rupper + 1;
1700f0063641Snicm 
17017f9a0536Snicm 		screen_write_initctx(ctx, &ttyctx, 1);
1702f0063641Snicm 		ttyctx.num = ctx->scrolled;
17036d5c64a0Snicm 		ttyctx.bg = ctx->bg;
1704f0063641Snicm 		tty_write(tty_cmd_scrollup, &ttyctx);
1705d2117533Snicm 
1706d2117533Snicm 		if (ctx->wp != NULL)
1707d2117533Snicm 			ctx->wp->flags |= PANE_REDRAWSCROLLBAR;
1708f0063641Snicm 	}
1709f0063641Snicm 	ctx->scrolled = 0;
17106d5c64a0Snicm 	ctx->bg = 8;
17116d5c64a0Snicm 
171246b92e93Snicm 	if (scroll_only)
171346b92e93Snicm 		return;
1714f0063641Snicm 
17158248eab7Snicm 	cx = s->cx; cy = s->cy;
17168248eab7Snicm 	for (y = 0; y < screen_size_y(s); y++) {
171750376775Snicm 		cl = &ctx->s->write_list[y];
17182612f635Snicm 		last = UINT_MAX;
1719a6b857afSnicm 		TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
17202612f635Snicm 			if (last != UINT_MAX && ci->x <= last) {
17212612f635Snicm 				fatalx("collect list not in order: %u <= %u",
17222612f635Snicm 				    ci->x, last);
17232612f635Snicm 			}
1724ad47aa1bSnicm 			screen_write_set_cursor(ctx, ci->x, y);
17252612f635Snicm 			if (ci->type == CLEAR) {
1726999176b1Snicm 				screen_write_initctx(ctx, &ttyctx, 1);
17278b17a0f3Snicm 				ttyctx.bg = ci->bg;
17282612f635Snicm 				ttyctx.num = ci->used;
17292612f635Snicm 				tty_write(tty_cmd_clearcharacter, &ttyctx);
17308b17a0f3Snicm 			} else {
1731999176b1Snicm 				screen_write_initctx(ctx, &ttyctx, 0);
17328248eab7Snicm 				ttyctx.cell = &ci->gc;
1733566401c6Snicm 				ttyctx.wrapped = ci->wrapped;
173450376775Snicm 				ttyctx.ptr = cl->data + ci->x;
17358248eab7Snicm 				ttyctx.num = ci->used;
17368248eab7Snicm 				tty_write(tty_cmd_cells, &ttyctx);
17378b17a0f3Snicm 			}
17382601a058Snicm 			items++;
17398248eab7Snicm 
174050376775Snicm 			TAILQ_REMOVE(&cl->items, ci, entry);
17412612f635Snicm 			screen_write_free_citem(ci);
17422612f635Snicm 			last = ci->x;
17438248eab7Snicm 		}
17448248eab7Snicm 	}
17458248eab7Snicm 	s->cx = cx; s->cy = cy;
17462601a058Snicm 
17472612f635Snicm 	log_debug("%s: flushed %u items (%s)", __func__, items, from);
17488248eab7Snicm }
17498248eab7Snicm 
17508248eab7Snicm /* Finish and store collected cells. */
17518248eab7Snicm void
17528248eab7Snicm screen_write_collect_end(struct screen_write_ctx *ctx)
17538248eab7Snicm {
17548248eab7Snicm 	struct screen			*s = ctx->s;
17552612f635Snicm 	struct screen_write_citem	*ci = ctx->item, *before;
17562612f635Snicm 	struct screen_write_cline	*cl = &s->write_list[s->cy];
17578248eab7Snicm 	struct grid_cell		 gc;
1758856ed6d3Snicm 	u_int				 xx;
17592612f635Snicm 	int				 wrapped = ci->wrapped;
17608248eab7Snicm 
17618248eab7Snicm 	if (ci->used == 0)
17628248eab7Snicm 		return;
17638248eab7Snicm 
17642612f635Snicm 	before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
17652612f635Snicm 	    &wrapped);
17668248eab7Snicm 	ci->x = s->cx;
17672612f635Snicm 	ci->wrapped = wrapped;
17682612f635Snicm 	if (before == NULL)
176950376775Snicm 		TAILQ_INSERT_TAIL(&cl->items, ci, entry);
17702612f635Snicm 	else
17712612f635Snicm 		TAILQ_INSERT_BEFORE(before, ci, entry);
17722612f635Snicm 	ctx->item = screen_write_get_citem();
17738248eab7Snicm 
1774977b6338Snicm 	log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
177550376775Snicm 	    (int)ci->used, cl->data + ci->x, s->cx, s->cy);
17768248eab7Snicm 
1777856ed6d3Snicm 	if (s->cx != 0) {
1778856ed6d3Snicm 		for (xx = s->cx; xx > 0; xx--) {
1779856ed6d3Snicm 			grid_view_get_cell(s->grid, xx, s->cy, &gc);
1780856ed6d3Snicm 			if (~gc.flags & GRID_FLAG_PADDING)
1781856ed6d3Snicm 				break;
17821e84472bSnicm 			grid_view_set_cell(s->grid, xx, s->cy,
17831e84472bSnicm 			    &grid_default_cell);
1784856ed6d3Snicm 		}
17857b470e93Snicm 		if (gc.data.width > 1) {
17861e84472bSnicm 			grid_view_set_cell(s->grid, xx, s->cy,
17871e84472bSnicm 			    &grid_default_cell);
1788856ed6d3Snicm 		}
17897b470e93Snicm 	}
1790856ed6d3Snicm 
179150376775Snicm 	grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
179250376775Snicm 	    ci->used);
17937b470e93Snicm 	screen_write_set_cursor(ctx, s->cx + ci->used, -1);
1794856ed6d3Snicm 
1795856ed6d3Snicm 	for (xx = s->cx; xx < screen_size_x(s); xx++) {
1796856ed6d3Snicm 		grid_view_get_cell(s->grid, xx, s->cy, &gc);
1797856ed6d3Snicm 		if (~gc.flags & GRID_FLAG_PADDING)
1798856ed6d3Snicm 			break;
1799856ed6d3Snicm 		grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
1800856ed6d3Snicm 	}
18018248eab7Snicm }
18028248eab7Snicm 
18038248eab7Snicm /* Write cell data, collecting if necessary. */
18048248eab7Snicm void
18058248eab7Snicm screen_write_collect_add(struct screen_write_ctx *ctx,
18068248eab7Snicm     const struct grid_cell *gc)
18078248eab7Snicm {
18088248eab7Snicm 	struct screen			*s = ctx->s;
18092612f635Snicm 	struct screen_write_citem	*ci;
18108248eab7Snicm 	u_int				 sx = screen_size_x(s);
18118248eab7Snicm 	int				 collect;
18128248eab7Snicm 
18138248eab7Snicm 	/*
18148248eab7Snicm 	 * Don't need to check that the attributes and whatnot are still the
18152601a058Snicm 	 * same - input_parse will end the collection when anything that isn't
181626a51baaSnicm 	 * a plain character is encountered.
18178248eab7Snicm 	 */
18188248eab7Snicm 
18198248eab7Snicm 	collect = 1;
18201e84472bSnicm 	if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
18218248eab7Snicm 		collect = 0;
182202d75531Snicm 	else if (gc->flags & GRID_FLAG_TAB)
182302d75531Snicm 		collect = 0;
18248248eab7Snicm 	else if (gc->attr & GRID_ATTR_CHARSET)
18258248eab7Snicm 		collect = 0;
18268248eab7Snicm 	else if (~s->mode & MODE_WRAP)
18278248eab7Snicm 		collect = 0;
18288248eab7Snicm 	else if (s->mode & MODE_INSERT)
18298248eab7Snicm 		collect = 0;
18303f2a5436Snicm 	else if (s->sel != NULL)
18318248eab7Snicm 		collect = 0;
18328248eab7Snicm 	if (!collect) {
18338248eab7Snicm 		screen_write_collect_end(ctx);
18349b53212eSnicm 		screen_write_collect_flush(ctx, 0, __func__);
18358248eab7Snicm 		screen_write_cell(ctx, gc);
18368248eab7Snicm 		return;
18378248eab7Snicm 	}
18388248eab7Snicm 
18398248eab7Snicm 	if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
18408248eab7Snicm 		screen_write_collect_end(ctx);
1841566401c6Snicm 	ci = ctx->item; /* may have changed */
1842566401c6Snicm 
18438248eab7Snicm 	if (s->cx > sx - 1) {
1844ddeb5b35Snicm 		log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1845566401c6Snicm 		ci->wrapped = 1;
18466d5c64a0Snicm 		screen_write_linefeed(ctx, 1, 8);
18477b470e93Snicm 		screen_write_set_cursor(ctx, 0, -1);
18488248eab7Snicm 	}
18498248eab7Snicm 
18508248eab7Snicm 	if (ci->used == 0)
18518248eab7Snicm 		memcpy(&ci->gc, gc, sizeof ci->gc);
185250376775Snicm 	if (ctx->s->write_list[s->cy].data == NULL)
185350376775Snicm 		ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s));
185450376775Snicm 	ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0];
18558248eab7Snicm }
18568248eab7Snicm 
1857311827fbSnicm /* Write cell data. */
1858311827fbSnicm void
1859e031297fSnicm screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
1860311827fbSnicm {
1861311827fbSnicm 	struct screen		*s = ctx->s;
1862311827fbSnicm 	struct grid		*gd = s->grid;
18632af49740Snicm 	const struct utf8_data	*ud = &gc->data;
18645fd00eceSnicm 	struct grid_line	*gl;
18655fd00eceSnicm 	struct grid_cell_entry	*gce;
18668248eab7Snicm 	struct grid_cell 	 tmp_gc, now_gc;
18678248eab7Snicm 	struct tty_ctx		 ttyctx;
18688248eab7Snicm 	u_int			 sx = screen_size_x(s), sy = screen_size_y(s);
18692af49740Snicm 	u_int		 	 width = ud->width, xx, not_wrap;
18708248eab7Snicm 	int			 selected, skip = 1;
18715fd00eceSnicm 
18728248eab7Snicm 	/* Ignore padding cells. */
1873311827fbSnicm 	if (gc->flags & GRID_FLAG_PADDING)
1874311827fbSnicm 		return;
1875311827fbSnicm 
18762af49740Snicm 	/* Get the previous cell to check for combining. */
18772af49740Snicm 	if (screen_write_combine(ctx, gc) != 0)
187847287340Snicm 		return;
1879311827fbSnicm 
188030133c68Snicm 	/* Flush any existing scrolling. */
18819b53212eSnicm 	screen_write_collect_flush(ctx, 1, __func__);
188230133c68Snicm 
18838248eab7Snicm 	/* If this character doesn't fit, ignore it. */
18848248eab7Snicm 	if ((~s->mode & MODE_WRAP) &&
18858248eab7Snicm 	    width > 1 &&
18868248eab7Snicm 	    (width > sx || (s->cx != sx && s->cx > sx - width)))
18878248eab7Snicm 		return;
18888248eab7Snicm 
18897bd6086bSnicm 	/* If in insert mode, make space for the cells. */
18908368ee61Snicm 	if (s->mode & MODE_INSERT) {
18918248eab7Snicm 		grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
18928248eab7Snicm 		skip = 0;
18938368ee61Snicm 	}
18947bd6086bSnicm 
189517022a89Snicm 	/* Check this will fit on the current line and wrap if not. */
18965fd00eceSnicm 	if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
18976204dbd8Snicm 		log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
18986d5c64a0Snicm 		screen_write_linefeed(ctx, 1, 8);
18997b470e93Snicm 		screen_write_set_cursor(ctx, 0, -1);
19009b53212eSnicm 		screen_write_collect_flush(ctx, 1, __func__);
1901311827fbSnicm 	}
1902311827fbSnicm 
190384c47e4aSnicm 	/* Sanity check cursor position. */
19045fd00eceSnicm 	if (s->cx > sx - width || s->cy > sy - 1)
1905311827fbSnicm 		return;
19067f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 0);
1907be47f69aSnicm 
1908311827fbSnicm 	/* Handle overwriting of UTF-8 characters. */
19097621e88fSnicm 	gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
19105fd00eceSnicm 	if (gl->flags & GRID_LINE_EXTENDED) {
19118e39655fSnicm 		grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
19128e39655fSnicm 		if (screen_write_overwrite(ctx, &now_gc, width))
19138e39655fSnicm 			skip = 0;
19145fd00eceSnicm 	}
1915311827fbSnicm 
1916311827fbSnicm 	/*
1917311827fbSnicm 	 * If the new character is UTF-8 wide, fill in padding cells. Have
1918311827fbSnicm 	 * already ensured there is enough room.
1919311827fbSnicm 	 */
19208e39655fSnicm 	for (xx = s->cx + 1; xx < s->cx + width; xx++) {
1921856ed6d3Snicm 		log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
192271a06389Snicm 		grid_view_set_padding(gd, xx, s->cy);
19238e39655fSnicm 		skip = 0;
19248e39655fSnicm 	}
19258e39655fSnicm 
19268e39655fSnicm 	/* If no change, do not draw. */
19275fd00eceSnicm 	if (skip) {
19285fd00eceSnicm 		if (s->cx >= gl->cellsize)
19295fd00eceSnicm 			skip = grid_cells_equal(gc, &grid_default_cell);
19305fd00eceSnicm 		else {
19315fd00eceSnicm 			gce = &gl->celldata[s->cx];
19325fd00eceSnicm 			if (gce->flags & GRID_FLAG_EXTENDED)
19335fd00eceSnicm 				skip = 0;
193468347226Snicm 			else if (gc->flags != gce->flags)
19355fd00eceSnicm 				skip = 0;
19365fd00eceSnicm 			else if (gc->attr != gce->data.attr)
19375fd00eceSnicm 				skip = 0;
19385fd00eceSnicm 			else if (gc->fg != gce->data.fg)
19395fd00eceSnicm 				skip = 0;
19405fd00eceSnicm 			else if (gc->bg != gce->data.bg)
19415fd00eceSnicm 				skip = 0;
194268347226Snicm 			else if (gc->data.width != 1)
194368347226Snicm 				skip = 0;
19448248eab7Snicm 			else if (gc->data.size != 1)
19458248eab7Snicm 				skip = 0;
194668347226Snicm 			else if (gce->data.data != gc->data.data[0])
19475fd00eceSnicm 				skip = 0;
19485fd00eceSnicm 		}
19495fd00eceSnicm 	}
1950311827fbSnicm 
19514d4d49acSnicm 	/* Update the selected flag and set the cell. */
1952bc1b8712Snicm 	selected = screen_check_selection(s, s->cx, s->cy);
19538248eab7Snicm 	if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
1954bc1b8712Snicm 		memcpy(&tmp_gc, gc, sizeof tmp_gc);
1955bc1b8712Snicm 		tmp_gc.flags |= GRID_FLAG_SELECTED;
1956bc1b8712Snicm 		grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
19578248eab7Snicm 	} else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
1958bc1b8712Snicm 		memcpy(&tmp_gc, gc, sizeof tmp_gc);
1959bc1b8712Snicm 		tmp_gc.flags &= ~GRID_FLAG_SELECTED;
1960bc1b8712Snicm 		grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
1961bc1b8712Snicm 	} else if (!skip)
1962311827fbSnicm 		grid_view_set_cell(gd, s->cx, s->cy, gc);
19638248eab7Snicm 	if (selected)
19648248eab7Snicm 		skip = 0;
1965311827fbSnicm 
196684c47e4aSnicm 	/*
196784c47e4aSnicm 	 * Move the cursor. If not wrapping, stick at the last character and
196884c47e4aSnicm 	 * replace it.
196984c47e4aSnicm 	 */
19702af49740Snicm 	not_wrap = !(s->mode & MODE_WRAP);
19712af49740Snicm 	if (s->cx <= sx - not_wrap - width)
19727b470e93Snicm 		screen_write_set_cursor(ctx, s->cx + width, -1);
197384c47e4aSnicm 	else
19742af49740Snicm 		screen_write_set_cursor(ctx,  sx - not_wrap, -1);
1975311827fbSnicm 
19768e39655fSnicm 	/* Create space for character in insert mode. */
19778248eab7Snicm 	if (s->mode & MODE_INSERT) {
19789b53212eSnicm 		screen_write_collect_flush(ctx, 0, __func__);
1979900b44f5Snicm 		ttyctx.num = width;
1980900b44f5Snicm 		tty_write(tty_cmd_insertcharacter, &ttyctx);
1981900b44f5Snicm 	}
19828e39655fSnicm 
19838e39655fSnicm 	/* Write to the screen. */
19848248eab7Snicm 	if (!skip) {
19858e39655fSnicm 		if (selected) {
19868ab25c40Snicm 			screen_select_cell(s, &tmp_gc, gc);
19870011c848Snicm 			ttyctx.cell = &tmp_gc;
19888248eab7Snicm 		} else
19898248eab7Snicm 			ttyctx.cell = gc;
1990714c38deSnicm 		tty_write(tty_cmd_cell, &ttyctx);
19912612f635Snicm 	}
1992311827fbSnicm }
1993311827fbSnicm 
19942af49740Snicm /* Combine a UTF-8 zero-width character onto the previous if necessary. */
19952af49740Snicm static int
19962af49740Snicm screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
1997e91c0c38Snicm {
1998e91c0c38Snicm 	struct screen		*s = ctx->s;
1999e91c0c38Snicm 	struct grid		*gd = s->grid;
20002af49740Snicm 	const struct utf8_data	*ud = &gc->data;
20012af49740Snicm 	u_int			 n, cx = s->cx, cy = s->cy;
20022af49740Snicm 	struct grid_cell	 last;
20032af49740Snicm 	struct tty_ctx		 ttyctx;
20042af49740Snicm 	int			 force_wide = 0, zero_width = 0;
2005e91c0c38Snicm 
20062af49740Snicm 	/*
20072af49740Snicm 	 * Is this character which makes no sense without being combined? If
20082af49740Snicm 	 * this is true then flag it here and discard the character (return 1)
20092af49740Snicm 	 * if we cannot combine it.
20102af49740Snicm 	 */
20112af49740Snicm 	if (utf8_is_zwj(ud))
20122af49740Snicm 		zero_width = 1;
20132af49740Snicm 	else if (utf8_is_vs(ud))
20142af49740Snicm 		zero_width = force_wide = 1;
20152af49740Snicm 	else if (ud->width == 0)
20162af49740Snicm 		zero_width = 1;
20172af49740Snicm 
20182af49740Snicm 	/* Cannot combine empty character or at left. */
20192af49740Snicm 	if (ud->size < 2 || cx == 0)
20202af49740Snicm 		return (zero_width);
20212af49740Snicm 	log_debug("%s: character %.*s at %u,%u (width %u)", __func__,
20222af49740Snicm 	    (int)ud->size, ud->data, cx, cy, ud->width);
20232af49740Snicm 
20242af49740Snicm 	/* Find the cell to combine with. */
20252af49740Snicm 	n = 1;
20262af49740Snicm 	grid_view_get_cell(gd, cx - n, cy, &last);
20272af49740Snicm 	if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) {
20282af49740Snicm 		n = 2;
20292af49740Snicm 		grid_view_get_cell(gd, cx - n, cy, &last);
2030a7969b8bSnicm 	}
20312af49740Snicm 	if (n != last.data.width || (last.flags & GRID_FLAG_PADDING))
20322af49740Snicm 		return (zero_width);
2033e91c0c38Snicm 
20342af49740Snicm 	/*
20352af49740Snicm 	 * Check if we need to combine characters. This could be zero width
20367c01ab0cSnicm 	 * (set above), a modifier character (with an existing Unicode
20372af49740Snicm 	 * character) or a previous ZWJ.
20382af49740Snicm 	 */
20392af49740Snicm 	if (!zero_width) {
20402af49740Snicm 		if (utf8_is_modifier(ud)) {
20412af49740Snicm 			if (last.data.size < 2)
20422af49740Snicm 				return (0);
20432af49740Snicm 			force_wide = 1;
20442af49740Snicm 		} else if (!utf8_has_zwj(&last.data))
20452af49740Snicm 			return (0);
20460fbc0128Snicm 	}
2047e91c0c38Snicm 
2048eed425c7Snicm 	/* Check if this combined character would be too long. */
2049eed425c7Snicm 	if (last.data.size + ud->size > sizeof last.data.data)
2050eed425c7Snicm 		return (0);
2051eed425c7Snicm 
20522af49740Snicm 	/* Combining; flush any pending output. */
20532af49740Snicm 	screen_write_collect_flush(ctx, 0, __func__);
20540fbc0128Snicm 
20552af49740Snicm 	log_debug("%s: %.*s -> %.*s at %u,%u (offset %u, width %u)", __func__,
20562af49740Snicm 	    (int)ud->size, ud->data, (int)last.data.size, last.data.data,
20572af49740Snicm 	    cx - n, cy, n, last.data.width);
2058e91c0c38Snicm 
2059e931849fSnicm 	/* Append the data. */
20602af49740Snicm 	memcpy(last.data.data + last.data.size, ud->data, ud->size);
20612af49740Snicm 	last.data.size += ud->size;
2062e931849fSnicm 
20632af49740Snicm 	/* Force the width to 2 for modifiers and variation selector. */
20642af49740Snicm 	if (last.data.width == 1 && force_wide) {
20652af49740Snicm 		last.data.width = 2;
20662af49740Snicm 		n = 2;
20672af49740Snicm 		cx++;
20682af49740Snicm 	} else
20692af49740Snicm 		force_wide = 0;
2070485d86f6Snicm 
2071e931849fSnicm 	/* Set the new cell. */
20722af49740Snicm 	grid_view_set_cell(gd, cx - n, cy, &last);
20732af49740Snicm 	if (force_wide)
2074173c810fSnicm 		grid_view_set_padding(gd, cx - 1, cy);
2075e91c0c38Snicm 
20762af49740Snicm 	/*
20772af49740Snicm 	 * Redraw the combined cell. If forcing the cell to width 2, reset the
20782af49740Snicm 	 * cached cursor position in the tty, since we don't really know
20792af49740Snicm 	 * whether the terminal thought the character was width 1 or width 2
20802af49740Snicm 	 * and what it is going to do now.
20812af49740Snicm 	 */
20822af49740Snicm 	screen_write_set_cursor(ctx, cx - n, cy);
20832af49740Snicm 	screen_write_initctx(ctx, &ttyctx, 0);
20842af49740Snicm 	ttyctx.cell = &last;
20852af49740Snicm 	ttyctx.num = force_wide; /* reset cached cursor position */
20862af49740Snicm 	tty_write(tty_cmd_cell, &ttyctx);
20872af49740Snicm 	screen_write_set_cursor(ctx, cx, cy);
20882af49740Snicm 
20892af49740Snicm 	return (1);
2090e91c0c38Snicm }
2091e91c0c38Snicm 
2092311827fbSnicm /*
2093311827fbSnicm  * UTF-8 wide characters are a bit of an annoyance. They take up more than one
2094311827fbSnicm  * cell on the screen, so following cells must not be drawn by marking them as
2095311827fbSnicm  * padding.
2096311827fbSnicm  *
2097311827fbSnicm  * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
2098311827fbSnicm  * character, it is necessary to also overwrite any other cells which covered
2099311827fbSnicm  * by the same character.
2100311827fbSnicm  */
21018e39655fSnicm static int
21028e39655fSnicm screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
21038e39655fSnicm     u_int width)
2104311827fbSnicm {
2105311827fbSnicm 	struct screen		*s = ctx->s;
2106311827fbSnicm 	struct grid		*gd = s->grid;
21078e39655fSnicm 	struct grid_cell	 tmp_gc;
2108311827fbSnicm 	u_int			 xx;
21098e39655fSnicm 	int			 done = 0;
2110311827fbSnicm 
21118e39655fSnicm 	if (gc->flags & GRID_FLAG_PADDING) {
2112311827fbSnicm 		/*
2113311827fbSnicm 		 * A padding cell, so clear any following and leading padding
2114311827fbSnicm 		 * cells back to the character. Don't overwrite the current
2115311827fbSnicm 		 * cell as that happens later anyway.
2116311827fbSnicm 		 */
2117311827fbSnicm 		xx = s->cx + 1;
2118311827fbSnicm 		while (--xx > 0) {
21198e39655fSnicm 			grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
21208e39655fSnicm 			if (~tmp_gc.flags & GRID_FLAG_PADDING)
2121311827fbSnicm 				break;
2122856ed6d3Snicm 			log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
2123311827fbSnicm 			grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2124311827fbSnicm 		}
2125311827fbSnicm 
2126311827fbSnicm 		/* Overwrite the character at the start of this padding. */
2127856ed6d3Snicm 		log_debug("%s: character at %u,%u", __func__, xx, s->cy);
2128311827fbSnicm 		grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
21298e39655fSnicm 		done = 1;
21302749bd13Snicm 	}
2131311827fbSnicm 
21322749bd13Snicm 	/*
213368347226Snicm 	 * Overwrite any padding cells that belong to any UTF-8 characters
213468347226Snicm 	 * we'll be overwriting with the current character.
21352749bd13Snicm 	 */
213668347226Snicm 	if (width != 1 ||
213768347226Snicm 	    gc->data.width != 1 ||
213868347226Snicm 	    gc->flags & GRID_FLAG_PADDING) {
21392749bd13Snicm 		xx = s->cx + width - 1;
2140311827fbSnicm 		while (++xx < screen_size_x(s)) {
21418e39655fSnicm 			grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
21428e39655fSnicm 			if (~tmp_gc.flags & GRID_FLAG_PADDING)
2143311827fbSnicm 				break;
21442afb856aSnicm 			log_debug("%s: overwrite at %u,%u", __func__, xx,
21452afb856aSnicm 			    s->cy);
2146c851f995Snicm 			if (gc->flags & GRID_FLAG_TAB) {
2147c851f995Snicm 				memcpy(&tmp_gc, gc, sizeof tmp_gc);
2148c851f995Snicm 				memset(tmp_gc.data.data, 0,
2149c851f995Snicm 				    sizeof tmp_gc.data.data);
2150c851f995Snicm 				*tmp_gc.data.data = ' ';
2151c851f995Snicm 				tmp_gc.data.width = tmp_gc.data.size =
2152c851f995Snicm 				    tmp_gc.data.have = 1;
2153c851f995Snicm 				grid_view_set_cell(gd, xx, s->cy, &tmp_gc);
2154c851f995Snicm 			} else
2155c851f995Snicm 				grid_view_set_cell(gd, xx, s->cy,
2156c851f995Snicm 				    &grid_default_cell);
21578e39655fSnicm 			done = 1;
2158311827fbSnicm 		}
2159de44f258Snicm 	}
2160885f7078Snicm 
21618e39655fSnicm 	return (done);
21628e39655fSnicm }
21638e39655fSnicm 
21649c8da6a8Snicm /* Set external clipboard. */
2165885f7078Snicm void
21661b09dd8dSnicm screen_write_setselection(struct screen_write_ctx *ctx, const char *flags,
21671b09dd8dSnicm     u_char *str, u_int len)
2168f9bce6b9Snicm {
2169f9bce6b9Snicm 	struct tty_ctx	ttyctx;
2170f9bce6b9Snicm 
21717f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 0);
2172f9bce6b9Snicm 	ttyctx.ptr = str;
21731b09dd8dSnicm 	ttyctx.ptr2 = (void *)flags;
2174f9bce6b9Snicm 	ttyctx.num = len;
2175f9bce6b9Snicm 
2176f9bce6b9Snicm 	tty_write(tty_cmd_setselection, &ttyctx);
2177f9bce6b9Snicm }
2178f9bce6b9Snicm 
21799c8da6a8Snicm /* Write unmodified string. */
2180f9bce6b9Snicm void
2181930d157dSnicm screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
2182930d157dSnicm     int allow_invisible_panes)
2183885f7078Snicm {
2184885f7078Snicm 	struct tty_ctx	ttyctx;
2185885f7078Snicm 
21867f9a0536Snicm 	screen_write_initctx(ctx, &ttyctx, 0);
2187885f7078Snicm 	ttyctx.ptr = str;
2188885f7078Snicm 	ttyctx.num = len;
2189930d157dSnicm 	ttyctx.allow_invisible_panes = allow_invisible_panes;
2190885f7078Snicm 
2191885f7078Snicm 	tty_write(tty_cmd_rawstring, &ttyctx);
2192885f7078Snicm }
219383e83a91Snicm 
219483e83a91Snicm /* Turn alternate screen on. */
219583e83a91Snicm void
219683e83a91Snicm screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
219783e83a91Snicm     int cursor)
219883e83a91Snicm {
219983e83a91Snicm 	struct tty_ctx		 ttyctx;
220083e83a91Snicm 	struct window_pane	*wp = ctx->wp;
220183e83a91Snicm 
220283e83a91Snicm 	if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
220383e83a91Snicm 		return;
220450fcbc36Snicm 
220550fcbc36Snicm 	screen_write_collect_flush(ctx, 0, __func__);
220683e83a91Snicm 	screen_alternate_on(ctx->s, gc, cursor);
2207*d62c220eSnicm 
2208*d62c220eSnicm 	if (wp != NULL)
22098b458060Snicm 		layout_fix_panes(wp->window, NULL);
221083e83a91Snicm 
221183e83a91Snicm 	screen_write_initctx(ctx, &ttyctx, 1);
2212924a158aSnicm 	if (ttyctx.redraw_cb != NULL)
221383e83a91Snicm 		ttyctx.redraw_cb(&ttyctx);
221483e83a91Snicm }
221583e83a91Snicm 
221683e83a91Snicm /* Turn alternate screen off. */
221783e83a91Snicm void
221883e83a91Snicm screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
221983e83a91Snicm     int cursor)
222083e83a91Snicm {
222183e83a91Snicm 	struct tty_ctx		 ttyctx;
222283e83a91Snicm 	struct window_pane	*wp = ctx->wp;
222383e83a91Snicm 
222483e83a91Snicm 	if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
222583e83a91Snicm 		return;
222650fcbc36Snicm 
222750fcbc36Snicm 	screen_write_collect_flush(ctx, 0, __func__);
222883e83a91Snicm 	screen_alternate_off(ctx->s, gc, cursor);
2229*d62c220eSnicm 
2230*d62c220eSnicm 	if (wp != NULL)
22318b458060Snicm 		layout_fix_panes(wp->window, NULL);
223283e83a91Snicm 
223383e83a91Snicm 	screen_write_initctx(ctx, &ttyctx, 1);
2234924a158aSnicm 	if (ttyctx.redraw_cb != NULL)
223583e83a91Snicm 		ttyctx.redraw_cb(&ttyctx);
223683e83a91Snicm }
2237