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
]);
402 gd
->linedata
[gd
->hsize
].time
= current_time
;
406 /* Clear the history. */
408 grid_clear_history(struct grid
*gd
)
410 grid_trim_history(gd
, gd
->hsize
);
415 gd
->linedata
= xreallocarray(gd
->linedata
, gd
->sy
,
416 sizeof *gd
->linedata
);
419 /* Scroll a region up, moving the top line into the history. */
421 grid_scroll_history_region(struct grid
*gd
, u_int upper
, u_int lower
, u_int bg
)
423 struct grid_line
*gl_history
, *gl_upper
;
426 /* Create a space for a new line. */
427 yy
= gd
->hsize
+ gd
->sy
;
428 gd
->linedata
= xreallocarray(gd
->linedata
, yy
+ 1,
429 sizeof *gd
->linedata
);
431 /* Move the entire screen down to free a space for this line. */
432 gl_history
= &gd
->linedata
[gd
->hsize
];
433 memmove(gl_history
+ 1, gl_history
, gd
->sy
* sizeof *gl_history
);
435 /* Adjust the region and find its start and end. */
437 gl_upper
= &gd
->linedata
[upper
];
440 /* Move the line into the history. */
441 memcpy(gl_history
, gl_upper
, sizeof *gl_history
);
442 gl_history
->time
= current_time
;
444 /* Then move the region up and clear the bottom line. */
445 memmove(gl_upper
, gl_upper
+ 1, (lower
- upper
) * sizeof *gl_upper
);
446 grid_empty_line(gd
, lower
, bg
);
448 /* Move the history offset down over the line. */
453 /* Expand line to fit to cell. */
455 grid_expand_line(struct grid
*gd
, u_int py
, u_int sx
, u_int bg
)
457 struct grid_line
*gl
;
460 gl
= &gd
->linedata
[py
];
461 if (sx
<= gl
->cellsize
)
466 else if (sx
< gd
->sx
/ 2)
468 else if (gd
->sx
> sx
)
471 gl
->celldata
= xreallocarray(gl
->celldata
, sx
, sizeof *gl
->celldata
);
472 for (xx
= gl
->cellsize
; xx
< sx
; xx
++)
473 grid_clear_cell(gd
, xx
, py
, bg
);
477 /* Empty a line and set background colour if needed. */
479 grid_empty_line(struct grid
*gd
, u_int py
, u_int bg
)
481 memset(&gd
->linedata
[py
], 0, sizeof gd
->linedata
[py
]);
482 if (!COLOUR_DEFAULT(bg
))
483 grid_expand_line(gd
, py
, gd
->sx
, bg
);
486 /* Peek at grid line. */
487 const struct grid_line
*
488 grid_peek_line(struct grid
*gd
, u_int py
)
490 if (grid_check_y(gd
, __func__
, py
) != 0)
492 return (&gd
->linedata
[py
]);
495 /* Get cell from line. */
497 grid_get_cell1(struct grid_line
*gl
, u_int px
, struct grid_cell
*gc
)
499 struct grid_cell_entry
*gce
= &gl
->celldata
[px
];
500 struct grid_extd_entry
*gee
;
502 if (gce
->flags
& GRID_FLAG_EXTENDED
) {
503 if (gce
->offset
>= gl
->extdsize
)
504 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
506 gee
= &gl
->extddata
[gce
->offset
];
507 gc
->flags
= gee
->flags
;
508 gc
->attr
= gee
->attr
;
512 utf8_to_data(gee
->data
, &gc
->data
);
517 gc
->flags
= gce
->flags
& ~(GRID_FLAG_FG256
|GRID_FLAG_BG256
);
518 gc
->attr
= gce
->data
.attr
;
519 gc
->fg
= gce
->data
.fg
;
520 if (gce
->flags
& GRID_FLAG_FG256
)
521 gc
->fg
|= COLOUR_FLAG_256
;
522 gc
->bg
= gce
->data
.bg
;
523 if (gce
->flags
& GRID_FLAG_BG256
)
524 gc
->bg
|= COLOUR_FLAG_256
;
526 utf8_set(&gc
->data
, gce
->data
.data
);
529 /* Get cell for reading. */
531 grid_get_cell(struct grid
*gd
, u_int px
, u_int py
, struct grid_cell
*gc
)
533 if (grid_check_y(gd
, __func__
, py
) != 0 ||
534 px
>= gd
->linedata
[py
].cellsize
)
535 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
537 grid_get_cell1(&gd
->linedata
[py
], px
, gc
);
540 /* Set cell at position. */
542 grid_set_cell(struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
)
544 struct grid_line
*gl
;
545 struct grid_cell_entry
*gce
;
547 if (grid_check_y(gd
, __func__
, py
) != 0)
550 grid_expand_line(gd
, py
, px
+ 1, 8);
552 gl
= &gd
->linedata
[py
];
553 if (px
+ 1 > gl
->cellused
)
554 gl
->cellused
= px
+ 1;
556 gce
= &gl
->celldata
[px
];
557 if (grid_need_extended_cell(gce
, gc
))
558 grid_extended_cell(gl
, gce
, gc
);
560 grid_store_cell(gce
, gc
, gc
->data
.data
[0]);
563 /* Set padding at position. */
565 grid_set_padding(struct grid
*gd
, u_int px
, u_int py
)
567 grid_set_cell(gd
, px
, py
, &grid_padding_cell
);
570 /* Set cells at position. */
572 grid_set_cells(struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
,
573 const char *s
, size_t slen
)
575 struct grid_line
*gl
;
576 struct grid_cell_entry
*gce
;
577 struct grid_extd_entry
*gee
;
580 if (grid_check_y(gd
, __func__
, py
) != 0)
583 grid_expand_line(gd
, py
, px
+ slen
, 8);
585 gl
= &gd
->linedata
[py
];
586 if (px
+ slen
> gl
->cellused
)
587 gl
->cellused
= px
+ slen
;
589 for (i
= 0; i
< slen
; i
++) {
590 gce
= &gl
->celldata
[px
+ i
];
591 if (grid_need_extended_cell(gce
, gc
)) {
592 gee
= grid_extended_cell(gl
, gce
, gc
);
593 gee
->data
= utf8_build_one(s
[i
]);
595 grid_store_cell(gce
, gc
, s
[i
]);
601 grid_clear(struct grid
*gd
, u_int px
, u_int py
, u_int nx
, u_int ny
, u_int bg
)
603 struct grid_line
*gl
;
604 u_int xx
, yy
, ox
, sx
;
606 if (nx
== 0 || ny
== 0)
609 if (px
== 0 && nx
== gd
->sx
) {
610 grid_clear_lines(gd
, py
, ny
, bg
);
614 if (grid_check_y(gd
, __func__
, py
) != 0)
616 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
619 for (yy
= py
; yy
< py
+ ny
; yy
++) {
620 gl
= &gd
->linedata
[yy
];
623 if (sx
> gl
->cellsize
)
626 if (COLOUR_DEFAULT(bg
)) {
633 grid_expand_line(gd
, yy
, px
+ ox
, 8); /* default bg first */
634 for (xx
= px
; xx
< px
+ ox
; xx
++)
635 grid_clear_cell(gd
, xx
, yy
, bg
);
639 /* Clear lines. This just frees and truncates the lines. */
641 grid_clear_lines(struct grid
*gd
, u_int py
, u_int ny
, u_int bg
)
648 if (grid_check_y(gd
, __func__
, py
) != 0)
650 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
653 for (yy
= py
; yy
< py
+ ny
; yy
++) {
654 grid_free_line(gd
, yy
);
655 grid_empty_line(gd
, yy
, bg
);
658 gd
->linedata
[py
- 1].flags
&= ~GRID_LINE_WRAPPED
;
661 /* Move a group of lines. */
663 grid_move_lines(struct grid
*gd
, u_int dy
, u_int py
, u_int ny
, u_int bg
)
667 if (ny
== 0 || py
== dy
)
670 if (grid_check_y(gd
, __func__
, py
) != 0)
672 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
674 if (grid_check_y(gd
, __func__
, dy
) != 0)
676 if (grid_check_y(gd
, __func__
, dy
+ ny
- 1) != 0)
679 /* Free any lines which are being replaced. */
680 for (yy
= dy
; yy
< dy
+ ny
; yy
++) {
681 if (yy
>= py
&& yy
< py
+ ny
)
683 grid_free_line(gd
, yy
);
686 gd
->linedata
[dy
- 1].flags
&= ~GRID_LINE_WRAPPED
;
688 memmove(&gd
->linedata
[dy
], &gd
->linedata
[py
],
689 ny
* (sizeof *gd
->linedata
));
692 * Wipe any lines that have been moved (without freeing them - they are
695 for (yy
= py
; yy
< py
+ ny
; yy
++) {
696 if (yy
< dy
|| yy
>= dy
+ ny
)
697 grid_empty_line(gd
, yy
, bg
);
699 if (py
!= 0 && (py
< dy
|| py
>= dy
+ ny
))
700 gd
->linedata
[py
- 1].flags
&= ~GRID_LINE_WRAPPED
;
703 /* Move a group of cells. */
705 grid_move_cells(struct grid
*gd
, u_int dx
, u_int px
, u_int py
, u_int nx
,
708 struct grid_line
*gl
;
711 if (nx
== 0 || px
== dx
)
714 if (grid_check_y(gd
, __func__
, py
) != 0)
716 gl
= &gd
->linedata
[py
];
718 grid_expand_line(gd
, py
, px
+ nx
, 8);
719 grid_expand_line(gd
, py
, dx
+ nx
, 8);
720 memmove(&gl
->celldata
[dx
], &gl
->celldata
[px
],
721 nx
* sizeof *gl
->celldata
);
722 if (dx
+ nx
> gl
->cellused
)
723 gl
->cellused
= dx
+ nx
;
725 /* Wipe any cells that have been moved. */
726 for (xx
= px
; xx
< px
+ nx
; xx
++) {
727 if (xx
>= dx
&& xx
< dx
+ nx
)
729 grid_clear_cell(gd
, xx
, py
, bg
);
733 /* Get ANSI foreground sequence. */
735 grid_string_cells_fg(const struct grid_cell
*gc
, int *values
)
741 if (gc
->fg
& COLOUR_FLAG_256
) {
744 values
[n
++] = gc
->fg
& 0xff;
745 } else if (gc
->fg
& COLOUR_FLAG_RGB
) {
748 colour_split_rgb(gc
->fg
, &r
, &g
, &b
);
762 values
[n
++] = gc
->fg
+ 30;
775 values
[n
++] = gc
->fg
;
782 /* Get ANSI background sequence. */
784 grid_string_cells_bg(const struct grid_cell
*gc
, int *values
)
790 if (gc
->bg
& COLOUR_FLAG_256
) {
793 values
[n
++] = gc
->bg
& 0xff;
794 } else if (gc
->bg
& COLOUR_FLAG_RGB
) {
797 colour_split_rgb(gc
->bg
, &r
, &g
, &b
);
811 values
[n
++] = gc
->bg
+ 40;
824 values
[n
++] = gc
->bg
+ 10;
831 /* Get underscore colour sequence. */
833 grid_string_cells_us(const struct grid_cell
*gc
, int *values
)
839 if (gc
->us
& COLOUR_FLAG_256
) {
842 values
[n
++] = gc
->us
& 0xff;
843 } else if (gc
->us
& COLOUR_FLAG_RGB
) {
846 colour_split_rgb(gc
->us
, &r
, &g
, &b
);
854 /* Add on SGR code. */
856 grid_string_cells_add_code(char *buf
, size_t len
, u_int n
, int *s
, int *newc
,
857 int *oldc
, size_t nnewc
, size_t noldc
, int escape_c0
)
864 memcmp(newc
, oldc
, nnewc
* sizeof newc
[0]) != 0 ||
865 (n
!= 0 && s
[0] == 0))) {
867 strlcat(buf
, "\\033[", len
);
869 strlcat(buf
, "\033[", len
);
870 for (i
= 0; i
< nnewc
; i
++) {
872 xsnprintf(tmp
, sizeof tmp
, "%d;", newc
[i
]);
874 xsnprintf(tmp
, sizeof tmp
, "%d", newc
[i
]);
875 strlcat(buf
, tmp
, len
);
877 strlcat(buf
, "m", len
);
882 * Returns ANSI code to set particular attributes (colour, bold and so on)
883 * given a current state.
886 grid_string_cells_code(const struct grid_cell
*lastgc
,
887 const struct grid_cell
*gc
, char *buf
, size_t len
, int escape_c0
)
889 int oldc
[64], newc
[64], s
[128];
890 size_t noldc
, nnewc
, n
, i
;
891 u_int attr
= gc
->attr
, lastattr
= lastgc
->attr
;
898 { GRID_ATTR_BRIGHT
, 1 },
899 { GRID_ATTR_DIM
, 2 },
900 { GRID_ATTR_ITALICS
, 3 },
901 { GRID_ATTR_UNDERSCORE
, 4 },
902 { GRID_ATTR_BLINK
, 5 },
903 { GRID_ATTR_REVERSE
, 7 },
904 { GRID_ATTR_HIDDEN
, 8 },
905 { GRID_ATTR_STRIKETHROUGH
, 9 },
906 { GRID_ATTR_UNDERSCORE_2
, 42 },
907 { GRID_ATTR_UNDERSCORE_3
, 43 },
908 { GRID_ATTR_UNDERSCORE_4
, 44 },
909 { GRID_ATTR_UNDERSCORE_5
, 45 },
910 { GRID_ATTR_OVERLINE
, 53 },
914 /* If any attribute is removed, begin with 0. */
915 for (i
= 0; i
< nitems(attrs
); i
++) {
916 if (((~attr
& attrs
[i
].mask
) &&
917 (lastattr
& attrs
[i
].mask
)) ||
918 (lastgc
->us
!= 0 && gc
->us
== 0)) {
920 lastattr
&= GRID_ATTR_CHARSET
;
924 /* For each attribute that is newly set, add its code. */
925 for (i
= 0; i
< nitems(attrs
); i
++) {
926 if ((attr
& attrs
[i
].mask
) && !(lastattr
& attrs
[i
].mask
))
927 s
[n
++] = attrs
[i
].code
;
930 /* Write the attributes. */
934 strlcat(buf
, "\\033[", len
);
936 strlcat(buf
, "\033[", len
);
937 for (i
= 0; i
< n
; i
++) {
939 xsnprintf(tmp
, sizeof tmp
, "%d", s
[i
]);
941 xsnprintf(tmp
, sizeof tmp
, "%d:%d", s
[i
] / 10,
944 strlcat(buf
, tmp
, len
);
946 strlcat(buf
, ";", len
);
948 strlcat(buf
, "m", len
);
951 /* If the foreground colour changed, write its parameters. */
952 nnewc
= grid_string_cells_fg(gc
, newc
);
953 noldc
= grid_string_cells_fg(lastgc
, oldc
);
954 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
957 /* If the background colour changed, append its parameters. */
958 nnewc
= grid_string_cells_bg(gc
, newc
);
959 noldc
= grid_string_cells_bg(lastgc
, oldc
);
960 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
963 /* If the underscore colour changed, append its parameters. */
964 nnewc
= grid_string_cells_us(gc
, newc
);
965 noldc
= grid_string_cells_us(lastgc
, oldc
);
966 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
969 /* Append shift in/shift out if needed. */
970 if ((attr
& GRID_ATTR_CHARSET
) && !(lastattr
& GRID_ATTR_CHARSET
)) {
972 strlcat(buf
, "\\016", len
); /* SO */
974 strlcat(buf
, "\016", len
); /* SO */
976 if (!(attr
& GRID_ATTR_CHARSET
) && (lastattr
& GRID_ATTR_CHARSET
)) {
978 strlcat(buf
, "\\017", len
); /* SI */
980 strlcat(buf
, "\017", len
); /* SI */
984 /* Convert cells into a string. */
986 grid_string_cells(struct grid
*gd
, u_int px
, u_int py
, u_int nx
,
987 struct grid_cell
**lastgc
, int with_codes
, int escape_c0
, int trim
)
990 static struct grid_cell lastgc1
;
992 char *buf
, code
[128];
993 size_t len
, off
, size
, codelen
;
995 const struct grid_line
*gl
;
997 if (lastgc
!= NULL
&& *lastgc
== NULL
) {
998 memcpy(&lastgc1
, &grid_default_cell
, sizeof lastgc1
);
1006 gl
= grid_peek_line(gd
, py
);
1007 for (xx
= px
; xx
< px
+ nx
; xx
++) {
1008 if (gl
== NULL
|| xx
>= gl
->cellused
)
1010 grid_get_cell(gd
, xx
, py
, &gc
);
1011 if (gc
.flags
& GRID_FLAG_PADDING
)
1015 grid_string_cells_code(*lastgc
, &gc
, code
, sizeof code
,
1017 codelen
= strlen(code
);
1018 memcpy(*lastgc
, &gc
, sizeof **lastgc
);
1022 data
= gc
.data
.data
;
1023 size
= gc
.data
.size
;
1024 if (escape_c0
&& size
== 1 && *data
== '\\') {
1029 while (len
< off
+ size
+ codelen
+ 1) {
1030 buf
= xreallocarray(buf
, 2, len
);
1035 memcpy(buf
+ off
, code
, codelen
);
1038 memcpy(buf
+ off
, data
, size
);
1043 while (off
> 0 && buf
[off
- 1] == ' ')
1052 * Duplicate a set of lines between two grids. Both source and destination
1053 * should be big enough.
1056 grid_duplicate_lines(struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
,
1059 struct grid_line
*dstl
, *srcl
;
1062 if (dy
+ ny
> dst
->hsize
+ dst
->sy
)
1063 ny
= dst
->hsize
+ dst
->sy
- dy
;
1064 if (sy
+ ny
> src
->hsize
+ src
->sy
)
1065 ny
= src
->hsize
+ src
->sy
- sy
;
1066 grid_free_lines(dst
, dy
, ny
);
1068 for (yy
= 0; yy
< ny
; yy
++) {
1069 srcl
= &src
->linedata
[sy
];
1070 dstl
= &dst
->linedata
[dy
];
1072 memcpy(dstl
, srcl
, sizeof *dstl
);
1073 if (srcl
->cellsize
!= 0) {
1074 dstl
->celldata
= xreallocarray(NULL
,
1075 srcl
->cellsize
, sizeof *dstl
->celldata
);
1076 memcpy(dstl
->celldata
, srcl
->celldata
,
1077 srcl
->cellsize
* sizeof *dstl
->celldata
);
1079 dstl
->celldata
= NULL
;
1080 if (srcl
->extdsize
!= 0) {
1081 dstl
->extdsize
= srcl
->extdsize
;
1082 dstl
->extddata
= xreallocarray(NULL
, dstl
->extdsize
,
1083 sizeof *dstl
->extddata
);
1084 memcpy(dstl
->extddata
, srcl
->extddata
, dstl
->extdsize
*
1085 sizeof *dstl
->extddata
);
1087 dstl
->extddata
= NULL
;
1094 /* Mark line as dead. */
1096 grid_reflow_dead(struct grid_line
*gl
)
1098 memset(gl
, 0, sizeof *gl
);
1099 gl
->flags
= GRID_LINE_DEAD
;
1102 /* Add lines, return the first new one. */
1103 static struct grid_line
*
1104 grid_reflow_add(struct grid
*gd
, u_int n
)
1106 struct grid_line
*gl
;
1107 u_int sy
= gd
->sy
+ n
;
1109 gd
->linedata
= xreallocarray(gd
->linedata
, sy
, sizeof *gd
->linedata
);
1110 gl
= &gd
->linedata
[gd
->sy
];
1111 memset(gl
, 0, n
* (sizeof *gl
));
1116 /* Move a line across. */
1117 static struct grid_line
*
1118 grid_reflow_move(struct grid
*gd
, struct grid_line
*from
)
1120 struct grid_line
*to
;
1122 to
= grid_reflow_add(gd
, 1);
1123 memcpy(to
, from
, sizeof *to
);
1124 grid_reflow_dead(from
);
1128 /* Join line below onto this one. */
1130 grid_reflow_join(struct grid
*target
, struct grid
*gd
, u_int sx
, u_int yy
,
1131 u_int width
, int already
)
1133 struct grid_line
*gl
, *from
= NULL
;
1134 struct grid_cell gc
;
1135 u_int lines
, left
, i
, to
, line
, want
= 0;
1140 * Add a new target line.
1144 gl
= grid_reflow_move(target
, &gd
->linedata
[yy
]);
1146 to
= target
->sy
- 1;
1147 gl
= &target
->linedata
[to
];
1152 * Loop until no more to consume or the target line is full.
1157 * If this is now the last line, there is nothing more to be
1160 if (yy
+ 1 + lines
== gd
->hsize
+ gd
->sy
)
1162 line
= yy
+ 1 + lines
;
1164 /* If the next line is empty, skip it. */
1165 if (~gd
->linedata
[line
].flags
& GRID_LINE_WRAPPED
)
1167 if (gd
->linedata
[line
].cellused
== 0) {
1175 * Is the destination line now full? Copy the first character
1176 * separately because we need to leave "from" set to the last
1177 * line if this line is full.
1179 grid_get_cell1(&gd
->linedata
[line
], 0, &gc
);
1180 if (width
+ gc
.data
.width
> sx
)
1182 width
+= gc
.data
.width
;
1183 grid_set_cell(target
, at
, to
, &gc
);
1186 /* Join as much more as possible onto the current line. */
1187 from
= &gd
->linedata
[line
];
1188 for (want
= 1; want
< from
->cellused
; want
++) {
1189 grid_get_cell1(from
, want
, &gc
);
1190 if (width
+ gc
.data
.width
> sx
)
1192 width
+= gc
.data
.width
;
1194 grid_set_cell(target
, at
, to
, &gc
);
1200 * If this line wasn't wrapped or we didn't consume the entire
1201 * line, don't try to join any further lines.
1203 if (!wrapped
|| want
!= from
->cellused
|| width
== sx
)
1210 * If we didn't consume the entire final line, then remove what we did
1211 * consume. If we consumed the entire line and it wasn't wrapped,
1212 * remove the wrap flag from this line.
1214 left
= from
->cellused
- want
;
1216 grid_move_cells(gd
, 0, want
, yy
+ lines
, left
, 8);
1217 from
->cellsize
= from
->cellused
= left
;
1219 } else if (!wrapped
)
1220 gl
->flags
&= ~GRID_LINE_WRAPPED
;
1222 /* Remove the lines that were completely consumed. */
1223 for (i
= yy
+ 1; i
< yy
+ 1 + lines
; i
++) {
1224 free(gd
->linedata
[i
].celldata
);
1225 free(gd
->linedata
[i
].extddata
);
1226 grid_reflow_dead(&gd
->linedata
[i
]);
1229 /* Adjust scroll position. */
1230 if (gd
->hscrolled
> to
+ lines
)
1231 gd
->hscrolled
-= lines
;
1232 else if (gd
->hscrolled
> to
)
1236 /* Split this line into several new ones */
1238 grid_reflow_split(struct grid
*target
, struct grid
*gd
, u_int sx
, u_int yy
,
1241 struct grid_line
*gl
= &gd
->linedata
[yy
], *first
;
1242 struct grid_cell gc
;
1243 u_int line
, lines
, width
, i
, xx
;
1244 u_int used
= gl
->cellused
;
1245 int flags
= gl
->flags
;
1247 /* How many lines do we need to insert? We know we need at least two. */
1248 if (~gl
->flags
& GRID_LINE_EXTENDED
)
1249 lines
= 1 + (gl
->cellused
- 1) / sx
;
1253 for (i
= at
; i
< used
; i
++) {
1254 grid_get_cell1(gl
, i
, &gc
);
1255 if (width
+ gc
.data
.width
> sx
) {
1259 width
+= gc
.data
.width
;
1263 /* Insert new lines. */
1264 line
= target
->sy
+ 1;
1265 first
= grid_reflow_add(target
, lines
);
1267 /* Copy sections from the original line. */
1270 for (i
= at
; i
< used
; i
++) {
1271 grid_get_cell1(gl
, i
, &gc
);
1272 if (width
+ gc
.data
.width
> sx
) {
1273 target
->linedata
[line
].flags
|= GRID_LINE_WRAPPED
;
1279 width
+= gc
.data
.width
;
1280 grid_set_cell(target
, xx
, line
, &gc
);
1283 if (flags
& GRID_LINE_WRAPPED
)
1284 target
->linedata
[line
].flags
|= GRID_LINE_WRAPPED
;
1286 /* Move the remainder of the original line. */
1287 gl
->cellsize
= gl
->cellused
= at
;
1288 gl
->flags
|= GRID_LINE_WRAPPED
;
1289 memcpy(first
, gl
, sizeof *first
);
1290 grid_reflow_dead(gl
);
1292 /* Adjust the scroll position. */
1293 if (yy
<= gd
->hscrolled
)
1294 gd
->hscrolled
+= lines
- 1;
1297 * If the original line had the wrapped flag and there is still space
1298 * in the last new line, try to join with the next lines.
1300 if (width
< sx
&& (flags
& GRID_LINE_WRAPPED
))
1301 grid_reflow_join(target
, gd
, sx
, yy
, width
, 1);
1304 /* Reflow lines on grid to new width. */
1306 grid_reflow(struct grid
*gd
, u_int sx
)
1308 struct grid
*target
;
1309 struct grid_line
*gl
;
1310 struct grid_cell gc
;
1311 u_int yy
, width
, i
, at
;
1314 * Create a destination grid. This is just used as a container for the
1315 * line data and may not be fully valid.
1317 target
= grid_create(gd
->sx
, 0, 0);
1320 * Loop over each source line.
1322 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
; yy
++) {
1323 gl
= &gd
->linedata
[yy
];
1324 if (gl
->flags
& GRID_LINE_DEAD
)
1328 * Work out the width of this line. at is the point at which
1329 * the available width is hit, and width is the full line
1333 if (~gl
->flags
& GRID_LINE_EXTENDED
) {
1334 width
= gl
->cellused
;
1340 for (i
= 0; i
< gl
->cellused
; i
++) {
1341 grid_get_cell1(gl
, i
, &gc
);
1342 if (at
== 0 && width
+ gc
.data
.width
> sx
)
1344 width
+= gc
.data
.width
;
1349 * If the line is exactly right, just move it across
1353 grid_reflow_move(target
, gl
);
1358 * If the line is too big, it needs to be split, whether or not
1359 * it was previously wrapped.
1362 grid_reflow_split(target
, gd
, sx
, yy
, at
);
1367 * If the line was previously wrapped, join as much as possible
1370 if (gl
->flags
& GRID_LINE_WRAPPED
)
1371 grid_reflow_join(target
, gd
, sx
, yy
, width
, 0);
1373 grid_reflow_move(target
, gl
);
1377 * Replace the old grid with the new.
1379 if (target
->sy
< gd
->sy
)
1380 grid_reflow_add(target
, gd
->sy
- target
->sy
);
1381 gd
->hsize
= target
->sy
- gd
->sy
;
1382 if (gd
->hscrolled
> gd
->hsize
)
1383 gd
->hscrolled
= gd
->hsize
;
1385 gd
->linedata
= target
->linedata
;
1389 /* Convert to position based on wrapped lines. */
1391 grid_wrap_position(struct grid
*gd
, u_int px
, u_int py
, u_int
*wx
, u_int
*wy
)
1393 u_int ax
= 0, ay
= 0, yy
;
1395 for (yy
= 0; yy
< py
; yy
++) {
1396 if (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1397 ax
+= gd
->linedata
[yy
].cellused
;
1403 if (px
>= gd
->linedata
[yy
].cellused
)
1411 /* Convert position based on wrapped lines back. */
1413 grid_unwrap_position(struct grid
*gd
, u_int
*px
, u_int
*py
, u_int wx
, u_int wy
)
1417 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
- 1; yy
++) {
1420 if (~gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1425 * yy is now 0 on the unwrapped line which contains wx. Walk forwards
1426 * until we find the end or the line now containing wx.
1428 if (wx
== UINT_MAX
) {
1429 while (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1431 wx
= gd
->linedata
[yy
].cellused
;
1433 while (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
) {
1434 if (wx
< gd
->linedata
[yy
].cellused
)
1436 wx
-= gd
->linedata
[yy
].cellused
;
1444 /* Get length of line. */
1446 grid_line_length(struct grid
*gd
, u_int py
)
1448 struct grid_cell gc
;
1451 px
= grid_get_line(gd
, py
)->cellsize
;
1455 grid_get_cell(gd
, px
- 1, py
, &gc
);
1456 if ((gc
.flags
& GRID_FLAG_PADDING
) ||
1457 gc
.data
.size
!= 1 ||
1458 *gc
.data
.data
!= ' ')