Annotation of src/usr.bin/tmux/grid.c, Revision 1.2
1.2 ! ray 1: /* $OpenBSD: grid.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
1.1 nicm 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->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)
46:
47: int grid_check_x(struct grid *, u_int);
48: int grid_check_y(struct grid *, u_int);
49:
50: #ifdef DEBUG
51: int
52: grid_check_x(struct grid *gd, u_int px)
53: {
54: if ((px) >= (gd)->sx)
55: log_fatalx("x out of range: %u", px);
56: return (0);
57: }
58:
59: int
60: grid_check_y(struct grid *gd, u_int py)
61: {
62: if ((py) >= (gd)->hsize + (gd)->sy)
63: log_fatalx("y out of range: %u", py);
64: return (0);
65: }
66: #else
67: int
68: grid_check_x(struct grid *gd, u_int px)
69: {
70: if ((px) >= (gd)->sx) {
71: log_debug("x out of range: %u", px);
72: return (-1);
73: }
74: return (0);
75: }
76:
77: int
78: grid_check_y(struct grid *gd, u_int py)
79: {
80: if ((py) >= (gd)->hsize + (gd)->sy) {
81: log_debug("y out of range: %u", py);
82: return (-1);
83: }
84: return (0);
85: }
86: #endif
87:
88: /* Create a new grid. */
89: struct grid *
90: grid_create(u_int sx, u_int sy, u_int hlimit)
91: {
92: struct grid *gd;
93:
94: gd = xmalloc(sizeof *gd);
95: gd->sx = sx;
96: gd->sy = sy;
97:
98: gd->hsize = 0;
99: gd->hlimit = hlimit;
100:
101: gd->size = xcalloc(gd->sy, sizeof *gd->size);
102: gd->data = xcalloc(gd->sy, sizeof *gd->data);
103:
104: gd->usize = xcalloc(gd->sy, sizeof *gd->usize);
105: gd->udata = xcalloc(gd->sy, sizeof *gd->udata);
106:
107: return (gd);
108: }
109:
110: /* Destroy grid. */
111: void
112: grid_destroy(struct grid *gd)
113: {
114: u_int yy;
115:
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]);
121: }
122:
123: if (gd->udata != NULL)
124: xfree(gd->udata);
125: if (gd->usize != NULL)
126: xfree(gd->usize);
127:
128: if (gd->data != NULL)
129: xfree(gd->data);
130: if (gd->size != NULL)
131: xfree(gd->size);
132:
133: xfree(gd);
134: }
135:
136: /* Compare grids. */
137: int
138: grid_compare(struct grid *ga, struct grid *gb)
139: {
140: struct grid_cell *gca, *gcb;
141: struct grid_utf8 *gua, *gub;
142: u_int xx, yy;
143:
144: if (ga->sx != gb->sx || ga->sy != ga->sy)
145: return (1);
146:
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);
161: }
162: }
163:
164: return (0);
165: }
166:
167: /* Scroll a line into the history. */
168: void
169: grid_scroll_line(struct grid *gd)
170: {
171: u_int yy;
172:
173: GRID_DEBUG(gd, "");
174:
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;
180:
181: grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy);
182: gd->hsize -= yy;
183: }
184:
185: yy = gd->hsize + gd->sy;
186:
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;
191:
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;
196:
197: gd->hsize++;
198: }
199:
200: /* Reduce line to fit to cell. */
201: void
202: grid_reduce_line(struct grid *gd, u_int py, u_int sx)
203: {
204: if (sx < gd->size[py]) {
205: gd->data[py] = xrealloc(gd->data[py], sx, sizeof **gd->data);
206: gd->size[py] = sx;
207: }
208: if (sx < gd->usize[py]) {
209: gd->udata[py] = xrealloc(gd->udata[py], sx, sizeof **gd->udata);
210: gd->usize[py] = sx;
211: }
212: }
213:
214: /* Expand line to fit to cell. */
215: void
216: grid_expand_line(struct grid *gd, u_int py, u_int sx)
217: {
218: u_int xx;
219:
220: if (sx <= gd->size[py])
221: return;
222:
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;
227: }
228:
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)
232: {
233: if (sx <= gd->usize[py])
234: return;
235:
236: gd->udata[py] = xrealloc(gd->udata[py], sx, sizeof **gd->udata);
237: gd->usize[py] = sx;
238: }
239:
240: /* Get cell for reading. */
241: const struct grid_cell *
242: grid_peek_cell(struct grid *gd, u_int px, u_int py)
243: {
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);
248:
249: if (px >= gd->size[py])
250: return (&grid_default_cell);
251: return (&gd->data[py][px]);
252: }
253:
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)
257: {
258: if (grid_check_x(gd, px) != 0)
259: return (NULL);
260: if (grid_check_y(gd, py) != 0)
261: return (NULL);
262:
263: grid_expand_line(gd, py, px + 1);
264: return (&gd->data[py][px]);
265: }
266:
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)
271: {
272: if (grid_check_x(gd, px) != 0)
273: return;
274: if (grid_check_y(gd, py) != 0)
275: return;
276:
277: grid_expand_line(gd, py, px + 1);
278: grid_put_cell(gd, px, py, gc);
279: }
280:
281: /* Get UTF-8 for reading. */
282: const struct grid_utf8 *
283: grid_peek_utf8(struct grid *gd, u_int px, u_int py)
284: {
285: if (grid_check_x(gd, px) != 0)
286: return (NULL);
287: if (grid_check_y(gd, py) != 0)
288: return (NULL);
289:
290: if (px >= gd->usize[py])
291: return (NULL);
292: return (&gd->udata[py][px]);
293: }
294:
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)
298: {
299: if (grid_check_x(gd, px) != 0)
300: return (NULL);
301: if (grid_check_y(gd, py) != 0)
302: return (NULL);
303:
304: grid_expand_line_utf8(gd, py, px + 1);
305: return (&gd->udata[py][px]);
306: }
307:
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)
312: {
313: if (grid_check_x(gd, px) != 0)
314: return;
315: if (grid_check_y(gd, py) != 0)
316: return;
317:
318: grid_expand_line_utf8(gd, py, px + 1);
319: grid_put_utf8(gd, px, py, gc);
320: }
321:
322: /*
323: * Clear area. Note this is different from a fill as it just omits unallocated
324: * cells.
325: */
326: void
327: grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny)
328: {
329: u_int xx, yy;
330:
331: GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny);
332:
333: if (nx == 0 || ny == 0)
334: return;
335:
336: if (px == 0 && nx == gd->sx) {
337: grid_clear_lines(gd, py, ny);
338: return;
339: }
340:
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;
349:
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);
355: }
356: }
357: }
358:
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)
362: {
363: u_int yy;
364:
365: GRID_DEBUG(gd, "py=%u, ny=%u", py, ny);
366:
367: if (ny == 0)
368: return;
369:
370: if (grid_check_y(gd, py) != 0)
371: return;
372: if (grid_check_y(gd, py + ny - 1) != 0)
373: return;
374:
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;
380: }
381: if (gd->udata[yy] != NULL) {
382: xfree(gd->udata[yy]);
383: gd->udata[yy] = NULL;
384: gd->usize[yy] = 0;
385: }
386: }
387: }
388:
389: /* Move a group of lines. */
390: void
391: grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny)
392: {
393: u_int yy;
394:
395: GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny);
396:
397: if (ny == 0 || py == dy)
398: return;
399:
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;
408:
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);
414: }
415:
416: memmove(&gd->data[dy], &gd->data[py], ny * (sizeof *gd->data));
417: memmove(&gd->size[dy], &gd->size[py], ny * (sizeof *gd->size));
418:
419: memmove(&gd->udata[dy], &gd->udata[py], ny * (sizeof *gd->udata));
420: memmove(&gd->usize[dy], &gd->usize[py], ny * (sizeof *gd->usize));
421:
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;
430: }
431: }
432:
433: /* Clear a group of cells. */
434: void
435: grid_clear_cells(struct grid *gd, u_int px, u_int py, u_int nx)
436: {
437: u_int xx;
438:
439: GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
440:
441: if (nx == 0)
442: return;
443:
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;
450:
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);
455: }
456: }
457:
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)
461: {
462: u_int xx;
463:
464: GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx);
465:
466: if (nx == 0 || px == dx)
467: return;
468:
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;
477:
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));
481:
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));
487: }
488:
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);
494: }
495: }