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>
26 * Grid data. This is the basic data structure that represents what is shown on
29 * A grid is a grid of cells (struct grid_cell). Lines are not allocated until
30 * cells in that line are written to. The grid is split into history and
31 * viewable data with the history starting at row (line) 0 and extending to
32 * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All
33 * functions in this file work on absolute coordinates, grid-view.c has
34 * functions which work on the screen data.
37 /* Default grid cell data. */
38 const struct grid_cell grid_default_cell
= { 0, 0, 8, 8, ' ' };
40 #define grid_put_cell(gd, px, py, gc) do { \
41 memcpy(&gd->linedata[py].celldata[px], \
42 gc, sizeof gd->linedata[py].celldata[px]); \
44 #define grid_put_utf8(gd, px, py, gc) do { \
45 memcpy(&gd->linedata[py].utf8data[px], \
46 gc, sizeof gd->linedata[py].utf8data[px]); \
49 int grid_check_x(struct grid
*, u_int
);
50 int grid_check_y(struct grid
*, u_int
);
54 grid_check_x(struct grid
*gd
, u_int px
)
57 log_fatalx("x out of range: %u", px
);
62 grid_check_y(struct grid
*gd
, u_int py
)
64 if ((py
) >= (gd
)->hsize
+ (gd
)->sy
)
65 log_fatalx("y out of range: %u", py
);
70 grid_check_x(struct grid
*gd
, u_int px
)
72 if ((px
) >= (gd
)->sx
) {
73 log_debug("x out of range: %u", px
);
80 grid_check_y(struct grid
*gd
, u_int py
)
82 if ((py
) >= (gd
)->hsize
+ (gd
)->sy
) {
83 log_debug("y out of range: %u", py
);
90 /* Create a new grid. */
92 grid_create(u_int sx
, u_int sy
, u_int hlimit
)
96 gd
= xmalloc(sizeof *gd
);
100 gd
->flags
= GRID_HISTORY
;
105 gd
->linedata
= xcalloc(gd
->sy
, sizeof *gd
->linedata
);
112 grid_destroy(struct grid
*gd
)
114 struct grid_line
*gl
;
117 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
; yy
++) {
118 gl
= &gd
->linedata
[yy
];
119 if (gl
->celldata
!= NULL
)
121 if (gl
->utf8data
!= NULL
)
132 grid_compare(struct grid
*ga
, struct grid
*gb
)
134 struct grid_line
*gla
, *glb
;
135 struct grid_cell
*gca
, *gcb
;
136 struct grid_utf8
*gua
, *gub
;
139 if (ga
->sx
!= gb
->sx
|| ga
->sy
!= ga
->sy
)
142 for (yy
= 0; yy
< ga
->sy
; yy
++) {
143 gla
= &ga
->linedata
[yy
];
144 glb
= &gb
->linedata
[yy
];
145 if (gla
->cellsize
!= glb
->cellsize
)
147 for (xx
= 0; xx
< ga
->sx
; xx
++) {
148 gca
= &gla
->celldata
[xx
];
149 gcb
= &glb
->celldata
[xx
];
150 if (memcmp(gca
, gcb
, sizeof (struct grid_cell
)) != 0)
152 if (!(gca
->flags
& GRID_FLAG_UTF8
))
154 gua
= &gla
->utf8data
[xx
];
155 gub
= &glb
->utf8data
[xx
];
156 if (memcmp(gua
, gub
, sizeof (struct grid_utf8
)) != 0)
165 * Collect lines from the history if at the limit. Free the top (oldest) 10%
169 grid_collect_history(struct grid
*gd
)
175 if (gd
->hsize
< gd
->hlimit
)
178 yy
= gd
->hlimit
/ 10;
182 grid_move_lines(gd
, 0, yy
, gd
->hsize
+ gd
->sy
- yy
);
187 * Scroll the entire visible screen, moving one line into the history. Just
188 * allocate a new line at the bottom and move the history size indicator.
191 grid_scroll_history(struct grid
*gd
)
197 yy
= gd
->hsize
+ gd
->sy
;
198 gd
->linedata
= xrealloc(gd
->linedata
, yy
+ 1, sizeof *gd
->linedata
);
199 memset(&gd
->linedata
[yy
], 0, sizeof gd
->linedata
[yy
]);
204 /* Scroll a region up, moving the top line into the history. */
206 grid_scroll_history_region(struct grid
*gd
, u_int upper
, u_int lower
)
208 struct grid_line
*gl_history
, *gl_upper
, *gl_lower
;
211 GRID_DEBUG(gd
, "upper=%u, lower=%u", upper
, lower
);
213 /* Create a space for a new line. */
214 yy
= gd
->hsize
+ gd
->sy
;
215 gd
->linedata
= xrealloc(gd
->linedata
, yy
+ 1, sizeof *gd
->linedata
);
217 /* Move the entire screen down to free a space for this line. */
218 gl_history
= &gd
->linedata
[gd
->hsize
];
219 memmove(gl_history
+ 1, gl_history
, gd
->sy
* sizeof *gl_history
);
221 /* Adjust the region and find its start and end. */
223 gl_upper
= &gd
->linedata
[upper
];
225 gl_lower
= &gd
->linedata
[lower
];
227 /* Move the line into the history. */
228 memcpy(gl_history
, gl_upper
, sizeof *gl_history
);
230 /* Then move the region up and clear the bottom line. */
231 memmove(gl_upper
, gl_upper
+ 1, (lower
- upper
) * sizeof *gl_upper
);
232 memset(gl_lower
, 0, sizeof *gl_lower
);
234 /* Move the history offset down over the line. */
238 /* Expand line to fit to cell. */
240 grid_expand_line(struct grid
*gd
, u_int py
, u_int sx
)
242 struct grid_line
*gl
;
245 gl
= &gd
->linedata
[py
];
246 if (sx
<= gl
->cellsize
)
249 gl
->celldata
= xrealloc(gl
->celldata
, sx
, sizeof *gl
->celldata
);
250 for (xx
= gl
->cellsize
; xx
< sx
; xx
++)
251 grid_put_cell(gd
, xx
, py
, &grid_default_cell
);
255 /* Expand line to fit to cell for UTF-8. */
257 grid_expand_line_utf8(struct grid
*gd
, u_int py
, u_int sx
)
259 struct grid_line
*gl
;
261 gl
= &gd
->linedata
[py
];
262 if (sx
<= gl
->utf8size
)
265 gl
->utf8data
= xrealloc(gl
->utf8data
, sx
, sizeof *gl
->utf8data
);
269 /* Get cell for reading. */
270 const struct grid_cell
*
271 grid_peek_cell(struct grid
*gd
, u_int px
, u_int py
)
273 if (grid_check_x(gd
, px
) != 0)
274 return (&grid_default_cell
);
275 if (grid_check_y(gd
, py
) != 0)
276 return (&grid_default_cell
);
278 if (px
>= gd
->linedata
[py
].cellsize
)
279 return (&grid_default_cell
);
280 return (&gd
->linedata
[py
].celldata
[px
]);
283 /* Get cell at relative position (for writing). */
285 grid_get_cell(struct grid
*gd
, u_int px
, u_int py
)
287 if (grid_check_x(gd
, px
) != 0)
289 if (grid_check_y(gd
, py
) != 0)
292 grid_expand_line(gd
, py
, px
+ 1);
293 return (&gd
->linedata
[py
].celldata
[px
]);
296 /* Set cell at relative position. */
299 struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
)
301 if (grid_check_x(gd
, px
) != 0)
303 if (grid_check_y(gd
, py
) != 0)
306 grid_expand_line(gd
, py
, px
+ 1);
307 grid_put_cell(gd
, px
, py
, gc
);
310 /* Get UTF-8 for reading. */
311 const struct grid_utf8
*
312 grid_peek_utf8(struct grid
*gd
, u_int px
, u_int py
)
314 if (grid_check_x(gd
, px
) != 0)
316 if (grid_check_y(gd
, py
) != 0)
319 if (px
>= gd
->linedata
[py
].utf8size
)
321 return (&gd
->linedata
[py
].utf8data
[px
]);
324 /* Get utf8 at relative position (for writing). */
326 grid_get_utf8(struct grid
*gd
, u_int px
, u_int py
)
328 if (grid_check_x(gd
, px
) != 0)
330 if (grid_check_y(gd
, py
) != 0)
333 grid_expand_line_utf8(gd
, py
, px
+ 1);
334 return (&gd
->linedata
[py
].utf8data
[px
]);
337 /* Set utf8 at relative position. */
340 struct grid
*gd
, u_int px
, u_int py
, const struct grid_utf8
*gc
)
342 if (grid_check_x(gd
, px
) != 0)
344 if (grid_check_y(gd
, py
) != 0)
347 grid_expand_line_utf8(gd
, py
, px
+ 1);
348 grid_put_utf8(gd
, px
, py
, gc
);
353 grid_clear(struct grid
*gd
, u_int px
, u_int py
, u_int nx
, u_int ny
)
357 GRID_DEBUG(gd
, "px=%u, py=%u, nx=%u, ny=%u", px
, py
, nx
, ny
);
359 if (nx
== 0 || ny
== 0)
362 if (px
== 0 && nx
== gd
->sx
) {
363 grid_clear_lines(gd
, py
, ny
);
367 if (grid_check_x(gd
, px
) != 0)
369 if (grid_check_x(gd
, px
+ nx
- 1) != 0)
371 if (grid_check_y(gd
, py
) != 0)
373 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
376 for (yy
= py
; yy
< py
+ ny
; yy
++) {
377 if (px
>= gd
->linedata
[yy
].cellsize
)
379 if (px
+ nx
>= gd
->linedata
[yy
].cellsize
) {
380 gd
->linedata
[yy
].cellsize
= px
;
383 for (xx
= px
; xx
< px
+ nx
; xx
++) {
384 if (xx
>= gd
->linedata
[yy
].cellsize
)
386 grid_put_cell(gd
, xx
, yy
, &grid_default_cell
);
391 /* Clear lines. This just frees and truncates the lines. */
393 grid_clear_lines(struct grid
*gd
, u_int py
, u_int ny
)
395 struct grid_line
*gl
;
398 GRID_DEBUG(gd
, "py=%u, ny=%u", py
, ny
);
403 if (grid_check_y(gd
, py
) != 0)
405 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
408 for (yy
= py
; yy
< py
+ ny
; yy
++) {
409 gl
= &gd
->linedata
[yy
];
410 if (gl
->celldata
!= NULL
)
412 if (gl
->utf8data
!= NULL
)
414 memset(gl
, 0, sizeof *gl
);
418 /* Move a group of lines. */
420 grid_move_lines(struct grid
*gd
, u_int dy
, u_int py
, u_int ny
)
424 GRID_DEBUG(gd
, "dy=%u, py=%u, ny=%u", dy
, py
, ny
);
426 if (ny
== 0 || py
== dy
)
429 if (grid_check_y(gd
, py
) != 0)
431 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
433 if (grid_check_y(gd
, dy
) != 0)
435 if (grid_check_y(gd
, dy
+ ny
- 1) != 0)
438 /* Free any lines which are being replaced. */
439 for (yy
= dy
; yy
< dy
+ ny
; yy
++) {
440 if (yy
>= py
&& yy
< py
+ ny
)
442 grid_clear_lines(gd
, yy
, 1);
446 &gd
->linedata
[dy
], &gd
->linedata
[py
], ny
* (sizeof *gd
->linedata
));
448 /* Wipe any lines that have been moved (without freeing them). */
449 for (yy
= py
; yy
< py
+ ny
; yy
++) {
450 if (yy
>= dy
&& yy
< dy
+ ny
)
452 memset(&gd
->linedata
[yy
], 0, sizeof gd
->linedata
[yy
]);
456 /* Move a group of cells. */
458 grid_move_cells(struct grid
*gd
, u_int dx
, u_int px
, u_int py
, u_int nx
)
460 struct grid_line
*gl
;
463 GRID_DEBUG(gd
, "dx=%u, px=%u, py=%u, nx=%u", dx
, px
, py
, nx
);
465 if (nx
== 0 || px
== dx
)
468 if (grid_check_x(gd
, px
) != 0)
470 if (grid_check_x(gd
, px
+ nx
- 1) != 0)
472 if (grid_check_x(gd
, dx
+ nx
- 1) != 0)
474 if (grid_check_y(gd
, py
) != 0)
476 gl
= &gd
->linedata
[py
];
478 grid_expand_line(gd
, py
, px
+ nx
);
479 grid_expand_line(gd
, py
, dx
+ nx
);
481 &gl
->celldata
[dx
], &gl
->celldata
[px
], nx
* sizeof *gl
->celldata
);
483 if (gl
->utf8data
!= NULL
) {
484 grid_expand_line_utf8(gd
, py
, px
+ nx
);
485 grid_expand_line_utf8(gd
, py
, dx
+ nx
);
486 memmove(&gl
->utf8data
[dx
],
487 &gl
->utf8data
[px
], nx
* sizeof *gl
->utf8data
);
490 /* Wipe any cells that have been moved. */
491 for (xx
= px
; xx
< px
+ nx
; xx
++) {
492 if (xx
>= dx
&& xx
< dx
+ nx
)
494 grid_put_cell(gd
, xx
, py
, &grid_default_cell
);
498 /* Convert cells into a string. */
500 grid_string_cells(struct grid
*gd
, u_int px
, u_int py
, u_int nx
)
502 const struct grid_cell
*gc
;
503 const struct grid_utf8
*gu
;
505 size_t len
, off
, size
;
508 GRID_DEBUG(gd
, "px=%u, py=%u, nx=%u", px
, py
, nx
);
514 for (xx
= px
; xx
< px
+ nx
; xx
++) {
515 gc
= grid_peek_cell(gd
, xx
, py
);
516 if (gc
->flags
& GRID_FLAG_PADDING
)
519 if (gc
->flags
& GRID_FLAG_UTF8
) {
520 gu
= grid_peek_utf8(gd
, xx
, py
);
522 size
= grid_utf8_size(gu
);
523 while (len
< off
+ size
+ 1) {
524 buf
= xrealloc(buf
, 2, len
);
528 off
+= grid_utf8_copy(gu
, buf
+ off
, len
- off
);
530 while (len
< off
+ 2) {
531 buf
= xrealloc(buf
, 2, len
);
535 buf
[off
++] = gc
->data
;
539 while (off
> 0 && buf
[off
- 1] == ' ')
546 * Duplicate a set of lines between two grids. If there aren't enough lines in
547 * either source or destination, the number of lines is limited to the number
551 grid_duplicate_lines(
552 struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
, u_int ny
)
554 struct grid_line
*dstl
, *srcl
;
557 GRID_DEBUG(src
, "dy=%u, sy=%u, ny=%u", dy
, sy
, ny
);
559 if (dy
+ ny
> dst
->hsize
+ dst
->sy
)
560 ny
= dst
->hsize
+ dst
->sy
- dy
;
561 if (sy
+ ny
> src
->hsize
+ src
->sy
)
562 ny
= src
->hsize
+ src
->sy
- sy
;
563 grid_clear_lines(dst
, dy
, ny
);
565 for (yy
= 0; yy
< ny
; yy
++) {
566 srcl
= &src
->linedata
[sy
];
567 dstl
= &dst
->linedata
[dy
];
569 memcpy(dstl
, srcl
, sizeof *dstl
);
570 if (srcl
->cellsize
!= 0) {
571 dstl
->celldata
= xcalloc(
572 srcl
->cellsize
, sizeof *dstl
->celldata
);
573 memcpy(dstl
->celldata
, srcl
->celldata
,
574 srcl
->cellsize
* sizeof *dstl
->celldata
);
576 if (srcl
->utf8size
!= 0) {
577 dstl
->utf8data
= xcalloc(
578 srcl
->utf8size
, sizeof *dstl
->utf8data
);
579 memcpy(dstl
->utf8data
, srcl
->utf8data
,
580 srcl
->utf8size
* sizeof *dstl
->utf8data
);