Annotation of src/usr.bin/tmux/grid.c, Revision 1.21
1.21 ! nicm 1: /* $OpenBSD: grid.c,v 1.20 2012/07/10 11:53:01 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:
1.20 nicm 21: #include <stdlib.h>
1.1 nicm 22: #include <string.h>
23:
24: #include "tmux.h"
25:
26: /*
27: * Grid data. This is the basic data structure that represents what is shown on
28: * screen.
29: *
30: * A grid is a grid of cells (struct grid_cell). Lines are not allocated until
31: * cells in that line are written to. The grid is split into history and
32: * viewable data with the history starting at row (line) 0 and extending to
33: * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All
34: * functions in this file work on absolute coordinates, grid-view.c has
35: * functions which work on the screen data.
36: */
37:
38: /* Default grid cell data. */
1.21 ! nicm 39: const struct grid_cell grid_default_cell = { 0, 0, 8, 8, (1 << 4) | 1, " " };
! 40: const struct grid_cell grid_marker_cell = { 0, 0, 8, 8, (1 << 4) | 1, "_" };
1.1 nicm 41:
42: #define grid_put_cell(gd, px, py, gc) do { \
1.10 nicm 43: memcpy(&gd->linedata[py].celldata[px], \
44: gc, sizeof gd->linedata[py].celldata[px]); \
1.1 nicm 45: } while (0)
46: #define grid_put_utf8(gd, px, py, gc) do { \
1.10 nicm 47: memcpy(&gd->linedata[py].utf8data[px], \
48: gc, sizeof gd->linedata[py].utf8data[px]); \
1.1 nicm 49: } while (0)
50:
51: int grid_check_y(struct grid *, u_int);
52:
53: #ifdef DEBUG
54: int
55: grid_check_y(struct grid *gd, u_int py)
56: {
57: if ((py) >= (gd)->hsize + (gd)->sy)
58: log_fatalx("y out of range: %u", py);
59: return (0);
60: }
61: #else
62: int
63: grid_check_y(struct grid *gd, u_int py)
64: {
65: if ((py) >= (gd)->hsize + (gd)->sy) {
66: log_debug("y out of range: %u", py);
67: return (-1);
68: }
69: return (0);
70: }
71: #endif
72:
73: /* Create a new grid. */
74: struct grid *
75: grid_create(u_int sx, u_int sy, u_int hlimit)
76: {
77: struct grid *gd;
78:
79: gd = xmalloc(sizeof *gd);
80: gd->sx = sx;
81: gd->sy = sy;
82:
1.7 nicm 83: gd->flags = GRID_HISTORY;
84:
1.1 nicm 85: gd->hsize = 0;
86: gd->hlimit = hlimit;
87:
1.10 nicm 88: gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
1.1 nicm 89:
90: return (gd);
91: }
92:
93: /* Destroy grid. */
94: void
95: grid_destroy(struct grid *gd)
96: {
1.10 nicm 97: struct grid_line *gl;
98: u_int yy;
1.1 nicm 99:
100: for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
1.10 nicm 101: gl = &gd->linedata[yy];
1.20 nicm 102: free(gl->celldata);
1.1 nicm 103: }
104:
1.20 nicm 105: free(gd->linedata);
1.1 nicm 106:
1.20 nicm 107: free(gd);
1.1 nicm 108: }
109:
110: /* Compare grids. */
111: int
112: grid_compare(struct grid *ga, struct grid *gb)
113: {
1.10 nicm 114: struct grid_line *gla, *glb;
1.1 nicm 115: struct grid_cell *gca, *gcb;
116: u_int xx, yy;
117:
118: if (ga->sx != gb->sx || ga->sy != ga->sy)
119: return (1);
120:
121: for (yy = 0; yy < ga->sy; yy++) {
1.10 nicm 122: gla = &ga->linedata[yy];
123: glb = &gb->linedata[yy];
124: if (gla->cellsize != glb->cellsize)
1.1 nicm 125: return (1);
126: for (xx = 0; xx < ga->sx; xx++) {
1.10 nicm 127: gca = &gla->celldata[xx];
128: gcb = &glb->celldata[xx];
1.1 nicm 129: if (memcmp(gca, gcb, sizeof (struct grid_cell)) != 0)
130: return (1);
131: }
132: }
133:
134: return (0);
135: }
136:
1.15 nicm 137: /*
138: * Collect lines from the history if at the limit. Free the top (oldest) 10%
139: * and shift up.
140: */
1.1 nicm 141: void
1.15 nicm 142: grid_collect_history(struct grid *gd)
1.1 nicm 143: {
144: u_int yy;
145:
1.17 nicm 146: GRID_DEBUG(gd, "");
1.1 nicm 147:
1.15 nicm 148: if (gd->hsize < gd->hlimit)
149: return;
150:
151: yy = gd->hlimit / 10;
152: if (yy < 1)
153: yy = 1;
154:
155: grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy);
156: gd->hsize -= yy;
157: }
158:
1.17 nicm 159: /*
1.15 nicm 160: * Scroll the entire visible screen, moving one line into the history. Just
161: * allocate a new line at the bottom and move the history size indicator.
162: */
163: void
164: grid_scroll_history(struct grid *gd)
165: {
166: u_int yy;
1.1 nicm 167:
1.17 nicm 168: GRID_DEBUG(gd, "");
1.1 nicm 169:
170: yy = gd->hsize + gd->sy;
1.15 nicm 171: gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata);
172: memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]);
1.17 nicm 173:
1.15 nicm 174: gd->hsize++;
175: }
1.1 nicm 176:
1.15 nicm 177: /* Scroll a region up, moving the top line into the history. */
178: void
179: grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower)
180: {
181: struct grid_line *gl_history, *gl_upper, *gl_lower;
182: u_int yy;
183:
1.17 nicm 184: GRID_DEBUG(gd, "upper=%u, lower=%u", upper, lower);
1.15 nicm 185:
186: /* Create a space for a new line. */
187: yy = gd->hsize + gd->sy;
1.10 nicm 188: gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata);
1.1 nicm 189:
1.15 nicm 190: /* Move the entire screen down to free a space for this line. */
191: gl_history = &gd->linedata[gd->hsize];
192: memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);
193:
194: /* Adjust the region and find its start and end. */
195: upper++;
196: gl_upper = &gd->linedata[upper];
197: lower++;
198: gl_lower = &gd->linedata[lower];
199:
200: /* Move the line into the history. */
201: memcpy(gl_history, gl_upper, sizeof *gl_history);
202:
203: /* Then move the region up and clear the bottom line. */
204: memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
205: memset(gl_lower, 0, sizeof *gl_lower);
206:
207: /* Move the history offset down over the line. */
1.1 nicm 208: gd->hsize++;
209: }
210:
211: /* Expand line to fit to cell. */
212: void
1.14 nicm 213: grid_expand_line(struct grid *gd, u_int py, u_int sx)
1.1 nicm 214: {
1.10 nicm 215: struct grid_line *gl;
1.14 nicm 216: u_int xx;
1.1 nicm 217:
1.10 nicm 218: gl = &gd->linedata[py];
1.14 nicm 219: if (sx <= gl->cellsize)
1.1 nicm 220: return;
221:
1.10 nicm 222: gl->celldata = xrealloc(gl->celldata, sx, sizeof *gl->celldata);
223: for (xx = gl->cellsize; xx < sx; xx++)
1.1 nicm 224: grid_put_cell(gd, xx, py, &grid_default_cell);
1.10 nicm 225: gl->cellsize = sx;
1.1 nicm 226: }
227:
228: /* Get cell for reading. */
229: const struct grid_cell *
230: grid_peek_cell(struct grid *gd, u_int px, u_int py)
231: {
232: if (grid_check_y(gd, py) != 0)
233: return (&grid_default_cell);
234:
1.10 nicm 235: if (px >= gd->linedata[py].cellsize)
1.1 nicm 236: return (&grid_default_cell);
1.10 nicm 237: return (&gd->linedata[py].celldata[px]);
1.1 nicm 238: }
239:
240: /* Get cell at relative position (for writing). */
241: struct grid_cell *
242: grid_get_cell(struct grid *gd, u_int px, u_int py)
243: {
244: if (grid_check_y(gd, py) != 0)
245: return (NULL);
246:
247: grid_expand_line(gd, py, px + 1);
1.10 nicm 248: return (&gd->linedata[py].celldata[px]);
1.1 nicm 249: }
250:
251: /* Set cell at relative position. */
252: void
253: grid_set_cell(
254: struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
255: {
256: if (grid_check_y(gd, py) != 0)
257: return;
258:
259: grid_expand_line(gd, py, px + 1);
260: grid_put_cell(gd, px, py, gc);
261: }
262:
1.14 nicm 263: /* Clear area. */
1.1 nicm 264: void
265: grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny)
266: {
267: u_int xx, yy;
268:
1.17 nicm 269: GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny);
1.1 nicm 270:
271: if (nx == 0 || ny == 0)
272: return;
273:
274: if (px == 0 && nx == gd->sx) {
275: grid_clear_lines(gd, py, ny);
276: return;
277: }
278:
279: if (grid_check_y(gd, py) != 0)
280: return;
281: if (grid_check_y(gd, py + ny - 1) != 0)
282: return;
283:
284: for (yy = py; yy < py + ny; yy++) {
1.14 nicm 285: if (px >= gd->linedata[yy].cellsize)
286: continue;
287: if (px + nx >= gd->linedata[yy].cellsize) {
288: gd->linedata[yy].cellsize = px;
289: continue;
290: }
1.1 nicm 291: for (xx = px; xx < px + nx; xx++) {
1.10 nicm 292: if (xx >= gd->linedata[yy].cellsize)
1.1 nicm 293: break;
294: grid_put_cell(gd, xx, yy, &grid_default_cell);
295: }
296: }
297: }
298:
299: /* Clear lines. This just frees and truncates the lines. */
300: void
301: grid_clear_lines(struct grid *gd, u_int py, u_int ny)
302: {
1.10 nicm 303: struct grid_line *gl;
304: u_int yy;
1.1 nicm 305:
1.17 nicm 306: GRID_DEBUG(gd, "py=%u, ny=%u", py, ny);
1.1 nicm 307:
308: if (ny == 0)
309: return;
310:
311: if (grid_check_y(gd, py) != 0)
312: return;
313: if (grid_check_y(gd, py + ny - 1) != 0)
314: return;
315:
316: for (yy = py; yy < py + ny; yy++) {
1.10 nicm 317: gl = &gd->linedata[yy];
1.20 nicm 318: free(gl->celldata);
1.10 nicm 319: memset(gl, 0, sizeof *gl);
1.1 nicm 320: }
321: }
322:
323: /* Move a group of lines. */
324: void
325: grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny)
326: {
327: u_int yy;
328:
1.17 nicm 329: GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny);
1.1 nicm 330:
331: if (ny == 0 || py == dy)
332: return;
333:
334: if (grid_check_y(gd, py) != 0)
335: return;
336: if (grid_check_y(gd, py + ny - 1) != 0)
337: return;
338: if (grid_check_y(gd, dy) != 0)
339: return;
340: if (grid_check_y(gd, dy + ny - 1) != 0)
341: return;
342:
343: /* Free any lines which are being replaced. */
344: for (yy = dy; yy < dy + ny; yy++) {
345: if (yy >= py && yy < py + ny)
346: continue;
347: grid_clear_lines(gd, yy, 1);
348: }
349:
1.10 nicm 350: memmove(
351: &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata));
1.1 nicm 352:
353: /* Wipe any lines that have been moved (without freeing them). */
354: for (yy = py; yy < py + ny; yy++) {
355: if (yy >= dy && yy < dy + ny)
356: continue;
1.10 nicm 357: memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]);
1.1 nicm 358: }
359: }
360:
361: /* Move a group of cells. */
362: void
363: grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx)
364: {
1.10 nicm 365: struct grid_line *gl;
366: u_int xx;
1.1 nicm 367:
1.17 nicm 368: GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx);
1.1 nicm 369:
370: if (nx == 0 || px == dx)
371: return;
372:
373: if (grid_check_y(gd, py) != 0)
374: return;
1.10 nicm 375: gl = &gd->linedata[py];
1.1 nicm 376:
377: grid_expand_line(gd, py, px + nx);
378: grid_expand_line(gd, py, dx + nx);
1.10 nicm 379: memmove(
380: &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata);
1.1 nicm 381:
382: /* Wipe any cells that have been moved. */
383: for (xx = px; xx < px + nx; xx++) {
384: if (xx >= dx && xx < dx + nx)
385: continue;
386: grid_put_cell(gd, xx, py, &grid_default_cell);
387: }
1.3 nicm 388: }
389:
390: /* Convert cells into a string. */
391: char *
392: grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx)
393: {
1.17 nicm 394: const struct grid_cell *gc;
1.21 ! nicm 395: struct utf8_data ud;
1.3 nicm 396: char *buf;
1.21 ! nicm 397: size_t len, off;
1.16 nicm 398: u_int xx;
1.3 nicm 399:
400: GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx);
401:
402: len = 128;
403: buf = xmalloc(len);
404: off = 0;
405:
406: for (xx = px; xx < px + nx; xx++) {
407: gc = grid_peek_cell(gd, xx, py);
408: if (gc->flags & GRID_FLAG_PADDING)
409: continue;
1.21 ! nicm 410: grid_cell_get(gc, &ud);
1.3 nicm 411:
1.21 ! nicm 412: while (len < off + ud.size + 1) {
! 413: buf = xrealloc(buf, 2, len);
! 414: len *= 2;
! 415: }
1.3 nicm 416:
1.21 ! nicm 417: memcpy(buf + off, ud.data, ud.size);
! 418: off += ud.size;
1.3 nicm 419: }
1.17 nicm 420:
1.4 nicm 421: while (off > 0 && buf[off - 1] == ' ')
422: off--;
1.3 nicm 423: buf[off] = '\0';
424: return (buf);
1.7 nicm 425: }
426:
1.17 nicm 427: /*
1.7 nicm 428: * Duplicate a set of lines between two grids. If there aren't enough lines in
429: * either source or destination, the number of lines is limited to the number
430: * available.
431: */
432: void
433: grid_duplicate_lines(
434: struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny)
435: {
1.10 nicm 436: struct grid_line *dstl, *srcl;
437: u_int yy;
1.7 nicm 438:
439: GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny);
440:
441: if (dy + ny > dst->hsize + dst->sy)
442: ny = dst->hsize + dst->sy - dy;
443: if (sy + ny > src->hsize + src->sy)
444: ny = src->hsize + src->sy - sy;
445: grid_clear_lines(dst, dy, ny);
446:
447: for (yy = 0; yy < ny; yy++) {
1.11 nicm 448: srcl = &src->linedata[sy];
449: dstl = &dst->linedata[dy];
1.10 nicm 450:
451: memcpy(dstl, srcl, sizeof *dstl);
452: if (srcl->cellsize != 0) {
453: dstl->celldata = xcalloc(
454: srcl->cellsize, sizeof *dstl->celldata);
455: memcpy(dstl->celldata, srcl->celldata,
456: srcl->cellsize * sizeof *dstl->celldata);
1.7 nicm 457: }
458:
1.10 nicm 459: sy++;
460: dy++;
1.7 nicm 461: }
1.1 nicm 462: }