Merge branch 'obsd-master'
[tmux.git] / grid.c
blob786294199989db8cc36576871d05613f43c8146e
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
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.
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.
19 #include <sys/types.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #include "tmux.h"
27 * Grid data. This is the basic data structure that represents what is shown on
28 * screen.
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.
38 /* Default grid cell data. */
39 const struct grid_cell grid_default_cell = {
40 { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0
44 * Padding grid cell data. Padding cells are the only zero width cell that
45 * appears in the grid - because of this, they are always extended cells.
47 static const struct grid_cell grid_padding_cell = {
48 { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0
51 /* Cleared grid cell data. */
52 static const struct grid_cell grid_cleared_cell = {
53 { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0
55 static const struct grid_cell_entry grid_cleared_entry = {
56 { .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED
59 /* Store cell in entry. */
60 static void
61 grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc,
62 u_char c)
64 gce->flags = (gc->flags & ~GRID_FLAG_CLEARED);
66 gce->data.fg = gc->fg & 0xff;
67 if (gc->fg & COLOUR_FLAG_256)
68 gce->flags |= GRID_FLAG_FG256;
70 gce->data.bg = gc->bg & 0xff;
71 if (gc->bg & COLOUR_FLAG_256)
72 gce->flags |= GRID_FLAG_BG256;
74 gce->data.attr = gc->attr;
75 gce->data.data = c;
78 /* Check if a cell should be an extended cell. */
79 static int
80 grid_need_extended_cell(const struct grid_cell_entry *gce,
81 const struct grid_cell *gc)
83 if (gce->flags & GRID_FLAG_EXTENDED)
84 return (1);
85 if (gc->attr > 0xff)
86 return (1);
87 if (gc->data.size != 1 || gc->data.width != 1)
88 return (1);
89 if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB))
90 return (1);
91 if (gc->us != 8) /* only supports 256 or RGB */
92 return (1);
93 if (gc->link != 0)
94 return (1);
95 return (0);
98 /* Get an extended cell. */
99 static void
100 grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
101 int flags)
103 u_int at = gl->extdsize + 1;
105 gl->extddata = xreallocarray(gl->extddata, at, sizeof *gl->extddata);
106 gl->extdsize = at;
108 gce->offset = at - 1;
109 gce->flags = (flags | GRID_FLAG_EXTENDED);
112 /* Set cell as extended. */
113 static struct grid_extd_entry *
114 grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce,
115 const struct grid_cell *gc)
117 struct grid_extd_entry *gee;
118 int flags = (gc->flags & ~GRID_FLAG_CLEARED);
119 utf8_char uc;
121 if (~gce->flags & GRID_FLAG_EXTENDED)
122 grid_get_extended_cell(gl, gce, flags);
123 else if (gce->offset >= gl->extdsize)
124 fatalx("offset too big");
125 gl->flags |= GRID_LINE_EXTENDED;
127 utf8_from_data(&gc->data, &uc);
129 gee = &gl->extddata[gce->offset];
130 gee->data = uc;
131 gee->attr = gc->attr;
132 gee->flags = flags;
133 gee->fg = gc->fg;
134 gee->bg = gc->bg;
135 gee->us = gc->us;
136 gee->link = gc->link;
137 return (gee);
140 /* Free up unused extended cells. */
141 static void
142 grid_compact_line(struct grid_line *gl)
144 int new_extdsize = 0;
145 struct grid_extd_entry *new_extddata;
146 struct grid_cell_entry *gce;
147 struct grid_extd_entry *gee;
148 u_int px, idx;
150 if (gl->extdsize == 0)
151 return;
153 for (px = 0; px < gl->cellsize; px++) {
154 gce = &gl->celldata[px];
155 if (gce->flags & GRID_FLAG_EXTENDED)
156 new_extdsize++;
159 if (new_extdsize == 0) {
160 free(gl->extddata);
161 gl->extddata = NULL;
162 gl->extdsize = 0;
163 return;
165 new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata);
167 idx = 0;
168 for (px = 0; px < gl->cellsize; px++) {
169 gce = &gl->celldata[px];
170 if (gce->flags & GRID_FLAG_EXTENDED) {
171 gee = &gl->extddata[gce->offset];
172 memcpy(&new_extddata[idx], gee, sizeof *gee);
173 gce->offset = idx++;
177 free(gl->extddata);
178 gl->extddata = new_extddata;
179 gl->extdsize = new_extdsize;
182 /* Get line data. */
183 struct grid_line *
184 grid_get_line(struct grid *gd, u_int line)
186 return (&gd->linedata[line]);
189 /* Adjust number of lines. */
190 void
191 grid_adjust_lines(struct grid *gd, u_int lines)
193 gd->linedata = xreallocarray(gd->linedata, lines, sizeof *gd->linedata);
196 /* Copy default into a cell. */
197 static void
198 grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg)
200 struct grid_line *gl = &gd->linedata[py];
201 struct grid_cell_entry *gce = &gl->celldata[px];
202 struct grid_extd_entry *gee;
204 memcpy(gce, &grid_cleared_entry, sizeof *gce);
205 if (bg != 8) {
206 if (bg & COLOUR_FLAG_RGB) {
207 grid_get_extended_cell(gl, gce, gce->flags);
208 gee = grid_extended_cell(gl, gce, &grid_cleared_cell);
209 gee->bg = bg;
210 } else {
211 if (bg & COLOUR_FLAG_256)
212 gce->flags |= GRID_FLAG_BG256;
213 gce->data.bg = bg;
218 /* Check grid y position. */
219 static int
220 grid_check_y(struct grid *gd, const char *from, u_int py)
222 if (py >= gd->hsize + gd->sy) {
223 log_debug("%s: y out of range: %u", from, py);
224 return (-1);
226 return (0);
229 /* Check if two styles are (visibly) the same. */
231 grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
233 if (gc1->fg != gc2->fg || gc1->bg != gc2->bg)
234 return (0);
235 if (gc1->attr != gc2->attr || gc1->flags != gc2->flags)
236 return (0);
237 if (gc1->link != gc2->link)
238 return (0);
239 return (1);
242 /* Compare grid cells. Return 1 if equal, 0 if not. */
244 grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2)
246 if (!grid_cells_look_equal(gc1, gc2))
247 return (0);
248 if (gc1->data.width != gc2->data.width)
249 return (0);
250 if (gc1->data.size != gc2->data.size)
251 return (0);
252 return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0);
255 /* Free one line. */
256 static void
257 grid_free_line(struct grid *gd, u_int py)
259 free(gd->linedata[py].celldata);
260 gd->linedata[py].celldata = NULL;
261 free(gd->linedata[py].extddata);
262 gd->linedata[py].extddata = NULL;
265 /* Free several lines. */
266 static void
267 grid_free_lines(struct grid *gd, u_int py, u_int ny)
269 u_int yy;
271 for (yy = py; yy < py + ny; yy++)
272 grid_free_line(gd, yy);
275 /* Create a new grid. */
276 struct grid *
277 grid_create(u_int sx, u_int sy, u_int hlimit)
279 struct grid *gd;
281 gd = xmalloc(sizeof *gd);
282 gd->sx = sx;
283 gd->sy = sy;
285 if (hlimit != 0)
286 gd->flags = GRID_HISTORY;
287 else
288 gd->flags = 0;
290 gd->hscrolled = 0;
291 gd->hsize = 0;
292 gd->hlimit = hlimit;
294 if (gd->sy != 0)
295 gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata);
296 else
297 gd->linedata = NULL;
299 return (gd);
302 /* Destroy grid. */
303 void
304 grid_destroy(struct grid *gd)
306 grid_free_lines(gd, 0, gd->hsize + gd->sy);
308 free(gd->linedata);
310 free(gd);
313 /* Compare grids. */
315 grid_compare(struct grid *ga, struct grid *gb)
317 struct grid_line *gla, *glb;
318 struct grid_cell gca, gcb;
319 u_int xx, yy;
321 if (ga->sx != gb->sx || ga->sy != gb->sy)
322 return (1);
324 for (yy = 0; yy < ga->sy; yy++) {
325 gla = &ga->linedata[yy];
326 glb = &gb->linedata[yy];
327 if (gla->cellsize != glb->cellsize)
328 return (1);
329 for (xx = 0; xx < gla->cellsize; xx++) {
330 grid_get_cell(ga, xx, yy, &gca);
331 grid_get_cell(gb, xx, yy, &gcb);
332 if (!grid_cells_equal(&gca, &gcb))
333 return (1);
337 return (0);
340 /* Trim lines from the history. */
341 static void
342 grid_trim_history(struct grid *gd, u_int ny)
344 grid_free_lines(gd, 0, ny);
345 memmove(&gd->linedata[0], &gd->linedata[ny],
346 (gd->hsize + gd->sy - ny) * (sizeof *gd->linedata));
350 * Collect lines from the history if at the limit. Free the top (oldest) 10%
351 * and shift up.
353 void
354 grid_collect_history(struct grid *gd)
356 u_int ny;
358 if (gd->hsize == 0 || gd->hsize < gd->hlimit)
359 return;
361 ny = gd->hlimit / 10;
362 if (ny < 1)
363 ny = 1;
364 if (ny > gd->hsize)
365 ny = gd->hsize;
368 * Free the lines from 0 to ny then move the remaining lines over
369 * them.
371 grid_trim_history(gd, ny);
373 gd->hsize -= ny;
374 if (gd->hscrolled > gd->hsize)
375 gd->hscrolled = gd->hsize;
378 /* Remove lines from the bottom of the history. */
379 void
380 grid_remove_history(struct grid *gd, u_int ny)
382 u_int yy;
384 if (ny > gd->hsize)
385 return;
386 for (yy = 0; yy < ny; yy++)
387 grid_free_line(gd, gd->hsize + gd->sy - 1 - yy);
388 gd->hsize -= ny;
392 * Scroll the entire visible screen, moving one line into the history. Just
393 * allocate a new line at the bottom and move the history size indicator.
395 void
396 grid_scroll_history(struct grid *gd, u_int bg)
398 u_int yy;
400 yy = gd->hsize + gd->sy;
401 gd->linedata = xreallocarray(gd->linedata, yy + 1,
402 sizeof *gd->linedata);
403 grid_empty_line(gd, yy, bg);
405 gd->hscrolled++;
406 grid_compact_line(&gd->linedata[gd->hsize]);
407 gd->linedata[gd->hsize].time = current_time;
408 gd->hsize++;
411 /* Clear the history. */
412 void
413 grid_clear_history(struct grid *gd)
415 grid_trim_history(gd, gd->hsize);
417 gd->hscrolled = 0;
418 gd->hsize = 0;
420 gd->linedata = xreallocarray(gd->linedata, gd->sy,
421 sizeof *gd->linedata);
424 /* Scroll a region up, moving the top line into the history. */
425 void
426 grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg)
428 struct grid_line *gl_history, *gl_upper;
429 u_int yy;
431 /* Create a space for a new line. */
432 yy = gd->hsize + gd->sy;
433 gd->linedata = xreallocarray(gd->linedata, yy + 1,
434 sizeof *gd->linedata);
436 /* Move the entire screen down to free a space for this line. */
437 gl_history = &gd->linedata[gd->hsize];
438 memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history);
440 /* Adjust the region and find its start and end. */
441 upper++;
442 gl_upper = &gd->linedata[upper];
443 lower++;
445 /* Move the line into the history. */
446 memcpy(gl_history, gl_upper, sizeof *gl_history);
447 gl_history->time = current_time;
449 /* Then move the region up and clear the bottom line. */
450 memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper);
451 grid_empty_line(gd, lower, bg);
453 /* Move the history offset down over the line. */
454 gd->hscrolled++;
455 gd->hsize++;
458 /* Expand line to fit to cell. */
459 static void
460 grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg)
462 struct grid_line *gl;
463 u_int xx;
465 gl = &gd->linedata[py];
466 if (sx <= gl->cellsize)
467 return;
469 if (sx < gd->sx / 4)
470 sx = gd->sx / 4;
471 else if (sx < gd->sx / 2)
472 sx = gd->sx / 2;
473 else if (gd->sx > sx)
474 sx = gd->sx;
476 gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata);
477 for (xx = gl->cellsize; xx < sx; xx++)
478 grid_clear_cell(gd, xx, py, bg);
479 gl->cellsize = sx;
482 /* Empty a line and set background colour if needed. */
483 void
484 grid_empty_line(struct grid *gd, u_int py, u_int bg)
486 memset(&gd->linedata[py], 0, sizeof gd->linedata[py]);
487 if (!COLOUR_DEFAULT(bg))
488 grid_expand_line(gd, py, gd->sx, bg);
491 /* Peek at grid line. */
492 const struct grid_line *
493 grid_peek_line(struct grid *gd, u_int py)
495 if (grid_check_y(gd, __func__, py) != 0)
496 return (NULL);
497 return (&gd->linedata[py]);
500 /* Get cell from line. */
501 static void
502 grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc)
504 struct grid_cell_entry *gce = &gl->celldata[px];
505 struct grid_extd_entry *gee;
507 if (gce->flags & GRID_FLAG_EXTENDED) {
508 if (gce->offset >= gl->extdsize)
509 memcpy(gc, &grid_default_cell, sizeof *gc);
510 else {
511 gee = &gl->extddata[gce->offset];
512 gc->flags = gee->flags;
513 gc->attr = gee->attr;
514 gc->fg = gee->fg;
515 gc->bg = gee->bg;
516 gc->us = gee->us;
517 gc->link = gee->link;
518 utf8_to_data(gee->data, &gc->data);
520 return;
523 gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
524 gc->attr = gce->data.attr;
525 gc->fg = gce->data.fg;
526 if (gce->flags & GRID_FLAG_FG256)
527 gc->fg |= COLOUR_FLAG_256;
528 gc->bg = gce->data.bg;
529 if (gce->flags & GRID_FLAG_BG256)
530 gc->bg |= COLOUR_FLAG_256;
531 gc->us = 8;
532 utf8_set(&gc->data, gce->data.data);
533 gc->link = 0;
536 /* Get cell for reading. */
537 void
538 grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc)
540 if (grid_check_y(gd, __func__, py) != 0 ||
541 px >= gd->linedata[py].cellsize)
542 memcpy(gc, &grid_default_cell, sizeof *gc);
543 else
544 grid_get_cell1(&gd->linedata[py], px, gc);
547 /* Set cell at position. */
548 void
549 grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc)
551 struct grid_line *gl;
552 struct grid_cell_entry *gce;
554 if (grid_check_y(gd, __func__, py) != 0)
555 return;
557 grid_expand_line(gd, py, px + 1, 8);
559 gl = &gd->linedata[py];
560 if (px + 1 > gl->cellused)
561 gl->cellused = px + 1;
563 gce = &gl->celldata[px];
564 if (grid_need_extended_cell(gce, gc))
565 grid_extended_cell(gl, gce, gc);
566 else
567 grid_store_cell(gce, gc, gc->data.data[0]);
570 /* Set padding at position. */
571 void
572 grid_set_padding(struct grid *gd, u_int px, u_int py)
574 grid_set_cell(gd, px, py, &grid_padding_cell);
577 /* Set cells at position. */
578 void
579 grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc,
580 const char *s, size_t slen)
582 struct grid_line *gl;
583 struct grid_cell_entry *gce;
584 struct grid_extd_entry *gee;
585 u_int i;
587 if (grid_check_y(gd, __func__, py) != 0)
588 return;
590 grid_expand_line(gd, py, px + slen, 8);
592 gl = &gd->linedata[py];
593 if (px + slen > gl->cellused)
594 gl->cellused = px + slen;
596 for (i = 0; i < slen; i++) {
597 gce = &gl->celldata[px + i];
598 if (grid_need_extended_cell(gce, gc)) {
599 gee = grid_extended_cell(gl, gce, gc);
600 gee->data = utf8_build_one(s[i]);
601 } else
602 grid_store_cell(gce, gc, s[i]);
606 /* Clear area. */
607 void
608 grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg)
610 struct grid_line *gl;
611 u_int xx, yy, ox, sx;
613 if (nx == 0 || ny == 0)
614 return;
616 if (px == 0 && nx == gd->sx) {
617 grid_clear_lines(gd, py, ny, bg);
618 return;
621 if (grid_check_y(gd, __func__, py) != 0)
622 return;
623 if (grid_check_y(gd, __func__, py + ny - 1) != 0)
624 return;
626 for (yy = py; yy < py + ny; yy++) {
627 gl = &gd->linedata[yy];
629 sx = gd->sx;
630 if (sx > gl->cellsize)
631 sx = gl->cellsize;
632 ox = nx;
633 if (COLOUR_DEFAULT(bg)) {
634 if (px > sx)
635 continue;
636 if (px + nx > sx)
637 ox = sx - px;
640 grid_expand_line(gd, yy, px + ox, 8); /* default bg first */
641 for (xx = px; xx < px + ox; xx++)
642 grid_clear_cell(gd, xx, yy, bg);
646 /* Clear lines. This just frees and truncates the lines. */
647 void
648 grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg)
650 u_int yy;
652 if (ny == 0)
653 return;
655 if (grid_check_y(gd, __func__, py) != 0)
656 return;
657 if (grid_check_y(gd, __func__, py + ny - 1) != 0)
658 return;
660 for (yy = py; yy < py + ny; yy++) {
661 grid_free_line(gd, yy);
662 grid_empty_line(gd, yy, bg);
664 if (py != 0)
665 gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
668 /* Move a group of lines. */
669 void
670 grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg)
672 u_int yy;
674 if (ny == 0 || py == dy)
675 return;
677 if (grid_check_y(gd, __func__, py) != 0)
678 return;
679 if (grid_check_y(gd, __func__, py + ny - 1) != 0)
680 return;
681 if (grid_check_y(gd, __func__, dy) != 0)
682 return;
683 if (grid_check_y(gd, __func__, dy + ny - 1) != 0)
684 return;
686 /* Free any lines which are being replaced. */
687 for (yy = dy; yy < dy + ny; yy++) {
688 if (yy >= py && yy < py + ny)
689 continue;
690 grid_free_line(gd, yy);
692 if (dy != 0)
693 gd->linedata[dy - 1].flags &= ~GRID_LINE_WRAPPED;
695 memmove(&gd->linedata[dy], &gd->linedata[py],
696 ny * (sizeof *gd->linedata));
699 * Wipe any lines that have been moved (without freeing them - they are
700 * still present).
702 for (yy = py; yy < py + ny; yy++) {
703 if (yy < dy || yy >= dy + ny)
704 grid_empty_line(gd, yy, bg);
706 if (py != 0 && (py < dy || py >= dy + ny))
707 gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED;
710 /* Move a group of cells. */
711 void
712 grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx,
713 u_int bg)
715 struct grid_line *gl;
716 u_int xx;
718 if (nx == 0 || px == dx)
719 return;
721 if (grid_check_y(gd, __func__, py) != 0)
722 return;
723 gl = &gd->linedata[py];
725 grid_expand_line(gd, py, px + nx, 8);
726 grid_expand_line(gd, py, dx + nx, 8);
727 memmove(&gl->celldata[dx], &gl->celldata[px],
728 nx * sizeof *gl->celldata);
729 if (dx + nx > gl->cellused)
730 gl->cellused = dx + nx;
732 /* Wipe any cells that have been moved. */
733 for (xx = px; xx < px + nx; xx++) {
734 if (xx >= dx && xx < dx + nx)
735 continue;
736 grid_clear_cell(gd, xx, py, bg);
740 /* Get ANSI foreground sequence. */
741 static size_t
742 grid_string_cells_fg(const struct grid_cell *gc, int *values)
744 size_t n;
745 u_char r, g, b;
747 n = 0;
748 if (gc->fg & COLOUR_FLAG_256) {
749 values[n++] = 38;
750 values[n++] = 5;
751 values[n++] = gc->fg & 0xff;
752 } else if (gc->fg & COLOUR_FLAG_RGB) {
753 values[n++] = 38;
754 values[n++] = 2;
755 colour_split_rgb(gc->fg, &r, &g, &b);
756 values[n++] = r;
757 values[n++] = g;
758 values[n++] = b;
759 } else {
760 switch (gc->fg) {
761 case 0:
762 case 1:
763 case 2:
764 case 3:
765 case 4:
766 case 5:
767 case 6:
768 case 7:
769 values[n++] = gc->fg + 30;
770 break;
771 case 8:
772 values[n++] = 39;
773 break;
774 case 90:
775 case 91:
776 case 92:
777 case 93:
778 case 94:
779 case 95:
780 case 96:
781 case 97:
782 values[n++] = gc->fg;
783 break;
786 return (n);
789 /* Get ANSI background sequence. */
790 static size_t
791 grid_string_cells_bg(const struct grid_cell *gc, int *values)
793 size_t n;
794 u_char r, g, b;
796 n = 0;
797 if (gc->bg & COLOUR_FLAG_256) {
798 values[n++] = 48;
799 values[n++] = 5;
800 values[n++] = gc->bg & 0xff;
801 } else if (gc->bg & COLOUR_FLAG_RGB) {
802 values[n++] = 48;
803 values[n++] = 2;
804 colour_split_rgb(gc->bg, &r, &g, &b);
805 values[n++] = r;
806 values[n++] = g;
807 values[n++] = b;
808 } else {
809 switch (gc->bg) {
810 case 0:
811 case 1:
812 case 2:
813 case 3:
814 case 4:
815 case 5:
816 case 6:
817 case 7:
818 values[n++] = gc->bg + 40;
819 break;
820 case 8:
821 values[n++] = 49;
822 break;
823 case 90:
824 case 91:
825 case 92:
826 case 93:
827 case 94:
828 case 95:
829 case 96:
830 case 97:
831 values[n++] = gc->bg + 10;
832 break;
835 return (n);
838 /* Get underscore colour sequence. */
839 static size_t
840 grid_string_cells_us(const struct grid_cell *gc, int *values)
842 size_t n;
843 u_char r, g, b;
845 n = 0;
846 if (gc->us & COLOUR_FLAG_256) {
847 values[n++] = 58;
848 values[n++] = 5;
849 values[n++] = gc->us & 0xff;
850 } else if (gc->us & COLOUR_FLAG_RGB) {
851 values[n++] = 58;
852 values[n++] = 2;
853 colour_split_rgb(gc->us, &r, &g, &b);
854 values[n++] = r;
855 values[n++] = g;
856 values[n++] = b;
858 return (n);
861 /* Add on SGR code. */
862 static void
863 grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc,
864 int *oldc, size_t nnewc, size_t noldc, int flags)
866 u_int i;
867 char tmp[64];
868 int reset = (n != 0 && s[0] == 0);
870 if (nnewc == 0)
871 return; /* no code to add */
872 if (!reset &&
873 nnewc == noldc &&
874 memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0)
875 return; /* no reset and colour unchanged */
876 if (reset && (newc[0] == 49 || newc[0] == 39))
877 return; /* reset and colour default */
879 if (flags & GRID_STRING_ESCAPE_SEQUENCES)
880 strlcat(buf, "\\033[", len);
881 else
882 strlcat(buf, "\033[", len);
883 for (i = 0; i < nnewc; i++) {
884 if (i + 1 < nnewc)
885 xsnprintf(tmp, sizeof tmp, "%d;", newc[i]);
886 else
887 xsnprintf(tmp, sizeof tmp, "%d", newc[i]);
888 strlcat(buf, tmp, len);
890 strlcat(buf, "m", len);
893 static int
894 grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id,
895 const char *uri, int flags)
897 char *tmp;
899 if (strlen(uri) + strlen(id) + 17 >= len)
900 return (0);
902 if (flags & GRID_STRING_ESCAPE_SEQUENCES)
903 strlcat(buf, "\\033]8;", len);
904 else
905 strlcat(buf, "\033]8;", len);
906 if (*id != '\0') {
907 xasprintf(&tmp, "id=%s;", id);
908 strlcat(buf, tmp, len);
909 free(tmp);
910 } else
911 strlcat(buf, ";", len);
912 strlcat(buf, uri, len);
913 if (flags & GRID_STRING_ESCAPE_SEQUENCES)
914 strlcat(buf, "\\033\\\\", len);
915 else
916 strlcat(buf, "\033\\", len);
917 return (1);
921 * Returns ANSI code to set particular attributes (colour, bold and so on)
922 * given a current state.
924 static void
925 grid_string_cells_code(const struct grid_cell *lastgc,
926 const struct grid_cell *gc, char *buf, size_t len, int flags,
927 struct screen *sc, int *has_link)
929 int oldc[64], newc[64], s[128];
930 size_t noldc, nnewc, n, i;
931 u_int attr = gc->attr, lastattr = lastgc->attr;
932 char tmp[64];
933 const char *uri, *id;
935 static const struct {
936 u_int mask;
937 u_int code;
938 } attrs[] = {
939 { GRID_ATTR_BRIGHT, 1 },
940 { GRID_ATTR_DIM, 2 },
941 { GRID_ATTR_ITALICS, 3 },
942 { GRID_ATTR_UNDERSCORE, 4 },
943 { GRID_ATTR_BLINK, 5 },
944 { GRID_ATTR_REVERSE, 7 },
945 { GRID_ATTR_HIDDEN, 8 },
946 { GRID_ATTR_STRIKETHROUGH, 9 },
947 { GRID_ATTR_UNDERSCORE_2, 42 },
948 { GRID_ATTR_UNDERSCORE_3, 43 },
949 { GRID_ATTR_UNDERSCORE_4, 44 },
950 { GRID_ATTR_UNDERSCORE_5, 45 },
951 { GRID_ATTR_OVERLINE, 53 },
953 n = 0;
955 /* If any attribute is removed, begin with 0. */
956 for (i = 0; i < nitems(attrs); i++) {
957 if (((~attr & attrs[i].mask) &&
958 (lastattr & attrs[i].mask)) ||
959 (lastgc->us != 8 && gc->us == 8)) {
960 s[n++] = 0;
961 lastattr &= GRID_ATTR_CHARSET;
962 break;
965 /* For each attribute that is newly set, add its code. */
966 for (i = 0; i < nitems(attrs); i++) {
967 if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask))
968 s[n++] = attrs[i].code;
971 /* Write the attributes. */
972 *buf = '\0';
973 if (n > 0) {
974 if (flags & GRID_STRING_ESCAPE_SEQUENCES)
975 strlcat(buf, "\\033[", len);
976 else
977 strlcat(buf, "\033[", len);
978 for (i = 0; i < n; i++) {
979 if (s[i] < 10)
980 xsnprintf(tmp, sizeof tmp, "%d", s[i]);
981 else {
982 xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10,
983 s[i] % 10);
985 strlcat(buf, tmp, len);
986 if (i + 1 < n)
987 strlcat(buf, ";", len);
989 strlcat(buf, "m", len);
992 /* If the foreground colour changed, write its parameters. */
993 nnewc = grid_string_cells_fg(gc, newc);
994 noldc = grid_string_cells_fg(lastgc, oldc);
995 grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
996 flags);
998 /* If the background colour changed, append its parameters. */
999 nnewc = grid_string_cells_bg(gc, newc);
1000 noldc = grid_string_cells_bg(lastgc, oldc);
1001 grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
1002 flags);
1004 /* If the underscore colour changed, append its parameters. */
1005 nnewc = grid_string_cells_us(gc, newc);
1006 noldc = grid_string_cells_us(lastgc, oldc);
1007 grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc,
1008 flags);
1010 /* Append shift in/shift out if needed. */
1011 if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) {
1012 if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1013 strlcat(buf, "\\016", len); /* SO */
1014 else
1015 strlcat(buf, "\016", len); /* SO */
1017 if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) {
1018 if (flags & GRID_STRING_ESCAPE_SEQUENCES)
1019 strlcat(buf, "\\017", len); /* SI */
1020 else
1021 strlcat(buf, "\017", len); /* SI */
1024 /* Add hyperlink if changed. */
1025 if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) {
1026 if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) {
1027 *has_link = grid_string_cells_add_hyperlink(buf, len,
1028 id, uri, flags);
1029 } else if (*has_link) {
1030 grid_string_cells_add_hyperlink(buf, len, "", "",
1031 flags);
1032 *has_link = 0;
1037 /* Convert cells into a string. */
1038 char *
1039 grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx,
1040 struct grid_cell **lastgc, int flags, struct screen *s)
1042 struct grid_cell gc;
1043 static struct grid_cell lastgc1;
1044 const char *data;
1045 char *buf, code[8192];
1046 size_t len, off, size, codelen;
1047 u_int xx, end;
1048 int has_link = 0;
1049 const struct grid_line *gl;
1051 if (lastgc != NULL && *lastgc == NULL) {
1052 memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1);
1053 *lastgc = &lastgc1;
1056 len = 128;
1057 buf = xmalloc(len);
1058 off = 0;
1060 gl = grid_peek_line(gd, py);
1061 if (flags & GRID_STRING_EMPTY_CELLS)
1062 end = gl->cellsize;
1063 else
1064 end = gl->cellused;
1065 for (xx = px; xx < px + nx; xx++) {
1066 if (gl == NULL || xx >= end)
1067 break;
1068 grid_get_cell(gd, xx, py, &gc);
1069 if (gc.flags & GRID_FLAG_PADDING)
1070 continue;
1072 if (flags & GRID_STRING_WITH_SEQUENCES) {
1073 grid_string_cells_code(*lastgc, &gc, code, sizeof code,
1074 flags, s, &has_link);
1075 codelen = strlen(code);
1076 memcpy(*lastgc, &gc, sizeof **lastgc);
1077 } else
1078 codelen = 0;
1080 data = gc.data.data;
1081 size = gc.data.size;
1082 if ((flags & GRID_STRING_ESCAPE_SEQUENCES) &&
1083 size == 1 &&
1084 *data == '\\') {
1085 data = "\\\\";
1086 size = 2;
1089 while (len < off + size + codelen + 1) {
1090 buf = xreallocarray(buf, 2, len);
1091 len *= 2;
1094 if (codelen != 0) {
1095 memcpy(buf + off, code, codelen);
1096 off += codelen;
1098 memcpy(buf + off, data, size);
1099 off += size;
1102 if (has_link) {
1103 grid_string_cells_add_hyperlink(code, sizeof code, "", "",
1104 flags);
1105 codelen = strlen(code);
1106 while (len < off + size + codelen + 1) {
1107 buf = xreallocarray(buf, 2, len);
1108 len *= 2;
1110 memcpy(buf + off, code, codelen);
1111 off += codelen;
1114 if (flags & GRID_STRING_TRIM_SPACES) {
1115 while (off > 0 && buf[off - 1] == ' ')
1116 off--;
1118 buf[off] = '\0';
1120 return (buf);
1124 * Duplicate a set of lines between two grids. Both source and destination
1125 * should be big enough.
1127 void
1128 grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy,
1129 u_int ny)
1131 struct grid_line *dstl, *srcl;
1132 u_int yy;
1134 if (dy + ny > dst->hsize + dst->sy)
1135 ny = dst->hsize + dst->sy - dy;
1136 if (sy + ny > src->hsize + src->sy)
1137 ny = src->hsize + src->sy - sy;
1138 grid_free_lines(dst, dy, ny);
1140 for (yy = 0; yy < ny; yy++) {
1141 srcl = &src->linedata[sy];
1142 dstl = &dst->linedata[dy];
1144 memcpy(dstl, srcl, sizeof *dstl);
1145 if (srcl->cellsize != 0) {
1146 dstl->celldata = xreallocarray(NULL,
1147 srcl->cellsize, sizeof *dstl->celldata);
1148 memcpy(dstl->celldata, srcl->celldata,
1149 srcl->cellsize * sizeof *dstl->celldata);
1150 } else
1151 dstl->celldata = NULL;
1152 if (srcl->extdsize != 0) {
1153 dstl->extdsize = srcl->extdsize;
1154 dstl->extddata = xreallocarray(NULL, dstl->extdsize,
1155 sizeof *dstl->extddata);
1156 memcpy(dstl->extddata, srcl->extddata, dstl->extdsize *
1157 sizeof *dstl->extddata);
1158 } else
1159 dstl->extddata = NULL;
1161 sy++;
1162 dy++;
1166 /* Mark line as dead. */
1167 static void
1168 grid_reflow_dead(struct grid_line *gl)
1170 memset(gl, 0, sizeof *gl);
1171 gl->flags = GRID_LINE_DEAD;
1174 /* Add lines, return the first new one. */
1175 static struct grid_line *
1176 grid_reflow_add(struct grid *gd, u_int n)
1178 struct grid_line *gl;
1179 u_int sy = gd->sy + n;
1181 gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata);
1182 gl = &gd->linedata[gd->sy];
1183 memset(gl, 0, n * (sizeof *gl));
1184 gd->sy = sy;
1185 return (gl);
1188 /* Move a line across. */
1189 static struct grid_line *
1190 grid_reflow_move(struct grid *gd, struct grid_line *from)
1192 struct grid_line *to;
1194 to = grid_reflow_add(gd, 1);
1195 memcpy(to, from, sizeof *to);
1196 grid_reflow_dead(from);
1197 return (to);
1200 /* Join line below onto this one. */
1201 static void
1202 grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
1203 u_int width, int already)
1205 struct grid_line *gl, *from = NULL;
1206 struct grid_cell gc;
1207 u_int lines, left, i, to, line, want = 0;
1208 u_int at;
1209 int wrapped = 1;
1212 * Add a new target line.
1214 if (!already) {
1215 to = target->sy;
1216 gl = grid_reflow_move(target, &gd->linedata[yy]);
1217 } else {
1218 to = target->sy - 1;
1219 gl = &target->linedata[to];
1221 at = gl->cellused;
1224 * Loop until no more to consume or the target line is full.
1226 lines = 0;
1227 for (;;) {
1229 * If this is now the last line, there is nothing more to be
1230 * done.
1232 if (yy + 1 + lines == gd->hsize + gd->sy)
1233 break;
1234 line = yy + 1 + lines;
1236 /* If the next line is empty, skip it. */
1237 if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
1238 wrapped = 0;
1239 if (gd->linedata[line].cellused == 0) {
1240 if (!wrapped)
1241 break;
1242 lines++;
1243 continue;
1247 * Is the destination line now full? Copy the first character
1248 * separately because we need to leave "from" set to the last
1249 * line if this line is full.
1251 grid_get_cell1(&gd->linedata[line], 0, &gc);
1252 if (width + gc.data.width > sx)
1253 break;
1254 width += gc.data.width;
1255 grid_set_cell(target, at, to, &gc);
1256 at++;
1258 /* Join as much more as possible onto the current line. */
1259 from = &gd->linedata[line];
1260 for (want = 1; want < from->cellused; want++) {
1261 grid_get_cell1(from, want, &gc);
1262 if (width + gc.data.width > sx)
1263 break;
1264 width += gc.data.width;
1266 grid_set_cell(target, at, to, &gc);
1267 at++;
1269 lines++;
1272 * If this line wasn't wrapped or we didn't consume the entire
1273 * line, don't try to join any further lines.
1275 if (!wrapped || want != from->cellused || width == sx)
1276 break;
1278 if (lines == 0)
1279 return;
1282 * If we didn't consume the entire final line, then remove what we did
1283 * consume. If we consumed the entire line and it wasn't wrapped,
1284 * remove the wrap flag from this line.
1286 left = from->cellused - want;
1287 if (left != 0) {
1288 grid_move_cells(gd, 0, want, yy + lines, left, 8);
1289 from->cellsize = from->cellused = left;
1290 lines--;
1291 } else if (!wrapped)
1292 gl->flags &= ~GRID_LINE_WRAPPED;
1294 /* Remove the lines that were completely consumed. */
1295 for (i = yy + 1; i < yy + 1 + lines; i++) {
1296 free(gd->linedata[i].celldata);
1297 free(gd->linedata[i].extddata);
1298 grid_reflow_dead(&gd->linedata[i]);
1301 /* Adjust scroll position. */
1302 if (gd->hscrolled > to + lines)
1303 gd->hscrolled -= lines;
1304 else if (gd->hscrolled > to)
1305 gd->hscrolled = to;
1308 /* Split this line into several new ones */
1309 static void
1310 grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
1311 u_int at)
1313 struct grid_line *gl = &gd->linedata[yy], *first;
1314 struct grid_cell gc;
1315 u_int line, lines, width, i, xx;
1316 u_int used = gl->cellused;
1317 int flags = gl->flags;
1319 /* How many lines do we need to insert? We know we need at least two. */
1320 if (~gl->flags & GRID_LINE_EXTENDED)
1321 lines = 1 + (gl->cellused - 1) / sx;
1322 else {
1323 lines = 2;
1324 width = 0;
1325 for (i = at; i < used; i++) {
1326 grid_get_cell1(gl, i, &gc);
1327 if (width + gc.data.width > sx) {
1328 lines++;
1329 width = 0;
1331 width += gc.data.width;
1335 /* Insert new lines. */
1336 line = target->sy + 1;
1337 first = grid_reflow_add(target, lines);
1339 /* Copy sections from the original line. */
1340 width = 0;
1341 xx = 0;
1342 for (i = at; i < used; i++) {
1343 grid_get_cell1(gl, i, &gc);
1344 if (width + gc.data.width > sx) {
1345 target->linedata[line].flags |= GRID_LINE_WRAPPED;
1347 line++;
1348 width = 0;
1349 xx = 0;
1351 width += gc.data.width;
1352 grid_set_cell(target, xx, line, &gc);
1353 xx++;
1355 if (flags & GRID_LINE_WRAPPED)
1356 target->linedata[line].flags |= GRID_LINE_WRAPPED;
1358 /* Move the remainder of the original line. */
1359 gl->cellsize = gl->cellused = at;
1360 gl->flags |= GRID_LINE_WRAPPED;
1361 memcpy(first, gl, sizeof *first);
1362 grid_reflow_dead(gl);
1364 /* Adjust the scroll position. */
1365 if (yy <= gd->hscrolled)
1366 gd->hscrolled += lines - 1;
1369 * If the original line had the wrapped flag and there is still space
1370 * in the last new line, try to join with the next lines.
1372 if (width < sx && (flags & GRID_LINE_WRAPPED))
1373 grid_reflow_join(target, gd, sx, yy, width, 1);
1376 /* Reflow lines on grid to new width. */
1377 void
1378 grid_reflow(struct grid *gd, u_int sx)
1380 struct grid *target;
1381 struct grid_line *gl;
1382 struct grid_cell gc;
1383 u_int yy, width, i, at;
1386 * Create a destination grid. This is just used as a container for the
1387 * line data and may not be fully valid.
1389 target = grid_create(gd->sx, 0, 0);
1392 * Loop over each source line.
1394 for (yy = 0; yy < gd->hsize + gd->sy; yy++) {
1395 gl = &gd->linedata[yy];
1396 if (gl->flags & GRID_LINE_DEAD)
1397 continue;
1400 * Work out the width of this line. at is the point at which
1401 * the available width is hit, and width is the full line
1402 * width.
1404 at = width = 0;
1405 if (~gl->flags & GRID_LINE_EXTENDED) {
1406 width = gl->cellused;
1407 if (width > sx)
1408 at = sx;
1409 else
1410 at = width;
1411 } else {
1412 for (i = 0; i < gl->cellused; i++) {
1413 grid_get_cell1(gl, i, &gc);
1414 if (at == 0 && width + gc.data.width > sx)
1415 at = i;
1416 width += gc.data.width;
1421 * If the line is exactly right, just move it across
1422 * unchanged.
1424 if (width == sx) {
1425 grid_reflow_move(target, gl);
1426 continue;
1430 * If the line is too big, it needs to be split, whether or not
1431 * it was previously wrapped.
1433 if (width > sx) {
1434 grid_reflow_split(target, gd, sx, yy, at);
1435 continue;
1439 * If the line was previously wrapped, join as much as possible
1440 * of the next line.
1442 if (gl->flags & GRID_LINE_WRAPPED)
1443 grid_reflow_join(target, gd, sx, yy, width, 0);
1444 else
1445 grid_reflow_move(target, gl);
1449 * Replace the old grid with the new.
1451 if (target->sy < gd->sy)
1452 grid_reflow_add(target, gd->sy - target->sy);
1453 gd->hsize = target->sy - gd->sy;
1454 if (gd->hscrolled > gd->hsize)
1455 gd->hscrolled = gd->hsize;
1456 free(gd->linedata);
1457 gd->linedata = target->linedata;
1458 free(target);
1461 /* Convert to position based on wrapped lines. */
1462 void
1463 grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy)
1465 u_int ax = 0, ay = 0, yy;
1467 for (yy = 0; yy < py; yy++) {
1468 if (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
1469 ax += gd->linedata[yy].cellused;
1470 else {
1471 ax = 0;
1472 ay++;
1475 if (px >= gd->linedata[yy].cellused)
1476 ax = UINT_MAX;
1477 else
1478 ax += px;
1479 *wx = ax;
1480 *wy = ay;
1483 /* Convert position based on wrapped lines back. */
1484 void
1485 grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy)
1487 u_int yy, ay = 0;
1489 for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) {
1490 if (ay == wy)
1491 break;
1492 if (~gd->linedata[yy].flags & GRID_LINE_WRAPPED)
1493 ay++;
1497 * yy is now 0 on the unwrapped line which contains wx. Walk forwards
1498 * until we find the end or the line now containing wx.
1500 if (wx == UINT_MAX) {
1501 while (gd->linedata[yy].flags & GRID_LINE_WRAPPED)
1502 yy++;
1503 wx = gd->linedata[yy].cellused;
1504 } else {
1505 while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) {
1506 if (wx < gd->linedata[yy].cellused)
1507 break;
1508 wx -= gd->linedata[yy].cellused;
1509 yy++;
1512 *px = wx;
1513 *py = yy;
1516 /* Get length of line. */
1517 u_int
1518 grid_line_length(struct grid *gd, u_int py)
1520 struct grid_cell gc;
1521 u_int px;
1523 px = grid_get_line(gd, py)->cellsize;
1524 if (px > gd->sx)
1525 px = gd->sx;
1526 while (px > 0) {
1527 grid_get_cell(gd, px - 1, py, &gc);
1528 if ((gc.flags & GRID_FLAG_PADDING) ||
1529 gc.data.size != 1 ||
1530 *gc.data.data != ' ')
1531 break;
1532 px--;
1534 return (px);