1*61e9d0deSnicm /* $OpenBSD: grid.c,v 1.135 2024/11/20 20:54:02 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 498ca8272Snicm * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 20311827fbSnicm 217d053cf9Snicm #include <stdlib.h> 22311827fbSnicm #include <string.h> 23311827fbSnicm 24311827fbSnicm #include "tmux.h" 25311827fbSnicm 26311827fbSnicm /* 27311827fbSnicm * Grid data. This is the basic data structure that represents what is shown on 28311827fbSnicm * screen. 29311827fbSnicm * 30311827fbSnicm * A grid is a grid of cells (struct grid_cell). Lines are not allocated until 31311827fbSnicm * cells in that line are written to. The grid is split into history and 32311827fbSnicm * viewable data with the history starting at row (line) 0 and extending to 33311827fbSnicm * (hsize - 1); from hsize to hsize + (sy - 1) is the viewable data. All 34311827fbSnicm * functions in this file work on absolute coordinates, grid-view.c has 35311827fbSnicm * functions which work on the screen data. 36311827fbSnicm */ 37311827fbSnicm 38311827fbSnicm /* Default grid cell data. */ 39e931849fSnicm const struct grid_cell grid_default_cell = { 40f353bcb0Snicm { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 8, 0 41e931849fSnicm }; 42d1450c2bSnicm 4371a06389Snicm /* 4471a06389Snicm * Padding grid cell data. Padding cells are the only zero width cell that 4571a06389Snicm * appears in the grid - because of this, they are always extended cells. 4671a06389Snicm */ 4771a06389Snicm static const struct grid_cell grid_padding_cell = { 48f353bcb0Snicm { { '!' }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 8, 8, 8, 0 4971a06389Snicm }; 5071a06389Snicm 51d1450c2bSnicm /* Cleared grid cell data. */ 5271a06389Snicm static const struct grid_cell grid_cleared_cell = { 53f353bcb0Snicm { { ' ' }, 0, 1, 1 }, 0, GRID_FLAG_CLEARED, 8, 8, 8, 0 54d1450c2bSnicm }; 55d1450c2bSnicm static const struct grid_cell_entry grid_cleared_entry = { 564b9d1c52Stb { .data = { 0, 8, 8, ' ' } }, GRID_FLAG_CLEARED 57e931849fSnicm }; 58311827fbSnicm 599c8da6a8Snicm /* Store cell in entry. */ 609c8da6a8Snicm static void 619c8da6a8Snicm grid_store_cell(struct grid_cell_entry *gce, const struct grid_cell *gc, 629c8da6a8Snicm u_char c) 639c8da6a8Snicm { 64d1450c2bSnicm gce->flags = (gc->flags & ~GRID_FLAG_CLEARED); 659c8da6a8Snicm 669c8da6a8Snicm gce->data.fg = gc->fg & 0xff; 679c8da6a8Snicm if (gc->fg & COLOUR_FLAG_256) 689c8da6a8Snicm gce->flags |= GRID_FLAG_FG256; 699c8da6a8Snicm 709c8da6a8Snicm gce->data.bg = gc->bg & 0xff; 719c8da6a8Snicm if (gc->bg & COLOUR_FLAG_256) 729c8da6a8Snicm gce->flags |= GRID_FLAG_BG256; 739c8da6a8Snicm 749c8da6a8Snicm gce->data.attr = gc->attr; 759c8da6a8Snicm gce->data.data = c; 769c8da6a8Snicm } 779c8da6a8Snicm 78d1450c2bSnicm /* Check if a cell should be an extended cell. */ 79d23e027aSnicm static int 80d23e027aSnicm grid_need_extended_cell(const struct grid_cell_entry *gce, 81d23e027aSnicm const struct grid_cell *gc) 82d23e027aSnicm { 83d23e027aSnicm if (gce->flags & GRID_FLAG_EXTENDED) 84d23e027aSnicm return (1); 851779e050Snicm if (gc->attr > 0xff) 861779e050Snicm return (1); 870663b627Snicm if (gc->data.size > 1 || gc->data.width > 1) 88d23e027aSnicm return (1); 89d23e027aSnicm if ((gc->fg & COLOUR_FLAG_RGB) || (gc->bg & COLOUR_FLAG_RGB)) 90d23e027aSnicm return (1); 9134568295Snicm if (gc->us != 8) /* only supports 256 or RGB */ 92bc174b93Snicm return (1); 932df6775cSnicm if (gc->link != 0) 942df6775cSnicm return (1); 9502d75531Snicm if (gc->flags & GRID_FLAG_TAB) 9602d75531Snicm return (1); 97d23e027aSnicm return (0); 98d23e027aSnicm } 99d23e027aSnicm 100d1450c2bSnicm /* Get an extended cell. */ 101d1450c2bSnicm static void 102d1450c2bSnicm grid_get_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, 103d1450c2bSnicm int flags) 104d1450c2bSnicm { 105d1450c2bSnicm u_int at = gl->extdsize + 1; 106d1450c2bSnicm 107d1450c2bSnicm gl->extddata = xreallocarray(gl->extddata, at, sizeof *gl->extddata); 108d1450c2bSnicm gl->extdsize = at; 109d1450c2bSnicm 110d1450c2bSnicm gce->offset = at - 1; 111d1450c2bSnicm gce->flags = (flags | GRID_FLAG_EXTENDED); 112d1450c2bSnicm } 113d1450c2bSnicm 114d1450c2bSnicm /* Set cell as extended. */ 1155832c8deSnicm static struct grid_extd_entry * 116d1450c2bSnicm grid_extended_cell(struct grid_line *gl, struct grid_cell_entry *gce, 117d1450c2bSnicm const struct grid_cell *gc) 118d1450c2bSnicm { 1195832c8deSnicm struct grid_extd_entry *gee; 120d1450c2bSnicm int flags = (gc->flags & ~GRID_FLAG_CLEARED); 121d21ef8e0Snicm utf8_char uc; 122d1450c2bSnicm 123d1450c2bSnicm if (~gce->flags & GRID_FLAG_EXTENDED) 124d1450c2bSnicm grid_get_extended_cell(gl, gce, flags); 125d1450c2bSnicm else if (gce->offset >= gl->extdsize) 126d1450c2bSnicm fatalx("offset too big"); 127d1450c2bSnicm gl->flags |= GRID_LINE_EXTENDED; 128d1450c2bSnicm 12902d75531Snicm if (gc->flags & GRID_FLAG_TAB) 13002d75531Snicm uc = gc->data.width; 13102d75531Snicm else 132d21ef8e0Snicm utf8_from_data(&gc->data, &uc); 133d21ef8e0Snicm 1345832c8deSnicm gee = &gl->extddata[gce->offset]; 135d21ef8e0Snicm gee->data = uc; 1365832c8deSnicm gee->attr = gc->attr; 1375832c8deSnicm gee->flags = flags; 1385832c8deSnicm gee->fg = gc->fg; 1395832c8deSnicm gee->bg = gc->bg; 1405832c8deSnicm gee->us = gc->us; 1412df6775cSnicm gee->link = gc->link; 1425832c8deSnicm return (gee); 143d1450c2bSnicm } 144d1450c2bSnicm 14505e39634Snicm /* Free up unused extended cells. */ 14605e39634Snicm static void 14705e39634Snicm grid_compact_line(struct grid_line *gl) 14805e39634Snicm { 14905e39634Snicm int new_extdsize = 0; 1505832c8deSnicm struct grid_extd_entry *new_extddata; 15105e39634Snicm struct grid_cell_entry *gce; 1525832c8deSnicm struct grid_extd_entry *gee; 15305e39634Snicm u_int px, idx; 15405e39634Snicm 15505e39634Snicm if (gl->extdsize == 0) 15605e39634Snicm return; 15705e39634Snicm 15805e39634Snicm for (px = 0; px < gl->cellsize; px++) { 15905e39634Snicm gce = &gl->celldata[px]; 16005e39634Snicm if (gce->flags & GRID_FLAG_EXTENDED) 16105e39634Snicm new_extdsize++; 16205e39634Snicm } 16305e39634Snicm 16405e39634Snicm if (new_extdsize == 0) { 16505e39634Snicm free(gl->extddata); 16605e39634Snicm gl->extddata = NULL; 16705e39634Snicm gl->extdsize = 0; 16805e39634Snicm return; 16905e39634Snicm } 17005e39634Snicm new_extddata = xreallocarray(NULL, new_extdsize, sizeof *gl->extddata); 17105e39634Snicm 17205e39634Snicm idx = 0; 17305e39634Snicm for (px = 0; px < gl->cellsize; px++) { 17405e39634Snicm gce = &gl->celldata[px]; 17505e39634Snicm if (gce->flags & GRID_FLAG_EXTENDED) { 1765832c8deSnicm gee = &gl->extddata[gce->offset]; 1775832c8deSnicm memcpy(&new_extddata[idx], gee, sizeof *gee); 17805e39634Snicm gce->offset = idx++; 17905e39634Snicm } 18005e39634Snicm } 18105e39634Snicm 18205e39634Snicm free(gl->extddata); 18305e39634Snicm gl->extddata = new_extddata; 18405e39634Snicm gl->extdsize = new_extdsize; 18505e39634Snicm } 18605e39634Snicm 187540a299fSnicm /* Get line data. */ 1887621e88fSnicm struct grid_line * 1897621e88fSnicm grid_get_line(struct grid *gd, u_int line) 1907621e88fSnicm { 1917621e88fSnicm return (&gd->linedata[line]); 1927621e88fSnicm } 1937621e88fSnicm 194540a299fSnicm /* Adjust number of lines. */ 1957621e88fSnicm void 1967621e88fSnicm grid_adjust_lines(struct grid *gd, u_int lines) 1977621e88fSnicm { 1987621e88fSnicm gd->linedata = xreallocarray(gd->linedata, lines, sizeof *gd->linedata); 1997621e88fSnicm } 2007621e88fSnicm 201e931849fSnicm /* Copy default into a cell. */ 202e931849fSnicm static void 203ae4624e7Snicm grid_clear_cell(struct grid *gd, u_int px, u_int py, u_int bg) 204e931849fSnicm { 2052d51ea10Snicm struct grid_line *gl = &gd->linedata[py]; 2062d51ea10Snicm struct grid_cell_entry *gce = &gl->celldata[px]; 2075832c8deSnicm struct grid_extd_entry *gee; 2082d51ea10Snicm 209d1450c2bSnicm memcpy(gce, &grid_cleared_entry, sizeof *gce); 21026a51baaSnicm if (bg != 8) { 2112d51ea10Snicm if (bg & COLOUR_FLAG_RGB) { 212d1450c2bSnicm grid_get_extended_cell(gl, gce, gce->flags); 2135832c8deSnicm gee = grid_extended_cell(gl, gce, &grid_cleared_cell); 2145832c8deSnicm gee->bg = bg; 2152d51ea10Snicm } else { 2162d51ea10Snicm if (bg & COLOUR_FLAG_256) 2172d51ea10Snicm gce->flags |= GRID_FLAG_BG256; 2182d51ea10Snicm gce->data.bg = bg; 2192d51ea10Snicm } 220e931849fSnicm } 22126a51baaSnicm } 222e931849fSnicm 2239d6f50a6Snicm /* Check grid y position. */ 2245fd00eceSnicm static int 22533af54cfSnicm grid_check_y(struct grid *gd, const char *from, u_int py) 226311827fbSnicm { 22763c9949dSnicm if (py >= gd->hsize + gd->sy) { 22833af54cfSnicm log_debug("%s: y out of range: %u", from, py); 229311827fbSnicm return (-1); 230311827fbSnicm } 231311827fbSnicm return (0); 232311827fbSnicm } 23344c84f9eSnicm 234bf46d0ceSnicm /* Check if two styles are (visibly) the same. */ 235bf46d0ceSnicm int 236bf46d0ceSnicm grid_cells_look_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) 237bf46d0ceSnicm { 238c851f995Snicm int flags1 = gc1->flags, flags2 = gc2->flags;; 239c851f995Snicm 240bf46d0ceSnicm if (gc1->fg != gc2->fg || gc1->bg != gc2->bg) 241bf46d0ceSnicm return (0); 242c851f995Snicm if (gc1->attr != gc2->attr) 243c851f995Snicm return (0); 244c851f995Snicm if ((flags1 & ~GRID_FLAG_CLEARED) != (flags2 & ~GRID_FLAG_CLEARED)) 245bf46d0ceSnicm return (0); 2462df6775cSnicm if (gc1->link != gc2->link) 2472df6775cSnicm return (0); 248bf46d0ceSnicm return (1); 249bf46d0ceSnicm } 250bf46d0ceSnicm 2515fd00eceSnicm /* Compare grid cells. Return 1 if equal, 0 if not. */ 2525fd00eceSnicm int 253bf46d0ceSnicm grid_cells_equal(const struct grid_cell *gc1, const struct grid_cell *gc2) 2545fd00eceSnicm { 255bf46d0ceSnicm if (!grid_cells_look_equal(gc1, gc2)) 2565fd00eceSnicm return (0); 257bf46d0ceSnicm if (gc1->data.width != gc2->data.width) 2585fd00eceSnicm return (0); 259bf46d0ceSnicm if (gc1->data.size != gc2->data.size) 2605fd00eceSnicm return (0); 261bf46d0ceSnicm return (memcmp(gc1->data.data, gc2->data.data, gc1->data.size) == 0); 2625fd00eceSnicm } 2635fd00eceSnicm 26402d75531Snicm /* Set grid cell to a tab. */ 26502d75531Snicm void 26602d75531Snicm grid_set_tab(struct grid_cell *gc, u_int width) 26702d75531Snicm { 268c851f995Snicm memset(gc->data.data, 0, sizeof gc->data.data); 26902d75531Snicm gc->flags |= GRID_FLAG_TAB; 27002d75531Snicm gc->data.width = gc->data.size = gc->data.have = width; 271c851f995Snicm memset(gc->data.data, ' ', gc->data.size); 27202d75531Snicm } 27302d75531Snicm 274bd28ed35Snicm /* Free one line. */ 275bd28ed35Snicm static void 276bd28ed35Snicm grid_free_line(struct grid *gd, u_int py) 277bd28ed35Snicm { 278bd28ed35Snicm free(gd->linedata[py].celldata); 279bd28ed35Snicm gd->linedata[py].celldata = NULL; 280bd28ed35Snicm free(gd->linedata[py].extddata); 281bd28ed35Snicm gd->linedata[py].extddata = NULL; 282bd28ed35Snicm } 283bd28ed35Snicm 284bd28ed35Snicm /* Free several lines. */ 285bd28ed35Snicm static void 286bd28ed35Snicm grid_free_lines(struct grid *gd, u_int py, u_int ny) 287bd28ed35Snicm { 288bd28ed35Snicm u_int yy; 289bd28ed35Snicm 290bd28ed35Snicm for (yy = py; yy < py + ny; yy++) 291bd28ed35Snicm grid_free_line(gd, yy); 292bd28ed35Snicm } 293bd28ed35Snicm 294311827fbSnicm /* Create a new grid. */ 295311827fbSnicm struct grid * 296311827fbSnicm grid_create(u_int sx, u_int sy, u_int hlimit) 297311827fbSnicm { 298311827fbSnicm struct grid *gd; 299311827fbSnicm 300311827fbSnicm gd = xmalloc(sizeof *gd); 301311827fbSnicm gd->sx = sx; 302311827fbSnicm gd->sy = sy; 303311827fbSnicm 3044bf7840fSnicm if (hlimit != 0) 305953d773cSnicm gd->flags = GRID_HISTORY; 3064bf7840fSnicm else 3074bf7840fSnicm gd->flags = 0; 308953d773cSnicm 30993ccd604Snicm gd->hscrolled = 0; 310311827fbSnicm gd->hsize = 0; 311311827fbSnicm gd->hlimit = hlimit; 312311827fbSnicm 313f2ca94d1Snicm if (gd->sy != 0) 3149554a682Snicm gd->linedata = xcalloc(gd->sy, sizeof *gd->linedata); 315f2ca94d1Snicm else 316f2ca94d1Snicm gd->linedata = NULL; 317311827fbSnicm 318311827fbSnicm return (gd); 319311827fbSnicm } 320311827fbSnicm 321311827fbSnicm /* Destroy grid. */ 322311827fbSnicm void 323311827fbSnicm grid_destroy(struct grid *gd) 324311827fbSnicm { 325bd28ed35Snicm grid_free_lines(gd, 0, gd->hsize + gd->sy); 326311827fbSnicm 3277d053cf9Snicm free(gd->linedata); 328311827fbSnicm 3297d053cf9Snicm free(gd); 330311827fbSnicm } 331311827fbSnicm 332311827fbSnicm /* Compare grids. */ 333311827fbSnicm int 334311827fbSnicm grid_compare(struct grid *ga, struct grid *gb) 335311827fbSnicm { 3369554a682Snicm struct grid_line *gla, *glb; 337e931849fSnicm struct grid_cell gca, gcb; 338311827fbSnicm u_int xx, yy; 339311827fbSnicm 340b5cbd9e8Snicm if (ga->sx != gb->sx || ga->sy != gb->sy) 341311827fbSnicm return (1); 342311827fbSnicm 343311827fbSnicm for (yy = 0; yy < ga->sy; yy++) { 3449554a682Snicm gla = &ga->linedata[yy]; 3459554a682Snicm glb = &gb->linedata[yy]; 3469554a682Snicm if (gla->cellsize != glb->cellsize) 347311827fbSnicm return (1); 348e931849fSnicm for (xx = 0; xx < gla->cellsize; xx++) { 349e931849fSnicm grid_get_cell(ga, xx, yy, &gca); 350e931849fSnicm grid_get_cell(gb, xx, yy, &gcb); 3515fd00eceSnicm if (!grid_cells_equal(&gca, &gcb)) 352311827fbSnicm return (1); 353311827fbSnicm } 354311827fbSnicm } 355311827fbSnicm 356311827fbSnicm return (0); 357311827fbSnicm } 358311827fbSnicm 35991c2caa4Snicm /* Trim lines from the history. */ 36091c2caa4Snicm static void 36191c2caa4Snicm grid_trim_history(struct grid *gd, u_int ny) 36291c2caa4Snicm { 36391c2caa4Snicm grid_free_lines(gd, 0, ny); 36491c2caa4Snicm memmove(&gd->linedata[0], &gd->linedata[ny], 36591c2caa4Snicm (gd->hsize + gd->sy - ny) * (sizeof *gd->linedata)); 36691c2caa4Snicm } 36791c2caa4Snicm 368b3cd1016Snicm /* 369b3cd1016Snicm * Collect lines from the history if at the limit. Free the top (oldest) 10% 370b3cd1016Snicm * and shift up. 371b3cd1016Snicm */ 372311827fbSnicm void 373bd28ed35Snicm grid_collect_history(struct grid *gd) 374311827fbSnicm { 375bd28ed35Snicm u_int ny; 376311827fbSnicm 377d5f2217bSnicm if (gd->hsize == 0 || gd->hsize < gd->hlimit) 378b3cd1016Snicm return; 379b3cd1016Snicm 380bd28ed35Snicm ny = gd->hlimit / 10; 381bd28ed35Snicm if (ny < 1) 382bd28ed35Snicm ny = 1; 383d5f2217bSnicm if (ny > gd->hsize) 384d5f2217bSnicm ny = gd->hsize; 385311827fbSnicm 386bd28ed35Snicm /* 387bd28ed35Snicm * Free the lines from 0 to ny then move the remaining lines over 388bd28ed35Snicm * them. 389bd28ed35Snicm */ 39091c2caa4Snicm grid_trim_history(gd, ny); 391bd28ed35Snicm 392bd28ed35Snicm gd->hsize -= ny; 39393ccd604Snicm if (gd->hscrolled > gd->hsize) 39493ccd604Snicm gd->hscrolled = gd->hsize; 395311827fbSnicm } 396311827fbSnicm 397a0a1bba7Snicm /* Remove lines from the bottom of the history. */ 398a0a1bba7Snicm void 399a0a1bba7Snicm grid_remove_history(struct grid *gd, u_int ny) 400a0a1bba7Snicm { 401a0a1bba7Snicm u_int yy; 402a0a1bba7Snicm 403a0a1bba7Snicm if (ny > gd->hsize) 404a0a1bba7Snicm return; 405a0a1bba7Snicm for (yy = 0; yy < ny; yy++) 406a0a1bba7Snicm grid_free_line(gd, gd->hsize + gd->sy - 1 - yy); 407a0a1bba7Snicm gd->hsize -= ny; 408a0a1bba7Snicm } 409a0a1bba7Snicm 410b3cd1016Snicm /* 411b3cd1016Snicm * Scroll the entire visible screen, moving one line into the history. Just 412b3cd1016Snicm * allocate a new line at the bottom and move the history size indicator. 413b3cd1016Snicm */ 414b3cd1016Snicm void 415ae4624e7Snicm grid_scroll_history(struct grid *gd, u_int bg) 416b3cd1016Snicm { 417b3cd1016Snicm u_int yy; 418311827fbSnicm 419b3cd1016Snicm yy = gd->hsize + gd->sy; 42064cf113cSnicm gd->linedata = xreallocarray(gd->linedata, yy + 1, 42164cf113cSnicm sizeof *gd->linedata); 422ae4624e7Snicm grid_empty_line(gd, yy, bg); 423311827fbSnicm 42493ccd604Snicm gd->hscrolled++; 42505e39634Snicm grid_compact_line(&gd->linedata[gd->hsize]); 426a711f92aSnicm gd->linedata[gd->hsize].time = current_time; 427311827fbSnicm gd->hsize++; 428311827fbSnicm } 429311827fbSnicm 430ca085602Snicm /* Clear the history. */ 431ca085602Snicm void 432ca085602Snicm grid_clear_history(struct grid *gd) 433ca085602Snicm { 43491c2caa4Snicm grid_trim_history(gd, gd->hsize); 435ca085602Snicm 43693ccd604Snicm gd->hscrolled = 0; 437ca085602Snicm gd->hsize = 0; 43893ccd604Snicm 439ca085602Snicm gd->linedata = xreallocarray(gd->linedata, gd->sy, 440ca085602Snicm sizeof *gd->linedata); 441ca085602Snicm } 442ca085602Snicm 443b3cd1016Snicm /* Scroll a region up, moving the top line into the history. */ 444b3cd1016Snicm void 4456d5c64a0Snicm grid_scroll_history_region(struct grid *gd, u_int upper, u_int lower, u_int bg) 446b3cd1016Snicm { 4471350555bSnicm struct grid_line *gl_history, *gl_upper; 448b3cd1016Snicm u_int yy; 449b3cd1016Snicm 450b3cd1016Snicm /* Create a space for a new line. */ 451b3cd1016Snicm yy = gd->hsize + gd->sy; 45264cf113cSnicm gd->linedata = xreallocarray(gd->linedata, yy + 1, 45364cf113cSnicm sizeof *gd->linedata); 454b3cd1016Snicm 455b3cd1016Snicm /* Move the entire screen down to free a space for this line. */ 456b3cd1016Snicm gl_history = &gd->linedata[gd->hsize]; 457b3cd1016Snicm memmove(gl_history + 1, gl_history, gd->sy * sizeof *gl_history); 458b3cd1016Snicm 459b3cd1016Snicm /* Adjust the region and find its start and end. */ 460b3cd1016Snicm upper++; 461b3cd1016Snicm gl_upper = &gd->linedata[upper]; 462b3cd1016Snicm lower++; 463b3cd1016Snicm 464b3cd1016Snicm /* Move the line into the history. */ 465b3cd1016Snicm memcpy(gl_history, gl_upper, sizeof *gl_history); 466a711f92aSnicm gl_history->time = current_time; 467b3cd1016Snicm 468b3cd1016Snicm /* Then move the region up and clear the bottom line. */ 469b3cd1016Snicm memmove(gl_upper, gl_upper + 1, (lower - upper) * sizeof *gl_upper); 4706d5c64a0Snicm grid_empty_line(gd, lower, bg); 471b3cd1016Snicm 472b3cd1016Snicm /* Move the history offset down over the line. */ 47393ccd604Snicm gd->hscrolled++; 474b3cd1016Snicm gd->hsize++; 475b3cd1016Snicm } 476b3cd1016Snicm 477311827fbSnicm /* Expand line to fit to cell. */ 478ced21769Snicm static void 479ae4624e7Snicm grid_expand_line(struct grid *gd, u_int py, u_int sx, u_int bg) 480311827fbSnicm { 4819554a682Snicm struct grid_line *gl; 4826d39d31dSnicm u_int xx; 483311827fbSnicm 4849554a682Snicm gl = &gd->linedata[py]; 4856d39d31dSnicm if (sx <= gl->cellsize) 486311827fbSnicm return; 487311827fbSnicm 48826cd2289Snicm if (sx < gd->sx / 4) 48926cd2289Snicm sx = gd->sx / 4; 49026cd2289Snicm else if (sx < gd->sx / 2) 49126cd2289Snicm sx = gd->sx / 2; 49214b12344Snicm else if (gd->sx > sx) 493b68f401aSnicm sx = gd->sx; 494b68f401aSnicm 49564cf113cSnicm gl->celldata = xreallocarray(gl->celldata, sx, sizeof *gl->celldata); 4969554a682Snicm for (xx = gl->cellsize; xx < sx; xx++) 497ae4624e7Snicm grid_clear_cell(gd, xx, py, bg); 4989554a682Snicm gl->cellsize = sx; 499311827fbSnicm } 500311827fbSnicm 501ae4624e7Snicm /* Empty a line and set background colour if needed. */ 50275343b37Snicm void 503ae4624e7Snicm grid_empty_line(struct grid *gd, u_int py, u_int bg) 504ae4624e7Snicm { 505ae4624e7Snicm memset(&gd->linedata[py], 0, sizeof gd->linedata[py]); 506d78fd057Snicm if (!COLOUR_DEFAULT(bg)) 507ae4624e7Snicm grid_expand_line(gd, py, gd->sx, bg); 508ae4624e7Snicm } 509ae4624e7Snicm 510e5d24057Snicm /* Peek at grid line. */ 511e5d24057Snicm const struct grid_line * 512e5d24057Snicm grid_peek_line(struct grid *gd, u_int py) 513e5d24057Snicm { 51433af54cfSnicm if (grid_check_y(gd, __func__, py) != 0) 515e5d24057Snicm return (NULL); 516e5d24057Snicm return (&gd->linedata[py]); 517e5d24057Snicm } 518e5d24057Snicm 519414c61adSnicm /* Get cell from line. */ 520414c61adSnicm static void 521414c61adSnicm grid_get_cell1(struct grid_line *gl, u_int px, struct grid_cell *gc) 522311827fbSnicm { 523414c61adSnicm struct grid_cell_entry *gce = &gl->celldata[px]; 5245832c8deSnicm struct grid_extd_entry *gee; 525311827fbSnicm 526e931849fSnicm if (gce->flags & GRID_FLAG_EXTENDED) { 527e931849fSnicm if (gce->offset >= gl->extdsize) 528e931849fSnicm memcpy(gc, &grid_default_cell, sizeof *gc); 5295832c8deSnicm else { 5305832c8deSnicm gee = &gl->extddata[gce->offset]; 5315832c8deSnicm gc->flags = gee->flags; 5325832c8deSnicm gc->attr = gee->attr; 5335832c8deSnicm gc->fg = gee->fg; 5345832c8deSnicm gc->bg = gee->bg; 5355832c8deSnicm gc->us = gee->us; 5362df6775cSnicm gc->link = gee->link; 53702d75531Snicm 53802d75531Snicm if (gc->flags & GRID_FLAG_TAB) 53902d75531Snicm grid_set_tab(gc, gee->data); 54002d75531Snicm else 54170a57860Snicm utf8_to_data(gee->data, &gc->data); 5425832c8deSnicm } 543e931849fSnicm return; 544e931849fSnicm } 545e931849fSnicm 54692c661c5Snicm gc->flags = gce->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); 547e931849fSnicm gc->attr = gce->data.attr; 548e931849fSnicm gc->fg = gce->data.fg; 54992c661c5Snicm if (gce->flags & GRID_FLAG_FG256) 55092c661c5Snicm gc->fg |= COLOUR_FLAG_256; 551e931849fSnicm gc->bg = gce->data.bg; 55292c661c5Snicm if (gce->flags & GRID_FLAG_BG256) 55392c661c5Snicm gc->bg |= COLOUR_FLAG_256; 554f353bcb0Snicm gc->us = 8; 555e931849fSnicm utf8_set(&gc->data, gce->data.data); 5562df6775cSnicm gc->link = 0; 557311827fbSnicm } 558311827fbSnicm 559414c61adSnicm /* Get cell for reading. */ 560414c61adSnicm void 561414c61adSnicm grid_get_cell(struct grid *gd, u_int px, u_int py, struct grid_cell *gc) 562414c61adSnicm { 56333af54cfSnicm if (grid_check_y(gd, __func__, py) != 0 || 564610f37cfSnicm px >= gd->linedata[py].cellsize) 565414c61adSnicm memcpy(gc, &grid_default_cell, sizeof *gc); 566610f37cfSnicm else 567610f37cfSnicm grid_get_cell1(&gd->linedata[py], px, gc); 568414c61adSnicm } 569414c61adSnicm 57071a06389Snicm /* Set cell at position. */ 571311827fbSnicm void 572f7b15bf9Snicm grid_set_cell(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc) 573311827fbSnicm { 574e931849fSnicm struct grid_line *gl; 575e931849fSnicm struct grid_cell_entry *gce; 576e931849fSnicm 57733af54cfSnicm if (grid_check_y(gd, __func__, py) != 0) 578311827fbSnicm return; 579311827fbSnicm 580ae4624e7Snicm grid_expand_line(gd, py, px + 1, 8); 581e931849fSnicm 582e931849fSnicm gl = &gd->linedata[py]; 583ae4624e7Snicm if (px + 1 > gl->cellused) 584ae4624e7Snicm gl->cellused = px + 1; 585ae4624e7Snicm 5869c8da6a8Snicm gce = &gl->celldata[px]; 587d23e027aSnicm if (grid_need_extended_cell(gce, gc)) 5882d51ea10Snicm grid_extended_cell(gl, gce, gc); 5899c8da6a8Snicm else 5909c8da6a8Snicm grid_store_cell(gce, gc, gc->data.data[0]); 591311827fbSnicm } 592311827fbSnicm 59371a06389Snicm /* Set padding at position. */ 59471a06389Snicm void 59571a06389Snicm grid_set_padding(struct grid *gd, u_int px, u_int py) 59671a06389Snicm { 59771a06389Snicm grid_set_cell(gd, px, py, &grid_padding_cell); 59871a06389Snicm } 59971a06389Snicm 60071a06389Snicm /* Set cells at position. */ 6018248eab7Snicm void 6028248eab7Snicm grid_set_cells(struct grid *gd, u_int px, u_int py, const struct grid_cell *gc, 6038248eab7Snicm const char *s, size_t slen) 6048248eab7Snicm { 6058248eab7Snicm struct grid_line *gl; 6068248eab7Snicm struct grid_cell_entry *gce; 6075832c8deSnicm struct grid_extd_entry *gee; 6088248eab7Snicm u_int i; 6098248eab7Snicm 61033af54cfSnicm if (grid_check_y(gd, __func__, py) != 0) 6118248eab7Snicm return; 6128248eab7Snicm 6138248eab7Snicm grid_expand_line(gd, py, px + slen, 8); 6148248eab7Snicm 6158248eab7Snicm gl = &gd->linedata[py]; 6168248eab7Snicm if (px + slen > gl->cellused) 6178248eab7Snicm gl->cellused = px + slen; 6188248eab7Snicm 6198248eab7Snicm for (i = 0; i < slen; i++) { 6208248eab7Snicm gce = &gl->celldata[px + i]; 621d23e027aSnicm if (grid_need_extended_cell(gce, gc)) { 6225832c8deSnicm gee = grid_extended_cell(gl, gce, gc); 623a49f5513Snicm gee->data = utf8_build_one(s[i]); 6248248eab7Snicm } else 6258248eab7Snicm grid_store_cell(gce, gc, s[i]); 6268248eab7Snicm } 6278248eab7Snicm } 6288248eab7Snicm 6296d39d31dSnicm /* Clear area. */ 630311827fbSnicm void 631ae4624e7Snicm grid_clear(struct grid *gd, u_int px, u_int py, u_int nx, u_int ny, u_int bg) 632311827fbSnicm { 633d78fd057Snicm struct grid_line *gl; 63415e17edaSnicm u_int xx, yy, ox, sx; 635311827fbSnicm 636311827fbSnicm if (nx == 0 || ny == 0) 637311827fbSnicm return; 638311827fbSnicm 639311827fbSnicm if (px == 0 && nx == gd->sx) { 640ae4624e7Snicm grid_clear_lines(gd, py, ny, bg); 641311827fbSnicm return; 642311827fbSnicm } 643311827fbSnicm 64433af54cfSnicm if (grid_check_y(gd, __func__, py) != 0) 645311827fbSnicm return; 64633af54cfSnicm if (grid_check_y(gd, __func__, py + ny - 1) != 0) 647311827fbSnicm return; 648311827fbSnicm 649311827fbSnicm for (yy = py; yy < py + ny; yy++) { 650d78fd057Snicm gl = &gd->linedata[yy]; 65115e17edaSnicm 65215e17edaSnicm sx = gd->sx; 65315e17edaSnicm if (sx > gl->cellsize) 65415e17edaSnicm sx = gl->cellsize; 65515e17edaSnicm ox = nx; 65615e17edaSnicm if (COLOUR_DEFAULT(bg)) { 65715e17edaSnicm if (px > sx) 6586d39d31dSnicm continue; 65915e17edaSnicm if (px + nx > sx) 66015e17edaSnicm ox = sx - px; 6616d39d31dSnicm } 66215e17edaSnicm 66315e17edaSnicm grid_expand_line(gd, yy, px + ox, 8); /* default bg first */ 66415e17edaSnicm for (xx = px; xx < px + ox; xx++) 665ae4624e7Snicm grid_clear_cell(gd, xx, yy, bg); 666311827fbSnicm } 667311827fbSnicm } 668311827fbSnicm 669311827fbSnicm /* Clear lines. This just frees and truncates the lines. */ 670311827fbSnicm void 671ae4624e7Snicm grid_clear_lines(struct grid *gd, u_int py, u_int ny, u_int bg) 672311827fbSnicm { 673311827fbSnicm u_int yy; 674311827fbSnicm 675311827fbSnicm if (ny == 0) 676311827fbSnicm return; 677311827fbSnicm 67833af54cfSnicm if (grid_check_y(gd, __func__, py) != 0) 679311827fbSnicm return; 68033af54cfSnicm if (grid_check_y(gd, __func__, py + ny - 1) != 0) 681311827fbSnicm return; 682311827fbSnicm 683311827fbSnicm for (yy = py; yy < py + ny; yy++) { 684bd28ed35Snicm grid_free_line(gd, yy); 685ae4624e7Snicm grid_empty_line(gd, yy, bg); 686311827fbSnicm } 687f256eafcSnicm if (py != 0) 688f256eafcSnicm gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; 689311827fbSnicm } 690311827fbSnicm 691311827fbSnicm /* Move a group of lines. */ 692311827fbSnicm void 693ae4624e7Snicm grid_move_lines(struct grid *gd, u_int dy, u_int py, u_int ny, u_int bg) 694311827fbSnicm { 695311827fbSnicm u_int yy; 696311827fbSnicm 697311827fbSnicm if (ny == 0 || py == dy) 698311827fbSnicm return; 699311827fbSnicm 70033af54cfSnicm if (grid_check_y(gd, __func__, py) != 0) 701311827fbSnicm return; 70233af54cfSnicm if (grid_check_y(gd, __func__, py + ny - 1) != 0) 703311827fbSnicm return; 70433af54cfSnicm if (grid_check_y(gd, __func__, dy) != 0) 705311827fbSnicm return; 70633af54cfSnicm if (grid_check_y(gd, __func__, dy + ny - 1) != 0) 707311827fbSnicm return; 708311827fbSnicm 709311827fbSnicm /* Free any lines which are being replaced. */ 710311827fbSnicm for (yy = dy; yy < dy + ny; yy++) { 711311827fbSnicm if (yy >= py && yy < py + ny) 712311827fbSnicm continue; 713bd28ed35Snicm grid_free_line(gd, yy); 714311827fbSnicm } 715f256eafcSnicm if (dy != 0) 716f256eafcSnicm gd->linedata[dy - 1].flags &= ~GRID_LINE_WRAPPED; 717311827fbSnicm 718ca085602Snicm memmove(&gd->linedata[dy], &gd->linedata[py], 719ca085602Snicm ny * (sizeof *gd->linedata)); 720311827fbSnicm 721bd28ed35Snicm /* 722bd28ed35Snicm * Wipe any lines that have been moved (without freeing them - they are 723bd28ed35Snicm * still present). 724bd28ed35Snicm */ 725311827fbSnicm for (yy = py; yy < py + ny; yy++) { 726ae4624e7Snicm if (yy < dy || yy >= dy + ny) 727ae4624e7Snicm grid_empty_line(gd, yy, bg); 728311827fbSnicm } 729f256eafcSnicm if (py != 0 && (py < dy || py >= dy + ny)) 730f256eafcSnicm gd->linedata[py - 1].flags &= ~GRID_LINE_WRAPPED; 731311827fbSnicm } 732311827fbSnicm 733311827fbSnicm /* Move a group of cells. */ 734311827fbSnicm void 735ae4624e7Snicm grid_move_cells(struct grid *gd, u_int dx, u_int px, u_int py, u_int nx, 736ae4624e7Snicm u_int bg) 737311827fbSnicm { 7389554a682Snicm struct grid_line *gl; 739311827fbSnicm u_int xx; 740311827fbSnicm 741311827fbSnicm if (nx == 0 || px == dx) 742311827fbSnicm return; 743311827fbSnicm 74433af54cfSnicm if (grid_check_y(gd, __func__, py) != 0) 745311827fbSnicm return; 7469554a682Snicm gl = &gd->linedata[py]; 747311827fbSnicm 748ae4624e7Snicm grid_expand_line(gd, py, px + nx, 8); 749ae4624e7Snicm grid_expand_line(gd, py, dx + nx, 8); 750ca085602Snicm memmove(&gl->celldata[dx], &gl->celldata[px], 751ca085602Snicm nx * sizeof *gl->celldata); 752f6d00760Snicm if (dx + nx > gl->cellused) 753f6d00760Snicm gl->cellused = dx + nx; 754311827fbSnicm 755311827fbSnicm /* Wipe any cells that have been moved. */ 756311827fbSnicm for (xx = px; xx < px + nx; xx++) { 757311827fbSnicm if (xx >= dx && xx < dx + nx) 758311827fbSnicm continue; 759ae4624e7Snicm grid_clear_cell(gd, xx, py, bg); 760311827fbSnicm } 761311827fbSnicm } 762b6d61ae9Snicm 76317472193Snicm /* Get ANSI foreground sequence. */ 7649883b791Snicm static size_t 76517472193Snicm grid_string_cells_fg(const struct grid_cell *gc, int *values) 76617472193Snicm { 76717472193Snicm size_t n; 76892c661c5Snicm u_char r, g, b; 76917472193Snicm 77017472193Snicm n = 0; 77192c661c5Snicm if (gc->fg & COLOUR_FLAG_256) { 77217472193Snicm values[n++] = 38; 77317472193Snicm values[n++] = 5; 77492c661c5Snicm values[n++] = gc->fg & 0xff; 77592c661c5Snicm } else if (gc->fg & COLOUR_FLAG_RGB) { 776baec7426Snicm values[n++] = 38; 777baec7426Snicm values[n++] = 2; 77892c661c5Snicm colour_split_rgb(gc->fg, &r, &g, &b); 77992c661c5Snicm values[n++] = r; 78092c661c5Snicm values[n++] = g; 78192c661c5Snicm values[n++] = b; 78217472193Snicm } else { 78317472193Snicm switch (gc->fg) { 78417472193Snicm case 0: 78517472193Snicm case 1: 78617472193Snicm case 2: 78717472193Snicm case 3: 78817472193Snicm case 4: 78917472193Snicm case 5: 79017472193Snicm case 6: 79117472193Snicm case 7: 79217472193Snicm values[n++] = gc->fg + 30; 79317472193Snicm break; 79417472193Snicm case 8: 79517472193Snicm values[n++] = 39; 79617472193Snicm break; 79717472193Snicm case 90: 79817472193Snicm case 91: 79917472193Snicm case 92: 80017472193Snicm case 93: 80117472193Snicm case 94: 80217472193Snicm case 95: 80317472193Snicm case 96: 80417472193Snicm case 97: 80517472193Snicm values[n++] = gc->fg; 80617472193Snicm break; 80717472193Snicm } 80817472193Snicm } 80917472193Snicm return (n); 81017472193Snicm } 81117472193Snicm 81217472193Snicm /* Get ANSI background sequence. */ 8139883b791Snicm static size_t 81417472193Snicm grid_string_cells_bg(const struct grid_cell *gc, int *values) 81517472193Snicm { 81617472193Snicm size_t n; 81792c661c5Snicm u_char r, g, b; 81817472193Snicm 81917472193Snicm n = 0; 82092c661c5Snicm if (gc->bg & COLOUR_FLAG_256) { 82117472193Snicm values[n++] = 48; 82217472193Snicm values[n++] = 5; 82392c661c5Snicm values[n++] = gc->bg & 0xff; 82492c661c5Snicm } else if (gc->bg & COLOUR_FLAG_RGB) { 825baec7426Snicm values[n++] = 48; 826baec7426Snicm values[n++] = 2; 82792c661c5Snicm colour_split_rgb(gc->bg, &r, &g, &b); 82892c661c5Snicm values[n++] = r; 82992c661c5Snicm values[n++] = g; 83092c661c5Snicm values[n++] = b; 83117472193Snicm } else { 83217472193Snicm switch (gc->bg) { 83317472193Snicm case 0: 83417472193Snicm case 1: 83517472193Snicm case 2: 83617472193Snicm case 3: 83717472193Snicm case 4: 83817472193Snicm case 5: 83917472193Snicm case 6: 84017472193Snicm case 7: 84117472193Snicm values[n++] = gc->bg + 40; 84217472193Snicm break; 84317472193Snicm case 8: 84417472193Snicm values[n++] = 49; 84517472193Snicm break; 846b11ba489Snicm case 90: 847b11ba489Snicm case 91: 848b11ba489Snicm case 92: 849b11ba489Snicm case 93: 850b11ba489Snicm case 94: 851b11ba489Snicm case 95: 852b11ba489Snicm case 96: 853b11ba489Snicm case 97: 854b11ba489Snicm values[n++] = gc->bg + 10; 85517472193Snicm break; 85617472193Snicm } 85717472193Snicm } 85817472193Snicm return (n); 85917472193Snicm } 86017472193Snicm 861f2f3d50aSnicm /* Get underscore colour sequence. */ 862f2f3d50aSnicm static size_t 863f2f3d50aSnicm grid_string_cells_us(const struct grid_cell *gc, int *values) 864f2f3d50aSnicm { 865f2f3d50aSnicm size_t n; 866f2f3d50aSnicm u_char r, g, b; 867f2f3d50aSnicm 868f2f3d50aSnicm n = 0; 869f2f3d50aSnicm if (gc->us & COLOUR_FLAG_256) { 870f2f3d50aSnicm values[n++] = 58; 871f2f3d50aSnicm values[n++] = 5; 872f2f3d50aSnicm values[n++] = gc->us & 0xff; 873f2f3d50aSnicm } else if (gc->us & COLOUR_FLAG_RGB) { 874f2f3d50aSnicm values[n++] = 58; 875f2f3d50aSnicm values[n++] = 2; 876f2f3d50aSnicm colour_split_rgb(gc->us, &r, &g, &b); 877f2f3d50aSnicm values[n++] = r; 878f2f3d50aSnicm values[n++] = g; 879f2f3d50aSnicm values[n++] = b; 880f2f3d50aSnicm } 881f2f3d50aSnicm return (n); 882f2f3d50aSnicm } 883f2f3d50aSnicm 884f2f3d50aSnicm /* Add on SGR code. */ 885f2f3d50aSnicm static void 886f2f3d50aSnicm grid_string_cells_add_code(char *buf, size_t len, u_int n, int *s, int *newc, 88732b2c715Snicm int *oldc, size_t nnewc, size_t noldc, int flags) 888f2f3d50aSnicm { 889f2f3d50aSnicm u_int i; 890f2f3d50aSnicm char tmp[64]; 89132b2c715Snicm int reset = (n != 0 && s[0] == 0); 892f2f3d50aSnicm 89332b2c715Snicm if (nnewc == 0) 89432b2c715Snicm return; /* no code to add */ 89532b2c715Snicm if (!reset && 89632b2c715Snicm nnewc == noldc && 89732b2c715Snicm memcmp(newc, oldc, nnewc * sizeof newc[0]) == 0) 89832b2c715Snicm return; /* no reset and colour unchanged */ 89932b2c715Snicm if (reset && (newc[0] == 49 || newc[0] == 39)) 90032b2c715Snicm return; /* reset and colour default */ 90132b2c715Snicm 90232b2c715Snicm if (flags & GRID_STRING_ESCAPE_SEQUENCES) 903f2f3d50aSnicm strlcat(buf, "\\033[", len); 904f2f3d50aSnicm else 905f2f3d50aSnicm strlcat(buf, "\033[", len); 906f2f3d50aSnicm for (i = 0; i < nnewc; i++) { 907f2f3d50aSnicm if (i + 1 < nnewc) 908f2f3d50aSnicm xsnprintf(tmp, sizeof tmp, "%d;", newc[i]); 909f2f3d50aSnicm else 910f2f3d50aSnicm xsnprintf(tmp, sizeof tmp, "%d", newc[i]); 911f2f3d50aSnicm strlcat(buf, tmp, len); 912f2f3d50aSnicm } 913f2f3d50aSnicm strlcat(buf, "m", len); 914f2f3d50aSnicm } 915f2f3d50aSnicm 916e5878dccSnicm static int 917e5878dccSnicm grid_string_cells_add_hyperlink(char *buf, size_t len, const char *id, 91832b2c715Snicm const char *uri, int flags) 919e5878dccSnicm { 920e5878dccSnicm char *tmp; 921e5878dccSnicm 922e5878dccSnicm if (strlen(uri) + strlen(id) + 17 >= len) 923e5878dccSnicm return (0); 924e5878dccSnicm 92532b2c715Snicm if (flags & GRID_STRING_ESCAPE_SEQUENCES) 926e5878dccSnicm strlcat(buf, "\\033]8;", len); 927e5878dccSnicm else 928e5878dccSnicm strlcat(buf, "\033]8;", len); 929e5878dccSnicm if (*id != '\0') { 930e5878dccSnicm xasprintf(&tmp, "id=%s;", id); 931e5878dccSnicm strlcat(buf, tmp, len); 932e5878dccSnicm free(tmp); 933e5878dccSnicm } else 934e5878dccSnicm strlcat(buf, ";", len); 935e5878dccSnicm strlcat(buf, uri, len); 93632b2c715Snicm if (flags & GRID_STRING_ESCAPE_SEQUENCES) 937e5878dccSnicm strlcat(buf, "\\033\\\\", len); 938e5878dccSnicm else 939e5878dccSnicm strlcat(buf, "\033\\", len); 940e5878dccSnicm return (1); 941e5878dccSnicm } 942e5878dccSnicm 94317472193Snicm /* 94417472193Snicm * Returns ANSI code to set particular attributes (colour, bold and so on) 94515b7c10bSnicm * given a current state. 94617472193Snicm */ 9479883b791Snicm static void 94817472193Snicm grid_string_cells_code(const struct grid_cell *lastgc, 94932b2c715Snicm const struct grid_cell *gc, char *buf, size_t len, int flags, 950e5878dccSnicm struct screen *sc, int *has_link) 95117472193Snicm { 952baec7426Snicm int oldc[64], newc[64], s[128]; 95317472193Snicm size_t noldc, nnewc, n, i; 9540a64112dSnicm u_int attr = gc->attr, lastattr = lastgc->attr; 95517472193Snicm char tmp[64]; 956e5878dccSnicm const char *uri, *id; 95717472193Snicm 95832b2c715Snicm static const struct { 95917472193Snicm u_int mask; 96017472193Snicm u_int code; 96117472193Snicm } attrs[] = { 96217472193Snicm { GRID_ATTR_BRIGHT, 1 }, 96317472193Snicm { GRID_ATTR_DIM, 2 }, 96417472193Snicm { GRID_ATTR_ITALICS, 3 }, 96517472193Snicm { GRID_ATTR_UNDERSCORE, 4 }, 96617472193Snicm { GRID_ATTR_BLINK, 5 }, 96717472193Snicm { GRID_ATTR_REVERSE, 7 }, 9681779e050Snicm { GRID_ATTR_HIDDEN, 8 }, 96942caa339Snicm { GRID_ATTR_STRIKETHROUGH, 9 }, 97042caa339Snicm { GRID_ATTR_UNDERSCORE_2, 42 }, 97142caa339Snicm { GRID_ATTR_UNDERSCORE_3, 43 }, 97242caa339Snicm { GRID_ATTR_UNDERSCORE_4, 44 }, 97342caa339Snicm { GRID_ATTR_UNDERSCORE_5, 45 }, 974d37269efSnicm { GRID_ATTR_OVERLINE, 53 }, 97517472193Snicm }; 97617472193Snicm n = 0; 97717472193Snicm 97817472193Snicm /* If any attribute is removed, begin with 0. */ 97917472193Snicm for (i = 0; i < nitems(attrs); i++) { 980f2f3d50aSnicm if (((~attr & attrs[i].mask) && 981f2f3d50aSnicm (lastattr & attrs[i].mask)) || 982f353bcb0Snicm (lastgc->us != 8 && gc->us == 8)) { 98317472193Snicm s[n++] = 0; 984ee61d68bSnicm lastattr &= GRID_ATTR_CHARSET; 98517472193Snicm break; 98617472193Snicm } 98717472193Snicm } 98817472193Snicm /* For each attribute that is newly set, add its code. */ 98917472193Snicm for (i = 0; i < nitems(attrs); i++) { 99017472193Snicm if ((attr & attrs[i].mask) && !(lastattr & attrs[i].mask)) 99117472193Snicm s[n++] = attrs[i].code; 99217472193Snicm } 99317472193Snicm 9940a64112dSnicm /* Write the attributes. */ 99517472193Snicm *buf = '\0'; 99617472193Snicm if (n > 0) { 99732b2c715Snicm if (flags & GRID_STRING_ESCAPE_SEQUENCES) 998e5d24057Snicm strlcat(buf, "\\033[", len); 999e5d24057Snicm else 100017472193Snicm strlcat(buf, "\033[", len); 100117472193Snicm for (i = 0; i < n; i++) { 100242caa339Snicm if (s[i] < 10) 100317472193Snicm xsnprintf(tmp, sizeof tmp, "%d", s[i]); 100442caa339Snicm else { 100542caa339Snicm xsnprintf(tmp, sizeof tmp, "%d:%d", s[i] / 10, 100642caa339Snicm s[i] % 10); 100742caa339Snicm } 100817472193Snicm strlcat(buf, tmp, len); 100942caa339Snicm if (i + 1 < n) 101042caa339Snicm strlcat(buf, ";", len); 101117472193Snicm } 101217472193Snicm strlcat(buf, "m", len); 101317472193Snicm } 101417472193Snicm 10150a64112dSnicm /* If the foreground colour changed, write its parameters. */ 10160a64112dSnicm nnewc = grid_string_cells_fg(gc, newc); 10170a64112dSnicm noldc = grid_string_cells_fg(lastgc, oldc); 1018f2f3d50aSnicm grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, 101932b2c715Snicm flags); 10200a64112dSnicm 10210a64112dSnicm /* If the background colour changed, append its parameters. */ 10220a64112dSnicm nnewc = grid_string_cells_bg(gc, newc); 10230a64112dSnicm noldc = grid_string_cells_bg(lastgc, oldc); 1024f2f3d50aSnicm grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, 102532b2c715Snicm flags); 1026f2f3d50aSnicm 1027f2f3d50aSnicm /* If the underscore colour changed, append its parameters. */ 1028f2f3d50aSnicm nnewc = grid_string_cells_us(gc, newc); 1029f2f3d50aSnicm noldc = grid_string_cells_us(lastgc, oldc); 1030f2f3d50aSnicm grid_string_cells_add_code(buf, len, n, s, newc, oldc, nnewc, noldc, 103132b2c715Snicm flags); 10320a64112dSnicm 103317472193Snicm /* Append shift in/shift out if needed. */ 1034e5d24057Snicm if ((attr & GRID_ATTR_CHARSET) && !(lastattr & GRID_ATTR_CHARSET)) { 103532b2c715Snicm if (flags & GRID_STRING_ESCAPE_SEQUENCES) 1036e5d24057Snicm strlcat(buf, "\\016", len); /* SO */ 1037e5d24057Snicm else 103817472193Snicm strlcat(buf, "\016", len); /* SO */ 1039e5d24057Snicm } 1040e5d24057Snicm if (!(attr & GRID_ATTR_CHARSET) && (lastattr & GRID_ATTR_CHARSET)) { 104132b2c715Snicm if (flags & GRID_STRING_ESCAPE_SEQUENCES) 1042e5d24057Snicm strlcat(buf, "\\017", len); /* SI */ 1043e5d24057Snicm else 104417472193Snicm strlcat(buf, "\017", len); /* SI */ 104517472193Snicm } 1046e5878dccSnicm 1047e5878dccSnicm /* Add hyperlink if changed. */ 1048e5878dccSnicm if (sc != NULL && sc->hyperlinks != NULL && lastgc->link != gc->link) { 1049e5878dccSnicm if (hyperlinks_get(sc->hyperlinks, gc->link, &uri, &id, NULL)) { 1050e5878dccSnicm *has_link = grid_string_cells_add_hyperlink(buf, len, 105132b2c715Snicm id, uri, flags); 1052e5878dccSnicm } else if (*has_link) { 1053e5878dccSnicm grid_string_cells_add_hyperlink(buf, len, "", "", 105432b2c715Snicm flags); 1055e5878dccSnicm *has_link = 0; 1056e5878dccSnicm } 1057e5878dccSnicm } 1058e5d24057Snicm } 105917472193Snicm 1060b6d61ae9Snicm /* Convert cells into a string. */ 1061b6d61ae9Snicm char * 106217472193Snicm grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx, 106332b2c715Snicm struct grid_cell **lastgc, int flags, struct screen *s) 1064b6d61ae9Snicm { 1065e931849fSnicm struct grid_cell gc; 106617472193Snicm static struct grid_cell lastgc1; 1067e5d24057Snicm const char *data; 1068e5878dccSnicm char *buf, code[8192]; 1069e5d24057Snicm size_t len, off, size, codelen; 10706af87e9aSnicm u_int xx, end; 10716af87e9aSnicm int has_link = 0; 107224e5de37Snicm const struct grid_line *gl; 1073b6d61ae9Snicm 107419488e89Snicm if (lastgc != NULL && *lastgc == NULL) { 107517472193Snicm memcpy(&lastgc1, &grid_default_cell, sizeof lastgc1); 107617472193Snicm *lastgc = &lastgc1; 107717472193Snicm } 107817472193Snicm 1079b6d61ae9Snicm len = 128; 1080b6d61ae9Snicm buf = xmalloc(len); 1081b6d61ae9Snicm off = 0; 1082b6d61ae9Snicm 108324e5de37Snicm gl = grid_peek_line(gd, py); 108432b2c715Snicm if (flags & GRID_STRING_EMPTY_CELLS) 108532b2c715Snicm end = gl->cellsize; 108632b2c715Snicm else 108732b2c715Snicm end = gl->cellused; 1088b6d61ae9Snicm for (xx = px; xx < px + nx; xx++) { 108932b2c715Snicm if (gl == NULL || xx >= end) 109024e5de37Snicm break; 1091e931849fSnicm grid_get_cell(gd, xx, py, &gc); 1092e931849fSnicm if (gc.flags & GRID_FLAG_PADDING) 1093b6d61ae9Snicm continue; 1094b6d61ae9Snicm 109532b2c715Snicm if (flags & GRID_STRING_WITH_SEQUENCES) { 1096e931849fSnicm grid_string_cells_code(*lastgc, &gc, code, sizeof code, 109732b2c715Snicm flags, s, &has_link); 109817472193Snicm codelen = strlen(code); 1099e931849fSnicm memcpy(*lastgc, &gc, sizeof **lastgc); 110017472193Snicm } else 110117472193Snicm codelen = 0; 110217472193Snicm 110302d75531Snicm if (gc.flags & GRID_FLAG_TAB) { 110402d75531Snicm data = "\t"; 110502d75531Snicm size = 1; 110602d75531Snicm } else { 1107e931849fSnicm data = gc.data.data; 1108e931849fSnicm size = gc.data.size; 110932b2c715Snicm if ((flags & GRID_STRING_ESCAPE_SEQUENCES) && 111032b2c715Snicm size == 1 && 111132b2c715Snicm *data == '\\') { 1112aa2a679aSnicm data = "\\\\"; 1113e5d24057Snicm size = 2; 1114e5d24057Snicm } 111502d75531Snicm } 1116e5d24057Snicm 1117e5d24057Snicm while (len < off + size + codelen + 1) { 111864cf113cSnicm buf = xreallocarray(buf, 2, len); 1119b6d61ae9Snicm len *= 2; 1120b6d61ae9Snicm } 1121b6d61ae9Snicm 112217472193Snicm if (codelen != 0) { 112317472193Snicm memcpy(buf + off, code, codelen); 112417472193Snicm off += codelen; 112517472193Snicm } 1126e5d24057Snicm memcpy(buf + off, data, size); 1127e5d24057Snicm off += size; 1128b6d61ae9Snicm } 1129b6d61ae9Snicm 1130e5878dccSnicm if (has_link) { 1131e5878dccSnicm grid_string_cells_add_hyperlink(code, sizeof code, "", "", 113232b2c715Snicm flags); 1133e5878dccSnicm codelen = strlen(code); 1134e5878dccSnicm while (len < off + size + codelen + 1) { 1135e5878dccSnicm buf = xreallocarray(buf, 2, len); 1136e5878dccSnicm len *= 2; 1137e5878dccSnicm } 1138e5878dccSnicm memcpy(buf + off, code, codelen); 1139e5878dccSnicm off += codelen; 1140e5878dccSnicm } 1141e5878dccSnicm 114232b2c715Snicm if (flags & GRID_STRING_TRIM_SPACES) { 1143570a3589Snicm while (off > 0 && buf[off - 1] == ' ') 1144570a3589Snicm off--; 1145d5aaf01dSnicm } 1146b6d61ae9Snicm buf[off] = '\0'; 1147e5d24057Snicm 1148b6d61ae9Snicm return (buf); 1149b6d61ae9Snicm } 1150953d773cSnicm 1151953d773cSnicm /* 1152bd28ed35Snicm * Duplicate a set of lines between two grids. Both source and destination 1153bd28ed35Snicm * should be big enough. 1154953d773cSnicm */ 1155953d773cSnicm void 1156f7b15bf9Snicm grid_duplicate_lines(struct grid *dst, u_int dy, struct grid *src, u_int sy, 1157f7b15bf9Snicm u_int ny) 1158953d773cSnicm { 11599554a682Snicm struct grid_line *dstl, *srcl; 1160953d773cSnicm u_int yy; 1161953d773cSnicm 1162953d773cSnicm if (dy + ny > dst->hsize + dst->sy) 1163953d773cSnicm ny = dst->hsize + dst->sy - dy; 1164953d773cSnicm if (sy + ny > src->hsize + src->sy) 1165953d773cSnicm ny = src->hsize + src->sy - sy; 1166bd28ed35Snicm grid_free_lines(dst, dy, ny); 1167953d773cSnicm 1168953d773cSnicm for (yy = 0; yy < ny; yy++) { 1169b9fa2487Snicm srcl = &src->linedata[sy]; 1170b9fa2487Snicm dstl = &dst->linedata[dy]; 11719554a682Snicm 11729554a682Snicm memcpy(dstl, srcl, sizeof *dstl); 11739554a682Snicm if (srcl->cellsize != 0) { 1174471ad77dSderaadt dstl->celldata = xreallocarray(NULL, 11759554a682Snicm srcl->cellsize, sizeof *dstl->celldata); 11769554a682Snicm memcpy(dstl->celldata, srcl->celldata, 11779554a682Snicm srcl->cellsize * sizeof *dstl->celldata); 1178abb4058aSnicm } else 1179abb4058aSnicm dstl->celldata = NULL; 1180e931849fSnicm if (srcl->extdsize != 0) { 1181e931849fSnicm dstl->extdsize = srcl->extdsize; 1182e931849fSnicm dstl->extddata = xreallocarray(NULL, dstl->extdsize, 1183e931849fSnicm sizeof *dstl->extddata); 1184e931849fSnicm memcpy(dstl->extddata, srcl->extddata, dstl->extdsize * 1185e931849fSnicm sizeof *dstl->extddata); 11869a569e26Snicm } else 11879a569e26Snicm dstl->extddata = NULL; 1188e931849fSnicm 11899554a682Snicm sy++; 11909554a682Snicm dy++; 1191953d773cSnicm } 1192953d773cSnicm } 1193db4b44a2Snicm 1194f2ca94d1Snicm /* Mark line as dead. */ 1195f2ca94d1Snicm static void 1196f2ca94d1Snicm grid_reflow_dead(struct grid_line *gl) 1197f2ca94d1Snicm { 1198f2ca94d1Snicm memset(gl, 0, sizeof *gl); 1199f2ca94d1Snicm gl->flags = GRID_LINE_DEAD; 1200f2ca94d1Snicm } 1201f2ca94d1Snicm 1202f2ca94d1Snicm /* Add lines, return the first new one. */ 1203f2ca94d1Snicm static struct grid_line * 1204f2ca94d1Snicm grid_reflow_add(struct grid *gd, u_int n) 1205f2ca94d1Snicm { 1206f2ca94d1Snicm struct grid_line *gl; 1207f2ca94d1Snicm u_int sy = gd->sy + n; 1208f2ca94d1Snicm 1209f2ca94d1Snicm gd->linedata = xreallocarray(gd->linedata, sy, sizeof *gd->linedata); 1210f2ca94d1Snicm gl = &gd->linedata[gd->sy]; 1211f2ca94d1Snicm memset(gl, 0, n * (sizeof *gl)); 1212f2ca94d1Snicm gd->sy = sy; 1213f2ca94d1Snicm return (gl); 1214f2ca94d1Snicm } 1215f2ca94d1Snicm 1216f2ca94d1Snicm /* Move a line across. */ 1217f2ca94d1Snicm static struct grid_line * 1218f2ca94d1Snicm grid_reflow_move(struct grid *gd, struct grid_line *from) 1219f2ca94d1Snicm { 1220f2ca94d1Snicm struct grid_line *to; 1221f2ca94d1Snicm 1222f2ca94d1Snicm to = grid_reflow_add(gd, 1); 1223f2ca94d1Snicm memcpy(to, from, sizeof *to); 1224f2ca94d1Snicm grid_reflow_dead(from); 1225f2ca94d1Snicm return (to); 1226f2ca94d1Snicm } 1227f2ca94d1Snicm 1228414c61adSnicm /* Join line below onto this one. */ 12299883b791Snicm static void 1230f2ca94d1Snicm grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy, 1231db552f66Snicm u_int width, int already) 1232e931849fSnicm { 1233ffce0102Snicm struct grid_line *gl, *from = NULL; 1234414c61adSnicm struct grid_cell gc; 1235ffce0102Snicm u_int lines, left, i, to, line, want = 0; 1236f2ca94d1Snicm u_int at; 1237414c61adSnicm int wrapped = 1; 1238e931849fSnicm 1239f2ca94d1Snicm /* 1240f2ca94d1Snicm * Add a new target line. 1241f2ca94d1Snicm */ 1242f2ca94d1Snicm if (!already) { 1243f2ca94d1Snicm to = target->sy; 1244f2ca94d1Snicm gl = grid_reflow_move(target, &gd->linedata[yy]); 1245f2ca94d1Snicm } else { 1246f2ca94d1Snicm to = target->sy - 1; 1247f2ca94d1Snicm gl = &target->linedata[to]; 1248f2ca94d1Snicm } 1249f2ca94d1Snicm at = gl->cellused; 1250f2ca94d1Snicm 1251f2ca94d1Snicm /* 1252f2ca94d1Snicm * Loop until no more to consume or the target line is full. 1253f2ca94d1Snicm */ 1254414c61adSnicm lines = 0; 1255414c61adSnicm for (;;) { 1256414c61adSnicm /* 1257414c61adSnicm * If this is now the last line, there is nothing more to be 1258414c61adSnicm * done. 1259414c61adSnicm */ 1260c72e5d61Snicm if (yy + 1 + lines == gd->hsize + gd->sy) 1261414c61adSnicm break; 1262414c61adSnicm line = yy + 1 + lines; 1263e931849fSnicm 1264414c61adSnicm /* If the next line is empty, skip it. */ 1265414c61adSnicm if (~gd->linedata[line].flags & GRID_LINE_WRAPPED) 1266414c61adSnicm wrapped = 0; 1267414c61adSnicm if (gd->linedata[line].cellused == 0) { 1268414c61adSnicm if (!wrapped) 1269414c61adSnicm break; 1270c72e5d61Snicm lines++; 1271e931849fSnicm continue; 127244c84f9eSnicm } 127344c84f9eSnicm 1274db4b44a2Snicm /* 1275414c61adSnicm * Is the destination line now full? Copy the first character 1276414c61adSnicm * separately because we need to leave "from" set to the last 1277414c61adSnicm * line if this line is full. 1278db4b44a2Snicm */ 1279414c61adSnicm grid_get_cell1(&gd->linedata[line], 0, &gc); 1280414c61adSnicm if (width + gc.data.width > sx) 1281414c61adSnicm break; 1282414c61adSnicm width += gc.data.width; 1283f2ca94d1Snicm grid_set_cell(target, at, to, &gc); 1284414c61adSnicm at++; 1285414c61adSnicm 1286414c61adSnicm /* Join as much more as possible onto the current line. */ 1287414c61adSnicm from = &gd->linedata[line]; 1288414c61adSnicm for (want = 1; want < from->cellused; want++) { 1289414c61adSnicm grid_get_cell1(from, want, &gc); 1290414c61adSnicm if (width + gc.data.width > sx) 1291414c61adSnicm break; 1292414c61adSnicm width += gc.data.width; 1293414c61adSnicm 1294f2ca94d1Snicm grid_set_cell(target, at, to, &gc); 1295414c61adSnicm at++; 1296414c61adSnicm } 1297414c61adSnicm lines++; 1298414c61adSnicm 1299414c61adSnicm /* 1300414c61adSnicm * If this line wasn't wrapped or we didn't consume the entire 1301414c61adSnicm * line, don't try to join any further lines. 1302414c61adSnicm */ 1303414c61adSnicm if (!wrapped || want != from->cellused || width == sx) 1304414c61adSnicm break; 1305414c61adSnicm } 1306414c61adSnicm if (lines == 0) 1307414c61adSnicm return; 1308414c61adSnicm 1309414c61adSnicm /* 1310414c61adSnicm * If we didn't consume the entire final line, then remove what we did 1311414c61adSnicm * consume. If we consumed the entire line and it wasn't wrapped, 1312414c61adSnicm * remove the wrap flag from this line. 1313414c61adSnicm */ 1314414c61adSnicm left = from->cellused - want; 1315414c61adSnicm if (left != 0) { 1316414c61adSnicm grid_move_cells(gd, 0, want, yy + lines, left, 8); 1317414c61adSnicm from->cellsize = from->cellused = left; 1318414c61adSnicm lines--; 1319414c61adSnicm } else if (!wrapped) 1320414c61adSnicm gl->flags &= ~GRID_LINE_WRAPPED; 1321414c61adSnicm 1322414c61adSnicm /* Remove the lines that were completely consumed. */ 1323f2ca94d1Snicm for (i = yy + 1; i < yy + 1 + lines; i++) { 1324f2ca94d1Snicm free(gd->linedata[i].celldata); 1325f2ca94d1Snicm free(gd->linedata[i].extddata); 1326f2ca94d1Snicm grid_reflow_dead(&gd->linedata[i]); 1327414c61adSnicm } 1328414c61adSnicm 1329db552f66Snicm /* Adjust scroll position. */ 1330f2ca94d1Snicm if (gd->hscrolled > to + lines) 1331414c61adSnicm gd->hscrolled -= lines; 1332f2ca94d1Snicm else if (gd->hscrolled > to) 1333f2ca94d1Snicm gd->hscrolled = to; 1334414c61adSnicm } 1335414c61adSnicm 1336414c61adSnicm /* Split this line into several new ones */ 1337414c61adSnicm static void 1338f2ca94d1Snicm grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy, 1339db552f66Snicm u_int at) 1340db4b44a2Snicm { 1341f2ca94d1Snicm struct grid_line *gl = &gd->linedata[yy], *first; 1342414c61adSnicm struct grid_cell gc; 1343f2ca94d1Snicm u_int line, lines, width, i, xx; 1344f2ca94d1Snicm u_int used = gl->cellused; 1345414c61adSnicm int flags = gl->flags; 1346db4b44a2Snicm 1347f2ca94d1Snicm /* How many lines do we need to insert? We know we need at least two. */ 1348414c61adSnicm if (~gl->flags & GRID_LINE_EXTENDED) 1349f2ca94d1Snicm lines = 1 + (gl->cellused - 1) / sx; 1350414c61adSnicm else { 1351f2ca94d1Snicm lines = 2; 1352414c61adSnicm width = 0; 1353414c61adSnicm for (i = at; i < used; i++) { 1354414c61adSnicm grid_get_cell1(gl, i, &gc); 1355414c61adSnicm if (width + gc.data.width > sx) { 1356414c61adSnicm lines++; 1357414c61adSnicm width = 0; 1358414c61adSnicm } 1359414c61adSnicm width += gc.data.width; 1360414c61adSnicm } 1361414c61adSnicm } 136244c84f9eSnicm 1363414c61adSnicm /* Insert new lines. */ 1364f2ca94d1Snicm line = target->sy + 1; 1365f2ca94d1Snicm first = grid_reflow_add(target, lines); 1366414c61adSnicm 1367414c61adSnicm /* Copy sections from the original line. */ 1368414c61adSnicm width = 0; 1369414c61adSnicm xx = 0; 1370414c61adSnicm for (i = at; i < used; i++) { 1371414c61adSnicm grid_get_cell1(gl, i, &gc); 1372414c61adSnicm if (width + gc.data.width > sx) { 1373f2ca94d1Snicm target->linedata[line].flags |= GRID_LINE_WRAPPED; 1374414c61adSnicm 1375414c61adSnicm line++; 1376414c61adSnicm width = 0; 1377414c61adSnicm xx = 0; 1378414c61adSnicm } 1379414c61adSnicm width += gc.data.width; 1380f2ca94d1Snicm grid_set_cell(target, xx, line, &gc); 1381414c61adSnicm xx++; 1382414c61adSnicm } 1383414c61adSnicm if (flags & GRID_LINE_WRAPPED) 1384f2ca94d1Snicm target->linedata[line].flags |= GRID_LINE_WRAPPED; 1385f2ca94d1Snicm 1386f2ca94d1Snicm /* Move the remainder of the original line. */ 1387f2ca94d1Snicm gl->cellsize = gl->cellused = at; 1388f2ca94d1Snicm gl->flags |= GRID_LINE_WRAPPED; 1389f2ca94d1Snicm memcpy(first, gl, sizeof *first); 1390f2ca94d1Snicm grid_reflow_dead(gl); 1391414c61adSnicm 1392db552f66Snicm /* Adjust the scroll position. */ 1393414c61adSnicm if (yy <= gd->hscrolled) 1394f2ca94d1Snicm gd->hscrolled += lines - 1; 1395414c61adSnicm 1396414c61adSnicm /* 1397414c61adSnicm * If the original line had the wrapped flag and there is still space 1398414c61adSnicm * in the last new line, try to join with the next lines. 1399414c61adSnicm */ 1400414c61adSnicm if (width < sx && (flags & GRID_LINE_WRAPPED)) 1401db552f66Snicm grid_reflow_join(target, gd, sx, yy, width, 1); 1402414c61adSnicm } 1403414c61adSnicm 1404414c61adSnicm /* Reflow lines on grid to new width. */ 1405414c61adSnicm void 1406db552f66Snicm grid_reflow(struct grid *gd, u_int sx) 1407414c61adSnicm { 1408f2ca94d1Snicm struct grid *target; 1409414c61adSnicm struct grid_line *gl; 1410414c61adSnicm struct grid_cell gc; 141114b12344Snicm u_int yy, width, i, at; 1412414c61adSnicm 1413414c61adSnicm /* 1414f2ca94d1Snicm * Create a destination grid. This is just used as a container for the 1415f2ca94d1Snicm * line data and may not be fully valid. 1416f2ca94d1Snicm */ 1417f2ca94d1Snicm target = grid_create(gd->sx, 0, 0); 1418f2ca94d1Snicm 1419f2ca94d1Snicm /* 1420f2ca94d1Snicm * Loop over each source line. 1421414c61adSnicm */ 1422414c61adSnicm for (yy = 0; yy < gd->hsize + gd->sy; yy++) { 1423414c61adSnicm gl = &gd->linedata[yy]; 1424f2ca94d1Snicm if (gl->flags & GRID_LINE_DEAD) 1425f2ca94d1Snicm continue; 1426414c61adSnicm 1427f2ca94d1Snicm /* 142814b12344Snicm * Work out the width of this line. at is the point at which 142914b12344Snicm * the available width is hit, and width is the full line 143014b12344Snicm * width. 1431f2ca94d1Snicm */ 143214b12344Snicm at = width = 0; 1433414c61adSnicm if (~gl->flags & GRID_LINE_EXTENDED) { 1434414c61adSnicm width = gl->cellused; 1435414c61adSnicm if (width > sx) 1436414c61adSnicm at = sx; 143744c84f9eSnicm else 1438414c61adSnicm at = width; 143944c84f9eSnicm } else { 1440414c61adSnicm for (i = 0; i < gl->cellused; i++) { 1441414c61adSnicm grid_get_cell1(gl, i, &gc); 1442414c61adSnicm if (at == 0 && width + gc.data.width > sx) 1443414c61adSnicm at = i; 1444414c61adSnicm width += gc.data.width; 1445db4b44a2Snicm } 1446db4b44a2Snicm } 1447db4b44a2Snicm 1448414c61adSnicm /* 144914b12344Snicm * If the line is exactly right, just move it across 145014b12344Snicm * unchanged. 1451414c61adSnicm */ 145214b12344Snicm if (width == sx) { 1453f2ca94d1Snicm grid_reflow_move(target, gl); 1454414c61adSnicm continue; 1455f2ca94d1Snicm } 1456414c61adSnicm 1457414c61adSnicm /* 1458414c61adSnicm * If the line is too big, it needs to be split, whether or not 1459414c61adSnicm * it was previously wrapped. 1460414c61adSnicm */ 1461414c61adSnicm if (width > sx) { 1462db552f66Snicm grid_reflow_split(target, gd, sx, yy, at); 1463414c61adSnicm continue; 1464414c61adSnicm } 1465414c61adSnicm 1466414c61adSnicm /* 1467414c61adSnicm * If the line was previously wrapped, join as much as possible 1468414c61adSnicm * of the next line. 1469414c61adSnicm */ 1470414c61adSnicm if (gl->flags & GRID_LINE_WRAPPED) 1471db552f66Snicm grid_reflow_join(target, gd, sx, yy, width, 0); 1472f2ca94d1Snicm else 1473f2ca94d1Snicm grid_reflow_move(target, gl); 1474414c61adSnicm } 1475414c61adSnicm 1476f2ca94d1Snicm /* 1477f2ca94d1Snicm * Replace the old grid with the new. 1478f2ca94d1Snicm */ 1479f2ca94d1Snicm if (target->sy < gd->sy) 1480f2ca94d1Snicm grid_reflow_add(target, gd->sy - target->sy); 1481f2ca94d1Snicm gd->hsize = target->sy - gd->sy; 148256aa75d3Snicm if (gd->hscrolled > gd->hsize) 148356aa75d3Snicm gd->hscrolled = gd->hsize; 1484f2ca94d1Snicm free(gd->linedata); 1485f2ca94d1Snicm gd->linedata = target->linedata; 1486f2ca94d1Snicm free(target); 1487db552f66Snicm } 1488f2ca94d1Snicm 148936f15d15Snicm /* Convert to position based on wrapped lines. */ 1490db552f66Snicm void 149136f15d15Snicm grid_wrap_position(struct grid *gd, u_int px, u_int py, u_int *wx, u_int *wy) 1492db552f66Snicm { 149336f15d15Snicm u_int ax = 0, ay = 0, yy; 1494db552f66Snicm 149536f15d15Snicm for (yy = 0; yy < py; yy++) { 149636f15d15Snicm if (gd->linedata[yy].flags & GRID_LINE_WRAPPED) 149736f15d15Snicm ax += gd->linedata[yy].cellused; 149836f15d15Snicm else { 149936f15d15Snicm ax = 0; 150036f15d15Snicm ay++; 150136f15d15Snicm } 150236f15d15Snicm } 150336f15d15Snicm if (px >= gd->linedata[yy].cellused) 150436f15d15Snicm ax = UINT_MAX; 150536f15d15Snicm else 150636f15d15Snicm ax += px; 150736f15d15Snicm *wx = ax; 150836f15d15Snicm *wy = ay; 150936f15d15Snicm } 151036f15d15Snicm 151136f15d15Snicm /* Convert position based on wrapped lines back. */ 151236f15d15Snicm void 151336f15d15Snicm grid_unwrap_position(struct grid *gd, u_int *px, u_int *py, u_int wx, u_int wy) 151436f15d15Snicm { 151545c83cdbSnicm u_int yy, ay = 0; 1516db552f66Snicm 1517db552f66Snicm for (yy = 0; yy < gd->hsize + gd->sy - 1; yy++) { 151836f15d15Snicm if (ay == wy) 1519db552f66Snicm break; 152045c83cdbSnicm if (~gd->linedata[yy].flags & GRID_LINE_WRAPPED) 152136f15d15Snicm ay++; 1522db552f66Snicm } 152336f15d15Snicm 152436f15d15Snicm /* 152536f15d15Snicm * yy is now 0 on the unwrapped line which contains wx. Walk forwards 152636f15d15Snicm * until we find the end or the line now containing wx. 152736f15d15Snicm */ 152836f15d15Snicm if (wx == UINT_MAX) { 152936f15d15Snicm while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) 153036f15d15Snicm yy++; 153136f15d15Snicm wx = gd->linedata[yy].cellused; 153236f15d15Snicm } else { 153336f15d15Snicm while (gd->linedata[yy].flags & GRID_LINE_WRAPPED) { 153436f15d15Snicm if (wx < gd->linedata[yy].cellused) 153536f15d15Snicm break; 153636f15d15Snicm wx -= gd->linedata[yy].cellused; 153736f15d15Snicm yy++; 153836f15d15Snicm } 153936f15d15Snicm } 154036f15d15Snicm *px = wx; 1541db552f66Snicm *py = yy; 1542db4b44a2Snicm } 15437db4c597Snicm 15447db4c597Snicm /* Get length of line. */ 15457db4c597Snicm u_int 15467db4c597Snicm grid_line_length(struct grid *gd, u_int py) 15477db4c597Snicm { 15487db4c597Snicm struct grid_cell gc; 15497db4c597Snicm u_int px; 15507db4c597Snicm 15517db4c597Snicm px = grid_get_line(gd, py)->cellsize; 15527db4c597Snicm if (px > gd->sx) 15537db4c597Snicm px = gd->sx; 15547db4c597Snicm while (px > 0) { 15557db4c597Snicm grid_get_cell(gd, px - 1, py, &gc); 15562b07bb4fSnicm if ((gc.flags & GRID_FLAG_PADDING) || 15572b07bb4fSnicm gc.data.size != 1 || 15582b07bb4fSnicm *gc.data.data != ' ') 15597db4c597Snicm break; 15607db4c597Snicm px--; 15617db4c597Snicm } 15627db4c597Snicm return (px); 15637db4c597Snicm } 1564*61e9d0deSnicm 1565*61e9d0deSnicm /* Check if character is in set. */ 1566*61e9d0deSnicm int 1567*61e9d0deSnicm grid_in_set(struct grid *gd, u_int px, u_int py, const char *set) 1568*61e9d0deSnicm { 1569*61e9d0deSnicm struct grid_cell gc, tmp_gc; 1570*61e9d0deSnicm u_int pxx; 1571*61e9d0deSnicm 1572*61e9d0deSnicm grid_get_cell(gd, px, py, &gc); 1573*61e9d0deSnicm if (strchr(set, '\t')) { 1574*61e9d0deSnicm if (gc.flags & GRID_FLAG_PADDING) { 1575*61e9d0deSnicm pxx = px; 1576*61e9d0deSnicm do 1577*61e9d0deSnicm grid_get_cell(gd, --pxx, py, &tmp_gc); 1578*61e9d0deSnicm while (pxx > 0 && tmp_gc.flags & GRID_FLAG_PADDING); 1579*61e9d0deSnicm if (tmp_gc.flags & GRID_FLAG_TAB) 1580*61e9d0deSnicm return (tmp_gc.data.width - (px - pxx)); 1581*61e9d0deSnicm } else if (gc.flags & GRID_FLAG_TAB) 1582*61e9d0deSnicm return (gc.data.width); 1583*61e9d0deSnicm } 1584*61e9d0deSnicm if (gc.flags & GRID_FLAG_PADDING) 1585*61e9d0deSnicm return (0); 1586*61e9d0deSnicm return (utf8_cstrhas(set, &gc.data)); 1587*61e9d0deSnicm } 1588