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>
27 * Grid data. This is the basic data structure that represents what is shown on
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, 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, 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, 0
55 static const struct grid_cell_entry grid_cleared_entry
= {
56 GRID_FLAG_CLEARED
, { .data
= { 0, 8, 8, ' ' } }
59 /* Store cell in entry. */
61 grid_store_cell(struct grid_cell_entry
*gce
, const struct grid_cell
*gc
,
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
;
78 /* Check if a cell should be an extended cell. */
80 grid_need_extended_cell(const struct grid_cell_entry
*gce
,
81 const struct grid_cell
*gc
)
83 if (gce
->flags
& GRID_FLAG_EXTENDED
)
87 if (gc
->data
.size
!= 1 || gc
->data
.width
!= 1)
89 if ((gc
->fg
& COLOUR_FLAG_RGB
) || (gc
->bg
& COLOUR_FLAG_RGB
))
91 if (gc
->us
!= 0) /* only supports 256 or RGB */
96 /* Get an extended cell. */
98 grid_get_extended_cell(struct grid_line
*gl
, struct grid_cell_entry
*gce
,
101 u_int at
= gl
->extdsize
+ 1;
103 gl
->extddata
= xreallocarray(gl
->extddata
, at
, sizeof *gl
->extddata
);
106 gce
->offset
= at
- 1;
107 gce
->flags
= (flags
| GRID_FLAG_EXTENDED
);
110 /* Set cell as extended. */
111 static struct grid_extd_entry
*
112 grid_extended_cell(struct grid_line
*gl
, struct grid_cell_entry
*gce
,
113 const struct grid_cell
*gc
)
115 struct grid_extd_entry
*gee
;
116 int flags
= (gc
->flags
& ~GRID_FLAG_CLEARED
);
119 if (~gce
->flags
& GRID_FLAG_EXTENDED
)
120 grid_get_extended_cell(gl
, gce
, flags
);
121 else if (gce
->offset
>= gl
->extdsize
)
122 fatalx("offset too big");
123 gl
->flags
|= GRID_LINE_EXTENDED
;
125 utf8_from_data(&gc
->data
, &uc
);
127 gee
= &gl
->extddata
[gce
->offset
];
129 gee
->attr
= gc
->attr
;
137 /* Free up unused extended cells. */
139 grid_compact_line(struct grid_line
*gl
)
141 int new_extdsize
= 0;
142 struct grid_extd_entry
*new_extddata
;
143 struct grid_cell_entry
*gce
;
144 struct grid_extd_entry
*gee
;
147 if (gl
->extdsize
== 0)
150 for (px
= 0; px
< gl
->cellsize
; px
++) {
151 gce
= &gl
->celldata
[px
];
152 if (gce
->flags
& GRID_FLAG_EXTENDED
)
156 if (new_extdsize
== 0) {
162 new_extddata
= xreallocarray(NULL
, new_extdsize
, sizeof *gl
->extddata
);
165 for (px
= 0; px
< gl
->cellsize
; px
++) {
166 gce
= &gl
->celldata
[px
];
167 if (gce
->flags
& GRID_FLAG_EXTENDED
) {
168 gee
= &gl
->extddata
[gce
->offset
];
169 memcpy(&new_extddata
[idx
], gee
, sizeof *gee
);
175 gl
->extddata
= new_extddata
;
176 gl
->extdsize
= new_extdsize
;
181 grid_get_line(struct grid
*gd
, u_int line
)
183 return (&gd
->linedata
[line
]);
186 /* Adjust number of lines. */
188 grid_adjust_lines(struct grid
*gd
, u_int lines
)
190 gd
->linedata
= xreallocarray(gd
->linedata
, lines
, sizeof *gd
->linedata
);
193 /* Copy default into a cell. */
195 grid_clear_cell(struct grid
*gd
, u_int px
, u_int py
, u_int bg
)
197 struct grid_line
*gl
= &gd
->linedata
[py
];
198 struct grid_cell_entry
*gce
= &gl
->celldata
[px
];
199 struct grid_extd_entry
*gee
;
201 memcpy(gce
, &grid_cleared_entry
, sizeof *gce
);
203 if (bg
& COLOUR_FLAG_RGB
) {
204 grid_get_extended_cell(gl
, gce
, gce
->flags
);
205 gee
= grid_extended_cell(gl
, gce
, &grid_cleared_cell
);
208 if (bg
& COLOUR_FLAG_256
)
209 gce
->flags
|= GRID_FLAG_BG256
;
215 /* Check grid y position. */
217 grid_check_y(struct grid
*gd
, const char *from
, u_int py
)
219 if (py
>= gd
->hsize
+ gd
->sy
) {
220 log_debug("%s: y out of range: %u", from
, py
);
226 /* Check if two styles are (visibly) the same. */
228 grid_cells_look_equal(const struct grid_cell
*gc1
, const struct grid_cell
*gc2
)
230 if (gc1
->fg
!= gc2
->fg
|| gc1
->bg
!= gc2
->bg
)
232 if (gc1
->attr
!= gc2
->attr
|| gc1
->flags
!= gc2
->flags
)
237 /* Compare grid cells. Return 1 if equal, 0 if not. */
239 grid_cells_equal(const struct grid_cell
*gc1
, const struct grid_cell
*gc2
)
241 if (!grid_cells_look_equal(gc1
, gc2
))
243 if (gc1
->data
.width
!= gc2
->data
.width
)
245 if (gc1
->data
.size
!= gc2
->data
.size
)
247 return (memcmp(gc1
->data
.data
, gc2
->data
.data
, gc1
->data
.size
) == 0);
252 grid_free_line(struct grid
*gd
, u_int py
)
254 free(gd
->linedata
[py
].celldata
);
255 gd
->linedata
[py
].celldata
= NULL
;
256 free(gd
->linedata
[py
].extddata
);
257 gd
->linedata
[py
].extddata
= NULL
;
260 /* Free several lines. */
262 grid_free_lines(struct grid
*gd
, u_int py
, u_int ny
)
266 for (yy
= py
; yy
< py
+ ny
; yy
++)
267 grid_free_line(gd
, yy
);
270 /* Create a new grid. */
272 grid_create(u_int sx
, u_int sy
, u_int hlimit
)
276 gd
= xmalloc(sizeof *gd
);
281 gd
->flags
= GRID_HISTORY
;
290 gd
->linedata
= xcalloc(gd
->sy
, sizeof *gd
->linedata
);
299 grid_destroy(struct grid
*gd
)
301 grid_free_lines(gd
, 0, gd
->hsize
+ gd
->sy
);
310 grid_compare(struct grid
*ga
, struct grid
*gb
)
312 struct grid_line
*gla
, *glb
;
313 struct grid_cell gca
, gcb
;
316 if (ga
->sx
!= gb
->sx
|| ga
->sy
!= gb
->sy
)
319 for (yy
= 0; yy
< ga
->sy
; yy
++) {
320 gla
= &ga
->linedata
[yy
];
321 glb
= &gb
->linedata
[yy
];
322 if (gla
->cellsize
!= glb
->cellsize
)
324 for (xx
= 0; xx
< gla
->cellsize
; xx
++) {
325 grid_get_cell(ga
, xx
, yy
, &gca
);
326 grid_get_cell(gb
, xx
, yy
, &gcb
);
327 if (!grid_cells_equal(&gca
, &gcb
))
335 /* Trim lines from the history. */
337 grid_trim_history(struct grid
*gd
, u_int ny
)
339 grid_free_lines(gd
, 0, ny
);
340 memmove(&gd
->linedata
[0], &gd
->linedata
[ny
],
341 (gd
->hsize
+ gd
->sy
- ny
) * (sizeof *gd
->linedata
));
345 * Collect lines from the history if at the limit. Free the top (oldest) 10%
349 grid_collect_history(struct grid
*gd
)
353 if (gd
->hsize
== 0 || gd
->hsize
< gd
->hlimit
)
356 ny
= gd
->hlimit
/ 10;
363 * Free the lines from 0 to ny then move the remaining lines over
366 grid_trim_history(gd
, ny
);
369 if (gd
->hscrolled
> gd
->hsize
)
370 gd
->hscrolled
= gd
->hsize
;
373 /* Remove lines from the bottom of the history. */
375 grid_remove_history(struct grid
*gd
, u_int ny
)
381 for (yy
= 0; yy
< ny
; yy
++)
382 grid_free_line(gd
, gd
->hsize
+ gd
->sy
- 1 - yy
);
387 * Scroll the entire visible screen, moving one line into the history. Just
388 * allocate a new line at the bottom and move the history size indicator.
391 grid_scroll_history(struct grid
*gd
, u_int bg
)
395 yy
= gd
->hsize
+ gd
->sy
;
396 gd
->linedata
= xreallocarray(gd
->linedata
, yy
+ 1,
397 sizeof *gd
->linedata
);
398 grid_empty_line(gd
, yy
, bg
);
401 grid_compact_line(&gd
->linedata
[gd
->hsize
]);
405 /* Clear the history. */
407 grid_clear_history(struct grid
*gd
)
409 grid_trim_history(gd
, gd
->hsize
);
414 gd
->linedata
= xreallocarray(gd
->linedata
, gd
->sy
,
415 sizeof *gd
->linedata
);
418 /* Scroll a region up, moving the top line into the history. */
420 grid_scroll_history_region(struct grid
*gd
, u_int upper
, u_int lower
, u_int bg
)
422 struct grid_line
*gl_history
, *gl_upper
;
425 /* Create a space for a new line. */
426 yy
= gd
->hsize
+ gd
->sy
;
427 gd
->linedata
= xreallocarray(gd
->linedata
, yy
+ 1,
428 sizeof *gd
->linedata
);
430 /* Move the entire screen down to free a space for this line. */
431 gl_history
= &gd
->linedata
[gd
->hsize
];
432 memmove(gl_history
+ 1, gl_history
, gd
->sy
* sizeof *gl_history
);
434 /* Adjust the region and find its start and end. */
436 gl_upper
= &gd
->linedata
[upper
];
439 /* Move the line into the history. */
440 memcpy(gl_history
, gl_upper
, sizeof *gl_history
);
442 /* Then move the region up and clear the bottom line. */
443 memmove(gl_upper
, gl_upper
+ 1, (lower
- upper
) * sizeof *gl_upper
);
444 grid_empty_line(gd
, lower
, bg
);
446 /* Move the history offset down over the line. */
451 /* Expand line to fit to cell. */
453 grid_expand_line(struct grid
*gd
, u_int py
, u_int sx
, u_int bg
)
455 struct grid_line
*gl
;
458 gl
= &gd
->linedata
[py
];
459 if (sx
<= gl
->cellsize
)
464 else if (sx
< gd
->sx
/ 2)
466 else if (gd
->sx
> sx
)
469 gl
->celldata
= xreallocarray(gl
->celldata
, sx
, sizeof *gl
->celldata
);
470 for (xx
= gl
->cellsize
; xx
< sx
; xx
++)
471 grid_clear_cell(gd
, xx
, py
, bg
);
475 /* Empty a line and set background colour if needed. */
477 grid_empty_line(struct grid
*gd
, u_int py
, u_int bg
)
479 memset(&gd
->linedata
[py
], 0, sizeof gd
->linedata
[py
]);
480 if (!COLOUR_DEFAULT(bg
))
481 grid_expand_line(gd
, py
, gd
->sx
, bg
);
484 /* Peek at grid line. */
485 const struct grid_line
*
486 grid_peek_line(struct grid
*gd
, u_int py
)
488 if (grid_check_y(gd
, __func__
, py
) != 0)
490 return (&gd
->linedata
[py
]);
493 /* Get cell from line. */
495 grid_get_cell1(struct grid_line
*gl
, u_int px
, struct grid_cell
*gc
)
497 struct grid_cell_entry
*gce
= &gl
->celldata
[px
];
498 struct grid_extd_entry
*gee
;
500 if (gce
->flags
& GRID_FLAG_EXTENDED
) {
501 if (gce
->offset
>= gl
->extdsize
)
502 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
504 gee
= &gl
->extddata
[gce
->offset
];
505 gc
->flags
= gee
->flags
;
506 gc
->attr
= gee
->attr
;
510 utf8_to_data(gee
->data
, &gc
->data
);
515 gc
->flags
= gce
->flags
& ~(GRID_FLAG_FG256
|GRID_FLAG_BG256
);
516 gc
->attr
= gce
->data
.attr
;
517 gc
->fg
= gce
->data
.fg
;
518 if (gce
->flags
& GRID_FLAG_FG256
)
519 gc
->fg
|= COLOUR_FLAG_256
;
520 gc
->bg
= gce
->data
.bg
;
521 if (gce
->flags
& GRID_FLAG_BG256
)
522 gc
->bg
|= COLOUR_FLAG_256
;
524 utf8_set(&gc
->data
, gce
->data
.data
);
527 /* Get cell for reading. */
529 grid_get_cell(struct grid
*gd
, u_int px
, u_int py
, struct grid_cell
*gc
)
531 if (grid_check_y(gd
, __func__
, py
) != 0 ||
532 px
>= gd
->linedata
[py
].cellsize
)
533 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
535 grid_get_cell1(&gd
->linedata
[py
], px
, gc
);
538 /* Set cell at position. */
540 grid_set_cell(struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
)
542 struct grid_line
*gl
;
543 struct grid_cell_entry
*gce
;
545 if (grid_check_y(gd
, __func__
, py
) != 0)
548 grid_expand_line(gd
, py
, px
+ 1, 8);
550 gl
= &gd
->linedata
[py
];
551 if (px
+ 1 > gl
->cellused
)
552 gl
->cellused
= px
+ 1;
554 gce
= &gl
->celldata
[px
];
555 if (grid_need_extended_cell(gce
, gc
))
556 grid_extended_cell(gl
, gce
, gc
);
558 grid_store_cell(gce
, gc
, gc
->data
.data
[0]);
561 /* Set padding at position. */
563 grid_set_padding(struct grid
*gd
, u_int px
, u_int py
)
565 grid_set_cell(gd
, px
, py
, &grid_padding_cell
);
568 /* Set cells at position. */
570 grid_set_cells(struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
,
571 const char *s
, size_t slen
)
573 struct grid_line
*gl
;
574 struct grid_cell_entry
*gce
;
575 struct grid_extd_entry
*gee
;
578 if (grid_check_y(gd
, __func__
, py
) != 0)
581 grid_expand_line(gd
, py
, px
+ slen
, 8);
583 gl
= &gd
->linedata
[py
];
584 if (px
+ slen
> gl
->cellused
)
585 gl
->cellused
= px
+ slen
;
587 for (i
= 0; i
< slen
; i
++) {
588 gce
= &gl
->celldata
[px
+ i
];
589 if (grid_need_extended_cell(gce
, gc
)) {
590 gee
= grid_extended_cell(gl
, gce
, gc
);
591 gee
->data
= utf8_build_one(s
[i
]);
593 grid_store_cell(gce
, gc
, s
[i
]);
599 grid_clear(struct grid
*gd
, u_int px
, u_int py
, u_int nx
, u_int ny
, u_int bg
)
601 struct grid_line
*gl
;
602 u_int xx
, yy
, ox
, sx
;
604 if (nx
== 0 || ny
== 0)
607 if (px
== 0 && nx
== gd
->sx
) {
608 grid_clear_lines(gd
, py
, ny
, bg
);
612 if (grid_check_y(gd
, __func__
, py
) != 0)
614 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
617 for (yy
= py
; yy
< py
+ ny
; yy
++) {
618 gl
= &gd
->linedata
[yy
];
621 if (sx
> gl
->cellsize
)
624 if (COLOUR_DEFAULT(bg
)) {
631 grid_expand_line(gd
, yy
, px
+ ox
, 8); /* default bg first */
632 for (xx
= px
; xx
< px
+ ox
; xx
++)
633 grid_clear_cell(gd
, xx
, yy
, bg
);
637 /* Clear lines. This just frees and truncates the lines. */
639 grid_clear_lines(struct grid
*gd
, u_int py
, u_int ny
, u_int bg
)
646 if (grid_check_y(gd
, __func__
, py
) != 0)
648 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
651 for (yy
= py
; yy
< py
+ ny
; yy
++) {
652 grid_free_line(gd
, yy
);
653 grid_empty_line(gd
, yy
, bg
);
656 gd
->linedata
[py
- 1].flags
&= ~GRID_LINE_WRAPPED
;
659 /* Move a group of lines. */
661 grid_move_lines(struct grid
*gd
, u_int dy
, u_int py
, u_int ny
, u_int bg
)
665 if (ny
== 0 || py
== dy
)
668 if (grid_check_y(gd
, __func__
, py
) != 0)
670 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
672 if (grid_check_y(gd
, __func__
, dy
) != 0)
674 if (grid_check_y(gd
, __func__
, dy
+ ny
- 1) != 0)
677 /* Free any lines which are being replaced. */
678 for (yy
= dy
; yy
< dy
+ ny
; yy
++) {
679 if (yy
>= py
&& yy
< py
+ ny
)
681 grid_free_line(gd
, yy
);
684 gd
->linedata
[dy
- 1].flags
&= ~GRID_LINE_WRAPPED
;
686 memmove(&gd
->linedata
[dy
], &gd
->linedata
[py
],
687 ny
* (sizeof *gd
->linedata
));
690 * Wipe any lines that have been moved (without freeing them - they are
693 for (yy
= py
; yy
< py
+ ny
; yy
++) {
694 if (yy
< dy
|| yy
>= dy
+ ny
)
695 grid_empty_line(gd
, yy
, bg
);
697 if (py
!= 0 && (py
< dy
|| py
>= dy
+ ny
))
698 gd
->linedata
[py
- 1].flags
&= ~GRID_LINE_WRAPPED
;
701 /* Move a group of cells. */
703 grid_move_cells(struct grid
*gd
, u_int dx
, u_int px
, u_int py
, u_int nx
,
706 struct grid_line
*gl
;
709 if (nx
== 0 || px
== dx
)
712 if (grid_check_y(gd
, __func__
, py
) != 0)
714 gl
= &gd
->linedata
[py
];
716 grid_expand_line(gd
, py
, px
+ nx
, 8);
717 grid_expand_line(gd
, py
, dx
+ nx
, 8);
718 memmove(&gl
->celldata
[dx
], &gl
->celldata
[px
],
719 nx
* sizeof *gl
->celldata
);
720 if (dx
+ nx
> gl
->cellused
)
721 gl
->cellused
= dx
+ nx
;
723 /* Wipe any cells that have been moved. */
724 for (xx
= px
; xx
< px
+ nx
; xx
++) {
725 if (xx
>= dx
&& xx
< dx
+ nx
)
727 grid_clear_cell(gd
, xx
, py
, bg
);
731 /* Get ANSI foreground sequence. */
733 grid_string_cells_fg(const struct grid_cell
*gc
, int *values
)
739 if (gc
->fg
& COLOUR_FLAG_256
) {
742 values
[n
++] = gc
->fg
& 0xff;
743 } else if (gc
->fg
& COLOUR_FLAG_RGB
) {
746 colour_split_rgb(gc
->fg
, &r
, &g
, &b
);
760 values
[n
++] = gc
->fg
+ 30;
773 values
[n
++] = gc
->fg
;
780 /* Get ANSI background sequence. */
782 grid_string_cells_bg(const struct grid_cell
*gc
, int *values
)
788 if (gc
->bg
& COLOUR_FLAG_256
) {
791 values
[n
++] = gc
->bg
& 0xff;
792 } else if (gc
->bg
& COLOUR_FLAG_RGB
) {
795 colour_split_rgb(gc
->bg
, &r
, &g
, &b
);
809 values
[n
++] = gc
->bg
+ 40;
822 values
[n
++] = gc
->bg
+ 10;
829 /* Get underscore colour sequence. */
831 grid_string_cells_us(const struct grid_cell
*gc
, int *values
)
837 if (gc
->us
& COLOUR_FLAG_256
) {
840 values
[n
++] = gc
->us
& 0xff;
841 } else if (gc
->us
& COLOUR_FLAG_RGB
) {
844 colour_split_rgb(gc
->us
, &r
, &g
, &b
);
852 /* Add on SGR code. */
854 grid_string_cells_add_code(char *buf
, size_t len
, u_int n
, int *s
, int *newc
,
855 int *oldc
, size_t nnewc
, size_t noldc
, int escape_c0
)
862 memcmp(newc
, oldc
, nnewc
* sizeof newc
[0]) != 0 ||
863 (n
!= 0 && s
[0] == 0))) {
865 strlcat(buf
, "\\033[", len
);
867 strlcat(buf
, "\033[", len
);
868 for (i
= 0; i
< nnewc
; i
++) {
870 xsnprintf(tmp
, sizeof tmp
, "%d;", newc
[i
]);
872 xsnprintf(tmp
, sizeof tmp
, "%d", newc
[i
]);
873 strlcat(buf
, tmp
, len
);
875 strlcat(buf
, "m", len
);
880 * Returns ANSI code to set particular attributes (colour, bold and so on)
881 * given a current state.
884 grid_string_cells_code(const struct grid_cell
*lastgc
,
885 const struct grid_cell
*gc
, char *buf
, size_t len
, int escape_c0
)
887 int oldc
[64], newc
[64], s
[128];
888 size_t noldc
, nnewc
, n
, i
;
889 u_int attr
= gc
->attr
, lastattr
= lastgc
->attr
;
896 { GRID_ATTR_BRIGHT
, 1 },
897 { GRID_ATTR_DIM
, 2 },
898 { GRID_ATTR_ITALICS
, 3 },
899 { GRID_ATTR_UNDERSCORE
, 4 },
900 { GRID_ATTR_BLINK
, 5 },
901 { GRID_ATTR_REVERSE
, 7 },
902 { GRID_ATTR_HIDDEN
, 8 },
903 { GRID_ATTR_STRIKETHROUGH
, 9 },
904 { GRID_ATTR_UNDERSCORE_2
, 42 },
905 { GRID_ATTR_UNDERSCORE_3
, 43 },
906 { GRID_ATTR_UNDERSCORE_4
, 44 },
907 { GRID_ATTR_UNDERSCORE_5
, 45 },
908 { GRID_ATTR_OVERLINE
, 53 },
912 /* If any attribute is removed, begin with 0. */
913 for (i
= 0; i
< nitems(attrs
); i
++) {
914 if (((~attr
& attrs
[i
].mask
) &&
915 (lastattr
& attrs
[i
].mask
)) ||
916 (lastgc
->us
!= 0 && gc
->us
== 0)) {
918 lastattr
&= GRID_ATTR_CHARSET
;
922 /* For each attribute that is newly set, add its code. */
923 for (i
= 0; i
< nitems(attrs
); i
++) {
924 if ((attr
& attrs
[i
].mask
) && !(lastattr
& attrs
[i
].mask
))
925 s
[n
++] = attrs
[i
].code
;
928 /* Write the attributes. */
932 strlcat(buf
, "\\033[", len
);
934 strlcat(buf
, "\033[", len
);
935 for (i
= 0; i
< n
; i
++) {
937 xsnprintf(tmp
, sizeof tmp
, "%d", s
[i
]);
939 xsnprintf(tmp
, sizeof tmp
, "%d:%d", s
[i
] / 10,
942 strlcat(buf
, tmp
, len
);
944 strlcat(buf
, ";", len
);
946 strlcat(buf
, "m", len
);
949 /* If the foreground colour changed, write its parameters. */
950 nnewc
= grid_string_cells_fg(gc
, newc
);
951 noldc
= grid_string_cells_fg(lastgc
, oldc
);
952 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
955 /* If the background colour changed, append its parameters. */
956 nnewc
= grid_string_cells_bg(gc
, newc
);
957 noldc
= grid_string_cells_bg(lastgc
, oldc
);
958 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
961 /* If the underscore colour changed, append its parameters. */
962 nnewc
= grid_string_cells_us(gc
, newc
);
963 noldc
= grid_string_cells_us(lastgc
, oldc
);
964 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
967 /* Append shift in/shift out if needed. */
968 if ((attr
& GRID_ATTR_CHARSET
) && !(lastattr
& GRID_ATTR_CHARSET
)) {
970 strlcat(buf
, "\\016", len
); /* SO */
972 strlcat(buf
, "\016", len
); /* SO */
974 if (!(attr
& GRID_ATTR_CHARSET
) && (lastattr
& GRID_ATTR_CHARSET
)) {
976 strlcat(buf
, "\\017", len
); /* SI */
978 strlcat(buf
, "\017", len
); /* SI */
982 /* Convert cells into a string. */
984 grid_string_cells(struct grid
*gd
, u_int px
, u_int py
, u_int nx
,
985 struct grid_cell
**lastgc
, int with_codes
, int escape_c0
, int trim
)
988 static struct grid_cell lastgc1
;
990 char *buf
, code
[128];
991 size_t len
, off
, size
, codelen
;
993 const struct grid_line
*gl
;
995 if (lastgc
!= NULL
&& *lastgc
== NULL
) {
996 memcpy(&lastgc1
, &grid_default_cell
, sizeof lastgc1
);
1004 gl
= grid_peek_line(gd
, py
);
1005 for (xx
= px
; xx
< px
+ nx
; xx
++) {
1006 if (gl
== NULL
|| xx
>= gl
->cellsize
)
1008 grid_get_cell(gd
, xx
, py
, &gc
);
1009 if (gc
.flags
& GRID_FLAG_PADDING
)
1013 grid_string_cells_code(*lastgc
, &gc
, code
, sizeof code
,
1015 codelen
= strlen(code
);
1016 memcpy(*lastgc
, &gc
, sizeof **lastgc
);
1020 data
= gc
.data
.data
;
1021 size
= gc
.data
.size
;
1022 if (escape_c0
&& size
== 1 && *data
== '\\') {
1027 while (len
< off
+ size
+ codelen
+ 1) {
1028 buf
= xreallocarray(buf
, 2, len
);
1033 memcpy(buf
+ off
, code
, codelen
);
1036 memcpy(buf
+ off
, data
, size
);
1041 while (off
> 0 && buf
[off
- 1] == ' ')
1050 * Duplicate a set of lines between two grids. Both source and destination
1051 * should be big enough.
1054 grid_duplicate_lines(struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
,
1057 struct grid_line
*dstl
, *srcl
;
1060 if (dy
+ ny
> dst
->hsize
+ dst
->sy
)
1061 ny
= dst
->hsize
+ dst
->sy
- dy
;
1062 if (sy
+ ny
> src
->hsize
+ src
->sy
)
1063 ny
= src
->hsize
+ src
->sy
- sy
;
1064 grid_free_lines(dst
, dy
, ny
);
1066 for (yy
= 0; yy
< ny
; yy
++) {
1067 srcl
= &src
->linedata
[sy
];
1068 dstl
= &dst
->linedata
[dy
];
1070 memcpy(dstl
, srcl
, sizeof *dstl
);
1071 if (srcl
->cellsize
!= 0) {
1072 dstl
->celldata
= xreallocarray(NULL
,
1073 srcl
->cellsize
, sizeof *dstl
->celldata
);
1074 memcpy(dstl
->celldata
, srcl
->celldata
,
1075 srcl
->cellsize
* sizeof *dstl
->celldata
);
1077 dstl
->celldata
= NULL
;
1078 if (srcl
->extdsize
!= 0) {
1079 dstl
->extdsize
= srcl
->extdsize
;
1080 dstl
->extddata
= xreallocarray(NULL
, dstl
->extdsize
,
1081 sizeof *dstl
->extddata
);
1082 memcpy(dstl
->extddata
, srcl
->extddata
, dstl
->extdsize
*
1083 sizeof *dstl
->extddata
);
1085 dstl
->extddata
= NULL
;
1092 /* Mark line as dead. */
1094 grid_reflow_dead(struct grid_line
*gl
)
1096 memset(gl
, 0, sizeof *gl
);
1097 gl
->flags
= GRID_LINE_DEAD
;
1100 /* Add lines, return the first new one. */
1101 static struct grid_line
*
1102 grid_reflow_add(struct grid
*gd
, u_int n
)
1104 struct grid_line
*gl
;
1105 u_int sy
= gd
->sy
+ n
;
1107 gd
->linedata
= xreallocarray(gd
->linedata
, sy
, sizeof *gd
->linedata
);
1108 gl
= &gd
->linedata
[gd
->sy
];
1109 memset(gl
, 0, n
* (sizeof *gl
));
1114 /* Move a line across. */
1115 static struct grid_line
*
1116 grid_reflow_move(struct grid
*gd
, struct grid_line
*from
)
1118 struct grid_line
*to
;
1120 to
= grid_reflow_add(gd
, 1);
1121 memcpy(to
, from
, sizeof *to
);
1122 grid_reflow_dead(from
);
1126 /* Join line below onto this one. */
1128 grid_reflow_join(struct grid
*target
, struct grid
*gd
, u_int sx
, u_int yy
,
1129 u_int width
, int already
)
1131 struct grid_line
*gl
, *from
= NULL
;
1132 struct grid_cell gc
;
1133 u_int lines
, left
, i
, to
, line
, want
= 0;
1138 * Add a new target line.
1142 gl
= grid_reflow_move(target
, &gd
->linedata
[yy
]);
1144 to
= target
->sy
- 1;
1145 gl
= &target
->linedata
[to
];
1150 * Loop until no more to consume or the target line is full.
1155 * If this is now the last line, there is nothing more to be
1158 if (yy
+ 1 + lines
== gd
->hsize
+ gd
->sy
)
1160 line
= yy
+ 1 + lines
;
1162 /* If the next line is empty, skip it. */
1163 if (~gd
->linedata
[line
].flags
& GRID_LINE_WRAPPED
)
1165 if (gd
->linedata
[line
].cellused
== 0) {
1173 * Is the destination line now full? Copy the first character
1174 * separately because we need to leave "from" set to the last
1175 * line if this line is full.
1177 grid_get_cell1(&gd
->linedata
[line
], 0, &gc
);
1178 if (width
+ gc
.data
.width
> sx
)
1180 width
+= gc
.data
.width
;
1181 grid_set_cell(target
, at
, to
, &gc
);
1184 /* Join as much more as possible onto the current line. */
1185 from
= &gd
->linedata
[line
];
1186 for (want
= 1; want
< from
->cellused
; want
++) {
1187 grid_get_cell1(from
, want
, &gc
);
1188 if (width
+ gc
.data
.width
> sx
)
1190 width
+= gc
.data
.width
;
1192 grid_set_cell(target
, at
, to
, &gc
);
1198 * If this line wasn't wrapped or we didn't consume the entire
1199 * line, don't try to join any further lines.
1201 if (!wrapped
|| want
!= from
->cellused
|| width
== sx
)
1208 * If we didn't consume the entire final line, then remove what we did
1209 * consume. If we consumed the entire line and it wasn't wrapped,
1210 * remove the wrap flag from this line.
1212 left
= from
->cellused
- want
;
1214 grid_move_cells(gd
, 0, want
, yy
+ lines
, left
, 8);
1215 from
->cellsize
= from
->cellused
= left
;
1217 } else if (!wrapped
)
1218 gl
->flags
&= ~GRID_LINE_WRAPPED
;
1220 /* Remove the lines that were completely consumed. */
1221 for (i
= yy
+ 1; i
< yy
+ 1 + lines
; i
++) {
1222 free(gd
->linedata
[i
].celldata
);
1223 free(gd
->linedata
[i
].extddata
);
1224 grid_reflow_dead(&gd
->linedata
[i
]);
1227 /* Adjust scroll position. */
1228 if (gd
->hscrolled
> to
+ lines
)
1229 gd
->hscrolled
-= lines
;
1230 else if (gd
->hscrolled
> to
)
1234 /* Split this line into several new ones */
1236 grid_reflow_split(struct grid
*target
, struct grid
*gd
, u_int sx
, u_int yy
,
1239 struct grid_line
*gl
= &gd
->linedata
[yy
], *first
;
1240 struct grid_cell gc
;
1241 u_int line
, lines
, width
, i
, xx
;
1242 u_int used
= gl
->cellused
;
1243 int flags
= gl
->flags
;
1245 /* How many lines do we need to insert? We know we need at least two. */
1246 if (~gl
->flags
& GRID_LINE_EXTENDED
)
1247 lines
= 1 + (gl
->cellused
- 1) / sx
;
1251 for (i
= at
; i
< used
; i
++) {
1252 grid_get_cell1(gl
, i
, &gc
);
1253 if (width
+ gc
.data
.width
> sx
) {
1257 width
+= gc
.data
.width
;
1261 /* Insert new lines. */
1262 line
= target
->sy
+ 1;
1263 first
= grid_reflow_add(target
, lines
);
1265 /* Copy sections from the original line. */
1268 for (i
= at
; i
< used
; i
++) {
1269 grid_get_cell1(gl
, i
, &gc
);
1270 if (width
+ gc
.data
.width
> sx
) {
1271 target
->linedata
[line
].flags
|= GRID_LINE_WRAPPED
;
1277 width
+= gc
.data
.width
;
1278 grid_set_cell(target
, xx
, line
, &gc
);
1281 if (flags
& GRID_LINE_WRAPPED
)
1282 target
->linedata
[line
].flags
|= GRID_LINE_WRAPPED
;
1284 /* Move the remainder of the original line. */
1285 gl
->cellsize
= gl
->cellused
= at
;
1286 gl
->flags
|= GRID_LINE_WRAPPED
;
1287 memcpy(first
, gl
, sizeof *first
);
1288 grid_reflow_dead(gl
);
1290 /* Adjust the scroll position. */
1291 if (yy
<= gd
->hscrolled
)
1292 gd
->hscrolled
+= lines
- 1;
1295 * If the original line had the wrapped flag and there is still space
1296 * in the last new line, try to join with the next lines.
1298 if (width
< sx
&& (flags
& GRID_LINE_WRAPPED
))
1299 grid_reflow_join(target
, gd
, sx
, yy
, width
, 1);
1302 /* Reflow lines on grid to new width. */
1304 grid_reflow(struct grid
*gd
, u_int sx
)
1306 struct grid
*target
;
1307 struct grid_line
*gl
;
1308 struct grid_cell gc
;
1309 u_int yy
, width
, i
, at
;
1312 * Create a destination grid. This is just used as a container for the
1313 * line data and may not be fully valid.
1315 target
= grid_create(gd
->sx
, 0, 0);
1318 * Loop over each source line.
1320 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
; yy
++) {
1321 gl
= &gd
->linedata
[yy
];
1322 if (gl
->flags
& GRID_LINE_DEAD
)
1326 * Work out the width of this line. at is the point at which
1327 * the available width is hit, and width is the full line
1331 if (~gl
->flags
& GRID_LINE_EXTENDED
) {
1332 width
= gl
->cellused
;
1338 for (i
= 0; i
< gl
->cellused
; i
++) {
1339 grid_get_cell1(gl
, i
, &gc
);
1340 if (at
== 0 && width
+ gc
.data
.width
> sx
)
1342 width
+= gc
.data
.width
;
1347 * If the line is exactly right, just move it across
1351 grid_reflow_move(target
, gl
);
1356 * If the line is too big, it needs to be split, whether or not
1357 * it was previously wrapped.
1360 grid_reflow_split(target
, gd
, sx
, yy
, at
);
1365 * If the line was previously wrapped, join as much as possible
1368 if (gl
->flags
& GRID_LINE_WRAPPED
)
1369 grid_reflow_join(target
, gd
, sx
, yy
, width
, 0);
1371 grid_reflow_move(target
, gl
);
1375 * Replace the old grid with the new.
1377 if (target
->sy
< gd
->sy
)
1378 grid_reflow_add(target
, gd
->sy
- target
->sy
);
1379 gd
->hsize
= target
->sy
- gd
->sy
;
1380 if (gd
->hscrolled
> gd
->hsize
)
1381 gd
->hscrolled
= gd
->hsize
;
1383 gd
->linedata
= target
->linedata
;
1387 /* Convert to position based on wrapped lines. */
1389 grid_wrap_position(struct grid
*gd
, u_int px
, u_int py
, u_int
*wx
, u_int
*wy
)
1391 u_int ax
= 0, ay
= 0, yy
;
1393 for (yy
= 0; yy
< py
; yy
++) {
1394 if (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1395 ax
+= gd
->linedata
[yy
].cellused
;
1401 if (px
>= gd
->linedata
[yy
].cellused
)
1409 /* Convert position based on wrapped lines back. */
1411 grid_unwrap_position(struct grid
*gd
, u_int
*px
, u_int
*py
, u_int wx
, u_int wy
)
1415 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
- 1; yy
++) {
1418 if (~gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1423 * yy is now 0 on the unwrapped line which contains wx. Walk forwards
1424 * until we find the end or the line now containing wx.
1426 if (wx
== UINT_MAX
) {
1427 while (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1429 wx
= gd
->linedata
[yy
].cellused
;
1431 while (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
) {
1432 if (wx
< gd
->linedata
[yy
].cellused
)
1434 wx
-= gd
->linedata
[yy
].cellused
;
1442 /* Get length of line. */
1444 grid_line_length(struct grid
*gd
, u_int py
)
1446 struct grid_cell gc
;
1449 px
= grid_get_line(gd
, py
)->cellsize
;
1453 grid_get_cell(gd
, px
- 1, py
, &gc
);
1454 if ((gc
.flags
& GRID_FLAG_PADDING
) ||
1455 gc
.data
.size
!= 1 ||
1456 *gc
.data
.data
!= ' ')