Annotation of src/usr.bin/tmux/grid.c, Revision 1.7
1.7 ! nicm 1: /* $OpenBSD: grid.c,v 1.6 2009/07/11 20:11:18 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:
1.7 ! nicm 98: gd->flags = GRID_HISTORY;
! 99:
1.1 nicm 100: gd->hsize = 0;
101: gd->hlimit = hlimit;
102:
103: gd->size = xcalloc(gd->sy, sizeof *gd->size);
104: gd->data = xcalloc(gd->sy, sizeof *gd->data);
105:
106: gd->usize = xcalloc(gd->sy, sizeof *gd->usize);
107: gd->udata = xcalloc(gd->sy, sizeof *gd->udata);
108:
109: return (gd);
110: }
111:
112: /* Destroy grid. */
113: void
114: grid_destroy(struct grid *gd)
115: {
116: u_int yy;
117:
118: for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
119: if (gd->udata[yy] != NULL)
120: xfree(gd->udata[yy]);
121: if (gd->data[yy] != NULL)
122: xfree(gd->data[yy]);
123: }
124:
125: if (gd->udata != NULL)
126: xfree(gd->udata);
127: if (gd->usize != NULL)
128: xfree(gd->usize);
129:
130: if (gd->data != NULL)
131: xfree(gd->data);
132: if (gd->size != NULL)
133: xfree(gd->size);
134:
135: xfree(gd);
136: }
137:
138: /* Compare grids. */
139: int
140: grid_compare(struct grid *ga, struct grid *gb)
141: {
142: struct grid_cell *gca, *gcb;
143: struct grid_utf8 *gua, *gub;
144: u_int xx, yy;
145:
146: if (ga->sx != gb->sx || ga->sy != ga->sy)
147: return (1);
148:
149: for (yy = 0; yy < ga->sy; yy++) {
150: if (ga->size[yy] != gb->size[yy])
151: return (1);
152: for (xx = 0; xx < ga->sx; xx++) {
153: gca = &ga->data[yy][xx];
154: gcb = &gb->data[yy][xx];
155: if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0)
156: return (1);
157: if (!(gca->flags & GRID_FLAG_UTF8))
158: continue;
159: gua = &ga->udata[yy][xx];
160: gub = &gb->udata[yy][xx];
161: if (memcmp(gua, gub, sizeof (struct grid_utf8)) != 0)
162: return (1);
163: }
164: }
165:
166: return (0);
167: }
168:
169: /* Scroll a line into the history. */
170: void
171: grid_scroll_line(struct grid *gd)
172: {
173: u_int yy;
174:
175: GRID_DEBUG(gd, "");
176:
1.6 nicm 177: if (gd->hsize >= gd->hlimit) {
1.1 nicm 178: /* If the limit is hit, free the bottom 10% and shift up. */
179: yy = gd->hlimit / 10;
180: if (yy < 1)
181: yy = 1;
182:
183: grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy);
184: gd->hsize -= yy;
185: }
186:
187: yy = gd->hsize + gd->sy;
188:
189: gd->size = xrealloc(gd->size, yy + 1, sizeof *gd->size);
190: gd->size[yy] = 0;
191: gd->data = xrealloc(gd->data, yy + 1, sizeof *gd->data);
192: gd->data[yy] = NULL;
193:
194: gd->usize = xrealloc(gd->usize, yy + 1, sizeof *gd->usize);
195: gd->usize[yy] = 0;
196: gd->udata = xrealloc(gd->udata, yy + 1, sizeof *gd->udata);
197: gd->udata[yy] = NULL;
198:
199: gd->hsize++;
200: }
201:
202: /* Reduce line to fit to cell. */
203: void
204: grid_reduce_line(struct grid *gd, u_int py, u_int sx)
205: {
206: if (sx < gd->size[py]) {
207: gd->data[py] = xrealloc(gd->data[py], sx, sizeof **gd->data);
208: gd->size[py] = sx;
209: }
210: if (sx < gd->usize[py]) {
211: gd->udata[py] = xrealloc(gd->udata[py], sx, sizeof **gd->udata);
212: gd->usize[py] = sx;
213: }
214: }
215:
216: /* Expand line to fit to cell. */
217: void
218: grid_expand_line(struct grid *gd, u_int py, u_int sx)
219: {
220: u_int xx;
221:
222: if (sx <= gd->size[py])
223: return;
224:
225: gd->data[py] = xrealloc(gd->data[py], sx, sizeof **gd->data);
226: for (xx = gd->size[py]; xx < sx; xx++)
227: grid_put_cell(gd, xx, py, &grid_default_cell);
228: gd->size[py] = sx;
229: }
230:
231: /* Expand line to fit to cell for UTF-8. */
232: void
233: grid_expand_line_utf8(struct grid *gd, u_int py, u_int sx)
234: {
235: if (sx <= gd->usize[py])
236: return;
237:
238: gd->udata[py] = xrealloc(gd->udata[py], sx, sizeof **gd->udata);
239: gd->usize[py] = sx;
240: }
241:
242: /* Get cell for reading. */
243: const struct grid_cell *
244: grid_peek_cell(struct grid *gd, u_int px, u_int py)
245: {
246: if (grid_check_x(gd, px) != 0)
247: return (&grid_default_cell);
248: if (grid_check_y(gd, py) != 0)
249: return (&grid_default_cell);
250:
251: if (px >= gd->size[py])
252: return (&grid_default_cell);
253: return (&gd->data[py][px]);
254: }
255:
256: /* Get cell at relative position (for writing). */
257: struct grid_cell *
258: grid_get_cell(struct grid *gd, u_int px, u_int py)
259: {
260: if (grid_check_x(gd, px) != 0)
261: return (NULL);
262: if (grid_check_y(gd, py) != 0)
263: return (NULL);
264:
265: grid_expand_line(gd, py, px + 1);
266: return (&gd->data[py][px]);
267: }
268:
269: /* Set cell at relative position. */
270: void
271: grid_set_cell(
272: struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
273: {
274: if (grid_check_x(gd, px) != 0)
275: return;
276: if (grid_check_y(gd, py) != 0)
277: return;
278:
279: grid_expand_line(gd, py, px + 1);
280: grid_put_cell(gd, px, py, gc);
281: }
282:
283: /* Get UTF-8 for reading. */
284: const struct grid_utf8 *
285: grid_peek_utf8(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: if (px >= gd->usize[py])
293: return (NULL);
294: return (&gd->udata[py][px]);
295: }
296:
297: /* Get utf8 at relative position (for writing). */
298: struct grid_utf8 *
299: grid_get_utf8(struct grid *gd, u_int px, u_int py)
300: {
301: if (grid_check_x(gd, px) != 0)
302: return (NULL);
303: if (grid_check_y(gd, py) != 0)
304: return (NULL);
305:
306: grid_expand_line_utf8(gd, py, px + 1);
307: return (&gd->udata[py][px]);
308: }
309:
310: /* Set utf8 at relative position. */
311: void
312: grid_set_utf8(
313: struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gc)
314: {
315: if (grid_check_x(gd, px) != 0)
316: return;
317: if (grid_check_y(gd, py) != 0)
318: return;
319:
320: grid_expand_line_utf8(gd, py, px + 1);
321: grid_put_utf8(gd, px, py, gc);
322: }
323:
324: /*
325: * Clear area. Note this is different from a fill as it just omits unallocated
326: * cells.
327: */
328: void
329: grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny)
330: {
331: u_int xx, yy;
332:
333: GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny);
334:
335: if (nx == 0 || ny == 0)
336: return;
337:
338: if (px == 0 && nx == gd->sx) {
339: grid_clear_lines(gd, py, ny);
340: return;
341: }
342:
343: if (grid_check_x(gd, px) != 0)
344: return;
345: if (grid_check_x(gd, px + nx - 1) != 0)
346: return;
347: if (grid_check_y(gd, py) != 0)
348: return;
349: if (grid_check_y(gd, py + ny - 1) != 0)
350: return;
351:
352: for (yy = py; yy < py + ny; yy++) {
353: for (xx = px; xx < px + nx; xx++) {
354: if (xx >= gd->size[yy])
355: break;
356: grid_put_cell(gd, xx, yy, &grid_default_cell);
357: }
358: }
359: }
360:
361: /* Clear lines. This just frees and truncates the lines. */
362: void
363: grid_clear_lines(struct grid *gd, u_int py, u_int ny)
364: {
365: u_int yy;
366:
367: GRID_DEBUG(gd, "py=%u, ny=%u", py, ny);
368:
369: if (ny == 0)
370: return;
371:
372: if (grid_check_y(gd, py) != 0)
373: return;
374: if (grid_check_y(gd, py + ny - 1) != 0)
375: return;
376:
377: for (yy = py; yy < py + ny; yy++) {
378: if (gd->data[yy] != NULL) {
379: xfree(gd->data[yy]);
380: gd->data[yy] = NULL;
381: gd->size[yy] = 0;
382: }
383: if (gd->udata[yy] != NULL) {
384: xfree(gd->udata[yy]);
385: gd->udata[yy] = NULL;
386: gd->usize[yy] = 0;
387: }
388: }
389: }
390:
391: /* Move a group of lines. */
392: void
393: grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny)
394: {
395: u_int yy;
396:
397: GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny);
398:
399: if (ny == 0 || py == dy)
400: return;
401:
402: if (grid_check_y(gd, py) != 0)
403: return;
404: if (grid_check_y(gd, py + ny - 1) != 0)
405: return;
406: if (grid_check_y(gd, dy) != 0)
407: return;
408: if (grid_check_y(gd, dy + ny - 1) != 0)
409: return;
410:
411: /* Free any lines which are being replaced. */
412: for (yy = dy; yy < dy + ny; yy++) {
413: if (yy >= py && yy < py + ny)
414: continue;
415: grid_clear_lines(gd, yy, 1);
416: }
417:
418: memmove(&gd->data[dy], &gd->data[py], ny * (sizeof *gd->data));
419: memmove(&gd->size[dy], &gd->size[py], ny * (sizeof *gd->size));
420:
421: memmove(&gd->udata[dy], &gd->udata[py], ny * (sizeof *gd->udata));
422: memmove(&gd->usize[dy], &gd->usize[py], ny * (sizeof *gd->usize));
423:
424: /* Wipe any lines that have been moved (without freeing them). */
425: for (yy = py; yy < py + ny; yy++) {
426: if (yy >= dy && yy < dy + ny)
427: continue;
428: gd->data[yy] = NULL;
429: gd->size[yy] = 0;
430: gd->udata[yy] = NULL;
431: gd->usize[yy] = 0;
432: }
433: }
434:
435: /* Move a group of cells. */
436: void
437: grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx)
438: {
439: u_int xx;
440:
441: GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx);
442:
443: if (nx == 0 || px == dx)
444: return;
445:
446: if (grid_check_x(gd, px) != 0)
447: return;
448: if (grid_check_x(gd, px + nx - 1) != 0)
449: return;
450: if (grid_check_x(gd, dx + nx - 1) != 0)
451: return;
452: if (grid_check_y(gd, py) != 0)
453: return;
454:
455: grid_expand_line(gd, py, px + nx);
456: grid_expand_line(gd, py, dx + nx);
457: memmove(&gd->data[py][dx], &gd->data[py][px], nx * (sizeof **gd->data));
458:
459: if (gd->udata[py] != NULL) {
460: grid_expand_line_utf8(gd, py, px + nx);
461: grid_expand_line_utf8(gd, py, dx + nx);
462: memmove(&gd->udata[py][dx],
463: &gd->udata[py][px], nx * (sizeof **gd->udata));
464: }
465:
466: /* Wipe any cells that have been moved. */
467: for (xx = px; xx < px + nx; xx++) {
468: if (xx >= dx && xx < dx + nx)
469: continue;
470: grid_put_cell(gd, xx, py, &grid_default_cell);
471: }
1.3 nicm 472: }
473:
474: /* Convert cells into a string. */
475: char *
476: grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
477: {
478: const struct grid_cell *gc;
479: const struct grid_utf8 *gu;
480: char *buf;
481: size_t len, off;
1.4 nicm 482: u_int xx, i;
1.3 nicm 483:
484: GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
485:
486: len = 128;
487: buf = xmalloc(len);
488: off = 0;
489:
490: for (xx = px; xx < px + nx; xx++) {
491: gc = grid_peek_cell(gd, xx, py);
492: if (gc->flags & GRID_FLAG_PADDING)
493: continue;
494:
495: if (gc->flags & GRID_FLAG_UTF8) {
496: while (len < off + UTF8_SIZE + 1) {
497: buf = xrealloc(buf, 2, len);
498: len *= 2;
499: }
500:
501: gu = grid_peek_utf8(gd, xx, py);
1.4 nicm 502: for (i = 0; i < UTF8_SIZE; i++) {
503: if (gu->data[i] == 0xff)
504: break;
505: buf[off++] = gu->data[i];
506: }
1.3 nicm 507: } else {
508: while (len < off + 2) {
509: buf = xrealloc(buf, 2, len);
510: len *= 2;
511: }
512:
513: buf[off++] = gc->data;
514: }
515: }
1.4 nicm 516:
517: while (off > 0 && buf[off - 1] == ' ')
518: off--;
1.3 nicm 519: buf[off] = '\0';
520: return (buf);
1.7 ! nicm 521: }
! 522:
! 523: /*
! 524: * Duplicate a set of lines between two grids. If there aren't enough lines in
! 525: * either source or destination, the number of lines is limited to the number
! 526: * available.
! 527: */
! 528: void
! 529: grid_duplicate_lines(
! 530: struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny)
! 531: {
! 532: u_int yy;
! 533:
! 534: GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny);
! 535:
! 536: if (dy + ny > dst->hsize + dst->sy)
! 537: ny = dst->hsize + dst->sy - dy;
! 538: if (sy + ny > src->hsize + src->sy)
! 539: ny = src->hsize + src->sy - sy;
! 540: grid_clear_lines(dst, dy, ny);
! 541:
! 542: for (yy = 0; yy < ny; yy++) {
! 543: dst->size[dy] = src->size[sy];
! 544: if (src->size[sy] == 0)
! 545: dst->data[dy] = NULL;
! 546: else {
! 547: dst->data[dy] = xcalloc(
! 548: src->size[sy], sizeof **dst->data);
! 549: memcpy(dst->data[dy], src->data[sy],
! 550: src->size[sy] * (sizeof **dst->data));
! 551: }
! 552:
! 553: dst->usize[dy] = src->usize[sy];
! 554: if (src->usize[sy] == 0)
! 555: dst->udata[dy] = NULL;
! 556: else {
! 557: dst->udata[sy] = xcalloc(
! 558: src->usize[sy], sizeof **dst->udata);
! 559: memcpy(dst->udata[dy], src->udata[sy],
! 560: src->usize[sy] * (sizeof **dst->udata));
! 561: }
! 562:
! 563: sy++; dy++;
! 564: }
1.1 nicm 565: }