Annotation of src/usr.bin/tmux/grid.c, Revision 1.1
1.1 ! nicm 1: /* $OpenBSD$ */
! 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: }
! 496:
! 497: