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
;
596 GRID_DEBUG(gd
, "px=%u, py=%u, nx=%u", px
, py
, nx
);
598 if (lastgc
!= NULL
&& *lastgc
== NULL
) {
599 memcpy(&lastgc1
, &grid_default_cell
, sizeof lastgc1
);
607 for (xx
= px
; xx
< px
+ nx
; xx
++) {
608 gc
= grid_peek_cell(gd
, xx
, py
);
609 if (gc
->flags
& GRID_FLAG_PADDING
)
611 grid_cell_get(gc
, &ud
);
614 grid_string_cells_code(*lastgc
, gc
, code
, sizeof code
,
616 codelen
= strlen(code
);
617 memcpy(*lastgc
, gc
, sizeof *gc
);
623 if (escape_c0
&& size
== 1 && *data
== '\\') {
628 while (len
< off
+ size
+ codelen
+ 1) {
629 buf
= xrealloc(buf
, 2, len
);
634 memcpy(buf
+ off
, code
, codelen
);
637 memcpy(buf
+ off
, data
, size
);
642 while (off
> 0 && buf
[off
- 1] == ' ')
651 * Duplicate a set of lines between two grids. If there aren't enough lines in
652 * either source or destination, the number of lines is limited to the number
656 grid_duplicate_lines(
657 struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
, u_int ny
)
659 struct grid_line
*dstl
, *srcl
;
662 GRID_DEBUG(src
, "dy=%u, sy=%u, ny=%u", dy
, sy
, ny
);
664 if (dy
+ ny
> dst
->hsize
+ dst
->sy
)
665 ny
= dst
->hsize
+ dst
->sy
- dy
;
666 if (sy
+ ny
> src
->hsize
+ src
->sy
)
667 ny
= src
->hsize
+ src
->sy
- sy
;
668 grid_clear_lines(dst
, dy
, ny
);
670 for (yy
= 0; yy
< ny
; yy
++) {
671 srcl
= &src
->linedata
[sy
];
672 dstl
= &dst
->linedata
[dy
];
674 memcpy(dstl
, srcl
, sizeof *dstl
);
675 if (srcl
->cellsize
!= 0) {
676 dstl
->celldata
= xcalloc(
677 srcl
->cellsize
, sizeof *dstl
->celldata
);
678 memcpy(dstl
->celldata
, srcl
->celldata
,
679 srcl
->cellsize
* sizeof *dstl
->celldata
);
687 /* Join line data. */
689 grid_reflow_join(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
,
692 struct grid_line
*dst_gl
= &dst
->linedata
[(*py
) - 1];
693 u_int left
, to_copy
, ox
, nx
;
695 /* How much is left on the old line? */
696 left
= new_x
- dst_gl
->cellsize
;
698 /* Work out how much to append. */
699 to_copy
= src_gl
->cellsize
;
702 ox
= dst_gl
->cellsize
;
705 /* Resize the destination line. */
706 dst_gl
->celldata
= xrealloc(dst_gl
->celldata
, nx
,
707 sizeof *dst_gl
->celldata
);
708 dst_gl
->cellsize
= nx
;
710 /* Append as much as possible. */
711 memcpy(&dst_gl
->celldata
[ox
], &src_gl
->celldata
[0],
712 to_copy
* sizeof src_gl
->celldata
[0]);
714 /* If there is any left in the source, split it. */
715 if (src_gl
->cellsize
> to_copy
) {
716 dst_gl
->flags
|= GRID_LINE_WRAPPED
;
718 src_gl
->cellsize
-= to_copy
;
719 grid_reflow_split(dst
, py
, src_gl
, new_x
, to_copy
);
723 /* Split line data. */
725 grid_reflow_split(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
,
726 u_int new_x
, u_int offset
)
728 struct grid_line
*dst_gl
= NULL
;
731 /* Loop and copy sections of the source line. */
732 while (src_gl
->cellsize
> 0) {
733 /* Create new line. */
734 if (*py
>= dst
->hsize
+ dst
->sy
)
735 grid_scroll_history(dst
);
736 dst_gl
= &dst
->linedata
[*py
];
739 /* How much should we copy? */
741 if (to_copy
> src_gl
->cellsize
)
742 to_copy
= src_gl
->cellsize
;
744 /* Expand destination line. */
745 dst_gl
->celldata
= xmalloc(to_copy
* sizeof *dst_gl
->celldata
);
746 dst_gl
->cellsize
= to_copy
;
747 dst_gl
->flags
|= GRID_LINE_WRAPPED
;
750 memcpy (&dst_gl
->celldata
[0], &src_gl
->celldata
[offset
],
751 to_copy
* sizeof dst_gl
->celldata
[0]);
753 /* Move offset and reduce old line size. */
755 src_gl
->cellsize
-= to_copy
;
758 /* Last line is not wrapped. */
760 dst_gl
->flags
&= ~GRID_LINE_WRAPPED
;
763 /* Move line data. */
765 grid_reflow_move(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
)
767 struct grid_line
*dst_gl
;
769 /* Create new line. */
770 if (*py
>= dst
->hsize
+ dst
->sy
)
771 grid_scroll_history(dst
);
772 dst_gl
= &dst
->linedata
[*py
];
775 /* Copy the old line. */
776 memcpy(dst_gl
, src_gl
, sizeof *dst_gl
);
777 dst_gl
->flags
&= ~GRID_LINE_WRAPPED
;
779 /* Clear old line. */
780 src_gl
->celldata
= NULL
;
784 * Reflow lines from src grid into dst grid of width new_x. Returns number of
785 * lines fewer in the visible area. The source grid is destroyed.
788 grid_reflow(struct grid
*dst
, struct grid
*src
, u_int new_x
)
791 int previous_wrapped
;
792 struct grid_line
*src_gl
;
797 previous_wrapped
= 0;
798 for (line
= 0; line
< sy
+ src
->hsize
; line
++) {
799 src_gl
= src
->linedata
+ line
;
800 if (!previous_wrapped
) {
801 /* Wasn't wrapped. If smaller, move to destination. */
802 if (src_gl
->cellsize
<= new_x
)
803 grid_reflow_move(dst
, &py
, src_gl
);
805 grid_reflow_split(dst
, &py
, src_gl
, new_x
, 0);
807 /* Previous was wrapped. Try to join. */
808 grid_reflow_join(dst
, &py
, src_gl
, new_x
);
810 previous_wrapped
= src_gl
->flags
& GRID_LINE_WRAPPED
;