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_y(struct grid
*, u_int
);
53 grid_check_y(struct grid
*gd
, u_int py
)
55 if ((py
) >= (gd
)->hsize
+ (gd
)->sy
)
56 log_fatalx("y out of range: %u", py
);
61 grid_check_y(struct grid
*gd
, u_int py
)
63 if ((py
) >= (gd
)->hsize
+ (gd
)->sy
) {
64 log_debug("y out of range: %u", py
);
71 /* Create a new grid. */
73 grid_create(u_int sx
, u_int sy
, u_int hlimit
)
77 gd
= xmalloc(sizeof *gd
);
81 gd
->flags
= GRID_HISTORY
;
86 gd
->linedata
= xcalloc(gd
->sy
, sizeof *gd
->linedata
);
93 grid_destroy(struct grid
*gd
)
98 for (yy
= 0; yy
< gd
->hsize
+ gd
->sy
; yy
++) {
99 gl
= &gd
->linedata
[yy
];
100 if (gl
->celldata
!= NULL
)
102 if (gl
->utf8data
!= NULL
)
113 grid_compare(struct grid
*ga
, struct grid
*gb
)
115 struct grid_line
*gla
, *glb
;
116 struct grid_cell
*gca
, *gcb
;
117 struct grid_utf8
*gua
, *gub
;
120 if (ga
->sx
!= gb
->sx
|| ga
->sy
!= ga
->sy
)
123 for (yy
= 0; yy
< ga
->sy
; yy
++) {
124 gla
= &ga
->linedata
[yy
];
125 glb
= &gb
->linedata
[yy
];
126 if (gla
->cellsize
!= glb
->cellsize
)
128 for (xx
= 0; xx
< ga
->sx
; xx
++) {
129 gca
= &gla
->celldata
[xx
];
130 gcb
= &glb
->celldata
[xx
];
131 if (memcmp(gca
, gcb
, sizeof (struct grid_cell
)) != 0)
133 if (!(gca
->flags
& GRID_FLAG_UTF8
))
135 gua
= &gla
->utf8data
[xx
];
136 gub
= &glb
->utf8data
[xx
];
137 if (memcmp(gua
, gub
, sizeof (struct grid_utf8
)) != 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 /* Expand line to fit to cell for UTF-8. */
238 grid_expand_line_utf8(struct grid
*gd
, u_int py
, u_int sx
)
240 struct grid_line
*gl
;
242 gl
= &gd
->linedata
[py
];
243 if (sx
<= gl
->utf8size
)
246 gl
->utf8data
= xrealloc(gl
->utf8data
, sx
, sizeof *gl
->utf8data
);
250 /* Get cell for reading. */
251 const struct grid_cell
*
252 grid_peek_cell(struct grid
*gd
, u_int px
, u_int py
)
254 if (grid_check_y(gd
, py
) != 0)
255 return (&grid_default_cell
);
257 if (px
>= gd
->linedata
[py
].cellsize
)
258 return (&grid_default_cell
);
259 return (&gd
->linedata
[py
].celldata
[px
]);
262 /* Get cell at relative position (for writing). */
264 grid_get_cell(struct grid
*gd
, u_int px
, u_int py
)
266 if (grid_check_y(gd
, py
) != 0)
269 grid_expand_line(gd
, py
, px
+ 1);
270 return (&gd
->linedata
[py
].celldata
[px
]);
273 /* Set cell at relative position. */
276 struct grid
*gd
, u_int px
, u_int py
, const struct grid_cell
*gc
)
278 if (grid_check_y(gd
, py
) != 0)
281 grid_expand_line(gd
, py
, px
+ 1);
282 grid_put_cell(gd
, px
, py
, gc
);
285 /* Get UTF-8 for reading. */
286 const struct grid_utf8
*
287 grid_peek_utf8(struct grid
*gd
, u_int px
, u_int py
)
289 if (grid_check_y(gd
, py
) != 0)
292 if (px
>= gd
->linedata
[py
].utf8size
)
294 return (&gd
->linedata
[py
].utf8data
[px
]);
297 /* Get utf8 at relative position (for writing). */
299 grid_get_utf8(struct grid
*gd
, u_int px
, u_int py
)
301 if (grid_check_y(gd
, py
) != 0)
304 grid_expand_line_utf8(gd
, py
, px
+ 1);
305 return (&gd
->linedata
[py
].utf8data
[px
]);
308 /* Set utf8 at relative position. */
311 struct grid
*gd
, u_int px
, u_int py
, const struct grid_utf8
*gc
)
313 if (grid_check_y(gd
, py
) != 0)
316 grid_expand_line_utf8(gd
, py
, px
+ 1);
317 grid_put_utf8(gd
, px
, py
, gc
);
322 grid_clear(struct grid
*gd
, u_int px
, u_int py
, u_int nx
, u_int ny
)
326 GRID_DEBUG(gd
, "px=%u, py=%u, nx=%u, ny=%u", px
, py
, nx
, ny
);
328 if (nx
== 0 || ny
== 0)
331 if (px
== 0 && nx
== gd
->sx
) {
332 grid_clear_lines(gd
, py
, ny
);
336 if (grid_check_y(gd
, py
) != 0)
338 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
341 for (yy
= py
; yy
< py
+ ny
; yy
++) {
342 if (px
>= gd
->linedata
[yy
].cellsize
)
344 if (px
+ nx
>= gd
->linedata
[yy
].cellsize
) {
345 gd
->linedata
[yy
].cellsize
= px
;
348 for (xx
= px
; xx
< px
+ nx
; xx
++) {
349 if (xx
>= gd
->linedata
[yy
].cellsize
)
351 grid_put_cell(gd
, xx
, yy
, &grid_default_cell
);
356 /* Clear lines. This just frees and truncates the lines. */
358 grid_clear_lines(struct grid
*gd
, u_int py
, u_int ny
)
360 struct grid_line
*gl
;
363 GRID_DEBUG(gd
, "py=%u, ny=%u", py
, ny
);
368 if (grid_check_y(gd
, py
) != 0)
370 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
373 for (yy
= py
; yy
< py
+ ny
; yy
++) {
374 gl
= &gd
->linedata
[yy
];
375 if (gl
->celldata
!= NULL
)
377 if (gl
->utf8data
!= NULL
)
379 memset(gl
, 0, sizeof *gl
);
383 /* Move a group of lines. */
385 grid_move_lines(struct grid
*gd
, u_int dy
, u_int py
, u_int ny
)
389 GRID_DEBUG(gd
, "dy=%u, py=%u, ny=%u", dy
, py
, ny
);
391 if (ny
== 0 || py
== dy
)
394 if (grid_check_y(gd
, py
) != 0)
396 if (grid_check_y(gd
, py
+ ny
- 1) != 0)
398 if (grid_check_y(gd
, dy
) != 0)
400 if (grid_check_y(gd
, dy
+ ny
- 1) != 0)
403 /* Free any lines which are being replaced. */
404 for (yy
= dy
; yy
< dy
+ ny
; yy
++) {
405 if (yy
>= py
&& yy
< py
+ ny
)
407 grid_clear_lines(gd
, yy
, 1);
411 &gd
->linedata
[dy
], &gd
->linedata
[py
], ny
* (sizeof *gd
->linedata
));
413 /* Wipe any lines that have been moved (without freeing them). */
414 for (yy
= py
; yy
< py
+ ny
; yy
++) {
415 if (yy
>= dy
&& yy
< dy
+ ny
)
417 memset(&gd
->linedata
[yy
], 0, sizeof gd
->linedata
[yy
]);
421 /* Move a group of cells. */
423 grid_move_cells(struct grid
*gd
, u_int dx
, u_int px
, u_int py
, u_int nx
)
425 struct grid_line
*gl
;
428 GRID_DEBUG(gd
, "dx=%u, px=%u, py=%u, nx=%u", dx
, px
, py
, nx
);
430 if (nx
== 0 || px
== dx
)
433 if (grid_check_y(gd
, py
) != 0)
435 gl
= &gd
->linedata
[py
];
437 grid_expand_line(gd
, py
, px
+ nx
);
438 grid_expand_line(gd
, py
, dx
+ nx
);
440 &gl
->celldata
[dx
], &gl
->celldata
[px
], nx
* sizeof *gl
->celldata
);
442 if (gl
->utf8data
!= NULL
) {
443 grid_expand_line_utf8(gd
, py
, px
+ nx
);
444 grid_expand_line_utf8(gd
, py
, dx
+ nx
);
445 memmove(&gl
->utf8data
[dx
],
446 &gl
->utf8data
[px
], nx
* sizeof *gl
->utf8data
);
449 /* Wipe any cells that have been moved. */
450 for (xx
= px
; xx
< px
+ nx
; xx
++) {
451 if (xx
>= dx
&& xx
< dx
+ nx
)
453 grid_put_cell(gd
, xx
, py
, &grid_default_cell
);
457 /* Convert cells into a string. */
459 grid_string_cells(struct grid
*gd
, u_int px
, u_int py
, u_int nx
)
461 const struct grid_cell
*gc
;
462 const struct grid_utf8
*gu
;
464 size_t len
, off
, size
;
467 GRID_DEBUG(gd
, "px=%u, py=%u, nx=%u", px
, py
, nx
);
473 for (xx
= px
; xx
< px
+ nx
; xx
++) {
474 gc
= grid_peek_cell(gd
, xx
, py
);
475 if (gc
->flags
& GRID_FLAG_PADDING
)
478 if (gc
->flags
& GRID_FLAG_UTF8
) {
479 gu
= grid_peek_utf8(gd
, xx
, py
);
481 size
= grid_utf8_size(gu
);
482 while (len
< off
+ size
+ 1) {
483 buf
= xrealloc(buf
, 2, len
);
487 off
+= grid_utf8_copy(gu
, buf
+ off
, len
- off
);
489 while (len
< off
+ 2) {
490 buf
= xrealloc(buf
, 2, len
);
494 buf
[off
++] = gc
->data
;
498 while (off
> 0 && buf
[off
- 1] == ' ')
505 * Duplicate a set of lines between two grids. If there aren't enough lines in
506 * either source or destination, the number of lines is limited to the number
510 grid_duplicate_lines(
511 struct grid
*dst
, u_int dy
, struct grid
*src
, u_int sy
, u_int ny
)
513 struct grid_line
*dstl
, *srcl
;
516 GRID_DEBUG(src
, "dy=%u, sy=%u, ny=%u", dy
, sy
, ny
);
518 if (dy
+ ny
> dst
->hsize
+ dst
->sy
)
519 ny
= dst
->hsize
+ dst
->sy
- dy
;
520 if (sy
+ ny
> src
->hsize
+ src
->sy
)
521 ny
= src
->hsize
+ src
->sy
- sy
;
522 grid_clear_lines(dst
, dy
, ny
);
524 for (yy
= 0; yy
< ny
; yy
++) {
525 srcl
= &src
->linedata
[sy
];
526 dstl
= &dst
->linedata
[dy
];
528 memcpy(dstl
, srcl
, sizeof *dstl
);
529 if (srcl
->cellsize
!= 0) {
530 dstl
->celldata
= xcalloc(
531 srcl
->cellsize
, sizeof *dstl
->celldata
);
532 memcpy(dstl
->celldata
, srcl
->celldata
,
533 srcl
->cellsize
* sizeof *dstl
->celldata
);
535 if (srcl
->utf8size
!= 0) {
536 dstl
->utf8data
= xcalloc(
537 srcl
->utf8size
, sizeof *dstl
->utf8data
);
538 memcpy(dstl
->utf8data
, srcl
->utf8data
,
539 srcl
->utf8size
* sizeof *dstl
->utf8data
);