1 /* $Id: grid.c,v 1.1.1.2 2011/08/17 18:40:04 jmmv Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Nicholas Marriott <nicm@users.sourceforge.net> 5 * 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. 9 * 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. 17 */ 18 19 #include <sys/types.h> 20 21 #include <string.h> 22 23 #include "tmux.h" 24 25 /* 26 * Grid data. This is the basic data structure that represents what is shown on 27 * screen. 28 * 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. 35 */ 36 37 /* Default grid cell data. */ 38 const struct grid_cell grid_default_cell = { 0, 0, 8, 8, ' ' }; 39 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]); \ 43 } while (0) 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]); \ 47 } while (0) 48 49 int grid_check_y(struct grid *, u_int); 50 51 #ifdef DEBUG 52 int 53 grid_check_y(struct grid *gd, u_int py) 54 { 55 if ((py) >= (gd)->hsize + (gd)->sy) 56 log_fatalx("y out of range: %u", py); 57 return (0); 58 } 59 #else 60 int 61 grid_check_y(struct grid *gd, u_int py) 62 { 63 if ((py) >= (gd)->hsize + (gd)->sy) { 64 log_debug("y out of range: %u", py); 65 return (-1); 66 } 67 return (0); 68 } 69 #endif 70 71 /* Create a new grid. */ 72 struct grid * 73 grid_create(u_int sx, u_int sy, u_int hlimit) 74 { 75 struct grid *gd; 76 77 gd = xmalloc(sizeof *gd); 78 gd->sx = sx; 79 gd->sy = sy; 80 81 gd->flags = GRID_HISTORY; 82 83 gd->hsize = 0; 84 gd->hlimit = hlimit; 85 86 gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); 87 88 return (gd); 89 } 90 91 /* Destroy grid. */ 92 void 93 grid_destroy(struct grid *gd) 94 { 95 struct grid_line *gl; 96 u_int yy; 97 98 for (yy = 0; yy < gd->hsize + gd->sy; yy++) { 99 gl = &gd->linedata[yy]; 100 if (gl->celldata != NULL) 101 xfree(gl->celldata); 102 if (gl->utf8data != NULL) 103 xfree(gl->utf8data); 104 } 105 106 xfree(gd->linedata); 107 108 xfree(gd); 109 } 110 111 /* Compare grids. */ 112 int 113 grid_compare(struct grid *ga, struct grid *gb) 114 { 115 struct grid_line *gla, *glb; 116 struct grid_cell *gca, *gcb; 117 struct grid_utf8 *gua, *gub; 118 u_int xx, yy; 119 120 if (ga->sx != gb->sx || ga->sy != ga->sy) 121 return (1); 122 123 for (yy = 0; yy < ga->sy; yy++) { 124 gla = &ga->linedata[yy]; 125 glb = &gb->linedata[yy]; 126 if (gla->cellsize != glb->cellsize) 127 return (1); 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) 132 return (1); 133 if (!(gca->flags & GRID_FLAG_UTF8)) 134 continue; 135 gua = &gla->utf8data[xx]; 136 gub = &glb->utf8data[xx]; 137 if (memcmp(gua, gub, sizeof (struct grid_utf8)) != 0) 138 return (1); 139 } 140 } 141 142 return (0); 143 } 144 145 /* 146 * Collect lines from the history if at the limit. Free the top (oldest) 10% 147 * and shift up. 148 */ 149 void 150 grid_collect_history(struct grid *gd) 151 { 152 u_int yy; 153 154 GRID_DEBUG(gd, ""); 155 156 if (gd->hsize < gd->hlimit) 157 return; 158 159 yy = gd->hlimit / 10; 160 if (yy < 1) 161 yy = 1; 162 163 grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy); 164 gd->hsize -= yy; 165 } 166 167 /* 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. 170 */ 171 void 172 grid_scroll_history(struct grid *gd) 173 { 174 u_int yy; 175 176 GRID_DEBUG(gd, ""); 177 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]); 181 182 gd->hsize++; 183 } 184 185 /* Scroll a region up, moving the top line into the history. */ 186 void 187 grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower) 188 { 189 struct grid_line *gl_history, *gl_upper, *gl_lower; 190 u_int yy; 191 192 GRID_DEBUG(gd, "upper=%u, lower=%u", upper, lower); 193 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); 197 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); 201 202 /* Adjust the region and find its start and end. */ 203 upper++; 204 gl_upper = &gd->linedata[upper]; 205 lower++; 206 gl_lower = &gd->linedata[lower]; 207 208 /* Move the line into the history. */ 209 memcpy(gl_history, gl_upper, sizeof *gl_history); 210 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); 214 215 /* Move the history offset down over the line. */ 216 gd->hsize++; 217 } 218 219 /* Expand line to fit to cell. */ 220 void 221 grid_expand_line(struct grid *gd, u_int py, u_int sx) 222 { 223 struct grid_line *gl; 224 u_int xx; 225 226 gl = &gd->linedata[py]; 227 if (sx <= gl->cellsize) 228 return; 229 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); 233 gl->cellsize = sx; 234 } 235 236 /* Expand line to fit to cell for UTF-8. */ 237 void 238 grid_expand_line_utf8(struct grid *gd, u_int py, u_int sx) 239 { 240 struct grid_line *gl; 241 242 gl = &gd->linedata[py]; 243 if (sx <= gl->utf8size) 244 return; 245 246 gl->utf8data = xrealloc(gl->utf8data, sx, sizeof *gl->utf8data); 247 gl->utf8size = sx; 248 } 249 250 /* Get cell for reading. */ 251 const struct grid_cell * 252 grid_peek_cell(struct grid *gd, u_int px, u_int py) 253 { 254 if (grid_check_y(gd, py) != 0) 255 return (&grid_default_cell); 256 257 if (px >= gd->linedata[py].cellsize) 258 return (&grid_default_cell); 259 return (&gd->linedata[py].celldata[px]); 260 } 261 262 /* Get cell at relative position (for writing). */ 263 struct grid_cell * 264 grid_get_cell(struct grid *gd, u_int px, u_int py) 265 { 266 if (grid_check_y(gd, py) != 0) 267 return (NULL); 268 269 grid_expand_line(gd, py, px + 1); 270 return (&gd->linedata[py].celldata[px]); 271 } 272 273 /* Set cell at relative position. */ 274 void 275 grid_set_cell( 276 struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) 277 { 278 if (grid_check_y(gd, py) != 0) 279 return; 280 281 grid_expand_line(gd, py, px + 1); 282 grid_put_cell(gd, px, py, gc); 283 } 284 285 /* Get UTF-8 for reading. */ 286 const struct grid_utf8 * 287 grid_peek_utf8(struct grid *gd, u_int px, u_int py) 288 { 289 if (grid_check_y(gd, py) != 0) 290 return (NULL); 291 292 if (px >= gd->linedata[py].utf8size) 293 return (NULL); 294 return (&gd->linedata[py].utf8data[px]); 295 } 296 297 /* Get utf8 at relative position (for writing). */ 298 struct grid_utf8 * 299 grid_get_utf8(struct grid *gd, u_int px, u_int py) 300 { 301 if (grid_check_y(gd, py) != 0) 302 return (NULL); 303 304 grid_expand_line_utf8(gd, py, px + 1); 305 return (&gd->linedata[py].utf8data[px]); 306 } 307 308 /* Set utf8 at relative position. */ 309 void 310 grid_set_utf8( 311 struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gc) 312 { 313 if (grid_check_y(gd, py) != 0) 314 return; 315 316 grid_expand_line_utf8(gd, py, px + 1); 317 grid_put_utf8(gd, px, py, gc); 318 } 319 320 /* Clear area. */ 321 void 322 grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) 323 { 324 u_int xx, yy; 325 326 GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); 327 328 if (nx == 0 || ny == 0) 329 return; 330 331 if (px == 0 && nx == gd->sx) { 332 grid_clear_lines(gd, py, ny); 333 return; 334 } 335 336 if (grid_check_y(gd, py) != 0) 337 return; 338 if (grid_check_y(gd, py + ny - 1) != 0) 339 return; 340 341 for (yy = py; yy < py + ny; yy++) { 342 if (px >= gd->linedata[yy].cellsize) 343 continue; 344 if (px + nx >= gd->linedata[yy].cellsize) { 345 gd->linedata[yy].cellsize = px; 346 continue; 347 } 348 for (xx = px; xx < px + nx; xx++) { 349 if (xx >= gd->linedata[yy].cellsize) 350 break; 351 grid_put_cell(gd, xx, yy, &grid_default_cell); 352 } 353 } 354 } 355 356 /* Clear lines. This just frees and truncates the lines. */ 357 void 358 grid_clear_lines(struct grid *gd, u_int py, u_int ny) 359 { 360 struct grid_line *gl; 361 u_int yy; 362 363 GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); 364 365 if (ny == 0) 366 return; 367 368 if (grid_check_y(gd, py) != 0) 369 return; 370 if (grid_check_y(gd, py + ny - 1) != 0) 371 return; 372 373 for (yy = py; yy < py + ny; yy++) { 374 gl = &gd->linedata[yy]; 375 if (gl->celldata != NULL) 376 xfree(gl->celldata); 377 if (gl->utf8data != NULL) 378 xfree(gl->utf8data); 379 memset(gl, 0, sizeof *gl); 380 } 381 } 382 383 /* Move a group of lines. */ 384 void 385 grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) 386 { 387 u_int yy; 388 389 GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny); 390 391 if (ny == 0 || py == dy) 392 return; 393 394 if (grid_check_y(gd, py) != 0) 395 return; 396 if (grid_check_y(gd, py + ny - 1) != 0) 397 return; 398 if (grid_check_y(gd, dy) != 0) 399 return; 400 if (grid_check_y(gd, dy + ny - 1) != 0) 401 return; 402 403 /* Free any lines which are being replaced. */ 404 for (yy = dy; yy < dy + ny; yy++) { 405 if (yy >= py && yy < py + ny) 406 continue; 407 grid_clear_lines(gd, yy, 1); 408 } 409 410 memmove( 411 &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata)); 412 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) 416 continue; 417 memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); 418 } 419 } 420 421 /* Move a group of cells. */ 422 void 423 grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) 424 { 425 struct grid_line *gl; 426 u_int xx; 427 428 GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx); 429 430 if (nx == 0 || px == dx) 431 return; 432 433 if (grid_check_y(gd, py) != 0) 434 return; 435 gl = &gd->linedata[py]; 436 437 grid_expand_line(gd, py, px + nx); 438 grid_expand_line(gd, py, dx + nx); 439 memmove( 440 &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata); 441 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); 447 } 448 449 /* Wipe any cells that have been moved. */ 450 for (xx = px; xx < px + nx; xx++) { 451 if (xx >= dx && xx < dx + nx) 452 continue; 453 grid_put_cell(gd, xx, py, &grid_default_cell); 454 } 455 } 456 457 /* Convert cells into a string. */ 458 char * 459 grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) 460 { 461 const struct grid_cell *gc; 462 const struct grid_utf8 *gu; 463 char *buf; 464 size_t len, off, size; 465 u_int xx; 466 467 GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); 468 469 len = 128; 470 buf = xmalloc(len); 471 off = 0; 472 473 for (xx = px; xx < px + nx; xx++) { 474 gc = grid_peek_cell(gd, xx, py); 475 if (gc->flags & GRID_FLAG_PADDING) 476 continue; 477 478 if (gc->flags & GRID_FLAG_UTF8) { 479 gu = grid_peek_utf8(gd, xx, py); 480 481 size = grid_utf8_size(gu); 482 while (len < off + size + 1) { 483 buf = xrealloc(buf, 2, len); 484 len *= 2; 485 } 486 487 off += grid_utf8_copy(gu, buf + off, len - off); 488 } else { 489 while (len < off + 2) { 490 buf = xrealloc(buf, 2, len); 491 len *= 2; 492 } 493 494 buf[off++] = gc->data; 495 } 496 } 497 498 while (off > 0 && buf[off - 1] == ' ') 499 off--; 500 buf[off] = '\0'; 501 return (buf); 502 } 503 504 /* 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 507 * available. 508 */ 509 void 510 grid_duplicate_lines( 511 struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny) 512 { 513 struct grid_line *dstl, *srcl; 514 u_int yy; 515 516 GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny); 517 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); 523 524 for (yy = 0; yy < ny; yy++) { 525 srcl = &src->linedata[sy]; 526 dstl = &dst->linedata[dy]; 527 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); 534 } 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); 540 } 541 542 sy++; 543 dy++; 544 } 545 } 546