4 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net>
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
= { 0, 0, 8, 8, (1 << 4) | 1, " " };
40 const struct grid_cell grid_marker_cell
= { 0, 0, 8, 8, (1 << 4) | 1, "_" };
42 #define grid_put_cell(gd, px, py, gc) do { \
43 memcpy(&gd->linedata[py].celldata[px], \
44 gc, sizeof gd->linedata[py].celldata[px]); \
46 #define grid_put_utf8(gd, px, py, gc) do { \
47 memcpy(&gd->linedata[py].utf8data[px], \
48 gc, sizeof gd->linedata[py].utf8data[px]); \
51 int grid_check_y(struct grid
*, u_int
);
55 grid_check_y(struct grid
*gd
, u_int py
)
57 if ((py
) >= (gd
)->hsize
+ (gd
)->sy
)
58 log_fatalx("y out of range: %u", py
);
63 grid_check_y(struct grid
*gd
, u_int py
)
65 if ((py
) >= (gd
)->hsize
+ (gd
)->sy
) {
66 log_debug("y out of range: %u", py
);
73 void grid_reflow_join(struct grid
*, u_int
*, struct grid_line
*, u_int
);
74 void grid_reflow_split(struct grid
*, u_int
*, struct grid_line
*, u_int
,
76 void grid_reflow_move(struct grid
*, u_int
*, struct grid_line
*);
77 size_t grid_string_cells_fg(const struct grid_cell
*, int *);
78 size_t grid_string_cells_bg(const struct grid_cell
*, int *);
79 void grid_string_cells_code(const struct grid_cell
*,
80 const struct grid_cell
*, char *, size_t, int);
82 /* Create a new grid. */
84 grid_create(u_int sx
, u_int sy
, u_int hlimit
)
88 gd
= xmalloc(sizeof *gd
);
92 gd
->flags
= GRID_HISTORY
;
97 gd
->linedata
= xcalloc(gd
->sy
, sizeof *gd
->linedata
);
104 grid_destroy(struct grid
*gd
)
106 struct grid_line
*gl
;
109 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
; yy
++) {
110 gl
= &gd
->linedata
[yy
];
121 grid_compare(struct grid
*ga
, struct grid
*gb
)
123 struct grid_line
*gla
, *glb
;
124 struct grid_cell
*gca
, *gcb
;
127 if (ga
->sx
!= gb
->sx
|| ga
->sy
!= ga
->sy
)
130 for (yy
= 0; yy
< ga
->sy
; yy
++) {
131 gla
= &ga
->linedata
[yy
];
132 glb
= &gb
->linedata
[yy
];
133 if (gla
->cellsize
!= glb
->cellsize
)
135 for (xx
= 0; xx
< ga
->sx
; xx
++) {
136 gca
= &gla
->celldata
[xx
];
137 gcb
= &glb
->celldata
[xx
];
138 if (memcmp(gca
, gcb
, sizeof (struct grid_cell
)) != 0)
147 * Collect lines from the history if at the limit. Free the top (oldest) 10%
151 grid_collect_history(struct grid
*gd
)
157 if (gd
->hsize
< gd
->hlimit
)
160 yy
= gd
->hlimit
/ 10;
164 grid_move_lines(gd
, 0, yy
, gd
->hsize
+ gd
->sy
- yy
);
169 * Scroll the entire visible screen, moving one line into the history. Just
170 * allocate a new line at the bottom and move the history size indicator.
173 grid_scroll_history(struct grid
*gd
)
179 yy
= gd
->hsize
+ gd
->sy
;
180 gd
->linedata
= xrealloc(gd
->linedata
, yy
+ 1, sizeof *gd
->linedata
);
181 memset(&gd
->linedata
[yy
], 0, sizeof gd
->linedata
[yy
]);
186 /* Scroll a region up, moving the top line into the history. */
188 grid_scroll_history_region(struct grid
*gd
, u_int upper
, u_int lower
)
190 struct grid_line
*gl_history
, *gl_upper
, *gl_lower
;
193 GRID_DEBUG(gd
, "upper=%u, lower=%u", upper
, lower
);
195 /* Create a space for a new line. */
196 yy
= gd
->hsize
+ gd
->sy
;
197 gd
->linedata
= xrealloc(gd
->linedata
, yy
+ 1, sizeof *gd
->linedata
);
199 /* Move the entire screen down to free a space for this line. */
200 gl_history
= &gd
->linedata
[gd
->hsize
];
201 memmove(gl_history
+ 1, gl_history
, gd
->sy
* sizeof *gl_history
);
203 /* Adjust the region and find its start and end. */
205 gl_upper
= &gd
->linedata
[upper
];
207 gl_lower
= &gd
->linedata
[lower
];
209 /* Move the line into the history. */
210 memcpy(gl_history
, gl_upper
, sizeof *gl_history
);
212 /* Then move the region up and clear the bottom line. */
213 memmove(gl_upper
, gl_upper
+ 1, (lower
- upper
) * sizeof *gl_upper
);
214 memset(gl_lower
, 0, sizeof *gl_lower
);
216 /* Move the history offset down over the line. */
220 /* Expand line to fit to cell. */
222 grid_expand_line(struct grid
*gd
, u_int py
, u_int sx
)
224 struct grid_line
*gl
;
227 gl
= &gd
->linedata
[py
];
228 if (sx
<= gl
->cellsize
)
231 gl
->celldata
= xrealloc(gl
->celldata
, sx
, sizeof *gl
->celldata
);
232 for (xx
= gl
->cellsize
; xx
< sx
; xx
++)
233 grid_put_cell(gd
, xx
, py
, &grid_default_cell
);
237 /* Peek at grid line. */
238 const struct grid_line
*
239 grid_peek_line(struct grid
*gd
, u_int py
)
241 if (grid_check_y(gd
, py
) != 0)
243 return (&gd
->linedata
[py
]);
246 /* Get cell for reading. */
247 const struct grid_cell
*
248 grid_peek_cell(struct grid
*gd
, u_int px
, u_int py
)
250 if (grid_check_y(gd
, py
) != 0)
251 return (&grid_default_cell
);
253 if (px
>= gd
->linedata
[py
].cellsize
)
254 return (&grid_default_cell
);
255 return (&gd
->linedata
[py
].celldata
[px
]);
258 /* Get cell at relative position (for writing). */
260 grid_get_cell(struct grid
*gd
, u_int px
, u_int py
)
262 if (grid_check_y(gd
, py
) != 0)
265 grid_expand_line(gd
, py
, px
+ 1);
266 return (&gd
->linedata
[py
].celldata
[px
]);
269 /* Set cell at relative position. */
272 struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
)
274 if (grid_check_y(gd
, py
) != 0)
277 grid_expand_line(gd
, py
, px
+ 1);
278 grid_put_cell(gd
, px
, py
, gc
);
283 grid_clear(struct grid
*gd
, u_int px
, u_int py
, u_int nx
, u_int ny
)
287 GRID_DEBUG(gd
, "px=%u, py=%u, nx=%u, ny=%u", px
, py
, nx
, ny
);
289 if (nx
== 0 || ny
== 0)
292 if (px
== 0 && nx
== gd
->sx
) {
293 grid_clear_lines(gd
, py
, ny
);
297 if (grid_check_y(gd
, py
) != 0)
299 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
302 for (yy
= py
; yy
< py
+ ny
; yy
++) {
303 if (px
>= gd
->linedata
[yy
].cellsize
)
305 if (px
+ nx
>= gd
->linedata
[yy
].cellsize
) {
306 gd
->linedata
[yy
].cellsize
= px
;
309 for (xx
= px
; xx
< px
+ nx
; xx
++) {
310 if (xx
>= gd
->linedata
[yy
].cellsize
)
312 grid_put_cell(gd
, xx
, yy
, &grid_default_cell
);
317 /* Clear lines. This just frees and truncates the lines. */
319 grid_clear_lines(struct grid
*gd
, u_int py
, u_int ny
)
321 struct grid_line
*gl
;
324 GRID_DEBUG(gd
, "py=%u, ny=%u", py
, ny
);
329 if (grid_check_y(gd
, py
) != 0)
331 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
334 for (yy
= py
; yy
< py
+ ny
; yy
++) {
335 gl
= &gd
->linedata
[yy
];
337 memset(gl
, 0, sizeof *gl
);
341 /* Move a group of lines. */
343 grid_move_lines(struct grid
*gd
, u_int dy
, u_int py
, u_int ny
)
347 GRID_DEBUG(gd
, "dy=%u, py=%u, ny=%u", dy
, py
, ny
);
349 if (ny
== 0 || py
== dy
)
352 if (grid_check_y(gd
, py
) != 0)
354 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
356 if (grid_check_y(gd
, dy
) != 0)
358 if (grid_check_y(gd
, dy
+ ny
- 1) != 0)
361 /* Free any lines which are being replaced. */
362 for (yy
= dy
; yy
< dy
+ ny
; yy
++) {
363 if (yy
>= py
&& yy
< py
+ ny
)
365 grid_clear_lines(gd
, yy
, 1);
369 &gd
->linedata
[dy
], &gd
->linedata
[py
], ny
* (sizeof *gd
->linedata
));
371 /* Wipe any lines that have been moved (without freeing them). */
372 for (yy
= py
; yy
< py
+ ny
; yy
++) {
373 if (yy
>= dy
&& yy
< dy
+ ny
)
375 memset(&gd
->linedata
[yy
], 0, sizeof gd
->linedata
[yy
]);
379 /* Move a group of cells. */
381 grid_move_cells(struct grid
*gd
, u_int dx
, u_int px
, u_int py
, u_int nx
)
383 struct grid_line
*gl
;
386 GRID_DEBUG(gd
, "dx=%u, px=%u, py=%u, nx=%u", dx
, px
, py
, nx
);
388 if (nx
== 0 || px
== dx
)
391 if (grid_check_y(gd
, py
) != 0)
393 gl
= &gd
->linedata
[py
];
395 grid_expand_line(gd
, py
, px
+ nx
);
396 grid_expand_line(gd
, py
, dx
+ nx
);
398 &gl
->celldata
[dx
], &gl
->celldata
[px
], nx
* sizeof *gl
->celldata
);
400 /* Wipe any cells that have been moved. */
401 for (xx
= px
; xx
< px
+ nx
; xx
++) {
402 if (xx
>= dx
&& xx
< dx
+ nx
)
404 grid_put_cell(gd
, xx
, py
, &grid_default_cell
);
408 /* Get ANSI foreground sequence. */
410 grid_string_cells_fg(const struct grid_cell
*gc
, int *values
)
415 if (gc
->flags
& GRID_FLAG_FG256
) {
418 values
[n
++] = gc
->fg
;
429 values
[n
++] = gc
->fg
+ 30;
442 values
[n
++] = gc
->fg
;
449 /* Get ANSI background sequence. */
451 grid_string_cells_bg(const struct grid_cell
*gc
, int *values
)
456 if (gc
->flags
& GRID_FLAG_BG256
) {
459 values
[n
++] = gc
->bg
;
470 values
[n
++] = gc
->bg
+ 40;
483 values
[n
++] = gc
->bg
- 10;
491 * Returns ANSI code to set particular attributes (colour, bold and so on)
492 * given a current state. The output buffer must be able to hold at least 57
496 grid_string_cells_code(const struct grid_cell
*lastgc
,
497 const struct grid_cell
*gc
, char *buf
, size_t len
, int escape_c0
)
499 int oldc
[16], newc
[16], s
[32];
500 size_t noldc
, nnewc
, n
, i
;
501 u_int attr
= gc
->attr
;
502 u_int lastattr
= lastgc
->attr
;
509 { GRID_ATTR_BRIGHT
, 1 },
510 { GRID_ATTR_DIM
, 2 },
511 { GRID_ATTR_ITALICS
, 3 },
512 { GRID_ATTR_UNDERSCORE
, 4 },
513 { GRID_ATTR_BLINK
, 5 },
514 { GRID_ATTR_REVERSE
, 7 },
515 { GRID_ATTR_HIDDEN
, 8 }
519 /* If any attribute is removed, begin with 0. */
520 for (i
= 0; i
< nitems(attrs
); i
++) {
521 if (!(attr
& attrs
[i
].mask
) && (lastattr
& attrs
[i
].mask
)) {
523 lastattr
&= GRID_ATTR_CHARSET
;
527 /* For each attribute that is newly set, add its code. */
528 for (i
= 0; i
< nitems(attrs
); i
++) {
529 if ((attr
& attrs
[i
].mask
) && !(lastattr
& attrs
[i
].mask
))
530 s
[n
++] = attrs
[i
].code
;
533 /* If the foreground c changed, append its parameters. */
534 nnewc
= grid_string_cells_fg(gc
, newc
);
535 noldc
= grid_string_cells_fg(lastgc
, oldc
);
536 if (nnewc
!= noldc
||
537 memcmp(newc
,oldc
, nnewc
* sizeof newc
[0]) != 0) {
538 for (i
= 0; i
< nnewc
; i
++)
542 /* If the background c changed, append its parameters. */
543 nnewc
= grid_string_cells_bg(gc
, newc
);
544 noldc
= grid_string_cells_bg(lastgc
, oldc
);
545 if (nnewc
!= noldc
||
546 memcmp(newc
, oldc
, nnewc
* sizeof newc
[0]) != 0) {
547 for (i
= 0; i
< nnewc
; i
++)
551 /* If there are any parameters, append an SGR code. */
555 strlcat(buf
, "\\033[", len
);
557 strlcat(buf
, "\033[", len
);
558 for (i
= 0; i
< n
; i
++) {
560 xsnprintf(tmp
, sizeof tmp
, "%d;", s
[i
]);
562 xsnprintf(tmp
, sizeof tmp
, "%d", s
[i
]);
563 strlcat(buf
, tmp
, len
);
565 strlcat(buf
, "m", len
);
568 /* Append shift in/shift out if needed. */
569 if ((attr
& GRID_ATTR_CHARSET
) && !(lastattr
& GRID_ATTR_CHARSET
)) {
571 strlcat(buf
, "\\016", len
); /* SO */
573 strlcat(buf
, "\016", len
); /* SO */
575 if (!(attr
& GRID_ATTR_CHARSET
) && (lastattr
& GRID_ATTR_CHARSET
)) {
577 strlcat(buf
, "\\017", len
); /* SI */
579 strlcat(buf
, "\017", len
); /* SI */
583 /* Convert cells into a string. */
585 grid_string_cells(struct grid
*gd
, u_int px
, u_int py
, u_int nx
,
586 struct grid_cell
**lastgc
, int with_codes
, int escape_c0
, int trim
)
588 const struct grid_cell
*gc
;
589 static struct grid_cell lastgc1
;
592 char *buf
, code
[128];
593 size_t len
, off
, size
, codelen
;
595 const struct grid_line
*gl
;
597 GRID_DEBUG(gd
, "px=%u, py=%u, nx=%u", px
, py
, nx
);
599 if (lastgc
!= NULL
&& *lastgc
== NULL
) {
600 memcpy(&lastgc1
, &grid_default_cell
, sizeof lastgc1
);
608 gl
= grid_peek_line(gd
, py
);
609 for (xx
= px
; xx
< px
+ nx
; xx
++) {
610 if (gl
== NULL
|| xx
>= gl
->cellsize
)
612 gc
= &gl
->celldata
[xx
];
613 if (gc
->flags
& GRID_FLAG_PADDING
)
615 grid_cell_get(gc
, &ud
);
618 grid_string_cells_code(*lastgc
, gc
, code
, sizeof code
,
620 codelen
= strlen(code
);
621 memcpy(*lastgc
, gc
, sizeof *gc
);
627 if (escape_c0
&& size
== 1 && *data
== '\\') {
632 while (len
< off
+ size
+ codelen
+ 1) {
633 buf
= xrealloc(buf
, 2, len
);
638 memcpy(buf
+ off
, code
, codelen
);
641 memcpy(buf
+ off
, data
, size
);
646 while (off
> 0 && buf
[off
- 1] == ' ')
655 * Duplicate a set of lines between two grids. If there aren't enough lines in
656 * either source or destination, the number of lines is limited to the number
660 grid_duplicate_lines(
661 struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
, u_int ny
)
663 struct grid_line
*dstl
, *srcl
;
666 GRID_DEBUG(src
, "dy=%u, sy=%u, ny=%u", dy
, sy
, ny
);
668 if (dy
+ ny
> dst
->hsize
+ dst
->sy
)
669 ny
= dst
->hsize
+ dst
->sy
- dy
;
670 if (sy
+ ny
> src
->hsize
+ src
->sy
)
671 ny
= src
->hsize
+ src
->sy
- sy
;
672 grid_clear_lines(dst
, dy
, ny
);
674 for (yy
= 0; yy
< ny
; yy
++) {
675 srcl
= &src
->linedata
[sy
];
676 dstl
= &dst
->linedata
[dy
];
678 memcpy(dstl
, srcl
, sizeof *dstl
);
679 if (srcl
->cellsize
!= 0) {
680 dstl
->celldata
= xcalloc(
681 srcl
->cellsize
, sizeof *dstl
->celldata
);
682 memcpy(dstl
->celldata
, srcl
->celldata
,
683 srcl
->cellsize
* sizeof *dstl
->celldata
);
691 /* Join line data. */
693 grid_reflow_join(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
,
696 struct grid_line
*dst_gl
= &dst
->linedata
[(*py
) - 1];
697 u_int left
, to_copy
, ox
, nx
;
699 /* How much is left on the old line? */
700 left
= new_x
- dst_gl
->cellsize
;
702 /* Work out how much to append. */
703 to_copy
= src_gl
->cellsize
;
706 ox
= dst_gl
->cellsize
;
709 /* Resize the destination line. */
710 dst_gl
->celldata
= xrealloc(dst_gl
->celldata
, nx
,
711 sizeof *dst_gl
->celldata
);
712 dst_gl
->cellsize
= nx
;
714 /* Append as much as possible. */
715 memcpy(&dst_gl
->celldata
[ox
], &src_gl
->celldata
[0],
716 to_copy
* sizeof src_gl
->celldata
[0]);
718 /* If there is any left in the source, split it. */
719 if (src_gl
->cellsize
> to_copy
) {
720 dst_gl
->flags
|= GRID_LINE_WRAPPED
;
722 src_gl
->cellsize
-= to_copy
;
723 grid_reflow_split(dst
, py
, src_gl
, new_x
, to_copy
);
727 /* Split line data. */
729 grid_reflow_split(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
,
730 u_int new_x
, u_int offset
)
732 struct grid_line
*dst_gl
= NULL
;
735 /* Loop and copy sections of the source line. */
736 while (src_gl
->cellsize
> 0) {
737 /* Create new line. */
738 if (*py
>= dst
->hsize
+ dst
->sy
)
739 grid_scroll_history(dst
);
740 dst_gl
= &dst
->linedata
[*py
];
743 /* How much should we copy? */
745 if (to_copy
> src_gl
->cellsize
)
746 to_copy
= src_gl
->cellsize
;
748 /* Expand destination line. */
749 dst_gl
->celldata
= xmalloc(to_copy
* sizeof *dst_gl
->celldata
);
750 dst_gl
->cellsize
= to_copy
;
751 dst_gl
->flags
|= GRID_LINE_WRAPPED
;
754 memcpy (&dst_gl
->celldata
[0], &src_gl
->celldata
[offset
],
755 to_copy
* sizeof dst_gl
->celldata
[0]);
757 /* Move offset and reduce old line size. */
759 src_gl
->cellsize
-= to_copy
;
762 /* Last line is not wrapped. */
764 dst_gl
->flags
&= ~GRID_LINE_WRAPPED
;
767 /* Move line data. */
769 grid_reflow_move(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
)
771 struct grid_line
*dst_gl
;
773 /* Create new line. */
774 if (*py
>= dst
->hsize
+ dst
->sy
)
775 grid_scroll_history(dst
);
776 dst_gl
= &dst
->linedata
[*py
];
779 /* Copy the old line. */
780 memcpy(dst_gl
, src_gl
, sizeof *dst_gl
);
781 dst_gl
->flags
&= ~GRID_LINE_WRAPPED
;
783 /* Clear old line. */
784 src_gl
->celldata
= NULL
;
788 * Reflow lines from src grid into dst grid of width new_x. Returns number of
789 * lines fewer in the visible area. The source grid is destroyed.
792 grid_reflow(struct grid
*dst
, struct grid
*src
, u_int new_x
)
795 int previous_wrapped
;
796 struct grid_line
*src_gl
;
801 previous_wrapped
= 0;
802 for (line
= 0; line
< sy
+ src
->hsize
; line
++) {
803 src_gl
= src
->linedata
+ line
;
804 if (!previous_wrapped
) {
805 /* Wasn't wrapped. If smaller, move to destination. */
806 if (src_gl
->cellsize
<= new_x
)
807 grid_reflow_move(dst
, &py
, src_gl
);
809 grid_reflow_split(dst
, &py
, src_gl
, new_x
, 0);
811 /* Previous was wrapped. Try to join. */
812 grid_reflow_join(dst
, &py
, src_gl
, new_x
);
814 previous_wrapped
= src_gl
->flags
& GRID_LINE_WRAPPED
;