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