1 /* $OpenBSD: grid.c,v 1.14 2009/09/15 15:14:09 nicm 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_x(struct grid *, u_int); 50 int grid_check_y(struct grid *, u_int); 51 52 #ifdef DEBUG 53 int 54 grid_check_x(struct grid *gd, u_int px) 55 { 56 if ((px) >= (gd)->sx) 57 log_fatalx("x out of range: %u", px); 58 return (0); 59 } 60 61 int 62 grid_check_y(struct grid *gd, u_int py) 63 { 64 if ((py) >= (gd)->hsize + (gd)->sy) 65 log_fatalx("y out of range: %u", py); 66 return (0); 67 } 68 #else 69 int 70 grid_check_x(struct grid *gd, u_int px) 71 { 72 if ((px) >= (gd)->sx) { 73 log_debug("x out of range: %u", px); 74 return (-1); 75 } 76 return (0); 77 } 78 79 int 80 grid_check_y(struct grid *gd, u_int py) 81 { 82 if ((py) >= (gd)->hsize + (gd)->sy) { 83 log_debug("y out of range: %u", py); 84 return (-1); 85 } 86 return (0); 87 } 88 #endif 89 90 /* Create a new grid. */ 91 struct grid * 92 grid_create(u_int sx, u_int sy, u_int hlimit) 93 { 94 struct grid *gd; 95 96 gd = xmalloc(sizeof *gd); 97 gd->sx = sx; 98 gd->sy = sy; 99 100 gd->flags = GRID_HISTORY; 101 102 gd->hsize = 0; 103 gd->hlimit = hlimit; 104 105 gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); 106 107 return (gd); 108 } 109 110 /* Destroy grid. */ 111 void 112 grid_destroy(struct grid *gd) 113 { 114 struct grid_line *gl; 115 u_int yy; 116 117 for (yy = 0; yy < gd->hsize + gd->sy; yy++) { 118 gl = &gd->linedata[yy]; 119 if (gl->celldata != NULL) 120 xfree(gl->celldata); 121 if (gl->utf8data != NULL) 122 xfree(gl->utf8data); 123 } 124 125 xfree(gd->linedata); 126 127 xfree(gd); 128 } 129 130 /* Compare grids. */ 131 int 132 grid_compare(struct grid *ga, struct grid *gb) 133 { 134 struct grid_line *gla, *glb; 135 struct grid_cell *gca, *gcb; 136 struct grid_utf8 *gua, *gub; 137 u_int xx, yy; 138 139 if (ga->sx != gb->sx || ga->sy != ga->sy) 140 return (1); 141 142 for (yy = 0; yy < ga->sy; yy++) { 143 gla = &ga->linedata[yy]; 144 glb = &gb->linedata[yy]; 145 if (gla->cellsize != glb->cellsize) 146 return (1); 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) 151 return (1); 152 if (!(gca->flags & GRID_FLAG_UTF8)) 153 continue; 154 gua = &gla->utf8data[xx]; 155 gub = &glb->utf8data[xx]; 156 if (memcmp(gua, gub, sizeof (struct grid_utf8)) != 0) 157 return (1); 158 } 159 } 160 161 return (0); 162 } 163 164 /* Scroll a line into the history. */ 165 void 166 grid_scroll_line(struct grid *gd) 167 { 168 u_int yy; 169 170 GRID_DEBUG(gd, ""); 171 172 if (gd->hsize >= gd->hlimit) { 173 /* If the limit is hit, free the bottom 10% and shift up. */ 174 yy = gd->hlimit / 10; 175 if (yy < 1) 176 yy = 1; 177 178 grid_move_lines(gd, 0, yy, gd->hsize + gd->sy - yy); 179 gd->hsize -= yy; 180 } 181 182 yy = gd->hsize + gd->sy; 183 184 gd->linedata = xrealloc(gd->linedata, yy + 1, sizeof *gd->linedata); 185 memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); 186 187 gd->hsize++; 188 } 189 190 /* Expand line to fit to cell. */ 191 void 192 grid_expand_line(struct grid *gd, u_int py, u_int sx) 193 { 194 struct grid_line *gl; 195 u_int xx; 196 197 gl = &gd->linedata[py]; 198 if (sx <= gl->cellsize) 199 return; 200 201 gl->celldata = xrealloc(gl->celldata, sx, sizeof *gl->celldata); 202 for (xx = gl->cellsize; xx < sx; xx++) 203 grid_put_cell(gd, xx, py, &grid_default_cell); 204 gl->cellsize = sx; 205 } 206 207 /* Expand line to fit to cell for UTF-8. */ 208 void 209 grid_expand_line_utf8(struct grid *gd, u_int py, u_int sx) 210 { 211 struct grid_line *gl; 212 213 gl = &gd->linedata[py]; 214 if (sx <= gl->utf8size) 215 return; 216 217 gl->utf8data = xrealloc(gl->utf8data, sx, sizeof *gl->utf8data); 218 gl->utf8size = sx; 219 } 220 221 /* Get cell for reading. */ 222 const struct grid_cell * 223 grid_peek_cell(struct grid *gd, u_int px, u_int py) 224 { 225 if (grid_check_x(gd, px) != 0) 226 return (&grid_default_cell); 227 if (grid_check_y(gd, py) != 0) 228 return (&grid_default_cell); 229 230 if (px >= gd->linedata[py].cellsize) 231 return (&grid_default_cell); 232 return (&gd->linedata[py].celldata[px]); 233 } 234 235 /* Get cell at relative position (for writing). */ 236 struct grid_cell * 237 grid_get_cell(struct grid *gd, u_int px, u_int py) 238 { 239 if (grid_check_x(gd, px) != 0) 240 return (NULL); 241 if (grid_check_y(gd, py) != 0) 242 return (NULL); 243 244 grid_expand_line(gd, py, px + 1); 245 return (&gd->linedata[py].celldata[px]); 246 } 247 248 /* Set cell at relative position. */ 249 void 250 grid_set_cell( 251 struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) 252 { 253 if (grid_check_x(gd, px) != 0) 254 return; 255 if (grid_check_y(gd, py) != 0) 256 return; 257 258 grid_expand_line(gd, py, px + 1); 259 grid_put_cell(gd, px, py, gc); 260 } 261 262 /* Get UTF-8 for reading. */ 263 const struct grid_utf8 * 264 grid_peek_utf8(struct grid *gd, u_int px, u_int py) 265 { 266 if (grid_check_x(gd, px) != 0) 267 return (NULL); 268 if (grid_check_y(gd, py) != 0) 269 return (NULL); 270 271 if (px >= gd->linedata[py].utf8size) 272 return (NULL); 273 return (&gd->linedata[py].utf8data[px]); 274 } 275 276 /* Get utf8 at relative position (for writing). */ 277 struct grid_utf8 * 278 grid_get_utf8(struct grid *gd, u_int px, u_int py) 279 { 280 if (grid_check_x(gd, px) != 0) 281 return (NULL); 282 if (grid_check_y(gd, py) != 0) 283 return (NULL); 284 285 grid_expand_line_utf8(gd, py, px + 1); 286 return (&gd->linedata[py].utf8data[px]); 287 } 288 289 /* Set utf8 at relative position. */ 290 void 291 grid_set_utf8( 292 struct grid *gd, u_int px, u_int py, const struct grid_utf8 *gc) 293 { 294 if (grid_check_x(gd, px) != 0) 295 return; 296 if (grid_check_y(gd, py) != 0) 297 return; 298 299 grid_expand_line_utf8(gd, py, px + 1); 300 grid_put_utf8(gd, px, py, gc); 301 } 302 303 /* Clear area. */ 304 void 305 grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny) 306 { 307 u_int xx, yy; 308 309 GRID_DEBUG(gd, "px=%u, py=%u, nx=%u, ny=%u", px, py, nx, ny); 310 311 if (nx == 0 || ny == 0) 312 return; 313 314 if (px == 0 && nx == gd->sx) { 315 grid_clear_lines(gd, py, ny); 316 return; 317 } 318 319 if (grid_check_x(gd, px) != 0) 320 return; 321 if (grid_check_x(gd, px + nx - 1) != 0) 322 return; 323 if (grid_check_y(gd, py) != 0) 324 return; 325 if (grid_check_y(gd, py + ny - 1) != 0) 326 return; 327 328 for (yy = py; yy < py + ny; yy++) { 329 if (px >= gd->linedata[yy].cellsize) 330 continue; 331 if (px + nx >= gd->linedata[yy].cellsize) { 332 gd->linedata[yy].cellsize = px; 333 continue; 334 } 335 for (xx = px; xx < px + nx; xx++) { 336 if (xx >= gd->linedata[yy].cellsize) 337 break; 338 grid_put_cell(gd, xx, yy, &grid_default_cell); 339 } 340 } 341 } 342 343 /* Clear lines. This just frees and truncates the lines. */ 344 void 345 grid_clear_lines(struct grid *gd, u_int py, u_int ny) 346 { 347 struct grid_line *gl; 348 u_int yy; 349 350 GRID_DEBUG(gd, "py=%u, ny=%u", py, ny); 351 352 if (ny == 0) 353 return; 354 355 if (grid_check_y(gd, py) != 0) 356 return; 357 if (grid_check_y(gd, py + ny - 1) != 0) 358 return; 359 360 for (yy = py; yy < py + ny; yy++) { 361 gl = &gd->linedata[yy]; 362 if (gl->celldata != NULL) 363 xfree(gl->celldata); 364 if (gl->utf8data != NULL) 365 xfree(gl->utf8data); 366 memset(gl, 0, sizeof *gl); 367 } 368 } 369 370 /* Move a group of lines. */ 371 void 372 grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny) 373 { 374 u_int yy; 375 376 GRID_DEBUG(gd, "dy=%u, py=%u, ny=%u", dy, py, ny); 377 378 if (ny == 0 || py == dy) 379 return; 380 381 if (grid_check_y(gd, py) != 0) 382 return; 383 if (grid_check_y(gd, py + ny - 1) != 0) 384 return; 385 if (grid_check_y(gd, dy) != 0) 386 return; 387 if (grid_check_y(gd, dy + ny - 1) != 0) 388 return; 389 390 /* Free any lines which are being replaced. */ 391 for (yy = dy; yy < dy + ny; yy++) { 392 if (yy >= py && yy < py + ny) 393 continue; 394 grid_clear_lines(gd, yy, 1); 395 } 396 397 memmove( 398 &gd->linedata[dy], &gd->linedata[py], ny * (sizeof *gd->linedata)); 399 400 /* Wipe any lines that have been moved (without freeing them). */ 401 for (yy = py; yy < py + ny; yy++) { 402 if (yy >= dy && yy < dy + ny) 403 continue; 404 memset(&gd->linedata[yy], 0, sizeof gd->linedata[yy]); 405 } 406 } 407 408 /* Move a group of cells. */ 409 void 410 grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx) 411 { 412 struct grid_line *gl; 413 u_int xx; 414 415 GRID_DEBUG(gd, "dx=%u, px=%u, py=%u, nx=%u", dx, px, py, nx); 416 417 if (nx == 0 || px == dx) 418 return; 419 420 if (grid_check_x(gd, px) != 0) 421 return; 422 if (grid_check_x(gd, px + nx - 1) != 0) 423 return; 424 if (grid_check_x(gd, dx + nx - 1) != 0) 425 return; 426 if (grid_check_y(gd, py) != 0) 427 return; 428 gl = &gd->linedata[py]; 429 430 grid_expand_line(gd, py, px + nx); 431 grid_expand_line(gd, py, dx + nx); 432 memmove( 433 &gl->celldata[dx], &gl->celldata[px], nx * sizeof *gl->celldata); 434 435 if (gl->utf8data != NULL) { 436 grid_expand_line_utf8(gd, py, px + nx); 437 grid_expand_line_utf8(gd, py, dx + nx); 438 memmove(&gl->utf8data[dx], 439 &gl->utf8data[px], nx * sizeof *gl->utf8data); 440 } 441 442 /* Wipe any cells that have been moved. */ 443 for (xx = px; xx < px + nx; xx++) { 444 if (xx >= dx && xx < dx + nx) 445 continue; 446 grid_put_cell(gd, xx, py, &grid_default_cell); 447 } 448 } 449 450 /* Convert cells into a string. */ 451 char * 452 grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) 453 { 454 const struct grid_cell *gc; 455 const struct grid_utf8 *gu; 456 char *buf; 457 size_t len, off; 458 u_int xx, i; 459 460 GRID_DEBUG(gd, "px=%u, py=%u, nx=%u", px, py, nx); 461 462 len = 128; 463 buf = xmalloc(len); 464 off = 0; 465 466 for (xx = px; xx < px + nx; xx++) { 467 gc = grid_peek_cell(gd, xx, py); 468 if (gc->flags & GRID_FLAG_PADDING) 469 continue; 470 471 if (gc->flags & GRID_FLAG_UTF8) { 472 while (len < off + UTF8_SIZE + 1) { 473 buf = xrealloc(buf, 2, len); 474 len *= 2; 475 } 476 477 gu = grid_peek_utf8(gd, xx, py); 478 for (i = 0; i < UTF8_SIZE; i++) { 479 if (gu->data[i] == 0xff) 480 break; 481 buf[off++] = gu->data[i]; 482 } 483 } else { 484 while (len < off + 2) { 485 buf = xrealloc(buf, 2, len); 486 len *= 2; 487 } 488 489 buf[off++] = gc->data; 490 } 491 } 492 493 while (off > 0 && buf[off - 1] == ' ') 494 off--; 495 buf[off] = '\0'; 496 return (buf); 497 } 498 499 /* 500 * Duplicate a set of lines between two grids. If there aren't enough lines in 501 * either source or destination, the number of lines is limited to the number 502 * available. 503 */ 504 void 505 grid_duplicate_lines( 506 struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny) 507 { 508 struct grid_line *dstl, *srcl; 509 u_int yy; 510 511 GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny); 512 513 if (dy + ny > dst->hsize + dst->sy) 514 ny = dst->hsize + dst->sy - dy; 515 if (sy + ny > src->hsize + src->sy) 516 ny = src->hsize + src->sy - sy; 517 grid_clear_lines(dst, dy, ny); 518 519 for (yy = 0; yy < ny; yy++) { 520 srcl = &src->linedata[sy]; 521 dstl = &dst->linedata[dy]; 522 523 memcpy(dstl, srcl, sizeof *dstl); 524 if (srcl->cellsize != 0) { 525 dstl->celldata = xcalloc( 526 srcl->cellsize, sizeof *dstl->celldata); 527 memcpy(dstl->celldata, srcl->celldata, 528 srcl->cellsize * sizeof *dstl->celldata); 529 } 530 if (srcl->utf8size != 0) { 531 dstl->utf8data = xcalloc( 532 srcl->utf8size, sizeof *dstl->utf8data); 533 memcpy(dstl->utf8data, srcl->utf8data, 534 srcl->utf8size * sizeof *dstl->utf8data); 535 } 536 537 sy++; 538 dy++; 539 } 540 } 541