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 */
98 /* Get an extended cell. */
100 grid_get_extended_cell(struct grid_line
*gl
, struct grid_cell_entry
*gce
,
103 u_int at
= gl
->extdsize
+ 1;
105 gl
->extddata
= xreallocarray(gl
->extddata
, at
, sizeof *gl
->extddata
);
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
);
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
];
131 gee
->attr
= gc
->attr
;
136 gee
->link
= gc
->link
;
140 /* Free up unused extended cells. */
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
;
150 if (gl
->extdsize
== 0)
153 for (px
= 0; px
< gl
->cellsize
; px
++) {
154 gce
= &gl
->celldata
[px
];
155 if (gce
->flags
& GRID_FLAG_EXTENDED
)
159 if (new_extdsize
== 0) {
165 new_extddata
= xreallocarray(NULL
, new_extdsize
, sizeof *gl
->extddata
);
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
);
178 gl
->extddata
= new_extddata
;
179 gl
->extdsize
= new_extdsize
;
184 grid_get_line(struct grid
*gd
, u_int line
)
186 return (&gd
->linedata
[line
]);
189 /* Adjust number of lines. */
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. */
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
);
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
);
211 if (bg
& COLOUR_FLAG_256
)
212 gce
->flags
|= GRID_FLAG_BG256
;
218 /* Check grid y position. */
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
);
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
)
235 if (gc1
->attr
!= gc2
->attr
|| gc1
->flags
!= gc2
->flags
)
237 if (gc1
->link
!= gc2
->link
)
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
))
248 if (gc1
->data
.width
!= gc2
->data
.width
)
250 if (gc1
->data
.size
!= gc2
->data
.size
)
252 return (memcmp(gc1
->data
.data
, gc2
->data
.data
, gc1
->data
.size
) == 0);
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. */
267 grid_free_lines(struct grid
*gd
, u_int py
, u_int ny
)
271 for (yy
= py
; yy
< py
+ ny
; yy
++)
272 grid_free_line(gd
, yy
);
275 /* Create a new grid. */
277 grid_create(u_int sx
, u_int sy
, u_int hlimit
)
281 gd
= xmalloc(sizeof *gd
);
286 gd
->flags
= GRID_HISTORY
;
295 gd
->linedata
= xcalloc(gd
->sy
, sizeof *gd
->linedata
);
304 grid_destroy(struct grid
*gd
)
306 grid_free_lines(gd
, 0, gd
->hsize
+ gd
->sy
);
315 grid_compare(struct grid
*ga
, struct grid
*gb
)
317 struct grid_line
*gla
, *glb
;
318 struct grid_cell gca
, gcb
;
321 if (ga
->sx
!= gb
->sx
|| ga
->sy
!= gb
->sy
)
324 for (yy
= 0; yy
< ga
->sy
; yy
++) {
325 gla
= &ga
->linedata
[yy
];
326 glb
= &gb
->linedata
[yy
];
327 if (gla
->cellsize
!= glb
->cellsize
)
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
))
340 /* Trim lines from the history. */
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%
354 grid_collect_history(struct grid
*gd
)
358 if (gd
->hsize
== 0 || gd
->hsize
< gd
->hlimit
)
361 ny
= gd
->hlimit
/ 10;
368 * Free the lines from 0 to ny then move the remaining lines over
371 grid_trim_history(gd
, ny
);
374 if (gd
->hscrolled
> gd
->hsize
)
375 gd
->hscrolled
= gd
->hsize
;
378 /* Remove lines from the bottom of the history. */
380 grid_remove_history(struct grid
*gd
, u_int ny
)
386 for (yy
= 0; yy
< ny
; yy
++)
387 grid_free_line(gd
, gd
->hsize
+ gd
->sy
- 1 - yy
);
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.
396 grid_scroll_history(struct grid
*gd
, u_int bg
)
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
);
406 grid_compact_line(&gd
->linedata
[gd
->hsize
]);
407 gd
->linedata
[gd
->hsize
].time
= current_time
;
411 /* Clear the history. */
413 grid_clear_history(struct grid
*gd
)
415 grid_trim_history(gd
, gd
->hsize
);
420 gd
->linedata
= xreallocarray(gd
->linedata
, gd
->sy
,
421 sizeof *gd
->linedata
);
424 /* Scroll a region up, moving the top line into the history. */
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
;
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. */
442 gl_upper
= &gd
->linedata
[upper
];
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. */
458 /* Expand line to fit to cell. */
460 grid_expand_line(struct grid
*gd
, u_int py
, u_int sx
, u_int bg
)
462 struct grid_line
*gl
;
465 gl
= &gd
->linedata
[py
];
466 if (sx
<= gl
->cellsize
)
471 else if (sx
< gd
->sx
/ 2)
473 else if (gd
->sx
> 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
);
482 /* Empty a line and set background colour if needed. */
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)
497 return (&gd
->linedata
[py
]);
500 /* Get cell from line. */
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
);
511 gee
= &gl
->extddata
[gce
->offset
];
512 gc
->flags
= gee
->flags
;
513 gc
->attr
= gee
->attr
;
517 gc
->link
= gee
->link
;
518 utf8_to_data(gee
->data
, &gc
->data
);
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
;
532 utf8_set(&gc
->data
, gce
->data
.data
);
536 /* Get cell for reading. */
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
);
544 grid_get_cell1(&gd
->linedata
[py
], px
, gc
);
547 /* Set cell at position. */
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)
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
);
567 grid_store_cell(gce
, gc
, gc
->data
.data
[0]);
570 /* Set padding at position. */
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. */
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
;
587 if (grid_check_y(gd
, __func__
, py
) != 0)
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
]);
602 grid_store_cell(gce
, gc
, s
[i
]);
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)
616 if (px
== 0 && nx
== gd
->sx
) {
617 grid_clear_lines(gd
, py
, ny
, bg
);
621 if (grid_check_y(gd
, __func__
, py
) != 0)
623 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
626 for (yy
= py
; yy
< py
+ ny
; yy
++) {
627 gl
= &gd
->linedata
[yy
];
630 if (sx
> gl
->cellsize
)
633 if (COLOUR_DEFAULT(bg
)) {
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. */
648 grid_clear_lines(struct grid
*gd
, u_int py
, u_int ny
, u_int bg
)
655 if (grid_check_y(gd
, __func__
, py
) != 0)
657 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
660 for (yy
= py
; yy
< py
+ ny
; yy
++) {
661 grid_free_line(gd
, yy
);
662 grid_empty_line(gd
, yy
, bg
);
665 gd
->linedata
[py
- 1].flags
&= ~GRID_LINE_WRAPPED
;
668 /* Move a group of lines. */
670 grid_move_lines(struct grid
*gd
, u_int dy
, u_int py
, u_int ny
, u_int bg
)
674 if (ny
== 0 || py
== dy
)
677 if (grid_check_y(gd
, __func__
, py
) != 0)
679 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
681 if (grid_check_y(gd
, __func__
, dy
) != 0)
683 if (grid_check_y(gd
, __func__
, dy
+ ny
- 1) != 0)
686 /* Free any lines which are being replaced. */
687 for (yy
= dy
; yy
< dy
+ ny
; yy
++) {
688 if (yy
>= py
&& yy
< py
+ ny
)
690 grid_free_line(gd
, yy
);
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
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. */
712 grid_move_cells(struct grid
*gd
, u_int dx
, u_int px
, u_int py
, u_int nx
,
715 struct grid_line
*gl
;
718 if (nx
== 0 || px
== dx
)
721 if (grid_check_y(gd
, __func__
, py
) != 0)
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
)
736 grid_clear_cell(gd
, xx
, py
, bg
);
740 /* Get ANSI foreground sequence. */
742 grid_string_cells_fg(const struct grid_cell
*gc
, int *values
)
748 if (gc
->fg
& COLOUR_FLAG_256
) {
751 values
[n
++] = gc
->fg
& 0xff;
752 } else if (gc
->fg
& COLOUR_FLAG_RGB
) {
755 colour_split_rgb(gc
->fg
, &r
, &g
, &b
);
769 values
[n
++] = gc
->fg
+ 30;
782 values
[n
++] = gc
->fg
;
789 /* Get ANSI background sequence. */
791 grid_string_cells_bg(const struct grid_cell
*gc
, int *values
)
797 if (gc
->bg
& COLOUR_FLAG_256
) {
800 values
[n
++] = gc
->bg
& 0xff;
801 } else if (gc
->bg
& COLOUR_FLAG_RGB
) {
804 colour_split_rgb(gc
->bg
, &r
, &g
, &b
);
818 values
[n
++] = gc
->bg
+ 40;
831 values
[n
++] = gc
->bg
+ 10;
838 /* Get underscore colour sequence. */
840 grid_string_cells_us(const struct grid_cell
*gc
, int *values
)
846 if (gc
->us
& COLOUR_FLAG_256
) {
849 values
[n
++] = gc
->us
& 0xff;
850 } else if (gc
->us
& COLOUR_FLAG_RGB
) {
853 colour_split_rgb(gc
->us
, &r
, &g
, &b
);
861 /* Add on SGR code. */
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
)
868 int reset
= (n
!= 0 && s
[0] == 0);
871 return; /* no code to add */
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
);
882 strlcat(buf
, "\033[", len
);
883 for (i
= 0; i
< nnewc
; i
++) {
885 xsnprintf(tmp
, sizeof tmp
, "%d;", newc
[i
]);
887 xsnprintf(tmp
, sizeof tmp
, "%d", newc
[i
]);
888 strlcat(buf
, tmp
, len
);
890 strlcat(buf
, "m", len
);
894 grid_string_cells_add_hyperlink(char *buf
, size_t len
, const char *id
,
895 const char *uri
, int flags
)
899 if (strlen(uri
) + strlen(id
) + 17 >= len
)
902 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
903 strlcat(buf
, "\\033]8;", len
);
905 strlcat(buf
, "\033]8;", len
);
907 xasprintf(&tmp
, "id=%s;", id
);
908 strlcat(buf
, tmp
, len
);
911 strlcat(buf
, ";", len
);
912 strlcat(buf
, uri
, len
);
913 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
914 strlcat(buf
, "\\033\\\\", len
);
916 strlcat(buf
, "\033\\", len
);
921 * Returns ANSI code to set particular attributes (colour, bold and so on)
922 * given a current state.
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
;
933 const char *uri
, *id
;
935 static const struct {
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 },
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)) {
961 lastattr
&= GRID_ATTR_CHARSET
;
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. */
974 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
975 strlcat(buf
, "\\033[", len
);
977 strlcat(buf
, "\033[", len
);
978 for (i
= 0; i
< n
; i
++) {
980 xsnprintf(tmp
, sizeof tmp
, "%d", s
[i
]);
982 xsnprintf(tmp
, sizeof tmp
, "%d:%d", s
[i
] / 10,
985 strlcat(buf
, tmp
, len
);
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
,
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
,
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
,
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 */
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 */
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
,
1029 } else if (*has_link
) {
1030 grid_string_cells_add_hyperlink(buf
, len
, "", "",
1037 /* Convert cells into a string. */
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
;
1045 char *buf
, code
[8192];
1046 size_t len
, off
, size
, codelen
;
1049 const struct grid_line
*gl
;
1051 if (lastgc
!= NULL
&& *lastgc
== NULL
) {
1052 memcpy(&lastgc1
, &grid_default_cell
, sizeof lastgc1
);
1060 gl
= grid_peek_line(gd
, py
);
1061 if (flags
& GRID_STRING_EMPTY_CELLS
)
1065 for (xx
= px
; xx
< px
+ nx
; xx
++) {
1066 if (gl
== NULL
|| xx
>= end
)
1068 grid_get_cell(gd
, xx
, py
, &gc
);
1069 if (gc
.flags
& GRID_FLAG_PADDING
)
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
);
1080 data
= gc
.data
.data
;
1081 size
= gc
.data
.size
;
1082 if ((flags
& GRID_STRING_ESCAPE_SEQUENCES
) &&
1089 while (len
< off
+ size
+ codelen
+ 1) {
1090 buf
= xreallocarray(buf
, 2, len
);
1095 memcpy(buf
+ off
, code
, codelen
);
1098 memcpy(buf
+ off
, data
, size
);
1103 grid_string_cells_add_hyperlink(code
, sizeof code
, "", "",
1105 codelen
= strlen(code
);
1106 while (len
< off
+ size
+ codelen
+ 1) {
1107 buf
= xreallocarray(buf
, 2, len
);
1110 memcpy(buf
+ off
, code
, codelen
);
1114 if (flags
& GRID_STRING_TRIM_SPACES
) {
1115 while (off
> 0 && buf
[off
- 1] == ' ')
1124 * Duplicate a set of lines between two grids. Both source and destination
1125 * should be big enough.
1128 grid_duplicate_lines(struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
,
1131 struct grid_line
*dstl
, *srcl
;
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
);
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
);
1159 dstl
->extddata
= NULL
;
1166 /* Mark line as dead. */
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
));
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
);
1200 /* Join line below onto this one. */
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;
1212 * Add a new target line.
1216 gl
= grid_reflow_move(target
, &gd
->linedata
[yy
]);
1218 to
= target
->sy
- 1;
1219 gl
= &target
->linedata
[to
];
1224 * Loop until no more to consume or the target line is full.
1229 * If this is now the last line, there is nothing more to be
1232 if (yy
+ 1 + lines
== gd
->hsize
+ gd
->sy
)
1234 line
= yy
+ 1 + lines
;
1236 /* If the next line is empty, skip it. */
1237 if (~gd
->linedata
[line
].flags
& GRID_LINE_WRAPPED
)
1239 if (gd
->linedata
[line
].cellused
== 0) {
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
)
1254 width
+= gc
.data
.width
;
1255 grid_set_cell(target
, at
, to
, &gc
);
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
)
1264 width
+= gc
.data
.width
;
1266 grid_set_cell(target
, at
, to
, &gc
);
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
)
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
;
1288 grid_move_cells(gd
, 0, want
, yy
+ lines
, left
, 8);
1289 from
->cellsize
= from
->cellused
= left
;
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
)
1308 /* Split this line into several new ones */
1310 grid_reflow_split(struct grid
*target
, struct grid
*gd
, u_int sx
, u_int yy
,
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
;
1325 for (i
= at
; i
< used
; i
++) {
1326 grid_get_cell1(gl
, i
, &gc
);
1327 if (width
+ gc
.data
.width
> sx
) {
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. */
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
;
1351 width
+= gc
.data
.width
;
1352 grid_set_cell(target
, xx
, line
, &gc
);
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. */
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
)
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
1405 if (~gl
->flags
& GRID_LINE_EXTENDED
) {
1406 width
= gl
->cellused
;
1412 for (i
= 0; i
< gl
->cellused
; i
++) {
1413 grid_get_cell1(gl
, i
, &gc
);
1414 if (at
== 0 && width
+ gc
.data
.width
> sx
)
1416 width
+= gc
.data
.width
;
1421 * If the line is exactly right, just move it across
1425 grid_reflow_move(target
, gl
);
1430 * If the line is too big, it needs to be split, whether or not
1431 * it was previously wrapped.
1434 grid_reflow_split(target
, gd
, sx
, yy
, at
);
1439 * If the line was previously wrapped, join as much as possible
1442 if (gl
->flags
& GRID_LINE_WRAPPED
)
1443 grid_reflow_join(target
, gd
, sx
, yy
, width
, 0);
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
;
1457 gd
->linedata
= target
->linedata
;
1461 /* Convert to position based on wrapped lines. */
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
;
1475 if (px
>= gd
->linedata
[yy
].cellused
)
1483 /* Convert position based on wrapped lines back. */
1485 grid_unwrap_position(struct grid
*gd
, u_int
*px
, u_int
*py
, u_int wx
, u_int wy
)
1489 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
- 1; yy
++) {
1492 if (~gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
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
)
1503 wx
= gd
->linedata
[yy
].cellused
;
1505 while (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
) {
1506 if (wx
< gd
->linedata
[yy
].cellused
)
1508 wx
-= gd
->linedata
[yy
].cellused
;
1516 /* Get length of line. */
1518 grid_line_length(struct grid
*gd
, u_int py
)
1520 struct grid_cell gc
;
1523 px
= grid_get_line(gd
, py
)->cellsize
;
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
!= ' ')