This commit was manufactured by cvs2svn to create tag 'TMUX_0_8'.
[tmux.git] / grid.c
bloba115d7443c4440c0bf372cbd0ca5da0a6f3fa70e
1 /* $Id: grid.c,v 1.15 2009-03-30 19:44:55 nicm Exp $ */
3 /*
4 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
21 #include <string.h>
23 #include "tmux.h"
26 * Grid data. This is the basic data structure that represents what is shown on
27 * screen.
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.
37 /* Default grid cell data. */
38 const struct grid_cell grid_default_cell = { 0, 0, 8, 8, ' ' };
40 #define grid_put_cell(gd, px, py, gc) do { \
41 memcpy(&gd->data[py][px], gc, sizeof gd->data[py][px]); \
42 } while (0)
43 #define grid_put_utf8(gd, px, py, gc) do { \
44 memcpy(&gd->udata[py][px], gc, sizeof gd->udata[py][px]); \
45 } while (0)
47 int grid_check_x(struct grid *, u_int);
48 int grid_check_y(struct grid *, u_int);
50 #ifdef DEBUG
51 int
52 grid_check_x(struct grid *gd, u_int px)
54 if ((px) >= (gd)->sx)
55 log_fatalx("x out of range: %u", px);
56 return (0);
59 int
60 grid_check_y(struct grid *gd, u_int py)
62 if ((py) >= (gd)->hsize + (gd)->sy)
63 log_fatalx("y out of range: %u", py);
64 return (0);
66 #else
67 int
68 grid_check_x(struct grid *gd, u_int px)
70 if ((px) >= (gd)->sx) {
71 log_debug("x out of range: %u", px);
72 return (-1);
74 return (0);
77 int
78 grid_check_y(struct grid *gd, u_int py)
80 if ((py) >= (gd)->hsize + (gd)->sy) {
81 log_debug("y out of range: %u", py);
82 return (-1);
84 return (0);
86 #endif
88 /* Create a new grid. */
89 struct grid *
90 grid_create(u_int sx, u_int sy, u_int hlimit)
92 struct grid *gd;
94 gd = xmalloc(sizeof *gd);
95 gd->sx = sx;
96 gd->sy = sy;
98 gd->hsize = 0;
99 gd->hlimit = hlimit;
101 gd->size = xcalloc(gd->sy, sizeof *gd->size);
102 gd->data = xcalloc(gd->sy, sizeof *gd->data);
104 gd->usize = xcalloc(gd->sy, sizeof *gd->usize);
105 gd->udata = xcalloc(gd->sy, sizeof *gd->udata);
107 return (gd);
110 /* Destroy grid. */
111 void
112 grid_destroy(struct grid *gd)
114 u_int yy;
116 for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
117 if (gd->udata[yy] != NULL)
118 xfree(gd->udata[yy]);
119 if (gd->data[yy] != NULL)
120 xfree(gd->data[yy]);
123 if (gd->udata != NULL)
124 xfree(gd->udata);
125 if (gd->usize != NULL)
126 xfree(gd->usize);
128 if (gd->data != NULL)
129 xfree(gd->data);
130 if (gd->size != NULL)
131 xfree(gd->size);
133 xfree(gd);
136 /* Compare grids. */
138 grid_compare(struct grid *ga, struct grid *gb)
140 struct grid_cell *gca, *gcb;
141 struct grid_utf8 *gua, *gub;
142 u_int xx, yy;
144 if (ga->sx != gb->sx || ga->sy != ga->sy)
145 return (1);
147 for (yy = 0; yy < ga->sy; yy++) {
148 if (ga->size[yy] != gb->size[yy])
149 return (1);
150 for (xx = 0; xx < ga->sx; xx++) {
151 gca = &ga->data[yy][xx];
152 gcb = &gb->data[yy][xx];
153 if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0)
154 return (1);
155 if (!(gca->flags & GRID_FLAG_UTF8))
156 continue;
157 gua = &ga->udata[yy][xx];
158 gub = &gb->udata[yy][xx];
159 if (memcmp(gua, gub, sizeof (struct grid_utf8)) != 0)
160 return (1);
164 return (0);
167 /* Scroll a line into the history. */
168 void
169 grid_scroll_line(struct grid *gd)
171 u_int yy;
173 GRID_DEBUG(gd, "");
175 if (gd->hsize >= gd->hlimit - 1) {
176 /* If the limit is hit, free the bottom 10% and shift up. */
177 yy = gd->hlimit / 10;
178 if (yy < 1)
179 yy = 1;
181 grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy);
182 gd->hsize -= yy;
185 yy = gd->hsize + gd->sy;
187 gd->size = xrealloc(gd->size, yy + 1, sizeof *gd->size);
188 gd->size[yy] = 0;
189 gd->data = xrealloc(gd->data, yy + 1, sizeof *gd->data);
190 gd->data[yy] = NULL;
192 gd->usize = xrealloc(gd->usize, yy + 1, sizeof *gd->usize);
193 gd->usize[yy] = 0;
194 gd->udata = xrealloc(gd->udata, yy + 1, sizeof *gd->udata);
195 gd->udata[yy] = NULL;
197 gd->hsize++;
200 /* Reduce line to fit to cell. */
201 void
202 grid_reduce_line(struct grid *gd, u_int py, u_int sx)
204 if (sx < gd->size[py]) {
205 gd->data[py] = xrealloc(gd->data[py], sx, sizeof **gd->data);
206 gd->size[py] = sx;
208 if (sx < gd->usize[py]) {
209 gd->udata[py] = xrealloc(gd->udata[py], sx, sizeof **gd->udata);
210 gd->usize[py] = sx;
214 /* Expand line to fit to cell. */
215 void
216 grid_expand_line(struct grid *gd, u_int py, u_int sx)
218 u_int xx;
220 if (sx <= gd->size[py])
221 return;
223 gd->data[py] = xrealloc(gd->data[py], sx, sizeof **gd->data);
224 for (xx = gd->size[py]; xx < sx; xx++)
225 grid_put_cell(gd, xx, py, &grid_default_cell);
226 gd->size[py] = sx;
229 /* Expand line to fit to cell for UTF-8. */
230 void
231 grid_expand_line_utf8(struct grid *gd, u_int py, u_int sx)
233 if (sx <= gd->usize[py])
234 return;
236 gd->udata[py] = xrealloc(gd->udata[py], sx, sizeof **gd->udata);
237 gd->usize[py] = sx;
240 /* Get cell for reading. */
241 const struct grid_cell *
242 grid_peek_cell(struct grid *gd, u_int px, u_int py)
244 if (grid_check_x(gd, px) != 0)
245 return (&grid_default_cell);
246 if (grid_check_y(gd, py) != 0)
247 return (&grid_default_cell);
249 if (px >= gd->size[py])
250 return (&grid_default_cell);
251 return (&gd->data[py][px]);
254 /* Get cell at relative position (for writing). */
255 struct grid_cell *
256 grid_get_cell(struct grid *gd, u_int px, u_int py)
258 if (grid_check_x(gd, px) != 0)
259 return (NULL);
260 if (grid_check_y(gd, py) != 0)
261 return (NULL);
263 grid_expand_line(gd, py, px + 1);
264 return (&gd->data[py][px]);
267 /* Set cell at relative position. */
268 void
269 grid_set_cell(
270 struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
272 if (grid_check_x(gd, px) != 0)
273 return;
274 if (grid_check_y(gd, py) != 0)
275 return;
277 grid_expand_line(gd, py, px + 1);
278 grid_put_cell(gd, px, py, gc);
281 /* Get UTF-8 for reading. */
282 const struct grid_utf8 *
283 grid_peek_utf8(struct grid *gd, u_int px, u_int py)
285 if (grid_check_x(gd, px) != 0)
286 return (NULL);
287 if (grid_check_y(gd, py) != 0)
288 return (NULL);
290 if (px >= gd->usize[py])
291 return (NULL);
292 return (&gd->udata[py][px]);
295 /* Get utf8 at relative position (for writing). */
296 struct grid_utf8 *
297 grid_get_utf8(struct grid *gd, u_int px, u_int py)
299 if (grid_check_x(gd, px) != 0)
300 return (NULL);
301 if (grid_check_y(gd, py) != 0)
302 return (NULL);
304 grid_expand_line_utf8(gd, py, px + 1);
305 return (&gd->udata[py][px]);
308 /* Set utf8 at relative position. */
309 void
310 grid_set_utf8(
311 struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gc)
313 if (grid_check_x(gd, px) != 0)
314 return;
315 if (grid_check_y(gd, py) != 0)
316 return;
318 grid_expand_line_utf8(gd, py, px + 1);
319 grid_put_utf8(gd, px, py, gc);
323 * Clear area. Note this is different from a fill as it just omits unallocated
324 * cells.
326 void
327 grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny)
329 u_int xx, yy;
331 GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny);
333 if (nx == 0 || ny == 0)
334 return;
336 if (px == 0 && nx == gd->sx) {
337 grid_clear_lines(gd, py, ny);
338 return;
341 if (grid_check_x(gd, px) != 0)
342 return;
343 if (grid_check_x(gd, px + nx - 1) != 0)
344 return;
345 if (grid_check_y(gd, py) != 0)
346 return;
347 if (grid_check_y(gd, py + ny - 1) != 0)
348 return;
350 for (yy = py; yy < py + ny; yy++) {
351 for (xx = px; xx < px + nx; xx++) {
352 if (xx >= gd->size[yy])
353 break;
354 grid_put_cell(gd, xx, yy, &grid_default_cell);
359 /* Clear lines. This just frees and truncates the lines. */
360 void
361 grid_clear_lines(struct grid *gd, u_int py, u_int ny)
363 u_int yy;
365 GRID_DEBUG(gd, "py=%u, ny=%u", py, ny);
367 if (ny == 0)
368 return;
370 if (grid_check_y(gd, py) != 0)
371 return;
372 if (grid_check_y(gd, py + ny - 1) != 0)
373 return;
375 for (yy = py; yy < py + ny; yy++) {
376 if (gd->data[yy] != NULL) {
377 xfree(gd->data[yy]);
378 gd->data[yy] = NULL;
379 gd->size[yy] = 0;
381 if (gd->udata[yy] != NULL) {
382 xfree(gd->udata[yy]);
383 gd->udata[yy] = NULL;
384 gd->usize[yy] = 0;
389 /* Move a group of lines. */
390 void
391 grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny)
393 u_int yy;
395 GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny);
397 if (ny == 0 || py == dy)
398 return;
400 if (grid_check_y(gd, py) != 0)
401 return;
402 if (grid_check_y(gd, py + ny - 1) != 0)
403 return;
404 if (grid_check_y(gd, dy) != 0)
405 return;
406 if (grid_check_y(gd, dy + ny - 1) != 0)
407 return;
409 /* Free any lines which are being replaced. */
410 for (yy = dy; yy < dy + ny; yy++) {
411 if (yy >= py && yy < py + ny)
412 continue;
413 grid_clear_lines(gd, yy, 1);
416 memmove(&gd->data[dy], &gd->data[py], ny * (sizeof *gd->data));
417 memmove(&gd->size[dy], &gd->size[py], ny * (sizeof *gd->size));
419 memmove(&gd->udata[dy], &gd->udata[py], ny * (sizeof *gd->udata));
420 memmove(&gd->usize[dy], &gd->usize[py], ny * (sizeof *gd->usize));
422 /* Wipe any lines that have been moved (without freeing them). */
423 for (yy = py; yy < py + ny; yy++) {
424 if (yy >= dy && yy < dy + ny)
425 continue;
426 gd->data[yy] = NULL;
427 gd->size[yy] = 0;
428 gd->udata[yy] = NULL;
429 gd->usize[yy] = 0;
433 /* Clear a group of cells. */
434 void
435 grid_clear_cells(struct grid *gd, u_int px, u_int py, u_int nx)
437 u_int xx;
439 GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
441 if (nx == 0)
442 return;
444 if (grid_check_x(gd, px) != 0)
445 return;
446 if (grid_check_x(gd, px + nx - 1) != 0)
447 return;
448 if (grid_check_y(gd, py) != 0)
449 return;
451 for (xx = px; xx < px + nx; xx++) {
452 if (xx >= gd->size[py])
453 break;
454 grid_put_cell(gd, xx, py, &grid_default_cell);
458 /* Move a group of cells. */
459 void
460 grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx)
462 u_int xx;
464 GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx);
466 if (nx == 0 || px == dx)
467 return;
469 if (grid_check_x(gd, px) != 0)
470 return;
471 if (grid_check_x(gd, px + nx - 1) != 0)
472 return;
473 if (grid_check_x(gd, dx + nx - 1) != 0)
474 return;
475 if (grid_check_y(gd, py) != 0)
476 return;
478 grid_expand_line(gd, py, px + nx);
479 grid_expand_line(gd, py, dx + nx);
480 memmove(&gd->data[py][dx], &gd->data[py][px], nx * (sizeof **gd->data));
482 if (gd->udata[py] != NULL) {
483 grid_expand_line_utf8(gd, py, px + nx);
484 grid_expand_line_utf8(gd, py, dx + nx);
485 memmove(&gd->udata[py][dx],
486 &gd->udata[py][px], nx * (sizeof **gd->udata));
489 /* Wipe any cells that have been moved. */
490 for (xx = px; xx < px + nx; xx++) {
491 if (xx >= dx && xx < dx + nx)
492 continue;
493 grid_put_cell(gd, xx, py, &grid_default_cell);