xref: /openbsd-src/usr.bin/tmux/grid.c (revision 61e9d0de7a4c2cb6906206c99b245d89b1ad9d87)
1*61e9d0deSnicm /* $OpenBSD: grid.c,v 1.135 2024/11/20 20:54:02 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm 
217d053cf9Snicm #include <stdlib.h>
22311827fbSnicm #include <string.h>
23311827fbSnicm 
24311827fbSnicm #include "tmux.h"
25311827fbSnicm 
26311827fbSnicm /*
27311827fbSnicm  * Grid data. This is the basic data structure that represents what is shown on
28311827fbSnicm  * screen.
29311827fbSnicm  *
30311827fbSnicm  * A grid is a grid of cells (struct grid_cell). Lines are not allocated until
31311827fbSnicm  * cells in that line are written to. The grid is split into history and
32311827fbSnicm  * viewable data with the history starting at row (line) 0 and extending to
33311827fbSnicm  * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All
34311827fbSnicm  * functions in this file work on absolute coordinates, grid-view.c has
35311827fbSnicm  * functions which work on the screen data.
36311827fbSnicm  */
37311827fbSnicm 
38311827fbSnicm /* Default grid cell data. */
39e931849fSnicm const struct grid_cell grid_default_cell = {
40f353bcb0Snicm 	{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
41e931849fSnicm };
42d1450c2bSnicm 
4371a06389Snicm /*
4471a06389Snicm  * Padding grid cell data. Padding cells are the only zero width cell that
4571a06389Snicm  * appears in the grid - because of this, they are always extended cells.
4671a06389Snicm  */
4771a06389Snicm static const struct grid_cell grid_padding_cell = {
48f353bcb0Snicm 	{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
4971a06389Snicm };
5071a06389Snicm 
51d1450c2bSnicm /* Cleared grid cell data. */
5271a06389Snicm static const struct grid_cell grid_cleared_cell = {
53f353bcb0Snicm 	{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
54d1450c2bSnicm };
55d1450c2bSnicm static const struct grid_cell_entry grid_cleared_entry = {
564b9d1c52Stb 	{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
57e931849fSnicm };
58311827fbSnicm 
599c8da6a8Snicm /* Store cell in entry. */
609c8da6a8Snicm static void
619c8da6a8Snicm grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
629c8da6a8Snicm     u_char c)
639c8da6a8Snicm {
64d1450c2bSnicm 	gce->flags = (gc->flags & ~GRID_FLAG_CLEARED);
659c8da6a8Snicm 
669c8da6a8Snicm 	gce->data.fg = gc->fg & 0xff;
679c8da6a8Snicm 	if (gc->fg & COLOUR_FLAG_256)
689c8da6a8Snicm 		gce->flags |= GRID_FLAG_FG256;
699c8da6a8Snicm 
709c8da6a8Snicm 	gce->data.bg = gc->bg & 0xff;
719c8da6a8Snicm 	if (gc->bg & COLOUR_FLAG_256)
729c8da6a8Snicm 		gce->flags |= GRID_FLAG_BG256;
739c8da6a8Snicm 
749c8da6a8Snicm 	gce->data.attr = gc->attr;
759c8da6a8Snicm 	gce->data.data = c;
769c8da6a8Snicm }
779c8da6a8Snicm 
78d1450c2bSnicm /* Check if a cell should be an extended cell. */
79d23e027aSnicm static int
80d23e027aSnicm grid_need_extended_cell(const struct grid_cell_entry *gce,
81d23e027aSnicm     const struct grid_cell *gc)
82d23e027aSnicm {
83d23e027aSnicm 	if (gce->flags & GRID_FLAG_EXTENDED)
84d23e027aSnicm 		return (1);
851779e050Snicm 	if (gc->attr > 0xff)
861779e050Snicm 		return (1);
870663b627Snicm 	if (gc->data.size > 1 || gc->data.width > 1)
88d23e027aSnicm 		return (1);
89d23e027aSnicm 	if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
90d23e027aSnicm 		return (1);
9134568295Snicm 	if (gc->us != 8) /* only supports 256 or RGB */
92bc174b93Snicm 		return (1);
932df6775cSnicm 	if (gc->link != 0)
942df6775cSnicm 		return (1);
9502d75531Snicm 	if (gc->flags & GRID_FLAG_TAB)
9602d75531Snicm 		return (1);
97d23e027aSnicm 	return (0);
98d23e027aSnicm }
99d23e027aSnicm 
100d1450c2bSnicm /* Get an extended cell. */
101d1450c2bSnicm static void
102d1450c2bSnicm grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
103d1450c2bSnicm     int flags)
104d1450c2bSnicm {
105d1450c2bSnicm 	u_int at = gl->extdsize + 1;
106d1450c2bSnicm 
107d1450c2bSnicm 	gl->extddata = xreallocarray(gl->extddata, at, sizeof *gl->extddata);
108d1450c2bSnicm 	gl->extdsize = at;
109d1450c2bSnicm 
110d1450c2bSnicm 	gce->offset = at - 1;
111d1450c2bSnicm 	gce->flags = (flags | GRID_FLAG_EXTENDED);
112d1450c2bSnicm }
113d1450c2bSnicm 
114d1450c2bSnicm /* Set cell as extended. */
1155832c8deSnicm static struct grid_extd_entry *
116d1450c2bSnicm grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
117d1450c2bSnicm     const struct grid_cell *gc)
118d1450c2bSnicm {
1195832c8deSnicm 	struct grid_extd_entry	*gee;
120d1450c2bSnicm 	int			 flags = (gc->flags & ~GRID_FLAG_CLEARED);
121d21ef8e0Snicm 	utf8_char		 uc;
122d1450c2bSnicm 
123d1450c2bSnicm 	if (~gce->flags & GRID_FLAG_EXTENDED)
124d1450c2bSnicm 		grid_get_extended_cell(gl, gce, flags);
125d1450c2bSnicm 	else if (gce->offset >= gl->extdsize)
126d1450c2bSnicm 		fatalx("offset too big");
127d1450c2bSnicm 	gl->flags |= GRID_LINE_EXTENDED;
128d1450c2bSnicm 
12902d75531Snicm 	if (gc->flags & GRID_FLAG_TAB)
13002d75531Snicm 		uc = gc->data.width;
13102d75531Snicm 	else
132d21ef8e0Snicm 		utf8_from_data(&gc->data, &uc);
133d21ef8e0Snicm 
1345832c8deSnicm 	gee = &gl->extddata[gce->offset];
135d21ef8e0Snicm 	gee->data = uc;
1365832c8deSnicm 	gee->attr = gc->attr;
1375832c8deSnicm 	gee->flags = flags;
1385832c8deSnicm 	gee->fg = gc->fg;
1395832c8deSnicm 	gee->bg = gc->bg;
1405832c8deSnicm 	gee->us = gc->us;
1412df6775cSnicm 	gee->link = gc->link;
1425832c8deSnicm 	return (gee);
143d1450c2bSnicm }
144d1450c2bSnicm 
14505e39634Snicm /* Free up unused extended cells. */
14605e39634Snicm static void
14705e39634Snicm grid_compact_line(struct grid_line *gl)
14805e39634Snicm {
14905e39634Snicm 	int			 new_extdsize = 0;
1505832c8deSnicm 	struct grid_extd_entry	*new_extddata;
15105e39634Snicm 	struct grid_cell_entry	*gce;
1525832c8deSnicm 	struct grid_extd_entry	*gee;
15305e39634Snicm 	u_int			 px, idx;
15405e39634Snicm 
15505e39634Snicm 	if (gl->extdsize == 0)
15605e39634Snicm 		return;
15705e39634Snicm 
15805e39634Snicm 	for (px = 0; px < gl->cellsize; px++) {
15905e39634Snicm 		gce = &gl->celldata[px];
16005e39634Snicm 		if (gce->flags & GRID_FLAG_EXTENDED)
16105e39634Snicm 			new_extdsize++;
16205e39634Snicm 	}
16305e39634Snicm 
16405e39634Snicm 	if (new_extdsize == 0) {
16505e39634Snicm 		free(gl->extddata);
16605e39634Snicm 		gl->extddata = NULL;
16705e39634Snicm 		gl->extdsize = 0;
16805e39634Snicm 		return;
16905e39634Snicm 	}
17005e39634Snicm 	new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata);
17105e39634Snicm 
17205e39634Snicm 	idx = 0;
17305e39634Snicm 	for (px = 0; px < gl->cellsize; px++) {
17405e39634Snicm 		gce = &gl->celldata[px];
17505e39634Snicm 		if (gce->flags & GRID_FLAG_EXTENDED) {
1765832c8deSnicm 			gee = &gl->extddata[gce->offset];
1775832c8deSnicm 			memcpy(&new_extddata[idx], gee, sizeof *gee);
17805e39634Snicm 			gce->offset = idx++;
17905e39634Snicm 		}
18005e39634Snicm 	}
18105e39634Snicm 
18205e39634Snicm 	free(gl->extddata);
18305e39634Snicm 	gl->extddata = new_extddata;
18405e39634Snicm 	gl->extdsize = new_extdsize;
18505e39634Snicm }
18605e39634Snicm 
187540a299fSnicm /* Get line data. */
1887621e88fSnicm struct grid_line *
1897621e88fSnicm grid_get_line(struct grid *gd, u_int line)
1907621e88fSnicm {
1917621e88fSnicm 	return (&gd->linedata[line]);
1927621e88fSnicm }
1937621e88fSnicm 
194540a299fSnicm /* Adjust number of lines. */
1957621e88fSnicm void
1967621e88fSnicm grid_adjust_lines(struct grid *gd, u_int lines)
1977621e88fSnicm {
1987621e88fSnicm 	gd->linedata = xreallocarray(gd->linedata, lines, sizeof *gd->linedata);
1997621e88fSnicm }
2007621e88fSnicm 
201e931849fSnicm /* Copy default into a cell. */
202e931849fSnicm static void
203ae4624e7Snicm grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
204e931849fSnicm {
2052d51ea10Snicm 	struct grid_line	*gl = &gd->linedata[py];
2062d51ea10Snicm 	struct grid_cell_entry	*gce = &gl->celldata[px];
2075832c8deSnicm 	struct grid_extd_entry	*gee;
2082d51ea10Snicm 
209d1450c2bSnicm 	memcpy(gce, &grid_cleared_entry, sizeof *gce);
21026a51baaSnicm 	if (bg != 8) {
2112d51ea10Snicm 		if (bg & COLOUR_FLAG_RGB) {
212d1450c2bSnicm 			grid_get_extended_cell(gl, gce, gce->flags);
2135832c8deSnicm 			gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
2145832c8deSnicm 			gee->bg = bg;
2152d51ea10Snicm 		} else {
2162d51ea10Snicm 			if (bg & COLOUR_FLAG_256)
2172d51ea10Snicm 				gce->flags |= GRID_FLAG_BG256;
2182d51ea10Snicm 			gce->data.bg = bg;
2192d51ea10Snicm 		}
220e931849fSnicm 	}
22126a51baaSnicm }
222e931849fSnicm 
2239d6f50a6Snicm /* Check grid y position. */
2245fd00eceSnicm static int
22533af54cfSnicm grid_check_y(struct grid *gd, const char *from, u_int py)
226311827fbSnicm {
22763c9949dSnicm 	if (py >= gd->hsize + gd->sy) {
22833af54cfSnicm 		log_debug("%s: y out of range: %u", from, py);
229311827fbSnicm 		return (-1);
230311827fbSnicm 	}
231311827fbSnicm 	return (0);
232311827fbSnicm }
23344c84f9eSnicm 
234bf46d0ceSnicm /* Check if two styles are (visibly) the same. */
235bf46d0ceSnicm int
236bf46d0ceSnicm grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
237bf46d0ceSnicm {
238c851f995Snicm 	int flags1 = gc1->flags, flags2 = gc2->flags;;
239c851f995Snicm 
240bf46d0ceSnicm 	if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
241bf46d0ceSnicm 		return (0);
242c851f995Snicm 	if (gc1->attr != gc2->attr)
243c851f995Snicm 		return (0);
244c851f995Snicm 	if ((flags1 & ~GRID_FLAG_CLEARED) != (flags2 & ~GRID_FLAG_CLEARED))
245bf46d0ceSnicm 		return (0);
2462df6775cSnicm 	if (gc1->link != gc2->link)
2472df6775cSnicm 		return (0);
248bf46d0ceSnicm 	return (1);
249bf46d0ceSnicm }
250bf46d0ceSnicm 
2515fd00eceSnicm /* Compare grid cells. Return 1 if equal, 0 if not. */
2525fd00eceSnicm int
253bf46d0ceSnicm grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
2545fd00eceSnicm {
255bf46d0ceSnicm 	if (!grid_cells_look_equal(gc1, gc2))
2565fd00eceSnicm 		return (0);
257bf46d0ceSnicm 	if (gc1->data.width != gc2->data.width)
2585fd00eceSnicm 		return (0);
259bf46d0ceSnicm 	if (gc1->data.size != gc2->data.size)
2605fd00eceSnicm 		return (0);
261bf46d0ceSnicm 	return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0);
2625fd00eceSnicm }
2635fd00eceSnicm 
26402d75531Snicm /* Set grid cell to a tab. */
26502d75531Snicm void
26602d75531Snicm grid_set_tab(struct grid_cell *gc, u_int width)
26702d75531Snicm {
268c851f995Snicm 	memset(gc->data.data, 0, sizeof gc->data.data);
26902d75531Snicm 	gc->flags |= GRID_FLAG_TAB;
27002d75531Snicm 	gc->data.width = gc->data.size = gc->data.have = width;
271c851f995Snicm 	memset(gc->data.data, ' ', gc->data.size);
27202d75531Snicm }
27302d75531Snicm 
274bd28ed35Snicm /* Free one line. */
275bd28ed35Snicm static void
276bd28ed35Snicm grid_free_line(struct grid *gd, u_int py)
277bd28ed35Snicm {
278bd28ed35Snicm 	free(gd->linedata[py].celldata);
279bd28ed35Snicm 	gd->linedata[py].celldata = NULL;
280bd28ed35Snicm 	free(gd->linedata[py].extddata);
281bd28ed35Snicm 	gd->linedata[py].extddata = NULL;
282bd28ed35Snicm }
283bd28ed35Snicm 
284bd28ed35Snicm /* Free several lines. */
285bd28ed35Snicm static void
286bd28ed35Snicm grid_free_lines(struct grid *gd, u_int py, u_int ny)
287bd28ed35Snicm {
288bd28ed35Snicm 	u_int	yy;
289bd28ed35Snicm 
290bd28ed35Snicm 	for (yy = py; yy < py + ny; yy++)
291bd28ed35Snicm 		grid_free_line(gd, yy);
292bd28ed35Snicm }
293bd28ed35Snicm 
294311827fbSnicm /* Create a new grid. */
295311827fbSnicm struct grid *
296311827fbSnicm grid_create(u_int sx, u_int sy, u_int hlimit)
297311827fbSnicm {
298311827fbSnicm 	struct grid	*gd;
299311827fbSnicm 
300311827fbSnicm 	gd = xmalloc(sizeof *gd);
301311827fbSnicm 	gd->sx = sx;
302311827fbSnicm 	gd->sy = sy;
303311827fbSnicm 
3044bf7840fSnicm 	if (hlimit != 0)
305953d773cSnicm 		gd->flags = GRID_HISTORY;
3064bf7840fSnicm 	else
3074bf7840fSnicm 		gd->flags = 0;
308953d773cSnicm 
30993ccd604Snicm 	gd->hscrolled = 0;
310311827fbSnicm 	gd->hsize = 0;
311311827fbSnicm 	gd->hlimit = hlimit;
312311827fbSnicm 
313f2ca94d1Snicm 	if (gd->sy != 0)
3149554a682Snicm 		gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
315f2ca94d1Snicm 	else
316f2ca94d1Snicm 		gd->linedata = NULL;
317311827fbSnicm 
318311827fbSnicm 	return (gd);
319311827fbSnicm }
320311827fbSnicm 
321311827fbSnicm /* Destroy grid. */
322311827fbSnicm void
323311827fbSnicm grid_destroy(struct grid *gd)
324311827fbSnicm {
325bd28ed35Snicm 	grid_free_lines(gd, 0, gd->hsize + gd->sy);
326311827fbSnicm 
3277d053cf9Snicm 	free(gd->linedata);
328311827fbSnicm 
3297d053cf9Snicm 	free(gd);
330311827fbSnicm }
331311827fbSnicm 
332311827fbSnicm /* Compare grids. */
333311827fbSnicm int
334311827fbSnicm grid_compare(struct grid *ga, struct grid *gb)
335311827fbSnicm {
3369554a682Snicm 	struct grid_line	*gla, *glb;
337e931849fSnicm 	struct grid_cell	 gca, gcb;
338311827fbSnicm 	u_int			 xx, yy;
339311827fbSnicm 
340b5cbd9e8Snicm 	if (ga->sx != gb->sx || ga->sy != gb->sy)
341311827fbSnicm 		return (1);
342311827fbSnicm 
343311827fbSnicm 	for (yy = 0; yy < ga->sy; yy++) {
3449554a682Snicm 		gla = &ga->linedata[yy];
3459554a682Snicm 		glb = &gb->linedata[yy];
3469554a682Snicm 		if (gla->cellsize != glb->cellsize)
347311827fbSnicm 			return (1);
348e931849fSnicm 		for (xx = 0; xx < gla->cellsize; xx++) {
349e931849fSnicm 			grid_get_cell(ga, xx, yy, &gca);
350e931849fSnicm 			grid_get_cell(gb, xx, yy, &gcb);
3515fd00eceSnicm 			if (!grid_cells_equal(&gca, &gcb))
352311827fbSnicm 				return (1);
353311827fbSnicm 		}
354311827fbSnicm 	}
355311827fbSnicm 
356311827fbSnicm 	return (0);
357311827fbSnicm }
358311827fbSnicm 
35991c2caa4Snicm /* Trim lines from the history. */
36091c2caa4Snicm static void
36191c2caa4Snicm grid_trim_history(struct grid *gd, u_int ny)
36291c2caa4Snicm {
36391c2caa4Snicm 	grid_free_lines(gd, 0, ny);
36491c2caa4Snicm 	memmove(&gd->linedata[0], &gd->linedata[ny],
36591c2caa4Snicm 	    (gd->hsize + gd->sy - ny) * (sizeof *gd->linedata));
36691c2caa4Snicm }
36791c2caa4Snicm 
368b3cd1016Snicm /*
369b3cd1016Snicm  * Collect lines from the history if at the limit. Free the top (oldest) 10%
370b3cd1016Snicm  * and shift up.
371b3cd1016Snicm  */
372311827fbSnicm void
373bd28ed35Snicm grid_collect_history(struct grid *gd)
374311827fbSnicm {
375bd28ed35Snicm 	u_int	ny;
376311827fbSnicm 
377d5f2217bSnicm 	if (gd->hsize == 0 || gd->hsize < gd->hlimit)
378b3cd1016Snicm 		return;
379b3cd1016Snicm 
380bd28ed35Snicm 	ny = gd->hlimit / 10;
381bd28ed35Snicm 	if (ny < 1)
382bd28ed35Snicm 		ny = 1;
383d5f2217bSnicm 	if (ny > gd->hsize)
384d5f2217bSnicm 		ny = gd->hsize;
385311827fbSnicm 
386bd28ed35Snicm 	/*
387bd28ed35Snicm 	 * Free the lines from 0 to ny then move the remaining lines over
388bd28ed35Snicm 	 * them.
389bd28ed35Snicm 	 */
39091c2caa4Snicm 	grid_trim_history(gd, ny);
391bd28ed35Snicm 
392bd28ed35Snicm 	gd->hsize -= ny;
39393ccd604Snicm 	if (gd->hscrolled > gd->hsize)
39493ccd604Snicm 		gd->hscrolled = gd->hsize;
395311827fbSnicm }
396311827fbSnicm 
397a0a1bba7Snicm /* Remove lines from the bottom of the history. */
398a0a1bba7Snicm void
399a0a1bba7Snicm grid_remove_history(struct grid *gd, u_int ny)
400a0a1bba7Snicm {
401a0a1bba7Snicm 	u_int	yy;
402a0a1bba7Snicm 
403a0a1bba7Snicm 	if (ny > gd->hsize)
404a0a1bba7Snicm 		return;
405a0a1bba7Snicm 	for (yy = 0; yy < ny; yy++)
406a0a1bba7Snicm 		grid_free_line(gd, gd->hsize + gd->sy - 1 - yy);
407a0a1bba7Snicm 	gd->hsize -= ny;
408a0a1bba7Snicm }
409a0a1bba7Snicm 
410b3cd1016Snicm /*
411b3cd1016Snicm  * Scroll the entire visible screen, moving one line into the history. Just
412b3cd1016Snicm  * allocate a new line at the bottom and move the history size indicator.
413b3cd1016Snicm  */
414b3cd1016Snicm void
415ae4624e7Snicm grid_scroll_history(struct grid *gd, u_int bg)
416b3cd1016Snicm {
417b3cd1016Snicm 	u_int	yy;
418311827fbSnicm 
419b3cd1016Snicm 	yy = gd->hsize + gd->sy;
42064cf113cSnicm 	gd->linedata = xreallocarray(gd->linedata, yy + 1,
42164cf113cSnicm 	    sizeof *gd->linedata);
422ae4624e7Snicm 	grid_empty_line(gd, yy, bg);
423311827fbSnicm 
42493ccd604Snicm 	gd->hscrolled++;
42505e39634Snicm 	grid_compact_line(&gd->linedata[gd->hsize]);
426a711f92aSnicm 	gd->linedata[gd->hsize].time = current_time;
427311827fbSnicm 	gd->hsize++;
428311827fbSnicm }
429311827fbSnicm 
430ca085602Snicm /* Clear the history. */
431ca085602Snicm void
432ca085602Snicm grid_clear_history(struct grid *gd)
433ca085602Snicm {
43491c2caa4Snicm 	grid_trim_history(gd, gd->hsize);
435ca085602Snicm 
43693ccd604Snicm 	gd->hscrolled = 0;
437ca085602Snicm 	gd->hsize = 0;
43893ccd604Snicm 
439ca085602Snicm 	gd->linedata = xreallocarray(gd->linedata, gd->sy,
440ca085602Snicm 	    sizeof *gd->linedata);
441ca085602Snicm }
442ca085602Snicm 
443b3cd1016Snicm /* Scroll a region up, moving the top line into the history. */
444b3cd1016Snicm void
4456d5c64a0Snicm grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
446b3cd1016Snicm {
4471350555bSnicm 	struct grid_line	*gl_history, *gl_upper;
448b3cd1016Snicm 	u_int			 yy;
449b3cd1016Snicm 
450b3cd1016Snicm 	/* Create a space for a new line. */
451b3cd1016Snicm 	yy = gd->hsize + gd->sy;
45264cf113cSnicm 	gd->linedata = xreallocarray(gd->linedata, yy + 1,
45364cf113cSnicm 	    sizeof *gd->linedata);
454b3cd1016Snicm 
455b3cd1016Snicm 	/* Move the entire screen down to free a space for this line. */
456b3cd1016Snicm 	gl_history = &gd->linedata[gd->hsize];
457b3cd1016Snicm 	memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);
458b3cd1016Snicm 
459b3cd1016Snicm 	/* Adjust the region and find its start and end. */
460b3cd1016Snicm 	upper++;
461b3cd1016Snicm 	gl_upper = &gd->linedata[upper];
462b3cd1016Snicm 	lower++;
463b3cd1016Snicm 
464b3cd1016Snicm 	/* Move the line into the history. */
465b3cd1016Snicm 	memcpy(gl_history, gl_upper, sizeof *gl_history);
466a711f92aSnicm 	gl_history->time = current_time;
467b3cd1016Snicm 
468b3cd1016Snicm 	/* Then move the region up and clear the bottom line. */
469b3cd1016Snicm 	memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
4706d5c64a0Snicm 	grid_empty_line(gd, lower, bg);
471b3cd1016Snicm 
472b3cd1016Snicm 	/* Move the history offset down over the line. */
47393ccd604Snicm 	gd->hscrolled++;
474b3cd1016Snicm 	gd->hsize++;
475b3cd1016Snicm }
476b3cd1016Snicm 
477311827fbSnicm /* Expand line to fit to cell. */
478ced21769Snicm static void
479ae4624e7Snicm grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
480311827fbSnicm {
4819554a682Snicm 	struct grid_line	*gl;
4826d39d31dSnicm 	u_int			 xx;
483311827fbSnicm 
4849554a682Snicm 	gl = &gd->linedata[py];
4856d39d31dSnicm 	if (sx <= gl->cellsize)
486311827fbSnicm 		return;
487311827fbSnicm 
48826cd2289Snicm 	if (sx < gd->sx / 4)
48926cd2289Snicm 		sx = gd->sx / 4;
49026cd2289Snicm 	else if (sx < gd->sx / 2)
49126cd2289Snicm 		sx = gd->sx / 2;
49214b12344Snicm 	else if (gd->sx > sx)
493b68f401aSnicm 		sx = gd->sx;
494b68f401aSnicm 
49564cf113cSnicm 	gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
4969554a682Snicm 	for (xx = gl->cellsize; xx < sx; xx++)
497ae4624e7Snicm 		grid_clear_cell(gd, xx, py, bg);
4989554a682Snicm 	gl->cellsize = sx;
499311827fbSnicm }
500311827fbSnicm 
501ae4624e7Snicm /* Empty a line and set background colour if needed. */
50275343b37Snicm void
503ae4624e7Snicm grid_empty_line(struct grid *gd, u_int py, u_int bg)
504ae4624e7Snicm {
505ae4624e7Snicm 	memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
506d78fd057Snicm 	if (!COLOUR_DEFAULT(bg))
507ae4624e7Snicm 		grid_expand_line(gd, py, gd->sx, bg);
508ae4624e7Snicm }
509ae4624e7Snicm 
510e5d24057Snicm /* Peek at grid line. */
511e5d24057Snicm const struct grid_line *
512e5d24057Snicm grid_peek_line(struct grid *gd, u_int py)
513e5d24057Snicm {
51433af54cfSnicm 	if (grid_check_y(gd, __func__, py) != 0)
515e5d24057Snicm 		return (NULL);
516e5d24057Snicm 	return (&gd->linedata[py]);
517e5d24057Snicm }
518e5d24057Snicm 
519414c61adSnicm /* Get cell from line. */
520414c61adSnicm static void
521414c61adSnicm grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
522311827fbSnicm {
523414c61adSnicm 	struct grid_cell_entry	*gce = &gl->celldata[px];
5245832c8deSnicm 	struct grid_extd_entry	*gee;
525311827fbSnicm 
526e931849fSnicm 	if (gce->flags & GRID_FLAG_EXTENDED) {
527e931849fSnicm 		if (gce->offset >= gl->extdsize)
528e931849fSnicm 			memcpy(gc, &grid_default_cell, sizeof *gc);
5295832c8deSnicm 		else {
5305832c8deSnicm 			gee = &gl->extddata[gce->offset];
5315832c8deSnicm 			gc->flags = gee->flags;
5325832c8deSnicm 			gc->attr = gee->attr;
5335832c8deSnicm 			gc->fg = gee->fg;
5345832c8deSnicm 			gc->bg = gee->bg;
5355832c8deSnicm 			gc->us = gee->us;
5362df6775cSnicm 			gc->link = gee->link;
53702d75531Snicm 
53802d75531Snicm 			if (gc->flags & GRID_FLAG_TAB)
53902d75531Snicm 				grid_set_tab(gc, gee->data);
54002d75531Snicm 			else
54170a57860Snicm 				utf8_to_data(gee->data, &gc->data);
5425832c8deSnicm 		}
543e931849fSnicm 		return;
544e931849fSnicm 	}
545e931849fSnicm 
54692c661c5Snicm 	gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
547e931849fSnicm 	gc->attr = gce->data.attr;
548e931849fSnicm 	gc->fg = gce->data.fg;
54992c661c5Snicm 	if (gce->flags & GRID_FLAG_FG256)
55092c661c5Snicm 		gc->fg |= COLOUR_FLAG_256;
551e931849fSnicm 	gc->bg = gce->data.bg;
55292c661c5Snicm 	if (gce->flags & GRID_FLAG_BG256)
55392c661c5Snicm 		gc->bg |= COLOUR_FLAG_256;
554f353bcb0Snicm 	gc->us = 8;
555e931849fSnicm 	utf8_set(&gc->data, gce->data.data);
5562df6775cSnicm 	gc->link = 0;
557311827fbSnicm }
558311827fbSnicm 
559414c61adSnicm /* Get cell for reading. */
560414c61adSnicm void
561414c61adSnicm grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
562414c61adSnicm {
56333af54cfSnicm 	if (grid_check_y(gd, __func__, py) != 0 ||
564610f37cfSnicm 	    px >= gd->linedata[py].cellsize)
565414c61adSnicm 		memcpy(gc, &grid_default_cell, sizeof *gc);
566610f37cfSnicm 	else
567610f37cfSnicm 		grid_get_cell1(&gd->linedata[py], px, gc);
568414c61adSnicm }
569414c61adSnicm 
57071a06389Snicm /* Set cell at position. */
571311827fbSnicm void
572f7b15bf9Snicm grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
573311827fbSnicm {
574e931849fSnicm 	struct grid_line	*gl;
575e931849fSnicm 	struct grid_cell_entry	*gce;
576e931849fSnicm 
57733af54cfSnicm 	if (grid_check_y(gd, __func__, py) != 0)
578311827fbSnicm 		return;
579311827fbSnicm 
580ae4624e7Snicm 	grid_expand_line(gd, py, px + 1, 8);
581e931849fSnicm 
582e931849fSnicm 	gl = &gd->linedata[py];
583ae4624e7Snicm 	if (px + 1 > gl->cellused)
584ae4624e7Snicm 		gl->cellused = px + 1;
585ae4624e7Snicm 
5869c8da6a8Snicm 	gce = &gl->celldata[px];
587d23e027aSnicm 	if (grid_need_extended_cell(gce, gc))
5882d51ea10Snicm 		grid_extended_cell(gl, gce, gc);
5899c8da6a8Snicm 	else
5909c8da6a8Snicm 		grid_store_cell(gce, gc, gc->data.data[0]);
591311827fbSnicm }
592311827fbSnicm 
59371a06389Snicm /* Set padding at position. */
59471a06389Snicm void
59571a06389Snicm grid_set_padding(struct grid *gd, u_int px, u_int py)
59671a06389Snicm {
59771a06389Snicm 	grid_set_cell(gd, px, py, &grid_padding_cell);
59871a06389Snicm }
59971a06389Snicm 
60071a06389Snicm /* Set cells at position. */
6018248eab7Snicm void
6028248eab7Snicm grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
6038248eab7Snicm     const char *s, size_t slen)
6048248eab7Snicm {
6058248eab7Snicm 	struct grid_line	*gl;
6068248eab7Snicm 	struct grid_cell_entry	*gce;
6075832c8deSnicm 	struct grid_extd_entry	*gee;
6088248eab7Snicm 	u_int			 i;
6098248eab7Snicm 
61033af54cfSnicm 	if (grid_check_y(gd, __func__, py) != 0)
6118248eab7Snicm 		return;
6128248eab7Snicm 
6138248eab7Snicm 	grid_expand_line(gd, py, px + slen, 8);
6148248eab7Snicm 
6158248eab7Snicm 	gl = &gd->linedata[py];
6168248eab7Snicm 	if (px + slen > gl->cellused)
6178248eab7Snicm 		gl->cellused = px + slen;
6188248eab7Snicm 
6198248eab7Snicm 	for (i = 0; i < slen; i++) {
6208248eab7Snicm 		gce = &gl->celldata[px + i];
621d23e027aSnicm 		if (grid_need_extended_cell(gce, gc)) {
6225832c8deSnicm 			gee = grid_extended_cell(gl, gce, gc);
623a49f5513Snicm 			gee->data = utf8_build_one(s[i]);
6248248eab7Snicm 		} else
6258248eab7Snicm 			grid_store_cell(gce, gc, s[i]);
6268248eab7Snicm 	}
6278248eab7Snicm }
6288248eab7Snicm 
6296d39d31dSnicm /* Clear area. */
630311827fbSnicm void
631ae4624e7Snicm grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
632311827fbSnicm {
633d78fd057Snicm 	struct grid_line	*gl;
63415e17edaSnicm 	u_int			 xx, yy, ox, sx;
635311827fbSnicm 
636311827fbSnicm 	if (nx == 0 || ny == 0)
637311827fbSnicm 		return;
638311827fbSnicm 
639311827fbSnicm 	if (px == 0 && nx == gd->sx) {
640ae4624e7Snicm 		grid_clear_lines(gd, py, ny, bg);
641311827fbSnicm 		return;
642311827fbSnicm 	}
643311827fbSnicm 
64433af54cfSnicm 	if (grid_check_y(gd, __func__, py) != 0)
645311827fbSnicm 		return;
64633af54cfSnicm 	if (grid_check_y(gd, __func__, py + ny - 1) != 0)
647311827fbSnicm 		return;
648311827fbSnicm 
649311827fbSnicm 	for (yy = py; yy < py + ny; yy++) {
650d78fd057Snicm 		gl = &gd->linedata[yy];
65115e17edaSnicm 
65215e17edaSnicm 		sx = gd->sx;
65315e17edaSnicm 		if (sx > gl->cellsize)
65415e17edaSnicm 			sx = gl->cellsize;
65515e17edaSnicm 		ox = nx;
65615e17edaSnicm 		if (COLOUR_DEFAULT(bg)) {
65715e17edaSnicm 			if (px > sx)
6586d39d31dSnicm 				continue;
65915e17edaSnicm 			if (px + nx > sx)
66015e17edaSnicm 				ox = sx - px;
6616d39d31dSnicm 		}
66215e17edaSnicm 
66315e17edaSnicm 		grid_expand_line(gd, yy, px + ox, 8); /* default bg first */
66415e17edaSnicm 		for (xx = px; xx < px + ox; xx++)
665ae4624e7Snicm 			grid_clear_cell(gd, xx, yy, bg);
666311827fbSnicm 	}
667311827fbSnicm }
668311827fbSnicm 
669311827fbSnicm /* Clear lines. This just frees and truncates the lines. */
670311827fbSnicm void
671ae4624e7Snicm grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
672311827fbSnicm {
673311827fbSnicm 	u_int	yy;
674311827fbSnicm 
675311827fbSnicm 	if (ny == 0)
676311827fbSnicm 		return;
677311827fbSnicm 
67833af54cfSnicm 	if (grid_check_y(gd, __func__, py) != 0)
679311827fbSnicm 		return;
68033af54cfSnicm 	if (grid_check_y(gd, __func__, py + ny - 1) != 0)
681311827fbSnicm 		return;
682311827fbSnicm 
683311827fbSnicm 	for (yy = py; yy < py + ny; yy++) {
684bd28ed35Snicm 		grid_free_line(gd, yy);
685ae4624e7Snicm 		grid_empty_line(gd, yy, bg);
686311827fbSnicm 	}
687f256eafcSnicm 	if (py != 0)
688f256eafcSnicm 		gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
689311827fbSnicm }
690311827fbSnicm 
691311827fbSnicm /* Move a group of lines. */
692311827fbSnicm void
693ae4624e7Snicm grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
694311827fbSnicm {
695311827fbSnicm 	u_int	yy;
696311827fbSnicm 
697311827fbSnicm 	if (ny == 0 || py == dy)
698311827fbSnicm 		return;
699311827fbSnicm 
70033af54cfSnicm 	if (grid_check_y(gd, __func__, py) != 0)
701311827fbSnicm 		return;
70233af54cfSnicm 	if (grid_check_y(gd, __func__, py + ny - 1) != 0)
703311827fbSnicm 		return;
70433af54cfSnicm 	if (grid_check_y(gd, __func__, dy) != 0)
705311827fbSnicm 		return;
70633af54cfSnicm 	if (grid_check_y(gd, __func__, dy + ny - 1) != 0)
707311827fbSnicm 		return;
708311827fbSnicm 
709311827fbSnicm 	/* Free any lines which are being replaced. */
710311827fbSnicm 	for (yy = dy; yy < dy + ny; yy++) {
711311827fbSnicm 		if (yy >= py && yy < py + ny)
712311827fbSnicm 			continue;
713bd28ed35Snicm 		grid_free_line(gd, yy);
714311827fbSnicm 	}
715f256eafcSnicm 	if (dy != 0)
716f256eafcSnicm 		gd->linedata[dy - 1].flags &= ~GRID_LINE_WRAPPED;
717311827fbSnicm 
718ca085602Snicm 	memmove(&gd->linedata[dy], &gd->linedata[py],
719ca085602Snicm 	    ny * (sizeof *gd->linedata));
720311827fbSnicm 
721bd28ed35Snicm 	/*
722bd28ed35Snicm 	 * Wipe any lines that have been moved (without freeing them - they are
723bd28ed35Snicm 	 * still present).
724bd28ed35Snicm 	 */
725311827fbSnicm 	for (yy = py; yy < py + ny; yy++) {
726ae4624e7Snicm 		if (yy < dy || yy >= dy + ny)
727ae4624e7Snicm 			grid_empty_line(gd, yy, bg);
728311827fbSnicm 	}
729f256eafcSnicm 	if (py != 0 && (py < dy || py >= dy + ny))
730f256eafcSnicm 		gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
731311827fbSnicm }
732311827fbSnicm 
733311827fbSnicm /* Move a group of cells. */
734311827fbSnicm void
735ae4624e7Snicm grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
736ae4624e7Snicm     u_int bg)
737311827fbSnicm {
7389554a682Snicm 	struct grid_line	*gl;
739311827fbSnicm 	u_int			 xx;
740311827fbSnicm 
741311827fbSnicm 	if (nx == 0 || px == dx)
742311827fbSnicm 		return;
743311827fbSnicm 
74433af54cfSnicm 	if (grid_check_y(gd, __func__, py) != 0)
745311827fbSnicm 		return;
7469554a682Snicm 	gl = &gd->linedata[py];
747311827fbSnicm 
748ae4624e7Snicm 	grid_expand_line(gd, py, px + nx, 8);
749ae4624e7Snicm 	grid_expand_line(gd, py, dx + nx, 8);
750ca085602Snicm 	memmove(&gl->celldata[dx], &gl->celldata[px],
751ca085602Snicm 	    nx * sizeof *gl->celldata);
752f6d00760Snicm 	if (dx + nx > gl->cellused)
753f6d00760Snicm 		gl->cellused = dx + nx;
754311827fbSnicm 
755311827fbSnicm 	/* Wipe any cells that have been moved. */
756311827fbSnicm 	for (xx = px; xx < px + nx; xx++) {
757311827fbSnicm 		if (xx >= dx && xx < dx + nx)
758311827fbSnicm 			continue;
759ae4624e7Snicm 		grid_clear_cell(gd, xx, py, bg);
760311827fbSnicm 	}
761311827fbSnicm }
762b6d61ae9Snicm 
76317472193Snicm /* Get ANSI foreground sequence. */
7649883b791Snicm static size_t
76517472193Snicm grid_string_cells_fg(const struct grid_cell *gc, int *values)
76617472193Snicm {
76717472193Snicm 	size_t	n;
76892c661c5Snicm 	u_char	r, g, b;
76917472193Snicm 
77017472193Snicm 	n = 0;
77192c661c5Snicm 	if (gc->fg & COLOUR_FLAG_256) {
77217472193Snicm 		values[n++] = 38;
77317472193Snicm 		values[n++] = 5;
77492c661c5Snicm 		values[n++] = gc->fg & 0xff;
77592c661c5Snicm 	} else if (gc->fg & COLOUR_FLAG_RGB) {
776baec7426Snicm 		values[n++] = 38;
777baec7426Snicm 		values[n++] = 2;
77892c661c5Snicm 		colour_split_rgb(gc->fg, &r, &g, &b);
77992c661c5Snicm 		values[n++] = r;
78092c661c5Snicm 		values[n++] = g;
78192c661c5Snicm 		values[n++] = b;
78217472193Snicm 	} else {
78317472193Snicm 		switch (gc->fg) {
78417472193Snicm 		case 0:
78517472193Snicm 		case 1:
78617472193Snicm 		case 2:
78717472193Snicm 		case 3:
78817472193Snicm 		case 4:
78917472193Snicm 		case 5:
79017472193Snicm 		case 6:
79117472193Snicm 		case 7:
79217472193Snicm 			values[n++] = gc->fg + 30;
79317472193Snicm 			break;
79417472193Snicm 		case 8:
79517472193Snicm 			values[n++] = 39;
79617472193Snicm 			break;
79717472193Snicm 		case 90:
79817472193Snicm 		case 91:
79917472193Snicm 		case 92:
80017472193Snicm 		case 93:
80117472193Snicm 		case 94:
80217472193Snicm 		case 95:
80317472193Snicm 		case 96:
80417472193Snicm 		case 97:
80517472193Snicm 			values[n++] = gc->fg;
80617472193Snicm 			break;
80717472193Snicm 		}
80817472193Snicm 	}
80917472193Snicm 	return (n);
81017472193Snicm }
81117472193Snicm 
81217472193Snicm /* Get ANSI background sequence. */
8139883b791Snicm static size_t
81417472193Snicm grid_string_cells_bg(const struct grid_cell *gc, int *values)
81517472193Snicm {
81617472193Snicm 	size_t	n;
81792c661c5Snicm 	u_char	r, g, b;
81817472193Snicm 
81917472193Snicm 	n = 0;
82092c661c5Snicm 	if (gc->bg & COLOUR_FLAG_256) {
82117472193Snicm 		values[n++] = 48;
82217472193Snicm 		values[n++] = 5;
82392c661c5Snicm 		values[n++] = gc->bg & 0xff;
82492c661c5Snicm 	} else if (gc->bg & COLOUR_FLAG_RGB) {
825baec7426Snicm 		values[n++] = 48;
826baec7426Snicm 		values[n++] = 2;
82792c661c5Snicm 		colour_split_rgb(gc->bg, &r, &g, &b);
82892c661c5Snicm 		values[n++] = r;
82992c661c5Snicm 		values[n++] = g;
83092c661c5Snicm 		values[n++] = b;
83117472193Snicm 	} else {
83217472193Snicm 		switch (gc->bg) {
83317472193Snicm 		case 0:
83417472193Snicm 		case 1:
83517472193Snicm 		case 2:
83617472193Snicm 		case 3:
83717472193Snicm 		case 4:
83817472193Snicm 		case 5:
83917472193Snicm 		case 6:
84017472193Snicm 		case 7:
84117472193Snicm 			values[n++] = gc->bg + 40;
84217472193Snicm 			break;
84317472193Snicm 		case 8:
84417472193Snicm 			values[n++] = 49;
84517472193Snicm 			break;
846b11ba489Snicm 		case 90:
847b11ba489Snicm 		case 91:
848b11ba489Snicm 		case 92:
849b11ba489Snicm 		case 93:
850b11ba489Snicm 		case 94:
851b11ba489Snicm 		case 95:
852b11ba489Snicm 		case 96:
853b11ba489Snicm 		case 97:
854b11ba489Snicm 			values[n++] = gc->bg + 10;
85517472193Snicm 			break;
85617472193Snicm 		}
85717472193Snicm 	}
85817472193Snicm 	return (n);
85917472193Snicm }
86017472193Snicm 
861f2f3d50aSnicm /* Get underscore colour sequence. */
862f2f3d50aSnicm static size_t
863f2f3d50aSnicm grid_string_cells_us(const struct grid_cell *gc, int *values)
864f2f3d50aSnicm {
865f2f3d50aSnicm 	size_t	n;
866f2f3d50aSnicm 	u_char	r, g, b;
867f2f3d50aSnicm 
868f2f3d50aSnicm 	n = 0;
869f2f3d50aSnicm 	if (gc->us & COLOUR_FLAG_256) {
870f2f3d50aSnicm 		values[n++] = 58;
871f2f3d50aSnicm 		values[n++] = 5;
872f2f3d50aSnicm 		values[n++] = gc->us & 0xff;
873f2f3d50aSnicm 	} else if (gc->us & COLOUR_FLAG_RGB) {
874f2f3d50aSnicm 		values[n++] = 58;
875f2f3d50aSnicm 		values[n++] = 2;
876f2f3d50aSnicm 		colour_split_rgb(gc->us, &r, &g, &b);
877f2f3d50aSnicm 		values[n++] = r;
878f2f3d50aSnicm 		values[n++] = g;
879f2f3d50aSnicm 		values[n++] = b;
880f2f3d50aSnicm 	}
881f2f3d50aSnicm 	return (n);
882f2f3d50aSnicm }
883f2f3d50aSnicm 
884f2f3d50aSnicm /* Add on SGR code. */
885f2f3d50aSnicm static void
886f2f3d50aSnicm grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
88732b2c715Snicm     int *oldc, size_t nnewc, size_t noldc, int flags)
888f2f3d50aSnicm {
889f2f3d50aSnicm 	u_int	i;
890f2f3d50aSnicm 	char	tmp[64];
89132b2c715Snicm 	int	reset = (n != 0 && s[0] == 0);
892f2f3d50aSnicm 
89332b2c715Snicm 	if (nnewc == 0)
89432b2c715Snicm 		return; /* no code to add */
89532b2c715Snicm 	if (!reset &&
89632b2c715Snicm 	    nnewc == noldc &&
89732b2c715Snicm 	    memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0)
89832b2c715Snicm 		return; /* no reset and colour unchanged */
89932b2c715Snicm 	if (reset && (newc[0] == 49 || newc[0] == 39))
90032b2c715Snicm 		return; /* reset and colour default */
90132b2c715Snicm 
90232b2c715Snicm 	if (flags & GRID_STRING_ESCAPE_SEQUENCES)
903f2f3d50aSnicm 		strlcat(buf, "\\033[", len);
904f2f3d50aSnicm 	else
905f2f3d50aSnicm 		strlcat(buf, "\033[", len);
906f2f3d50aSnicm 	for (i = 0; i < nnewc; i++) {
907f2f3d50aSnicm 		if (i + 1 < nnewc)
908f2f3d50aSnicm 			xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
909f2f3d50aSnicm 		else
910f2f3d50aSnicm 			xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
911f2f3d50aSnicm 		strlcat(buf, tmp, len);
912f2f3d50aSnicm 	}
913f2f3d50aSnicm 	strlcat(buf, "m", len);
914f2f3d50aSnicm }
915f2f3d50aSnicm 
916e5878dccSnicm static int
917e5878dccSnicm grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
91832b2c715Snicm     const char *uri, int flags)
919e5878dccSnicm {
920e5878dccSnicm 	char	*tmp;
921e5878dccSnicm 
922e5878dccSnicm 	if (strlen(uri) + strlen(id) + 17 >= len)
923e5878dccSnicm 		return (0);
924e5878dccSnicm 
92532b2c715Snicm 	if (flags & GRID_STRING_ESCAPE_SEQUENCES)
926e5878dccSnicm 		strlcat(buf, "\\033]8;", len);
927e5878dccSnicm 	else
928e5878dccSnicm 		strlcat(buf, "\033]8;", len);
929e5878dccSnicm 	if (*id != '\0') {
930e5878dccSnicm 		xasprintf(&tmp, "id=%s;", id);
931e5878dccSnicm 		strlcat(buf, tmp, len);
932e5878dccSnicm 		free(tmp);
933e5878dccSnicm 	} else
934e5878dccSnicm 		strlcat(buf, ";", len);
935e5878dccSnicm 	strlcat(buf, uri, len);
93632b2c715Snicm 	if (flags & GRID_STRING_ESCAPE_SEQUENCES)
937e5878dccSnicm 		strlcat(buf, "\\033\\\\", len);
938e5878dccSnicm 	else
939e5878dccSnicm 		strlcat(buf, "\033\\", len);
940e5878dccSnicm 	return (1);
941e5878dccSnicm }
942e5878dccSnicm 
94317472193Snicm /*
94417472193Snicm  * Returns ANSI code to set particular attributes (colour, bold and so on)
94515b7c10bSnicm  * given a current state.
94617472193Snicm  */
9479883b791Snicm static void
94817472193Snicm grid_string_cells_code(const struct grid_cell *lastgc,
94932b2c715Snicm     const struct grid_cell *gc, char *buf, size_t len, int flags,
950e5878dccSnicm     struct screen *sc, int *has_link)
95117472193Snicm {
952baec7426Snicm 	int			 oldc[64], newc[64], s[128];
95317472193Snicm 	size_t			 noldc, nnewc, n, i;
9540a64112dSnicm 	u_int			 attr = gc->attr, lastattr = lastgc->attr;
95517472193Snicm 	char			 tmp[64];
956e5878dccSnicm 	const char		*uri, *id;
95717472193Snicm 
95832b2c715Snicm 	static const struct {
95917472193Snicm 		u_int	mask;
96017472193Snicm 		u_int	code;
96117472193Snicm 	} attrs[] = {
96217472193Snicm 		{ GRID_ATTR_BRIGHT, 1 },
96317472193Snicm 		{ GRID_ATTR_DIM, 2 },
96417472193Snicm 		{ GRID_ATTR_ITALICS, 3 },
96517472193Snicm 		{ GRID_ATTR_UNDERSCORE, 4 },
96617472193Snicm 		{ GRID_ATTR_BLINK, 5 },
96717472193Snicm 		{ GRID_ATTR_REVERSE, 7 },
9681779e050Snicm 		{ GRID_ATTR_HIDDEN, 8 },
96942caa339Snicm 		{ GRID_ATTR_STRIKETHROUGH, 9 },
97042caa339Snicm 		{ GRID_ATTR_UNDERSCORE_2, 42 },
97142caa339Snicm 		{ GRID_ATTR_UNDERSCORE_3, 43 },
97242caa339Snicm 		{ GRID_ATTR_UNDERSCORE_4, 44 },
97342caa339Snicm 		{ GRID_ATTR_UNDERSCORE_5, 45 },
974d37269efSnicm 		{ GRID_ATTR_OVERLINE, 53 },
97517472193Snicm 	};
97617472193Snicm 	n = 0;
97717472193Snicm 
97817472193Snicm 	/* If any attribute is removed, begin with 0. */
97917472193Snicm 	for (i = 0; i < nitems(attrs); i++) {
980f2f3d50aSnicm 		if (((~attr & attrs[i].mask) &&
981f2f3d50aSnicm 		    (lastattr & attrs[i].mask)) ||
982f353bcb0Snicm 		    (lastgc->us != 8 && gc->us == 8)) {
98317472193Snicm 			s[n++] = 0;
984ee61d68bSnicm 			lastattr &= GRID_ATTR_CHARSET;
98517472193Snicm 			break;
98617472193Snicm 		}
98717472193Snicm 	}
98817472193Snicm 	/* For each attribute that is newly set, add its code. */
98917472193Snicm 	for (i = 0; i < nitems(attrs); i++) {
99017472193Snicm 		if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask))
99117472193Snicm 			s[n++] = attrs[i].code;
99217472193Snicm 	}
99317472193Snicm 
9940a64112dSnicm 	/* Write the attributes. */
99517472193Snicm 	*buf = '\0';
99617472193Snicm 	if (n > 0) {
99732b2c715Snicm 		if (flags & GRID_STRING_ESCAPE_SEQUENCES)
998e5d24057Snicm 			strlcat(buf, "\\033[", len);
999e5d24057Snicm 		else
100017472193Snicm 			strlcat(buf, "\033[", len);
100117472193Snicm 		for (i = 0; i < n; i++) {
100242caa339Snicm 			if (s[i] < 10)
100317472193Snicm 				xsnprintf(tmp, sizeof tmp, "%d", s[i]);
100442caa339Snicm 			else {
100542caa339Snicm 				xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10,
100642caa339Snicm 				    s[i] % 10);
100742caa339Snicm 			}
100817472193Snicm 			strlcat(buf, tmp, len);
100942caa339Snicm 			if (i + 1 < n)
101042caa339Snicm 				strlcat(buf, ";", len);
101117472193Snicm 		}
101217472193Snicm 		strlcat(buf, "m", len);
101317472193Snicm 	}
101417472193Snicm 
10150a64112dSnicm 	/* If the foreground colour changed, write its parameters. */
10160a64112dSnicm 	nnewc = grid_string_cells_fg(gc, newc);
10170a64112dSnicm 	noldc = grid_string_cells_fg(lastgc, oldc);
1018f2f3d50aSnicm 	grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
101932b2c715Snicm 	    flags);
10200a64112dSnicm 
10210a64112dSnicm 	/* If the background colour changed, append its parameters. */
10220a64112dSnicm 	nnewc = grid_string_cells_bg(gc, newc);
10230a64112dSnicm 	noldc = grid_string_cells_bg(lastgc, oldc);
1024f2f3d50aSnicm 	grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
102532b2c715Snicm 	    flags);
1026f2f3d50aSnicm 
1027f2f3d50aSnicm 	/* If the underscore colour changed, append its parameters. */
1028f2f3d50aSnicm 	nnewc = grid_string_cells_us(gc, newc);
1029f2f3d50aSnicm 	noldc = grid_string_cells_us(lastgc, oldc);
1030f2f3d50aSnicm 	grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
103132b2c715Snicm 	    flags);
10320a64112dSnicm 
103317472193Snicm 	/* Append shift in/shift out if needed. */
1034e5d24057Snicm 	if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
103532b2c715Snicm 		if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1036e5d24057Snicm 			strlcat(buf, "\\016", len); /* SO */
1037e5d24057Snicm 		else
103817472193Snicm 			strlcat(buf, "\016", len);  /* SO */
1039e5d24057Snicm 	}
1040e5d24057Snicm 	if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
104132b2c715Snicm 		if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1042e5d24057Snicm 			strlcat(buf, "\\017", len); /* SI */
1043e5d24057Snicm 		else
104417472193Snicm 			strlcat(buf, "\017", len);  /* SI */
104517472193Snicm 	}
1046e5878dccSnicm 
1047e5878dccSnicm 	/* Add hyperlink if changed. */
1048e5878dccSnicm 	if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
1049e5878dccSnicm 		if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
1050e5878dccSnicm 			*has_link = grid_string_cells_add_hyperlink(buf, len,
105132b2c715Snicm 			    id, uri, flags);
1052e5878dccSnicm 		} else if (*has_link) {
1053e5878dccSnicm 			grid_string_cells_add_hyperlink(buf, len, "", "",
105432b2c715Snicm 			    flags);
1055e5878dccSnicm 			*has_link = 0;
1056e5878dccSnicm 		}
1057e5878dccSnicm 	}
1058e5d24057Snicm }
105917472193Snicm 
1060b6d61ae9Snicm /* Convert cells into a string. */
1061b6d61ae9Snicm char *
106217472193Snicm grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
106332b2c715Snicm     struct grid_cell **lastgc, int flags, struct screen *s)
1064b6d61ae9Snicm {
1065e931849fSnicm 	struct grid_cell	 gc;
106617472193Snicm 	static struct grid_cell	 lastgc1;
1067e5d24057Snicm 	const char		*data;
1068e5878dccSnicm 	char			*buf, code[8192];
1069e5d24057Snicm 	size_t			 len, off, size, codelen;
10706af87e9aSnicm 	u_int			 xx, end;
10716af87e9aSnicm 	int			 has_link = 0;
107224e5de37Snicm 	const struct grid_line	*gl;
1073b6d61ae9Snicm 
107419488e89Snicm 	if (lastgc != NULL && *lastgc == NULL) {
107517472193Snicm 		memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1);
107617472193Snicm 		*lastgc = &lastgc1;
107717472193Snicm 	}
107817472193Snicm 
1079b6d61ae9Snicm 	len = 128;
1080b6d61ae9Snicm 	buf = xmalloc(len);
1081b6d61ae9Snicm 	off = 0;
1082b6d61ae9Snicm 
108324e5de37Snicm 	gl = grid_peek_line(gd, py);
108432b2c715Snicm 	if (flags & GRID_STRING_EMPTY_CELLS)
108532b2c715Snicm 		end = gl->cellsize;
108632b2c715Snicm 	else
108732b2c715Snicm 		end = gl->cellused;
1088b6d61ae9Snicm 	for (xx = px; xx < px + nx; xx++) {
108932b2c715Snicm 		if (gl == NULL || xx >= end)
109024e5de37Snicm 			break;
1091e931849fSnicm 		grid_get_cell(gd, xx, py, &gc);
1092e931849fSnicm 		if (gc.flags & GRID_FLAG_PADDING)
1093b6d61ae9Snicm 			continue;
1094b6d61ae9Snicm 
109532b2c715Snicm 		if (flags & GRID_STRING_WITH_SEQUENCES) {
1096e931849fSnicm 			grid_string_cells_code(*lastgc, &gc, code, sizeof code,
109732b2c715Snicm 			    flags, s, &has_link);
109817472193Snicm 			codelen = strlen(code);
1099e931849fSnicm 			memcpy(*lastgc, &gc, sizeof **lastgc);
110017472193Snicm 		} else
110117472193Snicm 			codelen = 0;
110217472193Snicm 
110302d75531Snicm 		if (gc.flags & GRID_FLAG_TAB) {
110402d75531Snicm 			data = "\t";
110502d75531Snicm 			size = 1;
110602d75531Snicm 		} else {
1107e931849fSnicm 			data = gc.data.data;
1108e931849fSnicm 			size = gc.data.size;
110932b2c715Snicm 			if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
111032b2c715Snicm 			    size == 1 &&
111132b2c715Snicm 			    *data == '\\') {
1112aa2a679aSnicm 				data = "\\\\";
1113e5d24057Snicm 				size = 2;
1114e5d24057Snicm 			}
111502d75531Snicm 		}
1116e5d24057Snicm 
1117e5d24057Snicm 		while (len < off + size + codelen + 1) {
111864cf113cSnicm 			buf = xreallocarray(buf, 2, len);
1119b6d61ae9Snicm 			len *= 2;
1120b6d61ae9Snicm 		}
1121b6d61ae9Snicm 
112217472193Snicm 		if (codelen != 0) {
112317472193Snicm 			memcpy(buf + off, code, codelen);
112417472193Snicm 			off += codelen;
112517472193Snicm 		}
1126e5d24057Snicm 		memcpy(buf + off, data, size);
1127e5d24057Snicm 		off += size;
1128b6d61ae9Snicm 	}
1129b6d61ae9Snicm 
1130e5878dccSnicm 	if (has_link) {
1131e5878dccSnicm 		grid_string_cells_add_hyperlink(code, sizeof code, "", "",
113232b2c715Snicm 		    flags);
1133e5878dccSnicm 		codelen = strlen(code);
1134e5878dccSnicm 		while (len < off + size + codelen + 1) {
1135e5878dccSnicm 			buf = xreallocarray(buf, 2, len);
1136e5878dccSnicm 			len *= 2;
1137e5878dccSnicm 		}
1138e5878dccSnicm 		memcpy(buf + off, code, codelen);
1139e5878dccSnicm 		off += codelen;
1140e5878dccSnicm 	}
1141e5878dccSnicm 
114232b2c715Snicm 	if (flags & GRID_STRING_TRIM_SPACES) {
1143570a3589Snicm 		while (off > 0 && buf[off - 1] == ' ')
1144570a3589Snicm 			off--;
1145d5aaf01dSnicm 	}
1146b6d61ae9Snicm 	buf[off] = '\0';
1147e5d24057Snicm 
1148b6d61ae9Snicm 	return (buf);
1149b6d61ae9Snicm }
1150953d773cSnicm 
1151953d773cSnicm /*
1152bd28ed35Snicm  * Duplicate a set of lines between two grids. Both source and destination
1153bd28ed35Snicm  * should be big enough.
1154953d773cSnicm  */
1155953d773cSnicm void
1156f7b15bf9Snicm grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
1157f7b15bf9Snicm     u_int ny)
1158953d773cSnicm {
11599554a682Snicm 	struct grid_line	*dstl, *srcl;
1160953d773cSnicm 	u_int			 yy;
1161953d773cSnicm 
1162953d773cSnicm 	if (dy + ny > dst->hsize + dst->sy)
1163953d773cSnicm 		ny = dst->hsize + dst->sy - dy;
1164953d773cSnicm 	if (sy + ny > src->hsize + src->sy)
1165953d773cSnicm 		ny = src->hsize + src->sy - sy;
1166bd28ed35Snicm 	grid_free_lines(dst, dy, ny);
1167953d773cSnicm 
1168953d773cSnicm 	for (yy = 0; yy < ny; yy++) {
1169b9fa2487Snicm 		srcl = &src->linedata[sy];
1170b9fa2487Snicm 		dstl = &dst->linedata[dy];
11719554a682Snicm 
11729554a682Snicm 		memcpy(dstl, srcl, sizeof *dstl);
11739554a682Snicm 		if (srcl->cellsize != 0) {
1174471ad77dSderaadt 			dstl->celldata = xreallocarray(NULL,
11759554a682Snicm 			    srcl->cellsize, sizeof *dstl->celldata);
11769554a682Snicm 			memcpy(dstl->celldata, srcl->celldata,
11779554a682Snicm 			    srcl->cellsize * sizeof *dstl->celldata);
1178abb4058aSnicm 		} else
1179abb4058aSnicm 			dstl->celldata = NULL;
1180e931849fSnicm 		if (srcl->extdsize != 0) {
1181e931849fSnicm 			dstl->extdsize = srcl->extdsize;
1182e931849fSnicm 			dstl->extddata = xreallocarray(NULL, dstl->extdsize,
1183e931849fSnicm 			    sizeof *dstl->extddata);
1184e931849fSnicm 			memcpy(dstl->extddata, srcl->extddata, dstl->extdsize *
1185e931849fSnicm 			    sizeof *dstl->extddata);
11869a569e26Snicm 		} else
11879a569e26Snicm 			dstl->extddata = NULL;
1188e931849fSnicm 
11899554a682Snicm 		sy++;
11909554a682Snicm 		dy++;
1191953d773cSnicm 	}
1192953d773cSnicm }
1193db4b44a2Snicm 
1194f2ca94d1Snicm /* Mark line as dead. */
1195f2ca94d1Snicm static void
1196f2ca94d1Snicm grid_reflow_dead(struct grid_line *gl)
1197f2ca94d1Snicm {
1198f2ca94d1Snicm 	memset(gl, 0, sizeof *gl);
1199f2ca94d1Snicm 	gl->flags = GRID_LINE_DEAD;
1200f2ca94d1Snicm }
1201f2ca94d1Snicm 
1202f2ca94d1Snicm /* Add lines, return the first new one. */
1203f2ca94d1Snicm static struct grid_line *
1204f2ca94d1Snicm grid_reflow_add(struct grid *gd, u_int n)
1205f2ca94d1Snicm {
1206f2ca94d1Snicm 	struct grid_line	*gl;
1207f2ca94d1Snicm 	u_int			 sy = gd->sy + n;
1208f2ca94d1Snicm 
1209f2ca94d1Snicm 	gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata);
1210f2ca94d1Snicm 	gl = &gd->linedata[gd->sy];
1211f2ca94d1Snicm 	memset(gl, 0, n * (sizeof *gl));
1212f2ca94d1Snicm 	gd->sy = sy;
1213f2ca94d1Snicm 	return (gl);
1214f2ca94d1Snicm }
1215f2ca94d1Snicm 
1216f2ca94d1Snicm /* Move a line across. */
1217f2ca94d1Snicm static struct grid_line *
1218f2ca94d1Snicm grid_reflow_move(struct grid *gd, struct grid_line *from)
1219f2ca94d1Snicm {
1220f2ca94d1Snicm 	struct grid_line	*to;
1221f2ca94d1Snicm 
1222f2ca94d1Snicm 	to = grid_reflow_add(gd, 1);
1223f2ca94d1Snicm 	memcpy(to, from, sizeof *to);
1224f2ca94d1Snicm 	grid_reflow_dead(from);
1225f2ca94d1Snicm 	return (to);
1226f2ca94d1Snicm }
1227f2ca94d1Snicm 
1228414c61adSnicm /* Join line below onto this one. */
12299883b791Snicm static void
1230f2ca94d1Snicm grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
1231db552f66Snicm     u_int width, int already)
1232e931849fSnicm {
1233ffce0102Snicm 	struct grid_line	*gl, *from = NULL;
1234414c61adSnicm 	struct grid_cell	 gc;
1235ffce0102Snicm 	u_int			 lines, left, i, to, line, want = 0;
1236f2ca94d1Snicm 	u_int			 at;
1237414c61adSnicm 	int			 wrapped = 1;
1238e931849fSnicm 
1239f2ca94d1Snicm 	/*
1240f2ca94d1Snicm 	 * Add a new target line.
1241f2ca94d1Snicm 	 */
1242f2ca94d1Snicm 	if (!already) {
1243f2ca94d1Snicm 		to = target->sy;
1244f2ca94d1Snicm 		gl = grid_reflow_move(target, &gd->linedata[yy]);
1245f2ca94d1Snicm 	} else {
1246f2ca94d1Snicm 		to = target->sy - 1;
1247f2ca94d1Snicm 		gl = &target->linedata[to];
1248f2ca94d1Snicm 	}
1249f2ca94d1Snicm 	at = gl->cellused;
1250f2ca94d1Snicm 
1251f2ca94d1Snicm 	/*
1252f2ca94d1Snicm 	 * Loop until no more to consume or the target line is full.
1253f2ca94d1Snicm 	 */
1254414c61adSnicm 	lines = 0;
1255414c61adSnicm 	for (;;) {
1256414c61adSnicm 		/*
1257414c61adSnicm 		 * If this is now the last line, there is nothing more to be
1258414c61adSnicm 		 * done.
1259414c61adSnicm 		 */
1260c72e5d61Snicm 		if (yy + 1 + lines == gd->hsize + gd->sy)
1261414c61adSnicm 			break;
1262414c61adSnicm 		line = yy + 1 + lines;
1263e931849fSnicm 
1264414c61adSnicm 		/* If the next line is empty, skip it. */
1265414c61adSnicm 		if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
1266414c61adSnicm 			wrapped = 0;
1267414c61adSnicm 		if (gd->linedata[line].cellused == 0) {
1268414c61adSnicm 			if (!wrapped)
1269414c61adSnicm 				break;
1270c72e5d61Snicm 			lines++;
1271e931849fSnicm 			continue;
127244c84f9eSnicm 		}
127344c84f9eSnicm 
1274db4b44a2Snicm 		/*
1275414c61adSnicm 		 * Is the destination line now full? Copy the first character
1276414c61adSnicm 		 * separately because we need to leave "from" set to the last
1277414c61adSnicm 		 * line if this line is full.
1278db4b44a2Snicm 		 */
1279414c61adSnicm 		grid_get_cell1(&gd->linedata[line], 0, &gc);
1280414c61adSnicm 		if (width + gc.data.width > sx)
1281414c61adSnicm 			break;
1282414c61adSnicm 		width += gc.data.width;
1283f2ca94d1Snicm 		grid_set_cell(target, at, to, &gc);
1284414c61adSnicm 		at++;
1285414c61adSnicm 
1286414c61adSnicm 		/* Join as much more as possible onto the current line. */
1287414c61adSnicm 		from = &gd->linedata[line];
1288414c61adSnicm 		for (want = 1; want < from->cellused; want++) {
1289414c61adSnicm 			grid_get_cell1(from, want, &gc);
1290414c61adSnicm 			if (width + gc.data.width > sx)
1291414c61adSnicm 				break;
1292414c61adSnicm 			width += gc.data.width;
1293414c61adSnicm 
1294f2ca94d1Snicm 			grid_set_cell(target, at, to, &gc);
1295414c61adSnicm 			at++;
1296414c61adSnicm 		}
1297414c61adSnicm 		lines++;
1298414c61adSnicm 
1299414c61adSnicm 		/*
1300414c61adSnicm 		 * If this line wasn't wrapped or we didn't consume the entire
1301414c61adSnicm 		 * line, don't try to join any further lines.
1302414c61adSnicm 		 */
1303414c61adSnicm 		if (!wrapped || want != from->cellused || width == sx)
1304414c61adSnicm 			break;
1305414c61adSnicm 	}
1306414c61adSnicm 	if (lines == 0)
1307414c61adSnicm 		return;
1308414c61adSnicm 
1309414c61adSnicm 	/*
1310414c61adSnicm 	 * If we didn't consume the entire final line, then remove what we did
1311414c61adSnicm 	 * consume. If we consumed the entire line and it wasn't wrapped,
1312414c61adSnicm 	 * remove the wrap flag from this line.
1313414c61adSnicm 	 */
1314414c61adSnicm 	left = from->cellused - want;
1315414c61adSnicm 	if (left != 0) {
1316414c61adSnicm 		grid_move_cells(gd, 0, want, yy + lines, left, 8);
1317414c61adSnicm 		from->cellsize = from->cellused = left;
1318414c61adSnicm 		lines--;
1319414c61adSnicm 	} else if (!wrapped)
1320414c61adSnicm 		gl->flags &= ~GRID_LINE_WRAPPED;
1321414c61adSnicm 
1322414c61adSnicm 	/* Remove the lines that were completely consumed. */
1323f2ca94d1Snicm 	for (i = yy + 1; i < yy + 1 + lines; i++) {
1324f2ca94d1Snicm 		free(gd->linedata[i].celldata);
1325f2ca94d1Snicm 		free(gd->linedata[i].extddata);
1326f2ca94d1Snicm 		grid_reflow_dead(&gd->linedata[i]);
1327414c61adSnicm 	}
1328414c61adSnicm 
1329db552f66Snicm 	/* Adjust scroll position. */
1330f2ca94d1Snicm 	if (gd->hscrolled > to + lines)
1331414c61adSnicm 		gd->hscrolled -= lines;
1332f2ca94d1Snicm 	else if (gd->hscrolled > to)
1333f2ca94d1Snicm 		gd->hscrolled = to;
1334414c61adSnicm }
1335414c61adSnicm 
1336414c61adSnicm /* Split this line into several new ones */
1337414c61adSnicm static void
1338f2ca94d1Snicm grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
1339db552f66Snicm     u_int at)
1340db4b44a2Snicm {
1341f2ca94d1Snicm 	struct grid_line	*gl = &gd->linedata[yy], *first;
1342414c61adSnicm 	struct grid_cell	 gc;
1343f2ca94d1Snicm 	u_int			 line, lines, width, i, xx;
1344f2ca94d1Snicm 	u_int			 used = gl->cellused;
1345414c61adSnicm 	int			 flags = gl->flags;
1346db4b44a2Snicm 
1347f2ca94d1Snicm 	/* How many lines do we need to insert? We know we need at least two. */
1348414c61adSnicm 	if (~gl->flags & GRID_LINE_EXTENDED)
1349f2ca94d1Snicm 		lines = 1 + (gl->cellused - 1) / sx;
1350414c61adSnicm 	else {
1351f2ca94d1Snicm 		lines = 2;
1352414c61adSnicm 		width = 0;
1353414c61adSnicm 		for (i = at; i < used; i++) {
1354414c61adSnicm 			grid_get_cell1(gl, i, &gc);
1355414c61adSnicm 			if (width + gc.data.width > sx) {
1356414c61adSnicm 				lines++;
1357414c61adSnicm 				width = 0;
1358414c61adSnicm 			}
1359414c61adSnicm 			width += gc.data.width;
1360414c61adSnicm 		}
1361414c61adSnicm 	}
136244c84f9eSnicm 
1363414c61adSnicm 	/* Insert new lines. */
1364f2ca94d1Snicm 	line = target->sy + 1;
1365f2ca94d1Snicm 	first = grid_reflow_add(target, lines);
1366414c61adSnicm 
1367414c61adSnicm 	/* Copy sections from the original line. */
1368414c61adSnicm 	width = 0;
1369414c61adSnicm 	xx = 0;
1370414c61adSnicm 	for (i = at; i < used; i++) {
1371414c61adSnicm 		grid_get_cell1(gl, i, &gc);
1372414c61adSnicm 		if (width + gc.data.width > sx) {
1373f2ca94d1Snicm 			target->linedata[line].flags |= GRID_LINE_WRAPPED;
1374414c61adSnicm 
1375414c61adSnicm 			line++;
1376414c61adSnicm 			width = 0;
1377414c61adSnicm 			xx = 0;
1378414c61adSnicm 		}
1379414c61adSnicm 		width += gc.data.width;
1380f2ca94d1Snicm 		grid_set_cell(target, xx, line, &gc);
1381414c61adSnicm 		xx++;
1382414c61adSnicm 	}
1383414c61adSnicm 	if (flags & GRID_LINE_WRAPPED)
1384f2ca94d1Snicm 		target->linedata[line].flags |= GRID_LINE_WRAPPED;
1385f2ca94d1Snicm 
1386f2ca94d1Snicm 	/* Move the remainder of the original line. */
1387f2ca94d1Snicm 	gl->cellsize = gl->cellused = at;
1388f2ca94d1Snicm 	gl->flags |= GRID_LINE_WRAPPED;
1389f2ca94d1Snicm 	memcpy(first, gl, sizeof *first);
1390f2ca94d1Snicm 	grid_reflow_dead(gl);
1391414c61adSnicm 
1392db552f66Snicm 	/* Adjust the scroll position. */
1393414c61adSnicm 	if (yy <= gd->hscrolled)
1394f2ca94d1Snicm 		gd->hscrolled += lines - 1;
1395414c61adSnicm 
1396414c61adSnicm 	/*
1397414c61adSnicm 	 * If the original line had the wrapped flag and there is still space
1398414c61adSnicm 	 * in the last new line, try to join with the next lines.
1399414c61adSnicm 	 */
1400414c61adSnicm 	if (width < sx && (flags & GRID_LINE_WRAPPED))
1401db552f66Snicm 		grid_reflow_join(target, gd, sx, yy, width, 1);
1402414c61adSnicm }
1403414c61adSnicm 
1404414c61adSnicm /* Reflow lines on grid to new width. */
1405414c61adSnicm void
1406db552f66Snicm grid_reflow(struct grid *gd, u_int sx)
1407414c61adSnicm {
1408f2ca94d1Snicm 	struct grid		*target;
1409414c61adSnicm 	struct grid_line	*gl;
1410414c61adSnicm 	struct grid_cell	 gc;
141114b12344Snicm 	u_int			 yy, width, i, at;
1412414c61adSnicm 
1413414c61adSnicm 	/*
1414f2ca94d1Snicm 	 * Create a destination grid. This is just used as a container for the
1415f2ca94d1Snicm 	 * line data and may not be fully valid.
1416f2ca94d1Snicm 	 */
1417f2ca94d1Snicm 	target = grid_create(gd->sx, 0, 0);
1418f2ca94d1Snicm 
1419f2ca94d1Snicm 	/*
1420f2ca94d1Snicm 	 * Loop over each source line.
1421414c61adSnicm 	 */
1422414c61adSnicm 	for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
1423414c61adSnicm 		gl = &gd->linedata[yy];
1424f2ca94d1Snicm 		if (gl->flags & GRID_LINE_DEAD)
1425f2ca94d1Snicm 			continue;
1426414c61adSnicm 
1427f2ca94d1Snicm 		/*
142814b12344Snicm 		 * Work out the width of this line. at is the point at which
142914b12344Snicm 		 * the available width is hit, and width is the full line
143014b12344Snicm 		 * width.
1431f2ca94d1Snicm 		 */
143214b12344Snicm 		at = width = 0;
1433414c61adSnicm 		if (~gl->flags & GRID_LINE_EXTENDED) {
1434414c61adSnicm 			width = gl->cellused;
1435414c61adSnicm 			if (width > sx)
1436414c61adSnicm 				at = sx;
143744c84f9eSnicm 			else
1438414c61adSnicm 				at = width;
143944c84f9eSnicm 		} else {
1440414c61adSnicm 			for (i = 0; i < gl->cellused; i++) {
1441414c61adSnicm 				grid_get_cell1(gl, i, &gc);
1442414c61adSnicm 				if (at == 0 && width + gc.data.width > sx)
1443414c61adSnicm 					at = i;
1444414c61adSnicm 				width += gc.data.width;
1445db4b44a2Snicm 			}
1446db4b44a2Snicm 		}
1447db4b44a2Snicm 
1448414c61adSnicm 		/*
144914b12344Snicm 		 * If the line is exactly right, just move it across
145014b12344Snicm 		 * unchanged.
1451414c61adSnicm 		 */
145214b12344Snicm 		if (width == sx) {
1453f2ca94d1Snicm 			grid_reflow_move(target, gl);
1454414c61adSnicm 			continue;
1455f2ca94d1Snicm 		}
1456414c61adSnicm 
1457414c61adSnicm 		/*
1458414c61adSnicm 		 * If the line is too big, it needs to be split, whether or not
1459414c61adSnicm 		 * it was previously wrapped.
1460414c61adSnicm 		 */
1461414c61adSnicm 		if (width > sx) {
1462db552f66Snicm 			grid_reflow_split(target, gd, sx, yy, at);
1463414c61adSnicm 			continue;
1464414c61adSnicm 		}
1465414c61adSnicm 
1466414c61adSnicm 		/*
1467414c61adSnicm 		 * If the line was previously wrapped, join as much as possible
1468414c61adSnicm 		 * of the next line.
1469414c61adSnicm 		 */
1470414c61adSnicm 		if (gl->flags & GRID_LINE_WRAPPED)
1471db552f66Snicm 			grid_reflow_join(target, gd, sx, yy, width, 0);
1472f2ca94d1Snicm 		else
1473f2ca94d1Snicm 			grid_reflow_move(target, gl);
1474414c61adSnicm 	}
1475414c61adSnicm 
1476f2ca94d1Snicm 	/*
1477f2ca94d1Snicm 	 * Replace the old grid with the new.
1478f2ca94d1Snicm 	 */
1479f2ca94d1Snicm 	if (target->sy < gd->sy)
1480f2ca94d1Snicm 		grid_reflow_add(target, gd->sy - target->sy);
1481f2ca94d1Snicm 	gd->hsize = target->sy - gd->sy;
148256aa75d3Snicm 	if (gd->hscrolled > gd->hsize)
148356aa75d3Snicm 		gd->hscrolled = gd->hsize;
1484f2ca94d1Snicm 	free(gd->linedata);
1485f2ca94d1Snicm 	gd->linedata = target->linedata;
1486f2ca94d1Snicm 	free(target);
1487db552f66Snicm }
1488f2ca94d1Snicm 
148936f15d15Snicm /* Convert to position based on wrapped lines. */
1490db552f66Snicm void
149136f15d15Snicm grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy)
1492db552f66Snicm {
149336f15d15Snicm 	u_int	ax = 0, ay = 0, yy;
1494db552f66Snicm 
149536f15d15Snicm 	for (yy = 0; yy < py; yy++) {
149636f15d15Snicm 		if (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
149736f15d15Snicm 			ax += gd->linedata[yy].cellused;
149836f15d15Snicm 		else {
149936f15d15Snicm 			ax = 0;
150036f15d15Snicm 			ay++;
150136f15d15Snicm 		}
150236f15d15Snicm 	}
150336f15d15Snicm 	if (px >= gd->linedata[yy].cellused)
150436f15d15Snicm 		ax = UINT_MAX;
150536f15d15Snicm 	else
150636f15d15Snicm 		ax += px;
150736f15d15Snicm 	*wx = ax;
150836f15d15Snicm 	*wy = ay;
150936f15d15Snicm }
151036f15d15Snicm 
151136f15d15Snicm /* Convert position based on wrapped lines back. */
151236f15d15Snicm void
151336f15d15Snicm grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
151436f15d15Snicm {
151545c83cdbSnicm 	u_int	yy, ay = 0;
1516db552f66Snicm 
1517db552f66Snicm 	for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
151836f15d15Snicm 		if (ay == wy)
1519db552f66Snicm 			break;
152045c83cdbSnicm 		if (~gd->linedata[yy].flags & GRID_LINE_WRAPPED)
152136f15d15Snicm 			ay++;
1522db552f66Snicm 	}
152336f15d15Snicm 
152436f15d15Snicm 	/*
152536f15d15Snicm 	 * yy is now 0 on the unwrapped line which contains wx. Walk forwards
152636f15d15Snicm 	 * until we find the end or the line now containing wx.
152736f15d15Snicm 	 */
152836f15d15Snicm 	if (wx == UINT_MAX) {
152936f15d15Snicm 		while (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
153036f15d15Snicm 			yy++;
153136f15d15Snicm 		wx = gd->linedata[yy].cellused;
153236f15d15Snicm 	} else {
153336f15d15Snicm 		while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) {
153436f15d15Snicm 			if (wx < gd->linedata[yy].cellused)
153536f15d15Snicm 				break;
153636f15d15Snicm 			wx -= gd->linedata[yy].cellused;
153736f15d15Snicm 			yy++;
153836f15d15Snicm 		}
153936f15d15Snicm 	}
154036f15d15Snicm 	*px = wx;
1541db552f66Snicm 	*py = yy;
1542db4b44a2Snicm }
15437db4c597Snicm 
15447db4c597Snicm /* Get length of line. */
15457db4c597Snicm u_int
15467db4c597Snicm grid_line_length(struct grid *gd, u_int py)
15477db4c597Snicm {
15487db4c597Snicm 	struct grid_cell	gc;
15497db4c597Snicm 	u_int			px;
15507db4c597Snicm 
15517db4c597Snicm 	px = grid_get_line(gd, py)->cellsize;
15527db4c597Snicm 	if (px > gd->sx)
15537db4c597Snicm 		px = gd->sx;
15547db4c597Snicm 	while (px > 0) {
15557db4c597Snicm 		grid_get_cell(gd, px - 1, py, &gc);
15562b07bb4fSnicm 		if ((gc.flags & GRID_FLAG_PADDING) ||
15572b07bb4fSnicm 		    gc.data.size != 1 ||
15582b07bb4fSnicm 		    *gc.data.data != ' ')
15597db4c597Snicm 			break;
15607db4c597Snicm 		px--;
15617db4c597Snicm 	}
15627db4c597Snicm 	return (px);
15637db4c597Snicm }
1564*61e9d0deSnicm 
1565*61e9d0deSnicm /* Check if character is in set. */
1566*61e9d0deSnicm int
1567*61e9d0deSnicm grid_in_set(struct grid *gd, u_int px, u_int py, const char *set)
1568*61e9d0deSnicm {
1569*61e9d0deSnicm 	struct grid_cell	gc, tmp_gc;
1570*61e9d0deSnicm 	u_int			pxx;
1571*61e9d0deSnicm 
1572*61e9d0deSnicm 	grid_get_cell(gd, px, py, &gc);
1573*61e9d0deSnicm 	if (strchr(set, '\t')) {
1574*61e9d0deSnicm 		if (gc.flags & GRID_FLAG_PADDING) {
1575*61e9d0deSnicm 			pxx = px;
1576*61e9d0deSnicm 			do
1577*61e9d0deSnicm 				grid_get_cell(gd, --pxx, py, &tmp_gc);
1578*61e9d0deSnicm 			while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING);
1579*61e9d0deSnicm 			if (tmp_gc.flags & GRID_FLAG_TAB)
1580*61e9d0deSnicm 				return (tmp_gc.data.width - (px - pxx));
1581*61e9d0deSnicm 		} else if (gc.flags & GRID_FLAG_TAB)
1582*61e9d0deSnicm 			return (gc.data.width);
1583*61e9d0deSnicm 	}
1584*61e9d0deSnicm 	if (gc.flags & GRID_FLAG_PADDING)
1585*61e9d0deSnicm 		return (0);
1586*61e9d0deSnicm 	return (utf8_cstrhas(set, &gc.data));
1587*61e9d0deSnicm }
1588