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, " " };
41 #define grid_put_cell(gd, px, py, gc) do { \
42 memcpy(&gd->linedata[py].celldata[px], \
43 gc, sizeof gd->linedata[py].celldata[px]); \
45 #define grid_put_utf8(gd, px, py, gc) do { \
46 memcpy(&gd->linedata[py].utf8data[px], \
47 gc, sizeof gd->linedata[py].utf8data[px]); \
50 int grid_check_y(struct grid
*, u_int
);
54 grid_check_y(struct grid
*gd
, u_int py
)
56 if ((py
) >= (gd
)->hsize
+ (gd
)->sy
)
57 log_fatalx("y out of range: %u", py
);
62 grid_check_y(struct grid
*gd
, u_int py
)
64 if ((py
) >= (gd
)->hsize
+ (gd
)->sy
) {
65 log_debug("y out of range: %u", py
);
72 void grid_reflow_join(struct grid
*, u_int
*, struct grid_line
*, u_int
);
73 void grid_reflow_split(struct grid
*, u_int
*, struct grid_line
*, u_int
,
75 void grid_reflow_move(struct grid
*, u_int
*, struct grid_line
*);
76 size_t grid_string_cells_fg(const struct grid_cell
*, int *);
77 size_t grid_string_cells_bg(const struct grid_cell
*, int *);
78 void grid_string_cells_code(const struct grid_cell
*,
79 const struct grid_cell
*, char *, size_t, int);
81 /* Create a new grid. */
83 grid_create(u_int sx
, u_int sy
, u_int hlimit
)
87 gd
= xmalloc(sizeof *gd
);
91 gd
->flags
= GRID_HISTORY
;
96 gd
->linedata
= xcalloc(gd
->sy
, sizeof *gd
->linedata
);
103 grid_destroy(struct grid
*gd
)
105 struct grid_line
*gl
;
108 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
; yy
++) {
109 gl
= &gd
->linedata
[yy
];
120 grid_compare(struct grid
*ga
, struct grid
*gb
)
122 struct grid_line
*gla
, *glb
;
123 struct grid_cell
*gca
, *gcb
;
126 if (ga
->sx
!= gb
->sx
|| ga
->sy
!= gb
->sy
)
129 for (yy
= 0; yy
< ga
->sy
; yy
++) {
130 gla
= &ga
->linedata
[yy
];
131 glb
= &gb
->linedata
[yy
];
132 if (gla
->cellsize
!= glb
->cellsize
)
134 for (xx
= 0; xx
< ga
->sx
; xx
++) {
135 gca
= &gla
->celldata
[xx
];
136 gcb
= &glb
->celldata
[xx
];
137 if (memcmp(gca
, gcb
, sizeof (struct grid_cell
)) != 0)
146 * Collect lines from the history if at the limit. Free the top (oldest) 10%
150 grid_collect_history(struct grid
*gd
)
156 if (gd
->hsize
< gd
->hlimit
)
159 yy
= gd
->hlimit
/ 10;
163 grid_move_lines(gd
, 0, yy
, gd
->hsize
+ gd
->sy
- yy
);
168 * Scroll the entire visible screen, moving one line into the history. Just
169 * allocate a new line at the bottom and move the history size indicator.
172 grid_scroll_history(struct grid
*gd
)
178 yy
= gd
->hsize
+ gd
->sy
;
179 gd
->linedata
= xrealloc(gd
->linedata
, yy
+ 1, sizeof *gd
->linedata
);
180 memset(&gd
->linedata
[yy
], 0, sizeof gd
->linedata
[yy
]);
185 /* Scroll a region up, moving the top line into the history. */
187 grid_scroll_history_region(struct grid
*gd
, u_int upper
, u_int lower
)
189 struct grid_line
*gl_history
, *gl_upper
, *gl_lower
;
192 GRID_DEBUG(gd
, "upper=%u, lower=%u", upper
, lower
);
194 /* Create a space for a new line. */
195 yy
= gd
->hsize
+ gd
->sy
;
196 gd
->linedata
= xrealloc(gd
->linedata
, yy
+ 1, sizeof *gd
->linedata
);
198 /* Move the entire screen down to free a space for this line. */
199 gl_history
= &gd
->linedata
[gd
->hsize
];
200 memmove(gl_history
+ 1, gl_history
, gd
->sy
* sizeof *gl_history
);
202 /* Adjust the region and find its start and end. */
204 gl_upper
= &gd
->linedata
[upper
];
206 gl_lower
= &gd
->linedata
[lower
];
208 /* Move the line into the history. */
209 memcpy(gl_history
, gl_upper
, sizeof *gl_history
);
211 /* Then move the region up and clear the bottom line. */
212 memmove(gl_upper
, gl_upper
+ 1, (lower
- upper
) * sizeof *gl_upper
);
213 memset(gl_lower
, 0, sizeof *gl_lower
);
215 /* Move the history offset down over the line. */
219 /* Expand line to fit to cell. */
221 grid_expand_line(struct grid
*gd
, u_int py
, u_int sx
)
223 struct grid_line
*gl
;
226 gl
= &gd
->linedata
[py
];
227 if (sx
<= gl
->cellsize
)
230 gl
->celldata
= xrealloc(gl
->celldata
, sx
, sizeof *gl
->celldata
);
231 for (xx
= gl
->cellsize
; xx
< sx
; xx
++)
232 grid_put_cell(gd
, xx
, py
, &grid_default_cell
);
236 /* Peek at grid line. */
237 const struct grid_line
*
238 grid_peek_line(struct grid
*gd
, u_int py
)
240 if (grid_check_y(gd
, py
) != 0)
242 return (&gd
->linedata
[py
]);
245 /* Get cell for reading. */
246 const struct grid_cell
*
247 grid_peek_cell(struct grid
*gd
, u_int px
, u_int py
)
249 if (grid_check_y(gd
, py
) != 0)
250 return (&grid_default_cell
);
252 if (px
>= gd
->linedata
[py
].cellsize
)
253 return (&grid_default_cell
);
254 return (&gd
->linedata
[py
].celldata
[px
]);
257 /* Get cell at relative position (for writing). */
259 grid_get_cell(struct grid
*gd
, u_int px
, u_int py
)
261 if (grid_check_y(gd
, py
) != 0)
264 grid_expand_line(gd
, py
, px
+ 1);
265 return (&gd
->linedata
[py
].celldata
[px
]);
268 /* Set cell at relative position. */
270 grid_set_cell(struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
)
272 if (grid_check_y(gd
, py
) != 0)
275 grid_expand_line(gd
, py
, px
+ 1);
276 grid_put_cell(gd
, px
, py
, gc
);
281 grid_clear(struct grid
*gd
, u_int px
, u_int py
, u_int nx
, u_int ny
)
285 GRID_DEBUG(gd
, "px=%u, py=%u, nx=%u, ny=%u", px
, py
, nx
, ny
);
287 if (nx
== 0 || ny
== 0)
290 if (px
== 0 && nx
== gd
->sx
) {
291 grid_clear_lines(gd
, py
, ny
);
295 if (grid_check_y(gd
, py
) != 0)
297 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
300 for (yy
= py
; yy
< py
+ ny
; yy
++) {
301 if (px
>= gd
->linedata
[yy
].cellsize
)
303 if (px
+ nx
>= gd
->linedata
[yy
].cellsize
) {
304 gd
->linedata
[yy
].cellsize
= px
;
307 for (xx
= px
; xx
< px
+ nx
; xx
++) {
308 if (xx
>= gd
->linedata
[yy
].cellsize
)
310 grid_put_cell(gd
, xx
, yy
, &grid_default_cell
);
315 /* Clear lines. This just frees and truncates the lines. */
317 grid_clear_lines(struct grid
*gd
, u_int py
, u_int ny
)
319 struct grid_line
*gl
;
322 GRID_DEBUG(gd
, "py=%u, ny=%u", py
, ny
);
327 if (grid_check_y(gd
, py
) != 0)
329 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
332 for (yy
= py
; yy
< py
+ ny
; yy
++) {
333 gl
= &gd
->linedata
[yy
];
335 memset(gl
, 0, sizeof *gl
);
339 /* Move a group of lines. */
341 grid_move_lines(struct grid
*gd
, u_int dy
, u_int py
, u_int ny
)
345 GRID_DEBUG(gd
, "dy=%u, py=%u, ny=%u", dy
, py
, ny
);
347 if (ny
== 0 || py
== dy
)
350 if (grid_check_y(gd
, py
) != 0)
352 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
354 if (grid_check_y(gd
, dy
) != 0)
356 if (grid_check_y(gd
, dy
+ ny
- 1) != 0)
359 /* Free any lines which are being replaced. */
360 for (yy
= dy
; yy
< dy
+ ny
; yy
++) {
361 if (yy
>= py
&& yy
< py
+ ny
)
363 grid_clear_lines(gd
, yy
, 1);
367 &gd
->linedata
[dy
], &gd
->linedata
[py
], ny
* (sizeof *gd
->linedata
));
369 /* Wipe any lines that have been moved (without freeing them). */
370 for (yy
= py
; yy
< py
+ ny
; yy
++) {
371 if (yy
>= dy
&& yy
< dy
+ ny
)
373 memset(&gd
->linedata
[yy
], 0, sizeof gd
->linedata
[yy
]);
377 /* Move a group of cells. */
379 grid_move_cells(struct grid
*gd
, u_int dx
, u_int px
, u_int py
, u_int nx
)
381 struct grid_line
*gl
;
384 GRID_DEBUG(gd
, "dx=%u, px=%u, py=%u, nx=%u", dx
, px
, py
, nx
);
386 if (nx
== 0 || px
== dx
)
389 if (grid_check_y(gd
, py
) != 0)
391 gl
= &gd
->linedata
[py
];
393 grid_expand_line(gd
, py
, px
+ nx
);
394 grid_expand_line(gd
, py
, dx
+ nx
);
396 &gl
->celldata
[dx
], &gl
->celldata
[px
], nx
* sizeof *gl
->celldata
);
398 /* Wipe any cells that have been moved. */
399 for (xx
= px
; xx
< px
+ nx
; xx
++) {
400 if (xx
>= dx
&& xx
< dx
+ nx
)
402 grid_put_cell(gd
, xx
, py
, &grid_default_cell
);
406 /* Get ANSI foreground sequence. */
408 grid_string_cells_fg(const struct grid_cell
*gc
, int *values
)
413 if (gc
->flags
& GRID_FLAG_FG256
) {
416 values
[n
++] = gc
->fg
;
427 values
[n
++] = gc
->fg
+ 30;
440 values
[n
++] = gc
->fg
;
447 /* Get ANSI background sequence. */
449 grid_string_cells_bg(const struct grid_cell
*gc
, int *values
)
454 if (gc
->flags
& GRID_FLAG_BG256
) {
457 values
[n
++] = gc
->bg
;
468 values
[n
++] = gc
->bg
+ 40;
481 values
[n
++] = gc
->bg
- 10;
489 * Returns ANSI code to set particular attributes (colour, bold and so on)
490 * given a current state. The output buffer must be able to hold at least 57
494 grid_string_cells_code(const struct grid_cell
*lastgc
,
495 const struct grid_cell
*gc
, char *buf
, size_t len
, int escape_c0
)
497 int oldc
[16], newc
[16], s
[32];
498 size_t noldc
, nnewc
, n
, i
;
499 u_int attr
= gc
->attr
;
500 u_int lastattr
= lastgc
->attr
;
507 { GRID_ATTR_BRIGHT
, 1 },
508 { GRID_ATTR_DIM
, 2 },
509 { GRID_ATTR_ITALICS
, 3 },
510 { GRID_ATTR_UNDERSCORE
, 4 },
511 { GRID_ATTR_BLINK
, 5 },
512 { GRID_ATTR_REVERSE
, 7 },
513 { GRID_ATTR_HIDDEN
, 8 }
517 /* If any attribute is removed, begin with 0. */
518 for (i
= 0; i
< nitems(attrs
); i
++) {
519 if (!(attr
& attrs
[i
].mask
) && (lastattr
& attrs
[i
].mask
)) {
521 lastattr
&= GRID_ATTR_CHARSET
;
525 /* For each attribute that is newly set, add its code. */
526 for (i
= 0; i
< nitems(attrs
); i
++) {
527 if ((attr
& attrs
[i
].mask
) && !(lastattr
& attrs
[i
].mask
))
528 s
[n
++] = attrs
[i
].code
;
531 /* If the foreground c changed, append its parameters. */
532 nnewc
= grid_string_cells_fg(gc
, newc
);
533 noldc
= grid_string_cells_fg(lastgc
, oldc
);
534 if (nnewc
!= noldc
||
535 memcmp(newc
,oldc
, nnewc
* sizeof newc
[0]) != 0) {
536 for (i
= 0; i
< nnewc
; i
++)
540 /* If the background c changed, append its parameters. */
541 nnewc
= grid_string_cells_bg(gc
, newc
);
542 noldc
= grid_string_cells_bg(lastgc
, oldc
);
543 if (nnewc
!= noldc
||
544 memcmp(newc
, oldc
, nnewc
* sizeof newc
[0]) != 0) {
545 for (i
= 0; i
< nnewc
; i
++)
549 /* If there are any parameters, append an SGR code. */
553 strlcat(buf
, "\\033[", len
);
555 strlcat(buf
, "\033[", len
);
556 for (i
= 0; i
< n
; i
++) {
558 xsnprintf(tmp
, sizeof tmp
, "%d;", s
[i
]);
560 xsnprintf(tmp
, sizeof tmp
, "%d", s
[i
]);
561 strlcat(buf
, tmp
, len
);
563 strlcat(buf
, "m", len
);
566 /* Append shift in/shift out if needed. */
567 if ((attr
& GRID_ATTR_CHARSET
) && !(lastattr
& GRID_ATTR_CHARSET
)) {
569 strlcat(buf
, "\\016", len
); /* SO */
571 strlcat(buf
, "\016", len
); /* SO */
573 if (!(attr
& GRID_ATTR_CHARSET
) && (lastattr
& GRID_ATTR_CHARSET
)) {
575 strlcat(buf
, "\\017", len
); /* SI */
577 strlcat(buf
, "\017", len
); /* SI */
581 /* Convert cells into a string. */
583 grid_string_cells(struct grid
*gd
, u_int px
, u_int py
, u_int nx
,
584 struct grid_cell
**lastgc
, int with_codes
, int escape_c0
, int trim
)
586 const struct grid_cell
*gc
;
587 static struct grid_cell lastgc1
;
590 char *buf
, code
[128];
591 size_t len
, off
, size
, codelen
;
593 const struct grid_line
*gl
;
595 GRID_DEBUG(gd
, "px=%u, py=%u, nx=%u", px
, py
, nx
);
597 if (lastgc
!= NULL
&& *lastgc
== NULL
) {
598 memcpy(&lastgc1
, &grid_default_cell
, sizeof lastgc1
);
606 gl
= grid_peek_line(gd
, py
);
607 for (xx
= px
; xx
< px
+ nx
; xx
++) {
608 if (gl
== NULL
|| xx
>= gl
->cellsize
)
610 gc
= &gl
->celldata
[xx
];
611 if (gc
->flags
& GRID_FLAG_PADDING
)
613 grid_cell_get(gc
, &ud
);
616 grid_string_cells_code(*lastgc
, gc
, code
, sizeof code
,
618 codelen
= strlen(code
);
619 memcpy(*lastgc
, gc
, sizeof *gc
);
623 data
= (const char *)ud
.data
;
625 if (escape_c0
&& size
== 1 && *data
== '\\') {
630 while (len
< off
+ size
+ codelen
+ 1) {
631 buf
= xrealloc(buf
, 2, len
);
636 memcpy(buf
+ off
, code
, codelen
);
639 memcpy(buf
+ off
, data
, size
);
644 while (off
> 0 && buf
[off
- 1] == ' ')
653 * Duplicate a set of lines between two grids. If there aren't enough lines in
654 * either source or destination, the number of lines is limited to the number
658 grid_duplicate_lines(struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
,
661 struct grid_line
*dstl
, *srcl
;
664 GRID_DEBUG(src
, "dy=%u, sy=%u, ny=%u", dy
, sy
, ny
);
666 if (dy
+ ny
> dst
->hsize
+ dst
->sy
)
667 ny
= dst
->hsize
+ dst
->sy
- dy
;
668 if (sy
+ ny
> src
->hsize
+ src
->sy
)
669 ny
= src
->hsize
+ src
->sy
- sy
;
670 grid_clear_lines(dst
, dy
, ny
);
672 for (yy
= 0; yy
< ny
; yy
++) {
673 srcl
= &src
->linedata
[sy
];
674 dstl
= &dst
->linedata
[dy
];
676 memcpy(dstl
, srcl
, sizeof *dstl
);
677 if (srcl
->cellsize
!= 0) {
678 dstl
->celldata
= xcalloc(
679 srcl
->cellsize
, sizeof *dstl
->celldata
);
680 memcpy(dstl
->celldata
, srcl
->celldata
,
681 srcl
->cellsize
* sizeof *dstl
->celldata
);
689 /* Join line data. */
691 grid_reflow_join(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
,
694 struct grid_line
*dst_gl
= &dst
->linedata
[(*py
) - 1];
695 u_int left
, to_copy
, ox
, nx
;
697 /* How much is left on the old line? */
698 left
= new_x
- dst_gl
->cellsize
;
700 /* Work out how much to append. */
701 to_copy
= src_gl
->cellsize
;
704 ox
= dst_gl
->cellsize
;
707 /* Resize the destination line. */
708 dst_gl
->celldata
= xrealloc(dst_gl
->celldata
, nx
,
709 sizeof *dst_gl
->celldata
);
710 dst_gl
->cellsize
= nx
;
712 /* Append as much as possible. */
713 memcpy(&dst_gl
->celldata
[ox
], &src_gl
->celldata
[0],
714 to_copy
* sizeof src_gl
->celldata
[0]);
716 /* If there is any left in the source, split it. */
717 if (src_gl
->cellsize
> to_copy
) {
718 dst_gl
->flags
|= GRID_LINE_WRAPPED
;
720 src_gl
->cellsize
-= to_copy
;
721 grid_reflow_split(dst
, py
, src_gl
, new_x
, to_copy
);
725 /* Split line data. */
727 grid_reflow_split(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
,
728 u_int new_x
, u_int offset
)
730 struct grid_line
*dst_gl
= NULL
;
733 /* Loop and copy sections of the source line. */
734 while (src_gl
->cellsize
> 0) {
735 /* Create new line. */
736 if (*py
>= dst
->hsize
+ dst
->sy
)
737 grid_scroll_history(dst
);
738 dst_gl
= &dst
->linedata
[*py
];
741 /* How much should we copy? */
743 if (to_copy
> src_gl
->cellsize
)
744 to_copy
= src_gl
->cellsize
;
746 /* Expand destination line. */
747 dst_gl
->celldata
= xmalloc(to_copy
* sizeof *dst_gl
->celldata
);
748 dst_gl
->cellsize
= to_copy
;
749 dst_gl
->flags
|= GRID_LINE_WRAPPED
;
752 memcpy(&dst_gl
->celldata
[0], &src_gl
->celldata
[offset
],
753 to_copy
* sizeof dst_gl
->celldata
[0]);
755 /* Move offset and reduce old line size. */
757 src_gl
->cellsize
-= to_copy
;
760 /* Last line is not wrapped. */
762 dst_gl
->flags
&= ~GRID_LINE_WRAPPED
;
765 /* Move line data. */
767 grid_reflow_move(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
)
769 struct grid_line
*dst_gl
;
771 /* Create new line. */
772 if (*py
>= dst
->hsize
+ dst
->sy
)
773 grid_scroll_history(dst
);
774 dst_gl
= &dst
->linedata
[*py
];
777 /* Copy the old line. */
778 memcpy(dst_gl
, src_gl
, sizeof *dst_gl
);
779 dst_gl
->flags
&= ~GRID_LINE_WRAPPED
;
781 /* Clear old line. */
782 src_gl
->celldata
= NULL
;
786 * Reflow lines from src grid into dst grid of width new_x. Returns number of
787 * lines fewer in the visible area. The source grid is destroyed.
790 grid_reflow(struct grid
*dst
, struct grid
*src
, u_int new_x
)
793 int previous_wrapped
;
794 struct grid_line
*src_gl
;
799 previous_wrapped
= 0;
800 for (line
= 0; line
< sy
+ src
->hsize
; line
++) {
801 src_gl
= src
->linedata
+ line
;
802 if (!previous_wrapped
) {
803 /* Wasn't wrapped. If smaller, move to destination. */
804 if (src_gl
->cellsize
<= new_x
)
805 grid_reflow_move(dst
, &py
, src_gl
);
807 grid_reflow_split(dst
, &py
, src_gl
, new_x
, 0);
809 /* Previous was wrapped. Try to join. */
810 grid_reflow_join(dst
, &py
, src_gl
, new_x
);
812 previous_wrapped
= src_gl
->flags
& GRID_LINE_WRAPPED
;