Annotation of embedaddon/tmux/grid.c, revision 1.1
1.1 ! misho 1: /* $OpenBSD$ */
! 2:
! 3: /*
! 4: * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
! 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 <stdlib.h>
! 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. */
! 39: const struct grid_cell grid_default_cell = {
! 40: 0, 0, 8, 8, { { ' ' }, 0, 1, 1 }
! 41: };
! 42: static const struct grid_cell_entry grid_default_entry = {
! 43: 0, { .data = { 0, 8, 8, ' ' } }
! 44: };
! 45:
! 46: static void grid_expand_line(struct grid *, u_int, u_int, u_int);
! 47: static void grid_empty_line(struct grid *, u_int, u_int);
! 48:
! 49: static void grid_reflow_copy(struct grid_line *, u_int, struct grid_line *,
! 50: u_int, u_int);
! 51: static void grid_reflow_join(struct grid *, u_int *, struct grid_line *,
! 52: u_int);
! 53: static void grid_reflow_split(struct grid *, u_int *, struct grid_line *,
! 54: u_int, u_int);
! 55: static void grid_reflow_move(struct grid *, u_int *, struct grid_line *);
! 56:
! 57: static size_t grid_string_cells_fg(const struct grid_cell *, int *);
! 58: static size_t grid_string_cells_bg(const struct grid_cell *, int *);
! 59: static void grid_string_cells_code(const struct grid_cell *,
! 60: const struct grid_cell *, char *, size_t, int);
! 61:
! 62: /* Store cell in entry. */
! 63: static void
! 64: grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
! 65: u_char c)
! 66: {
! 67: gce->flags = gc->flags;
! 68:
! 69: gce->data.fg = gc->fg & 0xff;
! 70: if (gc->fg & COLOUR_FLAG_256)
! 71: gce->flags |= GRID_FLAG_FG256;
! 72:
! 73: gce->data.bg = gc->bg & 0xff;
! 74: if (gc->bg & COLOUR_FLAG_256)
! 75: gce->flags |= GRID_FLAG_BG256;
! 76:
! 77: gce->data.attr = gc->attr;
! 78: gce->data.data = c;
! 79: }
! 80:
! 81: /* Check if a cell should be extended. */
! 82: static int
! 83: grid_need_extended_cell(const struct grid_cell_entry *gce,
! 84: const struct grid_cell *gc)
! 85: {
! 86: if (gce->flags & GRID_FLAG_EXTENDED)
! 87: return (1);
! 88: if (gc->attr > 0xff)
! 89: return (1);
! 90: if (gc->data.size != 1 || gc->data.width != 1)
! 91: return (1);
! 92: if ((gc->fg & COLOUR_FLAG_RGB) ||(gc->bg & COLOUR_FLAG_RGB))
! 93: return (1);
! 94: return (0);
! 95: }
! 96:
! 97: /* Set cell as extended. */
! 98: static struct grid_cell *
! 99: grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
! 100: const struct grid_cell *gc)
! 101: {
! 102: struct grid_cell *gcp;
! 103:
! 104: gl->flags |= GRID_LINE_EXTENDED;
! 105:
! 106: if (~gce->flags & GRID_FLAG_EXTENDED) {
! 107: gl->extddata = xreallocarray(gl->extddata, gl->extdsize + 1,
! 108: sizeof *gl->extddata);
! 109: gce->offset = gl->extdsize++;
! 110: gce->flags = gc->flags | GRID_FLAG_EXTENDED;
! 111: }
! 112: if (gce->offset >= gl->extdsize)
! 113: fatalx("offset too big");
! 114:
! 115: gcp = &gl->extddata[gce->offset];
! 116: memcpy(gcp, gc, sizeof *gcp);
! 117: return (gcp);
! 118: }
! 119:
! 120: /* Copy default into a cell. */
! 121: static void
! 122: grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
! 123: {
! 124: struct grid_line *gl = &gd->linedata[py];
! 125: struct grid_cell_entry *gce = &gl->celldata[px];
! 126: struct grid_cell *gc;
! 127:
! 128: memcpy(gce, &grid_default_entry, sizeof *gce);
! 129: if (bg & COLOUR_FLAG_RGB) {
! 130: gc = grid_extended_cell(gl, gce, &grid_default_cell);
! 131: gc->bg = bg;
! 132: } else {
! 133: if (bg & COLOUR_FLAG_256)
! 134: gce->flags |= GRID_FLAG_BG256;
! 135: gce->data.bg = bg;
! 136: }
! 137: }
! 138:
! 139: /* Check grid y position. */
! 140: static int
! 141: grid_check_y(struct grid *gd, u_int py)
! 142: {
! 143: if ((py) >= (gd)->hsize + (gd)->sy) {
! 144: log_debug("y out of range: %u", py);
! 145: return (-1);
! 146: }
! 147: return (0);
! 148: }
! 149:
! 150: /* Compare grid cells. Return 1 if equal, 0 if not. */
! 151: int
! 152: grid_cells_equal(const struct grid_cell *gca, const struct grid_cell *gcb)
! 153: {
! 154: if (gca->fg != gcb->fg || gca->bg != gcb->bg)
! 155: return (0);
! 156: if (gca->attr != gcb->attr || gca->flags != gcb->flags)
! 157: return (0);
! 158: if (gca->data.width != gcb->data.width)
! 159: return (0);
! 160: if (gca->data.size != gcb->data.size)
! 161: return (0);
! 162: return (memcmp(gca->data.data, gcb->data.data, gca->data.size) == 0);
! 163: }
! 164:
! 165: /* Create a new grid. */
! 166: struct grid *
! 167: grid_create(u_int sx, u_int sy, u_int hlimit)
! 168: {
! 169: struct grid *gd;
! 170:
! 171: gd = xmalloc(sizeof *gd);
! 172: gd->sx = sx;
! 173: gd->sy = sy;
! 174:
! 175: gd->flags = GRID_HISTORY;
! 176:
! 177: gd->hscrolled = 0;
! 178: gd->hsize = 0;
! 179: gd->hlimit = hlimit;
! 180:
! 181: gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
! 182:
! 183: return (gd);
! 184: }
! 185:
! 186: /* Destroy grid. */
! 187: void
! 188: grid_destroy(struct grid *gd)
! 189: {
! 190: struct grid_line *gl;
! 191: u_int yy;
! 192:
! 193: for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
! 194: gl = &gd->linedata[yy];
! 195: free(gl->celldata);
! 196: free(gl->extddata);
! 197: }
! 198:
! 199: free(gd->linedata);
! 200:
! 201: free(gd);
! 202: }
! 203:
! 204: /* Compare grids. */
! 205: int
! 206: grid_compare(struct grid *ga, struct grid *gb)
! 207: {
! 208: struct grid_line *gla, *glb;
! 209: struct grid_cell gca, gcb;
! 210: u_int xx, yy;
! 211:
! 212: if (ga->sx != gb->sx || ga->sy != gb->sy)
! 213: return (1);
! 214:
! 215: for (yy = 0; yy < ga->sy; yy++) {
! 216: gla = &ga->linedata[yy];
! 217: glb = &gb->linedata[yy];
! 218: if (gla->cellsize != glb->cellsize)
! 219: return (1);
! 220: for (xx = 0; xx < gla->cellsize; xx++) {
! 221: grid_get_cell(ga, xx, yy, &gca);
! 222: grid_get_cell(gb, xx, yy, &gcb);
! 223: if (!grid_cells_equal(&gca, &gcb))
! 224: return (1);
! 225: }
! 226: }
! 227:
! 228: return (0);
! 229: }
! 230:
! 231: /*
! 232: * Collect lines from the history if at the limit. Free the top (oldest) 10%
! 233: * and shift up.
! 234: */
! 235: void
! 236: grid_collect_history(struct grid *gd, u_int bg)
! 237: {
! 238: u_int yy;
! 239:
! 240: if (gd->hsize < gd->hlimit)
! 241: return;
! 242:
! 243: yy = gd->hlimit / 10;
! 244: if (yy < 1)
! 245: yy = 1;
! 246:
! 247: grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy, bg);
! 248: gd->hsize -= yy;
! 249: if (gd->hscrolled > gd->hsize)
! 250: gd->hscrolled = gd->hsize;
! 251: }
! 252:
! 253: /*
! 254: * Scroll the entire visible screen, moving one line into the history. Just
! 255: * allocate a new line at the bottom and move the history size indicator.
! 256: */
! 257: void
! 258: grid_scroll_history(struct grid *gd, u_int bg)
! 259: {
! 260: u_int yy;
! 261:
! 262: yy = gd->hsize + gd->sy;
! 263: gd->linedata = xreallocarray(gd->linedata, yy + 1,
! 264: sizeof *gd->linedata);
! 265: grid_empty_line(gd, yy, bg);
! 266:
! 267: gd->hscrolled++;
! 268: gd->hsize++;
! 269: }
! 270:
! 271: /* Clear the history. */
! 272: void
! 273: grid_clear_history(struct grid *gd)
! 274: {
! 275: grid_clear_lines(gd, 0, gd->hsize, 8);
! 276: grid_move_lines(gd, 0, gd->hsize, gd->sy, 8);
! 277:
! 278: gd->hscrolled = 0;
! 279: gd->hsize = 0;
! 280:
! 281: gd->linedata = xreallocarray(gd->linedata, gd->sy,
! 282: sizeof *gd->linedata);
! 283: }
! 284:
! 285: /* Scroll a region up, moving the top line into the history. */
! 286: void
! 287: grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower)
! 288: {
! 289: struct grid_line *gl_history, *gl_upper, *gl_lower;
! 290: u_int yy;
! 291:
! 292: /* Create a space for a new line. */
! 293: yy = gd->hsize + gd->sy;
! 294: gd->linedata = xreallocarray(gd->linedata, yy + 1,
! 295: sizeof *gd->linedata);
! 296:
! 297: /* Move the entire screen down to free a space for this line. */
! 298: gl_history = &gd->linedata[gd->hsize];
! 299: memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);
! 300:
! 301: /* Adjust the region and find its start and end. */
! 302: upper++;
! 303: gl_upper = &gd->linedata[upper];
! 304: lower++;
! 305: gl_lower = &gd->linedata[lower];
! 306:
! 307: /* Move the line into the history. */
! 308: memcpy(gl_history, gl_upper, sizeof *gl_history);
! 309:
! 310: /* Then move the region up and clear the bottom line. */
! 311: memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
! 312: memset(gl_lower, 0, sizeof *gl_lower);
! 313:
! 314: /* Move the history offset down over the line. */
! 315: gd->hscrolled++;
! 316: gd->hsize++;
! 317: }
! 318:
! 319: /* Expand line to fit to cell. */
! 320: static void
! 321: grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
! 322: {
! 323: struct grid_line *gl;
! 324: u_int xx;
! 325:
! 326: gl = &gd->linedata[py];
! 327: if (sx <= gl->cellsize)
! 328: return;
! 329:
! 330: if (sx < gd->sx / 4)
! 331: sx = gd->sx / 4;
! 332: else if (sx < gd->sx / 2)
! 333: sx = gd->sx / 2;
! 334: else
! 335: sx = gd->sx;
! 336:
! 337: gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
! 338: for (xx = gl->cellsize; xx < sx; xx++)
! 339: grid_clear_cell(gd, xx, py, bg);
! 340: gl->cellsize = sx;
! 341: }
! 342:
! 343: /* Empty a line and set background colour if needed. */
! 344: static void
! 345: grid_empty_line(struct grid *gd, u_int py, u_int bg)
! 346: {
! 347: memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
! 348: if (bg != 8)
! 349: grid_expand_line(gd, py, gd->sx, bg);
! 350: }
! 351:
! 352: /* Peek at grid line. */
! 353: const struct grid_line *
! 354: grid_peek_line(struct grid *gd, u_int py)
! 355: {
! 356: if (grid_check_y(gd, py) != 0)
! 357: return (NULL);
! 358: return (&gd->linedata[py]);
! 359: }
! 360:
! 361: /* Get cell for reading. */
! 362: void
! 363: grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
! 364: {
! 365: struct grid_line *gl;
! 366: struct grid_cell_entry *gce;
! 367:
! 368: if (grid_check_y(gd, py) != 0 || px >= gd->linedata[py].cellsize) {
! 369: memcpy(gc, &grid_default_cell, sizeof *gc);
! 370: return;
! 371: }
! 372:
! 373: gl = &gd->linedata[py];
! 374: gce = &gl->celldata[px];
! 375:
! 376: if (gce->flags & GRID_FLAG_EXTENDED) {
! 377: if (gce->offset >= gl->extdsize)
! 378: memcpy(gc, &grid_default_cell, sizeof *gc);
! 379: else
! 380: memcpy(gc, &gl->extddata[gce->offset], sizeof *gc);
! 381: return;
! 382: }
! 383:
! 384: gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
! 385: gc->attr = gce->data.attr;
! 386: gc->fg = gce->data.fg;
! 387: if (gce->flags & GRID_FLAG_FG256)
! 388: gc->fg |= COLOUR_FLAG_256;
! 389: gc->bg = gce->data.bg;
! 390: if (gce->flags & GRID_FLAG_BG256)
! 391: gc->bg |= COLOUR_FLAG_256;
! 392: utf8_set(&gc->data, gce->data.data);
! 393: }
! 394:
! 395: /* Set cell at relative position. */
! 396: void
! 397: grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
! 398: {
! 399: struct grid_line *gl;
! 400: struct grid_cell_entry *gce;
! 401:
! 402: if (grid_check_y(gd, py) != 0)
! 403: return;
! 404:
! 405: grid_expand_line(gd, py, px + 1, 8);
! 406:
! 407: gl = &gd->linedata[py];
! 408: if (px + 1 > gl->cellused)
! 409: gl->cellused = px + 1;
! 410:
! 411: gce = &gl->celldata[px];
! 412: if (grid_need_extended_cell(gce, gc))
! 413: grid_extended_cell(gl, gce, gc);
! 414: else
! 415: grid_store_cell(gce, gc, gc->data.data[0]);
! 416: }
! 417:
! 418: /* Set cells at relative position. */
! 419: void
! 420: grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
! 421: const char *s, size_t slen)
! 422: {
! 423: struct grid_line *gl;
! 424: struct grid_cell_entry *gce;
! 425: struct grid_cell *gcp;
! 426: u_int i;
! 427:
! 428: if (grid_check_y(gd, py) != 0)
! 429: return;
! 430:
! 431: grid_expand_line(gd, py, px + slen, 8);
! 432:
! 433: gl = &gd->linedata[py];
! 434: if (px + slen > gl->cellused)
! 435: gl->cellused = px + slen;
! 436:
! 437: for (i = 0; i < slen; i++) {
! 438: gce = &gl->celldata[px + i];
! 439: if (grid_need_extended_cell(gce, gc)) {
! 440: gcp = grid_extended_cell(gl, gce, gc);
! 441: utf8_set(&gcp->data, s[i]);
! 442: } else
! 443: grid_store_cell(gce, gc, s[i]);
! 444: }
! 445: }
! 446:
! 447: /* Clear area. */
! 448: void
! 449: grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
! 450: {
! 451: u_int xx, yy;
! 452:
! 453: if (nx == 0 || ny == 0)
! 454: return;
! 455:
! 456: if (px == 0 && nx == gd->sx) {
! 457: grid_clear_lines(gd, py, ny, bg);
! 458: return;
! 459: }
! 460:
! 461: if (grid_check_y(gd, py) != 0)
! 462: return;
! 463: if (grid_check_y(gd, py + ny - 1) != 0)
! 464: return;
! 465:
! 466: for (yy = py; yy < py + ny; yy++) {
! 467: if (px + nx >= gd->sx && px < gd->linedata[yy].cellused)
! 468: gd->linedata[yy].cellused = px;
! 469: if (px > gd->linedata[yy].cellsize && bg == 8)
! 470: continue;
! 471: if (px + nx >= gd->linedata[yy].cellsize && bg == 8) {
! 472: gd->linedata[yy].cellsize = px;
! 473: continue;
! 474: }
! 475: grid_expand_line(gd, yy, px + nx, bg);
! 476: for (xx = px; xx < px + nx; xx++)
! 477: grid_clear_cell(gd, xx, yy, bg);
! 478: }
! 479: }
! 480:
! 481: /* Clear lines. This just frees and truncates the lines. */
! 482: void
! 483: grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
! 484: {
! 485: struct grid_line *gl;
! 486: u_int yy;
! 487:
! 488: if (ny == 0)
! 489: return;
! 490:
! 491: if (grid_check_y(gd, py) != 0)
! 492: return;
! 493: if (grid_check_y(gd, py + ny - 1) != 0)
! 494: return;
! 495:
! 496: for (yy = py; yy < py + ny; yy++) {
! 497: gl = &gd->linedata[yy];
! 498: free(gl->celldata);
! 499: free(gl->extddata);
! 500: grid_empty_line(gd, yy, bg);
! 501: }
! 502: }
! 503:
! 504: /* Move a group of lines. */
! 505: void
! 506: grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
! 507: {
! 508: u_int yy;
! 509:
! 510: if (ny == 0 || py == dy)
! 511: return;
! 512:
! 513: if (grid_check_y(gd, py) != 0)
! 514: return;
! 515: if (grid_check_y(gd, py + ny - 1) != 0)
! 516: return;
! 517: if (grid_check_y(gd, dy) != 0)
! 518: return;
! 519: if (grid_check_y(gd, dy + ny - 1) != 0)
! 520: return;
! 521:
! 522: /* Free any lines which are being replaced. */
! 523: for (yy = dy; yy < dy + ny; yy++) {
! 524: if (yy >= py && yy < py + ny)
! 525: continue;
! 526: grid_clear_lines(gd, yy, 1, bg);
! 527: }
! 528:
! 529: memmove(&gd->linedata[dy], &gd->linedata[py],
! 530: ny * (sizeof *gd->linedata));
! 531:
! 532: /* Wipe any lines that have been moved (without freeing them). */
! 533: for (yy = py; yy < py + ny; yy++) {
! 534: if (yy < dy || yy >= dy + ny)
! 535: grid_empty_line(gd, yy, bg);
! 536: }
! 537: }
! 538:
! 539: /* Move a group of cells. */
! 540: void
! 541: grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
! 542: u_int bg)
! 543: {
! 544: struct grid_line *gl;
! 545: u_int xx;
! 546:
! 547: if (nx == 0 || px == dx)
! 548: return;
! 549:
! 550: if (grid_check_y(gd, py) != 0)
! 551: return;
! 552: gl = &gd->linedata[py];
! 553:
! 554: grid_expand_line(gd, py, px + nx, 8);
! 555: grid_expand_line(gd, py, dx + nx, 8);
! 556: memmove(&gl->celldata[dx], &gl->celldata[px],
! 557: nx * sizeof *gl->celldata);
! 558: if (dx + nx > gl->cellused)
! 559: gl->cellused = dx + nx;
! 560:
! 561: /* Wipe any cells that have been moved. */
! 562: for (xx = px; xx < px + nx; xx++) {
! 563: if (xx >= dx && xx < dx + nx)
! 564: continue;
! 565: grid_clear_cell(gd, xx, py, bg);
! 566: }
! 567: }
! 568:
! 569: /* Get ANSI foreground sequence. */
! 570: static size_t
! 571: grid_string_cells_fg(const struct grid_cell *gc, int *values)
! 572: {
! 573: size_t n;
! 574: u_char r, g, b;
! 575:
! 576: n = 0;
! 577: if (gc->fg & COLOUR_FLAG_256) {
! 578: values[n++] = 38;
! 579: values[n++] = 5;
! 580: values[n++] = gc->fg & 0xff;
! 581: } else if (gc->fg & COLOUR_FLAG_RGB) {
! 582: values[n++] = 38;
! 583: values[n++] = 2;
! 584: colour_split_rgb(gc->fg, &r, &g, &b);
! 585: values[n++] = r;
! 586: values[n++] = g;
! 587: values[n++] = b;
! 588: } else {
! 589: switch (gc->fg) {
! 590: case 0:
! 591: case 1:
! 592: case 2:
! 593: case 3:
! 594: case 4:
! 595: case 5:
! 596: case 6:
! 597: case 7:
! 598: values[n++] = gc->fg + 30;
! 599: break;
! 600: case 8:
! 601: values[n++] = 39;
! 602: break;
! 603: case 90:
! 604: case 91:
! 605: case 92:
! 606: case 93:
! 607: case 94:
! 608: case 95:
! 609: case 96:
! 610: case 97:
! 611: values[n++] = gc->fg;
! 612: break;
! 613: }
! 614: }
! 615: return (n);
! 616: }
! 617:
! 618: /* Get ANSI background sequence. */
! 619: static size_t
! 620: grid_string_cells_bg(const struct grid_cell *gc, int *values)
! 621: {
! 622: size_t n;
! 623: u_char r, g, b;
! 624:
! 625: n = 0;
! 626: if (gc->bg & COLOUR_FLAG_256) {
! 627: values[n++] = 48;
! 628: values[n++] = 5;
! 629: values[n++] = gc->bg & 0xff;
! 630: } else if (gc->bg & COLOUR_FLAG_RGB) {
! 631: values[n++] = 48;
! 632: values[n++] = 2;
! 633: colour_split_rgb(gc->bg, &r, &g, &b);
! 634: values[n++] = r;
! 635: values[n++] = g;
! 636: values[n++] = b;
! 637: } else {
! 638: switch (gc->bg) {
! 639: case 0:
! 640: case 1:
! 641: case 2:
! 642: case 3:
! 643: case 4:
! 644: case 5:
! 645: case 6:
! 646: case 7:
! 647: values[n++] = gc->bg + 40;
! 648: break;
! 649: case 8:
! 650: values[n++] = 49;
! 651: break;
! 652: case 100:
! 653: case 101:
! 654: case 102:
! 655: case 103:
! 656: case 104:
! 657: case 105:
! 658: case 106:
! 659: case 107:
! 660: values[n++] = gc->bg - 10;
! 661: break;
! 662: }
! 663: }
! 664: return (n);
! 665: }
! 666:
! 667: /*
! 668: * Returns ANSI code to set particular attributes (colour, bold and so on)
! 669: * given a current state. The output buffer must be able to hold at least 57
! 670: * bytes.
! 671: */
! 672: static void
! 673: grid_string_cells_code(const struct grid_cell *lastgc,
! 674: const struct grid_cell *gc, char *buf, size_t len, int escape_c0)
! 675: {
! 676: int oldc[64], newc[64], s[128];
! 677: size_t noldc, nnewc, n, i;
! 678: u_int attr = gc->attr;
! 679: u_int lastattr = lastgc->attr;
! 680: char tmp[64];
! 681:
! 682: struct {
! 683: u_int mask;
! 684: u_int code;
! 685: } attrs[] = {
! 686: { GRID_ATTR_BRIGHT, 1 },
! 687: { GRID_ATTR_DIM, 2 },
! 688: { GRID_ATTR_ITALICS, 3 },
! 689: { GRID_ATTR_UNDERSCORE, 4 },
! 690: { GRID_ATTR_BLINK, 5 },
! 691: { GRID_ATTR_REVERSE, 7 },
! 692: { GRID_ATTR_HIDDEN, 8 },
! 693: { GRID_ATTR_STRIKETHROUGH, 9 }
! 694: };
! 695: n = 0;
! 696:
! 697: /* If any attribute is removed, begin with 0. */
! 698: for (i = 0; i < nitems(attrs); i++) {
! 699: if (!(attr & attrs[i].mask) && (lastattr & attrs[i].mask)) {
! 700: s[n++] = 0;
! 701: lastattr &= GRID_ATTR_CHARSET;
! 702: break;
! 703: }
! 704: }
! 705: /* For each attribute that is newly set, add its code. */
! 706: for (i = 0; i < nitems(attrs); i++) {
! 707: if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask))
! 708: s[n++] = attrs[i].code;
! 709: }
! 710:
! 711: /* If the foreground colour changed, append its parameters. */
! 712: nnewc = grid_string_cells_fg(gc, newc);
! 713: noldc = grid_string_cells_fg(lastgc, oldc);
! 714: if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) {
! 715: for (i = 0; i < nnewc; i++)
! 716: s[n++] = newc[i];
! 717: }
! 718:
! 719: /* If the background colour changed, append its parameters. */
! 720: nnewc = grid_string_cells_bg(gc, newc);
! 721: noldc = grid_string_cells_bg(lastgc, oldc);
! 722: if (nnewc != noldc || memcmp(newc, oldc, nnewc * sizeof newc[0]) != 0) {
! 723: for (i = 0; i < nnewc; i++)
! 724: s[n++] = newc[i];
! 725: }
! 726:
! 727: /* If there are any parameters, append an SGR code. */
! 728: *buf = '\0';
! 729: if (n > 0) {
! 730: if (escape_c0)
! 731: strlcat(buf, "\\033[", len);
! 732: else
! 733: strlcat(buf, "\033[", len);
! 734: for (i = 0; i < n; i++) {
! 735: if (i + 1 < n)
! 736: xsnprintf(tmp, sizeof tmp, "%d;", s[i]);
! 737: else
! 738: xsnprintf(tmp, sizeof tmp, "%d", s[i]);
! 739: strlcat(buf, tmp, len);
! 740: }
! 741: strlcat(buf, "m", len);
! 742: }
! 743:
! 744: /* Append shift in/shift out if needed. */
! 745: if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
! 746: if (escape_c0)
! 747: strlcat(buf, "\\016", len); /* SO */
! 748: else
! 749: strlcat(buf, "\016", len); /* SO */
! 750: }
! 751: if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
! 752: if (escape_c0)
! 753: strlcat(buf, "\\017", len); /* SI */
! 754: else
! 755: strlcat(buf, "\017", len); /* SI */
! 756: }
! 757: }
! 758:
! 759: /* Convert cells into a string. */
! 760: char *
! 761: grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
! 762: struct grid_cell **lastgc, int with_codes, int escape_c0, int trim)
! 763: {
! 764: struct grid_cell gc;
! 765: static struct grid_cell lastgc1;
! 766: const char *data;
! 767: char *buf, code[128];
! 768: size_t len, off, size, codelen;
! 769: u_int xx;
! 770: const struct grid_line *gl;
! 771:
! 772: if (lastgc != NULL && *lastgc == NULL) {
! 773: memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1);
! 774: *lastgc = &lastgc1;
! 775: }
! 776:
! 777: len = 128;
! 778: buf = xmalloc(len);
! 779: off = 0;
! 780:
! 781: gl = grid_peek_line(gd, py);
! 782: for (xx = px; xx < px + nx; xx++) {
! 783: if (gl == NULL || xx >= gl->cellsize)
! 784: break;
! 785: grid_get_cell(gd, xx, py, &gc);
! 786: if (gc.flags & GRID_FLAG_PADDING)
! 787: continue;
! 788:
! 789: if (with_codes) {
! 790: grid_string_cells_code(*lastgc, &gc, code, sizeof code,
! 791: escape_c0);
! 792: codelen = strlen(code);
! 793: memcpy(*lastgc, &gc, sizeof **lastgc);
! 794: } else
! 795: codelen = 0;
! 796:
! 797: data = gc.data.data;
! 798: size = gc.data.size;
! 799: if (escape_c0 && size == 1 && *data == '\\') {
! 800: data = "\\\\";
! 801: size = 2;
! 802: }
! 803:
! 804: while (len < off + size + codelen + 1) {
! 805: buf = xreallocarray(buf, 2, len);
! 806: len *= 2;
! 807: }
! 808:
! 809: if (codelen != 0) {
! 810: memcpy(buf + off, code, codelen);
! 811: off += codelen;
! 812: }
! 813: memcpy(buf + off, data, size);
! 814: off += size;
! 815: }
! 816:
! 817: if (trim) {
! 818: while (off > 0 && buf[off - 1] == ' ')
! 819: off--;
! 820: }
! 821: buf[off] = '\0';
! 822:
! 823: return (buf);
! 824: }
! 825:
! 826: /*
! 827: * Duplicate a set of lines between two grids. If there aren't enough lines in
! 828: * either source or destination, the number of lines is limited to the number
! 829: * available.
! 830: */
! 831: void
! 832: grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
! 833: u_int ny)
! 834: {
! 835: struct grid_line *dstl, *srcl;
! 836: u_int yy;
! 837:
! 838: if (dy + ny > dst->hsize + dst->sy)
! 839: ny = dst->hsize + dst->sy - dy;
! 840: if (sy + ny > src->hsize + src->sy)
! 841: ny = src->hsize + src->sy - sy;
! 842: grid_clear_lines(dst, dy, ny, 8);
! 843:
! 844: for (yy = 0; yy < ny; yy++) {
! 845: srcl = &src->linedata[sy];
! 846: dstl = &dst->linedata[dy];
! 847:
! 848: memcpy(dstl, srcl, sizeof *dstl);
! 849: if (srcl->cellsize != 0) {
! 850: dstl->celldata = xreallocarray(NULL,
! 851: srcl->cellsize, sizeof *dstl->celldata);
! 852: memcpy(dstl->celldata, srcl->celldata,
! 853: srcl->cellsize * sizeof *dstl->celldata);
! 854: } else
! 855: dstl->celldata = NULL;
! 856:
! 857: if (srcl->extdsize != 0) {
! 858: dstl->extdsize = srcl->extdsize;
! 859: dstl->extddata = xreallocarray(NULL, dstl->extdsize,
! 860: sizeof *dstl->extddata);
! 861: memcpy(dstl->extddata, srcl->extddata, dstl->extdsize *
! 862: sizeof *dstl->extddata);
! 863: }
! 864:
! 865: sy++;
! 866: dy++;
! 867: }
! 868: }
! 869:
! 870: /* Copy a section of a line. */
! 871: static void
! 872: grid_reflow_copy(struct grid_line *dst_gl, u_int to, struct grid_line *src_gl,
! 873: u_int from, u_int to_copy)
! 874: {
! 875: struct grid_cell_entry *gce;
! 876: u_int i, was;
! 877:
! 878: memcpy(&dst_gl->celldata[to], &src_gl->celldata[from],
! 879: to_copy * sizeof *dst_gl->celldata);
! 880:
! 881: for (i = to; i < to + to_copy; i++) {
! 882: gce = &dst_gl->celldata[i];
! 883: if (~gce->flags & GRID_FLAG_EXTENDED)
! 884: continue;
! 885: was = gce->offset;
! 886:
! 887: dst_gl->extddata = xreallocarray(dst_gl->extddata,
! 888: dst_gl->extdsize + 1, sizeof *dst_gl->extddata);
! 889: gce->offset = dst_gl->extdsize++;
! 890: memcpy(&dst_gl->extddata[gce->offset], &src_gl->extddata[was],
! 891: sizeof *dst_gl->extddata);
! 892: }
! 893: }
! 894:
! 895: /* Join line data. */
! 896: static void
! 897: grid_reflow_join(struct grid *dst, u_int *py, struct grid_line *src_gl,
! 898: u_int new_x)
! 899: {
! 900: struct grid_line *dst_gl = &dst->linedata[(*py) - 1];
! 901: u_int left, to_copy, ox, nx;
! 902:
! 903: /* How much is left on the old line? */
! 904: left = new_x - dst_gl->cellused;
! 905:
! 906: /* Work out how much to append. */
! 907: to_copy = src_gl->cellused;
! 908: if (to_copy > left)
! 909: to_copy = left;
! 910: ox = dst_gl->cellused;
! 911: nx = ox + to_copy;
! 912:
! 913: /* Resize the destination line. */
! 914: dst_gl->celldata = xreallocarray(dst_gl->celldata, nx,
! 915: sizeof *dst_gl->celldata);
! 916: dst_gl->cellsize = dst_gl->cellused = nx;
! 917:
! 918: /* Append as much as possible. */
! 919: grid_reflow_copy(dst_gl, ox, src_gl, 0, to_copy);
! 920:
! 921: /* If there is any left in the source, split it. */
! 922: if (src_gl->cellused > to_copy) {
! 923: dst_gl->flags |= GRID_LINE_WRAPPED;
! 924:
! 925: src_gl->cellused -= to_copy;
! 926: grid_reflow_split(dst, py, src_gl, new_x, to_copy);
! 927: }
! 928: }
! 929:
! 930: /* Split line data. */
! 931: static void
! 932: grid_reflow_split(struct grid *dst, u_int *py, struct grid_line *src_gl,
! 933: u_int new_x, u_int offset)
! 934: {
! 935: struct grid_line *dst_gl = NULL;
! 936: u_int to_copy;
! 937:
! 938: /* Loop and copy sections of the source line. */
! 939: while (src_gl->cellused > 0) {
! 940: /* Create new line. */
! 941: if (*py >= dst->hsize + dst->sy)
! 942: grid_scroll_history(dst, 8);
! 943: dst_gl = &dst->linedata[*py];
! 944: (*py)++;
! 945:
! 946: /* How much should we copy? */
! 947: to_copy = new_x;
! 948: if (to_copy > src_gl->cellused)
! 949: to_copy = src_gl->cellused;
! 950:
! 951: /* Expand destination line. */
! 952: dst_gl->celldata = xreallocarray(NULL, to_copy,
! 953: sizeof *dst_gl->celldata);
! 954: dst_gl->cellsize = dst_gl->cellused = to_copy;
! 955: dst_gl->flags |= GRID_LINE_WRAPPED;
! 956:
! 957: /* Copy the data. */
! 958: grid_reflow_copy(dst_gl, 0, src_gl, offset, to_copy);
! 959:
! 960: /* Move offset and reduce old line size. */
! 961: offset += to_copy;
! 962: src_gl->cellused -= to_copy;
! 963: }
! 964:
! 965: /* Last line is not wrapped. */
! 966: if (dst_gl != NULL)
! 967: dst_gl->flags &= ~GRID_LINE_WRAPPED;
! 968: }
! 969:
! 970: /* Move line data. */
! 971: static void
! 972: grid_reflow_move(struct grid *dst, u_int *py, struct grid_line *src_gl)
! 973: {
! 974: struct grid_line *dst_gl;
! 975:
! 976: /* Create new line. */
! 977: if (*py >= dst->hsize + dst->sy)
! 978: grid_scroll_history(dst, 8);
! 979: dst_gl = &dst->linedata[*py];
! 980: (*py)++;
! 981:
! 982: /* Copy the old line. */
! 983: memcpy(dst_gl, src_gl, sizeof *dst_gl);
! 984: dst_gl->flags &= ~GRID_LINE_WRAPPED;
! 985:
! 986: /* Clear old line. */
! 987: src_gl->celldata = NULL;
! 988: src_gl->extddata = NULL;
! 989: }
! 990:
! 991: /*
! 992: * Reflow lines from src grid into dst grid of width new_x. Returns number of
! 993: * lines fewer in the visible area. The source grid is destroyed.
! 994: */
! 995: u_int
! 996: grid_reflow(struct grid *dst, struct grid *src, u_int new_x)
! 997: {
! 998: u_int py, sy, line;
! 999: int previous_wrapped;
! 1000: struct grid_line *src_gl;
! 1001:
! 1002: py = 0;
! 1003: sy = src->sy;
! 1004:
! 1005: previous_wrapped = 0;
! 1006: for (line = 0; line < sy + src->hsize; line++) {
! 1007: src_gl = src->linedata + line;
! 1008: if (!previous_wrapped) {
! 1009: /* Wasn't wrapped. If smaller, move to destination. */
! 1010: if (src_gl->cellused <= new_x)
! 1011: grid_reflow_move(dst, &py, src_gl);
! 1012: else
! 1013: grid_reflow_split(dst, &py, src_gl, new_x, 0);
! 1014: } else {
! 1015: /* Previous was wrapped. Try to join. */
! 1016: grid_reflow_join(dst, &py, src_gl, new_x);
! 1017: }
! 1018: previous_wrapped = (src_gl->flags & GRID_LINE_WRAPPED);
! 1019:
! 1020: /* This is where we started scrolling. */
! 1021: if (line == sy + src->hsize - src->hscrolled - 1)
! 1022: dst->hscrolled = 0;
! 1023: }
! 1024:
! 1025: grid_destroy(src);
! 1026:
! 1027: if (py > sy)
! 1028: return (0);
! 1029: return (sy - py);
! 1030: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>