Annotation of src/usr.bin/tmux/grid.c, Revision 1.11
1.11 ! nicm 1: /* $OpenBSD: grid.c,v 1.10 2009/08/08 13:29:27 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 { \
1.10 nicm 41: memcpy(&gd->linedata[py].celldata[px], \
42: gc, sizeof gd->linedata[py].celldata[px]); \
1.1 nicm 43: } while (0)
44: #define grid_put_utf8(gd, px, py, gc) do { \
1.10 nicm 45: memcpy(&gd->linedata[py].utf8data[px], \
46: gc, sizeof gd->linedata[py].utf8data[px]); \
1.1 nicm 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:
1.7 nicm 100: gd->flags = GRID_HISTORY;
101:
1.1 nicm 102: gd->hsize = 0;
103: gd->hlimit = hlimit;
104:
1.10 nicm 105: gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
1.1 nicm 106:
107: return (gd);
108: }
109:
110: /* Destroy grid. */
111: void
112: grid_destroy(struct grid *gd)
113: {
1.10 nicm 114: struct grid_line *gl;
115: u_int yy;
1.1 nicm 116:
117: for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
1.10 nicm 118: gl = &gd->linedata[yy];
119: if (gl->celldata != NULL)
120: xfree(gl->celldata);
121: if (gl->utf8data != NULL)
122: xfree(gl->utf8data);
1.1 nicm 123: }
124:
1.10 nicm 125: xfree(gd->linedata);
1.1 nicm 126:
127: xfree(gd);
128: }
129:
130: /* Compare grids. */
131: int
132: grid_compare(struct grid *ga, struct grid *gb)
133: {
1.10 nicm 134: struct grid_line *gla, *glb;
1.1 nicm 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++) {
1.10 nicm 143: gla = &ga->linedata[yy];
144: glb = &gb->linedata[yy];
145: if (gla->cellsize != glb->cellsize)
1.1 nicm 146: return (1);
147: for (xx = 0; xx < ga->sx; xx++) {
1.10 nicm 148: gca = &gla->celldata[xx];
149: gcb = &glb->celldata[xx];
1.1 nicm 150: if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0)
151: return (1);
152: if (!(gca->flags & GRID_FLAG_UTF8))
153: continue;
1.10 nicm 154: gua = &gla->utf8data[xx];
155: gub = &glb->utf8data[xx];
1.1 nicm 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:
1.6 nicm 172: if (gd->hsize >= gd->hlimit) {
1.1 nicm 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:
1.10 nicm 184: gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata);
185: memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]);
1.1 nicm 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: {
1.10 nicm 194: struct grid_line *gl;
195: u_int xx;
1.1 nicm 196:
1.10 nicm 197: gl = &gd->linedata[py];
198: if (sx <= gl->cellsize)
1.1 nicm 199: return;
200:
1.10 nicm 201: gl->celldata = xrealloc(gl->celldata, sx, sizeof *gl->celldata);
202: for (xx = gl->cellsize; xx < sx; xx++)
1.1 nicm 203: grid_put_cell(gd, xx, py, &grid_default_cell);
1.10 nicm 204: gl->cellsize = sx;
1.1 nicm 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: {
1.10 nicm 211: struct grid_line *gl;
212:
213: gl = &gd->linedata[py];
214: if (sx <= gl->utf8size)
1.1 nicm 215: return;
216:
1.10 nicm 217: gl->utf8data = xrealloc(gl->utf8data, sx, sizeof *gl->utf8data);
218: gl->utf8size = sx;
1.1 nicm 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:
1.10 nicm 230: if (px >= gd->linedata[py].cellsize)
1.1 nicm 231: return (&grid_default_cell);
1.10 nicm 232: return (&gd->linedata[py].celldata[px]);
1.1 nicm 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);
1.10 nicm 245: return (&gd->linedata[py].celldata[px]);
1.1 nicm 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:
1.10 nicm 271: if (px >= gd->linedata[py].utf8size)
1.1 nicm 272: return (NULL);
1.10 nicm 273: return (&gd->linedata[py].utf8data[px]);
1.1 nicm 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);
1.10 nicm 286: return (&gd->linedata[py].utf8data[px]);
1.1 nicm 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: /*
304: * Clear area. Note this is different from a fill as it just omits unallocated
305: * cells.
306: */
307: void
308: grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny)
309: {
310: u_int xx, yy;
311:
312: GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny);
313:
314: if (nx == 0 || ny == 0)
315: return;
316:
317: if (px == 0 && nx == gd->sx) {
318: grid_clear_lines(gd, py, ny);
319: return;
320: }
321:
322: if (grid_check_x(gd, px) != 0)
323: return;
324: if (grid_check_x(gd, px + nx - 1) != 0)
325: return;
326: if (grid_check_y(gd, py) != 0)
327: return;
328: if (grid_check_y(gd, py + ny - 1) != 0)
329: return;
330:
331: for (yy = py; yy < py + ny; yy++) {
332: for (xx = px; xx < px + nx; xx++) {
1.10 nicm 333: if (xx >= gd->linedata[yy].cellsize)
1.1 nicm 334: break;
335: grid_put_cell(gd, xx, yy, &grid_default_cell);
336: }
337: }
338: }
339:
340: /* Clear lines. This just frees and truncates the lines. */
341: void
342: grid_clear_lines(struct grid *gd, u_int py, u_int ny)
343: {
1.10 nicm 344: struct grid_line *gl;
345: u_int yy;
1.1 nicm 346:
347: GRID_DEBUG(gd, "py=%u, ny=%u", py, ny);
348:
349: if (ny == 0)
350: return;
351:
352: if (grid_check_y(gd, py) != 0)
353: return;
354: if (grid_check_y(gd, py + ny - 1) != 0)
355: return;
356:
357: for (yy = py; yy < py + ny; yy++) {
1.10 nicm 358: gl = &gd->linedata[yy];
359: if (gl->celldata != NULL)
360: xfree(gl->celldata);
361: if (gl->utf8data != NULL)
362: xfree(gl->utf8data);
363: memset(gl, 0, sizeof *gl);
1.1 nicm 364: }
365: }
366:
367: /* Move a group of lines. */
368: void
369: grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny)
370: {
371: u_int yy;
372:
373: GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny);
374:
375: if (ny == 0 || py == dy)
376: return;
377:
378: if (grid_check_y(gd, py) != 0)
379: return;
380: if (grid_check_y(gd, py + ny - 1) != 0)
381: return;
382: if (grid_check_y(gd, dy) != 0)
383: return;
384: if (grid_check_y(gd, dy + ny - 1) != 0)
385: return;
386:
387: /* Free any lines which are being replaced. */
388: for (yy = dy; yy < dy + ny; yy++) {
389: if (yy >= py && yy < py + ny)
390: continue;
391: grid_clear_lines(gd, yy, 1);
392: }
393:
1.10 nicm 394: memmove(
395: &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata));
1.1 nicm 396:
397: /* Wipe any lines that have been moved (without freeing them). */
398: for (yy = py; yy < py + ny; yy++) {
399: if (yy >= dy && yy < dy + ny)
400: continue;
1.10 nicm 401: memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]);
1.1 nicm 402: }
403: }
404:
405: /* Move a group of cells. */
406: void
407: grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx)
408: {
1.10 nicm 409: struct grid_line *gl;
410: u_int xx;
1.1 nicm 411:
412: GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx);
413:
414: if (nx == 0 || px == dx)
415: return;
416:
417: if (grid_check_x(gd, px) != 0)
418: return;
419: if (grid_check_x(gd, px + nx - 1) != 0)
420: return;
421: if (grid_check_x(gd, dx + nx - 1) != 0)
422: return;
423: if (grid_check_y(gd, py) != 0)
424: return;
1.10 nicm 425: gl = &gd->linedata[py];
1.1 nicm 426:
427: grid_expand_line(gd, py, px + nx);
428: grid_expand_line(gd, py, dx + nx);
1.10 nicm 429: memmove(
430: &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata);
1.1 nicm 431:
1.10 nicm 432: if (gl->utf8data != NULL) {
1.1 nicm 433: grid_expand_line_utf8(gd, py, px + nx);
434: grid_expand_line_utf8(gd, py, dx + nx);
1.10 nicm 435: memmove(&gl->utf8data[dx],
436: &gl->utf8data[px], nx * sizeof *gl->utf8data);
1.1 nicm 437: }
438:
439: /* Wipe any cells that have been moved. */
440: for (xx = px; xx < px + nx; xx++) {
441: if (xx >= dx && xx < dx + nx)
442: continue;
443: grid_put_cell(gd, xx, py, &grid_default_cell);
444: }
1.3 nicm 445: }
446:
447: /* Convert cells into a string. */
448: char *
449: grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
450: {
451: const struct grid_cell *gc;
452: const struct grid_utf8 *gu;
453: char *buf;
454: size_t len, off;
1.4 nicm 455: u_int xx, i;
1.3 nicm 456:
457: GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
458:
459: len = 128;
460: buf = xmalloc(len);
461: off = 0;
462:
463: for (xx = px; xx < px + nx; xx++) {
464: gc = grid_peek_cell(gd, xx, py);
465: if (gc->flags & GRID_FLAG_PADDING)
466: continue;
467:
468: if (gc->flags & GRID_FLAG_UTF8) {
469: while (len < off + UTF8_SIZE + 1) {
470: buf = xrealloc(buf, 2, len);
471: len *= 2;
472: }
473:
474: gu = grid_peek_utf8(gd, xx, py);
1.4 nicm 475: for (i = 0; i < UTF8_SIZE; i++) {
476: if (gu->data[i] == 0xff)
477: break;
478: buf[off++] = gu->data[i];
479: }
1.3 nicm 480: } else {
481: while (len < off + 2) {
482: buf = xrealloc(buf, 2, len);
483: len *= 2;
484: }
485:
486: buf[off++] = gc->data;
487: }
488: }
1.4 nicm 489:
490: while (off > 0 && buf[off - 1] == ' ')
491: off--;
1.3 nicm 492: buf[off] = '\0';
493: return (buf);
1.7 nicm 494: }
495:
496: /*
497: * Duplicate a set of lines between two grids. If there aren't enough lines in
498: * either source or destination, the number of lines is limited to the number
499: * available.
500: */
501: void
502: grid_duplicate_lines(
503: struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny)
504: {
1.10 nicm 505: struct grid_line *dstl, *srcl;
506: u_int yy;
1.7 nicm 507:
508: GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny);
509:
510: if (dy + ny > dst->hsize + dst->sy)
511: ny = dst->hsize + dst->sy - dy;
512: if (sy + ny > src->hsize + src->sy)
513: ny = src->hsize + src->sy - sy;
514: grid_clear_lines(dst, dy, ny);
515:
516: for (yy = 0; yy < ny; yy++) {
1.11 ! nicm 517: srcl = &src->linedata[sy];
! 518: dstl = &dst->linedata[dy];
1.10 nicm 519:
520: memcpy(dstl, srcl, sizeof *dstl);
521: if (srcl->cellsize != 0) {
522: dstl->celldata = xcalloc(
523: srcl->cellsize, sizeof *dstl->celldata);
524: memcpy(dstl->celldata, srcl->celldata,
525: srcl->cellsize * sizeof *dstl->celldata);
1.7 nicm 526: }
1.10 nicm 527: if (srcl->utf8size != 0) {
528: dstl->utf8data = xcalloc(
529: srcl->utf8size, sizeof *dstl->utf8data);
530: memcpy(dstl->utf8data, srcl->utf8data,
531: srcl->utf8size * sizeof *dstl->utf8data);
1.7 nicm 532: }
533:
1.10 nicm 534: sy++;
535: dy++;
1.7 nicm 536: }
1.1 nicm 537: }