xref: /openbsd-src/usr.bin/tmux/grid.c (revision 7bbe964f6b7d22ad07ca46292495604f942eba4e)
1 /* $OpenBSD: grid.c,v 1.15 2009/10/13 15:38:37 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 /*
165  * Collect lines from the history if at the limit. Free the top (oldest) 10%
166  * and shift up.
167  */
168 void
169 grid_collect_history(struct grid *gd)
170 {
171 	u_int	yy;
172 
173  	GRID_DEBUG(gd, "");
174 
175 	if (gd->hsize < gd->hlimit)
176 		return;
177 
178 	yy = gd->hlimit / 10;
179 	if (yy < 1)
180 		yy = 1;
181 
182 	grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy);
183 	gd->hsize -= yy;
184 }
185 
186 /*
187  * Scroll the entire visible screen, moving one line into the history. Just
188  * allocate a new line at the bottom and move the history size indicator.
189  */
190 void
191 grid_scroll_history(struct grid *gd)
192 {
193 	u_int	yy;
194 
195  	GRID_DEBUG(gd, "");
196 
197 	yy = gd->hsize + gd->sy;
198 	gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata);
199 	memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]);
200 
201 	gd->hsize++;
202 }
203 
204 /* Scroll a region up, moving the top line into the history. */
205 void
206 grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower)
207 {
208 	struct grid_line	*gl_history, *gl_upper, *gl_lower;
209 	u_int			 yy;
210 
211  	GRID_DEBUG(gd, "upper=%u, lower=%u", upper, lower);
212 
213 	/* Create a space for a new line. */
214 	yy = gd->hsize + gd->sy;
215 	gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata);
216 
217 	/* Move the entire screen down to free a space for this line. */
218 	gl_history = &gd->linedata[gd->hsize];
219 	memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);
220 
221 	/* Adjust the region and find its start and end. */
222 	upper++;
223 	gl_upper = &gd->linedata[upper];
224 	lower++;
225 	gl_lower = &gd->linedata[lower];
226 
227 	/* Move the line into the history. */
228 	memcpy(gl_history, gl_upper, sizeof *gl_history);
229 
230 	/* Then move the region up and clear the bottom line. */
231 	memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
232 	memset(gl_lower, 0, sizeof *gl_lower);
233 
234 	/* Move the history offset down over the line. */
235 	gd->hsize++;
236 }
237 
238 /* Expand line to fit to cell. */
239 void
240 grid_expand_line(struct grid *gd, u_int py, u_int sx)
241 {
242 	struct grid_line	*gl;
243 	u_int			 xx;
244 
245 	gl = &gd->linedata[py];
246 	if (sx <= gl->cellsize)
247 		return;
248 
249 	gl->celldata = xrealloc(gl->celldata, sx, sizeof *gl->celldata);
250 	for (xx = gl->cellsize; xx < sx; xx++)
251 		grid_put_cell(gd, xx, py, &grid_default_cell);
252 	gl->cellsize = sx;
253 }
254 
255 /* Expand line to fit to cell for UTF-8. */
256 void
257 grid_expand_line_utf8(struct grid *gd, u_int py, u_int sx)
258 {
259 	struct grid_line	*gl;
260 
261 	gl = &gd->linedata[py];
262 	if (sx <= gl->utf8size)
263 		return;
264 
265 	gl->utf8data = xrealloc(gl->utf8data, sx, sizeof *gl->utf8data);
266 	gl->utf8size = sx;
267 }
268 
269 /* Get cell for reading. */
270 const struct grid_cell *
271 grid_peek_cell(struct grid *gd, u_int px, u_int py)
272 {
273 	if (grid_check_x(gd, px) != 0)
274 		return (&grid_default_cell);
275 	if (grid_check_y(gd, py) != 0)
276 		return (&grid_default_cell);
277 
278 	if (px >= gd->linedata[py].cellsize)
279 		return (&grid_default_cell);
280 	return (&gd->linedata[py].celldata[px]);
281 }
282 
283 /* Get cell at relative position (for writing). */
284 struct grid_cell *
285 grid_get_cell(struct grid *gd, u_int px, u_int py)
286 {
287 	if (grid_check_x(gd, px) != 0)
288 		return (NULL);
289 	if (grid_check_y(gd, py) != 0)
290 		return (NULL);
291 
292 	grid_expand_line(gd, py, px + 1);
293 	return (&gd->linedata[py].celldata[px]);
294 }
295 
296 /* Set cell at relative position. */
297 void
298 grid_set_cell(
299     struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
300 {
301 	if (grid_check_x(gd, px) != 0)
302 		return;
303 	if (grid_check_y(gd, py) != 0)
304 		return;
305 
306 	grid_expand_line(gd, py, px + 1);
307 	grid_put_cell(gd, px, py, gc);
308 }
309 
310 /* Get UTF-8 for reading. */
311 const struct grid_utf8 *
312 grid_peek_utf8(struct grid *gd, u_int px, u_int py)
313 {
314 	if (grid_check_x(gd, px) != 0)
315 		return (NULL);
316 	if (grid_check_y(gd, py) != 0)
317 		return (NULL);
318 
319 	if (px >= gd->linedata[py].utf8size)
320 		return (NULL);
321 	return (&gd->linedata[py].utf8data[px]);
322 }
323 
324 /* Get utf8 at relative position (for writing). */
325 struct grid_utf8 *
326 grid_get_utf8(struct grid *gd, u_int px, u_int py)
327 {
328 	if (grid_check_x(gd, px) != 0)
329 		return (NULL);
330 	if (grid_check_y(gd, py) != 0)
331 		return (NULL);
332 
333 	grid_expand_line_utf8(gd, py, px + 1);
334 	return (&gd->linedata[py].utf8data[px]);
335 }
336 
337 /* Set utf8 at relative position. */
338 void
339 grid_set_utf8(
340     struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gc)
341 {
342 	if (grid_check_x(gd, px) != 0)
343 		return;
344 	if (grid_check_y(gd, py) != 0)
345 		return;
346 
347 	grid_expand_line_utf8(gd, py, px + 1);
348 	grid_put_utf8(gd, px, py, gc);
349 }
350 
351 /* Clear area. */
352 void
353 grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny)
354 {
355 	u_int	xx, yy;
356 
357  	GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny);
358 
359 	if (nx == 0 || ny == 0)
360 		return;
361 
362 	if (px == 0 && nx == gd->sx) {
363 		grid_clear_lines(gd, py, ny);
364 		return;
365 	}
366 
367 	if (grid_check_x(gd, px) != 0)
368 		return;
369 	if (grid_check_x(gd, px + nx - 1) != 0)
370 		return;
371 	if (grid_check_y(gd, py) != 0)
372 		return;
373 	if (grid_check_y(gd, py + ny - 1) != 0)
374 		return;
375 
376 	for (yy = py; yy < py + ny; yy++) {
377 		if (px >= gd->linedata[yy].cellsize)
378 			continue;
379 		if (px + nx >= gd->linedata[yy].cellsize) {
380 			gd->linedata[yy].cellsize = px;
381 			continue;
382 		}
383 		for (xx = px; xx < px + nx; xx++) {
384 			if (xx >= gd->linedata[yy].cellsize)
385 				break;
386 			grid_put_cell(gd, xx, yy, &grid_default_cell);
387 		}
388 	}
389 }
390 
391 /* Clear lines. This just frees and truncates the lines. */
392 void
393 grid_clear_lines(struct grid *gd, u_int py, u_int ny)
394 {
395 	struct grid_line	*gl;
396 	u_int			 yy;
397 
398  	GRID_DEBUG(gd, "py=%u, ny=%u", py, ny);
399 
400 	if (ny == 0)
401 		return;
402 
403 	if (grid_check_y(gd, py) != 0)
404 		return;
405 	if (grid_check_y(gd, py + ny - 1) != 0)
406 		return;
407 
408 	for (yy = py; yy < py + ny; yy++) {
409 		gl = &gd->linedata[yy];
410 		if (gl->celldata != NULL)
411 			xfree(gl->celldata);
412 		if (gl->utf8data != NULL)
413 			xfree(gl->utf8data);
414 		memset(gl, 0, sizeof *gl);
415 	}
416 }
417 
418 /* Move a group of lines. */
419 void
420 grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny)
421 {
422 	u_int	yy;
423 
424  	GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny);
425 
426 	if (ny == 0 || py == dy)
427 		return;
428 
429 	if (grid_check_y(gd, py) != 0)
430 		return;
431 	if (grid_check_y(gd, py + ny - 1) != 0)
432 		return;
433 	if (grid_check_y(gd, dy) != 0)
434 		return;
435 	if (grid_check_y(gd, dy + ny - 1) != 0)
436 		return;
437 
438 	/* Free any lines which are being replaced. */
439 	for (yy = dy; yy < dy + ny; yy++) {
440 		if (yy >= py && yy < py + ny)
441 			continue;
442 		grid_clear_lines(gd, yy, 1);
443 	}
444 
445 	memmove(
446 	    &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata));
447 
448 	/* Wipe any lines that have been moved (without freeing them). */
449 	for (yy = py; yy < py + ny; yy++) {
450 		if (yy >= dy && yy < dy + ny)
451 			continue;
452 		memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]);
453 	}
454 }
455 
456 /* Move a group of cells. */
457 void
458 grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx)
459 {
460 	struct grid_line	*gl;
461 	u_int			 xx;
462 
463  	GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx);
464 
465 	if (nx == 0 || px == dx)
466 		return;
467 
468 	if (grid_check_x(gd, px) != 0)
469 		return;
470 	if (grid_check_x(gd, px + nx - 1) != 0)
471 		return;
472 	if (grid_check_x(gd, dx + nx - 1) != 0)
473 		return;
474 	if (grid_check_y(gd, py) != 0)
475 		return;
476 	gl = &gd->linedata[py];
477 
478 	grid_expand_line(gd, py, px + nx);
479 	grid_expand_line(gd, py, dx + nx);
480 	memmove(
481 	    &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata);
482 
483 	if (gl->utf8data != NULL) {
484 		grid_expand_line_utf8(gd, py, px + nx);
485 		grid_expand_line_utf8(gd, py, dx + nx);
486 		memmove(&gl->utf8data[dx],
487 		    &gl->utf8data[px], nx * sizeof *gl->utf8data);
488 	}
489 
490 	/* Wipe any cells that have been moved. */
491 	for (xx = px; xx < px + nx; xx++) {
492 		if (xx >= dx && xx < dx + nx)
493 			continue;
494 		grid_put_cell(gd, xx, py, &grid_default_cell);
495 	}
496 }
497 
498 /* Convert cells into a string. */
499 char *
500 grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
501 {
502  	const struct grid_cell	*gc;
503  	const struct grid_utf8	*gu;
504 	char			*buf;
505 	size_t			 len, off;
506 	u_int			 xx, i;
507 
508 	GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
509 
510 	len = 128;
511 	buf = xmalloc(len);
512 	off = 0;
513 
514 	for (xx = px; xx < px + nx; xx++) {
515 		gc = grid_peek_cell(gd, xx, py);
516 		if (gc->flags & GRID_FLAG_PADDING)
517 			continue;
518 
519 		if (gc->flags & GRID_FLAG_UTF8) {
520 			while (len < off + UTF8_SIZE + 1) {
521 				buf = xrealloc(buf, 2, len);
522 				len *= 2;
523 			}
524 
525 			gu = grid_peek_utf8(gd, xx, py);
526 			for (i = 0; i < UTF8_SIZE; i++) {
527 				if (gu->data[i] == 0xff)
528 					break;
529 				buf[off++] = gu->data[i];
530 			}
531 		} else {
532 			while (len < off + 2) {
533 				buf = xrealloc(buf, 2, len);
534 				len *= 2;
535 			}
536 
537 			buf[off++] = gc->data;
538 		}
539 	}
540 
541 	while (off > 0 && buf[off - 1] == ' ')
542 		off--;
543 	buf[off] = '\0';
544 	return (buf);
545 }
546 
547 /*
548  * Duplicate a set of lines between two grids. If there aren't enough lines in
549  * either source or destination, the number of lines is limited to the number
550  * available.
551  */
552 void
553 grid_duplicate_lines(
554     struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny)
555 {
556 	struct grid_line	*dstl, *srcl;
557 	u_int			 yy;
558 
559 	GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny);
560 
561 	if (dy + ny > dst->hsize + dst->sy)
562 		ny = dst->hsize + dst->sy - dy;
563 	if (sy + ny > src->hsize + src->sy)
564 		ny = src->hsize + src->sy - sy;
565 	grid_clear_lines(dst, dy, ny);
566 
567 	for (yy = 0; yy < ny; yy++) {
568 		srcl = &src->linedata[sy];
569 		dstl = &dst->linedata[dy];
570 
571 		memcpy(dstl, srcl, sizeof *dstl);
572 		if (srcl->cellsize != 0) {
573 			dstl->celldata = xcalloc(
574 			    srcl->cellsize, sizeof *dstl->celldata);
575 			memcpy(dstl->celldata, srcl->celldata,
576 			    srcl->cellsize * sizeof *dstl->celldata);
577 		}
578 		if (srcl->utf8size != 0) {
579 			dstl->utf8data = xcalloc(
580 			    srcl->utf8size, sizeof *dstl->utf8data);
581 			memcpy(dstl->utf8data, srcl->utf8data,
582 			    srcl->utf8size * sizeof *dstl->utf8data);
583 		}
584 
585 		sy++;
586 		dy++;
587 	}
588 }
589