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
!= gb
->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. */
271 grid_set_cell(struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
)
273 if (grid_check_y(gd
, py
) != 0)
276 grid_expand_line(gd
, py
, px
+ 1);
277 grid_put_cell(gd
, px
, py
, gc
);
282 grid_clear(struct grid
*gd
, u_int px
, u_int py
, u_int nx
, u_int ny
)
286 GRID_DEBUG(gd
, "px=%u, py=%u, nx=%u, ny=%u", px
, py
, nx
, ny
);
288 if (nx
== 0 || ny
== 0)
291 if (px
== 0 && nx
== gd
->sx
) {
292 grid_clear_lines(gd
, py
, ny
);
296 if (grid_check_y(gd
, py
) != 0)
298 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
301 for (yy
= py
; yy
< py
+ ny
; yy
++) {
302 if (px
>= gd
->linedata
[yy
].cellsize
)
304 if (px
+ nx
>= gd
->linedata
[yy
].cellsize
) {
305 gd
->linedata
[yy
].cellsize
= px
;
308 for (xx
= px
; xx
< px
+ nx
; xx
++) {
309 if (xx
>= gd
->linedata
[yy
].cellsize
)
311 grid_put_cell(gd
, xx
, yy
, &grid_default_cell
);
316 /* Clear lines. This just frees and truncates the lines. */
318 grid_clear_lines(struct grid
*gd
, u_int py
, u_int ny
)
320 struct grid_line
*gl
;
323 GRID_DEBUG(gd
, "py=%u, ny=%u", py
, ny
);
328 if (grid_check_y(gd
, py
) != 0)
330 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
333 for (yy
= py
; yy
< py
+ ny
; yy
++) {
334 gl
= &gd
->linedata
[yy
];
336 memset(gl
, 0, sizeof *gl
);
340 /* Move a group of lines. */
342 grid_move_lines(struct grid
*gd
, u_int dy
, u_int py
, u_int ny
)
346 GRID_DEBUG(gd
, "dy=%u, py=%u, ny=%u", dy
, py
, ny
);
348 if (ny
== 0 || py
== dy
)
351 if (grid_check_y(gd
, py
) != 0)
353 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
355 if (grid_check_y(gd
, dy
) != 0)
357 if (grid_check_y(gd
, dy
+ ny
- 1) != 0)
360 /* Free any lines which are being replaced. */
361 for (yy
= dy
; yy
< dy
+ ny
; yy
++) {
362 if (yy
>= py
&& yy
< py
+ ny
)
364 grid_clear_lines(gd
, yy
, 1);
368 &gd
->linedata
[dy
], &gd
->linedata
[py
], ny
* (sizeof *gd
->linedata
));
370 /* Wipe any lines that have been moved (without freeing them). */
371 for (yy
= py
; yy
< py
+ ny
; yy
++) {
372 if (yy
>= dy
&& yy
< dy
+ ny
)
374 memset(&gd
->linedata
[yy
], 0, sizeof gd
->linedata
[yy
]);
378 /* Move a group of cells. */
380 grid_move_cells(struct grid
*gd
, u_int dx
, u_int px
, u_int py
, u_int nx
)
382 struct grid_line
*gl
;
385 GRID_DEBUG(gd
, "dx=%u, px=%u, py=%u, nx=%u", dx
, px
, py
, nx
);
387 if (nx
== 0 || px
== dx
)
390 if (grid_check_y(gd
, py
) != 0)
392 gl
= &gd
->linedata
[py
];
394 grid_expand_line(gd
, py
, px
+ nx
);
395 grid_expand_line(gd
, py
, dx
+ nx
);
397 &gl
->celldata
[dx
], &gl
->celldata
[px
], nx
* sizeof *gl
->celldata
);
399 /* Wipe any cells that have been moved. */
400 for (xx
= px
; xx
< px
+ nx
; xx
++) {
401 if (xx
>= dx
&& xx
< dx
+ nx
)
403 grid_put_cell(gd
, xx
, py
, &grid_default_cell
);
407 /* Get ANSI foreground sequence. */
409 grid_string_cells_fg(const struct grid_cell
*gc
, int *values
)
414 if (gc
->flags
& GRID_FLAG_FG256
) {
417 values
[n
++] = gc
->fg
;
428 values
[n
++] = gc
->fg
+ 30;
441 values
[n
++] = gc
->fg
;
448 /* Get ANSI background sequence. */
450 grid_string_cells_bg(const struct grid_cell
*gc
, int *values
)
455 if (gc
->flags
& GRID_FLAG_BG256
) {
458 values
[n
++] = gc
->bg
;
469 values
[n
++] = gc
->bg
+ 40;
482 values
[n
++] = gc
->bg
- 10;
490 * Returns ANSI code to set particular attributes (colour, bold and so on)
491 * given a current state. The output buffer must be able to hold at least 57
495 grid_string_cells_code(const struct grid_cell
*lastgc
,
496 const struct grid_cell
*gc
, char *buf
, size_t len
, int escape_c0
)
498 int oldc
[16], newc
[16], s
[32];
499 size_t noldc
, nnewc
, n
, i
;
500 u_int attr
= gc
->attr
;
501 u_int lastattr
= lastgc
->attr
;
508 { GRID_ATTR_BRIGHT
, 1 },
509 { GRID_ATTR_DIM
, 2 },
510 { GRID_ATTR_ITALICS
, 3 },
511 { GRID_ATTR_UNDERSCORE
, 4 },
512 { GRID_ATTR_BLINK
, 5 },
513 { GRID_ATTR_REVERSE
, 7 },
514 { GRID_ATTR_HIDDEN
, 8 }
518 /* If any attribute is removed, begin with 0. */
519 for (i
= 0; i
< nitems(attrs
); i
++) {
520 if (!(attr
& attrs
[i
].mask
) && (lastattr
& attrs
[i
].mask
)) {
522 lastattr
&= GRID_ATTR_CHARSET
;
526 /* For each attribute that is newly set, add its code. */
527 for (i
= 0; i
< nitems(attrs
); i
++) {
528 if ((attr
& attrs
[i
].mask
) && !(lastattr
& attrs
[i
].mask
))
529 s
[n
++] = attrs
[i
].code
;
532 /* If the foreground c changed, append its parameters. */
533 nnewc
= grid_string_cells_fg(gc
, newc
);
534 noldc
= grid_string_cells_fg(lastgc
, oldc
);
535 if (nnewc
!= noldc
||
536 memcmp(newc
,oldc
, nnewc
* sizeof newc
[0]) != 0) {
537 for (i
= 0; i
< nnewc
; i
++)
541 /* If the background c changed, append its parameters. */
542 nnewc
= grid_string_cells_bg(gc
, newc
);
543 noldc
= grid_string_cells_bg(lastgc
, oldc
);
544 if (nnewc
!= noldc
||
545 memcmp(newc
, oldc
, nnewc
* sizeof newc
[0]) != 0) {
546 for (i
= 0; i
< nnewc
; i
++)
550 /* If there are any parameters, append an SGR code. */
554 strlcat(buf
, "\\033[", len
);
556 strlcat(buf
, "\033[", len
);
557 for (i
= 0; i
< n
; i
++) {
559 xsnprintf(tmp
, sizeof tmp
, "%d;", s
[i
]);
561 xsnprintf(tmp
, sizeof tmp
, "%d", s
[i
]);
562 strlcat(buf
, tmp
, len
);
564 strlcat(buf
, "m", len
);
567 /* Append shift in/shift out if needed. */
568 if ((attr
& GRID_ATTR_CHARSET
) && !(lastattr
& GRID_ATTR_CHARSET
)) {
570 strlcat(buf
, "\\016", len
); /* SO */
572 strlcat(buf
, "\016", len
); /* SO */
574 if (!(attr
& GRID_ATTR_CHARSET
) && (lastattr
& GRID_ATTR_CHARSET
)) {
576 strlcat(buf
, "\\017", len
); /* SI */
578 strlcat(buf
, "\017", len
); /* SI */
582 /* Convert cells into a string. */
584 grid_string_cells(struct grid
*gd
, u_int px
, u_int py
, u_int nx
,
585 struct grid_cell
**lastgc
, int with_codes
, int escape_c0
, int trim
)
587 const struct grid_cell
*gc
;
588 static struct grid_cell lastgc1
;
591 char *buf
, code
[128];
592 size_t len
, off
, size
, codelen
;
594 const struct grid_line
*gl
;
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 gl
= grid_peek_line(gd
, py
);
608 for (xx
= px
; xx
< px
+ nx
; xx
++) {
609 if (gl
== NULL
|| xx
>= gl
->cellsize
)
611 gc
= &gl
->celldata
[xx
];
612 if (gc
->flags
& GRID_FLAG_PADDING
)
614 grid_cell_get(gc
, &ud
);
617 grid_string_cells_code(*lastgc
, gc
, code
, sizeof code
,
619 codelen
= strlen(code
);
620 memcpy(*lastgc
, gc
, sizeof *gc
);
626 if (escape_c0
&& size
== 1 && *data
== '\\') {
631 while (len
< off
+ size
+ codelen
+ 1) {
632 buf
= xrealloc(buf
, 2, len
);
637 memcpy(buf
+ off
, code
, codelen
);
640 memcpy(buf
+ off
, data
, size
);
645 while (off
> 0 && buf
[off
- 1] == ' ')
654 * Duplicate a set of lines between two grids. If there aren't enough lines in
655 * either source or destination, the number of lines is limited to the number
659 grid_duplicate_lines(struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
,
662 struct grid_line
*dstl
, *srcl
;
665 GRID_DEBUG(src
, "dy=%u, sy=%u, ny=%u", dy
, sy
, ny
);
667 if (dy
+ ny
> dst
->hsize
+ dst
->sy
)
668 ny
= dst
->hsize
+ dst
->sy
- dy
;
669 if (sy
+ ny
> src
->hsize
+ src
->sy
)
670 ny
= src
->hsize
+ src
->sy
- sy
;
671 grid_clear_lines(dst
, dy
, ny
);
673 for (yy
= 0; yy
< ny
; yy
++) {
674 srcl
= &src
->linedata
[sy
];
675 dstl
= &dst
->linedata
[dy
];
677 memcpy(dstl
, srcl
, sizeof *dstl
);
678 if (srcl
->cellsize
!= 0) {
679 dstl
->celldata
= xcalloc(
680 srcl
->cellsize
, sizeof *dstl
->celldata
);
681 memcpy(dstl
->celldata
, srcl
->celldata
,
682 srcl
->cellsize
* sizeof *dstl
->celldata
);
690 /* Join line data. */
692 grid_reflow_join(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
,
695 struct grid_line
*dst_gl
= &dst
->linedata
[(*py
) - 1];
696 u_int left
, to_copy
, ox
, nx
;
698 /* How much is left on the old line? */
699 left
= new_x
- dst_gl
->cellsize
;
701 /* Work out how much to append. */
702 to_copy
= src_gl
->cellsize
;
705 ox
= dst_gl
->cellsize
;
708 /* Resize the destination line. */
709 dst_gl
->celldata
= xrealloc(dst_gl
->celldata
, nx
,
710 sizeof *dst_gl
->celldata
);
711 dst_gl
->cellsize
= nx
;
713 /* Append as much as possible. */
714 memcpy(&dst_gl
->celldata
[ox
], &src_gl
->celldata
[0],
715 to_copy
* sizeof src_gl
->celldata
[0]);
717 /* If there is any left in the source, split it. */
718 if (src_gl
->cellsize
> to_copy
) {
719 dst_gl
->flags
|= GRID_LINE_WRAPPED
;
721 src_gl
->cellsize
-= to_copy
;
722 grid_reflow_split(dst
, py
, src_gl
, new_x
, to_copy
);
726 /* Split line data. */
728 grid_reflow_split(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
,
729 u_int new_x
, u_int offset
)
731 struct grid_line
*dst_gl
= NULL
;
734 /* Loop and copy sections of the source line. */
735 while (src_gl
->cellsize
> 0) {
736 /* Create new line. */
737 if (*py
>= dst
->hsize
+ dst
->sy
)
738 grid_scroll_history(dst
);
739 dst_gl
= &dst
->linedata
[*py
];
742 /* How much should we copy? */
744 if (to_copy
> src_gl
->cellsize
)
745 to_copy
= src_gl
->cellsize
;
747 /* Expand destination line. */
748 dst_gl
->celldata
= xmalloc(to_copy
* sizeof *dst_gl
->celldata
);
749 dst_gl
->cellsize
= to_copy
;
750 dst_gl
->flags
|= GRID_LINE_WRAPPED
;
753 memcpy (&dst_gl
->celldata
[0], &src_gl
->celldata
[offset
],
754 to_copy
* sizeof dst_gl
->celldata
[0]);
756 /* Move offset and reduce old line size. */
758 src_gl
->cellsize
-= to_copy
;
761 /* Last line is not wrapped. */
763 dst_gl
->flags
&= ~GRID_LINE_WRAPPED
;
766 /* Move line data. */
768 grid_reflow_move(struct grid
*dst
, u_int
*py
, struct grid_line
*src_gl
)
770 struct grid_line
*dst_gl
;
772 /* Create new line. */
773 if (*py
>= dst
->hsize
+ dst
->sy
)
774 grid_scroll_history(dst
);
775 dst_gl
= &dst
->linedata
[*py
];
778 /* Copy the old line. */
779 memcpy(dst_gl
, src_gl
, sizeof *dst_gl
);
780 dst_gl
->flags
&= ~GRID_LINE_WRAPPED
;
782 /* Clear old line. */
783 src_gl
->celldata
= NULL
;
787 * Reflow lines from src grid into dst grid of width new_x. Returns number of
788 * lines fewer in the visible area. The source grid is destroyed.
791 grid_reflow(struct grid
*dst
, struct grid
*src
, u_int new_x
)
794 int previous_wrapped
;
795 struct grid_line
*src_gl
;
800 previous_wrapped
= 0;
801 for (line
= 0; line
< sy
+ src
->hsize
; line
++) {
802 src_gl
= src
->linedata
+ line
;
803 if (!previous_wrapped
) {
804 /* Wasn't wrapped. If smaller, move to destination. */
805 if (src_gl
->cellsize
<= new_x
)
806 grid_reflow_move(dst
, &py
, src_gl
);
808 grid_reflow_split(dst
, &py
, src_gl
, new_x
, 0);
810 /* Previous was wrapped. Try to join. */
811 grid_reflow_join(dst
, &py
, src_gl
, new_x
);
813 previous_wrapped
= src_gl
->flags
& GRID_LINE_WRAPPED
;