xref: /netbsd-src/external/bsd/tmux/dist/grid.c (revision 890b6d91a44b7fcb2dfbcbc1e93463086e462d2d)
199e242abSchristos /* $OpenBSD$ */
2698d5317Sjmmv 
3698d5317Sjmmv /*
4f26e8bc9Schristos  * Copyright (c) 2008 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 
21928fc495Schristos #include <stdlib.h>
22698d5317Sjmmv #include <string.h>
23698d5317Sjmmv 
24698d5317Sjmmv #include "tmux.h"
25698d5317Sjmmv 
26698d5317Sjmmv /*
27698d5317Sjmmv  * Grid data. This is the basic data structure that represents what is shown on
28698d5317Sjmmv  * screen.
29698d5317Sjmmv  *
30698d5317Sjmmv  * A grid is a grid of cells (struct grid_cell). Lines are not allocated until
31698d5317Sjmmv  * cells in that line are written to. The grid is split into history and
32698d5317Sjmmv  * viewable data with the history starting at row (line) 0 and extending to
33698d5317Sjmmv  * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All
34698d5317Sjmmv  * functions in this file work on absolute coordinates, grid-view.c has
35698d5317Sjmmv  * functions which work on the screen data.
36698d5317Sjmmv  */
37698d5317Sjmmv 
38698d5317Sjmmv /* Default grid cell data. */
39f26e8bc9Schristos const struct grid_cell grid_default_cell = {
40f844e94eSwiz 	{ { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
41f26e8bc9Schristos };
420a274e86Schristos 
43e271dbb8Schristos /*
44e271dbb8Schristos  * Padding grid cell data. Padding cells are the only zero width cell that
45e271dbb8Schristos  * appears in the grid - because of this, they are always extended cells.
46e271dbb8Schristos  */
47e271dbb8Schristos static const struct grid_cell grid_padding_cell = {
48f844e94eSwiz 	{ { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
49e271dbb8Schristos };
50e271dbb8Schristos 
510a274e86Schristos /* Cleared grid cell data. */
52e271dbb8Schristos static const struct grid_cell grid_cleared_cell = {
53f844e94eSwiz 	{ { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
540a274e86Schristos };
550a274e86Schristos static const struct grid_cell_entry grid_cleared_entry = {
56f844e94eSwiz 	{ .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
57f26e8bc9Schristos };
58698d5317Sjmmv 
59e9a2d6faSchristos /* Store cell in entry. */
60e9a2d6faSchristos static void
61e9a2d6faSchristos grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
62e9a2d6faSchristos     u_char c)
63e9a2d6faSchristos {
640a274e86Schristos 	gce->flags = (gc->flags & ~GRID_FLAG_CLEARED);
65e9a2d6faSchristos 
66e9a2d6faSchristos 	gce->data.fg = gc->fg & 0xff;
67e9a2d6faSchristos 	if (gc->fg & COLOUR_FLAG_256)
68e9a2d6faSchristos 		gce->flags |= GRID_FLAG_FG256;
69e9a2d6faSchristos 
70e9a2d6faSchristos 	gce->data.bg = gc->bg & 0xff;
71e9a2d6faSchristos 	if (gc->bg & COLOUR_FLAG_256)
72e9a2d6faSchristos 		gce->flags |= GRID_FLAG_BG256;
73e9a2d6faSchristos 
74e9a2d6faSchristos 	gce->data.attr = gc->attr;
75e9a2d6faSchristos 	gce->data.data = c;
76e9a2d6faSchristos }
77e9a2d6faSchristos 
780a274e86Schristos /* Check if a cell should be an extended cell. */
79e9a2d6faSchristos static int
80e9a2d6faSchristos grid_need_extended_cell(const struct grid_cell_entry *gce,
81e9a2d6faSchristos     const struct grid_cell *gc)
82e9a2d6faSchristos {
83e9a2d6faSchristos 	if (gce->flags & GRID_FLAG_EXTENDED)
84e9a2d6faSchristos 		return (1);
85e9a2d6faSchristos 	if (gc->attr > 0xff)
86e9a2d6faSchristos 		return (1);
87e9a2d6faSchristos 	if (gc->data.size != 1 || gc->data.width != 1)
88e9a2d6faSchristos 		return (1);
89e9a2d6faSchristos 	if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
90e9a2d6faSchristos 		return (1);
91*890b6d91Swiz 	if (gc->us != 8) /* only supports 256 or RGB */
9230744affSchristos 		return (1);
93f844e94eSwiz 	if (gc->link != 0)
94f844e94eSwiz 		return (1);
95e9a2d6faSchristos 	return (0);
96e9a2d6faSchristos }
97e9a2d6faSchristos 
980a274e86Schristos /* Get an extended cell. */
990a274e86Schristos static void
1000a274e86Schristos grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
1010a274e86Schristos     int flags)
1020a274e86Schristos {
1030a274e86Schristos 	u_int at = gl->extdsize + 1;
1040a274e86Schristos 
1050a274e86Schristos 	gl->extddata = xreallocarray(gl->extddata, at, sizeof *gl->extddata);
1060a274e86Schristos 	gl->extdsize = at;
1070a274e86Schristos 
1080a274e86Schristos 	gce->offset = at - 1;
1090a274e86Schristos 	gce->flags = (flags | GRID_FLAG_EXTENDED);
1100a274e86Schristos }
1110a274e86Schristos 
1120a274e86Schristos /* Set cell as extended. */
113e271dbb8Schristos static struct grid_extd_entry *
1140a274e86Schristos grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
1150a274e86Schristos     const struct grid_cell *gc)
1160a274e86Schristos {
117e271dbb8Schristos 	struct grid_extd_entry	*gee;
1180a274e86Schristos 	int			 flags = (gc->flags & ~GRID_FLAG_CLEARED);
119e271dbb8Schristos 	utf8_char		 uc;
1200a274e86Schristos 
1210a274e86Schristos 	if (~gce->flags & GRID_FLAG_EXTENDED)
1220a274e86Schristos 		grid_get_extended_cell(gl, gce, flags);
1230a274e86Schristos 	else if (gce->offset >= gl->extdsize)
1240a274e86Schristos 		fatalx("offset too big");
1250a274e86Schristos 	gl->flags |= GRID_LINE_EXTENDED;
1260a274e86Schristos 
127e271dbb8Schristos 	utf8_from_data(&gc->data, &uc);
128e271dbb8Schristos 
129e271dbb8Schristos 	gee = &gl->extddata[gce->offset];
130e271dbb8Schristos 	gee->data = uc;
131e271dbb8Schristos 	gee->attr = gc->attr;
132e271dbb8Schristos 	gee->flags = flags;
133e271dbb8Schristos 	gee->fg = gc->fg;
134e271dbb8Schristos 	gee->bg = gc->bg;
135e271dbb8Schristos 	gee->us = gc->us;
136f844e94eSwiz 	gee->link = gc->link;
137e271dbb8Schristos 	return (gee);
1380a274e86Schristos }
1390a274e86Schristos 
140fe99a117Schristos /* Free up unused extended cells. */
141fe99a117Schristos static void
142fe99a117Schristos grid_compact_line(struct grid_line *gl)
143fe99a117Schristos {
144fe99a117Schristos 	int			 new_extdsize = 0;
145e271dbb8Schristos 	struct grid_extd_entry	*new_extddata;
146fe99a117Schristos 	struct grid_cell_entry	*gce;
147e271dbb8Schristos 	struct grid_extd_entry	*gee;
148fe99a117Schristos 	u_int			 px, idx;
149fe99a117Schristos 
150fe99a117Schristos 	if (gl->extdsize == 0)
151fe99a117Schristos 		return;
152fe99a117Schristos 
153fe99a117Schristos 	for (px = 0; px < gl->cellsize; px++) {
154fe99a117Schristos 		gce = &gl->celldata[px];
155fe99a117Schristos 		if (gce->flags & GRID_FLAG_EXTENDED)
156fe99a117Schristos 			new_extdsize++;
157fe99a117Schristos 	}
158fe99a117Schristos 
159fe99a117Schristos 	if (new_extdsize == 0) {
160fe99a117Schristos 		free(gl->extddata);
161fe99a117Schristos 		gl->extddata = NULL;
162fe99a117Schristos 		gl->extdsize = 0;
163fe99a117Schristos 		return;
164fe99a117Schristos 	}
165fe99a117Schristos 	new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata);
166fe99a117Schristos 
167fe99a117Schristos 	idx = 0;
168fe99a117Schristos 	for (px = 0; px < gl->cellsize; px++) {
169fe99a117Schristos 		gce = &gl->celldata[px];
170fe99a117Schristos 		if (gce->flags & GRID_FLAG_EXTENDED) {
171e271dbb8Schristos 			gee = &gl->extddata[gce->offset];
172e271dbb8Schristos 			memcpy(&new_extddata[idx], gee, sizeof *gee);
173fe99a117Schristos 			gce->offset = idx++;
174fe99a117Schristos 		}
175fe99a117Schristos 	}
176fe99a117Schristos 
177fe99a117Schristos 	free(gl->extddata);
178fe99a117Schristos 	gl->extddata = new_extddata;
179fe99a117Schristos 	gl->extdsize = new_extdsize;
180fe99a117Schristos }
181fe99a117Schristos 
182c7e17de0Schristos /* Get line data. */
183c7e17de0Schristos struct grid_line *
184c7e17de0Schristos grid_get_line(struct grid *gd, u_int line)
185c7e17de0Schristos {
186c7e17de0Schristos 	return (&gd->linedata[line]);
187c7e17de0Schristos }
188c7e17de0Schristos 
189c7e17de0Schristos /* Adjust number of lines. */
190c7e17de0Schristos void
191c7e17de0Schristos grid_adjust_lines(struct grid *gd, u_int lines)
192c7e17de0Schristos {
193c7e17de0Schristos 	gd->linedata = xreallocarray(gd->linedata, lines, sizeof *gd->linedata);
194c7e17de0Schristos }
195c7e17de0Schristos 
196f26e8bc9Schristos /* Copy default into a cell. */
197f26e8bc9Schristos static void
198e9a2d6faSchristos grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
199f26e8bc9Schristos {
200e9a2d6faSchristos 	struct grid_line	*gl = &gd->linedata[py];
201e9a2d6faSchristos 	struct grid_cell_entry	*gce = &gl->celldata[px];
202e271dbb8Schristos 	struct grid_extd_entry	*gee;
203e9a2d6faSchristos 
2040a274e86Schristos 	memcpy(gce, &grid_cleared_entry, sizeof *gce);
20568e6ba84Schristos 	if (bg != 8) {
206e9a2d6faSchristos 		if (bg & COLOUR_FLAG_RGB) {
2070a274e86Schristos 			grid_get_extended_cell(gl, gce, gce->flags);
208e271dbb8Schristos 			gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
209e271dbb8Schristos 			gee->bg = bg;
210e9a2d6faSchristos 		} else {
211e9a2d6faSchristos 			if (bg & COLOUR_FLAG_256)
212e9a2d6faSchristos 				gce->flags |= GRID_FLAG_BG256;
213e9a2d6faSchristos 			gce->data.bg = bg;
214e9a2d6faSchristos 		}
215f26e8bc9Schristos 	}
21668e6ba84Schristos }
217f26e8bc9Schristos 
21899e242abSchristos /* Check grid y position. */
219e9a2d6faSchristos static int
220c7e17de0Schristos grid_check_y(struct grid *gd, const char *from, u_int py)
221698d5317Sjmmv {
222c7e17de0Schristos 	if (py >= gd->hsize + gd->sy) {
223c7e17de0Schristos 		log_debug("%s: y out of range: %u", from, py);
224698d5317Sjmmv 		return (-1);
225698d5317Sjmmv 	}
226698d5317Sjmmv 	return (0);
227698d5317Sjmmv }
228928fc495Schristos 
229e271dbb8Schristos /* Check if two styles are (visibly) the same. */
230e271dbb8Schristos int
231e271dbb8Schristos grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
232e271dbb8Schristos {
233e271dbb8Schristos 	if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
234e271dbb8Schristos 		return (0);
235e271dbb8Schristos 	if (gc1->attr != gc2->attr || gc1->flags != gc2->flags)
236e271dbb8Schristos 		return (0);
237f844e94eSwiz 	if (gc1->link != gc2->link)
238f844e94eSwiz 		return (0);
239e271dbb8Schristos 	return (1);
240e271dbb8Schristos }
241e271dbb8Schristos 
242e9a2d6faSchristos /* Compare grid cells. Return 1 if equal, 0 if not. */
243e9a2d6faSchristos int
244e271dbb8Schristos grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
245e9a2d6faSchristos {
246e271dbb8Schristos 	if (!grid_cells_look_equal(gc1, gc2))
247e9a2d6faSchristos 		return (0);
248e271dbb8Schristos 	if (gc1->data.width != gc2->data.width)
249e9a2d6faSchristos 		return (0);
250e271dbb8Schristos 	if (gc1->data.size != gc2->data.size)
251e9a2d6faSchristos 		return (0);
252e271dbb8Schristos 	return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0);
253e9a2d6faSchristos }
254e9a2d6faSchristos 
255fe99a117Schristos /* Free one line. */
256fe99a117Schristos static void
257fe99a117Schristos grid_free_line(struct grid *gd, u_int py)
258fe99a117Schristos {
259fe99a117Schristos 	free(gd->linedata[py].celldata);
260fe99a117Schristos 	gd->linedata[py].celldata = NULL;
261fe99a117Schristos 	free(gd->linedata[py].extddata);
262fe99a117Schristos 	gd->linedata[py].extddata = NULL;
263fe99a117Schristos }
264fe99a117Schristos 
265fe99a117Schristos /* Free several lines. */
266fe99a117Schristos static void
267fe99a117Schristos grid_free_lines(struct grid *gd, u_int py, u_int ny)
268fe99a117Schristos {
269fe99a117Schristos 	u_int	yy;
270fe99a117Schristos 
271fe99a117Schristos 	for (yy = py; yy < py + ny; yy++)
272fe99a117Schristos 		grid_free_line(gd, yy);
273fe99a117Schristos }
274fe99a117Schristos 
275698d5317Sjmmv /* Create a new grid. */
276698d5317Sjmmv struct grid *
277698d5317Sjmmv grid_create(u_int sx, u_int sy, u_int hlimit)
278698d5317Sjmmv {
279698d5317Sjmmv 	struct grid	*gd;
280698d5317Sjmmv 
281698d5317Sjmmv 	gd = xmalloc(sizeof *gd);
282698d5317Sjmmv 	gd->sx = sx;
283698d5317Sjmmv 	gd->sy = sy;
284698d5317Sjmmv 
285e271dbb8Schristos 	if (hlimit != 0)
286698d5317Sjmmv 		gd->flags = GRID_HISTORY;
287e271dbb8Schristos 	else
288e271dbb8Schristos 		gd->flags = 0;
289698d5317Sjmmv 
290e9a2d6faSchristos 	gd->hscrolled = 0;
291698d5317Sjmmv 	gd->hsize = 0;
292698d5317Sjmmv 	gd->hlimit = hlimit;
293698d5317Sjmmv 
294c7e17de0Schristos 	if (gd->sy != 0)
295698d5317Sjmmv 		gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
296c7e17de0Schristos 	else
297c7e17de0Schristos 		gd->linedata = NULL;
298698d5317Sjmmv 
299698d5317Sjmmv 	return (gd);
300698d5317Sjmmv }
301698d5317Sjmmv 
302698d5317Sjmmv /* Destroy grid. */
303698d5317Sjmmv void
304698d5317Sjmmv grid_destroy(struct grid *gd)
305698d5317Sjmmv {
306fe99a117Schristos 	grid_free_lines(gd, 0, gd->hsize + gd->sy);
307698d5317Sjmmv 
308928fc495Schristos 	free(gd->linedata);
309698d5317Sjmmv 
310928fc495Schristos 	free(gd);
311698d5317Sjmmv }
312698d5317Sjmmv 
313698d5317Sjmmv /* Compare grids. */
314698d5317Sjmmv int
315698d5317Sjmmv grid_compare(struct grid *ga, struct grid *gb)
316698d5317Sjmmv {
317698d5317Sjmmv 	struct grid_line	*gla, *glb;
318f26e8bc9Schristos 	struct grid_cell	 gca, gcb;
319698d5317Sjmmv 	u_int			 xx, yy;
320698d5317Sjmmv 
321928fc495Schristos 	if (ga->sx != gb->sx || ga->sy != gb->sy)
322698d5317Sjmmv 		return (1);
323698d5317Sjmmv 
324698d5317Sjmmv 	for (yy = 0; yy < ga->sy; yy++) {
325698d5317Sjmmv 		gla = &ga->linedata[yy];
326698d5317Sjmmv 		glb = &gb->linedata[yy];
327698d5317Sjmmv 		if (gla->cellsize != glb->cellsize)
328698d5317Sjmmv 			return (1);
329f26e8bc9Schristos 		for (xx = 0; xx < gla->cellsize; xx++) {
330f26e8bc9Schristos 			grid_get_cell(ga, xx, yy, &gca);
331f26e8bc9Schristos 			grid_get_cell(gb, xx, yy, &gcb);
332e9a2d6faSchristos 			if (!grid_cells_equal(&gca, &gcb))
333698d5317Sjmmv 				return (1);
334698d5317Sjmmv 		}
335698d5317Sjmmv 	}
336698d5317Sjmmv 
337698d5317Sjmmv 	return (0);
338698d5317Sjmmv }
339698d5317Sjmmv 
340c7e17de0Schristos /* Trim lines from the history. */
341c7e17de0Schristos static void
342c7e17de0Schristos grid_trim_history(struct grid *gd, u_int ny)
343c7e17de0Schristos {
344c7e17de0Schristos 	grid_free_lines(gd, 0, ny);
345c7e17de0Schristos 	memmove(&gd->linedata[0], &gd->linedata[ny],
346c7e17de0Schristos 	    (gd->hsize + gd->sy - ny) * (sizeof *gd->linedata));
347c7e17de0Schristos }
348c7e17de0Schristos 
349698d5317Sjmmv /*
350698d5317Sjmmv  * Collect lines from the history if at the limit. Free the top (oldest) 10%
351698d5317Sjmmv  * and shift up.
352698d5317Sjmmv  */
353698d5317Sjmmv void
354fe99a117Schristos grid_collect_history(struct grid *gd)
355698d5317Sjmmv {
356fe99a117Schristos 	u_int	ny;
357698d5317Sjmmv 
358fe99a117Schristos 	if (gd->hsize == 0 || gd->hsize < gd->hlimit)
359698d5317Sjmmv 		return;
360698d5317Sjmmv 
361fe99a117Schristos 	ny = gd->hlimit / 10;
362fe99a117Schristos 	if (ny < 1)
363fe99a117Schristos 		ny = 1;
364fe99a117Schristos 	if (ny > gd->hsize)
365fe99a117Schristos 		ny = gd->hsize;
366698d5317Sjmmv 
367fe99a117Schristos 	/*
368fe99a117Schristos 	 * Free the lines from 0 to ny then move the remaining lines over
369fe99a117Schristos 	 * them.
370fe99a117Schristos 	 */
371c7e17de0Schristos 	grid_trim_history(gd, ny);
372fe99a117Schristos 
373fe99a117Schristos 	gd->hsize -= ny;
374e9a2d6faSchristos 	if (gd->hscrolled > gd->hsize)
375e9a2d6faSchristos 		gd->hscrolled = gd->hsize;
376698d5317Sjmmv }
377698d5317Sjmmv 
378e271dbb8Schristos /* Remove lines from the bottom of the history. */
379e271dbb8Schristos void
380e271dbb8Schristos grid_remove_history(struct grid *gd, u_int ny)
381e271dbb8Schristos {
382e271dbb8Schristos 	u_int	yy;
383e271dbb8Schristos 
384e271dbb8Schristos 	if (ny > gd->hsize)
385e271dbb8Schristos 		return;
386e271dbb8Schristos 	for (yy = 0; yy < ny; yy++)
387e271dbb8Schristos 		grid_free_line(gd, gd->hsize + gd->sy - 1 - yy);
388e271dbb8Schristos 	gd->hsize -= ny;
389e271dbb8Schristos }
390e271dbb8Schristos 
391698d5317Sjmmv /*
392698d5317Sjmmv  * Scroll the entire visible screen, moving one line into the history. Just
393698d5317Sjmmv  * allocate a new line at the bottom and move the history size indicator.
394698d5317Sjmmv  */
395698d5317Sjmmv void
396e9a2d6faSchristos grid_scroll_history(struct grid *gd, u_int bg)
397698d5317Sjmmv {
398698d5317Sjmmv 	u_int	yy;
399698d5317Sjmmv 
400698d5317Sjmmv 	yy = gd->hsize + gd->sy;
40199e242abSchristos 	gd->linedata = xreallocarray(gd->linedata, yy + 1,
40299e242abSchristos 	    sizeof *gd->linedata);
403e9a2d6faSchristos 	grid_empty_line(gd, yy, bg);
404698d5317Sjmmv 
405e9a2d6faSchristos 	gd->hscrolled++;
406fe99a117Schristos 	grid_compact_line(&gd->linedata[gd->hsize]);
407f844e94eSwiz 	gd->linedata[gd->hsize].time = current_time;
408698d5317Sjmmv 	gd->hsize++;
409698d5317Sjmmv }
410698d5317Sjmmv 
41199e242abSchristos /* Clear the history. */
41299e242abSchristos void
41399e242abSchristos grid_clear_history(struct grid *gd)
41499e242abSchristos {
415c7e17de0Schristos 	grid_trim_history(gd, gd->hsize);
41699e242abSchristos 
417e9a2d6faSchristos 	gd->hscrolled = 0;
41899e242abSchristos 	gd->hsize = 0;
419e9a2d6faSchristos 
42099e242abSchristos 	gd->linedata = xreallocarray(gd->linedata, gd->sy,
42199e242abSchristos 	    sizeof *gd->linedata);
42299e242abSchristos }
42399e242abSchristos 
424698d5317Sjmmv /* Scroll a region up, moving the top line into the history. */
425698d5317Sjmmv void
426fe99a117Schristos grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
427698d5317Sjmmv {
428fe99a117Schristos 	struct grid_line	*gl_history, *gl_upper;
429698d5317Sjmmv 	u_int			 yy;
430698d5317Sjmmv 
431698d5317Sjmmv 	/* Create a space for a new line. */
432698d5317Sjmmv 	yy = gd->hsize + gd->sy;
43399e242abSchristos 	gd->linedata = xreallocarray(gd->linedata, yy + 1,
43499e242abSchristos 	    sizeof *gd->linedata);
435698d5317Sjmmv 
436698d5317Sjmmv 	/* Move the entire screen down to free a space for this line. */
437698d5317Sjmmv 	gl_history = &gd->linedata[gd->hsize];
438698d5317Sjmmv 	memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);
439698d5317Sjmmv 
440698d5317Sjmmv 	/* Adjust the region and find its start and end. */
441698d5317Sjmmv 	upper++;
442698d5317Sjmmv 	gl_upper = &gd->linedata[upper];
443698d5317Sjmmv 	lower++;
444698d5317Sjmmv 
445698d5317Sjmmv 	/* Move the line into the history. */
446698d5317Sjmmv 	memcpy(gl_history, gl_upper, sizeof *gl_history);
447f844e94eSwiz 	gl_history->time = current_time;
448698d5317Sjmmv 
449698d5317Sjmmv 	/* Then move the region up and clear the bottom line. */
450698d5317Sjmmv 	memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
451fe99a117Schristos 	grid_empty_line(gd, lower, bg);
452698d5317Sjmmv 
453698d5317Sjmmv 	/* Move the history offset down over the line. */
454e9a2d6faSchristos 	gd->hscrolled++;
455698d5317Sjmmv 	gd->hsize++;
456698d5317Sjmmv }
457698d5317Sjmmv 
458698d5317Sjmmv /* Expand line to fit to cell. */
459e9a2d6faSchristos static void
460e9a2d6faSchristos grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
461698d5317Sjmmv {
462698d5317Sjmmv 	struct grid_line	*gl;
463698d5317Sjmmv 	u_int			 xx;
464698d5317Sjmmv 
465698d5317Sjmmv 	gl = &gd->linedata[py];
466698d5317Sjmmv 	if (sx <= gl->cellsize)
467698d5317Sjmmv 		return;
468698d5317Sjmmv 
469e9a2d6faSchristos 	if (sx < gd->sx / 4)
470e9a2d6faSchristos 		sx = gd->sx / 4;
471e9a2d6faSchristos 	else if (sx < gd->sx / 2)
472e9a2d6faSchristos 		sx = gd->sx / 2;
473e271dbb8Schristos 	else if (gd->sx > sx)
474e9a2d6faSchristos 		sx = gd->sx;
475e9a2d6faSchristos 
47699e242abSchristos 	gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
477698d5317Sjmmv 	for (xx = gl->cellsize; xx < sx; xx++)
478e9a2d6faSchristos 		grid_clear_cell(gd, xx, py, bg);
479698d5317Sjmmv 	gl->cellsize = sx;
480698d5317Sjmmv }
481698d5317Sjmmv 
482e9a2d6faSchristos /* Empty a line and set background colour if needed. */
483e271dbb8Schristos void
484e9a2d6faSchristos grid_empty_line(struct grid *gd, u_int py, u_int bg)
485e9a2d6faSchristos {
486e9a2d6faSchristos 	memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
4870a274e86Schristos 	if (!COLOUR_DEFAULT(bg))
488e9a2d6faSchristos 		grid_expand_line(gd, py, gd->sx, bg);
489e9a2d6faSchristos }
490e9a2d6faSchristos 
491928fc495Schristos /* Peek at grid line. */
492928fc495Schristos const struct grid_line *
493928fc495Schristos grid_peek_line(struct grid *gd, u_int py)
494698d5317Sjmmv {
495c7e17de0Schristos 	if (grid_check_y(gd, __func__, py) != 0)
496928fc495Schristos 		return (NULL);
497928fc495Schristos 	return (&gd->linedata[py]);
498698d5317Sjmmv }
499698d5317Sjmmv 
500c7e17de0Schristos /* Get cell from line. */
501c7e17de0Schristos static void
502c7e17de0Schristos grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
503698d5317Sjmmv {
504c7e17de0Schristos 	struct grid_cell_entry	*gce = &gl->celldata[px];
505e271dbb8Schristos 	struct grid_extd_entry	*gee;
506698d5317Sjmmv 
507f26e8bc9Schristos 	if (gce->flags & GRID_FLAG_EXTENDED) {
508f26e8bc9Schristos 		if (gce->offset >= gl->extdsize)
509f26e8bc9Schristos 			memcpy(gc, &grid_default_cell, sizeof *gc);
510e271dbb8Schristos 		else {
511e271dbb8Schristos 			gee = &gl->extddata[gce->offset];
512e271dbb8Schristos 			gc->flags = gee->flags;
513e271dbb8Schristos 			gc->attr = gee->attr;
514e271dbb8Schristos 			gc->fg = gee->fg;
515e271dbb8Schristos 			gc->bg = gee->bg;
516e271dbb8Schristos 			gc->us = gee->us;
517f844e94eSwiz 			gc->link = gee->link;
518e271dbb8Schristos 			utf8_to_data(gee->data, &gc->data);
519e271dbb8Schristos 		}
520f26e8bc9Schristos 		return;
521f26e8bc9Schristos 	}
522f26e8bc9Schristos 
523e9a2d6faSchristos 	gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
524f26e8bc9Schristos 	gc->attr = gce->data.attr;
525f26e8bc9Schristos 	gc->fg = gce->data.fg;
526e9a2d6faSchristos 	if (gce->flags & GRID_FLAG_FG256)
527e9a2d6faSchristos 		gc->fg |= COLOUR_FLAG_256;
528f26e8bc9Schristos 	gc->bg = gce->data.bg;
529e9a2d6faSchristos 	if (gce->flags & GRID_FLAG_BG256)
530e9a2d6faSchristos 		gc->bg |= COLOUR_FLAG_256;
531f844e94eSwiz 	gc->us = 8;
532f26e8bc9Schristos 	utf8_set(&gc->data, gce->data.data);
533f844e94eSwiz 	gc->link = 0;
534698d5317Sjmmv }
535698d5317Sjmmv 
536c7e17de0Schristos /* Get cell for reading. */
537c7e17de0Schristos void
538c7e17de0Schristos grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
539c7e17de0Schristos {
540c7e17de0Schristos 	if (grid_check_y(gd, __func__, py) != 0 ||
5410a274e86Schristos 	    px >= gd->linedata[py].cellsize)
542c7e17de0Schristos 		memcpy(gc, &grid_default_cell, sizeof *gc);
5430a274e86Schristos 	else
5440a274e86Schristos 		grid_get_cell1(&gd->linedata[py], px, gc);
545c7e17de0Schristos }
546c7e17de0Schristos 
547e271dbb8Schristos /* Set cell at position. */
548698d5317Sjmmv void
549928fc495Schristos grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
550698d5317Sjmmv {
551f26e8bc9Schristos 	struct grid_line	*gl;
552f26e8bc9Schristos 	struct grid_cell_entry	*gce;
553f26e8bc9Schristos 
554c7e17de0Schristos 	if (grid_check_y(gd, __func__, py) != 0)
555698d5317Sjmmv 		return;
556698d5317Sjmmv 
557e9a2d6faSchristos 	grid_expand_line(gd, py, px + 1, 8);
558f26e8bc9Schristos 
559f26e8bc9Schristos 	gl = &gd->linedata[py];
560e9a2d6faSchristos 	if (px + 1 > gl->cellused)
561e9a2d6faSchristos 		gl->cellused = px + 1;
562e9a2d6faSchristos 
563f26e8bc9Schristos 	gce = &gl->celldata[px];
564e9a2d6faSchristos 	if (grid_need_extended_cell(gce, gc))
565e9a2d6faSchristos 		grid_extended_cell(gl, gce, gc);
566e9a2d6faSchristos 	else
567e9a2d6faSchristos 		grid_store_cell(gce, gc, gc->data.data[0]);
568f26e8bc9Schristos }
569f26e8bc9Schristos 
570e271dbb8Schristos /* Set padding at position. */
571e271dbb8Schristos void
572e271dbb8Schristos grid_set_padding(struct grid *gd, u_int px, u_int py)
573e271dbb8Schristos {
574e271dbb8Schristos 	grid_set_cell(gd, px, py, &grid_padding_cell);
575e271dbb8Schristos }
576e271dbb8Schristos 
577e271dbb8Schristos /* Set cells at position. */
578e9a2d6faSchristos void
579e9a2d6faSchristos grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
580e9a2d6faSchristos     const char *s, size_t slen)
581e9a2d6faSchristos {
582e9a2d6faSchristos 	struct grid_line	*gl;
583e9a2d6faSchristos 	struct grid_cell_entry	*gce;
584e271dbb8Schristos 	struct grid_extd_entry	*gee;
585e9a2d6faSchristos 	u_int			 i;
586e9a2d6faSchristos 
587c7e17de0Schristos 	if (grid_check_y(gd, __func__, py) != 0)
588f26e8bc9Schristos 		return;
589f26e8bc9Schristos 
590e9a2d6faSchristos 	grid_expand_line(gd, py, px + slen, 8);
591e9a2d6faSchristos 
592e9a2d6faSchristos 	gl = &gd->linedata[py];
593e9a2d6faSchristos 	if (px + slen > gl->cellused)
594e9a2d6faSchristos 		gl->cellused = px + slen;
595e9a2d6faSchristos 
596e9a2d6faSchristos 	for (i = 0; i < slen; i++) {
597e9a2d6faSchristos 		gce = &gl->celldata[px + i];
598e9a2d6faSchristos 		if (grid_need_extended_cell(gce, gc)) {
599e271dbb8Schristos 			gee = grid_extended_cell(gl, gce, gc);
600e271dbb8Schristos 			gee->data = utf8_build_one(s[i]);
601e9a2d6faSchristos 		} else
602e9a2d6faSchristos 			grid_store_cell(gce, gc, s[i]);
603e9a2d6faSchristos 	}
604698d5317Sjmmv }
605698d5317Sjmmv 
606698d5317Sjmmv /* Clear area. */
607698d5317Sjmmv void
608e9a2d6faSchristos grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
609698d5317Sjmmv {
6100a274e86Schristos 	struct grid_line	*gl;
61130744affSchristos 	u_int			 xx, yy, ox, sx;
612698d5317Sjmmv 
613698d5317Sjmmv 	if (nx == 0 || ny == 0)
614698d5317Sjmmv 		return;
615698d5317Sjmmv 
616698d5317Sjmmv 	if (px == 0 && nx == gd->sx) {
617e9a2d6faSchristos 		grid_clear_lines(gd, py, ny, bg);
618698d5317Sjmmv 		return;
619698d5317Sjmmv 	}
620698d5317Sjmmv 
621c7e17de0Schristos 	if (grid_check_y(gd, __func__, py) != 0)
622698d5317Sjmmv 		return;
623c7e17de0Schristos 	if (grid_check_y(gd, __func__, py + ny - 1) != 0)
624698d5317Sjmmv 		return;
625698d5317Sjmmv 
626698d5317Sjmmv 	for (yy = py; yy < py + ny; yy++) {
6270a274e86Schristos 		gl = &gd->linedata[yy];
62830744affSchristos 
62930744affSchristos 		sx = gd->sx;
63030744affSchristos 		if (sx > gl->cellsize)
63130744affSchristos 			sx = gl->cellsize;
63230744affSchristos 		ox = nx;
63330744affSchristos 		if (COLOUR_DEFAULT(bg)) {
63430744affSchristos 			if (px > sx)
635698d5317Sjmmv 				continue;
63630744affSchristos 			if (px + nx > sx)
63730744affSchristos 				ox = sx - px;
638698d5317Sjmmv 		}
63930744affSchristos 
64030744affSchristos 		grid_expand_line(gd, yy, px + ox, 8); /* default bg first */
64130744affSchristos 		for (xx = px; xx < px + ox; xx++)
642e9a2d6faSchristos 			grid_clear_cell(gd, xx, yy, bg);
643698d5317Sjmmv 	}
644698d5317Sjmmv }
645698d5317Sjmmv 
646698d5317Sjmmv /* Clear lines. This just frees and truncates the lines. */
647698d5317Sjmmv void
648e9a2d6faSchristos grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
649698d5317Sjmmv {
650698d5317Sjmmv 	u_int	yy;
651698d5317Sjmmv 
652698d5317Sjmmv 	if (ny == 0)
653698d5317Sjmmv 		return;
654698d5317Sjmmv 
655c7e17de0Schristos 	if (grid_check_y(gd, __func__, py) != 0)
656698d5317Sjmmv 		return;
657c7e17de0Schristos 	if (grid_check_y(gd, __func__, py + ny - 1) != 0)
658698d5317Sjmmv 		return;
659698d5317Sjmmv 
660698d5317Sjmmv 	for (yy = py; yy < py + ny; yy++) {
661fe99a117Schristos 		grid_free_line(gd, yy);
662e9a2d6faSchristos 		grid_empty_line(gd, yy, bg);
663698d5317Sjmmv 	}
664e271dbb8Schristos 	if (py != 0)
665e271dbb8Schristos 		gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
666698d5317Sjmmv }
667698d5317Sjmmv 
668698d5317Sjmmv /* Move a group of lines. */
669698d5317Sjmmv void
670e9a2d6faSchristos grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
671698d5317Sjmmv {
672698d5317Sjmmv 	u_int	yy;
673698d5317Sjmmv 
674698d5317Sjmmv 	if (ny == 0 || py == dy)
675698d5317Sjmmv 		return;
676698d5317Sjmmv 
677c7e17de0Schristos 	if (grid_check_y(gd, __func__, py) != 0)
678698d5317Sjmmv 		return;
679c7e17de0Schristos 	if (grid_check_y(gd, __func__, py + ny - 1) != 0)
680698d5317Sjmmv 		return;
681c7e17de0Schristos 	if (grid_check_y(gd, __func__, dy) != 0)
682698d5317Sjmmv 		return;
683c7e17de0Schristos 	if (grid_check_y(gd, __func__, dy + ny - 1) != 0)
684698d5317Sjmmv 		return;
685698d5317Sjmmv 
686698d5317Sjmmv 	/* Free any lines which are being replaced. */
687698d5317Sjmmv 	for (yy = dy; yy < dy + ny; yy++) {
688698d5317Sjmmv 		if (yy >= py && yy < py + ny)
689698d5317Sjmmv 			continue;
690fe99a117Schristos 		grid_free_line(gd, yy);
691698d5317Sjmmv 	}
692e271dbb8Schristos 	if (dy != 0)
693e271dbb8Schristos 		gd->linedata[dy - 1].flags &= ~GRID_LINE_WRAPPED;
694698d5317Sjmmv 
69599e242abSchristos 	memmove(&gd->linedata[dy], &gd->linedata[py],
69699e242abSchristos 	    ny * (sizeof *gd->linedata));
697698d5317Sjmmv 
698fe99a117Schristos 	/*
699fe99a117Schristos 	 * Wipe any lines that have been moved (without freeing them - they are
700fe99a117Schristos 	 * still present).
701fe99a117Schristos 	 */
702698d5317Sjmmv 	for (yy = py; yy < py + ny; yy++) {
703e9a2d6faSchristos 		if (yy < dy || yy >= dy + ny)
704e9a2d6faSchristos 			grid_empty_line(gd, yy, bg);
705698d5317Sjmmv 	}
706e271dbb8Schristos 	if (py != 0 && (py < dy || py >= dy + ny))
707e271dbb8Schristos 		gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
708698d5317Sjmmv }
709698d5317Sjmmv 
710698d5317Sjmmv /* Move a group of cells. */
711698d5317Sjmmv void
712e9a2d6faSchristos grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
713e9a2d6faSchristos     u_int bg)
714698d5317Sjmmv {
715698d5317Sjmmv 	struct grid_line	*gl;
716698d5317Sjmmv 	u_int			 xx;
717698d5317Sjmmv 
718698d5317Sjmmv 	if (nx == 0 || px == dx)
719698d5317Sjmmv 		return;
720698d5317Sjmmv 
721c7e17de0Schristos 	if (grid_check_y(gd, __func__, py) != 0)
722698d5317Sjmmv 		return;
723698d5317Sjmmv 	gl = &gd->linedata[py];
724698d5317Sjmmv 
725e9a2d6faSchristos 	grid_expand_line(gd, py, px + nx, 8);
726e9a2d6faSchristos 	grid_expand_line(gd, py, dx + nx, 8);
72799e242abSchristos 	memmove(&gl->celldata[dx], &gl->celldata[px],
72899e242abSchristos 	    nx * sizeof *gl->celldata);
729e9a2d6faSchristos 	if (dx + nx > gl->cellused)
730e9a2d6faSchristos 		gl->cellused = dx + nx;
731698d5317Sjmmv 
732698d5317Sjmmv 	/* Wipe any cells that have been moved. */
733698d5317Sjmmv 	for (xx = px; xx < px + nx; xx++) {
734698d5317Sjmmv 		if (xx >= dx && xx < dx + nx)
735698d5317Sjmmv 			continue;
736e9a2d6faSchristos 		grid_clear_cell(gd, xx, py, bg);
737698d5317Sjmmv 	}
738698d5317Sjmmv }
739698d5317Sjmmv 
740928fc495Schristos /* Get ANSI foreground sequence. */
741e9a2d6faSchristos static size_t
742928fc495Schristos grid_string_cells_fg(const struct grid_cell *gc, int *values)
743928fc495Schristos {
744928fc495Schristos 	size_t	n;
745e9a2d6faSchristos 	u_char	r, g, b;
746928fc495Schristos 
747928fc495Schristos 	n = 0;
748e9a2d6faSchristos 	if (gc->fg & COLOUR_FLAG_256) {
749928fc495Schristos 		values[n++] = 38;
750928fc495Schristos 		values[n++] = 5;
751e9a2d6faSchristos 		values[n++] = gc->fg & 0xff;
752e9a2d6faSchristos 	} else if (gc->fg & COLOUR_FLAG_RGB) {
753f26e8bc9Schristos 		values[n++] = 38;
754f26e8bc9Schristos 		values[n++] = 2;
755e9a2d6faSchristos 		colour_split_rgb(gc->fg, &r, &g, &b);
756e9a2d6faSchristos 		values[n++] = r;
757e9a2d6faSchristos 		values[n++] = g;
758e9a2d6faSchristos 		values[n++] = b;
759928fc495Schristos 	} else {
760928fc495Schristos 		switch (gc->fg) {
761928fc495Schristos 		case 0:
762928fc495Schristos 		case 1:
763928fc495Schristos 		case 2:
764928fc495Schristos 		case 3:
765928fc495Schristos 		case 4:
766928fc495Schristos 		case 5:
767928fc495Schristos 		case 6:
768928fc495Schristos 		case 7:
769928fc495Schristos 			values[n++] = gc->fg + 30;
770928fc495Schristos 			break;
771928fc495Schristos 		case 8:
772928fc495Schristos 			values[n++] = 39;
773928fc495Schristos 			break;
774928fc495Schristos 		case 90:
775928fc495Schristos 		case 91:
776928fc495Schristos 		case 92:
777928fc495Schristos 		case 93:
778928fc495Schristos 		case 94:
779928fc495Schristos 		case 95:
780928fc495Schristos 		case 96:
781928fc495Schristos 		case 97:
782928fc495Schristos 			values[n++] = gc->fg;
783928fc495Schristos 			break;
784928fc495Schristos 		}
785928fc495Schristos 	}
786928fc495Schristos 	return (n);
787928fc495Schristos }
788928fc495Schristos 
789928fc495Schristos /* Get ANSI background sequence. */
790e9a2d6faSchristos static size_t
791928fc495Schristos grid_string_cells_bg(const struct grid_cell *gc, int *values)
792928fc495Schristos {
793928fc495Schristos 	size_t	n;
794e9a2d6faSchristos 	u_char	r, g, b;
795928fc495Schristos 
796928fc495Schristos 	n = 0;
797e9a2d6faSchristos 	if (gc->bg & COLOUR_FLAG_256) {
798928fc495Schristos 		values[n++] = 48;
799928fc495Schristos 		values[n++] = 5;
800e9a2d6faSchristos 		values[n++] = gc->bg & 0xff;
801e9a2d6faSchristos 	} else if (gc->bg & COLOUR_FLAG_RGB) {
802f26e8bc9Schristos 		values[n++] = 48;
803f26e8bc9Schristos 		values[n++] = 2;
804e9a2d6faSchristos 		colour_split_rgb(gc->bg, &r, &g, &b);
805e9a2d6faSchristos 		values[n++] = r;
806e9a2d6faSchristos 		values[n++] = g;
807e9a2d6faSchristos 		values[n++] = b;
808928fc495Schristos 	} else {
809928fc495Schristos 		switch (gc->bg) {
810928fc495Schristos 		case 0:
811928fc495Schristos 		case 1:
812928fc495Schristos 		case 2:
813928fc495Schristos 		case 3:
814928fc495Schristos 		case 4:
815928fc495Schristos 		case 5:
816928fc495Schristos 		case 6:
817928fc495Schristos 		case 7:
818928fc495Schristos 			values[n++] = gc->bg + 40;
819928fc495Schristos 			break;
820928fc495Schristos 		case 8:
821928fc495Schristos 			values[n++] = 49;
822928fc495Schristos 			break;
823e271dbb8Schristos 		case 90:
824e271dbb8Schristos 		case 91:
825e271dbb8Schristos 		case 92:
826e271dbb8Schristos 		case 93:
827e271dbb8Schristos 		case 94:
828e271dbb8Schristos 		case 95:
829e271dbb8Schristos 		case 96:
830e271dbb8Schristos 		case 97:
831e271dbb8Schristos 			values[n++] = gc->bg + 10;
832928fc495Schristos 			break;
833928fc495Schristos 		}
834928fc495Schristos 	}
835928fc495Schristos 	return (n);
836928fc495Schristos }
837928fc495Schristos 
83846548964Swiz /* Get underscore colour sequence. */
83946548964Swiz static size_t
84046548964Swiz grid_string_cells_us(const struct grid_cell *gc, int *values)
84146548964Swiz {
84246548964Swiz 	size_t	n;
84346548964Swiz 	u_char	r, g, b;
84446548964Swiz 
84546548964Swiz 	n = 0;
84646548964Swiz 	if (gc->us & COLOUR_FLAG_256) {
84746548964Swiz 		values[n++] = 58;
84846548964Swiz 		values[n++] = 5;
84946548964Swiz 		values[n++] = gc->us & 0xff;
85046548964Swiz 	} else if (gc->us & COLOUR_FLAG_RGB) {
85146548964Swiz 		values[n++] = 58;
85246548964Swiz 		values[n++] = 2;
85346548964Swiz 		colour_split_rgb(gc->us, &r, &g, &b);
85446548964Swiz 		values[n++] = r;
85546548964Swiz 		values[n++] = g;
85646548964Swiz 		values[n++] = b;
85746548964Swiz 	}
85846548964Swiz 	return (n);
85946548964Swiz }
86046548964Swiz 
86146548964Swiz /* Add on SGR code. */
86246548964Swiz static void
86346548964Swiz grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
864f844e94eSwiz     int *oldc, size_t nnewc, size_t noldc, int flags)
86546548964Swiz {
86646548964Swiz 	u_int	i;
86746548964Swiz 	char	tmp[64];
868f844e94eSwiz 	int	reset = (n != 0 && s[0] == 0);
86946548964Swiz 
870f844e94eSwiz 	if (nnewc == 0)
871f844e94eSwiz 		return; /* no code to add */
872f844e94eSwiz 	if (!reset &&
873f844e94eSwiz 	    nnewc == noldc &&
874f844e94eSwiz 	    memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0)
875f844e94eSwiz 		return; /* no reset and colour unchanged */
876f844e94eSwiz 	if (reset && (newc[0] == 49 || newc[0] == 39))
877f844e94eSwiz 		return; /* reset and colour default */
878f844e94eSwiz 
879f844e94eSwiz 	if (flags & GRID_STRING_ESCAPE_SEQUENCES)
88046548964Swiz 		strlcat(buf, "\\033[", len);
88146548964Swiz 	else
88246548964Swiz 		strlcat(buf, "\033[", len);
88346548964Swiz 	for (i = 0; i < nnewc; i++) {
88446548964Swiz 		if (i + 1 < nnewc)
88546548964Swiz 			xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
88646548964Swiz 		else
88746548964Swiz 			xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
88846548964Swiz 		strlcat(buf, tmp, len);
88946548964Swiz 	}
89046548964Swiz 	strlcat(buf, "m", len);
89146548964Swiz }
892f844e94eSwiz 
893f844e94eSwiz static int
894f844e94eSwiz grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
895f844e94eSwiz     const char *uri, int flags)
896f844e94eSwiz {
897f844e94eSwiz 	char	*tmp;
898f844e94eSwiz 
899f844e94eSwiz 	if (strlen(uri) + strlen(id) + 17 >= len)
900f844e94eSwiz 		return (0);
901f844e94eSwiz 
902f844e94eSwiz 	if (flags & GRID_STRING_ESCAPE_SEQUENCES)
903f844e94eSwiz 		strlcat(buf, "\\033]8;", len);
904f844e94eSwiz 	else
905f844e94eSwiz 		strlcat(buf, "\033]8;", len);
906f844e94eSwiz 	if (*id != '\0') {
907f844e94eSwiz 		xasprintf(&tmp, "id=%s;", id);
908f844e94eSwiz 		strlcat(buf, tmp, len);
909f844e94eSwiz 		free(tmp);
910f844e94eSwiz 	} else
911f844e94eSwiz 		strlcat(buf, ";", len);
912f844e94eSwiz 	strlcat(buf, uri, len);
913f844e94eSwiz 	if (flags & GRID_STRING_ESCAPE_SEQUENCES)
914f844e94eSwiz 		strlcat(buf, "\\033\\\\", len);
915f844e94eSwiz 	else
916f844e94eSwiz 		strlcat(buf, "\033\\", len);
917f844e94eSwiz 	return (1);
91846548964Swiz }
91946548964Swiz 
920928fc495Schristos /*
921928fc495Schristos  * Returns ANSI code to set particular attributes (colour, bold and so on)
922fe99a117Schristos  * given a current state.
923928fc495Schristos  */
924e9a2d6faSchristos static void
925928fc495Schristos grid_string_cells_code(const struct grid_cell *lastgc,
926f844e94eSwiz     const struct grid_cell *gc, char *buf, size_t len, int flags,
927f844e94eSwiz     struct screen *sc, int *has_link)
928928fc495Schristos {
929f26e8bc9Schristos 	int			 oldc[64], newc[64], s[128];
930928fc495Schristos 	size_t			 noldc, nnewc, n, i;
931fe99a117Schristos 	u_int			 attr = gc->attr, lastattr = lastgc->attr;
932928fc495Schristos 	char			 tmp[64];
933f844e94eSwiz 	const char		*uri, *id;
934928fc495Schristos 
935f844e94eSwiz 	static const struct {
936928fc495Schristos 		u_int	mask;
937928fc495Schristos 		u_int	code;
938928fc495Schristos 	} attrs[] = {
939928fc495Schristos 		{ GRID_ATTR_BRIGHT, 1 },
940928fc495Schristos 		{ GRID_ATTR_DIM, 2 },
941928fc495Schristos 		{ GRID_ATTR_ITALICS, 3 },
942928fc495Schristos 		{ GRID_ATTR_UNDERSCORE, 4 },
943928fc495Schristos 		{ GRID_ATTR_BLINK, 5 },
944928fc495Schristos 		{ GRID_ATTR_REVERSE, 7 },
945e9a2d6faSchristos 		{ GRID_ATTR_HIDDEN, 8 },
9460a274e86Schristos 		{ GRID_ATTR_STRIKETHROUGH, 9 },
9470a274e86Schristos 		{ GRID_ATTR_UNDERSCORE_2, 42 },
9480a274e86Schristos 		{ GRID_ATTR_UNDERSCORE_3, 43 },
9490a274e86Schristos 		{ GRID_ATTR_UNDERSCORE_4, 44 },
9500a274e86Schristos 		{ GRID_ATTR_UNDERSCORE_5, 45 },
95130744affSchristos 		{ GRID_ATTR_OVERLINE, 53 },
952928fc495Schristos 	};
953928fc495Schristos 	n = 0;
954928fc495Schristos 
955928fc495Schristos 	/* If any attribute is removed, begin with 0. */
956928fc495Schristos 	for (i = 0; i < nitems(attrs); i++) {
95746548964Swiz 		if (((~attr & attrs[i].mask) &&
95846548964Swiz 		    (lastattr & attrs[i].mask)) ||
959f844e94eSwiz 		    (lastgc->us != 8 && gc->us == 8)) {
960928fc495Schristos 			s[n++] = 0;
961928fc495Schristos 			lastattr &= GRID_ATTR_CHARSET;
962928fc495Schristos 			break;
963928fc495Schristos 		}
964928fc495Schristos 	}
965928fc495Schristos 	/* For each attribute that is newly set, add its code. */
966928fc495Schristos 	for (i = 0; i < nitems(attrs); i++) {
967928fc495Schristos 		if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask))
968928fc495Schristos 			s[n++] = attrs[i].code;
969928fc495Schristos 	}
970928fc495Schristos 
971fe99a117Schristos 	/* Write the attributes. */
972928fc495Schristos 	*buf = '\0';
973928fc495Schristos 	if (n > 0) {
974f844e94eSwiz 		if (flags & GRID_STRING_ESCAPE_SEQUENCES)
975928fc495Schristos 			strlcat(buf, "\\033[", len);
976928fc495Schristos 		else
977928fc495Schristos 			strlcat(buf, "\033[", len);
978928fc495Schristos 		for (i = 0; i < n; i++) {
9790a274e86Schristos 			if (s[i] < 10)
980928fc495Schristos 				xsnprintf(tmp, sizeof tmp, "%d", s[i]);
9810a274e86Schristos 			else {
9820a274e86Schristos 				xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10,
9830a274e86Schristos 				    s[i] % 10);
9840a274e86Schristos 			}
985928fc495Schristos 			strlcat(buf, tmp, len);
9860a274e86Schristos 			if (i + 1 < n)
9870a274e86Schristos 				strlcat(buf, ";", len);
988928fc495Schristos 		}
989928fc495Schristos 		strlcat(buf, "m", len);
990928fc495Schristos 	}
991928fc495Schristos 
992fe99a117Schristos 	/* If the foreground colour changed, write its parameters. */
993fe99a117Schristos 	nnewc = grid_string_cells_fg(gc, newc);
994fe99a117Schristos 	noldc = grid_string_cells_fg(lastgc, oldc);
99546548964Swiz 	grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
996f844e94eSwiz 	    flags);
997fe99a117Schristos 
998fe99a117Schristos 	/* If the background colour changed, append its parameters. */
999fe99a117Schristos 	nnewc = grid_string_cells_bg(gc, newc);
1000fe99a117Schristos 	noldc = grid_string_cells_bg(lastgc, oldc);
100146548964Swiz 	grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
1002f844e94eSwiz 	    flags);
100346548964Swiz 
100446548964Swiz 	/* If the underscore colour changed, append its parameters. */
100546548964Swiz 	nnewc = grid_string_cells_us(gc, newc);
100646548964Swiz 	noldc = grid_string_cells_us(lastgc, oldc);
100746548964Swiz 	grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
1008f844e94eSwiz 	    flags);
1009fe99a117Schristos 
1010928fc495Schristos 	/* Append shift in/shift out if needed. */
1011928fc495Schristos 	if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
1012f844e94eSwiz 		if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1013928fc495Schristos 			strlcat(buf, "\\016", len); /* SO */
1014928fc495Schristos 		else
1015928fc495Schristos 			strlcat(buf, "\016", len);  /* SO */
1016928fc495Schristos 	}
1017928fc495Schristos 	if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
1018f844e94eSwiz 		if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1019928fc495Schristos 			strlcat(buf, "\\017", len); /* SI */
1020928fc495Schristos 		else
1021928fc495Schristos 			strlcat(buf, "\017", len);  /* SI */
1022928fc495Schristos 	}
1023f844e94eSwiz 
1024f844e94eSwiz 	/* Add hyperlink if changed. */
1025f844e94eSwiz 	if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
1026f844e94eSwiz 		if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
1027f844e94eSwiz 			*has_link = grid_string_cells_add_hyperlink(buf, len,
1028f844e94eSwiz 			    id, uri, flags);
1029f844e94eSwiz 		} else if (*has_link) {
1030f844e94eSwiz 			grid_string_cells_add_hyperlink(buf, len, "", "",
1031f844e94eSwiz 			    flags);
1032f844e94eSwiz 			*has_link = 0;
1033f844e94eSwiz 		}
1034f844e94eSwiz 	}
1035928fc495Schristos }
1036928fc495Schristos 
1037698d5317Sjmmv /* Convert cells into a string. */
1038698d5317Sjmmv char *
1039928fc495Schristos grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
1040f844e94eSwiz     struct grid_cell **lastgc, int flags, struct screen *s)
1041698d5317Sjmmv {
1042f26e8bc9Schristos 	struct grid_cell	 gc;
1043928fc495Schristos 	static struct grid_cell	 lastgc1;
1044928fc495Schristos 	const char		*data;
1045f844e94eSwiz 	char			*buf, code[8192];
1046928fc495Schristos 	size_t			 len, off, size, codelen;
1047f844e94eSwiz 	u_int			 xx, end;
1048f844e94eSwiz 	int			 has_link = 0;
1049928fc495Schristos 	const struct grid_line	*gl;
1050698d5317Sjmmv 
1051928fc495Schristos 	if (lastgc != NULL && *lastgc == NULL) {
1052928fc495Schristos 		memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1);
1053928fc495Schristos 		*lastgc = &lastgc1;
1054928fc495Schristos 	}
1055928fc495Schristos 
1056698d5317Sjmmv 	len = 128;
1057698d5317Sjmmv 	buf = xmalloc(len);
1058698d5317Sjmmv 	off = 0;
1059698d5317Sjmmv 
1060928fc495Schristos 	gl = grid_peek_line(gd, py);
1061f844e94eSwiz 	if (flags & GRID_STRING_EMPTY_CELLS)
1062f844e94eSwiz 		end = gl->cellsize;
1063f844e94eSwiz 	else
1064f844e94eSwiz 		end = gl->cellused;
1065698d5317Sjmmv 	for (xx = px; xx < px + nx; xx++) {
1066f844e94eSwiz 		if (gl == NULL || xx >= end)
1067928fc495Schristos 			break;
1068f26e8bc9Schristos 		grid_get_cell(gd, xx, py, &gc);
1069f26e8bc9Schristos 		if (gc.flags & GRID_FLAG_PADDING)
1070698d5317Sjmmv 			continue;
1071698d5317Sjmmv 
1072f844e94eSwiz 		if (flags & GRID_STRING_WITH_SEQUENCES) {
1073f26e8bc9Schristos 			grid_string_cells_code(*lastgc, &gc, code, sizeof code,
1074f844e94eSwiz 			    flags, s, &has_link);
1075928fc495Schristos 			codelen = strlen(code);
1076f26e8bc9Schristos 			memcpy(*lastgc, &gc, sizeof **lastgc);
1077928fc495Schristos 		} else
1078928fc495Schristos 			codelen = 0;
1079698d5317Sjmmv 
1080f26e8bc9Schristos 		data = (void *)gc.data.data;
1081f26e8bc9Schristos 		size = gc.data.size;
1082f844e94eSwiz 		if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
1083f844e94eSwiz 		    size == 1 &&
1084f844e94eSwiz 		    *data == '\\') {
1085928fc495Schristos 			data = "\\\\";
1086928fc495Schristos 			size = 2;
1087928fc495Schristos 		}
1088928fc495Schristos 
1089928fc495Schristos 		while (len < off + size + codelen + 1) {
109099e242abSchristos 			buf = xreallocarray(buf, 2, len);
1091698d5317Sjmmv 			len *= 2;
1092698d5317Sjmmv 		}
1093698d5317Sjmmv 
1094928fc495Schristos 		if (codelen != 0) {
1095928fc495Schristos 			memcpy(buf + off, code, codelen);
1096928fc495Schristos 			off += codelen;
1097928fc495Schristos 		}
1098928fc495Schristos 		memcpy(buf + off, data, size);
1099928fc495Schristos 		off += size;
1100698d5317Sjmmv 	}
1101698d5317Sjmmv 
1102f844e94eSwiz 	if (has_link) {
1103f844e94eSwiz 		grid_string_cells_add_hyperlink(code, sizeof code, "", "",
1104f844e94eSwiz 		    flags);
1105f844e94eSwiz 		codelen = strlen(code);
1106f844e94eSwiz 		while (len < off + size + codelen + 1) {
1107f844e94eSwiz 			buf = xreallocarray(buf, 2, len);
1108f844e94eSwiz 			len *= 2;
1109f844e94eSwiz 		}
1110f844e94eSwiz 		memcpy(buf + off, code, codelen);
1111f844e94eSwiz 		off += codelen;
1112f844e94eSwiz 	}
1113f844e94eSwiz 
1114f844e94eSwiz 	if (flags & GRID_STRING_TRIM_SPACES) {
1115698d5317Sjmmv 		while (off > 0 && buf[off - 1] == ' ')
1116698d5317Sjmmv 			off--;
1117928fc495Schristos 	}
1118698d5317Sjmmv 	buf[off] = '\0';
1119928fc495Schristos 
1120698d5317Sjmmv 	return (buf);
1121698d5317Sjmmv }
1122698d5317Sjmmv 
1123698d5317Sjmmv /*
1124fe99a117Schristos  * Duplicate a set of lines between two grids. Both source and destination
1125fe99a117Schristos  * should be big enough.
1126698d5317Sjmmv  */
1127698d5317Sjmmv void
1128928fc495Schristos grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
1129928fc495Schristos     u_int ny)
1130698d5317Sjmmv {
1131698d5317Sjmmv 	struct grid_line	*dstl, *srcl;
1132698d5317Sjmmv 	u_int			 yy;
1133698d5317Sjmmv 
1134698d5317Sjmmv 	if (dy + ny > dst->hsize + dst->sy)
1135698d5317Sjmmv 		ny = dst->hsize + dst->sy - dy;
1136698d5317Sjmmv 	if (sy + ny > src->hsize + src->sy)
1137698d5317Sjmmv 		ny = src->hsize + src->sy - sy;
1138fe99a117Schristos 	grid_free_lines(dst, dy, ny);
1139698d5317Sjmmv 
1140698d5317Sjmmv 	for (yy = 0; yy < ny; yy++) {
1141698d5317Sjmmv 		srcl = &src->linedata[sy];
1142698d5317Sjmmv 		dstl = &dst->linedata[dy];
1143698d5317Sjmmv 
1144698d5317Sjmmv 		memcpy(dstl, srcl, sizeof *dstl);
1145698d5317Sjmmv 		if (srcl->cellsize != 0) {
114699e242abSchristos 			dstl->celldata = xreallocarray(NULL,
1147698d5317Sjmmv 			    srcl->cellsize, sizeof *dstl->celldata);
1148698d5317Sjmmv 			memcpy(dstl->celldata, srcl->celldata,
1149698d5317Sjmmv 			    srcl->cellsize * sizeof *dstl->celldata);
115099e242abSchristos 		} else
115199e242abSchristos 			dstl->celldata = NULL;
1152f26e8bc9Schristos 		if (srcl->extdsize != 0) {
1153f26e8bc9Schristos 			dstl->extdsize = srcl->extdsize;
1154f26e8bc9Schristos 			dstl->extddata = xreallocarray(NULL, dstl->extdsize,
1155f26e8bc9Schristos 			    sizeof *dstl->extddata);
1156f26e8bc9Schristos 			memcpy(dstl->extddata, srcl->extddata, dstl->extdsize *
1157f26e8bc9Schristos 			    sizeof *dstl->extddata);
1158e271dbb8Schristos 		} else
1159e271dbb8Schristos 			dstl->extddata = NULL;
1160f26e8bc9Schristos 
1161698d5317Sjmmv 		sy++;
1162698d5317Sjmmv 		dy++;
1163698d5317Sjmmv 	}
1164698d5317Sjmmv }
1165928fc495Schristos 
1166c7e17de0Schristos /* Mark line as dead. */
1167e9a2d6faSchristos static void
1168c7e17de0Schristos grid_reflow_dead(struct grid_line *gl)
1169f26e8bc9Schristos {
1170c7e17de0Schristos 	memset(gl, 0, sizeof *gl);
1171c7e17de0Schristos 	gl->flags = GRID_LINE_DEAD;
1172c7e17de0Schristos }
1173f26e8bc9Schristos 
1174c7e17de0Schristos /* Add lines, return the first new one. */
1175c7e17de0Schristos static struct grid_line *
1176c7e17de0Schristos grid_reflow_add(struct grid *gd, u_int n)
1177c7e17de0Schristos {
1178c7e17de0Schristos 	struct grid_line	*gl;
1179c7e17de0Schristos 	u_int			 sy = gd->sy + n;
1180f26e8bc9Schristos 
1181c7e17de0Schristos 	gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata);
1182c7e17de0Schristos 	gl = &gd->linedata[gd->sy];
1183c7e17de0Schristos 	memset(gl, 0, n * (sizeof *gl));
1184c7e17de0Schristos 	gd->sy = sy;
1185c7e17de0Schristos 	return (gl);
1186c7e17de0Schristos }
1187c7e17de0Schristos 
1188c7e17de0Schristos /* Move a line across. */
1189c7e17de0Schristos static struct grid_line *
1190c7e17de0Schristos grid_reflow_move(struct grid *gd, struct grid_line *from)
1191c7e17de0Schristos {
1192c7e17de0Schristos 	struct grid_line	*to;
1193c7e17de0Schristos 
1194c7e17de0Schristos 	to = grid_reflow_add(gd, 1);
1195c7e17de0Schristos 	memcpy(to, from, sizeof *to);
1196c7e17de0Schristos 	grid_reflow_dead(from);
1197c7e17de0Schristos 	return (to);
1198c7e17de0Schristos }
1199c7e17de0Schristos 
1200c7e17de0Schristos /* Join line below onto this one. */
1201c7e17de0Schristos static void
1202c7e17de0Schristos grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
12030a274e86Schristos     u_int width, int already)
1204c7e17de0Schristos {
1205c7e17de0Schristos 	struct grid_line	*gl, *from = NULL;
1206c7e17de0Schristos 	struct grid_cell	 gc;
1207c7e17de0Schristos 	u_int			 lines, left, i, to, line, want = 0;
1208c7e17de0Schristos 	u_int			 at;
1209c7e17de0Schristos 	int			 wrapped = 1;
1210c7e17de0Schristos 
1211c7e17de0Schristos 	/*
1212c7e17de0Schristos 	 * Add a new target line.
1213c7e17de0Schristos 	 */
1214c7e17de0Schristos 	if (!already) {
1215c7e17de0Schristos 		to = target->sy;
1216c7e17de0Schristos 		gl = grid_reflow_move(target, &gd->linedata[yy]);
1217c7e17de0Schristos 	} else {
1218c7e17de0Schristos 		to = target->sy - 1;
1219c7e17de0Schristos 		gl = &target->linedata[to];
1220c7e17de0Schristos 	}
1221c7e17de0Schristos 	at = gl->cellused;
1222c7e17de0Schristos 
1223c7e17de0Schristos 	/*
1224c7e17de0Schristos 	 * Loop until no more to consume or the target line is full.
1225c7e17de0Schristos 	 */
1226c7e17de0Schristos 	lines = 0;
1227c7e17de0Schristos 	for (;;) {
1228c7e17de0Schristos 		/*
1229c7e17de0Schristos 		 * If this is now the last line, there is nothing more to be
1230c7e17de0Schristos 		 * done.
1231c7e17de0Schristos 		 */
1232c7e17de0Schristos 		if (yy + 1 + lines == gd->hsize + gd->sy)
1233c7e17de0Schristos 			break;
1234c7e17de0Schristos 		line = yy + 1 + lines;
1235c7e17de0Schristos 
1236c7e17de0Schristos 		/* If the next line is empty, skip it. */
1237c7e17de0Schristos 		if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
1238c7e17de0Schristos 			wrapped = 0;
1239c7e17de0Schristos 		if (gd->linedata[line].cellused == 0) {
1240c7e17de0Schristos 			if (!wrapped)
1241c7e17de0Schristos 				break;
1242c7e17de0Schristos 			lines++;
1243f26e8bc9Schristos 			continue;
1244928fc495Schristos 		}
1245928fc495Schristos 
1246928fc495Schristos 		/*
1247c7e17de0Schristos 		 * Is the destination line now full? Copy the first character
1248c7e17de0Schristos 		 * separately because we need to leave "from" set to the last
1249c7e17de0Schristos 		 * line if this line is full.
1250928fc495Schristos 		 */
1251c7e17de0Schristos 		grid_get_cell1(&gd->linedata[line], 0, &gc);
1252c7e17de0Schristos 		if (width + gc.data.width > sx)
1253c7e17de0Schristos 			break;
1254c7e17de0Schristos 		width += gc.data.width;
1255c7e17de0Schristos 		grid_set_cell(target, at, to, &gc);
1256c7e17de0Schristos 		at++;
1257c7e17de0Schristos 
1258c7e17de0Schristos 		/* Join as much more as possible onto the current line. */
1259c7e17de0Schristos 		from = &gd->linedata[line];
1260c7e17de0Schristos 		for (want = 1; want < from->cellused; want++) {
1261c7e17de0Schristos 			grid_get_cell1(from, want, &gc);
1262c7e17de0Schristos 			if (width + gc.data.width > sx)
1263c7e17de0Schristos 				break;
1264c7e17de0Schristos 			width += gc.data.width;
1265c7e17de0Schristos 
1266c7e17de0Schristos 			grid_set_cell(target, at, to, &gc);
1267c7e17de0Schristos 			at++;
1268c7e17de0Schristos 		}
1269c7e17de0Schristos 		lines++;
1270c7e17de0Schristos 
1271c7e17de0Schristos 		/*
1272c7e17de0Schristos 		 * If this line wasn't wrapped or we didn't consume the entire
1273c7e17de0Schristos 		 * line, don't try to join any further lines.
1274c7e17de0Schristos 		 */
1275c7e17de0Schristos 		if (!wrapped || want != from->cellused || width == sx)
1276c7e17de0Schristos 			break;
1277c7e17de0Schristos 	}
1278c7e17de0Schristos 	if (lines == 0)
1279c7e17de0Schristos 		return;
1280c7e17de0Schristos 
1281c7e17de0Schristos 	/*
1282c7e17de0Schristos 	 * If we didn't consume the entire final line, then remove what we did
1283c7e17de0Schristos 	 * consume. If we consumed the entire line and it wasn't wrapped,
1284c7e17de0Schristos 	 * remove the wrap flag from this line.
1285c7e17de0Schristos 	 */
1286c7e17de0Schristos 	left = from->cellused - want;
1287c7e17de0Schristos 	if (left != 0) {
1288c7e17de0Schristos 		grid_move_cells(gd, 0, want, yy + lines, left, 8);
1289c7e17de0Schristos 		from->cellsize = from->cellused = left;
1290c7e17de0Schristos 		lines--;
1291c7e17de0Schristos 	} else if (!wrapped)
1292c7e17de0Schristos 		gl->flags &= ~GRID_LINE_WRAPPED;
1293c7e17de0Schristos 
1294c7e17de0Schristos 	/* Remove the lines that were completely consumed. */
1295c7e17de0Schristos 	for (i = yy + 1; i < yy + 1 + lines; i++) {
1296c7e17de0Schristos 		free(gd->linedata[i].celldata);
1297c7e17de0Schristos 		free(gd->linedata[i].extddata);
1298c7e17de0Schristos 		grid_reflow_dead(&gd->linedata[i]);
1299c7e17de0Schristos 	}
1300c7e17de0Schristos 
13010a274e86Schristos 	/* Adjust scroll position. */
1302c7e17de0Schristos 	if (gd->hscrolled > to + lines)
1303c7e17de0Schristos 		gd->hscrolled -= lines;
1304c7e17de0Schristos 	else if (gd->hscrolled > to)
1305c7e17de0Schristos 		gd->hscrolled = to;
1306c7e17de0Schristos }
1307c7e17de0Schristos 
1308c7e17de0Schristos /* Split this line into several new ones */
1309c7e17de0Schristos static void
1310c7e17de0Schristos grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
13110a274e86Schristos     u_int at)
1312928fc495Schristos {
1313c7e17de0Schristos 	struct grid_line	*gl = &gd->linedata[yy], *first;
1314c7e17de0Schristos 	struct grid_cell	 gc;
1315c7e17de0Schristos 	u_int			 line, lines, width, i, xx;
1316c7e17de0Schristos 	u_int			 used = gl->cellused;
1317c7e17de0Schristos 	int			 flags = gl->flags;
1318928fc495Schristos 
1319c7e17de0Schristos 	/* How many lines do we need to insert? We know we need at least two. */
1320c7e17de0Schristos 	if (~gl->flags & GRID_LINE_EXTENDED)
1321c7e17de0Schristos 		lines = 1 + (gl->cellused - 1) / sx;
1322c7e17de0Schristos 	else {
1323c7e17de0Schristos 		lines = 2;
1324c7e17de0Schristos 		width = 0;
1325c7e17de0Schristos 		for (i = at; i < used; i++) {
1326c7e17de0Schristos 			grid_get_cell1(gl, i, &gc);
1327c7e17de0Schristos 			if (width + gc.data.width > sx) {
1328c7e17de0Schristos 				lines++;
1329c7e17de0Schristos 				width = 0;
1330c7e17de0Schristos 			}
1331c7e17de0Schristos 			width += gc.data.width;
1332c7e17de0Schristos 		}
1333c7e17de0Schristos 	}
1334928fc495Schristos 
1335c7e17de0Schristos 	/* Insert new lines. */
1336c7e17de0Schristos 	line = target->sy + 1;
1337c7e17de0Schristos 	first = grid_reflow_add(target, lines);
1338c7e17de0Schristos 
1339c7e17de0Schristos 	/* Copy sections from the original line. */
1340c7e17de0Schristos 	width = 0;
1341c7e17de0Schristos 	xx = 0;
1342c7e17de0Schristos 	for (i = at; i < used; i++) {
1343c7e17de0Schristos 		grid_get_cell1(gl, i, &gc);
1344c7e17de0Schristos 		if (width + gc.data.width > sx) {
1345c7e17de0Schristos 			target->linedata[line].flags |= GRID_LINE_WRAPPED;
1346c7e17de0Schristos 
1347c7e17de0Schristos 			line++;
1348c7e17de0Schristos 			width = 0;
1349c7e17de0Schristos 			xx = 0;
1350c7e17de0Schristos 		}
1351c7e17de0Schristos 		width += gc.data.width;
1352c7e17de0Schristos 		grid_set_cell(target, xx, line, &gc);
1353c7e17de0Schristos 		xx++;
1354c7e17de0Schristos 	}
1355c7e17de0Schristos 	if (flags & GRID_LINE_WRAPPED)
1356c7e17de0Schristos 		target->linedata[line].flags |= GRID_LINE_WRAPPED;
1357c7e17de0Schristos 
1358c7e17de0Schristos 	/* Move the remainder of the original line. */
1359c7e17de0Schristos 	gl->cellsize = gl->cellused = at;
1360c7e17de0Schristos 	gl->flags |= GRID_LINE_WRAPPED;
1361c7e17de0Schristos 	memcpy(first, gl, sizeof *first);
1362c7e17de0Schristos 	grid_reflow_dead(gl);
1363c7e17de0Schristos 
13640a274e86Schristos 	/* Adjust the scroll position. */
1365c7e17de0Schristos 	if (yy <= gd->hscrolled)
1366c7e17de0Schristos 		gd->hscrolled += lines - 1;
1367c7e17de0Schristos 
1368c7e17de0Schristos 	/*
1369c7e17de0Schristos 	 * If the original line had the wrapped flag and there is still space
1370c7e17de0Schristos 	 * in the last new line, try to join with the next lines.
1371c7e17de0Schristos 	 */
1372c7e17de0Schristos 	if (width < sx && (flags & GRID_LINE_WRAPPED))
13730a274e86Schristos 		grid_reflow_join(target, gd, sx, yy, width, 1);
1374c7e17de0Schristos }
1375c7e17de0Schristos 
1376c7e17de0Schristos /* Reflow lines on grid to new width. */
1377c7e17de0Schristos void
13780a274e86Schristos grid_reflow(struct grid *gd, u_int sx)
1379c7e17de0Schristos {
1380c7e17de0Schristos 	struct grid		*target;
1381c7e17de0Schristos 	struct grid_line	*gl;
1382c7e17de0Schristos 	struct grid_cell	 gc;
1383e271dbb8Schristos 	u_int			 yy, width, i, at;
1384c7e17de0Schristos 
1385c7e17de0Schristos 	/*
1386c7e17de0Schristos 	 * Create a destination grid. This is just used as a container for the
1387c7e17de0Schristos 	 * line data and may not be fully valid.
1388c7e17de0Schristos 	 */
1389c7e17de0Schristos 	target = grid_create(gd->sx, 0, 0);
1390c7e17de0Schristos 
1391c7e17de0Schristos 	/*
1392c7e17de0Schristos 	 * Loop over each source line.
1393c7e17de0Schristos 	 */
1394c7e17de0Schristos 	for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
1395c7e17de0Schristos 		gl = &gd->linedata[yy];
1396c7e17de0Schristos 		if (gl->flags & GRID_LINE_DEAD)
1397c7e17de0Schristos 			continue;
1398c7e17de0Schristos 
1399c7e17de0Schristos 		/*
1400e271dbb8Schristos 		 * Work out the width of this line. at is the point at which
1401e271dbb8Schristos 		 * the available width is hit, and width is the full line
1402e271dbb8Schristos 		 * width.
1403c7e17de0Schristos 		 */
1404e271dbb8Schristos 		at = width = 0;
1405c7e17de0Schristos 		if (~gl->flags & GRID_LINE_EXTENDED) {
1406c7e17de0Schristos 			width = gl->cellused;
1407c7e17de0Schristos 			if (width > sx)
1408c7e17de0Schristos 				at = sx;
1409928fc495Schristos 			else
1410c7e17de0Schristos 				at = width;
1411928fc495Schristos 		} else {
1412c7e17de0Schristos 			for (i = 0; i < gl->cellused; i++) {
1413c7e17de0Schristos 				grid_get_cell1(gl, i, &gc);
1414c7e17de0Schristos 				if (at == 0 && width + gc.data.width > sx)
1415c7e17de0Schristos 					at = i;
1416c7e17de0Schristos 				width += gc.data.width;
1417928fc495Schristos 			}
1418928fc495Schristos 		}
1419928fc495Schristos 
1420c7e17de0Schristos 		/*
1421e271dbb8Schristos 		 * If the line is exactly right, just move it across
1422e271dbb8Schristos 		 * unchanged.
1423c7e17de0Schristos 		 */
1424e271dbb8Schristos 		if (width == sx) {
1425c7e17de0Schristos 			grid_reflow_move(target, gl);
1426c7e17de0Schristos 			continue;
1427c7e17de0Schristos 		}
1428928fc495Schristos 
1429c7e17de0Schristos 		/*
1430c7e17de0Schristos 		 * If the line is too big, it needs to be split, whether or not
1431c7e17de0Schristos 		 * it was previously wrapped.
1432c7e17de0Schristos 		 */
1433c7e17de0Schristos 		if (width > sx) {
14340a274e86Schristos 			grid_reflow_split(target, gd, sx, yy, at);
1435c7e17de0Schristos 			continue;
1436c7e17de0Schristos 		}
1437c7e17de0Schristos 
1438c7e17de0Schristos 		/*
1439c7e17de0Schristos 		 * If the line was previously wrapped, join as much as possible
1440c7e17de0Schristos 		 * of the next line.
1441c7e17de0Schristos 		 */
1442c7e17de0Schristos 		if (gl->flags & GRID_LINE_WRAPPED)
14430a274e86Schristos 			grid_reflow_join(target, gd, sx, yy, width, 0);
1444c7e17de0Schristos 		else
1445c7e17de0Schristos 			grid_reflow_move(target, gl);
1446c7e17de0Schristos 	}
1447c7e17de0Schristos 
1448c7e17de0Schristos 	/*
1449c7e17de0Schristos 	 * Replace the old grid with the new.
1450c7e17de0Schristos 	 */
1451c7e17de0Schristos 	if (target->sy < gd->sy)
1452c7e17de0Schristos 		grid_reflow_add(target, gd->sy - target->sy);
1453c7e17de0Schristos 	gd->hsize = target->sy - gd->sy;
14540a274e86Schristos 	if (gd->hscrolled > gd->hsize)
14550a274e86Schristos 		gd->hscrolled = gd->hsize;
1456c7e17de0Schristos 	free(gd->linedata);
1457c7e17de0Schristos 	gd->linedata = target->linedata;
1458c7e17de0Schristos 	free(target);
14590a274e86Schristos }
14600a274e86Schristos 
14610a274e86Schristos /* Convert to position based on wrapped lines. */
14620a274e86Schristos void
14630a274e86Schristos grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy)
14640a274e86Schristos {
14650a274e86Schristos 	u_int	ax = 0, ay = 0, yy;
14660a274e86Schristos 
14670a274e86Schristos 	for (yy = 0; yy < py; yy++) {
14680a274e86Schristos 		if (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
14690a274e86Schristos 			ax += gd->linedata[yy].cellused;
14700a274e86Schristos 		else {
14710a274e86Schristos 			ax = 0;
14720a274e86Schristos 			ay++;
14730a274e86Schristos 		}
14740a274e86Schristos 	}
14750a274e86Schristos 	if (px >= gd->linedata[yy].cellused)
14760a274e86Schristos 		ax = UINT_MAX;
14770a274e86Schristos 	else
14780a274e86Schristos 		ax += px;
14790a274e86Schristos 	*wx = ax;
14800a274e86Schristos 	*wy = ay;
14810a274e86Schristos }
14820a274e86Schristos 
14830a274e86Schristos /* Convert position based on wrapped lines back. */
14840a274e86Schristos void
14850a274e86Schristos grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
14860a274e86Schristos {
1487e271dbb8Schristos 	u_int	yy, ay = 0;
14880a274e86Schristos 
14890a274e86Schristos 	for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
14900a274e86Schristos 		if (ay == wy)
14910a274e86Schristos 			break;
1492e271dbb8Schristos 		if (~gd->linedata[yy].flags & GRID_LINE_WRAPPED)
14930a274e86Schristos 			ay++;
14940a274e86Schristos 	}
1495c7e17de0Schristos 
1496c7e17de0Schristos 	/*
14970a274e86Schristos 	 * yy is now 0 on the unwrapped line which contains wx. Walk forwards
14980a274e86Schristos 	 * until we find the end or the line now containing wx.
1499c7e17de0Schristos 	 */
15000a274e86Schristos 	if (wx == UINT_MAX) {
15010a274e86Schristos 		while (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
15020a274e86Schristos 			yy++;
15030a274e86Schristos 		wx = gd->linedata[yy].cellused;
15040a274e86Schristos 	} else {
15050a274e86Schristos 		while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) {
15060a274e86Schristos 			if (wx < gd->linedata[yy].cellused)
15070a274e86Schristos 				break;
15080a274e86Schristos 			wx -= gd->linedata[yy].cellused;
15090a274e86Schristos 			yy++;
15100a274e86Schristos 		}
15110a274e86Schristos 	}
15120a274e86Schristos 	*px = wx;
15130a274e86Schristos 	*py = yy;
1514928fc495Schristos }
151530744affSchristos 
151630744affSchristos /* Get length of line. */
151730744affSchristos u_int
151830744affSchristos grid_line_length(struct grid *gd, u_int py)
151930744affSchristos {
152030744affSchristos 	struct grid_cell	gc;
152130744affSchristos 	u_int			px;
152230744affSchristos 
152330744affSchristos 	px = grid_get_line(gd, py)->cellsize;
152430744affSchristos 	if (px > gd->sx)
152530744affSchristos 		px = gd->sx;
152630744affSchristos 	while (px > 0) {
152730744affSchristos 		grid_get_cell(gd, px - 1, py, &gc);
1528e271dbb8Schristos 		if ((gc.flags & GRID_FLAG_PADDING) ||
1529e271dbb8Schristos 		    gc.data.size != 1 ||
1530e271dbb8Schristos 		    *gc.data.data != ' ')
153130744affSchristos 			break;
153230744affSchristos 		px--;
153330744affSchristos 	}
153430744affSchristos 	return (px);
153530744affSchristos }
1536