xref: /openbsd-src/usr.bin/tmux/grid.c (revision 43003dfe3ad45d1698bed8a37f2b0f5b14f20d4f)
1 /* $OpenBSD: grid.c,v 1.14 2009/09/15 15:14:09 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <string.h>
22 
23 #include "tmux.h"
24 
25 /*
26  * Grid data. This is the basic data structure that represents what is shown on
27  * screen.
28  *
29  * A grid is a grid of cells (struct grid_cell). Lines are not allocated until
30  * cells in that line are written to. The grid is split into history and
31  * viewable data with the history starting at row (line) 0 and extending to
32  * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All
33  * functions in this file work on absolute coordinates, grid-view.c has
34  * functions which work on the screen data.
35  */
36 
37 /* Default grid cell data. */
38 const struct grid_cell grid_default_cell = { 0, 0, 8, 8, ' ' };
39 
40 #define grid_put_cell(gd, px, py, gc) do {			\
41 	memcpy(&gd->linedata[py].celldata[px], 			\
42 	    gc, sizeof gd->linedata[py].celldata[px]);		\
43 } while (0)
44 #define grid_put_utf8(gd, px, py, gc) do {			\
45 	memcpy(&gd->linedata[py].utf8data[px], 			\
46 	    gc, sizeof gd->linedata[py].utf8data[px]);		\
47 } while (0)
48 
49 int	grid_check_x(struct grid *, u_int);
50 int	grid_check_y(struct grid *, u_int);
51 
52 #ifdef DEBUG
53 int
54 grid_check_x(struct grid *gd, u_int px)
55 {
56 	if ((px) >= (gd)->sx)
57 		log_fatalx("x out of range: %u", px);
58 	return (0);
59 }
60 
61 int
62 grid_check_y(struct grid *gd, u_int py)
63 {
64 	if ((py) >= (gd)->hsize + (gd)->sy)
65 		log_fatalx("y out of range: %u", py);
66 	return (0);
67 }
68 #else
69 int
70 grid_check_x(struct grid *gd, u_int px)
71 {
72 	if ((px) >= (gd)->sx) {
73 		log_debug("x out of range: %u", px);
74 		return (-1);
75 	}
76 	return (0);
77 }
78 
79 int
80 grid_check_y(struct grid *gd, u_int py)
81 {
82 	if ((py) >= (gd)->hsize + (gd)->sy) {
83 		log_debug("y out of range: %u", py);
84 		return (-1);
85 	}
86 	return (0);
87 }
88 #endif
89 
90 /* Create a new grid. */
91 struct grid *
92 grid_create(u_int sx, u_int sy, u_int hlimit)
93 {
94 	struct grid	*gd;
95 
96 	gd = xmalloc(sizeof *gd);
97 	gd->sx = sx;
98 	gd->sy = sy;
99 
100 	gd->flags = GRID_HISTORY;
101 
102 	gd->hsize = 0;
103 	gd->hlimit = hlimit;
104 
105 	gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
106 
107 	return (gd);
108 }
109 
110 /* Destroy grid. */
111 void
112 grid_destroy(struct grid *gd)
113 {
114 	struct grid_line	*gl;
115 	u_int			 yy;
116 
117 	for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
118 		gl = &gd->linedata[yy];
119 		if (gl->celldata != NULL)
120 			xfree(gl->celldata);
121 		if (gl->utf8data != NULL)
122 			xfree(gl->utf8data);
123 	}
124 
125 	xfree(gd->linedata);
126 
127 	xfree(gd);
128 }
129 
130 /* Compare grids. */
131 int
132 grid_compare(struct grid *ga, struct grid *gb)
133 {
134 	struct grid_line	*gla, *glb;
135 	struct grid_cell	*gca, *gcb;
136 	struct grid_utf8	*gua, *gub;
137 	u_int			 xx, yy;
138 
139 	if (ga->sx != gb->sx || ga->sy != ga->sy)
140 		return (1);
141 
142 	for (yy = 0; yy < ga->sy; yy++) {
143 		gla = &ga->linedata[yy];
144 		glb = &gb->linedata[yy];
145 		if (gla->cellsize != glb->cellsize)
146 			return (1);
147 		for (xx = 0; xx < ga->sx; xx++) {
148 			gca = &gla->celldata[xx];
149 			gcb = &glb->celldata[xx];
150 			if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0)
151 				return (1);
152 			if (!(gca->flags & GRID_FLAG_UTF8))
153 				continue;
154 			gua = &gla->utf8data[xx];
155 			gub = &glb->utf8data[xx];
156 			if (memcmp(gua, gub, sizeof (struct grid_utf8)) != 0)
157 				return (1);
158 		}
159 	}
160 
161 	return (0);
162 }
163 
164 /* Scroll a line into the history. */
165 void
166 grid_scroll_line(struct grid *gd)
167 {
168 	u_int	yy;
169 
170  	GRID_DEBUG(gd, "");
171 
172 	if (gd->hsize >= gd->hlimit) {
173 		/* If the limit is hit, free the bottom 10% and shift up. */
174 		yy = gd->hlimit / 10;
175 		if (yy < 1)
176 			yy = 1;
177 
178 		grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy);
179 		gd->hsize -= yy;
180 	}
181 
182 	yy = gd->hsize + gd->sy;
183 
184 	gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata);
185 	memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]);
186 
187 	gd->hsize++;
188 }
189 
190 /* Expand line to fit to cell. */
191 void
192 grid_expand_line(struct grid *gd, u_int py, u_int sx)
193 {
194 	struct grid_line	*gl;
195 	u_int			 xx;
196 
197 	gl = &gd->linedata[py];
198 	if (sx <= gl->cellsize)
199 		return;
200 
201 	gl->celldata = xrealloc(gl->celldata, sx, sizeof *gl->celldata);
202 	for (xx = gl->cellsize; xx < sx; xx++)
203 		grid_put_cell(gd, xx, py, &grid_default_cell);
204 	gl->cellsize = sx;
205 }
206 
207 /* Expand line to fit to cell for UTF-8. */
208 void
209 grid_expand_line_utf8(struct grid *gd, u_int py, u_int sx)
210 {
211 	struct grid_line	*gl;
212 
213 	gl = &gd->linedata[py];
214 	if (sx <= gl->utf8size)
215 		return;
216 
217 	gl->utf8data = xrealloc(gl->utf8data, sx, sizeof *gl->utf8data);
218 	gl->utf8size = sx;
219 }
220 
221 /* Get cell for reading. */
222 const struct grid_cell *
223 grid_peek_cell(struct grid *gd, u_int px, u_int py)
224 {
225 	if (grid_check_x(gd, px) != 0)
226 		return (&grid_default_cell);
227 	if (grid_check_y(gd, py) != 0)
228 		return (&grid_default_cell);
229 
230 	if (px >= gd->linedata[py].cellsize)
231 		return (&grid_default_cell);
232 	return (&gd->linedata[py].celldata[px]);
233 }
234 
235 /* Get cell at relative position (for writing). */
236 struct grid_cell *
237 grid_get_cell(struct grid *gd, u_int px, u_int py)
238 {
239 	if (grid_check_x(gd, px) != 0)
240 		return (NULL);
241 	if (grid_check_y(gd, py) != 0)
242 		return (NULL);
243 
244 	grid_expand_line(gd, py, px + 1);
245 	return (&gd->linedata[py].celldata[px]);
246 }
247 
248 /* Set cell at relative position. */
249 void
250 grid_set_cell(
251     struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
252 {
253 	if (grid_check_x(gd, px) != 0)
254 		return;
255 	if (grid_check_y(gd, py) != 0)
256 		return;
257 
258 	grid_expand_line(gd, py, px + 1);
259 	grid_put_cell(gd, px, py, gc);
260 }
261 
262 /* Get UTF-8 for reading. */
263 const struct grid_utf8 *
264 grid_peek_utf8(struct grid *gd, u_int px, u_int py)
265 {
266 	if (grid_check_x(gd, px) != 0)
267 		return (NULL);
268 	if (grid_check_y(gd, py) != 0)
269 		return (NULL);
270 
271 	if (px >= gd->linedata[py].utf8size)
272 		return (NULL);
273 	return (&gd->linedata[py].utf8data[px]);
274 }
275 
276 /* Get utf8 at relative position (for writing). */
277 struct grid_utf8 *
278 grid_get_utf8(struct grid *gd, u_int px, u_int py)
279 {
280 	if (grid_check_x(gd, px) != 0)
281 		return (NULL);
282 	if (grid_check_y(gd, py) != 0)
283 		return (NULL);
284 
285 	grid_expand_line_utf8(gd, py, px + 1);
286 	return (&gd->linedata[py].utf8data[px]);
287 }
288 
289 /* Set utf8 at relative position. */
290 void
291 grid_set_utf8(
292     struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gc)
293 {
294 	if (grid_check_x(gd, px) != 0)
295 		return;
296 	if (grid_check_y(gd, py) != 0)
297 		return;
298 
299 	grid_expand_line_utf8(gd, py, px + 1);
300 	grid_put_utf8(gd, px, py, gc);
301 }
302 
303 /* Clear area. */
304 void
305 grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny)
306 {
307 	u_int	xx, yy;
308 
309  	GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny);
310 
311 	if (nx == 0 || ny == 0)
312 		return;
313 
314 	if (px == 0 && nx == gd->sx) {
315 		grid_clear_lines(gd, py, ny);
316 		return;
317 	}
318 
319 	if (grid_check_x(gd, px) != 0)
320 		return;
321 	if (grid_check_x(gd, px + nx - 1) != 0)
322 		return;
323 	if (grid_check_y(gd, py) != 0)
324 		return;
325 	if (grid_check_y(gd, py + ny - 1) != 0)
326 		return;
327 
328 	for (yy = py; yy < py + ny; yy++) {
329 		if (px >= gd->linedata[yy].cellsize)
330 			continue;
331 		if (px + nx >= gd->linedata[yy].cellsize) {
332 			gd->linedata[yy].cellsize = px;
333 			continue;
334 		}
335 		for (xx = px; xx < px + nx; xx++) {
336 			if (xx >= gd->linedata[yy].cellsize)
337 				break;
338 			grid_put_cell(gd, xx, yy, &grid_default_cell);
339 		}
340 	}
341 }
342 
343 /* Clear lines. This just frees and truncates the lines. */
344 void
345 grid_clear_lines(struct grid *gd, u_int py, u_int ny)
346 {
347 	struct grid_line	*gl;
348 	u_int			 yy;
349 
350  	GRID_DEBUG(gd, "py=%u, ny=%u", py, ny);
351 
352 	if (ny == 0)
353 		return;
354 
355 	if (grid_check_y(gd, py) != 0)
356 		return;
357 	if (grid_check_y(gd, py + ny - 1) != 0)
358 		return;
359 
360 	for (yy = py; yy < py + ny; yy++) {
361 		gl = &gd->linedata[yy];
362 		if (gl->celldata != NULL)
363 			xfree(gl->celldata);
364 		if (gl->utf8data != NULL)
365 			xfree(gl->utf8data);
366 		memset(gl, 0, sizeof *gl);
367 	}
368 }
369 
370 /* Move a group of lines. */
371 void
372 grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny)
373 {
374 	u_int	yy;
375 
376  	GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny);
377 
378 	if (ny == 0 || py == dy)
379 		return;
380 
381 	if (grid_check_y(gd, py) != 0)
382 		return;
383 	if (grid_check_y(gd, py + ny - 1) != 0)
384 		return;
385 	if (grid_check_y(gd, dy) != 0)
386 		return;
387 	if (grid_check_y(gd, dy + ny - 1) != 0)
388 		return;
389 
390 	/* Free any lines which are being replaced. */
391 	for (yy = dy; yy < dy + ny; yy++) {
392 		if (yy >= py && yy < py + ny)
393 			continue;
394 		grid_clear_lines(gd, yy, 1);
395 	}
396 
397 	memmove(
398 	    &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata));
399 
400 	/* Wipe any lines that have been moved (without freeing them). */
401 	for (yy = py; yy < py + ny; yy++) {
402 		if (yy >= dy && yy < dy + ny)
403 			continue;
404 		memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]);
405 	}
406 }
407 
408 /* Move a group of cells. */
409 void
410 grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx)
411 {
412 	struct grid_line	*gl;
413 	u_int			 xx;
414 
415  	GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx);
416 
417 	if (nx == 0 || px == dx)
418 		return;
419 
420 	if (grid_check_x(gd, px) != 0)
421 		return;
422 	if (grid_check_x(gd, px + nx - 1) != 0)
423 		return;
424 	if (grid_check_x(gd, dx + nx - 1) != 0)
425 		return;
426 	if (grid_check_y(gd, py) != 0)
427 		return;
428 	gl = &gd->linedata[py];
429 
430 	grid_expand_line(gd, py, px + nx);
431 	grid_expand_line(gd, py, dx + nx);
432 	memmove(
433 	    &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata);
434 
435 	if (gl->utf8data != NULL) {
436 		grid_expand_line_utf8(gd, py, px + nx);
437 		grid_expand_line_utf8(gd, py, dx + nx);
438 		memmove(&gl->utf8data[dx],
439 		    &gl->utf8data[px], nx * sizeof *gl->utf8data);
440 	}
441 
442 	/* Wipe any cells that have been moved. */
443 	for (xx = px; xx < px + nx; xx++) {
444 		if (xx >= dx && xx < dx + nx)
445 			continue;
446 		grid_put_cell(gd, xx, py, &grid_default_cell);
447 	}
448 }
449 
450 /* Convert cells into a string. */
451 char *
452 grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
453 {
454  	const struct grid_cell	*gc;
455  	const struct grid_utf8	*gu;
456 	char			*buf;
457 	size_t			 len, off;
458 	u_int			 xx, i;
459 
460 	GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
461 
462 	len = 128;
463 	buf = xmalloc(len);
464 	off = 0;
465 
466 	for (xx = px; xx < px + nx; xx++) {
467 		gc = grid_peek_cell(gd, xx, py);
468 		if (gc->flags & GRID_FLAG_PADDING)
469 			continue;
470 
471 		if (gc->flags & GRID_FLAG_UTF8) {
472 			while (len < off + UTF8_SIZE + 1) {
473 				buf = xrealloc(buf, 2, len);
474 				len *= 2;
475 			}
476 
477 			gu = grid_peek_utf8(gd, xx, py);
478 			for (i = 0; i < UTF8_SIZE; i++) {
479 				if (gu->data[i] == 0xff)
480 					break;
481 				buf[off++] = gu->data[i];
482 			}
483 		} else {
484 			while (len < off + 2) {
485 				buf = xrealloc(buf, 2, len);
486 				len *= 2;
487 			}
488 
489 			buf[off++] = gc->data;
490 		}
491 	}
492 
493 	while (off > 0 && buf[off - 1] == ' ')
494 		off--;
495 	buf[off] = '\0';
496 	return (buf);
497 }
498 
499 /*
500  * Duplicate a set of lines between two grids. If there aren't enough lines in
501  * either source or destination, the number of lines is limited to the number
502  * available.
503  */
504 void
505 grid_duplicate_lines(
506     struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny)
507 {
508 	struct grid_line	*dstl, *srcl;
509 	u_int			 yy;
510 
511 	GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny);
512 
513 	if (dy + ny > dst->hsize + dst->sy)
514 		ny = dst->hsize + dst->sy - dy;
515 	if (sy + ny > src->hsize + src->sy)
516 		ny = src->hsize + src->sy - sy;
517 	grid_clear_lines(dst, dy, ny);
518 
519 	for (yy = 0; yy < ny; yy++) {
520 		srcl = &src->linedata[sy];
521 		dstl = &dst->linedata[dy];
522 
523 		memcpy(dstl, srcl, sizeof *dstl);
524 		if (srcl->cellsize != 0) {
525 			dstl->celldata = xcalloc(
526 			    srcl->cellsize, sizeof *dstl->celldata);
527 			memcpy(dstl->celldata, srcl->celldata,
528 			    srcl->cellsize * sizeof *dstl->celldata);
529 		}
530 		if (srcl->utf8size != 0) {
531 			dstl->utf8data = xcalloc(
532 			    srcl->utf8size, sizeof *dstl->utf8data);
533 			memcpy(dstl->utf8data, srcl->utf8data,
534 			    srcl->utf8size * sizeof *dstl->utf8data);
535 		}
536 
537 		sy++;
538 		dy++;
539 	}
540 }
541