xref: /netbsd-src/external/bsd/tmux/dist/screen-write.c (revision 890b6d91a44b7fcb2dfbcbc1e93463086e462d2d)
199e242abSchristos /* $OpenBSD$ */
2698d5317Sjmmv 
3698d5317Sjmmv /*
4f26e8bc9Schristos  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5698d5317Sjmmv  *
6698d5317Sjmmv  * Permission to use, copy, modify, and distribute this software for any
7698d5317Sjmmv  * purpose with or without fee is hereby granted, provided that the above
8698d5317Sjmmv  * copyright notice and this permission notice appear in all copies.
9698d5317Sjmmv  *
10698d5317Sjmmv  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11698d5317Sjmmv  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12698d5317Sjmmv  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13698d5317Sjmmv  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14698d5317Sjmmv  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15698d5317Sjmmv  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16698d5317Sjmmv  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17698d5317Sjmmv  */
18698d5317Sjmmv 
19698d5317Sjmmv #include <sys/types.h>
20698d5317Sjmmv 
2161fba46bSchristos #include <stdlib.h>
22698d5317Sjmmv #include <string.h>
23698d5317Sjmmv 
24698d5317Sjmmv #include "tmux.h"
25698d5317Sjmmv 
26e271dbb8Schristos static struct screen_write_citem *screen_write_collect_trim(
27e271dbb8Schristos 		    struct screen_write_ctx *, u_int, u_int, u_int, int *);
28e9a2d6faSchristos static void	screen_write_collect_clear(struct screen_write_ctx *, u_int,
29e9a2d6faSchristos 		    u_int);
30e271dbb8Schristos static void	screen_write_collect_scroll(struct screen_write_ctx *, u_int);
31e271dbb8Schristos static void	screen_write_collect_flush(struct screen_write_ctx *, int,
32e271dbb8Schristos 		    const char *);
33e9a2d6faSchristos static int	screen_write_overwrite(struct screen_write_ctx *,
34e9a2d6faSchristos 		    struct grid_cell *, u_int);
35f844e94eSwiz static int	screen_write_combine(struct screen_write_ctx *,
36f844e94eSwiz 		    const struct grid_cell *);
37e9a2d6faSchristos 
38e271dbb8Schristos struct screen_write_citem {
39e9a2d6faSchristos 	u_int				x;
40fe99a117Schristos 	int				wrapped;
41e9a2d6faSchristos 
42e271dbb8Schristos 	enum { TEXT, CLEAR }		type;
43e9a2d6faSchristos 	u_int				used;
44e271dbb8Schristos 	u_int				bg;
45e9a2d6faSchristos 
46e9a2d6faSchristos 	struct grid_cell		gc;
47e9a2d6faSchristos 
48e271dbb8Schristos 	TAILQ_ENTRY(screen_write_citem) entry;
49e9a2d6faSchristos };
50e271dbb8Schristos struct screen_write_cline {
51e271dbb8Schristos 	char				*data;
52e271dbb8Schristos 	TAILQ_HEAD(, screen_write_citem) items;
53e9a2d6faSchristos };
54e271dbb8Schristos TAILQ_HEAD(, screen_write_citem)  screen_write_citem_freelist =
55e271dbb8Schristos     TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist);
56e271dbb8Schristos 
57e271dbb8Schristos static struct screen_write_citem *
58e271dbb8Schristos screen_write_get_citem(void)
59e271dbb8Schristos {
60e271dbb8Schristos     struct screen_write_citem	*ci;
61e271dbb8Schristos 
62e271dbb8Schristos     ci = TAILQ_FIRST(&screen_write_citem_freelist);
63e271dbb8Schristos     if (ci != NULL) {
64e271dbb8Schristos         TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
65e271dbb8Schristos         memset(ci, 0, sizeof *ci);
66e271dbb8Schristos         return (ci);
67e271dbb8Schristos     }
68e271dbb8Schristos     return (xcalloc(1, sizeof *ci));
69e271dbb8Schristos }
70e271dbb8Schristos 
71e271dbb8Schristos static void
72e271dbb8Schristos screen_write_free_citem(struct screen_write_citem *ci)
73e271dbb8Schristos {
74e271dbb8Schristos     TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
75e271dbb8Schristos }
76e9a2d6faSchristos 
770a274e86Schristos static void
780a274e86Schristos screen_write_offset_timer(__unused int fd, __unused short events, void *data)
790a274e86Schristos {
800a274e86Schristos 	struct window	*w = data;
810a274e86Schristos 
820a274e86Schristos 	tty_update_window_offset(w);
830a274e86Schristos }
840a274e86Schristos 
850a274e86Schristos /* Set cursor position. */
860a274e86Schristos static void
870a274e86Schristos screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy)
880a274e86Schristos {
890a274e86Schristos 	struct window_pane	*wp = ctx->wp;
900a274e86Schristos 	struct window		*w;
910a274e86Schristos 	struct screen		*s = ctx->s;
920a274e86Schristos 	struct timeval		 tv = { .tv_usec = 10000 };
930a274e86Schristos 
940a274e86Schristos 	if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy)
950a274e86Schristos 		return;
960a274e86Schristos 
970a274e86Schristos 	if (cx != -1) {
980a274e86Schristos 		if ((u_int)cx > screen_size_x(s)) /* allow last column */
990a274e86Schristos 			cx = screen_size_x(s) - 1;
1000a274e86Schristos 		s->cx = cx;
1010a274e86Schristos 	}
1020a274e86Schristos 	if (cy != -1) {
1030a274e86Schristos 		if ((u_int)cy > screen_size_y(s) - 1)
1040a274e86Schristos 			cy = screen_size_y(s) - 1;
1050a274e86Schristos 		s->cy = cy;
1060a274e86Schristos 	}
1070a274e86Schristos 
1080a274e86Schristos 	if (wp == NULL)
1090a274e86Schristos 		return;
1100a274e86Schristos 	w = wp->window;
1110a274e86Schristos 
1120a274e86Schristos 	if (!event_initialized(&w->offset_timer))
1130a274e86Schristos 		evtimer_set(&w->offset_timer, screen_write_offset_timer, w);
1140a274e86Schristos 	if (!evtimer_pending(&w->offset_timer, NULL))
1150a274e86Schristos 		evtimer_add(&w->offset_timer, &tv);
1160a274e86Schristos }
1170a274e86Schristos 
118e271dbb8Schristos /* Do a full redraw. */
119e271dbb8Schristos static void
120e271dbb8Schristos screen_write_redraw_cb(const struct tty_ctx *ttyctx)
121e271dbb8Schristos {
122e271dbb8Schristos 	struct window_pane	*wp = ttyctx->arg;
123e271dbb8Schristos 
124e271dbb8Schristos 	if (wp != NULL)
125e271dbb8Schristos 		wp->flags |= PANE_REDRAW;
126e271dbb8Schristos }
127e271dbb8Schristos 
128e271dbb8Schristos /* Update context for client. */
129e271dbb8Schristos static int
130e271dbb8Schristos screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
131e271dbb8Schristos {
132e271dbb8Schristos 	struct window_pane	*wp = ttyctx->arg;
133e271dbb8Schristos 
134f844e94eSwiz 	if (ttyctx->allow_invisible_panes) {
135f844e94eSwiz 		if (session_has(c->session, wp->window))
136f844e94eSwiz 			return (1);
137f844e94eSwiz 		return (0);
138f844e94eSwiz 	}
139f844e94eSwiz 
140e271dbb8Schristos 	if (c->session->curw->window != wp->window)
141e271dbb8Schristos 		return (0);
142e271dbb8Schristos 	if (wp->layout_cell == NULL)
143e271dbb8Schristos 		return (0);
144e271dbb8Schristos 
145e271dbb8Schristos 	if (wp->flags & (PANE_REDRAW|PANE_DROP))
146e271dbb8Schristos 		return (-1);
147e271dbb8Schristos 	if (c->flags & CLIENT_REDRAWPANES) {
148e271dbb8Schristos 		/*
149e271dbb8Schristos 		 * Redraw is already deferred to redraw another pane - redraw
150e271dbb8Schristos 		 * this one also when that happens.
151e271dbb8Schristos 		 */
152e271dbb8Schristos 		log_debug("%s: adding %%%u to deferred redraw", __func__,
153e271dbb8Schristos 		    wp->id);
154e271dbb8Schristos 		wp->flags |= PANE_REDRAW;
155e271dbb8Schristos 		return (-1);
156e271dbb8Schristos 	}
157e271dbb8Schristos 
158e271dbb8Schristos 	ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy,
159e271dbb8Schristos 	    &ttyctx->wsx, &ttyctx->wsy);
160e271dbb8Schristos 
161e271dbb8Schristos 	ttyctx->xoff = ttyctx->rxoff = wp->xoff;
162e271dbb8Schristos 	ttyctx->yoff = ttyctx->ryoff = wp->yoff;
163e271dbb8Schristos 
164e271dbb8Schristos 	if (status_at_line(c) == 0)
165e271dbb8Schristos 		ttyctx->yoff += status_line_size(c);
166e271dbb8Schristos 
167e271dbb8Schristos 	return (1);
168e271dbb8Schristos }
169e271dbb8Schristos 
170e271dbb8Schristos /* Set up context for TTY command. */
171e271dbb8Schristos static void
172e271dbb8Schristos screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
173e271dbb8Schristos     int sync)
174e271dbb8Schristos {
175e271dbb8Schristos 	struct screen	*s = ctx->s;
176e271dbb8Schristos 
177e271dbb8Schristos 	memset(ttyctx, 0, sizeof *ttyctx);
178e271dbb8Schristos 
179e271dbb8Schristos 	ttyctx->s = s;
180e271dbb8Schristos 	ttyctx->sx = screen_size_x(s);
181e271dbb8Schristos 	ttyctx->sy = screen_size_y(s);
182e271dbb8Schristos 
183e271dbb8Schristos 	ttyctx->ocx = s->cx;
184e271dbb8Schristos 	ttyctx->ocy = s->cy;
185e271dbb8Schristos 	ttyctx->orlower = s->rlower;
186e271dbb8Schristos 	ttyctx->orupper = s->rupper;
187e271dbb8Schristos 
18846548964Swiz 	memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
18946548964Swiz 	if (ctx->init_ctx_cb != NULL) {
190e271dbb8Schristos 		ctx->init_ctx_cb(ctx, ttyctx);
19146548964Swiz 		if (ttyctx->palette != NULL) {
19246548964Swiz 			if (ttyctx->defaults.fg == 8)
19346548964Swiz 				ttyctx->defaults.fg = ttyctx->palette->fg;
19446548964Swiz 			if (ttyctx->defaults.bg == 8)
19546548964Swiz 				ttyctx->defaults.bg = ttyctx->palette->bg;
19646548964Swiz 		}
19746548964Swiz 	} else {
198e271dbb8Schristos 		ttyctx->redraw_cb = screen_write_redraw_cb;
19946548964Swiz 		if (ctx->wp != NULL) {
20046548964Swiz 			tty_default_colours(&ttyctx->defaults, ctx->wp);
20146548964Swiz 			ttyctx->palette = &ctx->wp->palette;
202e271dbb8Schristos 			ttyctx->set_client_cb = screen_write_set_client_cb;
203e271dbb8Schristos 			ttyctx->arg = ctx->wp;
204e271dbb8Schristos 		}
20546548964Swiz 	}
206e271dbb8Schristos 
20746548964Swiz 	if (~ctx->flags & SCREEN_WRITE_SYNC) {
20846548964Swiz 		/*
20946548964Swiz 		 * For the active pane or for an overlay (no pane), we want to
21046548964Swiz 		 * only use synchronized updates if requested (commands that
21146548964Swiz 		 * move the cursor); for other panes, always use it, since the
21246548964Swiz 		 * cursor will have to move.
21346548964Swiz 		 */
21446548964Swiz 		if (ctx->wp != NULL) {
21546548964Swiz 			if (ctx->wp != ctx->wp->window->active)
21646548964Swiz 				ttyctx->num = 1;
21746548964Swiz 			else
21846548964Swiz 				ttyctx->num = sync;
21946548964Swiz 		} else
22046548964Swiz 			ttyctx->num = 0x10|sync;
221e271dbb8Schristos 		tty_write(tty_cmd_syncstart, ttyctx);
222e271dbb8Schristos 		ctx->flags |= SCREEN_WRITE_SYNC;
223e271dbb8Schristos 	}
224e271dbb8Schristos }
225e271dbb8Schristos 
226e271dbb8Schristos /* Make write list. */
227698d5317Sjmmv void
228e271dbb8Schristos screen_write_make_list(struct screen *s)
229698d5317Sjmmv {
230e9a2d6faSchristos 	u_int	y;
231e9a2d6faSchristos 
232e271dbb8Schristos 	s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list);
233e271dbb8Schristos 	for (y = 0; y < screen_size_y(s); y++)
234e271dbb8Schristos 		TAILQ_INIT(&s->write_list[y].items);
235e271dbb8Schristos }
236e271dbb8Schristos 
237e271dbb8Schristos /* Free write list. */
238e271dbb8Schristos void
239e271dbb8Schristos screen_write_free_list(struct screen *s)
240e271dbb8Schristos {
241e271dbb8Schristos 	u_int	y;
242e271dbb8Schristos 
243e271dbb8Schristos 	for (y = 0; y < screen_size_y(s); y++)
244e271dbb8Schristos 		free(s->write_list[y].data);
245e271dbb8Schristos 	free(s->write_list);
246e271dbb8Schristos }
247e271dbb8Schristos 
248e271dbb8Schristos /* Set up for writing. */
249e271dbb8Schristos static void
250e271dbb8Schristos screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
251e271dbb8Schristos {
252e9a2d6faSchristos 	memset(ctx, 0, sizeof *ctx);
253e9a2d6faSchristos 
254698d5317Sjmmv 	ctx->s = s;
255e9a2d6faSchristos 
256e271dbb8Schristos 	if (ctx->s->write_list == NULL)
257e271dbb8Schristos 		screen_write_make_list(ctx->s);
258e271dbb8Schristos 	ctx->item = screen_write_get_citem();
259e9a2d6faSchristos 
260fe99a117Schristos 	ctx->scrolled = 0;
261fe99a117Schristos 	ctx->bg = 8;
262e271dbb8Schristos }
263e271dbb8Schristos 
264e271dbb8Schristos /* Initialize writing with a pane. */
265e271dbb8Schristos void
266e271dbb8Schristos screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp,
267e271dbb8Schristos     struct screen *s)
268e271dbb8Schristos {
269e271dbb8Schristos 	if (s == NULL)
270e271dbb8Schristos 		s = wp->screen;
271e271dbb8Schristos 	screen_write_init(ctx, s);
272e271dbb8Schristos 	ctx->wp = wp;
273fe99a117Schristos 
27468e6ba84Schristos 	if (log_get_level() != 0) {
27568e6ba84Schristos 		log_debug("%s: size %ux%u, pane %%%u (at %u,%u)",
276e271dbb8Schristos 		    __func__, screen_size_x(ctx->s), screen_size_y(ctx->s),
277e271dbb8Schristos 		    wp->id, wp->xoff, wp->yoff);
2780a274e86Schristos 	}
27968e6ba84Schristos }
280e271dbb8Schristos 
281e271dbb8Schristos /* Initialize writing with a callback. */
282e271dbb8Schristos void
283e271dbb8Schristos screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s,
284e271dbb8Schristos     screen_write_init_ctx_cb cb, void *arg)
285e271dbb8Schristos {
286e271dbb8Schristos 	screen_write_init(ctx, s);
287e271dbb8Schristos 
288e271dbb8Schristos 	ctx->init_ctx_cb = cb;
289e271dbb8Schristos 	ctx->arg = arg;
290e271dbb8Schristos 
291e271dbb8Schristos 	if (log_get_level() != 0) {
292e271dbb8Schristos 		log_debug("%s: size %ux%u, with callback", __func__,
293e271dbb8Schristos 		    screen_size_x(ctx->s), screen_size_y(ctx->s));
294e271dbb8Schristos 	}
295e271dbb8Schristos }
296e271dbb8Schristos 
297e271dbb8Schristos /* Initialize writing. */
298e271dbb8Schristos void
299e271dbb8Schristos screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
300e271dbb8Schristos {
301e271dbb8Schristos 	screen_write_init(ctx, s);
302e271dbb8Schristos 
303e271dbb8Schristos 	if (log_get_level() != 0) {
304e271dbb8Schristos 		log_debug("%s: size %ux%u, no pane", __func__,
305e271dbb8Schristos 		    screen_size_x(ctx->s), screen_size_y(ctx->s));
306e271dbb8Schristos 	}
307698d5317Sjmmv }
308698d5317Sjmmv 
309698d5317Sjmmv /* Finish writing. */
310698d5317Sjmmv void
311e9a2d6faSchristos screen_write_stop(struct screen_write_ctx *ctx)
312698d5317Sjmmv {
313e9a2d6faSchristos 	screen_write_collect_end(ctx);
314e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
315e9a2d6faSchristos 
316e271dbb8Schristos 	screen_write_free_citem(ctx->item);
317698d5317Sjmmv }
318698d5317Sjmmv 
31961fba46bSchristos /* Reset screen state. */
32061fba46bSchristos void
32161fba46bSchristos screen_write_reset(struct screen_write_ctx *ctx)
32261fba46bSchristos {
32361fba46bSchristos 	struct screen	*s = ctx->s;
32461fba46bSchristos 
32561fba46bSchristos 	screen_reset_tabs(s);
32661fba46bSchristos 	screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
32761fba46bSchristos 
3280a274e86Schristos 	s->mode = MODE_CURSOR|MODE_WRAP;
329*890b6d91Swiz 
330f844e94eSwiz 	if (options_get_number(global_options, "extended-keys") == 2)
331*890b6d91Swiz 		s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED;
33261fba46bSchristos 
333e9a2d6faSchristos 	screen_write_clearscreen(ctx, 8);
3340a274e86Schristos 	screen_write_set_cursor(ctx, 0, 0);
33561fba46bSchristos }
33661fba46bSchristos 
337698d5317Sjmmv /* Write character. */
338698d5317Sjmmv void
339e9a2d6faSchristos screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
34099e242abSchristos     u_char ch)
341698d5317Sjmmv {
342e9a2d6faSchristos 	struct grid_cell	gc;
343e9a2d6faSchristos 
344e9a2d6faSchristos 	memcpy(&gc, gcp, sizeof gc);
345e9a2d6faSchristos 
346e9a2d6faSchristos 	utf8_set(&gc.data, ch);
347e9a2d6faSchristos 	screen_write_cell(ctx, &gc);
348698d5317Sjmmv }
349698d5317Sjmmv 
350698d5317Sjmmv /* Calculate string length. */
35199e242abSchristos size_t
352f26e8bc9Schristos screen_write_strlen(const char *fmt, ...)
353698d5317Sjmmv {
354698d5317Sjmmv 	va_list			ap;
355698d5317Sjmmv 	char   	       	       *msg;
356f26e8bc9Schristos 	struct utf8_data	ud;
357f26e8bc9Schristos 	u_char 	      	       *ptr;
358698d5317Sjmmv 	size_t			left, size = 0;
359f26e8bc9Schristos 	enum utf8_state		more;
360698d5317Sjmmv 
361698d5317Sjmmv 	va_start(ap, fmt);
362698d5317Sjmmv 	xvasprintf(&msg, fmt, ap);
363698d5317Sjmmv 	va_end(ap);
364698d5317Sjmmv 
365f26e8bc9Schristos 	ptr = (u_char *)msg;
366698d5317Sjmmv 	while (*ptr != '\0') {
367f26e8bc9Schristos 		if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) {
368698d5317Sjmmv 			ptr++;
369698d5317Sjmmv 
370f26e8bc9Schristos 			left = strlen((char *)ptr);
371f26e8bc9Schristos 			if (left < (size_t)ud.size - 1)
372698d5317Sjmmv 				break;
373f26e8bc9Schristos 			while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE)
374698d5317Sjmmv 				ptr++;
375698d5317Sjmmv 			ptr++;
376698d5317Sjmmv 
377f26e8bc9Schristos 			if (more == UTF8_DONE)
378f26e8bc9Schristos 				size += ud.width;
379698d5317Sjmmv 		} else {
380f26e8bc9Schristos 			if (*ptr > 0x1f && *ptr < 0x7f)
381698d5317Sjmmv 				size++;
382698d5317Sjmmv 			ptr++;
383698d5317Sjmmv 		}
384698d5317Sjmmv 	}
385698d5317Sjmmv 
38661fba46bSchristos 	free(msg);
387698d5317Sjmmv 	return (size);
388698d5317Sjmmv }
389698d5317Sjmmv 
390e271dbb8Schristos /* Write string wrapped over lines. */
391e271dbb8Schristos int
392e271dbb8Schristos screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width,
393e271dbb8Schristos     u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...)
394e271dbb8Schristos {
395e271dbb8Schristos 	struct screen		*s = ctx->s;
396e271dbb8Schristos 	va_list			 ap;
397e271dbb8Schristos 	char			*tmp;
398e271dbb8Schristos 	u_int			 cy = s->cy, i, end, next, idx = 0, at, left;
399e271dbb8Schristos 	struct utf8_data	*text;
400e271dbb8Schristos 	struct grid_cell	 gc;
401e271dbb8Schristos 
402e271dbb8Schristos 	memcpy(&gc, gcp, sizeof gc);
403e271dbb8Schristos 
404e271dbb8Schristos 	va_start(ap, fmt);
405e271dbb8Schristos 	xvasprintf(&tmp, fmt, ap);
406e271dbb8Schristos 	va_end(ap);
407e271dbb8Schristos 
408e271dbb8Schristos 	text = utf8_fromcstr(tmp);
409e271dbb8Schristos 	free(tmp);
410e271dbb8Schristos 
411e271dbb8Schristos 	left = (cx + width) - s->cx;
412e271dbb8Schristos 	for (;;) {
413e271dbb8Schristos 		/* Find the end of what can fit on the line. */
414e271dbb8Schristos 		at = 0;
415e271dbb8Schristos 		for (end = idx; text[end].size != 0; end++) {
416e271dbb8Schristos 			if (text[end].size == 1 && text[end].data[0] == '\n')
417e271dbb8Schristos 				break;
418e271dbb8Schristos 			if (at + text[end].width > left)
419e271dbb8Schristos 				break;
420e271dbb8Schristos 			at += text[end].width;
421e271dbb8Schristos 		}
422e271dbb8Schristos 
423e271dbb8Schristos 		/*
424e271dbb8Schristos 		 * If we're on a space, that's the end. If not, walk back to
425e271dbb8Schristos 		 * try and find one.
426e271dbb8Schristos 		 */
427e271dbb8Schristos 		if (text[end].size == 0)
428e271dbb8Schristos 			next = end;
429e271dbb8Schristos 		else if (text[end].size == 1 && text[end].data[0] == '\n')
430e271dbb8Schristos 			next = end + 1;
431e271dbb8Schristos 		else if (text[end].size == 1 && text[end].data[0] == ' ')
432e271dbb8Schristos 			next = end + 1;
433e271dbb8Schristos 		else {
434e271dbb8Schristos 			for (i = end; i > idx; i--) {
435e271dbb8Schristos 				if (text[i].size == 1 && text[i].data[0] == ' ')
436e271dbb8Schristos 					break;
437e271dbb8Schristos 			}
438e271dbb8Schristos 			if (i != idx) {
439e271dbb8Schristos 				next = i + 1;
440e271dbb8Schristos 				end = i;
441e271dbb8Schristos 			} else
442e271dbb8Schristos 				next = end;
443e271dbb8Schristos 		}
444e271dbb8Schristos 
445e271dbb8Schristos 		/* Print the line. */
446e271dbb8Schristos 		for (i = idx; i < end; i++) {
447e271dbb8Schristos 			utf8_copy(&gc.data, &text[i]);
448e271dbb8Schristos 			screen_write_cell(ctx, &gc);
449e271dbb8Schristos 		}
450e271dbb8Schristos 
451e271dbb8Schristos 		/* If at the bottom, stop. */
452e271dbb8Schristos 		idx = next;
453e271dbb8Schristos 		if (s->cy == cy + lines - 1 || text[idx].size == 0)
454e271dbb8Schristos 			break;
455e271dbb8Schristos 
456e271dbb8Schristos 		screen_write_cursormove(ctx, cx, s->cy + 1, 0);
457e271dbb8Schristos 		left = width;
458e271dbb8Schristos 	}
459e271dbb8Schristos 
460e271dbb8Schristos 	/*
461e271dbb8Schristos 	 * Fail if on the last line and there is more to come or at the end, or
462e271dbb8Schristos 	 * if the text was not entirely consumed.
463e271dbb8Schristos 	 */
464e271dbb8Schristos 	if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) ||
465e271dbb8Schristos 	    text[idx].size != 0) {
466e271dbb8Schristos 		free(text);
467e271dbb8Schristos 		return (0);
468e271dbb8Schristos 	}
469e271dbb8Schristos 	free(text);
470e271dbb8Schristos 
471e271dbb8Schristos 	/*
472e271dbb8Schristos 	 * If no more to come, move to the next line. Otherwise, leave on
473e271dbb8Schristos 	 * the same line (except if at the end).
474e271dbb8Schristos 	 */
475e271dbb8Schristos 	if (!more || s->cx == cx + width)
476e271dbb8Schristos 		screen_write_cursormove(ctx, cx, s->cy + 1, 0);
477e271dbb8Schristos 	return (1);
478e271dbb8Schristos }
479e271dbb8Schristos 
480e271dbb8Schristos /* Write simple string (no maximum length). */
48199e242abSchristos void
482e9a2d6faSchristos screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
48399e242abSchristos     const char *fmt, ...)
484698d5317Sjmmv {
485698d5317Sjmmv 	va_list	ap;
486698d5317Sjmmv 
487698d5317Sjmmv 	va_start(ap, fmt);
488e9a2d6faSchristos 	screen_write_vnputs(ctx, -1, gcp, fmt, ap);
489698d5317Sjmmv 	va_end(ap);
490698d5317Sjmmv }
491698d5317Sjmmv 
492698d5317Sjmmv /* Write string with length limit (-1 for unlimited). */
49399e242abSchristos void
49499e242abSchristos screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen,
495e9a2d6faSchristos     const struct grid_cell *gcp, const char *fmt, ...)
496698d5317Sjmmv {
497698d5317Sjmmv 	va_list	ap;
498698d5317Sjmmv 
499698d5317Sjmmv 	va_start(ap, fmt);
500e9a2d6faSchristos 	screen_write_vnputs(ctx, maxlen, gcp, fmt, ap);
501698d5317Sjmmv 	va_end(ap);
502698d5317Sjmmv }
503698d5317Sjmmv 
504698d5317Sjmmv void
505698d5317Sjmmv screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
506e9a2d6faSchristos     const struct grid_cell *gcp, const char *fmt, va_list ap)
507698d5317Sjmmv {
508e9a2d6faSchristos 	struct grid_cell	gc;
509e9a2d6faSchristos 	struct utf8_data       *ud = &gc.data;
510698d5317Sjmmv 	char   		       *msg;
511f26e8bc9Schristos 	u_char 		       *ptr;
512698d5317Sjmmv 	size_t		 	left, size = 0;
513f26e8bc9Schristos 	enum utf8_state		more;
514698d5317Sjmmv 
515e9a2d6faSchristos 	memcpy(&gc, gcp, sizeof gc);
516698d5317Sjmmv 	xvasprintf(&msg, fmt, ap);
517698d5317Sjmmv 
518f26e8bc9Schristos 	ptr = (u_char *)msg;
519698d5317Sjmmv 	while (*ptr != '\0') {
520e9a2d6faSchristos 		if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) {
521698d5317Sjmmv 			ptr++;
522698d5317Sjmmv 
523f26e8bc9Schristos 			left = strlen((char *)ptr);
524e9a2d6faSchristos 			if (left < (size_t)ud->size - 1)
525698d5317Sjmmv 				break;
526e9a2d6faSchristos 			while ((more = utf8_append(ud, *ptr)) == UTF8_MORE)
527698d5317Sjmmv 				ptr++;
528698d5317Sjmmv 			ptr++;
529698d5317Sjmmv 
530e9a2d6faSchristos 			if (more != UTF8_DONE)
531e9a2d6faSchristos 				continue;
532e9a2d6faSchristos 			if (maxlen > 0 && size + ud->width > (size_t)maxlen) {
533698d5317Sjmmv 				while (size < (size_t)maxlen) {
534e9a2d6faSchristos 					screen_write_putc(ctx, &gc, ' ');
535698d5317Sjmmv 					size++;
536698d5317Sjmmv 				}
537698d5317Sjmmv 				break;
538698d5317Sjmmv 			}
539e9a2d6faSchristos 			size += ud->width;
540e9a2d6faSchristos 			screen_write_cell(ctx, &gc);
541698d5317Sjmmv 		} else {
542698d5317Sjmmv 			if (maxlen > 0 && size + 1 > (size_t)maxlen)
543698d5317Sjmmv 				break;
544698d5317Sjmmv 
54561fba46bSchristos 			if (*ptr == '\001')
546e9a2d6faSchristos 				gc.attr ^= GRID_ATTR_CHARSET;
547e271dbb8Schristos 			else if (*ptr == '\n') {
548e271dbb8Schristos 				screen_write_linefeed(ctx, 0, 8);
549e271dbb8Schristos 				screen_write_carriagereturn(ctx);
550e271dbb8Schristos 			} else if (*ptr > 0x1f && *ptr < 0x7f) {
551698d5317Sjmmv 				size++;
552e9a2d6faSchristos 				screen_write_putc(ctx, &gc, *ptr);
55361fba46bSchristos 			}
554698d5317Sjmmv 			ptr++;
555698d5317Sjmmv 		}
556698d5317Sjmmv 	}
557698d5317Sjmmv 
55861fba46bSchristos 	free(msg);
559698d5317Sjmmv }
560698d5317Sjmmv 
561c7e17de0Schristos /*
562e271dbb8Schristos  * Copy from another screen but without the selection stuff. Assumes the target
563e271dbb8Schristos  * region is already big enough.
564c7e17de0Schristos  */
565c7e17de0Schristos void
566c7e17de0Schristos screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
567c7e17de0Schristos     u_int px, u_int py, u_int nx, u_int ny)
568c7e17de0Schristos {
569c7e17de0Schristos 	struct screen		*s = ctx->s;
570c7e17de0Schristos 	struct grid		*gd = src->grid;
571c7e17de0Schristos 	struct grid_cell	 gc;
572c7e17de0Schristos 	u_int		 	 xx, yy, cx, cy;
573c7e17de0Schristos 
574c7e17de0Schristos 	if (nx == 0 || ny == 0)
575c7e17de0Schristos 		return;
576c7e17de0Schristos 
577c7e17de0Schristos 	cy = s->cy;
578c7e17de0Schristos 	for (yy = py; yy < py + ny; yy++) {
579c7e17de0Schristos 		if (yy >= gd->hsize + gd->sy)
580c7e17de0Schristos 			break;
581c7e17de0Schristos 		cx = s->cx;
582c7e17de0Schristos 		for (xx = px; xx < px + nx; xx++) {
583c7e17de0Schristos 			if (xx >= grid_get_line(gd, yy)->cellsize)
584c7e17de0Schristos 				break;
585c7e17de0Schristos 			grid_get_cell(gd, xx, yy, &gc);
586c7e17de0Schristos 			if (xx + gc.data.width > px + nx)
587c7e17de0Schristos 				break;
588c7e17de0Schristos 			grid_view_set_cell(ctx->s->grid, cx, cy, &gc);
589c7e17de0Schristos 			cx++;
590c7e17de0Schristos 		}
591c7e17de0Schristos 		cy++;
592c7e17de0Schristos 	}
593c7e17de0Schristos }
594c7e17de0Schristos 
595f844e94eSwiz /* Select character set for drawing border lines. */
596f844e94eSwiz static void
597f844e94eSwiz screen_write_box_border_set(enum box_lines lines, int cell_type,
598f844e94eSwiz     struct grid_cell *gc)
599f844e94eSwiz {
600f844e94eSwiz 	switch (lines) {
601f844e94eSwiz         case BOX_LINES_NONE:
602f844e94eSwiz 		break;
603f844e94eSwiz         case BOX_LINES_DOUBLE:
604f844e94eSwiz                 gc->attr &= ~GRID_ATTR_CHARSET;
605f844e94eSwiz                 utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
606f844e94eSwiz 		break;
607f844e94eSwiz         case BOX_LINES_HEAVY:
608f844e94eSwiz                 gc->attr &= ~GRID_ATTR_CHARSET;
609f844e94eSwiz                 utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
610f844e94eSwiz 		break;
611f844e94eSwiz         case BOX_LINES_ROUNDED:
612f844e94eSwiz                 gc->attr &= ~GRID_ATTR_CHARSET;
613f844e94eSwiz                 utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
614f844e94eSwiz 		break;
615f844e94eSwiz         case BOX_LINES_SIMPLE:
616f844e94eSwiz                 gc->attr &= ~GRID_ATTR_CHARSET;
617f844e94eSwiz                 utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
618f844e94eSwiz                 break;
619f844e94eSwiz         case BOX_LINES_PADDED:
620f844e94eSwiz                 gc->attr &= ~GRID_ATTR_CHARSET;
621f844e94eSwiz                 utf8_set(&gc->data, PADDED_BORDERS[cell_type]);
622f844e94eSwiz                 break;
623f844e94eSwiz 	case BOX_LINES_SINGLE:
624f844e94eSwiz 	case BOX_LINES_DEFAULT:
625f844e94eSwiz 		gc->attr |= GRID_ATTR_CHARSET;
626f844e94eSwiz 		utf8_set(&gc->data, CELL_BORDERS[cell_type]);
627f844e94eSwiz 		break;
628f844e94eSwiz 	}
629f844e94eSwiz }
630f844e94eSwiz 
631fe99a117Schristos /* Draw a horizontal line on screen. */
632fe99a117Schristos void
633f844e94eSwiz screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right,
634f844e94eSwiz    enum box_lines lines, const struct grid_cell *border_gc)
635fe99a117Schristos {
636fe99a117Schristos 	struct screen		*s = ctx->s;
637fe99a117Schristos 	struct grid_cell	 gc;
638fe99a117Schristos 	u_int			 cx, cy, i;
639fe99a117Schristos 
640fe99a117Schristos 	cx = s->cx;
641fe99a117Schristos 	cy = s->cy;
642fe99a117Schristos 
643f844e94eSwiz 	if (border_gc != NULL)
644f844e94eSwiz 		memcpy(&gc, border_gc, sizeof gc);
645f844e94eSwiz 	else
646fe99a117Schristos 		memcpy(&gc, &grid_default_cell, sizeof gc);
647fe99a117Schristos 	gc.attr |= GRID_ATTR_CHARSET;
648fe99a117Schristos 
649f844e94eSwiz 	if (left)
650f844e94eSwiz 		screen_write_box_border_set(lines, CELL_LEFTJOIN, &gc);
651f844e94eSwiz 	else
652f844e94eSwiz 		screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
653f844e94eSwiz 	screen_write_cell(ctx, &gc);
654f844e94eSwiz 
655f844e94eSwiz 	screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
656fe99a117Schristos 	for (i = 1; i < nx - 1; i++)
657f844e94eSwiz 		screen_write_cell(ctx, &gc);
658f844e94eSwiz 
659f844e94eSwiz 	if (right)
660f844e94eSwiz 		screen_write_box_border_set(lines, CELL_RIGHTJOIN, &gc);
661f844e94eSwiz 	else
662f844e94eSwiz 		screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
663f844e94eSwiz 	screen_write_cell(ctx, &gc);
664fe99a117Schristos 
6650a274e86Schristos 	screen_write_set_cursor(ctx, cx, cy);
666fe99a117Schristos }
667fe99a117Schristos 
66846548964Swiz /* Draw a vertical line on screen. */
669fe99a117Schristos void
670fe99a117Schristos screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
671fe99a117Schristos {
672fe99a117Schristos 	struct screen		*s = ctx->s;
673fe99a117Schristos 	struct grid_cell	 gc;
674fe99a117Schristos 	u_int			 cx, cy, i;
675fe99a117Schristos 
676fe99a117Schristos 	cx = s->cx;
677fe99a117Schristos 	cy = s->cy;
678fe99a117Schristos 
679fe99a117Schristos 	memcpy(&gc, &grid_default_cell, sizeof gc);
680fe99a117Schristos 	gc.attr |= GRID_ATTR_CHARSET;
681fe99a117Schristos 
682fe99a117Schristos 	screen_write_putc(ctx, &gc, top ? 'w' : 'x');
683fe99a117Schristos 	for (i = 1; i < ny - 1; i++) {
6840a274e86Schristos 		screen_write_set_cursor(ctx, cx, cy + i);
685fe99a117Schristos 		screen_write_putc(ctx, &gc, 'x');
686fe99a117Schristos 	}
6870a274e86Schristos 	screen_write_set_cursor(ctx, cx, cy + ny - 1);
688fe99a117Schristos 	screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
689fe99a117Schristos 
6900a274e86Schristos 	screen_write_set_cursor(ctx, cx, cy);
691fe99a117Schristos }
692fe99a117Schristos 
69330744affSchristos /* Draw a menu on screen. */
69430744affSchristos void
695f844e94eSwiz screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice,
696f844e94eSwiz     enum box_lines lines, const struct grid_cell *menu_gc,
697f844e94eSwiz     const struct grid_cell *border_gc, const struct grid_cell *choice_gc)
69830744affSchristos {
69930744affSchristos 	struct screen		*s = ctx->s;
700e271dbb8Schristos 	struct grid_cell	 default_gc;
701e271dbb8Schristos 	const struct grid_cell	*gc = &default_gc;
702f844e94eSwiz 	u_int			 cx, cy, i, j, width = menu->width;
70330744affSchristos 	const char		*name;
70430744affSchristos 
70530744affSchristos 	cx = s->cx;
70630744affSchristos 	cy = s->cy;
70730744affSchristos 
708f844e94eSwiz 	memcpy(&default_gc, menu_gc, sizeof default_gc);
70930744affSchristos 
710f844e94eSwiz 	screen_write_box(ctx, menu->width + 4, menu->count + 2, lines,
711f844e94eSwiz 	    border_gc, menu->title);
71230744affSchristos 
71330744affSchristos 	for (i = 0; i < menu->count; i++) {
71430744affSchristos 		name = menu->items[i].name;
71530744affSchristos 		if (name == NULL) {
71630744affSchristos 			screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
717f844e94eSwiz 			screen_write_hline(ctx, width + 4, 1, 1, lines,
718f844e94eSwiz 			    border_gc);
719f844e94eSwiz 			continue;
720f844e94eSwiz 		}
721f844e94eSwiz 
72230744affSchristos 		if (choice >= 0 && i == (u_int)choice && *name != '-')
723e271dbb8Schristos 			gc = choice_gc;
724f844e94eSwiz 
725f844e94eSwiz 		screen_write_cursormove(ctx, cx + 1, cy + 1 + i, 0);
726f844e94eSwiz 		for (j = 0; j < width + 2; j++)
727e271dbb8Schristos 			screen_write_putc(ctx, gc, ' ');
728f844e94eSwiz 
72930744affSchristos 		screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
73030744affSchristos 		if (*name == '-') {
731e271dbb8Schristos 			default_gc.attr |= GRID_ATTR_DIM;
732f844e94eSwiz 			format_draw(ctx, gc, width, name + 1, NULL, 0);
733e271dbb8Schristos 			default_gc.attr &= ~GRID_ATTR_DIM;
734f844e94eSwiz 			continue;
73530744affSchristos 		}
736f844e94eSwiz 
737f844e94eSwiz 		format_draw(ctx, gc, width, name, NULL, 0);
738f844e94eSwiz 		gc = &default_gc;
73930744affSchristos 	}
74030744affSchristos 
74130744affSchristos 	screen_write_set_cursor(ctx, cx, cy);
74230744affSchristos }
74330744affSchristos 
744fe99a117Schristos /* Draw a box on screen. */
745fe99a117Schristos void
74646548964Swiz screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny,
74746548964Swiz     enum box_lines lines, const struct grid_cell *gcp, const char *title)
748fe99a117Schristos {
749fe99a117Schristos 	struct screen		*s = ctx->s;
750fe99a117Schristos 	struct grid_cell         gc;
751fe99a117Schristos 	u_int			 cx, cy, i;
752fe99a117Schristos 
753fe99a117Schristos 	cx = s->cx;
754fe99a117Schristos 	cy = s->cy;
755fe99a117Schristos 
75646548964Swiz 	if (gcp != NULL)
75746548964Swiz 		memcpy(&gc, gcp, sizeof gc);
75846548964Swiz 	else
759fe99a117Schristos 		memcpy(&gc, &grid_default_cell, sizeof gc);
76046548964Swiz 
761fe99a117Schristos 	gc.attr |= GRID_ATTR_CHARSET;
76246548964Swiz 	gc.flags |= GRID_FLAG_NOPALETTE;
763fe99a117Schristos 
76446548964Swiz 	/* Draw top border */
76546548964Swiz 	screen_write_box_border_set(lines, CELL_TOPLEFT, &gc);
76646548964Swiz 	screen_write_cell(ctx, &gc);
76746548964Swiz 	screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
768fe99a117Schristos 	for (i = 1; i < nx - 1; i++)
76946548964Swiz 		screen_write_cell(ctx, &gc);
77046548964Swiz 	screen_write_box_border_set(lines, CELL_TOPRIGHT, &gc);
77146548964Swiz 	screen_write_cell(ctx, &gc);
772fe99a117Schristos 
77346548964Swiz 	/* Draw bottom border */
7740a274e86Schristos 	screen_write_set_cursor(ctx, cx, cy + ny - 1);
77546548964Swiz 	screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc);
77646548964Swiz 	screen_write_cell(ctx, &gc);
77746548964Swiz 	screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
778fe99a117Schristos 	for (i = 1; i < nx - 1; i++)
77946548964Swiz 		screen_write_cell(ctx, &gc);
78046548964Swiz 	screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc);
78146548964Swiz 	screen_write_cell(ctx, &gc);
782fe99a117Schristos 
78346548964Swiz 	/* Draw sides */
78446548964Swiz 	screen_write_box_border_set(lines, CELL_TOPBOTTOM, &gc);
785fe99a117Schristos 	for (i = 1; i < ny - 1; i++) {
78646548964Swiz 		/* left side */
7870a274e86Schristos 		screen_write_set_cursor(ctx, cx, cy + i);
78846548964Swiz 		screen_write_cell(ctx, &gc);
78946548964Swiz 		/* right side */
7900a274e86Schristos 		screen_write_set_cursor(ctx, cx + nx - 1, cy + i);
79146548964Swiz 		screen_write_cell(ctx, &gc);
79246548964Swiz 	}
79346548964Swiz 
79446548964Swiz 	if (title != NULL) {
79546548964Swiz 		gc.attr &= ~GRID_ATTR_CHARSET;
79646548964Swiz 		screen_write_cursormove(ctx, cx + 2, cy, 0);
79746548964Swiz 		format_draw(ctx, &gc, nx - 4, title, NULL, 0);
798fe99a117Schristos 	}
799fe99a117Schristos 
8000a274e86Schristos 	screen_write_set_cursor(ctx, cx, cy);
801fe99a117Schristos }
802fe99a117Schristos 
803c7e17de0Schristos /*
804c7e17de0Schristos  * Write a preview version of a window. Assumes target area is big enough and
805c7e17de0Schristos  * already cleared.
806c7e17de0Schristos  */
807fe99a117Schristos void
808fe99a117Schristos screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
809fe99a117Schristos     u_int ny)
810fe99a117Schristos {
811fe99a117Schristos 	struct screen		*s = ctx->s;
812fe99a117Schristos 	struct grid_cell	 gc;
813fe99a117Schristos 	u_int			 cx, cy, px, py;
814fe99a117Schristos 
815fe99a117Schristos 	cx = s->cx;
816fe99a117Schristos 	cy = s->cy;
817fe99a117Schristos 
818fe99a117Schristos 	/*
819fe99a117Schristos 	 * If the cursor is on, pick the area around the cursor, otherwise use
820fe99a117Schristos 	 * the top left.
821fe99a117Schristos 	 */
822fe99a117Schristos 	if (src->mode & MODE_CURSOR) {
823fe99a117Schristos 		px = src->cx;
824fe99a117Schristos 		if (px < nx / 3)
825fe99a117Schristos 			px = 0;
826fe99a117Schristos 		else
827fe99a117Schristos 			px = px - nx / 3;
828fe99a117Schristos 		if (px + nx > screen_size_x(src)) {
829fe99a117Schristos 			if (nx > screen_size_x(src))
830fe99a117Schristos 				px = 0;
831fe99a117Schristos 			else
832fe99a117Schristos 				px = screen_size_x(src) - nx;
833fe99a117Schristos 		}
834fe99a117Schristos 		py = src->cy;
835fe99a117Schristos 		if (py < ny / 3)
836fe99a117Schristos 			py = 0;
837fe99a117Schristos 		else
838fe99a117Schristos 			py = py - ny / 3;
839fe99a117Schristos 		if (py + ny > screen_size_y(src)) {
840fe99a117Schristos 			if (ny > screen_size_y(src))
841fe99a117Schristos 				py = 0;
842fe99a117Schristos 			else
843fe99a117Schristos 				py = screen_size_y(src) - ny;
844fe99a117Schristos 		}
845fe99a117Schristos 	} else {
846fe99a117Schristos 		px = 0;
847fe99a117Schristos 		py = 0;
848fe99a117Schristos 	}
849fe99a117Schristos 
850c7e17de0Schristos 	screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
851fe99a117Schristos 
852fe99a117Schristos 	if (src->mode & MODE_CURSOR) {
853fe99a117Schristos 		grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
854fe99a117Schristos 		gc.attr |= GRID_ATTR_REVERSE;
8550a274e86Schristos 		screen_write_set_cursor(ctx, cx + (src->cx - px),
856fe99a117Schristos 		    cy + (src->cy - py));
857fe99a117Schristos 		screen_write_cell(ctx, &gc);
858fe99a117Schristos 	}
859fe99a117Schristos }
860fe99a117Schristos 
86161fba46bSchristos /* Set a mode. */
86261fba46bSchristos void
86361fba46bSchristos screen_write_mode_set(struct screen_write_ctx *ctx, int mode)
86461fba46bSchristos {
86561fba46bSchristos 	struct screen	*s = ctx->s;
86661fba46bSchristos 
86761fba46bSchristos 	s->mode |= mode;
86846548964Swiz 
86946548964Swiz 	if (log_get_level() != 0)
87046548964Swiz 		log_debug("%s: %s", __func__, screen_mode_to_string(mode));
87161fba46bSchristos }
87261fba46bSchristos 
87361fba46bSchristos /* Clear a mode. */
87461fba46bSchristos void
87561fba46bSchristos screen_write_mode_clear(struct screen_write_ctx *ctx, int mode)
87661fba46bSchristos {
87761fba46bSchristos 	struct screen	*s = ctx->s;
87861fba46bSchristos 
87961fba46bSchristos 	s->mode &= ~mode;
88046548964Swiz 
88146548964Swiz 	if (log_get_level() != 0)
88246548964Swiz 		log_debug("%s: %s", __func__, screen_mode_to_string(mode));
883698d5317Sjmmv }
884698d5317Sjmmv 
885698d5317Sjmmv /* Cursor up by ny. */
886698d5317Sjmmv void
887698d5317Sjmmv screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
888698d5317Sjmmv {
889698d5317Sjmmv 	struct screen	*s = ctx->s;
8900a274e86Schristos 	u_int		 cx = s->cx, cy = s->cy;
891698d5317Sjmmv 
892698d5317Sjmmv 	if (ny == 0)
893698d5317Sjmmv 		ny = 1;
894698d5317Sjmmv 
8950a274e86Schristos 	if (cy < s->rupper) {
896698d5317Sjmmv 		/* Above region. */
8970a274e86Schristos 		if (ny > cy)
8980a274e86Schristos 			ny = cy;
899698d5317Sjmmv 	} else {
900698d5317Sjmmv 		/* Below region. */
9010a274e86Schristos 		if (ny > cy - s->rupper)
9020a274e86Schristos 			ny = cy - s->rupper;
903698d5317Sjmmv 	}
9040a274e86Schristos 	if (cx == screen_size_x(s))
9050a274e86Schristos 		cx--;
906698d5317Sjmmv 
9070a274e86Schristos 	cy -= ny;
9080a274e86Schristos 
9090a274e86Schristos 	screen_write_set_cursor(ctx, cx, cy);
910698d5317Sjmmv }
911698d5317Sjmmv 
912698d5317Sjmmv /* Cursor down by ny. */
913698d5317Sjmmv void
914698d5317Sjmmv screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
915698d5317Sjmmv {
916698d5317Sjmmv 	struct screen	*s = ctx->s;
9170a274e86Schristos 	u_int		 cx = s->cx, cy = s->cy;
918698d5317Sjmmv 
919698d5317Sjmmv 	if (ny == 0)
920698d5317Sjmmv 		ny = 1;
921698d5317Sjmmv 
9220a274e86Schristos 	if (cy > s->rlower) {
923698d5317Sjmmv 		/* Below region. */
9240a274e86Schristos 		if (ny > screen_size_y(s) - 1 - cy)
9250a274e86Schristos 			ny = screen_size_y(s) - 1 - cy;
926698d5317Sjmmv 	} else {
927698d5317Sjmmv 		/* Above region. */
9280a274e86Schristos 		if (ny > s->rlower - cy)
9290a274e86Schristos 			ny = s->rlower - cy;
930698d5317Sjmmv 	}
9310a274e86Schristos 	if (cx == screen_size_x(s))
9320a274e86Schristos 	    cx--;
9330a274e86Schristos 	else if (ny == 0)
934698d5317Sjmmv 		return;
935698d5317Sjmmv 
9360a274e86Schristos 	cy += ny;
9370a274e86Schristos 
9380a274e86Schristos 	screen_write_set_cursor(ctx, cx, cy);
939698d5317Sjmmv }
940698d5317Sjmmv 
941698d5317Sjmmv /* Cursor right by nx. */
942698d5317Sjmmv void
943698d5317Sjmmv screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
944698d5317Sjmmv {
945698d5317Sjmmv 	struct screen	*s = ctx->s;
9460a274e86Schristos 	u_int		 cx = s->cx, cy = s->cy;
947698d5317Sjmmv 
948698d5317Sjmmv 	if (nx == 0)
949698d5317Sjmmv 		nx = 1;
950698d5317Sjmmv 
9510a274e86Schristos 	if (nx > screen_size_x(s) - 1 - cx)
9520a274e86Schristos 		nx = screen_size_x(s) - 1 - cx;
953698d5317Sjmmv 	if (nx == 0)
954698d5317Sjmmv 		return;
955698d5317Sjmmv 
9560a274e86Schristos 	cx += nx;
9570a274e86Schristos 
9580a274e86Schristos 	screen_write_set_cursor(ctx, cx, cy);
959698d5317Sjmmv }
960698d5317Sjmmv 
961698d5317Sjmmv /* Cursor left by nx. */
962698d5317Sjmmv void
963698d5317Sjmmv screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
964698d5317Sjmmv {
965698d5317Sjmmv 	struct screen	*s = ctx->s;
9660a274e86Schristos 	u_int		 cx = s->cx, cy = s->cy;
967698d5317Sjmmv 
968698d5317Sjmmv 	if (nx == 0)
969698d5317Sjmmv 		nx = 1;
970698d5317Sjmmv 
9710a274e86Schristos 	if (nx > cx)
9720a274e86Schristos 		nx = cx;
973698d5317Sjmmv 	if (nx == 0)
974698d5317Sjmmv 		return;
975698d5317Sjmmv 
9760a274e86Schristos 	cx -= nx;
9770a274e86Schristos 
9780a274e86Schristos 	screen_write_set_cursor(ctx, cx, cy);
979698d5317Sjmmv }
980698d5317Sjmmv 
981698d5317Sjmmv /* Backspace; cursor left unless at start of wrapped line when can move up. */
982698d5317Sjmmv void
983698d5317Sjmmv screen_write_backspace(struct screen_write_ctx *ctx)
984698d5317Sjmmv {
985698d5317Sjmmv 	struct screen		*s = ctx->s;
986698d5317Sjmmv 	struct grid_line	*gl;
9870a274e86Schristos 	u_int			 cx = s->cx, cy = s->cy;
988698d5317Sjmmv 
9890a274e86Schristos 	if (cx == 0) {
9900a274e86Schristos 		if (cy == 0)
991698d5317Sjmmv 			return;
9920a274e86Schristos 		gl = grid_get_line(s->grid, s->grid->hsize + cy - 1);
993698d5317Sjmmv 		if (gl->flags & GRID_LINE_WRAPPED) {
9940a274e86Schristos 			cy--;
9950a274e86Schristos 			cx = screen_size_x(s) - 1;
996698d5317Sjmmv 		}
997698d5317Sjmmv 	} else
9980a274e86Schristos 		cx--;
9990a274e86Schristos 
10000a274e86Schristos 	screen_write_set_cursor(ctx, cx, cy);
1001698d5317Sjmmv }
1002698d5317Sjmmv 
1003698d5317Sjmmv /* VT100 alignment test. */
1004698d5317Sjmmv void
1005698d5317Sjmmv screen_write_alignmenttest(struct screen_write_ctx *ctx)
1006698d5317Sjmmv {
1007698d5317Sjmmv 	struct screen		*s = ctx->s;
1008698d5317Sjmmv 	struct tty_ctx	 	 ttyctx;
1009698d5317Sjmmv 	struct grid_cell       	 gc;
1010698d5317Sjmmv 	u_int			 xx, yy;
1011698d5317Sjmmv 
1012698d5317Sjmmv 	memcpy(&gc, &grid_default_cell, sizeof gc);
1013f26e8bc9Schristos 	utf8_set(&gc.data, 'E');
1014698d5317Sjmmv 
1015f844e94eSwiz #ifdef ENABLE_SIXEL
1016f844e94eSwiz 	if (image_free_all(s) && ctx->wp != NULL)
1017f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1018f844e94eSwiz #endif
1019f844e94eSwiz 
1020698d5317Sjmmv 	for (yy = 0; yy < screen_size_y(s); yy++) {
1021698d5317Sjmmv 		for (xx = 0; xx < screen_size_x(s); xx++)
1022698d5317Sjmmv 			grid_view_set_cell(s->grid, xx, yy, &gc);
1023698d5317Sjmmv 	}
1024698d5317Sjmmv 
10250a274e86Schristos 	screen_write_set_cursor(ctx, 0, 0);
1026698d5317Sjmmv 
1027698d5317Sjmmv 	s->rupper = 0;
1028698d5317Sjmmv 	s->rlower = screen_size_y(s) - 1;
1029698d5317Sjmmv 
1030e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 1);
10310a274e86Schristos 
1032e9a2d6faSchristos 	screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
1033698d5317Sjmmv 	tty_write(tty_cmd_alignmenttest, &ttyctx);
1034698d5317Sjmmv }
1035698d5317Sjmmv 
1036698d5317Sjmmv /* Insert nx characters. */
1037698d5317Sjmmv void
1038e9a2d6faSchristos screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1039698d5317Sjmmv {
1040698d5317Sjmmv 	struct screen	*s = ctx->s;
1041698d5317Sjmmv 	struct tty_ctx	 ttyctx;
1042698d5317Sjmmv 
1043698d5317Sjmmv 	if (nx == 0)
1044698d5317Sjmmv 		nx = 1;
1045698d5317Sjmmv 
1046698d5317Sjmmv 	if (nx > screen_size_x(s) - s->cx)
1047698d5317Sjmmv 		nx = screen_size_x(s) - s->cx;
1048698d5317Sjmmv 	if (nx == 0)
1049698d5317Sjmmv 		return;
1050698d5317Sjmmv 
1051e9a2d6faSchristos 	if (s->cx > screen_size_x(s) - 1)
1052e9a2d6faSchristos 		return;
1053698d5317Sjmmv 
1054f844e94eSwiz #ifdef ENABLE_SIXEL
1055f844e94eSwiz 	if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1056f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1057f844e94eSwiz #endif
1058f844e94eSwiz 
1059e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 0);
1060e9a2d6faSchristos 	ttyctx.bg = bg;
1061698d5317Sjmmv 
1062e9a2d6faSchristos 	grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg);
1063e9a2d6faSchristos 
1064e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
1065698d5317Sjmmv 	ttyctx.num = nx;
1066698d5317Sjmmv 	tty_write(tty_cmd_insertcharacter, &ttyctx);
1067698d5317Sjmmv }
1068698d5317Sjmmv 
1069698d5317Sjmmv /* Delete nx characters. */
1070698d5317Sjmmv void
1071e9a2d6faSchristos screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1072698d5317Sjmmv {
1073698d5317Sjmmv 	struct screen	*s = ctx->s;
1074698d5317Sjmmv 	struct tty_ctx	 ttyctx;
1075698d5317Sjmmv 
1076698d5317Sjmmv 	if (nx == 0)
1077698d5317Sjmmv 		nx = 1;
1078698d5317Sjmmv 
1079698d5317Sjmmv 	if (nx > screen_size_x(s) - s->cx)
1080698d5317Sjmmv 		nx = screen_size_x(s) - s->cx;
1081698d5317Sjmmv 	if (nx == 0)
1082698d5317Sjmmv 		return;
1083698d5317Sjmmv 
1084e9a2d6faSchristos 	if (s->cx > screen_size_x(s) - 1)
1085e9a2d6faSchristos 		return;
1086698d5317Sjmmv 
1087f844e94eSwiz #ifdef ENABLE_SIXEL
1088f844e94eSwiz 	if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1089f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1090f844e94eSwiz #endif
1091f844e94eSwiz 
1092e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 0);
1093e9a2d6faSchristos 	ttyctx.bg = bg;
1094698d5317Sjmmv 
1095e9a2d6faSchristos 	grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg);
1096e9a2d6faSchristos 
1097e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
1098698d5317Sjmmv 	ttyctx.num = nx;
1099698d5317Sjmmv 	tty_write(tty_cmd_deletecharacter, &ttyctx);
1100698d5317Sjmmv }
1101698d5317Sjmmv 
110261fba46bSchristos /* Clear nx characters. */
110361fba46bSchristos void
1104fe99a117Schristos screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
110561fba46bSchristos {
110661fba46bSchristos 	struct screen	*s = ctx->s;
110761fba46bSchristos 	struct tty_ctx	 ttyctx;
110861fba46bSchristos 
110961fba46bSchristos 	if (nx == 0)
111061fba46bSchristos 		nx = 1;
111161fba46bSchristos 
111261fba46bSchristos 	if (nx > screen_size_x(s) - s->cx)
111361fba46bSchristos 		nx = screen_size_x(s) - s->cx;
111461fba46bSchristos 	if (nx == 0)
111561fba46bSchristos 		return;
111661fba46bSchristos 
1117e9a2d6faSchristos 	if (s->cx > screen_size_x(s) - 1)
1118e9a2d6faSchristos 		return;
111961fba46bSchristos 
1120f844e94eSwiz #ifdef ENABLE_SIXEL
1121f844e94eSwiz 	if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1122f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1123f844e94eSwiz #endif
1124f844e94eSwiz 
1125e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 0);
1126fe99a117Schristos 	ttyctx.bg = bg;
112761fba46bSchristos 
1128fe99a117Schristos 	grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg);
1129e9a2d6faSchristos 
1130e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
113161fba46bSchristos 	ttyctx.num = nx;
113261fba46bSchristos 	tty_write(tty_cmd_clearcharacter, &ttyctx);
113361fba46bSchristos }
113461fba46bSchristos 
1135698d5317Sjmmv /* Insert ny lines. */
1136698d5317Sjmmv void
1137e9a2d6faSchristos screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1138698d5317Sjmmv {
1139698d5317Sjmmv 	struct screen	*s = ctx->s;
1140e9a2d6faSchristos 	struct grid	*gd = s->grid;
1141698d5317Sjmmv 	struct tty_ctx	 ttyctx;
1142698d5317Sjmmv 
1143f844e94eSwiz #ifdef ENABLE_SIXEL
1144f844e94eSwiz 	u_int		 sy = screen_size_y(s);
1145f844e94eSwiz #endif
1146f844e94eSwiz 
1147698d5317Sjmmv 	if (ny == 0)
1148698d5317Sjmmv 		ny = 1;
1149698d5317Sjmmv 
1150f844e94eSwiz #ifdef ENABLE_SIXEL
1151f844e94eSwiz 	if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1152f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1153f844e94eSwiz #endif
1154f844e94eSwiz 
1155698d5317Sjmmv 	if (s->cy < s->rupper || s->cy > s->rlower) {
1156698d5317Sjmmv 		if (ny > screen_size_y(s) - s->cy)
1157698d5317Sjmmv 			ny = screen_size_y(s) - s->cy;
1158698d5317Sjmmv 		if (ny == 0)
1159698d5317Sjmmv 			return;
1160698d5317Sjmmv 
1161e271dbb8Schristos 		screen_write_initctx(ctx, &ttyctx, 1);
1162e9a2d6faSchristos 		ttyctx.bg = bg;
1163698d5317Sjmmv 
1164e9a2d6faSchristos 		grid_view_insert_lines(gd, s->cy, ny, bg);
1165698d5317Sjmmv 
1166e271dbb8Schristos 		screen_write_collect_flush(ctx, 0, __func__);
1167698d5317Sjmmv 		ttyctx.num = ny;
1168698d5317Sjmmv 		tty_write(tty_cmd_insertline, &ttyctx);
1169698d5317Sjmmv 		return;
1170698d5317Sjmmv 	}
1171698d5317Sjmmv 
1172698d5317Sjmmv 	if (ny > s->rlower + 1 - s->cy)
1173698d5317Sjmmv 		ny = s->rlower + 1 - s->cy;
1174698d5317Sjmmv 	if (ny == 0)
1175698d5317Sjmmv 		return;
1176698d5317Sjmmv 
1177e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 1);
1178e9a2d6faSchristos 	ttyctx.bg = bg;
1179698d5317Sjmmv 
1180698d5317Sjmmv 	if (s->cy < s->rupper || s->cy > s->rlower)
1181e9a2d6faSchristos 		grid_view_insert_lines(gd, s->cy, ny, bg);
1182698d5317Sjmmv 	else
1183e9a2d6faSchristos 		grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg);
1184698d5317Sjmmv 
1185e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
1186e271dbb8Schristos 
1187698d5317Sjmmv 	ttyctx.num = ny;
1188698d5317Sjmmv 	tty_write(tty_cmd_insertline, &ttyctx);
1189698d5317Sjmmv }
1190698d5317Sjmmv 
1191698d5317Sjmmv /* Delete ny lines. */
1192698d5317Sjmmv void
1193e9a2d6faSchristos screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1194698d5317Sjmmv {
1195698d5317Sjmmv 	struct screen	*s = ctx->s;
1196e9a2d6faSchristos 	struct grid	*gd = s->grid;
1197698d5317Sjmmv 	struct tty_ctx	 ttyctx;
1198f844e94eSwiz 	u_int		 sy = screen_size_y(s);
1199698d5317Sjmmv 
1200698d5317Sjmmv 	if (ny == 0)
1201698d5317Sjmmv 		ny = 1;
1202698d5317Sjmmv 
1203f844e94eSwiz #ifdef ENABLE_SIXEL
1204f844e94eSwiz 	if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1205f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1206f844e94eSwiz #endif
1207f844e94eSwiz 
1208698d5317Sjmmv 	if (s->cy < s->rupper || s->cy > s->rlower) {
1209f844e94eSwiz 		if (ny > sy - s->cy)
1210f844e94eSwiz 			ny = sy - s->cy;
1211698d5317Sjmmv 		if (ny == 0)
1212698d5317Sjmmv 			return;
1213698d5317Sjmmv 
1214e271dbb8Schristos 		screen_write_initctx(ctx, &ttyctx, 1);
1215e9a2d6faSchristos 		ttyctx.bg = bg;
1216698d5317Sjmmv 
1217e9a2d6faSchristos 		grid_view_delete_lines(gd, s->cy, ny, bg);
1218698d5317Sjmmv 
1219e271dbb8Schristos 		screen_write_collect_flush(ctx, 0, __func__);
1220698d5317Sjmmv 		ttyctx.num = ny;
1221698d5317Sjmmv 		tty_write(tty_cmd_deleteline, &ttyctx);
1222698d5317Sjmmv 		return;
1223698d5317Sjmmv 	}
1224698d5317Sjmmv 
1225698d5317Sjmmv 	if (ny > s->rlower + 1 - s->cy)
1226698d5317Sjmmv 		ny = s->rlower + 1 - s->cy;
1227698d5317Sjmmv 	if (ny == 0)
1228698d5317Sjmmv 		return;
1229698d5317Sjmmv 
1230e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 1);
1231e9a2d6faSchristos 	ttyctx.bg = bg;
1232698d5317Sjmmv 
1233698d5317Sjmmv 	if (s->cy < s->rupper || s->cy > s->rlower)
1234e9a2d6faSchristos 		grid_view_delete_lines(gd, s->cy, ny, bg);
1235698d5317Sjmmv 	else
1236e9a2d6faSchristos 		grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg);
1237698d5317Sjmmv 
1238e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
1239698d5317Sjmmv 	ttyctx.num = ny;
1240698d5317Sjmmv 	tty_write(tty_cmd_deleteline, &ttyctx);
1241698d5317Sjmmv }
1242698d5317Sjmmv 
1243698d5317Sjmmv /* Clear line at cursor. */
1244698d5317Sjmmv void
1245e9a2d6faSchristos screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
1246698d5317Sjmmv {
1247698d5317Sjmmv 	struct screen			*s = ctx->s;
1248e9a2d6faSchristos 	struct grid_line		*gl;
1249e9a2d6faSchristos 	u_int				 sx = screen_size_x(s);
1250e271dbb8Schristos 	struct screen_write_citem	*ci = ctx->item;
1251698d5317Sjmmv 
1252c7e17de0Schristos 	gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
12530a274e86Schristos 	if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
1254e9a2d6faSchristos 		return;
1255698d5317Sjmmv 
1256f844e94eSwiz #ifdef ENABLE_SIXEL
1257f844e94eSwiz 	if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1258f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1259f844e94eSwiz #endif
1260f844e94eSwiz 
1261e9a2d6faSchristos 	grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1262e9a2d6faSchristos 
1263e9a2d6faSchristos 	screen_write_collect_clear(ctx, s->cy, 1);
1264e271dbb8Schristos 	ci->x = 0;
1265e271dbb8Schristos 	ci->used = sx;
1266e271dbb8Schristos 	ci->type = CLEAR;
1267e271dbb8Schristos 	ci->bg = bg;
1268e271dbb8Schristos 	TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1269e271dbb8Schristos 	ctx->item = screen_write_get_citem();
1270698d5317Sjmmv }
1271698d5317Sjmmv 
1272698d5317Sjmmv /* Clear to end of line from cursor. */
1273698d5317Sjmmv void
1274e9a2d6faSchristos screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
1275698d5317Sjmmv {
1276698d5317Sjmmv 	struct screen			*s = ctx->s;
1277e9a2d6faSchristos 	struct grid_line		*gl;
1278e9a2d6faSchristos 	u_int				 sx = screen_size_x(s);
1279e271dbb8Schristos 	struct screen_write_citem	*ci = ctx->item, *before;
1280e271dbb8Schristos 
1281e271dbb8Schristos 	if (s->cx == 0) {
1282e271dbb8Schristos 		screen_write_clearline(ctx, bg);
1283e271dbb8Schristos 		return;
1284e271dbb8Schristos 	}
1285698d5317Sjmmv 
1286c7e17de0Schristos 	gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
12870a274e86Schristos 	if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
1288e9a2d6faSchristos 		return;
1289698d5317Sjmmv 
1290f844e94eSwiz #ifdef ENABLE_SIXEL
1291f844e94eSwiz 	if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1292f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1293f844e94eSwiz #endif
1294f844e94eSwiz 
1295e9a2d6faSchristos 	grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
1296698d5317Sjmmv 
1297e271dbb8Schristos  	before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
1298e271dbb8Schristos 	ci->x = s->cx;
1299e271dbb8Schristos 	ci->used = sx - s->cx;
1300e271dbb8Schristos 	ci->type = CLEAR;
1301e271dbb8Schristos 	ci->bg = bg;
1302e271dbb8Schristos 	if (before == NULL)
1303e271dbb8Schristos 		TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1304e271dbb8Schristos 	else
1305e271dbb8Schristos 		TAILQ_INSERT_BEFORE(before, ci, entry);
1306e271dbb8Schristos 	ctx->item = screen_write_get_citem();
1307698d5317Sjmmv }
1308698d5317Sjmmv 
1309698d5317Sjmmv /* Clear to start of line from cursor. */
1310698d5317Sjmmv void
1311e9a2d6faSchristos screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
1312698d5317Sjmmv {
1313698d5317Sjmmv 	struct screen			 *s = ctx->s;
1314e9a2d6faSchristos 	u_int				 sx = screen_size_x(s);
1315e271dbb8Schristos 	struct screen_write_citem	*ci = ctx->item, *before;
1316698d5317Sjmmv 
1317e271dbb8Schristos 	if (s->cx >= sx - 1) {
1318e271dbb8Schristos 		screen_write_clearline(ctx, bg);
1319e271dbb8Schristos 		return;
1320e271dbb8Schristos 	}
1321698d5317Sjmmv 
1322f844e94eSwiz #ifdef ENABLE_SIXEL
1323f844e94eSwiz 	if (image_check_line(s, s->cy, 1) && ctx->wp != NULL)
1324f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1325f844e94eSwiz #endif
1326f844e94eSwiz 
1327698d5317Sjmmv 	if (s->cx > sx - 1)
1328e9a2d6faSchristos 		grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1329698d5317Sjmmv 	else
1330e9a2d6faSchristos 		grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1331698d5317Sjmmv 
1332e271dbb8Schristos 	before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
1333e271dbb8Schristos 	ci->x = 0;
1334e271dbb8Schristos 	ci->used = s->cx + 1;
1335e271dbb8Schristos 	ci->type = CLEAR;
1336e271dbb8Schristos 	ci->bg = bg;
1337e271dbb8Schristos 	if (before == NULL)
1338e271dbb8Schristos 		TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1339e271dbb8Schristos 	else
1340e271dbb8Schristos 		TAILQ_INSERT_BEFORE(before, ci, entry);
1341e271dbb8Schristos 	ctx->item = screen_write_get_citem();
1342698d5317Sjmmv }
1343698d5317Sjmmv 
1344698d5317Sjmmv /* Move cursor to px,py. */
1345698d5317Sjmmv void
13460a274e86Schristos screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
13470a274e86Schristos     int origin)
1348698d5317Sjmmv {
1349698d5317Sjmmv 	struct screen	*s = ctx->s;
1350698d5317Sjmmv 
13510a274e86Schristos 	if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
13520a274e86Schristos 		if ((u_int)py > s->rlower - s->rupper)
13530a274e86Schristos 			py = s->rlower;
13540a274e86Schristos 		else
13550a274e86Schristos 			py += s->rupper;
13560a274e86Schristos 	}
13570a274e86Schristos 
13580a274e86Schristos 	if (px != -1 && (u_int)px > screen_size_x(s) - 1)
1359698d5317Sjmmv 		px = screen_size_x(s) - 1;
13600a274e86Schristos 	if (py != -1 && (u_int)py > screen_size_y(s) - 1)
1361698d5317Sjmmv 		py = screen_size_y(s) - 1;
1362698d5317Sjmmv 
1363e271dbb8Schristos 	log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
13640a274e86Schristos 	screen_write_set_cursor(ctx, px, py);
1365698d5317Sjmmv }
1366698d5317Sjmmv 
1367698d5317Sjmmv /* Reverse index (up with scroll). */
1368698d5317Sjmmv void
1369fe99a117Schristos screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
1370698d5317Sjmmv {
1371698d5317Sjmmv 	struct screen	*s = ctx->s;
1372698d5317Sjmmv 	struct tty_ctx	 ttyctx;
1373698d5317Sjmmv 
1374e271dbb8Schristos 	if (s->cy == s->rupper) {
1375f844e94eSwiz #ifdef ENABLE_SIXEL
1376f844e94eSwiz 		if (image_free_all(s) && ctx->wp != NULL)
1377f844e94eSwiz 			ctx->wp->flags |= PANE_REDRAW;
1378f844e94eSwiz #endif
1379f844e94eSwiz 
1380e271dbb8Schristos 		grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
1381e271dbb8Schristos 		screen_write_collect_flush(ctx, 0, __func__);
1382e271dbb8Schristos 
1383e271dbb8Schristos 		screen_write_initctx(ctx, &ttyctx, 1);
1384fe99a117Schristos 		ttyctx.bg = bg;
1385698d5317Sjmmv 
1386e271dbb8Schristos 		tty_write(tty_cmd_reverseindex, &ttyctx);
1387e271dbb8Schristos 	} else if (s->cy > 0)
13880a274e86Schristos 		screen_write_set_cursor(ctx, -1, s->cy - 1);
1389698d5317Sjmmv 
1390698d5317Sjmmv }
1391698d5317Sjmmv 
1392698d5317Sjmmv /* Set scroll region. */
1393698d5317Sjmmv void
1394f26e8bc9Schristos screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
1395f26e8bc9Schristos     u_int rlower)
1396698d5317Sjmmv {
1397698d5317Sjmmv 	struct screen	*s = ctx->s;
1398698d5317Sjmmv 
1399698d5317Sjmmv 	if (rupper > screen_size_y(s) - 1)
1400698d5317Sjmmv 		rupper = screen_size_y(s) - 1;
1401698d5317Sjmmv 	if (rlower > screen_size_y(s) - 1)
1402698d5317Sjmmv 		rlower = screen_size_y(s) - 1;
1403698d5317Sjmmv 	if (rupper >= rlower)	/* cannot be one line */
1404698d5317Sjmmv 		return;
1405698d5317Sjmmv 
1406e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
1407e9a2d6faSchristos 
1408698d5317Sjmmv 	/* Cursor moves to top-left. */
14090a274e86Schristos 	screen_write_set_cursor(ctx, 0, 0);
1410698d5317Sjmmv 
1411698d5317Sjmmv 	s->rupper = rupper;
1412698d5317Sjmmv 	s->rlower = rlower;
1413698d5317Sjmmv }
1414698d5317Sjmmv 
1415698d5317Sjmmv /* Line feed. */
1416698d5317Sjmmv void
1417fe99a117Schristos screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
1418698d5317Sjmmv {
1419698d5317Sjmmv 	struct screen		*s = ctx->s;
1420e9a2d6faSchristos 	struct grid		*gd = s->grid;
1421698d5317Sjmmv 	struct grid_line	*gl;
1422f844e94eSwiz #ifdef ENABLE_SIXEL
1423f844e94eSwiz 	int			 redraw = 0;
1424f844e94eSwiz #endif
1425f844e94eSwiz 	u_int			 rupper = s->rupper, rlower = s->rlower;
1426698d5317Sjmmv 
1427c7e17de0Schristos 	gl = grid_get_line(gd, gd->hsize + s->cy);
1428698d5317Sjmmv 	if (wrapped)
1429698d5317Sjmmv 		gl->flags |= GRID_LINE_WRAPPED;
1430698d5317Sjmmv 
1431e9a2d6faSchristos 	log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1432f844e94eSwiz 	    rupper, rlower);
1433698d5317Sjmmv 
1434fe99a117Schristos 	if (bg != ctx->bg) {
1435e271dbb8Schristos 		screen_write_collect_flush(ctx, 1, __func__);
1436fe99a117Schristos 		ctx->bg = bg;
1437fe99a117Schristos 	}
1438fe99a117Schristos 
1439e9a2d6faSchristos 	if (s->cy == s->rlower) {
1440f844e94eSwiz #ifdef ENABLE_SIXEL
1441f844e94eSwiz 		if (rlower == screen_size_y(s) - 1)
1442f844e94eSwiz 			redraw = image_scroll_up(s, 1);
1443f844e94eSwiz 		else
1444f844e94eSwiz 			redraw = image_check_line(s, rupper, rlower - rupper);
1445f844e94eSwiz 		if (redraw && ctx->wp != NULL)
1446f844e94eSwiz 			ctx->wp->flags |= PANE_REDRAW;
1447f844e94eSwiz #endif
1448fe99a117Schristos 		grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1449e271dbb8Schristos 		screen_write_collect_scroll(ctx, bg);
1450e9a2d6faSchristos 		ctx->scrolled++;
1451e9a2d6faSchristos 	} else if (s->cy < screen_size_y(s) - 1)
14520a274e86Schristos 		screen_write_set_cursor(ctx, -1, s->cy + 1);
1453e9a2d6faSchristos }
1454e9a2d6faSchristos 
1455e9a2d6faSchristos /* Scroll up. */
1456e9a2d6faSchristos void
1457fe99a117Schristos screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1458e9a2d6faSchristos {
1459e9a2d6faSchristos 	struct screen	*s = ctx->s;
1460e9a2d6faSchristos 	struct grid	*gd = s->grid;
1461e9a2d6faSchristos 	u_int		 i;
1462e9a2d6faSchristos 
1463e9a2d6faSchristos 	if (lines == 0)
1464e9a2d6faSchristos 		lines = 1;
1465e9a2d6faSchristos 	else if (lines > s->rlower - s->rupper + 1)
1466e9a2d6faSchristos 		lines = s->rlower - s->rupper + 1;
1467e9a2d6faSchristos 
1468fe99a117Schristos 	if (bg != ctx->bg) {
1469e271dbb8Schristos 		screen_write_collect_flush(ctx, 1, __func__);
1470fe99a117Schristos 		ctx->bg = bg;
1471fe99a117Schristos 	}
1472fe99a117Schristos 
1473f844e94eSwiz #ifdef ENABLE_SIXEL
1474f844e94eSwiz 	if (image_scroll_up(s, lines) && ctx->wp != NULL)
1475f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1476f844e94eSwiz #endif
1477f844e94eSwiz 
1478e9a2d6faSchristos 	for (i = 0; i < lines; i++) {
1479fe99a117Schristos 		grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1480e271dbb8Schristos 		screen_write_collect_scroll(ctx, bg);
1481e9a2d6faSchristos 	}
1482e9a2d6faSchristos 	ctx->scrolled += lines;
1483698d5317Sjmmv }
1484698d5317Sjmmv 
148530744affSchristos /* Scroll down. */
148630744affSchristos void
148730744affSchristos screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
148830744affSchristos {
148930744affSchristos 	struct screen	*s = ctx->s;
149030744affSchristos 	struct grid	*gd = s->grid;
149130744affSchristos 	struct tty_ctx	 ttyctx;
149230744affSchristos 	u_int		 i;
149330744affSchristos 
1494e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 1);
149530744affSchristos 	ttyctx.bg = bg;
149630744affSchristos 
149730744affSchristos 	if (lines == 0)
149830744affSchristos 		lines = 1;
149930744affSchristos 	else if (lines > s->rlower - s->rupper + 1)
150030744affSchristos 		lines = s->rlower - s->rupper + 1;
150130744affSchristos 
1502f844e94eSwiz #ifdef ENABLE_SIXEL
1503f844e94eSwiz 	if (image_free_all(s) && ctx->wp != NULL)
1504f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1505f844e94eSwiz #endif
1506f844e94eSwiz 
150730744affSchristos 	for (i = 0; i < lines; i++)
150830744affSchristos 		grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
150930744affSchristos 
1510e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
151130744affSchristos 	ttyctx.num = lines;
151230744affSchristos 	tty_write(tty_cmd_scrolldown, &ttyctx);
151330744affSchristos }
151430744affSchristos 
1515698d5317Sjmmv /* Carriage return (cursor to start of line). */
1516698d5317Sjmmv void
1517698d5317Sjmmv screen_write_carriagereturn(struct screen_write_ctx *ctx)
1518698d5317Sjmmv {
15190a274e86Schristos 	screen_write_set_cursor(ctx, 0, -1);
1520698d5317Sjmmv }
1521698d5317Sjmmv 
1522698d5317Sjmmv /* Clear to end of screen from cursor. */
1523698d5317Sjmmv void
1524e9a2d6faSchristos screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
1525698d5317Sjmmv {
1526698d5317Sjmmv 	struct screen	*s = ctx->s;
1527e9a2d6faSchristos 	struct grid	*gd = s->grid;
1528698d5317Sjmmv 	struct tty_ctx	 ttyctx;
1529e9a2d6faSchristos 	u_int		 sx = screen_size_x(s), sy = screen_size_y(s);
1530698d5317Sjmmv 
1531f844e94eSwiz #ifdef ENABLE_SIXEL
1532f844e94eSwiz 	if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL)
1533f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1534f844e94eSwiz #endif
1535f844e94eSwiz 
1536e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 1);
1537e9a2d6faSchristos 	ttyctx.bg = bg;
1538698d5317Sjmmv 
15390f3d2746Sjmmv 	/* Scroll into history if it is enabled and clearing entire screen. */
154046548964Swiz 	if (s->cx == 0 &&
154146548964Swiz 	    s->cy == 0 &&
154246548964Swiz 	    (gd->flags & GRID_HISTORY) &&
154346548964Swiz 	    ctx->wp != NULL &&
154446548964Swiz 	    options_get_number(ctx->wp->options, "scroll-on-clear"))
1545e9a2d6faSchristos 		grid_view_clear_history(gd, bg);
15460f3d2746Sjmmv 	else {
1547698d5317Sjmmv 		if (s->cx <= sx - 1)
1548e9a2d6faSchristos 			grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg);
1549e9a2d6faSchristos 		grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg);
15500f3d2746Sjmmv 	}
1551698d5317Sjmmv 
1552e9a2d6faSchristos 	screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
1553e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
1554698d5317Sjmmv 	tty_write(tty_cmd_clearendofscreen, &ttyctx);
1555698d5317Sjmmv }
1556698d5317Sjmmv 
1557698d5317Sjmmv /* Clear to start of screen. */
1558698d5317Sjmmv void
1559e9a2d6faSchristos screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
1560698d5317Sjmmv {
1561698d5317Sjmmv 	struct screen	*s = ctx->s;
1562698d5317Sjmmv 	struct tty_ctx	 ttyctx;
1563e9a2d6faSchristos 	u_int		 sx = screen_size_x(s);
1564698d5317Sjmmv 
1565f844e94eSwiz #ifdef ENABLE_SIXEL
1566f844e94eSwiz 	if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL)
1567f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1568f844e94eSwiz #endif
1569f844e94eSwiz 
1570e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 1);
1571e9a2d6faSchristos 	ttyctx.bg = bg;
1572698d5317Sjmmv 
1573698d5317Sjmmv 	if (s->cy > 0)
1574e9a2d6faSchristos 		grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
1575698d5317Sjmmv 	if (s->cx > sx - 1)
1576fe99a117Schristos 		grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1577698d5317Sjmmv 	else
1578fe99a117Schristos 		grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1579698d5317Sjmmv 
1580e9a2d6faSchristos 	screen_write_collect_clear(ctx, 0, s->cy);
1581e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
1582698d5317Sjmmv 	tty_write(tty_cmd_clearstartofscreen, &ttyctx);
1583698d5317Sjmmv }
1584698d5317Sjmmv 
1585698d5317Sjmmv /* Clear entire screen. */
1586698d5317Sjmmv void
1587e9a2d6faSchristos screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
1588698d5317Sjmmv {
1589698d5317Sjmmv 	struct screen	*s = ctx->s;
1590698d5317Sjmmv 	struct tty_ctx	 ttyctx;
1591e9a2d6faSchristos 	u_int		 sx = screen_size_x(s), sy = screen_size_y(s);
1592698d5317Sjmmv 
1593f844e94eSwiz #ifdef ENABLE_SIXEL
1594f844e94eSwiz 	if (image_free_all(s) && ctx->wp != NULL)
1595f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1596f844e94eSwiz #endif
1597f844e94eSwiz 
1598e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 1);
1599e9a2d6faSchristos 	ttyctx.bg = bg;
1600698d5317Sjmmv 
16010f3d2746Sjmmv 	/* Scroll into history if it is enabled. */
160246548964Swiz 	if ((s->grid->flags & GRID_HISTORY) &&
160346548964Swiz 	    ctx->wp != NULL &&
160446548964Swiz 	    options_get_number(ctx->wp->options, "scroll-on-clear"))
1605e9a2d6faSchristos 		grid_view_clear_history(s->grid, bg);
1606f26e8bc9Schristos 	else
1607e9a2d6faSchristos 		grid_view_clear(s->grid, 0, 0, sx, sy, bg);
1608698d5317Sjmmv 
1609e9a2d6faSchristos 	screen_write_collect_clear(ctx, 0, sy);
1610698d5317Sjmmv 	tty_write(tty_cmd_clearscreen, &ttyctx);
1611698d5317Sjmmv }
1612698d5317Sjmmv 
161361fba46bSchristos /* Clear entire history. */
161461fba46bSchristos void
161561fba46bSchristos screen_write_clearhistory(struct screen_write_ctx *ctx)
161661fba46bSchristos {
161730744affSchristos 	grid_clear_history(ctx->s->grid);
1618e9a2d6faSchristos }
1619e9a2d6faSchristos 
162046548964Swiz /* Force a full redraw. */
162146548964Swiz void
162246548964Swiz screen_write_fullredraw(struct screen_write_ctx *ctx)
162346548964Swiz {
162446548964Swiz 	struct tty_ctx	 ttyctx;
162546548964Swiz 
162646548964Swiz 	screen_write_collect_flush(ctx, 0, __func__);
162746548964Swiz 
162846548964Swiz 	screen_write_initctx(ctx, &ttyctx, 1);
1629f844e94eSwiz 	if (ttyctx.redraw_cb != NULL)
163046548964Swiz 		ttyctx.redraw_cb(&ttyctx);
163146548964Swiz }
163246548964Swiz 
1633e271dbb8Schristos /* Trim collected items. */
1634e271dbb8Schristos static struct screen_write_citem *
1635e271dbb8Schristos screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
1636e271dbb8Schristos     u_int used, int *wrapped)
1637e271dbb8Schristos {
1638e271dbb8Schristos 	struct screen_write_cline	*cl = &ctx->s->write_list[y];
1639e271dbb8Schristos 	struct screen_write_citem	*ci, *ci2, *tmp, *before = NULL;
1640e271dbb8Schristos 	u_int				 sx = x, ex = x + used - 1;
1641e271dbb8Schristos 	u_int				 csx, cex;
1642e271dbb8Schristos 
1643e271dbb8Schristos 	if (TAILQ_EMPTY(&cl->items))
1644e271dbb8Schristos 		return (NULL);
1645e271dbb8Schristos 	TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1646e271dbb8Schristos 		csx = ci->x;
1647e271dbb8Schristos 		cex = ci->x + ci->used - 1;
1648e271dbb8Schristos 
1649e271dbb8Schristos 		/* Item is entirely before. */
1650e271dbb8Schristos 		if (cex < sx) {
1651e271dbb8Schristos 			log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
1652e271dbb8Schristos 			    csx, cex, sx, ex);
1653e271dbb8Schristos 			continue;
1654e271dbb8Schristos 		}
1655e271dbb8Schristos 
1656e271dbb8Schristos 		/* Item is entirely after. */
1657e271dbb8Schristos 		if (csx > ex) {
1658e271dbb8Schristos 			log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
1659e271dbb8Schristos 			    csx, cex, sx, ex);
1660e271dbb8Schristos 			before = ci;
1661e271dbb8Schristos 			break;
1662e271dbb8Schristos 		}
1663e271dbb8Schristos 
1664e271dbb8Schristos 		/* Item is entirely inside. */
1665e271dbb8Schristos 		if (csx >= sx && cex <= ex) {
1666e271dbb8Schristos 			log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
1667e271dbb8Schristos 			    csx, cex, sx, ex);
1668e271dbb8Schristos 			TAILQ_REMOVE(&cl->items, ci, entry);
1669e271dbb8Schristos 			screen_write_free_citem(ci);
1670e271dbb8Schristos 			if (csx == 0 && ci->wrapped && wrapped != NULL)
1671e271dbb8Schristos 				*wrapped = 1;
1672e271dbb8Schristos 			continue;
1673e271dbb8Schristos 		}
1674e271dbb8Schristos 
1675e271dbb8Schristos 		/* Item under the start. */
1676e271dbb8Schristos 		if (csx < sx && cex >= sx && cex <= ex) {
1677e271dbb8Schristos 			log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
1678e271dbb8Schristos 			    csx, cex, sx, ex);
1679e271dbb8Schristos 			ci->used = sx - csx;
1680e271dbb8Schristos 			log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1681e271dbb8Schristos 			    ci->x + ci->used + 1);
1682e271dbb8Schristos 			continue;
1683e271dbb8Schristos 		}
1684e271dbb8Schristos 
1685e271dbb8Schristos 		/* Item covers the end. */
1686e271dbb8Schristos 		if (cex > ex && csx >= sx && csx <= ex) {
1687e271dbb8Schristos 			log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
1688e271dbb8Schristos 			    csx, cex, sx, ex);
1689e271dbb8Schristos 			ci->x = ex + 1;
1690e271dbb8Schristos 			ci->used = cex - ex;
1691e271dbb8Schristos 			log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1692e271dbb8Schristos 			    ci->x + ci->used + 1);
1693e271dbb8Schristos 			before = ci;
1694e271dbb8Schristos 			break;
1695e271dbb8Schristos 		}
1696e271dbb8Schristos 
1697e271dbb8Schristos 		/* Item must cover both sides. */
1698e271dbb8Schristos 		log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
1699e271dbb8Schristos 		    csx, cex, sx, ex);
1700e271dbb8Schristos 		ci2 = screen_write_get_citem();
1701e271dbb8Schristos 		ci2->type = ci->type;
1702e271dbb8Schristos 		ci2->bg = ci->bg;
1703e271dbb8Schristos 		memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
1704e271dbb8Schristos 		TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
1705e271dbb8Schristos 
1706e271dbb8Schristos 		ci->used = sx - csx;
1707e271dbb8Schristos 		ci2->x = ex + 1;
1708e271dbb8Schristos 		ci2->used = cex - ex;
1709e271dbb8Schristos 
1710e271dbb8Schristos 		log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
1711e271dbb8Schristos 		    ci->x, ci->x + ci->used - 1, ci, ci2->x,
1712e271dbb8Schristos 		    ci2->x + ci2->used - 1, ci2);
1713e271dbb8Schristos 		before = ci2;
1714e271dbb8Schristos 		break;
1715e271dbb8Schristos 	}
1716e271dbb8Schristos 	return (before);
1717e271dbb8Schristos }
1718e271dbb8Schristos 
1719e271dbb8Schristos /* Clear collected lines. */
1720e9a2d6faSchristos static void
1721e9a2d6faSchristos screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
1722e9a2d6faSchristos {
1723e271dbb8Schristos 	struct screen_write_cline	*cl;
1724e9a2d6faSchristos 	u_int				 i;
1725e9a2d6faSchristos 
1726e9a2d6faSchristos 	for (i = y; i < y + n; i++) {
1727e271dbb8Schristos 		cl = &ctx->s->write_list[i];
1728e271dbb8Schristos 		TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
1729e9a2d6faSchristos 	}
1730e9a2d6faSchristos }
1731e9a2d6faSchristos 
1732e9a2d6faSchristos /* Scroll collected lines up. */
1733e9a2d6faSchristos static void
1734e271dbb8Schristos screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
1735e9a2d6faSchristos {
1736e9a2d6faSchristos 	struct screen			*s = ctx->s;
1737e271dbb8Schristos 	struct screen_write_cline	*cl;
1738e9a2d6faSchristos 	u_int				 y;
1739e271dbb8Schristos 	char				*saved;
1740e271dbb8Schristos 	struct screen_write_citem	*ci;
1741e9a2d6faSchristos 
1742e9a2d6faSchristos 	log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1743e9a2d6faSchristos 	    s->rupper, s->rlower);
1744e9a2d6faSchristos 
1745e9a2d6faSchristos 	screen_write_collect_clear(ctx, s->rupper, 1);
1746e271dbb8Schristos 	saved = ctx->s->write_list[s->rupper].data;
1747e9a2d6faSchristos 	for (y = s->rupper; y < s->rlower; y++) {
1748e271dbb8Schristos 		cl = &ctx->s->write_list[y + 1];
1749e271dbb8Schristos 		TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
1750e271dbb8Schristos 		ctx->s->write_list[y].data = cl->data;
1751e9a2d6faSchristos 	}
1752e271dbb8Schristos 	ctx->s->write_list[s->rlower].data = saved;
1753e271dbb8Schristos 
1754e271dbb8Schristos 	ci = screen_write_get_citem();
1755e271dbb8Schristos 	ci->x = 0;
1756e271dbb8Schristos 	ci->used = screen_size_x(s);
1757e271dbb8Schristos 	ci->type = CLEAR;
1758e271dbb8Schristos 	ci->bg = bg;
1759e271dbb8Schristos 	TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
1760e9a2d6faSchristos }
1761e9a2d6faSchristos 
1762e9a2d6faSchristos /* Flush collected lines. */
1763e9a2d6faSchristos static void
1764e271dbb8Schristos screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
1765e271dbb8Schristos     const char *from)
1766e9a2d6faSchristos {
1767e9a2d6faSchristos 	struct screen			*s = ctx->s;
1768e271dbb8Schristos 	struct screen_write_citem	*ci, *tmp;
1769e271dbb8Schristos 	struct screen_write_cline	*cl;
1770e271dbb8Schristos 	u_int				 y, cx, cy, last, items = 0;
1771e9a2d6faSchristos 	struct tty_ctx			 ttyctx;
1772e9a2d6faSchristos 
1773e9a2d6faSchristos 	if (ctx->scrolled != 0) {
1774e9a2d6faSchristos 		log_debug("%s: scrolled %u (region %u-%u)", __func__,
1775e9a2d6faSchristos 		    ctx->scrolled, s->rupper, s->rlower);
1776e9a2d6faSchristos 		if (ctx->scrolled > s->rlower - s->rupper + 1)
1777e9a2d6faSchristos 			ctx->scrolled = s->rlower - s->rupper + 1;
1778e9a2d6faSchristos 
1779e271dbb8Schristos 		screen_write_initctx(ctx, &ttyctx, 1);
1780e9a2d6faSchristos 		ttyctx.num = ctx->scrolled;
1781fe99a117Schristos 		ttyctx.bg = ctx->bg;
1782e9a2d6faSchristos 		tty_write(tty_cmd_scrollup, &ttyctx);
1783e9a2d6faSchristos 	}
1784e9a2d6faSchristos 	ctx->scrolled = 0;
1785fe99a117Schristos 	ctx->bg = 8;
1786fe99a117Schristos 
1787e9a2d6faSchristos 	if (scroll_only)
1788e9a2d6faSchristos 		return;
1789e9a2d6faSchristos 
1790e9a2d6faSchristos 	cx = s->cx; cy = s->cy;
1791e9a2d6faSchristos 	for (y = 0; y < screen_size_y(s); y++) {
1792e271dbb8Schristos 		cl = &ctx->s->write_list[y];
1793e271dbb8Schristos 		last = UINT_MAX;
1794e271dbb8Schristos 		TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1795e271dbb8Schristos 			if (last != UINT_MAX && ci->x <= last) {
1796e271dbb8Schristos 				fatalx("collect list not in order: %u <= %u",
1797e271dbb8Schristos 				    ci->x, last);
1798e271dbb8Schristos 			}
17990a274e86Schristos 			screen_write_set_cursor(ctx, ci->x, y);
1800e271dbb8Schristos 			if (ci->type == CLEAR) {
1801e271dbb8Schristos 				screen_write_initctx(ctx, &ttyctx, 1);
1802e271dbb8Schristos 				ttyctx.bg = ci->bg;
1803e271dbb8Schristos 				ttyctx.num = ci->used;
1804e271dbb8Schristos 				tty_write(tty_cmd_clearcharacter, &ttyctx);
1805e271dbb8Schristos 			} else {
1806e271dbb8Schristos 				screen_write_initctx(ctx, &ttyctx, 0);
1807e9a2d6faSchristos 				ttyctx.cell = &ci->gc;
1808fe99a117Schristos 				ttyctx.wrapped = ci->wrapped;
1809e271dbb8Schristos 				ttyctx.ptr = cl->data + ci->x;
1810e9a2d6faSchristos 				ttyctx.num = ci->used;
1811e9a2d6faSchristos 				tty_write(tty_cmd_cells, &ttyctx);
1812e271dbb8Schristos 			}
1813e9a2d6faSchristos 			items++;
1814e9a2d6faSchristos 
1815e271dbb8Schristos 			TAILQ_REMOVE(&cl->items, ci, entry);
1816e271dbb8Schristos 			screen_write_free_citem(ci);
1817e271dbb8Schristos 			last = ci->x;
1818e9a2d6faSchristos 		}
1819e9a2d6faSchristos 	}
1820e9a2d6faSchristos 	s->cx = cx; s->cy = cy;
1821e9a2d6faSchristos 
1822e271dbb8Schristos 	log_debug("%s: flushed %u items (%s)", __func__, items, from);
1823e9a2d6faSchristos }
1824e9a2d6faSchristos 
1825e9a2d6faSchristos /* Finish and store collected cells. */
1826e9a2d6faSchristos void
1827e9a2d6faSchristos screen_write_collect_end(struct screen_write_ctx *ctx)
1828e9a2d6faSchristos {
1829e9a2d6faSchristos 	struct screen			*s = ctx->s;
1830e271dbb8Schristos 	struct screen_write_citem	*ci = ctx->item, *before;
1831e271dbb8Schristos 	struct screen_write_cline	*cl = &s->write_list[s->cy];
1832e9a2d6faSchristos 	struct grid_cell		 gc;
1833c7e17de0Schristos 	u_int				 xx;
1834e271dbb8Schristos 	int				 wrapped = ci->wrapped;
1835e9a2d6faSchristos 
1836e9a2d6faSchristos 	if (ci->used == 0)
1837e9a2d6faSchristos 		return;
1838e9a2d6faSchristos 
1839e271dbb8Schristos 	before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
1840e271dbb8Schristos 	    &wrapped);
1841e9a2d6faSchristos 	ci->x = s->cx;
1842e271dbb8Schristos 	ci->wrapped = wrapped;
1843e271dbb8Schristos 	if (before == NULL)
1844e271dbb8Schristos 		TAILQ_INSERT_TAIL(&cl->items, ci, entry);
1845e271dbb8Schristos 	else
1846e271dbb8Schristos 		TAILQ_INSERT_BEFORE(before, ci, entry);
1847e271dbb8Schristos 	ctx->item = screen_write_get_citem();
1848e9a2d6faSchristos 
1849e271dbb8Schristos 	log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
1850e271dbb8Schristos 	    (int)ci->used, cl->data + ci->x, s->cx, s->cy);
1851e9a2d6faSchristos 
1852c7e17de0Schristos 	if (s->cx != 0) {
1853c7e17de0Schristos 		for (xx = s->cx; xx > 0; xx--) {
1854c7e17de0Schristos 			grid_view_get_cell(s->grid, xx, s->cy, &gc);
1855c7e17de0Schristos 			if (~gc.flags & GRID_FLAG_PADDING)
1856c7e17de0Schristos 				break;
1857c7e17de0Schristos 			grid_view_set_cell(s->grid, xx, s->cy,
1858c7e17de0Schristos 			    &grid_default_cell);
1859c7e17de0Schristos 		}
18600a274e86Schristos 		if (gc.data.width > 1) {
1861c7e17de0Schristos 			grid_view_set_cell(s->grid, xx, s->cy,
1862c7e17de0Schristos 			    &grid_default_cell);
1863c7e17de0Schristos 		}
18640a274e86Schristos 	}
1865c7e17de0Schristos 
1866f844e94eSwiz #ifdef ENABLE_SIXEL
1867f844e94eSwiz 	if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL)
1868f844e94eSwiz 		ctx->wp->flags |= PANE_REDRAW;
1869f844e94eSwiz #endif
1870f844e94eSwiz 
1871e271dbb8Schristos 	grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
1872e271dbb8Schristos 	    ci->used);
18730a274e86Schristos 	screen_write_set_cursor(ctx, s->cx + ci->used, -1);
1874c7e17de0Schristos 
1875c7e17de0Schristos 	for (xx = s->cx; xx < screen_size_x(s); xx++) {
1876c7e17de0Schristos 		grid_view_get_cell(s->grid, xx, s->cy, &gc);
1877c7e17de0Schristos 		if (~gc.flags & GRID_FLAG_PADDING)
1878c7e17de0Schristos 			break;
1879c7e17de0Schristos 		grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
1880c7e17de0Schristos 	}
1881e9a2d6faSchristos }
1882e9a2d6faSchristos 
1883e9a2d6faSchristos /* Write cell data, collecting if necessary. */
1884e9a2d6faSchristos void
1885e9a2d6faSchristos screen_write_collect_add(struct screen_write_ctx *ctx,
1886e9a2d6faSchristos     const struct grid_cell *gc)
1887e9a2d6faSchristos {
1888e9a2d6faSchristos 	struct screen			*s = ctx->s;
1889e271dbb8Schristos 	struct screen_write_citem	*ci;
1890e9a2d6faSchristos 	u_int				 sx = screen_size_x(s);
1891e9a2d6faSchristos 	int				 collect;
1892e9a2d6faSchristos 
1893e9a2d6faSchristos 	/*
1894e9a2d6faSchristos 	 * Don't need to check that the attributes and whatnot are still the
1895e9a2d6faSchristos 	 * same - input_parse will end the collection when anything that isn't
189668e6ba84Schristos 	 * a plain character is encountered.
1897e9a2d6faSchristos 	 */
1898e9a2d6faSchristos 
1899e9a2d6faSchristos 	collect = 1;
1900c7e17de0Schristos 	if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
1901e9a2d6faSchristos 		collect = 0;
1902e9a2d6faSchristos 	else if (gc->attr & GRID_ATTR_CHARSET)
1903e9a2d6faSchristos 		collect = 0;
1904e9a2d6faSchristos 	else if (~s->mode & MODE_WRAP)
1905e9a2d6faSchristos 		collect = 0;
1906e9a2d6faSchristos 	else if (s->mode & MODE_INSERT)
1907e9a2d6faSchristos 		collect = 0;
1908c7e17de0Schristos 	else if (s->sel != NULL)
1909e9a2d6faSchristos 		collect = 0;
1910e9a2d6faSchristos 	if (!collect) {
1911e9a2d6faSchristos 		screen_write_collect_end(ctx);
1912e271dbb8Schristos 		screen_write_collect_flush(ctx, 0, __func__);
1913e9a2d6faSchristos 		screen_write_cell(ctx, gc);
1914e9a2d6faSchristos 		return;
1915e9a2d6faSchristos 	}
1916e9a2d6faSchristos 
1917e9a2d6faSchristos 	if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
1918e9a2d6faSchristos 		screen_write_collect_end(ctx);
1919fe99a117Schristos 	ci = ctx->item; /* may have changed */
1920fe99a117Schristos 
1921e9a2d6faSchristos 	if (s->cx > sx - 1) {
1922e9a2d6faSchristos 		log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1923fe99a117Schristos 		ci->wrapped = 1;
1924fe99a117Schristos 		screen_write_linefeed(ctx, 1, 8);
19250a274e86Schristos 		screen_write_set_cursor(ctx, 0, -1);
1926e9a2d6faSchristos 	}
1927e9a2d6faSchristos 
1928e9a2d6faSchristos 	if (ci->used == 0)
1929e9a2d6faSchristos 		memcpy(&ci->gc, gc, sizeof ci->gc);
1930e271dbb8Schristos 	if (ctx->s->write_list[s->cy].data == NULL)
1931e271dbb8Schristos 		ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s));
1932e271dbb8Schristos 	ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0];
193361fba46bSchristos }
193461fba46bSchristos 
1935698d5317Sjmmv /* Write cell data. */
1936698d5317Sjmmv void
193761fba46bSchristos screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
1938698d5317Sjmmv {
1939698d5317Sjmmv 	struct screen		*s = ctx->s;
1940698d5317Sjmmv 	struct grid		*gd = s->grid;
194146548964Swiz 	const struct utf8_data	*ud = &gc->data;
1942e9a2d6faSchristos 	struct grid_line	*gl;
1943e9a2d6faSchristos 	struct grid_cell_entry	*gce;
1944e9a2d6faSchristos 	struct grid_cell 	 tmp_gc, now_gc;
1945698d5317Sjmmv 	struct tty_ctx		 ttyctx;
1946e9a2d6faSchristos 	u_int			 sx = screen_size_x(s), sy = screen_size_y(s);
1947f844e94eSwiz 	u_int		 	 width = ud->width, xx, not_wrap;
1948e9a2d6faSchristos 	int			 selected, skip = 1;
1949698d5317Sjmmv 
1950e9a2d6faSchristos 	/* Ignore padding cells. */
1951698d5317Sjmmv 	if (gc->flags & GRID_FLAG_PADDING)
1952698d5317Sjmmv 		return;
1953698d5317Sjmmv 
1954f844e94eSwiz 	/* Get the previous cell to check for combining. */
1955f844e94eSwiz 	if (screen_write_combine(ctx, gc) != 0)
195646548964Swiz 		return;
1957698d5317Sjmmv 
1958e9a2d6faSchristos 	/* Flush any existing scrolling. */
1959e271dbb8Schristos 	screen_write_collect_flush(ctx, 1, __func__);
1960e9a2d6faSchristos 
1961e9a2d6faSchristos 	/* If this character doesn't fit, ignore it. */
1962e9a2d6faSchristos 	if ((~s->mode & MODE_WRAP) &&
1963e9a2d6faSchristos 	    width > 1 &&
1964e9a2d6faSchristos 	    (width > sx || (s->cx != sx && s->cx > sx - width)))
1965e9a2d6faSchristos 		return;
1966698d5317Sjmmv 
1967698d5317Sjmmv 	/* If in insert mode, make space for the cells. */
1968e9a2d6faSchristos 	if (s->mode & MODE_INSERT) {
1969e9a2d6faSchristos 		grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
1970e9a2d6faSchristos 		skip = 0;
1971e9a2d6faSchristos 	}
1972698d5317Sjmmv 
1973698d5317Sjmmv 	/* Check this will fit on the current line and wrap if not. */
1974e9a2d6faSchristos 	if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
1975fe99a117Schristos 		log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1976fe99a117Schristos 		screen_write_linefeed(ctx, 1, 8);
19770a274e86Schristos 		screen_write_set_cursor(ctx, 0, -1);
1978e271dbb8Schristos 		screen_write_collect_flush(ctx, 1, __func__);
1979698d5317Sjmmv 	}
1980698d5317Sjmmv 
198161fba46bSchristos 	/* Sanity check cursor position. */
1982e9a2d6faSchristos 	if (s->cx > sx - width || s->cy > sy - 1)
1983698d5317Sjmmv 		return;
1984e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 0);
1985698d5317Sjmmv 
1986698d5317Sjmmv 	/* Handle overwriting of UTF-8 characters. */
1987c7e17de0Schristos 	gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1988e9a2d6faSchristos 	if (gl->flags & GRID_LINE_EXTENDED) {
1989e9a2d6faSchristos 		grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
1990e9a2d6faSchristos 		if (screen_write_overwrite(ctx, &now_gc, width))
1991e9a2d6faSchristos 			skip = 0;
1992e9a2d6faSchristos 	}
1993698d5317Sjmmv 
1994698d5317Sjmmv 	/*
1995698d5317Sjmmv 	 * If the new character is UTF-8 wide, fill in padding cells. Have
1996698d5317Sjmmv 	 * already ensured there is enough room.
1997698d5317Sjmmv 	 */
1998e9a2d6faSchristos 	for (xx = s->cx + 1; xx < s->cx + width; xx++) {
1999c7e17de0Schristos 		log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
2000e271dbb8Schristos 		grid_view_set_padding(gd, xx, s->cy);
2001e9a2d6faSchristos 		skip = 0;
2002e9a2d6faSchristos 	}
2003698d5317Sjmmv 
2004e9a2d6faSchristos 	/* If no change, do not draw. */
2005e9a2d6faSchristos 	if (skip) {
2006e9a2d6faSchristos 		if (s->cx >= gl->cellsize)
2007e9a2d6faSchristos 			skip = grid_cells_equal(gc, &grid_default_cell);
2008e9a2d6faSchristos 		else {
2009e9a2d6faSchristos 			gce = &gl->celldata[s->cx];
2010e9a2d6faSchristos 			if (gce->flags & GRID_FLAG_EXTENDED)
2011e9a2d6faSchristos 				skip = 0;
2012e9a2d6faSchristos 			else if (gc->flags != gce->flags)
2013e9a2d6faSchristos 				skip = 0;
2014e9a2d6faSchristos 			else if (gc->attr != gce->data.attr)
2015e9a2d6faSchristos 				skip = 0;
2016e9a2d6faSchristos 			else if (gc->fg != gce->data.fg)
2017e9a2d6faSchristos 				skip = 0;
2018e9a2d6faSchristos 			else if (gc->bg != gce->data.bg)
2019e9a2d6faSchristos 				skip = 0;
2020e9a2d6faSchristos 			else if (gc->data.width != 1)
2021e9a2d6faSchristos 				skip = 0;
2022e9a2d6faSchristos 			else if (gc->data.size != 1)
2023e9a2d6faSchristos 				skip = 0;
2024e9a2d6faSchristos 			else if (gce->data.data != gc->data.data[0])
2025e9a2d6faSchristos 				skip = 0;
2026e9a2d6faSchristos 		}
2027e9a2d6faSchristos 	}
2028e9a2d6faSchristos 
2029fe99a117Schristos 	/* Update the selected flag and set the cell. */
2030e9a2d6faSchristos 	selected = screen_check_selection(s, s->cx, s->cy);
2031e9a2d6faSchristos 	if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
2032e9a2d6faSchristos 		memcpy(&tmp_gc, gc, sizeof tmp_gc);
2033e9a2d6faSchristos 		tmp_gc.flags |= GRID_FLAG_SELECTED;
2034e9a2d6faSchristos 		grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
2035e9a2d6faSchristos 	} else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
2036e9a2d6faSchristos 		memcpy(&tmp_gc, gc, sizeof tmp_gc);
2037e9a2d6faSchristos 		tmp_gc.flags &= ~GRID_FLAG_SELECTED;
2038e9a2d6faSchristos 		grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
2039e9a2d6faSchristos 	} else if (!skip)
2040698d5317Sjmmv 		grid_view_set_cell(gd, s->cx, s->cy, gc);
2041e9a2d6faSchristos 	if (selected)
2042e9a2d6faSchristos 		skip = 0;
2043698d5317Sjmmv 
204461fba46bSchristos 	/*
204561fba46bSchristos 	 * Move the cursor. If not wrapping, stick at the last character and
204661fba46bSchristos 	 * replace it.
204761fba46bSchristos 	 */
2048f844e94eSwiz 	not_wrap = !(s->mode & MODE_WRAP);
2049f844e94eSwiz 	if (s->cx <= sx - not_wrap - width)
20500a274e86Schristos 		screen_write_set_cursor(ctx, s->cx + width, -1);
205161fba46bSchristos 	else
2052f844e94eSwiz 		screen_write_set_cursor(ctx,  sx - not_wrap, -1);
2053698d5317Sjmmv 
2054e9a2d6faSchristos 	/* Create space for character in insert mode. */
2055e9a2d6faSchristos 	if (s->mode & MODE_INSERT) {
2056e271dbb8Schristos 		screen_write_collect_flush(ctx, 0, __func__);
2057698d5317Sjmmv 		ttyctx.num = width;
2058698d5317Sjmmv 		tty_write(tty_cmd_insertcharacter, &ttyctx);
2059698d5317Sjmmv 	}
2060e9a2d6faSchristos 
2061e9a2d6faSchristos 	/* Write to the screen. */
2062e9a2d6faSchristos 	if (!skip) {
2063e9a2d6faSchristos 		if (selected) {
2064e9a2d6faSchristos 			screen_select_cell(s, &tmp_gc, gc);
2065698d5317Sjmmv 			ttyctx.cell = &tmp_gc;
2066e9a2d6faSchristos 		} else
2067698d5317Sjmmv 			ttyctx.cell = gc;
2068698d5317Sjmmv 		tty_write(tty_cmd_cell, &ttyctx);
2069e271dbb8Schristos 	}
2070698d5317Sjmmv }
2071698d5317Sjmmv 
2072f844e94eSwiz /* Combine a UTF-8 zero-width character onto the previous if necessary. */
2073f844e94eSwiz static int
2074f844e94eSwiz screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
2075698d5317Sjmmv {
2076698d5317Sjmmv 	struct screen		*s = ctx->s;
2077698d5317Sjmmv 	struct grid		*gd = s->grid;
2078f844e94eSwiz 	const struct utf8_data	*ud = &gc->data;
2079f844e94eSwiz 	u_int			 n, cx = s->cx, cy = s->cy;
2080f844e94eSwiz 	struct grid_cell	 last;
2081f844e94eSwiz 	struct tty_ctx		 ttyctx;
2082f844e94eSwiz 	int			 force_wide = 0, zero_width = 0;
2083698d5317Sjmmv 
2084f844e94eSwiz 	/*
2085f844e94eSwiz 	 * Is this character which makes no sense without being combined? If
2086f844e94eSwiz 	 * this is true then flag it here and discard the character (return 1)
2087f844e94eSwiz 	 * if we cannot combine it.
2088f844e94eSwiz 	 */
2089f844e94eSwiz 	if (utf8_is_zwj(ud))
2090f844e94eSwiz 		zero_width = 1;
2091f844e94eSwiz 	else if (utf8_is_vs(ud))
2092f844e94eSwiz 		zero_width = force_wide = 1;
2093f844e94eSwiz 	else if (ud->width == 0)
2094f844e94eSwiz 		zero_width = 1;
2095698d5317Sjmmv 
2096f844e94eSwiz 	/* Cannot combine empty character or at left. */
2097f844e94eSwiz 	if (ud->size < 2 || cx == 0)
2098f844e94eSwiz 		return (zero_width);
2099f844e94eSwiz 	log_debug("%s: character %.*s at %u,%u (width %u)", __func__,
2100f844e94eSwiz 	    (int)ud->size, ud->data, cx, cy, ud->width);
2101698d5317Sjmmv 
2102f844e94eSwiz 	/* Find the cell to combine with. */
2103f844e94eSwiz 	n = 1;
2104f844e94eSwiz 	grid_view_get_cell(gd, cx - n, cy, &last);
2105f844e94eSwiz 	if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) {
2106f844e94eSwiz 		n = 2;
2107f844e94eSwiz 		grid_view_get_cell(gd, cx - n, cy, &last);
2108e9a2d6faSchristos 	}
2109f844e94eSwiz 	if (n != last.data.width || (last.flags & GRID_FLAG_PADDING))
2110f844e94eSwiz 		return (zero_width);
2111698d5317Sjmmv 
2112f844e94eSwiz 	/*
2113f844e94eSwiz 	 * Check if we need to combine characters. This could be zero width
2114f844e94eSwiz 	 * (set above), a modifier character (with an existing Unicode
2115f844e94eSwiz 	 * character) or a previous ZWJ.
2116f844e94eSwiz 	 */
2117f844e94eSwiz 	if (!zero_width) {
2118f844e94eSwiz 		if (utf8_is_modifier(ud)) {
2119f844e94eSwiz 			if (last.data.size < 2)
2120f844e94eSwiz 				return (0);
2121f844e94eSwiz 			force_wide = 1;
2122f844e94eSwiz 		} else if (!utf8_has_zwj(&last.data))
2123f844e94eSwiz 			return (0);
2124f844e94eSwiz 	}
2125e9a2d6faSchristos 
2126f844e94eSwiz 	/* Check if this combined character would be too long. */
2127f844e94eSwiz 	if (last.data.size + ud->size > sizeof last.data.data)
2128f844e94eSwiz 		return (0);
2129f844e94eSwiz 
2130f844e94eSwiz 	/* Combining; flush any pending output. */
2131f844e94eSwiz 	screen_write_collect_flush(ctx, 0, __func__);
2132f844e94eSwiz 
2133f844e94eSwiz 	log_debug("%s: %.*s -> %.*s at %u,%u (offset %u, width %u)", __func__,
2134f844e94eSwiz 	    (int)ud->size, ud->data, (int)last.data.size, last.data.data,
2135f844e94eSwiz 	    cx - n, cy, n, last.data.width);
2136698d5317Sjmmv 
2137f26e8bc9Schristos 	/* Append the data. */
2138f844e94eSwiz 	memcpy(last.data.data + last.data.size, ud->data, ud->size);
2139f844e94eSwiz 	last.data.size += ud->size;
2140f844e94eSwiz 
2141f844e94eSwiz 	/* Force the width to 2 for modifiers and variation selector. */
2142f844e94eSwiz 	if (last.data.width == 1 && force_wide) {
2143f844e94eSwiz 		last.data.width = 2;
2144f844e94eSwiz 		n = 2;
2145f844e94eSwiz 		cx++;
2146f844e94eSwiz 	} else
2147f844e94eSwiz 		force_wide = 0;
2148f26e8bc9Schristos 
2149f26e8bc9Schristos 	/* Set the new cell. */
2150f844e94eSwiz 	grid_view_set_cell(gd, cx - n, cy, &last);
2151f844e94eSwiz 	if (force_wide)
2152*890b6d91Swiz 		grid_view_set_padding(gd, cx - 1, cy);
2153698d5317Sjmmv 
2154f844e94eSwiz 	/*
2155f844e94eSwiz 	 * Redraw the combined cell. If forcing the cell to width 2, reset the
2156f844e94eSwiz 	 * cached cursor position in the tty, since we don't really know
2157f844e94eSwiz 	 * whether the terminal thought the character was width 1 or width 2
2158f844e94eSwiz 	 * and what it is going to do now.
2159f844e94eSwiz 	 */
2160f844e94eSwiz 	screen_write_set_cursor(ctx, cx - n, cy);
2161f844e94eSwiz 	screen_write_initctx(ctx, &ttyctx, 0);
2162f844e94eSwiz 	ttyctx.cell = &last;
2163f844e94eSwiz 	ttyctx.num = force_wide; /* reset cached cursor position */
2164f844e94eSwiz 	tty_write(tty_cmd_cell, &ttyctx);
2165f844e94eSwiz 	screen_write_set_cursor(ctx, cx, cy);
2166f844e94eSwiz 
2167f844e94eSwiz 	return (1);
2168698d5317Sjmmv }
2169698d5317Sjmmv 
2170698d5317Sjmmv /*
2171698d5317Sjmmv  * UTF-8 wide characters are a bit of an annoyance. They take up more than one
2172698d5317Sjmmv  * cell on the screen, so following cells must not be drawn by marking them as
2173698d5317Sjmmv  * padding.
2174698d5317Sjmmv  *
2175698d5317Sjmmv  * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
2176698d5317Sjmmv  * character, it is necessary to also overwrite any other cells which covered
2177698d5317Sjmmv  * by the same character.
2178698d5317Sjmmv  */
2179e9a2d6faSchristos static int
2180e9a2d6faSchristos screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
2181e9a2d6faSchristos     u_int width)
2182698d5317Sjmmv {
2183698d5317Sjmmv 	struct screen		*s = ctx->s;
2184698d5317Sjmmv 	struct grid		*gd = s->grid;
2185e9a2d6faSchristos 	struct grid_cell	 tmp_gc;
2186698d5317Sjmmv 	u_int			 xx;
2187e9a2d6faSchristos 	int			 done = 0;
2188698d5317Sjmmv 
2189e9a2d6faSchristos 	if (gc->flags & GRID_FLAG_PADDING) {
2190698d5317Sjmmv 		/*
2191698d5317Sjmmv 		 * A padding cell, so clear any following and leading padding
2192698d5317Sjmmv 		 * cells back to the character. Don't overwrite the current
2193698d5317Sjmmv 		 * cell as that happens later anyway.
2194698d5317Sjmmv 		 */
2195698d5317Sjmmv 		xx = s->cx + 1;
2196698d5317Sjmmv 		while (--xx > 0) {
2197e9a2d6faSchristos 			grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2198e9a2d6faSchristos 			if (~tmp_gc.flags & GRID_FLAG_PADDING)
2199698d5317Sjmmv 				break;
2200c7e17de0Schristos 			log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
2201698d5317Sjmmv 			grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2202698d5317Sjmmv 		}
2203698d5317Sjmmv 
2204698d5317Sjmmv 		/* Overwrite the character at the start of this padding. */
2205c7e17de0Schristos 		log_debug("%s: character at %u,%u", __func__, xx, s->cy);
2206698d5317Sjmmv 		grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2207e9a2d6faSchristos 		done = 1;
2208698d5317Sjmmv 	}
2209698d5317Sjmmv 
2210698d5317Sjmmv 	/*
2211e9a2d6faSchristos 	 * Overwrite any padding cells that belong to any UTF-8 characters
2212698d5317Sjmmv 	 * we'll be overwriting with the current character.
2213698d5317Sjmmv 	 */
2214e9a2d6faSchristos 	if (width != 1 ||
2215e9a2d6faSchristos 	    gc->data.width != 1 ||
2216e9a2d6faSchristos 	    gc->flags & GRID_FLAG_PADDING) {
2217698d5317Sjmmv 		xx = s->cx + width - 1;
2218698d5317Sjmmv 		while (++xx < screen_size_x(s)) {
2219e9a2d6faSchristos 			grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2220e9a2d6faSchristos 			if (~tmp_gc.flags & GRID_FLAG_PADDING)
2221698d5317Sjmmv 				break;
222268e6ba84Schristos 			log_debug("%s: overwrite at %u,%u", __func__, xx,
222368e6ba84Schristos 			    s->cy);
2224698d5317Sjmmv 			grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2225e9a2d6faSchristos 			done = 1;
2226698d5317Sjmmv 		}
2227698d5317Sjmmv 	}
22280f3d2746Sjmmv 
2229e9a2d6faSchristos 	return (done);
2230e9a2d6faSchristos }
2231e9a2d6faSchristos 
2232e9a2d6faSchristos /* Set external clipboard. */
22330f3d2746Sjmmv void
2234f844e94eSwiz screen_write_setselection(struct screen_write_ctx *ctx, const char *flags,
2235f844e94eSwiz     u_char *str, u_int len)
22360f3d2746Sjmmv {
22370f3d2746Sjmmv 	struct tty_ctx	ttyctx;
22380f3d2746Sjmmv 
2239e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 0);
22400f3d2746Sjmmv 	ttyctx.ptr = str;
2241f844e94eSwiz 	ttyctx.ptr2 = __UNCONST(flags);
22420f3d2746Sjmmv 	ttyctx.num = len;
22430f3d2746Sjmmv 
22440f3d2746Sjmmv 	tty_write(tty_cmd_setselection, &ttyctx);
22450f3d2746Sjmmv }
22460f3d2746Sjmmv 
2247e9a2d6faSchristos /* Write unmodified string. */
22480f3d2746Sjmmv void
2249f844e94eSwiz screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
2250f844e94eSwiz     int allow_invisible_panes)
22510f3d2746Sjmmv {
22520f3d2746Sjmmv 	struct tty_ctx	ttyctx;
22530f3d2746Sjmmv 
2254e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 0);
22550f3d2746Sjmmv 	ttyctx.ptr = str;
22560f3d2746Sjmmv 	ttyctx.num = len;
2257f844e94eSwiz 	ttyctx.allow_invisible_panes = allow_invisible_panes;
22580f3d2746Sjmmv 
22590f3d2746Sjmmv 	tty_write(tty_cmd_rawstring, &ttyctx);
22600f3d2746Sjmmv }
2261e271dbb8Schristos 
2262f844e94eSwiz #ifdef ENABLE_SIXEL
2263f844e94eSwiz /* Write a SIXEL image. */
2264f844e94eSwiz void
2265f844e94eSwiz screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si,
2266f844e94eSwiz     u_int bg)
2267f844e94eSwiz {
2268f844e94eSwiz 	struct screen		*s = ctx->s;
2269f844e94eSwiz 	struct grid		*gd = s->grid;
2270f844e94eSwiz 	struct tty_ctx		 ttyctx;
2271f844e94eSwiz 	u_int			 x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines;
2272f844e94eSwiz 	struct sixel_image	*new;
2273f844e94eSwiz 
2274f844e94eSwiz 	sixel_size_in_cells(si, &x, &y);
2275f844e94eSwiz 	if (x > screen_size_x(s) || y > screen_size_y(s)) {
2276f844e94eSwiz 		if (x > screen_size_x(s) - cx)
2277f844e94eSwiz 			sx = screen_size_x(s) - cx;
2278f844e94eSwiz 		else
2279f844e94eSwiz 			sx = x;
2280f844e94eSwiz 		if (y > screen_size_y(s) - 1)
2281f844e94eSwiz 			sy = screen_size_y(s) - 1;
2282f844e94eSwiz 		else
2283f844e94eSwiz 			sy = y;
2284f844e94eSwiz 		new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1);
2285f844e94eSwiz 		sixel_free(si);
2286f844e94eSwiz 		si = new;
2287*890b6d91Swiz 
2288*890b6d91Swiz 		/* Bail out if the image cannot be scaled. */
2289*890b6d91Swiz 		if (si == NULL)
2290*890b6d91Swiz 			return;
2291f844e94eSwiz 		sixel_size_in_cells(si, &x, &y);
2292f844e94eSwiz 	}
2293f844e94eSwiz 
2294f844e94eSwiz 	sy = screen_size_y(s) - cy;
2295f844e94eSwiz 	if (sy < y) {
2296f844e94eSwiz 		lines = y - sy + 1;
2297f844e94eSwiz 		if (image_scroll_up(s, lines) && ctx->wp != NULL)
2298f844e94eSwiz 			ctx->wp->flags |= PANE_REDRAW;
2299f844e94eSwiz 		for (i = 0; i < lines; i++) {
2300f844e94eSwiz 			grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1,
2301f844e94eSwiz 			    bg);
2302f844e94eSwiz 			screen_write_collect_scroll(ctx, bg);
2303f844e94eSwiz 		}
2304f844e94eSwiz 		ctx->scrolled += lines;
2305f844e94eSwiz 		if (lines > cy)
2306f844e94eSwiz 			screen_write_cursormove(ctx, -1, 0, 0);
2307f844e94eSwiz 		else
2308f844e94eSwiz 			screen_write_cursormove(ctx, -1, cy - lines, 0);
2309f844e94eSwiz 	}
2310f844e94eSwiz 	screen_write_collect_flush(ctx, 0, __func__);
2311f844e94eSwiz 
2312f844e94eSwiz 	screen_write_initctx(ctx, &ttyctx, 0);
2313f844e94eSwiz 	ttyctx.ptr = image_store(s, si);
2314f844e94eSwiz 
2315f844e94eSwiz 	tty_write(tty_cmd_sixelimage, &ttyctx);
2316f844e94eSwiz 
2317f844e94eSwiz 	screen_write_cursormove(ctx, 0, cy + y, 0);
2318f844e94eSwiz }
2319f844e94eSwiz #endif
2320f844e94eSwiz 
2321e271dbb8Schristos /* Turn alternate screen on. */
2322e271dbb8Schristos void
2323e271dbb8Schristos screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
2324e271dbb8Schristos     int cursor)
2325e271dbb8Schristos {
2326e271dbb8Schristos 	struct tty_ctx		 ttyctx;
2327e271dbb8Schristos 	struct window_pane	*wp = ctx->wp;
2328e271dbb8Schristos 
2329e271dbb8Schristos 	if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2330e271dbb8Schristos 		return;
2331e271dbb8Schristos 
2332e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
2333e271dbb8Schristos 	screen_alternate_on(ctx->s, gc, cursor);
2334e271dbb8Schristos 
2335e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 1);
2336f844e94eSwiz 	if (ttyctx.redraw_cb != NULL)
2337e271dbb8Schristos 		ttyctx.redraw_cb(&ttyctx);
2338e271dbb8Schristos }
2339e271dbb8Schristos 
2340e271dbb8Schristos /* Turn alternate screen off. */
2341e271dbb8Schristos void
2342e271dbb8Schristos screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
2343e271dbb8Schristos     int cursor)
2344e271dbb8Schristos {
2345e271dbb8Schristos 	struct tty_ctx		 ttyctx;
2346e271dbb8Schristos 	struct window_pane	*wp = ctx->wp;
2347e271dbb8Schristos 
2348e271dbb8Schristos 	if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2349e271dbb8Schristos 		return;
2350e271dbb8Schristos 
2351e271dbb8Schristos 	screen_write_collect_flush(ctx, 0, __func__);
2352e271dbb8Schristos 	screen_alternate_off(ctx->s, gc, cursor);
2353e271dbb8Schristos 
2354e271dbb8Schristos 	screen_write_initctx(ctx, &ttyctx, 1);
2355f844e94eSwiz 	if (ttyctx.redraw_cb != NULL)
2356e271dbb8Schristos 		ttyctx.redraw_cb(&ttyctx);
2357e271dbb8Schristos }
2358