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 int flags1
= gc1
->flags
, flags2
= gc2
->flags
;;
240 if (gc1
->fg
!= gc2
->fg
|| gc1
->bg
!= gc2
->bg
)
242 if (gc1
->attr
!= gc2
->attr
)
244 if ((flags1
& ~GRID_FLAG_CLEARED
) != (flags2
& ~GRID_FLAG_CLEARED
))
246 if (gc1
->link
!= gc2
->link
)
251 /* Compare grid cells. Return 1 if equal, 0 if not. */
253 grid_cells_equal(const struct grid_cell
*gc1
, const struct grid_cell
*gc2
)
255 if (!grid_cells_look_equal(gc1
, gc2
))
257 if (gc1
->data
.width
!= gc2
->data
.width
)
259 if (gc1
->data
.size
!= gc2
->data
.size
)
261 return (memcmp(gc1
->data
.data
, gc2
->data
.data
, gc1
->data
.size
) == 0);
264 /* Set grid cell to a tab. */
266 grid_set_tab(struct grid_cell
*gc
, u_int width
)
268 memset(gc
->data
.data
, 0, sizeof gc
->data
.data
);
269 gc
->flags
|= GRID_FLAG_TAB
;
270 gc
->data
.width
= gc
->data
.size
= gc
->data
.have
= width
;
271 memset(gc
->data
.data
, ' ', gc
->data
.size
);
276 grid_free_line(struct grid
*gd
, u_int py
)
278 free(gd
->linedata
[py
].celldata
);
279 gd
->linedata
[py
].celldata
= NULL
;
280 free(gd
->linedata
[py
].extddata
);
281 gd
->linedata
[py
].extddata
= NULL
;
284 /* Free several lines. */
286 grid_free_lines(struct grid
*gd
, u_int py
, u_int ny
)
290 for (yy
= py
; yy
< py
+ ny
; yy
++)
291 grid_free_line(gd
, yy
);
294 /* Create a new grid. */
296 grid_create(u_int sx
, u_int sy
, u_int hlimit
)
300 gd
= xmalloc(sizeof *gd
);
305 gd
->flags
= GRID_HISTORY
;
314 gd
->linedata
= xcalloc(gd
->sy
, sizeof *gd
->linedata
);
323 grid_destroy(struct grid
*gd
)
325 grid_free_lines(gd
, 0, gd
->hsize
+ gd
->sy
);
334 grid_compare(struct grid
*ga
, struct grid
*gb
)
336 struct grid_line
*gla
, *glb
;
337 struct grid_cell gca
, gcb
;
340 if (ga
->sx
!= gb
->sx
|| ga
->sy
!= gb
->sy
)
343 for (yy
= 0; yy
< ga
->sy
; yy
++) {
344 gla
= &ga
->linedata
[yy
];
345 glb
= &gb
->linedata
[yy
];
346 if (gla
->cellsize
!= glb
->cellsize
)
348 for (xx
= 0; xx
< gla
->cellsize
; xx
++) {
349 grid_get_cell(ga
, xx
, yy
, &gca
);
350 grid_get_cell(gb
, xx
, yy
, &gcb
);
351 if (!grid_cells_equal(&gca
, &gcb
))
359 /* Trim lines from the history. */
361 grid_trim_history(struct grid
*gd
, u_int ny
)
363 grid_free_lines(gd
, 0, ny
);
364 memmove(&gd
->linedata
[0], &gd
->linedata
[ny
],
365 (gd
->hsize
+ gd
->sy
- ny
) * (sizeof *gd
->linedata
));
369 * Collect lines from the history if at the limit. Free the top (oldest) 10%
373 grid_collect_history(struct grid
*gd
)
377 if (gd
->hsize
== 0 || gd
->hsize
< gd
->hlimit
)
380 ny
= gd
->hlimit
/ 10;
387 * Free the lines from 0 to ny then move the remaining lines over
390 grid_trim_history(gd
, ny
);
393 if (gd
->hscrolled
> gd
->hsize
)
394 gd
->hscrolled
= gd
->hsize
;
397 /* Remove lines from the bottom of the history. */
399 grid_remove_history(struct grid
*gd
, u_int ny
)
405 for (yy
= 0; yy
< ny
; yy
++)
406 grid_free_line(gd
, gd
->hsize
+ gd
->sy
- 1 - yy
);
411 * Scroll the entire visible screen, moving one line into the history. Just
412 * allocate a new line at the bottom and move the history size indicator.
415 grid_scroll_history(struct grid
*gd
, u_int bg
)
419 yy
= gd
->hsize
+ gd
->sy
;
420 gd
->linedata
= xreallocarray(gd
->linedata
, yy
+ 1,
421 sizeof *gd
->linedata
);
422 grid_empty_line(gd
, yy
, bg
);
425 grid_compact_line(&gd
->linedata
[gd
->hsize
]);
426 gd
->linedata
[gd
->hsize
].time
= current_time
;
430 /* Clear the history. */
432 grid_clear_history(struct grid
*gd
)
434 grid_trim_history(gd
, gd
->hsize
);
439 gd
->linedata
= xreallocarray(gd
->linedata
, gd
->sy
,
440 sizeof *gd
->linedata
);
443 /* Scroll a region up, moving the top line into the history. */
445 grid_scroll_history_region(struct grid
*gd
, u_int upper
, u_int lower
, u_int bg
)
447 struct grid_line
*gl_history
, *gl_upper
;
450 /* Create a space for a new line. */
451 yy
= gd
->hsize
+ gd
->sy
;
452 gd
->linedata
= xreallocarray(gd
->linedata
, yy
+ 1,
453 sizeof *gd
->linedata
);
455 /* Move the entire screen down to free a space for this line. */
456 gl_history
= &gd
->linedata
[gd
->hsize
];
457 memmove(gl_history
+ 1, gl_history
, gd
->sy
* sizeof *gl_history
);
459 /* Adjust the region and find its start and end. */
461 gl_upper
= &gd
->linedata
[upper
];
464 /* Move the line into the history. */
465 memcpy(gl_history
, gl_upper
, sizeof *gl_history
);
466 gl_history
->time
= current_time
;
468 /* Then move the region up and clear the bottom line. */
469 memmove(gl_upper
, gl_upper
+ 1, (lower
- upper
) * sizeof *gl_upper
);
470 grid_empty_line(gd
, lower
, bg
);
472 /* Move the history offset down over the line. */
477 /* Expand line to fit to cell. */
479 grid_expand_line(struct grid
*gd
, u_int py
, u_int sx
, u_int bg
)
481 struct grid_line
*gl
;
484 gl
= &gd
->linedata
[py
];
485 if (sx
<= gl
->cellsize
)
490 else if (sx
< gd
->sx
/ 2)
492 else if (gd
->sx
> sx
)
495 gl
->celldata
= xreallocarray(gl
->celldata
, sx
, sizeof *gl
->celldata
);
496 for (xx
= gl
->cellsize
; xx
< sx
; xx
++)
497 grid_clear_cell(gd
, xx
, py
, bg
);
501 /* Empty a line and set background colour if needed. */
503 grid_empty_line(struct grid
*gd
, u_int py
, u_int bg
)
505 memset(&gd
->linedata
[py
], 0, sizeof gd
->linedata
[py
]);
506 if (!COLOUR_DEFAULT(bg
))
507 grid_expand_line(gd
, py
, gd
->sx
, bg
);
510 /* Peek at grid line. */
511 const struct grid_line
*
512 grid_peek_line(struct grid
*gd
, u_int py
)
514 if (grid_check_y(gd
, __func__
, py
) != 0)
516 return (&gd
->linedata
[py
]);
519 /* Get cell from line. */
521 grid_get_cell1(struct grid_line
*gl
, u_int px
, struct grid_cell
*gc
)
523 struct grid_cell_entry
*gce
= &gl
->celldata
[px
];
524 struct grid_extd_entry
*gee
;
526 if (gce
->flags
& GRID_FLAG_EXTENDED
) {
527 if (gce
->offset
>= gl
->extdsize
)
528 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
530 gee
= &gl
->extddata
[gce
->offset
];
531 gc
->flags
= gee
->flags
;
532 gc
->attr
= gee
->attr
;
536 gc
->link
= gee
->link
;
538 if (gc
->flags
& GRID_FLAG_TAB
)
539 grid_set_tab(gc
, gee
->data
);
541 utf8_to_data(gee
->data
, &gc
->data
);
546 gc
->flags
= gce
->flags
& ~(GRID_FLAG_FG256
|GRID_FLAG_BG256
);
547 gc
->attr
= gce
->data
.attr
;
548 gc
->fg
= gce
->data
.fg
;
549 if (gce
->flags
& GRID_FLAG_FG256
)
550 gc
->fg
|= COLOUR_FLAG_256
;
551 gc
->bg
= gce
->data
.bg
;
552 if (gce
->flags
& GRID_FLAG_BG256
)
553 gc
->bg
|= COLOUR_FLAG_256
;
555 utf8_set(&gc
->data
, gce
->data
.data
);
559 /* Get cell for reading. */
561 grid_get_cell(struct grid
*gd
, u_int px
, u_int py
, struct grid_cell
*gc
)
563 if (grid_check_y(gd
, __func__
, py
) != 0 ||
564 px
>= gd
->linedata
[py
].cellsize
)
565 memcpy(gc
, &grid_default_cell
, sizeof *gc
);
567 grid_get_cell1(&gd
->linedata
[py
], px
, gc
);
570 /* Set cell at position. */
572 grid_set_cell(struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
)
574 struct grid_line
*gl
;
575 struct grid_cell_entry
*gce
;
577 if (grid_check_y(gd
, __func__
, py
) != 0)
580 grid_expand_line(gd
, py
, px
+ 1, 8);
582 gl
= &gd
->linedata
[py
];
583 if (px
+ 1 > gl
->cellused
)
584 gl
->cellused
= px
+ 1;
586 gce
= &gl
->celldata
[px
];
587 if (grid_need_extended_cell(gce
, gc
))
588 grid_extended_cell(gl
, gce
, gc
);
590 grid_store_cell(gce
, gc
, gc
->data
.data
[0]);
593 /* Set padding at position. */
595 grid_set_padding(struct grid
*gd
, u_int px
, u_int py
)
597 grid_set_cell(gd
, px
, py
, &grid_padding_cell
);
600 /* Set cells at position. */
602 grid_set_cells(struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
,
603 const char *s
, size_t slen
)
605 struct grid_line
*gl
;
606 struct grid_cell_entry
*gce
;
607 struct grid_extd_entry
*gee
;
610 if (grid_check_y(gd
, __func__
, py
) != 0)
613 grid_expand_line(gd
, py
, px
+ slen
, 8);
615 gl
= &gd
->linedata
[py
];
616 if (px
+ slen
> gl
->cellused
)
617 gl
->cellused
= px
+ slen
;
619 for (i
= 0; i
< slen
; i
++) {
620 gce
= &gl
->celldata
[px
+ i
];
621 if (grid_need_extended_cell(gce
, gc
)) {
622 gee
= grid_extended_cell(gl
, gce
, gc
);
623 gee
->data
= utf8_build_one(s
[i
]);
625 grid_store_cell(gce
, gc
, s
[i
]);
631 grid_clear(struct grid
*gd
, u_int px
, u_int py
, u_int nx
, u_int ny
, u_int bg
)
633 struct grid_line
*gl
;
634 u_int xx
, yy
, ox
, sx
;
636 if (nx
== 0 || ny
== 0)
639 if (px
== 0 && nx
== gd
->sx
) {
640 grid_clear_lines(gd
, py
, ny
, bg
);
644 if (grid_check_y(gd
, __func__
, py
) != 0)
646 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
649 for (yy
= py
; yy
< py
+ ny
; yy
++) {
650 gl
= &gd
->linedata
[yy
];
653 if (sx
> gl
->cellsize
)
656 if (COLOUR_DEFAULT(bg
)) {
663 grid_expand_line(gd
, yy
, px
+ ox
, 8); /* default bg first */
664 for (xx
= px
; xx
< px
+ ox
; xx
++)
665 grid_clear_cell(gd
, xx
, yy
, bg
);
669 /* Clear lines. This just frees and truncates the lines. */
671 grid_clear_lines(struct grid
*gd
, u_int py
, u_int ny
, u_int bg
)
678 if (grid_check_y(gd
, __func__
, py
) != 0)
680 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
683 for (yy
= py
; yy
< py
+ ny
; yy
++) {
684 grid_free_line(gd
, yy
);
685 grid_empty_line(gd
, yy
, bg
);
688 gd
->linedata
[py
- 1].flags
&= ~GRID_LINE_WRAPPED
;
691 /* Move a group of lines. */
693 grid_move_lines(struct grid
*gd
, u_int dy
, u_int py
, u_int ny
, u_int bg
)
697 if (ny
== 0 || py
== dy
)
700 if (grid_check_y(gd
, __func__
, py
) != 0)
702 if (grid_check_y(gd
, __func__
, py
+ ny
- 1) != 0)
704 if (grid_check_y(gd
, __func__
, dy
) != 0)
706 if (grid_check_y(gd
, __func__
, dy
+ ny
- 1) != 0)
709 /* Free any lines which are being replaced. */
710 for (yy
= dy
; yy
< dy
+ ny
; yy
++) {
711 if (yy
>= py
&& yy
< py
+ ny
)
713 grid_free_line(gd
, yy
);
716 gd
->linedata
[dy
- 1].flags
&= ~GRID_LINE_WRAPPED
;
718 memmove(&gd
->linedata
[dy
], &gd
->linedata
[py
],
719 ny
* (sizeof *gd
->linedata
));
722 * Wipe any lines that have been moved (without freeing them - they are
725 for (yy
= py
; yy
< py
+ ny
; yy
++) {
726 if (yy
< dy
|| yy
>= dy
+ ny
)
727 grid_empty_line(gd
, yy
, bg
);
729 if (py
!= 0 && (py
< dy
|| py
>= dy
+ ny
))
730 gd
->linedata
[py
- 1].flags
&= ~GRID_LINE_WRAPPED
;
733 /* Move a group of cells. */
735 grid_move_cells(struct grid
*gd
, u_int dx
, u_int px
, u_int py
, u_int nx
,
738 struct grid_line
*gl
;
741 if (nx
== 0 || px
== dx
)
744 if (grid_check_y(gd
, __func__
, py
) != 0)
746 gl
= &gd
->linedata
[py
];
748 grid_expand_line(gd
, py
, px
+ nx
, 8);
749 grid_expand_line(gd
, py
, dx
+ nx
, 8);
750 memmove(&gl
->celldata
[dx
], &gl
->celldata
[px
],
751 nx
* sizeof *gl
->celldata
);
752 if (dx
+ nx
> gl
->cellused
)
753 gl
->cellused
= dx
+ nx
;
755 /* Wipe any cells that have been moved. */
756 for (xx
= px
; xx
< px
+ nx
; xx
++) {
757 if (xx
>= dx
&& xx
< dx
+ nx
)
759 grid_clear_cell(gd
, xx
, py
, bg
);
763 /* Get ANSI foreground sequence. */
765 grid_string_cells_fg(const struct grid_cell
*gc
, int *values
)
771 if (gc
->fg
& COLOUR_FLAG_256
) {
774 values
[n
++] = gc
->fg
& 0xff;
775 } else if (gc
->fg
& COLOUR_FLAG_RGB
) {
778 colour_split_rgb(gc
->fg
, &r
, &g
, &b
);
792 values
[n
++] = gc
->fg
+ 30;
805 values
[n
++] = gc
->fg
;
812 /* Get ANSI background sequence. */
814 grid_string_cells_bg(const struct grid_cell
*gc
, int *values
)
820 if (gc
->bg
& COLOUR_FLAG_256
) {
823 values
[n
++] = gc
->bg
& 0xff;
824 } else if (gc
->bg
& COLOUR_FLAG_RGB
) {
827 colour_split_rgb(gc
->bg
, &r
, &g
, &b
);
841 values
[n
++] = gc
->bg
+ 40;
854 values
[n
++] = gc
->bg
+ 10;
861 /* Get underscore colour sequence. */
863 grid_string_cells_us(const struct grid_cell
*gc
, int *values
)
869 if (gc
->us
& COLOUR_FLAG_256
) {
872 values
[n
++] = gc
->us
& 0xff;
873 } else if (gc
->us
& COLOUR_FLAG_RGB
) {
876 colour_split_rgb(gc
->us
, &r
, &g
, &b
);
884 /* Add on SGR code. */
886 grid_string_cells_add_code(char *buf
, size_t len
, u_int n
, int *s
, int *newc
,
887 int *oldc
, size_t nnewc
, size_t noldc
, int flags
)
891 int reset
= (n
!= 0 && s
[0] == 0);
894 return; /* no code to add */
897 memcmp(newc
, oldc
, nnewc
* sizeof newc
[0]) == 0)
898 return; /* no reset and colour unchanged */
899 if (reset
&& (newc
[0] == 49 || newc
[0] == 39))
900 return; /* reset and colour default */
902 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
903 strlcat(buf
, "\\033[", len
);
905 strlcat(buf
, "\033[", len
);
906 for (i
= 0; i
< nnewc
; i
++) {
908 xsnprintf(tmp
, sizeof tmp
, "%d;", newc
[i
]);
910 xsnprintf(tmp
, sizeof tmp
, "%d", newc
[i
]);
911 strlcat(buf
, tmp
, len
);
913 strlcat(buf
, "m", len
);
917 grid_string_cells_add_hyperlink(char *buf
, size_t len
, const char *id
,
918 const char *uri
, int flags
)
922 if (strlen(uri
) + strlen(id
) + 17 >= len
)
925 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
926 strlcat(buf
, "\\033]8;", len
);
928 strlcat(buf
, "\033]8;", len
);
930 xasprintf(&tmp
, "id=%s;", id
);
931 strlcat(buf
, tmp
, len
);
934 strlcat(buf
, ";", len
);
935 strlcat(buf
, uri
, len
);
936 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
937 strlcat(buf
, "\\033\\\\", len
);
939 strlcat(buf
, "\033\\", len
);
944 * Returns ANSI code to set particular attributes (colour, bold and so on)
945 * given a current state.
948 grid_string_cells_code(const struct grid_cell
*lastgc
,
949 const struct grid_cell
*gc
, char *buf
, size_t len
, int flags
,
950 struct screen
*sc
, int *has_link
)
952 int oldc
[64], newc
[64], s
[128];
953 size_t noldc
, nnewc
, n
, i
;
954 u_int attr
= gc
->attr
, lastattr
= lastgc
->attr
;
956 const char *uri
, *id
;
958 static const struct {
962 { GRID_ATTR_BRIGHT
, 1 },
963 { GRID_ATTR_DIM
, 2 },
964 { GRID_ATTR_ITALICS
, 3 },
965 { GRID_ATTR_UNDERSCORE
, 4 },
966 { GRID_ATTR_BLINK
, 5 },
967 { GRID_ATTR_REVERSE
, 7 },
968 { GRID_ATTR_HIDDEN
, 8 },
969 { GRID_ATTR_STRIKETHROUGH
, 9 },
970 { GRID_ATTR_UNDERSCORE_2
, 42 },
971 { GRID_ATTR_UNDERSCORE_3
, 43 },
972 { GRID_ATTR_UNDERSCORE_4
, 44 },
973 { GRID_ATTR_UNDERSCORE_5
, 45 },
974 { GRID_ATTR_OVERLINE
, 53 },
978 /* If any attribute is removed, begin with 0. */
979 for (i
= 0; i
< nitems(attrs
); i
++) {
980 if (((~attr
& attrs
[i
].mask
) &&
981 (lastattr
& attrs
[i
].mask
)) ||
982 (lastgc
->us
!= 8 && gc
->us
== 8)) {
984 lastattr
&= GRID_ATTR_CHARSET
;
988 /* For each attribute that is newly set, add its code. */
989 for (i
= 0; i
< nitems(attrs
); i
++) {
990 if ((attr
& attrs
[i
].mask
) && !(lastattr
& attrs
[i
].mask
))
991 s
[n
++] = attrs
[i
].code
;
994 /* Write the attributes. */
997 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
998 strlcat(buf
, "\\033[", len
);
1000 strlcat(buf
, "\033[", len
);
1001 for (i
= 0; i
< n
; i
++) {
1003 xsnprintf(tmp
, sizeof tmp
, "%d", s
[i
]);
1005 xsnprintf(tmp
, sizeof tmp
, "%d:%d", s
[i
] / 10,
1008 strlcat(buf
, tmp
, len
);
1010 strlcat(buf
, ";", len
);
1012 strlcat(buf
, "m", len
);
1015 /* If the foreground colour changed, write its parameters. */
1016 nnewc
= grid_string_cells_fg(gc
, newc
);
1017 noldc
= grid_string_cells_fg(lastgc
, oldc
);
1018 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
1021 /* If the background colour changed, append its parameters. */
1022 nnewc
= grid_string_cells_bg(gc
, newc
);
1023 noldc
= grid_string_cells_bg(lastgc
, oldc
);
1024 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
1027 /* If the underscore colour changed, append its parameters. */
1028 nnewc
= grid_string_cells_us(gc
, newc
);
1029 noldc
= grid_string_cells_us(lastgc
, oldc
);
1030 grid_string_cells_add_code(buf
, len
, n
, s
, newc
, oldc
, nnewc
, noldc
,
1033 /* Append shift in/shift out if needed. */
1034 if ((attr
& GRID_ATTR_CHARSET
) && !(lastattr
& GRID_ATTR_CHARSET
)) {
1035 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
1036 strlcat(buf
, "\\016", len
); /* SO */
1038 strlcat(buf
, "\016", len
); /* SO */
1040 if (!(attr
& GRID_ATTR_CHARSET
) && (lastattr
& GRID_ATTR_CHARSET
)) {
1041 if (flags
& GRID_STRING_ESCAPE_SEQUENCES
)
1042 strlcat(buf
, "\\017", len
); /* SI */
1044 strlcat(buf
, "\017", len
); /* SI */
1047 /* Add hyperlink if changed. */
1048 if (sc
!= NULL
&& sc
->hyperlinks
!= NULL
&& lastgc
->link
!= gc
->link
) {
1049 if (hyperlinks_get(sc
->hyperlinks
, gc
->link
, &uri
, &id
, NULL
)) {
1050 *has_link
= grid_string_cells_add_hyperlink(buf
, len
,
1052 } else if (*has_link
) {
1053 grid_string_cells_add_hyperlink(buf
, len
, "", "",
1060 /* Convert cells into a string. */
1062 grid_string_cells(struct grid
*gd
, u_int px
, u_int py
, u_int nx
,
1063 struct grid_cell
**lastgc
, int flags
, struct screen
*s
)
1065 struct grid_cell gc
;
1066 static struct grid_cell lastgc1
;
1068 char *buf
, code
[8192];
1069 size_t len
, off
, size
, codelen
;
1072 const struct grid_line
*gl
;
1074 if (lastgc
!= NULL
&& *lastgc
== NULL
) {
1075 memcpy(&lastgc1
, &grid_default_cell
, sizeof lastgc1
);
1083 gl
= grid_peek_line(gd
, py
);
1084 if (flags
& GRID_STRING_EMPTY_CELLS
)
1088 for (xx
= px
; xx
< px
+ nx
; xx
++) {
1089 if (gl
== NULL
|| xx
>= end
)
1091 grid_get_cell(gd
, xx
, py
, &gc
);
1092 if (gc
.flags
& GRID_FLAG_PADDING
)
1095 if (flags
& GRID_STRING_WITH_SEQUENCES
) {
1096 grid_string_cells_code(*lastgc
, &gc
, code
, sizeof code
,
1097 flags
, s
, &has_link
);
1098 codelen
= strlen(code
);
1099 memcpy(*lastgc
, &gc
, sizeof **lastgc
);
1103 if (gc
.flags
& GRID_FLAG_TAB
) {
1107 data
= gc
.data
.data
;
1108 size
= gc
.data
.size
;
1109 if ((flags
& GRID_STRING_ESCAPE_SEQUENCES
) &&
1117 while (len
< off
+ size
+ codelen
+ 1) {
1118 buf
= xreallocarray(buf
, 2, len
);
1123 memcpy(buf
+ off
, code
, codelen
);
1126 memcpy(buf
+ off
, data
, size
);
1131 grid_string_cells_add_hyperlink(code
, sizeof code
, "", "",
1133 codelen
= strlen(code
);
1134 while (len
< off
+ size
+ codelen
+ 1) {
1135 buf
= xreallocarray(buf
, 2, len
);
1138 memcpy(buf
+ off
, code
, codelen
);
1142 if (flags
& GRID_STRING_TRIM_SPACES
) {
1143 while (off
> 0 && buf
[off
- 1] == ' ')
1152 * Duplicate a set of lines between two grids. Both source and destination
1153 * should be big enough.
1156 grid_duplicate_lines(struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
,
1159 struct grid_line
*dstl
, *srcl
;
1162 if (dy
+ ny
> dst
->hsize
+ dst
->sy
)
1163 ny
= dst
->hsize
+ dst
->sy
- dy
;
1164 if (sy
+ ny
> src
->hsize
+ src
->sy
)
1165 ny
= src
->hsize
+ src
->sy
- sy
;
1166 grid_free_lines(dst
, dy
, ny
);
1168 for (yy
= 0; yy
< ny
; yy
++) {
1169 srcl
= &src
->linedata
[sy
];
1170 dstl
= &dst
->linedata
[dy
];
1172 memcpy(dstl
, srcl
, sizeof *dstl
);
1173 if (srcl
->cellsize
!= 0) {
1174 dstl
->celldata
= xreallocarray(NULL
,
1175 srcl
->cellsize
, sizeof *dstl
->celldata
);
1176 memcpy(dstl
->celldata
, srcl
->celldata
,
1177 srcl
->cellsize
* sizeof *dstl
->celldata
);
1179 dstl
->celldata
= NULL
;
1180 if (srcl
->extdsize
!= 0) {
1181 dstl
->extdsize
= srcl
->extdsize
;
1182 dstl
->extddata
= xreallocarray(NULL
, dstl
->extdsize
,
1183 sizeof *dstl
->extddata
);
1184 memcpy(dstl
->extddata
, srcl
->extddata
, dstl
->extdsize
*
1185 sizeof *dstl
->extddata
);
1187 dstl
->extddata
= NULL
;
1194 /* Mark line as dead. */
1196 grid_reflow_dead(struct grid_line
*gl
)
1198 memset(gl
, 0, sizeof *gl
);
1199 gl
->flags
= GRID_LINE_DEAD
;
1202 /* Add lines, return the first new one. */
1203 static struct grid_line
*
1204 grid_reflow_add(struct grid
*gd
, u_int n
)
1206 struct grid_line
*gl
;
1207 u_int sy
= gd
->sy
+ n
;
1209 gd
->linedata
= xreallocarray(gd
->linedata
, sy
, sizeof *gd
->linedata
);
1210 gl
= &gd
->linedata
[gd
->sy
];
1211 memset(gl
, 0, n
* (sizeof *gl
));
1216 /* Move a line across. */
1217 static struct grid_line
*
1218 grid_reflow_move(struct grid
*gd
, struct grid_line
*from
)
1220 struct grid_line
*to
;
1222 to
= grid_reflow_add(gd
, 1);
1223 memcpy(to
, from
, sizeof *to
);
1224 grid_reflow_dead(from
);
1228 /* Join line below onto this one. */
1230 grid_reflow_join(struct grid
*target
, struct grid
*gd
, u_int sx
, u_int yy
,
1231 u_int width
, int already
)
1233 struct grid_line
*gl
, *from
= NULL
;
1234 struct grid_cell gc
;
1235 u_int lines
, left
, i
, to
, line
, want
= 0;
1240 * Add a new target line.
1244 gl
= grid_reflow_move(target
, &gd
->linedata
[yy
]);
1246 to
= target
->sy
- 1;
1247 gl
= &target
->linedata
[to
];
1252 * Loop until no more to consume or the target line is full.
1257 * If this is now the last line, there is nothing more to be
1260 if (yy
+ 1 + lines
== gd
->hsize
+ gd
->sy
)
1262 line
= yy
+ 1 + lines
;
1264 /* If the next line is empty, skip it. */
1265 if (~gd
->linedata
[line
].flags
& GRID_LINE_WRAPPED
)
1267 if (gd
->linedata
[line
].cellused
== 0) {
1275 * Is the destination line now full? Copy the first character
1276 * separately because we need to leave "from" set to the last
1277 * line if this line is full.
1279 grid_get_cell1(&gd
->linedata
[line
], 0, &gc
);
1280 if (width
+ gc
.data
.width
> sx
)
1282 width
+= gc
.data
.width
;
1283 grid_set_cell(target
, at
, to
, &gc
);
1286 /* Join as much more as possible onto the current line. */
1287 from
= &gd
->linedata
[line
];
1288 for (want
= 1; want
< from
->cellused
; want
++) {
1289 grid_get_cell1(from
, want
, &gc
);
1290 if (width
+ gc
.data
.width
> sx
)
1292 width
+= gc
.data
.width
;
1294 grid_set_cell(target
, at
, to
, &gc
);
1300 * If this line wasn't wrapped or we didn't consume the entire
1301 * line, don't try to join any further lines.
1303 if (!wrapped
|| want
!= from
->cellused
|| width
== sx
)
1310 * If we didn't consume the entire final line, then remove what we did
1311 * consume. If we consumed the entire line and it wasn't wrapped,
1312 * remove the wrap flag from this line.
1314 left
= from
->cellused
- want
;
1316 grid_move_cells(gd
, 0, want
, yy
+ lines
, left
, 8);
1317 from
->cellsize
= from
->cellused
= left
;
1319 } else if (!wrapped
)
1320 gl
->flags
&= ~GRID_LINE_WRAPPED
;
1322 /* Remove the lines that were completely consumed. */
1323 for (i
= yy
+ 1; i
< yy
+ 1 + lines
; i
++) {
1324 free(gd
->linedata
[i
].celldata
);
1325 free(gd
->linedata
[i
].extddata
);
1326 grid_reflow_dead(&gd
->linedata
[i
]);
1329 /* Adjust scroll position. */
1330 if (gd
->hscrolled
> to
+ lines
)
1331 gd
->hscrolled
-= lines
;
1332 else if (gd
->hscrolled
> to
)
1336 /* Split this line into several new ones */
1338 grid_reflow_split(struct grid
*target
, struct grid
*gd
, u_int sx
, u_int yy
,
1341 struct grid_line
*gl
= &gd
->linedata
[yy
], *first
;
1342 struct grid_cell gc
;
1343 u_int line
, lines
, width
, i
, xx
;
1344 u_int used
= gl
->cellused
;
1345 int flags
= gl
->flags
;
1347 /* How many lines do we need to insert? We know we need at least two. */
1348 if (~gl
->flags
& GRID_LINE_EXTENDED
)
1349 lines
= 1 + (gl
->cellused
- 1) / sx
;
1353 for (i
= at
; i
< used
; i
++) {
1354 grid_get_cell1(gl
, i
, &gc
);
1355 if (width
+ gc
.data
.width
> sx
) {
1359 width
+= gc
.data
.width
;
1363 /* Insert new lines. */
1364 line
= target
->sy
+ 1;
1365 first
= grid_reflow_add(target
, lines
);
1367 /* Copy sections from the original line. */
1370 for (i
= at
; i
< used
; i
++) {
1371 grid_get_cell1(gl
, i
, &gc
);
1372 if (width
+ gc
.data
.width
> sx
) {
1373 target
->linedata
[line
].flags
|= GRID_LINE_WRAPPED
;
1379 width
+= gc
.data
.width
;
1380 grid_set_cell(target
, xx
, line
, &gc
);
1383 if (flags
& GRID_LINE_WRAPPED
)
1384 target
->linedata
[line
].flags
|= GRID_LINE_WRAPPED
;
1386 /* Move the remainder of the original line. */
1387 gl
->cellsize
= gl
->cellused
= at
;
1388 gl
->flags
|= GRID_LINE_WRAPPED
;
1389 memcpy(first
, gl
, sizeof *first
);
1390 grid_reflow_dead(gl
);
1392 /* Adjust the scroll position. */
1393 if (yy
<= gd
->hscrolled
)
1394 gd
->hscrolled
+= lines
- 1;
1397 * If the original line had the wrapped flag and there is still space
1398 * in the last new line, try to join with the next lines.
1400 if (width
< sx
&& (flags
& GRID_LINE_WRAPPED
))
1401 grid_reflow_join(target
, gd
, sx
, yy
, width
, 1);
1404 /* Reflow lines on grid to new width. */
1406 grid_reflow(struct grid
*gd
, u_int sx
)
1408 struct grid
*target
;
1409 struct grid_line
*gl
;
1410 struct grid_cell gc
;
1411 u_int yy
, width
, i
, at
;
1414 * Create a destination grid. This is just used as a container for the
1415 * line data and may not be fully valid.
1417 target
= grid_create(gd
->sx
, 0, 0);
1420 * Loop over each source line.
1422 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
; yy
++) {
1423 gl
= &gd
->linedata
[yy
];
1424 if (gl
->flags
& GRID_LINE_DEAD
)
1428 * Work out the width of this line. at is the point at which
1429 * the available width is hit, and width is the full line
1433 if (~gl
->flags
& GRID_LINE_EXTENDED
) {
1434 width
= gl
->cellused
;
1440 for (i
= 0; i
< gl
->cellused
; i
++) {
1441 grid_get_cell1(gl
, i
, &gc
);
1442 if (at
== 0 && width
+ gc
.data
.width
> sx
)
1444 width
+= gc
.data
.width
;
1449 * If the line is exactly right, just move it across
1453 grid_reflow_move(target
, gl
);
1458 * If the line is too big, it needs to be split, whether or not
1459 * it was previously wrapped.
1462 grid_reflow_split(target
, gd
, sx
, yy
, at
);
1467 * If the line was previously wrapped, join as much as possible
1470 if (gl
->flags
& GRID_LINE_WRAPPED
)
1471 grid_reflow_join(target
, gd
, sx
, yy
, width
, 0);
1473 grid_reflow_move(target
, gl
);
1477 * Replace the old grid with the new.
1479 if (target
->sy
< gd
->sy
)
1480 grid_reflow_add(target
, gd
->sy
- target
->sy
);
1481 gd
->hsize
= target
->sy
- gd
->sy
;
1482 if (gd
->hscrolled
> gd
->hsize
)
1483 gd
->hscrolled
= gd
->hsize
;
1485 gd
->linedata
= target
->linedata
;
1489 /* Convert to position based on wrapped lines. */
1491 grid_wrap_position(struct grid
*gd
, u_int px
, u_int py
, u_int
*wx
, u_int
*wy
)
1493 u_int ax
= 0, ay
= 0, yy
;
1495 for (yy
= 0; yy
< py
; yy
++) {
1496 if (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1497 ax
+= gd
->linedata
[yy
].cellused
;
1503 if (px
>= gd
->linedata
[yy
].cellused
)
1511 /* Convert position based on wrapped lines back. */
1513 grid_unwrap_position(struct grid
*gd
, u_int
*px
, u_int
*py
, u_int wx
, u_int wy
)
1517 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
- 1; yy
++) {
1520 if (~gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1525 * yy is now 0 on the unwrapped line which contains wx. Walk forwards
1526 * until we find the end or the line now containing wx.
1528 if (wx
== UINT_MAX
) {
1529 while (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
)
1531 wx
= gd
->linedata
[yy
].cellused
;
1533 while (gd
->linedata
[yy
].flags
& GRID_LINE_WRAPPED
) {
1534 if (wx
< gd
->linedata
[yy
].cellused
)
1536 wx
-= gd
->linedata
[yy
].cellused
;
1544 /* Get length of line. */
1546 grid_line_length(struct grid
*gd
, u_int py
)
1548 struct grid_cell gc
;
1551 px
= grid_get_line(gd
, py
)->cellsize
;
1555 grid_get_cell(gd
, px
- 1, py
, &gc
);
1556 if ((gc
.flags
& GRID_FLAG_PADDING
) ||
1557 gc
.data
.size
!= 1 ||
1558 *gc
.data
.data
!= ' ')