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, 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. */
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
!= 8) /* only supports 256 or RGB */
95 if (gc
->flags
& GRID_FLAG_TAB
)
100 /* Get an extended cell. */
102 grid_get_extended_cell(struct grid_line
*gl
, struct grid_cell_entry
*gce
,
105 u_int at
= gl
->extdsize
+ 1;
107 gl
->extddata
= xreallocarray(gl
->extddata
, at
, sizeof *gl
->extddata
);
110 gce
->offset
= at
- 1;
111 gce
->flags
= (flags
| GRID_FLAG_EXTENDED
);
114 /* Set cell as extended. */
115 static struct grid_extd_entry
*
116 grid_extended_cell(struct grid_line
*gl
, struct grid_cell_entry
*gce
,
117 const struct grid_cell
*gc
)
119 struct grid_extd_entry
*gee
;
120 int flags
= (gc
->flags
& ~GRID_FLAG_CLEARED
);
123 if (~gce
->flags
& GRID_FLAG_EXTENDED
)
124 grid_get_extended_cell(gl
, gce
, flags
);
125 else if (gce
->offset
>= gl
->extdsize
)
126 fatalx("offset too big");
127 gl
->flags
|= GRID_LINE_EXTENDED
;
129 if (gc
->flags
& GRID_FLAG_TAB
)
132 utf8_from_data(&gc
->data
, &uc
);
134 gee
= &gl
->extddata
[gce
->offset
];
136 gee
->attr
= gc
->attr
;
141 gee
->link
= gc
->link
;
145 /* Free up unused extended cells. */
147 grid_compact_line(struct grid_line
*gl
)
149 int new_extdsize
= 0;
150 struct grid_extd_entry
*new_extddata
;
151 struct grid_cell_entry
*gce
;
152 struct grid_extd_entry
*gee
;
155 if (gl
->extdsize
== 0)
158 for (px
= 0; px
< gl
->cellsize
; px
++) {
159 gce
= &gl
->celldata
[px
];
160 if (gce
->flags
& GRID_FLAG_EXTENDED
)
164 if (new_extdsize
== 0) {
170 new_extddata
= xreallocarray(NULL
, new_extdsize
, sizeof *gl
->extddata
);
173 for (px
= 0; px
< gl
->cellsize
; px
++) {
174 gce
= &gl
->celldata
[px
];
175 if (gce
->flags
& GRID_FLAG_EXTENDED
) {
176 gee
= &gl
->extddata
[gce
->offset
];
177 memcpy(&new_extddata
[idx
], gee
, sizeof *gee
);
183 gl
->extddata
= new_extddata
;
184 gl
->extdsize
= new_extdsize
;
189 grid_get_line(struct grid
*gd
, u_int line
)
191 return (&gd
->linedata
[line
]);
194 /* Adjust number of lines. */
196 grid_adjust_lines(struct grid
*gd
, u_int lines
)
198 gd
->linedata
= xreallocarray(gd
->linedata
, lines
, sizeof *gd
->linedata
);
201 /* Copy default into a cell. */
203 grid_clear_cell(struct grid
*gd
, u_int px
, u_int py
, u_int bg
)
205 struct grid_line
*gl
= &gd
->linedata
[py
];
206 struct grid_cell_entry
*gce
= &gl
->celldata
[px
];
207 struct grid_extd_entry
*gee
;
209 memcpy(gce
, &grid_cleared_entry
, sizeof *gce
);
211 if (bg
& COLOUR_FLAG_RGB
) {
212 grid_get_extended_cell(gl
, gce
, gce
->flags
);
213 gee
= grid_extended_cell(gl
, gce
, &grid_cleared_cell
);
216 if (bg
& COLOUR_FLAG_256
)
217 gce
->flags
|= GRID_FLAG_BG256
;
223 /* Check grid y position. */
225 grid_check_y(struct grid
*gd
, const char *from
, u_int py
)
227 if (py
>= gd
->hsize
+ gd
->sy
) {
228 log_debug("%s: y out of range: %u", from
, py
);
234 /* Check if two styles are (visibly) the same. */
236 grid_cells_look_equal(const struct grid_cell
*gc1
, const struct grid_cell
*gc2
)
238 if (gc1
->fg
!= gc2
->fg
|| gc1
->bg
!= gc2
->bg
)
240 if (gc1
->attr
!= gc2
->attr
|| gc1
->flags
!= gc2
->flags
)
242 if (gc1
->link
!= gc2
->link
)
247 /* Compare grid cells. Return 1 if equal, 0 if not. */
249 grid_cells_equal(const struct grid_cell
*gc1
, const struct grid_cell
*gc2
)
251 if (!grid_cells_look_equal(gc1
, gc2
))
253 if (gc1
->data
.width
!= gc2
->data
.width
)
255 if (gc1
->data
.size
!= gc2
->data
.size
)
257 return (memcmp(gc1
->data
.data
, gc2
->data
.data
, gc1
->data
.size
) == 0);
260 /* Set grid cell to a tab. */
262 grid_set_tab(struct grid_cell
*gc
, u_int width
)
264 memset(&gc
->data
, 0, sizeof gc
->data
);
265 gc
->flags
|= GRID_FLAG_TAB
;
266 gc
->data
.width
= gc
->data
.size
= gc
->data
.have
= width
;
267 memset(&gc
->data
, ' ', gc
->data
.size
);
272 grid_free_line(struct grid
*gd
, u_int py
)
274 free(gd
->linedata
[py
].celldata
);
275 gd
->linedata
[py
].celldata
= NULL
;
276 free(gd
->linedata
[py
].extddata
);
277 gd
->linedata
[py
].extddata
= NULL
;
280 /* Free several lines. */
282 grid_free_lines(struct grid
*gd
, u_int py
, u_int ny
)
286 for (yy
= py
; yy
< py
+ ny
; yy
++)
287 grid_free_line(gd
, yy
);
290 /* Create a new grid. */
292 grid_create(u_int sx
, u_int sy
, u_int hlimit
)
296 gd
= xmalloc(sizeof *gd
);
301 gd
->flags
= GRID_HISTORY
;
310 gd
->linedata
= xcalloc(gd
->sy
, sizeof *gd
->linedata
);
319 grid_destroy(struct grid
*gd
)
321 grid_free_lines(gd
, 0, gd
->hsize
+ gd
->sy
);
330 grid_compare(struct grid
*ga
, struct grid
*gb
)
332 struct grid_line
*gla
, *glb
;
333 struct grid_cell gca
, gcb
;
336 if (ga
->sx
!= gb
->sx
|| ga
->sy
!= gb
->sy
)
339 for (yy
= 0; yy
< ga
->sy
; yy
++) {
340 gla
= &ga
->linedata
[yy
];
341 glb
= &gb
->linedata
[yy
];
342 if (gla
->cellsize
!= glb
->cellsize
)
344 for (xx
= 0; xx
< gla
->cellsize
; xx
++) {
345 grid_get_cell(ga
, xx
, yy
, &gca
);
346 grid_get_cell(gb
, xx
, yy
, &gcb
);
347 if (!grid_cells_equal(&gca
, &gcb
))
355 /* Trim lines from the history. */
357 grid_trim_history(struct grid
*gd
, u_int ny
)
359 grid_free_lines(gd
, 0, ny
);
360 memmove(&gd
->linedata
[0], &gd
->linedata
[ny
],
361 (gd
->hsize
+ gd
->sy
- ny
) * (sizeof *gd
->linedata
));
365 * Collect lines from the history if at the limit. Free the top (oldest) 10%
369 grid_collect_history(struct grid
*gd
)
373 if (gd
->hsize
== 0 || gd
->hsize
< gd
->hlimit
)
376 ny
= gd
->hlimit
/ 10;
383 * Free the lines from 0 to ny then move the remaining lines over
386 grid_trim_history(gd
, ny
);
389 if (gd
->hscrolled
> gd
->hsize
)
390 gd
->hscrolled
= gd
->hsize
;
393 /* Remove lines from the bottom of the history. */
395 grid_remove_history(struct grid
*gd
, u_int ny
)
401 for (yy
= 0; yy
< ny
; yy
++)
402 grid_free_line(gd
, gd
->hsize
+ gd
->sy
- 1 - yy
);
407 * Scroll the entire visible screen, moving one line into the history. Just
408 * allocate a new line at the bottom and move the history size indicator.
411 grid_scroll_history(struct grid
*gd
, u_int bg
)
415 yy
= gd
->hsize
+ gd
->sy
;
416 gd
->linedata
= xreallocarray(gd
->linedata
, yy
+ 1,
417 sizeof *gd
->linedata
);
418 grid_empty_line(gd
, yy
, bg
);
421 grid_compact_line(&gd
->linedata
[gd
->hsize
]);
422 gd
->linedata
[gd
->hsize
].time
= current_time
;
426 /* Clear the history. */
428 grid_clear_history(struct grid
*gd
)
430 grid_trim_history(gd
, gd
->hsize
);
435 gd
->linedata
= xreallocarray(gd
->linedata
, gd
->sy
,
436 sizeof *gd
->linedata
);
439 /* Scroll a region up, moving the top line into the history. */
441 grid_scroll_history_region(struct grid
*gd
, u_int upper
, u_int lower
, u_int bg
)
443 struct grid_line
*gl_history
, *gl_upper
;
446 /* Create a space for a new line. */
447 yy
= gd
->hsize
+ gd
->sy
;
448 gd
->linedata
= xreallocarray(gd
->linedata
, yy
+ 1,
449 sizeof *gd
->linedata
);
451 /* Move the entire screen down to free a space for this line. */
452 gl_history
= &gd
->linedata
[gd
->hsize
];
453 memmove(gl_history
+ 1, gl_history
, gd
->sy
* sizeof *gl_history
);
455 /* Adjust the region and find its start and end. */
457 gl_upper
= &gd
->linedata
[upper
];
460 /* Move the line into the history. */
461 memcpy(gl_history
, gl_upper
, sizeof *gl_history
);
462 gl_history
->time
= current_time
;
464 /* Then move the region up and clear the bottom line. */
465 memmove(gl_upper
, gl_upper
+ 1, (lower
- upper
) * sizeof *gl_upper
);
466 grid_empty_line(gd
, lower
, bg
);
468 /* Move the history offset down over the line. */
473 /* Expand line to fit to cell. */
475 grid_expand_line(struct grid
*gd
, u_int py
, u_int sx
, u_int bg
)
477 struct grid_line
*gl
;
480 gl
= &gd
->linedata
[py
];
481 if (sx
<= gl
->cellsize
)
486 else if (sx
< gd
->sx
/ 2)
488 else if (gd
->sx
> sx
)
491 gl
->celldata
= xreallocarray(gl
->celldata
, sx
, sizeof *gl
->celldata
);
492 for (xx
= gl
->cellsize
; xx
< sx
; xx
++)
493 grid_clear_cell(gd
, xx
, py
, bg
);
497 /* Empty a line and set background colour if needed. */
499 grid_empty_line(struct grid
*gd
, u_int py
, u_int bg
)
501 memset(&gd
->linedata
[py
], 0, sizeof gd
->linedata
[py
]);
502 if (!COLOUR_DEFAULT(bg
))
503 grid_expand_line(gd
, py
, gd
->sx
, bg
);
506 /* Peek at grid line. */
507 const struct grid_line
*
508 grid_peek_line(struct grid
*gd
, u_int py
)
510 if (grid_check_y(gd
, __func__
, py
) != 0)
512 return (&gd
->linedata
[py
]);
515 /* Get cell from line. */
517 grid_get_cell1(struct grid_line
*gl
, u_int px
, struct grid_cell
*gc
)
519 struct grid_cell_entry
*gce
= &gl
->celldata
[px
];
520 struct grid_extd_entry
*gee
;
522 if (gce
->flags
& GRID_FLAG_EXTENDED
) {
523 if (gce
->offset
>= gl
->extdsize
)
524 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
526 gee
= &gl
->extddata
[gce
->offset
];
527 gc
->flags
= gee
->flags
;
528 gc
->attr
= gee
->attr
;
532 gc
->link
= gee
->link
;
534 if (gc
->flags
& GRID_FLAG_TAB
)
535 grid_set_tab(gc
, gee
->data
);
537 utf8_to_data(gee
->data
, &gc
->data
);
542 gc
->flags
= gce
->flags
& ~(GRID_FLAG_FG256
|GRID_FLAG_BG256
);
543 gc
->attr
= gce
->data
.attr
;
544 gc
->fg
= gce
->data
.fg
;
545 if (gce
->flags
& GRID_FLAG_FG256
)
546 gc
->fg
|= COLOUR_FLAG_256
;
547 gc
->bg
= gce
->data
.bg
;
548 if (gce
->flags
& GRID_FLAG_BG256
)
549 gc
->bg
|= COLOUR_FLAG_256
;
551 utf8_set(&gc
->data
, gce
->data
.data
);
555 /* Get cell for reading. */
557 grid_get_cell(struct grid
*gd
, u_int px
, u_int py
, struct grid_cell
*gc
)
559 if (grid_check_y(gd
, __func__
, py
) != 0 ||
560 px
>= gd
->linedata
[py
].cellsize
)
561 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
563 grid_get_cell1(&gd
->linedata
[py
], px
, gc
);
566 /* Set cell at position. */
568 grid_set_cell(struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
)
570 struct grid_line
*gl
;
571 struct grid_cell_entry
*gce
;
573 if (grid_check_y(gd
, __func__
, py
) != 0)
576 grid_expand_line(gd
, py
, px
+ 1, 8);
578 gl
= &gd
->linedata
[py
];
579 if (px
+ 1 > gl
->cellused
)
580 gl
->cellused
= px
+ 1;
582 gce
= &gl
->celldata
[px
];
583 if (grid_need_extended_cell(gce
, gc
))
584 grid_extended_cell(gl
, gce
, gc
);
586 grid_store_cell(gce
, gc
, gc
->data
.data
[0]);
589 /* Set padding at position. */
591 grid_set_padding(struct grid
*gd
, u_int px
, u_int py
)
593 grid_set_cell(gd
, px
, py
, &grid_padding_cell
);
596 /* Set cells at position. */
598 grid_set_cells(struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
,
599 const char *s
, size_t slen
)
601 struct grid_line
*gl
;
602 struct grid_cell_entry
*gce
;
603 struct grid_extd_entry
*gee
;
606 if (grid_check_y(gd
, __func__
, py
) != 0)
609 grid_expand_line(gd
, py
, px
+ slen
, 8);
611 gl
= &gd
->linedata
[py
];
612 if (px
+ slen
> gl
->cellused
)
613 gl
->cellused
= px
+ slen
;
615 for (i
= 0; i
< slen
; i
++) {
616 gce
= &gl
->celldata
[px
+ i
];
617 if (grid_need_extended_cell(gce
, gc
)) {
618 gee
= grid_extended_cell(gl
, gce
, gc
);
619 gee
->data
= utf8_build_one(s
[i
]);
621 grid_store_cell(gce
, gc
, s
[i
]);
627 grid_clear(struct grid
*gd
, u_int px
, u_int py
, u_int nx
, u_int ny
, u_int bg
)
629 struct grid_line
*gl
;
630 u_int xx
, yy
, ox
, sx
;
632 if (nx
== 0 || ny
== 0)
635 if (px
== 0 && nx
== gd
->sx
) {
636 grid_clear_lines(gd
, py
, ny
, bg
);
640 if (grid_check_y(gd
, __func__
, py
) != 0)
642 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
645 for (yy
= py
; yy
< py
+ ny
; yy
++) {
646 gl
= &gd
->linedata
[yy
];
649 if (sx
> gl
->cellsize
)
652 if (COLOUR_DEFAULT(bg
)) {
659 grid_expand_line(gd
, yy
, px
+ ox
, 8); /* default bg first */
660 for (xx
= px
; xx
< px
+ ox
; xx
++)
661 grid_clear_cell(gd
, xx
, yy
, bg
);
665 /* Clear lines. This just frees and truncates the lines. */
667 grid_clear_lines(struct grid
*gd
, u_int py
, u_int ny
, u_int bg
)
674 if (grid_check_y(gd
, __func__
, py
) != 0)
676 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
679 for (yy
= py
; yy
< py
+ ny
; yy
++) {
680 grid_free_line(gd
, yy
);
681 grid_empty_line(gd
, yy
, bg
);
684 gd
->linedata
[py
- 1].flags
&= ~GRID_LINE_WRAPPED
;
687 /* Move a group of lines. */
689 grid_move_lines(struct grid
*gd
, u_int dy
, u_int py
, u_int ny
, u_int bg
)
693 if (ny
== 0 || py
== dy
)
696 if (grid_check_y(gd
, __func__
, py
) != 0)
698 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
700 if (grid_check_y(gd
, __func__
, dy
) != 0)
702 if (grid_check_y(gd
, __func__
, dy
+ ny
- 1) != 0)
705 /* Free any lines which are being replaced. */
706 for (yy
= dy
; yy
< dy
+ ny
; yy
++) {
707 if (yy
>= py
&& yy
< py
+ ny
)
709 grid_free_line(gd
, yy
);
712 gd
->linedata
[dy
- 1].flags
&= ~GRID_LINE_WRAPPED
;
714 memmove(&gd
->linedata
[dy
], &gd
->linedata
[py
],
715 ny
* (sizeof *gd
->linedata
));
718 * Wipe any lines that have been moved (without freeing them - they are
721 for (yy
= py
; yy
< py
+ ny
; yy
++) {
722 if (yy
< dy
|| yy
>= dy
+ ny
)
723 grid_empty_line(gd
, yy
, bg
);
725 if (py
!= 0 && (py
< dy
|| py
>= dy
+ ny
))
726 gd
->linedata
[py
- 1].flags
&= ~GRID_LINE_WRAPPED
;
729 /* Move a group of cells. */
731 grid_move_cells(struct grid
*gd
, u_int dx
, u_int px
, u_int py
, u_int nx
,
734 struct grid_line
*gl
;
737 if (nx
== 0 || px
== dx
)
740 if (grid_check_y(gd
, __func__
, py
) != 0)
742 gl
= &gd
->linedata
[py
];
744 grid_expand_line(gd
, py
, px
+ nx
, 8);
745 grid_expand_line(gd
, py
, dx
+ nx
, 8);
746 memmove(&gl
->celldata
[dx
], &gl
->celldata
[px
],
747 nx
* sizeof *gl
->celldata
);
748 if (dx
+ nx
> gl
->cellused
)
749 gl
->cellused
= dx
+ nx
;
751 /* Wipe any cells that have been moved. */
752 for (xx
= px
; xx
< px
+ nx
; xx
++) {
753 if (xx
>= dx
&& xx
< dx
+ nx
)
755 grid_clear_cell(gd
, xx
, py
, bg
);
759 /* Get ANSI foreground sequence. */
761 grid_string_cells_fg(const struct grid_cell
*gc
, int *values
)
767 if (gc
->fg
& COLOUR_FLAG_256
) {
770 values
[n
++] = gc
->fg
& 0xff;
771 } else if (gc
->fg
& COLOUR_FLAG_RGB
) {
774 colour_split_rgb(gc
->fg
, &r
, &g
, &b
);
788 values
[n
++] = gc
->fg
+ 30;
801 values
[n
++] = gc
->fg
;
808 /* Get ANSI background sequence. */
810 grid_string_cells_bg(const struct grid_cell
*gc
, int *values
)
816 if (gc
->bg
& COLOUR_FLAG_256
) {
819 values
[n
++] = gc
->bg
& 0xff;
820 } else if (gc
->bg
& COLOUR_FLAG_RGB
) {
823 colour_split_rgb(gc
->bg
, &r
, &g
, &b
);
837 values
[n
++] = gc
->bg
+ 40;
850 values
[n
++] = gc
->bg
+ 10;
857 /* Get underscore colour sequence. */
859 grid_string_cells_us(const struct grid_cell
*gc
, int *values
)
865 if (gc
->us
& COLOUR_FLAG_256
) {
868 values
[n
++] = gc
->us
& 0xff;
869 } else if (gc
->us
& COLOUR_FLAG_RGB
) {
872 colour_split_rgb(gc
->us
, &r
, &g
, &b
);
880 /* Add on SGR code. */
882 grid_string_cells_add_code(char *buf
, size_t len
, u_int n
, int *s
, int *newc
,
883 int *oldc
, size_t nnewc
, size_t noldc
, int flags
)
887 int reset
= (n
!= 0 && s
[0] == 0);
890 return; /* no code to add */
893 memcmp(newc
, oldc
, nnewc
* sizeof newc
[0]) == 0)
894 return; /* no reset and colour unchanged */
895 if (reset
&& (newc
[0] == 49 || newc
[0] == 39))
896 return; /* reset and colour default */
898 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
899 strlcat(buf
, "\\033[", len
);
901 strlcat(buf
, "\033[", len
);
902 for (i
= 0; i
< nnewc
; i
++) {
904 xsnprintf(tmp
, sizeof tmp
, "%d;", newc
[i
]);
906 xsnprintf(tmp
, sizeof tmp
, "%d", newc
[i
]);
907 strlcat(buf
, tmp
, len
);
909 strlcat(buf
, "m", len
);
913 grid_string_cells_add_hyperlink(char *buf
, size_t len
, const char *id
,
914 const char *uri
, int flags
)
918 if (strlen(uri
) + strlen(id
) + 17 >= len
)
921 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
922 strlcat(buf
, "\\033]8;", len
);
924 strlcat(buf
, "\033]8;", len
);
926 xasprintf(&tmp
, "id=%s;", id
);
927 strlcat(buf
, tmp
, len
);
930 strlcat(buf
, ";", len
);
931 strlcat(buf
, uri
, len
);
932 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
933 strlcat(buf
, "\\033\\\\", len
);
935 strlcat(buf
, "\033\\", len
);
940 * Returns ANSI code to set particular attributes (colour, bold and so on)
941 * given a current state.
944 grid_string_cells_code(const struct grid_cell
*lastgc
,
945 const struct grid_cell
*gc
, char *buf
, size_t len
, int flags
,
946 struct screen
*sc
, int *has_link
)
948 int oldc
[64], newc
[64], s
[128];
949 size_t noldc
, nnewc
, n
, i
;
950 u_int attr
= gc
->attr
, lastattr
= lastgc
->attr
;
952 const char *uri
, *id
;
954 static const struct {
958 { GRID_ATTR_BRIGHT
, 1 },
959 { GRID_ATTR_DIM
, 2 },
960 { GRID_ATTR_ITALICS
, 3 },
961 { GRID_ATTR_UNDERSCORE
, 4 },
962 { GRID_ATTR_BLINK
, 5 },
963 { GRID_ATTR_REVERSE
, 7 },
964 { GRID_ATTR_HIDDEN
, 8 },
965 { GRID_ATTR_STRIKETHROUGH
, 9 },
966 { GRID_ATTR_UNDERSCORE_2
, 42 },
967 { GRID_ATTR_UNDERSCORE_3
, 43 },
968 { GRID_ATTR_UNDERSCORE_4
, 44 },
969 { GRID_ATTR_UNDERSCORE_5
, 45 },
970 { GRID_ATTR_OVERLINE
, 53 },
974 /* If any attribute is removed, begin with 0. */
975 for (i
= 0; i
< nitems(attrs
); i
++) {
976 if (((~attr
& attrs
[i
].mask
) &&
977 (lastattr
& attrs
[i
].mask
)) ||
978 (lastgc
->us
!= 8 && gc
->us
== 8)) {
980 lastattr
&= GRID_ATTR_CHARSET
;
984 /* For each attribute that is newly set, add its code. */
985 for (i
= 0; i
< nitems(attrs
); i
++) {
986 if ((attr
& attrs
[i
].mask
) && !(lastattr
& attrs
[i
].mask
))
987 s
[n
++] = attrs
[i
].code
;
990 /* Write the attributes. */
993 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
994 strlcat(buf
, "\\033[", len
);
996 strlcat(buf
, "\033[", len
);
997 for (i
= 0; i
< n
; i
++) {
999 xsnprintf(tmp
, sizeof tmp
, "%d", s
[i
]);
1001 xsnprintf(tmp
, sizeof tmp
, "%d:%d", s
[i
] / 10,
1004 strlcat(buf
, tmp
, len
);
1006 strlcat(buf
, ";", len
);
1008 strlcat(buf
, "m", len
);
1011 /* If the foreground colour changed, write its parameters. */
1012 nnewc
= grid_string_cells_fg(gc
, newc
);
1013 noldc
= grid_string_cells_fg(lastgc
, oldc
);
1014 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
1017 /* If the background colour changed, append its parameters. */
1018 nnewc
= grid_string_cells_bg(gc
, newc
);
1019 noldc
= grid_string_cells_bg(lastgc
, oldc
);
1020 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
1023 /* If the underscore colour changed, append its parameters. */
1024 nnewc
= grid_string_cells_us(gc
, newc
);
1025 noldc
= grid_string_cells_us(lastgc
, oldc
);
1026 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
1029 /* Append shift in/shift out if needed. */
1030 if ((attr
& GRID_ATTR_CHARSET
) && !(lastattr
& GRID_ATTR_CHARSET
)) {
1031 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
1032 strlcat(buf
, "\\016", len
); /* SO */
1034 strlcat(buf
, "\016", len
); /* SO */
1036 if (!(attr
& GRID_ATTR_CHARSET
) && (lastattr
& GRID_ATTR_CHARSET
)) {
1037 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
1038 strlcat(buf
, "\\017", len
); /* SI */
1040 strlcat(buf
, "\017", len
); /* SI */
1043 /* Add hyperlink if changed. */
1044 if (sc
!= NULL
&& sc
->hyperlinks
!= NULL
&& lastgc
->link
!= gc
->link
) {
1045 if (hyperlinks_get(sc
->hyperlinks
, gc
->link
, &uri
, &id
, NULL
)) {
1046 *has_link
= grid_string_cells_add_hyperlink(buf
, len
,
1048 } else if (*has_link
) {
1049 grid_string_cells_add_hyperlink(buf
, len
, "", "",
1056 /* Convert cells into a string. */
1058 grid_string_cells(struct grid
*gd
, u_int px
, u_int py
, u_int nx
,
1059 struct grid_cell
**lastgc
, int flags
, struct screen
*s
)
1061 struct grid_cell gc
;
1062 static struct grid_cell lastgc1
;
1064 char *buf
, code
[8192];
1065 size_t len
, off
, size
, codelen
;
1068 const struct grid_line
*gl
;
1070 if (lastgc
!= NULL
&& *lastgc
== NULL
) {
1071 memcpy(&lastgc1
, &grid_default_cell
, sizeof lastgc1
);
1079 gl
= grid_peek_line(gd
, py
);
1080 if (flags
& GRID_STRING_EMPTY_CELLS
)
1084 for (xx
= px
; xx
< px
+ nx
; xx
++) {
1085 if (gl
== NULL
|| xx
>= end
)
1087 grid_get_cell(gd
, xx
, py
, &gc
);
1088 if (gc
.flags
& GRID_FLAG_PADDING
)
1091 if (flags
& GRID_STRING_WITH_SEQUENCES
) {
1092 grid_string_cells_code(*lastgc
, &gc
, code
, sizeof code
,
1093 flags
, s
, &has_link
);
1094 codelen
= strlen(code
);
1095 memcpy(*lastgc
, &gc
, sizeof **lastgc
);
1099 if (gc
.flags
& GRID_FLAG_TAB
) {
1103 data
= gc
.data
.data
;
1104 size
= gc
.data
.size
;
1105 if ((flags
& GRID_STRING_ESCAPE_SEQUENCES
) &&
1113 while (len
< off
+ size
+ codelen
+ 1) {
1114 buf
= xreallocarray(buf
, 2, len
);
1119 memcpy(buf
+ off
, code
, codelen
);
1122 memcpy(buf
+ off
, data
, size
);
1127 grid_string_cells_add_hyperlink(code
, sizeof code
, "", "",
1129 codelen
= strlen(code
);
1130 while (len
< off
+ size
+ codelen
+ 1) {
1131 buf
= xreallocarray(buf
, 2, len
);
1134 memcpy(buf
+ off
, code
, codelen
);
1138 if (flags
& GRID_STRING_TRIM_SPACES
) {
1139 while (off
> 0 && buf
[off
- 1] == ' ')
1148 * Duplicate a set of lines between two grids. Both source and destination
1149 * should be big enough.
1152 grid_duplicate_lines(struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
,
1155 struct grid_line
*dstl
, *srcl
;
1158 if (dy
+ ny
> dst
->hsize
+ dst
->sy
)
1159 ny
= dst
->hsize
+ dst
->sy
- dy
;
1160 if (sy
+ ny
> src
->hsize
+ src
->sy
)
1161 ny
= src
->hsize
+ src
->sy
- sy
;
1162 grid_free_lines(dst
, dy
, ny
);
1164 for (yy
= 0; yy
< ny
; yy
++) {
1165 srcl
= &src
->linedata
[sy
];
1166 dstl
= &dst
->linedata
[dy
];
1168 memcpy(dstl
, srcl
, sizeof *dstl
);
1169 if (srcl
->cellsize
!= 0) {
1170 dstl
->celldata
= xreallocarray(NULL
,
1171 srcl
->cellsize
, sizeof *dstl
->celldata
);
1172 memcpy(dstl
->celldata
, srcl
->celldata
,
1173 srcl
->cellsize
* sizeof *dstl
->celldata
);
1175 dstl
->celldata
= NULL
;
1176 if (srcl
->extdsize
!= 0) {
1177 dstl
->extdsize
= srcl
->extdsize
;
1178 dstl
->extddata
= xreallocarray(NULL
, dstl
->extdsize
,
1179 sizeof *dstl
->extddata
);
1180 memcpy(dstl
->extddata
, srcl
->extddata
, dstl
->extdsize
*
1181 sizeof *dstl
->extddata
);
1183 dstl
->extddata
= NULL
;
1190 /* Mark line as dead. */
1192 grid_reflow_dead(struct grid_line
*gl
)
1194 memset(gl
, 0, sizeof *gl
);
1195 gl
->flags
= GRID_LINE_DEAD
;
1198 /* Add lines, return the first new one. */
1199 static struct grid_line
*
1200 grid_reflow_add(struct grid
*gd
, u_int n
)
1202 struct grid_line
*gl
;
1203 u_int sy
= gd
->sy
+ n
;
1205 gd
->linedata
= xreallocarray(gd
->linedata
, sy
, sizeof *gd
->linedata
);
1206 gl
= &gd
->linedata
[gd
->sy
];
1207 memset(gl
, 0, n
* (sizeof *gl
));
1212 /* Move a line across. */
1213 static struct grid_line
*
1214 grid_reflow_move(struct grid
*gd
, struct grid_line
*from
)
1216 struct grid_line
*to
;
1218 to
= grid_reflow_add(gd
, 1);
1219 memcpy(to
, from
, sizeof *to
);
1220 grid_reflow_dead(from
);
1224 /* Join line below onto this one. */
1226 grid_reflow_join(struct grid
*target
, struct grid
*gd
, u_int sx
, u_int yy
,
1227 u_int width
, int already
)
1229 struct grid_line
*gl
, *from
= NULL
;
1230 struct grid_cell gc
;
1231 u_int lines
, left
, i
, to
, line
, want
= 0;
1236 * Add a new target line.
1240 gl
= grid_reflow_move(target
, &gd
->linedata
[yy
]);
1242 to
= target
->sy
- 1;
1243 gl
= &target
->linedata
[to
];
1248 * Loop until no more to consume or the target line is full.
1253 * If this is now the last line, there is nothing more to be
1256 if (yy
+ 1 + lines
== gd
->hsize
+ gd
->sy
)
1258 line
= yy
+ 1 + lines
;
1260 /* If the next line is empty, skip it. */
1261 if (~gd
->linedata
[line
].flags
& GRID_LINE_WRAPPED
)
1263 if (gd
->linedata
[line
].cellused
== 0) {
1271 * Is the destination line now full? Copy the first character
1272 * separately because we need to leave "from" set to the last
1273 * line if this line is full.
1275 grid_get_cell1(&gd
->linedata
[line
], 0, &gc
);
1276 if (width
+ gc
.data
.width
> sx
)
1278 width
+= gc
.data
.width
;
1279 grid_set_cell(target
, at
, to
, &gc
);
1282 /* Join as much more as possible onto the current line. */
1283 from
= &gd
->linedata
[line
];
1284 for (want
= 1; want
< from
->cellused
; want
++) {
1285 grid_get_cell1(from
, want
, &gc
);
1286 if (width
+ gc
.data
.width
> sx
)
1288 width
+= gc
.data
.width
;
1290 grid_set_cell(target
, at
, to
, &gc
);
1296 * If this line wasn't wrapped or we didn't consume the entire
1297 * line, don't try to join any further lines.
1299 if (!wrapped
|| want
!= from
->cellused
|| width
== sx
)
1306 * If we didn't consume the entire final line, then remove what we did
1307 * consume. If we consumed the entire line and it wasn't wrapped,
1308 * remove the wrap flag from this line.
1310 left
= from
->cellused
- want
;
1312 grid_move_cells(gd
, 0, want
, yy
+ lines
, left
, 8);
1313 from
->cellsize
= from
->cellused
= left
;
1315 } else if (!wrapped
)
1316 gl
->flags
&= ~GRID_LINE_WRAPPED
;
1318 /* Remove the lines that were completely consumed. */
1319 for (i
= yy
+ 1; i
< yy
+ 1 + lines
; i
++) {
1320 free(gd
->linedata
[i
].celldata
);
1321 free(gd
->linedata
[i
].extddata
);
1322 grid_reflow_dead(&gd
->linedata
[i
]);
1325 /* Adjust scroll position. */
1326 if (gd
->hscrolled
> to
+ lines
)
1327 gd
->hscrolled
-= lines
;
1328 else if (gd
->hscrolled
> to
)
1332 /* Split this line into several new ones */
1334 grid_reflow_split(struct grid
*target
, struct grid
*gd
, u_int sx
, u_int yy
,
1337 struct grid_line
*gl
= &gd
->linedata
[yy
], *first
;
1338 struct grid_cell gc
;
1339 u_int line
, lines
, width
, i
, xx
;
1340 u_int used
= gl
->cellused
;
1341 int flags
= gl
->flags
;
1343 /* How many lines do we need to insert? We know we need at least two. */
1344 if (~gl
->flags
& GRID_LINE_EXTENDED
)
1345 lines
= 1 + (gl
->cellused
- 1) / sx
;
1349 for (i
= at
; i
< used
; i
++) {
1350 grid_get_cell1(gl
, i
, &gc
);
1351 if (width
+ gc
.data
.width
> sx
) {
1355 width
+= gc
.data
.width
;
1359 /* Insert new lines. */
1360 line
= target
->sy
+ 1;
1361 first
= grid_reflow_add(target
, lines
);
1363 /* Copy sections from the original line. */
1366 for (i
= at
; i
< used
; i
++) {
1367 grid_get_cell1(gl
, i
, &gc
);
1368 if (width
+ gc
.data
.width
> sx
) {
1369 target
->linedata
[line
].flags
|= GRID_LINE_WRAPPED
;
1375 width
+= gc
.data
.width
;
1376 grid_set_cell(target
, xx
, line
, &gc
);
1379 if (flags
& GRID_LINE_WRAPPED
)
1380 target
->linedata
[line
].flags
|= GRID_LINE_WRAPPED
;
1382 /* Move the remainder of the original line. */
1383 gl
->cellsize
= gl
->cellused
= at
;
1384 gl
->flags
|= GRID_LINE_WRAPPED
;
1385 memcpy(first
, gl
, sizeof *first
);
1386 grid_reflow_dead(gl
);
1388 /* Adjust the scroll position. */
1389 if (yy
<= gd
->hscrolled
)
1390 gd
->hscrolled
+= lines
- 1;
1393 * If the original line had the wrapped flag and there is still space
1394 * in the last new line, try to join with the next lines.
1396 if (width
< sx
&& (flags
& GRID_LINE_WRAPPED
))
1397 grid_reflow_join(target
, gd
, sx
, yy
, width
, 1);
1400 /* Reflow lines on grid to new width. */
1402 grid_reflow(struct grid
*gd
, u_int sx
)
1404 struct grid
*target
;
1405 struct grid_line
*gl
;
1406 struct grid_cell gc
;
1407 u_int yy
, width
, i
, at
;
1410 * Create a destination grid. This is just used as a container for the
1411 * line data and may not be fully valid.
1413 target
= grid_create(gd
->sx
, 0, 0);
1416 * Loop over each source line.
1418 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
; yy
++) {
1419 gl
= &gd
->linedata
[yy
];
1420 if (gl
->flags
& GRID_LINE_DEAD
)
1424 * Work out the width of this line. at is the point at which
1425 * the available width is hit, and width is the full line
1429 if (~gl
->flags
& GRID_LINE_EXTENDED
) {
1430 width
= gl
->cellused
;
1436 for (i
= 0; i
< gl
->cellused
; i
++) {
1437 grid_get_cell1(gl
, i
, &gc
);
1438 if (at
== 0 && width
+ gc
.data
.width
> sx
)
1440 width
+= gc
.data
.width
;
1445 * If the line is exactly right, just move it across
1449 grid_reflow_move(target
, gl
);
1454 * If the line is too big, it needs to be split, whether or not
1455 * it was previously wrapped.
1458 grid_reflow_split(target
, gd
, sx
, yy
, at
);
1463 * If the line was previously wrapped, join as much as possible
1466 if (gl
->flags
& GRID_LINE_WRAPPED
)
1467 grid_reflow_join(target
, gd
, sx
, yy
, width
, 0);
1469 grid_reflow_move(target
, gl
);
1473 * Replace the old grid with the new.
1475 if (target
->sy
< gd
->sy
)
1476 grid_reflow_add(target
, gd
->sy
- target
->sy
);
1477 gd
->hsize
= target
->sy
- gd
->sy
;
1478 if (gd
->hscrolled
> gd
->hsize
)
1479 gd
->hscrolled
= gd
->hsize
;
1481 gd
->linedata
= target
->linedata
;
1485 /* Convert to position based on wrapped lines. */
1487 grid_wrap_position(struct grid
*gd
, u_int px
, u_int py
, u_int
*wx
, u_int
*wy
)
1489 u_int ax
= 0, ay
= 0, yy
;
1491 for (yy
= 0; yy
< py
; yy
++) {
1492 if (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1493 ax
+= gd
->linedata
[yy
].cellused
;
1499 if (px
>= gd
->linedata
[yy
].cellused
)
1507 /* Convert position based on wrapped lines back. */
1509 grid_unwrap_position(struct grid
*gd
, u_int
*px
, u_int
*py
, u_int wx
, u_int wy
)
1513 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
- 1; yy
++) {
1516 if (~gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1521 * yy is now 0 on the unwrapped line which contains wx. Walk forwards
1522 * until we find the end or the line now containing wx.
1524 if (wx
== UINT_MAX
) {
1525 while (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1527 wx
= gd
->linedata
[yy
].cellused
;
1529 while (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
) {
1530 if (wx
< gd
->linedata
[yy
].cellused
)
1532 wx
-= gd
->linedata
[yy
].cellused
;
1540 /* Get length of line. */
1542 grid_line_length(struct grid
*gd
, u_int py
)
1544 struct grid_cell gc
;
1547 px
= grid_get_line(gd
, py
)->cellsize
;
1551 grid_get_cell(gd
, px
- 1, py
, &gc
);
1552 if ((gc
.flags
& GRID_FLAG_PADDING
) ||
1553 gc
.data
.size
!= 1 ||
1554 *gc
.data
.data
!= ' ')